System.Data.SQLite

Login
This project makes use of Eagle, provided by Mistachkin Systems.
Eagle: Secure Software Automation

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

Changes In Branch tkt-8d928c3e88 Excluding Merge-Ins

This is equivalent to a diff from c2e4f24377 to 45312da90e

2015-01-14
21:26
Address all the LINQ issues mentioned in ticket [8d928c3e88]. Make the storage schema (SSDL) files more consistent with their provider names. check-in: 9ff0f0adf0 user: mistachkin tags: trunk
2015-01-13
04:08
Fix extra single quotes around the formatted DateTime values in the LINQ assembly. Use GetSettingValue to access the new environment variables. Closed-Leaf check-in: 45312da90e user: mistachkin tags: tkt-8d928c3e88
03:39
In the LINQ assembly, when surrounding single quotes are required for a formatted DateTime value, be sure to escape contained single quotes. check-in: b999c9c818 user: mistachkin tags: tkt-8d928c3e88
03:34
Merge updates from trunk. check-in: a15efc1fd5 user: mistachkin tags: tkt-8d928c3e88
03:34
Attempt to make the 'legacy' NuGet package configuration transform work the same as the EF6 one. check-in: c2e4f24377 user: mistachkin tags: trunk
03:30
Remove a comment that is no longer accurate. check-in: 812281ccef user: mistachkin tags: trunk

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

76
77
78
79
80
81
82











83
84
85
86
87
88
89
        </font>
      </p>
      <table width="80%" cellpadding="0" cellspacing="0">
        <tr valign="top">
          <th>Name</th>
          <th>Description</th>
        </tr>











        <tr valign="top">
          <td>Force_SQLiteLog</td>
          <td>If this environment variable is set [to anything], the SQLite
          logging subsystem may be initialized in a non-default application
          domain.  By default, this is not allowed due to the potential for
          application domain unloading issues.</td>
        </tr>







>
>
>
>
>
>
>
>
>
>
>







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
        </font>
      </p>
      <table width="80%" cellpadding="0" cellspacing="0">
        <tr valign="top">
          <th>Name</th>
          <th>Description</th>
        </tr>
        <tr valign="top">
          <td>AppendManifestToken_SQLiteProviderManifest</td>
          <td>If this environment variable is set [to anything], it will be
          used by the System.Data.SQLite.Linq.SQLiteProviderManifest class
          (and the System.Data.SQLite.EF6.SQLiteProviderManifest class) to
          modify future provider manifest tokens by appending the value of
          the environment variable to the existing provider manifest token,
          if any.  Typically, in order for the constructed provider manifest
          token to be syntactically correct, the environment variable value
          [to be appended] must begin with a semicolon.</td>
        </tr>
        <tr valign="top">
          <td>Force_SQLiteLog</td>
          <td>If this environment variable is set [to anything], the SQLite
          logging subsystem may be initialized in a non-default application
          domain.  By default, this is not allowed due to the potential for
          application domain unloading issues.</td>
        </tr>
149
150
151
152
153
154
155






156
157
158
159
160
161
162
          <td>PROCESSOR_ARCHITECTURE</td>
          <td>This environment variable is normally set by the operating
          system itself and should reflect the native processor architecture
          of the current process (e.g. a 32-bit x86 application running on a
          64-bit x64 operating system should have the value &quot;x86&quot;).
          </td>
        </tr>






        <tr valign="top">
          <td>TypeName_SQLiteProviderServices</td>
          <td>If this environment variable is set [to anything], it will be
          used by the System.Data.SQLite.SQLiteFactory class as the type name
          containing the System.Data.Common.DbProviderServices implementation
          that should be used.</td>
        </tr>







>
>
>
>
>
>







160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
          <td>PROCESSOR_ARCHITECTURE</td>
          <td>This environment variable is normally set by the operating
          system itself and should reflect the native processor architecture
          of the current process (e.g. a 32-bit x86 application running on a
          64-bit x64 operating system should have the value &quot;x86&quot;).
          </td>
        </tr>
        <tr valign="top">
          <td>SQLite_ForceLogPrepare</td>
          <td>If this environment variable is set [to anything], all calls to
          prepare a SQL query will be logged, regardless of the flags for the
          associated connection.</td>
        </tr>
        <tr valign="top">
          <td>TypeName_SQLiteProviderServices</td>
          <td>If this environment variable is set [to anything], it will be
          used by the System.Data.SQLite.SQLiteFactory class as the type name
          containing the System.Data.Common.DbProviderServices implementation
          that should be used.</td>
        </tr>

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

51
52
53
54
55
56
57



58
59
60
61
62
63
64
      <li>Defer disposing of connections created by the static SQLiteCommand.Execute method when a data reader is returned. Fix for <a href="http://system.data.sqlite.org/index.html/info/daeaf3150a">[daeaf3150a]</a>.</li>
      <li>Wrap SELECT statements in parenthesis if they have an ORDER BY, LIMIT, or OFFSET clause and a compound operator is involved. Fix for <a href="http://system.data.sqlite.org/index.html/info/0a32885109">[0a32885109]</a>.</li>
      <li>In the SQLiteDataReader.VerifyType method, remove duplicate &quot;if&quot; statement for the DbType.SByte value and move the remaining &quot;if&quot; to the Int64 affinity. Fix for <a href="http://system.data.sqlite.org/index.html/info/c5cc2fb334">[c5cc2fb334]</a>.&nbsp;<b>** Potentially Incompatible Change **</b></li>
      <li>Handle Julian Day values that fall outside of the supported range for OLE Automation dates. Fix for <a href="http://system.data.sqlite.org/index.html/info/3e783eecbe">[3e783eecbe]</a>.&nbsp;<b>** Potentially Incompatible Change **</b></li>
      <li>Make sure the interop files are copied when publishing a project that refers to a NuGet package containing them. Fix for <a href="http://system.data.sqlite.org/index.html/info/e796ac82c1">[e796ac82c1]</a>.&nbsp;<b>** Potentially Incompatible Change **</b></li>
      <li>Make sure the interop files are copied before the PostBuildEvent. Fix for <a href="http://system.data.sqlite.org/index.html/info/f16c93a932">[f16c93a932]</a>.&nbsp;<b>** Potentially Incompatible Change **</b></li>
      <li>Modify GetSchemaTable method to avoid setting SchemaTableColumn.IsKey column to true when more than one table is referenced. Fix for <a href="http://system.data.sqlite.org/index.html/info/47c6fa04d3">[47c6fa04d3]</a>.&nbsp;<b>** Potentially Incompatible Change **</b></li>



    </ul>
    <p><b>1.0.94.0 - September 9, 2014</b></p>
    <ul>
      <li>Updated to <a href="http://www.sqlite.org/releaselog/3_8_6.html">SQLite 3.8.6</a>.</li>
      <li>Updated to <a href="http://www.nuget.org/packages/EntityFramework/6.1.1">Entity Framework 6.1.1</a>.</li>
      <li>Refactor and simplify NuGet packages in order to support per-solution SQLite interop assembly files.&nbsp;<b>** Potentially Incompatible Change **</b></li>
      <li>Add RefreshFlags method to the SQLiteDataReader class to forcibly refresh its connection flags.</li>







>
>
>







51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
      <li>Defer disposing of connections created by the static SQLiteCommand.Execute method when a data reader is returned. Fix for <a href="http://system.data.sqlite.org/index.html/info/daeaf3150a">[daeaf3150a]</a>.</li>
      <li>Wrap SELECT statements in parenthesis if they have an ORDER BY, LIMIT, or OFFSET clause and a compound operator is involved. Fix for <a href="http://system.data.sqlite.org/index.html/info/0a32885109">[0a32885109]</a>.</li>
      <li>In the SQLiteDataReader.VerifyType method, remove duplicate &quot;if&quot; statement for the DbType.SByte value and move the remaining &quot;if&quot; to the Int64 affinity. Fix for <a href="http://system.data.sqlite.org/index.html/info/c5cc2fb334">[c5cc2fb334]</a>.&nbsp;<b>** Potentially Incompatible Change **</b></li>
      <li>Handle Julian Day values that fall outside of the supported range for OLE Automation dates. Fix for <a href="http://system.data.sqlite.org/index.html/info/3e783eecbe">[3e783eecbe]</a>.&nbsp;<b>** Potentially Incompatible Change **</b></li>
      <li>Make sure the interop files are copied when publishing a project that refers to a NuGet package containing them. Fix for <a href="http://system.data.sqlite.org/index.html/info/e796ac82c1">[e796ac82c1]</a>.&nbsp;<b>** Potentially Incompatible Change **</b></li>
      <li>Make sure the interop files are copied before the PostBuildEvent. Fix for <a href="http://system.data.sqlite.org/index.html/info/f16c93a932">[f16c93a932]</a>.&nbsp;<b>** Potentially Incompatible Change **</b></li>
      <li>Modify GetSchemaTable method to avoid setting SchemaTableColumn.IsKey column to true when more than one table is referenced. Fix for <a href="http://system.data.sqlite.org/index.html/info/47c6fa04d3">[47c6fa04d3]</a>.&nbsp;<b>** Potentially Incompatible Change **</b></li>
      <li>Add AppendManifestToken_SQLiteProviderManifest environment variable to enable better integration between LINQ and the underlying store connection.</li>
      <li>Add SQLite_ForceLogPrepare environment variable to force logging of all prepared SQL regardless of the flags for the associated connection.</li>
      <li>Honor the DateTimeFormat, DateTimeKind, DateTimeFormatString, BinaryGUID connection string and/or provider manifest token properties from within the LINQ assembly. Fix for <a href="http://system.data.sqlite.org/index.html/info/8d928c3e88">[8d928c3e88]</a>.&nbsp;<b>** Potentially Incompatible Change **</b></li>
    </ul>
    <p><b>1.0.94.0 - September 9, 2014</b></p>
    <ul>
      <li>Updated to <a href="http://www.sqlite.org/releaselog/3_8_6.html">SQLite 3.8.6</a>.</li>
      <li>Updated to <a href="http://www.nuget.org/packages/EntityFramework/6.1.1">Entity Framework 6.1.1</a>.</li>
      <li>Refactor and simplify NuGet packages in order to support per-solution SQLite interop assembly files.&nbsp;<b>** Potentially Incompatible Change **</b></li>
      <li>Add RefreshFlags method to the SQLiteDataReader class to forcibly refresh its connection flags.</li>

