--- /dev/null
+/*\r
+\r
+ Derby - Class org.apache.derby.impl.sql.execute.UpdateStatisticsConstantAction\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.sql.execute;\r
+\r
+import org.apache.derby.iapi.sql.dictionary.TableDescriptor;\r
+import org.apache.derby.iapi.sql.dictionary.DataDictionary;\r
+import org.apache.derby.iapi.sql.dictionary.StatisticsDescriptor;\r
+import org.apache.derby.iapi.sql.Activation;\r
+import org.apache.derby.iapi.error.StandardException;\r
+\r
+import org.apache.derby.iapi.sql.execute.ExecIndexRow;\r
+import org.apache.derby.iapi.store.access.TransactionController;\r
+import org.apache.derby.iapi.types.DataValueDescriptor;\r
+import org.apache.derby.iapi.store.access.GroupFetchScanController;\r
+import org.apache.derby.iapi.store.access.ConglomerateController;\r
+import org.apache.derby.catalog.UUID;\r
+import org.apache.derby.catalog.types.StatisticsImpl;\r
+import org.apache.derby.iapi.sql.depend.DependencyManager;\r
+import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;\r
+\r
+/**\r
+ * This class describes actions that are performed for an \r
+ * UPDATE STATISTICS Statement at execution time.\r
+ */\r
+\r
+class UpdateStatisticsConstantAction extends DDLConstantAction\r
+{\r
+ private UUID tableUUID;\r
+ private UUID[] objectUUID;\r
+ private String objectName;\r
+ private boolean forTable;\r
+ private long[] conglomerateNumber;\r
+ private ExecIndexRow[] indexRow;\r
+\r
+ /* RUNTIME state of the system is maintained in these objects.\r
+ * rowBufferOne simply reuses the index row prepared by\r
+ * makeConstantAction. rowBufferTwo is a clone (an extra copy) of\r
+ * objects. rowBufferCurrent just switches between rowBufferOne and\r
+ * rowBufferTwo. \r
+ */\r
+ private DataValueDescriptor[][] rowBufferArray;\r
+ private DataValueDescriptor[] rowBuffer;\r
+ private DataValueDescriptor[] lastUniqueKey;\r
+\r
+ private static final int GROUP_FETCH_SIZE = 16;\r
+\r
+ public UpdateStatisticsConstantAction() {};\r
+\r
+ public UpdateStatisticsConstantAction(boolean forTable,\r
+ String objectName,\r
+ UUID tableUUID,\r
+ UUID[] objectUUID,\r
+ long[] conglomerateNumber,\r
+ ExecIndexRow[] indexRow)\r
+ {\r
+ \r
+ this.forTable = forTable;\r
+ this.objectName = objectName;\r
+ this.tableUUID = tableUUID;\r
+ this.objectUUID = objectUUID;\r
+ this.conglomerateNumber = conglomerateNumber;\r
+ this.indexRow = indexRow;\r
+ }\r
+\r
+ public String toString()\r
+ {\r
+ return "UPDATE STATISTICS FOR " + (forTable ? "TABLE" : "INDEX") + " " +\r
+ objectName; \r
+ \r
+ }\r
+\r
+ public void executeConstantAction(Activation activation) \r
+ throws StandardException\r
+ \r
+ {\r
+ GroupFetchScanController gsc = null;\r
+ TransactionController tc = activation.getTransactionController();\r
+ LanguageConnectionContext lcc = activation.getLanguageConnectionContext();\r
+ DataDictionary dd = lcc.getDataDictionary();\r
+ DependencyManager dm = dd.getDependencyManager();\r
+\r
+ \r
+ dd.startWriting(lcc);\r
+\r
+ TableDescriptor td = dd.getTableDescriptor(tableUUID);\r
+ dm.invalidateFor(td, DependencyManager.UPDATE_STATISTICS, lcc);\r
+\r
+ for (int indexNumber = 0; indexNumber < conglomerateNumber.length;\r
+ indexNumber++) \r
+ {\r
+ if (conglomerateNumber[indexNumber] == -1)\r
+ continue;\r
+\r
+ int numCols = indexRow[indexNumber].nColumns() - 1;;\r
+ long[] cardinality = new long[numCols];\r
+ long numRows = 0;\r
+ initializeRowBuffers(indexRow[indexNumber]);\r
+\r
+ try\r
+ {\r
+ /* Read uncommited, with record locking. Actually CS store may\r
+ not hold record locks */\r
+ gsc = \r
+ tc.openGroupFetchScan(\r
+ conglomerateNumber[indexNumber], \r
+ false, // hold\r
+ 0, // openMode: for read\r
+ TransactionController.MODE_RECORD, // locking\r
+ TransactionController.ISOLATION_READ_UNCOMMITTED, //isolation level\r
+ null, // scancolumnlist-- want everything.\r
+ null, // startkeyvalue-- start from the beginning.\r
+ 0,\r
+ null, // qualifiers, none!\r
+ null, // stopkeyvalue,\r
+ 0);\r
+ \r
+ boolean firstRow = true;\r
+ int rowsFetched = 0;\r
+ while ((rowsFetched = gsc.fetchNextGroup(rowBufferArray, null)) > 0)\r
+ {\r
+ for (int i = 0; i < rowsFetched; i++)\r
+ {\r
+ int whichPositionChanged = compareWithPrevKey(i, firstRow);\r
+ firstRow = false;\r
+ if (whichPositionChanged >= 0)\r
+ {\r
+ for (int j = whichPositionChanged; j < cardinality.length; j++)\r
+ cardinality[j]++;\r
+ }\r
+ numRows++;\r
+ }\r
+\r
+ DataValueDescriptor[] tmp;\r
+ tmp = rowBufferArray[GROUP_FETCH_SIZE - 1];\r
+ rowBufferArray[GROUP_FETCH_SIZE - 1] = lastUniqueKey;\r
+ lastUniqueKey = tmp;\r
+ } // while\r
+ } // try\r
+ finally \r
+ {\r
+ if (gsc != null)\r
+ {\r
+ gsc.close();\r
+ gsc = null;\r
+ }\r
+ }\r
+\r
+ if (numRows == 0)\r
+ {\r
+ /* if there is no data in the table: no need to write anything\r
+ * to sys.systatstics.\r
+ */\r
+ break; \r
+ } \r
+\r
+ StatisticsDescriptor statDesc;\r
+ \r
+ dd.dropStatisticsDescriptors(tableUUID, objectUUID[indexNumber],\r
+ tc); \r
+\r
+ for (int i = 0; i < indexRow[indexNumber].nColumns() - 1; i++)\r
+ {\r
+ statDesc = new StatisticsDescriptor(dd, dd.getUUIDFactory().createUUID(),\r
+ objectUUID[indexNumber],\r
+ tableUUID,\r
+ "I",\r
+ new StatisticsImpl(numRows,\r
+ cardinality[i]),\r
+ i + 1);\r
+ dd.addDescriptor(statDesc, null,\r
+ DataDictionary.SYSSTATISTICS_CATALOG_NUM,\r
+ true, tc);\r
+ } // for each leading column (c1) (c1,c2)....\r
+\r
+ } // for each index.\r
+ }\r
+\r
+\r
+ private void initializeRowBuffers(ExecIndexRow ir)\r
+ {\r
+\r
+ rowBufferArray = new DataValueDescriptor[GROUP_FETCH_SIZE][];\r
+ lastUniqueKey = ir.getRowArrayClone();\r
+ rowBufferArray[0] = ir.getRowArray(); // 1 gets old objects.\r
+ }\r
+\r
+ private int compareWithPrevKey(int index, boolean firstRow)\r
+ throws StandardException\r
+ {\r
+ if (firstRow)\r
+ return 0;\r
+\r
+ DataValueDescriptor[] prev = (index == 0) ? lastUniqueKey : rowBufferArray[index - 1];\r
+ DataValueDescriptor[] curr = rowBufferArray[index];\r
+ // no point trying to do rowlocation; hence - 1\r
+ for (int i = 0; i < (prev.length - 1); i++)\r
+ {\r
+ DataValueDescriptor dvd = (DataValueDescriptor)prev[i];\r
+\r
+ if (dvd.isNull())\r
+ return i; // nulls are counted as unique values.\r
+\r
+ if (prev[i].compare(curr[i]) != 0)\r
+ {\r
+ return i;\r
+ }\r
+ }\r
+\r
+ return -1;\r
+ }\r
+\r
+ \r
+\r
+}\r