System.Data.SQLite

Check-in [0b6d0e63ca]
Login

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

Overview
Comment:Work in progress for ticket [8d928c3e88]. These changes are not yet fully tested.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | tkt-8d928c3e88
Files: files | file ages | folders
SHA1: 0b6d0e63ca2b1cd2a0125aef7117e422e07cebeb
User & Date: mistachkin 2015-01-08 04:55:10.227
Context
2015-01-08
17:42
More work on testability. check-in: d8ed5e99ae user: mistachkin tags: tkt-8d928c3e88
04:55
Work in progress for ticket [8d928c3e88]. These changes are not yet fully tested. check-in: 0b6d0e63ca user: mistachkin tags: tkt-8d928c3e88
2015-01-07
01:11
Configuration file cleanup: omit the 'remove' element for the legacy 'System.Data.SQLite' provider from the LINQ-specific and EF6-specific configuration files. check-in: 573499eaf2 user: mistachkin tags: trunk
Changes
Unified Diff Ignore Whitespace Patch
Changes to System.Data.SQLite.Linq/SQL Generation/SqlGenerator.cs.
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892




893
894
895
896
897
898
899
        switch (typeKind)
        {
          case PrimitiveTypeKind.Int32:
            result.Append(e.Value.ToString());
            break;

          case PrimitiveTypeKind.Binary:
            result.Append(" X'");
            result.Append(ByteArrayToBinaryString((Byte[])e.Value));
            result.Append("' ");
            break;

          case PrimitiveTypeKind.Boolean:
            result.Append((bool)e.Value ? "1" : "0");
            break;

          case PrimitiveTypeKind.Byte:
            result.Append(e.Value.ToString());
            break;

          case PrimitiveTypeKind.DateTime:
            result.Append(EscapeSingleQuote(SQLiteConvert.ToString((System.DateTime)e.Value, SQLiteDateFormats.ISO8601, DateTimeKind.Unspecified, null), false /* IsUnicode */));




            break;

          case PrimitiveTypeKind.Decimal:
            string strDecimal = ((Decimal)e.Value).ToString(CultureInfo.InvariantCulture);
            // if the decimal value has no decimal part, cast as decimal to preserve type
            // if the number has precision > int64 max precision, it will be handled as decimal by sql server
            // and does not need cast. if precision is lest then 20, then cast using Max(literal precision, sql default precision)







<
|
<











|
>
>
>
>







871
872
873
874
875
876
877

878

879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
        switch (typeKind)
        {
          case PrimitiveTypeKind.Int32:
            result.Append(e.Value.ToString());
            break;

          case PrimitiveTypeKind.Binary:

            ToBlobLiteral((byte[])e.Value, result);

            break;

          case PrimitiveTypeKind.Boolean:
            result.Append((bool)e.Value ? "1" : "0");
            break;

          case PrimitiveTypeKind.Byte:
            result.Append(e.Value.ToString());
            break;

          case PrimitiveTypeKind.DateTime:
            result.Append(EscapeSingleQuote(SQLiteConvert.ToString(
                (System.DateTime)e.Value, _manifest._dateTimeFormat,
                _manifest._dateTimeKind, _manifest._dateTimeFormatString),
                false /* IsUnicode */));

            break;

          case PrimitiveTypeKind.Decimal:
            string strDecimal = ((Decimal)e.Value).ToString(CultureInfo.InvariantCulture);
            // if the decimal value has no decimal part, cast as decimal to preserve type
            // if the number has precision > int64 max precision, it will be handled as decimal by sql server
            // and does not need cast. if precision is lest then 20, then cast using Max(literal precision, sql default precision)
919
920
921
922
923
924
925






926

927
928
929
930
931
932
933
            break;

          case PrimitiveTypeKind.Double:
            result.Append(((Double)e.Value).ToString(CultureInfo.InvariantCulture));
            break;

          case PrimitiveTypeKind.Guid:






            result.Append(EscapeSingleQuote(e.Value.ToString(), false /* IsUnicode */));

            break;

          case PrimitiveTypeKind.Int16:
            result.Append(e.Value.ToString());
            break;

          case PrimitiveTypeKind.Int64:







>
>
>
>
>
>
|
>







921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
            break;

          case PrimitiveTypeKind.Double:
            result.Append(((Double)e.Value).ToString(CultureInfo.InvariantCulture));
            break;

          case PrimitiveTypeKind.Guid:
            {
                object value = e.Value;

                if (_manifest._binaryGuid && (value is Guid))
                    ToBlobLiteral(((Guid)value).ToByteArray(), result);
                else
                    result.Append(EscapeSingleQuote(e.Value.ToString(), false /* IsUnicode */));
            }
            break;

          case PrimitiveTypeKind.Int16:
            result.Append(e.Value.ToString());
            break;

          case PrimitiveTypeKind.Int64:
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
    }

    private static ISqlFragment HandleGetDateFunction(SqlGenerator sqlgen, DbFunctionExpression e)
    {
      SqlBuilder result = new SqlBuilder();
      Debug.Assert(e.Arguments.Count == 0, "Canonical getdate function should have no arguments");

      switch (sqlgen._manifest._dateFormat)
      {
        case SQLiteDateFormats.Ticks:
          result.Append("(STRFTIME('%s', 'now') * 10000000 + 621355968000000000)");
          break;
        case SQLiteDateFormats.JulianDay:
          result.Append("CAST(STRFTIME('%J', 'now') AS double)");
          break;
        default:
          result.Append("STRFTIME('%Y-%m-%d %H:%M:%S', 'now')");
          break;
      }

      return result;
    }

    private static ISqlFragment HandleGetUtcDateFunction(SqlGenerator sqlgen, DbFunctionExpression e)
    {
      SqlBuilder result = new SqlBuilder();
      Debug.Assert(e.Arguments.Count == 0, "Canonical getutcdate function should have no arguments");

      switch (sqlgen._manifest._dateFormat)
      {
        case SQLiteDateFormats.Ticks:
          result.Append("(STRFTIME('%s', 'now', 'utc') * 10000000 + 621355968000000000)");
          break;
        case SQLiteDateFormats.JulianDay:
          result.Append("CAST(STRFTIME('%J', 'now', 'utc') AS double)");
          break;







|




















|







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
    }

    private static ISqlFragment HandleGetDateFunction(SqlGenerator sqlgen, DbFunctionExpression e)
    {
      SqlBuilder result = new SqlBuilder();
      Debug.Assert(e.Arguments.Count == 0, "Canonical getdate function should have no arguments");

      switch (sqlgen._manifest._dateTimeFormat)
      {
        case SQLiteDateFormats.Ticks:
          result.Append("(STRFTIME('%s', 'now') * 10000000 + 621355968000000000)");
          break;
        case SQLiteDateFormats.JulianDay:
          result.Append("CAST(STRFTIME('%J', 'now') AS double)");
          break;
        default:
          result.Append("STRFTIME('%Y-%m-%d %H:%M:%S', 'now')");
          break;
      }

      return result;
    }

    private static ISqlFragment HandleGetUtcDateFunction(SqlGenerator sqlgen, DbFunctionExpression e)
    {
      SqlBuilder result = new SqlBuilder();
      Debug.Assert(e.Arguments.Count == 0, "Canonical getutcdate function should have no arguments");

      switch (sqlgen._manifest._dateTimeFormat)
      {
        case SQLiteDateFormats.Ticks:
          result.Append("(STRFTIME('%s', 'now', 'utc') * 10000000 + 621355968000000000)");
          break;
        case SQLiteDateFormats.JulianDay:
          result.Append("CAST(STRFTIME('%J', 'now', 'utc') AS double)");
          break;
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
      {
        result.Append("CAST(STRFTIME('");

        // expand the datepart literal as tsql kword
        result.Append(trans);
        result.Append("', ");

        switch (sqlgen._manifest._dateFormat)
        {
          case SQLiteDateFormats.Ticks:
            result.Append(String.Format("(({0} - 621355968000000000) / 10000000.0)", e.Arguments[1].Accept(sqlgen)));
            break;
          default:
            result.Append(e.Arguments[1].Accept(sqlgen));
            break;
        }

        result.Append(") AS integer)");
      }
      else
      {
        result.Append("CAST(SUBSTR(STRFTIME('%f', ");

        switch (sqlgen._manifest._dateFormat)
        {
          case SQLiteDateFormats.Ticks:
            result.Append(String.Format("(({0} - 621355968000000000) / 10000000.0)", e.Arguments[1].Accept(sqlgen)));
            break;
          default:
            result.Append(e.Arguments[1].Accept(sqlgen));
            break;







|















|







2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
      {
        result.Append("CAST(STRFTIME('");

        // expand the datepart literal as tsql kword
        result.Append(trans);
        result.Append("', ");

        switch (sqlgen._manifest._dateTimeFormat)
        {
          case SQLiteDateFormats.Ticks:
            result.Append(String.Format("(({0} - 621355968000000000) / 10000000.0)", e.Arguments[1].Accept(sqlgen)));
            break;
          default:
            result.Append(e.Arguments[1].Accept(sqlgen));
            break;
        }

        result.Append(") AS integer)");
      }
      else
      {
        result.Append("CAST(SUBSTR(STRFTIME('%f', ");

        switch (sqlgen._manifest._dateTimeFormat)
        {
          case SQLiteDateFormats.Ticks:
            result.Append(String.Format("(({0} - 621355968000000000) / 10000000.0)", e.Arguments[1].Accept(sqlgen)));
            break;
          default:
            result.Append(e.Arguments[1].Accept(sqlgen));
            break;
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
    /// <param name="e"></param>
    /// <returns></returns>
    private static ISqlFragment HandleCanonicalFunctionDateAdd(SqlGenerator sqlgen, DbFunctionExpression e)
    {
      SqlBuilder result = new SqlBuilder();
      Debug.Assert(e.Arguments.Count == 2, "Canonical datepart functions should have exactly two arguments");

      switch (sqlgen._manifest._dateFormat)
      {
        case SQLiteDateFormats.Ticks:
          result.Append(String.Format("(STRFTIME('%s', JULIANDAY({1}) + ({0} / 86400.0)) * 10000000 + 621355968000000000)", e.Arguments[0].Accept(sqlgen), e.Arguments[1].Accept(sqlgen)));
          break;
        case SQLiteDateFormats.JulianDay:
          result.Append(String.Format("CAST(STRFTIME('%J', JULIANDAY({1}) + ({0} / 86400.0)) AS double)", e.Arguments[0].Accept(sqlgen), e.Arguments[1].Accept(sqlgen)));
          break;







|







2819
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
2830
2831
2832
2833
    /// <param name="e"></param>
    /// <returns></returns>
    private static ISqlFragment HandleCanonicalFunctionDateAdd(SqlGenerator sqlgen, DbFunctionExpression e)
    {
      SqlBuilder result = new SqlBuilder();
      Debug.Assert(e.Arguments.Count == 2, "Canonical datepart functions should have exactly two arguments");

      switch (sqlgen._manifest._dateTimeFormat)
      {
        case SQLiteDateFormats.Ticks:
          result.Append(String.Format("(STRFTIME('%s', JULIANDAY({1}) + ({0} / 86400.0)) * 10000000 + 621355968000000000)", e.Arguments[0].Accept(sqlgen), e.Arguments[1].Accept(sqlgen)));
          break;
        case SQLiteDateFormats.JulianDay:
          result.Append(String.Format("CAST(STRFTIME('%J', JULIANDAY({1}) + ({0} / 86400.0)) AS double)", e.Arguments[0].Accept(sqlgen), e.Arguments[1].Accept(sqlgen)));
          break;
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
    /// <param name="e"></param>
    /// <returns></returns>
    private static ISqlFragment HandleCanonicalFunctionDateSubtract(SqlGenerator sqlgen, DbFunctionExpression e)
    {
      SqlBuilder result = new SqlBuilder();
      Debug.Assert(e.Arguments.Count == 2, "Canonical datepart functions should have exactly two arguments");

      switch (sqlgen._manifest._dateFormat)
      {
        case SQLiteDateFormats.Ticks:
          result.Append(String.Format("CAST((({0} - 621355968000000000) / 10000000.0)  - (({1} - 621355968000000000) / 10000000.0) * 86400.0 AS integer)", e.Arguments[0].Accept(sqlgen), e.Arguments[1].Accept(sqlgen)));
          break;
        default:
          result.Append(String.Format("CAST((JULIANDAY({1}) - JULIANDAY({0})) * 86400.0 AS integer)", e.Arguments[0].Accept(sqlgen), e.Arguments[1].Accept(sqlgen)));
          break;







|







2846
2847
2848
2849
2850
2851
2852
2853
2854
2855
2856
2857
2858
2859
2860
    /// <param name="e"></param>
    /// <returns></returns>
    private static ISqlFragment HandleCanonicalFunctionDateSubtract(SqlGenerator sqlgen, DbFunctionExpression e)
    {
      SqlBuilder result = new SqlBuilder();
      Debug.Assert(e.Arguments.Count == 2, "Canonical datepart functions should have exactly two arguments");

      switch (sqlgen._manifest._dateTimeFormat)
      {
        case SQLiteDateFormats.Ticks:
          result.Append(String.Format("CAST((({0} - 621355968000000000) / 10000000.0)  - (({1} - 621355968000000000) / 10000000.0) * 86400.0 AS integer)", e.Arguments[0].Accept(sqlgen), e.Arguments[1].Accept(sqlgen)));
          break;
        default:
          result.Append(String.Format("CAST((JULIANDAY({1}) - JULIANDAY({0})) * 86400.0 AS integer)", e.Arguments[0].Accept(sqlgen), e.Arguments[1].Accept(sqlgen)));
          break;
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
      SqlBuilder result = new SqlBuilder();
      result.Append("CAST(STRFTIME('");
      result.Append(trans);
      result.Append("', ");

      Debug.Assert(e.Arguments.Count == 1, "Canonical datepart functions should have exactly one argument");

      switch (sqlgen._manifest._dateFormat)
      {
        case SQLiteDateFormats.Ticks:
          result.Append(String.Format("(({0} - 621355968000000000) / 10000000.0)", e.Arguments[0].Accept(sqlgen)));
          break;
        default:
          result.Append(e.Arguments[0].Accept(sqlgen));
          break;







|







2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
      SqlBuilder result = new SqlBuilder();
      result.Append("CAST(STRFTIME('");
      result.Append(trans);
      result.Append("', ");

      Debug.Assert(e.Arguments.Count == 1, "Canonical datepart functions should have exactly one argument");

      switch (sqlgen._manifest._dateTimeFormat)
      {
        case SQLiteDateFormats.Ticks:
          result.Append(String.Format("(({0} - 621355968000000000) / 10000000.0)", e.Arguments[0].Accept(sqlgen)));
          break;
        default:
          result.Append(e.Arguments[0].Accept(sqlgen));
          break;
3421
3422
3423
3424
3425
3426
3427
3428
3429
3430
3431
3432
3433
3434
3435
      selectStatement.From.AppendLine();
      selectStatement.From.Append(") ");


      return selectStatement;
    }


    /// <summary>
    /// Before we embed a string literal in a SQL string, we should
    /// convert all ' to '', and enclose the whole string in single quotes.
    /// </summary>
    /// <param name="s"></param>
    /// <param name="isUnicode"></param>
    /// <returns>The escaped sql string.</returns>







<







3430
3431
3432
3433
3434
3435
3436

3437
3438
3439
3440
3441
3442
3443
      selectStatement.From.AppendLine();
      selectStatement.From.Append(") ");


      return selectStatement;
    }


    /// <summary>
    /// Before we embed a string literal in a SQL string, we should
    /// convert all ' to '', and enclose the whole string in single quotes.
    /// </summary>
    /// <param name="s"></param>
    /// <param name="isUnicode"></param>
    /// <returns>The escaped sql string.</returns>
3957
3958
3959
3960
3961
3962
3963


















3964

3965



3966

3967

3968
3969

3970
3971

3972
3973
3974
3975
3976
3977
3978
        // Should we actually support this?
        //result.Append(QuoteIdentifier((string)function.MetadataProperties["Schema"].Value ?? "dbo"));
        //result.Append(".");
        result.Append(QuoteIdentifier(storeFunctionName));
      }
    }



















    static string ByteArrayToBinaryString(Byte[] binaryArray)

    {



      StringBuilder sb = new StringBuilder(binaryArray.Length * 2);

      for (int i = 0; i < binaryArray.Length; i++)

      {
        sb.Append(hexDigits[(binaryArray[i] & 0xF0) >> 4]).Append(hexDigits[binaryArray[i] & 0x0F]);

      }
      return sb.ToString();

    }

    /// <summary>
    /// Helper method for the Group By visitor
    /// Returns true if at least one of the aggregates in the given list
    /// has an argument that is not a <see cref="DbPropertyExpression"/> 
    /// over <see cref="DbVariableReferenceExpression"/>







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







3965
3966
3967
3968
3969
3970
3971
3972
3973
3974
3975
3976
3977
3978
3979
3980
3981
3982
3983
3984
3985
3986
3987
3988
3989
3990
3991
3992
3993
3994
3995
3996
3997
3998
3999
4000
4001
4002
4003
4004
4005
4006
4007
4008
4009
4010
4011
4012
        // Should we actually support this?
        //result.Append(QuoteIdentifier((string)function.MetadataProperties["Schema"].Value ?? "dbo"));
        //result.Append(".");
        result.Append(QuoteIdentifier(storeFunctionName));
      }
    }

    /// <summary>
    /// Appends the literal BLOB string representation of the specified
    /// byte array to the <see cref="StringBuilder" />, creating it if
    /// necessary.
    /// </summary>
    /// <param name="bytes">
    /// The byte array to be formatted as a literal BLOB string.
    /// </param>
    /// <param name="builder">
    /// The <see cref="StringBuilder" /> object to use.  If null, a new
    /// instance will be created.
    /// </param>
    private static void ToBlobLiteral(
        byte[] bytes,
        SqlBuilder builder
        )
    {
        int capacity = (bytes != null) ? (bytes.Length * 2) + 3 : 4;

        if (bytes == null)
        {
            builder.Append("NULL"); /* TODO: Reasonable? */
            return;
        }

        builder.Append(" X'");

        for (int index = 0; index < bytes.Length; index++)
        {
            builder.Append(hexDigits[(bytes[index] & 0xF0) >> 4]);
            builder.Append(hexDigits[bytes[index] & 0x0F]);
        }

        builder.Append("' ");
    }

    /// <summary>
    /// Helper method for the Group By visitor
    /// Returns true if at least one of the aggregates in the given list
    /// has an argument that is not a <see cref="DbPropertyExpression"/> 
    /// over <see cref="DbVariableReferenceExpression"/>
Changes to System.Data.SQLite.Linq/SQLiteProviderManifest.cs.
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
#if USE_ENTITY_FRAMEWORK_6
namespace System.Data.SQLite.EF6
#else
namespace System.Data.SQLite.Linq
#endif
{
  using System;

  using System.Data;
  using System.Reflection;
  using System.IO;
  using System.Xml;
  using System.Data.Common;

#if USE_ENTITY_FRAMEWORK_6
  using System.Data.Entity.Core;
  using System.Data.Entity.Core.Common;
  using System.Data.Entity.Core.Metadata.Edm;
#else
  using System.Data.Metadata.Edm;
#endif

  /// <summary>
  /// The Provider Manifest for SQL Server
  /// </summary>
  internal sealed class SQLiteProviderManifest : DbXmlEnabledProviderManifest
  {
    internal SQLiteDateFormats _dateFormat;




    /// <summary>
    /// Constructs the provider manifest.
    /// </summary>
    /// <remarks>
    /// We pass the token as a DateTimeFormat enum text, because all the datetime functions
    /// are vastly different depending on how the user is opening the connection




    /// </remarks>
    /// <param name="manifestToken">A token used to infer the capabilities of the store</param>


    public SQLiteProviderManifest(string manifestToken)
      : base(SQLiteProviderManifest.GetProviderManifest())
    {
      _dateFormat = (SQLiteDateFormats)Enum.Parse(typeof(SQLiteDateFormats), manifestToken, true);
    }

    internal static XmlReader GetProviderManifest()
    {
      return GetXmlResource("System.Data.SQLite.SQLiteProviderServices.ProviderManifest.xml");
    }


































































































    /// <summary>
    /// Returns manifest information for the provider
    /// </summary>
    /// <param name="informationType">The name of the information to be retrieved.</param>
    /// <returns>An XmlReader at the begining of the information requested.</returns>
    protected override XmlReader GetDbInformation(string informationType)







>



















|
>
>
>





|
|
>
>
>
>

|
>
>

|

|


|



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







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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
#if USE_ENTITY_FRAMEWORK_6
namespace System.Data.SQLite.EF6
#else
namespace System.Data.SQLite.Linq
#endif
{
  using System;
  using System.Collections.Generic;
  using System.Data;
  using System.Reflection;
  using System.IO;
  using System.Xml;
  using System.Data.Common;

#if USE_ENTITY_FRAMEWORK_6
  using System.Data.Entity.Core;
  using System.Data.Entity.Core.Common;
  using System.Data.Entity.Core.Metadata.Edm;
#else
  using System.Data.Metadata.Edm;
#endif

  /// <summary>
  /// The Provider Manifest for SQL Server
  /// </summary>
  internal sealed class SQLiteProviderManifest : DbXmlEnabledProviderManifest
  {
    internal SQLiteDateFormats _dateTimeFormat;
    internal DateTimeKind _dateTimeKind;
    internal string _dateTimeFormatString;
    internal bool _binaryGuid;

    /// <summary>
    /// Constructs the provider manifest.
    /// </summary>
    /// <remarks>
    /// Previously, the manifest token was interpreted as a <see cref="SQLiteDateFormats" />,
    /// because the <see cref="DateTime" /> functions are vastly different depending on the
    /// connection was opened.  However, the manifest token may specify a connection string
    /// instead.  WARNING: Only the "DateTimeFormat", "DateTimeKind", "DateTimeFormatString",
    /// and "BinaryGUID" connection parameters are extracted from it.  All other connection
    /// parameters, if any are present, are silently ignored.
    /// </remarks>
    /// <param name="manifestToken">
    /// A token used to infer the capabilities of the store.
    /// </param>
    public SQLiteProviderManifest(string manifestToken)
      : base(GetProviderManifest())
    {
        SetFromOptions(ParseProviderManifestToken(manifestToken));
    }

    private static XmlReader GetProviderManifest()
    {
      return GetXmlResource("System.Data.SQLite.SQLiteProviderServices.ProviderManifest.xml");
    }

    /// <summary>
    /// Attempts to parse a provider manifest token.  It must contain either a
    /// legacy string that specifies the <see cref="SQLiteDateFormats" /> value
    /// -OR- string that uses the standard connection string syntax; otherwise,
    /// the results are undefined.
    /// </summary>
    /// <param name="manifestToken">
    /// The manifest token to parse.
    /// </param>
    /// <returns>
    /// The dictionary containing the connection string parameters.
    /// </returns>
    internal static SortedList<string, string> ParseProviderManifestToken(
        string manifestToken
        )
    {
        return SQLiteConnection.ParseConnectionString(manifestToken, false, true);
    }

    /// <summary>
    /// Attempts to set the provider manifest options from the specified
    /// connection string parameters.  An exception may be thrown if one
    /// or more of the connection string parameter values do not conform
    /// to the expected type.
    /// </summary>
    /// <param name="opts">
    /// The dictionary containing the connection string parameters.
    /// </param>
    internal void SetFromOptions(
        SortedList<string, string> opts
        )
    {
        _dateTimeFormat = SQLiteConnection.DefaultDateTimeFormat;
        _dateTimeKind = SQLiteConnection.DefaultDateTimeKind;
        _dateTimeFormatString = SQLiteConnection.DefaultDateTimeFormatString;
        _binaryGuid = /* SQLiteConnection.DefaultBinaryGUID; */ false; /* COMPAT: Legacy. */

        if (opts == null)
            return;

#if !PLATFORM_COMPACTFRAMEWORK
        string[] names = Enum.GetNames(typeof(SQLiteDateFormats));
#else
        string[] names = {
            SQLiteDateFormats.Ticks.ToString(),
            SQLiteDateFormats.ISO8601.ToString(),
            SQLiteDateFormats.JulianDay.ToString(),
            SQLiteDateFormats.UnixEpoch.ToString(),
            SQLiteDateFormats.InvariantCulture.ToString(),
            SQLiteDateFormats.CurrentCulture.ToString(),
            "Default"
        };
#endif

        foreach (string name in names)
        {
            if (String.IsNullOrEmpty(name))
                continue;

            object value = SQLiteConnection.FindKey(opts, name, null);

            if (value == null)
                continue;

            _dateTimeFormat = (SQLiteDateFormats)Enum.Parse(
                typeof(SQLiteDateFormats), name, true);
        }

        object enumValue;

        enumValue = SQLiteConnection.TryParseEnum(
            typeof(SQLiteDateFormats), SQLiteConnection.FindKey(
            opts, "DateTimeFormat", null), true);

        if (enumValue is SQLiteDateFormats)
            _dateTimeFormat = (SQLiteDateFormats)enumValue;

        enumValue = SQLiteConnection.TryParseEnum(
            typeof(DateTimeKind), SQLiteConnection.FindKey(
            opts, "DateTimeKind", null), true);

        if (enumValue is DateTimeKind)
            _dateTimeKind = (DateTimeKind)enumValue;

        string stringValue = SQLiteConnection.FindKey(
            opts, "DateTimeFormatString", null);

        if (stringValue != null)
            _dateTimeFormatString = stringValue;

        stringValue = SQLiteConnection.FindKey(
            opts, "BinaryGUID", null);

        if (stringValue != null)
            _binaryGuid = SQLiteConvert.ToBoolean(stringValue);
    }

    /// <summary>
    /// Returns manifest information for the provider
    /// </summary>
    /// <param name="informationType">The name of the information to be retrieved.</param>
    /// <returns>An XmlReader at the begining of the information requested.</returns>
    protected override XmlReader GetDbInformation(string informationType)
Changes to System.Data.SQLite.Linq/SQLiteProviderServices.cs.
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
        command.Dispose();
        throw;
      }
    }

    protected override string GetDbProviderManifestToken(DbConnection connection)
    {
      if (String.IsNullOrEmpty(connection.ConnectionString))
        throw new ArgumentNullException("ConnectionString");

      bool parseViaFramework = false;

      if (connection is SQLiteConnection)
          parseViaFramework = ((SQLiteConnection)connection).ParseViaFramework;

      SortedList<string, string> opts = parseViaFramework ?
          SQLiteConnection.ParseConnectionStringViaFramework(connection.ConnectionString, false) :
          SQLiteConnection.ParseConnectionString(connection.ConnectionString);

      return SQLiteConnection.FindKey(opts, "DateTimeFormat", "ISO8601");
    }

    protected override DbProviderManifest GetDbProviderManifest(string versionHint)
    {
      return new SQLiteProviderManifest(versionHint);
    }








|
|

<
|
|
<

<
<
|
<
<







106
107
108
109
110
111
112
113
114
115

116
117

118


119


120
121
122
123
124
125
126
        command.Dispose();
        throw;
      }
    }

    protected override string GetDbProviderManifestToken(DbConnection connection)
    {
        if (connection == null)
            throw new ArgumentNullException("connection");


        if (String.IsNullOrEmpty(connection.ConnectionString))
            throw new ArgumentNullException("ConnectionString");




        return connection.ConnectionString;


    }

    protected override DbProviderManifest GetDbProviderManifest(string versionHint)
    {
      return new SQLiteProviderManifest(versionHint);
    }

Changes to System.Data.SQLite/SQLiteConnection.cs.
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
    internal const IsolationLevel DeferredIsolationLevel = IsolationLevel.ReadCommitted;
    internal const IsolationLevel ImmediateIsolationLevel = IsolationLevel.Serializable;

    private const SQLiteConnectionFlags DefaultFlags = SQLiteConnectionFlags.Default;
    private const SQLiteSynchronousEnum DefaultSynchronous = SQLiteSynchronousEnum.Default;
    private const SQLiteJournalModeEnum DefaultJournalMode = SQLiteJournalModeEnum.Default;
    private const IsolationLevel DefaultIsolationLevel = IsolationLevel.Serializable;
    private const SQLiteDateFormats DefaultDateTimeFormat = SQLiteDateFormats.ISO8601;
    private const DateTimeKind DefaultDateTimeKind = DateTimeKind.Unspecified;
    private const string DefaultDateTimeFormatString = null;
    private const string DefaultDataSource = null;
    private const string DefaultUri = null;
    private const string DefaultFullUri = null;
    private const string DefaultHexPassword = null;
    private const string DefaultPassword = null;
    private const int DefaultVersion = 3;
    private const int DefaultPageSize = 1024;
    private const int DefaultMaxPageCount = 0;
    private const int DefaultCacheSize = 2000;
    private const int DefaultMaxPoolSize = 100;
    private const int DefaultConnectionTimeout = 30;
    private const bool DefaultNoSharedFlags = false;
    private const bool DefaultFailIfMissing = false;
    private const bool DefaultReadOnly = false;
    private const bool DefaultBinaryGUID = true;
    private const bool DefaultUseUTF16Encoding = false;
    private const bool DefaultToFullPath = true;
    private const bool DefaultPooling = false; // TODO: Maybe promote this to static property?
    private const bool DefaultLegacyFormat = false;
    private const bool DefaultForeignKeys = false;
    private const bool DefaultEnlist = true;
    private const bool DefaultSetDefaults = true;







|
|
|














|







348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
    internal const IsolationLevel DeferredIsolationLevel = IsolationLevel.ReadCommitted;
    internal const IsolationLevel ImmediateIsolationLevel = IsolationLevel.Serializable;

    private const SQLiteConnectionFlags DefaultFlags = SQLiteConnectionFlags.Default;
    private const SQLiteSynchronousEnum DefaultSynchronous = SQLiteSynchronousEnum.Default;
    private const SQLiteJournalModeEnum DefaultJournalMode = SQLiteJournalModeEnum.Default;
    private const IsolationLevel DefaultIsolationLevel = IsolationLevel.Serializable;
    internal const SQLiteDateFormats DefaultDateTimeFormat = SQLiteDateFormats.Default;
    internal const DateTimeKind DefaultDateTimeKind = DateTimeKind.Unspecified;
    internal const string DefaultDateTimeFormatString = null;
    private const string DefaultDataSource = null;
    private const string DefaultUri = null;
    private const string DefaultFullUri = null;
    private const string DefaultHexPassword = null;
    private const string DefaultPassword = null;
    private const int DefaultVersion = 3;
    private const int DefaultPageSize = 1024;
    private const int DefaultMaxPageCount = 0;
    private const int DefaultCacheSize = 2000;
    private const int DefaultMaxPoolSize = 100;
    private const int DefaultConnectionTimeout = 30;
    private const bool DefaultNoSharedFlags = false;
    private const bool DefaultFailIfMissing = false;
    private const bool DefaultReadOnly = false;
    internal const bool DefaultBinaryGUID = true;
    private const bool DefaultUseUTF16Encoding = false;
    private const bool DefaultToFullPath = true;
    private const bool DefaultPooling = false; // TODO: Maybe promote this to static property?
    private const bool DefaultLegacyFormat = false;
    private const bool DefaultForeignKeys = false;
    private const bool DefaultEnlist = true;
    private const bool DefaultSetDefaults = true;
1195
1196
1197
1198
1199
1200
1201




















1202
1203
1204

1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216

        if (handle.IsClosed)
            throw new InvalidOperationException("The connection handle is closed.");
    }

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





















    private static SortedList<string, string> ParseConnectionString(
        string connectionString,
        bool parseViaFramework

        )
    {
        return parseViaFramework ?
            ParseConnectionStringViaFramework(connectionString, false) :
            ParseConnectionString(connectionString);
    }

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

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







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

|
>




|







1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237

        if (handle.IsClosed)
            throw new InvalidOperationException("The connection handle is closed.");
    }

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

    /// <summary>
    /// Parses a connection string into component parts using the custom
    /// connection string parser.  An exception may be thrown if the syntax
    /// of the connection string is incorrect.
    /// </summary>
    /// <param name="connectionString">
    /// The connection string to parse.
    /// </param>
    /// <param name="parseViaFramework">
    /// Non-zero to parse the connection string using the algorithm provided
    /// by the framework itself.  This is not applicable when running on the
    /// .NET Compact Framework.
    /// </param>
    /// <param name="allowNameOnly">
    /// Non-zero if names are allowed without values.
    /// </param>
    /// <returns>
    /// The list of key/value pairs corresponding to the parameters specified
    /// within the connection string.
    /// </returns>
    internal static SortedList<string, string> ParseConnectionString(
        string connectionString,
        bool parseViaFramework,
        bool allowNameOnly
        )
    {
        return parseViaFramework ?
            ParseConnectionStringViaFramework(connectionString, false) :
            ParseConnectionString(connectionString, allowNameOnly);
    }

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

    private void SetupSQLiteBase(SortedList<string, string> opts)
    {
        object enumValue;
1886
1887
1888
1889
1890
1891
1892
1893
1894

1895
1896





1897



1898



1899
1900
1901
1902
1903
1904
1905
      else if (path.StartsWith ("/", StringComparison.OrdinalIgnoreCase))
            return path;
      else
            throw new InvalidOperationException ("Invalid connection string: invalid URI");
    }

    /// <summary>
    /// Parses the connection string into component parts using the custom
    /// connection string parser.

    /// </summary>
    /// <param name="connectionString">The connection string to parse</param>





    /// <returns>An array of key-value pairs representing each parameter of the connection string</returns>



    internal static SortedList<string, string> ParseConnectionString(string connectionString)



    {
      string s = connectionString;
      int n;
      SortedList<string, string> ls = new SortedList<string, string>(StringComparer.OrdinalIgnoreCase);

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







|
|
>

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







1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
      else if (path.StartsWith ("/", StringComparison.OrdinalIgnoreCase))
            return path;
      else
            throw new InvalidOperationException ("Invalid connection string: invalid URI");
    }

    /// <summary>
    /// Parses a connection string into component parts using the custom
    /// connection string parser.  An exception may be thrown if the syntax
    /// of the connection string is incorrect.
    /// </summary>
    /// <param name="connectionString">
    /// The connection string to parse.
    /// </param>
    /// <param name="allowNameOnly">
    /// Non-zero if names are allowed without values.
    /// </param>
    /// <returns>
    /// The list of key/value pairs corresponding to the parameters specified
    /// within the connection string.
    /// </returns>
    private static SortedList<string, string> ParseConnectionString(
        string connectionString,
        bool allowNameOnly
        )
    {
      string s = connectionString;
      int n;
      SortedList<string, string> ls = new SortedList<string, string>(StringComparer.OrdinalIgnoreCase);

      // First split into semi-colon delimited values.
      string error = null;
1929
1930
1931
1932
1933
1934
1935


1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955

1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
        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
          throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, "Invalid ConnectionString format for part \"{0}\", no equal sign found", arParts[n]));
      }
      return ls;
    }

    /// <summary>
    /// Parses a connection string using the built-in (i.e. framework provided)
    /// connection string parser class and returns the key/value pairs.  An
    /// exception may be thrown if the connection string is invalid or cannot be
    /// parsed.  When compiled for the .NET Compact Framework, the custom
    /// connection string parser is always used instead because the framework
    /// provided one is unavailable there.
    /// </summary>
    /// <param name="connectionString">
    /// The connection string to parse.
    /// </param>
    /// <param name="strict">
    /// Non-zero to throw an exception if any connection string values are not of
    /// the <see cref="String" /> type.

    /// </param>
    /// <returns>The list of key/value pairs.</returns>
    internal static SortedList<string, string> ParseConnectionStringViaFramework(
        string connectionString,
        bool strict
        )
    {
#if !PLATFORM_COMPACTFRAMEWORK
        DbConnectionStringBuilder connectionStringBuilder
            = new DbConnectionStringBuilder();







>
>



















|
>


|







1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
        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(String.Format(CultureInfo.CurrentCulture, "Invalid ConnectionString format for part \"{0}\", no equal sign found", arParts[n]));
      }
      return ls;
    }

    /// <summary>
    /// Parses a connection string using the built-in (i.e. framework provided)
    /// connection string parser class and returns the key/value pairs.  An
    /// exception may be thrown if the connection string is invalid or cannot be
    /// parsed.  When compiled for the .NET Compact Framework, the custom
    /// connection string parser is always used instead because the framework
    /// provided one is unavailable there.
    /// </summary>
    /// <param name="connectionString">
    /// The connection string to parse.
    /// </param>
    /// <param name="strict">
    /// Non-zero to throw an exception if any connection string values are not of
    /// the <see cref="String" /> type.  This is not applicable when running on
    /// the .NET Compact Framework.
    /// </param>
    /// <returns>The list of key/value pairs.</returns>
    private static SortedList<string, string> ParseConnectionStringViaFramework(
        string connectionString,
        bool strict
        )
    {
#if !PLATFORM_COMPACTFRAMEWORK
        DbConnectionStringBuilder connectionStringBuilder
            = new DbConnectionStringBuilder();
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
        return result;
#else
        //
        // NOTE: On the .NET Compact Framework, always use our custom connection
        //       string parser as the built-in (i.e. framework provided) one is
        //       unavailable.
        //
        return ParseConnectionString(connectionString);
#endif
    }

#if !PLATFORM_COMPACTFRAMEWORK
    /// <summary>
    /// Manual distributed transaction enlistment support
    /// </summary>







|







2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
        return result;
#else
        //
        // NOTE: On the .NET Compact Framework, always use our custom connection
        //       string parser as the built-in (i.e. framework provided) one is
        //       unavailable.
        //
        return ParseConnectionString(connectionString, false);
#endif
    }

#if !PLATFORM_COMPACTFRAMEWORK
    /// <summary>
    /// Manual distributed transaction enlistment support
    /// </summary>
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416

      if (_connectionState != ConnectionState.Closed)
        throw new InvalidOperationException();

      Close();

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

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

      object enumValue;








|







2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452

      if (_connectionState != ConnectionState.Closed)
        throw new InvalidOperationException();

      Close();

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

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

      object enumValue;

Changes to Tests/basic.eagle.
2257
2258
2259
2260
2261
2262
2263
2264

2265
2266
2267
2268
2269
2270
2271
      "OneTwo=ThreeFour" "\"OneTwo\"=\"ThreeFour\"" \
      "One Two=Three Four" "\"One Two\"=\"Three Four\"" \
      "OneTwo=ThreeFour;" "\"OneTwo\"=\"ThreeFour\";" \
      "One Two=Three Four;" "\"One Two\"=\"Three Four\";"]

  foreach string $strings {
    set list [object invoke -flags +NonPublic \
        System.Data.SQLite.SQLiteConnection ParseConnectionString $string]


    object foreach -alias pair $list {
      lappend result [list [$pair Key] [$pair Value]]
    }
  }

  set result







|
>







2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
      "OneTwo=ThreeFour" "\"OneTwo\"=\"ThreeFour\"" \
      "One Two=Three Four" "\"One Two\"=\"Three Four\"" \
      "OneTwo=ThreeFour;" "\"OneTwo\"=\"ThreeFour\";" \
      "One Two=Three Four;" "\"One Two\"=\"Three Four\";"]

  foreach string $strings {
    set list [object invoke -flags +NonPublic \
        System.Data.SQLite.SQLiteConnection ParseConnectionString \
        $string false]

    object foreach -alias pair $list {
      lappend result [list [$pair Key] [$pair Value]]
    }
  }

  set result
Added Tests/tkt-8d928c3e88.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
###############################################################################
#
# tkt-8d928c3e88.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
runSQLiteTestFilesPrologue

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

runTest {test tkt-8d928c3e88-1.1 {LINQ with BinaryGUID} -body {
  #
  # NOTE: Re-copy the reference database file used for this unit test to the
  #       build directory in case it has been changed by a previous test run.
  #
  file copy -force $northwindEfDbFile \
      [file join [getBuildDirectory] [file tail $northwindEfDbFile]]

  set result [list]
  set output ""

  set code [catch {
    testClrExec $testLinqExeFile [list -eventflags Wait -directory \
        [file dirname $testLinqExeFile] -nocarriagereturns -stdout output \
        -success 0] -binaryguid
  } error]

  tlog "---- BEGIN STDOUT OUTPUT\n"
  tlog $output
  tlog "\n---- END STDOUT OUTPUT\n"

  lappend result $code

  if {$code == 0} then {
    lappend result [string trim $output]
  } else {
    lappend result [string trim $error]
  }

  set result
} -cleanup {
  unset -nocomplain code output error result
} -constraints {eagle monoToDo SQLite file_System.Data.SQLite.dll testExec\
file_System.Data.SQLite.Linq.dll file_testlinq.exe file_northwindEF.db} \
-result {}}

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

runSQLiteTestFilesEpilogue
runSQLiteTestEpilogue
runTestEpilogue
Changes to Tests/tkt-e47b3d8346.eagle.
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
runTest {test tkt-e47b3d8346-1.1 {parse semi-colon in data source} -setup {
  unset -nocomplain result list pair
} -body {
  set result [list]

  set list [object invoke -flags +NonPublic \
      System.Data.SQLite.SQLiteConnection ParseConnectionString \
      {Data Source="C:\full\path\to\file.db;more.data.here";}]

  object foreach -alias pair $list {
    lappend result [list [$pair Key] [$pair Value]]
  }

  set result
} -cleanup {







|







23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
runTest {test tkt-e47b3d8346-1.1 {parse semi-colon in data source} -setup {
  unset -nocomplain result list pair
} -body {
  set result [list]

  set list [object invoke -flags +NonPublic \
      System.Data.SQLite.SQLiteConnection ParseConnectionString \
      {Data Source="C:\full\path\to\file.db;more.data.here";} false]

  object foreach -alias pair $list {
    lappend result [list [$pair Key] [$pair Value]]
  }

  set result
} -cleanup {
Changes to testlinq/Program.cs.
126
127
128
129
130
131
132




133
134
135
136
137
138
139

                      return EFTransactionTest(value);
                  }
              case "update":
                  {
                      return UpdateTest();
                  }




              default:
                  {
                      Console.WriteLine("unknown test \"{0}\"", arg);
                      return 1;
                  }
          }
      }







>
>
>
>







126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143

                      return EFTransactionTest(value);
                  }
              case "update":
                  {
                      return UpdateTest();
                  }
              case "binaryguid":
                  {
                      return BinaryGuidTest();
                  }
              default:
                  {
                      Console.WriteLine("unknown test \"{0}\"", arg);
                      return 1;
                  }
          }
      }
472
473
474
475
476
477
478

























479
480
481
482
483
484
485
                      db.AcceptAllChanges();
                  }
              }

              Console.WriteLine(
                  "inserted {0} updated {1}", counts[0], counts[1]);
          }


























          return 0;
      }

      private static int DateTimeTest()
      {
          using (northwindEFEntities db = new northwindEFEntities())







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







476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
                      db.AcceptAllChanges();
                  }
              }

              Console.WriteLine(
                  "inserted {0} updated {1}", counts[0], counts[1]);
          }

          return 0;
      }

      //
      // NOTE: Used to test the BinaryGUID fix (i.e. BLOB literal formatting
      //       of GUID values when the BinaryGUID connection property has been
      //       enabled).
      //
      private static int BinaryGuidTest()
      {
          using (northwindEFEntities db = new northwindEFEntities())
          {
              string sql = "SELECT VALUE GUID '25334ef0-bd18-43e6-863f-ae5d376ac75e' FROM Orders AS o WHERE o.OrderID = 10248;";
              ObjectQuery<string> query = db.CreateQuery<string>(sql);

              foreach (string s in query)
                  Console.WriteLine(s);

              sql = "SELECT VALUE GUID '25334ef0-bd18-43e6-863f-ae5d376ac75e' FROM Orders AS o WHERE o.OrderID = 10248;";
              query = db.CreateQuery<string>(sql);

              foreach (string s in query)
                  Console.WriteLine(s);
          }

          return 0;
      }

      private static int DateTimeTest()
      {
          using (northwindEFEntities db = new northwindEFEntities())