Changes to System.Data.SQLite.Linq/Resources/SQLiteProviderServices.StoreSchemaDefinition.EF6.ssdl.

273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
    <AssociationSet Name="STableConstraints" Association="Self.TableTableConstraint" >
      <End Role="Parent" EntitySet="STables"/>
      <End Role="Constraint" EntitySet="SConstraints"/>
    </AssociationSet>
    <AssociationSet Name="SConstraintConstraintColumns" Association="Self.ConstraintConstraintColumn" >
      <End Role="ConstraintColumn" EntitySet="SConstraintColumns"/>
      <End Role="Constraint" EntitySet="SConstraints"/>
    </AssociationSet>    
    <AssociationSet Name="SConstraintForeignKeys" Association="Self.ConstraintForeignKey" >
      <End Role="ForeignKey" EntitySet="SForeignKeys"/>
      <End Role="Constraint" EntitySet="SForeignKeyConstraints"/>
    </AssociationSet>
    <AssociationSet Name="SFromForeignKeyColumns" Association="Self.FromForeignKeyColumn" >
      <End Role="ForeignKey" EntitySet="SForeignKeys"/>
      <End Role="Column" EntitySet="STableColumns"/>







|







273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
    <AssociationSet Name="STableConstraints" Association="Self.TableTableConstraint" >
      <End Role="Parent" EntitySet="STables"/>
      <End Role="Constraint" EntitySet="SConstraints"/>
    </AssociationSet>
    <AssociationSet Name="SConstraintConstraintColumns" Association="Self.ConstraintConstraintColumn" >
      <End Role="ConstraintColumn" EntitySet="SConstraintColumns"/>
      <End Role="Constraint" EntitySet="SConstraints"/>
    </AssociationSet>
    <AssociationSet Name="SConstraintForeignKeys" Association="Self.ConstraintForeignKey" >
      <End Role="ForeignKey" EntitySet="SForeignKeys"/>
      <End Role="Constraint" EntitySet="SForeignKeyConstraints"/>
    </AssociationSet>
    <AssociationSet Name="SFromForeignKeyColumns" Association="Self.FromForeignKeyColumn" >
      <End Role="ForeignKey" EntitySet="SForeignKeys"/>
      <End Role="Column" EntitySet="STableColumns"/>
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
  <EntityType Name="CheckConstraint">
    <Key>
      <PropertyRef Name="Id" />
    </Key>
    <Property Name="Id" Nullable="false" Type="nvarchar" />
    <Property Name="Expression" Nullable="true" Type="nvarchar" />
  </EntityType>
  
  <EntityType Name="ConstraintColumn">
    <Key>
      <PropertyRef Name="ConstraintId" />
      <PropertyRef Name="ColumnId" />
    </Key>
    <Property Name="ConstraintId" Nullable="false" Type="nvarchar" />
    <Property Name="ColumnId" Nullable="false" Type="nvarchar" />







|







475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
  <EntityType Name="CheckConstraint">
    <Key>
      <PropertyRef Name="Id" />
    </Key>
    <Property Name="Id" Nullable="false" Type="nvarchar" />
    <Property Name="Expression" Nullable="true" Type="nvarchar" />
  </EntityType>

  <EntityType Name="ConstraintColumn">
    <Key>
      <PropertyRef Name="ConstraintId" />
      <PropertyRef Name="ColumnId" />
    </Key>
    <Property Name="ConstraintId" Nullable="false" Type="nvarchar" />
    <Property Name="ColumnId" Nullable="false" Type="nvarchar" />

Changes to System.Data.SQLite.Linq/Resources/SQLiteProviderServices.StoreSchemaDefinition.Linq.ssdl.

1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="utf-8"?>
<Schema Namespace="EFSQLite" Provider="System.Data.SQLite" ProviderManifestToken="ISO8601" Alias="Self" xmlns="http://schemas.microsoft.com/ado/2006/04/edm/ssdl">
  <EntityContainer Name="Schema">
    <EntitySet Name="STables" EntityType="Self.Table">
      <DefiningQuery>
        SELECT
        '[' || TABLE_NAME || ']' COLLATE NOCASE [Id]
        ,   TABLE_CATALOG [CatalogName]
        ,   TABLE_SCHEMA [SchemaName]

|







1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="utf-8"?>
<Schema Namespace="EFSQLite" Provider="System.Data.SQLite.Linq" ProviderManifestToken="ISO8601" Alias="Self" xmlns="http://schemas.microsoft.com/ado/2006/04/edm/ssdl">
  <EntityContainer Name="Schema">
    <EntitySet Name="STables" EntityType="Self.Table">
      <DefiningQuery>
        SELECT
        '[' || TABLE_NAME || ']' COLLATE NOCASE [Id]
        ,   TABLE_CATALOG [CatalogName]
        ,   TABLE_SCHEMA [SchemaName]
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
    <AssociationSet Name="STableConstraints" Association="Self.TableTableConstraint" >
      <End Role="Parent" EntitySet="STables"/>
      <End Role="Constraint" EntitySet="SConstraints"/>
    </AssociationSet>
    <AssociationSet Name="SConstraintConstraintColumns" Association="Self.ConstraintConstraintColumn" >
      <End Role="ConstraintColumn" EntitySet="SConstraintColumns"/>
      <End Role="Constraint" EntitySet="SConstraints"/>
    </AssociationSet>    
    <AssociationSet Name="SConstraintForeignKeys" Association="Self.ConstraintForeignKey" >
      <End Role="ForeignKey" EntitySet="SForeignKeys"/>
      <End Role="Constraint" EntitySet="SForeignKeyConstraints"/>
    </AssociationSet>
    <AssociationSet Name="SFromForeignKeyColumns" Association="Self.FromForeignKeyColumn" >
      <End Role="ForeignKey" EntitySet="SForeignKeys"/>
      <End Role="Column" EntitySet="STableColumns"/>







|







273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
    <AssociationSet Name="STableConstraints" Association="Self.TableTableConstraint" >
      <End Role="Parent" EntitySet="STables"/>
      <End Role="Constraint" EntitySet="SConstraints"/>
    </AssociationSet>
    <AssociationSet Name="SConstraintConstraintColumns" Association="Self.ConstraintConstraintColumn" >
      <End Role="ConstraintColumn" EntitySet="SConstraintColumns"/>
      <End Role="Constraint" EntitySet="SConstraints"/>
    </AssociationSet>
    <AssociationSet Name="SConstraintForeignKeys" Association="Self.ConstraintForeignKey" >
      <End Role="ForeignKey" EntitySet="SForeignKeys"/>
      <End Role="Constraint" EntitySet="SForeignKeyConstraints"/>
    </AssociationSet>
    <AssociationSet Name="SFromForeignKeyColumns" Association="Self.FromForeignKeyColumn" >
      <End Role="ForeignKey" EntitySet="SForeignKeys"/>
      <End Role="Column" EntitySet="STableColumns"/>
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
  <EntityType Name="CheckConstraint">
    <Key>
      <PropertyRef Name="Id" />
    </Key>
    <Property Name="Id" Nullable="false" Type="nvarchar" />
    <Property Name="Expression" Nullable="true" Type="nvarchar" />
  </EntityType>
  
  <EntityType Name="ConstraintColumn">
    <Key>
      <PropertyRef Name="ConstraintId" />
      <PropertyRef Name="ColumnId" />
    </Key>
    <Property Name="ConstraintId" Nullable="false" Type="nvarchar" />
    <Property Name="ColumnId" Nullable="false" Type="nvarchar" />







|







475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
  <EntityType Name="CheckConstraint">
    <Key>
      <PropertyRef Name="Id" />
    </Key>
    <Property Name="Id" Nullable="false" Type="nvarchar" />
    <Property Name="Expression" Nullable="true" Type="nvarchar" />
  </EntityType>

  <EntityType Name="ConstraintColumn">
    <Key>
      <PropertyRef Name="ConstraintId" />
      <PropertyRef Name="ColumnId" />
    </Key>
    <Property Name="ConstraintId" Nullable="false" Type="nvarchar" />
    <Property Name="ColumnId" Nullable="false" Type="nvarchar" />

Changes to System.Data.SQLite.Linq/SQL Generation/SqlBuilder.cs.

97
98
99
100
101
102
103




104
105
106
107
108
109
110
          else
          {
            ISqlFragment sqlFragment = (o as ISqlFragment);
            if (null != sqlFragment)
            {
              sqlFragment.WriteSql(writer, sqlGenerator);
            }




            else
            {
              throw new InvalidOperationException();
            }
          }
        }
      }







