System.Data.SQLite
Check-in [45312da90e]
Not logged in

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

Overview
Comment:Fix extra single quotes around the formatted DateTime values in the LINQ assembly. Use GetSettingValue to access the new environment variables.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | tkt-8d928c3e88
Files: files | file ages | folders
SHA1: 45312da90e224763e718532f9c715a5f2b7d9290
User & Date: mistachkin 2015-01-13 04:08:26
Context
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
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

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

   116    116     /// Renaming issues.
   117    117     /// When rows or columns are renamed, we produce names that are unique globally
   118    118     /// with respect to the query.  The names are derived from the original names,
   119    119     /// with an integer as a suffix. e.g. CustomerId will be renamed to CustomerId1,
   120    120     /// CustomerId2 etc.
   121    121     ///
   122    122     /// Since the names generated are globally unique, they will not conflict when the
   123         -  /// columns of a JOIN SELECT statement are joined with another JOIN. 
          123  +  /// columns of a JOIN SELECT statement are joined with another JOIN.
   124    124     ///
   125    125     /// </para>
   126    126     ///
   127    127     /// <para>
   128    128     /// Record flattening.
   129    129     /// SQL server does not have the concept of records.  However, a join statement
   130    130     /// produces records.  We have to flatten the record accesses into a simple
................................................................................
   434    434         return functionNameToOperatorDictionary;
   435    435       }
   436    436   
   437    437       #endregion
   438    438   
   439    439       #region Constructor
   440    440       /// <summary>
   441         -    /// Basic constructor. 
          441  +    /// Basic constructor.
   442    442       /// </summary>
   443    443       private SqlGenerator(SQLiteProviderManifest manifest)
   444    444       {
   445    445         _manifest = manifest;
   446    446       }
   447    447       #endregion
   448    448   
................................................................................
   462    462   
   463    463         //Handle Query
   464    464         DbQueryCommandTree queryCommandTree = tree as DbQueryCommandTree;
   465    465         if (queryCommandTree != null)
   466    466         {
   467    467           SqlGenerator sqlGen = new SqlGenerator(manifest);
   468    468           parameters = null;
   469         -        
          469  +
   470    470           string sql = sqlGen.GenerateSql((DbQueryCommandTree)tree);
   471    471   
   472    472           return sql;
   473    473         }
   474    474   
   475    475         //Handle Function
   476    476         DbFunctionCommandTree DbFunctionCommandTree = tree as DbFunctionCommandTree;
................................................................................
   891    891   
   892    892               string dateString = SQLiteConvert.ToString(
   893    893                   (System.DateTime)e.Value, _manifest._dateTimeFormat,
   894    894                   _manifest._dateTimeKind, _manifest._dateTimeFormatString);
   895    895   
   896    896               if (needQuotes)
   897    897               {
   898         -                result.Append("\'");
   899         -                result.Append(EscapeSingleQuote(dateString, false /* IsUnicode */));
   900         -                result.Append("\'");
          898  +                result.Append(EscapeSingleQuote(
          899  +                    dateString, false /* IsUnicode */));
   901    900               }
   902    901               else
   903    902               {
   904    903                   result.Append(dateString);
   905    904               }
   906    905               break;
   907    906   
................................................................................
  1143   1142       /// Lambda functions are not supported.
  1144   1143       /// The functions supported are:
  1145   1144       /// <list type="number">
  1146   1145       /// <item>Canonical Functions - We recognize these by their dataspace, it is DataSpace.CSpace</item>
  1147   1146       /// <item>Store Functions - We recognize these by the BuiltInAttribute and not being Canonical</item>
  1148   1147       /// <item>User-defined Functions - All the rest except for Lambda functions</item>
  1149   1148       /// </list>
  1150         -    /// We handle Canonical and Store functions the same way: If they are in the list of functions 
         1149  +    /// We handle Canonical and Store functions the same way: If they are in the list of functions
  1151   1150       /// that need special handling, we invoke the appropriate handler, otherwise we translate them to
  1152   1151       /// FunctionName(arg1, arg2, ..., argn).
  1153   1152       /// We translate user-defined functions to NamespaceName.FunctionName(arg1, arg2, ..., argn).
  1154   1153       /// </summary>
  1155   1154       /// <param name="e"></param>
  1156   1155       /// <returns>A <see cref="SqlBuilder"/></returns>
  1157   1156       public override ISqlFragment Visit(DbFunctionExpression e)
