diff --git a/duckdb-pgq b/duckdb-pgq index bdbc6067..8b4b2643 160000 --- a/duckdb-pgq +++ b/duckdb-pgq @@ -1 +1 @@ -Subproject commit bdbc6067198d19bf8d1455d1fb6aa534d0d2785e +Subproject commit 8b4b2643a2895780b5138c1a51b665cf475addf0 diff --git a/duckpgq/include/duckpgq_extension.hpp b/duckpgq/include/duckpgq_extension.hpp index eccc0d2f..97c6e2d2 100644 --- a/duckpgq/include/duckpgq_extension.hpp +++ b/duckpgq/include/duckpgq_extension.hpp @@ -45,6 +45,9 @@ ParserExtensionPlanResult duckpgq_plan(ParserExtensionInfo *info, ClientContext &, unique_ptr); +ParserExtensionPlanResult duckpgq_handle_statement(unique_ptr &statement); + + struct DuckPGQParserExtension : public ParserExtension { DuckPGQParserExtension() : ParserExtension() { parse_function = duckpgq_parse; diff --git a/duckpgq/src/duckpgq_extension.cpp b/duckpgq/src/duckpgq_extension.cpp index 1cadbe71..cd5b3dfb 100644 --- a/duckpgq/src/duckpgq_extension.cpp +++ b/duckpgq/src/duckpgq_extension.cpp @@ -1,6 +1,9 @@ #define DUCKDB_EXTENSION_MAIN #include "duckpgq_extension.hpp" + +#include + #include "duckdb/function/scalar_function.hpp" #include "duckpgq/duckpgq_functions.hpp" @@ -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" @@ -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 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(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(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(statement); auto select_node = dynamic_cast(select_statement->node.get()); @@ -131,18 +118,24 @@ duckpgq_plan(ParserExtensionInfo *, ClientContext &context, auto function = dynamic_cast(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(); + auto create_property_graph = dynamic_cast(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(create_statement.info.get()); + duckpgq_handle_statement(create_table->query.get(), duckpgq_state); } if (statement->type == StatementType::DROP_STATEMENT) { ParserExtensionPlanResult result; @@ -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(); + auto select_statement = dynamic_cast(explain_statement.stmt.get()); + duckpgq_handle_statement(select_statement, duckpgq_state); + } + if (statement->type == StatementType::COPY_STATEMENT) { + auto ©_statement = statement->Cast(); + auto select_node = dynamic_cast(copy_statement.select_statement.get()); + auto from_table_function = + dynamic_cast(select_node->from_table.get()); + auto function = + dynamic_cast(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(); + 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 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(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(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"; } diff --git a/test/sql/copy_to_duckpgq.test b/test/sql/copy_to_duckpgq.test new file mode 100644 index 00000000..a0e12974 --- /dev/null +++ b/test/sql/copy_to_duckpgq.test @@ -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 diff --git a/test/sql/explain_duckpgq.test b/test/sql/explain_duckpgq.test index 957016bd..37255858 100644 --- a/test/sql/explain_duckpgq.test +++ b/test/sql/explain_duckpgq.test @@ -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; \ No newline at end of file diff --git a/test/sql/snb/snb.test b/test/sql/snb/snb.test index f04f2cba..b4ef7a35 100644 --- a/test/sql/snb/snb.test +++ b/test/sql/snb/snb.test @@ -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