>
>
>
>







97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
          else
          {
            ISqlFragment sqlFragment = (o as ISqlFragment);
            if (null != sqlFragment)
            {
              sqlFragment.WriteSql(writer, sqlGenerator);
            }
            else if (o is char)
            {
              writer.Write((char)o);
            }
            else
            {
              throw new InvalidOperationException();
            }
          }
        }
      }

Changes to System.Data.SQLite.Linq/SQL Generation/SqlGenerator.cs.

116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
  /// Renaming issues.
  /// When rows or columns are renamed, we produce names that are unique globally
  /// with respect to the query.  The names are derived from the original names,
  /// with an integer as a suffix. e.g. CustomerId will be renamed to CustomerId1,
  /// CustomerId2 etc.
  ///
  /// Since the names generated are globally unique, they will not conflict when the
  /// columns of a JOIN SELECT statement are joined with another JOIN. 
  ///
  /// </para>
  ///
  /// <para>
  /// Record flattening.
  /// SQL server does not have the concept of records.  However, a join statement
  /// produces records.  We have to flatten the record accesses into a simple







|







116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
  /// Renaming issues.
  /// When rows or columns are renamed, we produce names that are unique globally
  /// with respect to the query.  The names are derived from the original names,
  /// with an integer as a suffix. e.g. CustomerId will be renamed to CustomerId1,
  /// CustomerId2 etc.
  ///
  /// Since the names generated are globally unique, they will not conflict when the
  /// columns of a JOIN SELECT statement are joined with another JOIN.
  ///
  /// </para>
  ///
  /// <para>
  /// Record flattening.
  /// SQL server does not have the concept of records.  However, a join statement
  /// produces records.  We have to flatten the record accesses into a simple
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
      return functionNameToOperatorDictionary;
    }

    #endregion

    #region Constructor
    /// <summary>
    /// Basic constructor. 
    /// </summary>
    private SqlGenerator(SQLiteProviderManifest manifest)
    {
      _manifest = manifest;
    }
    #endregion








|







434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
      return functionNameToOperatorDictionary;
    }

    #endregion

    #region Constructor
    /// <summary>
    /// Basic constructor.
    /// </summary>
    private SqlGenerator(SQLiteProviderManifest manifest)
    {
      _manifest = manifest;
    }
    #endregion

462
463
464
465
466
467
468
469
470
471
472
473
474
475
476

      //Handle Query
      DbQueryCommandTree queryCommandTree = tree as DbQueryCommandTree;
      if (queryCommandTree != null)
      {
        SqlGenerator sqlGen = new SqlGenerator(manifest);
        parameters = null;
        
        string sql = sqlGen.GenerateSql((DbQueryCommandTree)tree);

        return sql;
      }

      //Handle Function
      DbFunctionCommandTree DbFunctionCommandTree = tree as DbFunctionCommandTree;







|







462
463
464
465
466
467
468
469
470
471
472
473
474
475
476

      //Handle Query
      DbQueryCommandTree queryCommandTree = tree as DbQueryCommandTree;
      if (queryCommandTree != null)
      {
        SqlGenerator sqlGen = new SqlGenerator(manifest);
        parameters = null;

        string sql = sqlGen.GenerateSql((DbQueryCommandTree)tree);

        return sql;
      }

      //Handle Function
      DbFunctionCommandTree DbFunctionCommandTree = tree as DbFunctionCommandTree;
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
902
903
904
905
906
907
908
909
910
911
        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:
            bool needQuotes = NeedSingleQuotes(_manifest._dateTimeFormat);

            string dateString = SQLiteConvert.ToString(
                (System.DateTime)e.Value, _manifest._dateTimeFormat,
                _manifest._dateTimeKind, _manifest._dateTimeFormatString);

            if (needQuotes)
            {
                result.Append(EscapeSingleQuote(
                    dateString, false /* IsUnicode */));
            }
            else
            {
                result.Append(dateString);
            }
            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:







>
>
>
>
>
>
|
>







931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
            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:
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
    /// Lambda functions are not supported.
    /// The functions supported are:
    /// <list type="number">
    /// <item>Canonical Functions - We recognize these by their dataspace, it is DataSpace.CSpace</item>
    /// <item>Store Functions - We recognize these by the BuiltInAttribute and not being Canonical</item>
    /// <item>User-defined Functions - All the rest except for Lambda functions</item>
    /// </list>
    /// We handle Canonical and Store functions the same way: If they are in the list of functions 
    /// that need special handling, we invoke the appropriate handler, otherwise we translate them to
    /// FunctionName(arg1, arg2, ..., argn).
    /// We translate user-defined functions to NamespaceName.FunctionName(arg1, arg2, ..., argn).
    /// </summary>
    /// <param name="e"></param>
    /// <returns>A <see cref="SqlBuilder"/></returns>
    public override ISqlFragment Visit(DbFunctionExpression e)







|







