DeveelDB  20151217
complete SQL database system, primarly developed for .NET/Mono frameworks
IndexRangeSet.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.Sql;
22 
23 namespace Deveel.Data.Index {
24  public sealed class IndexRangeSet {
25  private readonly List<IndexRange> ranges;
26 
27  public IndexRangeSet()
28  : this(new[] {IndexRange.FullRange}) {
29  }
30 
31  private IndexRangeSet(IEnumerable<IndexRange> ranges) {
32  this.ranges = new List<IndexRange>(ranges);
33  }
34 
35  private static IndexRange IntersectOn(IndexRange range, SqlExpressionType op, DataObject value, bool nullCheck) {
36  var start = range.StartValue;
37  var startPosition = range.StartOffset;
38  var end = range.EndValue;
39  var endPosition = range.EndOffset;
40 
41  bool inclusive = op == SqlExpressionType.Is ||
42  op == SqlExpressionType.Equal ||
43  op == SqlExpressionType.GreaterOrEqualThan ||
44  op == SqlExpressionType.SmallerOrEqualThan;
45 
46  if (op == SqlExpressionType.Is ||
47  op == SqlExpressionType.Equal ||
48  op == SqlExpressionType.GreaterThan ||
49  op == SqlExpressionType.GreaterOrEqualThan) {
50  // With this operator, NULL values must return null.
51  if (nullCheck && value.IsNull) {
52  return IndexRange.Null;
53  }
54 
55  if (start.Equals(IndexRange.FirstInSet)) {
56  start = value;
57  startPosition = inclusive ? RangeFieldOffset.FirstValue : RangeFieldOffset.AfterLastValue;
58  } else {
59  int c = value.CompareTo(start);
60  if ((c == 0 && startPosition == RangeFieldOffset.FirstValue) || c > 0) {
61  start = value;
62  startPosition = inclusive ? RangeFieldOffset.FirstValue : RangeFieldOffset.AfterLastValue;
63  }
64  }
65  }
66 
67  if (op == SqlExpressionType.Is ||
68  op == SqlExpressionType.Equal ||
69  op == SqlExpressionType.SmallerThan ||
70  op == SqlExpressionType.SmallerOrEqualThan) {
71  // With this operator, NULL values must return null.
72  if (nullCheck && value.IsNull) {
73  return IndexRange.Null;
74  }
75 
76  // If start is first in set, then we have to change it to after NULL
77  if (nullCheck && start.Equals(IndexRange.FirstInSet)) {
78  start = DataObject.Null();
79  startPosition = RangeFieldOffset.AfterLastValue;
80  }
81 
82  if (end.Equals(IndexRange.LastInSet)) {
83  end = value;
84  endPosition = inclusive ? RangeFieldOffset.LastValue : RangeFieldOffset.BeforeFirstValue;
85  } else {
86  int c = value.CompareTo(end);
87  if ((c == 0 && endPosition == RangeFieldOffset.LastValue) || c < 0) {
88  end = value;
89  endPosition = inclusive ? RangeFieldOffset.LastValue : RangeFieldOffset.BeforeFirstValue;
90  }
91  }
92  }
93 
94  // If start and end are not null types (if either are, then it means it
95  // is a placeholder value meaning start or end of set).
96  if (!start.Equals(IndexRange.FirstInSet) &&
97  !end.Equals(IndexRange.LastInSet)) {
98  // If start is higher than end, return null
99  int c = start.CompareTo(end);
100  if ((c == 0 && (startPosition == RangeFieldOffset.AfterLastValue ||
101  endPosition == RangeFieldOffset.BeforeFirstValue)) ||
102  c > 0) {
103  return IndexRange.Null;
104  }
105  }
106 
107  // The new intersected range
108  return new IndexRange(startPosition, start, endPosition, end);
109  }
110 
114  private static bool IntersectedBy(IndexRange range1, IndexRange range2) {
115  var startFlag1 = range1.StartOffset;
116  var start1 = range1.StartValue;
117  var endFlag1 = range1.EndOffset;
118  var end1 = range1.EndValue;
119 
120  var startFlag2 = range2.StartOffset;
121  var start2 = range2.StartValue;
122  var endFlag2 = range2.EndOffset;
123  var end2 = range2.EndValue;
124 
125  var startCell1 = start1.Equals(IndexRange.FirstInSet) ? null : start1;
126  var endCell1 = end1.Equals(IndexRange.LastInSet) ? null : end1;
127  var startCell2 = start2.Equals(IndexRange.FirstInSet) ? null : start2;
128  var endCell2 = end2.Equals(IndexRange.LastInSet) ? null : end2;
129 
130  bool intersect1 = false;
131  if (startCell1 != null && endCell2 != null) {
132  int c = startCell1.CompareTo(endCell2);
133  if (c < 0 ||
134  (c == 0 && (startFlag1 == RangeFieldOffset.FirstValue ||
135  endFlag2 == RangeFieldOffset.LastValue))) {
136  intersect1 = true;
137  }
138  } else {
139  intersect1 = true;
140  }
141 
142  bool intersect2 = false;
143  if (startCell2 != null && endCell1 != null) {
144  int c = startCell2.CompareTo(endCell1);
145  if (c < 0 ||
146  (c == 0 && (startFlag2 == RangeFieldOffset.FirstValue ||
147  endFlag1 == RangeFieldOffset.LastValue))) {
148  intersect2 = true;
149  }
150  } else {
151  intersect2 = true;
152  }
153 
154  return (intersect1 && intersect2);
155  }
156 
164  var startPosition1 = range1.StartOffset;
165  var start1 = range1.StartValue;
166  var endPosition1 = range1.EndOffset;
167  var end1 = range1.EndValue;
168 
169  var startPosition2 = range2.StartOffset;
170  var start2 = range2.StartValue;
171  var endPosition2 = range2.EndOffset;
172  var end2 = range2.EndValue;
173 
174  if (!start1.Equals(IndexRange.FirstInSet)) {
175  if (!start2.Equals(IndexRange.FirstInSet)) {
176  var cell = start1;
177  int c = cell.CompareTo(start2);
178  if (c > 0 ||
179  c == 0 && startPosition1 == RangeFieldOffset.AfterLastValue &&
180  startPosition2 == RangeFieldOffset.FirstValue) {
181  start1 = start2;
182  startPosition1 = startPosition2;
183  }
184  } else {
185  start1 = start2;
186  startPosition1 = startPosition2;
187  }
188  }
189 
190  if (!end1.Equals(IndexRange.LastInSet)) {
191  if (!end2.Equals(IndexRange.LastInSet)) {
192  var cell = end1;
193  int c = cell.CompareTo(end2);
194  if (c < 0 ||
195  c == 0 && endPosition1 == RangeFieldOffset.BeforeFirstValue &&
196  endPosition2 == RangeFieldOffset.LastValue) {
197  end1 = end2;
198  endPosition1 = endPosition2;
199  }
200  } else {
201  end1 = end2;
202  endPosition1 = endPosition2;
203  }
204  }
205 
206  return new IndexRange(startPosition1, start1, endPosition1, end1);
207  }
208 
210  lock (this) {
211  int sz = ranges.Count;
212  var list = ranges.GetRange(0, sz);
213 
214  if (op.IsSubQuery())
215  op = op.SubQueryPlainType();
216 
217  if (op == SqlExpressionType.NotEqual ||
218  op == SqlExpressionType.IsNot) {
219  bool nullCheck = op == SqlExpressionType.NotEqual;
220  int j = 0;
221  while (j < sz) {
222  var range = list[j];
223  var leftRange = IntersectOn(range, SqlExpressionType.SmallerThan, value, nullCheck);
224  var rightRange = IntersectOn(range, SqlExpressionType.GreaterThan, value, nullCheck);
225  list.RemoveAt(j);
226  if (leftRange != IndexRange.Null) {
227  list.Add(leftRange);
228  }
229  if (rightRange != IndexRange.Null) {
230  list.Add(rightRange);
231  }
232  j++;
233  }
234 
235  return new IndexRangeSet(list);
236  } else {
237  bool nullCheck = op != SqlExpressionType.Is;
238  int j = 0;
239  while (j < sz) {
240  var range = list[j];
241  range = IntersectOn(range, op, value, nullCheck);
242  if (range == IndexRange.Null) {
243  list.RemoveAt(j);
244  } else {
245  list[j] = range;
246  }
247  j++;
248  }
249 
250  return new IndexRangeSet(list);
251  }
252  }
253  }
254 
259  lock (this) {
260  var rangeSet = new List<IndexRange>(ranges);
261  var inputSet = unionTo.ranges;
262 
263  int inSz = inputSet.Count;
264  int n = 0;
265  while (n < inSz) {
266  var inRange = inputSet[n];
267  int sz = rangeSet.Count;
268  var i = rangeSet.GetRange(0, sz);
269  int j = 0;
270  while (j < i.Count) {
271  var range = i[j];
272  if (IntersectedBy(inRange, range)) {
273  i.RemoveAt(j);
274  inRange = ChangeRangeSizeToEncompass(inRange, range);
275  }
276  j++;
277  }
278 
279  // Insert into sorted position
280  var startPoint = inRange.StartOffset;
281  var start = inRange.StartValue;
282  var endPoint = inRange.EndOffset;
283  var end = inRange.EndValue;
284 
285  if (start == IndexRange.FirstInSet) {
286  rangeSet.Insert(0, inRange);
287  } else {
288  var startCell = start;
289  i = rangeSet.GetRange(0, rangeSet.Count);
290  j = 0;
291  while (j < i.Count) {
292  var range = i[j];
293  var curStart = range.StartValue;
294  if (!curStart.Equals(IndexRange.FirstInSet)) {
295  if (curStart.CompareTo(startCell) > 0) {
296  i[j] = i[j - 1];
297  break;
298  }
299  }
300  j++;
301  }
302  i.Add(inRange);
303  }
304  n++;
305  }
306 
307  return new IndexRangeSet(rangeSet);
308  }
309  }
310 
311  public IndexRange[] ToArray() {
312  lock (this) {
313  return ranges.ToArray();
314  }
315  }
316  }
317 }
bool IsNull
Gets a value that indicates if this object is materialized as null.
Definition: DataObject.cs:91
override bool Equals(object obj)
Definition: DataObject.cs:179
static IndexRange ChangeRangeSizeToEncompass(IndexRange range1, IndexRange range2)
Alters the first range so it encompasses the second range.
Describes the range of values to select from an index.
Definition: IndexRange.cs:38
static IndexRange IntersectOn(IndexRange range, SqlExpressionType op, DataObject value, bool nullCheck)
static readonly IndexRange FullRange
The entire range of values in an index (including NULL)
Definition: IndexRange.cs:74
readonly List< IndexRange > ranges
static DataObject Null(SqlType type)
Definition: DataObject.cs:630
DataObject EndValue
Gets the last value of the range.
Definition: IndexRange.cs:103
SqlExpressionType
All the possible type of SqlExpression supported
static readonly DataObject FirstInSet
Definition: IndexRange.cs:42
RangeFieldOffset EndOffset
Gets the offset of the last value of the range.
Definition: IndexRange.cs:98
static bool IntersectedBy(IndexRange range1, IndexRange range2)
Returns true if the two SelectableRange ranges intersect.
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
static readonly IndexRange Null
Definition: IndexRange.cs:83
DataObject StartValue
Gets the first value of the range.
Definition: IndexRange.cs:93
static readonly DataObject LastInSet
Definition: IndexRange.cs:47
IndexRangeSet Intersect(SqlExpressionType op, DataObject value)
IndexRangeSet(IEnumerable< IndexRange > ranges)
RangeFieldOffset
The absolute offset of a field in a range of a selection.
RangeFieldOffset StartOffset
Gets the offset of the first value of the range.
Definition: IndexRange.cs:88
IndexRangeSet Union(IndexRangeSet unionTo)
Unions the current range set with the given range set.