System.Data.SQLite
Check-in [599dfac7d8]
Not logged in

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Make it possible to customize the bound parameter and data reader value handling on a per-connection, per-database type name basis.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 599dfac7d8f47ca97be04458148258fab69daaba
User & Date: mistachkin 2016-06-20 20:50:05
Context
2016-06-21
17:13
Update test case to account for the newly added return code. check-in: 4f5d049647 user: mistachkin tags: trunk
2016-06-20
20:50
Make it possible to customize the bound parameter and data reader value handling on a per-connection, per-database type name basis. check-in: 599dfac7d8 user: mistachkin tags: trunk
20:45
Pickup the SQLite core library 3.13 docs from upstream. check-in: 34d41e04cf user: mistachkin tags: trunk
20:35
Fix compilation with the .NET Compact Framework. Closed-Leaf check-in: 3ab3245ae4 user: mistachkin tags: customDataTypes
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to Doc/Extra/Provider/version.html.

    44     44       <div id="mainBody">
    45     45       <h1 class="heading">Version History</h1>
    46     46       <p><b>1.0.102.0 - June XX, 2016 <font color="red">(release scheduled)</font></b></p>
    47     47       <ul>
    48     48         <li>Updated to <a href="https://www.sqlite.org/releaselog/3_13_0.html">SQLite 3.13.0</a>.</li>
    49     49         <li>Update the SQLiteConnection.EnableExtensions method to make use of the new SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION option, when available.&nbsp;<b>** Potentially Incompatible Change **</b></li>
    50     50         <li>Prevent the SQLiteCommand.ExecuteScalar method from throwing an exception when there are no result columns.&nbsp;<b>** Potentially Incompatible Change **</b></li>
           51  +      <li>Support per-connection customization for binding parameters and reading values, based on the database type name.</li>
           52  +      <li>Add TypeName property to the SQLiteParameter class.</li>
    51     53         <li>Add VerifyOnly method to the SQLiteCommand class.</li>
    52     54         <li>Add IsReadOnly method to the SQLiteConnection class.</li>
    53     55       </ul>
    54     56       <p><b>1.0.101.0 - April 19, 2016</b></p>
    55     57       <ul>
    56     58         <li>Updated to <a href="https://www.sqlite.org/releaselog/3_12_2.html">SQLite 3.12.2</a>.</li>
    57     59         <li>Add binary package release for Mono on POSIX.</li>

Changes to Setup/data/verify.lst.

   846    846     Tests/tkt-e1b2e0f769.eagle
   847    847     Tests/tkt-e30b820248.eagle
   848    848     Tests/tkt-e47b3d8346.eagle
   849    849     Tests/tkt-ef2216192d.eagle
   850    850     Tests/tkt-f2c47a01eb.eagle
   851    851     Tests/tkt-f8dbab8baf.eagle
   852    852     Tests/tkt-fe50b8c2e8.eagle
          853  +  Tests/types.eagle
   853    854     Tests/version.eagle
   854    855     Tests/vtab.eagle
   855    856     tools/
   856    857     tools/install/
   857    858     tools/install/Installer.2005.csproj
   858    859     tools/install/Installer.2008.csproj
   859    860     tools/install/Installer.2010.csproj

Changes to System.Data.SQLite/SQLiteBase.cs.

  1156   1156   
  1157   1157         /// <summary>
  1158   1158         /// When returning column values as a <see cref="String" />, skip
  1159   1159         /// verifying their affinity.
  1160   1160         /// </summary>
  1161   1161         NoVerifyTextAffinity = 0x200000000,
  1162   1162   
         1163  +      /// <summary>
         1164  +      /// Enable using per-connection mappings between type names and
         1165  +      /// <see cref="SQLiteBindValueCallback" /> values.  Also see the
         1166  +      /// <see cref="SQLiteConnection.ClearTypeCallbacks" />,
         1167  +      /// <see cref="SQLiteConnection.TryGetTypeCallbacks" />, and
         1168  +      /// <see cref="SQLiteConnection.SetTypeCallbacks" /> methods.
         1169  +      /// </summary>
         1170  +      UseConnectionBindValueCallbacks = 0x400000000,
         1171  +
         1172  +      /// <summary>
         1173  +      /// Enable using per-connection mappings between type names and
         1174  +      /// <see cref="SQLiteReadValueCallback" /> values.  Also see the
         1175  +      /// <see cref="SQLiteConnection.ClearTypeCallbacks" />,
         1176  +      /// <see cref="SQLiteConnection.TryGetTypeCallbacks" />, and
         1177  +      /// <see cref="SQLiteConnection.SetTypeCallbacks" /> methods.
         1178  +      /// </summary>
         1179  +      UseConnectionReadValueCallbacks = 0x800000000,
         1180  +
         1181  +      /// <summary>
         1182  +      /// If the database type name has not been explicitly set for the
         1183  +      /// parameter specified, fallback to using the parameter name.
         1184  +      /// </summary>
         1185  +      UseParameterNameForTypeName = 0x1000000000,
         1186  +
         1187  +      /// <summary>
         1188  +      /// If the database type name has not been explicitly set for the
         1189  +      /// parameter specified, fallback to using the database type name
         1190  +      /// associated with the <see cref="DbType" /> value.
         1191  +      /// </summary>
         1192  +      UseParameterDbTypeForTypeName = 0x2000000000,
         1193  +
  1163   1194         /// <summary>
  1164   1195         /// When binding parameter values or returning column values, always
  1165   1196         /// treat them as though they were plain text (i.e. no numeric,
  1166   1197         /// date/time, or other conversions should be attempted).
  1167   1198         /// </summary>
  1168   1199         BindAndGetAllAsText = BindAllAsText | GetAllAsText,
  1169   1200   
................................................................................
  1187   1218         /// date/time, or other conversions should be attempted) and always
  1188   1219         /// use the invariant culture when converting their values to strings
  1189   1220         /// or from strings.
  1190   1221         /// </summary>
  1191   1222         ConvertAndBindAndGetAllAsInvariantText = BindAndGetAllAsText |
  1192   1223                                                  ConvertAndBindInvariantText,
  1193   1224   
         1225  +      /// <summary>
         1226  +      /// Enables use of all per-connection value handling callbacks.
         1227  +      /// </summary>
         1228  +      UseConnectionAllValueCallbacks = UseConnectionBindValueCallbacks |
         1229  +                                       UseConnectionReadValueCallbacks,
         1230  +
         1231  +      /// <summary>
         1232  +      /// Enables use of all applicable <see cref="SQLiteParameter" />
         1233  +      /// properties as fallbacks for the database type name.
         1234  +      /// </summary>
         1235  +      UseParameterAnythingForTypeName = UseParameterNameForTypeName |
         1236  +                                        UseParameterDbTypeForTypeName,
         1237  +
  1194   1238         /// <summary>
  1195   1239         /// Enable all logging.
  1196   1240         /// </summary>
  1197   1241   #if INTEROP_VIRTUAL_TABLE
  1198   1242         LogAll = LogPrepare | LogPreBind | LogBind |
  1199   1243                  LogCallbackException | LogBackup | LogModuleError |
  1200   1244                  LogModuleException,