1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
    /// Lambda functions are not supported.
    /// The functions supported are:
    /// <list type="number">
    /// <item>Canonical Functions - We recognize these by their dataspace, it is DataSpace.CSpace</item>
    /// <item>Store Functions - We recognize these by the BuiltInAttribute and not being Canonical</item>
    /// <item>User-defined Functions - All the rest except for Lambda functions</item>
    /// </list>
    /// We handle Canonical and Store functions the same way: If they are in the list of functions
    /// that need special handling, we invoke the appropriate handler, otherwise we translate them to
    /// FunctionName(arg1, arg2, ..., argn).
    /// We translate user-defined functions to NamespaceName.FunctionName(arg1, arg2, ..., argn).
    /// </summary>
    /// <param name="e"></param>
    /// <returns>A <see cref="SqlBuilder"/></returns>
    public override ISqlFragment Visit(DbFunctionExpression e)
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
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
    }

    /// <summary>
    /// <see cref="Visit(DbFilterExpression)"/> for general details.
    /// We modify both the GroupBy and the Select fields of the SqlSelectStatement.
    /// GroupBy gets just the keys without aliases,
    /// and Select gets the keys and the aggregates with aliases.
    /// 
    /// Whenever there exists at least one aggregate with an argument that is not is not a simple
    /// <see cref="DbPropertyExpression"/>  over <see cref="DbVariableReferenceExpression"/>, 
    /// we create a nested query in which we alias the arguments to the aggregates. 
    /// That is due to the following two limitations of Sql Server:
    /// <list type="number">
    /// <item>If an expression being aggregated contains an outer reference, then that outer 
    /// reference must be the only column referenced in the expression </item>
    /// <item>Sql Server cannot perform an aggregate function on an expression containing 
    /// an aggregate or a subquery. </item>
    /// </list>
    /// 
    /// The default translation, without inner query is: 
    /// 
    ///     SELECT 
    ///         kexp1 AS key1, kexp2 AS key2,... kexpn AS keyn, 
    ///         aggf1(aexpr1) AS agg1, .. aggfn(aexprn) AS aggn
    ///     FROM input AS a
    ///     GROUP BY kexp1, kexp2, .. kexpn
    /// 
    /// When we inject an innner query, the equivalent translation is:
    /// 
    ///     SELECT 
    ///         key1 AS key1, key2 AS key2, .. keyn AS keys,  
    ///         aggf1(agg1) AS agg1, aggfn(aggn) AS aggn
    ///     FROM (
    ///             SELECT 
    ///                 kexp1 AS key1, kexp2 AS key2,... kexpn AS keyn, 
    ///                 aexpr1 AS agg1, .. aexprn AS aggn
    ///             FROM input AS a
    ///         ) as a
    ///     GROUP BY key1, key2, keyn
    /// 
    /// </summary>
    /// <param name="e"></param>
    /// <returns>A <see cref="SqlSelectStatement"/></returns>
    public override ISqlFragment Visit(DbGroupByExpression e)
    {
      Symbol fromSymbol;
      //SqlSelectStatement result = VisitInputExpression(e.Input.Expression,







|

|
|


|

|


|
|
|
|
|



|

|
|
|


|
|




|







1193
1194
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
1238
1239
    }

    /// <summary>
    /// <see cref="Visit(DbFilterExpression)"/> for general details.
    /// We modify both the GroupBy and the Select fields of the SqlSelectStatement.
    /// GroupBy gets just the keys without aliases,
    /// and Select gets the keys and the aggregates with aliases.
    ///
    /// Whenever there exists at least one aggregate with an argument that is not is not a simple
    /// <see cref="DbPropertyExpression"/>  over <see cref="DbVariableReferenceExpression"/>,
    /// we create a nested query in which we alias the arguments to the aggregates.
    /// That is due to the following two limitations of Sql Server:
    /// <list type="number">
    /// <item>If an expression being aggregated contains an outer reference, then that outer
    /// reference must be the only column referenced in the expression </item>
    /// <item>Sql Server cannot perform an aggregate function on an expression containing
    /// an aggregate or a subquery. </item>
    /// </list>
    ///
    /// The default translation, without inner query is:
    ///
    ///     SELECT
    ///         kexp1 AS key1, kexp2 AS key2,... kexpn AS keyn,
    ///         aggf1(aexpr1) AS agg1, .. aggfn(aexprn) AS aggn
    ///     FROM input AS a
    ///     GROUP BY kexp1, kexp2, .. kexpn
    ///
    /// When we inject an innner query, the equivalent translation is:
    ///
    ///     SELECT
    ///         key1 AS key1, key2 AS key2, .. keyn AS keys,
    ///         aggf1(agg1) AS agg1, aggfn(aggn) AS aggn
    ///     FROM (
    ///             SELECT
    ///                 kexp1 AS key1, kexp2 AS key2,... kexpn AS keyn,
    ///                 aexpr1 AS agg1, .. aexprn AS aggn
    ///             FROM input AS a
    ///         ) as a
    ///     GROUP BY key1, key2, keyn
    ///
    /// </summary>
    /// <param name="e"></param>
    /// <returns>A <see cref="SqlSelectStatement"/></returns>
    public override ISqlFragment Visit(DbGroupByExpression e)
    {
      Symbol fromSymbol;
      //SqlSelectStatement result = VisitInputExpression(e.Input.Expression,
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
      symbolTable.Add(e.Input.GroupVariableName, fromSymbol);


      // The enumerator is shared by both the keys and the aggregates,
      // so, we do not close it in between.
      RowType groupByType = MetadataHelpers.GetEdmType<RowType>(MetadataHelpers.GetEdmType<CollectionType>(e.ResultType).TypeUsage);

      //Whenever there exists at least one aggregate with an argument that is not simply a PropertyExpression 
      // over a VarRefExpression, we need a nested query in which we alias the arguments to the aggregates.
      bool needsInnerQuery = NeedsInnerQuery(e.Aggregates);

      SqlSelectStatement result;
      if (needsInnerQuery)
      {
        //Create the inner query







|







1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
      symbolTable.Add(e.Input.GroupVariableName, fromSymbol);


      // The enumerator is shared by both the keys and the aggregates,
      // so, we do not close it in between.
      RowType groupByType = MetadataHelpers.GetEdmType<RowType>(MetadataHelpers.GetEdmType<CollectionType>(e.ResultType).TypeUsage);

      //Whenever there exists at least one aggregate with an argument that is not simply a PropertyExpression
      // over a VarRefExpression, we need a nested query in which we alias the arguments to the aggregates.
      bool needsInnerQuery = NeedsInnerQuery(e.Aggregates);

      SqlSelectStatement result;
      if (needsInnerQuery)
      {
        //Create the inner query
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
            // The inner query contains the default translation Key AS Alias
            innerQuery.Select.Append(separator);
            innerQuery.Select.AppendLine();
            innerQuery.Select.Append(keySql);
            innerQuery.Select.Append(" AS ");
            innerQuery.Select.Append(alias);

            //The outer resulting query projects over the key aliased in the inner query: 
            //  fromSymbol.Alias AS Alias
            result.Select.Append(separator);
            result.Select.AppendLine();
            result.Select.Append(fromSymbol);
            result.Select.Append(".");
            result.Select.Append(alias);
            result.Select.Append(" AS ");







|







1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
            // The inner query contains the default translation Key AS Alias
            innerQuery.Select.Append(separator);
            innerQuery.Select.AppendLine();
            innerQuery.Select.Append(keySql);
            innerQuery.Select.Append(" AS ");
            innerQuery.Select.Append(alias);

            //The outer resulting query projects over the key aliased in the inner query:
            //  fromSymbol.Alias AS Alias
            result.Select.Append(separator);
            result.Select.AppendLine();
            result.Select.Append(fromSymbol);
            result.Select.Append(".");
            result.Select.Append(alias);
            result.Select.Append(" AS ");
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
    public override ISqlFragment Visit(DbLikeExpression e)
    {
      SqlBuilder result = new SqlBuilder();
      result.Append(e.Argument.Accept(this));
      result.Append(" LIKE ");
      result.Append(e.Pattern.Accept(this));

      // if the ESCAPE expression is a DbNullExpression, then that's tantamount to 
      // not having an ESCAPE at all
      if (e.Escape.ExpressionKind != DbExpressionKind.Null)
      {
        result.Append(" ESCAPE ");
        result.Append(e.Escape.Accept(this));
      }








|







1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
    public override ISqlFragment Visit(DbLikeExpression e)
    {
      SqlBuilder result = new SqlBuilder();
      result.Append(e.Argument.Accept(this));
      result.Append(" LIKE ");
      result.Append(e.Pattern.Accept(this));

      // if the ESCAPE expression is a DbNullExpression, then that's tantamount to
      // not having an ESCAPE at all
      if (e.Escape.ExpressionKind != DbExpressionKind.Null)
      {
        result.Append(" ESCAPE ");
        result.Append(e.Escape.Accept(this));
      }

1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
      throw new NotSupportedException();
    }

    /// <summary>
    /// For Sql9 it translates to:
    /// SELECT Y.x1, Y.x2, ..., Y.xn
    /// FROM (
    ///     SELECT X.x1, X.x2, ..., X.xn, row_number() OVER (ORDER BY sk1, sk2, ...) AS [row_number] 
    ///     FROM input as X 
    ///     ) as Y
    /// WHERE Y.[row_number] > count 
    /// ORDER BY sk1, sk2, ...
    /// </summary>
    /// <param name="e"></param>
    /// <returns>A <see cref="SqlBuilder"/></returns>
    public override ISqlFragment Visit(DbSkipExpression e)
    {
        Debug.Assert(e.Count is DbConstantExpression || e.Count is DbParameterReferenceExpression, "DbLimitExpression.Count is of invalid expression type");







|
|

|







1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
      throw new NotSupportedException();
    }

    /// <summary>
    /// For Sql9 it translates to:
    /// SELECT Y.x1, Y.x2, ..., Y.xn
    /// FROM (
    ///     SELECT X.x1, X.x2, ..., X.xn, row_number() OVER (ORDER BY sk1, sk2, ...) AS [row_number]
    ///     FROM input as X
    ///     ) as Y
    /// WHERE Y.[row_number] > count
    /// ORDER BY sk1, sk2, ...
    /// </summary>
    /// <param name="e"></param>
    /// <returns>A <see cref="SqlBuilder"/></returns>
    public override ISqlFragment Visit(DbSkipExpression e)
    {
        Debug.Assert(e.Count is DbConstantExpression || e.Count is DbParameterReferenceExpression, "DbLimitExpression.Count is of invalid expression type");
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
        throw new InvalidOperationException("Special handling should be called only for functions in the list of special functions");

      return handlers[e.Function.Name](this, e);
    }

    /// <summary>
    /// Handles functions that are translated into TSQL operators.
    /// The given function should have one or two arguments. 
    /// Functions with one arguemnt are translated into 
    ///     op arg
    /// Functions with two arguments are translated into
    ///     arg0 op arg1
    /// Also, the arguments can be optionaly enclosed in parethesis
    /// </summary>
    /// <param name="e"></param>
    /// <param name="parenthesiseArguments">Whether the arguments should be enclosed in parethesis</param>







|
|







2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
        throw new InvalidOperationException("Special handling should be called only for functions in the list of special functions");

      return handlers[e.Function.Name](this, e);
    }

    /// <summary>
    /// Handles functions that are translated into TSQL operators.
    /// The given function should have one or two arguments.
    /// Functions with one arguemnt are translated into
    ///     op arg
    /// Functions with two arguments are translated into
    ///     arg0 op arg1
    /// Also, the arguments can be optionaly enclosed in parethesis
    /// </summary>
    /// <param name="e"></param>
    /// <param name="parenthesiseArguments">Whether the arguments should be enclosed in parethesis</param>
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;







|




















|







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
    }

    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;







|















|







2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
2813
2814
2815
2816
      {
        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
2825
2826
2827
2828
2829
2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
2853
2854
2855
2856
2857
2858
2859
2860
2861
2862
2863
2864
2865
    /// <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;
        default:
          result.Append(String.Format("STRFTIME('%Y-%m-%d %H:%M:%S', JULIANDAY({1}) + ({0} / 86400.0))", e.Arguments[0].Accept(sqlgen), e.Arguments[1].Accept(sqlgen)));
          break;
      }

      return result;
    }

    /// <summary>
    /// DateSubtract(datetime1, datetime2) -> DATEDIFF ( seconds , startdate , enddate ) 
    /// </summary>
    /// <param name="sqlgen"></param>
    /// <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;
      }

      return result;
    }

    /// <summary>
    /// Handler for canonical functions for extracting date parts. 
    /// For example:
    ///     Year(date) -> DATEPART( year, date)
    /// </summary>
    /// <param name="sqlgen"></param>
    /// <param name="e"></param>
    /// <returns></returns>
    private static ISqlFragment HandleCanonicalFunctionDatepart(SqlGenerator sqlgen, DbFunctionExpression e)







|
















|









|













|







2829
2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
2853
2854
2855
2856
2857
2858
2859
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
    /// <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;
        default:
          result.Append(String.Format("STRFTIME('%Y-%m-%d %H:%M:%S', JULIANDAY({1}) + ({0} / 86400.0))", e.Arguments[0].Accept(sqlgen), e.Arguments[1].Accept(sqlgen)));
          break;
      }

      return result;
    }

    /// <summary>
    /// DateSubtract(datetime1, datetime2) -> DATEDIFF ( seconds , startdate , enddate )
    /// </summary>
    /// <param name="sqlgen"></param>
    /// <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;
      }

      return result;
    }

    /// <summary>
    /// Handler for canonical functions for extracting date parts.
    /// For example:
    ///     Year(date) -> DATEPART( year, date)
    /// </summary>
    /// <param name="sqlgen"></param>
    /// <param name="e"></param>
    /// <returns></returns>
    private static ISqlFragment HandleCanonicalFunctionDatepart(SqlGenerator sqlgen, DbFunctionExpression e)
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;







|