................................................................................
  1194   1193       }
  1195   1194   
  1196   1195       /// <summary>
  1197   1196       /// <see cref="Visit(DbFilterExpression)"/> for general details.
  1198   1197       /// We modify both the GroupBy and the Select fields of the SqlSelectStatement.
  1199   1198       /// GroupBy gets just the keys without aliases,
  1200   1199       /// and Select gets the keys and the aggregates with aliases.
  1201         -    /// 
         1200  +    ///
  1202   1201       /// Whenever there exists at least one aggregate with an argument that is not is not a simple
  1203         -    /// <see cref="DbPropertyExpression"/>  over <see cref="DbVariableReferenceExpression"/>, 
  1204         -    /// we create a nested query in which we alias the arguments to the aggregates. 
         1202  +    /// <see cref="DbPropertyExpression"/>  over <see cref="DbVariableReferenceExpression"/>,
         1203  +    /// we create a nested query in which we alias the arguments to the aggregates.
  1205   1204       /// That is due to the following two limitations of Sql Server:
  1206   1205       /// <list type="number">
  1207         -    /// <item>If an expression being aggregated contains an outer reference, then that outer 
         1206  +    /// <item>If an expression being aggregated contains an outer reference, then that outer
  1208   1207       /// reference must be the only column referenced in the expression </item>
  1209         -    /// <item>Sql Server cannot perform an aggregate function on an expression containing 
         1208  +    /// <item>Sql Server cannot perform an aggregate function on an expression containing
  1210   1209       /// an aggregate or a subquery. </item>
  1211   1210       /// </list>
  1212         -    /// 
  1213         -    /// The default translation, without inner query is: 
  1214         -    /// 
  1215         -    ///     SELECT 
  1216         -    ///         kexp1 AS key1, kexp2 AS key2,... kexpn AS keyn, 
         1211  +    ///
         1212  +    /// The default translation, without inner query is:
         1213  +    ///
         1214  +    ///     SELECT
         1215  +    ///         kexp1 AS key1, kexp2 AS key2,... kexpn AS keyn,
  1217   1216       ///         aggf1(aexpr1) AS agg1, .. aggfn(aexprn) AS aggn
  1218   1217       ///     FROM input AS a
  1219   1218       ///     GROUP BY kexp1, kexp2, .. kexpn
  1220         -    /// 
         1219  +    ///
  1221   1220       /// When we inject an innner query, the equivalent translation is:
  1222         -    /// 
  1223         -    ///     SELECT 
  1224         -    ///         key1 AS key1, key2 AS key2, .. keyn AS keys,  
         1221  +    ///
         1222  +    ///     SELECT
         1223  +    ///         key1 AS key1, key2 AS key2, .. keyn AS keys,
  1225   1224       ///         aggf1(agg1) AS agg1, aggfn(aggn) AS aggn
  1226   1225       ///     FROM (
  1227         -    ///             SELECT 
  1228         -    ///                 kexp1 AS key1, kexp2 AS key2,... kexpn AS keyn, 
         1226  +    ///             SELECT
         1227  +    ///                 kexp1 AS key1, kexp2 AS key2,... kexpn AS keyn,
  1229   1228       ///                 aexpr1 AS agg1, .. aexprn AS aggn
  1230   1229       ///             FROM input AS a
  1231   1230       ///         ) as a
  1232   1231       ///     GROUP BY key1, key2, keyn
  1233         -    /// 
         1232  +    ///
  1234   1233       /// </summary>
  1235   1234       /// <param name="e"></param>
  1236   1235       /// <returns>A <see cref="SqlSelectStatement"/></returns>
  1237   1236       public override ISqlFragment Visit(DbGroupByExpression e)
  1238   1237       {
  1239   1238         Symbol fromSymbol;
  1240   1239         //SqlSelectStatement result = VisitInputExpression(e.Input.Expression,
................................................................................
  1256   1255         symbolTable.Add(e.Input.GroupVariableName, fromSymbol);
  1257   1256   
  1258   1257   
  1259   1258         // The enumerator is shared by both the keys and the aggregates,
  1260   1259         // so, we do not close it in between.
  1261   1260         RowType groupByType = MetadataHelpers.GetEdmType<RowType>(MetadataHelpers.GetEdmType<CollectionType>(e.ResultType).TypeUsage);
  1262   1261   
  1263         -      //Whenever there exists at least one aggregate with an argument that is not simply a PropertyExpression 
         1262  +      //Whenever there exists at least one aggregate with an argument that is not simply a PropertyExpression
  1264   1263         // over a VarRefExpression, we need a nested query in which we alias the arguments to the aggregates.
  1265   1264         bool needsInnerQuery = NeedsInnerQuery(e.Aggregates);
  1266   1265   
  1267   1266         SqlSelectStatement result;
  1268   1267         if (needsInnerQuery)
  1269   1268         {
  1270   1269           //Create the inner query
................................................................................
  1308   1307               // The inner query contains the default translation Key AS Alias
  1309   1308               innerQuery.Select.Append(separator);
  1310   1309               innerQuery.Select.AppendLine();
  1311   1310               innerQuery.Select.Append(keySql);
  1312   1311               innerQuery.Select.Append(" AS ");
  1313   1312               innerQuery.Select.Append(alias);
  1314   1313   
  1315         -            //The outer resulting query projects over the key aliased in the inner query: 
         1314  +            //The outer resulting query projects over the key aliased in the inner query:
  1316   1315               //  fromSymbol.Alias AS Alias
  1317   1316               result.Select.Append(separator);
  1318   1317               result.Select.AppendLine();
  1319   1318               result.Select.Append(fromSymbol);
  1320   1319               result.Select.Append(".");
  1321   1320               result.Select.Append(alias);
  1322   1321               result.Select.Append(" AS ");
................................................................................
  1482   1481       public override ISqlFragment Visit(DbLikeExpression e)
  1483   1482       {
  1484   1483         SqlBuilder result = new SqlBuilder();
  1485   1484         result.Append(e.Argument.Accept(this));
  1486   1485         result.Append(" LIKE ");
  1487   1486         result.Append(e.Pattern.Accept(this));
  1488   1487   
  1489         -      // if the ESCAPE expression is a DbNullExpression, then that's tantamount to 
         1488  +      // if the ESCAPE expression is a DbNullExpression, then that's tantamount to
  1490   1489         // not having an ESCAPE at all
  1491   1490         if (e.Escape.ExpressionKind != DbExpressionKind.Null)
  1492   1491         {
  1493   1492           result.Append(" ESCAPE ");
  1494   1493           result.Append(e.Escape.Accept(this));
  1495   1494         }
  1496   1495   
................................................................................
  1832   1831         throw new NotSupportedException();
  1833   1832       }
  1834   1833   
  1835   1834       /// <summary>
  1836   1835       /// For Sql9 it translates to:
  1837   1836       /// SELECT Y.x1, Y.x2, ..., Y.xn
  1838   1837       /// FROM (
  1839         -    ///     SELECT X.x1, X.x2, ..., X.xn, row_number() OVER (ORDER BY sk1, sk2, ...) AS [row_number] 
  1840         -    ///     FROM input as X 
         1838  +    ///     SELECT X.x1, X.x2, ..., X.xn, row_number() OVER (ORDER BY sk1, sk2, ...) AS [row_number]
         1839  +    ///     FROM input as X
  1841   1840       ///     ) as Y
  1842         -    /// WHERE Y.[row_number] > count 
         1841  +    /// WHERE Y.[row_number] > count
  1843   1842       /// ORDER BY sk1, sk2, ...
  1844   1843       /// </summary>
  1845   1844       /// <param name="e"></param>
  1846   1845       /// <returns>A <see cref="SqlBuilder"/></returns>
  1847   1846       public override ISqlFragment Visit(DbSkipExpression e)
  1848   1847       {
  1849   1848           Debug.Assert(e.Count is DbConstantExpression || e.Count is DbParameterReferenceExpression, "DbLimitExpression.Count is of invalid expression type");
................................................................................
  2635   2634           throw new InvalidOperationException("Special handling should be called only for functions in the list of special functions");
  2636   2635   
  2637   2636         return handlers[e.Function.Name](this, e);
  2638   2637       }
  2639   2638   
  2640   2639       /// <summary>
  2641   2640       /// Handles functions that are translated into TSQL operators.
  2642         -    /// The given function should have one or two arguments. 
  2643         -    /// Functions with one arguemnt are translated into 
         2641  +    /// The given function should have one or two arguments.
         2642  +    /// Functions with one arguemnt are translated into
  2644   2643       ///     op arg
  2645   2644       /// Functions with two arguments are translated into
  2646   2645       ///     arg0 op arg1
  2647   2646       /// Also, the arguments can be optionaly enclosed in parethesis
  2648   2647       /// </summary>
  2649   2648       /// <param name="e"></param>
  2650   2649       /// <param name="parenthesiseArguments">Whether the arguments should be enclosed in parethesis</param>
................................................................................
  2847   2846             break;
  2848   2847         }
  2849   2848   
  2850   2849         return result;
  2851   2850       }
  2852   2851   
  2853   2852       /// <summary>
  2854         -    /// DateSubtract(datetime1, datetime2) -> DATEDIFF ( seconds , startdate , enddate ) 
         2853  +    /// DateSubtract(datetime1, datetime2) -> DATEDIFF ( seconds , startdate , enddate )
  2855   2854       /// </summary>
  2856   2855       /// <param name="sqlgen"></param>
  2857   2856       /// <param name="e"></param>
  2858   2857       /// <returns></returns>
  2859   2858       private static ISqlFragment HandleCanonicalFunctionDateSubtract(SqlGenerator sqlgen, DbFunctionExpression e)
  2860   2859       {
  2861   2860         SqlBuilder result = new SqlBuilder();
................................................................................
  2871   2870             break;
  2872   2871         }
  2873   2872   
  2874   2873         return result;
  2875   2874       }
  2876   2875   
  2877   2876       /// <summary>
  2878         -    /// Handler for canonical functions for extracting date parts. 
         2877  +    /// Handler for canonical functions for extracting date parts.
  2879   2878       /// For example:
  2880   2879       ///     Year(date) -> DATEPART( year, date)
  2881   2880       /// </summary>
  2882   2881       /// <param name="sqlgen"></param>
  2883   2882       /// <param name="e"></param>
  2884   2883       /// <returns></returns>
  2885   2884       private static ISqlFragment HandleCanonicalFunctionDatepart(SqlGenerator sqlgen, DbFunctionExpression e)
