Selection.cs 43 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069
  1. //------------------------------------------------------------------------------
  2. // <copyright file="Selection.cs" company="Microsoft">
  3. // Copyright (c) Microsoft Corporation. All rights reserved.
  4. // </copyright>
  5. // <owner current="true" primary="true">[....]</owner>
  6. // <owner current="true" primary="false">[....]</owner>
  7. //------------------------------------------------------------------------------
  8. namespace System.Data {
  9. using System;
  10. using System.Diagnostics;
  11. using System.ComponentModel;
  12. using System.Collections.Generic;
  13. using System.Threading;
  14. internal struct IndexField {
  15. public readonly DataColumn Column;
  16. public readonly bool IsDescending; // false = Asc; true = Desc what is default value for this?
  17. internal IndexField(DataColumn column, bool isDescending) {
  18. Debug.Assert(column != null, "null column");
  19. Column = column;
  20. IsDescending = isDescending;
  21. }
  22. public static bool operator == (IndexField if1, IndexField if2) {
  23. return if1.Column == if2.Column && if1.IsDescending == if2.IsDescending;
  24. }
  25. public static bool operator !=(IndexField if1, IndexField if2) {
  26. return !(if1 == if2);
  27. }
  28. // must override Equals if == operator is defined
  29. public override bool Equals(object obj) {
  30. if (obj is IndexField)
  31. return this == (IndexField)obj;
  32. else
  33. return false;
  34. }
  35. // must override GetHashCode if Equals is redefined
  36. public override int GetHashCode() {
  37. return Column.GetHashCode() ^ IsDescending.GetHashCode();
  38. }
  39. }
  40. internal sealed class Index {
  41. private sealed class IndexTree : RBTree<int> {
  42. private readonly Index _index;
  43. internal IndexTree(Index index) : base(TreeAccessMethod.KEY_SEARCH_AND_INDEX) {
  44. _index = index;
  45. }
  46. protected override int CompareNode (int record1, int record2) {
  47. return _index.CompareRecords(record1, record2);
  48. }
  49. protected override int CompareSateliteTreeNode (int record1, int record2) {
  50. return _index.CompareDuplicateRecords(record1, record2);
  51. }
  52. }
  53. // these constants are used to update a DataRow when the record and Row are known, but don't match
  54. private const int DoNotReplaceCompareRecord = 0;
  55. private const int ReplaceNewRecordForCompare = 1;
  56. private const int ReplaceOldRecordForCompare = 2;
  57. private readonly DataTable table;
  58. internal readonly IndexField[] IndexFields;
  59. /// <summary>Allow a user implemented comparision of two DataRow</summary>
  60. /// <remarks>User must use correct DataRowVersion in comparison or index corruption will happen</remarks>
  61. private readonly System.Comparison<DataRow> _comparison;
  62. private readonly DataViewRowState recordStates;
  63. private WeakReference rowFilter;
  64. private IndexTree records;
  65. private int recordCount;
  66. private int refCount;
  67. private Listeners<DataViewListener> _listeners;
  68. private bool suspendEvents;
  69. private readonly static object[] zeroObjects = new object[0];
  70. private readonly bool isSharable;
  71. private readonly bool _hasRemoteAggregate;
  72. internal const Int32 MaskBits = unchecked((int)0x7FFFFFFF);
  73. private static int _objectTypeCount; // Bid counter
  74. private readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount);
  75. public Index(DataTable table, IndexField[] indexFields, DataViewRowState recordStates, IFilter rowFilter)
  76. : this(table, indexFields, null, recordStates, rowFilter) { }
  77. public Index(DataTable table, System.Comparison<DataRow> comparison, DataViewRowState recordStates, IFilter rowFilter)
  78. : this(table, GetAllFields(table.Columns), comparison, recordStates, rowFilter) { }
  79. // for the delegate methods, we don't know what the dependent columns are - so all columns are dependent
  80. private static IndexField[] GetAllFields(DataColumnCollection columns) {
  81. IndexField[] fields = new IndexField[columns.Count];
  82. for(int i = 0; i < fields.Length; ++i) {
  83. fields[i] = new IndexField(columns[i], false);
  84. }
  85. return fields;
  86. }
  87. private Index(DataTable table, IndexField[] indexFields, System.Comparison<DataRow> comparison, DataViewRowState recordStates, IFilter rowFilter) {
  88. Bid.Trace("<ds.Index.Index|API> %d#, table=%d, recordStates=%d{ds.DataViewRowState}\n",
  89. ObjectID, (table != null) ? table.ObjectID : 0, (int)recordStates);
  90. Debug.Assert(indexFields != null);
  91. Debug.Assert(null != table, "null table");
  92. if ((recordStates &
  93. (~(DataViewRowState.CurrentRows | DataViewRowState.OriginalRows))) != 0) {
  94. throw ExceptionBuilder.RecordStateRange();
  95. }
  96. this.table = table;
  97. _listeners = new Listeners<DataViewListener>(ObjectID,
  98. delegate(DataViewListener listener)
  99. {
  100. return (null != listener);
  101. });
  102. IndexFields = indexFields;
  103. this.recordStates = recordStates;
  104. _comparison = comparison;
  105. DataColumnCollection columns = table.Columns;
  106. isSharable = (rowFilter == null) && (comparison == null); // a filter or comparison make an index unsharable
  107. if (null != rowFilter) {
  108. this.rowFilter = new WeakReference(rowFilter);
  109. DataExpression expr = (rowFilter as DataExpression);
  110. if (null != expr) {
  111. _hasRemoteAggregate = expr.HasRemoteAggregate();
  112. }
  113. }
  114. InitRecords(rowFilter);
  115. // do not AddRef in ctor, every caller should be responsible to AddRef it
  116. // if caller does not AddRef, it is expected to be a one-time read operation because the index won't be maintained on writes
  117. }
  118. public bool Equal(IndexField[] indexDesc, DataViewRowState recordStates, IFilter rowFilter) {
  119. if (
  120. !isSharable ||
  121. IndexFields.Length != indexDesc.Length ||
  122. this.recordStates != recordStates ||
  123. null != rowFilter
  124. ) {
  125. return false;
  126. }
  127. for (int loop = 0; loop < IndexFields.Length; loop++) {
  128. if (IndexFields[loop].Column!= indexDesc[loop].Column ||
  129. IndexFields[loop].IsDescending != indexDesc[loop].IsDescending) {
  130. return false;
  131. }
  132. }
  133. return true;
  134. }
  135. internal bool HasRemoteAggregate {
  136. get {
  137. return _hasRemoteAggregate;
  138. }
  139. }
  140. internal int ObjectID {
  141. get {
  142. return _objectID;
  143. }
  144. }
  145. public DataViewRowState RecordStates {
  146. get { return recordStates; }
  147. }
  148. public IFilter RowFilter {
  149. get { return (IFilter)((null != rowFilter) ? rowFilter.Target : null); }
  150. }
  151. public int GetRecord(int recordIndex) {
  152. Debug.Assert (recordIndex >= 0 && recordIndex < recordCount, "recordIndex out of range");
  153. return records[recordIndex];
  154. }
  155. public bool HasDuplicates {
  156. get {
  157. return records.HasDuplicates;
  158. }
  159. }
  160. public int RecordCount {
  161. get {
  162. return recordCount;
  163. }
  164. }
  165. public bool IsSharable {
  166. get {
  167. return isSharable;
  168. }
  169. }
  170. private bool AcceptRecord(int record) {
  171. return AcceptRecord(record, RowFilter);
  172. }
  173. private bool AcceptRecord(int record, IFilter filter) {
  174. Bid.Trace("<ds.Index.AcceptRecord|API> %d#, record=%d\n", ObjectID, record);
  175. if (filter == null)
  176. return true;
  177. DataRow row = table.recordManager[record];
  178. if (row == null)
  179. return true;
  180. //
  181. DataRowVersion version = DataRowVersion.Default;
  182. if (row.oldRecord == record) {
  183. version = DataRowVersion.Original;
  184. }
  185. else if (row.newRecord == record) {
  186. version = DataRowVersion.Current;
  187. }
  188. else if (row.tempRecord == record) {
  189. version = DataRowVersion.Proposed;
  190. }
  191. return filter.Invoke(row, version);
  192. }
  193. /// <remarks>Only call from inside a lock(this)</remarks>
  194. internal void ListChangedAdd(DataViewListener listener) {
  195. _listeners.Add(listener);
  196. }
  197. /// <remarks>Only call from inside a lock(this)</remarks>
  198. internal void ListChangedRemove(DataViewListener listener) {
  199. _listeners.Remove(listener);
  200. }
  201. public int RefCount {
  202. get {
  203. return refCount;
  204. }
  205. }
  206. public void AddRef() {
  207. Bid.Trace("<ds.Index.AddRef|API> %d#\n", ObjectID);
  208. LockCookie lc = table.indexesLock.UpgradeToWriterLock(-1);
  209. try {
  210. Debug.Assert(0 <= refCount, "AddRef on disposed index");
  211. Debug.Assert(null != records, "null records");
  212. if (refCount == 0) {
  213. table.ShadowIndexCopy();
  214. table.indexes.Add(this);
  215. }
  216. refCount++;
  217. }
  218. finally {
  219. table.indexesLock.DowngradeFromWriterLock(ref lc);
  220. }
  221. }
  222. public int RemoveRef() {
  223. Bid.Trace("<ds.Index.RemoveRef|API> %d#\n", ObjectID);
  224. int count;
  225. LockCookie lc = table.indexesLock.UpgradeToWriterLock(-1);
  226. try {
  227. count = --refCount;
  228. if (refCount <= 0) {
  229. table.ShadowIndexCopy();
  230. table.indexes.Remove(this);
  231. }
  232. }
  233. finally {
  234. table.indexesLock.DowngradeFromWriterLock(ref lc);
  235. }
  236. return count;
  237. }
  238. private void ApplyChangeAction(int record, int action, int changeRecord) {
  239. if (action != 0) {
  240. if (action > 0) {
  241. if (AcceptRecord(record)) {
  242. InsertRecord(record, true);
  243. }
  244. }
  245. else if ((null != _comparison) && (-1 != record)) {
  246. // when removing a record, the DataRow has already been updated to the newer record
  247. // depending on changeRecord, either the new or old record needs be backdated to record
  248. // for Comparison<DataRow> to operate correctly
  249. DeleteRecord(GetIndex(record, changeRecord));
  250. }
  251. else {
  252. // unnecessary codepath other than keeping original code path for redbits
  253. DeleteRecord(GetIndex(record));
  254. }
  255. }
  256. }
  257. public bool CheckUnique() {
  258. #if DEBUG
  259. Debug.Assert(records.CheckUnique(records.root) != HasDuplicates, "CheckUnique difference");
  260. #endif
  261. return !HasDuplicates;
  262. }
  263. // only used for main tree compare, not satalite tree
  264. private int CompareRecords(int record1, int record2) {
  265. if (null != _comparison) {
  266. return CompareDataRows(record1, record2);
  267. }
  268. if (0 < IndexFields.Length) {
  269. for (int i = 0; i < IndexFields.Length; i++) {
  270. int c = IndexFields[i].Column.Compare(record1, record2);
  271. if (c != 0) {
  272. return (IndexFields[i].IsDescending ? -c : c);
  273. }
  274. }
  275. return 0;
  276. }
  277. else {
  278. Debug.Assert(null != table.recordManager[record1], "record1 no datarow");
  279. Debug.Assert(null != table.recordManager[record2], "record2 no datarow");
  280. // DataRow needs to always updated appropriately via GetIndex(int,int)
  281. //table.recordManager.VerifyRecord(record1, table.recordManager[record1]);
  282. //table.recordManager.VerifyRecord(record2, table.recordManager[record2]);
  283. // Need to use compare because subtraction will wrap
  284. // to positive for very large neg numbers, etc.
  285. return table.Rows.IndexOf(table.recordManager[record1]).CompareTo(table.Rows.IndexOf(table.recordManager[record2]));
  286. }
  287. }
  288. private int CompareDataRows(int record1, int record2)
  289. {
  290. table.recordManager.VerifyRecord(record1, table.recordManager[record1]);
  291. table.recordManager.VerifyRecord(record2, table.recordManager[record2]);
  292. return _comparison(table.recordManager[record1], table.recordManager[record2]);
  293. }
  294. // PS: same as previous CompareRecords, except it compares row state if needed
  295. // only used for satalite tree compare
  296. private int CompareDuplicateRecords(int record1, int record2) {
  297. #if DEBUG
  298. if (null != _comparison) {
  299. Debug.Assert(0 == CompareDataRows(record1, record2), "duplicate record not a duplicate by user function");
  300. }
  301. else if (record1 != record2) {
  302. for (int i = 0; i < IndexFields.Length; i++) {
  303. int c = IndexFields[i].Column.Compare(record1, record2);
  304. Debug.Assert(0 == c, "duplicate record not a duplicate");
  305. }
  306. }
  307. #endif
  308. Debug.Assert(null != table.recordManager[record1], "record1 no datarow");
  309. Debug.Assert(null != table.recordManager[record2], "record2 no datarow");
  310. // DataRow needs to always updated appropriately via GetIndex(int,int)
  311. //table.recordManager.VerifyRecord(record1, table.recordManager[record1]);
  312. //table.recordManager.VerifyRecord(record2, table.recordManager[record2]);
  313. if (null == table.recordManager[record1]) {
  314. return ((null == table.recordManager[record2]) ? 0 : -1);
  315. }
  316. else if (null == table.recordManager[record2]) {
  317. return 1;
  318. }
  319. // Need to use compare because subtraction will wrap
  320. // to positive for very large neg numbers, etc.
  321. int diff = table.recordManager[record1].rowID.CompareTo(table.recordManager[record2].rowID);
  322. // if they're two records in the same row, we need to be able to distinguish them.
  323. if ((diff == 0) && (record1 != record2)) {
  324. diff = ((int)table.recordManager[record1].GetRecordState(record1)).CompareTo((int)table.recordManager[record2].GetRecordState(record2));
  325. }
  326. return diff;
  327. }
  328. private int CompareRecordToKey(int record1, object[] vals) {
  329. for (int i = 0; i < IndexFields.Length; i++) {
  330. int c = IndexFields[i].Column.CompareValueTo(record1, vals[i]);
  331. if (c != 0) {
  332. return (IndexFields[i].IsDescending ? -c : c);
  333. }
  334. }
  335. return 0;
  336. }
  337. // DeleteRecordFromIndex deletes the given record from index and does not fire any Event. IT SHOULD NOT FIRE EVENT
  338. // I added this since I can not use existing DeleteRecord which is not silent operation
  339. public void DeleteRecordFromIndex(int recordIndex) { // this is for expression use, to maintain expression columns's sort , filter etc. do not fire event
  340. DeleteRecord(recordIndex, false);
  341. }
  342. // old and existing DeleteRecord behavior, we can not use this for silently deleting
  343. private void DeleteRecord(int recordIndex) {
  344. DeleteRecord(recordIndex, true);
  345. }
  346. private void DeleteRecord(int recordIndex, bool fireEvent) {
  347. Bid.Trace("<ds.Index.DeleteRecord|INFO> %d#, recordIndex=%d, fireEvent=%d{bool}\n", ObjectID, recordIndex, fireEvent);
  348. if (recordIndex >= 0) {
  349. recordCount--;
  350. int record = records.DeleteByIndex(recordIndex);
  351. MaintainDataView(ListChangedType.ItemDeleted, record, !fireEvent);
  352. if (fireEvent) {
  353. // 1) Webdata 104939 do not fix this, it would be breaking change
  354. // 2) newRecord = -1, oldrecord = recordIndex;
  355. OnListChanged(ListChangedType.ItemDeleted, recordIndex);
  356. }
  357. }
  358. }
  359. // SQLBU 428961: Serious performance issue when creating DataView
  360. // this improves performance by allowing DataView to iterating instead of computing for records over index
  361. // this will also allow Linq over DataSet to enumerate over the index
  362. // avoid boxing by returning RBTreeEnumerator (a struct) instead of IEnumerator<int>
  363. public RBTree<int>.RBTreeEnumerator GetEnumerator(int startIndex) {
  364. return new IndexTree.RBTreeEnumerator(records, startIndex);
  365. }
  366. // What it actually does is find the index in the records[] that
  367. // this record inhabits, and if it doesn't, suggests what index it would
  368. // inhabit while setting the high bit.
  369. //
  370. public int GetIndex(int record) {
  371. int index = records.GetIndexByKey(record);
  372. return index;
  373. }
  374. /// <summary>
  375. /// When searching by value for a specific record, the DataRow may require backdating to reflect the appropriate state
  376. /// otherwise on Delete of a DataRow in the Added state, would result in the <see cref="System.Comparison&lt;DataRow&gt;"/> where the row
  377. /// reflection record would be in the Detatched instead of Added state.
  378. /// </summary>
  379. private int GetIndex(int record, int changeRecord) {
  380. Debug.Assert(null != _comparison, "missing comparison");
  381. int index;
  382. DataRow row = table.recordManager[record];
  383. int a = row.newRecord;
  384. int b = row.oldRecord;
  385. try {
  386. switch(changeRecord) {
  387. case ReplaceNewRecordForCompare:
  388. row.newRecord = record;
  389. break;
  390. case ReplaceOldRecordForCompare:
  391. row.oldRecord = record;
  392. break;
  393. }
  394. table.recordManager.VerifyRecord(record, row);
  395. index = records.GetIndexByKey(record);
  396. }
  397. finally {
  398. switch(changeRecord) {
  399. case ReplaceNewRecordForCompare:
  400. Debug.Assert(record == row.newRecord, "newRecord has change during GetIndex");
  401. row.newRecord = a;
  402. break;
  403. case ReplaceOldRecordForCompare:
  404. Debug.Assert(record == row.oldRecord, "oldRecord has change during GetIndex");
  405. row.oldRecord = b;
  406. break;
  407. }
  408. #if DEBUG
  409. if (-1 != a) {
  410. table.recordManager.VerifyRecord(a, row);
  411. }
  412. #endif
  413. }
  414. return index;
  415. }
  416. public object[] GetUniqueKeyValues() {
  417. if (IndexFields == null || IndexFields.Length == 0) {
  418. return zeroObjects;
  419. }
  420. List<object[]> list = new List<object[]>();
  421. GetUniqueKeyValues(list, records.root);
  422. return list.ToArray();
  423. }
  424. /// <summary>
  425. /// Find index of maintree node that matches key in record
  426. /// </summary>
  427. public int FindRecord(int record) {
  428. int nodeId = records.Search(record);
  429. if (nodeId!=IndexTree.NIL)
  430. return records.GetIndexByNode(nodeId); //always returns the First record index
  431. else
  432. return -1;
  433. }
  434. public int FindRecordByKey(object key) {
  435. int nodeId = FindNodeByKey(key);
  436. if (IndexTree.NIL != nodeId) {
  437. return records.GetIndexByNode(nodeId);
  438. }
  439. return -1; // return -1 to user indicating record not found
  440. }
  441. public int FindRecordByKey(object[] key) {
  442. int nodeId = FindNodeByKeys(key);
  443. if (IndexTree.NIL != nodeId) {
  444. return records.GetIndexByNode(nodeId);
  445. }
  446. return -1; // return -1 to user indicating record not found
  447. }
  448. private int FindNodeByKey(object originalKey) {
  449. int x, c;
  450. if (IndexFields.Length != 1) {
  451. throw ExceptionBuilder.IndexKeyLength(IndexFields.Length, 1);
  452. }
  453. x = records.root;
  454. if (IndexTree.NIL != x) { // otherwise storage may not exist
  455. DataColumn column = IndexFields[0].Column;
  456. object key = column.ConvertValue(originalKey);
  457. x = records.root;
  458. if (IndexFields[0].IsDescending) {
  459. while (IndexTree.NIL != x) {
  460. c = column.CompareValueTo(records.Key(x), key);
  461. if (c == 0) { break; }
  462. if (c < 0) { x = records.Left(x); } // < for decsending
  463. else { x = records.Right(x); }
  464. }
  465. }
  466. else {
  467. while (IndexTree.NIL != x) {
  468. c = column.CompareValueTo(records.Key(x), key);
  469. if (c == 0) { break; }
  470. if (c > 0) { x = records.Left(x); } // > for ascending
  471. else { x = records.Right(x); }
  472. }
  473. }
  474. }
  475. return x;
  476. }
  477. private int FindNodeByKeys(object[] originalKey) {
  478. int x, c;
  479. c = ((null != originalKey) ? originalKey.Length : 0);
  480. if ((0 == c) || (IndexFields.Length != c)) {
  481. throw ExceptionBuilder.IndexKeyLength(IndexFields.Length, c);
  482. }
  483. x = records.root;
  484. if (IndexTree.NIL != x) { // otherwise storage may not exist
  485. // copy array to avoid changing original
  486. object[] key = new object[originalKey.Length];
  487. for(int i = 0; i < originalKey.Length; ++i) {
  488. key[i] = IndexFields[i].Column.ConvertValue(originalKey[i]);
  489. }
  490. x = records.root;
  491. while (IndexTree.NIL != x) {
  492. c = CompareRecordToKey(records.Key(x), key);
  493. if (c == 0) { break; }
  494. if (c > 0) { x = records.Left(x); }
  495. else { x = records.Right(x); }
  496. }
  497. }
  498. return x;
  499. }
  500. private int FindNodeByKeyRecord(int record) {
  501. int x, c;
  502. x = records.root;
  503. if (IndexTree.NIL != x) { // otherwise storage may not exist
  504. x = records.root;
  505. while (IndexTree.NIL != x) {
  506. c = CompareRecords(records.Key(x), record);
  507. if (c == 0) { break; }
  508. if (c > 0) { x = records.Left(x); }
  509. else { x = records.Right(x); }
  510. }
  511. }
  512. return x;
  513. }
  514. internal delegate int ComparisonBySelector<TKey,TRow>(TKey key, TRow row) where TRow:DataRow;
  515. /// <summary>This method exists for LinqDataView to keep a level of abstraction away from the RBTree</summary>
  516. internal Range FindRecords<TKey,TRow>(ComparisonBySelector<TKey,TRow> comparison, TKey key) where TRow:DataRow
  517. {
  518. int x = records.root;
  519. while (IndexTree.NIL != x)
  520. {
  521. int c = comparison(key, (TRow)table.recordManager[records.Key(x)]);
  522. if (c == 0) { break; }
  523. if (c < 0) { x = records.Left(x); }
  524. else { x = records.Right(x); }
  525. }
  526. return GetRangeFromNode(x);
  527. }
  528. private Range GetRangeFromNode(int nodeId)
  529. {
  530. // fill range with the min and max indexes of matching record (i.e min and max of satelite tree)
  531. // min index is the index of the node in main tree, and max is the min + size of satelite tree-1
  532. if (IndexTree.NIL == nodeId) {
  533. return new Range();
  534. }
  535. int recordIndex = records.GetIndexByNode(nodeId);
  536. if (records.Next (nodeId) == IndexTree.NIL)
  537. return new Range (recordIndex, recordIndex);
  538. int span = records.SubTreeSize(records.Next(nodeId));
  539. return new Range (recordIndex, recordIndex + span - 1);
  540. }
  541. public Range FindRecords(object key) {
  542. int nodeId = FindNodeByKey (key); // main tree node associated with key
  543. return GetRangeFromNode(nodeId);
  544. }
  545. public Range FindRecords(object[] key) {
  546. int nodeId = FindNodeByKeys (key); // main tree node associated with key
  547. return GetRangeFromNode(nodeId);
  548. }
  549. internal void FireResetEvent() {
  550. Bid.Trace("<ds.Index.FireResetEvent|API> %d#\n", ObjectID);
  551. if (DoListChanged) {
  552. OnListChanged(DataView.ResetEventArgs);
  553. }
  554. }
  555. private int GetChangeAction(DataViewRowState oldState, DataViewRowState newState) {
  556. int oldIncluded = ((int)recordStates & (int)oldState) == 0? 0: 1;
  557. int newIncluded = ((int)recordStates & (int)newState) == 0? 0: 1;
  558. return newIncluded - oldIncluded;
  559. }
  560. /// <summary>Determine if the record that needs backdating is the newRecord or oldRecord or neither</summary>
  561. private static int GetReplaceAction(DataViewRowState oldState)
  562. {
  563. return ((0 != (DataViewRowState.CurrentRows & oldState)) ? ReplaceNewRecordForCompare : // Added/ModifiedCurrent/Unchanged
  564. ((0 != (DataViewRowState.OriginalRows & oldState)) ? ReplaceOldRecordForCompare : // Deleted/ModififedOriginal
  565. DoNotReplaceCompareRecord)); // None
  566. }
  567. public DataRow GetRow(int i) {
  568. return table.recordManager[GetRecord(i)];
  569. }
  570. public DataRow[] GetRows(Object[] values) {
  571. return GetRows(FindRecords(values));
  572. }
  573. public DataRow[] GetRows(Range range) {
  574. DataRow[] newRows = table.NewRowArray(range.Count);
  575. if (0 < newRows.Length) {
  576. RBTree<int>.RBTreeEnumerator iterator = GetEnumerator(range.Min);
  577. for (int i = 0; i < newRows.Length && iterator.MoveNext(); i++) {
  578. newRows[i] = table.recordManager[iterator.Current];
  579. }
  580. }
  581. return newRows;
  582. }
  583. private void InitRecords(IFilter filter) {
  584. DataViewRowState states = recordStates;
  585. // SQLBU 428961: Serious performance issue when creating DataView
  586. // this improves performance when the is no filter, like with the default view (creating after rows added)
  587. // we know the records are in the correct order, just append to end, duplicates not possible
  588. bool append = (0 == IndexFields.Length);
  589. records = new IndexTree(this);
  590. recordCount = 0;
  591. // SQLBU 428961: Serious performance issue when creating DataView
  592. // this improves performance by iterating of the index instead of computing record by index
  593. foreach(DataRow b in table.Rows)
  594. {
  595. int record = -1;
  596. if (b.oldRecord == b.newRecord) {
  597. if ((int)(states & DataViewRowState.Unchanged) != 0) {
  598. record = b.oldRecord;
  599. }
  600. }
  601. else if (b.oldRecord == -1) {
  602. if ((int)(states & DataViewRowState.Added) != 0) {
  603. record = b.newRecord;
  604. }
  605. }
  606. else if (b.newRecord == -1) {
  607. if ((int)(states & DataViewRowState.Deleted) != 0) {
  608. record = b.oldRecord;
  609. }
  610. }
  611. else {
  612. if ((int)(states & DataViewRowState.ModifiedCurrent) != 0) {
  613. record = b.newRecord;
  614. }
  615. else if ((int)(states & DataViewRowState.ModifiedOriginal) != 0) {
  616. record = b.oldRecord;
  617. }
  618. }
  619. if (record != -1 && AcceptRecord(record, filter))
  620. {
  621. records.InsertAt(-1, record, append);
  622. recordCount++;
  623. }
  624. }
  625. }
  626. // InsertRecordToIndex inserts the given record to index and does not fire any Event. IT SHOULD NOT FIRE EVENT
  627. // I added this since I can not use existing InsertRecord which is not silent operation
  628. // it returns the position that record is inserted
  629. public int InsertRecordToIndex(int record) {
  630. int pos = -1;
  631. if (AcceptRecord(record)) {
  632. pos = InsertRecord(record, false);
  633. }
  634. return pos;
  635. }
  636. // existing functionality, it calls the overlaod with fireEvent== true, so it still fires the event
  637. private int InsertRecord(int record, bool fireEvent) {
  638. Bid.Trace("<ds.Index.InsertRecord|INFO> %d#, record=%d, fireEvent=%d{bool}\n", ObjectID, record, fireEvent);
  639. // SQLBU 428961: Serious performance issue when creating DataView
  640. // this improves performance when the is no filter, like with the default view (creating before rows added)
  641. // we know can append when the new record is the last row in table, normal insertion pattern
  642. bool append = false;
  643. if ((0 == IndexFields.Length) && (null != table))
  644. {
  645. DataRow row = table.recordManager[record];
  646. append = (table.Rows.IndexOf(row)+1 == table.Rows.Count);
  647. }
  648. int nodeId = records.InsertAt(-1, record, append);
  649. recordCount++;
  650. MaintainDataView(ListChangedType.ItemAdded, record, !fireEvent);
  651. if (fireEvent) {
  652. if (DoListChanged) {
  653. OnListChanged(ListChangedType.ItemAdded, records.GetIndexByNode(nodeId));
  654. }
  655. return 0;
  656. }
  657. else {
  658. return records.GetIndexByNode(nodeId);
  659. }
  660. }
  661. // Search for specified key
  662. public bool IsKeyInIndex(object key) {
  663. int x_id = FindNodeByKey(key);
  664. return (IndexTree.NIL != x_id);
  665. }
  666. public bool IsKeyInIndex(object[] key) {
  667. int x_id = FindNodeByKeys(key);
  668. return (IndexTree.NIL != x_id);
  669. }
  670. public bool IsKeyRecordInIndex(int record) {
  671. int x_id = FindNodeByKeyRecord(record);
  672. return (IndexTree.NIL != x_id);
  673. }
  674. private bool DoListChanged {
  675. get { return (!suspendEvents && _listeners.HasListeners && !table.AreIndexEventsSuspended); }
  676. }
  677. private void OnListChanged(ListChangedType changedType, int newIndex, int oldIndex) {
  678. if (DoListChanged) {
  679. OnListChanged(new ListChangedEventArgs(changedType, newIndex, oldIndex));
  680. }
  681. }
  682. private void OnListChanged(ListChangedType changedType, int index) {
  683. if (DoListChanged) {
  684. OnListChanged(new ListChangedEventArgs(changedType, index));
  685. }
  686. }
  687. private void OnListChanged(ListChangedEventArgs e) {
  688. Bid.Trace("<ds.Index.OnListChanged|INFO> %d#\n", ObjectID);
  689. Debug.Assert(DoListChanged, "supposed to check DoListChanged before calling to delay create ListChangedEventArgs");
  690. _listeners.Notify(e, false, false,
  691. delegate(DataViewListener listener, ListChangedEventArgs args, bool arg2, bool arg3)
  692. {
  693. listener.IndexListChanged(args);
  694. });
  695. }
  696. private void MaintainDataView(ListChangedType changedType, int record, bool trackAddRemove) {
  697. Debug.Assert(-1 <= record, "bad record#");
  698. _listeners.Notify(changedType, ((0 <= record) ? table.recordManager[record] : null), trackAddRemove,
  699. delegate(DataViewListener listener, ListChangedType type, DataRow row, bool track)
  700. {
  701. listener.MaintainDataView(changedType, row, track);
  702. });
  703. }
  704. public void Reset() {
  705. Bid.Trace("<ds.Index.Reset|API> %d#\n", ObjectID);
  706. InitRecords(RowFilter);
  707. MaintainDataView(ListChangedType.Reset, -1, false); // SQLBU 360388
  708. FireResetEvent();
  709. }
  710. public void RecordChanged(int record) {
  711. Bid.Trace("<ds.Index.RecordChanged|API> %d#, record=%d\n", ObjectID, record);
  712. if (DoListChanged) {
  713. int index = GetIndex(record);
  714. if (index >= 0) {
  715. OnListChanged(ListChangedType.ItemChanged, index);
  716. }
  717. }
  718. }
  719. // new RecordChanged which takes oldIndex and newIndex and fires onListChanged
  720. public void RecordChanged(int oldIndex, int newIndex) {
  721. Bid.Trace("<ds.Index.RecordChanged|API> %d#, oldIndex=%d, newIndex=%d\n", ObjectID, oldIndex, newIndex);
  722. if (oldIndex > -1 || newIndex > -1) { // no need to fire if it was not and will not be in index: this check means at least one version should be in index
  723. if (oldIndex == newIndex ) {
  724. OnListChanged(ListChangedType.ItemChanged, newIndex, oldIndex);
  725. }
  726. else if (oldIndex == -1) { // it is added
  727. OnListChanged(ListChangedType.ItemAdded, newIndex, oldIndex);
  728. }
  729. else if (newIndex == -1) { // its deleted
  730. // Do not fix this. see bug Bug 271076 for explanation.
  731. OnListChanged(ListChangedType.ItemDeleted, oldIndex);
  732. }
  733. else {
  734. OnListChanged(ListChangedType.ItemMoved, newIndex, oldIndex);
  735. }
  736. }
  737. }
  738. public void RecordStateChanged(int record, DataViewRowState oldState, DataViewRowState newState) {
  739. Bid.Trace("<ds.Index.RecordStateChanged|API> %d#, record=%d, oldState=%d{ds.DataViewRowState}, newState=%d{ds.DataViewRowState}\n", ObjectID, record, (int)oldState, (int)newState);
  740. int action = GetChangeAction(oldState, newState);
  741. ApplyChangeAction(record, action, GetReplaceAction(oldState));
  742. }
  743. public void RecordStateChanged(int oldRecord, DataViewRowState oldOldState, DataViewRowState oldNewState,
  744. int newRecord, DataViewRowState newOldState, DataViewRowState newNewState) {
  745. Bid.Trace("<ds.Index.RecordStateChanged|API> %d#, oldRecord=%d, oldOldState=%d{ds.DataViewRowState}, oldNewState=%d{ds.DataViewRowState}, newRecord=%d, newOldState=%d{ds.DataViewRowState}, newNewState=%d{ds.DataViewRowState}\n", ObjectID,oldRecord, (int)oldOldState, (int)oldNewState, newRecord, (int)newOldState, (int)newNewState);
  746. Debug.Assert((-1 == oldRecord) || (-1 == newRecord) ||
  747. table.recordManager[oldRecord] == table.recordManager[newRecord],
  748. "not the same DataRow when updating oldRecord and newRecord");
  749. int oldAction = GetChangeAction(oldOldState, oldNewState);
  750. int newAction = GetChangeAction(newOldState, newNewState);
  751. if (oldAction == -1 && newAction == 1 && AcceptRecord(newRecord)) {
  752. int oldRecordIndex;
  753. if ((null != _comparison) && oldAction < 0)
  754. { // when oldRecord is being removed, allow GetIndexByKey updating the DataRow for Comparison<DataRow>
  755. oldRecordIndex = GetIndex (oldRecord, GetReplaceAction(oldOldState));
  756. }
  757. else {
  758. oldRecordIndex = GetIndex (oldRecord);
  759. }
  760. if ((null == _comparison) && oldRecordIndex != -1 && CompareRecords(oldRecord, newRecord)==0) {
  761. records.UpdateNodeKey(oldRecord, newRecord); //change in place, as Both records have same key value
  762. int commonIndexLocation = GetIndex(newRecord);
  763. OnListChanged(ListChangedType.ItemChanged, commonIndexLocation, commonIndexLocation);
  764. }
  765. else {
  766. suspendEvents = true;
  767. if (oldRecordIndex != -1) {
  768. records.DeleteByIndex(oldRecordIndex); // DeleteByIndex doesn't require searching by key
  769. recordCount--;
  770. }
  771. records.Insert(newRecord);
  772. recordCount++;
  773. suspendEvents = false;
  774. int newRecordIndex = GetIndex (newRecord);
  775. if(oldRecordIndex == newRecordIndex) { // if the position is the same
  776. OnListChanged (ListChangedType.ItemChanged, newRecordIndex, oldRecordIndex); // be carefull remove oldrecord index if needed
  777. }
  778. else {
  779. if (oldRecordIndex == -1) {
  780. MaintainDataView(ListChangedType.ItemAdded, newRecord, false);
  781. OnListChanged(ListChangedType.ItemAdded, GetIndex(newRecord)); // oldLocation would be -1
  782. }
  783. else {
  784. OnListChanged (ListChangedType.ItemMoved, newRecordIndex, oldRecordIndex);
  785. }
  786. }
  787. }
  788. }
  789. else {
  790. ApplyChangeAction(oldRecord, oldAction, GetReplaceAction(oldOldState));
  791. ApplyChangeAction(newRecord, newAction, GetReplaceAction(newOldState));
  792. }
  793. }
  794. internal DataTable Table {
  795. get {
  796. return table;
  797. }
  798. }
  799. private void GetUniqueKeyValues(List<object[]> list, int curNodeId) {
  800. if (curNodeId != IndexTree.NIL) {
  801. GetUniqueKeyValues(list, records.Left(curNodeId));
  802. int record = records.Key(curNodeId);
  803. object[] element = new object[IndexFields.Length]; // number of columns in PK
  804. for (int j = 0; j < element.Length; ++j) {
  805. element[j] = IndexFields[j].Column[record];
  806. }
  807. list.Add(element);
  808. GetUniqueKeyValues(list, records.Right(curNodeId));
  809. }
  810. }
  811. internal static int IndexOfReference<T>(List<T> list, T item) where T : class {
  812. if (null != list) {
  813. for (int i = 0; i < list.Count; ++i) {
  814. if (Object.ReferenceEquals(list[i], item)) {
  815. return i;
  816. }
  817. }
  818. }
  819. return -1;
  820. }
  821. internal static bool ContainsReference<T>(List<T> list, T item) where T : class {
  822. return (0 <= IndexOfReference(list, item));
  823. }
  824. }
  825. internal sealed class Listeners<TElem> where TElem : class
  826. {
  827. private readonly List<TElem> listeners;
  828. private readonly Func<TElem, bool> filter;
  829. private readonly int ObjectID;
  830. private int _listenerReaderCount;
  831. /// <summary>Wish this was defined in mscorlib.dll instead of System.Core.dll</summary>
  832. internal delegate void Action<T1, T2, T3, T4>(T1 arg1, T2 arg2, T3 arg3, T4 arg4);
  833. /// <summary>Wish this was defined in mscorlib.dll instead of System.Core.dll</summary>
  834. internal delegate TResult Func<T1, TResult>(T1 arg1);
  835. internal Listeners(int ObjectID, Func<TElem, bool> notifyFilter) {
  836. listeners = new List<TElem>();
  837. filter = notifyFilter;
  838. this.ObjectID = ObjectID;
  839. _listenerReaderCount = 0;
  840. }
  841. internal bool HasListeners {
  842. get { return (0 < listeners.Count); }
  843. }
  844. /// <remarks>Only call from inside a lock</remarks>
  845. internal void Add(TElem listener) {
  846. Debug.Assert(null != listener, "null listener");
  847. Debug.Assert(!Index.ContainsReference(listeners, listener), "already contains reference");
  848. listeners.Add(listener);
  849. }
  850. internal int IndexOfReference(TElem listener) {
  851. return Index.IndexOfReference(listeners, listener);
  852. }
  853. /// <remarks>Only call from inside a lock</remarks>
  854. internal void Remove(TElem listener) {
  855. Debug.Assert(null != listener, "null listener");
  856. int index = IndexOfReference(listener);
  857. Debug.Assert(0 <= index, "listeners don't contain listener");
  858. listeners[index] = null;
  859. if (0 == _listenerReaderCount) {
  860. listeners.RemoveAt(index);
  861. listeners.TrimExcess();
  862. }
  863. }
  864. /// <summary>
  865. /// Write operation which means user must control multi-thread and we can assume single thread
  866. /// </summary>
  867. internal void Notify<T1, T2, T3>(T1 arg1, T2 arg2, T3 arg3, Action<TElem, T1, T2, T3> action) {
  868. Debug.Assert(null != action, "no action");
  869. Debug.Assert(0 <= _listenerReaderCount, "negative _listEventCount");
  870. int count = listeners.Count;
  871. if (0 < count) {
  872. int nullIndex = -1;
  873. // protect against listeners shrinking via Remove
  874. _listenerReaderCount++;
  875. try {
  876. // protect against listeners growing via Add since new listeners will already have the Notify in progress
  877. for (int i = 0; i < count; ++i) {
  878. // protect against listener being set to null (instead of being removed)
  879. TElem listener = listeners[i];
  880. if (filter(listener)) {
  881. // perform the action on each listener
  882. // some actions may throw an exception blocking remaning listeners from being notified (just like events)
  883. action(listener, arg1, arg2, arg3);
  884. }
  885. else {
  886. listeners[i] = null;
  887. nullIndex = i;
  888. }
  889. }
  890. }
  891. finally {
  892. _listenerReaderCount--;
  893. }
  894. if (0 == _listenerReaderCount) {
  895. RemoveNullListeners(nullIndex);
  896. }
  897. }
  898. }
  899. private void RemoveNullListeners(int nullIndex) {
  900. Debug.Assert((-1 == nullIndex) || (null == listeners[nullIndex]), "non-null listener");
  901. Debug.Assert(0 == _listenerReaderCount, "0 < _listenerReaderCount");
  902. for (int i = nullIndex; 0 <= i; --i) {
  903. if (null == listeners[i]) {
  904. listeners.RemoveAt(i);
  905. }
  906. }
  907. }
  908. }
  909. }