2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
      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;
3310
3311
3312
3313
3314
3315
3316
3317
3318
3319
3320
3321
3322
3323
3324
      {
        symbolTable.Add(inputVarName, fromSymbol);
      }
    }

    /// <summary>
    /// Translates a list of SortClauses.
    /// Used in the translation of OrderBy 
    /// </summary>
    /// <param name="orderByClause">The SqlBuilder to which the sort keys should be appended</param>
    /// <param name="sortKeys"></param>
    void AddSortKeys(SqlBuilder orderByClause, IList<DbSortClause> sortKeys)
    {
      string separator = "";
      foreach (DbSortClause sortClause in sortKeys)







|







3329
3330
3331
3332
3333
3334
3335
3336
3337
3338
3339
3340
3341
3342
3343
      {
        symbolTable.Add(inputVarName, fromSymbol);
      }
    }

    /// <summary>
    /// Translates a list of SortClauses.
    /// Used in the translation of OrderBy
    /// </summary>
    /// <param name="orderByClause">The SqlBuilder to which the sort keys should be appended</param>
    /// <param name="sortKeys"></param>
    void AddSortKeys(SqlBuilder orderByClause, IList<DbSortClause> sortKeys)
    {
      string separator = "";
      foreach (DbSortClause sortClause in sortKeys)
3421
3422
3423
3424
3425
3426
3427



















3428
3429
3430
3431
3432
3433
3434
3435
3436
3437
3438
3439
3440
3441
3442
3443
3444
3445
3446
3447
3448
3449
3450
      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>
    private static string EscapeSingleQuote(string s, bool isUnicode)
    {
      return "'" + s.Replace("'", "''") + "'";
    }

    /// <summary>
    /// Returns the sql primitive/native type name. 
    /// It will include size, precision or scale depending on type information present in the 
    /// type facets
    /// </summary>
    /// <param name="type"></param>
    /// <returns></returns>
    private string GetSqlPrimitiveType(TypeUsage type)
    {
      PrimitiveType primitiveType = MetadataHelpers.GetEdmType<PrimitiveType>(type);







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














|
|







3440
3441
3442
3443
3444
3445
3446
3447
3448
3449
3450
3451
3452
3453
3454
3455
3456
3457
3458
3459
3460
3461
3462
3463
3464
3465
3466
3467
3468
3469
3470
3471
3472
3473
3474
3475
3476
3477
3478
3479
3480
3481
3482
3483
3484
3485
3486
3487
3488
      selectStatement.From.AppendLine();
      selectStatement.From.Append(") ");


      return selectStatement;
    }

    /// <summary>
    /// Determines if values of the specified <see cref="SQLiteDateFormats" />
    /// require wrapping in single quotes.
    /// </summary>
    /// <param name="format">
    /// The <see cref="SQLiteDateFormats" /> format.
    /// </param>
    /// <returns>
    /// Non-zero if single quotes are required for a value in the specified
    /// <see cref="SQLiteDateFormats" />.
    /// </returns>
    private static bool NeedSingleQuotes(
        SQLiteDateFormats format
        )
    {
        return format != SQLiteDateFormats.Ticks &&
            format != SQLiteDateFormats.JulianDay &&
            format != SQLiteDateFormats.UnixEpoch;
    }

    /// <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>
    private static string EscapeSingleQuote(string s, bool isUnicode)
    {
      return "'" + s.Replace("'", "''") + "'";
    }

    /// <summary>
    /// Returns the sql primitive/native type name.
    /// It will include size, precision or scale depending on type information present in the
    /// type facets
    /// </summary>
    /// <param name="type"></param>
    /// <returns></returns>
    private string GetSqlPrimitiveType(TypeUsage type)
    {
      PrimitiveType primitiveType = MetadataHelpers.GetEdmType<PrimitiveType>(type);
3563
3564
3565
3566
3567
3568
3569
3570
3571
3572
3573
3574
3575
3576
3577
    /// <returns></returns>
    private ISqlFragment HandleCountExpression(DbExpression e)
    {
      ISqlFragment result;

      if (e.ExpressionKind == DbExpressionKind.Constant)
      {
        //For constant expression we should not cast the value, 
        // thus we don't go throught the default DbConstantExpression handling
        SqlBuilder sqlBuilder = new SqlBuilder();
        sqlBuilder.Append(((DbConstantExpression)e).Value.ToString());
        result = sqlBuilder;
      }
      else
      {







|







3601
3602
3603
3604
3605
3606
3607
3608
3609
3610
3611
3612
3613
3614
3615
    /// <returns></returns>
    private ISqlFragment HandleCountExpression(DbExpression e)
    {
      ISqlFragment result;

      if (e.ExpressionKind == DbExpressionKind.Constant)
      {
        //For constant expression we should not cast the value,
        // thus we don't go throught the default DbConstantExpression handling
        SqlBuilder sqlBuilder = new SqlBuilder();
        sqlBuilder.Append(((DbConstantExpression)e).Value.ToString());
        result = sqlBuilder;
      }
      else
      {
3648
3649
3650
3651
3652
3653
3654
3655
3656
3657
3658
3659
3660
3661
3662
    /// <returns></returns>
    bool IsCompatible(SqlSelectStatement result, DbExpressionKind expressionKind)
    {
      switch (expressionKind)
      {
        case DbExpressionKind.Distinct:
          return result.Top == null
            // The projection after distinct may not project all 
            // columns used in the Order By
              && result.OrderBy.IsEmpty;

        case DbExpressionKind.Filter:
          return result.Select.IsEmpty
                  && result.Where.IsEmpty
                  && result.GroupBy.IsEmpty







|







3686
3687
3688
3689
3690
3691
3692
3693
3694
3695
3696
3697
3698
3699
3700
    /// <returns></returns>
    bool IsCompatible(SqlSelectStatement result, DbExpressionKind expressionKind)
    {
      switch (expressionKind)
      {
        case DbExpressionKind.Distinct:
          return result.Top == null
            // The projection after distinct may not project all
            // columns used in the Order By
              && result.OrderBy.IsEmpty;

        case DbExpressionKind.Filter:
          return result.Select.IsEmpty
                  && result.Where.IsEmpty
                  && result.GroupBy.IsEmpty
3771
3772
3773
3774
3775
3776
3777
3778
3779
3780
3781
3782
3783
3784
3785
    /// For the rest, we need to treat them as there was a dummy
    /// <code>
    /// -- originally {expression}
    /// -- change that to
    /// SELECT *
    /// FROM {expression} as c
    /// </code>
    /// 
    /// DbLimitExpression needs to start the statement but not add the default columns
    /// </summary>
    /// <param name="e"></param>
    /// <param name="addDefaultColumns"></param>
    /// <returns></returns>
    SqlSelectStatement VisitExpressionEnsureSqlStatement(DbExpression e, bool addDefaultColumns)
    {







|







3809
3810
3811
3812
3813
3814
3815
3816
3817
3818
3819
3820
3821
3822
3823
    /// For the rest, we need to treat them as there was a dummy
    /// <code>
    /// -- originally {expression}
    /// -- change that to
    /// SELECT *
    /// FROM {expression} as c
    /// </code>
    ///
    /// DbLimitExpression needs to start the statement but not add the default columns
    /// </summary>
    /// <param name="e"></param>
    /// <param name="addDefaultColumns"></param>
    /// <returns></returns>
    SqlSelectStatement VisitExpressionEnsureSqlStatement(DbExpression e, bool addDefaultColumns)
    {
3957
3958
3959
3960
3961
3962
3963


















3964

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
4013
4014
4015
4016
4017
4018
4019
4020
4021
4022
4023
4024
        // 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"/>
    /// </summary>
    /// <param name="aggregates"></param>
    /// <returns></returns>
    static bool NeedsInnerQuery(IList<DbAggregate> aggregates)
    {
      foreach (DbAggregate aggregate in aggregates)
      {
        Debug.Assert(aggregate.Arguments.Count == 1);
        if (!IsPropertyOverVarRef(aggregate.Arguments[0]))
        {
          return true;
        }
      }
      return false;
    }

    /// <summary>
    /// Determines whether the given expression is a <see cref="DbPropertyExpression"/> 
    /// over <see cref="DbVariableReferenceExpression"/>
    /// </summary>
    /// <param name="expression"></param>
    /// <returns></returns>
    static bool IsPropertyOverVarRef(DbExpression expression)
    {
      DbPropertyExpression propertyExpression = expression as DbPropertyExpression;
      if (propertyExpression == null)
      {
        return false;
      }
      DbVariableReferenceExpression varRefExpression = propertyExpression.Instance as DbVariableReferenceExpression;
      if (varRefExpression == null)
      {
        return false;
      }
      return true;
    }

    #endregion
    
    private class KeyFieldExpressionComparer : IEqualityComparer<DbExpression>
    {
      // Fields
      internal static readonly SqlGenerator.KeyFieldExpressionComparer Singleton = new SqlGenerator.KeyFieldExpressionComparer();

      // Methods
      private KeyFieldExpressionComparer()







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





|


















|




















|







3995
3996
3997
3998
3999
4000
4001
4002
4003
4004
4005
4006
4007
4008
4009
4010
4011
4012
4013
4014
4015
4016
4017
4018
4019
4020
4021
4022
4023
4024
4025
4026
4027
4028
4029
4030
4031
4032
4033
4034
4035
4036
4037
4038
4039
4040
4041
4042
4043
4044
4045
4046
4047
4048
4049
4050
4051
4052
4053
4054
4055
4056
4057
4058
4059
4060
4061
4062
4063
4064
4065
4066
4067
4068
4069
4070
4071
4072
4073
4074
4075
4076
4077
4078
4079
4080
4081
4082
4083
4084
4085
4086
4087
4088
        // 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="SqlBuilder" />.
    /// </summary>
    /// <param name="bytes">
    /// The byte array to be formatted as a literal BLOB string.
    /// </param>
    /// <param name="builder">
    /// The <see cref="SqlBuilder" /> object to use.  If null, an exception
    /// will be thrown.
    /// </param>
    private static void ToBlobLiteral(
        byte[] bytes,
        SqlBuilder builder
        )
    {
        if (builder == null)
            throw new ArgumentNullException("builder");

        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"/>
    /// </summary>
    /// <param name="aggregates"></param>
    /// <returns></returns>
    static bool NeedsInnerQuery(IList<DbAggregate> aggregates)
    {
      foreach (DbAggregate aggregate in aggregates)
      {
        Debug.Assert(aggregate.Arguments.Count == 1);
        if (!IsPropertyOverVarRef(aggregate.Arguments[0]))
        {
          return true;
        }
      }
      return false;
    }

    /// <summary>
    /// Determines whether the given expression is a <see cref="DbPropertyExpression"/>
    /// over <see cref="DbVariableReferenceExpression"/>
    /// </summary>
    /// <param name="expression"></param>
    /// <returns></returns>
    static bool IsPropertyOverVarRef(DbExpression expression)
    {
      DbPropertyExpression propertyExpression = expression as DbPropertyExpression;
      if (propertyExpression == null)
      {
        return false;
      }
      DbVariableReferenceExpression varRefExpression = propertyExpression.Instance as DbVariableReferenceExpression;
      if (varRefExpression == null)
      {
        return false;
      }
      return true;
    }

    #endregion

    private class KeyFieldExpressionComparer : IEqualityComparer<DbExpression>
    {
      // Fields
      internal static readonly SqlGenerator.KeyFieldExpressionComparer Singleton = new SqlGenerator.KeyFieldExpressionComparer();

      // Methods
      private KeyFieldExpressionComparer()

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
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
#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.IO;
  using System.Reflection;

#if !PLATFORM_COMPACTFRAMEWORK
  using System.Text;
#endif

  using System.Xml;

#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.Common;
  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(GetProviderManifestToken(manifestToken)));
    }

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

    /// <summary>
    /// Determines and returns the effective provider manifest token to use,
    /// based on the specified provider manifest token and the environment,
    /// if applicable.
    /// </summary>
    /// <param name="manifestToken">
    /// The original provider manifest token passed to the constructor for this
    /// class.
    /// </param>
    /// <returns>
    /// The effective provider manifest token.
    /// </returns>
    private static string GetProviderManifestToken(
        string manifestToken
        )
    {
#if !PLATFORM_COMPACTFRAMEWORK
        string value = UnsafeNativeMethods.GetSettingValue(
            "AppendManifestToken_SQLiteProviderManifest", null);

        if (String.IsNullOrEmpty(value))
            return manifestToken;

        int capacity = value.Length;

        if (manifestToken != null)
            capacity += manifestToken.Length;

        StringBuilder builder = new StringBuilder(capacity);

        builder.Append(manifestToken);
        builder.Append(value);

        return builder.ToString();
#else
        //
        // NOTE: The .NET Compact Framework lacks environment variable support.
        //       Therefore, just return the original provider manifest token
        //       verbatim in this case.
        //
        return manifestToken;
#endif
    }

    /// <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>
    private 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.

8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#if USE_ENTITY_FRAMEWORK_6
namespace System.Data.SQLite.EF6
#else
namespace System.Data.SQLite.Linq
#endif
{
  using System;
  using System.Data.Common;
  using System.Diagnostics;
  using System.Collections.Generic;
  using System.Text;
  using System.Globalization;

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







|
|
|
|
|







8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#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.Common;
  using System.Diagnostics;
  using System.Globalization;
  using System.Text;

#if USE_ENTITY_FRAMEWORK_6
  using System.Data.Entity.Core.Common;
  using System.Data.Entity.Core.Metadata.Edm;
  using System.Data.Entity.Core.Common.CommandTrees;
#else
  using System.Data.Metadata.Edm;
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/Configurations/System.Data.SQLite.dll.config.

1
2
3
4
5
6
7
8
9
10
11














12
13
14
15
16
17
18
<?xml version="1.0"?>
<!--
 *
 * System.Data.SQLite.dll.config -
 *
 * Written by Joe Mistachkin.
 * Released to the public domain, use at your own risk!
 *
-->
<configuration>
  <appSettings>














    <!--
        NOTE: If this configuration variable is set [to anything], the SQLite
              logging subsystem may be initialized in a non-default application
              domain.  By default, this is not allowed due to the potential
              for application domain unloading issues.
    -->
    <!--











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







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
<?xml version="1.0"?>
<!--
 *
 * System.Data.SQLite.dll.config -
 *
 * Written by Joe Mistachkin.
 * Released to the public domain, use at your own risk!
 *
-->
<configuration>
  <appSettings>
    <!--
        NOTE: If this environment variable is set [to anything], it will be
              used by the System.Data.SQLite.Linq.SQLiteProviderManifest class
              (and the System.Data.SQLite.EF6.SQLiteProviderManifest class) to
              modify future provider manifest tokens by appending the value of
              the environment variable to the existing provider manifest token,
              if any.  Typically, in order for the constructed provider
              manifest token to be syntactically correct, the environment
              variable value [to be appended] must begin with a semicolon.
    -->
    <!--
    <add key="AppendManifestToken_SQLiteProviderManifest" value="" />
    -->

    <!--
        NOTE: If this configuration variable is set [to anything], the SQLite
              logging subsystem may be initialized in a non-default application
              domain.  By default, this is not allowed due to the potential
              for application domain unloading issues.
    -->
    <!--
103
104
105
106
107
108
109









110
111
112
113
114
115
116
              architecture of the current process (e.g. a 32-bit x86
              application running on a 64-bit x64 operating system should have
              the value "x86").
    -->
    <!--
    <add key="PROCESSOR_ARCHITECTURE" value="%PROCESSOR_ARCHITECTURE%" />
    -->










    <!--
        NOTE: If this environment variable is set [to anything], it will be
              used by the System.Data.SQLite.SQLiteFactory class as the type
              name containing the System.Data.Common.DbProviderServices
              implementation that should be used.
    -->







>
>
>
>
>
>
>
>
>







117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
              architecture of the current process (e.g. a 32-bit x86
              application running on a 64-bit x64 operating system should have
              the value "x86").
    -->
    <!--
    <add key="PROCESSOR_ARCHITECTURE" value="%PROCESSOR_ARCHITECTURE%" />
    -->

    <!--
        NOTE: If this environment variable is set [to anything], all calls to
              prepare a SQL query will be logged, regardless of the flags for
              the associated connection.
    -->
    <!--
    <add key="SQLite_ForceLogPrepare" value="1" />
    -->

    <!--
        NOTE: If this environment variable is set [to anything], it will be
              used by the System.Data.SQLite.SQLiteFactory class as the type
              name containing the System.Data.Common.DbProviderServices
              implementation that should be used.
    -->

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

883
884
885
886
887
888
889













































890
891
892
893
894
895
896

    internal override string GetLastError(string defValue)
    {
        string result = SQLiteBase.GetLastError(_sql, _sql);
        if (String.IsNullOrEmpty(result)) result = defValue;
        return result;
    }














































    internal override SQLiteStatement Prepare(SQLiteConnection cnn, string strSql, SQLiteStatement previous, uint timeoutMS, ref string strRemain)
    {
      if (!String.IsNullOrEmpty(strSql))
      {
        //
        // NOTE: SQLite does not support the concept of separate schemas







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







883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941

    internal override string GetLastError(string defValue)
    {
        string result = SQLiteBase.GetLastError(_sql, _sql);
        if (String.IsNullOrEmpty(result)) result = defValue;
        return result;
    }

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

    #region Query Diagnostics Support
#if !PLATFORM_COMPACTFRAMEWORK
    /// <summary>
    /// This field is used to keep track of whether or not the
    /// "SQLite_ForceLogPrepare" environment variable has been queried.  If so,
    /// it will only be non-zero if the environment variable was present.
    /// </summary>
    private static bool? forceLogPrepare = null;

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

    /// <summary>
    /// Determines if all calls to prepare a SQL query will be logged,
    /// regardless of the flags for the associated connection.
    /// </summary>
    /// <returns>
    /// Non-zero to log all calls to prepare a SQL query.
    /// </returns>
    private static bool ForceLogPrepare()
    {
        lock (syncRoot)
        {
            if (forceLogPrepare == null)
            {
                if (UnsafeNativeMethods.GetSettingValue(
                        "SQLite_ForceLogPrepare", null) != null)
                {
                    forceLogPrepare = true;
                }
                else
                {
                    forceLogPrepare = false;
                }
            }

            return (bool)forceLogPrepare;
        }
    }
#endif
    #endregion

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

    internal override SQLiteStatement Prepare(SQLiteConnection cnn, string strSql, SQLiteStatement previous, uint timeoutMS, ref string strRemain)
    {
      if (!String.IsNullOrEmpty(strSql))
      {
        //
        // NOTE: SQLite does not support the concept of separate schemas
911
912
913
914
915
916
917




918
919
920
921
922
923
924
925
              "{0}.", baseSchemaName), String.Empty);
        }
      }

      SQLiteConnectionFlags flags =
          (cnn != null) ? cnn.Flags : SQLiteConnectionFlags.Default;





      if ((flags & SQLiteConnectionFlags.LogPrepare) == SQLiteConnectionFlags.LogPrepare)
      {
          if ((strSql == null) || (strSql.Length == 0) || (strSql.Trim().Length == 0))
              SQLiteLog.LogMessage("Preparing {<nothing>}...");
          else
              SQLiteLog.LogMessage(String.Format(
                  CultureInfo.CurrentCulture, "Preparing {{{0}}}...", strSql));
      }







>
>
>
>
|







956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
              "{0}.", baseSchemaName), String.Empty);
        }
      }

      SQLiteConnectionFlags flags =
          (cnn != null) ? cnn.Flags : SQLiteConnectionFlags.Default;

      if (
#if !PLATFORM_COMPACTFRAMEWORK
          ForceLogPrepare() ||
#endif
          ((flags & SQLiteConnectionFlags.LogPrepare) == SQLiteConnectionFlags.LogPrepare))
      {
          if ((strSql == null) || (strSql.Length == 0) || (strSql.Trim().Length == 0))
              SQLiteLog.LogMessage("Preparing {<nothing>}...");
          else
              SQLiteLog.LogMessage(String.Format(
                  CultureInfo.CurrentCulture, "Preparing {{{0}}}...", strSql));
      }

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
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
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
###############################################################################
#
# 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 w/BinaryGUID=false} -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 false
  } 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 {0 2d3d2d3d-2d3d-2d3d-2d3d-2d3d2d3d2d3d}}

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

runTest {test tkt-8d928c3e88-1.2 {LINQ w/BinaryGUID=true} -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 true
  } 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 {0 =-=-=-=--=-=-=-=}}

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

runTest {test tkt-8d928c3e88-2.1 {LINQ w/DateTimeFormat=Ticks} -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] -datetime2 Ticks
  } 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} \
-match regexp -result \
{\sWHERE \[Extent1\]\.\[OrderDate\] < 629876736000000000\s}}

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

runTest {test tkt-8d928c3e88-2.2 {LINQ w/DateTimeFormat=JulianDay} -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] -datetime2 JulianDay
  } 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} \
-match regexp -result {\sWHERE \[Extent1\]\.\[OrderDate\] < 2450449\.5\s}}

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

runTest {test tkt-8d928c3e88-2.3 {LINQ w/DateTimeFormat=UnixEpoch} -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] -datetime2 UnixEpoch
  } 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} \
