System.Data.SQLite

Check-in [02ed8cae60]
Login

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

Overview
Comment:Add HidePassword connection flag to remove the password from the connection string once the database is opened. Pursuant to [23d8d6171e].
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 02ed8cae60ffa75c6a9572392e354c970dbecf85
User & Date: mistachkin 2018-12-23 04:56:32.806
Context
2018-12-23
18:32
Use the EventArg connection string for debug tracing. check-in: 4e730fa902 user: mistachkin tags: trunk
04:56
Add HidePassword connection flag to remove the password from the connection string once the database is opened. Pursuant to [23d8d6171e]. check-in: 02ed8cae60 user: mistachkin tags: trunk
00:01
Add experimental StrictConformance connection flag to force strict compliance to the ADO.NET standard. Pursuant to [e36e05e299]. check-in: 4012cc2587 user: mistachkin tags: trunk
Changes
Unified Diff Ignore Whitespace Patch
Changes to Doc/Extra/Provider/version.html.
42
43
44
45
46
47
48

49
50
51
52
53
54
55
    </div>
    <div id="mainSection">
    <div id="mainBody">
    <h1 class="heading">Version History</h1>
    <p><b>1.0.110.0 - December XX, 2018 <font color="red">(release scheduled)</font></b></p>
    <ul>
      <li>Updated to <a href="https://www.sqlite.org/releaselog/3_26_0.html">SQLite 3.26.0</a>.</li>

      <li>Add experimental StrictConformance connection flag to force strict compliance to the ADO.NET standard. Pursuant to <a href="https://system.data.sqlite.org/index.html/info/e36e05e299">[e36e05e299]</a>.</li>
    </ul>
    <p><b>1.0.109.0 - August 15, 2018</b></p>
    <ul>
      <li>Updated to <a href="https://www.sqlite.org/releaselog/3_24_0.html">SQLite 3.24.0</a>.</li>
      <li>Updated to <a href="https://www.nuget.org/packages/EntityFramework/6.2.0">Entity Framework 6.2.0</a>.</li>
      <li>Do not attempt to initialize the logging subsystem more than once.&nbsp;<b>** Potentially Incompatible Change **</b></li>







>







42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
    </div>
    <div id="mainSection">
    <div id="mainBody">
    <h1 class="heading">Version History</h1>
    <p><b>1.0.110.0 - December XX, 2018 <font color="red">(release scheduled)</font></b></p>
    <ul>
      <li>Updated to <a href="https://www.sqlite.org/releaselog/3_26_0.html">SQLite 3.26.0</a>.</li>
      <li>Add HidePassword connection flag to remove the password from the connection string once the database is opened. Pursuant to <a href="https://system.data.sqlite.org/index.html/info/23d8d6171e">[23d8d6171e]</a>.</li>
      <li>Add experimental StrictConformance connection flag to force strict compliance to the ADO.NET standard. Pursuant to <a href="https://system.data.sqlite.org/index.html/info/e36e05e299">[e36e05e299]</a>.</li>
    </ul>
    <p><b>1.0.109.0 - August 15, 2018</b></p>
    <ul>
      <li>Updated to <a href="https://www.sqlite.org/releaselog/3_24_0.html">SQLite 3.24.0</a>.</li>
      <li>Updated to <a href="https://www.nuget.org/packages/EntityFramework/6.2.0">Entity Framework 6.2.0</a>.</li>
      <li>Do not attempt to initialize the logging subsystem more than once.&nbsp;<b>** Potentially Incompatible Change **</b></li>
Changes to Setup/data/verify.lst.
865
866
867
868
869
870
871

872
873
874
875
876
877
878
  Tests/tkt-0d5b1ef362.eagle
  Tests/tkt-0e48e80333.eagle
  Tests/tkt-0ed01c447c.eagle
  Tests/tkt-17045010df.eagle
  Tests/tkt-1c456ae75f.eagle
  Tests/tkt-1f7bfff467.eagle
  Tests/tkt-201128cc88.eagle

  Tests/tkt-2556655d1b.eagle
  Tests/tkt-2abbf2c244.eagle
  Tests/tkt-2c630bffa7.eagle
  Tests/tkt-2ce0870fad.eagle
  Tests/tkt-3113734605.eagle
  Tests/tkt-343d392b51.eagle
  Tests/tkt-3567020edf.eagle







>







865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
  Tests/tkt-0d5b1ef362.eagle
  Tests/tkt-0e48e80333.eagle
  Tests/tkt-0ed01c447c.eagle
  Tests/tkt-17045010df.eagle
  Tests/tkt-1c456ae75f.eagle
  Tests/tkt-1f7bfff467.eagle
  Tests/tkt-201128cc88.eagle
  Tests/tkt-23d8d6171e.eagle
  Tests/tkt-2556655d1b.eagle
  Tests/tkt-2abbf2c244.eagle
  Tests/tkt-2c630bffa7.eagle
  Tests/tkt-2ce0870fad.eagle
  Tests/tkt-3113734605.eagle
  Tests/tkt-343d392b51.eagle
  Tests/tkt-3567020edf.eagle
Changes to System.Data.SQLite/SQLite3.cs.
3194
3195
3196
3197
3198
3199
3200













3201
3202
3203




3204
3205
3206
3207
3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
3219
3220
3221




3222
3223
3224
3225
3226
3227
3228
    /// Add a log message via the SQLite sqlite3_log interface.
    internal static void StaticLogMessage(SQLiteErrorCode iErrCode, string zMessage)
    {
      UnsafeNativeMethods.sqlite3_log(iErrCode, ToUTF8(zMessage));
    }