Changes to System.Data.SQLite/SQLiteConnection.cs.

    17     17     using System.Reflection;
    18     18     using System.Runtime.InteropServices;
    19     19     using System.IO;
    20     20     using System.Text;
    21     21   
    22     22     /////////////////////////////////////////////////////////////////////////////////////////////////
    23     23   
           24  +  /// <summary>
           25  +  /// This class represents a single value to be returned
           26  +  /// from the <see cref="SQLiteDataReader" /> class via
           27  +  /// its <see cref="SQLiteDataReader.GetBoolean" />,
           28  +  /// <see cref="SQLiteDataReader.GetByte" />,
           29  +  /// <see cref="SQLiteDataReader.GetBytes" />,
           30  +  /// <see cref="SQLiteDataReader.GetChar" />,
           31  +  /// <see cref="SQLiteDataReader.GetChars" />,
           32  +  /// <see cref="SQLiteDataReader.GetDateTime" />,
           33  +  /// <see cref="SQLiteDataReader.GetDecimal" />,
           34  +  /// <see cref="SQLiteDataReader.GetDouble" />,
           35  +  /// <see cref="SQLiteDataReader.GetFloat" />,
           36  +  /// <see cref="SQLiteDataReader.GetGuid" />,
           37  +  /// <see cref="SQLiteDataReader.GetInt16" />,
           38  +  /// <see cref="SQLiteDataReader.GetInt32" />,
           39  +  /// <see cref="SQLiteDataReader.GetInt64" />,
           40  +  /// <see cref="SQLiteDataReader.GetString" />, or
           41  +  /// <see cref="SQLiteDataReader.GetValue" /> method.  If the value of the
           42  +  /// associated public field of this class is null upon returning from the
           43  +  /// callback, the null value will only be used if the return type for the
           44  +  /// <see cref="SQLiteDataReader" /> method called is not a value type.
           45  +  /// If the value to be returned from the <see cref="SQLiteDataReader" />
           46  +  /// method is unsuitable (e.g. null with a value type), an exception will
           47  +  /// be thrown.
           48  +  /// </summary>
           49  +  public sealed class SQLiteDataReaderValue
           50  +  {
           51  +      /// <summary>
           52  +      /// The value to be returned from the
           53  +      /// <see cref="SQLiteDataReader.GetBoolean" /> method -OR- null to
           54  +      /// indicate an error.
           55  +      /// </summary>
           56  +      public bool? BooleanValue;
           57  +
           58  +      /// <summary>
           59  +      /// The value to be returned from the
           60  +      /// <see cref="SQLiteDataReader.GetByte" /> method -OR- null to
           61  +      /// indicate an error.
           62  +      /// </summary>
           63  +      public byte? ByteValue;
           64  +
           65  +      /// <summary>
           66  +      /// The value to be returned from the
           67  +      /// <see cref="SQLiteDataReader.GetBytes" /> method.
           68  +      /// </summary>
           69  +      public byte[] BytesValue;
           70  +
           71  +      /// <summary>
           72  +      /// The value to be returned from the
           73  +      /// <see cref="SQLiteDataReader.GetChar" /> method -OR- null to
           74  +      /// indicate an error.
           75  +      /// </summary>
           76  +      public char? CharValue;
           77  +
           78  +      /// <summary>
           79  +      /// The value to be returned from the
           80  +      /// <see cref="SQLiteDataReader.GetChars" /> method.
           81  +      /// </summary>
           82  +      public char[] CharsValue;
           83  +
           84  +      /// <summary>
           85  +      /// The value to be returned from the
           86  +      /// <see cref="SQLiteDataReader.GetDateTime" /> method -OR- null to
           87  +      /// indicate an error.
           88  +      /// </summary>
           89  +      public DateTime? DateTimeValue;
           90  +
           91  +      /// <summary>
           92  +      /// The value to be returned from the
           93  +      /// <see cref="SQLiteDataReader.GetDecimal" /> method -OR- null to
           94  +      /// indicate an error.
           95  +      /// </summary>
           96  +      public decimal? DecimalValue;
           97  +
           98  +      /// <summary>
           99  +      /// The value to be returned from the
          100  +      /// <see cref="SQLiteDataReader.GetDouble" /> method -OR- null to
          101  +      /// indicate an error.
          102  +      /// </summary>
          103  +      public double? DoubleValue;
          104  +
          105  +      /// <summary>
          106  +      /// The value to be returned from the
          107  +      /// <see cref="SQLiteDataReader.GetFloat" /> method -OR- null to
          108  +      /// indicate an error.
          109  +      /// </summary>
          110  +      public float? FloatValue;
          111  +
          112  +      /// <summary>
          113  +      /// The value to be returned from the
          114  +      /// <see cref="SQLiteDataReader.GetGuid" /> method -OR- null to
          115  +      /// indicate an error.
          116  +      /// </summary>
          117  +      public Guid? GuidValue;
          118  +
          119  +      /// <summary>
          120  +      /// The value to be returned from the
          121  +      /// <see cref="SQLiteDataReader.GetInt16" /> method -OR- null to
          122  +      /// indicate an error.
          123  +      /// </summary>
          124  +      public short? Int16Value;
          125  +
          126  +      /// <summary>
          127  +      /// The value to be returned from the
          128  +      /// <see cref="SQLiteDataReader.GetInt32" /> method -OR- null to
          129  +      /// indicate an error.
          130  +      /// </summary>
          131  +      public int? Int32Value;
          132  +
          133  +      /// <summary>
          134  +      /// The value to be returned from the
          135  +      /// <see cref="SQLiteDataReader.GetInt64" /> method -OR- null to
          136  +      /// indicate an error.
          137  +      /// </summary>
          138  +      public long? Int64Value;
          139  +
          140  +      /// <summary>
          141  +      /// The value to be returned from the
          142  +      /// <see cref="SQLiteDataReader.GetString" /> method.
          143  +      /// </summary>
          144  +      public string StringValue;
          145  +
          146  +      /// <summary>
          147  +      /// The value to be returned from the
          148  +      /// <see cref="SQLiteDataReader.GetValue" /> method.
          149  +      /// </summary>
          150  +      public object Value;
          151  +  }
          152  +
          153  +  /////////////////////////////////////////////////////////////////////////////////////////////////
          154  +
          155  +  /// <summary>
          156  +  /// This class represents the parameters that are provided
          157  +  /// to the <see cref="SQLiteDataReader.GetBytes" /> and
          158  +  /// <see cref="SQLiteDataReader.GetChars" /> methods, with
          159  +  /// the exception of the column index (provided separately).
          160  +  /// </summary>
          161  +  public class SQLiteReadArrayEventArgs : EventArgs
          162  +  {
          163  +      #region Private Data
          164  +      /// <summary>
          165  +      /// Provides the underlying storage for the
          166  +      /// <see cref="DataOffset" /> property.
          167  +      /// </summary>
          168  +      private long dataOffset;
          169  +
          170  +      /// <summary>
          171  +      /// Provides the underlying storage for the
          172  +      /// <see cref="ByteBuffer" /> property.
          173  +      /// </summary>
          174  +      private byte[] byteBuffer;
          175  +
          176  +      /// <summary>
          177  +      /// Provides the underlying storage for the
          178  +      /// <see cref="CharBuffer" /> property.
          179  +      /// </summary>
          180  +      private char[] charBuffer;
          181  +
          182  +      /// <summary>
          183  +      /// Provides the underlying storage for the
          184  +      /// <see cref="BufferOffset" /> property.
          185  +      /// </summary>
          186  +      private int bufferOffset;
          187  +
          188  +      /// <summary>
          189  +      /// Provides the underlying storage for the
          190  +      /// <see cref="Length" /> property.
          191  +      /// </summary>
          192  +      private int length;
          193  +      #endregion
          194  +
          195  +      /////////////////////////////////////////////////////////////////////////
          196  +
          197  +      #region Private Constructors
          198  +      /// <summary>
          199  +      /// Constructs an instance of this class to pass into a user-defined
          200  +      /// callback associated with the <see cref="SQLiteDataReader.GetBytes" />
          201  +      /// method.
          202  +      /// </summary>
          203  +      /// <param name="dataOffset">
          204  +      /// The value that was originally specified for the "dataOffset"
          205  +      /// parameter to the <see cref="SQLiteDataReader.GetBytes" /> or
          206  +      /// <see cref="SQLiteDataReader.GetChars" /> methods.
          207  +      /// </param>
          208  +      /// <param name="byteBuffer">
          209  +      /// The value that was originally specified for the "buffer"
          210  +      /// parameter to the <see cref="SQLiteDataReader.GetBytes" />
          211  +      /// method.
          212  +      /// </param>
          213  +      /// <param name="bufferOffset">
          214  +      /// The value that was originally specified for the "bufferOffset"
          215  +      /// parameter to the <see cref="SQLiteDataReader.GetBytes" /> or
          216  +      /// <see cref="SQLiteDataReader.GetChars" /> methods.
          217  +      /// </param>
          218  +      /// <param name="length">
          219  +      /// The value that was originally specified for the "length"
          220  +      /// parameter to the <see cref="SQLiteDataReader.GetBytes" /> or
          221  +      /// <see cref="SQLiteDataReader.GetChars" /> methods.
          222  +      /// </param>
          223  +      internal SQLiteReadArrayEventArgs(
          224  +          long dataOffset,
          225  +          byte[] byteBuffer,
          226  +          int bufferOffset,
          227  +          int length
          228  +          )
          229  +      {
          230  +          this.dataOffset = dataOffset;
          231  +          this.byteBuffer = byteBuffer;
          232  +          this.bufferOffset = bufferOffset;
          233  +          this.length = length;
          234  +      }
          235  +
          236  +      /////////////////////////////////////////////////////////////////////////
          237  +
          238  +      /// <summary>
          239  +      /// Constructs an instance of this class to pass into a user-defined
          240  +      /// callback associated with the <see cref="SQLiteDataReader.GetChars" />
          241  +      /// method.
          242  +      /// </summary>
          243  +      /// <param name="dataOffset">
          244  +      /// The value that was originally specified for the "dataOffset"
          245  +      /// parameter to the <see cref="SQLiteDataReader.GetBytes" /> or
          246  +      /// <see cref="SQLiteDataReader.GetChars" /> methods.
          247  +      /// </param>
          248  +      /// <param name="charBuffer">
          249  +      /// The value that was originally specified for the "buffer"
          250  +      /// parameter to the <see cref="SQLiteDataReader.GetChars" />
          251  +      /// method.
          252  +      /// </param>
          253  +      /// <param name="bufferOffset">
          254  +      /// The value that was originally specified for the "bufferOffset"
          255  +      /// parameter to the <see cref="SQLiteDataReader.GetBytes" /> or
          256  +      /// <see cref="SQLiteDataReader.GetChars" /> methods.
          257  +      /// </param>
          258  +      /// <param name="length">
          259  +      /// The value that was originally specified for the "length"
          260  +      /// parameter to the <see cref="SQLiteDataReader.GetBytes" /> or
          261  +      /// <see cref="SQLiteDataReader.GetChars" /> methods.
          262  +      /// </param>
          263  +      internal SQLiteReadArrayEventArgs(
          264  +          long dataOffset,
          265  +          char[] charBuffer,
          266  +          int bufferOffset,
          267  +          int length
          268  +          )
          269  +      {
          270  +          this.dataOffset = dataOffset;
          271  +          this.charBuffer = charBuffer;
          272  +          this.bufferOffset = bufferOffset;
          273  +          this.length = length;
          274  +      }
          275  +      #endregion
          276  +
          277  +      /////////////////////////////////////////////////////////////////////////
          278  +
          279  +      #region Public Properties
          280  +      /// <summary>
          281  +      /// The value that was originally specified for the "dataOffset"
          282  +      /// parameter to the <see cref="SQLiteDataReader.GetBytes" /> or
          283  +      /// <see cref="SQLiteDataReader.GetChars" /> methods.
          284  +      /// </summary>
          285  +      public long DataOffset
          286  +      {
          287  +          get { return dataOffset; }
          288  +          set { dataOffset = value; }
          289  +      }
          290  +
          291  +      /////////////////////////////////////////////////////////////////////////
          292  +
          293  +      /// <summary>
          294  +      /// The value that was originally specified for the "buffer"
          295  +      /// parameter to the <see cref="SQLiteDataReader.GetBytes" />
          296  +      /// method.
          297  +      /// </summary>
          298  +      public byte[] ByteBuffer
          299  +      {
          300  +          get { return byteBuffer; }
          301  +      }
          302  +
          303  +      /////////////////////////////////////////////////////////////////////////
          304  +
          305  +      /// <summary>
          306  +      /// The value that was originally specified for the "buffer"
          307  +      /// parameter to the <see cref="SQLiteDataReader.GetChars" />
          308  +      /// method.
          309  +      /// </summary>
          310  +      public char[] CharBuffer
          311  +      {
          312  +          get { return charBuffer; }
          313  +      }
          314  +
          315  +      /////////////////////////////////////////////////////////////////////////
          316  +
          317  +      /// <summary>
          318  +      /// The value that was originally specified for the "bufferOffset"
          319  +      /// parameter to the <see cref="SQLiteDataReader.GetBytes" /> or
          320  +      /// <see cref="SQLiteDataReader.GetChars" /> methods.
          321  +      /// </summary>
          322  +      public int BufferOffset
          323  +      {
          324  +          get { return bufferOffset; }
          325  +          set { bufferOffset = value; }
          326  +      }
          327  +
          328  +      /////////////////////////////////////////////////////////////////////////
          329  +
          330  +      /// <summary>
          331  +      /// The value that was originally specified for the "length"
          332  +      /// parameter to the <see cref="SQLiteDataReader.GetBytes" /> or
          333  +      /// <see cref="SQLiteDataReader.GetChars" /> methods.
          334  +      /// </summary>
          335  +      public int Length
          336  +      {
          337  +          get { return length; }
          338  +          set { length = value; }
          339  +      }
          340  +      #endregion
          341  +  }
          342  +
          343  +  /////////////////////////////////////////////////////////////////////////////////////////////////
          344  +
          345  +  /// <summary>
          346  +  /// This class represents the parameters and return values for the
          347  +  /// <see cref="SQLiteDataReader.GetBoolean" />,
          348  +  /// <see cref="SQLiteDataReader.GetByte" />,
          349  +  /// <see cref="SQLiteDataReader.GetBytes" />,
          350  +  /// <see cref="SQLiteDataReader.GetChar" />,
          351  +  /// <see cref="SQLiteDataReader.GetChars" />,
          352  +  /// <see cref="SQLiteDataReader.GetDateTime" />,
          353  +  /// <see cref="SQLiteDataReader.GetDecimal" />,
          354  +  /// <see cref="SQLiteDataReader.GetDouble" />,
          355  +  /// <see cref="SQLiteDataReader.GetFloat" />,
          356  +  /// <see cref="SQLiteDataReader.GetGuid" />,
          357  +  /// <see cref="SQLiteDataReader.GetInt16" />,
          358  +  /// <see cref="SQLiteDataReader.GetInt32" />,
          359  +  /// <see cref="SQLiteDataReader.GetInt64" />,
          360  +  /// <see cref="SQLiteDataReader.GetString" />, and
          361  +  /// <see cref="SQLiteDataReader.GetValue" /> methods.
          362  +  /// </summary>
          363  +  public class SQLiteReadValueEventArgs : EventArgs
          364  +  {
          365  +      #region Private Data
          366  +      /// <summary>
          367  +      /// Provides the underlying storage for the
          368  +      /// <see cref="MethodName" /> property.
          369  +      /// </summary>
          370  +      private string methodName;
          371  +
          372  +      /// <summary>
          373  +      /// Provides the underlying storage for the
          374  +      /// <see cref="ArrayEventArgs" /> property.
          375  +      /// </summary>
          376  +      private SQLiteReadArrayEventArgs arrayEventArgs;
          377  +
          378  +      /// <summary>
          379  +      /// Provides the underlying storage for the
          380  +      /// <see cref="Value" /> property.
          381  +      /// </summary>
          382  +      private SQLiteDataReaderValue value;
          383  +      #endregion
          384  +
          385  +      /////////////////////////////////////////////////////////////////////////
          386  +
          387  +      #region Private Constructors
          388  +      /// <summary>
          389  +      /// Constructs a new instance of this class.  Depending on the method
          390  +      /// being called, the <paramref name="arrayEventArgs" /> and/or
          391  +      /// <paramref name="value" /> parameters may be null.
          392  +      /// </summary>
          393  +      /// <param name="methodName">
          394  +      /// The name of the <see cref="SQLiteDataReader" /> method that was
          395  +      /// responsible for invoking this callback.
          396  +      /// </param>
          397  +      /// <param name="arrayEventArgs">
          398  +      /// If the <see cref="SQLiteDataReader.GetBytes" /> or
          399  +      /// <see cref="SQLiteDataReader.GetChars" /> method is being called,
          400  +      /// this object will contain the array related parameters for that
          401  +      /// method.
          402  +      /// </param>
          403  +      /// <param name="value">
          404  +      /// This may be used by the callback to set the return value for the
          405  +      /// called <see cref="SQLiteDataReader" /> method.
          406  +      /// </param>
          407  +      internal SQLiteReadValueEventArgs(
          408  +          string methodName,
          409  +          SQLiteReadArrayEventArgs arrayEventArgs,
          410  +          SQLiteDataReaderValue value
          411  +          )
          412  +      {
          413  +          this.methodName = methodName;
          414  +          this.arrayEventArgs = arrayEventArgs;
          415  +          this.value = value;
          416  +      }
          417  +      #endregion
          418  +
          419  +      /////////////////////////////////////////////////////////////////////////
          420  +
          421  +      #region Public Properties
          422  +      /// <summary>
          423  +      /// The name of the <see cref="SQLiteDataReader" /> method that was
          424  +      /// responsible for invoking this callback.
          425  +      /// </summary>
          426  +      public string MethodName
          427  +      {
          428  +          get { return methodName; }
          429  +      }
          430  +
          431  +      /////////////////////////////////////////////////////////////////////////
          432  +
          433  +      /// <summary>
          434  +      /// If the <see cref="SQLiteDataReader.GetBytes" /> or
          435  +      /// <see cref="SQLiteDataReader.GetChars" /> method is being called,
          436  +      /// this object will contain the array related parameters for that
          437  +      /// method.
          438  +      /// </summary>
          439  +      public SQLiteReadArrayEventArgs ArrayEventArgs
          440  +      {
          441  +          get { return arrayEventArgs; }
          442  +      }
          443  +
          444  +      /////////////////////////////////////////////////////////////////////////
          445  +
          446  +      /// <summary>
          447  +      /// This may be used by the callback to set the return value for the
          448  +      /// called <see cref="SQLiteDataReader" /> method.
          449  +      /// </summary>
          450  +      public SQLiteDataReaderValue Value
          451  +      {
          452  +          get { return value; }
          453  +      }
          454  +      #endregion
          455  +  }
          456  +
          457  +  /////////////////////////////////////////////////////////////////////////////////////////////////
          458  +
          459  +  /// <summary>
          460  +  /// This represents a method that will be called in response to a request to
          461  +  /// bind a parameter to a command.  If an exception is thrown, it will cause
          462  +  /// the parameter binding operation to fail -AND- it will continue to unwind
          463  +  /// the call stack.
          464  +  /// </summary>
          465  +  /// <param name="convert">
          466  +  /// The <see cref="SQLiteConvert" /> instance in use.
          467  +  /// </param>
          468  +  /// <param name="command">
          469  +  /// The <see cref="SQLiteCommand" /> instance in use.
          470  +  /// </param>
          471  +  /// <param name="flags">
          472  +  /// The flags associated with the <see cref="SQLiteConnection" /> instance
          473  +  /// in use.
          474  +  /// </param>
          475  +  /// <param name="parameter">
          476  +  /// The <see cref="SQLiteParameter" /> instance being bound to the command.
          477  +  /// </param>
          478  +  /// <param name="typeName">
          479  +  /// The database type name associated with this callback.
          480  +  /// </param>
          481  +  /// <param name="index">
          482  +  /// The ordinal of the parameter being bound to the command.
          483  +  /// </param>
          484  +  /// <param name="userData">
          485  +  /// The data originally used when registering this callback.
          486  +  /// </param>
          487  +  /// <param name="complete">
          488  +  /// Non-zero if the default handling for the parameter binding call should
          489  +  /// be skipped (i.e. the parameter should not be bound at all).  Great care
          490  +  /// should be used when setting this to non-zero.
          491  +  /// </param>
          492  +  public delegate void SQLiteBindValueCallback(
          493  +      SQLiteConvert convert,
          494  +      SQLiteCommand command,
          495  +      SQLiteConnectionFlags flags,
          496  +      SQLiteParameter parameter,
          497  +      string typeName,
          498  +      int index,
          499  +      object userData,
          500  +      out bool complete
          501  +  );
          502  +
          503  +  /////////////////////////////////////////////////////////////////////////////////////////////////
          504  +
          505  +  /// <summary>
          506  +  /// This represents a method that will be called in response to a request
          507  +  /// to read a value from a data reader.  If an exception is thrown, it will
          508  +  /// cause the data reader operation to fail -AND- it will continue to unwind
          509  +  /// the call stack.
          510  +  /// </summary>
          511  +  /// <param name="convert">
          512  +  /// The <see cref="SQLiteConvert" /> instance in use.
          513  +  /// </param>
          514  +  /// <param name="dataReader">
          515  +  /// The <see cref="SQLiteDataReader" /> instance in use.
          516  +  /// </param>
          517  +  /// <param name="flags">
          518  +  /// The flags associated with the <see cref="SQLiteConnection" /> instance
          519  +  /// in use.
          520  +  /// </param>
          521  +  /// <param name="eventArgs">
          522  +  /// The parameter and return type data for the column being read from the
          523  +  /// data reader.
          524  +  /// </param>
          525  +  /// <param name="typeName">
          526  +  /// The database type name associated with this callback.
          527  +  /// </param>
          528  +  /// <param name="index">
          529  +  /// The zero based index of the column being read from the data reader.
          530  +  /// </param>
          531  +  /// <param name="userData">
          532  +  /// The data originally used when registering this callback.
          533  +  /// </param>
          534  +  /// <param name="complete">
          535  +  /// Non-zero if the default handling for the data reader call should be
          536  +  /// skipped.  If this is set to non-zero and the necessary return value
          537  +  /// is unavailable or unsuitable, an exception will be thrown.
          538  +  /// </param>
          539  +  public delegate void SQLiteReadValueCallback(
          540  +      SQLiteConvert convert,
          541  +      SQLiteDataReader dataReader,
          542  +      SQLiteConnectionFlags flags,
          543  +      SQLiteReadValueEventArgs eventArgs,
          544  +      string typeName,
          545  +      int index,
          546  +      object userData,
          547  +      out bool complete
          548  +  );
          549  +
          550  +  /////////////////////////////////////////////////////////////////////////////////////////////////
          551  +
          552  +  /// <summary>
          553  +  /// This class represents the custom data type handling callbacks
          554  +  /// for a single type name.
          555  +  /// </summary>
          556  +  public sealed class SQLiteTypeCallbacks
          557  +  {
          558  +      #region Private Data
          559  +      /// <summary>
          560  +      /// Provides the underlying storage for the
          561  +      /// <see cref="TypeName" /> property.
          562  +      /// </summary>
          563  +      private string typeName;
          564  +
          565  +      /// <summary>
          566  +      /// Provides the underlying storage for the
          567  +      /// <see cref="BindValueCallback" /> property.
          568  +      /// </summary>
          569  +      private SQLiteBindValueCallback bindValueCallback;
          570  +
          571  +      /// <summary>
          572  +      /// Provides the underlying storage for the
          573  +      /// <see cref="ReadValueCallback" /> property.
          574  +      /// </summary>
          575  +      private SQLiteReadValueCallback readValueCallback;
          576  +
          577  +      /// <summary>
          578  +      /// Provides the underlying storage for the
          579  +      /// <see cref="BindValueUserData" /> property.
          580  +      /// </summary>
          581  +      private object bindValueUserData;
          582  +
          583  +      /// <summary>
          584  +      /// Provides the underlying storage for the
          585  +      /// <see cref="ReadValueUserData" /> property.
          586  +      /// </summary>
          587  +      private object readValueUserData;
          588  +      #endregion
          589  +
          590  +      /////////////////////////////////////////////////////////////////////////
          591  +
          592  +      #region Private Constructors
          593  +      /// <summary>
          594  +      /// Constructs an instance of this class.
          595  +      /// </summary>
          596  +      /// <param name="bindValueCallback">
          597  +      /// The custom paramater binding callback.  This parameter may be null.
          598  +      /// </param>
          599  +      /// <param name="readValueCallback">
          600  +      /// The custom data reader value callback.  This parameter may be null.
          601  +      /// </param>
          602  +      /// <param name="bindValueUserData">
          603  +      /// The extra data to pass into the parameter binding callback.  This
          604  +      /// parameter may be null.
          605  +      /// </param>
          606  +      /// <param name="readValueUserData">
          607  +      /// The extra data to pass into the data reader value callback.  This
          608  +      /// parameter may be null.
          609  +      /// </param>
          610  +      private SQLiteTypeCallbacks(
          611  +          SQLiteBindValueCallback bindValueCallback,
          612  +          SQLiteReadValueCallback readValueCallback,
          613  +          object bindValueUserData,
          614  +          object readValueUserData
          615  +          )
          616  +      {
          617  +          this.bindValueCallback = bindValueCallback;
          618  +          this.readValueCallback = readValueCallback;
          619  +          this.bindValueUserData = bindValueUserData;
          620  +          this.readValueUserData = readValueUserData;
          621  +      }
          622  +      #endregion
          623  +
          624  +      /////////////////////////////////////////////////////////////////////////
          625  +
          626  +      #region Static "Factory" Methods
          627  +      /// <summary>
          628  +      /// Creates an instance of the <see cref="SQLiteTypeCallbacks" /> class.
          629  +      /// </summary>
          630  +      /// <param name="bindValueCallback">
          631  +      /// The custom paramater binding callback.  This parameter may be null.
          632  +      /// </param>
          633  +      /// <param name="readValueCallback">
          634  +      /// The custom data reader value callback.  This parameter may be null.
          635  +      /// </param>
          636  +      /// <param name="bindValueUserData">
          637  +      /// The extra data to pass into the parameter binding callback.  This
          638  +      /// parameter may be null.
          639  +      /// </param>
          640  +      /// <param name="readValueUserData">
          641  +      /// The extra data to pass into the data reader value callback.  This
          642  +      /// parameter may be null.
          643  +      /// </param>
          644  +      public static SQLiteTypeCallbacks Create(
          645  +          SQLiteBindValueCallback bindValueCallback,
          646  +          SQLiteReadValueCallback readValueCallback,
          647  +          object bindValueUserData,
          648  +          object readValueUserData
          649  +          )
          650  +      {
          651  +          return new SQLiteTypeCallbacks(
          652  +              bindValueCallback, readValueCallback, bindValueUserData,
          653  +              readValueUserData);
          654  +      }
          655  +      #endregion
          656  +
          657  +      /////////////////////////////////////////////////////////////////////////
          658  +
          659  +      #region Public Properties
          660  +      /// <summary>
          661  +      /// The database type name that the callbacks contained in this class
          662  +      /// will apply to.  This value may not be null.
          663  +      /// </summary>
          664  +      public string TypeName
          665  +      {
          666  +          get { return typeName; }
          667  +          internal set { typeName = value; }
          668  +      }
          669  +
          670  +      /////////////////////////////////////////////////////////////////////////
          671  +
          672  +      /// <summary>
          673  +      /// The custom paramater binding callback.  This value may be null.
          674  +      /// </summary>
          675  +      public SQLiteBindValueCallback BindValueCallback
          676  +      {
          677  +          get { return bindValueCallback; }
          678  +      }
          679  +
          680  +      /////////////////////////////////////////////////////////////////////////
          681  +
          682  +      /// <summary>
          683  +      /// The custom data reader value callback.  This value may be null.
          684  +      /// </summary>
          685  +      public SQLiteReadValueCallback ReadValueCallback
          686  +      {
          687  +          get { return readValueCallback; }
          688  +      }
          689  +
          690  +      /////////////////////////////////////////////////////////////////////////
          691  +
          692  +      /// <summary>
          693  +      /// The extra data to pass into the parameter binding callback.  This
          694  +      /// value may be null.
          695  +      /// </summary>
          696  +      public object BindValueUserData
          697  +      {
          698  +          get { return bindValueUserData; }
          699  +      }
          700  +
          701  +      /////////////////////////////////////////////////////////////////////////
          702  +
          703  +      /// <summary>
          704  +      /// The extra data to pass into the data reader value callback.  This
          705  +      /// value may be null.
          706  +      /// </summary>
          707  +      public object ReadValueUserData
          708  +      {
          709  +          get { return readValueUserData; }
          710  +      }
          711  +      #endregion
          712  +  }
          713  +
          714  +  /////////////////////////////////////////////////////////////////////////////////////////////////
          715  +
          716  +  /// <summary>
          717  +  /// This class represents the mappings between database type names
          718  +  /// and their associated custom data type handling callbacks.
          719  +  /// </summary>
          720  +  internal sealed class SQLiteTypeCallbacksMap
          721  +      : Dictionary<string, SQLiteTypeCallbacks>
          722  +  {
          723  +      /// <summary>
          724  +      /// Constructs an (empty) instance of this class.
          725  +      /// </summary>
          726  +      public SQLiteTypeCallbacksMap()
          727  +          : base(new TypeNameStringComparer())
          728  +      {
          729  +          // do nothing.
          730  +      }
          731  +  }
          732  +
          733  +  /////////////////////////////////////////////////////////////////////////////////////////////////
          734  +
    24    735     /// <summary>
    25    736     /// Event data for connection event handlers.
    26    737     /// </summary>
    27    738     public class ConnectionEventArgs : EventArgs
    28    739     {
    29    740         /// <summary>
    30    741         /// The type of event being raised.
................................................................................
   674   1385   
   675   1386       /// <summary>
   676   1387       /// The per-connection mappings between type names and <see cref="DbType" />
   677   1388       /// values.  These mappings override the corresponding global mappings.
   678   1389       /// </summary>
   679   1390       internal SQLiteDbTypeMap _typeNames;
   680   1391   
         1392  +    /// <summary>
         1393  +    /// The per-connection mappings between type names and optional callbacks
         1394  +    /// for parameter binding and value reading.
         1395  +    /// </summary>
         1396  +    private SQLiteTypeCallbacksMap _typeCallbacks;
         1397  +
   681   1398       /// <summary>
   682   1399       /// The base SQLite object to interop with
   683   1400       /// </summary>
   684   1401       internal SQLiteBase _sql;
   685   1402       /// <summary>
   686   1403       /// The database filename minus path and extension
   687   1404       /// </summary>
................................................................................
   898   1615         }
   899   1616   #endif
   900   1617   
   901   1618         _cachedSettings = new Dictionary<string, object>(
   902   1619             new TypeNameStringComparer());
   903   1620   
   904   1621         _typeNames = new SQLiteDbTypeMap();
         1622  +      _typeCallbacks = new SQLiteTypeCallbacksMap();
   905   1623         _parseViaFramework = parseViaFramework;
   906   1624         _flags = SQLiteConnectionFlags.None;
   907   1625         _defaultDbType = null;
   908   1626         _defaultTypeName = null;
   909   1627         _vfsName = null;
   910   1628         _connectionState = ConnectionState.Closed;
   911   1629         _connectionString = null;