-match regexp -result {\sWHERE \[Extent1\]\.\[OrderDate\] < 852076800\s}}

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

runTest {test tkt-8d928c3e88-2.4 {LINQ w/DateTimeFormat=ISO8601} -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] -datetime2 ISO8601
  } 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} \
-match regexp -result \
{\sWHERE \[Extent1\]\.\[OrderDate\] < '1997-01-01 00:00:00'\s}}

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

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 readme.htm.

218
219
220
221
222
223
224



225
226
227
228
229
230
231
    <li>Defer disposing of connections created by the static SQLiteCommand.Execute method when a data reader is returned. Fix for [daeaf3150a].</li>
    <li>Wrap SELECT statements in parenthesis if they have an ORDER BY, LIMIT, or OFFSET clause and a compound operator is involved. Fix for [0a32885109].</li>
    <li>In the SQLiteDataReader.VerifyType method, remove duplicate &quot;if&quot; statement for the DbType.SByte value and move the remaining &quot;if&quot; to the Int64 affinity. Fix for [c5cc2fb334].&nbsp;<b>** Potentially Incompatible Change **</b></li>
    <li>Handle Julian Day values that fall outside of the supported range for OLE Automation dates. Fix for [3e783eecbe].&nbsp;<b>** Potentially Incompatible Change **</b></li>
    <li>Make sure the interop files are copied when publishing a project that refers to a NuGet package containing them. Fix for [e796ac82c1].&nbsp;<b>** Potentially Incompatible Change **</b></li>
    <li>Make sure the interop files are copied before the PostBuildEvent. Fix for [f16c93a932].&nbsp;<b>** Potentially Incompatible Change **</b></li>
    <li>Modify GetSchemaTable method to avoid setting SchemaTableColumn.IsKey column to true when more than one table is referenced. Fix for [47c6fa04d3].&nbsp;<b>** Potentially Incompatible Change **</b></li>



