Skip to content

[CXH-16] fix list tables pagination adding database and schema as constraints#104

Merged
btipling merged 5 commits intomainfrom
luisinasantos/fix-table-pagination
Feb 24, 2026
Merged

[CXH-16] fix list tables pagination adding database and schema as constraints#104
btipling merged 5 commits intomainfrom
luisinasantos/fix-table-pagination

Conversation

@luisina-santos
Copy link
Contributor

@luisina-santos luisina-santos commented Feb 23, 2026

Summary by CodeRabbit

  • New Features

    • Lists schemas first and pages tables per-schema for more reliable, multi-level pagination.
    • Recognizes shared/system databases and skips INFORMATION_SCHEMA during discovery.
  • Bug Fixes

    • Scoped table discovery to database/schema context for improved accuracy.
    • Next-page tokens now advance predictably across schemas and their tables; returns empty results when no schemas present.
  • Style / Messages

    • Error and debug messages now reference schema-level operations for clearer troubleshooting.

@luisina-santos luisina-santos requested a review from a team February 23, 2026 18:22
@coderabbitai
Copy link

coderabbitai bot commented Feb 23, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

List pagination now enumerates schemas in a database into a pagination.Bag (skipping INFORMATION_SCHEMA) and then pages tables per-schema (page size 200) using schema-scoped table listing. Snowflake client gains schema listing and a ListTablesInSchema API with simplified single-part cursors.

Changes

Cohort / File(s) Summary
Connector pagination / listing
pkg/connector/tables.go
Rewrote listing to populate a pagination.Bag of schemas and iterate per-schema. On first call, list schemas (exclude INFORMATION_SCHEMA) and push PageState entries; then list tables via schema-scoped calls (limit 200), update per-schema cursors, and use Bag.NextToken for cross-schema pagination. Added pagination import and updated error messages.
Snowflake schema/table APIs
pkg/snowflake/table.go
Added Schema type and ListSchemasInDatabase(ctx, databaseName). Replaced ListTablesInAccount with ListTablesInSchema(ctx, databaseName, schemaName, cursor, limit), switched queries to SHOW ... IN SCHEMA "<db>"."<schema>", simplified cursor to last table name, updated parsing, error messages, and logging to schema context; removed legacy multi-part cursor handling.
sequenceDiagram
  autonumber
  participant Connector as Connector (pagination)
  participant Bag as pagination.Bag
  participant Snowflake as Snowflake Client
  participant ResourceSink as Resource Iterator

  Connector->>Snowflake: ListSchemasInDatabase(database)
  Snowflake-->>Connector: schemas[]
  Connector->>Bag: push(schemas as PageState)
  loop per-schema
    Connector->>Bag: pop() -> schemaPage
    Connector->>Snowflake: ListTablesInSchema(database, schemaPage.ResourceID, schemaPage.Cursor, limit=200)
    Snowflake-->>Connector: tables[], nextCursor
    Connector->>ResourceSink: emit(table resources)
    alt nextCursor != ""
      Connector->>Bag: update current schemaPage cursor = nextCursor
    else
      Connector->>Bag: advance to next schema
    end
  end
  Connector->>ResourceSink: return nextToken(from Bag) / finished
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐇 I hopped through databases, schemas in a bag,
Pushed each name in tight and neat, no lag.
Tables unfurled in two-hundred streams,
Cursors trimmed to simpler dreams,
A rabbit applauds this tidy tagging! 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title clearly summarizes the main change: adding database and schema constraints to fix list tables pagination, which aligns with the substantive changes across both modified files.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch luisinasantos/fix-table-pagination

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pkg/snowflake/table.go`:
- Around line 111-116: The current database-level pagination using SHOW TABLES
IN DATABASE and setting nextCursor = last.Name (see tables, nextCursor,
last.Name) is incorrect across schemas and will skip/duplicate entries; replace
it by either (A) enumerating per schema: list schemas, then call SHOW TABLES IN
SCHEMA for each schema and maintain a cursor per schema, or (B) switch to
querying INFORMATION_SCHEMA.TABLES with an explicit ORDER BY schema_name,
table_name and use LIMIT/OFFSET or a composite keyset cursor (schema_name +
table_name) instead of last.Name; update the pagination logic that builds the
query and the nextCursor value to use the composite cursor or per-schema cursors
accordingly (modify the code around where SHOW TABLES IN DATABASE is issued and
where nextCursor is computed).

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 555f978 and 31c794c.

📒 Files selected for processing (2)
  • pkg/connector/tables.go
  • pkg/snowflake/table.go

@luisina-santos luisina-santos force-pushed the luisinasantos/fix-table-pagination branch from 31c794c to e493bf0 Compare February 23, 2026 19:30
@luisina-santos luisina-santos changed the title fix list tables pagination adding database as constraint fix list tables pagination adding database and schema as constraints Feb 23, 2026
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
pkg/connector/tables.go (1)

139-143: GetDatabase is called on every page — consider caching isSharedOrSystemDB in the bag state.

The database's shared/system status is constant across pages. Repeating this network call for every page of a large result set wastes one round-trip per page.

One approach: store the flag in the first PageState (e.g., in ResourceTypeID or a separate token prefix), or derive it from the parentResourceID profile if it's already stored there.

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pkg/snowflake/table.go`:
- Around line 51-79: ListSchemasInDatabase currently lacks the 422/privilege
error handling present in ListTablesInSchema; add the same privilege-check
pattern by extracting the logger with l := ctxzap.Extract(ctx) at the top of
ListSchemasInDatabase, then in the resp2 handling block (where resp2 and
response are used) detect the 422 privilege response the same way
ListTablesInSchema does, log a debug message including databaseName and
response/statement handle, and return a gRPC PermissionDenied error with
contextual schema/database info instead of the raw HTTP error; mirror the exact
checks and logging used in ListTablesInSchema for resp2/response to keep
behavior consistent.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 31c794c and e493bf0.