#if INTEROP_CODEC || INTEROP_INCLUDE_SEE













    internal override void SetPassword(byte[] passwordBytes)
    {
      SQLiteErrorCode n = UnsafeNativeMethods.sqlite3_key(_sql, passwordBytes, passwordBytes.Length);




      if (n != SQLiteErrorCode.Ok) throw new SQLiteException(n, GetLastError());

      if (_usePool)
      {
        _usePool = false;

#if !NET_COMPACT_20 && TRACE_CONNECTION
        Trace.WriteLine(HelperMethods.StringFormat(
          CultureInfo.CurrentCulture,
          "SetPassword (Pool) Disabled: {0}",
          HandleToString()));
#endif
      }
    }

    internal override void ChangePassword(byte[] newPasswordBytes)
    {
      SQLiteErrorCode n = UnsafeNativeMethods.sqlite3_rekey(_sql, newPasswordBytes, (newPasswordBytes == null) ? 0 : newPasswordBytes.Length);




      if (n != SQLiteErrorCode.Ok) throw new SQLiteException(n, GetLastError());

      if (_usePool)
      {
        _usePool = false;

#if !NET_COMPACT_20 && TRACE_CONNECTION







>
>
>
>
>
>
>
>
>
>
>
>
>



>
>
>
>


















>
>
>
>







3194
3195
3196
3197
3198
3199
3200
3201
3202
3203
3204
3205
3206
3207
3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
3219
3220
3221
3222
3223
3224
3225
3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
    /// Add a log message via the SQLite sqlite3_log interface.
    internal static void StaticLogMessage(SQLiteErrorCode iErrCode, string zMessage)
    {
      UnsafeNativeMethods.sqlite3_log(iErrCode, ToUTF8(zMessage));
    }

#if INTEROP_CODEC || INTEROP_INCLUDE_SEE
    private static void ZeroPassword(byte[] passwordBytes)
    {
        if (passwordBytes == null) return;

        for (int index = 0; index < passwordBytes.Length; index++)
        {
            byte value = (byte)((index + 1) % byte.MaxValue);

            passwordBytes[index] = value;
            passwordBytes[index] ^= value;
        }
    }

    internal override void SetPassword(byte[] passwordBytes)
    {
      SQLiteErrorCode n = UnsafeNativeMethods.sqlite3_key(_sql, passwordBytes, passwordBytes.Length);

      if (HelperMethods.HasFlags(_flags, SQLiteConnectionFlags.HidePassword))
        ZeroPassword(passwordBytes);

      if (n != SQLiteErrorCode.Ok) throw new SQLiteException(n, GetLastError());

      if (_usePool)
      {
        _usePool = false;

#if !NET_COMPACT_20 && TRACE_CONNECTION
        Trace.WriteLine(HelperMethods.StringFormat(
          CultureInfo.CurrentCulture,
          "SetPassword (Pool) Disabled: {0}",
          HandleToString()));
#endif
      }
    }

    internal override void ChangePassword(byte[] newPasswordBytes)
    {
      SQLiteErrorCode n = UnsafeNativeMethods.sqlite3_rekey(_sql, newPasswordBytes, (newPasswordBytes == null) ? 0 : newPasswordBytes.Length);

      if (HelperMethods.HasFlags(_flags, SQLiteConnectionFlags.HidePassword))
        ZeroPassword(newPasswordBytes);

      if (n != SQLiteErrorCode.Ok) throw new SQLiteException(n, GetLastError());

      if (_usePool)
      {
        _usePool = false;

#if !NET_COMPACT_20 && TRACE_CONNECTION
Changes to System.Data.SQLite/SQLiteBase.cs.
1325
1326
1327
1328
1329
1330
1331









1332
1333
1334
1335
1336
1337
1338
      /// <summary>
      /// <b>EXPERIMENTAL</b> --
      /// Enable strict conformance to the ADO.NET standard, e.g. use of
      /// thrown exceptions to indicate common error conditions.
      /// </summary>
      StrictConformance = 0x800000000000,










      /// <summary>
      /// When binding parameter values or returning column values, always
      /// treat them as though they were plain text (i.e. no numeric,
      /// date/time, or other conversions should be attempted).
      /// </summary>
      BindAndGetAllAsText = BindAllAsText | GetAllAsText,








>
>
>
>
>
>
>
>
>







1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
      /// <summary>
      /// <b>EXPERIMENTAL</b> --
      /// Enable strict conformance to the ADO.NET standard, e.g. use of
      /// thrown exceptions to indicate common error conditions.
      /// </summary>
      StrictConformance = 0x800000000000,

      /// <summary>
      /// <b>EXPERIMENTAL</b> --
      /// When opening a connection, attempt to hide the password from the
      /// connection string, etc.  Given the memory architecture of the CLR,
      /// (and P/Invoke) this is not 100% reliable and should not be relied
      /// upon for security critical uses or applications.
      /// </summary>
      HidePassword = 0x1000000000000,

      /// <summary>
      /// When binding parameter values or returning column values, always
      /// treat them as though they were plain text (i.e. no numeric,
      /// date/time, or other conversions should be attempted).
      /// </summary>
      BindAndGetAllAsText = BindAllAsText | GetAllAsText,

Changes to System.Data.SQLite/SQLiteConnection.cs.
2643
2644
2645
2646
2647
2648
2649



























































































































2650
2651
2652
2653
2654
2655
2656
        return parseViaFramework ?
            ParseConnectionStringViaFramework(connection, connectionString, false) :
            ParseConnectionString(connection, connectionString, allowNameOnly);
    }

    ///////////////////////////////////////////////////////////////////////////////////////////////




























































































































    private void SetupSQLiteBase(SortedList<string, string> opts)
    {
        object enumValue;

        enumValue = TryParseEnum(
            typeof(SQLiteDateFormats), FindKey(opts, "DateTimeFormat",
            DefaultDateTimeFormat.ToString()), true);







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
        return parseViaFramework ?
            ParseConnectionStringViaFramework(connection, connectionString, false) :
            ParseConnectionString(connection, connectionString, allowNameOnly);
    }

    ///////////////////////////////////////////////////////////////////////////////////////////////

#if INTEROP_CODEC || INTEROP_INCLUDE_SEE
    /// <summary>
    /// Attempts to escape the specified connection string property name or
    /// value in a way that is compatible with the connection string parser.
    /// </summary>
    /// <param name="value">
    /// The connection string property name or value to escape.
    /// </param>
    /// <param name="allowEquals">
    /// Non-zero if the equals sign is permitted in the string.  If this is
    /// zero and the string contains an equals sign, an exception will be
    /// thrown.
    /// </param>
    /// <returns>
    /// The original string, with all special characters escaped.  If the
    /// original string contains equals signs, they will not be escaped.
    /// Instead, they will be preserved verbatim.
    /// </returns>
    private static string EscapeForConnectionString(
        string value,
        bool allowEquals
        )
    {
        if (String.IsNullOrEmpty(value))
            return value;

        if (value.IndexOfAny(SQLiteConvert.SpecialChars) == -1)
            return value;

        int length = value.Length;
        StringBuilder builder = new StringBuilder(length);

        for (int index = 0; index < length; index++)
        {
            char character = value[index];

            switch (character)
            {
                case SQLiteConvert.QuoteChar:
                case SQLiteConvert.AltQuoteChar:
                case SQLiteConvert.PairChar:
                case SQLiteConvert.EscapeChar:
                    {
                        builder.Append(SQLiteConvert.EscapeChar);
                        builder.Append(character);
                        break;
                    }
                case SQLiteConvert.ValueChar:
                    {
                        if (allowEquals)
                        {
                            //
                            // HACK: The connection string parser allows
                            //       connection string property values
                            //       to contain equals signs; however,
                            //       they cannot be escaped.
                            //
                            // builder.Append(SQLiteConvert.EscapeChar);
                            builder.Append(character);
                        }
                        else
                        {
                            throw new ArgumentException(
                                "equals sign character is not allowed here");
                        }
                        break;
                    }
                default:
                    {
                        builder.Append(character);
                        break;
                    }
            }
        }

        return builder.ToString();
    }

    ///////////////////////////////////////////////////////////////////////////////////////////////

    /// <summary>
    /// Builds a connection string from a list of key/value pairs.
    /// </summary>
    /// <param name="opts">
    /// The list of key/value pairs corresponding to the parameters to be
    /// specified within the connection string.
    /// </param>
    /// <returns>
    /// The connection string.  Depending on how the connection string was
    /// originally parsed, the returned connection string value may not be
    /// usable in a subsequent call to the <see cref="Open" /> method.
    /// </returns>
    private static string BuildConnectionString(
        SortedList<string, string> opts
        )
    {
        if (opts == null) return null;
        StringBuilder builder = new StringBuilder();

        foreach (KeyValuePair<string, string> pair in opts)
        {
#if NET_COMPACT_20
            builder.Append(HelperMethods.StringFormat(
                CultureInfo.InvariantCulture, "{0}{1}{2}{3}",
                EscapeForConnectionString(pair.Key, false),
                SQLiteConvert.ValueChar,
                EscapeForConnectionString(pair.Value, true),
                SQLiteConvert.PairChar));
#else
            builder.AppendFormat("{0}{1}{2}{3}",
                EscapeForConnectionString(pair.Key, false),
                SQLiteConvert.ValueChar,
                EscapeForConnectionString(pair.Value, true),
                SQLiteConvert.PairChar);
#endif
        }

        return builder.ToString();
    }
#endif

    ///////////////////////////////////////////////////////////////////////////////////////////////

    private void SetupSQLiteBase(SortedList<string, string> opts)
    {
        object enumValue;

        enumValue = TryParseEnum(
            typeof(SQLiteDateFormats), FindKey(opts, "DateTimeFormat",
            DefaultDateTimeFormat.ToString()), true);
3356
3357
3358
3359
3360
3361
3362
3363
3364
3365
3366
3367
3368
3369
3370
3371
3372
      SortedList<string, string> ls = new SortedList<string, string>(StringComparer.OrdinalIgnoreCase);

      // First split into semi-colon delimited values.
      string error = null;
      string[] arParts;

      if (ShouldUseLegacyConnectionStringParser(connection))
          arParts = SQLiteConvert.Split(s, ';');
      else
          arParts = SQLiteConvert.NewSplit(s, ';', true, ref error);

      if (arParts == null)
      {
          throw new ArgumentException(HelperMethods.StringFormat(
              CultureInfo.CurrentCulture,
              "Invalid ConnectionString format, cannot parse: {0}", (error != null) ?
              error : "could not split connection string into properties"));







|

|







3479
3480
3481
3482
3483
3484
3485
3486
3487
3488
3489
3490
3491
3492
3493
3494
3495
      SortedList<string, string> ls = new SortedList<string, string>(StringComparer.OrdinalIgnoreCase);

      // First split into semi-colon delimited values.
      string error = null;
      string[] arParts;

      if (ShouldUseLegacyConnectionStringParser(connection))
          arParts = SQLiteConvert.Split(s, SQLiteConvert.PairChar);
      else
          arParts = SQLiteConvert.NewSplit(s, SQLiteConvert.PairChar, true, ref error);

      if (arParts == null)
      {
          throw new ArgumentException(HelperMethods.StringFormat(
              CultureInfo.CurrentCulture,
              "Invalid ConnectionString format, cannot parse: {0}", (error != null) ?
              error : "could not split connection string into properties"));
3380
3381
3382
3383
3384
3385
3386
3387
3388
3389
3390
3391
3392
3393
3394
          continue;

        arParts[n] = arParts[n].Trim();

        if (arParts[n].Length == 0)
          continue;

        int indexOf = arParts[n].IndexOf('=');

        if (indexOf != -1)
          ls.Add(UnwrapString(arParts[n].Substring(0, indexOf).Trim()), UnwrapString(arParts[n].Substring(indexOf + 1).Trim()));
        else if (allowNameOnly)
          ls.Add(UnwrapString(arParts[n].Trim()), String.Empty);
        else
          throw new ArgumentException(HelperMethods.StringFormat(CultureInfo.CurrentCulture, "Invalid ConnectionString format for part \"{0}\", no equal sign found", arParts[n]));







|







3503
3504
3505
3506
3507
3508
3509
3510
3511
3512
3513
3514
3515
3516
3517
          continue;

        arParts[n] = arParts[n].Trim();

        if (arParts[n].Length == 0)
          continue;

        int indexOf = arParts[n].IndexOf(SQLiteConvert.ValueChar);

        if (indexOf != -1)
          ls.Add(UnwrapString(arParts[n].Substring(0, indexOf).Trim()), UnwrapString(arParts[n].Substring(indexOf + 1).Trim()));
        else if (allowNameOnly)
          ls.Add(UnwrapString(arParts[n].Trim()), String.Empty);
        else
          throw new ArgumentException(HelperMethods.StringFormat(CultureInfo.CurrentCulture, "Invalid ConnectionString format for part \"{0}\", no equal sign found", arParts[n]));
4079
4080
4081
4082
4083
4084
4085
4086
4087
4088
4089
4090
4091
4092
4093
4094
4095
4096
4097
4098
4099
        throw new InvalidOperationException();

      Close();

      SortedList<string, string> opts = ParseConnectionString(
          this, _connectionString, _parseViaFramework, false);

      OnChanged(this, new ConnectionEventArgs(
          SQLiteConnectionEventType.ConnectionString, null, null, null, null,
          null, _connectionString, new object[] { opts }));

      object enumValue;

      enumValue = TryParseEnum(typeof(SQLiteConnectionFlags), FindKey(opts, "Flags", null), true);

      //
      // BUGFIX: Always preserve the pre-existing instance flags.  This is OK
      //         because when the connection object is initially created, they
      //         are "None"; therefore, OR-ing the connection string property
      //         flags with the instance flags would produce exactly the same
      //         result.  If the "Flags" connection string property is absent,







<
<
<
<
<
<
|







4202
4203
4204
4205
4206
4207
4208






4209
4210
4211
4212
4213
4214
4215
4216
        throw new InvalidOperationException();

      Close();

      SortedList<string, string> opts = ParseConnectionString(
          this, _connectionString, _parseViaFramework, false);







      object enumValue = TryParseEnum(typeof(SQLiteConnectionFlags), FindKey(opts, "Flags", null), true);

      //
      // BUGFIX: Always preserve the pre-existing instance flags.  This is OK
      //         because when the connection object is initially created, they
      //         are "None"; therefore, OR-ing the connection string property
      //         flags with the instance flags would produce exactly the same
      //         result.  If the "Flags" connection string property is absent,
4109
4110
4111
4112
4113
4114
4115









































4116
4117
4118
4119
4120
4121
4122
          _flags |= (SQLiteConnectionFlags)enumValue;
      else if (!noDefaultFlags)
          _flags |= DefaultFlags;

      bool noSharedFlags = SQLiteConvert.ToBoolean(FindKey(opts, "NoSharedFlags", DefaultNoSharedFlags.ToString()));
      if (!noSharedFlags) { lock (_syncRoot) { _flags |= _sharedFlags; } }










































      enumValue = TryParseEnum(typeof(DbType), FindKey(opts, "DefaultDbType", null), true);
      _defaultDbType = (enumValue is DbType) ? (DbType)enumValue : (DbType?)null;

      //
      // NOTE: Nullable values types are not supported by the .NET Framework
      //       ADO.NET support components that work with the connection string
      //       builder; therefore, translate the "invalid value" used by the







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







4226
4227
4228
4229
4230
4231
4232
4233
4234
4235
4236
4237
4238
4239
4240
4241
4242
4243
4244
4245
4246
4247
4248
4249
4250
4251
4252
4253
4254
4255
4256
4257
4258
4259
4260
4261
4262
4263
4264
4265
4266
4267
4268
4269
4270
4271
4272
4273
4274
4275
4276
4277
4278
4279
4280
          _flags |= (SQLiteConnectionFlags)enumValue;
      else if (!noDefaultFlags)
          _flags |= DefaultFlags;

      bool noSharedFlags = SQLiteConvert.ToBoolean(FindKey(opts, "NoSharedFlags", DefaultNoSharedFlags.ToString()));
      if (!noSharedFlags) { lock (_syncRoot) { _flags |= _sharedFlags; } }

#if INTEROP_CODEC || INTEROP_INCLUDE_SEE
      bool hidePassword = HelperMethods.HasFlags(_flags, SQLiteConnectionFlags.HidePassword);
#endif

      SortedList<string, string> eventArgOpts = opts;
      string eventArgConnectionString = _connectionString;

#if INTEROP_CODEC || INTEROP_INCLUDE_SEE
      if (hidePassword)
      {
          eventArgOpts = new SortedList<string, string>(
              StringComparer.OrdinalIgnoreCase);

          foreach (KeyValuePair<string, string> pair in opts)
          {
              if (String.Equals(
                    pair.Key, "Password",
                    StringComparison.OrdinalIgnoreCase))
              {
                  continue;
              }

              if (String.Equals(
                    pair.Key, "HexPassword",
                    StringComparison.OrdinalIgnoreCase))
              {
                  continue;
              }

              eventArgOpts.Add(pair.Key, pair.Value);
          }

          eventArgConnectionString = BuildConnectionString(
              eventArgOpts);
      }
#endif

      OnChanged(this, new ConnectionEventArgs(
          SQLiteConnectionEventType.ConnectionString, null, null, null, null,
          null, eventArgConnectionString, new object[] { eventArgOpts }));

      enumValue = TryParseEnum(typeof(DbType), FindKey(opts, "DefaultDbType", null), true);
      _defaultDbType = (enumValue is DbType) ? (DbType)enumValue : (DbType?)null;

      //
      // NOTE: Nullable values types are not supported by the .NET Framework
      //       ADO.NET support components that work with the connection string
      //       builder; therefore, translate the "invalid value" used by the
4294
4295
4296
4297
4298
4299
4300




4301

4302

4303
4304



4305











4306
4307
4308
4309
4310
4311
4312
            _sql.SetPassword(hexPasswordBytes);
        }
        else
        {
            string password = FindKey(opts, "Password", DefaultPassword);

            if (password != null)




                _sql.SetPassword(UTF8Encoding.UTF8.GetBytes(password));

            else if (_password != null)

                _sql.SetPassword(_password);
        }



        _password = null;











#else
        if (FindKey(opts, "HexPassword", DefaultHexPassword) != null)
        {
            throw new SQLiteException(SQLiteErrorCode.Error,
                "Cannot use \"HexPassword\" connection string property: " +
                "library was not built with encryption support, please " +
                "see \"https://www.sqlite.org/see\" for more information");







>
>
>
>
|
>

>

|
>
>
>
|
>
>
>
>
>
>
>
>
>
>
>







4452
4453
4454
4455
4456
4457
4458
4459
4460
4461
4462
4463
4464
4465
4466
4467
4468
4469
4470
4471
4472
4473
4474
4475
4476
4477
4478
4479
4480
4481
4482
4483
4484
4485
4486
4487
4488
4489
4490
            _sql.SetPassword(hexPasswordBytes);
        }
        else
        {
            string password = FindKey(opts, "Password", DefaultPassword);

            if (password != null)
            {
                byte[] passwordBytes = UTF8Encoding.UTF8.GetBytes(
                    password); /* throw */

                _sql.SetPassword(passwordBytes);
            }
            else if (_password != null)
            {
                _sql.SetPassword(_password);
            }
        }

        hexPassword = null; /* IMMUTABLE */
        _password = null; /* IMMUTABLE */

        if (hidePassword)
        {
            if (opts.ContainsKey("HexPassword"))
                opts["HexPassword"] = String.Empty;

            if (opts.ContainsKey("Password"))
                opts["Password"] = String.Empty;

            _connectionString = BuildConnectionString(opts);
        }
#else
        if (FindKey(opts, "HexPassword", DefaultHexPassword) != null)
        {
            throw new SQLiteException(SQLiteErrorCode.Error,
                "Cannot use \"HexPassword\" connection string property: " +
                "library was not built with encryption support, please " +
                "see \"https://www.sqlite.org/see\" for more information");
4455
4456
4457
4458
4459
4460
4461
4462
4463
4464
4465
4466
4467
4468
4469
          _connectionState = oldstate;

          StateChangeEventArgs eventArgs = null;
          OnStateChange(ConnectionState.Open, ref eventArgs);

          OnChanged(this, new ConnectionEventArgs(
              SQLiteConnectionEventType.Opened, eventArgs, null, null, null,
              null, _connectionString, new object[] { opts }));

#if DEBUG
          _debugString = HelperMethods.StringFormat(
              CultureInfo.InvariantCulture,
              "openThreadId = {0}, connectionString = {1}",
              HelperMethods.GetThreadId(), _connectionString);
#endif







|







4633
4634
4635
4636
4637
4638
4639
4640
4641
4642
4643
4644
4645
4646
4647
          _connectionState = oldstate;

          StateChangeEventArgs eventArgs = null;
          OnStateChange(ConnectionState.Open, ref eventArgs);

          OnChanged(this, new ConnectionEventArgs(
              SQLiteConnectionEventType.Opened, eventArgs, null, null, null,
              null, eventArgConnectionString, new object[] { eventArgOpts }));

#if DEBUG
          _debugString = HelperMethods.StringFormat(
              CultureInfo.InvariantCulture,
              "openThreadId = {0}, connectionString = {1}",
              HelperMethods.GetThreadId(), _connectionString);
#endif
5254
5255
5256
5257
5258
5259
5260
5261
5262
5263










5264
5265
5266
5267
5268
5269
5270
    /// <remarks>
    /// No readers or writers may be active for this process.  The database must already be open
    /// and if it already was password protected, the existing password must already have been supplied.
    /// </remarks>
    /// <param name="newPassword">The new password to assign to the database</param>
    public void ChangePassword(string newPassword)
    {
      CheckDisposed();

      ChangePassword(String.IsNullOrEmpty(newPassword) ? null : UTF8Encoding.UTF8.GetBytes(newPassword));










    }

    /// <summary>
    /// Change the password (or assign a password) to an open database.
    /// </summary>
    /// <remarks>
    /// No readers or writers may be active for this process.  The database must already be open







|

|
>
>
>
>
>
>
>
>
>
>







5432
5433
5434
5435
5436
5437
5438
5439
5440
5441
5442
5443
5444
5445
5446
5447
5448
5449
5450
5451
5452
5453
5454
5455
5456
5457
5458
    /// <remarks>
    /// No readers or writers may be active for this process.  The database must already be open
    /// and if it already was password protected, the existing password must already have been supplied.
    /// </remarks>
    /// <param name="newPassword">The new password to assign to the database</param>
    public void ChangePassword(string newPassword)
    {
        CheckDisposed();

        if (!String.IsNullOrEmpty(newPassword))
        {
            byte[] newPasswordBytes = UTF8Encoding.UTF8.GetBytes(
                newPassword); /* throw */

            ChangePassword(newPasswordBytes);
        }
        else
        {
            ChangePassword((byte[])null);
        }
    }

    /// <summary>
    /// Change the password (or assign a password) to an open database.
    /// </summary>
    /// <remarks>
    /// No readers or writers may be active for this process.  The database must already be open
5284
5285
5286
5287
5288
5289
5290
5291
5292
5293










5294
5295
5296
5297
5298
5299
5300
5301
5302
5303
5304
5305
5306
5307
5308
5309







5310
5311
5312
5313
5314
5315
5316
    /// <summary>
    /// Sets the password for a password-protected database.  A password-protected database is
    /// unusable for any operation until the password has been set.
    /// </summary>
    /// <param name="databasePassword">The password for the database</param>
    public void SetPassword(string databasePassword)
    {
      CheckDisposed();

      SetPassword(String.IsNullOrEmpty(databasePassword) ? null : UTF8Encoding.UTF8.GetBytes(databasePassword));










    }

    /// <summary>
    /// Sets the password for a password-protected database.  A password-protected database is
    /// unusable for any operation until the password has been set.
    /// </summary>
    /// <param name="databasePassword">The password for the database</param>
    public void SetPassword(byte[] databasePassword)
    {
      CheckDisposed();

      if (_connectionState != ConnectionState.Closed)
        throw new InvalidOperationException("Password can only be set before the database is opened.");

      if (databasePassword != null)
        if (databasePassword.Length == 0) databasePassword = null;








      _password = databasePassword;
    }
#endif

    /// <summary>
    /// Queries or modifies the number of retries or the retry interval (in milliseconds) for







|

|
>
>
>
>
>
>
>
>
>
>
















>
>
>
>
>
>
>







5472
5473
5474
5475
5476
5477
5478
5479
5480
5481
5482
5483
5484
5485
5486
5487
5488
5489
5490
5491
5492
5493
5494
5495
5496
5497
5498
5499
5500
5501
5502
5503
5504
5505
5506
5507
5508
5509
5510
5511
5512
5513
5514
5515
5516
5517
5518
5519
5520
5521
    /// <summary>
    /// Sets the password for a password-protected database.  A password-protected database is
    /// unusable for any operation until the password has been set.
    /// </summary>
    /// <param name="databasePassword">The password for the database</param>
    public void SetPassword(string databasePassword)
    {
        CheckDisposed();

        if (!String.IsNullOrEmpty(databasePassword))
        {
            byte[] databasePasswordBytes = UTF8Encoding.UTF8.GetBytes(
                databasePassword); /* throw */

            SetPassword(databasePasswordBytes);
        }
        else
        {
            SetPassword((byte[])null);
        }
    }

    /// <summary>
    /// Sets the password for a password-protected database.  A password-protected database is
    /// unusable for any operation until the password has been set.
    /// </summary>
    /// <param name="databasePassword">The password for the database</param>
    public void SetPassword(byte[] databasePassword)
    {
      CheckDisposed();

      if (_connectionState != ConnectionState.Closed)
        throw new InvalidOperationException("Password can only be set before the database is opened.");

      if (databasePassword != null)
        if (databasePassword.Length == 0) databasePassword = null;

      if ((databasePassword != null) &&
          HelperMethods.HasFlags(_flags, SQLiteConnectionFlags.HidePassword))
      {
          throw new InvalidOperationException(
              "With 'HidePassword' enabled, passwords can only be set via the connection string.");
      }

      _password = databasePassword;
    }
#endif

    /// <summary>
    /// Queries or modifies the number of retries or the retry interval (in milliseconds) for
5412
5413
5414
5415
5416
5417
5418

5419









5420
5421
5422
5423

5424
5425
5426
5427
5428
5429
5430
            // NOTE: The string is null or empty, return it verbatim.
            //
            return value;
        }

        int length = value.Length;


        if (((value[0] == '\'') && (value[length - 1] == '\'')) ||









            ((value[0] == '"') && (value[length - 1] == '"')))
        {
            //
            // NOTE: Remove the first and last character.

            //
            return value.Substring(1, length - 2);
        }

        //
        // NOTE: No match, return the input string verbatim.
        //







>
|
>
>
>
>
>
>
>
>
>
|


|
>







5617
5618
5619
5620
5621
5622
5623
5624
5625
5626
5627
5628
5629
5630
5631
5632
5633
5634
5635
5636
5637
5638
5639
5640
5641
5642
5643
5644
5645
5646
            // NOTE: The string is null or empty, return it verbatim.
            //
            return value;
        }

        int length = value.Length;

        if ((value[0] == SQLiteConvert.QuoteChar) &&
            (value[length - 1] == SQLiteConvert.QuoteChar))
        {
            //
            // NOTE: Remove the first and last character, which are
            //       both double quotes.
            //
            return value.Substring(1, length - 2);
        }

        if ((value[0] == SQLiteConvert.AltQuoteChar) &&
            (value[length - 1] == SQLiteConvert.AltQuoteChar))
        {
            //
            // NOTE: Remove the first and last character, which are
            //       both single quotes.
            //
            return value.Substring(1, length - 2);
        }

        //
        // NOTE: No match, return the input string verbatim.
        //
Changes to System.Data.SQLite/SQLiteConvert.cs.
19
20
21
22
23
24
25














































26
27
28
29
30
31
32
  using System.Text;

  /// <summary>
  /// This base class provides datatype conversion services for the SQLite provider.
  /// </summary>
  public abstract class SQLiteConvert
  {














































    /// <summary>
    /// The fallback default database type when one cannot be obtained from an
    /// existing connection instance.
    /// </summary>
    private const DbType FallbackDefaultDbType = DbType.Object;

    /// <summary>







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
  using System.Text;

  /// <summary>
  /// This base class provides datatype conversion services for the SQLite provider.
  /// </summary>
  public abstract class SQLiteConvert
  {
    /// <summary>
    /// This character is used to escape other characters, including itself, in
    /// connection string property names and values.
    /// </summary>
    internal const char EscapeChar = '\\';

    /// <summary>
    /// This character can be used to wrap connection string property names and
    /// values.  Normally, it is optional; however, when used, it must be the
    /// first -AND- last character of that connection string property name -OR-
    /// value.
    /// </summary>
    internal const char QuoteChar = '"';

    /// <summary>
    /// This character can be used to wrap connection string property names and
    /// values.  Normally, it is optional; however, when used, it must be the
    /// first -AND- last character of that connection string property name -OR-
    /// value.
    /// </summary>
    internal const char AltQuoteChar = '\'';

    /// <summary>
    /// The character is used to separate the name and value for a connection
    /// string property.  This character cannot be present in any connection
    /// string property name.  This character can be present in a connection
    /// string property value; however, this should be avoided unless deemed
    /// absolutely necessary.
    /// </summary>
    internal const char ValueChar = '=';

    /// <summary>
    /// This character is used to separate connection string properties.  When
    /// the "No_SQLiteConnectionNewParser" setting is enabled, this character
    /// may not appear in connection string property names -OR- values.
    /// </summary>
    internal const char PairChar = ';';

    /// <summary>
    /// These are the characters that are special to the connection string
    /// parser.
    /// </summary>
    internal static readonly char[] SpecialChars = {
        QuoteChar, AltQuoteChar, PairChar, ValueChar, EscapeChar
    };

    /// <summary>
    /// The fallback default database type when one cannot be obtained from an
    /// existing connection instance.
    /// </summary>
    private const DbType FallbackDefaultDbType = DbType.Object;

    /// <summary>
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
    /// Note that the leading and trailing spaces were removed from each item during the split.
    /// </remarks>
    /// <param name="source">Source string to split apart</param>
    /// <param name="separator">Separator character</param>
    /// <returns>A string array of the split up elements</returns>
    public static string[] Split(string source, char separator)
    {
      char[] toks = new char[2] { '\"', separator };
      char[] quot = new char[1] { '\"' };
      int n = 0;
      List<string> ls = new List<string>();
      string s;

      while (source.Length > 0)
      {
        n = source.IndexOfAny(toks, n);







|
|







911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
    /// Note that the leading and trailing spaces were removed from each item during the split.
    /// </remarks>
    /// <param name="source">Source string to split apart</param>
    /// <param name="separator">Separator character</param>
    /// <returns>A string array of the split up elements</returns>
    public static string[] Split(string source, char separator)
    {
      char[] toks = new char[2] { QuoteChar, separator };
      char[] quot = new char[1] { QuoteChar };
      int n = 0;
      List<string> ls = new List<string>();
      string s;

      while (source.Length > 0)
      {
        n = source.IndexOfAny(toks, n);
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
    internal static string[] NewSplit(
        string value,
        char separator,
        bool keepQuote,
        ref string error
        )
    {
        const char EscapeChar = '\\';
        const char QuoteChar = '\"';

        //
        // NOTE: It is illegal for the separator character to be either a
        //       backslash or a double-quote because both of those characters
        //       are used for escaping other characters (e.g. the separator
        //       character).
        //
        if ((separator == EscapeChar) || (separator == QuoteChar))







<
<
<







993
994
995
996
997
998
999



1000
1001
1002
1003
1004
1005
1006
    internal static string[] NewSplit(
        string value,
        char separator,
        bool keepQuote,
        ref string error
        )
    {



        //
        // NOTE: It is illegal for the separator character to be either a
        //       backslash or a double-quote because both of those characters
        //       are used for escaping other characters (e.g. the separator
        //       character).
        //
        if ((separator == EscapeChar) || (separator == QuoteChar))
Changes to Tests/basic.eagle.
3143
3144
3145
3146
3147
3148
3149

3150
3151
3152
3153
3154
3155
3156

###############################################################################

runTest {test data-1.68 {unset env(DefaultFlags_SQLiteConnection)} -setup {
  saveSQLiteConnectionEnvironment

  unset -nocomplain env(DefaultFlags_SQLiteConnection)


  setupDb [set fileName data-1.68.db]
} -body {
  set connection [getDbConnection]

  list [object invoke System.Data.SQLite.SQLiteConnection DefaultFlags] \
      [$connection Flags]







>







3143
3144
3145
3146
3147
3148
3149
3150
3151
3152
3153
3154
3155
3156
3157

###############################################################################

runTest {test data-1.68 {unset env(DefaultFlags_SQLiteConnection)} -setup {
  saveSQLiteConnectionEnvironment

  unset -nocomplain env(DefaultFlags_SQLiteConnection)
  unset -nocomplain ::connection_flags

  setupDb [set fileName data-1.68.db]
} -body {
  set connection [getDbConnection]

  list [object invoke System.Data.SQLite.SQLiteConnection DefaultFlags] \
      [$connection Flags]
3167
3168
3169
3170
3171
3172
3173

3174
3175
3176
3177
3178
3179
3180

###############################################################################

runTest {test data-1.69 {set env(DefaultFlags_SQLiteConnection)} -setup {
  saveSQLiteConnectionEnvironment

  set env(DefaultFlags_SQLiteConnection) "DetectTextAffinity, DetectStringType"


  setupDb [set fileName data-1.69.db]
} -body {
  set connection [getDbConnection]

  list [object invoke System.Data.SQLite.SQLiteConnection DefaultFlags] \
      [$connection Flags]







>







3168
3169
3170
3171
3172
3173
3174
3175
3176
3177
3178
3179
3180
3181
3182

###############################################################################

runTest {test data-1.69 {set env(DefaultFlags_SQLiteConnection)} -setup {
  saveSQLiteConnectionEnvironment

  set env(DefaultFlags_SQLiteConnection) "DetectTextAffinity, DetectStringType"
  unset -nocomplain ::connection_flags

  setupDb [set fileName data-1.69.db]
} -body {
  set connection [getDbConnection]

  list [object invoke System.Data.SQLite.SQLiteConnection DefaultFlags] \
      [$connection Flags]
Added Tests/tkt-23d8d6171e.eagle.
































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
###############################################################################
#
# tkt-23d8d6171e.eagle --
#
# Written by Joe Mistachkin.
# Released to the public domain, use at your own risk!
#
###############################################################################

package require Eagle
package require Eagle.Library
package require Eagle.Test

runTestPrologue

###############################################################################

package require System.Data.SQLite.Test
runSQLiteTestPrologue

###############################################################################

runTest {test tkt-23d8d6171e-1.1 {HidePassword flag w/Password} -setup {
  setupDb [set fileName tkt-23d8d6171e-1.1.db] "" "" "" HidePassword \
      "Password=1234;"
} -body {
  sql execute $db "CREATE TABLE t1(x);"
  sql execute $db "INSERT INTO t1 (x) VALUES(1);"

  set result [list]

  lappend result [catch {sql execute -execute scalar $db \
      "SELECT COUNT(*) FROM t1;"} error] \
      [extractSystemDataSQLiteExceptionMessage $error]

  lappend result [catch {sql execute $db \
      "INSERT INTO t1 (x) VALUES(1);"} error] \
      [extractSystemDataSQLiteExceptionMessage $error]

  lappend result [catch {sql execute -execute scalar $db \
      "SELECT COUNT(*) FROM t1;"} error] \
      [extractSystemDataSQLiteExceptionMessage $error]

  set connection [getDbConnection]

  lappend result [$connection -flags +NonPublic _password]

  lappend result [expr {
    [string first 1234 [$connection ConnectionString]] != -1
  }]

  lappend result [expr {
    [string first ";Password=;" [$connection ConnectionString]] != -1
  }]

  lappend result [expr {
    [string first ";HexPassword=;" [$connection ConnectionString]] != -1
  }]

  set result
} -cleanup {
  freeDbConnection

  unset -nocomplain connection

  cleanupDb $fileName

  unset -nocomplain error result db fileName
} -constraints {eagle System.Data.SQLite.Encryption monoBug28 command.sql\
compile.DATA SQLite System.Data.SQLite} -result \
{0 1 0 1 0 2 {} False True False}}

###############################################################################

runTest {test tkt-23d8d6171e-1.2 {HidePassword flag w/HexPassword} -setup {
  setupDb [set fileName tkt-23d8d6171e-1.2.db] "" "" "" HidePassword \
      "HexPassword=1234;"
} -body {
  sql execute $db "CREATE TABLE t1(x);"
  sql execute $db "INSERT INTO t1 (x) VALUES(1);"

  set result [list]

  lappend result [catch {sql execute -execute scalar $db \
      "SELECT COUNT(*) FROM t1;"} error] \
      [extractSystemDataSQLiteExceptionMessage $error]

  lappend result [catch {sql execute $db \
      "INSERT INTO t1 (x) VALUES(1);"} error] \
      [extractSystemDataSQLiteExceptionMessage $error]

  lappend result [catch {sql execute -execute scalar $db \
      "SELECT COUNT(*) FROM t1;"} error] \
      [extractSystemDataSQLiteExceptionMessage $error]

  set connection [getDbConnection]

  lappend result [$connection -flags +NonPublic _password]

  lappend result [expr {
    [string first 1234 [$connection ConnectionString]] != -1
  }]

  lappend result [expr {
    [string first ";Password=;" [$connection ConnectionString]] != -1
  }]

  lappend result [expr {
    [string first ";HexPassword=;" [$connection ConnectionString]] != -1
  }]

  set result
} -cleanup {
  freeDbConnection

  unset -nocomplain connection

  cleanupDb $fileName

  unset -nocomplain error result db fileName
} -constraints {eagle System.Data.SQLite.Encryption monoBug28 command.sql\
compile.DATA SQLite System.Data.SQLite} -result \
{0 1 0 1 0 2 {} False False True}}

###############################################################################

runSQLiteTestEpilogue
runTestEpilogue
Changes to lib/System.Data.SQLite/common.eagle.
3543
3544
3545
3546
3547
3548
3549








3550
3551
3552
3553
3554
3555
3556
3557









3558
3559
3560
3561
3562
3563
3564

    proc saveSQLiteConnectionEnvironment {} {
      upvar 1 savedEnv savedEnv

      saveEnvironmentVariables [list \
          DefaultFlags_SQLiteConnection No_SQLiteConnectionNewParser] \
          savedEnv








    }

    proc restoreSQLiteConnectionEnvironment {} {
      upvar 1 savedEnv savedEnv

      restoreEnvironmentVariables [list \
          DefaultFlags_SQLiteConnection No_SQLiteConnectionNewParser] \
          savedEnv









    }

    proc saveSQLiteConvertEnvironment {} {
      upvar 1 savedEnv savedEnv

      saveEnvironmentVariables [list \
          Use_SQLiteConvert_DefaultDbType Use_SQLiteConvert_DefaultTypeName] \







>
>
>
>
>
>
>
>








>
>
>
>
>
>
>
>
>







3543
3544
3545
3546
3547
3548
3549
3550
3551
3552
3553
3554
3555
3556
3557
3558
3559
3560
3561
3562
3563
3564
3565
3566
3567
3568
3569
3570
3571
3572
3573
3574
3575
3576
3577
3578
3579
3580
3581

    proc saveSQLiteConnectionEnvironment {} {
      upvar 1 savedEnv savedEnv

      saveEnvironmentVariables [list \
          DefaultFlags_SQLiteConnection No_SQLiteConnectionNewParser] \
          savedEnv

      upvar 1 savedConnectionFlags savedConnectionFlags

      if {[info exists ::connection_flags]} then {
        set savedConnectionFlags $::connection_flags
      } else {
        unset -nocomplain savedConnectionFlags
      }
    }

    proc restoreSQLiteConnectionEnvironment {} {
      upvar 1 savedEnv savedEnv

      restoreEnvironmentVariables [list \
          DefaultFlags_SQLiteConnection No_SQLiteConnectionNewParser] \
          savedEnv

      upvar 1 savedConnectionFlags savedConnectionFlags

      if {[info exists savedConnectionFlags]} then {
        set ::connection_flags $savedConnectionFlags
        unset -nocomplain savedConnectionFlags
      } else {
        unset -nocomplain ::connection_flags
      }
    }

    proc saveSQLiteConvertEnvironment {} {
      upvar 1 savedEnv savedEnv

      saveEnvironmentVariables [list \
          Use_SQLiteConvert_DefaultDbType Use_SQLiteConvert_DefaultTypeName] \
Changes to readme.htm.
208
209
210
211
212
213
214

215
216
217
218
219
220
221
<h2><b>Version History</b></h2>

<p>
    <b>1.0.110.0 - December XX, 2018 <font color="red">(release scheduled)</font></b>
</p>
<ul>
    <li>Updated to <a href="https://www.sqlite.org/releaselog/3_26_0.html">SQLite 3.26.0</a>.</li>

    <li>Add experimental StrictConformance connection flag to force strict compliance to the ADO.NET standard. Pursuant to [e36e05e299].</li>
</ul>
<p>
    <b>1.0.109.0 - August 15, 2018</b>
</p>
<ul>
    <li>Updated to <a href="https://www.sqlite.org/releaselog/3_24_0.html">SQLite 3.24.0</a>.</li>







>







208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
<h2><b>Version History</b></h2>

<p>
    <b>1.0.110.0 - December XX, 2018 <font color="red">(release scheduled)</font></b>
</p>
<ul>
    <li>Updated to <a href="https://www.sqlite.org/releaselog/3_26_0.html">SQLite 3.26.0</a>.</li>
    <li>Add HidePassword connection flag to remove the password from the connection string once the database is opened. Pursuant to [23d8d6171e].</li>
    <li>Add experimental StrictConformance connection flag to force strict compliance to the ADO.NET standard. Pursuant to [e36e05e299].</li>
</ul>
<p>
    <b>1.0.109.0 - August 15, 2018</b>
</p>
<ul>
    <li>Updated to <a href="https://www.sqlite.org/releaselog/3_24_0.html">SQLite 3.24.0</a>.</li>
Changes to www/news.wiki.
45
46
47
48
49
50
51

52
53
54
55
56
57
58
<div align="center"><h2><b>Version History</b></h2></div>

<p>
    <b>1.0.110.0 - December XX, 2018 <font color="red">(release scheduled)</font></b>
</p>
<ul>
    <li>Updated to [https://www.sqlite.org/releaselog/3_26_0.html|SQLite 3.26.0].</li>

    <li>Add experimental StrictConformance connection flag to force strict compliance to the ADO.NET standard. Pursuant to [e36e05e299].</li>
</ul>
<p>
    <b>1.0.109.0 - August 15, 2018</b>
</p>
<ul>
    <li>Updated to [https://www.sqlite.org/releaselog/3_24_0.html|SQLite 3.24.0].</li>







>







45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
<div align="center"><h2><b>Version History</b></h2></div>

<p>
    <b>1.0.110.0 - December XX, 2018 <font color="red">(release scheduled)</font></b>
</p>
<ul>
    <li>Updated to [https://www.sqlite.org/releaselog/3_26_0.html|SQLite 3.26.0].</li>
    <li>Add HidePassword connection flag to remove the password from the connection string once the database is opened. Pursuant to [23d8d6171e].</li>
    <li>Add experimental StrictConformance connection flag to force strict compliance to the ADO.NET standard. Pursuant to [e36e05e299].</li>
</ul>
<p>
    <b>1.0.109.0 - August 15, 2018</b>
</p>
<ul>
    <li>Updated to [https://www.sqlite.org/releaselog/3_24_0.html|SQLite 3.24.0].</li>