DSInternals/Src/Microsoft.Database.Isam/ColumnAccessor.cs

422 lines
15 KiB
C#

// ---------------------------------------------------------------------------
// <copyright file="ColumnAccessor.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------
// <summary>
// </summary>
// ---------------------------------------------------------------------
namespace Microsoft.Database.Isam
{
using System;
using System.Collections;
using Microsoft.Isam.Esent.Interop;
/// <summary>
/// Wraps a JET_TABLEID with indexed accessors
/// </summary>
public class ColumnAccessor : IEnumerable
{
/// <summary>
/// The underlying session.
/// </summary>
private readonly IsamSession isamSession;
/// <summary>
/// The underlying tableid.
/// </summary>
private readonly JET_TABLEID tableid;
/// <summary>
/// The grbits to use for retrieving the column.
/// </summary>
private readonly RetrieveColumnGrbit grbit;
/// <summary>
/// The underlying cursor.
/// </summary>
private Cursor cursor;
/// <summary>
/// The Update Identifier.
/// </summary>
private long updateID = 0;
/// <summary>
/// Initializes a new instance of the <see cref="ColumnAccessor" /> class.
/// </summary>
/// <param name="cursor">The cursor.</param>
/// <param name="isamSession">The session.</param>
/// <param name="tableid">The tableid.</param>
/// <param name="grbit">The grbit.</param>
internal ColumnAccessor(Cursor cursor, IsamSession isamSession, JET_TABLEID tableid, RetrieveColumnGrbit grbit)
{
this.cursor = cursor;
this.isamSession = isamSession;
this.tableid = tableid;
this.grbit = grbit;
}
/// <summary>
/// Gets the update identifier.
/// </summary>
internal long UpdateID
{
get
{
return this.updateID;
}
}
/// <summary>
/// Accessor for itag 1 of a column, converted to/from an object
/// </summary>
/// <value>
/// The <see cref="System.Object"/>.
/// </value>
/// <param name="columnName">Name of the column.</param>
/// <returns>The value stored within.</returns>
public object this[string columnName]
{
get
{
return this.RetrieveColumn(columnName, 0);
}
set
{
this.SetColumn(columnName, 0, value);
}
}
/// <summary>
/// Accessor for any itag of a column, converted to/from an object
/// </summary>
/// <value>
/// The <see cref="System.Object"/>.
/// </value>
/// <param name="columnName">Name of the column.</param>
/// <param name="index">The index.</param>
/// <returns>The value stored within.</returns>
/// <remarks>
/// The index argument is 0-based, we add 1 to get the itag
/// </remarks>
public object this[string columnName, int index]
{
get
{
return this.RetrieveColumn(columnName, index);
}
set
{
this.SetColumn(columnName, index, value);
}
}
/// <summary>
/// Accessor for itag 1 of a column, converted to/from an object
/// </summary>
/// <value>
/// The <see cref="System.Object"/>.
/// </value>
/// <param name="columnid">The columnid.</param>
/// <returns>The value stored within.</returns>
public object this[Columnid columnid]
{
get
{
return this.RetrieveColumn(columnid.InteropColumnid, columnid.Coltyp, columnid.IsAscii, 0);
}
set
{
this.SetColumn(columnid.InteropColumnid, columnid.Coltyp, columnid.IsAscii, 0, value);
}
}
/// <summary>
/// Accessor for any itag of a column, converted to/from an object
/// </summary>
/// <value>
/// The <see cref="System.Object"/>.
/// </value>
/// <param name="columnid">The columnid.</param>
/// <param name="index">The index.</param>
/// <returns>The value stored within.</returns>
/// <remarks>
/// The index argument is 0-based, we add 1 to get the itag
/// </remarks>
public object this[Columnid columnid, int index]
{
get
{
return this.RetrieveColumn(columnid.InteropColumnid, columnid.Coltyp, columnid.IsAscii, index);
}
set
{
this.SetColumn(columnid.InteropColumnid, columnid.Coltyp, columnid.IsAscii, index, value);
}
}
/// <summary>
/// Calculates the size of the specified multivalue stored in the specified column.
/// </summary>
/// <param name="columnName">Name of the column.</param>
/// <param name="index">The index.</param>
/// <returns>Returns the size of the specified multivalue stored in the specified column.</returns>
public long SizeOf(string columnName, int index)
{
lock (this.isamSession)
{
this.cursor.CheckDisposed();
using (IsamTransaction trx = new IsamTransaction(this.isamSession, true))
{
return this.SizeOf(this.cursor.TableDefinition.Columns[columnName].Columnid, index);
}
}
}
/// <summary>
/// Calculates the size of the specified column.
/// </summary>
/// <param name="columnName">Name of the column.</param>
/// <returns>Returns the size of the specified column.</returns>
public long SizeOf(string columnName)
{
return this.SizeOf(columnName, 0);
}
/// <summary>
/// Calculates the size of the specified multivalue stored in the specified column.
/// </summary>
/// <param name="columnid">The columnid.</param>
/// <param name="index">The index.</param>
/// <returns>Returns the size of the specified multivalue stored in the specified column.</returns>
public long SizeOf(Columnid columnid, int index)
{
lock (this.isamSession)
{
this.cursor.CheckDisposed();
if ((this.grbit & RetrieveColumnGrbit.RetrieveCopy) == 0)
{
this.cursor.CheckRecord();
}
int itagSequence = index + 1;
int? size = Api.RetrieveColumnSize(
this.isamSession.Sesid,
this.tableid,
columnid.InteropColumnid,
itagSequence,
RetrieveColumnGrbit.None);
return size.GetValueOrDefault();
}
}
/// <summary>
/// Calculates the size of the specified column.
/// </summary>
/// <param name="columnid">The columnid.</param>
/// <returns>Returns the size of the specified column.</returns>
public long SizeOf(Columnid columnid)
{
return this.SizeOf(columnid, 0);
}
/// <summary>
/// Fetches an enumerator containing all the field values in the record.
/// </summary>
/// <returns>
/// Returns an enumerator containing all the field values in the record.
/// </returns>
/// <remarks>
/// This is the type safe version that may not work in other CLR
/// languages.
/// </remarks>
public RecordEnumerator GetEnumerator()
{
// return the enumerator for the field collection
return this.cursor.GetFields(this.grbit).GetEnumerator();
}
/// <summary>
/// Fetches an enumerator containing all the field values in the record
/// </summary>
/// <returns>
/// An <see cref="T:System.Collections.IEnumerator" /> object that can be used to iterate through the collection.
/// </returns>
/// <remarks>
/// This is the standard version that will work with other CLR
/// languages.
/// </remarks>
IEnumerator IEnumerable.GetEnumerator()
{
return (IEnumerator)this.GetEnumerator();
}
/// <summary>
/// Retrieves the column.
/// </summary>
/// <param name="columnName">Name of the column.</param>
/// <param name="index">The index.</param>
/// <returns>The value stored within.</returns>
private object RetrieveColumn(string columnName, int index)
{
lock (this.isamSession)
{
this.cursor.CheckDisposed();
ColumnDefinition columnDefinition = this.cursor.TableDefinition.Columns[columnName];
return this.RetrieveColumn(columnDefinition, index);
}
}
/// <summary>
/// Retrieves the column.
/// </summary>
/// <param name="columnDefinition">The column definition.</param>
/// <param name="index">The index.</param>
/// <returns>The value stored within.</returns>
private object RetrieveColumn(ColumnDefinition columnDefinition, int index)
{
lock (this.isamSession)
{
this.cursor.CheckDisposed();
using (IsamTransaction trx = new IsamTransaction(this.isamSession, true))
{
Columnid columnid = columnDefinition.Columnid;
return this.RetrieveColumn(columnid.InteropColumnid, columnid.Coltyp, columnid.IsAscii, index);
}
}
}
/// <summary>
/// Sets the column.
/// </summary>
/// <param name="columnName">Name of the column.</param>
/// <param name="index">The index.</param>
/// <param name="obj">The object.</param>
private void SetColumn(string columnName, int index, object obj)
{
lock (this.isamSession)
{
this.cursor.CheckDisposed();
// Note that this overload doesn't start a transaction, but assumes the
// implementation does the transaction.
ColumnDefinition columnDefinition = this.cursor.TableDefinition.Columns[columnName];
this.SetColumn(columnDefinition, index, obj);
}
}
/// <summary>
/// Sets the column.
/// </summary>
/// <param name="columnDefinition">The column definition.</param>
/// <param name="index">The index.</param>
/// <param name="obj">The object.</param>
private void SetColumn(ColumnDefinition columnDefinition, int index, object obj)
{
lock (this.isamSession)
{
this.cursor.CheckDisposed();
using (IsamTransaction trx = new IsamTransaction(this.isamSession, true))
{
Columnid columnid = columnDefinition.Columnid;
this.SetColumn(columnid.InteropColumnid, columnid.Coltyp, columnid.IsAscii, index, obj);
trx.Commit();
}
}
}
/// <summary>
/// Retrieves the column.
/// </summary>
/// <param name="columnid">The columnid.</param>
/// <param name="coltyp">The coltyp.</param>
/// <param name="isAscii">Whether a textual column is Ascii.</param>
/// <param name="index">The index.</param>
/// <returns>The value stored within.</returns>
/// <remarks>
/// Itags start at 1, so we add 1 to the index
/// </remarks>
private object RetrieveColumn(JET_COLUMNID columnid, JET_coltyp coltyp, bool isAscii, int index)
{
lock (this.isamSession)
{
this.cursor.CheckDisposed();
if ((this.grbit & RetrieveColumnGrbit.RetrieveCopy) == 0)
{
this.cursor.CheckRecord();
}
JET_RETINFO retinfo = new JET_RETINFO();
retinfo.ibLongValue = 0;
retinfo.itagSequence = index + 1;
byte[] bytes = Api.RetrieveColumn(this.isamSession.Sesid, this.tableid, columnid, this.grbit, retinfo);
object obj = Converter.ObjectFromBytes(coltyp, isAscii, bytes);
return obj;
}
}
/// <summary>
/// Sets the column.
/// </summary>
/// <param name="columnid">The columnid.</param>
/// <param name="coltyp">The coltyp.</param>
/// <param name="isAscii">Whether a textual column is Ascii.</param>
/// <param name="index">The index.</param>
/// <param name="obj">The object.</param>
/// <exception cref="System.InvalidOperationException">You may only update fields through Cursor.EditRecord.</exception>
/// <remarks>
/// Itags start at 1, so we add 1 to the index
/// </remarks>
private void SetColumn(JET_COLUMNID columnid, JET_coltyp coltyp, bool isAscii, int index, object obj)
{
lock (this.isamSession)
{
this.cursor.CheckDisposed();
if ((this.grbit & RetrieveColumnGrbit.RetrieveCopy) == 0)
{
this.cursor.CheckRecord();
throw new InvalidOperationException("You may only update fields through Cursor.EditRecord.");
}
// if this is a Sort or PreSortTemporary TT and we are setting
// an LV column then always attempt to use intrinsic LVs
SetColumnGrbit grbitSet = SetColumnGrbit.None;
if ((this.cursor.TableDefinition.Type == TableType.Sort
|| this.cursor.TableDefinition.Type == TableType.PreSortTemporary)
&& (coltyp == JET_coltyp.LongText || coltyp == JET_coltyp.LongBinary))
{
grbitSet = grbitSet | SetColumnGrbit.IntrinsicLV;
}
JET_SETINFO setinfo = new JET_SETINFO();
setinfo.ibLongValue = 0;
setinfo.itagSequence = index + 1;
byte[] bytes = Converter.BytesFromObject(coltyp, isAscii, obj);
int bytesLength = bytes == null ? 0 : bytes.Length;
Api.JetSetColumn(this.isamSession.Sesid, this.tableid, columnid, bytes, bytesLength, grbitSet, setinfo);
this.updateID++;
}
}
}
}