DeveelDB  20151217
complete SQL database system, primarly developed for .NET/Mono frameworks
BlindSearchIndex.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 using System.Linq;
20 
21 using Deveel.Data.Sql;
22 using Deveel.Data.Sql.Tables;
23 
24 namespace Deveel.Data.Index {
25  public class BlindSearchIndex : ColumnIndex {
26  public BlindSearchIndex(ITable table, int columnOffset)
27  : base(table, columnOffset) {
28  }
29 
30  public override string IndexType {
31  get { return DefaultIndexTypes.BlindSearch; }
32  }
33 
34  private void AssertNotReadOnly() {
35  if (IsReadOnly)
36  throw new ArgumentException("Cannot mutate a read-only index.");
37  }
38 
39  private int HighestSearch(DataObject ob, IList<int> list, int lower, int higher) {
40  if ((higher - lower) <= 5) {
41  // Start from the bottom up until we find the highest val
42  for (var i = higher; i >= lower; --i) {
43  int res = ob.CompareTo(GetValue(list[i]));
44  if (res >= 0)
45  return i + 1;
46  }
47  // Didn't find return lowest
48  return lower;
49  }
50 
51  var mid = (lower + higher)/2;
52  int compResult = ob.CompareTo(GetValue(list[mid]));
53 
54  if (compResult == 0)
55  // We know the bottom is between 'mid' and 'higher'
56  return HighestSearch(ob, list, mid, higher);
57 
58  if (compResult < 0)
59  return HighestSearch(ob, list, lower, mid - 1);
60 
61  return HighestSearch(ob, list, mid + 1, higher);
62  }
63 
64  private void DoInsertSort(IList<int> list, int row) {
65  int listSize = list.Count;
66  if (listSize == 0) {
67  list.Add(row);
68  } else {
69  var point = HighestSearch(GetValue(row), list, 0, listSize - 1);
70  if (point == listSize) {
71  list.Add(row);
72  } else {
73  list.Insert(point, row);
74  }
75  }
76  }
77 
78  public override ColumnIndex Copy(ITable table, bool readOnly) {
79  return new BlindSearchIndex(table, ColumnOffset);
80  }
81 
82  public override void Insert(int rowNumber) {
83  AssertNotReadOnly();
84  }
85 
86  public override void Remove(int rowNumber) {
87  AssertNotReadOnly();
88  }
89 
90  public override IEnumerable<int> SelectRange(IndexRange[] ranges) {
91  int setSize = Table.RowCount;
92  // If no items in the set return an empty set
93  if (setSize == 0)
94  return new List<int>(0);
95 
96  var checker = new RangeChecker(this, ranges);
97  return checker.Resolve();
98  }
99 
100  public override IEnumerable<int> SelectAll() {
101  var rowList = new List<int>(Table.RowCount);
102  var e = Table.GetEnumerator();
103  while (e.MoveNext()) {
104  DoInsertSort(rowList, e.Current.RowId.RowNumber);
105  }
106  return rowList;
107  }
108 
109  #region RangeChecker
110 
111  class RangeChecker {
112  private readonly BlindSearchIndex scheme;
113 
118  private IEnumerable<int> sortedSet;
119 
120  // The list of flags for each check in the range.
121  // Either 0 for no check, 1 for < or >, 2 for <= or >=.
122  private readonly byte[] lowerFlags;
123  private readonly byte[] upperFlags;
124 
125  // The TObject objects to check against.
126  private readonly DataObject[] lowerCells;
127  private readonly DataObject[] upperCells;
128 
129  private const byte NoCheck = 0;
130  private const byte CheckLesserOrGreater = 1;
131  private const byte CheckLesserEqualOrGreaterEqual = 2;
132 
133  public RangeChecker(BlindSearchIndex scheme, IndexRange[] ranges) {
134  this.scheme = scheme;
135 
136  int size = ranges.Length;
137  lowerFlags = new byte[size];
138  upperFlags = new byte[size];
139  lowerCells = new DataObject[size];
140  upperCells = new DataObject[size];
141  for (int i = 0; i < ranges.Length; ++i) {
142  SetupRange(i, ranges[i]);
143  }
144  }
145 
146  private void ResolveSortedSet() {
147  if (sortedSet == null) {
148  sortedSet = scheme.SelectAll();
149  }
150  }
151 
158  if (ob.Equals(IndexRange.FirstInSet)) {
159  ResolveSortedSet();
160  return scheme.GetValue(sortedSet.First());
161 
162  }
163  if (ob.Equals(IndexRange.LastInSet)) {
164  ResolveSortedSet();
165  return scheme.GetValue(sortedSet.Last());
166  }
167 
168  return ob;
169  }
170 
176  private void SetupRange(int i, IndexRange range) {
177  var l = range.StartValue;
178  var lf = range.StartOffset;
179  var u = range.EndValue;
180  var uf = range.EndOffset;
181 
182  // Handle lower first
183  if (l.Equals(IndexRange.FirstInSet) &&
184  lf.Equals(RangeFieldOffset.FirstValue)) {
185  // Special case no lower check
186  lowerFlags[i] = NoCheck;
187  } else {
188  if (lf.Equals(RangeFieldOffset.FirstValue)) {
189  lowerFlags[i] = CheckLesserEqualOrGreaterEqual; // >=
190  } else if (lf.Equals(RangeFieldOffset.AfterLastValue)) {
191  lowerFlags[i] = CheckLesserOrGreater; // >
192  } else {
193  throw new InvalidOperationException("Incorrect lower flag.");
194  }
195  lowerCells[i] = ResolveCell(l);
196  }
197 
198  // Now handle upper
199  if (u.Equals(IndexRange.LastInSet) &&
200  uf.Equals(RangeFieldOffset.LastValue)) {
201  // Special case no upper check
202  upperFlags[i] = NoCheck;
203  } else {
204  if (uf.Equals(RangeFieldOffset.LastValue)) {
205  upperFlags[i] = CheckLesserEqualOrGreaterEqual; // <=
206  } else if (uf.Equals( RangeFieldOffset.BeforeFirstValue)) {
207  upperFlags[i] = CheckLesserOrGreater; // <
208  } else {
209  throw new InvalidOperationException("Incorrect upper flag.");
210  }
211  upperCells[i] = ResolveCell(u);
212  }
213 
214  }
215 
220  public IEnumerable<int> Resolve() {
221  // The idea here is to only need to scan the column once to find all
222  // the cells that meet our criteria.
223  var list = new List<int>();
224  var e = scheme.Table.GetEnumerator();
225 
226  int compareTally = 0;
227 
228  int size = lowerFlags.Length;
229  while (e.MoveNext()) {
230  int row = e.Current.RowId.RowNumber;
231  // For each range
232  for (int i = 0; i < size; ++i) {
233  bool result = true;
234  byte lf = lowerFlags[i];
235  if (lf != NoCheck) {
236  ++compareTally;
237  var v = scheme.GetValue(row);
238  int compare = lowerCells[i].CompareTo(v);
239  if (lf == CheckLesserOrGreater) { // >
240  result = (compare < 0);
241  } else if (lf == CheckLesserEqualOrGreaterEqual) { // >=
242  result = (compare <= 0);
243  } else {
244  throw new InvalidOperationException("Incorrect flag.");
245  }
246  }
247  if (result) {
248  byte uf = upperFlags[i];
249  if (uf != NoCheck) {
250  ++compareTally;
251  var v = scheme.GetValue(row);
252  int compare = upperCells[i].CompareTo(v);
253  if (uf == CheckLesserOrGreater) { // <
254  result = (compare > 0);
255  } else if (uf == CheckLesserEqualOrGreaterEqual) { // >=
256  result = (compare >= 0);
257  } else {
258  throw new InvalidOperationException("Incorrect flag.");
259  }
260  }
261  // Pick this row
262  if (result) {
263  scheme.DoInsertSort(list, row);
264  break;
265  }
266  }
267  }
268  }
269 
270  return list.AsEnumerable();
271  }
272  }
273 
274  #endregion
275  }
276 }
Defines the contract to access the data contained into a table of a database.
Definition: ITable.cs:40
override ColumnIndex Copy(ITable table, bool readOnly)
override bool Equals(object obj)
Definition: DataObject.cs:179
IEnumerable< int > Resolve()
Resolves the ranges.
Describes the range of values to select from an index.
Definition: IndexRange.cs:38
DataObject ResolveCell(DataObject ob)
Resolves a cell.
DataObject EndValue
Gets the last value of the range.
Definition: IndexRange.cs:103
abstract IEnumerator< Row > GetEnumerator()
override void Insert(int rowNumber)
static readonly DataObject FirstInSet
Definition: IndexRange.cs:42
RangeFieldOffset EndOffset
Gets the offset of the last value of the range.
Definition: IndexRange.cs:98
override IEnumerable< int > SelectAll()
IEnumerable< int > sortedSet
The sorted list of all items in the set created as a cache for finding the first and last values...
BlindSearchIndex(ITable table, int columnOffset)
int CompareTo(DataObject other)
Definition: DataObject.cs:131
Represents a dynamic object that encapsulates a defined SqlType and a compatible constant ISqlObject ...
Definition: DataObject.cs:35
DataObject StartValue
Gets the first value of the range.
Definition: IndexRange.cs:93
RangeChecker(BlindSearchIndex scheme, IndexRange[] ranges)
static readonly DataObject LastInSet
Definition: IndexRange.cs:47
void SetupRange(int i, IndexRange range)
Set up a range.
override IEnumerable< int > SelectRange(IndexRange[] ranges)
RangeFieldOffset
The absolute offset of a field in a range of a selection.
void DoInsertSort(IList< int > list, int row)
abstract int RowCount
Definition: Table.cs:67
override void Remove(int rowNumber)
int HighestSearch(DataObject ob, IList< int > list, int lower, int higher)
RangeFieldOffset StartOffset
Gets the offset of the first value of the range.
Definition: IndexRange.cs:88