2 // System.Data.Common.Key.cs
\r
5 // Boris Kirzner <borisk@mainsoft.com>
\r
6 // Konstantin Triger (kostat@mainsoft.com)
\r
10 * Copyright (c) 2002-2004 Mainsoft Corporation.
\r
12 * Permission is hereby granted, free of charge, to any person obtaining a
\r
13 * copy of this software and associated documentation files (the "Software"),
\r
14 * to deal in the Software without restriction, including without limitation
\r
15 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
\r
16 * and/or sell copies of the Software, and to permit persons to whom the
\r
17 * Software is furnished to do so, subject to the following conditions:
\r
19 * The above copyright notice and this permission notice shall be included in
\r
20 * all copies or substantial portions of the Software.
\r
22 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
\r
23 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
\r
24 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
\r
25 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
\r
26 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
\r
27 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
\r
28 * DEALINGS IN THE SOFTWARE.
\r
32 using System.Collections;
\r
36 namespace System.Data.Common
\r
38 enum IndexDuplicatesState { Unknown, True, False };
\r
40 /// Summary description for Index.
\r
42 internal class Index
\r
50 IndexDuplicatesState _hasDuplicates;
\r
52 #endregion // Fields
\r
54 #region Constructors
\r
56 internal Index(Key key)
\r
62 #endregion // Constructors
\r
81 internal int RefCount
\r
88 internal int IndexToRecord(int index){
\r
89 return index < 0 ? index : Array[index];
\r
100 internal bool HasDuplicates
\r
103 if (_array == null || _hasDuplicates == IndexDuplicatesState.Unknown) {
\r
105 if (_hasDuplicates == IndexDuplicatesState.Unknown) {
\r
106 // check for duplicates
\r
107 _hasDuplicates = IndexDuplicatesState.False;
\r
108 for(int i = 0; i < Size - 1; i++) {
\r
109 if (Key.CompareRecords(Array[i],Array[i+1]) == 0) {
\r
110 _hasDuplicates = IndexDuplicatesState.True;
\r
116 return (_hasDuplicates == IndexDuplicatesState.True);
\r
120 #endregion // Properties
\r
124 internal int[] Duplicates {
\r
126 if (!HasDuplicates)
\r
129 ArrayList dups = new ArrayList();
\r
131 bool inRange = false;
\r
132 for(int i = 0; i < Size - 1; i++) {
\r
133 if (Key.CompareRecords(Array[i],Array[i+1]) == 0){
\r
135 dups.Add(Array[i]);
\r
139 dups.Add(Array[i+1]);
\r
145 return (int[])dups.ToArray(typeof(int));
\r
149 private void EnsureArray()
\r
151 if (_array == null) {
\r
156 internal int[] GetAll()
\r
161 internal DataRow[] GetAllRows ()
\r
163 DataRow[] list = new DataRow [Size];
\r
164 for (int i=0; i < Size; ++i)
\r
165 list [i] = Key.Table.RecordCache [Array [i]];
\r
169 internal DataRow[] GetDistinctRows ()
\r
171 ArrayList list = new ArrayList ();
\r
172 list.Add (Key.Table.RecordCache [Array [0]]);
\r
173 int currRecord = Array [0];
\r
174 for (int i=1; i < Size; ++i) {
\r
175 if (Key.CompareRecords (currRecord, Array [i]) == 0)
\r
177 list.Add (Key.Table.RecordCache [Array [i]]);
\r
178 currRecord = Array [i];
\r
180 return (DataRow[])list.ToArray (typeof (DataRow));
\r
183 internal void Reset()
\r
189 private void RebuildIndex()
\r
191 // consider better capacity approximation
\r
192 _array = new int[Key.Table.RecordCache.CurrentCapacity];
\r
194 foreach(DataRow row in Key.Table.Rows) {
\r
195 int record = Key.GetRecord(row);
\r
196 if (record != -1) {
\r
197 _array[_size++] = record;
\r
200 _hasDuplicates = IndexDuplicatesState.False;
\r
201 // Note : MergeSort may update hasDuplicates to True
\r
205 private void Sort()
\r
207 //QuickSort(Array,0,Size-1);
\r
208 MergeSort(Array,Size);
\r
212 * Returns record number of the record equal to the key values supplied
\r
213 * in the meaning of index key, or -1 if no equal record found.
\r
215 internal int Find(object[] keys)
\r
217 int index = FindIndex(keys);
\r
218 return IndexToRecord(index);
\r
222 * Returns record index (location) of the record equal to the key values supplied
\r
223 * in the meaning of index key, or -1 if no equal record found.
\r
225 internal int FindIndex(object[] keys)
\r
227 if (keys == null || keys.Length != Key.Columns.Length) {
\r
228 throw new ArgumentException("Expecting " + Key.Columns.Length + " value(s) for the key being indexed, " +
\r
229 "but received " + ((keys == null) ? 0 : keys.Length) + " value(s).");
\r
232 int tmp = Key.Table.RecordCache.NewRecord();
\r
234 // init key values for temporal record
\r
235 for(int i = 0; i < Key.Columns.Length; i++) {
\r
236 Key.Columns[i].DataContainer[tmp] = keys[i];
\r
238 return FindIndex(tmp);
\r
240 // catch(FormatException) {
\r
243 // catch(InvalidCastException) {
\r
247 Key.Table.RecordCache.DisposeRecord(tmp);
\r
252 * Returns record number of the record equal to the record supplied
\r
253 * in the meaning of index key, or -1 if no equal record found.
\r
255 internal int Find(int record)
\r
257 int index = FindIndex(record);
\r
258 return IndexToRecord(index);
\r
262 * Returns array of record numbers of the records equal equal to the key values supplied
\r
263 * in the meaning of index key, or -1 if no equal record found.
\r
265 internal int[] FindAll(object[] keys)
\r
267 int[] indexes = FindAllIndexes(keys);
\r
268 IndexesToRecords(indexes);
\r
273 * Returns array of indexes of the records inside the index equal equal to the key values supplied
\r
274 * in the meaning of index key, or -1 if no equal record found.
\r
276 internal int[] FindAllIndexes(object[] keys)
\r
278 if (keys == null || keys.Length != Key.Columns.Length) {
\r
279 throw new ArgumentException("Expecting " + Key.Columns.Length + " value(s) for the key being indexed," +
\r
280 "but received " + ((keys == null) ? 0 : keys.Length) + " value(s).");
\r
283 int tmp = Key.Table.RecordCache.NewRecord();
\r
285 // init key values for temporal record
\r
286 for(int i = 0; i < Key.Columns.Length; i++) {
\r
287 Key.Columns[i].DataContainer[tmp] = keys[i];
\r
289 return FindAllIndexes(tmp);
\r
291 catch(FormatException) {
\r
294 catch(InvalidCastException) {
\r
298 Key.Table.RecordCache.DisposeRecord(tmp);
\r
303 * Returns array of record numbers of the records equal to the record supplied
\r
304 * in the meaning of index key, or empty list if no equal records found.
\r
306 internal int[] FindAll(int record)
\r
308 int[] indexes = FindAllIndexes(record);
\r
309 IndexesToRecords(indexes);
\r
314 * Returns array of indexes of the records inside the index that equal to the record supplied
\r
315 * in the meaning of index key, or empty list if no equal records found.
\r
317 internal int[] FindAllIndexes(int record)
\r
319 int index = FindIndex(record);
\r
325 int startIndex = index++;
\r
326 int endIndex = index;
\r
328 for(;startIndex >= 0 && Key.CompareRecords(Array[startIndex],record) == 0;startIndex--);
\r
329 for(;endIndex < Size && Key.CompareRecords(Array[endIndex],record) == 0;endIndex++);
\r
331 int length = endIndex - startIndex - 1;
\r
332 int[] indexes = new int[length];
\r
334 for(int i = 0; i < length; i++) {
\r
335 indexes[i] = ++startIndex;
\r
342 * Returns index inside the array where record number of the record equal to the record supplied
\r
343 * in the meaning of index key is sored, or -1 if no equal record found.
\r
345 private int FindIndex(int record)
\r
350 return BinarySearch(Array,0,Size - 1,record);
\r
354 * Finds exact location of the record specified
\r
356 private int FindIndexExact(int record)
\r
358 for (int i = 0, size = Size; i < size; i++)
\r
359 if (Array[i] == record)
\r
366 * Returns array of records from the indexes (locations) inside the index
\r
368 private void IndexesToRecords(int[] indexes)
\r
370 for(int i = 0; i < indexes.Length; i++) {
\r
371 indexes[i] = Array[indexes[i]];
\r
375 internal void Delete(DataRow row)
\r
377 int oldRecord = Key.GetRecord(row);
\r
382 internal void Delete(int oldRecord)
\r
384 if (oldRecord == -1)
\r
387 int index = FindIndexExact(oldRecord);
\r
389 if ((_hasDuplicates == IndexDuplicatesState.True)) {
\r
394 c1 = Key.CompareRecords(Array[index - 1],oldRecord);
\r
396 if (index < Size - 1) {
\r
397 c2 = Key.CompareRecords(Array[index + 1],oldRecord);
\r
400 if (c1 == 0 ^ c2 == 0) {
\r
401 _hasDuplicates = IndexDuplicatesState.Unknown;
\r
408 private void Remove(int index)
\r
411 System.Array.Copy(Array,index+1,Array,index,Size - index - 1);
\r
417 internal void Update(DataRow row,int oldRecord, DataRowVersion oldVersion, DataRowState oldState)
\r
419 bool contains = Key.ContainsVersion (oldState, oldVersion);
\r
420 int newRecord = Key.GetRecord(row);
\r
421 // the record did not appeared in the index before update
\r
422 if (oldRecord == -1 || Size == 0 || !contains) {
\r
423 if (newRecord >= 0) {
\r
424 if (FindIndexExact(newRecord) < 0)
\r
425 Add(row,newRecord);
\r
430 // the record will not appeare in the index after update
\r
431 if (newRecord < 0 || !Key.CanContain (newRecord)) {
\r
432 Delete (oldRecord);
\r
436 int oldIdx = FindIndexExact(oldRecord);
\r
438 if( oldIdx == -1 ) {
\r
439 Add(row,newRecord);
\r
444 int compare = Key.CompareRecords(Array[oldIdx],newRecord);
\r
450 if (compare == 0) {
\r
451 if (Array[oldIdx] == newRecord) {
\r
452 // we deal with the same record that didn't change
\r
453 // in the context of current index.
\r
454 // so , do nothing.
\r
459 if ((_hasDuplicates == IndexDuplicatesState.True)) {
\r
461 c1 = Key.CompareRecords(Array[oldIdx - 1],newRecord);
\r
463 if (oldIdx < Size - 1) {
\r
464 c2 = Key.CompareRecords(Array[oldIdx + 1],newRecord);
\r
467 if ((c1 == 0 ^ c2 == 0) && compare != 0) {
\r
468 _hasDuplicates = IndexDuplicatesState.Unknown;
\r
473 if ((oldIdx == 0 && compare > 0) || (oldIdx == (Size - 1) && compare < 0) || (compare == 0)) {
\r
474 // no need to switch cells
\r
479 // search after the old place
\r
480 start = oldIdx + 1;
\r
484 // search before the old palce
\r
489 newIdx = LazyBinarySearch(Array,start,end,newRecord);
\r
491 if (oldIdx < newIdx) {
\r
492 System.Array.Copy(Array,oldIdx + 1,Array,oldIdx,newIdx - oldIdx);
\r
493 if (Key.CompareRecords (Array [newIdx], newRecord) > 0)
\r
496 else if (oldIdx > newIdx){
\r
497 System.Array.Copy(Array,newIdx,Array,newIdx + 1,oldIdx - newIdx);
\r
498 if (Key.CompareRecords (Array [newIdx], newRecord) < 0)
\r
502 Array[newIdx] = newRecord;
\r
504 if (compare != 0) {
\r
505 if (!(_hasDuplicates == IndexDuplicatesState.True)) {
\r
507 c1 = Key.CompareRecords(Array[newIdx - 1],newRecord);
\r
509 if (newIdx < Size - 1) {
\r
510 c2 = Key.CompareRecords(Array[newIdx + 1],newRecord);
\r
513 if (c1 == 0 || c2 == 0) {
\r
514 _hasDuplicates = IndexDuplicatesState.True;
\r
520 internal void Add(DataRow row) {
\r
521 Add(row, Key.GetRecord(row));
\r
524 private void Add(DataRow row,int newRecord)
\r
528 if (newRecord < 0 || !Key.CanContain (newRecord))
\r
535 newIdx = LazyBinarySearch(Array,0,Size - 1,newRecord);
\r
536 // if newl value is greater - insert afer old value
\r
537 // else - insert before old value
\r
538 if (Key.CompareRecords(Array[newIdx],newRecord) < 0) {
\r
543 Insert(newIdx,newRecord);
\r
547 if (!(_hasDuplicates == IndexDuplicatesState.True)) {
\r
549 c1 = Key.CompareRecords(Array[newIdx - 1],newRecord);
\r
551 if (newIdx < Size - 1) {
\r
552 c2 = Key.CompareRecords(Array[newIdx + 1],newRecord);
\r
555 if (c1 == 0 || c2 == 0) {
\r
556 _hasDuplicates = IndexDuplicatesState.True;
\r
561 private void Insert(int index,int r)
\r
563 if (Array.Length == Size) {
\r
564 int[] tmp = (Size == 0) ? new int[16] : new int[Size << 1];
\r
565 System.Array.Copy(Array,0,tmp,0,index);
\r
567 System.Array.Copy(Array,index,tmp,index + 1,Size - index);
\r
571 System.Array.Copy(Array,index,Array,index + 1,Size - index);
\r
577 private void MergeSort(int[] to, int length)
\r
579 int[] from = new int[length];
\r
580 System.Array.Copy(to, 0, from, 0, from.Length);
\r
582 MergeSort(from, to, 0, from.Length);
\r
585 private void MergeSort(int[] from, int[] to,int p, int r)
\r
587 int q = (p + r) >> 1;
\r
592 MergeSort(to, from, p, q);
\r
593 MergeSort(to, from, q, r);
\r
596 for (int middle = q, current = p;;) {
\r
597 int res = Key.CompareRecords(from[p],from[q]);
\r
599 to[current++] = from[q++];
\r
602 while( p < middle) {
\r
603 to[current++] = from[p++];
\r
611 _hasDuplicates = IndexDuplicatesState.True;
\r
614 to[current++] = from[p++];
\r
618 to[current++] = from[q++];
\r
626 private void QuickSort(int[] a,int p,int r)
\r
629 int q = Partition(a,p,r);
\r
631 QuickSort(a,q+1,r);
\r
635 private int Partition(int[] a,int p,int r)
\r
642 // decrement upper limit while values are greater then border value
\r
646 while(Key.CompareRecords(a[j],x) > 0); //while(a[j] > x);
\r
651 while(Key.CompareRecords(a[i],x) < 0); //while(a[i] < x);
\r
664 private int BinarySearch(int[] a, int p, int r,int b)
\r
666 int i = LazyBinarySearch(a,p,r,b);
\r
668 return (Key.CompareRecords(a[i],b) == 0) ? i : -1;
\r
671 // Lazy binary search only returns the cell number the search finished in,
\r
672 // but does not checks that the correct value was actually found
\r
673 private int LazyBinarySearch(int[] a, int p, int r,int b)
\r
679 int q = (p+r) >> 1;
\r
681 int compare = Key.CompareRecords(a[q],b);
\r
682 if (compare < 0) { // if (a[q] < b) {
\r
683 return LazyBinarySearch(a,q+1,r,b);
\r
685 else if (compare > 0) { // a[q] > b
\r
686 return LazyBinarySearch(a,p,q,b);
\r
688 else { // a[q] == b
\r
693 internal void AddRef()
\r
698 internal void RemoveRef()
\r
704 // Prints indexes. For debugging.
\r
705 internal void Print ()
\r
707 for (int i=0; i < Size; i++) {
\r
708 Console.Write ("Index {0} record {1}: ", i, Array [i]);
\r
709 for (int j=0; j < Key.Table.Columns.Count; j++) {
\r
710 DataColumn col = Key.Table.Columns [j];
\r
711 if (Array [i] >= 0)
\r
712 Console.Write ("{0,15} ", col [Array [i]]);
\r
714 Console.WriteLine ();
\r
719 #endregion // Methods
\r