📒 Files selected for processing (2)
  • pkg/connector/tables.go
  • pkg/snowflake/table.go

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
pkg/snowflake/table.go (1)

155-174: ⚠️ Potential issue | 🟠 Major

Guard resp1/resp2 before deferring closeResponseBody.

Same nil-check risk here as above; a nil response on network error can panic when closing.

🛠️ Suggested fix
 resp1, err := c.Do(req, uhttp.WithJSONResponse(&response))
-defer closeResponseBody(resp1)
+if resp1 != nil {
+	defer closeResponseBody(resp1)
+}
 ...
 resp2, err := c.Do(req, uhttp.WithJSONResponse(&response))
-defer closeResponseBody(resp2)
+if resp2 != nil {
+	defer closeResponseBody(resp2)
+}

As per coding guidelines, "Always check if http.Response is not nil before attempting to close resp.Body, as resp can be nil on network errors".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/snowflake/table.go` around lines 155 - 174, The defer calls to
closeResponseBody use resp1/resp2 which can be nil on network errors; update the
code around the c.Do calls that populate response (the ListTablesRawResponse
path using resp1 and the subsequent GetStatementResponse + c.Do using resp2) to
guard before deferring by checking the response is non-nil (e.g., if resp1 !=
nil { defer closeResponseBody(resp1) }) and do the same for resp2 so
closeResponseBody is only deferred when the http.Response exists.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pkg/snowflake/table.go`:
- Around line 189-193: The current logic that sets nextCursor uses if
len(tables) >= limit which can panic when limit == 0 and tables is empty; update
the condition around the slice access in pkg/snowflake/table.go (the block that
sets nextCursor based on tables and limit) to ensure you only index tables when
limit > 0 and len(tables) > 0 (e.g. require limit > 0 && len(tables) >= limit or
check len(tables) > 0 before using tables[len(tables)-1]), so you never access
tables[len(tables)-1] on an empty slice.
- Around line 64-82: The defer calls to closeResponseBody for resp1 and resp2
can panic because resp may be nil on network errors; update the code around the
c.Do calls (where resp1 and resp2 are returned) to only defer closeResponseBody
when the response is non-nil (e.g., check resp1 != nil before deferring, or use
a short inline defer func() that checks the variable for nil before calling
closeResponseBody), keeping the surrounding error handling in
GetStatementResponse and ListSchemasRawResponse logic intact so you still return
errors as before.

---

Outside diff comments:
In `@pkg/snowflake/table.go`:
- Around line 155-174: The defer calls to closeResponseBody use resp1/resp2
which can be nil on network errors; update the code around the c.Do calls that
populate response (the ListTablesRawResponse path using resp1 and the subsequent
GetStatementResponse + c.Do using resp2) to guard before deferring by checking
the response is non-nil (e.g., if resp1 != nil { defer closeResponseBody(resp1)
}) and do the same for resp2 so closeResponseBody is only deferred when the
http.Response exists.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e493bf0 and e7758a9.

📒 Files selected for processing (1)
  • pkg/snowflake/table.go

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pkg/snowflake/table.go`:
- Around line 51-92: In ListSchemasInDatabase, several raw errors are returned
directly; wrap each with the "baton-snowflake" prefix using fmt.Errorf("%s: %w",
...) before returning. Specifically, wrap errors returned from
c.PostStatementRequest (when err != nil), from the first c.Do call (the
non-permission-denied branch where you currently return err), from
c.GetStatementResponse (when err != nil), and from the second c.Do call
(non-permission-denied branch). Use descriptive messages (e.g.,
"baton-snowflake: failed to post statement", "baton-snowflake: failed to fetch
statement response", "baton-snowflake: failed to get statement result") and %w
to preserve error chaining when returning the error from ListSchemasInDatabase.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e7758a9 and 8eb28c8.

📒 Files selected for processing (2)
  • pkg/connector/tables.go
  • pkg/snowflake/table.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • pkg/connector/tables.go

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pkg/connector/tables.go`:
- Around line 139-165: When seeding the pagination bag in ListTables (bag,
bag.Unmarshal, bag.Current), handle GetDatabase returning UnprocessableEntity or
nil parentDB instead of failing: call o.client.GetDatabase(ctx, databaseName)
inside a branch that treats UnprocessableEntity as "shared/system" (use
isDBSharedOrSystem or replicate its logic) and set sharedFlag = "shared" when
appropriate; if GetDatabase returns nil without error, treat as non-shared but
avoid dereferencing parentDB; only return an error for unexpected GetDatabase
failures, then continue to call o.client.ListSchemasInDatabase and seed the bag
with PageState.ResourceTypeID set correctly.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8eb28c8 and fb28257.

📒 Files selected for processing (1)
  • pkg/connector/tables.go

@luisina-santos luisina-santos changed the title fix list tables pagination adding database and schema as constraints [CXH-16] fix list tables pagination adding database and schema as constraints Feb 23, 2026
@linear
Copy link

linear bot commented Feb 23, 2026

@btipling btipling merged commit e555016 into main Feb 24, 2026
8 checks passed
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.

2 participants