Skip to content

feat(datasources): add query subcommand and reshape preview-query response#41

Merged
joalves merged 13 commits into
mainfrom
feat/datasources-query
May 28, 2026
Merged

feat(datasources): add query subcommand and reshape preview-query response#41
joalves merged 13 commits into
mainfrom
feat/datasources-query

Conversation

@joalves
Copy link
Copy Markdown
Collaborator

@joalves joalves commented May 28, 2026

Summary

  • New abs datasources query <id> [sql] subcommand for ad-hoc SQL: takes the datasource id positionally and SQL either positionally, via --sql <text>, or via --sql - (stdin). Optional --limit <n>. --json-config retained as an escape hatch.
  • Both query and preview-query now reshape the columnar API response ({ columnNames, columnTypes, rows }) into row-objects via the existing columnarToRows helper, so -o table, -o vertical, -o json, etc. render naturally. Same convention events list already uses.
  • --raw preserves the original columnar payload on both commands.

Before

$ abs ds preview-query --json-config '{...}' -o vertical
columnNames: experiment_id, cnt
columnTypes: INT64, INT64
       rows: 1, 538217, 5, 250000, 7, 244786, ...

After

$ abs ds query 6 "SELECT experiment_id, COUNT(*) AS cnt FROM exposures GROUP BY 1" --limit 3
┌───────────────┬────────┐
│ experiment_id │ cnt    │
├───────────────┼────────┤
│ 1             │ 538217 │
│ 5             │ 250000 │
│ 7             │ 244786 │
└───────────────┴────────┘

$ abs ds query 6 "..." -o vertical
*** 1. row ***
experiment_id: 1
          cnt: 538217
...

Behavior change

preview-query's default output shape flips from { columnNames, columnTypes, rows } to Array<Record<string, unknown>>. Scripts parsing rows[i][j] will break; pass --raw to restore the previous shape exactly.

Test plan

  • Unit tests cover positional/--sql/--limit/--json-config, every mutual-exclusion error path, stdin reading + empty-stdin rejection, reshape default, --raw passthrough — for both query and preview-query.
  • npm run typecheck clean
  • npm run lint clean
  • npm run test:run — 2465 passed / 4 skipped / 0 failed
  • Manual E2E against dev-1 (datasource 6, BigQuery): positional + --limit → table; -o vertical → per-row transpose; --raw -o yaml → columnar payload preserved; echo "SELECT 1" | abs ds query 6 --sql - → table; validation errors exit 1 with clear messages.

Out of scope

  • No changes to validate-query, introspect, test, schema, or any other datasources subcommand.
  • No client-side SQL parsing; server is authoritative.
  • No streaming/pagination — --limit is the only knob.

Summary by CodeRabbit

  • New Features
    • Added a datasources query subcommand with inline SQL, --sql (including --sql - to read from stdin), --limit, --json-config and a --raw option to preserve columnar output; default reshapes to row objects.
  • Documentation
    • Updated datasources docs with query examples and explanation of output formats and --raw.
  • Tests
    • Expanded test coverage for query paths, stdin handling, input validation and --raw vs reshaped output.
  • Chores
    • Added a utility to read stdin text and wired input validation with clear error behaviour.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 28, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 25bbb7cf-b8fd-49f0-b19a-b976c561f5ed

📥 Commits

Reviewing files that changed from the base of the PR and between 04e1940 and a3187e8.

📒 Files selected for processing (2)
  • src/commands/datasources/datasources.test.ts
  • src/commands/datasources/index.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/commands/datasources/index.ts
  • src/commands/datasources/datasources.test.ts

Walkthrough

Adds a new datasources query subcommand supporting SQL via positional arg, --sql (including - to read from stdin), or --json-config, with optional --limit. Introduces readStdinText() to consume piped stdin, re-exports columnarToRows from core datasources, applies columnar->row reshaping to preview-query and query outputs by default (unless --raw), and includes comprehensive tests plus README examples.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • absmartly/cli-ts#34: Updates datasources CLI flag handling from --config to --json-config, related to this PR's --json-config handling.

Poem

I nibble lines of SQL so fine,
From stdin or flag, each trailing newline,
Columns fold into tidy rows,
--raw keeps them as the server shows,
A hopping CLI, ready to dine 🐇

🚥 Pre-merge checks | ✅ 4 | ❌ 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 (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the two main changes: adding a new query subcommand and reshaping the preview-query response to convert columnar format to row objects.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/datasources-query

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

src/commands/datasources/datasources.test.ts

ESLint skipped: missing config or dependency (missing-dependency). The ESLint configuration references a package that is not available in the sandbox.

src/commands/datasources/index.ts

ESLint skipped: the ESLint configuration for this file references a package that is not available in the sandbox.


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

Copy link
Copy Markdown

@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)
src/lib/utils/stdin.ts (1)

34-43: ⚡ Quick win

Deduplicate stdin consumption logic to prevent drift.

readStdinText() repeats the same stdin-read path already used by readLinesFromStdin(). Consolidating to a single reader keeps behaviour changes in one place.