................................................................................
  1380   2098   
  1381   2099               _typeNames.Add(new SQLiteDbTypeMapping(typeName, dataType, primary));
  1382   2100           }
  1383   2101   
  1384   2102           return result;
  1385   2103       }
  1386   2104       #endregion
         2105  +
         2106  +    ///////////////////////////////////////////////////////////////////////////////////////////////
         2107  +
         2108  +    #region Per-Connection Type Callbacks
         2109  +    /// <summary>
         2110  +    /// Clears the per-connection type callbacks.
         2111  +    /// </summary>
         2112  +    /// <returns>
         2113  +    /// The total number of per-connection type callbacks cleared.
         2114  +    /// </returns>
         2115  +    public int ClearTypeCallbacks()
         2116  +    {
         2117  +        CheckDisposed();
         2118  +
         2119  +        int result = -1; /* NO CALLBACKS */
         2120  +
         2121  +        if (_typeCallbacks != null)
         2122  +        {
         2123  +            result = _typeCallbacks.Count;
         2124  +            _typeCallbacks.Clear();
         2125  +        }
         2126  +
         2127  +        return result;
         2128  +    }
         2129  +
         2130  +    ///////////////////////////////////////////////////////////////////////////////////////////////
         2131  +
         2132  +    /// <summary>
         2133  +    /// Attempts to get the per-connection type callbacks for the specified
         2134  +    /// database type name.
         2135  +    /// </summary>
         2136  +    /// <param name="typeName">
         2137  +    /// The database type name.
         2138  +    /// </param>
         2139  +    /// <param name="callbacks">
         2140  +    /// Upon success, this parameter will contain the object holding the
         2141  +    /// callbacks for the database type name.  Upon failure, this parameter
         2142  +    /// will be null.
         2143  +    /// </param>
         2144  +    /// <returns>
         2145  +    /// Non-zero upon success; otherwise, zero.
         2146  +    /// </returns>
         2147  +    public bool TryGetTypeCallbacks(
         2148  +        string typeName,
         2149  +        out SQLiteTypeCallbacks callbacks
         2150  +        )
         2151  +    {
         2152  +        CheckDisposed();
         2153  +
         2154  +        if (typeName == null)
         2155  +            throw new ArgumentNullException("typeName");
         2156  +
         2157  +        if (_typeCallbacks == null)
         2158  +        {
         2159  +            callbacks = null;
         2160  +            return false;
         2161  +        }
         2162  +
         2163  +        return _typeCallbacks.TryGetValue(typeName, out callbacks);
         2164  +    }
         2165  +
         2166  +    ///////////////////////////////////////////////////////////////////////////////////////////////
         2167  +
         2168  +    /// <summary>
         2169  +    /// Sets, resets, or clears the per-connection type callbacks for the
         2170  +    /// specified database type name.
         2171  +    /// </summary>
         2172  +    /// <param name="typeName">
         2173  +    /// The database type name.
         2174  +    /// </param>
         2175  +    /// <param name="callbacks">
         2176  +    /// The object holding the callbacks for the database type name.  If
         2177  +    /// this parameter is null, any callbacks for the database type name
         2178  +    /// will be removed if they are present.
         2179  +    /// </param>
         2180  +    /// <returns>
         2181  +    /// Non-zero if callbacks were set or removed; otherwise, zero.
         2182  +    /// </returns>
         2183  +    public bool SetTypeCallbacks(
         2184  +        string typeName,
         2185  +        SQLiteTypeCallbacks callbacks
         2186  +        )
         2187  +    {
         2188  +        CheckDisposed();
         2189  +
         2190  +        if (typeName == null)
         2191  +            throw new ArgumentNullException("typeName");
         2192  +
         2193  +        if (_typeCallbacks == null)
         2194  +            return false;
         2195  +
         2196  +        if (callbacks == null)
         2197  +            return _typeCallbacks.Remove(typeName);
         2198  +
         2199  +        callbacks.TypeName = typeName;
         2200  +        _typeCallbacks[typeName] = callbacks;
         2201  +
         2202  +        return true;
         2203  +    }
         2204  +    #endregion
  1387   2205   
  1388   2206       ///////////////////////////////////////////////////////////////////////////////////////////////
  1389   2207   
  1390   2208       /// <summary>
  1391   2209       /// Attempts to bind the specified <see cref="SQLiteFunction" /> object
  1392   2210       /// instance to this connection.
  1393   2211       /// </summary>

