Skip to content

feat: read SQL query from file with -f/--file flag (#157)#165

Merged
vmvarela merged 6 commits into
masterfrom
issue-157/query-from-file
Jun 13, 2026
Merged

feat: read SQL query from file with -f/--file flag (#157)#165
vmvarela merged 6 commits into
masterfrom
issue-157/query-from-file

Conversation

@vmvarela

Copy link
Copy Markdown
Owner

Summary

Adds -f <file> / --file <file> flag to read the SQL query from a file instead of the command line.

Closes #157

Changes

  • src/args.zig: Added query_file field to ParsedArgs, flag parsing for -f/--file/-f=/--file=, updated usage text
  • src/main.zig: Reads query file contents after arg parsing, errors with exit 1 if file can't be read
  • build.zig: 6 integration tests covering all flag forms and error case

Usage

sql-pipe -f analysis.sql data.csv
cat data.csv | sql-pipe -f analysis.sql
sql-pipe --file=query.sql data.csv

Acceptance Criteria

  • -f <file> / --file <file> reads SQL query from file
  • Works with both file arguments and stdin input
  • Error message if file doesn't exist or can't be read
  • All existing tests pass
  • New tests cover file-based queries

@vmvarela vmvarela added type:feature New functionality priority:medium Should be done soon size:xs Trivial — less than 1 hour labels Jun 13, 2026
@vmvarela

Copy link
Copy Markdown
Owner Author

Code Review

🐛 Bug found: Memory leak

// Before (leak):
const contents = std.Io.Dir.cwd().readFileAlloc(io.io(), path, **allocator**, .limited(...));

The query file contents were allocated on the GPA allocator (gpa) but never freed. All other parsed-args data uses the arena allocator, which is freed once at the end. Fixed by using args_arena.allocator().

🐛 Bug found: MissingQuery handler missing

error.MissingQuery was not handled in the error switch — it fell through to else => {} which printed usage without any error message. Added a specific handler that prints "error: no SQL query provided" before usage.

🐛 Missing -f value produced wrong error

When sql-pipe -f (with no file path) was invoked, it returned error.MissingQuery — semantically incorrect (the query isn't missing, the file path is). Fixed by adding InvalidQueryFile error variant and guarding against empty paths from -f= / --file=.

✅ Tests and linter pass

zig build test, zig build unit-test, and ziglint src build.zig all green.

@vmvarela

Copy link
Copy Markdown
Owner Author

Council Review — Summary

Three expert models (alpha, gamma) reviewed this PR. Key findings:

New issue found: Empty query file → cryptic SQL error (High)

When -f empty.sql is used with a zero-byte file, readFileAlloc returns "", which gets passed to SQLite. The user sees a confusing SQL parse error instead of a clear message.

Fix: Added explicit contents.len == 0 check after reading the file, with a clear error: "query file '{s}' is empty".

Verified fixes from first review

Both councillors confirmed all four earlier fixes (memory leak, MissingQuery handler, InvalidQueryFile variant, empty path guard) are correct and complete.

Remaining recommendations (low priority — can be tracked as follow-up issues)

  • Add tests for -f combined with --output, --disk, and JSON output format
  • Consider mutual exclusion check for -f + --columns/--validate/--sample (currently silently ignored)
  • Path with spaces edge case test

Consensus: Approve

Both councillors recommended merging. The implementation is clean, follows codebase conventions, and the critical gaps found (memory leak, empty file) have been addressed.

vmvarela added 3 commits June 13, 2026 10:44
- Add conflict checks for -f with --columns/--validate/--sample
- Detect multiple -f/--file flags (error: only one allowed)
- Trim whitespace from query file before empty check
- Add 6 integration tests (157h-157l) for new error paths
- Update man page SYNOPSIS with stdin form
@vmvarela

Copy link
Copy Markdown
Owner Author

Oracle Review — Fixes Applied

@oracle found 3 critical bugs and several important gaps. Fixes applied:

✅ Fixed

# Issue Fix
1 -f con --columns/--validate/--sample ignorado silenciosamente Conflict checks extendidos: query_file != null ahora detecta el conflicto
2 Múltiples -f flags sobrescriben silenciosamente Nuevo error MultipleQueryFiles — "only one -f/--file flag is allowed"
3 Query file con solo whitespace pasa el check len == 0 std.mem.trim() antes del check — ahora detecta archivos con solo espacios/tabs/newlines

ℹ️ No fixado (decisión deliberada)

# Issue Razón
4 -f sin input de datos da error SQL confuso Comportamiento consistente con queries posicionales. SELECT 1 sin datos debe funcionar. Romperlo sería peor UX.

Tests agregados (157h-157l)

  • 157h: Query file con solo whitespace → error "is empty"
  • 157i: -f + --columns → error "cannot be combined"
  • 157j: -f + --validate → error "cannot be combined"
  • 157k: -f + --sample → error "cannot be combined"
  • 157l: Múltiples -f → error "only one -f/--file"

Man page

  • SYNOPSIS actualizado con forma stdin: sql-pipe -f <file> [OPTIONS]

Verificación

  • zig build test
  • zig build unit-test
  • ziglint src build.zig

@vmvarela vmvarela merged commit 2a0a866 into master Jun 13, 2026
4 checks passed
@vmvarela vmvarela deleted the issue-157/query-from-file branch June 13, 2026 09:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

priority:medium Should be done soon size:xs Trivial — less than 1 hour type:feature New functionality

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Query from file with -f/--file flag

1 participant