Skip to content

Commit

Permalink
Merge branch 'master' into rigger
Browse files Browse the repository at this point in the history
  • Loading branch information
Mytherin committed Apr 9, 2020
2 parents ef2286f + 4ad62e9 commit 9f8bca8
Show file tree
Hide file tree
Showing 21 changed files with 11,507 additions and 11,208 deletions.
15 changes: 12 additions & 3 deletions src/catalog/catalog_entry/view_catalog_entry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ using namespace std;

void ViewCatalogEntry::Initialize(CreateViewInfo *info) {
query = move(info->query);
aliases = info->aliases;
this->aliases = info->aliases;
this->types = info->types;
this->temporary = info->temporary;
}

Expand All @@ -27,8 +28,12 @@ void ViewCatalogEntry::Serialize(Serializer &serializer) {
query->Serialize(serializer);
assert(aliases.size() <= numeric_limits<uint32_t>::max());
serializer.Write<uint32_t>((uint32_t)aliases.size());
for (auto &s : aliases) {
serializer.WriteString(s);
for (auto &alias : aliases) {
serializer.WriteString(alias);
}
serializer.Write<uint32_t>((uint32_t)types.size());
for (auto &sql_type : types) {
sql_type.Serialize(serializer);
}
}

Expand All @@ -41,5 +46,9 @@ unique_ptr<CreateViewInfo> ViewCatalogEntry::Deserialize(Deserializer &source) {
for (uint32_t i = 0; i < alias_count; i++) {
info->aliases.push_back(source.Read<string>());
}
auto type_count = source.Read<uint32_t>();
for (uint32_t i = 0; i < type_count; i++) {
info->types.push_back(SQLType::Deserialize(source));
}
return info;
}
80 changes: 64 additions & 16 deletions src/function/table/sqlite/pragma_table_info.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include "duckdb/catalog/catalog.hpp"
#include "duckdb/catalog/catalog_entry/table_catalog_entry.hpp"
#include "duckdb/catalog/catalog_entry/view_catalog_entry.hpp"
#include "duckdb/common/exception.hpp"
#include "duckdb/catalog/catalog.hpp"

Expand All @@ -15,7 +16,7 @@ struct PragmaTableFunctionData : public TableFunctionData {
PragmaTableFunctionData() : entry(nullptr), offset(0) {
}

TableCatalogEntry *entry;
CatalogEntry *entry;
idx_t offset;
};

Expand All @@ -42,30 +43,19 @@ static unique_ptr<FunctionData> pragma_table_info_bind(ClientContext &context, v
return make_unique<PragmaTableFunctionData>();
}

static void pragma_table_info(ClientContext &context, vector<Value> &input, DataChunk &output, FunctionData *dataptr) {
auto &data = *((PragmaTableFunctionData *)dataptr);
if (!data.entry) {
// first call: load the entry from the catalog
assert(input.size() == 1);

auto table_name = input[0].GetValue<string>();
// look up the table name in the catalog
auto &catalog = Catalog::GetCatalog(context);
data.entry = catalog.GetEntry<TableCatalogEntry>(context, DEFAULT_SCHEMA, table_name);
}

if (data.offset >= data.entry->columns.size()) {
static void pragma_table_info_table(PragmaTableFunctionData &data, TableCatalogEntry *table, DataChunk &output) {
if (data.offset >= table->columns.size()) {
// finished returning values
return;
}
// start returning values
// either fill up the chunk or return all the remaining columns
idx_t next = min(data.offset + STANDARD_VECTOR_SIZE, (idx_t)data.entry->columns.size());
idx_t next = min(data.offset + STANDARD_VECTOR_SIZE, (idx_t)table->columns.size());
output.SetCardinality(next - data.offset);

for (idx_t i = data.offset; i < next; i++) {
auto index = i - data.offset;
auto &column = data.entry->columns[i];
auto &column = table->columns[i];
// return values:
// "cid", TypeId::INT32
assert(column.oid < (idx_t)std::numeric_limits<int32_t>::max());
Expand All @@ -88,6 +78,64 @@ static void pragma_table_info(ClientContext &context, vector<Value> &input, Data
data.offset = next;
}

static void pragma_table_info_view(PragmaTableFunctionData &data, ViewCatalogEntry *view, DataChunk &output) {
if (data.offset >= view->types.size()) {
// finished returning values
return;
}
// start returning values
// either fill up the chunk or return all the remaining columns
idx_t next = min(data.offset + STANDARD_VECTOR_SIZE, (idx_t)view->types.size());
output.SetCardinality(next - data.offset);

for (idx_t i = data.offset; i < next; i++) {
auto index = i - data.offset;
auto type = view->types[index];
auto &name = view->aliases[index];
// return values:
// "cid", TypeId::INT32

output.SetValue(0, index, Value::INTEGER((int32_t) index));
// "name", TypeId::VARCHAR
output.SetValue(1, index, Value(name));
// "type", TypeId::VARCHAR
output.SetValue(2, index, Value(SQLTypeToString(type)));
// "notnull", TypeId::BOOL
output.SetValue(3, index, Value::BOOLEAN(false));
// "dflt_value", TypeId::VARCHAR
output.SetValue(4, index, Value());
// "pk", TypeId::BOOL
output.SetValue(5, index, Value::BOOLEAN(false));
}
data.offset = next;
}

static void pragma_table_info(ClientContext &context, vector<Value> &input, DataChunk &output, FunctionData *dataptr) {
auto &data = *((PragmaTableFunctionData *)dataptr);
if (!data.entry) {
// first call: load the entry from the catalog
assert(input.size() == 1);

string schema, table_name;
auto range_var = input[0].GetValue<string>();
Catalog::ParseRangeVar(range_var, schema, table_name);

// look up the table name in the catalog
auto &catalog = Catalog::GetCatalog(context);
data.entry = catalog.GetEntry(context, CatalogType::TABLE, schema, table_name);
}
switch(data.entry->type) {
case CatalogType::TABLE:
pragma_table_info_table(data, (TableCatalogEntry*) data.entry, output);
break;
case CatalogType::VIEW:
pragma_table_info_view(data, (ViewCatalogEntry*) data.entry, output);
break;
default:
throw NotImplementedException("Unimplemented catalog type for pragma_table_info");
}
}

void PragmaTableInfo::RegisterFunction(BuiltinFunctions &set) {
set.AddFunction(
TableFunction("pragma_table_info", {SQLType::VARCHAR}, pragma_table_info_bind, pragma_table_info, nullptr));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ class ViewCatalogEntry : public StandardEntry {
unique_ptr<QueryNode> query;
//! The set of aliases associated with the view
vector<string> aliases;
//! The returned types of the view
vector<SQLType> types;

public:
//! Serialize the meta information of the ViewCatalogEntry a serializer
Expand Down
4 changes: 3 additions & 1 deletion src/include/duckdb/parser/parsed_data/create_view_info.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@ struct CreateViewInfo : public CreateInfo {

//! Table name to insert to
string view_name;
//! Aliases of the
//! Aliases of the view
vector<string> aliases;
//! Return types
vector<SQLType> types;
//! The QueryNode of the view
unique_ptr<QueryNode> query;
};
Expand Down
1 change: 1 addition & 0 deletions src/include/duckdb/parser/transformer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ class Transformer {
unique_ptr<PragmaStatement> TransformPragma(PGNode *node);
unique_ptr<ExplainStatement> TransformExplain(PGNode *node);
unique_ptr<VacuumStatement> TransformVacuum(PGNode *node);
unique_ptr<PragmaStatement> TransformShow(PGNode *node);

unique_ptr<PrepareStatement> TransformPrepare(PGNode *node);
unique_ptr<ExecuteStatement> TransformExecute(PGNode *node);
Expand Down
1 change: 1 addition & 0 deletions src/parser/transform/statement/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ add_library_unity(duckdb_transformer_statement
transform_select.cpp
transform_select_node.cpp
transform_prepare.cpp
transform_show.cpp
transform_transaction.cpp
transform_update.cpp
transform_create_sequence.cpp
Expand Down
27 changes: 27 additions & 0 deletions src/parser/transform/statement/transform_show.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#include "duckdb/parser/statement/pragma_statement.hpp"
#include "duckdb/parser/transformer.hpp"

using namespace duckdb;
using namespace std;

unique_ptr<PragmaStatement> Transformer::TransformShow(PGNode *node) {
// we transform SHOW x into PRAGMA SHOW('x')

auto stmt = reinterpret_cast<PGVariableShowStmt *>(node);

auto result = make_unique<PragmaStatement>();
auto &info = *result->info;

if (string(stmt->name) == "tables") {
// show all tables
info.name = "show_tables";
info.pragma_type = PragmaType::NOTHING;
} else {
// show one specific table
info.name = "show";
info.pragma_type = PragmaType::CALL;
info.parameters.push_back(Value(stmt->name));
}

return result;
}
3 changes: 2 additions & 1 deletion src/parser/transformer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ unique_ptr<SQLStatement> Transformer::TransformStatement(PGNode *stmt) {
return TransformExplain(stmt);
case T_PGVacuumStmt:
return TransformVacuum(stmt);

case T_PGVariableShowStmt:
return TransformShow(stmt);
default:
throw NotImplementedException(NodetypeToString(stmt->type));
}
Expand Down
5 changes: 5 additions & 0 deletions src/planner/binder/statement/bind_create_view.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,10 @@ unique_ptr<BoundCreateInfo> Binder::BindCreateViewInfo(unique_ptr<CreateInfo> in
if (base.aliases.size() > query_node->names.size()) {
throw BinderException("More VIEW aliases than columns in query result");
}
// fill up the aliases with the remaining names of the bound query
for(idx_t i = base.aliases.size(); i < query_node->names.size(); i++) {
base.aliases.push_back(query_node->names[i]);
}
base.types = query_node->types;
return result;
}
9 changes: 8 additions & 1 deletion src/planner/binder/tableref/bind_basetableref.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,14 @@ unique_ptr<BoundTableRef> Binder::Bind(BaseTableRef &expr) {
subquery.alias = expr.alias.empty() ? expr.table_name : expr.alias;
subquery.column_name_alias = view_catalog_entry->aliases;
// bind the child subquery
return Bind(subquery);
auto bound_child = Bind(subquery);
assert(bound_child->type == TableReferenceType::SUBQUERY);
// verify that the types and names match up with the expected types and names
auto &bound_subquery = (BoundSubqueryRef&) *bound_child;
if (bound_subquery.subquery->types != view_catalog_entry->types) {
throw BinderException("Contents of view were altered: types don't match!");
}
return bound_child;
}
default:
throw NotImplementedException("Catalog entry type");
Expand Down
50 changes: 39 additions & 11 deletions src/planner/pragma_handler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@
#include "duckdb/parser/expression/constant_expression.hpp"
#include "duckdb/parser/expression/star_expression.hpp"
#include "duckdb/parser/tableref/table_function_ref.hpp"
#include "duckdb/parser/expression/columnref_expression.hpp"
#include "duckdb/parser/expression/function_expression.hpp"

#include "duckdb/parser/parsed_data/pragma_info.hpp"
#include "duckdb/parser/parser.hpp"

#include "duckdb/common/string_util.hpp"

Expand All @@ -28,17 +29,44 @@ unique_ptr<SQLStatement> PragmaHandler::HandlePragma(PragmaInfo &pragma) {
}
// generate a SelectStatement that selects from the pragma_table_info function
// i.e. SELECT * FROM pragma_table_info('table_name')
auto select_statement = make_unique<SelectStatement>();
auto select_node = make_unique<SelectNode>();
select_node->select_list.push_back(make_unique<StarExpression>());
Parser parser;
parser.ParseQuery("SELECT * FROM pragma_table_info()");

// push the table name parameter into the table function
auto select_statement = move(parser.statements[0]);
auto &select = (SelectStatement&) *select_statement;
auto &select_node = (SelectNode&) *select.node;
auto &table_function = (TableFunctionRef&) *select_node.from_table;
auto &function = (FunctionExpression&) *table_function.function;
function.children.push_back(make_unique<ConstantExpression>(SQLTypeId::VARCHAR, pragma.parameters[0]));
return select_statement;
} else if (keyword == "show_tables") {
if (pragma.pragma_type != PragmaType::NOTHING) {
throw ParserException("Invalid PRAGMA show_tables: cannot be called");
}
// turn into SELECT name FROM sqlite_master();
Parser parser;
parser.ParseQuery("SELECT name FROM sqlite_master() ORDER BY name");
return move(parser.statements[0]);
} else if (keyword == "show") {
if (pragma.pragma_type != PragmaType::CALL) {
throw ParserException("Invalid PRAGMA show_tables: expected a function call");
}
if (pragma.parameters.size() != 1) {
throw ParserException("Invalid PRAGMA show_tables: show_tables does not take any arguments");
}
// PRAGMA table_info but with some aliases
Parser parser;
parser.ParseQuery("SELECT name AS \"Field\", type as \"Type\", CASE WHEN \"notnull\" THEN 'NO' ELSE 'YES' END AS \"Null\", NULL AS \"Key\", dflt_value AS \"Default\", NULL AS \"Extra\" FROM pragma_table_info()");

vector<unique_ptr<ParsedExpression>> children;
children.push_back(make_unique<ConstantExpression>(SQLTypeId::VARCHAR, pragma.parameters[0]));
auto table_function = make_unique<TableFunctionRef>();
table_function->function = make_unique<FunctionExpression>(DEFAULT_SCHEMA, "pragma_table_info", children);
select_node->from_table = move(table_function);
select_statement->node = move(select_node);
return move(select_statement);
// push the table name parameter into the table function
auto select_statement = move(parser.statements[0]);
auto &select = (SelectStatement&) *select_statement;
auto &select_node = (SelectNode&) *select.node;
auto &table_function = (TableFunctionRef&) *select_node.from_table;
auto &function = (FunctionExpression&) *table_function.function;
function.children.push_back(make_unique<ConstantExpression>(SQLTypeId::VARCHAR, pragma.parameters[0]));
return select_statement;
}
return nullptr;
}
1 change: 1 addition & 0 deletions test/sql/pragma/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
add_library_unity(test_sql_pragma
OBJECT
test_pragma.cpp
test_show_tables.cpp
test_table_info.cpp)
set(ALL_OBJECT_FILES
${ALL_OBJECT_FILES} $<TARGET_OBJECTS:test_sql_pragma>
Expand Down
51 changes: 51 additions & 0 deletions test/sql/pragma/test_show_tables.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#include "catch.hpp"
#include "test_helpers.hpp"

using namespace duckdb;
using namespace std;

TEST_CASE("Test SHOW/DESCRIBE tables", "[pragma]") {
unique_ptr<QueryResult> result;
DuckDB db(nullptr);
Connection con(db);

REQUIRE_NO_FAIL(con.Query("CREATE TABLE integers(i INTEGER, j INTEGER)"));
REQUIRE_NO_FAIL(con.Query("CREATE VIEW v1 AS SELECT DATE '1992-01-01' AS k"));

// SHOW and DESCRIBE are aliases
result = con.Query("SHOW TABLES");
REQUIRE(CHECK_COLUMN(result, 0, {"integers", "v1"}));
result = con.Query("DESCRIBE TABLES");
REQUIRE(CHECK_COLUMN(result, 0, {"integers", "v1"}));
// internally they are equivalent to PRAGMA SHOW_TABLES();
result = con.Query("PRAGMA show_tables");
REQUIRE(CHECK_COLUMN(result, 0, {"integers", "v1"}));

result = con.Query("SHOW integers");
// Field | Type | Null | Key | Default | Extra
REQUIRE(CHECK_COLUMN(result, 0, {"i", "j"}));
REQUIRE(CHECK_COLUMN(result, 1, {"INTEGER", "INTEGER"}));
REQUIRE(CHECK_COLUMN(result, 2, {"YES", "YES"}));
REQUIRE(CHECK_COLUMN(result, 3, {Value(), Value()}));
REQUIRE(CHECK_COLUMN(result, 4, {"NULL", "NULL"}));
REQUIRE(CHECK_COLUMN(result, 5, {Value(), Value()}));
// equivalent to PRAGMA SHOW('integers')
result = con.Query("PRAGMA SHOW('integers')");
// Field | Type | Null | Key | Default | Extra
REQUIRE(CHECK_COLUMN(result, 0, {"i", "j"}));
REQUIRE(CHECK_COLUMN(result, 1, {"INTEGER", "INTEGER"}));
REQUIRE(CHECK_COLUMN(result, 2, {"YES", "YES"}));
REQUIRE(CHECK_COLUMN(result, 3, {Value(), Value()}));
REQUIRE(CHECK_COLUMN(result, 4, {"NULL", "NULL"}));
REQUIRE(CHECK_COLUMN(result, 5, {Value(), Value()}));

// we can also describe views
result = con.Query("DESCRIBE v1");
// Field | Type | Null | Key | Default | Extra
REQUIRE(CHECK_COLUMN(result, 0, {"k"}));
REQUIRE(CHECK_COLUMN(result, 1, {"DATE"}));
REQUIRE(CHECK_COLUMN(result, 2, {"YES"}));
REQUIRE(CHECK_COLUMN(result, 3, {Value()}));
REQUIRE(CHECK_COLUMN(result, 4, {Value()}));
REQUIRE(CHECK_COLUMN(result, 5, {Value()}));
}
Loading

0 comments on commit 9f8bca8

Please sign in to comment.