diff --git a/dataset/rel-group/copy.cypher b/dataset/rel-group/copy.cypher new file mode 100644 index 00000000000..7f0777002f7 --- /dev/null +++ b/dataset/rel-group/copy.cypher @@ -0,0 +1,8 @@ +COPY personA FROM "dataset/rel-group/node.csv" (HeaDER=true, deLim=','); +COPY personB FROM "dataset/rel-group/node.csv" (HeaDER=true, deLim=','); +COPY personC FROM "dataset/rel-group/node.csv" (HeaDER=true, deLim=','); +COPY knows_personA_personB FROM "dataset/rel-group/edge.csv"; +COPY knows_personA_personC FROM "dataset/rel-group/edge.csv"; +COPY knows_personB_personC FROM "dataset/rel-group/edge.csv"; +COPY likes_personA_personB FROM "dataset/rel-group/edge.csv"; +COPY likes_personB_personA FROM "dataset/rel-group/edge.csv"; diff --git a/dataset/rel-group/edge.csv b/dataset/rel-group/edge.csv new file mode 100644 index 00000000000..de5e57a6bb2 --- /dev/null +++ b/dataset/rel-group/edge.csv @@ -0,0 +1,14 @@ +0,2,2021-06-30 +0,3,2021-06-30 +0,5,2021-06-30 +2,0,2021-06-30 +2,3,1950-05-14 +2,5,1950-05-14 +3,0,2021-06-30 +3,2,1950-05-14 +3,5,2000-01-01 +5,0,2021-06-30 +5,2,1950-05-14 +5,3,2000-01-01 +7,8,1905-12-12 +7,9,1905-12-12 \ No newline at end of file diff --git a/dataset/rel-group/node.csv b/dataset/rel-group/node.csv new file mode 100644 index 00000000000..58c9acd770f --- /dev/null +++ b/dataset/rel-group/node.csv @@ -0,0 +1,9 @@ +id,fname +0,Alice +2,Bob +3,Carol +5,Dan +7,Elizabeth +8,Farooq +9,Greg +10,Hubert Blaine Wolfeschlegelsteinhausenbergerdorff diff --git a/dataset/rel-group/schema.cypher b/dataset/rel-group/schema.cypher new file mode 100644 index 00000000000..1dc2f7a43d5 --- /dev/null +++ b/dataset/rel-group/schema.cypher @@ -0,0 +1,5 @@ +create node table personA (ID INt64, fName StRING, PRIMARY KEY (ID)); +create node table personB (ID INt64, fName StRING, PRIMARY KEY (ID)); +create node table personC (ID INt64, fName StRING, PRIMARY KEY (ID)); +create rel table group knows (FROM personA TO personB, FROM personA To personC, FROM personB TO personC, date DATE); +create rel table group likes (FROM personA TO personB, FROM personB To personA, date DATE); diff --git a/src/binder/bind/bind_copy.cpp b/src/binder/bind/bind_copy.cpp index 12525b89b4f..4beec8e1ec7 100644 --- a/src/binder/bind/bind_copy.cpp +++ b/src/binder/bind/bind_copy.cpp @@ -88,7 +88,7 @@ expression_vector Binder::bindColumnExpressions( return columnExpressions; } -bool Binder::bindContainsSerial(TableSchema* tableSchema, CopyDescription::FileType fileType) { +static bool bindContainsSerial(TableSchema* tableSchema, CopyDescription::FileType fileType) { bool containsSerial = false; for (auto& property : tableSchema->properties) { if (property->getDataType()->getLogicalTypeID() == LogicalTypeID::SERIAL) { @@ -104,7 +104,7 @@ std::unique_ptr Binder::bindCopyFromClause(const Statement& stat auto catalogContent = catalog.getReadOnlyVersion(); auto tableName = copyStatement.getTableName(); // Bind to table schema. - validateTableExist(catalog, tableName); + validateNodeRelTableExist(tableName); auto tableID = catalogContent->getTableID(tableName); auto tableSchema = catalogContent->getTableSchema(tableID); // Bind csv reader configuration @@ -127,17 +127,17 @@ std::unique_ptr Binder::bindCopyFromClause(const Statement& stat actualFileType, boundFilePaths, std::move(csvReaderConfig)); auto nodeOffsetExpression = createVariable(std::string(Property::OFFSET_NAME), LogicalType{LogicalTypeID::INT64}); - auto boundOffsetExpression = tableSchema->tableType == TableType::REL ? - createVariable(std::string(Property::REL_BOUND_OFFSET_NAME), - LogicalType{LogicalTypeID::ARROW_COLUMN}) : - nullptr; - auto nbrOffsetExpression = tableSchema->tableType == TableType::REL ? - createVariable(std::string(Property::REL_NBR_OFFSET_NAME), - LogicalType{LogicalTypeID::ARROW_COLUMN}) : - nullptr; + std::shared_ptr boundOffset = nullptr; + std::shared_ptr nbrOffset = nullptr; + if (tableSchema->tableType == TableType::REL) { + boundOffset = createVariable( + std::string(Property::REL_BOUND_OFFSET_NAME), LogicalType{LogicalTypeID::ARROW_COLUMN}); + nbrOffset = createVariable( + std::string(Property::REL_NBR_OFFSET_NAME), LogicalType{LogicalTypeID::ARROW_COLUMN}); + } auto boundCopyFromInfo = std::make_unique(std::move(copyDescription), tableSchema, std::move(columnExpressions), std::move(nodeOffsetExpression), - std::move(boundOffsetExpression), std::move(nbrOffsetExpression), containsSerial); + std::move(boundOffset), std::move(nbrOffset), containsSerial); return std::make_unique(std::move(boundCopyFromInfo)); } diff --git a/src/binder/bind/bind_ddl.cpp b/src/binder/bind/bind_ddl.cpp index c0321dbad77..2c7093cb7f5 100644 --- a/src/binder/bind/bind_ddl.cpp +++ b/src/binder/bind/bind_ddl.cpp @@ -234,7 +234,7 @@ std::unique_ptr Binder::bindCreateTable(const parser::Statement& std::unique_ptr Binder::bindDropTable(const Statement& statement) { auto& dropTable = (DropTable&)statement; auto tableName = dropTable.getTableName(); - validateTableExist(catalog, tableName); + validateNodeRelTableExist(tableName); auto catalogContent = catalog.getReadOnlyVersion(); auto tableID = catalogContent->getTableID(tableName); if (catalogContent->containNodeTable(tableName)) { @@ -247,7 +247,7 @@ std::unique_ptr Binder::bindRenameTable(const Statement& stateme auto renameTable = (RenameTable&)statement; auto tableName = renameTable.getTableName(); auto catalogContent = catalog.getReadOnlyVersion(); - validateTableExist(catalog, tableName); + validateNodeRelTableExist(tableName); if (catalogContent->containTable(renameTable.getNewName())) { throw BinderException("Table: " + renameTable.getNewName() + " already exists."); } @@ -258,7 +258,7 @@ std::unique_ptr Binder::bindRenameTable(const Statement& stateme std::unique_ptr Binder::bindAddProperty(const Statement& statement) { auto& addProperty = (AddProperty&)statement; auto tableName = addProperty.getTableName(); - validateTableExist(catalog, tableName); + validateNodeRelTableExist(tableName); auto catalogContent = catalog.getReadOnlyVersion(); auto tableID = catalogContent->getTableID(tableName); auto dataType = bindDataType(addProperty.getDataType()); @@ -277,7 +277,7 @@ std::unique_ptr Binder::bindAddProperty(const Statement& stateme std::unique_ptr Binder::bindDropProperty(const Statement& statement) { auto& dropProperty = (DropProperty&)statement; auto tableName = dropProperty.getTableName(); - validateTableExist(catalog, tableName); + validateNodeRelTableExist(tableName); auto catalogContent = catalog.getReadOnlyVersion(); auto tableID = catalogContent->getTableID(tableName); auto tableSchema = catalogContent->getTableSchema(tableID); @@ -292,7 +292,7 @@ std::unique_ptr Binder::bindDropProperty(const Statement& statem std::unique_ptr Binder::bindRenameProperty(const Statement& statement) { auto& renameProperty = (RenameProperty&)statement; auto tableName = renameProperty.getTableName(); - validateTableExist(catalog, tableName); + validateNodeRelTableExist(tableName); auto catalogContent = catalog.getReadOnlyVersion(); auto tableID = catalogContent->getTableID(tableName); auto tableSchema = catalogContent->getTableSchema(tableID); diff --git a/src/binder/bind/bind_graph_pattern.cpp b/src/binder/bind/bind_graph_pattern.cpp index ff4314e3df2..4f74d5a807b 100644 --- a/src/binder/bind/bind_graph_pattern.cpp +++ b/src/binder/bind/bind_graph_pattern.cpp @@ -4,6 +4,7 @@ #include "binder/expression/path_expression.h" #include "binder/expression/property_expression.h" #include "catalog/node_table_schema.h" +#include "catalog/rel_table_group_schema.h" #include "catalog/rel_table_schema.h" using namespace kuzu::common; @@ -457,39 +458,39 @@ std::vector Binder::bindNodeTableIDs(const std::vector& } std::vector Binder::bindRelTableIDs(const std::vector& tableNames) { - if (catalog.getReadOnlyVersion()->getRelTableIDs().empty()) { + auto catalogContent = catalog.getReadOnlyVersion(); + if (catalogContent->getRelTableIDs().empty()) { throw BinderException("No rel table exists in database."); } // Rewrite empty rel pattern "-[]-" as all rel tables std::unordered_set tableIDs; if (tableNames.empty()) { - for (auto tableID : catalog.getReadOnlyVersion()->getRelTableIDs()) { + for (auto tableID : catalogContent->getRelTableIDs()) { tableIDs.insert(tableID); } } for (auto& tableName : tableNames) { - tableIDs.insert(bindRelTableID(tableName)); + validateTableExist(tableName); + auto tableID = catalogContent->getTableID(tableName); + auto tableSchema = catalogContent->getTableSchema(tableID); + switch (tableSchema->getTableType()) { + case TableType::REL: { + tableIDs.insert(tableID); + } break; + case TableType::REL_GROUP: { + auto relGroupSchema = reinterpret_cast(tableSchema); + for (auto& relTableID : relGroupSchema->getRelTableIDs()) { + tableIDs.insert(relTableID); + } + } break; + default: + throw BinderException("Rel table " + tableName + " does not exist."); + } } auto result = std::vector{tableIDs.begin(), tableIDs.end()}; std::sort(result.begin(), result.end()); return result; } -table_id_t Binder::bindRelTableID(const std::string& tableName) const { - auto catalogContent = catalog.getReadOnlyVersion(); - auto errorMsg; - if (!catalogContent->containTable(tableName)) { - throw BinderException() - } - auto tableID = catalogContent->getTableID(tableName); - auto tableSchema = catalogContent.getT - if (!catalog.getReadOnlyVersion()->containRelTable(tableName)) { - throw BinderException("Rel table " + tableName + " does not exist."); - } - return catalog.getReadOnlyVersion()->getTableID(tableName); -} - - - } // namespace binder } // namespace kuzu diff --git a/src/binder/binder.cpp b/src/binder/binder.cpp index 70e247565c2..354689615d2 100644 --- a/src/binder/binder.cpp +++ b/src/binder/binder.cpp @@ -67,12 +67,12 @@ std::shared_ptr Binder::bindWhereExpression(const ParsedExpression& return whereExpression; } - table_id_t Binder::bindNodeTableID(const std::string& tableName) const { - if (!catalog.getReadOnlyVersion()->containNodeTable(tableName)) { + auto catalogContent = catalog.getReadOnlyVersion(); + if (!catalogContent->containNodeTable(tableName)) { throw BinderException("Node table " + tableName + " does not exist."); } - return catalog.getReadOnlyVersion()->getTableID(tableName); + return catalogContent->getTableID(tableName); } std::shared_ptr Binder::createVariable( @@ -158,10 +158,16 @@ void Binder::validateReadNotFollowUpdate(const NormalizedSingleQuery& singleQuer } } -void Binder::validateTableExist(const Catalog& _catalog, std::string& tableName) { - if (!_catalog.getReadOnlyVersion()->containNodeTable(tableName) && - !_catalog.getReadOnlyVersion()->containRelTable(tableName)) { - throw BinderException("Node/Rel " + tableName + " does not exist."); +void Binder::validateTableExist(const std::string& tableName) { + if (!catalog.getReadOnlyVersion()->containTable(tableName)) { + throw BinderException("Table " + tableName + " does not exist."); + } +} + +void Binder::validateNodeRelTableExist(const std::string& tableName) { + if (!catalog.getReadOnlyVersion()->containNodeTable(tableName) && + !catalog.getReadOnlyVersion()->containRelTable(tableName)) { + throw BinderException("Table " + tableName + " does not exist."); } } diff --git a/src/catalog/catalog_content.cpp b/src/catalog/catalog_content.cpp index ca573badbee..d3503a1d543 100644 --- a/src/catalog/catalog_content.cpp +++ b/src/catalog/catalog_content.cpp @@ -109,22 +109,6 @@ table_id_t CatalogContent::addRdfGraphSchema(const BoundCreateTableInfo& info) { return rdfGraphID; } -bool CatalogContent::containNodeTable(const std::string& tableName) const { - if (!tableNameToIDMap.contains(tableName)) { - return false; - } - auto tableID = getTableID(tableName); - return tableSchemas.at(tableID)->tableType == TableType::NODE; -} - -bool CatalogContent::containRelTable(const std::string& tableName) const { - if (!tableNameToIDMap.contains(tableName)) { - return false; - } - auto tableID = getTableID(tableName); - return tableSchemas.at(tableID)->tableType == TableType::REL; -} - Property* CatalogContent::getNodeProperty( table_id_t tableID, const std::string& propertyName) const { for (auto& property : tableSchemas.at(tableID)->properties) { @@ -268,7 +252,15 @@ void CatalogContent::registerBuiltInFunctions() { builtInTableFunctions = std::make_unique(); } -std::vector CatalogContent::getTableSchemas(common::TableType tableType) const { +bool CatalogContent::containTable(const std::string& tableName, TableType tableType) const { + if (!tableNameToIDMap.contains(tableName)) { + return false; + } + auto tableID = getTableID(tableName); + return tableSchemas.at(tableID)->tableType == tableType; +} + +std::vector CatalogContent::getTableSchemas(TableType tableType) const { std::vector result; for (auto& [id, schema] : tableSchemas) { if (schema->getTableType() == tableType) { diff --git a/src/include/binder/binder.h b/src/include/binder/binder.h index cd5a8d67cf2..9d75222c52f 100644 --- a/src/include/binder/binder.h +++ b/src/include/binder/binder.h @@ -77,7 +77,7 @@ class Binder { std::shared_ptr bindWhereExpression( const parser::ParsedExpression& parsedExpression); - common::table_id_t bindRelTableID(const std::string& tableName) const; + // catalog::TableSchema bindRelTableID(const std::string& tableName) const; common::table_id_t bindNodeTableID(const std::string& tableName) const; std::shared_ptr createVariable( @@ -105,8 +105,6 @@ class Binder { catalog::TableSchema* tableSchema, const std::string& propertyName); /*** bind copy from/to ***/ - static bool bindContainsSerial( - catalog::TableSchema* tableSchema, common::CopyDescription::FileType fileType); expression_vector bindColumnExpressions( catalog::TableSchema* tableSchema, common::CopyDescription::FileType fileType); std::unique_ptr bindCopyFromClause(const parser::Statement& statement); @@ -243,7 +241,9 @@ class Binder { // multiple statement. static void validateReadNotFollowUpdate(const NormalizedSingleQuery& singleQuery); - static void validateTableExist(const catalog::Catalog& _catalog, std::string& tableName); + void validateTableExist(const std::string& tableName); + // TODO(Xiyang): remove this validation once we refactor DDL. + void validateNodeRelTableExist(const std::string& tableName); static bool validateStringParsingOptionName(std::string& parsingOptionName); diff --git a/src/include/catalog/catalog_content.h b/src/include/catalog/catalog_content.h index 5d87539ba34..374a29af17c 100644 --- a/src/include/catalog/catalog_content.h +++ b/src/include/catalog/catalog_content.h @@ -29,9 +29,19 @@ class CatalogContent { registerBuiltInFunctions(); } + // TODO:remove potentailly inline bool containTable(const std::string& name) const { return tableNameToIDMap.contains(name); } + inline bool containNodeTable(const std::string& tableName) const { + return containTable(tableName, common::TableType::NODE); + } + inline bool containRelTable(const std::string& tableName) const { + return containTable(tableName, common::TableType::REL); + } + inline bool containRelGroup(const std::string& tableName) const { + return containTable(tableName, common::TableType::REL_GROUP); + } inline std::string getTableName(common::table_id_t tableID) const { assert(tableSchemas.contains(tableID)); return getTableSchema(tableID)->tableName; @@ -53,10 +63,6 @@ class CatalogContent { common::table_id_t addRelTableGroupSchema(const binder::BoundCreateTableInfo& info); common::table_id_t addRdfGraphSchema(const binder::BoundCreateTableInfo& info); - bool containNodeTable(const std::string& tableName) const; - - bool containRelTable(const std::string& tableName) const; - /** * Node and Rel property functions. */ @@ -113,6 +119,7 @@ class CatalogContent { void registerBuiltInFunctions(); + bool containTable(const std::string& tableName, common::TableType tableType) const; std::vector getTableSchemas(common::TableType tableType) const; std::vector getTableIDs(common::TableType tableType) const; diff --git a/test/test_files/ddl/ddl.test b/test/test_files/ddl/ddl.test index 3d182844bad..63f6e6cf2ff 100644 --- a/test/test_files/ddl/ddl.test +++ b/test/test_files/ddl/ddl.test @@ -8,12 +8,12 @@ ---- ok -STATEMENT MATCH ()-[e:studyAt]->() RETURN count(*) ---- error -Binder exception: Rel table studyAt does not exist. +Binder exception: Table studyAt does not exist. -STATEMENT DROP TABLE workAt ---- ok -STATEMENT MATCH ()-[e:workAt]->() RETURN count(*) ---- error -Binder exception: Rel table workAt does not exist. +Binder exception: Table workAt does not exist. -STATEMENT DROP TABLE organisation ---- ok -STATEMENT MATCH (n:organisation) RETURN count(*) diff --git a/test/test_files/exceptions/binder/binder_error.test b/test/test_files/exceptions/binder/binder_error.test index 39323b0e332..305bb233a2c 100644 --- a/test/test_files/exceptions/binder/binder_error.test +++ b/test/test_files/exceptions/binder/binder_error.test @@ -275,7 +275,7 @@ Binder exception: Unrecognized parsing csv option: PK. -LOG CopyCSVInvalidSchemaName -STATEMENT COPY university FROM "person_0_0.csv" (pk=",") ---- error -Binder exception: Node/Rel university does not exist. +Binder exception: Table university does not exist. -LOG CopyCSVInvalidEscapeChar -STATEMENT COPY person FROM "person_0_0.csv" (ESCAPE="..") @@ -305,7 +305,7 @@ Binder exception: PropertyName: _id is an internal reserved propertyName. -LOG DropNotExistsTable -STATEMENT DROP TABLE person1; ---- error -Binder exception: Node/Rel person1 does not exist. +Binder exception: Table person1 does not exist. -LOG InvalidLimitNumberType -STATEMENT MATCH (a:person) RETURN a.age LIMIT "abc" @@ -360,7 +360,7 @@ DISTINCT (SERIAL) -> SERIAL -LOG DropColumnFromNonExistedTable -STATEMENT alter table person1 drop k ---- error -Binder exception: Node/Rel person1 does not exist. +Binder exception: Table person1 does not exist. -LOG DropNonExistedColumn -STATEMENT alter table person drop random @@ -385,7 +385,7 @@ Binder exception: Expression 3.2 has data type STRING but expect INT64. Implicit -LOG RenameNonExistedTable -STATEMENT alter table person1 rename to person2 ---- error -Binder exception: Node/Rel person1 does not exist. +Binder exception: Table person1 does not exist. -LOG RenameTableDuplicateName -STATEMENT alter table person rename to organisation @@ -395,7 +395,7 @@ Binder exception: Table: organisation already exists. -LOG RenamePropertyOfNonExistedTable -STATEMENT alter table person1 rename col1 to col2 ---- error -Binder exception: Node/Rel person1 does not exist. +Binder exception: Table person1 does not exist. -LOG RenamePropertyDuplicateName -STATEMENT alter table person rename fName to gender diff --git a/test/test_files/tinysnb/rel_group/basic.test b/test/test_files/tinysnb/rel_group/basic.test index a01b737daed..1a8c5d7fe19 100644 --- a/test/test_files/tinysnb/rel_group/basic.test +++ b/test/test_files/tinysnb/rel_group/basic.test @@ -1,13 +1,39 @@ -GROUP RelGroupTest --DATASET CSV tinysnb +-DATASET CSV rel-group -- -CASE RelGroupTest1 --STATEMENT CREATE REL TABLE GROUP likes (FROM person TO person, FROM person TO like) ----- ok --STATEMENT MATCH (a:person) WHERE a.ID > 8 RETURN a.ID, a.gender,a.isStudent, a.isWorker, a.age, a.eyeSight +-STATEMENT MATCH (a)-[e:knows]->(b) WHERE a.ID = 0 AND b.ID = 2 RETURN label(e), label(b), b.* ---- 3 -10|2|False|True|83|4.900000 -80|||True|22|1.100000 -9|2|False|False|40|4.900000 +knows_personA_personB|personB|2|Bob +knows_personA_personC|personC|2|Bob +knows_personB_personC|personC|2|Bob +-STATEMENT MATCH (a:personA)-[e:knows]->(b) WHERE a.ID = 0 AND b.ID = 2 RETURN label(e), label(b), b.* +---- 2 +knows_personA_personB|personB|2|Bob +knows_personA_personC|personC|2|Bob +-STATEMENT MATCH (a:personA)-[e:knows]->(b:personB) WHERE a.ID = 0 AND b.ID = 2 RETURN label(e), label(b), b.* +---- 1 +knows_personA_personB|personB|2|Bob +-STATEMENT MATCH (a:personA)-[e:likes]->(b:personB) WHERE a.ID = 0 RETURN label(e), label(b), b.* +---- 3 +likes_personA_personB|personB|2|Bob +likes_personA_personB|personB|3|Carol +likes_personA_personB|personB|5|Dan +-STATEMENT MATCH (a)-[e:likes]-(b) WHERE a.ID = 0 RETURN COUNT(*) +---- 1 +12 +-STATEMENT MATCH (a)-[e]->(b) WHERE a.ID = 0 AND b.ID = 2 RETURN label(e), label(b), b.* +---- 5 +knows_personA_personB|personB|2|Bob +knows_personA_personC|personC|2|Bob +knows_personB_personC|personC|2|Bob +likes_personA_personB|personB|2|Bob +likes_personB_personA|personA|2|Bob +-STATEMENT COPY knows FROM "a.csv"; +---- error +Binder exception: Table knows does not exist. +-STATEMENT DROP TABLE knows; +---- error +Binder exception: Table knows does not exist.