/* * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2.0, * as published by the Free Software Foundation. * * This program is also distributed with certain software (including * but not limited to OpenSSL) that is licensed under separate terms, * as designated in a particular file or component or in included license * documentation. The authors of MySQL hereby grant you an additional * permission to link the program and your derivative works with the * separately licensed software that they have included with MySQL. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License, version 2.0, for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include "plugin/x/src/find_statement_builder.h" #include "unittest/gunit/xplugin/xpl/mysqlx_pb_wrapper.h" namespace xpl { namespace test { class Find_statement_builder_stub : public Find_statement_builder { public: explicit Find_statement_builder_stub(Expression_generator *gen) : Find_statement_builder(*gen) {} using Find_statement_builder::add_document_projection; using Find_statement_builder::add_grouping; using Find_statement_builder::add_grouping_criteria; using Find_statement_builder::add_row_locking; using Find_statement_builder::add_table_projection; }; class Find_statement_builder_test : public ::testing::Test { public: Find_statement_builder_test() : args(*msg.mutable_args()), expr_gen(nullptr) {} std::unique_ptr builder( Expression_generator::Prep_stmt_placeholder_list *ids = nullptr) { expr_gen.reset(new Expression_generator(&query, args, schema, is_table_data_model(msg))); if (ids) expr_gen->set_prep_stmt_placeholder_list(ids); std::unique_ptr stub( new Find_statement_builder_stub(expr_gen.get())); return stub; } Find_statement_builder::Find msg; Expression_generator::Arg_list &args; Query_string_builder query; std::string schema; std::unique_ptr expr_gen; Expression_generator::Prep_stmt_placeholder_list placeholders; enum { k_document = 0, k_table = 1 }; }; TEST_F(Find_statement_builder_test, add_projection_table_empty) { ASSERT_NO_THROW(builder()->add_table_projection(Projection_list())); EXPECT_EQ("*", query.get()); } TEST_F(Find_statement_builder_test, add_document_projection_empty) { ASSERT_NO_THROW(builder()->add_document_projection(Projection_list())); EXPECT_EQ("doc", query.get()); } TEST_F(Find_statement_builder_test, add_document_projection_wildcards) { ASSERT_THROW(builder()->add_document_projection(Projection_list{ {Operator("*", Column_identifier("", "xtable"))}}), ngs::Error_code); } TEST_F(Find_statement_builder_test, add_document_projection_wildcards_mix) { ASSERT_THROW(builder()->add_document_projection(Projection_list{ {Column_identifier("xfield", "xtable")}, {Operator("*", Column_identifier("", "xtable"))}}), ngs::Error_code); } TEST_F(Find_statement_builder_test, add_projection_table_one_member_item) { ASSERT_NO_THROW(builder()->add_table_projection( Projection_list{{Column_identifier(Document_path{"alpha"})}})); EXPECT_EQ("JSON_EXTRACT(doc,'$.alpha')", query.get()); } TEST_F(Find_statement_builder_test, add_projection_table_one_item) { ASSERT_NO_THROW(builder()->add_table_projection( Projection_list{{Column_identifier("alpha")}})); EXPECT_EQ("`alpha`", query.get()); } TEST_F(Find_statement_builder_test, add_projection_table_two_items) { ASSERT_NO_THROW(builder()->add_table_projection(Projection_list{ {Column_identifier("alpha")}, {Column_identifier("beta")}})); EXPECT_EQ("`alpha`,`beta`", query.get()); } TEST_F(Find_statement_builder_test, add_projection_table_two_items_placeholder) { args = Expression_list{2.2}; ASSERT_NO_THROW(builder()->add_table_projection( Projection_list{{Column_identifier("alpha")}, {Placeholder(0)}})); EXPECT_EQ("`alpha`,2.2", query.get()); } TEST_F(Find_statement_builder_test, add_projection_table_two_items_placeholder_no_args) { ASSERT_THROW(builder()->add_table_projection(Projection_list{ {Column_identifier("alpha")}, {Placeholder(0)}}), Expression_generator::Error); } TEST_F(Find_statement_builder_test, add_projection_table_two_items_placeholder_no_args_get_placeholder) { ASSERT_NO_THROW(builder(&placeholders) ->add_table_projection(Projection_list{ {Column_identifier("alpha")}, {Placeholder(0)}})); EXPECT_EQ("`alpha`,?", query.get()); EXPECT_EQ(Expression_generator::Prep_stmt_placeholder_list{0}, placeholders); } TEST_F(Find_statement_builder_test, add_projection_table_one_item_with_alias) { ASSERT_NO_THROW(builder()->add_table_projection( Projection_list{{Column_identifier("alpha"), "beta"}})); EXPECT_EQ("`alpha` AS `beta`", query.get()); } TEST_F(Find_statement_builder_test, add_projection_document_one_item_no_alias) { ASSERT_THROW(builder()->add_document_projection( Projection_list{{Column_identifier("alpha")}}), ngs::Error_code); } TEST_F(Find_statement_builder_test, add_projection_document_one_item) { ASSERT_NO_THROW(builder()->add_document_projection( Projection_list{{Column_identifier("alpha", "xtable"), "beta"}})); EXPECT_EQ("JSON_OBJECT('beta', `xtable`.`alpha`) AS doc", query.get()); } TEST_F(Find_statement_builder_test, add_projection_document_one_member_item) { ASSERT_NO_THROW(builder()->add_document_projection( Projection_list{{Column_identifier(Document_path{"alpha"}), "beta"}})); EXPECT_EQ("JSON_OBJECT('beta', JSON_EXTRACT(doc,'$.alpha')) AS doc", query.get()); } TEST_F(Find_statement_builder_test, add_projection_document_two_member_items) { ASSERT_NO_THROW(builder()->add_document_projection( Projection_list{{Column_identifier(Document_path{"alpha"}), "beta"}, {Column_identifier(Document_path{"first"}), "second"}})); EXPECT_EQ( "JSON_OBJECT('beta', JSON_EXTRACT(doc,'$.alpha')," "'second', JSON_EXTRACT(doc,'$.first')) AS doc", query.get()); } TEST_F(Find_statement_builder_test, add_projection_document_two_member_items_placeholder) { args = Expression_list{2.2}; ASSERT_NO_THROW(builder()->add_document_projection( Projection_list{{Column_identifier(Document_path{"alpha"}), "beta"}, {Placeholder(0), "second"}})); EXPECT_EQ( "JSON_OBJECT('beta', JSON_EXTRACT(doc,'$.alpha')," "'second', 2.2) AS doc", query.get()); } TEST_F(Find_statement_builder_test, add_gruping_empty) { ASSERT_NO_THROW(builder()->add_grouping(Grouping_list{})); EXPECT_EQ("", query.get()); } TEST_F(Find_statement_builder_test, add_gruping_one_item) { ASSERT_NO_THROW( builder()->add_grouping(Grouping_list{Column_identifier("alpha")})); EXPECT_EQ(" GROUP BY `alpha`", query.get()); } TEST_F(Find_statement_builder_test, add_gruping_two_items) { ASSERT_NO_THROW(builder()->add_grouping(Grouping_list{ {Column_identifier("alpha")}, {Column_identifier("beta")}})); EXPECT_EQ(" GROUP BY `alpha`,`beta`", query.get()); } TEST_F(Find_statement_builder_test, add_gruping_two_items_placeholder) { args = Expression_list{2}; ASSERT_NO_THROW(builder()->add_grouping( Grouping_list{{Column_identifier("alpha")}, {Placeholder(0)}})); EXPECT_EQ(" GROUP BY `alpha`,2", query.get()); } TEST_F(Find_statement_builder_test, add_gruping_criteria) { ASSERT_NO_THROW(builder()->add_grouping_criteria(Grouping_criteria( Operator(">", Column_identifier("alpha"), Scalar(1.0))))); EXPECT_STREQ(" HAVING (`alpha` > 1)", query.get().c_str()); } TEST_F(Find_statement_builder_test, add_gruping_criteria_placeholder) { args = Expression_list{2.3}; ASSERT_NO_THROW(builder()->add_grouping_criteria(Grouping_criteria( Operator(">", Column_identifier("alpha"), Placeholder(0))))); EXPECT_EQ(" HAVING (`alpha` > 2.3)", query.get()); } TEST_F(Find_statement_builder_test, add_row_lock_shared) { msg.set_locking(Mysqlx::Crud::Find::SHARED_LOCK); ASSERT_NO_THROW(builder()->add_row_locking(msg)); EXPECT_EQ(" FOR SHARE", query.get()); } TEST_F(Find_statement_builder_test, add_row_lock_shared_with_grouping) { msg.set_locking(Mysqlx::Crud::Find::SHARED_LOCK); ASSERT_NO_THROW( builder()->add_grouping(Grouping_list{Column_identifier("alpha")})); ASSERT_NO_THROW(builder()->add_row_locking(msg)); EXPECT_EQ(" GROUP BY `alpha` FOR SHARE", query.get()); } TEST_F(Find_statement_builder_test, add_row_lock_shared_nowait) { msg.set_locking(Mysqlx::Crud::Find::SHARED_LOCK); msg.set_locking_options(Mysqlx::Crud::Find::NOWAIT); ASSERT_NO_THROW(builder()->add_row_locking(msg)); EXPECT_EQ(" FOR SHARE NOWAIT", query.get()); } TEST_F(Find_statement_builder_test, add_row_lock_shared_skip_locked) { msg.set_locking(Mysqlx::Crud::Find::SHARED_LOCK); msg.set_locking_options(Mysqlx::Crud::Find::SKIP_LOCKED); ASSERT_NO_THROW(builder()->add_row_locking(msg)); EXPECT_EQ(" FOR SHARE SKIP LOCKED", query.get()); } TEST_F(Find_statement_builder_test, add_row_lock_exclusive) { msg.set_locking(Mysqlx::Crud::Find::EXCLUSIVE_LOCK); ASSERT_NO_THROW(builder()->add_row_locking(msg)); EXPECT_EQ(" FOR UPDATE", query.get()); } TEST_F(Find_statement_builder_test, add_row_lock_exclusive_with_grouping) { msg.set_locking(Mysqlx::Crud::Find::EXCLUSIVE_LOCK); ASSERT_NO_THROW( builder()->add_grouping(Grouping_list{Column_identifier("alpha")})); ASSERT_NO_THROW(builder()->add_row_locking(msg)); EXPECT_EQ(" GROUP BY `alpha` FOR UPDATE", query.get()); } TEST_F(Find_statement_builder_test, add_row_lock_exclusive_nowait) { msg.set_locking(Mysqlx::Crud::Find::EXCLUSIVE_LOCK); msg.set_locking_options(Mysqlx::Crud::Find::NOWAIT); ASSERT_NO_THROW(builder()->add_row_locking(msg)); EXPECT_EQ(" FOR UPDATE NOWAIT", query.get()); } TEST_F(Find_statement_builder_test, add_row_lock_exclusive_skip_locked) { msg.set_locking(Mysqlx::Crud::Find::EXCLUSIVE_LOCK); msg.set_locking_options(Mysqlx::Crud::Find::SKIP_LOCKED); ASSERT_NO_THROW(builder()->add_row_locking(msg)); EXPECT_EQ(" FOR UPDATE SKIP LOCKED", query.get()); } TEST_F(Find_statement_builder_test, skip_locked_set_but_no_locking) { msg.set_locking_options(Mysqlx::Crud::Find::SKIP_LOCKED); ASSERT_THROW(builder()->add_row_locking(msg), ngs::Error_code); } TEST_F(Find_statement_builder_test, nowait_set_but_no_locking) { msg.set_locking_options(Mysqlx::Crud::Find::NOWAIT); ASSERT_THROW(builder()->add_row_locking(msg), ngs::Error_code); } TEST_F(Find_statement_builder_test, build_table) { msg = Find({"xtable", "xschema"}, Mysqlx::Crud::TABLE) .projection({Column_identifier("alpha"), "zeta"}) .criteria(Operator(">", Column_identifier{"delta"}, Scalar{1.0})) .order({Column_identifier{"gamma"}, Order::Base::DESC}) .grouping(Column_identifier{"beta"}) .grouping_criteria( Operator("<", Column_identifier{"lambda"}, Scalar{2.0})); ASSERT_NO_THROW(builder()->build(msg)); EXPECT_EQ( "SELECT `alpha` AS `zeta` " "FROM `xschema`.`xtable` " "WHERE (`delta` > 1) " "GROUP BY `beta` " "HAVING (`lambda` < 2) " "ORDER BY `gamma` DESC", query.get()); } TEST_F(Find_statement_builder_test, build_document_no_grouping) { msg = Find({"xtable", "xschema"}, Mysqlx::Crud::DOCUMENT) .projection({Column_identifier{Document_path{"alpha"}}, "zeta"}) .criteria(Operator(">", Column_identifier{Document_path{"delta"}}, Scalar{1.0})) .order( {Column_identifier{Document_path{"gamma"}}, Order::Base::DESC}); ASSERT_NO_THROW(builder()->build(msg)); EXPECT_STREQ( "SELECT JSON_OBJECT('zeta', JSON_EXTRACT(doc,'$.alpha')) AS doc " "FROM `xschema`.`xtable` " "WHERE (JSON_EXTRACT(doc,'$.delta') > 1) " "ORDER BY JSON_EXTRACT(doc,'$.gamma') DESC", query.get().c_str()); } TEST_F(Find_statement_builder_test, build_document_with_grouping_and_criteria) { msg = Find({"xtable", "xschema"}, Mysqlx::Crud::DOCUMENT) .projection({Column_identifier{Document_path{"alpha"}}, "zeta"}) .criteria(Operator(">", Column_identifier{Document_path{"delta"}}, Scalar{1.0})) .order({Column_identifier(Document_path{"beta"}), Order::Base::DESC}) .grouping(Column_identifier{Document_path{"alpha"}}) .grouping_criteria(Operator( "<", Column_identifier{Document_path{"lambda"}}, Scalar{2.0})); ASSERT_NO_THROW(builder()->build(msg)); EXPECT_STREQ( "SELECT JSON_OBJECT('zeta', `_DERIVED_TABLE_`.`zeta`) AS doc FROM (" "SELECT JSON_EXTRACT(doc,'$.alpha') AS `zeta` " "FROM `xschema`.`xtable` " "WHERE (JSON_EXTRACT(doc,'$.delta') > 1) " "GROUP BY JSON_EXTRACT(doc,'$.alpha') " "HAVING (JSON_EXTRACT(doc,'$.lambda') < 2) " "ORDER BY JSON_EXTRACT(doc,'$.beta') DESC" ") AS `_DERIVED_TABLE_`", query.get().c_str()); } TEST_F(Find_statement_builder_test, build_document_with_grouping) { msg = Find({"xtable", "xschema"}, Mysqlx::Crud::DOCUMENT) .projection({{Column_identifier{Document_path{"alpha"}}, "zeta"}, {Column_identifier(Document_path{"gama"}), "ksi"}}) .grouping({{Column_identifier(Document_path{"alpha"})}, {Column_identifier(Document_path{"gama"})}}); ASSERT_NO_THROW(builder()->build(msg)); EXPECT_STREQ( "SELECT JSON_OBJECT('zeta', `_DERIVED_TABLE_`.`zeta`,'ksi', " "`_DERIVED_TABLE_`.`ksi`) AS doc FROM (" "SELECT JSON_EXTRACT(doc,'$.alpha') AS `zeta`,JSON_EXTRACT(doc,'$.gama') " "AS `ksi` " "FROM `xschema`.`xtable` " "GROUP BY JSON_EXTRACT(doc,'$.alpha'),JSON_EXTRACT(doc,'$.gama')" ") AS `_DERIVED_TABLE_`", query.get().c_str()); } TEST_F(Find_statement_builder_test, build_document_with_grouping_no_projection) { msg = Find({"xtable", "xschema"}, Mysqlx::Crud::DOCUMENT) .grouping(Column_identifier(Document_path{"beta"})); EXPECT_THROW(builder()->build(msg), ngs::Error_code); } TEST_F(Find_statement_builder_test, build_document_shared_lock) { msg = Find({"xtable", "xschema"}, Mysqlx::Crud::DOCUMENT) .locking(Find::Base::SHARED_LOCK) .projection({Column_identifier{Document_path{"alpha"}}, "zeta"}) .criteria(Operator(">", Column_identifier{Document_path{"delta"}}, Scalar{1.0})) .order( {Column_identifier{Document_path{"gamma"}}, Order::Base::DESC}); ASSERT_NO_THROW(builder()->build(msg)); EXPECT_STREQ( "SELECT JSON_OBJECT('zeta', JSON_EXTRACT(doc,'$.alpha')) AS doc " "FROM `xschema`.`xtable` " "WHERE (JSON_EXTRACT(doc,'$.delta') > 1) " "ORDER BY JSON_EXTRACT(doc,'$.gamma') DESC " "FOR SHARE", query.get().c_str()); } TEST_F(Find_statement_builder_test, build_document_exclusive_lock) { msg = Find({"xtable", "xschema"}, Mysqlx::Crud::DOCUMENT) .locking(Find::Base::EXCLUSIVE_LOCK) .projection({Column_identifier(Document_path{"alpha"}), "zeta"}) .criteria(Operator(">", Column_identifier{Document_path{"delta"}}, Scalar{1.0})) .order( {Column_identifier{Document_path{"gamma"}}, Order::Base::DESC}); ASSERT_NO_THROW(builder()->build(msg)); EXPECT_STREQ( "SELECT JSON_OBJECT('zeta', JSON_EXTRACT(doc,'$.alpha')) AS doc " "FROM `xschema`.`xtable` " "WHERE (JSON_EXTRACT(doc,'$.delta') > 1) " "ORDER BY JSON_EXTRACT(doc,'$.gamma') DESC " "FOR UPDATE", query.get().c_str()); } } // namespace test } // namespace xpl