1 /*
2    Copyright (c) 2014, 2021, Oracle and/or its affiliates.
3 
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License, version 2.0,
6    as published by the Free Software Foundation.
7 
8    This program is also distributed with certain software (including
9    but not limited to OpenSSL) that is licensed under separate terms,
10    as designated in a particular file or component or in included license
11    documentation.  The authors of MySQL hereby grant you an additional
12    permission to link the program and your derivative works with the
13    separately licensed software that they have included with MySQL.
14 
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License, version 2.0, for more details.
19 
20    You should have received a copy of the GNU General Public License
21    along with this program; if not, write to the Free Software Foundation,
22    51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
23 
24 // First include (the generated) my_config.h, to get correct platform defines.
25 #include "my_config.h"
26 #include <gtest/gtest.h>
27 
28 #include "opt_costmodel.h"
29 #include "opt_costconstantcache.h"
30 #include "fake_table.h"
31 #include "test_utils.h"
32 
33 
34 namespace costmodel_unittest {
35 
36 using my_testing::Server_initializer;
37 
38 class CostModelTest : public ::testing::Test
39 {
40 protected:
SetUp()41   virtual void SetUp()
42   {
43     // Add a storage engine to the hton2plugin array.
44     // This is needed for the cost model to add cost constants
45     // for the storage engine
46     LEX_STRING engine_name= {C_STRING_WITH_LEN("InnoDB")};
47 
48     hton2plugin[0]= new st_plugin_int();
49     hton2plugin[0]->name= engine_name;
50 
51     initializer.SetUp();
52   }
TearDown()53   virtual void TearDown()
54   {
55     initializer.TearDown();
56     delete hton2plugin[0];
57     hton2plugin[0]= NULL;
58   }
59 
thd()60   THD *thd() { return initializer.thd(); }
61 
62   Server_initializer initializer;
63 };
64 
65 /*
66   Tests for temporary tables that are not dependent on hard coded cost
67   constants.
68 */
test_tmptable_cost(const Cost_model_server * cm,Cost_model_server::enum_tmptable_type tmp_table_type)69 void test_tmptable_cost(const Cost_model_server *cm,
70                         Cost_model_server::enum_tmptable_type tmp_table_type)
71 {
72   const uint rows= 3;
73 
74   // Cost of inserting and reading data in a temporary table
75   EXPECT_EQ(cm->tmptable_readwrite_cost(tmp_table_type, rows, rows),
76             rows * cm->tmptable_readwrite_cost(tmp_table_type, 1.0, 1.0));
77 }
78 
79 
80 /*
81   Test the Cost_model_server interface.
82 */
TEST_F(CostModelTest,CostModelServer)83 TEST_F(CostModelTest, CostModelServer)
84 {
85   const uint rows= 3;
86 
87   // Create and initialize the server cost model
88   Cost_model_server cm;
89   cm.init();
90 
91   // Create and initialize a cost constant object that will be used
92   // for verifying default values for cost constants
93   const Server_cost_constants default_server_cost;
94 
95   // Test row evaluate cost
96   EXPECT_EQ(cm.row_evaluate_cost(1.0), default_server_cost.row_evaluate_cost());
97   EXPECT_EQ(cm.row_evaluate_cost(rows),
98             rows * cm.row_evaluate_cost(1.0));
99 
100   // Test key compare cost
101   EXPECT_EQ(cm.key_compare_cost(1.0), default_server_cost.key_compare_cost());
102   EXPECT_EQ(cm.key_compare_cost(rows), rows * cm.key_compare_cost(1.0));
103 
104   // Cost of creating a tempoary table without inserting data into it
105   EXPECT_EQ(cm.tmptable_create_cost(Cost_model_server::MEMORY_TMPTABLE),
106             default_server_cost.memory_temptable_create_cost());
107   EXPECT_EQ(cm.tmptable_create_cost(Cost_model_server::DISK_TMPTABLE),
108             default_server_cost.disk_temptable_create_cost());
109 
110   // Cost of inserting one row in a temporary table
111   EXPECT_EQ(cm.tmptable_readwrite_cost(Cost_model_server::MEMORY_TMPTABLE,
112                                        1.0, 0.0),
113             default_server_cost.memory_temptable_row_cost());
114   EXPECT_EQ(cm.tmptable_readwrite_cost(Cost_model_server::DISK_TMPTABLE,
115                                        1.0, 0.0),
116             default_server_cost.disk_temptable_row_cost());
117 
118   // Cost of reading one row in a temporary table
119   EXPECT_EQ(cm.tmptable_readwrite_cost(Cost_model_server::MEMORY_TMPTABLE,
120                                        0.0, 1.0),
121             default_server_cost.memory_temptable_row_cost());
122   EXPECT_EQ(cm.tmptable_readwrite_cost(Cost_model_server::DISK_TMPTABLE,
123                                        0.0, 1.0),
124             default_server_cost.disk_temptable_row_cost());
125 
126   // Tests for temporary tables that are independent of cost constants
127   test_tmptable_cost(&cm, Cost_model_server::MEMORY_TMPTABLE);
128   test_tmptable_cost(&cm, Cost_model_server::DISK_TMPTABLE);
129 }
130 
131 
132 /*
133   Test the Cost_model_table interface.
134 */
TEST_F(CostModelTest,CostModelTable)135 TEST_F(CostModelTest, CostModelTable)
136 {
137   const uint rows= 3;
138   const double blocks= 4.0;
139   const uint key= 0;
140 
141   // A table is needed in order to initialize the table cost model
142   Fake_TABLE table(1, false);
143 
144   // Create and initialize a cost model table object
145   Cost_model_server cost_model_server;
146   cost_model_server.init();
147   Cost_model_table cm;
148   cm.init(&cost_model_server, &table);
149 
150   // Create and initialize a cost constant object that will be used
151   // for verifying default values for cost constants
152   const Server_cost_constants default_server_cost;
153   const SE_cost_constants default_engine_cost;
154 
155   // Test row evaluate cost
156   EXPECT_EQ(cm.row_evaluate_cost(1.0), default_server_cost.row_evaluate_cost());
157   EXPECT_EQ(cm.row_evaluate_cost(rows),
158             rows * cm.row_evaluate_cost(1.0));
159 
160   // Test key compare cost
161   EXPECT_EQ(cm.key_compare_cost(1.0), default_server_cost.key_compare_cost());
162   EXPECT_EQ(cm.key_compare_cost(rows),
163             rows * cm.key_compare_cost(1.0));
164 
165   // Test io block read cost
166   EXPECT_EQ(cm.io_block_read_cost(1.0),
167             default_engine_cost.io_block_read_cost());
168   EXPECT_EQ(cm.io_block_read_cost(blocks),
169             blocks * cm.io_block_read_cost(1.0));
170 
171   // Test page_read_cost() with table in memory buffer
172   EXPECT_EQ(cm.page_read_cost(1.0),
173             default_engine_cost.memory_block_read_cost());
174   EXPECT_EQ(cm.page_read_cost(blocks),
175             blocks * cm.page_read_cost(1.0));
176 
177   // Test page_read_cost() with table data on disk
178   table.file->stats.table_in_mem_estimate= 0.0; // Table is on disk
179   EXPECT_EQ(cm.page_read_cost(1.0),
180             default_engine_cost.io_block_read_cost());
181   EXPECT_EQ(cm.page_read_cost(blocks),
182             blocks * cm.page_read_cost(1.0));
183 
184   // Test page_read_cost_index() with index data in memory
185   table.key_info[key].set_in_memory_estimate(1.0); // Index is in memory
186   EXPECT_EQ(cm.page_read_cost_index(key, 1.0),
187             default_engine_cost.memory_block_read_cost());
188   EXPECT_EQ(cm.page_read_cost_index(key, blocks),
189             blocks * cm.page_read_cost_index(key, 1.0));
190 
191   // Test page_read_oost_index() with index data on disk
192   table.key_info[key].set_in_memory_estimate(0.0); // Index is on disk
193   EXPECT_EQ(cm.page_read_cost_index(key, 1.0),
194             default_engine_cost.io_block_read_cost());
195   EXPECT_EQ(cm.page_read_cost_index(key, blocks),
196             blocks * cm.page_read_cost_index(key, 1.0));
197 
198   // Test disk seek base cost
199   EXPECT_EQ(cm.disk_seek_base_cost(),
200             DISK_SEEK_BASE_COST * cm.io_block_read_cost(1.0));
201 
202   // Test disk seek cost
203   EXPECT_GT(cm.disk_seek_cost(2.0), cm.disk_seek_cost(1.0));
204 }
205 
206 }
207