001: /**
002: * Sequoia: Database clustering technology.
003: * Copyright (C) 2002-2004 French National Institute For Research In Computer
004: * Science And Control (INRIA).
005: * Copyright (C) 2005 AmicoSoft, Inc. dba Emic Networks
006: * Contact: sequoia@continuent.org
007: *
008: * Licensed under the Apache License, Version 2.0 (the "License");
009: * you may not use this file except in compliance with the License.
010: * You may obtain a copy of the License at
011: *
012: * http://www.apache.org/licenses/LICENSE-2.0
013: *
014: * Unless required by applicable law or agreed to in writing, software
015: * distributed under the License is distributed on an "AS IS" BASIS,
016: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: * See the License for the specific language governing permissions and
018: * limitations under the License.
019: *
020: * Initial developer(s): Emmanuel Cecchet.
021: * Contributor(s): Nicolas Modrzyk.
022: */package org.continuent.sequoia.controller.cache.result;
023:
024: import java.util.ArrayList;
025: import java.util.Collection;
026: import java.util.Iterator;
027:
028: import org.continuent.sequoia.common.sql.schema.TableColumn;
029: import org.continuent.sequoia.controller.backend.result.ControllerResultSet;
030: import org.continuent.sequoia.controller.cache.result.entries.AbstractResultCacheEntry;
031: import org.continuent.sequoia.controller.cache.result.schema.CacheDatabaseColumn;
032: import org.continuent.sequoia.controller.cache.result.schema.CacheDatabaseTable;
033: import org.continuent.sequoia.controller.requests.AbstractWriteRequest;
034: import org.continuent.sequoia.controller.requests.DeleteRequest;
035: import org.continuent.sequoia.controller.requests.ParsingGranularities;
036: import org.continuent.sequoia.controller.requests.RequestType;
037: import org.continuent.sequoia.controller.requests.SelectRequest;
038: import org.continuent.sequoia.controller.requests.UpdateRequest;
039:
040: /**
041: * This is a query cache implementation with a column unique granularity:
042: * <ul>
043: * <li><code>COLUMN_UNIQUE</code>: same as <code>COLUMN</code> except that
044: * <code>UNIQUE</code> queries that selects a single row based on a key are
045: * invalidated only when needed.
046: * </ul>
047: *
048: * @author <a href="mailto:Emmanuel.Cecchet@inria.fr">Emmanuel Cecchet </a>
049: * @author <a href="mailto:Nicolas.Modrzyk@inrialpes.fr">Nicolas Modrzyk </a>
050: * @version 1.0
051: */
052: public class ResultCacheColumnUnique extends ResultCache {
053:
054: /**
055: * Builds a new ResultCache with a column unique granularity.
056: *
057: * @param maxEntries maximum number of entries
058: * @param pendingTimeout pending timeout for concurrent queries
059: */
060: public ResultCacheColumnUnique(int maxEntries, int pendingTimeout) {
061: super (maxEntries, pendingTimeout);
062: parsingGranularity = ParsingGranularities.COLUMN_UNIQUE;
063: }
064:
065: /**
066: * @see org.continuent.sequoia.controller.cache.result.ResultCache#processAddToCache(AbstractResultCacheEntry)
067: */
068: public void processAddToCache(AbstractResultCacheEntry qe) {
069: SelectRequest request = qe.getRequest();
070: ArrayList selectedColumns = request.getSelect();
071: // Update the tables columns dependencies
072: Collection from = request.getFrom();
073: if (from == null)
074: return;
075: if (selectedColumns == null || selectedColumns.isEmpty()) {
076: logger
077: .warn("No parsing of select clause found - Fallback to table granularity");
078: for (Iterator i = from.iterator(); i.hasNext();) {
079: CacheDatabaseTable table = cdbs.getTable((String) i
080: .next());
081: table.addCacheEntry(qe);
082: // Add all columns, entries will be added below.
083: ArrayList columns = table.getColumns();
084: for (int j = 0; j < columns.size(); j++) {
085: ((CacheDatabaseColumn) columns.get(j))
086: .addCacheEntry(qe);
087: }
088: return;
089: }
090: }
091: for (Iterator i = request.getSelect().iterator(); i.hasNext();) {
092: TableColumn tc = (TableColumn) i.next();
093: cdbs.getTable(tc.getTableName()).getColumn(
094: tc.getColumnName()).addCacheEntry(qe);
095: }
096: if (request.getWhere() != null) { // Add all columns dependencies
097: for (Iterator i = request.getWhere().iterator(); i
098: .hasNext();) {
099: TableColumn tc = (TableColumn) i.next();
100: cdbs.getTable(tc.getTableName()).getColumn(
101: tc.getColumnName()).addCacheEntry(qe);
102: }
103: if (request.getCacheAbility() == RequestType.UNIQUE_CACHEABLE) { // Add a specific entry for this pk
104: String tableName = (String) from.iterator().next();
105: AbstractResultCacheEntry entry = cdbs.getTable(
106: tableName).getPkResultCacheEntry(
107: request.getPkValue());
108: if (entry != null) {
109: if (entry.isValid()) { // Do not add an entry which has a lower selection than the current
110: // one
111: if (entry.getRequest().getSelect().size() >= request
112: .getSelect().size())
113: return;
114: }
115: }
116: cdbs.getTable(tableName).addPkCacheEntry(
117: request.getPkValue(), qe);
118: }
119: }
120: }
121:
122: /**
123: * @see org.continuent.sequoia.controller.cache.result.AbstractResultCache#isUpdateNecessary(org.continuent.sequoia.controller.requests.UpdateRequest)
124: */
125: public boolean isUpdateNecessary(UpdateRequest request) {
126: if (request.getCacheAbility() != RequestType.UNIQUE_CACHEABLE)
127: return true;
128: CacheDatabaseTable cacheTable = cdbs.getTable(request
129: .getTableName());
130: if (request.getColumns() == null)
131: return true;
132: String pk = request.getPk();
133: AbstractResultCacheEntry qce = cacheTable
134: .getPkResultCacheEntry(pk);
135: if (qce != null) {
136: if (!qce.isValid())
137: return true;
138: ControllerResultSet rs = qce.getResult();
139: if (rs == null)
140: return true;
141: else
142: return needInvalidate(rs, request)[1];
143: } else
144: return true;
145: }
146:
147: /**
148: * @see org.continuent.sequoia.controller.cache.result.ResultCache#processWriteNotify(org.continuent.sequoia.controller.requests.AbstractWriteRequest)
149: */
150: protected void processWriteNotify(AbstractWriteRequest request) {
151: // Sanity check
152: CacheDatabaseTable cacheTable = cdbs.getTable(request
153: .getTableName());
154: if (request.getColumns() == null) {
155: logger
156: .warn("No column parsing found - Fallback to table granularity ("
157: + request.getUniqueKey() + ")");
158: cacheTable.invalidateAll();
159: return;
160: }
161: if (request.isInsert()) {
162: for (Iterator i = request.getColumns().iterator(); i
163: .hasNext();) {
164: TableColumn tc = (TableColumn) i.next();
165: cdbs.getTable(tc.getTableName()).getColumn(
166: tc.getColumnName()).invalidateAllNonUnique();
167: }
168: } else {
169: if (request.getCacheAbility() == RequestType.UNIQUE_CACHEABLE) {
170: if (request.isUpdate()) {
171: String pk = ((UpdateRequest) request).getPk();
172: AbstractResultCacheEntry qce = cacheTable
173: .getPkResultCacheEntry(pk);
174: if (qce != null) {
175: boolean[] invalidate = needInvalidate(qce
176: .getResult(), (UpdateRequest) request);
177: if (invalidate[0]) { // We must invalidate this entry
178: cacheTable.removePkResultCacheEntry(pk);
179: return;
180: } else {
181: if (logger.isDebugEnabled())
182: logger
183: .debug("No invalidate needed for request:"
184: + request
185: .getSqlShortForm(20));
186: return; // We don't need to invalidate
187: }
188: }
189: } else if (request.isDelete()) { // Invalidate the corresponding cache entry
190: cacheTable
191: .removePkResultCacheEntry(((DeleteRequest) request)
192: .getPk());
193: return;
194: }
195: }
196: // At this point this is a non unique write query or a request
197: // we didn't handle properly (unknown request for example)
198: for (Iterator i = request.getColumns().iterator(); i
199: .hasNext();) {
200: TableColumn tc = (TableColumn) i.next();
201: CacheDatabaseTable table = cdbs.getTable(tc
202: .getTableName());
203: table.invalidateAll(); // Pk are associated to tables
204: if (!request.isAlter())
205: table.getColumn(tc.getColumnName()).invalidateAll();
206: }
207: }
208: }
209:
210: /**
211: * @see org.continuent.sequoia.controller.cache.result.ResultCache#getName()
212: */
213: public String getName() {
214: return "columnUnique";
215: }
216:
217: }
|