</ul>
<p>
    <b>1.0.94.0 - September 9, 2014</b>
</p>
<ul>
    <li>Updated to <a href="http://www.sqlite.org/releaselog/3_8_6.html">SQLite 3.8.6</a>.</li>
    <li>Updated to <a href="http://www.nuget.org/packages/EntityFramework/6.1.1">Entity Framework 6.1.1</a>.</li>







>
>
>







218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
    <li>Defer disposing of connections created by the static SQLiteCommand.Execute method when a data reader is returned. Fix for [daeaf3150a].</li>
    <li>Wrap SELECT statements in parenthesis if they have an ORDER BY, LIMIT, or OFFSET clause and a compound operator is involved. Fix for [0a32885109].</li>
    <li>In the SQLiteDataReader.VerifyType method, remove duplicate &quot;if&quot; statement for the DbType.SByte value and move the remaining &quot;if&quot; to the Int64 affinity. Fix for [c5cc2fb334].&nbsp;<b>** Potentially Incompatible Change **</b></li>
    <li>Handle Julian Day values that fall outside of the supported range for OLE Automation dates. Fix for [3e783eecbe].&nbsp;<b>** Potentially Incompatible Change **</b></li>
    <li>Make sure the interop files are copied when publishing a project that refers to a NuGet package containing them. Fix for [e796ac82c1].&nbsp;<b>** Potentially Incompatible Change **</b></li>
    <li>Make sure the interop files are copied before the PostBuildEvent. Fix for [f16c93a932].&nbsp;<b>** Potentially Incompatible Change **</b></li>
    <li>Modify GetSchemaTable method to avoid setting SchemaTableColumn.IsKey column to true when more than one table is referenced. Fix for [47c6fa04d3].&nbsp;<b>** Potentially Incompatible Change **</b></li>
    <li>Add AppendManifestToken_SQLiteProviderManifest environment variable to enable better integration between LINQ and the underlying store connection.</li>
    <li>Add SQLite_ForceLogPrepare environment variable to force logging of all prepared SQL regardless of the flags for the associated connection.</li>
    <li>Honor the DateTimeFormat, DateTimeKind, DateTimeFormatString, BinaryGUID connection string and/or provider manifest token properties from within the LINQ assembly. Fix for [8d928c3e88].&nbsp;<b>** Potentially Incompatible Change **</b></li>
</ul>
<p>
    <b>1.0.94.0 - September 9, 2014</b>
</p>
<ul>
    <li>Updated to <a href="http://www.sqlite.org/releaselog/3_8_6.html">SQLite 3.8.6</a>.</li>
    <li>Updated to <a href="http://www.nuget.org/packages/EntityFramework/6.1.1">Entity Framework 6.1.1</a>.</li>

Changes to testlinq/Program.cs.

1
2
3
4
5
6
7
8

9
10

11
12
13
14

15
16

17
18
19
20
21
22
23
/********************************************************
 * ADO.NET 2.0 Data Provider for SQLite Version 3.X
 * Written by Robert Simpson (robert@blackcastlesoft.com)
 *
 * Released to the public domain, use at your own risk!
 ********************************************************/

using System;

using System.Diagnostics;
using System.Linq;

using System.Text;
using System.Transactions;

#if USE_ENTITY_FRAMEWORK_6

using System.Data.Entity.Core.Objects;
#else

using System.Data.Objects;
#endif

