Skip to content

Commit

Permalink
Merge pull request #71 from cwida/61-support-explain-queries
Browse files Browse the repository at this point in the history
Adding create property graph
  • Loading branch information
Dtenwolde authored Jan 19, 2024
2 parents a751a9b + 9a6f526 commit 7f37c8b
Show file tree
Hide file tree
Showing 6 changed files with 217 additions and 35 deletions.
2 changes: 1 addition & 1 deletion duckdb-pgq
3 changes: 3 additions & 0 deletions duckpgq/include/duckpgq_extension.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ ParserExtensionPlanResult duckpgq_plan(ParserExtensionInfo *info,
ClientContext &,
unique_ptr<ParserExtensionParseData>);

ParserExtensionPlanResult duckpgq_handle_statement(unique_ptr<SQLStatement> &statement);


struct DuckPGQParserExtension : public ParserExtension {
DuckPGQParserExtension() : ParserExtension() {
parse_function = duckpgq_parse;
Expand Down
98 changes: 71 additions & 27 deletions duckpgq/src/duckpgq_extension.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
#define DUCKDB_EXTENSION_MAIN

#include "duckpgq_extension.hpp"

#include <duckdb/parser/statement/insert_statement.hpp>

#include "duckdb/function/scalar_function.hpp"
#include "duckpgq/duckpgq_functions.hpp"

Expand All @@ -12,6 +15,9 @@
#include "duckdb/parser/parsed_data/create_scalar_function_info.hpp"
#include "duckdb/parser/parsed_data/create_table_function_info.hpp"
#include "duckdb/parser/query_node/select_node.hpp"
#include "duckdb/parser/statement/copy_statement.hpp"
#include "duckdb/parser/parsed_data/create_table_info.hpp"

#include "duckdb/parser/statement/extension_statement.hpp"

#include "duckpgq/functions/tablefunctions/drop_property_graph.hpp"
Expand Down Expand Up @@ -103,26 +109,7 @@ BoundStatement duckpgq_bind(ClientContext &context, Binder &binder,
throw BinderException("Unable to find DuckPGQ Parse Data");
}

ParserExtensionPlanResult
duckpgq_plan(ParserExtensionInfo *, ClientContext &context,
unique_ptr<ParserExtensionParseData> parse_data) {
auto duckpgq_state_entry = context.registered_state.find("duckpgq");
DuckPGQState *duckpgq_state;
if (duckpgq_state_entry == context.registered_state.end()) {
auto state = make_shared<DuckPGQState>(std::move(parse_data));
context.registered_state["duckpgq"] = state;
duckpgq_state = state.get();
} else {
duckpgq_state = (DuckPGQState *)duckpgq_state_entry->second.get();
duckpgq_state->parse_data = std::move(parse_data);
}
auto duckpgq_parse_data =
dynamic_cast<DuckPGQParseData *>(duckpgq_state->parse_data.get());

if (!duckpgq_parse_data) {
throw BinderException("No DuckPGQ parse data found");
}
auto statement = duckpgq_parse_data->statement.get();
ParserExtensionPlanResult duckpgq_handle_statement(SQLStatement *statement, DuckPGQState &duckpgq_state) {
if (statement->type == StatementType::SELECT_STATEMENT) {
auto select_statement = dynamic_cast<SelectStatement *>(statement);
auto select_node = dynamic_cast<SelectNode *>(select_statement->node.get());
Expand All @@ -131,18 +118,24 @@ duckpgq_plan(ParserExtensionInfo *, ClientContext &context,
auto function =
dynamic_cast<FunctionExpression *>(from_table_function->function.get());
if (function->function_name == "duckpgq_match") {
duckpgq_state->transform_expression =
duckpgq_state.transform_expression =
std::move(std::move(function->children[0]));
function->children.pop_back();
}
throw Exception("use duckpgq_bind instead");
}
if (statement->type == StatementType::CREATE_STATEMENT) {
ParserExtensionPlanResult result;
result.function = CreatePropertyGraphFunction();
result.requires_valid_transaction = true;
result.return_type = StatementReturnType::QUERY_RESULT;
return result;
auto &create_statement = statement->Cast<CreateStatement>();
auto create_property_graph = dynamic_cast<CreatePropertyGraphInfo*>(create_statement.info.get());
if (create_property_graph) {
ParserExtensionPlanResult result;
result.function = CreatePropertyGraphFunction();
result.requires_valid_transaction = true;
result.return_type = StatementReturnType::QUERY_RESULT;
return result;
}
auto create_table = reinterpret_cast<CreateTableInfo*>(create_statement.info.get());
duckpgq_handle_statement(create_table->query.get(), duckpgq_state);
}
if (statement->type == StatementType::DROP_STATEMENT) {
ParserExtensionPlanResult result;
Expand All @@ -151,7 +144,58 @@ duckpgq_plan(ParserExtensionInfo *, ClientContext &context,
result.return_type = StatementReturnType::QUERY_RESULT;
return result;
}
throw BinderException("Unknown DuckPGQ query encountered");
if (statement->type == StatementType::EXPLAIN_STATEMENT) {
auto &explain_statement = statement->Cast<ExplainStatement>();
auto select_statement = dynamic_cast<SelectStatement*>(explain_statement.stmt.get());
duckpgq_handle_statement(select_statement, duckpgq_state);
}
if (statement->type == StatementType::COPY_STATEMENT) {
auto &copy_statement = statement->Cast<CopyStatement>();
auto select_node = dynamic_cast<SelectNode *>(copy_statement.select_statement.get());
auto from_table_function =
dynamic_cast<TableFunctionRef *>(select_node->from_table.get());
auto function =
dynamic_cast<FunctionExpression *>(from_table_function->function.get());
if (function->function_name == "duckpgq_match") {
duckpgq_state.transform_expression =
std::move(std::move(function->children[0]));
function->children.pop_back();
}
throw Exception("use duckpgq_bind instead");
}
if (statement->type == StatementType::INSERT_STATEMENT) {
auto &insert_statement = statement->Cast<InsertStatement>();
duckpgq_handle_statement(insert_statement.select_statement.get(), duckpgq_state);
}

// Preferably throw NotImplementedExpection here, but only BinderExceptions are caught properly on MacOS right now
throw BinderException("%s has not been implemented yet for DuckPGQ queries", StatementTypeToString(statement->type));
}

ParserExtensionPlanResult
duckpgq_plan(ParserExtensionInfo *, ClientContext &context,
unique_ptr<ParserExtensionParseData> parse_data) {
auto duckpgq_state_entry = context.registered_state.find("duckpgq");
DuckPGQState *duckpgq_state;
if (duckpgq_state_entry == context.registered_state.end()) {
auto state = make_shared<DuckPGQState>(std::move(parse_data));
context.registered_state["duckpgq"] = state;
duckpgq_state = state.get();
} else {
duckpgq_state = (DuckPGQState *)duckpgq_state_entry->second.get();
duckpgq_state->parse_data = std::move(parse_data);
}
auto duckpgq_parse_data =
dynamic_cast<DuckPGQParseData *>(duckpgq_state->parse_data.get());

if (!duckpgq_parse_data) {
throw BinderException("No DuckPGQ parse data found");
}

auto statement = duckpgq_parse_data->statement.get();
return duckpgq_handle_statement(statement, *duckpgq_state);


}

std::string DuckpgqExtension::Name() { return "duckpgq"; }
Expand Down
85 changes: 85 additions & 0 deletions test/sql/copy_to_duckpgq.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# name: test/sql/sqlpgq/copy_to_duckpgq.test
# group: [duckpgq]

require duckpgq

statement ok
import database 'duckdb-pgq/data/SNB0.003'

statement ok
-CREATE PROPERTY GRAPH snb
VERTEX TABLES (
Person LABEL Person,
Forum LABEL Forum,
Organisation LABEL Organisation IN typemask(company, university),
Place LABEL Place,
Tag LABEL Tag,
TagClass LABEL TagClass,
Country LABEL Country,
City LABEL City,
Message LABEL Message
)
EDGE TABLES (
Person_knows_person SOURCE KEY (Person1Id) REFERENCES Person (id)
DESTINATION KEY (Person2Id) REFERENCES Person (id)
LABEL Knows,
Forum_hasMember_Person SOURCE KEY (ForumId) REFERENCES Forum (id)
DESTINATION KEY (PersonId) REFERENCES Person (id)
LABEL hasMember,
Forum_hasTag_Tag SOURCE KEY (ForumId) REFERENCES Forum (id)
DESTINATION KEY (TagId) REFERENCES Tag (id)
LABEL Forum_hasTag,
Person_hasInterest_Tag SOURCE KEY (PersonId) REFERENCES Person (id)
DESTINATION KEY (TagId) REFERENCES Tag (id)
LABEL hasInterest,
person_workAt_Organisation SOURCE KEY (PersonId) REFERENCES Person (id)
DESTINATION KEY (OrganisationId) REFERENCES Organisation (id)
LABEL workAt_Organisation,
Person_likes_Message SOURCE KEY (PersonId) REFERENCES Person (id)
DESTINATION KEY (id) REFERENCES Message (id)
LABEL likes_Message,
Message_hasTag_Tag SOURCE KEY (id) REFERENCES Message (id)
DESTINATION KEY (TagId) REFERENCES Tag (id)
LABEL message_hasTag,
Message_hasAuthor_Person SOURCE KEY (messageId) REFERENCES Message (id)
DESTINATION KEY (PersonId) REFERENCES Person (id)
LABEL hasAuthor,
Message_replyOf_Message SOURCE KEY (messageId) REFERENCES Message (id)
DESTINATION KEY (ParentMessageId) REFERENCES Message (id)
LABEL replyOf
);

# IS1
statement ok
-COPY (FROM GRAPH_TABLE (snb
MATCH (a is person where a.id = 17592186044461)
COLUMNS(a.firstName, a.lastName, a.birthday, a.locationIP, a.browserUsed, a.LocationCityId, a.gender, a.creationDate)
) tmp) TO '__TEST_DIR__/is1.csv' (HEADER FALSE);

query IIIIIIII
SELECT * FROM '__TEST_DIR__/is1.csv';
----
Ali Abouba 1987-05-29 41.203.147.168 Internet Explorer 1264 male 2011-05-12 02:46:47.595

statement ok
-CREATE TABLE result as (FROM GRAPH_TABLE (snb
MATCH (a is person where a.id = 17592186044461)
COLUMNS(a.firstName, a.lastName, a.birthday, a.locationIP, a.browserUsed, a.LocationCityId, a.gender, a.creationDate)
) tmp);

query IIIIIIII
SELECT * FROM result;
----
Ali Abouba 1987-05-29 41.203.147.168 Internet Explorer 1264 male 2011-05-12 02:46:47.595+00

statement ok
-INSERT INTO result (FROM GRAPH_TABLE (snb
MATCH (a is person where a.id = 17592186044461)
COLUMNS(a.firstName, a.lastName, a.birthday, a.locationIP, a.browserUsed, a.LocationCityId, a.gender, a.creationDate)
) tmp)

query IIIIIIII
SELECT * FROM result;
----
Ali Abouba 1987-05-29 41.203.147.168 Internet Explorer 1264 male 2011-05-12 02:46:47.595+00
Ali Abouba 1987-05-29 41.203.147.168 Internet Explorer 1264 male 2011-05-12 02:46:47.595+00
59 changes: 54 additions & 5 deletions test/sql/explain_duckpgq.test
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,63 @@ require duckpgq
statement ok
import database 'duckdb-pgq/data/SNB0.003'

statement ok
-CREATE PROPERTY GRAPH snb
VERTEX TABLES (
Person LABEL Person,
Forum LABEL Forum,
Organisation LABEL Organisation IN typemask(company, university),
Place LABEL Place,
Tag LABEL Tag,
TagClass LABEL TagClass,
Country LABEL Country,
City LABEL City,
Message LABEL Message
)
EDGE TABLES (
Person_knows_person SOURCE KEY (Person1Id) REFERENCES Person (id)
DESTINATION KEY (Person2Id) REFERENCES Person (id)
LABEL Knows,
Forum_hasMember_Person SOURCE KEY (ForumId) REFERENCES Forum (id)
DESTINATION KEY (PersonId) REFERENCES Person (id)
LABEL hasMember,
Forum_hasTag_Tag SOURCE KEY (ForumId) REFERENCES Forum (id)
DESTINATION KEY (TagId) REFERENCES Tag (id)
LABEL Forum_hasTag,
Person_hasInterest_Tag SOURCE KEY (PersonId) REFERENCES Person (id)
DESTINATION KEY (TagId) REFERENCES Tag (id)
LABEL hasInterest,
person_workAt_Organisation SOURCE KEY (PersonId) REFERENCES Person (id)
DESTINATION KEY (OrganisationId) REFERENCES Organisation (id)
LABEL workAt_Organisation,
Person_likes_Message SOURCE KEY (PersonId) REFERENCES Person (id)
DESTINATION KEY (id) REFERENCES Message (id)
LABEL likes_Message,
Message_hasTag_Tag SOURCE KEY (id) REFERENCES Message (id)
DESTINATION KEY (TagId) REFERENCES Tag (id)
LABEL message_hasTag,
Message_hasAuthor_Person SOURCE KEY (messageId) REFERENCES Message (id)
DESTINATION KEY (PersonId) REFERENCES Person (id)
LABEL hasAuthor,
Message_replyOf_Message SOURCE KEY (messageId) REFERENCES Message (id)
DESTINATION KEY (ParentMessageId) REFERENCES Message (id)
LABEL replyOf
);

#IC 2
# EXPLAIN queries are not yet supported (https://github.com/cwida/duckpgq-extension/issues/61)
statement error
statement ok
-EXPLAIN FROM GRAPH_TABLE (snb
MATCH (a:Person WHERE a.id = 17592186044461)-[k:knows]-(b:Person)<-[au:hasAuthor]-(m:message WHERE m.creationDate < '2010-10-16')
COLUMNS (a.id, a.firstName, a.lastName, m.id as messageId, coalesce(m.imageFile, m.content), m.creationDate)
) tmp
ORDER BY creationDate DESC, Messageid ASC
LIMIT 20
----
Binder Error: Unknown DuckPGQ query encountered
LIMIT 20;

#IC 2
statement ok
-EXPLAIN ANALYZE FROM GRAPH_TABLE (snb
MATCH (a:Person WHERE a.id = 17592186044461)-[k:knows]-(b:Person)<-[au:hasAuthor]-(m:message WHERE m.creationDate < '2010-10-16')
COLUMNS (a.id, a.firstName, a.lastName, m.id as messageId, coalesce(m.imageFile, m.content), m.creationDate)
) tmp
ORDER BY creationDate DESC, Messageid ASC
LIMIT 20;
5 changes: 3 additions & 2 deletions test/sql/snb/snb.test
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,8 @@ query IIIII
MATCH (replyAuthor:person)<-[au2:hasAuthor]-(c:message where c.ParentMessageId is not null)-[r:replyOf]->(m:message where m.id = 618475290624)-[au:hasAuthor]->(messageAuthor:person),
(replyAuthor:person)-[k:knows]-(messageAuthor:person)
COLUMNS (c.id,c.content,c.creationDate, replyAuthor.id % 10, replyAuthor.firstName || replyAuthor.lastName)
) tmp;
) tmp
ORDER BY tmp.content;
----
962072674305 yes 2012-07-08 23:48:41.63+00 1 AlimGuliyev
962072674306 thanks 2012-07-08 20:32:03.239+00 1 AlimGuliyev
962072674305 yes 2012-07-08 23:48:41.63+00 1 AlimGuliyev

0 comments on commit 7f37c8b

Please sign in to comment.