Skip to content
Open
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
35 changes: 34 additions & 1 deletion axiom/cli/Console.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,12 @@
#include <sys/ioctl.h>
#include <unistd.h>
#include <algorithm>
#include <cstdio>
#include <iostream>
#include <iterator>
#include <optional>
#include <set>
#include <sstream>
#include "axiom/cli/LiveProgressDisplay.h"
#include "axiom/cli/QueryInterruptHandler.h"
#include "axiom/cli/ResultPrinter.h"
Expand All @@ -49,6 +51,13 @@ DEFINE_uint64(

DEFINE_int32(max_rows, 100, "Max number of printed result rows");

DEFINE_string(
pager,
"less -SFX",
"Command used to view query results on a terminal. The default disables "
"line wrapping and exits automatically for short results. Set to empty to "
"print directly to stdout.");

DEFINE_int32(num_workers, 4, "Number of in-process workers");
DEFINE_int32(num_drivers, 4, "Number of drivers per worker");

Expand Down Expand Up @@ -104,6 +113,25 @@ int terminalWidth(int fileDescriptor) {
return kDefaultTerminalWidth;
}

// Sends formatted query results to the configured terminal pager. A pager is
// deliberately used only when stdout is a terminal: redirected output remains
// plain text for scripts and pipes. Return false so callers can fall back to
// stdout when the pager cannot be started or fails.
bool printWithPager(const std::string& text) {
if (!isatty(STDOUT_FILENO) || FLAGS_pager.empty()) {
return false;
}

std::cout.flush();
FILE* pager = popen(FLAGS_pager.c_str(), "w");
if (pager == nullptr) {
return false;
}

const auto written = std::fwrite(text.data(), 1, text.size(), pager);
return written == text.size() && pclose(pager) == 0;
}

// Extracts a file path argument from a dot-command string like ".run <file>".
// Returns the trimmed path, or empty string if none.
std::string parseDotCommandPath(const std::string& command, size_t prefixLen) {
Expand Down Expand Up @@ -275,7 +303,12 @@ bool Console::runOnce(
if (FLAGS_debug && !result.results.empty()) {
std::cout << result.results.front()->rowType()->toString() << std::endl;
}
cli::printResults(result.results, FLAGS_max_rows);
std::ostringstream formattedResults;
cli::printResults(result.results, FLAGS_max_rows, formattedResults);
const auto output = formattedResults.str();
if (!printWithPager(output)) {
std::cout << output;
}
}

if (printTiming) {
Expand Down
4 changes: 4 additions & 0 deletions axiom/cli/Console.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ DECLARE_string(data_path);
DECLARE_string(data_format);
DECLARE_string(etc_dir);
DECLARE_bool(debug);
DECLARE_string(pager);

namespace axiom::sql {

Expand Down Expand Up @@ -61,6 +62,9 @@ class Console {
/// Runs the CLI. Executes `--query` if set, otherwise reads piped
/// stdin if non-interactive, otherwise enters the interactive REPL.
/// Honors `--repeat` for `--query` and piped-stdin paths.
/// Result tables written to a terminal are shown through `--pager` (by
/// default `less -SFX`), which keeps wide columns on one logical line for
/// horizontal navigation. Set `--pager=` to print directly to stdout.
///
/// Throws VeloxUserError on invalid CLI flags. Query failures during
/// execution are caught internally and printed to stderr; they do
Expand Down
1 change: 1 addition & 0 deletions axiom/cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ The examples below use `axiom_sql` for brevity. With Buck, replace
| `--num_drivers` | `4` | Number of drivers per worker (parallelism). |
| `--v2` | `false` | Route queries through the v2 optimizer. `EXPLAIN (type graph\|optimized)` is not supported under v2 yet. |
| `--max_rows` | `100` | Maximum number of printed result rows. |
| `--pager` | `less -SFX` | Terminal command used for result tables. `-S` keeps wide columns on one logical line so they can be viewed with left/right navigation. Set `--pager=` to disable it. |
| `--show_live_progress` | `false` | Draw the [live progress](#live-progress) grid for `--query` and piped-stdin runs (when stderr is a terminal). The interactive REPL always shows it; this only opts the non-interactive paths in. |
| `--debug` | `false` | Enable debug mode (logging to stderr; adds per-split and CPU-time detail lines to the live progress display). |

Expand Down
38 changes: 19 additions & 19 deletions axiom/cli/ResultPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,14 @@ int64_t countResults(const std::vector<RowVectorPtr>& results) {

int32_t printResults(
const std::vector<RowVectorPtr>& results,
int64_t maxRows) {
int64_t maxRows,
std::ostream& output) {
const auto numRows = countResults(results);

auto printFooter = [&]() {
std::cout << "(" << numRows << " rows in " << results.size() << " batches)"
<< std::endl
<< std::endl;
output << "(" << numRows << " rows in " << results.size() << " batches)"
<< std::endl
<< std::endl;
};

if (numRows == 0) {
Expand All @@ -65,35 +66,35 @@ int32_t printResults(
}

auto printSeparator = [&]() {
std::cout << std::setfill('-');
output << std::setfill('-');
for (auto i = 0; i < numColumns; ++i) {
if (i > 0) {
std::cout << "-+-";
output << "-+-";
}
std::cout << std::setw(widths[i]) << "";
output << std::setw(widths[i]) << "";
}
std::cout << std::endl;
std::cout << std::setfill(' ');
output << std::endl;
output << std::setfill(' ');
};

auto printRow = [&](const auto& row) {
for (auto i = 0; i < numColumns; ++i) {
if (i > 0) {
std::cout << " | ";
output << " | ";
}
if (alignLeft[i]) {
std::cout << std::left;
output << std::left;
// Skip padding on the last column to avoid trailing whitespace.
if (i < numColumns - 1) {
std::cout << std::setw(widths[i]);
output << std::setw(widths[i]);
}
} else {
std::cout << std::right;
std::cout << std::setw(widths[i]);
output << std::right;
output << std::setw(widths[i]);
}
std::cout << row[i];
output << row[i];
}
std::cout << std::endl;
output << std::endl;
};

int32_t numPrinted = 0;
Expand All @@ -108,9 +109,8 @@ int32_t printResults(
}

if (numPrinted < numRows) {
std::cout << std::endl;
std::cout << "..." << (numRows - numPrinted) << " more rows."
<< std::endl;
output << std::endl;
output << "..." << (numRows - numPrinted) << " more rows." << std::endl;
}

printFooter();
Expand Down
7 changes: 5 additions & 2 deletions axiom/cli/ResultPrinter.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,20 @@

#pragma once

#include <ostream>
#include <vector>
#include "velox/vector/ComplexVector.h"

namespace axiom::cli {

/// Prints results to stdout in a formatted table.
/// Prints results to a formatted table.
/// @param results The results to print.
/// @param maxRows The maximum number of rows to print.
/// @param output The stream that receives the formatted table.
/// @return The total number of rows in the results.
int32_t printResults(
const std::vector<facebook::velox::RowVectorPtr>& results,
int64_t maxRows);
int64_t maxRows,
std::ostream& output);

} // namespace axiom::cli
Loading