Changes to System.Data.SQLite/SQLiteDataReader.cs.

   424    424                   if (typ == DbType.Binary) return affinity;
   425    425                   if (typ == DbType.String) return affinity;
   426    426                   break;
   427    427           }
   428    428   
   429    429           throw new InvalidCastException();
   430    430       }
          431  +
          432  +    /// <summary>
          433  +    /// Invokes the data reader value callback configured for the database
          434  +    /// type name associated with the specified column.  If no data reader
          435  +    /// value callback is available for the database type name, do nothing.
          436  +    /// </summary>
          437  +    /// <param name="index">
          438  +    /// The index of the column being read.
          439  +    /// </param>
          440  +    /// <param name="eventArgs">
          441  +    /// The extra event data to pass into the callback.
          442  +    /// </param>
          443  +    /// <param name="complete">
          444  +    /// Non-zero if the default handling for the data reader call should be
          445  +    /// skipped.  If this is set to non-zero and the necessary return value
          446  +    /// is unavailable or unsuitable, an exception will be thrown.
          447  +    /// </param>
          448  +    private void InvokeReadValueCallback(
          449  +        int index,
          450  +        SQLiteReadValueEventArgs eventArgs,
          451  +        out bool complete
          452  +        )
          453  +    {
          454  +        complete = false;
          455  +        SQLiteConnectionFlags oldFlags = _flags;
          456  +        _flags &= ~SQLiteConnectionFlags.UseConnectionReadValueCallbacks;
          457  +
          458  +        try
          459  +        {
          460  +            string typeName = GetDataTypeName(index);
          461  +
          462  +            if (typeName == null)
          463  +                return;
          464  +
          465  +            SQLiteConnection connection = GetConnection(this);
          466  +
          467  +            if (connection == null)
          468  +                return;
          469  +
          470  +            SQLiteTypeCallbacks callbacks;
          471  +
          472  +            if (!connection.TryGetTypeCallbacks(typeName, out callbacks) ||
          473  +                (callbacks == null))
          474  +            {
          475  +                return;
          476  +            }
          477  +
          478  +            SQLiteReadValueCallback callback = callbacks.ReadValueCallback;
          479  +
          480  +            if (callback == null)
          481  +                return;
          482  +
          483  +            object userData = callbacks.ReadValueUserData;
          484  +
          485  +            callback(
          486  +                _activeStatement._sql, this, oldFlags, eventArgs, typeName,
          487  +                index, userData, out complete); /* throw */
          488  +        }
          489  +        finally
          490  +        {
          491  +            _flags |= SQLiteConnectionFlags.UseConnectionReadValueCallbacks;
          492  +        }
          493  +    }
   431    494   
   432    495       /// <summary>
   433    496       /// Retrieves the column as a boolean value
   434    497       /// </summary>
   435    498       /// <param name="i">The index of the column.</param>
   436    499       /// <returns>bool</returns>
   437    500       public override bool GetBoolean(int i)
   438    501       {
   439    502           CheckDisposed();
   440    503           VerifyForGet();
          504  +
          505  +        if ((_flags & SQLiteConnectionFlags.UseConnectionReadValueCallbacks) == SQLiteConnectionFlags.UseConnectionReadValueCallbacks)
          506  +        {
          507  +            SQLiteDataReaderValue value = new SQLiteDataReaderValue();
          508  +            bool complete;
          509  +
          510  +            InvokeReadValueCallback(i, new SQLiteReadValueEventArgs(
          511  +                "GetBoolean", null, value), out complete);
          512  +
          513  +            if (complete)
          514  +            {
          515  +                if (value.BooleanValue == null)
          516  +                    throw new SQLiteException("missing boolean return value");
          517  +
          518  +                return (bool)value.BooleanValue;
          519  +            }
          520  +        }
   441    521   
   442    522           if (i >= PrivateVisibleFieldCount && _keyInfo != null)
   443    523               return _keyInfo.GetBoolean(i - PrivateVisibleFieldCount);
   444    524   
   445    525           VerifyType(i, DbType.Boolean);
   446    526           return Convert.ToBoolean(GetValue(i), CultureInfo.CurrentCulture);
   447    527       }
................................................................................
   451    531       /// </summary>
   452    532       /// <param name="i">The index of the column.</param>
   453    533       /// <returns>byte</returns>
   454    534       public override byte GetByte(int i)
   455    535       {
   456    536           CheckDisposed();
   457    537           VerifyForGet();
          538  +
          539  +        if ((_flags & SQLiteConnectionFlags.UseConnectionReadValueCallbacks) == SQLiteConnectionFlags.UseConnectionReadValueCallbacks)
          540  +        {
          541  +            SQLiteDataReaderValue value = new SQLiteDataReaderValue();
          542  +            bool complete;
          543  +
          544  +            InvokeReadValueCallback(i, new SQLiteReadValueEventArgs(
          545  +                "GetByte", null, value), out complete);
          546  +
          547  +            if (complete)
          548  +            {
          549  +                if (value.ByteValue == null)
          550  +                    throw new SQLiteException("missing byte return value");
          551  +
          552  +                return (byte)value.ByteValue;
          553  +            }
          554  +        }
   458    555   
   459    556           if (i >= PrivateVisibleFieldCount && _keyInfo != null)
   460    557               return _keyInfo.GetByte(i - PrivateVisibleFieldCount);
   461    558   
   462    559           VerifyType(i, DbType.Byte);
   463    560           return Convert.ToByte(_activeStatement._sql.GetInt32(_activeStatement, i));
   464    561       }
................................................................................
   475    572       /// <remarks>
   476    573       /// To determine the number of bytes in the column, pass a null value for the buffer.  The total length will be returned.
   477    574       /// </remarks>
   478    575       public override long GetBytes(int i, long fieldOffset, byte[] buffer, int bufferoffset, int length)
   479    576       {
   480    577           CheckDisposed();
   481    578           VerifyForGet();
          579  +
          580  +        if ((_flags & SQLiteConnectionFlags.UseConnectionReadValueCallbacks) == SQLiteConnectionFlags.UseConnectionReadValueCallbacks)
          581  +        {
          582  +            SQLiteReadArrayEventArgs eventArgs = new SQLiteReadArrayEventArgs(
          583  +                fieldOffset, buffer, bufferoffset, length);
          584  +
          585  +            SQLiteDataReaderValue value = new SQLiteDataReaderValue();
          586  +            bool complete;
          587  +
          588  +            InvokeReadValueCallback(i, new SQLiteReadValueEventArgs(
          589  +                "GetBytes", eventArgs, value), out complete);
          590  +
          591  +            if (complete)
          592  +            {
          593  +                byte[] bytes = value.BytesValue;
          594  +
          595  +                if (bytes != null)
          596  +                {
          597  +#if !PLATFORM_COMPACTFRAMEWORK
          598  +                    Array.Copy(bytes, /* throw */
          599  +                        eventArgs.DataOffset, eventArgs.ByteBuffer,
          600  +                        eventArgs.BufferOffset, eventArgs.Length);
          601  +#else
          602  +                    Array.Copy(bytes, /* throw */
          603  +                        (int)eventArgs.DataOffset, eventArgs.ByteBuffer,
          604  +                        eventArgs.BufferOffset, eventArgs.Length);
          605  +#endif
          606  +
          607  +                    return eventArgs.Length;
          608  +                }
          609  +                else
          610  +                {
          611  +                    return -1;
          612  +                }
          613  +            }
          614  +        }
   482    615   
   483    616           if (i >= PrivateVisibleFieldCount && _keyInfo != null)
   484    617               return _keyInfo.GetBytes(i - PrivateVisibleFieldCount, fieldOffset, buffer, bufferoffset, length);
   485    618   
   486    619           VerifyType(i, DbType.Binary);
   487    620           return _activeStatement._sql.GetBytes(_activeStatement, i, (int)fieldOffset, buffer, bufferoffset, length);
   488    621       }
................................................................................
   492    625       /// </summary>
   493    626       /// <param name="i">The index of the column.</param>
   494    627       /// <returns>char</returns>
   495    628       public override char GetChar(int i)
   496    629       {
   497    630           CheckDisposed();
   498    631           VerifyForGet();
          632  +
          633  +        if ((_flags & SQLiteConnectionFlags.UseConnectionReadValueCallbacks) == SQLiteConnectionFlags.UseConnectionReadValueCallbacks)
          634  +        {
          635  +            SQLiteDataReaderValue value = new SQLiteDataReaderValue();
          636  +            bool complete;
          637  +
          638  +            InvokeReadValueCallback(i, new SQLiteReadValueEventArgs(
          639  +                "GetChar", null, value), out complete);
          640  +
          641  +            if (complete)
          642  +            {
          643  +                if (value.CharValue == null)
          644  +                    throw new SQLiteException("missing character return value");
          645  +
          646  +                return (char)value.CharValue;
          647  +            }
          648  +        }
   499    649   
   500    650           if (i >= PrivateVisibleFieldCount && _keyInfo != null)
   501    651               return _keyInfo.GetChar(i - PrivateVisibleFieldCount);
   502    652   
   503    653           VerifyType(i, DbType.SByte);
   504    654           return Convert.ToChar(_activeStatement._sql.GetInt32(_activeStatement, i));
   505    655       }
