Update Reference Sources to .NET Framework 4.6.1
[mono.git] / mcs / class / referencesource / System.Data.Entity / System / Data / Query / ResultAssembly / BridgeDataRecord.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="BridgeDataRecord.cs" company="Microsoft">
3 //      Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 // @owner  [....]
6 // @backupOwner [....]
7 //---------------------------------------------------------------------
8
9 namespace System.Data.Query.ResultAssembly {
10
11     using System.Collections;
12     using System.Collections.Generic;
13     using System.ComponentModel;
14     using System.Data;
15     using System.Data.Common;
16     using System.Data.Common.CommandTrees;
17     using System.Data.Common.Internal.Materialization;
18     using System.Data.Metadata.Edm;
19     using System.Data.Query.PlanCompiler;
20     using System.Diagnostics;
21     using System.Text;
22     using System.Threading;
23
24     /// <summary>
25     /// DbDataRecord functionality for the bridge.
26     /// </summary>
27     sealed internal class BridgeDataRecord : DbDataRecord, IExtendedDataRecord {
28
29         #region state
30
31         /// <summary>
32         /// How deep down the hierarchy are we?
33         /// </summary>
34         internal readonly int Depth;
35
36         /// <summary>
37         /// Where the data comes from
38         /// </summary>
39         private readonly Shaper<RecordState> Shaper;
40
41         /// <summary>
42         /// The current record that we're responsible for; this will change from row to row
43         /// on the source data reader.  Will be set to null when parent the enumerator has  
44         /// returned false.
45         /// </summary>
46         private RecordState _source;
47
48         /// <summary>
49         /// Current state of the record; 
50         /// </summary>
51         private Status _status;
52         private enum Status {
53             Open = 0,
54             ClosedImplicitly = 1,
55             ClosedExplicitly = 2,
56         };
57
58         /// <summary>
59         /// the column ordinal of the last column read, used to enforce sequential access
60         /// </summary>
61         private int _lastColumnRead;
62
63         /// <summary>
64         /// the last data offset of a read returned, used to enforce sequential access
65         /// </summary>
66         private long _lastDataOffsetRead;
67
68         /// <summary>
69         /// the last ordinal that IsDBNull was called for; used to avoid re-reading the value; 
70         /// </summary>
71         private int _lastOrdinalCheckedForNull;
72
73         /// <summary>
74         /// value, of the last column that IsDBNull was called for; used to avoid re-reading the value; 
75         /// </summary>
76         private object _lastValueCheckedForNull;
77         
78         /// <summary>
79         /// Set to the current data record when we hand them out.  (For data reader columns,
80         /// we use it's attached data record) The Close, GetValue and Read methods ensures 
81         /// that this is implicitly closed when we move past it.
82         /// </summary>
83         private BridgeDataReader _currentNestedReader;
84         private BridgeDataRecord _currentNestedRecord;
85
86         #endregion
87
88         #region constructors
89
90         internal BridgeDataRecord(Shaper<RecordState> shaper, int depth)
91             : base() {
92             Debug.Assert(null != shaper, "null shaper?");
93             Shaper = shaper;
94             Depth = depth;
95             // Rest of state is set through the SetRecordSource method.
96         }
97
98         #endregion
99
100         #region state management
101
102         /// <summary>
103         /// Called by our owning datareader when it is explicitly closed; will
104         /// not be called for nested structures, they go through the ClosedImplicitly.
105         /// path instead.
106         /// </summary>
107         internal void CloseExplicitly() {
108             _status = Status.ClosedExplicitly;
109             _source = null; // can't have data any longer once we're closed.
110             CloseNestedObjectImplicitly();
111         }
112
113         /// <summary>
114         /// Called by our parent object to ensure that we're marked as implicitly 
115         /// closed;  will not be called for root level data readers.
116         /// </summary>
117         internal void CloseImplicitly() {
118             _status = Status.ClosedImplicitly;
119             _source = null; // can't have data any longer once we're closed.
120             CloseNestedObjectImplicitly();
121         }
122
123         /// <summary>
124         /// Ensure that whatever column we're currently processing is implicitly closed;
125         /// </summary>
126         private void CloseNestedObjectImplicitly() {
127             // it would be nice to use Interlocked.Exchange to avoid multi-thread `race condition risk
128             // when the the bridge is being misused by the user accessing it with multiple threads.
129             // but this is called frequently enough to have a performance impact
130             BridgeDataRecord currentNestedRecord = _currentNestedRecord;
131             if (null != currentNestedRecord) {
132                 _currentNestedRecord = null;
133                 currentNestedRecord.CloseImplicitly();
134             }
135             BridgeDataReader currentNestedReader = _currentNestedReader;
136             if (null != currentNestedReader) {
137                 _currentNestedReader = null;
138                 currentNestedReader.CloseImplicitly();
139             }
140         }
141
142         /// <summary>
143         /// Should be called after each Read on the data reader.
144         /// </summary>
145         internal void SetRecordSource(RecordState newSource, bool hasData) {
146             Debug.Assert(null == _currentNestedRecord, "didn't close the nested record?");
147             Debug.Assert(null == _currentNestedReader, "didn't close the nested reader?");
148
149             // A peculiar behavior of IEnumerator is that when MoveNext() returns
150             // false, the Current still points to the last value, which is not 
151             // what we really want to reflect here.
152             if (hasData) {
153                 Debug.Assert(null != newSource, "hasData but null newSource?"); // this shouldn't happen...
154                 _source = newSource;
155             }
156             else {
157                 _source = null;
158             }
159             _status = Status.Open;
160
161             _lastColumnRead = -1;
162             _lastDataOffsetRead = -1;
163             _lastOrdinalCheckedForNull = -1;
164             _lastValueCheckedForNull = null;
165         }
166
167         #endregion
168
169         #region assertion helpers
170
171         /// <summary>
172         /// Ensures that the reader is actually open, and throws an exception if not
173         /// </summary>
174         private void AssertReaderIsOpen() {
175             if (IsExplicitlyClosed) {
176                 throw EntityUtil.ClosedDataReaderError();
177             }
178             if (IsImplicitlyClosed) {
179                 throw EntityUtil.ImplicitlyClosedDataReaderError();
180             }
181         }
182
183         /// <summary>
184         /// Helper method.
185         /// </summary>
186         private void AssertReaderIsOpenWithData() {
187             AssertReaderIsOpen();
188
189             if (!HasData) {
190                 throw EntityUtil.NoData();
191             }
192         }
193
194         /// <summary>
195         /// Ensures that sequential access rules are being obeyed for non-array
196         /// getter methods, throws the appropriate exception if not.  Also ensures
197         /// that the last column and array offset is set appropriately.
198         /// </summary>
199         /// <param name="ordinal"></param>
200         private void AssertSequentialAccess(int ordinal) {
201             Debug.Assert(null != _source, "null _source?"); // we should have already called AssertReaderIsOpen.
202
203             if (ordinal < 0 || ordinal >= _source.ColumnCount) {
204                 throw EntityUtil.ArgumentOutOfRange("ordinal");
205             }
206             if (_lastColumnRead >= ordinal) {
207                 throw EntityUtil.NonSequentialColumnAccess(ordinal, _lastColumnRead + 1);
208             }
209             _lastColumnRead = ordinal;
210             // SQLBUDT #442001 -- we need to mark things that are not using GetBytes/GetChars
211             //                    in a way that prevents them from being read a second time 
212             //                    using those methods.  Pointing past any potential data is
213             //                    how we do that.
214             _lastDataOffsetRead = long.MaxValue;
215         }
216
217         /// <summary>
218         /// Ensures that sequential access rules are being obeyed for array offset
219         /// getter methods, throws the appropriate exception if not.  Also ensures
220         /// that the last column and array offset is set appropriately.
221         /// </summary>
222         /// <param name="ordinal"></param>
223         /// <param name="dataOffset"></param>
224         /// <param name="methodName"></param>
225         private void AssertSequentialAccess(int ordinal, long dataOffset, string methodName) {
226             Debug.Assert(null != _source, "null _source?"); // we should have already called AssertReaderIsOpen.
227
228             if (ordinal < 0 || ordinal >= _source.ColumnCount) {
229                 throw EntityUtil.ArgumentOutOfRange("ordinal");
230             }
231             if (_lastColumnRead > ordinal || (_lastColumnRead == ordinal && _lastDataOffsetRead == long.MaxValue)) {
232                 throw EntityUtil.NonSequentialColumnAccess(ordinal, _lastColumnRead + 1);
233             }
234             if (_lastColumnRead == ordinal) {
235                 if (_lastDataOffsetRead >= dataOffset) {
236                     throw EntityUtil.NonSequentialArrayOffsetAccess(dataOffset, _lastDataOffsetRead + 1, methodName);
237                 }
238                 // _lastDataOffsetRead will be set by GetBytes/GetChars, since we need to set it
239                 // to the last offset that was actually read, which isn't necessarily what was 
240                 // requested.
241             }
242             else {
243                 // Doin' a new thang...
244                 _lastColumnRead = ordinal;
245                 _lastDataOffsetRead = -1;
246             }
247         }
248
249         /// <summary>
250         /// True when the record has data (SetRecordSource was called with true)
251         /// </summary>
252         internal bool HasData {
253             get {
254                 bool result = (_source != null);
255                 return result;
256             }
257         }
258
259         /// <summary>
260         /// True so long as we haven't been closed either implicity or explictly
261         /// </summary>
262         internal bool IsClosed {
263             get {
264                 return (_status != Status.Open);
265             }
266         }
267
268         /// <summary>
269         /// Determine whether we have been explicitly closed by our owning 
270         /// data reader; only data records that are responsible for processing 
271         /// data reader requests can be explicitly closed;
272         /// </summary>
273         internal bool IsExplicitlyClosed {
274             get {
275                 return (_status == Status.ClosedExplicitly);
276             }
277         }
278
279         /// <summary>
280         /// Determine whether the parent data reader or record moved on from
281         /// where we can be considered open, (because the consumer of the 
282         /// parent data reader/record called either the GetValue() or Read() 
283         /// methods on the parent);
284         /// </summary>
285         internal bool IsImplicitlyClosed {
286             get {
287                 return (_status == Status.ClosedImplicitly);
288             }
289         }
290         #endregion
291
292         #region metadata properties and methods
293
294         /// <summary>
295         /// implementation of DbDataRecord.DataRecordInfo property
296         /// </summary>
297         public DataRecordInfo DataRecordInfo {
298             get {
299                 AssertReaderIsOpen();
300                 DataRecordInfo result = _source.DataRecordInfo;
301                 return result;
302             }
303         }
304
305         /// <summary>
306         /// implementation of DbDataRecord.FieldCount property
307         /// </summary>
308         override public int FieldCount {
309             get {
310                 AssertReaderIsOpen();
311                 return _source.ColumnCount;
312             }
313         }
314
315         /// <summary>
316         /// Helper method to get the edm TypeUsage for the specified column;
317         /// 
318         /// If the column requested is a record, we'll pick up whatever the
319         /// current record says it is, otherwise we'll take whatever was stored
320         /// on our record state.
321         /// </summary>
322         /// <param name="ordinal"></param>
323         /// <returns></returns>
324         private TypeUsage GetTypeUsage(int ordinal) {
325             // Some folks are picky about the exception we throw
326             if (ordinal < 0 || ordinal >= _source.ColumnCount) {
327                 throw EntityUtil.ArgumentOutOfRange("ordinal");
328             }
329             TypeUsage result;
330
331             // 
332             RecordState recordState = _source.CurrentColumnValues[ordinal] as RecordState;
333             if (null != recordState) {
334                 result = recordState.DataRecordInfo.RecordType;
335             }
336             else {
337                 result = _source.GetTypeUsage(ordinal);
338             }
339             return result;
340         }
341
342         /// <summary>
343         /// implementation of DbDataRecord.GetDataTypeName() method
344         /// </summary>
345         /// <param name="ordinal"></param>
346         /// <returns></returns>
347         override public string GetDataTypeName(int ordinal) {
348             AssertReaderIsOpenWithData();
349             return TypeHelpers.GetFullName(GetTypeUsage(ordinal));
350         }
351
352         /// <summary>
353         /// implementation of DbDataRecord.GetFieldType() method
354         /// </summary>
355         /// <param name="ordinal"></param>
356         /// <returns></returns>
357         override public Type GetFieldType(int ordinal) {
358             AssertReaderIsOpenWithData();
359             return BridgeDataReader.GetClrTypeFromTypeMetadata(GetTypeUsage(ordinal));
360         }
361
362         /// <summary>
363         /// implementation of DbDataRecord.GetName() method
364         /// </summary>
365         /// <param name="ordinal"></param>
366         /// <returns></returns>        
367         override public string GetName(int ordinal) {
368             AssertReaderIsOpen();
369             return _source.GetName(ordinal);
370         }
371
372         /// <summary>
373         /// implementation of DbDataRecord.GetOrdinal() method
374         /// </summary>
375         /// <param name="name"></param>
376         /// <returns></returns>
377         override public int GetOrdinal(string name) {
378             AssertReaderIsOpen();
379             return _source.GetOrdinal(name);
380         }
381
382         #endregion
383
384         #region general getter methods and indexer properties
385
386         /// <summary>
387         /// implementation for DbDataRecord[ordinal] indexer property
388         /// </summary>
389         /// <param name="ordinal"></param>
390         /// <returns></returns>
391         override public object this[int ordinal] {
392             get {
393                 return GetValue(ordinal);
394             }
395         }
396
397         /// <summary>
398         /// implementation for DbDataRecord[name] indexer property
399         /// </summary>
400         /// <param name="name"></param>
401         /// <returns></returns>
402         override public object this[string name] {
403             get {
404                 return GetValue(GetOrdinal(name));
405             }
406         }
407
408         /// <summary>
409         /// implementation for DbDataRecord.GetValue() method
410         /// 
411         /// This method is used by most of the column getters on this
412         /// class to retrieve the value from the source reader.  Therefore,
413         /// it asserts all the good things, like that the reader is open,
414         /// and that it has data, and that you're not trying to circumvent
415         /// sequential access requirements.
416         /// </summary>
417         /// <param name="ordinal"></param>
418         /// <returns></returns>
419         override public Object GetValue(int ordinal) {
420             AssertReaderIsOpenWithData();
421             AssertSequentialAccess(ordinal);
422
423             object result = null;
424
425             if (ordinal == _lastOrdinalCheckedForNull) {
426                 result = _lastValueCheckedForNull;
427             }
428             else {
429                 _lastOrdinalCheckedForNull = -1;
430                 _lastValueCheckedForNull = null;
431
432                 CloseNestedObjectImplicitly();
433
434                 result = _source.CurrentColumnValues[ordinal];
435
436                 // If we've got something that's nested, then make sure we
437                 // update the current nested record with it so we can be certain
438                 // to close it implicitly when we move past it.
439                 if (_source.IsNestedObject(ordinal)) {
440                     result = GetNestedObjectValue(result);
441                 }
442             }
443             return result;
444         }
445
446         /// <summary>
447         /// For nested objects (records/readers) we have a bit more work to do; this
448         /// method extracts it all out from the main GetValue method so it doesn't 
449         /// have to be so big.
450         /// </summary>
451         /// <param name="result"></param>
452         /// <returns></returns>
453         private object GetNestedObjectValue(object result) {
454             if (result != DBNull.Value) {
455                 RecordState recordState = result as RecordState;
456                 if (null != recordState) {
457                     if (recordState.IsNull) {
458                         result = DBNull.Value;
459                     }
460                     else {
461                         BridgeDataRecord nestedRecord = new BridgeDataRecord(Shaper, Depth + 1);
462                         nestedRecord.SetRecordSource(recordState, true);
463                         result = nestedRecord;
464                         _currentNestedRecord = nestedRecord;
465                         _currentNestedReader = null;
466                     }
467                 }
468                 else {
469                     Coordinator<RecordState> coordinator = result as Coordinator<RecordState>;
470                     if (null != coordinator) {
471                         BridgeDataReader nestedReader = new BridgeDataReader(Shaper, coordinator.TypedCoordinatorFactory, Depth + 1, nextResultShaperInfos: null);
472                         result = nestedReader;
473                         _currentNestedRecord = null;
474                         _currentNestedReader = nestedReader;
475                     }
476                     else {
477                         Debug.Fail("unexpected type of nested object result: " + result.GetType().ToString());
478                     }
479                 }
480             }
481             return result;
482         }
483
484         /// <summary>
485         /// implementation for DbDataRecord.GetValues() method
486         /// </summary>
487         /// <param name="values"></param>
488         /// <returns></returns>
489         override public int GetValues(object[] values) {
490             EntityUtil.CheckArgumentNull(values, "values");
491
492             int copy = Math.Min(values.Length, FieldCount);
493             for (int i = 0; i < copy; ++i) {
494                 values[i] = GetValue(i);
495             }
496             return copy;
497         }
498
499         #endregion
500
501         #region simple scalar value getter methods
502
503         /// <summary>
504         /// implementation of DbDataRecord.GetBoolean() method
505         /// </summary>
506         /// <param name="ordinal"></param>
507         /// <returns></returns>
508         override public bool GetBoolean(int ordinal) {
509             return (bool)GetValue(ordinal);
510         }
511
512         /// <summary>
513         /// implementation of DbDataRecord.GetByte() method
514         /// </summary>
515         /// <param name="ordinal"></param>
516         /// <returns></returns>
517         override public byte GetByte(int ordinal) {
518             return (byte)GetValue(ordinal);
519         }
520
521         /// <summary>
522         /// implementation of DbDataRecord.GetChar() method
523         /// </summary>
524         /// <param name="ordinal"></param>
525         /// <returns></returns>
526         override public char GetChar(int ordinal) {
527             return (char)GetValue(ordinal);
528         }
529
530         /// <summary>
531         /// implementation of DbDataRecord.GetDateTime() method
532         /// </summary>
533         /// <param name="ordinal"></param>
534         /// <returns></returns>
535         override public DateTime GetDateTime(int ordinal) {
536             return (DateTime)GetValue(ordinal);
537         }
538
539         /// <summary>
540         /// implementation of DbDataRecord.GetDecimal() method
541         /// </summary>
542         /// <param name="ordinal"></param>
543         /// <returns></returns>
544         override public Decimal GetDecimal(int ordinal) {
545             return (Decimal)GetValue(ordinal);
546         }
547
548         /// <summary>
549         /// implementation of DbDataRecord.GetDouble() method
550         /// </summary>
551         /// <param name="ordinal"></param>
552         /// <returns></returns>
553         override public double GetDouble(int ordinal) {
554             return (double)GetValue(ordinal);
555         }
556
557         /// <summary>
558         /// implementation of DbDataRecord.GetFloat() method
559         /// </summary>
560         /// <param name="ordinal"></param>
561         /// <returns></returns>
562         override public float GetFloat(int ordinal) {
563             return (float)GetValue(ordinal);
564         }
565
566         /// <summary>
567         /// implementation of DbDataRecord.GetGuid() method
568         /// </summary>
569         /// <param name="ordinal"></param>
570         /// <returns></returns>
571         override public Guid GetGuid(int ordinal) {
572             return (Guid)GetValue(ordinal);
573         }
574
575         /// <summary>
576         /// implementation of DbDataRecord.GetInt16() method
577         /// </summary>
578         /// <param name="ordinal"></param>
579         /// <returns></returns>
580         override public Int16 GetInt16(int ordinal) {
581             return (Int16)GetValue(ordinal);
582         }
583
584         /// <summary>
585         /// implementation of DbDataRecord.GetInt32() method
586         /// </summary>
587         /// <param name="ordinal"></param>
588         /// <returns></returns>
589         override public Int32 GetInt32(int ordinal) {
590             return (Int32)GetValue(ordinal);
591         }
592
593         /// <summary>
594         /// implementation of DbDataRecord.GetInt64() method
595         /// </summary>
596         /// <param name="ordinal"></param>
597         /// <returns></returns>
598         override public Int64 GetInt64(int ordinal) {
599             return (Int64)GetValue(ordinal);
600         }
601
602         /// <summary>
603         /// implementation of DbDataRecord.GetString() method
604         /// </summary>
605         /// <param name="ordinal"></param>
606         /// <returns></returns>
607         override public String GetString(int ordinal) {
608             return (String)GetValue(ordinal);
609         }
610
611         /// <summary>
612         /// implementation of DbDataRecord.IsDBNull() method
613         /// </summary>
614         /// <param name="ordinal"></param>
615         /// <returns></returns>
616         override public bool IsDBNull(int ordinal) {
617             // This seems like a hack, but the the problem is that I need 
618             // to make sure I don't monkey with caching things, and if I
619             // call IsDBNull directly on the store reader, I'll potentially
620             // lose data because I'm expecting SequentialAccess rules.
621
622             object columnValue = GetValue(ordinal);
623
624             // Need to backup one because we technically didn't read the
625             // value yet but the GetValue method advanced our pointer to
626             // what the value was.  Another hack, but it's way less code
627             // than trying to avoid advancing to begin with.
628             _lastColumnRead--;
629             _lastDataOffsetRead = -1;
630
631             // So as to avoid reconstructing nested records, readers, and
632             // rereading data from the iterator source cache, we just cache
633             // the value we read and the ordinal it came from, so if someone
634             // is doing the right thing(TM) and calling IsDBNull before calling
635             // GetValue, we won't construct another one.
636             _lastValueCheckedForNull = columnValue;
637             _lastOrdinalCheckedForNull = ordinal;
638
639             bool result = (DBNull.Value == columnValue);
640
641             return result;
642         }
643
644         #endregion
645
646         #region array scalar value getter methods
647
648         /// <summary>
649         /// implementation for DbDataRecord.GetBytes() method
650         /// </summary>
651         /// <param name="ordinal"></param>
652         /// <param name="dataOffset"></param>
653         /// <param name="buffer"></param>
654         /// <param name="bufferOffset"></param>
655         /// <param name="length"></param>
656         /// <returns></returns>
657         override public long GetBytes(int ordinal, long dataOffset, byte[] buffer, int bufferOffset, int length) {
658             AssertReaderIsOpenWithData();
659             AssertSequentialAccess(ordinal, dataOffset, "GetBytes");
660
661             long result = _source.GetBytes(ordinal, dataOffset, buffer, bufferOffset, length);
662
663             if (buffer != null) {
664                 _lastDataOffsetRead = dataOffset + result - 1; // just what was read, nothing more.
665             }
666             return result;
667         }
668
669         /// <summary>
670         /// implementation for DbDataRecord.GetChars() method
671         /// </summary>
672         /// <param name="ordinal"></param>
673         /// <param name="dataOffset"></param>
674         /// <param name="buffer"></param>
675         /// <param name="bufferOffset"></param>
676         /// <param name="length"></param>
677         /// <returns></returns>
678         override public long GetChars(int ordinal, long dataOffset, char[] buffer, int bufferOffset, int length) {
679             AssertReaderIsOpenWithData();
680             AssertSequentialAccess(ordinal, dataOffset, "GetChars");
681
682             long result = _source.GetChars(ordinal, dataOffset, buffer, bufferOffset, length);
683
684             if (buffer != null) {
685                 _lastDataOffsetRead = dataOffset + result - 1; // just what was read, nothing more.
686             }
687             return result;
688         }
689
690         #endregion
691
692         #region complex type getters
693
694         /// <summary>
695         /// implementation for DbDataRecord.GetData() method
696         /// </summary>
697         /// <param name="ordinal"></param>
698         /// <returns></returns>
699         override protected DbDataReader GetDbDataReader(int ordinal) {
700             return (DbDataReader)GetValue(ordinal);
701         }
702
703         /// <summary>
704         /// implementation for DbDataRecord.GetDataRecord() method
705         /// </summary>
706         /// <param name="ordinal"></param>
707         /// <returns></returns>
708         public DbDataRecord GetDataRecord(int ordinal) {
709             return (DbDataRecord)GetValue(ordinal);
710         }
711
712         /// <summary>
713         /// Used to return a nested result
714         /// </summary>
715         /// <param name="ordinal"></param>
716         /// <returns></returns>
717         public DbDataReader GetDataReader(int ordinal) {
718             return this.GetDbDataReader(ordinal);
719         }
720
721         #endregion
722     }
723 }