--- /dev/null
+/*\r
+\r
+ Derby - Class org.apache.derby.impl.store.access.btree.BTreeCostController\r
+\r
+ Licensed to the Apache Software Foundation (ASF) under one or more\r
+ contributor license agreements. See the NOTICE file distributed with\r
+ this work for additional information regarding copyright ownership.\r
+ The ASF licenses this file to you under the Apache License, Version 2.0\r
+ (the "License"); you may not use this file except in compliance with\r
+ the License. You may obtain a copy of the License at\r
+\r
+ http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+ Unless required by applicable law or agreed to in writing, software\r
+ distributed under the License is distributed on an "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ See the License for the specific language governing permissions and\r
+ limitations under the License.\r
+\r
+ */\r
+\r
+package org.apache.derby.impl.store.access.btree;\r
+\r
+import org.apache.derby.iapi.reference.Property;\r
+import org.apache.derby.iapi.reference.SQLState;\r
+\r
+import org.apache.derby.iapi.services.sanity.SanityManager;\r
+\r
+import org.apache.derby.iapi.error.StandardException; \r
+\r
+import org.apache.derby.iapi.store.access.conglomerate.Conglomerate;\r
+import org.apache.derby.iapi.store.access.conglomerate.LogicalUndo;\r
+import org.apache.derby.iapi.store.access.conglomerate.TransactionManager;\r
+\r
+import org.apache.derby.iapi.store.access.DynamicCompiledOpenConglomInfo;\r
+import org.apache.derby.iapi.store.access.ScanController;\r
+import org.apache.derby.iapi.store.access.StoreCostController;\r
+import org.apache.derby.iapi.store.access.StoreCostResult;\r
+\r
+import org.apache.derby.iapi.store.raw.ContainerHandle;\r
+import org.apache.derby.iapi.store.raw.Transaction;\r
+\r
+import org.apache.derby.iapi.types.DataValueDescriptor;\r
+\r
+import org.apache.derby.iapi.types.RowLocation;\r
+\r
+import org.apache.derby.iapi.services.io.FormatableBitSet;\r
+import java.util.Properties;\r
+\r
+/**\r
+\r
+The StoreCostController interface provides methods that an access client\r
+(most likely the system optimizer) can use to get store's estimated cost of\r
+various operations on the conglomerate the StoreCostController was opened\r
+for.\r
+<p>\r
+It is likely that the implementation of StoreCostController will open \r
+the conglomerate and will leave the conglomerate open until the\r
+StoreCostController is closed. This represents a significant amount of\r
+work, so the caller if possible should attempt to open the StoreCostController\r
+once per unit of work and rather than close and reopen the controller. For\r
+instance if the optimizer needs to cost 2 different scans against a single\r
+conglomerate, it should use one instance of the StoreCostController.\r
+<p>\r
+The locking behavior of the implementation of a StoreCostController is\r
+undefined, it may or may not get locks on the underlying conglomerate. It\r
+may or may not hold locks until end of transaction. \r
+An optimal implementation will not get any locks on the underlying \r
+conglomerate, thus allowing concurrent access to the table by a executing\r
+query while another query is optimizing.\r
+<p>\r
+@see org.apache.derby.iapi.store.access.TransactionController#openStoreCost\r
+\r
+**/\r
+\r
+public class BTreeCostController extends OpenBTree \r
+ implements StoreCostController\r
+{\r
+\r
+ // 1.5 numbers on mikem old machine:\r
+ //\r
+ // The magic numbers are based on the following benchmark results:\r
+ //\r
+ // no col one int col all cols\r
+ // ------ ----------- --------\r
+ //100 byte heap fetch by row loc, cached 0.3625 0.5098 0.6629\r
+ //100 byte heap fetch by row loc, uncached 1.3605769 1.5168269 1.5769231\r
+ //4 byte heap fetch by row loc, cached 0.3745 0.4016 0.3766\r
+ //4 byte heap fetch by row loc, uncached 4.1938777 3.5714285 4.4897957\r
+ //\r
+ // no col one int col all cols\r
+ // ------ ----------- --------\r
+ //Int col one level btree\r
+ // fetch by exact key, cached 0.781 1.012 0.42\r
+ // fetch by exact key, sort merge 1.081 1.221 0.851\r
+ // fetch by exact key, uncached 0.0 0.0 0.0\r
+ //Int col two level btree\r
+ // fetch by exact key, cached 1.062 1.342 0.871\r
+ // fetch by exact key, sort merge 1.893 2.273 1.633\r
+ // fetch by exact key, uncached 5.7238097 5.3428574 4.7714286\r
+ //String key one level btree\r
+ // fetch by exact key, cached 1.082 0.811 0.781\r
+ // fetch by exact key, sort merge 1.572 1.683 1.141\r
+ // fetch by exact key, uncached 0.0 0.0 0.0\r
+ //String key two level btree\r
+ // fetch by exact key, cached 2.143 2.664 1.953\r
+ // fetch by exact key, sort merge 3.775 4.116 3.505\r
+ // fetch by exact key, uncached 4.639474 5.0052633 4.4289474\r
+\r
+ // mikem new machine - insane, codeline, non-jit 1.1.7 numbers\r
+ //\r
+ // no col one int col all cols\r
+ // ------ ----------- --------\r
+ //100 byte heap fetch by row loc, cached 0.1662 0.4597 0.5618\r
+ //100 byte heap fetch by row loc, uncached 0.7565947 1.2601918 1.6690648\r
+ //4 byte heap fetch by row loc, cached 0.1702 0.1983 0.1903\r
+ //4 byte heap fetch by row loc, uncached 1.5068493 1.3013699 1.6438357\r
+ //\r
+ // no col one int col all cols\r
+ // ------ ----------- --------\r
+ // Int col one level btree\r
+ // fetch by exact key, cached 0.271 0.511 0.33\r
+ // fetch by exact key, sort merge 0.691 0.921 0.771\r
+ // fetch by exact key, uncached 0.0 0.0 0.0\r
+ // Int col two level btree\r
+ // fetch by exact key, cached 0.541 0.711 0.561\r
+ // fetch by exact key, sort merge 1.432 1.682 1.533\r
+ // fetch by exact key, uncached 3.142857 3.6285715 3.2380953\r
+ // String key one level btree\r
+ // fetch by exact key, cached 0.611 0.851 0.701\r
+ // fetch by exact key, sort merge 1.051 1.272 1.122\r
+ // fetch by exact key, uncached 0.0 0.0 0.0\r
+ // String key two level btree\r
+ // fetch by exact key, cached 1.532 1.843 1.622\r
+ // fetch by exact key, sort merge 2.844 3.155 2.984\r
+ // fetch by exact key, uncached 3.4 3.636842 3.531579\r
+ // \r
+\r
+\r
+ // The following costs are search costs to find a row on a leaf, use\r
+ // the heap costs to determine scan costs, for now ignore qualifier \r
+ // application and stop comparisons.\r
+ // I used the int key, 2 level numbers divided by 2 to get per level.\r
+ \r
+ private static final double \r
+ BTREE_CACHED_FETCH_BY_KEY_PER_LEVEL = (0.541 / 2);\r
+\r
+ private static final double \r
+ BTREE_SORTMERGE_FETCH_BY_KEY_PER_LEVEL = (1.432 / 2);\r
+\r
+ private static final double \r
+ BTREE_UNCACHED_FETCH_BY_KEY_PER_LEVEL = (3.143 / 2);\r
+\r
+ // saved values passed to init().\r
+ TransactionManager init_xact_manager;\r
+ Transaction init_rawtran;\r
+ Conglomerate init_conglomerate;\r
+\r
+ /**\r
+ * Only lookup these estimates from raw store once.\r
+ **/\r
+ long num_pages;\r
+ long num_rows;\r
+ long page_size;\r
+ int tree_height;\r
+\r
+ /* Constructors for This class: */\r
+\r
+ public BTreeCostController()\r
+ {\r
+ }\r
+\r
+ /* Private/Protected methods of This class: */\r
+\r
+ /**\r
+ * Initialize the cost controller.\r
+ * <p>\r
+ * Save initialize parameters away, and open the underlying container.\r
+ * <p>\r
+ *\r
+ * @param xact_manager access manager transaction.\r
+ * @param rawtran Raw store transaction.\r
+ *\r
+ * @exception StandardException Standard exception policy.\r
+ **/\r
+ public void init(\r
+ TransactionManager xact_manager,\r
+ BTree conglomerate,\r
+ Transaction rawtran)\r
+ throws StandardException\r
+ {\r
+ super.init(\r
+ xact_manager, \r
+ xact_manager, \r
+ (ContainerHandle) null, // open the btree.\r
+ rawtran, \r
+ false,\r
+ ContainerHandle.MODE_READONLY,\r
+ TransactionManager.MODE_NONE,\r
+ (BTreeLockingPolicy) null, // RESOLVE (mikem) - this means\r
+ // no locks during costing - will\r
+ // that work?????\r
+ conglomerate, \r
+ (LogicalUndo) null, // read only, so no undo necessary\r
+ (DynamicCompiledOpenConglomInfo) null);\r
+\r
+ // look up costs from raw store. For btrees these numbers are out\r
+ // of whack as they want to be leaf specific numbers but they include\r
+ // every page branch and leafs.\r
+ num_pages = this.container.getEstimatedPageCount(/* unused flag */ 0);\r
+\r
+ // subtract one row for every page to account for internal control row\r
+ // which exists on every page.\r
+ num_rows = \r
+ this.container.getEstimatedRowCount(/*unused flag*/ 0) - num_pages;\r
+\r
+ Properties prop = new Properties();\r
+ prop.put(Property.PAGE_SIZE_PARAMETER, "");\r
+ this.container.getContainerProperties(prop);\r
+ page_size = \r
+ Integer.parseInt(prop.getProperty(Property.PAGE_SIZE_PARAMETER));\r
+\r
+ tree_height = getHeight();\r
+\r
+ return;\r
+ }\r
+\r
+ /* Public Methods of This class: */\r
+\r
+ /**\r
+ * Close the controller.\r
+ * <p>\r
+ * Close the open controller. This method always succeeds, and never \r
+ * throws any exceptions. Callers must not use the StoreCostController \r
+ * Cost controller after closing it; they are strongly advised to clear\r
+ * out the scan controller reference after closing.\r
+ * <p>\r
+ **/\r
+ public void close()\r
+ throws StandardException\r
+ {\r
+ super.close();\r
+ }\r
+\r
+ /**\r
+ * Return the cost of calling ConglomerateController.fetch().\r
+ * <p>\r
+ * Return the estimated cost of calling ConglomerateController.fetch()\r
+ * on the current conglomerate. This gives the cost of finding a record\r
+ * in the conglomerate given the exact RowLocation of the record in\r
+ * question. \r
+ * <p>\r
+ * The validColumns parameter describes what kind of row \r
+ * is being fetched, ie. it may be cheaper to fetch a partial row than a \r
+ * complete row.\r
+ * <p>\r
+ *\r
+ *\r
+ * @param validColumns A description of which columns to return from\r
+ * row on the page into "templateRow." templateRow,\r
+ * and validColumns work together to\r
+ * describe the row to be returned by the fetch - \r
+ * see RowUtil for description of how these three \r
+ * parameters work together to describe a fetched \r
+ * "row".\r
+ *\r
+ * @param access_type Describe the type of access the query will be\r
+ * performing to the ConglomerateController. \r
+ *\r
+ * STORECOST_CLUSTERED - The location of one fetch\r
+ * is likely clustered "close" to the next \r
+ * fetch. For instance if the query plan were\r
+ * to sort the RowLocations of a heap and then\r
+ * use those RowLocations sequentially to \r
+ * probe into the heap, then this flag should\r
+ * be specified. If this flag is not set then\r
+ * access to the table is assumed to be\r
+ * random - ie. the type of access one gets \r
+ * if you scan an index and probe each row\r
+ * in turn into the base table is "random".\r
+ *\r
+ *\r
+ * @return The cost of the fetch.\r
+ *\r
+ * @exception StandardException Standard exception policy.\r
+ *\r
+ * @see org.apache.derby.iapi.store.access.RowUtil\r
+ **/\r
+ public double getFetchFromRowLocationCost(\r
+ FormatableBitSet validColumns,\r
+ int access_type)\r
+ throws StandardException\r
+ {\r
+ throw StandardException.newException(\r
+ SQLState.BTREE_UNIMPLEMENTED_FEATURE);\r
+ }\r
+\r
+ /**\r
+ * Return the cost of exact key lookup.\r
+ * <p>\r
+ * Return the estimated cost of calling ScanController.fetch()\r
+ * on the current conglomerate, with start and stop positions set such\r
+ * that an exact match is expected.\r
+ * <p>\r
+ * This call returns the cost of a fetchNext() performed on a scan which\r
+ * has been positioned with a start position which specifies exact match\r
+ * on all keys in the row.\r
+ * <p>\r
+ * Example:\r
+ * <p>\r
+ * In the case of a btree this call can be used to determine the cost of\r
+ * doing an exact probe into btree, giving all key columns. This cost\r
+ * can be used if the client knows it will be doing an exact key probe\r
+ * but does not have the key's at optimize time to use to make a call to\r
+ * getScanCost()\r
+ * <p>\r
+ *\r
+ *\r
+ * @param validColumns A description of which columns to return from\r
+ * row on the page into "templateRow." templateRow,\r
+ * and validColumns work together to\r
+ * describe the row to be returned by the fetch - \r
+ * see RowUtil for description of how these three \r
+ * parameters work together to describe a fetched \r
+ * "row".\r
+ *\r
+ * @param access_type Describe the type of access the query will be\r
+ * performing to the ScanController. \r
+ *\r
+ * STORECOST_CLUSTERED - The location of one scan\r
+ * is likely clustered "close" to the previous \r
+ * scan. For instance if the query plan were\r
+ * to used repeated "reopenScan()'s" to probe\r
+ * for the next key in an index, then this flag\r
+ * should be be specified. If this flag is not \r
+ * set then each scan will be costed independant\r
+ * of any other predicted scan access.\r
+ *\r
+ * @return The cost of the fetch.\r
+ *\r
+ * @exception StandardException Standard exception policy.\r
+ *\r
+ * @see org.apache.derby.iapi.store.access.RowUtil\r
+ **/\r
+ public double getFetchFromFullKeyCost(\r
+ FormatableBitSet validColumns,\r
+ int access_type)\r
+ throws StandardException\r
+ {\r
+ double ret_cost;\r
+\r
+ if ((access_type & StoreCostController.STORECOST_CLUSTERED) == 0)\r
+ {\r
+ // uncached fetch\r
+ ret_cost = BTREE_UNCACHED_FETCH_BY_KEY_PER_LEVEL;\r
+ }\r
+ else\r
+ {\r
+ ret_cost = BTREE_SORTMERGE_FETCH_BY_KEY_PER_LEVEL;\r
+ }\r
+ ret_cost *= tree_height;\r
+\r
+ return(ret_cost);\r
+ }\r
+\r
+\r
+ /**\r
+ * Calculate the cost of a scan.\r
+ * <p>\r
+ * Cause this object to calculate the cost of performing the described\r
+ * scan. The interface is setup such that first a call is made to\r
+ * calcualteScanCost(), and then subsequent calls to accessor routines\r
+ * are made to get various pieces of information about the cost of\r
+ * the scan.\r
+ * <p>\r
+ * For the purposes of costing this routine is going to assume that \r
+ * a page will remain in cache between the time one next()/fetchNext()\r
+ * call and a subsequent next()/fetchNext() call is made within a scan.\r
+ * <p>\r
+ * The result of costing the scan is placed in the "cost_result". \r
+ * The cost of the scan is stored by calling \r
+ * cost_result.setEstimatedCost(cost).\r
+ * The estimated row count is stored by calling \r
+ * cost_result.setEstimatedRowCount(row_count).\r
+ * <p>\r
+ * The estimated cost of the scan assumes the caller will \r
+ * execute a fetchNext() loop for every row that qualifies between\r
+ * start and stop position. Note that this cost is different than\r
+ * execution a next(),fetch() loop; or if the scan is going to be\r
+ * terminated by client prior to reaching the stop condition.\r
+ * <p>\r
+ * The estimated number of rows returned from the scan \r
+ * assumes the caller will execute a fetchNext() loop for every \r
+ * row that qualifies between start and stop position.\r
+ * <p>\r
+ *\r
+ *\r
+ * @param scan_type The type of scan that will be executed. There\r
+ * are currently 2 types:\r
+ * STORECOST_SCAN_NORMAL - scans will be executed\r
+ * using the standard next/fetch, where each fetch\r
+ * can retrieve 1 or many rows (if fetchNextGroup()\r
+ * interface is used).\r
+ *\r
+ * STORECOST_SCAN_SET - The entire result set will\r
+ * be retrieved using the the fetchSet() interface.\r
+ *\r
+ * @param row_count Estimated total row count of the table. The \r
+ * current system tracks row counts in heaps better\r
+ * than btree's (btree's have "rows" which are not\r
+ * user rows - branch rows, control rows), so \r
+ * if available the client should\r
+ * pass in the base table's row count into this\r
+ * routine to be used as the index's row count.\r
+ * If the caller has no idea, pass in -1.\r
+ *\r
+ * @param group_size The number of rows to be returned by a single\r
+ * fetch call for STORECOST_SCAN_NORMAL scans.\r
+ *\r
+ * @param forUpdate Should be true if the caller intends to update \r
+ * through the scan.\r
+ * \r
+ * @param scanColumnList A description of which columns to return from \r
+ * every fetch in the scan. template, \r
+ * and scanColumnList work together\r
+ * to describe the row to be returned by the scan - \r
+ * see RowUtil for description of how these three \r
+ * parameters work together to describe a "row".\r
+ * \r
+ * @param template A prototypical row which the scan may use to\r
+ * maintain its position in the conglomerate. Not \r
+ * all access method scan types will require this, \r
+ * if they don't it's ok to pass in null.\r
+ * In order to scan a conglomerate one must \r
+ * allocate 2 separate "row" templates. The "row" \r
+ * template passed into openScan is for the private\r
+ * use of the scan itself, and no access to it\r
+ * should be made by the caller while the scan is \r
+ * still open. Because of this the scanner must \r
+ * allocate another "row" template to hold the \r
+ * values returned from fetch(). Note that this \r
+ * template must be for the full row, whether a \r
+ * partial row scan is being executed or not.\r
+ *\r
+ * @param startKeyValue An indexable row which holds a (partial) key \r
+ * value which, in combination with the \r
+ * startSearchOperator, defines the starting \r
+ * position of the scan. If null, the starting\r
+ * position of the scan is the first row of the \r
+ * conglomerate. The startKeyValue must only\r
+ * reference columns included in the scanColumnList.\r
+ *\r
+ * @param startSearchOperator \r
+ * an operator which defines how the startKeyValue\r
+ * is to be searched for. If startSearchOperation \r
+ * is ScanController.GE, the scan starts on the \r
+ * first row which is greater than or equal to the \r
+ * startKeyValue. If startSearchOperation is \r
+ * ScanController.GT, the scan starts on the first\r
+ * row whose key is greater than startKeyValue. The\r
+ * startSearchOperation parameter is ignored if the\r
+ * startKeyValue parameter is null.\r
+ *\r
+ * @param stopKeyValue An indexable row which holds a (partial) key \r
+ * value which, in combination with the \r
+ * stopSearchOperator, defines the ending position\r
+ * of the scan. If null, the ending position of the\r
+ * scan is the last row of the conglomerate. The\r
+ * stopKeyValue must only reference columns included\r
+ * in the scanColumnList.\r
+ *\r
+ * @param stopSearchOperator\r
+ * an operator which defines how the stopKeyValue\r
+ * is used to determine the scan stopping position. \r
+ * If stopSearchOperation is ScanController.GE, the\r
+ * scan stops just before the first row which is\r
+ * greater than or equal to the stopKeyValue. If \r
+ * stopSearchOperation is ScanController.GT, the \r
+ * scan stops just before the first row whose key \r
+ * is greater than startKeyValue. The\r
+ * stopSearchOperation parameter is ignored if the\r
+ * stopKeyValue parameter is null.\r
+ *\r
+ * \r
+ * @param access_type Describe the type of access the query will be\r
+ * performing to the ScanController. \r
+ *\r
+ * STORECOST_CLUSTERED - The location of one scan\r
+ * is likely clustered "close" to the previous \r
+ * scan. For instance if the query plan were\r
+ * to used repeated "reopenScan()'s" to probe\r
+ * for the next key in an index, then this flag\r
+ * should be be specified. If this flag is not \r
+ * set then each scan will be costed independant\r
+ * of any other predicted scan access.\r
+ *\r
+ *\r
+ * @exception StandardException Standard exception policy.\r
+ *\r
+ * @see org.apache.derby.iapi.store.access.RowUtil\r
+ **/\r
+ public void getScanCost(\r
+ int scan_type,\r
+ long row_count,\r
+ int group_size,\r
+ boolean forUpdate,\r
+ FormatableBitSet scanColumnList,\r
+ DataValueDescriptor[] template,\r
+ DataValueDescriptor[] startKeyValue,\r
+ int startSearchOperator,\r
+ DataValueDescriptor[] stopKeyValue,\r
+ int stopSearchOperator,\r
+ boolean reopen_scan,\r
+ int access_type,\r
+ StoreCostResult cost_result)\r
+ throws StandardException\r
+ {\r
+ float left_of_start;\r
+ float left_of_stop;\r
+ ControlRow control_row = null;\r
+ long input_row_count = (row_count < 0 ? num_rows : row_count);\r
+\r
+ try\r
+ {\r
+ // Find the starting page and row slot.\r
+ if (startKeyValue == null)\r
+ {\r
+ left_of_start = 0;\r
+ }\r
+ else\r
+ {\r
+ // Search for the starting row.\r
+\r
+ SearchParameters sp = new SearchParameters(\r
+ startKeyValue, \r
+ ((startSearchOperator == ScanController.GE) ? \r
+ SearchParameters.POSITION_LEFT_OF_PARTIAL_KEY_MATCH : \r
+ SearchParameters.POSITION_RIGHT_OF_PARTIAL_KEY_MATCH),\r
+ template, this, true);\r
+\r
+ control_row =\r
+ ControlRow.get(this, BTree.ROOTPAGEID).search(sp);\r
+\r
+ control_row.release();\r
+ control_row = null;\r
+\r
+ left_of_start = sp.left_fraction;\r
+ }\r
+\r
+ if (stopKeyValue == null)\r
+ {\r
+ left_of_stop = 1;\r
+ }\r
+ else\r
+ {\r
+ // Search for the stopping row.\r
+\r
+ SearchParameters sp = \r
+ new SearchParameters(\r
+ stopKeyValue, \r
+ ((stopSearchOperator == ScanController.GE) ? \r
+ SearchParameters.POSITION_LEFT_OF_PARTIAL_KEY_MATCH : \r
+ SearchParameters.POSITION_RIGHT_OF_PARTIAL_KEY_MATCH),\r
+ template, this, true);\r
+\r
+ control_row =\r
+ ControlRow.get(this, BTree.ROOTPAGEID).search(sp);\r
+\r
+ control_row.release();\r
+ control_row = null;\r
+\r
+ left_of_stop = sp.left_fraction;\r
+ }\r
+\r
+ // System.out.println(\r
+ // "\n\tleft_of_start = " + left_of_start +\r
+ // "\n\tleft_of_stop = " + left_of_stop);\r
+\r
+ // what percentage of rows are between start and stop?\r
+\r
+ float ret_fraction = left_of_stop - left_of_start;\r
+\r
+ // If for some reason the stop position comes before the start\r
+ // position, assume 0 rows will return from query.\r
+ if (ret_fraction < 0)\r
+ ret_fraction = 0;\r
+\r
+ // Never return estimate of more rows than exist, sometimes \r
+ // the recursive estimation through the btree may return a number\r
+ // like 1.00001.\r
+ if (ret_fraction > 1)\r
+ ret_fraction = 1;\r
+\r
+ float estimated_row_count = input_row_count * ret_fraction;\r
+\r
+ // first the base cost of positioning on the first row in the scan.\r
+ double cost = \r
+ getFetchFromFullKeyCost(scanColumnList, access_type);\r
+\r
+ // add the base cost of bringing each page for the first time into\r
+ // the cache. This is basically the cost of bringing each leaf\r
+ // uncached into the cache and reading the control row off of it.:\r
+ cost += \r
+ (num_pages * ret_fraction) * BASE_UNCACHED_ROW_FETCH_COST;\r
+\r
+ // Now some magic to try and figure out the cost of doing a\r
+ // scan along the leaf level of the tree. Mostly just assume\r
+ // the costs are the same as the heap, and ignore qualifier\r
+ // processing and stop row comparisons for now.\r
+\r
+ // the base cost of getting each of the rows from a page assumed\r
+ // to already be cached (by the scan fetch) - this is only for all\r
+ // rows after the initial row on the page has been accounted for\r
+ // under the BASE_UNCACHED_ROW_FETCH_COST cost.:\r
+ long cached_row_count = ((long) estimated_row_count) - num_pages;\r
+ if (cached_row_count < 0)\r
+ cached_row_count = 0;\r
+\r
+ if (scan_type == StoreCostController.STORECOST_SCAN_NORMAL)\r
+ cost += cached_row_count * BASE_GROUPSCAN_ROW_COST;\r
+ else\r
+ cost += cached_row_count * BASE_HASHSCAN_ROW_FETCH_COST;\r
+\r
+ // finally add the cost associated with the number of bytes in row:\r
+ long row_size = \r
+ (input_row_count == 0) ? \r
+ 4 : (num_pages * page_size) / input_row_count;\r
+\r
+ cost += \r
+ (estimated_row_count * row_size) * BASE_ROW_PER_BYTECOST;\r
+\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ if (cost < 0)\r
+ SanityManager.THROWASSERT("cost " + cost);\r
+\r
+ if (estimated_row_count < 0)\r
+ SanityManager.THROWASSERT(\r
+ "estimated_row_count = " + estimated_row_count);\r
+ }\r
+\r
+ // return the cost\r
+ cost_result.setEstimatedCost(cost);\r
+\r
+ // RESOLVE - should we make sure this number is > 0?\r
+ cost_result.setEstimatedRowCount(Math.round(estimated_row_count));\r
+ }\r
+ finally\r
+ {\r
+ if (control_row != null)\r
+ control_row.release();\r
+ }\r
+\r
+ // System.out.println("BTreeCostController.getScanCost():" + \r
+ // "\n\t cost = " + cost_result.getEstimatedCost() +\r
+ // "\n\t rows = " + cost_result.getEstimatedRowCount());\r
+\r
+ return;\r
+ }\r
+\r
+ /**\r
+ * Return an "empty" row location object of the correct type.\r
+ * <p>\r
+ *\r
+ * @return The empty Rowlocation.\r
+ *\r
+ * @exception StandardException Standard exception policy.\r
+ **/\r
+ public RowLocation newRowLocationTemplate()\r
+ throws StandardException\r
+ {\r
+ throw StandardException.newException(\r
+ SQLState.BTREE_UNIMPLEMENTED_FEATURE);\r
+ }\r
+}\r