♻️ Proposed refactor
 export async function readLinesFromStdin(): Promise<string[]> {
-  if (!isStdinPiped()) {
-    console.error('Waiting for input on stdin... (pipe data or press Ctrl+D to end)');
-  }
-  const chunks: Buffer[] = [];
-  for await (const chunk of process.stdin) {
-    chunks.push(chunk as Buffer);
-  }
-  return Buffer.concat(chunks)
-    .toString('utf-8')
+  const text = await readStdinText();
+  return text
     .split('\n')
     .map((line) => line.trim())
     .filter((line) => line.length > 0);
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/utils/stdin.ts` around lines 34 - 43, The readStdinText() duplicates
the stdin consumption logic already implemented in readLinesFromStdin();
refactor to deduplicate by extracting the common reader or delegating: either
create a single helper (e.g., getStdinBuffer/getStdinChunks) that contains the
for-await loop and returns a Buffer/string, and have both readStdinText() and
readLinesFromStdin() call it, or implement readStdinText() by calling
readLinesFromStdin() and joining lines into a single string; update references
to readStdinText and readLinesFromStdin accordingly and ensure the initial
"Waiting for input..." message remains consistent in the single shared reader.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/commands/datasources/index.ts`:
- Around line 186-202: The code currently accepts --json-config but ignores the
parsed positional datasource id (variable id) allowing the JSON body to override
it; update the branch handling options.jsonConfig (where config =
validateJSON(...)) to enforce the positional id as authoritative by checking
config.datasource_id (or equivalent key) and either overwrite it with the
positional id (config.datasource_id = id) or fail-fast if the JSON value exists
and differs (throw or console.error + process.exit(1)); ensure you update the
same scope where validateJSON is called so downstream code uses the positional
id.

---

Nitpick comments:
In `@src/lib/utils/stdin.ts`:
- Around line 34-43: The readStdinText() duplicates the stdin consumption logic
already implemented in readLinesFromStdin(); refactor to deduplicate by
extracting the common reader or delegating: either create a single helper (e.g.,
getStdinBuffer/getStdinChunks) that contains the for-await loop and returns a
Buffer/string, and have both readStdinText() and readLinesFromStdin() call it,
or implement readStdinText() by calling readLinesFromStdin() and joining lines
into a single string; update references to readStdinText and readLinesFromStdin
accordingly and ensure the initial "Waiting for input..." message remains
consistent in the single shared reader.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7011b9c3-77de-4199-81d1-6616b2af1aee

📥 Commits

Reviewing files that changed from the base of the PR and between 5556a9d and 04e1940.

📒 Files selected for processing (7)
  • README.md
  • src/commands/datasources/datasources.test.ts
  • src/commands/datasources/index.ts
  • src/core/datasources/datasources.ts
  • src/core/datasources/index.ts
  • src/lib/utils/stdin.test.ts
  • src/lib/utils/stdin.ts

Comment thread src/commands/datasources/index.ts Outdated
Comment on lines +186 to +202
if (options.jsonConfig !== undefined) {
if (
positionalSql !== undefined ||
options.sql !== undefined ||
options.limit !== undefined
) {
console.error(
chalk.red(
'✗ --json-config cannot be combined with positional SQL, --sql, or --limit.'
)
);
process.exit(1);
}
config = validateJSON(options.jsonConfig, '--json-config') as Record<
string,
unknown
>;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Keep the positional <id> authoritative when --json-config is used.

This branch parses the required datasource ID and then ignores it. abs datasources query 6 --json-config '{"datasource_id":7,"query":"..."}' will run against datasource 7, which is very easy to miss in scripts. Please either inject datasource_id: id here or fail fast when the JSON body disagrees with the positional argument.

Suggested fix
-          config = validateJSON(options.jsonConfig, '--json-config') as Record<
-            string,
-            unknown
-          >;
+          const parsed = validateJSON(options.jsonConfig, '--json-config') as Record<
+            string,
+            unknown
+          >;
+          if (parsed.datasource_id !== undefined && parsed.datasource_id !== id) {
+            console.error(
+              chalk.red('✗ --json-config datasource_id must match the positional <id>.')
+            );
+            process.exit(1);
+          }
+          config = { ...parsed, datasource_id: id };
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/commands/datasources/index.ts` around lines 186 - 202, The code currently
accepts --json-config but ignores the parsed positional datasource id (variable
id) allowing the JSON body to override it; update the branch handling
options.jsonConfig (where config = validateJSON(...)) to enforce the positional
id as authoritative by checking config.datasource_id (or equivalent key) and
either overwrite it with the positional id (config.datasource_id = id) or
fail-fast if the JSON value exists and differs (throw or console.error +
process.exit(1)); ensure you update the same scope where validateJSON is called
so downstream code uses the positional id.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 28, 2026

Actionable comments posted: 0

@joalves joalves added this pull request to the merge queue May 28, 2026
Merged via the queue into main with commit b211c63 May 28, 2026
5 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.

1 participant