DeveelDB  20151217
complete SQL database system, primarly developed for .NET/Mono frameworks
LocalRowCache.cs
Go to the documentation of this file.
1 //
2 // Copyright 2010-2015 Deveel
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16 
17 using System;
18 using System.Collections.Generic;
19 
20 using Deveel.Data.Caching;
21 using Deveel.Data.Protocol;
22 using Deveel.Data.Sql.Objects;
23 
24 namespace Deveel.Data.Client {
32  sealed class LocalRowCache {
33  private readonly DeveelDbConnection connection;
34 
38  private readonly Cache rowCache;
39 
40  public LocalRowCache(DeveelDbConnection connection) {
41  this.connection = connection;
42  rowCache = new SizeLimitedCache(connection.Settings.RowCacheSize);
43  }
44 
59  public QueryResultPart GetResultPart(int resultId, int rowIndex, int rowCount, int colCount, int totalRowCount) {
60  lock (this) {
61  // What was requested....
62  int origRowIndex = rowIndex;
63  int origRowCount = rowCount;
64 
65  var cachedRows = new List<CachedRow>();
66 
67  // The top row that isn't found in the cache.
68  bool foundNotcached = false;
69  // Look for the top row in the block that hasn't been cached
70  for (int r = 0; r < rowCount && !foundNotcached; ++r) {
71  int daRow = rowIndex + r;
72  // Is the row in the cache?
73  var rowRef = new RowRef(resultId, daRow);
74  // Not in cache so mark this as top row not in cache...
75  object rowObj;
76  if (!rowCache.TryGet(rowRef, out rowObj)) {
77  rowIndex = daRow;
78  if (rowIndex + rowCount > totalRowCount) {
79  rowCount = totalRowCount - rowIndex;
80  }
81  foundNotcached = true;
82  } else {
83  var row = (CachedRow) rowObj;
84  cachedRows.Add(row);
85  }
86  }
87 
88  var notCachedRows = new List<CachedRow>();
89  if (foundNotcached) {
90 
91  // Now work up from the bottom and find row that isn't in cache....
92  foundNotcached = false;
93  // Look for the bottom row in the block that hasn't been cached
94  for (int r = rowCount - 1; r >= 0 && !foundNotcached; --r) {
95  int daRow = rowIndex + r;
96  // Is the row in the cache?
97  var rowRef = new RowRef(resultId, daRow);
98  // Not in cache so mark this as top row not in cache...
99  object rowObj;
100  if (!rowCache.TryGet(rowRef, out rowObj)) {
101  if (rowIndex == origRowIndex) {
102  rowIndex = rowIndex - (rowCount - (r + 1));
103  if (rowIndex < 0) {
104  rowCount = rowCount + rowIndex;
105  rowIndex = 0;
106  }
107  } else {
108  rowCount = r + 1;
109  }
110  foundNotcached = true;
111  } else {
112  var row = (CachedRow) rowObj;
113  notCachedRows.Insert(0, row);
114  }
115  }
116  }
117 
118  // Some of it not in the cache...
119  if (foundNotcached) {
120  // Request a part of a result from the server (blocks)
121  QueryResultPart block = connection.RequestResultPart(resultId, rowIndex, rowCount);
122 
123  for (int r = 0; r < rowCount; ++r) {
124  var rowData = new ISqlObject[block.ColumnCount];
125  var dataSizes = new int[block.ColumnCount];
126 
127  int theRow = (rowIndex + r);
128  int colSize = 0;
129  var row = block.GetRow(r);
130  for (int c = 0; c < colCount; ++c) {
131  var ob = row.Values[c];
132  rowData[c] = ob;
133  dataSizes[c] = row.ValueSizes[c];
134  colSize += row.ValueSizes[c];
135  }
136 
137  var cachedRow = new CachedRow {
138  ResultId = resultId,
139  Row = theRow,
140  RowData = rowData,
141  Sizes = dataSizes
142  };
143 
144  // Don't cache if it's over a certain size,
145  if (colSize <= 3200) {
146  rowCache.Set(new RowRef(resultId, theRow), cachedRow);
147  }
148 
149  cachedRows.Add(cachedRow);
150  }
151  }
152 
153  // At this point, the cached rows should be completely in the cache so
154  // retrieve it from the cache.
155  var resultPart = new QueryResultPart(colCount);
156  int low = origRowIndex;
157  int high = origRowIndex + origRowCount;
158 
159  foreach (CachedRow row in cachedRows) {
160  if (row.ResultId != resultId)
161  continue;
162 
163  // Put into the result block
164  if (row.Row >= low && row.Row < high) {
165  var rowArray = new ISqlObject[colCount];
166  var rowSizes = new int[colCount];
167  for (int c = 0; c < colCount; ++c) {
168  rowArray[c] = row.RowData[c];
169  rowSizes[c] = row.Sizes[c];
170  }
171 
172  resultPart.AddRow(new QueryResultRow(rowArray, rowSizes));
173  }
174  }
175 
176  foreach (CachedRow row in notCachedRows) {
177  if (row.ResultId != resultId)
178  continue;
179 
180  // Put into the result block
181  if (row.Row >= low && row.Row < high) {
182  var rowArray = new ISqlObject[colCount];
183  var sizes = new int[colCount];
184  for (int c = 0; c < colCount; ++c) {
185  rowArray[c] = row.RowData[c];
186  sizes[c] = row.Sizes[c];
187  }
188 
189  resultPart.AddRow(new QueryResultRow(rowArray, sizes));
190  }
191  }
192 
193  // And return the result (phew!)
194  return resultPart;
195  }
196  }
197 
201  public void Clear() {
202  lock (this) {
203  rowCache.Clear();
204  }
205  }
206 
207  // ---------- Inner classes ----------
208 
212  private sealed class RowRef {
213  private readonly int tableId;
214  private readonly int row;
215 
216  internal RowRef(int tableId, int row) {
217  this.tableId = tableId;
218  this.row = row;
219  }
220 
221  public override int GetHashCode() {
222  return (int)tableId + (row * 35331);
223  }
224 
225  public override bool Equals(Object ob) {
226  var dest = (RowRef)ob;
227  return (row == dest.row && tableId == dest.tableId);
228  }
229  }
230 
234  private sealed class CachedRow {
235  public int ResultId;
236  public int Row;
237  public ISqlObject[] RowData;
238  public int[] Sizes;
239  }
240 
241  }
242 }
QueryResultRow GetRow(int index)
void Clear()
Flushes the complete contents of the cache.
Defines the contract for a valid SQL Object
Definition: ISqlObject.cs:23
A cache that stores rows retrieved from the server in result set's.
Represents a cache of Objects. /summary>
Definition: Cache.cs:25
LocalRowCache(DeveelDbConnection connection)
DeveelDbConnectionStringBuilder Settings
readonly Cache rowCache
The actual cache that stores the rows.
Used for the hash key in the cache.
QueryResultPart GetResultPart(int resultId, int rowIndex, int rowCount, int colCount, int totalRowCount)
Requests a block of parts.
readonly DeveelDbConnection connection