................................................................................
  3330   3329         {
  3331   3330           symbolTable.Add(inputVarName, fromSymbol);
  3332   3331         }
  3333   3332       }
  3334   3333   
  3335   3334       /// <summary>
  3336   3335       /// Translates a list of SortClauses.
  3337         -    /// Used in the translation of OrderBy 
         3336  +    /// Used in the translation of OrderBy
  3338   3337       /// </summary>
  3339   3338       /// <param name="orderByClause">The SqlBuilder to which the sort keys should be appended</param>
  3340   3339       /// <param name="sortKeys"></param>
  3341   3340       void AddSortKeys(SqlBuilder orderByClause, IList<DbSortClause> sortKeys)
  3342   3341       {
  3343   3342         string separator = "";
  3344   3343         foreach (DbSortClause sortClause in sortKeys)
................................................................................
  3474   3473       /// <returns>The escaped sql string.</returns>
  3475   3474       private static string EscapeSingleQuote(string s, bool isUnicode)
  3476   3475       {
  3477   3476         return "'" + s.Replace("'", "''") + "'";
  3478   3477       }
  3479   3478   
  3480   3479       /// <summary>
  3481         -    /// Returns the sql primitive/native type name. 
  3482         -    /// It will include size, precision or scale depending on type information present in the 
         3480  +    /// Returns the sql primitive/native type name.
         3481  +    /// It will include size, precision or scale depending on type information present in the
  3483   3482       /// type facets
  3484   3483       /// </summary>
  3485   3484       /// <param name="type"></param>
  3486   3485       /// <returns></returns>
  3487   3486       private string GetSqlPrimitiveType(TypeUsage type)
  3488   3487       {
  3489   3488         PrimitiveType primitiveType = MetadataHelpers.GetEdmType<PrimitiveType>(type);
................................................................................
  3602   3601       /// <returns></returns>
  3603   3602       private ISqlFragment HandleCountExpression(DbExpression e)
  3604   3603       {
  3605   3604         ISqlFragment result;
  3606   3605   
  3607   3606         if (e.ExpressionKind == DbExpressionKind.Constant)
  3608   3607         {
  3609         -        //For constant expression we should not cast the value, 
         3608  +        //For constant expression we should not cast the value,
  3610   3609           // thus we don't go throught the default DbConstantExpression handling
  3611   3610           SqlBuilder sqlBuilder = new SqlBuilder();
  3612   3611           sqlBuilder.Append(((DbConstantExpression)e).Value.ToString());
  3613   3612           result = sqlBuilder;
  3614   3613         }
  3615   3614         else
  3616   3615         {
................................................................................
  3687   3686       /// <returns></returns>
  3688   3687       bool IsCompatible(SqlSelectStatement result, DbExpressionKind expressionKind)
  3689   3688       {
  3690   3689         switch (expressionKind)
  3691   3690         {
  3692   3691           case DbExpressionKind.Distinct:
  3693   3692             return result.Top == null
  3694         -            // The projection after distinct may not project all 
         3693  +            // The projection after distinct may not project all
  3695   3694               // columns used in the Order By
  3696   3695                 && result.OrderBy.IsEmpty;
  3697   3696   
  3698   3697           case DbExpressionKind.Filter:
  3699   3698             return result.Select.IsEmpty
  3700   3699                     && result.Where.IsEmpty
  3701   3700                     && result.GroupBy.IsEmpty
................................................................................
  3810   3809       /// For the rest, we need to treat them as there was a dummy
  3811   3810       /// <code>
  3812   3811       /// -- originally {expression}
  3813   3812       /// -- change that to
  3814   3813       /// SELECT *
  3815   3814       /// FROM {expression} as c
  3816   3815       /// </code>
  3817         -    /// 
         3816  +    ///
  3818   3817       /// DbLimitExpression needs to start the statement but not add the default columns
  3819   3818       /// </summary>
  3820   3819       /// <param name="e"></param>
  3821   3820       /// <param name="addDefaultColumns"></param>
  3822   3821       /// <returns></returns>
  3823   3822       SqlSelectStatement VisitExpressionEnsureSqlStatement(DbExpression e, bool addDefaultColumns)
  3824   3823       {
................................................................................
  4035   4034   
  4036   4035           builder.Append("' ");
  4037   4036       }
  4038   4037   
  4039   4038       /// <summary>
  4040   4039       /// Helper method for the Group By visitor
  4041   4040       /// Returns true if at least one of the aggregates in the given list
  4042         -    /// has an argument that is not a <see cref="DbPropertyExpression"/> 
         4041  +    /// has an argument that is not a <see cref="DbPropertyExpression"/>
  4043   4042       /// over <see cref="DbVariableReferenceExpression"/>
  4044   4043       /// </summary>
  4045   4044       /// <param name="aggregates"></param>
  4046   4045       /// <returns></returns>
  4047   4046       static bool NeedsInnerQuery(IList<DbAggregate> aggregates)
  4048   4047       {
  4049   4048         foreach (DbAggregate aggregate in aggregates)
................................................................................
  4054   4053             return true;
  4055   4054           }
  4056   4055         }
  4057   4056         return false;
  4058   4057       }
  4059   4058   
  4060   4059       /// <summary>
  4061         -    /// Determines whether the given expression is a <see cref="DbPropertyExpression"/> 
         4060  +    /// Determines whether the given expression is a <see cref="DbPropertyExpression"/>
  4062   4061       /// over <see cref="DbVariableReferenceExpression"/>
  4063   4062       /// </summary>
  4064   4063       /// <param name="expression"></param>
  4065   4064       /// <returns></returns>
  4066   4065       static bool IsPropertyOverVarRef(DbExpression expression)
  4067   4066       {
  4068   4067         DbPropertyExpression propertyExpression = expression as DbPropertyExpression;
................................................................................
  4075   4074         {
  4076   4075           return false;
  4077   4076         }
  4078   4077         return true;
  4079   4078       }
  4080   4079   
  4081   4080       #endregion
  4082         -    
         4081  +
  4083   4082       private class KeyFieldExpressionComparer : IEqualityComparer<DbExpression>
  4084   4083       {
  4085   4084         // Fields
  4086   4085         internal static readonly SqlGenerator.KeyFieldExpressionComparer Singleton = new SqlGenerator.KeyFieldExpressionComparer();
  4087   4086   
  4088   4087         // Methods
  4089   4088         private KeyFieldExpressionComparer()

Changes to System.Data.SQLite.Linq/SQLiteProviderManifest.cs.

    79     79       /// The effective provider manifest token.
    80     80       /// </returns>
    81     81       private static string GetProviderManifestToken(
    82     82           string manifestToken
    83     83           )
    84     84       {
    85     85   #if !PLATFORM_COMPACTFRAMEWORK
    86         -        string value = Environment.GetEnvironmentVariable(
    87         -            "AppendManifestToken_SQLiteProviderManifest");
           86  +        string value = UnsafeNativeMethods.GetSettingValue(
           87  +            "AppendManifestToken_SQLiteProviderManifest", null);
    88     88   
    89     89           if (String.IsNullOrEmpty(value))
    90     90               return manifestToken;
    91     91   
    92     92           int capacity = value.Length;
    93     93   
    94     94           if (manifestToken != null)

Changes to System.Data.SQLite/Configurations/System.Data.SQLite.dll.config.

     5      5    *
     6      6    * Written by Joe Mistachkin.
     7      7    * Released to the public domain, use at your own risk!
     8      8    *
     9      9   -->
    10     10   <configuration>
    11     11     <appSettings>
           12  +    <!--
           13  +        NOTE: If this environment variable is set [to anything], it will be
           14  +              used by the System.Data.SQLite.Linq.SQLiteProviderManifest class
           15  +              (and the System.Data.SQLite.EF6.SQLiteProviderManifest class) to
           16  +              modify future provider manifest tokens by appending the value of
           17  +              the environment variable to the existing provider manifest token,
           18  +              if any.  Typically, in order for the constructed provider
           19  +              manifest token to be syntactically correct, the environment
           20  +              variable value [to be appended] must begin with a semicolon.
           21  +    -->
           22  +    <!--
           23  +    <add key="AppendManifestToken_SQLiteProviderManifest" value="" />
           24  +    -->
           25  +
    12     26       <!--
    13     27           NOTE: If this configuration variable is set [to anything], the SQLite
    14     28                 logging subsystem may be initialized in a non-default application
    15     29                 domain.  By default, this is not allowed due to the potential
    16     30                 for application domain unloading issues.
    17     31       -->
    18     32       <!--
................................................................................
   103    117                 architecture of the current process (e.g. a 32-bit x86
   104    118                 application running on a 64-bit x64 operating system should have
   105    119                 the value "x86").
   106    120       -->
   107    121       <!--
   108    122       <add key="PROCESSOR_ARCHITECTURE" value="%PROCESSOR_ARCHITECTURE%" />
   109    123       -->
          124  +
          125  +    <!--
          126  +        NOTE: If this environment variable is set [to anything], all calls to
          127  +              prepare a SQL query will be logged, regardless of the flags for
          128  +              the associated connection.
          129  +    -->
          130  +    <!--
          131  +    <add key="SQLite_ForceLogPrepare" value="1" />
          132  +    -->
   110    133   
   111    134       <!--
   112    135           NOTE: If this environment variable is set [to anything], it will be
   113    136                 used by the System.Data.SQLite.SQLiteFactory class as the type
   114    137                 name containing the System.Data.Common.DbProviderServices
   115    138                 implementation that should be used.
   116    139       -->

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

   910    910       /// </returns>
   911    911       private static bool ForceLogPrepare()
   912    912       {
   913    913           lock (syncRoot)
   914    914           {
   915    915               if (forceLogPrepare == null)
   916    916               {
   917         -                if (Environment.GetEnvironmentVariable(
   918         -                        "SQLite_ForceLogPrepare") != null)
          917  +                if (UnsafeNativeMethods.GetSettingValue(
          918  +                        "SQLite_ForceLogPrepare", null) != null)
   919    919                   {
   920    920                       forceLogPrepare = true;
   921    921                   }
   922    922                   else
   923    923                   {
   924    924                       forceLogPrepare = false;
   925    925                   }