namespace testlinq
{
  class Program
  {








>


>




>


>







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
/********************************************************
 * ADO.NET 2.0 Data Provider for SQLite Version 3.X
 * Written by Robert Simpson (robert@blackcastlesoft.com)
 *
 * Released to the public domain, use at your own risk!
 ********************************************************/

using System;
using System.Data.Common;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Transactions;

#if USE_ENTITY_FRAMEWORK_6
using System.Data.Entity.Core.EntityClient;
using System.Data.Entity.Core.Objects;
#else
using System.Data.EntityClient;
using System.Data.Objects;
#endif

namespace testlinq
{
  class Program
  {
57
58
59
60
61
62
63










64
65
66
67
68
69
70
              case "old":
                  {
                      return OldTests();
                  }
              case "datetime":
                  {
                      return DateTimeTest();










                  }
              case "skip":
                  {
                      int pageSize = 0;

                      if (args.Length > 1)
                      {







>
>
>
>
>
>
>
>
>
>







61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
              case "old":
                  {
                      return OldTests();
                  }
              case "datetime":
                  {
                      return DateTimeTest();
                  }
              case "datetime2":
                  {
                      string dateTimeFormat = null;

                      if (args.Length > 1)
                          dateTimeFormat = args[1];

                      DateTimeTest2(dateTimeFormat);
                      return 0;
                  }
              case "skip":
                  {
                      int pageSize = 0;

                      if (args.Length > 1)
                      {
126
127
128
129
130
131
132


















133
134
135
136
137
138
139









































140
141
142
143
144
145
146

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


















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










































      //
      // NOTE: Used to test the fix for ticket [8b7d179c3c].
      //
      private static int SkipTest(int pageSize)
      {
          using (northwindEFEntities db = new northwindEFEntities())







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







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







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
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219

                      return EFTransactionTest(value);
                  }
              case "update":
                  {
                      return UpdateTest();
                  }
              case "binaryguid":
                  {
                      bool value = false;

                      if (args.Length > 1)
                      {
                          if (!bool.TryParse(args[1], out value))
                          {
                              Console.WriteLine(
                                  "cannot parse \"{0}\" as boolean",
                                  args[1]);

                              return 1;
                          }
                      }

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

      /// <summary>
      /// Attempts to obtain the underlying store connection
      /// (a <see cref="DbConnection" />) from the specified
      /// <see cref="EntityConnection" />.
      /// </summary>
      /// <param name="entityConnection">
      /// The <see cref="EntityConnection" /> to use.
      /// </param>
      /// <returns>
      /// The <see cref="DbConnection" /> -OR- null if it
      /// cannot be determined.
      /// </returns>
      private static DbConnection GetStoreConnection(
          EntityConnection entityConnection
          )
      {
          //
          // NOTE: No entity connection, no store connection.
          //
          if (entityConnection == null)
              return null;

          //
          // HACK: We need the underlying store connection and
          //       the legacy versions of the .NET Framework do
          //       not expose it; therefore, attempt to grab it
          //       by force.
          //
          FieldInfo fieldInfo = typeof(EntityConnection).GetField(
              "_storeConnection", BindingFlags.Instance |
              BindingFlags.NonPublic);

          //
          // NOTE: If the field is not found, just return null.
          //
          if (fieldInfo == null)
              return null;

          return fieldInfo.GetValue(entityConnection) as DbConnection;
      }

      //
      // NOTE: Used to test the fix for ticket [8b7d179c3c].
      //
      private static int SkipTest(int pageSize)
      {
          using (northwindEFEntities db = new northwindEFEntities())
475
476
477
478
479
480
481






























482
483
484
485
486
487
488
489
490
491
492

































493
494
495
496
497
498
499

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

          return 0;
      }































      private static int DateTimeTest()
      {
          using (northwindEFEntities db = new northwindEFEntities())
          {
              DateTime dateTime = new DateTime(1997, 1, 1, 0, 0, 0, DateTimeKind.Local);
              int c1 = db.Orders.Where(i => i.OrderDate == new DateTime(1997, 1, 1, 0, 0, 0, DateTimeKind.Local)).Count();
              int c2 = db.Orders.Where(i => i.OrderDate == dateTime).Count();
              return c1 == c2 ? 0 : 1;
          }
      }


































    private static int OldTests()
    {
      using (northwindEFEntities db = new northwindEFEntities())
      {
        {
          string entitySQL = "SELECT VALUE o FROM Orders AS o WHERE SQLite.DatePart('yyyy', o.OrderDate) = 1997 ORDER BY o.OrderID;";







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











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







548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635

              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(bool binaryGuid)
      {
          Environment.SetEnvironmentVariable(
              "AppendManifestToken_SQLiteProviderManifest",
              String.Format(";BinaryGUID={0};", binaryGuid));

          using (northwindEFEntities db = new northwindEFEntities())
          {
              string sql = "SELECT VALUE GUID " +
                  "'2d3d2d3d-2d3d-2d3d-2d3d-2d3d2d3d2d3d' " +
                  "FROM Orders AS o WHERE o.OrderID = 10248;";

              ObjectQuery<string> query = db.CreateQuery<string>(sql);

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

          Environment.SetEnvironmentVariable(
              "AppendManifestToken_SQLiteProviderManifest",
              null);

          return 0;
      }

      private static int DateTimeTest()
      {
          using (northwindEFEntities db = new northwindEFEntities())
          {
              DateTime dateTime = new DateTime(1997, 1, 1, 0, 0, 0, DateTimeKind.Local);
              int c1 = db.Orders.Where(i => i.OrderDate == new DateTime(1997, 1, 1, 0, 0, 0, DateTimeKind.Local)).Count();
              int c2 = db.Orders.Where(i => i.OrderDate == dateTime).Count();
              return c1 == c2 ? 0 : 1;
          }
      }

      private static void DateTimeTest2(
          string dateTimeFormat
          )
      {
          TraceListener listener = new ConsoleTraceListener();

          Trace.Listeners.Add(listener);
          Environment.SetEnvironmentVariable("SQLite_ForceLogPrepare", "1");

          if (dateTimeFormat != null)
          {
              Environment.SetEnvironmentVariable(
                  "AppendManifestToken_SQLiteProviderManifest",
                  String.Format(";DateTimeFormat={0};", dateTimeFormat));
          }

          using (northwindEFEntities db = new northwindEFEntities())
          {
              db.Orders.Where(i => i.OrderDate <
                  new DateTime(1997, 1, 1, 0, 0, 0, DateTimeKind.Local)).Count();
          }

          if (dateTimeFormat != null)
          {
              Environment.SetEnvironmentVariable(
                  "AppendManifestToken_SQLiteProviderManifest",
                  null);
          }

          Environment.SetEnvironmentVariable("SQLite_ForceLogPrepare", null);
          Trace.Listeners.Remove(listener);
      }

    private static int OldTests()
    {
      using (northwindEFEntities db = new northwindEFEntities())
      {
        {
          string entitySQL = "SELECT VALUE o FROM Orders AS o WHERE SQLite.DatePart('yyyy', o.OrderDate) = 1997 ORDER BY o.OrderID;";

Changes to www/news.wiki.

12
13
14
15
16
17
18



19
20
21
22
23
24
25
    <li>Defer disposing of connections created by the static SQLiteCommand.Execute method when a data reader is returned. Fix for [daeaf3150a].</li>
    <li>Wrap SELECT statements in parenthesis if they have an ORDER BY, LIMIT, or OFFSET clause and a compound operator is involved. Fix for [0a32885109].</li>
    <li>In the SQLiteDataReader.VerifyType method, remove duplicate &quot;if&quot; statement for the DbType.SByte value and move the remaining &quot;if&quot; to the Int64 affinity.  Fix for [c5cc2fb334].&nbsp;<b>** Potentially Incompatible Change **</b></li>
    <li>Handle Julian Day values that fall outside of the supported range for OLE Automation dates. Fix for [3e783eecbe].&nbsp;<b>** Potentially Incompatible Change **</b></li>
    <li>Make sure the interop files are copied when publishing a project that refers to a NuGet package containing them. Fix for [e796ac82c1].&nbsp;<b>** Potentially Incompatible Change **</b></li>
    <li>Make sure the interop files are copied before the PostBuildEvent. Fix for [f16c93a932].&nbsp;<b>** Potentially Incompatible Change **</b></li>
    <li>Modify GetSchemaTable method to avoid setting SchemaTableColumn.IsKey column to true when more than one table is referenced. Fix for [47c6fa04d3].&nbsp;<b>** Potentially Incompatible Change **</b></li>



</ul>
<p>
    <b>1.0.94.0 - September 9, 2014</b>
</p>
<ul>
    <li>Updated to [http://www.sqlite.org/releaselog/3_8_6.html|SQLite 3.8.6].</li>
    <li>Updated to [http://www.nuget.org/packages/EntityFramework/6.1.1|Entity Framework 6.1.1].</li>







>
>
>







12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
    <li>Defer disposing of connections created by the static SQLiteCommand.Execute method when a data reader is returned. Fix for [daeaf3150a].</li>
    <li>Wrap SELECT statements in parenthesis if they have an ORDER BY, LIMIT, or OFFSET clause and a compound operator is involved. Fix for [0a32885109].</li>
    <li>In the SQLiteDataReader.VerifyType method, remove duplicate &quot;if&quot; statement for the DbType.SByte value and move the remaining &quot;if&quot; to the Int64 affinity.  Fix for [c5cc2fb334].&nbsp;<b>** Potentially Incompatible Change **</b></li>
    <li>Handle Julian Day values that fall outside of the supported range for OLE Automation dates. Fix for [3e783eecbe].&nbsp;<b>** Potentially Incompatible Change **</b></li>
    <li>Make sure the interop files are copied when publishing a project that refers to a NuGet package containing them. Fix for [e796ac82c1].&nbsp;<b>** Potentially Incompatible Change **</b></li>
    <li>Make sure the interop files are copied before the PostBuildEvent. Fix for [f16c93a932].&nbsp;<b>** Potentially Incompatible Change **</b></li>
    <li>Modify GetSchemaTable method to avoid setting SchemaTableColumn.IsKey column to true when more than one table is referenced. Fix for [47c6fa04d3].&nbsp;<b>** Potentially Incompatible Change **</b></li>
    <li>Add AppendManifestToken_SQLiteProviderManifest environment variable to enable better integration between LINQ and the underlying store connection.</li>
    <li>Add SQLite_ForceLogPrepare environment variable to force logging of all prepared SQL regardless of the flags for the associated connection.</li>
    <li>Honor the DateTimeFormat, DateTimeKind, DateTimeFormatString, BinaryGUID connection string and/or provider manifest token properties from within the LINQ assembly. Fix for [8d928c3e88].&nbsp;<b>** Potentially Incompatible Change **</b></li>
</ul>
<p>
    <b>1.0.94.0 - September 9, 2014</b>
</p>
<ul>
    <li>Updated to [http://www.sqlite.org/releaselog/3_8_6.html|SQLite 3.8.6].</li>
    <li>Updated to [http://www.nuget.org/packages/EntityFramework/6.1.1|Entity Framework 6.1.1].</li>