Merge pull request #1910 from theopolis/null

Allow NULL values, stop using -1 as int/double invalid values
This commit is contained in:
Teddy Reed 2016-03-09 19:23:35 -08:00
commit 03d0d7e835
5 changed files with 94 additions and 39 deletions

View File

@ -11,6 +11,7 @@
#include <iostream>
#include <sstream>
#include <osquery/flags.h>
#include <osquery/core.h>
#include "osquery/core/conversions.h"
@ -18,6 +19,8 @@
namespace osquery {
SHELL_FLAG(string, nullvalue, "", "Set string for NULL values, default ''");
static std::vector<size_t> kOffset = {0, 0};
static std::string kToken = "|";
@ -25,27 +28,26 @@ std::string generateToken(const std::map<std::string, size_t>& lengths,
const std::vector<std::string>& columns) {
std::string out = "+";
for (const auto& col : columns) {
if (lengths.count(col) > 0) {
if (getenv("ENHANCE") != nullptr) {
std::string e = "\xF0\x9F\x90\x8C";
e[2] += kOffset[1];
e[3] += kOffset[0];
for (size_t i = 0; i < lengths.at(col) + 2; i++) {
e[3] = '\x8c' + kOffset[0]++;
if (e[3] == '\xbf') {
e[3] = '\x80';
kOffset[1] = (kOffset[1] > 3 && kOffset[1] < 8) ? 9 : kOffset[1];
e[2] = '\x90' + ++kOffset[1];
kOffset[0] = 0;
}
if (kOffset[1] == ('\x97' - '\x8d')) {
kOffset = {0, 0};
}
out += e.c_str();
size_t size = ((lengths.count(col) > 0) ? lengths.at(col) : col.size()) + 2;
if (getenv("ENHANCE") != nullptr) {
std::string e = "\xF0\x9F\x90\x8C";
e[2] += kOffset[1];
e[3] += kOffset[0];
for (size_t i = 0; i < size; i++) {
e[3] = '\x8c' + kOffset[0]++;
if (e[3] == '\xbf') {
e[3] = '\x80';
kOffset[1] = (kOffset[1] > 3 && kOffset[1] < 8) ? 9 : kOffset[1];
e[2] = '\x90' + ++kOffset[1];
kOffset[0] = 0;
}
} else {
out += std::string(lengths.at(col) + 2, '-');
if (kOffset[1] == ('\x97' - '\x8d')) {
kOffset = {0, 0};
}
out += e.c_str();
}
} else {
out += std::string(size, '-');
}
out += "+";
}
@ -63,13 +65,12 @@ std::string generateHeader(const std::map<std::string, size_t>& lengths,
for (const auto& col : columns) {
out += " " + col;
if (lengths.count(col) > 0) {
int buffer_size = lengths.at(col) - utf8StringSize(col) + 1;
int buffer_size = lengths.at(col) - utf8StringSize(col);
if (buffer_size > 0) {
out += std::string(buffer_size, ' ');
} else {
out += ' ';
}
}
out += ' ';
out += kToken;
}
out += "\n";
@ -81,15 +82,21 @@ std::string generateRow(const Row& r,
const std::vector<std::string>& order) {
std::string out;
for (const auto& column : order) {
if (r.count(column) == 0 || lengths.count(column) == 0) {
continue;
}
// Print a terminator for the previous value or lhs, followed by spaces.
size_t size = 0;
int buffer_size = lengths.at(column) - utf8StringSize(r.at(column)) + 1;
if (buffer_size > 0) {
out += kToken + " " + r.at(column) + std::string(buffer_size, ' ');
// Print a terminator for the previous value or lhs, followed by spaces.
out += kToken + ' ';
if (r.count(column) == 0 || lengths.count(column) == 0) {
size = column.size() - utf8StringSize(FLAGS_nullvalue);
out += FLAGS_nullvalue;
} else {
int buffer_size = lengths.at(column) - utf8StringSize(r.at(column));
if (buffer_size >= 0) {
size = static_cast<size_t>(buffer_size);
out += r.at(column);
}
}
out += std::string(size + 1, ' ');
}
if (out.size() > 0) {
@ -126,6 +133,7 @@ void jsonPrint(const QueryData& q) {
printf("[\n");
for (size_t i = 0; i < q.size(); ++i) {
std::string row_string;
if (serializeRowJSON(q[i], row_string).ok()) {
row_string.pop_back();
printf(" %s", row_string.c_str());

View File

@ -43,13 +43,14 @@ SHELL_FLAG(bool, csv, false, "Set output mode to 'csv'");
SHELL_FLAG(bool, json, false, "Set output mode to 'json'");
SHELL_FLAG(bool, line, false, "Set output mode to 'line'");
SHELL_FLAG(bool, list, false, "Set output mode to 'list'");
SHELL_FLAG(string, nullvalue, "", "Set string for NULL values, default ''");
SHELL_FLAG(string, separator, "|", "Set output field separator, default '|'");
SHELL_FLAG(bool, header, true, "Toggle column headers true/false");
/// Define short-hand shell switches.
SHELL_FLAG(bool, L, false, "List all table names");
SHELL_FLAG(string, A, "", "Select all from a table");
DECLARE_string(nullvalue);
}
static char zHelp[] =
@ -694,8 +695,10 @@ static int shell_callback(
osquery::Row r;
for (i = 0; i < nArg; ++i) {
if (azCol[i] != nullptr && azArg[i] != nullptr) {
r[std::string(azCol[i])] = std::string(azArg[i]);
if (azCol[i] != nullptr) {
r[std::string(azCol[i])] = (azArg[i] == nullptr)
? osquery::FLAGS_nullvalue
: std::string(azArg[i]);
}
}
osquery::computeRowLengths(r, p->prettyPrint->lengths);

View File

@ -236,4 +236,40 @@ TEST_F(VirtualTableTests, test_json_extract) {
ASSERT_EQ(results.size(), 1U);
EXPECT_EQ(results[0]["test"], "1");
}
TEST_F(VirtualTableTests, test_null_values) {
auto dbc = SQLiteDBManager::get();
std::string statement = "SELECT NULL as null_value;";
{
QueryData results;
auto status = queryInternal(statement, results, dbc->db());
EXPECT_TRUE(status.ok());
EXPECT_EQ(results[0]["null_value"], "");
}
// Try INTEGER.
{
QueryData results;
statement = "SELECT CAST(NULL as INTEGER) as null_value;";
queryInternal(statement, results, dbc->db());
EXPECT_EQ(results[0]["null_value"], "");
}
// BIGINT.
{
QueryData results;
statement = "SELECT CAST(NULL as BIGINT) as null_value;";
queryInternal(statement, results, dbc->db());
EXPECT_EQ(results[0]["null_value"], "");
}
// Try DOUBLE.
{
QueryData results;
statement = "SELECT CAST(NULL as DOUBLE) as null_value;";
queryInternal(statement, results, dbc->db());
EXPECT_EQ(results[0]["null_value"], "");
}
}
}

View File

@ -182,7 +182,11 @@ int xColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int col) {
// Attempt to cast each xFilter-populated row/column to the SQLite type.
const auto &value = pCur->data[pCur->row][column_name];
if (type == TEXT_TYPE) {
if (pCur->data[pCur->row].count(column_name) == 0) {
// Missing content.
VLOG(1) << "Error " << column_name << " is empty";
sqlite3_result_null(ctx);
} else if (type == TEXT_TYPE) {
sqlite3_result_text(ctx, value.c_str(), value.size(), SQLITE_STATIC);
} else if (type == INTEGER_TYPE) {
long afinite;
@ -190,17 +194,19 @@ int xColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int col) {
afinite > INT_MAX) {
VLOG(1) << "Error casting " << column_name << " (" << value
<< ") to INTEGER";
afinite = -1;
sqlite3_result_null(ctx);
} else {
sqlite3_result_int(ctx, (int)afinite);
}
sqlite3_result_int(ctx, (int)afinite);
} else if (type == BIGINT_TYPE || type == UNSIGNED_BIGINT_TYPE) {
long long afinite;
if (!safeStrtoll(value, 10, afinite)) {
VLOG(1) << "Error casting " << column_name << " (" << value
<< ") to BIGINT";
afinite = -1;
sqlite3_result_null(ctx);
} else {
sqlite3_result_int64(ctx, afinite);
}
sqlite3_result_int64(ctx, afinite);
} else if (type == DOUBLE_TYPE) {
char *end = nullptr;
double afinite = strtod(value.c_str(), &end);
@ -208,8 +214,10 @@ int xColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int col) {
afinite = 0;
VLOG(1) << "Error casting " << column_name << " (" << value
<< ") to DOUBLE";
sqlite3_result_null(ctx);
} else {
sqlite3_result_double(ctx, afinite);
}
sqlite3_result_double(ctx, afinite);
} else {
LOG(ERROR) << "Error unknown column type " << column_name;
}

View File

@ -545,7 +545,7 @@ void genProcessMemoryMap(int pid, QueryData &results) {
}
}
QueryData genProcessMemoryMap(QueryContext& context) {
QueryData genProcessMemoryMap(QueryContext &context) {
QueryData results;
auto pidlist = getProcList(context);