................................................................................
   516    666       /// <remarks>
   517    667       /// To determine the number of characters in the column, pass a null value for the buffer.  The total length will be returned.
   518    668       /// </remarks>
   519    669       public override long GetChars(int i, long fieldoffset, char[] buffer, int bufferoffset, int length)
   520    670       {
   521    671           CheckDisposed();
   522    672           VerifyForGet();
          673  +
          674  +        if ((_flags & SQLiteConnectionFlags.UseConnectionReadValueCallbacks) == SQLiteConnectionFlags.UseConnectionReadValueCallbacks)
          675  +        {
          676  +            SQLiteReadArrayEventArgs eventArgs = new SQLiteReadArrayEventArgs(
          677  +                fieldoffset, buffer, bufferoffset, length);
          678  +
          679  +            SQLiteDataReaderValue value = new SQLiteDataReaderValue();
          680  +            bool complete;
          681  +
          682  +            InvokeReadValueCallback(i, new SQLiteReadValueEventArgs(
          683  +                "GetChars", eventArgs, value), out complete);
          684  +
          685  +            if (complete)
          686  +            {
          687  +                char[] chars = value.CharsValue;
          688  +
          689  +                if (chars != null)
          690  +                {
          691  +#if !PLATFORM_COMPACTFRAMEWORK
          692  +                    Array.Copy(chars, /* throw */
          693  +                        eventArgs.DataOffset, eventArgs.CharBuffer,
          694  +                        eventArgs.BufferOffset, eventArgs.Length);
          695  +#else
          696  +                    Array.Copy(chars, /* throw */
          697  +                        (int)eventArgs.DataOffset, eventArgs.CharBuffer,
          698  +                        eventArgs.BufferOffset, eventArgs.Length);
          699  +#endif
          700  +
          701  +                    return eventArgs.Length;
          702  +                }
          703  +                else
          704  +                {
          705  +                    return -1;
          706  +                }
          707  +            }
          708  +        }
   523    709   
   524    710           if (i >= PrivateVisibleFieldCount && _keyInfo != null)
   525    711               return _keyInfo.GetChars(i - PrivateVisibleFieldCount, fieldoffset, buffer, bufferoffset, length);
   526    712   
   527    713           if ((_flags & SQLiteConnectionFlags.NoVerifyTextAffinity) != SQLiteConnectionFlags.NoVerifyTextAffinity)
   528    714               VerifyType(i, DbType.String);
   529    715   
................................................................................
   551    737       /// </summary>
   552    738       /// <param name="i">The index of the column.</param>
   553    739       /// <returns>DateTime</returns>
   554    740       public override DateTime GetDateTime(int i)
   555    741       {
   556    742           CheckDisposed();
   557    743           VerifyForGet();
          744  +
          745  +        if ((_flags & SQLiteConnectionFlags.UseConnectionReadValueCallbacks) == SQLiteConnectionFlags.UseConnectionReadValueCallbacks)
          746  +        {
          747  +            SQLiteDataReaderValue value = new SQLiteDataReaderValue();
          748  +            bool complete;
          749  +
          750  +            InvokeReadValueCallback(i, new SQLiteReadValueEventArgs(
          751  +                "GetDateTime", null, value), out complete);
          752  +
          753  +            if (complete)
          754  +            {
          755  +                if (value.DateTimeValue == null)
          756  +                    throw new SQLiteException("missing date/time return value");
          757  +
          758  +                return (DateTime)value.DateTimeValue;
          759  +            }
          760  +        }
   558    761   
   559    762           if (i >= PrivateVisibleFieldCount && _keyInfo != null)
   560    763               return _keyInfo.GetDateTime(i - PrivateVisibleFieldCount);
   561    764   
   562    765           VerifyType(i, DbType.DateTime);
   563    766           return _activeStatement._sql.GetDateTime(_activeStatement, i);
   564    767       }
................................................................................
   568    771       /// </summary>
   569    772       /// <param name="i">The index of the column.</param>
   570    773       /// <returns>decimal</returns>
   571    774       public override decimal GetDecimal(int i)
   572    775       {
   573    776           CheckDisposed();
   574    777           VerifyForGet();
          778  +
          779  +        if ((_flags & SQLiteConnectionFlags.UseConnectionReadValueCallbacks) == SQLiteConnectionFlags.UseConnectionReadValueCallbacks)
          780  +        {
          781  +            SQLiteDataReaderValue value = new SQLiteDataReaderValue();
          782  +            bool complete;
          783  +
          784  +            InvokeReadValueCallback(i, new SQLiteReadValueEventArgs(
          785  +                "GetDecimal", null, value), out complete);
          786  +
          787  +            if (complete)
          788  +            {
          789  +                if (value.DecimalValue == null)
          790  +                    throw new SQLiteException("missing decimal return value");
          791  +
          792  +                return (decimal)value.DecimalValue;
          793  +            }
          794  +        }
   575    795   
   576    796           if (i >= PrivateVisibleFieldCount && _keyInfo != null)
   577    797               return _keyInfo.GetDecimal(i - PrivateVisibleFieldCount);
   578    798   
   579    799           VerifyType(i, DbType.Decimal);
   580    800           return Decimal.Parse(_activeStatement._sql.GetText(_activeStatement, i), NumberStyles.AllowDecimalPoint | NumberStyles.AllowExponent | NumberStyles.AllowLeadingSign, CultureInfo.InvariantCulture);
   581    801       }
................................................................................
   585    805       /// </summary>
   586    806       /// <param name="i">The index of the column.</param>
   587    807       /// <returns>double</returns>
   588    808       public override double GetDouble(int i)
   589    809       {
   590    810           CheckDisposed();
   591    811           VerifyForGet();
          812  +
          813  +        if ((_flags & SQLiteConnectionFlags.UseConnectionReadValueCallbacks) == SQLiteConnectionFlags.UseConnectionReadValueCallbacks)
          814  +        {
          815  +            SQLiteDataReaderValue value = new SQLiteDataReaderValue();
          816  +            bool complete;
          817  +
          818  +            InvokeReadValueCallback(i, new SQLiteReadValueEventArgs(
          819  +                "GetDouble", null, value), out complete);
          820  +
          821  +            if (complete)
          822  +            {
          823  +                if (value.DoubleValue == null)
          824  +                    throw new SQLiteException("missing double return value");
          825  +
          826  +                return (double)value.DoubleValue;
          827  +            }
          828  +        }
   592    829   
   593    830           if (i >= PrivateVisibleFieldCount && _keyInfo != null)
   594    831               return _keyInfo.GetDouble(i - PrivateVisibleFieldCount);
   595    832   
   596    833           VerifyType(i, DbType.Double);
   597    834           return _activeStatement._sql.GetDouble(_activeStatement, i);
   598    835       }
................................................................................
   617    854       /// </summary>
   618    855       /// <param name="i">The index of the column.</param>
   619    856       /// <returns>float</returns>
   620    857       public override float GetFloat(int i)
   621    858       {
   622    859           CheckDisposed();
   623    860           VerifyForGet();
          861  +
          862  +        if ((_flags & SQLiteConnectionFlags.UseConnectionReadValueCallbacks) == SQLiteConnectionFlags.UseConnectionReadValueCallbacks)
          863  +        {
          864  +            SQLiteDataReaderValue value = new SQLiteDataReaderValue();
          865  +            bool complete;
          866  +
          867  +            InvokeReadValueCallback(i, new SQLiteReadValueEventArgs(
          868  +                "GetFloat", null, value), out complete);
          869  +
          870  +            if (complete)
          871  +            {
          872  +                if (value.FloatValue == null)
          873  +                    throw new SQLiteException("missing float return value");
          874  +
          875  +                return (float)value.FloatValue;
          876  +            }
          877  +        }
   624    878   
   625    879           if (i >= PrivateVisibleFieldCount && _keyInfo != null)
   626    880               return _keyInfo.GetFloat(i - PrivateVisibleFieldCount);
   627    881   
   628    882           VerifyType(i, DbType.Single);
   629    883           return Convert.ToSingle(_activeStatement._sql.GetDouble(_activeStatement, i));
   630    884       }
................................................................................
   634    888       /// </summary>
   635    889       /// <param name="i">The index of the column.</param>
   636    890       /// <returns>Guid</returns>
   637    891       public override Guid GetGuid(int i)
   638    892       {
   639    893           CheckDisposed();
   640    894           VerifyForGet();
          895  +
          896  +        if ((_flags & SQLiteConnectionFlags.UseConnectionReadValueCallbacks) == SQLiteConnectionFlags.UseConnectionReadValueCallbacks)
          897  +        {
          898  +            SQLiteDataReaderValue value = new SQLiteDataReaderValue();
          899  +            bool complete;
          900  +
          901  +            InvokeReadValueCallback(i, new SQLiteReadValueEventArgs(
          902  +                "GetGuid", null, value), out complete);
          903  +
          904  +            if (complete)
          905  +            {
          906  +                if (value.GuidValue == null)
          907  +                    throw new SQLiteException("missing guid return value");
          908  +
          909  +                return (Guid)value.GuidValue;
          910  +            }
          911  +        }
   641    912   
   642    913           if (i >= PrivateVisibleFieldCount && _keyInfo != null)
   643    914               return _keyInfo.GetGuid(i - PrivateVisibleFieldCount);
   644    915   
   645    916           TypeAffinity affinity = VerifyType(i, DbType.Guid);
   646    917           if (affinity == TypeAffinity.Blob)
   647    918           {
................................................................................
   658    929       /// </summary>
   659    930       /// <param name="i">The index of the column.</param>
   660    931       /// <returns>Int16</returns>
   661    932       public override Int16 GetInt16(int i)
   662    933       {
   663    934           CheckDisposed();
   664    935           VerifyForGet();
          936  +
          937  +        if ((_flags & SQLiteConnectionFlags.UseConnectionReadValueCallbacks) == SQLiteConnectionFlags.UseConnectionReadValueCallbacks)
          938  +        {
          939  +            SQLiteDataReaderValue value = new SQLiteDataReaderValue();
          940  +            bool complete;
          941  +
          942  +            InvokeReadValueCallback(i, new SQLiteReadValueEventArgs(
          943  +                "GetInt16", null, value), out complete);
          944  +
          945  +            if (complete)
          946  +            {
          947  +                if (value.Int16Value == null)
          948  +                    throw new SQLiteException("missing int16 return value");
          949  +
          950  +                return (Int16)value.Int16Value;
          951  +            }
          952  +        }
   665    953   
   666    954           if (i >= PrivateVisibleFieldCount && _keyInfo != null)
   667    955               return _keyInfo.GetInt16(i - PrivateVisibleFieldCount);
   668    956   
   669    957           VerifyType(i, DbType.Int16);
   670    958           return Convert.ToInt16(_activeStatement._sql.GetInt32(_activeStatement, i));
   671    959       }
................................................................................
   675    963       /// </summary>
   676    964       /// <param name="i">The index of the column.</param>
   677    965       /// <returns>Int32</returns>
   678    966       public override Int32 GetInt32(int i)
   679    967       {
   680    968           CheckDisposed();
   681    969           VerifyForGet();
          970  +
          971  +        if ((_flags & SQLiteConnectionFlags.UseConnectionReadValueCallbacks) == SQLiteConnectionFlags.UseConnectionReadValueCallbacks)
          972  +        {
          973  +            SQLiteDataReaderValue value = new SQLiteDataReaderValue();
          974  +            bool complete;
          975  +
          976  +            InvokeReadValueCallback(i, new SQLiteReadValueEventArgs(
          977  +                "GetInt32", null, value), out complete);
          978  +
          979  +            if (complete)
          980  +            {
          981  +                if (value.Int32Value == null)
          982  +                    throw new SQLiteException("missing int32 return value");
          983  +
          984  +                return (Int32)value.Int32Value;
          985  +            }
          986  +        }
   682    987   
   683    988           if (i >= PrivateVisibleFieldCount && _keyInfo != null)
   684    989               return _keyInfo.GetInt32(i - PrivateVisibleFieldCount);
   685    990   
   686    991           VerifyType(i, DbType.Int32);
   687    992           return _activeStatement._sql.GetInt32(_activeStatement, i);
   688    993       }
................................................................................
   692    997       /// </summary>
   693    998       /// <param name="i">The index of the column.</param>
   694    999       /// <returns>Int64</returns>
   695   1000       public override Int64 GetInt64(int i)
   696   1001       {
   697   1002           CheckDisposed();
   698   1003           VerifyForGet();
         1004  +
         1005  +        if ((_flags & SQLiteConnectionFlags.UseConnectionReadValueCallbacks) == SQLiteConnectionFlags.UseConnectionReadValueCallbacks)
         1006  +        {
         1007  +            SQLiteDataReaderValue value = new SQLiteDataReaderValue();
         1008  +            bool complete;
         1009  +
         1010  +            InvokeReadValueCallback(i, new SQLiteReadValueEventArgs(
         1011  +                "GetInt64", null, value), out complete);
         1012  +
         1013  +            if (complete)
         1014  +            {
         1015  +                if (value.Int64Value == null)
         1016  +                    throw new SQLiteException("missing int64 return value");
         1017  +
         1018  +                return (Int64)value.Int64Value;
         1019  +            }
         1020  +        }
   699   1021   
   700   1022           if (i >= PrivateVisibleFieldCount && _keyInfo != null)
   701   1023               return _keyInfo.GetInt64(i - PrivateVisibleFieldCount);
   702   1024   
   703   1025           VerifyType(i, DbType.Int64);
   704   1026           return _activeStatement._sql.GetInt64(_activeStatement, i);
   705   1027       }
................................................................................
  1228   1550       /// </summary>
  1229   1551       /// <param name="i">The index of the column.</param>
  1230   1552       /// <returns>string</returns>
  1231   1553       public override string GetString(int i)
  1232   1554       {
  1233   1555           CheckDisposed();
  1234   1556           VerifyForGet();
         1557  +
         1558  +        if ((_flags & SQLiteConnectionFlags.UseConnectionReadValueCallbacks) == SQLiteConnectionFlags.UseConnectionReadValueCallbacks)
         1559  +        {
         1560  +            SQLiteDataReaderValue value = new SQLiteDataReaderValue();
         1561  +            bool complete;
         1562  +
         1563  +            InvokeReadValueCallback(i, new SQLiteReadValueEventArgs(
         1564  +                "GetString", null, value), out complete);
         1565  +
         1566  +            if (complete)
         1567  +                return value.StringValue;
         1568  +        }
  1235   1569   
  1236   1570           if (i >= PrivateVisibleFieldCount && _keyInfo != null)
  1237   1571               return _keyInfo.GetString(i - PrivateVisibleFieldCount);
  1238   1572   
  1239   1573           if ((_flags & SQLiteConnectionFlags.NoVerifyTextAffinity) != SQLiteConnectionFlags.NoVerifyTextAffinity)
  1240   1574               VerifyType(i, DbType.String);
  1241   1575   
................................................................................
  1247   1581       /// </summary>
  1248   1582       /// <param name="i">The index of the column.</param>
  1249   1583       /// <returns>object</returns>
  1250   1584       public override object GetValue(int i)
  1251   1585       {
  1252   1586           CheckDisposed();
  1253   1587           VerifyForGet();
         1588  +
         1589  +        if ((_flags & SQLiteConnectionFlags.UseConnectionReadValueCallbacks) == SQLiteConnectionFlags.UseConnectionReadValueCallbacks)
         1590  +        {
         1591  +            SQLiteDataReaderValue value = new SQLiteDataReaderValue();
         1592  +            bool complete;
         1593  +
         1594  +            InvokeReadValueCallback(i, new SQLiteReadValueEventArgs(
         1595  +                "GetValue", null, value), out complete);
         1596  +
         1597  +            if (complete)
         1598  +                return value.Value;
         1599  +        }
  1254   1600   
  1255   1601           if (i >= PrivateVisibleFieldCount && _keyInfo != null)
  1256   1602               return _keyInfo.GetValue(i - PrivateVisibleFieldCount);
  1257   1603   
  1258   1604           SQLiteType typ = GetSQLiteType(_flags, i);
  1259   1605   
  1260   1606           if (((_flags & SQLiteConnectionFlags.DetectTextAffinity) == SQLiteConnectionFlags.DetectTextAffinity) &&

Changes to System.Data.SQLite/SQLiteParameter.cs.

    50     50       /// The data size, unused by SQLite
    51     51       /// </summary>
    52     52       private int            _dataSize;
    53     53   
    54     54       private bool           _nullable;
    55     55       private bool           _nullMapping;
    56     56   
           57  +    /// <summary>
           58  +    /// The database type name associated with this parameter, if any.
           59  +    /// </summary>
           60  +    private string         _typeName;
           61  +
    57     62       /// <summary>
    58     63       /// Constructor used when creating for use with a specific command.
    59     64       /// </summary>
    60     65       /// <param name="command">
    61     66       /// The command associated with this parameter.
    62     67       /// </param>
    63     68       internal SQLiteParameter(
................................................................................
   472    477         set
   473    478         {
   474    479           _objValue = value;
   475    480           if (_dbType == UnknownDbType && _objValue != null && _objValue != DBNull.Value) // If the DbType has never been assigned, try to glean one from the value's datatype
   476    481             _dbType = SQLiteConvert.TypeToDbType(_objValue.GetType());
   477    482         }
   478    483       }
          484  +
          485  +    /// <summary>
          486  +    /// The database type name associated with this parameter, if any.
          487  +    /// </summary>
          488  +    public string TypeName
          489  +    {
          490  +        get
          491  +        {
          492  +            return _typeName;
          493  +        }
          494  +        set
          495  +        {
          496  +            _typeName = value;
          497  +        }
          498  +    }
   479    499   
   480    500       /// <summary>
   481    501       /// Clones a parameter
   482    502       /// </summary>
   483    503       /// <returns>A new, unassociated SQLiteParameter</returns>
   484    504       public object Clone()
   485    505       {
   486    506         SQLiteParameter newparam = new SQLiteParameter(this);
   487    507   
   488    508         return newparam;
   489    509       }
   490    510     }
   491    511   }

Changes to System.Data.SQLite/SQLiteStatement.cs.

   233    233   
   234    234         int x = _paramNames.Length;
   235    235         for (int n = 0; n < x; n++)
   236    236         {
   237    237           BindParameter(n + 1, _paramValues[n]);
   238    238         }
   239    239       }
          240  +
          241  +    /// <summary>
          242  +    /// This method attempts to query the database connection associated with
          243  +    /// the statement in use.  If the underlying command or connection is
          244  +    /// unavailable, a null value will be returned.
          245  +    /// </summary>
          246  +    /// <returns>
          247  +    /// The connection object -OR- null if it is unavailable.
          248  +    /// </returns>
          249  +    private static SQLiteConnection GetConnection(
          250  +        SQLiteStatement statement
          251  +        )
          252  +    {
          253  +        try
          254  +        {
          255  +            if (statement != null)
          256  +            {
          257  +                SQLiteCommand command = statement._command;
          258  +
          259  +                if (command != null)
          260  +                {
          261  +                    SQLiteConnection connection = command.Connection;
          262  +
          263  +                    if (connection != null)
          264  +                        return connection;
          265  +                }
          266  +            }
          267  +        }
          268  +        catch (ObjectDisposedException)
          269  +        {
          270  +            // do nothing.
          271  +        }
          272  +
          273  +        return null;
          274  +    }
          275  +
          276  +    /// <summary>
          277  +    /// Invokes the parameter binding callback configured for the database
          278  +    /// type name associated with the specified column.  If no parameter
          279  +    /// binding callback is available for the database type name, do
          280  +    /// nothing.
          281  +    /// </summary>
          282  +    /// <param name="index">
          283  +    /// The index of the column being read.
          284  +    /// </param>
          285  +    /// <param name="parameter">
          286  +    /// The <see cref="SQLiteParameter" /> instance being bound to the
          287  +    /// command.
          288  +    /// </param>
          289  +    /// <param name="complete">
          290  +    /// Non-zero if the default handling for the parameter binding call
          291  +    /// should be skipped (i.e. the parameter should not be bound at all).
          292  +    /// Great care should be used when setting this to non-zero.
          293  +    /// </param>
          294  +    private void InvokeBindValueCallback(
          295  +        int index,
          296  +        SQLiteParameter parameter,
          297  +        out bool complete
          298  +        )
          299  +    {
          300  +        complete = false;
          301  +        SQLiteConnectionFlags oldFlags = _flags;
          302  +        _flags &= ~SQLiteConnectionFlags.UseConnectionBindValueCallbacks;
          303  +
          304  +        try
          305  +        {
          306  +            if (parameter == null)
          307  +                return;
          308  +
          309  +            SQLiteConnection connection = GetConnection(this);
          310  +
          311  +            if (connection == null)
          312  +                return;
          313  +
          314  +            //
          315  +            // NOTE: First, always look for an explicitly set database type
          316  +            //       name.
          317  +            //
          318  +            string typeName = parameter.TypeName;
          319  +
          320  +            if (typeName == null)
          321  +            {
          322  +                //
          323  +                // NOTE: Are we allowed to fallback to using the parameter name
          324  +                //       as the basis for looking up the binding callback?
          325  +                //
          326  +                if ((_flags & SQLiteConnectionFlags.UseParameterNameForTypeName)
          327  +                        == SQLiteConnectionFlags.UseParameterNameForTypeName)
          328  +                {
          329  +                    typeName = parameter.ParameterName;
          330  +                }
          331  +            }
          332  +
          333  +            if (typeName == null)
          334  +            {
          335  +                //
          336  +                // NOTE: Are we allowed to fallback to using the database type
          337  +                //       name translated from the DbType as the basis for looking
          338  +                //       up the binding callback?
          339  +                //
          340  +                if ((_flags & SQLiteConnectionFlags.UseParameterDbTypeForTypeName)
          341  +                        == SQLiteConnectionFlags.UseParameterDbTypeForTypeName)
          342  +                {
          343  +                    typeName = SQLiteConvert.DbTypeToTypeName(
          344  +                        connection, parameter.DbType, _flags);
          345  +                }
          346  +            }
          347  +
          348  +            if (typeName == null)
          349  +                return;
          350  +
          351  +            SQLiteTypeCallbacks callbacks;
          352  +
          353  +            if (!connection.TryGetTypeCallbacks(typeName, out callbacks) ||
          354  +                (callbacks == null))
          355  +            {
          356  +                return;
          357  +            }
          358  +
          359  +            SQLiteBindValueCallback callback = callbacks.BindValueCallback;
          360  +
          361  +            if (callback == null)
          362  +                return;
          363  +
          364  +            object userData = callbacks.BindValueUserData;
          365  +
          366  +            callback(
          367  +                _sql, _command, oldFlags, parameter, typeName, index,
          368  +                userData, out complete); /* throw */
          369  +        }
          370  +        finally
          371  +        {
          372  +            _flags |= SQLiteConnectionFlags.UseConnectionBindValueCallbacks;
          373  +        }
          374  +    }
   240    375   
   241    376       /// <summary>
   242    377       /// Perform the bind operation for an individual parameter
   243    378       /// </summary>
   244    379       /// <param name="index">The index of the parameter to bind</param>
   245    380       /// <param name="param">The parameter we're binding</param>
   246    381       private void BindParameter(int index, SQLiteParameter param)
   247    382       {
   248    383         if (param == null)
   249    384           throw new SQLiteException("Insufficient parameters supplied to the command");
          385  +
          386  +      if ((_flags & SQLiteConnectionFlags.UseConnectionBindValueCallbacks) == SQLiteConnectionFlags.UseConnectionBindValueCallbacks)
          387  +      {
          388  +          bool complete;
          389  +
          390  +          InvokeBindValueCallback(index, param, out complete);
          391  +
          392  +          if (complete)
          393  +              return;
          394  +      }
   250    395   
   251    396         object obj = param.Value;
   252    397         DbType objType = param.DbType;
   253    398   
   254    399         if ((obj != null) && (objType == DbType.Object))
   255    400             objType = SQLiteConvert.TypeToDbType(obj.GetType());
   256    401   

Added Tests/types.eagle.

            1  +###############################################################################
            2  +#
            3  +# types.eagle --
            4  +#
            5  +# Written by Joe Mistachkin.
            6  +# Released to the public domain, use at your own risk!
            7  +#
            8  +###############################################################################
            9  +
           10  +package require Eagle
           11  +package require Eagle.Library
           12  +package require Eagle.Test
           13  +
           14  +runTestPrologue
           15  +
           16  +###############################################################################
           17  +
           18  +package require System.Data.SQLite.Test
           19  +runSQLiteTestPrologue
           20  +
           21  +###############################################################################
           22  +
           23  +proc bindValueCallback1 {
           24  +        convert command flags parameter typeName index userData
           25  +        completeVarName } {
           26  +  lappend ::log(bind) [list convert [isObjectHandle $convert]]
           27  +  lappend ::log(bind) [list command [isObjectHandle $command]]
           28  +  lappend ::log(bind) [list flags [getStringFromObjectHandle $flags]]
           29  +  lappend ::log(bind) [list parameter [isObjectHandle $parameter]]
           30  +  lappend ::log(bind) [list typeName [getStringFromObjectHandle $typeName]]
           31  +  lappend ::log(bind) [list index [getStringFromObjectHandle $index]]
           32  +  lappend ::log(bind) [list userData [getStringFromObjectHandle $userData]]
           33  +
           34  +  if {[getStringFromObjectHandle $userData] == 3} then {
           35  +    upvar 1 $completeVarName complete; unset complete
           36  +    error "parameter binding canceled"
           37  +  }
           38  +
           39  +  if {[getStringFromObjectHandle $userData] == 2} then {
           40  +    $parameter DbType String
           41  +    $parameter Value custom
           42  +  }
           43  +
           44  +  if {[getStringFromObjectHandle $userData] == 1} then {
           45  +    upvar 1 $completeVarName complete
           46  +    set complete [object invoke -create System.Boolean Parse True]
           47  +  }
           48  +}
           49  +
           50  +###############################################################################
           51  +
           52  +proc readValueCallback1 {
           53  +        convert dataReader flags eventArgs typeName index userData
           54  +        completeVarName } {
           55  +  lappend ::log(read) [list convert [isObjectHandle $convert]]
           56  +  lappend ::log(read) [list dataReader [isObjectHandle $dataReader]]
           57  +  lappend ::log(read) [list flags [getStringFromObjectHandle $flags]]
           58  +  lappend ::log(read) [list eventArgs [isObjectHandle $eventArgs]]
           59  +  lappend ::log(read) [list typeName [getStringFromObjectHandle $typeName]]
           60  +  lappend ::log(read) [list index [getStringFromObjectHandle $index]]
           61  +  lappend ::log(read) [list userData [getStringFromObjectHandle $userData]]
           62  +
           63  +  if {[getStringFromObjectHandle $userData] == 3} then {
           64  +    upvar 1 $completeVarName complete; unset complete
           65  +    error "reading of value canceled"
           66  +  }
           67  +
           68  +  if {[getStringFromObjectHandle $userData] == 1} then {
           69  +    upvar 1 $completeVarName complete
           70  +    set complete [object invoke -create System.Boolean Parse True]
           71  +  }
           72  +}
           73  +
           74  +###############################################################################
           75  +
           76  +runTest {test types-1.1 {type callbacks management} -setup {
           77  +  setupDb [set fileName types-1.1.db]
           78  +} -body {
           79  +  set connection [getDbConnection]
           80  +
           81  +  set result [list]
           82  +
           83  +  lappend result [$connection ClearTypeCallbacks]
           84  +
           85  +  set typeCallbacks(1) [object invoke \
           86  +      System.Data.SQLite.SQLiteTypeCallbacks Create \
           87  +      null null null null]
           88  +
           89  +  set typeCallbacks(2) null
           90  +
           91  +  lappend result [$connection SetTypeCallbacks TEST $typeCallbacks(1)]
           92  +  lappend result [$connection TryGetTypeCallbacks TEST typeCallbacks(2)]
           93  +  lappend result [$connection ClearTypeCallbacks]
           94  +  lappend result [$connection SetTypeCallbacks TEST $typeCallbacks(1)]
           95  +  lappend result [expr {$typeCallbacks(1) eq $typeCallbacks(2)}]
           96  +
           97  +  set result
           98  +} -cleanup {
           99  +  freeDbConnection
          100  +
          101  +  cleanupDb $fileName
          102  +
          103  +  unset -nocomplain typeCallbacks
          104  +  unset -nocomplain result connection db fileName
          105  +} -constraints {eagle command.object monoBug28 command.sql compile.DATA SQLite\
          106  +System.Data.SQLite} -result {0 True True 1 True True}}
          107  +
          108  +###############################################################################
          109  +
          110  +set readArgs [list \
          111  +    convert dataReader flags eventArgs typeName index userData \
          112  +    completeVarName]
          113  +
          114  +set params [list \
          115  +    [list Boolean    GetBoolean    false   BooleanValue    true    0] \
          116  +    [list Byte       GetByte       false   ByteValue       true    0] \
          117  +    [list Byte       GetBytes      true    BytesValue      false   0] \
          118  +    [list Char       GetChar       false   CharValue       true    0] \
          119  +    [list Char       GetChars      true    CharsValue      false   0] \
          120  +    [list DateTime   GetDateTime   false   DateTimeValue   true    0] \
          121  +    [list Decimal    GetDecimal    false   DecimalValue    true    0] \
          122  +    [list Double     GetDouble     false   DoubleValue     true    0] \
          123  +    [list Single     GetFloat      false   FloatValue      true    0] \
          124  +    [list Guid       GetGuid       false   GuidValue       true    \
          125  +        00000000-0000-0000-0000-000000000000] \
          126  +    [list Int16      GetInt16      false   Int16Value      true    0] \
          127  +    [list Int32      GetInt32      false   Int32Value      true    0] \
          128  +    [list Int64      GetInt64      false   Int64Value      true    0] \
          129  +    [list String     GetString     false   StringValue     false   null] \
          130  +    [list Object     GetValue      false   Value           false   null]]
          131  +
          132  +###############################################################################
          133  +
          134  +set expectedResults [list \
          135  +    {False False True False False False False False} \
          136  +    {0 0 1 0 0 0 0 0} \
          137  +    {0 1 48 1 49 3 {49 46 50} 5 {116 104 114\
          138  +        101 101} 1 4 27 {50 48 49 54 45 48 54\
          139  +        45 49 57 32 49 57 58 53 48 58 48 52 46\
          140  +        49 50 51 52 53 54 55} 36 {55 52 102 49\
          141  +        102 52 48 50 45 100 100 101 102 45 52\
          142  +        48 50 55 45 97 55 56 102 45 51 56 52 55\
          143  +        102 97 57 55 98 56 51 48}} \
          144  +    "<NUL> <NUL> \x01 <NUL> <NUL> <NUL> <NUL> <NUL>" \
          145  +    "0 1 0 1 1 3 {1 . 2} 1 t 1 \x04 1 2 1 7" \
          146  +    {{0001-01-01 00:00:00Z} {0001-01-01 00:00:00Z}\
          147  +        {0001-01-01 00:00:00Z} {0001-01-01 00:00:00Z}\
          148  +        {0001-01-01 00:00:00Z} {0001-01-01 00:00:00Z}\
          149  +        {2016-06-19 19:50:04.1234567Z} {0001-01-01\
          150  +        00:00:00Z}} \
          151  +    {0 0 1 1.2 0 0 0 0} \
          152  +    {0 0 1 1.2 0 0 0 0} \
          153  +    {0 0 1 1.2 0 0 0 0} \
          154  +    {00000000-0000-0000-0000-000000000000\
          155  +        00000000-0000-0000-0000-000000000000\
          156  +        00000000-0000-0000-0000-000000000000\
          157  +        00000000-0000-0000-0000-000000000000\
          158  +        00000000-0000-0000-0000-000000000000\
          159  +        00060504-0000-0000-0000-000000000000\
          160  +        00000000-0000-0000-0000-000000000000\
          161  +        74f1f402-ddef-4027-a78f-3847fa97b830} \
          162  +    {0 0 1 0 0 0 0 0} \
          163  +    {0 0 1 0 0 0 0 0} \
          164  +    {0 0 1 0 0 0 0 0} \
          165  +    "<NUL> <NUL> <NUL> <NUL> three \x04\x05\x06\
          166  +        {2016-06-19 19:50:04.1234567}\
          167  +        74f1f402-ddef-4027-a78f-3847fa97b830" \
          168  +    {{} 0 1 1.2 three {4 5 6} {2016-06-19\
          169  +        19:50:04.1234567}\
          170  +        74f1f402-ddef-4027-a78f-3847fa97b830}]
          171  +
          172  +###############################################################################
          173  +
          174  +set savedDateTimeFormat [object invoke Interpreter.GetActive DateTimeFormat]
          175  +if {![isObjectHandle $savedDateTimeFormat]} then {set savedDateTimeFormat null}
          176  +object invoke Interpreter.GetActive DateTimeFormat [getDateTimeFormat]
          177  +
          178  +###############################################################################
          179  +
          180  +for {set i 0} {$i < [llength $params]} {incr i} {
          181  +  foreach {
          182  +    typeName methodName isArray propertyName isRequired value
          183  +  } [lindex $params $i] break
          184  +
          185  +  set expectedResult [lindex $expectedResults $i]
          186  +
          187  +  #############################################################################
          188  +
          189  +  proc readValueCallback2 $readArgs [subst {
          190  +    if {$isArray} then {
          191  +      if {\[catch {
          192  +        set dataOffset \[\$eventArgs ArrayEventArgs.DataOffset\]
          193  +
          194  +        set buffer \[\$eventArgs -create [appendArgs ArrayEventArgs. \
          195  +            $typeName Buffer]\]
          196  +
          197  +        set bufferOffset \[\$eventArgs ArrayEventArgs.BufferOffset\]
          198  +        set length \[\$eventArgs ArrayEventArgs.Length\]
          199  +
          200  +        set readValue \[\$dataReader \[\$eventArgs MethodName\] \
          201  +            \$index \$dataOffset \$buffer \$bufferOffset \$length\]
          202  +
          203  +        \$eventArgs \[appendArgs Value. $propertyName\] \$readValue
          204  +      } error\]} then {
          205  +        set readValue \[\$dataReader -tostring GetValue \$index\]
          206  +
          207  +        if {"$typeName" eq "Char"} then {
          208  +          set string \[object create -alias String \$readValue\]
          209  +          set buffer \[\$string -create -alias ToCharArray]
          210  +        } else {
          211  +          set buffer \[object invoke -create -alias \
          212  +              System.Text.Encoding.UTF8 GetBytes \$readValue\]
          213  +        }
          214  +
          215  +        \$eventArgs \[appendArgs Value. $propertyName\] \$buffer
          216  +        \$eventArgs ArrayEventArgs.Length \[\$buffer Length\]
          217  +      } else {
          218  +        set buffer \[\$eventArgs -create [appendArgs ArrayEventArgs. \
          219  +            $typeName Buffer]\]
          220  +
          221  +        \$eventArgs \[appendArgs Value. $propertyName\] \$buffer
          222  +      }
          223  +    } else {
          224  +      if {\[catch {
          225  +        set readValue \[\$dataReader \[\$eventArgs MethodName\] \$index\]
          226  +
          227  +        if {"$typeName" eq "Char"} then {
          228  +          set readValue \[object invoke -create Char Parse \$readValue\]
          229  +        }
          230  +
          231  +        \$eventArgs \[appendArgs Value. $propertyName\] \$readValue
          232  +      } error\]} then {
          233  +        \$eventArgs \[appendArgs Value. $propertyName\] {$value}
          234  +      }
          235  +    }
          236  +
          237  +    upvar 1 \$completeVarName complete
          238  +    set complete \[object invoke -create System.Boolean Parse True\]
          239  +  }]
          240  +
          241  +  #############################################################################
          242  +
          243  +  runTest {test [appendArgs types-2. $i] [appendArgs $methodName " callback"] \
          244  +      -setup [subst -nocommands {
          245  +    set typeName {$typeName}
          246  +    set methodName {$methodName}
          247  +    set isArray {$isArray}
          248  +
          249  +    setupDb [set fileName [appendArgs types-2. $i .db]] "" "" "" \
          250  +        UseConnectionReadValueCallbacks
          251  +  }] -body {
          252  +    sql execute $db {
          253  +      CREATE TABLE t1(x INTEGER, y SPECIAL);
          254  +      INSERT INTO t1 (x, y) VALUES(1, NULL);
          255  +      INSERT INTO t1 (x, y) VALUES(2, 0);
          256  +      INSERT INTO t1 (x, y) VALUES(3, 1);
          257  +      INSERT INTO t1 (x, y) VALUES(4, 1.2);
          258  +      INSERT INTO t1 (x, y) VALUES(5, 'three');
          259  +      INSERT INTO t1 (x, y) VALUES(6, X'040506');
          260  +      INSERT INTO t1 (x, y) VALUES(7, '2016-06-19 19:50:04.1234567');
          261  +      INSERT INTO t1 (x, y) VALUES(8, '74f1f402-ddef-4027-a78f-3847fa97b830');
          262  +    }
          263  +
          264  +    set callback {-callbackflags +Default readValueCallback2}
          265  +    set connection [getDbConnection]
          266  +
          267  +    set result [list]
          268  +
          269  +    set typeCallbacks [object invoke -marshalflags +DynamicCallback \
          270  +        System.Data.SQLite.SQLiteTypeCallbacks Create null $callback \
          271  +        null null]
          272  +
          273  +    $connection SetTypeCallbacks SPECIAL $typeCallbacks
          274  +
          275  +    set dataReader [sql execute -execute reader -format datareader \
          276  +        -alias $db "SELECT y FROM t1 ORDER BY x;"]
          277  +
          278  +    while {[$dataReader Read]} {
          279  +      if {$isArray} then {
          280  +        set buffer [object invoke \
          281  +            -create Array CreateInstance $typeName 100]
          282  +
          283  +        if {[catch {
          284  +          $dataReader $methodName 0 0 $buffer 0 1
          285  +        } value] == 0} then {
          286  +          lappend result $value
          287  +
          288  +          if {$value > 0} then {
          289  +            set list [object create -alias StringList $buffer]
          290  +
          291  +            lappend result [object invoke StringList GetRange \
          292  +                $list 0 [expr {$value - 1}] false]
          293  +          }
          294  +        } else {
          295  +          lappend result [list error(array) $::errorCode]
          296  +        }
          297  +      } else {
          298  +        if {[catch {
          299  +          $dataReader $methodName 0
          300  +        } value] == 0} then {
          301  +          if {$value eq "\x00"} then {
          302  +            lappend result <NUL>
          303  +          } else {
          304  +            lappend result [getStringFromObjectHandle $value]
          305  +          }
          306  +        } else {
          307  +          lappend result [list error(value) $::errorCode]
          308  +        }
          309  +      }
          310  +    }
          311  +
          312  +    set result
          313  +  } -cleanup {
          314  +    catch {object removecallback $callback}
          315  +
          316  +    unset -nocomplain dataReader
          317  +    freeDbConnection
          318  +
          319  +    cleanupDb $fileName
          320  +
          321  +    unset -nocomplain buffer typeCallbacks callback value list
          322  +    unset -nocomplain result connection db fileName
          323  +    unset -nocomplain typeName methodName isArray
          324  +  } -constraints {eagle command.object monoBug28 command.sql compile.DATA\
          325  +SQLite System.Data.SQLite} -result $expectedResult}
          326  +
          327  +  rename readValueCallback2 ""
          328  +}
          329  +
          330  +###############################################################################
          331  +
          332  +object invoke Interpreter.GetActive DateTimeFormat $savedDateTimeFormat
          333  +unset -nocomplain savedDateTimeFormat
          334  +
          335  +###############################################################################
          336  +
          337  +unset -nocomplain i readArgs params typeName methodName isArray propertyName \
          338  +    isRequired expectedResults expectedResult
          339  +
          340  +###############################################################################
          341  +
          342  +runTest {test types-3.1 {bind callback (incomplete)} -setup {
          343  +  unset -nocomplain log
          344  +
          345  +  setupDb [set fileName types-3.1.db] "" "" "" \
          346  +      "UseConnectionBindValueCallbacks UseParameterNameForTypeName"
          347  +} -body {
          348  +  sql execute $db {
          349  +    CREATE TABLE t1(x SPECIAL);
          350  +  }
          351  +
          352  +  set callback {-callbackflags +Default bindValueCallback1}
          353  +  set connection [getDbConnection]
          354  +
          355  +  set typeCallbacks [object invoke -marshalflags +DynamicCallback \
          356  +      System.Data.SQLite.SQLiteTypeCallbacks Create $callback null \
          357  +      0 null]
          358  +
          359  +  $connection SetTypeCallbacks SPECIAL $typeCallbacks
          360  +
          361  +  sql execute $db {
          362  +    INSERT INTO t1 (x) VALUES(?);
          363  +  } [list Special Int64 1234]
          364  +
          365  +  set result [list]
          366  +
          367  +  lappend result [expr {
          368  +    [info exists log(bind)] ? $log(bind) : "<MISSING>"
          369  +  }]
          370  +
          371  +  lappend result [sql execute -execute reader -format list $db \
          372  +      "SELECT * FROM t1 ORDER BY x;"]
          373  +
          374  +  set result
          375  +} -cleanup {
          376  +  catch {object removecallback $callback}
          377  +
          378  +  freeDbConnection
          379  +
          380  +  cleanupDb $fileName
          381  +
          382  +  unset -nocomplain result typeCallbacks callback log connection db fileName
          383  +} -constraints {eagle command.object monoBug28 command.sql compile.DATA SQLite\
          384  +System.Data.SQLite} -result {{{convert true} {command true} {flags\
          385  +{UseConnectionBindValueCallbacks, UseParameterNameForTypeName}} {parameter\
          386  +true} {typeName Special} {index 1} {userData 0}} 1234}}
          387  +
          388  +###############################################################################
          389  +
          390  +runTest {test types-3.2 {bind callback (complete)} -setup {
          391  +  unset -nocomplain log
          392  +
          393  +  setupDb [set fileName types-3.2.db] "" "" "" \
          394  +      "UseConnectionBindValueCallbacks UseParameterNameForTypeName"
          395  +} -body {
          396  +  sql execute $db {
          397  +    CREATE TABLE t1(x SPECIAL);
          398  +  }
          399  +
          400  +  set callback {-callbackflags +Default bindValueCallback1}
          401  +  set connection [getDbConnection]
          402  +
          403  +  set typeCallbacks [object invoke -marshalflags +DynamicCallback \
          404  +      System.Data.SQLite.SQLiteTypeCallbacks Create $callback null \
          405  +      1 null]
          406  +
          407  +  $connection SetTypeCallbacks SPECIAL $typeCallbacks
          408  +
          409  +  sql execute $db {
          410  +    INSERT INTO t1 (x) VALUES(?);
          411  +  } [list Special Int64 5678]
          412  +
          413  +  set result [list]
          414  +
          415  +  lappend result [expr {
          416  +    [info exists log(bind)] ? $log(bind) : "<MISSING>"
          417  +  }]
          418  +
          419  +  lappend result [sql execute -execute reader -format list $db \
          420  +      "SELECT * FROM t1 ORDER BY x;"]
          421  +
          422  +  set result
          423  +} -cleanup {
          424  +  catch {object removecallback $callback}
          425  +
          426  +  freeDbConnection
          427  +
          428  +  cleanupDb $fileName
          429  +
          430  +  unset -nocomplain result typeCallbacks callback log connection db fileName
          431  +} -constraints {eagle command.object monoBug28 command.sql compile.DATA SQLite\
          432  +System.Data.SQLite} -result {{{convert true} {command true} {flags\
          433  +{UseConnectionBindValueCallbacks, UseParameterNameForTypeName}} {parameter\
          434  +true} {typeName Special} {index 1} {userData 1}} {}}}
          435  +
          436  +###############################################################################
          437  +
          438  +runTest {test types-3.3 {bind callback (modify/incomplete)} -setup {
          439  +  unset -nocomplain log
          440  +
          441  +  setupDb [set fileName types-3.3.db] "" "" "" \
          442  +      "UseConnectionBindValueCallbacks UseParameterNameForTypeName"
          443  +} -body {
          444  +  sql execute $db {
          445  +    CREATE TABLE t1(x SPECIAL);
          446  +  }
          447  +
          448  +  set callback {-callbackflags +Default bindValueCallback1}
          449  +  set connection [getDbConnection]
          450  +
          451  +  set typeCallbacks [object invoke -marshalflags +DynamicCallback \
          452  +      System.Data.SQLite.SQLiteTypeCallbacks Create $callback null \
          453  +      2 null]
          454  +
          455  +  $connection SetTypeCallbacks SPECIAL $typeCallbacks
          456  +
          457  +  sql execute $db {
          458  +    INSERT INTO t1 (x) VALUES(?);
          459  +  } [list Special Int64 9999]
          460  +
          461  +  set result [list]
          462  +
          463  +  lappend result [expr {
          464  +    [info exists log(bind)] ? $log(bind) : "<MISSING>"
          465  +  }]
          466  +
          467  +  lappend result [sql execute -execute reader -format list $db \
          468  +      "SELECT * FROM t1 ORDER BY x;"]
          469  +
          470  +  set result
          471  +} -cleanup {
          472  +  catch {object removecallback $callback}
          473  +
          474  +  freeDbConnection
          475  +
          476  +  cleanupDb $fileName
          477  +
          478  +  unset -nocomplain result typeCallbacks callback log connection db fileName
          479  +} -constraints {eagle command.object monoBug28 command.sql compile.DATA SQLite\
          480  +System.Data.SQLite} -result {{{convert true} {command true} {flags\
          481  +{UseConnectionBindValueCallbacks, UseParameterNameForTypeName}} {parameter\
          482  +true} {typeName Special} {index 1} {userData 2}} custom}}
          483  +
          484  +###############################################################################
          485  +
          486  +runTest {test types-4.1 {read callback (exception)} -setup {
          487  +  unset -nocomplain log
          488  +
          489  +  setupDb [set fileName types-4.1.db] "" "" "" \
          490  +      UseConnectionReadValueCallbacks
          491  +} -body {
          492  +  sql execute $db {
          493  +    CREATE TABLE t1(x SPECIAL);
          494  +    INSERT INTO t1 (x) VALUES(8888);
          495  +  }
          496  +
          497  +  set callback {-callbackflags {+Default ThrowOnError} readValueCallback1}
          498  +  set connection [getDbConnection]
          499  +
          500  +  set typeCallbacks [object invoke -marshalflags +DynamicCallback \
          501  +      System.Data.SQLite.SQLiteTypeCallbacks Create null $callback \
          502  +      null 3]
          503  +
          504  +  $connection SetTypeCallbacks SPECIAL $typeCallbacks
          505  +
          506  +  set result [list]
          507  +
          508  +  lappend result [catch {
          509  +    sql execute -execute reader -format list $db {SELECT * FROM t1 ORDER BY x;}
          510  +  } error]
          511  +
          512  +  lappend result [extractSystemDataSQLiteExceptionMessage $error]
          513  +
          514  +  lappend result [catch {
          515  +    sql execute -execute scalar $db {SELECT COUNT(*) FROM t1;}
          516  +  } error]
          517  +
          518  +  lappend result [extractSystemDataSQLiteExceptionMessage $error]
          519  +  lappend result [expr {[info exists log(read)] ? $log(read) : "<MISSING>"}]
          520  +
          521  +  set result
          522  +} -cleanup {
          523  +  catch {object removecallback $callback}
          524  +
          525  +  freeDbConnection
          526  +
          527  +  cleanupDb $fileName
          528  +
          529  +  unset -nocomplain error result typeCallbacks callback log connection db \
          530  +      fileName
          531  +} -constraints {eagle command.object monoBug28 command.sql compile.DATA SQLite\
          532  +System.Data.SQLite} -result {1 {reading of value canceled} 0 1 {{convert true}\
          533  +{dataReader true} {flags UseConnectionReadValueCallbacks} {eventArgs true}\
          534  +{typeName SPECIAL} {index 0} {userData 3}}}}
          535  +
          536  +###############################################################################
          537  +
          538  +runTest {test types-5.1 {bind callback (exception)} -setup {
          539  +  unset -nocomplain log
          540  +
          541  +  setupDb [set fileName types-5.1.db] "" "" "" \
          542  +      "UseConnectionBindValueCallbacks UseParameterNameForTypeName"
          543  +} -body {
          544  +  sql execute $db {
          545  +    CREATE TABLE t1(x SPECIAL);
          546  +  }
          547  +
          548  +  set callback {-callbackflags {+Default ThrowOnError} bindValueCallback1}
          549  +  set connection [getDbConnection]
          550  +
          551  +  set typeCallbacks [object invoke -marshalflags +DynamicCallback \
          552  +      System.Data.SQLite.SQLiteTypeCallbacks Create $callback null \
          553  +      3 null]
          554  +
          555  +  $connection SetTypeCallbacks SPECIAL $typeCallbacks
          556  +
          557  +  catch {
          558  +    sql execute $db {
          559  +      INSERT INTO t1 (x) VALUES(?);
          560  +    } [list Special Int64 4321]
          561  +  }
          562  +
          563  +  set result [list]
          564  +
          565  +  lappend result [expr {
          566  +    [info exists log(bind)] ? $log(bind) : "<MISSING>"
          567  +  }]
          568  +
          569  +  lappend result [sql execute -execute reader -format list $db \
          570  +      "SELECT * FROM t1 ORDER BY x;"]
          571  +
          572  +  set result
          573  +} -cleanup {
          574  +  catch {object removecallback $callback}
          575  +
          576  +  freeDbConnection
          577  +
          578  +  cleanupDb $fileName
          579  +
          580  +  unset -nocomplain result typeCallbacks callback log connection db fileName
          581  +} -constraints {eagle command.object monoBug28 command.sql compile.DATA SQLite\
          582  +System.Data.SQLite} -result {{{convert true} {command true} {flags\
          583  +{UseConnectionBindValueCallbacks, UseParameterNameForTypeName}} {parameter\
          584  +true} {typeName Special} {index 1} {userData 3}} {}}}
          585  +
          586  +###############################################################################
          587  +
          588  +catch {eval object dispose [info objects System#Boolean#*]}
          589  +
          590  +###############################################################################
          591  +
          592  +rename readValueCallback1 ""
          593  +rename bindValueCallback1 ""
          594  +
          595  +###############################################################################
          596  +
          597  +runSQLiteTestEpilogue
          598  +runTestEpilogue

Changes to lib/System.Data.SQLite/common.eagle.

  1823   1823           #       System.Data.SQLite, and suitable for round-tripping with the
  1824   1824           #       DateTime class of the framework.  If this value is changed,
  1825   1825           #       various tests may fail.
  1826   1826           #
  1827   1827           return "yyyy-MM-dd HH:mm:ss.FFFFFFFK"
  1828   1828         }
  1829   1829       }
         1830  +
         1831  +    proc getProperties { object varName } {
         1832  +      upvar 1 $varName properties
         1833  +
         1834  +      set count 0
         1835  +      set names [list]
         1836  +
         1837  +      if {[isObjectHandle $object] && $object ne "null"} then {
         1838  +        eval lappend names [object members \
         1839  +            -membertypes Property -nameonly $object]
         1840  +
         1841  +        eval lappend names [object members \
         1842  +            -membertypes Field -nameonly $object]
         1843  +      }
         1844  +
         1845  +      foreach name $names {
         1846  +        if {[catch {
         1847  +          object invoke -objectflags +NoDispose $object $name
         1848  +        } value] == 0} then {
         1849  +          if {[isObjectHandle $value] && $value ne "null"} then {
         1850  +            set error null; object invoke -flags +NonPublic \
         1851  +                -marshalflags +NoHandle Interpreter.GetActive \
         1852  +                AddObjectReference Ok $value error
         1853  +
         1854  +            lappend properties(objects) [list $name $value]
         1855  +          } else {
         1856  +            lappend properties(values) [list $name $value]
         1857  +          }
         1858  +
         1859  +          incr count
         1860  +        } else {
         1861  +          lappend properties(errors) [list $name $::errorCode]
         1862  +        }
         1863  +      }
         1864  +
         1865  +      return $count
         1866  +    }
         1867  +
         1868  +    proc getAllProperties { object varName } {
         1869  +      upvar 1 $varName properties
         1870  +      set value $object
         1871  +
         1872  +      while {true} {
         1873  +        if {![info exists properties(seenObjects)] || \
         1874  +            $value ni $properties(seenObjects)} then {
         1875  +          getProperties $value properties
         1876  +          lappend properties(seenObjects) $value
         1877  +        }
         1878  +
         1879  +        if {![info exists properties(objects)]} then {
         1880  +          break
         1881  +        }
         1882  +
         1883  +        if {[llength $properties(objects)] == 0} then {
         1884  +          unset properties(objects); break
         1885  +        }
         1886  +
         1887  +        set value [lindex [lindex $properties(objects) 0] end]
         1888  +        set properties(objects) [lrange $properties(objects) 1 end]
         1889  +      }
         1890  +
         1891  +      if {[info exists properties(seenObjects)]} then {
         1892  +        foreach value $properties(seenObjects) {
         1893  +          if {$value eq $object} continue
         1894  +          catch {object dispose $value}
         1895  +        }
         1896  +
         1897  +        unset properties(seenObjects)
         1898  +      }
         1899  +    }
         1900  +
         1901  +    proc getVariables { varNames {objects false} } {
         1902  +      set result [list]
         1903  +
         1904  +      foreach varName $varNames {
         1905  +        if {[uplevel 1 [list array exists $varName]]} then {
         1906  +          set arrayName $varName
         1907  +
         1908  +          foreach elementName [uplevel 1 [list array names $arrayName]] {
         1909  +            set name [appendArgs $arrayName ( $elementName )]
         1910  +            set varValue [uplevel 1 [list set $name]]
         1911  +
         1912  +            if {$objects && [isObjectHandle $varValue]} then {
         1913  +              unset -nocomplain properties
         1914  +              getAllProperties $varValue properties
         1915  +
         1916  +              lappend result [list $name [array get properties]]
         1917  +            } else {
         1918  +              lappend result [list $name $varValue]
         1919  +            }
         1920  +          }
         1921  +        } else {
         1922  +          set varValue [uplevel 1 [list set $varName]]
         1923  +
         1924  +          if {$objects && [isObjectHandle $varValue]} then {
         1925  +            unset -nocomplain properties
         1926  +            getAllProperties $varValue properties
         1927  +
         1928  +            lappend result [list $varName [array get properties]]
         1929  +          } else {
         1930  +            lappend result [list $varName $varValue]
         1931  +          }
         1932  +        }
         1933  +      }
         1934  +
         1935  +      return $result
         1936  +    }
  1830   1937   
  1831   1938       proc enumerableToList { enumerable } {
  1832   1939         set result [list]
  1833   1940   
  1834   1941         if {[string length $enumerable] == 0 || $enumerable eq "null"} then {
  1835   1942           return $result
  1836   1943         }
................................................................................
  2006   2113         #
  2007   2114         # NOTE: If the string conforms to format of the normal exception
  2008   2115         #       error strings, extract and return only the error message
  2009   2116         #       portion itself.
  2010   2117         #
  2011   2118         set patterns [list \
  2012   2119         {System\.Data\.SQLite\.SQLiteException \(0x80004005\): (.+?)  (?: )?at} \
  2013         -      {System\.Data\.SQLite\.SQLiteException: (.+?)  (?: )?at}]
         2120  +      {System\.Data\.SQLite\.SQLiteException: (.+?)  (?: )?at} \
         2121  +      {Eagle\._Components\.Public\.ScriptException: (.+?)  (?: )?at}]
  2014   2122   
  2015   2123         foreach pattern $patterns {
  2016   2124           if {[regexp -- $pattern $value dummy message]} then {
  2017   2125             set message [string map [list \r\n \n] [string trim $message]]
  2018   2126             set lines [split $message \n]
  2019   2127   
  2020   2128             if {[llength $lines] == 2} then {
................................................................................
  2080   2188               ![array exists ::dataSource]} then {
  2081   2189             set database [appendArgs "data source \"" $::dataSource \"]
  2082   2190           } else {
  2083   2191             set database <unknown>
  2084   2192           }
  2085   2193         }
  2086   2194   
         2195  +      #
         2196  +      # NOTE: Even though there is only one source of flags so far, they
         2197  +      #       must be combined using the correct syntax for enumerated
         2198  +      #       flag values for the .NET Framework.
         2199  +      #
         2200  +      set flags [combineFlags $flags ""]
         2201  +
  2087   2202         #
  2088   2203         # NOTE: Show (and log) the local connection flags and the associated
  2089   2204         #       data source or file name.
  2090   2205         #
  2091   2206         if {!$quiet} then {
  2092   2207           if {![info exists ::no(emitLocalFlags)] && \
  2093   2208               (![info exists ::no(emitLocalFlagsIfNone)] || \

Changes to readme.htm.

   211    211   <p>
   212    212       <b>1.0.102.0 - June XX, 2016 <font color="red">(release scheduled)</font></b>
   213    213   </p>
   214    214   <ul>
   215    215       <li>Updated to <a href="https://www.sqlite.org/releaselog/3_13_0.html">SQLite 3.13.0</a>.</li>
   216    216       <li>Update the SQLiteConnection.EnableExtensions method to make use of the new SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION option, when available.&nbsp;<b>** Potentially Incompatible Change **</b></li>
   217    217       <li>Prevent the SQLiteCommand.ExecuteScalar method from throwing an exception when there are no result columns.&nbsp;<b>** Potentially Incompatible Change **</b></li>
          218  +    <li>Support per-connection customization for binding parameters and reading values, based on the database type name.</li>
          219  +    <li>Add TypeName property to the SQLiteParameter class.</li>
   218    220       <li>Add VerifyOnly method to the SQLiteCommand class.</li>
   219    221       <li>Add IsReadOnly method to the SQLiteConnection class.</li>
   220    222   </ul>
   221    223   <p>
   222    224       <b>1.0.101.0 - April 19, 2016</b>
   223    225   </p>
   224    226   <ul>

Changes to www/news.wiki.

     5      5   <p>
     6      6       <b>1.0.102.0 - June XX, 2016 <font color="red">(release scheduled)</font></b>
     7      7   </p>
     8      8   <ul>
     9      9       <li>Updated to [https://www.sqlite.org/releaselog/3_13_0.html|SQLite 3.13.0].</li>
    10     10       <li>Update the SQLiteConnection.EnableExtensions method to make use of the new SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION option, when available.&nbsp;<b>** Potentially Incompatible Change **</b></li>
    11     11       <li>Prevent the SQLiteCommand.ExecuteScalar method from throwing an exception when there are no result columns.&nbsp;<b>** Potentially Incompatible Change **</b></li>
           12  +    <li>Support per-connection customization for binding parameters and reading values, based on the database type name.</li>
           13  +    <li>Add TypeName property to the SQLiteParameter class.</li>
    12     14       <li>Add VerifyOnly method to the SQLiteCommand class.</li>
    13     15       <li>Add IsReadOnly method to the SQLiteConnection class.</li>
    14     16   </ul>
    15     17   <p>
    16     18       <b>1.0.101.0 - April 19, 2016</b>
    17     19   </p>
    18     20   <ul>