Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ on:
env:
SQLITE_VERSION: "3490100"
SQLITE_YEAR: "2025"
ZIG_VERSION: "0.15.0"
ZIG_VERSION: "0.16.0"

jobs:
build-and-test:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ on:
env:
SQLITE_VERSION: "3490100"
SQLITE_YEAR: "2025"
ZIG_VERSION: "0.15.0"
ZIG_VERSION: "0.16.0"

jobs:
build:
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ $ cat events.csv \
| `--no-type-inference` | Treat all columns as TEXT (skip auto-detection) |
| `-H`, `--header` | Print column names as the first output row |
| `--json` | Output results as a JSON array of objects (mutually exclusive with `-d`, `--tsv`, `-H`) |
| `--max-rows <n>` | Stop if more than `n` data rows are read (exit 1) |
| `-h`, `--help` | Show usage help and exit |
| `-V`, `--version` | Print version and exit |

Expand Down
56 changes: 53 additions & 3 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ pub fn build(b: *std.Build) void {
[]const u8,
"version",
"Override version string (default: from build.zig.zon)",
) orelse "0.2.0";
) orelse "0.0.0-dev";

const exe = b.addExecutable(.{
.name = "sql-pipe",
Expand All @@ -35,8 +35,8 @@ pub fn build(b: *std.Build) void {
exe.root_module.addOptions("build_options", build_options);

if (bundle_sqlite) {
exe.addIncludePath(b.path("lib"));
exe.addCSourceFile(.{
exe.root_module.addIncludePath(b.path("lib"));
exe.root_module.addCSourceFile(.{
.file = b.path("lib/sqlite3.c"),
.flags = &.{"-DSQLITE_OMIT_LOAD_EXTENSION=1"},
});
Expand Down Expand Up @@ -230,6 +230,56 @@ pub fn build(b: *std.Build) void {
test_dup_col_stdout.step.dependOn(b.getInstallStep());
test_step.dependOn(&test_dup_col_stdout.step);

// Integration test 20: --max-rows under limit succeeds
const test_max_rows_under = b.addSystemCommand(&.{
"bash", "-c",
\\printf 'name,age\nAlice,30\nBob,25\n' | ./zig-out/bin/sql-pipe --max-rows 5 'SELECT name FROM t ORDER BY name' | diff - <(printf 'Alice\nBob\n')
});
test_max_rows_under.step.dependOn(b.getInstallStep());
test_step.dependOn(&test_max_rows_under.step);

// Integration test 21: --max-rows limit hit exits 1 with error message
const test_max_rows_hit = b.addSystemCommand(&.{
"bash", "-c",
\\msg=$(printf 'name,age\nAlice,30\nBob,25\nCarol,35\n' | ./zig-out/bin/sql-pipe --max-rows 1 'SELECT * FROM t' 2>&1 >/dev/null; echo "EXIT:$?")
\\echo "$msg" | grep -q 'error: input exceeds --max-rows limit (1 rows)' && echo "$msg" | grep -q 'EXIT:1'
});
test_max_rows_hit.step.dependOn(b.getInstallStep());
test_step.dependOn(&test_max_rows_hit.step);

// Integration test 22: --max-rows 0 exits 1 with error message
const test_max_rows_zero = b.addSystemCommand(&.{
"bash", "-c",
\\msg=$(printf 'name,age\nAlice,30\n' | ./zig-out/bin/sql-pipe --max-rows 0 'SELECT * FROM t' 2>&1 >/dev/null; echo "EXIT:$?")
\\echo "$msg" | grep -q 'error: --max-rows must be a positive integer' && echo "$msg" | grep -q 'EXIT:1'
});
test_max_rows_zero.step.dependOn(b.getInstallStep());
test_step.dependOn(&test_max_rows_zero.step);

// Integration test 23: --max-rows=5 (equals form) succeeds
const test_max_rows_equals = b.addSystemCommand(&.{
"bash", "-c",
\\printf 'name,age\nAlice,30\nBob,25\n' | ./zig-out/bin/sql-pipe --max-rows=5 'SELECT name FROM t ORDER BY name' | diff - <(printf 'Alice\nBob\n')
});
test_max_rows_equals.step.dependOn(b.getInstallStep());
test_step.dependOn(&test_max_rows_equals.step);

// Integration test 24: --max-rows with non-numeric value exits 1
const test_max_rows_nan = b.addSystemCommand(&.{
"bash", "-c",
\\printf 'name,age\nAlice,30\n' | ./zig-out/bin/sql-pipe --max-rows abc 'SELECT * FROM t' 2>&1 >/dev/null; test $? -eq 1
});
test_max_rows_nan.step.dependOn(b.getInstallStep());
test_step.dependOn(&test_max_rows_nan.step);

// Integration test 25: --no-type-inference --max-rows 1 hits limit and exits 1
const test_max_rows_streaming = b.addSystemCommand(&.{
"bash", "-c",
\\msg=$(printf 'name,age\nAlice,30\nBob,25\n' | ./zig-out/bin/sql-pipe --no-type-inference --max-rows 1 'SELECT * FROM t' 2>&1 >/dev/null; echo "EXIT:$?") && echo "$msg" | grep -q 'EXIT:1'
});
test_max_rows_streaming.step.dependOn(b.getInstallStep());
test_step.dependOn(&test_max_rows_streaming.step);

// Unit tests for the RFC 4180 CSV parser (src/csv.zig)
const unit_tests = b.addTest(.{
.root_module = b.createModule(.{
Expand Down
4 changes: 2 additions & 2 deletions build.zig.zon
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
.{
.name = .sql_pipe,
.version = "0.2.0",
.version = "0.0.0-dev",
.fingerprint = 0xf649b9ac95d768ab,
.minimum_zig_version = "0.15.0",
.minimum_zig_version = "0.16.0",
.paths = .{"."},
.dependencies = .{},
}
5 changes: 5 additions & 0 deletions docs/sql-pipe.1.scd
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@ OPTIONS
number, or null). Mutually exclusive with *--delimiter*, *--tsv*, and
*--header*.

*--max-rows* <n>
Stop reading input after inserting <n> data rows and exit with
code 1 and an error message. Use this to guard against
accidentally piping extremely large files into memory.

*-h, --help*
Print the help message and exit with code 0.

Expand Down
Loading
Loading