2014-12-18 18:50:47 +00:00
|
|
|
/*
|
2016-02-11 19:48:58 +00:00
|
|
|
* Copyright (c) 2014-present, Facebook, Inc.
|
2014-12-18 18:50:47 +00:00
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* This source code is licensed under the BSD-style license found in the
|
2015-01-22 21:57:14 +00:00
|
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
2014-12-18 18:50:47 +00:00
|
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
|
|
*
|
|
|
|
*/
|
2014-12-03 04:36:46 +00:00
|
|
|
|
2016-02-25 01:58:58 +00:00
|
|
|
#include <atomic>
|
|
|
|
|
2016-09-03 02:13:37 +00:00
|
|
|
#include <osquery/core.h>
|
2015-12-03 03:57:24 +00:00
|
|
|
#include <osquery/flags.h>
|
2014-12-03 23:14:02 +00:00
|
|
|
#include <osquery/logger.h>
|
2016-09-03 02:13:37 +00:00
|
|
|
#include <osquery/system.h>
|
2015-01-12 18:02:44 +00:00
|
|
|
|
2015-02-03 05:21:36 +00:00
|
|
|
#include "osquery/sql/virtual_table.h"
|
2014-12-03 04:36:46 +00:00
|
|
|
|
|
|
|
namespace osquery {
|
2015-12-03 03:57:24 +00:00
|
|
|
|
2016-08-31 22:32:20 +00:00
|
|
|
FLAG(bool, enable_foreign, false, "Enable no-op foreign virtual tables");
|
2016-04-11 16:33:45 +00:00
|
|
|
|
2015-12-03 03:57:24 +00:00
|
|
|
SHELL_FLAG(bool, planner, false, "Enable osquery runtime planner output");
|
|
|
|
|
2016-09-08 22:44:32 +00:00
|
|
|
DECLARE_bool(disable_events);
|
|
|
|
|
2016-03-20 23:05:13 +00:00
|
|
|
/**
|
|
|
|
* @brief A protection around concurrent table attach requests.
|
|
|
|
*
|
|
|
|
* Table attaching is not concurrent. Attaching is the only unprotected SQLite
|
|
|
|
* operation from osquery's usage perspective. The extensions API allows for
|
|
|
|
* concurrent access of non-thread-safe database resources for attaching table
|
|
|
|
* schema and filter routing instructions.
|
|
|
|
*/
|
|
|
|
Mutex kAttachMutex;
|
|
|
|
|
2016-04-11 16:33:45 +00:00
|
|
|
/**
|
|
|
|
* A generated foreign amalgamation file includes schema for all tables.
|
|
|
|
*
|
|
|
|
* When the build system generates TablePlugin%s from the .table spec files, it
|
|
|
|
* reads the foreign-platform tables and generates an associated schema plugin.
|
|
|
|
* These plugins are amalgamated into 'foreign_amalgamation' and do not call
|
|
|
|
* their filter generation functions.
|
|
|
|
*/
|
|
|
|
void registerForeignTables();
|
|
|
|
|
2014-12-03 04:36:46 +00:00
|
|
|
namespace tables {
|
2015-11-15 21:21:16 +00:00
|
|
|
namespace sqlite {
|
2014-12-03 04:36:46 +00:00
|
|
|
|
2016-02-25 01:58:58 +00:00
|
|
|
/// For planner and debugging an incrementing cursor ID is used.
|
|
|
|
static std::atomic<size_t> kPlannerCursorID{0};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief A next-ID for within-query constraints stacking.
|
|
|
|
*
|
|
|
|
* As constraints are evaluated within xBestIndex, an IDX is assigned for
|
|
|
|
* operator and operand retrieval during xFilter/scanning.
|
|
|
|
*/
|
|
|
|
static std::atomic<size_t> kConstraintIndexID{0};
|
2015-12-03 03:57:24 +00:00
|
|
|
|
2016-02-25 01:58:58 +00:00
|
|
|
static inline std::string opString(unsigned char op) {
|
2015-12-03 03:57:24 +00:00
|
|
|
switch (op) {
|
|
|
|
case EQUALS:
|
|
|
|
return "=";
|
|
|
|
case GREATER_THAN:
|
|
|
|
return ">";
|
|
|
|
case LESS_THAN_OR_EQUALS:
|
|
|
|
return "<=";
|
|
|
|
case LESS_THAN:
|
|
|
|
return "<";
|
|
|
|
case GREATER_THAN_OR_EQUALS:
|
|
|
|
return ">=";
|
2016-02-17 07:10:08 +00:00
|
|
|
case LIKE:
|
|
|
|
return "LIKE";
|
|
|
|
case MATCH:
|
|
|
|
return "MATCH";
|
|
|
|
case GLOB:
|
|
|
|
return "GLOB";
|
|
|
|
case REGEXP:
|
|
|
|
return "REGEX";
|
|
|
|
case UNIQUE:
|
|
|
|
return "UNIQUE";
|
2015-12-03 03:57:24 +00:00
|
|
|
}
|
|
|
|
return "?";
|
|
|
|
}
|
|
|
|
|
2016-09-03 02:13:37 +00:00
|
|
|
inline std::string table_doc(const std::string& name) {
|
|
|
|
return "https://osquery.io/docs/#" + name;
|
|
|
|
}
|
|
|
|
|
2016-08-31 22:32:20 +00:00
|
|
|
static void plan(const std::string& output) {
|
2015-12-03 03:57:24 +00:00
|
|
|
if (FLAGS_planner) {
|
|
|
|
fprintf(stderr, "osquery planner: %s\n", output.c_str());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-31 22:32:20 +00:00
|
|
|
int xOpen(sqlite3_vtab* tab, sqlite3_vtab_cursor** ppCursor) {
|
2014-12-03 05:02:50 +00:00
|
|
|
int rc = SQLITE_NOMEM;
|
2016-08-31 22:32:20 +00:00
|
|
|
auto* pCur = new BaseCursor;
|
|
|
|
auto* pVtab = (VirtualTable*)tab;
|
2015-11-29 23:12:01 +00:00
|
|
|
if (pCur != nullptr) {
|
2015-12-03 03:57:24 +00:00
|
|
|
plan("Opening cursor (" + std::to_string(kPlannerCursorID) +
|
|
|
|
") for table: " + pVtab->content->name);
|
|
|
|
pCur->id = kPlannerCursorID++;
|
|
|
|
pCur->base.pVtab = tab;
|
2016-08-31 22:32:20 +00:00
|
|
|
*ppCursor = (sqlite3_vtab_cursor*)pCur;
|
2014-12-03 05:02:50 +00:00
|
|
|
rc = SQLITE_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2016-08-31 22:32:20 +00:00
|
|
|
int xClose(sqlite3_vtab_cursor* cur) {
|
|
|
|
BaseCursor* pCur = (BaseCursor*)cur;
|
2015-12-03 03:57:24 +00:00
|
|
|
plan("Closing cursor (" + std::to_string(pCur->id) + ")");
|
2014-12-03 05:02:50 +00:00
|
|
|
delete pCur;
|
|
|
|
return SQLITE_OK;
|
|
|
|
}
|
|
|
|
|
2016-08-31 22:32:20 +00:00
|
|
|
int xEof(sqlite3_vtab_cursor* cur) {
|
|
|
|
BaseCursor* pCur = (BaseCursor*)cur;
|
2015-11-20 19:03:57 +00:00
|
|
|
if (pCur->row >= pCur->n) {
|
2015-08-17 23:58:54 +00:00
|
|
|
// If the requested row exceeds the size of the row set then all rows
|
|
|
|
// have been visited, clear the data container.
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
2014-12-03 16:29:36 +00:00
|
|
|
}
|
|
|
|
|
2016-08-31 22:32:20 +00:00
|
|
|
int xDestroy(sqlite3_vtab* p) {
|
|
|
|
auto* pVtab = (VirtualTable*)p;
|
2015-01-30 18:44:25 +00:00
|
|
|
delete pVtab->content;
|
2014-12-03 16:29:36 +00:00
|
|
|
delete pVtab;
|
|
|
|
return SQLITE_OK;
|
|
|
|
}
|
|
|
|
|
2016-08-31 22:32:20 +00:00
|
|
|
int xNext(sqlite3_vtab_cursor* cur) {
|
|
|
|
BaseCursor* pCur = (BaseCursor*)cur;
|
2014-12-03 05:02:50 +00:00
|
|
|
pCur->row++;
|
|
|
|
return SQLITE_OK;
|
|
|
|
}
|
|
|
|
|
2016-08-31 22:32:20 +00:00
|
|
|
int xRowid(sqlite3_vtab_cursor* cur, sqlite_int64* pRowid) {
|
|
|
|
const BaseCursor* pCur = (BaseCursor*)cur;
|
2014-12-03 05:02:50 +00:00
|
|
|
*pRowid = pCur->row;
|
|
|
|
return SQLITE_OK;
|
|
|
|
}
|
|
|
|
|
2016-08-31 22:32:20 +00:00
|
|
|
int xCreate(sqlite3* db,
|
|
|
|
void* pAux,
|
2015-01-30 18:44:25 +00:00
|
|
|
int argc,
|
2016-08-31 22:32:20 +00:00
|
|
|
const char* const* argv,
|
|
|
|
sqlite3_vtab** ppVtab,
|
|
|
|
char** pzErr) {
|
|
|
|
auto* pVtab = new VirtualTable;
|
2015-01-30 18:44:25 +00:00
|
|
|
if (!pVtab || argc == 0 || argv[0] == nullptr) {
|
2015-11-01 05:20:23 +00:00
|
|
|
delete pVtab;
|
|
|
|
return SQLITE_NOMEM;
|
2015-01-30 18:44:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
memset(pVtab, 0, sizeof(VirtualTable));
|
|
|
|
pVtab->content = new VirtualTableContent;
|
2016-08-31 22:32:20 +00:00
|
|
|
pVtab->instance = (SQLiteDBInstance*)pAux;
|
2015-01-30 18:44:25 +00:00
|
|
|
|
2015-11-20 19:03:57 +00:00
|
|
|
// Create a TablePlugin Registry call, expect column details as the response.
|
2015-01-30 18:44:25 +00:00
|
|
|
PluginResponse response;
|
|
|
|
pVtab->content->name = std::string(argv[0]);
|
2016-08-31 22:32:20 +00:00
|
|
|
const auto& name = pVtab->content->name;
|
2015-02-23 05:56:52 +00:00
|
|
|
// Get the table column information.
|
2016-05-19 16:40:43 +00:00
|
|
|
auto status =
|
|
|
|
Registry::call("table", name, {{"action", "columns"}}, response);
|
2015-01-31 01:52:14 +00:00
|
|
|
if (!status.ok() || response.size() == 0) {
|
2015-11-01 05:20:23 +00:00
|
|
|
delete pVtab->content;
|
|
|
|
delete pVtab;
|
|
|
|
return SQLITE_ERROR;
|
2015-01-31 01:52:14 +00:00
|
|
|
}
|
|
|
|
|
2015-11-20 19:03:57 +00:00
|
|
|
// Generate an SQL create table statement from the retrieved column details.
|
2016-05-19 16:40:43 +00:00
|
|
|
// This call to columnDefinition requests column aliases (as HIDDEN columns).
|
|
|
|
auto statement = "CREATE TABLE " + name + columnDefinition(response, true);
|
2015-02-23 05:56:52 +00:00
|
|
|
int rc = sqlite3_declare_vtab(db, statement.c_str());
|
2015-11-01 05:20:23 +00:00
|
|
|
if (rc != SQLITE_OK || !status.ok() || response.size() == 0) {
|
2016-08-31 22:32:20 +00:00
|
|
|
LOG(ERROR) << "Error creating virtual table: " << name << " (" << rc
|
|
|
|
<< "): " << getStringForSQLiteReturnCode(rc);
|
|
|
|
VLOG(1) << "Cannot create virtual table using: " << statement;
|
2015-11-01 05:20:23 +00:00
|
|
|
delete pVtab->content;
|
|
|
|
delete pVtab;
|
|
|
|
return (rc != SQLITE_OK) ? rc : SQLITE_ERROR;
|
2015-01-31 01:52:14 +00:00
|
|
|
}
|
|
|
|
|
2016-05-19 16:40:43 +00:00
|
|
|
// Tables may request aliases as views.
|
|
|
|
std::set<std::string> views;
|
|
|
|
|
2015-11-20 19:03:57 +00:00
|
|
|
// Keep a local copy of the column details in the VirtualTableContent struct.
|
|
|
|
// This allows introspection into the column type without additional calls.
|
2016-08-31 22:32:20 +00:00
|
|
|
for (const auto& column : response) {
|
2016-05-19 16:40:43 +00:00
|
|
|
if (column.count("id") == 0) {
|
|
|
|
// This does not define a column type.
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (column.at("id") == "column" && column.count("name") &&
|
|
|
|
column.count("type")) {
|
|
|
|
// This is a malformed column definition.
|
|
|
|
// Populate the virtual table specific persistent column information.
|
|
|
|
pVtab->content->columns.push_back(std::make_tuple(
|
2016-08-31 22:32:20 +00:00
|
|
|
column.at("name"),
|
|
|
|
columnTypeName(column.at("type")),
|
2016-05-19 16:40:43 +00:00
|
|
|
(ColumnOptions)AS_LITERAL(INTEGER_LITERAL, column.at("op"))));
|
|
|
|
} else if (column.at("id") == "alias" && column.count("alias")) {
|
|
|
|
// Create associated views for table aliases.
|
|
|
|
views.insert(column.at("alias"));
|
|
|
|
} else if (column.at("id") == "columnAlias" && column.count("name") &&
|
|
|
|
column.count("target")) {
|
|
|
|
// Record the column in the set of columns.
|
|
|
|
// This is required because SQLITE uses indexes to identify columns.
|
|
|
|
// Use an UNKNOWN_TYPE as a pseudo-mask, since the type does not matter.
|
2016-08-31 22:32:20 +00:00
|
|
|
pVtab->content->columns.push_back(std::make_tuple(
|
|
|
|
column.at("name"), UNKNOWN_TYPE, ColumnOptions::HIDDEN));
|
2016-05-19 16:40:43 +00:00
|
|
|
// Record a mapping of the requested column alias name.
|
|
|
|
size_t target_index = 0;
|
|
|
|
for (size_t i = 0; i < pVtab->content->columns.size(); i++) {
|
2016-08-31 22:32:20 +00:00
|
|
|
const auto& target_column = pVtab->content->columns[i];
|
2016-05-19 16:40:43 +00:00
|
|
|
if (std::get<0>(target_column) == column.at("target")) {
|
|
|
|
target_index = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
pVtab->content->aliases[column.at("name")] = target_index;
|
2016-08-31 22:32:20 +00:00
|
|
|
} else if (column.at("id") == "attributes") {
|
|
|
|
// Store the attributes locally so they may be passed to the SQL object.
|
|
|
|
pVtab->content->attributes =
|
|
|
|
(TableAttributes)AS_LITERAL(INTEGER_LITERAL, column.at("attributes"));
|
2016-05-19 16:40:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create the requested 'aliases'.
|
2016-08-31 22:32:20 +00:00
|
|
|
for (const auto& view : views) {
|
2016-05-19 16:40:43 +00:00
|
|
|
auto statement = "CREATE VIEW " + view + " AS SELECT * FROM " + name;
|
|
|
|
sqlite3_exec(db, statement.c_str(), nullptr, nullptr, nullptr);
|
2015-01-30 18:44:25 +00:00
|
|
|
}
|
2016-08-31 22:32:20 +00:00
|
|
|
*ppVtab = (sqlite3_vtab*)pVtab;
|
2015-01-30 18:44:25 +00:00
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2016-08-31 22:32:20 +00:00
|
|
|
int xColumn(sqlite3_vtab_cursor* cur, sqlite3_context* ctx, int col) {
|
|
|
|
BaseCursor* pCur = (BaseCursor*)cur;
|
|
|
|
const auto* pVtab = (VirtualTable*)cur->pVtab;
|
2015-11-01 05:20:23 +00:00
|
|
|
if (col >= static_cast<int>(pVtab->content->columns.size())) {
|
2015-08-17 23:58:54 +00:00
|
|
|
// Requested column index greater than column set size.
|
2015-01-30 18:44:25 +00:00
|
|
|
return SQLITE_ERROR;
|
|
|
|
}
|
2015-11-20 19:03:57 +00:00
|
|
|
if (pCur->row >= pCur->data.size()) {
|
2015-08-17 23:58:54 +00:00
|
|
|
// Request row index greater than row set size.
|
2015-01-30 18:44:25 +00:00
|
|
|
return SQLITE_ERROR;
|
|
|
|
}
|
|
|
|
|
2016-08-31 22:32:20 +00:00
|
|
|
auto& column_name = std::get<0>(pVtab->content->columns[col]);
|
|
|
|
auto& type = std::get<1>(pVtab->content->columns[col]);
|
2016-05-19 16:40:43 +00:00
|
|
|
if (pVtab->content->aliases.count(column_name)) {
|
|
|
|
// Overwrite the aliased column with the type and name of the new column.
|
|
|
|
type = std::get<1>(
|
|
|
|
pVtab->content->columns[pVtab->content->aliases.at(column_name)]);
|
|
|
|
column_name = std::get<0>(
|
|
|
|
pVtab->content->columns[pVtab->content->aliases.at(column_name)]);
|
|
|
|
}
|
|
|
|
|
2015-07-09 05:37:35 +00:00
|
|
|
// Attempt to cast each xFilter-populated row/column to the SQLite type.
|
2016-08-31 22:32:20 +00:00
|
|
|
const auto& value = pCur->data[pCur->row][column_name];
|
2016-03-09 00:41:19 +00:00
|
|
|
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) {
|
2016-09-02 22:04:03 +00:00
|
|
|
sqlite3_result_text(
|
|
|
|
ctx, value.c_str(), static_cast<int>(value.size()), SQLITE_STATIC);
|
2015-11-09 09:19:45 +00:00
|
|
|
} else if (type == INTEGER_TYPE) {
|
2015-11-01 09:12:48 +00:00
|
|
|
long afinite;
|
2016-09-22 20:32:45 +00:00
|
|
|
if (!safeStrtol(value, 0, afinite) || afinite < INT_MIN ||
|
2015-11-01 09:12:48 +00:00
|
|
|
afinite > INT_MAX) {
|
2015-02-11 22:27:19 +00:00
|
|
|
VLOG(1) << "Error casting " << column_name << " (" << value
|
|
|
|
<< ") to INTEGER";
|
2016-03-09 00:41:19 +00:00
|
|
|
sqlite3_result_null(ctx);
|
|
|
|
} else {
|
|
|
|
sqlite3_result_int(ctx, (int)afinite);
|
2015-01-30 18:44:25 +00:00
|
|
|
}
|
2015-12-10 01:28:30 +00:00
|
|
|
} else if (type == BIGINT_TYPE || type == UNSIGNED_BIGINT_TYPE) {
|
2015-11-01 09:12:48 +00:00
|
|
|
long long afinite;
|
2016-09-22 20:32:45 +00:00
|
|
|
if (!safeStrtoll(value, 0, afinite)) {
|
2015-02-11 22:27:19 +00:00
|
|
|
VLOG(1) << "Error casting " << column_name << " (" << value
|
|
|
|
<< ") to BIGINT";
|
2016-03-09 00:41:19 +00:00
|
|
|
sqlite3_result_null(ctx);
|
|
|
|
} else {
|
|
|
|
sqlite3_result_int64(ctx, afinite);
|
2015-01-30 18:44:25 +00:00
|
|
|
}
|
2015-11-09 09:19:45 +00:00
|
|
|
} else if (type == DOUBLE_TYPE) {
|
2016-08-31 22:32:20 +00:00
|
|
|
char* end = nullptr;
|
2015-08-14 01:04:03 +00:00
|
|
|
double afinite = strtod(value.c_str(), &end);
|
|
|
|
if (end == nullptr || end == value.c_str() || *end != '\0') {
|
2015-08-18 08:35:10 +00:00
|
|
|
VLOG(1) << "Error casting " << column_name << " (" << value
|
2015-06-22 07:55:17 +00:00
|
|
|
<< ") to DOUBLE";
|
2016-03-09 00:41:19 +00:00
|
|
|
sqlite3_result_null(ctx);
|
|
|
|
} else {
|
|
|
|
sqlite3_result_double(ctx, afinite);
|
2015-06-22 07:55:17 +00:00
|
|
|
}
|
2015-12-10 01:28:30 +00:00
|
|
|
} else {
|
|
|
|
LOG(ERROR) << "Error unknown column type " << column_name;
|
2015-01-30 18:44:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return SQLITE_OK;
|
|
|
|
}
|
|
|
|
|
2016-08-31 22:32:20 +00:00
|
|
|
static int xBestIndex(sqlite3_vtab* tab, sqlite3_index_info* pIdxInfo) {
|
|
|
|
auto* pVtab = (VirtualTable*)tab;
|
|
|
|
const auto& columns = pVtab->content->columns;
|
2016-06-06 16:36:53 +00:00
|
|
|
|
2015-11-20 19:03:57 +00:00
|
|
|
ConstraintSet constraints;
|
|
|
|
// Keep track of the index used for each valid constraint.
|
|
|
|
// Expect this index to correspond with argv within xFilter.
|
|
|
|
size_t expr_index = 0;
|
|
|
|
// If any constraints are unusable increment the cost of the index.
|
2016-03-02 17:49:49 +00:00
|
|
|
double cost = 1;
|
2015-11-20 19:03:57 +00:00
|
|
|
// Expressions operating on the same virtual table are loosely identified by
|
|
|
|
// the consecutive sets of terms each of the constraint sets are applied onto.
|
|
|
|
// Subsequent attempts from failed (unusable) constraints replace the set,
|
|
|
|
// while new sets of terms append.
|
2015-11-01 05:20:23 +00:00
|
|
|
if (pIdxInfo->nConstraint > 0) {
|
|
|
|
for (size_t i = 0; i < static_cast<size_t>(pIdxInfo->nConstraint); ++i) {
|
2015-11-20 19:03:57 +00:00
|
|
|
// Record the term index (this index exists across all expressions).
|
2016-08-31 22:32:20 +00:00
|
|
|
const auto& constraint_info = pIdxInfo->aConstraint[i];
|
2016-01-22 11:05:33 +00:00
|
|
|
#if defined(DEBUG)
|
|
|
|
plan("Evaluating constraints for table: " + pVtab->content->name +
|
|
|
|
" [index=" + std::to_string(i) + " column=" +
|
2016-01-27 19:27:00 +00:00
|
|
|
std::to_string(constraint_info.iColumn) + " term=" +
|
2016-01-30 02:12:37 +00:00
|
|
|
std::to_string((int)constraint_info.iTermOffset) + " usable=" +
|
2016-01-27 19:27:00 +00:00
|
|
|
std::to_string((int)constraint_info.usable) + "]");
|
2016-01-22 11:05:33 +00:00
|
|
|
#endif
|
2016-01-27 19:27:00 +00:00
|
|
|
if (!constraint_info.usable) {
|
2015-11-01 05:20:23 +00:00
|
|
|
// A higher cost less priority, prefer more usable query constraints.
|
|
|
|
cost += 10;
|
|
|
|
continue;
|
|
|
|
}
|
2016-01-22 11:05:33 +00:00
|
|
|
|
2015-11-20 19:03:57 +00:00
|
|
|
// Lookup the column name given an index into the table column set.
|
2016-01-27 19:27:00 +00:00
|
|
|
if (constraint_info.iColumn < 0 ||
|
|
|
|
static_cast<size_t>(constraint_info.iColumn) >=
|
|
|
|
pVtab->content->columns.size()) {
|
|
|
|
cost += 10;
|
|
|
|
continue;
|
|
|
|
}
|
2016-08-31 22:32:20 +00:00
|
|
|
const auto& name = std::get<0>(columns[constraint_info.iColumn]);
|
2015-11-20 19:03:57 +00:00
|
|
|
// Save a pair of the name and the constraint operator.
|
|
|
|
// Use this constraint during xFilter by performing a scan and column
|
|
|
|
// name lookup through out all cursor constraint lists.
|
|
|
|
constraints.push_back(
|
2016-01-27 19:27:00 +00:00
|
|
|
std::make_pair(name, Constraint(constraint_info.op)));
|
2016-09-02 22:04:03 +00:00
|
|
|
pIdxInfo->aConstraintUsage[i].argvIndex = static_cast<int>(++expr_index);
|
2016-01-22 11:05:33 +00:00
|
|
|
#if defined(DEBUG)
|
|
|
|
plan("Adding constraint for table: " + pVtab->content->name +
|
|
|
|
" [column=" + name + " arg_index=" + std::to_string(expr_index) +
|
2016-06-06 16:36:53 +00:00
|
|
|
" op=" + std::to_string(constraint_info.op) + "]");
|
2016-01-22 11:05:33 +00:00
|
|
|
#endif
|
2014-12-03 04:36:46 +00:00
|
|
|
}
|
2015-01-30 18:44:25 +00:00
|
|
|
}
|
|
|
|
|
2016-08-31 22:32:20 +00:00
|
|
|
auto requiredSatisfied = [&constraints](const std::string& name) {
|
|
|
|
for (const auto& constraint : constraints) {
|
2016-06-06 16:36:53 +00:00
|
|
|
if (constraint.first == name) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// No constraint exists.
|
|
|
|
return false;
|
|
|
|
};
|
|
|
|
|
|
|
|
// Check the table for a required column.
|
|
|
|
boost::optional<bool> satisfied;
|
2016-08-31 22:32:20 +00:00
|
|
|
for (const auto& column : columns) {
|
|
|
|
auto& name = std::get<0>(column);
|
|
|
|
auto& options = std::get<2>(column);
|
|
|
|
if (options & ColumnOptions::REQUIRED) {
|
2016-06-06 16:36:53 +00:00
|
|
|
// This column is required, check if a constraint exists.
|
|
|
|
if (requiredSatisfied(name)) {
|
|
|
|
satisfied = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
// A column was required, but not satisfied.
|
|
|
|
// Multiple columns may be marked REQUIRED, continue to check.
|
|
|
|
satisfied = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if a REQUIRED column exists but was not satisfied via a constraint.
|
|
|
|
if (satisfied.is_initialized() && !*satisfied) {
|
|
|
|
cost += 1e10;
|
|
|
|
}
|
|
|
|
|
2016-09-02 22:04:03 +00:00
|
|
|
pIdxInfo->idxNum = static_cast<int>(kConstraintIndexID++);
|
2016-01-22 11:05:33 +00:00
|
|
|
#if defined(DEBUG)
|
|
|
|
plan("Recording constraint set for table: " + pVtab->content->name +
|
|
|
|
" [cost=" + std::to_string(cost) + " size=" +
|
|
|
|
std::to_string(constraints.size()) + " idx=" +
|
|
|
|
std::to_string(pIdxInfo->idxNum) + "]");
|
|
|
|
#endif
|
2016-05-09 17:32:33 +00:00
|
|
|
// Add the constraint set to the table's tracked constraints.
|
2016-01-22 11:05:33 +00:00
|
|
|
pVtab->content->constraints[pIdxInfo->idxNum] = std::move(constraints);
|
2015-01-30 18:44:25 +00:00
|
|
|
pIdxInfo->estimatedCost = cost;
|
|
|
|
return SQLITE_OK;
|
|
|
|
}
|
|
|
|
|
2016-08-31 22:32:20 +00:00
|
|
|
static int xFilter(sqlite3_vtab_cursor* pVtabCursor,
|
2015-01-30 18:44:25 +00:00
|
|
|
int idxNum,
|
2016-08-31 22:32:20 +00:00
|
|
|
const char* idxStr,
|
2015-01-30 18:44:25 +00:00
|
|
|
int argc,
|
2016-08-31 22:32:20 +00:00
|
|
|
sqlite3_value** argv) {
|
|
|
|
BaseCursor* pCur = (BaseCursor*)pVtabCursor;
|
|
|
|
auto* pVtab = (VirtualTable*)pVtabCursor->pVtab;
|
|
|
|
auto* content = pVtab->content;
|
2016-02-25 01:58:58 +00:00
|
|
|
pVtab->instance->addAffectedTable(content);
|
2015-01-30 18:44:25 +00:00
|
|
|
|
|
|
|
pCur->row = 0;
|
2015-11-20 19:03:57 +00:00
|
|
|
pCur->n = 0;
|
2016-05-09 17:32:33 +00:00
|
|
|
QueryContext context(content);
|
2015-01-30 18:44:25 +00:00
|
|
|
|
2016-09-03 02:13:37 +00:00
|
|
|
// Track required columns, this is different than the requirements check
|
|
|
|
// that occurs within BestIndex because this scan includes a cursor.
|
|
|
|
// For each cursor used, if a requirement exists, we need to scan the
|
|
|
|
// selected set of constraints for a match.
|
|
|
|
bool required_satisfied = true;
|
|
|
|
|
|
|
|
// The specialized table attribute USER_BASED imposes a special requirement
|
|
|
|
// for UID. This may be represented in the requirements, but otherwise
|
|
|
|
// would benefit from specific notification to the caller.
|
|
|
|
bool user_based_satisfied = !(
|
|
|
|
(content->attributes & TableAttributes::USER_BASED) > 0 && isUserAdmin());
|
|
|
|
|
2016-09-08 22:44:32 +00:00
|
|
|
// For event-based tables, help the caller if events are disabled.
|
|
|
|
bool events_satisfied =
|
|
|
|
((content->attributes & TableAttributes::EVENT_BASED) == 0 ||
|
|
|
|
!FLAGS_disable_events);
|
|
|
|
|
2016-09-03 02:13:37 +00:00
|
|
|
std::map<std::string, ColumnOptions> options;
|
2015-11-20 19:03:57 +00:00
|
|
|
for (size_t i = 0; i < content->columns.size(); ++i) {
|
2015-07-09 05:37:35 +00:00
|
|
|
// Set the column affinity for each optional constraint list.
|
|
|
|
// There is a separate list for each column name.
|
2016-09-03 02:13:37 +00:00
|
|
|
auto column_name = std::get<0>(content->columns[i]);
|
|
|
|
context.constraints[column_name].affinity =
|
2016-05-18 19:23:52 +00:00
|
|
|
std::get<1>(content->columns[i]);
|
2016-09-03 02:13:37 +00:00
|
|
|
// Save the column options for comparison within constraints enumeration.
|
|
|
|
options[column_name] = std::get<2>(content->columns[i]);
|
|
|
|
if (options[column_name] & ColumnOptions::REQUIRED) {
|
|
|
|
required_satisfied = false;
|
|
|
|
}
|
2015-11-20 19:03:57 +00:00
|
|
|
}
|
|
|
|
|
2016-01-22 11:05:33 +00:00
|
|
|
// Filtering between cursors happens iteratively, not consecutively.
|
|
|
|
// If there are multiple sets of constraints, they apply to each cursor.
|
|
|
|
#if defined(DEBUG)
|
|
|
|
plan("Filtering called for table: " + content->name + " [constraint_count=" +
|
|
|
|
std::to_string(content->constraints.size()) + " argc=" +
|
|
|
|
std::to_string(argc) + " idx=" + std::to_string(idxNum) + "]");
|
|
|
|
#endif
|
2015-01-30 18:44:25 +00:00
|
|
|
|
2015-07-09 05:37:35 +00:00
|
|
|
// Iterate over every argument to xFilter, filling in constraint values.
|
2015-11-20 19:03:57 +00:00
|
|
|
if (content->constraints.size() > 0) {
|
2016-08-31 22:32:20 +00:00
|
|
|
auto& constraints = content->constraints[idxNum];
|
2015-11-20 19:03:57 +00:00
|
|
|
if (argc > 0) {
|
|
|
|
for (size_t i = 0; i < static_cast<size_t>(argc); ++i) {
|
2016-08-31 22:32:20 +00:00
|
|
|
auto expr = (const char*)sqlite3_value_text(argv[i]);
|
2015-11-20 19:03:57 +00:00
|
|
|
if (expr == nullptr || expr[0] == 0) {
|
|
|
|
// SQLite did not expose the expression value.
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
// Set the expression from SQLite's now-populated argv.
|
2016-08-31 22:32:20 +00:00
|
|
|
auto& constraint = constraints[i];
|
2015-11-20 19:03:57 +00:00
|
|
|
constraint.second.expr = std::string(expr);
|
2015-12-03 03:57:24 +00:00
|
|
|
plan("Adding constraint to cursor (" + std::to_string(pCur->id) +
|
|
|
|
"): " + constraint.first + " " + opString(constraint.second.op) +
|
|
|
|
" " + constraint.second.expr);
|
2015-11-20 19:03:57 +00:00
|
|
|
// Add the constraint to the column-sorted query request map.
|
|
|
|
context.constraints[constraint.first].add(constraint.second);
|
2016-09-03 02:13:37 +00:00
|
|
|
|
|
|
|
if (options[constraint.first] & ColumnOptions::REQUIRED) {
|
|
|
|
// A required option exists in the constraints.
|
|
|
|
required_satisfied = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!user_based_satisfied && constraint.first == "uid") {
|
|
|
|
// UID was required and exists in the constraints.
|
|
|
|
user_based_satisfied = true;
|
|
|
|
}
|
2015-11-01 05:20:23 +00:00
|
|
|
}
|
2015-11-20 19:03:57 +00:00
|
|
|
} else if (constraints.size() > 0) {
|
|
|
|
// Constraints failed.
|
2015-04-07 08:20:05 +00:00
|
|
|
}
|
2015-01-30 18:44:25 +00:00
|
|
|
}
|
2016-01-22 11:05:33 +00:00
|
|
|
|
2016-09-03 02:13:37 +00:00
|
|
|
if (!user_based_satisfied) {
|
|
|
|
LOG(WARNING) << "The " << pVtab->content->name
|
|
|
|
<< " table returns data based on the current user by default, "
|
|
|
|
"consider JOINing against the users table";
|
|
|
|
} else if (!required_satisfied) {
|
|
|
|
LOG(WARNING)
|
|
|
|
<< "Table " << pVtab->content->name
|
|
|
|
<< " was queried without a required column in the WHERE clause";
|
2016-09-08 22:44:32 +00:00
|
|
|
} else if (!events_satisfied) {
|
|
|
|
LOG(WARNING) << "Table " << pVtab->content->name
|
|
|
|
<< " is event-based but events are disabled";
|
2016-09-03 02:13:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Provide a helpful reference to table documentation within the shell.
|
|
|
|
if (kToolType == ToolType::SHELL &&
|
2016-09-08 22:44:32 +00:00
|
|
|
(!user_based_satisfied || !required_satisfied || !events_satisfied)) {
|
2016-09-03 02:13:37 +00:00
|
|
|
LOG(WARNING) << "Please see the table documentation: "
|
|
|
|
<< table_doc(pVtab->content->name);
|
|
|
|
}
|
|
|
|
|
2015-08-17 23:58:54 +00:00
|
|
|
// Reset the virtual table contents.
|
2015-11-20 19:03:57 +00:00
|
|
|
pCur->data.clear();
|
2016-09-03 02:13:37 +00:00
|
|
|
options.clear();
|
|
|
|
|
2015-08-17 23:58:54 +00:00
|
|
|
// Generate the row data set.
|
2015-12-03 03:57:24 +00:00
|
|
|
plan("Scanning rows for cursor (" + std::to_string(pCur->id) + ")");
|
2016-05-09 17:32:33 +00:00
|
|
|
Registry::callTable(pVtab->content->name, context, pCur->data);
|
2015-03-18 19:01:58 +00:00
|
|
|
|
2015-08-17 23:58:54 +00:00
|
|
|
// Set the number of rows.
|
2015-11-20 19:03:57 +00:00
|
|
|
pCur->n = pCur->data.size();
|
2015-01-30 18:44:25 +00:00
|
|
|
return SQLITE_OK;
|
|
|
|
}
|
2015-05-20 20:27:53 +00:00
|
|
|
}
|
2015-11-15 21:21:16 +00:00
|
|
|
}
|
2015-01-30 18:44:25 +00:00
|
|
|
|
2016-08-31 22:32:20 +00:00
|
|
|
Status attachTableInternal(const std::string& name,
|
|
|
|
const std::string& statement,
|
|
|
|
const SQLiteDBInstanceRef& instance) {
|
2015-04-30 08:41:01 +00:00
|
|
|
if (SQLiteDBManager::isDisabled(name)) {
|
2015-11-09 02:31:50 +00:00
|
|
|
VLOG(1) << "Table " << name << " is disabled, not attaching";
|
2015-04-30 08:41:01 +00:00
|
|
|
return Status(0, getStringForSQLiteReturnCode(0));
|
|
|
|
}
|
|
|
|
|
2015-02-23 05:56:52 +00:00
|
|
|
// A static module structure does not need specific logic per-table.
|
2015-05-20 20:27:53 +00:00
|
|
|
// clang-format off
|
2015-01-30 18:44:25 +00:00
|
|
|
static sqlite3_module module = {
|
2015-05-20 20:27:53 +00:00
|
|
|
0,
|
2015-11-15 21:21:16 +00:00
|
|
|
tables::sqlite::xCreate,
|
|
|
|
tables::sqlite::xCreate,
|
|
|
|
tables::sqlite::xBestIndex,
|
|
|
|
tables::sqlite::xDestroy,
|
|
|
|
tables::sqlite::xDestroy,
|
|
|
|
tables::sqlite::xOpen,
|
|
|
|
tables::sqlite::xClose,
|
|
|
|
tables::sqlite::xFilter,
|
|
|
|
tables::sqlite::xNext,
|
|
|
|
tables::sqlite::xEof,
|
|
|
|
tables::sqlite::xColumn,
|
|
|
|
tables::sqlite::xRowid,
|
2016-01-15 08:53:38 +00:00
|
|
|
nullptr, /* Update */
|
|
|
|
nullptr, /* Begin */
|
|
|
|
nullptr, /* Sync */
|
|
|
|
nullptr, /* Commit */
|
|
|
|
nullptr, /* Rollback */
|
|
|
|
nullptr, /* FindFunction */
|
|
|
|
nullptr, /* Rename */
|
2016-02-07 03:20:25 +00:00
|
|
|
nullptr, /* Savepoint */
|
|
|
|
nullptr, /* Release */
|
|
|
|
nullptr, /* RollbackTo */
|
2015-01-30 18:44:25 +00:00
|
|
|
};
|
2015-05-20 20:27:53 +00:00
|
|
|
// clang-format on
|
2015-01-30 18:44:25 +00:00
|
|
|
|
2015-02-23 05:56:52 +00:00
|
|
|
// Note, if the clientData API is used then this will save a registry call
|
|
|
|
// within xCreate.
|
2016-03-20 23:05:13 +00:00
|
|
|
WriteLock lock(kAttachMutex);
|
2016-08-31 22:32:20 +00:00
|
|
|
int rc = sqlite3_create_module(
|
|
|
|
instance->db(), name.c_str(), &module, (void*)&(*instance));
|
2015-02-23 05:56:52 +00:00
|
|
|
if (rc == SQLITE_OK || rc == SQLITE_MISUSE) {
|
|
|
|
auto format =
|
|
|
|
"CREATE VIRTUAL TABLE temp." + name + " USING " + name + statement;
|
2016-02-25 01:58:58 +00:00
|
|
|
rc = sqlite3_exec(instance->db(), format.c_str(), nullptr, nullptr, 0);
|
2015-02-23 05:56:52 +00:00
|
|
|
} else {
|
|
|
|
LOG(ERROR) << "Error attaching table: " << name << " (" << rc << ")";
|
2015-01-31 01:52:14 +00:00
|
|
|
}
|
2015-02-23 05:56:52 +00:00
|
|
|
return Status(rc, getStringForSQLiteReturnCode(rc));
|
|
|
|
}
|
2015-01-31 01:52:14 +00:00
|
|
|
|
2016-08-31 22:32:20 +00:00
|
|
|
Status detachTableInternal(const std::string& name, sqlite3* db) {
|
2016-03-20 23:05:13 +00:00
|
|
|
WriteLock lock(kAttachMutex);
|
2015-02-23 05:56:52 +00:00
|
|
|
auto format = "DROP TABLE IF EXISTS temp." + name;
|
2015-04-27 09:12:58 +00:00
|
|
|
int rc = sqlite3_exec(db, format.c_str(), nullptr, nullptr, 0);
|
2015-02-23 05:56:52 +00:00
|
|
|
if (rc != SQLITE_OK) {
|
|
|
|
LOG(ERROR) << "Error detaching table: " << name << " (" << rc << ")";
|
2015-01-30 18:44:25 +00:00
|
|
|
}
|
2015-02-23 05:56:52 +00:00
|
|
|
|
|
|
|
return Status(rc, getStringForSQLiteReturnCode(rc));
|
2014-12-03 04:36:46 +00:00
|
|
|
}
|
|
|
|
|
2016-03-20 23:05:13 +00:00
|
|
|
Status attachFunctionInternal(
|
2016-08-31 22:32:20 +00:00
|
|
|
const std::string& name,
|
2016-03-20 23:05:13 +00:00
|
|
|
std::function<
|
2016-08-31 22:32:20 +00:00
|
|
|
void(sqlite3_context* context, int argc, sqlite3_value** argv)> func) {
|
2016-03-20 23:05:13 +00:00
|
|
|
// Hold the manager connection instance again in callbacks.
|
|
|
|
auto dbc = SQLiteDBManager::get();
|
|
|
|
// Add some shell-specific functions to the instance.
|
|
|
|
WriteLock lock(kAttachMutex);
|
|
|
|
int rc = sqlite3_create_function(
|
|
|
|
dbc->db(),
|
|
|
|
name.c_str(),
|
|
|
|
0,
|
|
|
|
SQLITE_UTF8,
|
|
|
|
nullptr,
|
2016-08-31 22:32:20 +00:00
|
|
|
*func.target<void (*)(sqlite3_context*, int, sqlite3_value**)>(),
|
2016-03-20 23:05:13 +00:00
|
|
|
nullptr,
|
|
|
|
nullptr);
|
|
|
|
return Status(rc);
|
|
|
|
}
|
|
|
|
|
2016-08-31 22:32:20 +00:00
|
|
|
void attachVirtualTables(const SQLiteDBInstanceRef& instance) {
|
2016-04-11 16:33:45 +00:00
|
|
|
if (FLAGS_enable_foreign) {
|
|
|
|
registerForeignTables();
|
|
|
|
}
|
|
|
|
|
2015-02-23 05:56:52 +00:00
|
|
|
PluginResponse response;
|
2016-08-31 22:32:20 +00:00
|
|
|
for (const auto& name : Registry::names("table")) {
|
2015-02-23 05:56:52 +00:00
|
|
|
// Column information is nice for virtual table create call.
|
|
|
|
auto status =
|
|
|
|
Registry::call("table", name, {{"action", "columns"}}, response);
|
|
|
|
if (status.ok()) {
|
2016-05-19 16:40:43 +00:00
|
|
|
auto statement = columnDefinition(response, true);
|
2016-02-25 01:58:58 +00:00
|
|
|
attachTableInternal(name, statement, instance);
|
2014-12-03 04:36:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|