Index: System.Data.SQLite.Linq/SQL ================================================================== --- System.Data.SQLite.Linq/SQL +++ System.Data.SQLite.Linq/SQL @@ -1,359 +1,717 @@ -/******************************************************** - * 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! - ********************************************************/ +//--------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +//--------------------------------------------------------------------- namespace System.Data.SQLite { using System; using System.Collections.Generic; - using System.Data; - using System.Data.Common.CommandTrees; - using System.Data.Entity; - using System.Runtime.CompilerServices; - - internal class SqlChecker : DbExpressionVisitor - { - private static Type sql8rewriter; - - static SqlChecker() - { - sql8rewriter = Type.GetType("System.Data.SqlClient.SqlGen.Sql8ExpressionRewriter, System.Data.Entity, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", false); - } - - private SqlChecker() - { + using System.Diagnostics; + using System.Globalization; + using System.IO; + using System.Text; + using System.Data; + using System.Data.Common; + using System.Data.Metadata.Edm; + using System.Data.Common.CommandTrees; + using System.Data.Common.Utils; + using System.Data.Mapping.Update.Internal; + + /// + /// Class generating SQL for a DML command tree. + /// + internal static class DmlSqlGenerator + { + private static readonly int s_commandTextBuilderInitialCapacity = 256; + + internal static string GenerateUpdateSql(DbUpdateCommandTree tree, out List parameters) + { + StringBuilder commandText = new StringBuilder(s_commandTextBuilderInitialCapacity); + ExpressionTranslator translator = new ExpressionTranslator(commandText, tree, null != tree.Returning, "UpdateFunction"); + + // update [schemaName].[tableName] + commandText.Append("UPDATE "); + tree.Target.Expression.Accept(translator); + commandText.AppendLine(); + + // set c1 = ..., c2 = ..., ... + bool first = true; + commandText.Append("SET "); + foreach (DbSetClause setClause in tree.SetClauses) + { + if (first) { first = false; } + else { commandText.Append(", "); } + setClause.Property.Accept(translator); + commandText.Append(" = "); + setClause.Value.Accept(translator); + } + + if (first) + { + // If first is still true, it indicates there were no set + // clauses. Introduce a fake set clause so that: + // - we acquire the appropriate locks + // - server-gen columns (e.g. timestamp) get recomputed + // + // We use the following pattern: + // + // update Foo + // set @i = 0 + // where ... + DbParameter parameter = translator.CreateParameter(default(Int32), DbType.Int32); + commandText.Append(parameter.ParameterName); + commandText.Append(" = 0"); + } + commandText.AppendLine(); + + // where c1 = ..., c2 = ... + commandText.Append("WHERE "); + tree.Predicate.Accept(translator); + commandText.AppendLine(); + + // generate returning sql + GenerateReturningSql(commandText, tree, translator, tree.Returning); + + parameters = translator.Parameters; + return commandText.ToString(); + } + + internal static string GenerateDeleteSql(DbDeleteCommandTree tree, out List parameters) + { + StringBuilder commandText = new StringBuilder(s_commandTextBuilderInitialCapacity); + ExpressionTranslator translator = new ExpressionTranslator(commandText, tree, false, "DeleteFunction"); + + // delete [schemaName].[tableName] + commandText.Append("DELETE FROM "); + tree.Target.Expression.Accept(translator); + commandText.AppendLine(); + + // where c1 = ... AND c2 = ... + commandText.Append("WHERE "); + tree.Predicate.Accept(translator); + + parameters = translator.Parameters; + + commandText.AppendLine(";"); + return commandText.ToString(); + } + + internal static string GenerateInsertSql(DbInsertCommandTree tree, out List parameters) + { + StringBuilder commandText = new StringBuilder(s_commandTextBuilderInitialCapacity); + ExpressionTranslator translator = new ExpressionTranslator(commandText, tree, null != tree.Returning, "InsertFunction"); + + // insert [schemaName].[tableName] + commandText.Append("INSERT INTO "); + tree.Target.Expression.Accept(translator); + + if (tree.SetClauses.Count > 0) + { + // (c1, c2, c3, ...) + commandText.Append("("); + bool first = true; + foreach (DbSetClause setClause in tree.SetClauses) + { + if (first) { first = false; } + else { commandText.Append(", "); } + setClause.Property.Accept(translator); + } + commandText.AppendLine(")"); + + // values c1, c2, ... + first = true; + commandText.Append(" VALUES ("); + foreach (DbSetClause setClause in tree.SetClauses) + { + if (first) { first = false; } + else { commandText.Append(", "); } + setClause.Value.Accept(translator); + + translator.RegisterMemberValue(setClause.Property, setClause.Value); + } + commandText.AppendLine(");"); + } + else // No columns specified. Insert an empty row containing default values by inserting null into the rowid + { + commandText.AppendLine(" DEFAULT VALUES;"); + } + + // generate returning sql + GenerateReturningSql(commandText, tree, translator, tree.Returning); + + parameters = translator.Parameters; + return commandText.ToString(); + } + + // Generates T-SQL describing a member + // Requires: member must belong to an entity type (a safe requirement for DML + // SQL gen, where we only access table columns) + private static string GenerateMemberTSql(EdmMember member) + { + return SqlGenerator.QuoteIdentifier(member.Name); + } + + /// + /// Generates SQL fragment returning server-generated values. + /// Requires: translator knows about member values so that we can figure out + /// how to construct the key predicate. + /// + /// Sample SQL: + /// + /// select IdentityValue + /// from dbo.MyTable + /// where @@ROWCOUNT > 0 and IdentityValue = scope_identity() + /// + /// or + /// + /// select TimestamptValue + /// from dbo.MyTable + /// where @@ROWCOUNT > 0 and Id = 1 + /// + /// Note that we filter on rowcount to ensure no rows are returned if no rows were modified. + /// + /// + /// Builder containing command text + /// Modification command tree + /// Translator used to produce DML SQL statement + /// for the tree + /// Returning expression. If null, the method returns + /// immediately without producing a SELECT statement. + private static void GenerateReturningSql(StringBuilder commandText, DbModificationCommandTree tree, + ExpressionTranslator translator, DbExpression returning) + { + // Nothing to do if there is no Returning expression + if (null == returning) { return; } + + // select + commandText.Append("SELECT "); + returning.Accept(translator); + commandText.AppendLine(); + + // from + commandText.Append("FROM "); + tree.Target.Expression.Accept(translator); + commandText.AppendLine(); + + // where + commandText.Append("WHERE last_rows_affected() > 0"); + EntitySetBase table = ((DbScanExpression)tree.Target.Expression).Target; + bool identity = false; + foreach (EdmMember keyMember in table.ElementType.KeyMembers) + { + commandText.Append(" AND "); + commandText.Append(GenerateMemberTSql(keyMember)); + commandText.Append(" = "); + + // retrieve member value sql. the translator remembers member values + // as it constructs the DML statement (which precedes the "returning" + // SQL) + DbParameter value; + if (translator.MemberValues.TryGetValue(keyMember, out value)) + { + commandText.Append(value.ParameterName); + } + else + { + // if no value is registered for the key member, it means it is an identity + // which can be retrieved using the scope_identity() function + if (identity) + { + // there can be only one server generated key + throw new NotSupportedException(string.Format("Server generated keys are only supported for identity columns. More than one key column is marked as server generated in table '{0}'.", table.Name)); + } + commandText.AppendLine("last_insert_rowid();"); + identity = true; + } + } } /// - /// SQLite doesn't support things like SKIP and a few other things. So determine if the query has to be rewritten - /// - /// - /// Microsoft went to all the trouble of making things like SKIP work on Sql Server 2000 by doing a rewrite of the commandtree. - /// However, all that fancy stuff is hidden from us. Thanks to reflection however, we can go ahead and use the Sql 2000 rewriter code - /// they made. - /// - /// The tree to inspect for a rewrite - /// Returns a new query tree if it needs rewriting - internal static DbQueryCommandTree Rewrite(DbQueryCommandTree tree) - { - SqlChecker visitor = new SqlChecker(); - if (tree.Query.Accept(visitor)) - { - tree = sql8rewriter.InvokeMember("Rewrite", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.InvokeMethod | System.Reflection.BindingFlags.Static, null, null, new object[] { tree }) as DbQueryCommandTree; - } - return tree; - } - - public override bool Visit(DbAndExpression expression) - { - return VisitBinaryExpression(expression); - } - - public override bool Visit(DbApplyExpression expression) - { - throw new NotSupportedException("apply expression"); - } - - public override bool Visit(DbArithmeticExpression expression) - { - return VisitExpressionList(expression.Arguments); - } - - public override bool Visit(DbCaseExpression expression) - { - bool flag1 = VisitExpressionList(expression.When); - bool flag2 = VisitExpressionList(expression.Then); - bool flag3 = VisitExpression(expression.Else); - - return (flag1 || flag2 || flag3); - } - - public override bool Visit(DbCastExpression expression) - { - return VisitUnaryExpression(expression); - } - - public override bool Visit(DbComparisonExpression expression) - { - return VisitBinaryExpression(expression); - } - - public override bool Visit(DbConstantExpression expression) - { - return false; - } - - public override bool Visit(DbCrossJoinExpression expression) - { - return VisitExpressionBindingList(expression.Inputs); - } - - public override bool Visit(DbDerefExpression expression) - { - return VisitUnaryExpression(expression); - } - - public override bool Visit(DbDistinctExpression expression) - { - return VisitUnaryExpression(expression); - } - - public override bool Visit(DbElementExpression expression) - { - return VisitUnaryExpression(expression); - } - - public override bool Visit(DbEntityRefExpression expression) - { - return VisitUnaryExpression(expression); - } - - public override bool Visit(DbExceptExpression expression) - { - VisitExpression(expression.Left); - VisitExpression(expression.Right); - return true; - } - - public override bool Visit(DbExpression expression) - { - throw new NotSupportedException(expression.GetType().FullName); - } - - public override bool Visit(DbFilterExpression expression) - { - bool flag1 = VisitExpressionBinding(expression.Input); - bool flag2 = VisitExpression(expression.Predicate); - - return (flag1 || flag2); - } - - public override bool Visit(DbFunctionExpression expression) - { - return VisitExpressionList(expression.Arguments); - } - - public override bool Visit(DbGroupByExpression expression) - { - bool flag1 = VisitExpression(expression.Input.Expression); - bool flag2 = VisitExpressionList(expression.Keys); - bool flag3 = VisitAggregateList(expression.Aggregates); - - return (flag1 || flag2 || flag3); - } - - public override bool Visit(DbIntersectExpression expression) - { - VisitExpression(expression.Left); - VisitExpression(expression.Right); - return true; - } - - public override bool Visit(DbIsEmptyExpression expression) - { - return VisitUnaryExpression(expression); - } - - public override bool Visit(DbIsNullExpression expression) - { - return VisitUnaryExpression(expression); - } - - public override bool Visit(DbIsOfExpression expression) - { - return VisitUnaryExpression(expression); - } - - public override bool Visit(DbJoinExpression expression) - { - bool flag1 = VisitExpressionBinding(expression.Left); - bool flag2 = VisitExpressionBinding(expression.Right); - bool flag3 = VisitExpression(expression.JoinCondition); - return (flag1 || flag2 || flag3); - } - - public override bool Visit(DbLikeExpression expression) - { - bool flag1 = VisitExpression(expression.Argument); - bool flag2 = VisitExpression(expression.Pattern); - bool flag3 = VisitExpression(expression.Escape); - return (flag1 || flag2 || flag3); - } - - public override bool Visit(DbLimitExpression expression) - { - return VisitExpression(expression.Argument); - } - - public override bool Visit(DbNewInstanceExpression expression) - { - return VisitExpressionList(expression.Arguments); - } - - public override bool Visit(DbNotExpression expression) - { - return VisitUnaryExpression(expression); - } - - public override bool Visit(DbNullExpression expression) - { - return false; - } - - public override bool Visit(DbOfTypeExpression expression) - { - return VisitUnaryExpression(expression); - } - - public override bool Visit(DbOrExpression expression) - { - return VisitBinaryExpression(expression); - } - - public override bool Visit(DbParameterReferenceExpression expression) - { - return false; - } - - public override bool Visit(DbProjectExpression expression) - { - bool flag1 = VisitExpressionBinding(expression.Input); - bool flag2 = VisitExpression(expression.Projection); - return (flag1 || flag2); - } - - public override bool Visit(DbPropertyExpression expression) - { - return VisitExpression(expression.Instance); - } - - public override bool Visit(DbQuantifierExpression expression) - { - bool flag1 = VisitExpressionBinding(expression.Input); - bool flag2 = VisitExpression(expression.Predicate); - return (flag1 || flag2); - } - - public override bool Visit(DbRefExpression expression) - { - return VisitUnaryExpression(expression); - } - - public override bool Visit(DbRefKeyExpression expression) - { - return VisitUnaryExpression(expression); - } - - public override bool Visit(DbRelationshipNavigationExpression expression) - { - return VisitExpression(expression.NavigationSource); - } - - public override bool Visit(DbScanExpression expression) - { - return false; - } - - public override bool Visit(DbSkipExpression expression) - { - VisitExpressionBinding(expression.Input); - VisitSortClauseList(expression.SortOrder); - VisitExpression(expression.Count); - return true; - } - - public override bool Visit(DbSortExpression expression) - { - bool flag1 = VisitExpressionBinding(expression.Input); - bool flag2 = VisitSortClauseList(expression.SortOrder); - return (flag1 || flag2); - } - - public override bool Visit(DbTreatExpression expression) - { - return VisitUnaryExpression(expression); - } - - public override bool Visit(DbUnionAllExpression expression) - { - return VisitBinaryExpression(expression); - } - - public override bool Visit(DbVariableReferenceExpression expression) - { - return false; - } - - private bool VisitAggregate(DbAggregate aggregate) - { - return VisitExpressionList(aggregate.Arguments); - } - - private bool VisitAggregateList(IList list) - { - return VisitList(new ListElementHandler(VisitAggregate), list); - } - - private bool VisitBinaryExpression(DbBinaryExpression expr) - { - bool flag1 = VisitExpression(expr.Left); - bool flag2 = VisitExpression(expr.Right); - return (flag1 || flag2); - } - - private bool VisitExpression(DbExpression expression) - { - if (expression == null) - { - return false; - } - return expression.Accept(this); - } - - private bool VisitExpressionBinding(DbExpressionBinding expressionBinding) - { - return VisitExpression(expressionBinding.Expression); - } - - private bool VisitExpressionBindingList(IList list) - { - return VisitList(new ListElementHandler(VisitExpressionBinding), list); - } - - private bool VisitExpressionList(IList list) - { - return VisitList(new ListElementHandler(VisitExpression), list); - } - - private static bool VisitList(ListElementHandler handler, IList list) - { - bool flag = false; - foreach (TElementType local in list) - { - bool flag2 = handler(local); - flag = flag || flag2; - } - return flag; - } - - private bool VisitSortClause(DbSortClause sortClause) - { - return VisitExpression(sortClause.Expression); - } - - private bool VisitSortClauseList(IList list) - { - return VisitList(new ListElementHandler(VisitSortClause), list); - } - - private bool VisitUnaryExpression(DbUnaryExpression expr) - { - return VisitExpression(expr.Argument); - } - - private delegate bool ListElementHandler(TElementType element); - } -} + /// Lightweight expression translator for DML expression trees, which have constrained + /// scope and support. + /// + private class ExpressionTranslator : DbExpressionVisitor + { + /// + /// Initialize a new expression translator populating the given string builder + /// with command text. Command text builder and command tree must not be null. + /// + /// Command text with which to populate commands + /// Command tree generating SQL + /// Indicates whether the translator should preserve + /// member values while compiling t-SQL (only needed for server generation) + internal ExpressionTranslator(StringBuilder commandText, DbModificationCommandTree commandTree, + bool preserveMemberValues, string kind) + { + Debug.Assert(null != commandText); + Debug.Assert(null != commandTree); + _kind = kind; + _commandText = commandText; + _commandTree = commandTree; + _parameters = new List(); + _memberValues = preserveMemberValues ? new Dictionary() : + null; + } + + private readonly StringBuilder _commandText; + private readonly DbModificationCommandTree _commandTree; + private readonly List _parameters; + private readonly Dictionary _memberValues; + private int parameterNameCount = 0; + private string _kind; + + internal List Parameters { get { return _parameters; } } + internal Dictionary MemberValues { get { return _memberValues; } } + + // generate parameter (name based on parameter ordinal) + internal SQLiteParameter CreateParameter(object value, TypeUsage type) + { + PrimitiveTypeKind primitiveType = MetadataHelpers.GetPrimitiveTypeKind(type); + DbType dbType = MetadataHelpers.GetDbType(primitiveType); + return CreateParameter(value, dbType); + } + + // Creates a new parameter for a value in this expression translator + internal SQLiteParameter CreateParameter(object value, DbType dbType) + { + string parameterName = string.Concat("@p", parameterNameCount.ToString(CultureInfo.InvariantCulture)); + parameterNameCount++; + SQLiteParameter parameter = new SQLiteParameter(parameterName, value); + parameter.DbType = dbType; + _parameters.Add(parameter); + return parameter; + } + + #region Basics + + public override void Visit(DbApplyExpression expression) + { + if (expression == null) throw new ArgumentException("expression"); + + VisitExpressionBindingPre(expression.Input); + if (expression.Apply != null) + { + VisitExpression(expression.Apply.Expression); + } + VisitExpressionBindingPost(expression.Input); + } + + public override void Visit(DbArithmeticExpression expression) + { + if (expression == null) throw new ArgumentException("expression"); + VisitExpressionList(expression.Arguments); + } + + public override void Visit(DbCaseExpression expression) + { + if (expression == null) throw new ArgumentException("expression"); + VisitExpressionList(expression.When); + VisitExpressionList(expression.Then); + VisitExpression(expression.Else); + } + + public override void Visit(DbCastExpression expression) + { + VisitUnaryExpression(expression); + } + + public override void Visit(DbCrossJoinExpression expression) + { + if (expression == null) throw new ArgumentException("expression"); + foreach (DbExpressionBinding binding in expression.Inputs) + { + VisitExpressionBindingPre(binding); + } + foreach (DbExpressionBinding binding2 in expression.Inputs) + { + VisitExpressionBindingPost(binding2); + } + } + + public override void Visit(DbDerefExpression expression) + { + VisitUnaryExpression(expression); + } + + public override void Visit(DbDistinctExpression expression) + { + VisitUnaryExpression(expression); + } + + public override void Visit(DbElementExpression expression) + { + VisitUnaryExpression(expression); + } + + public override void Visit(DbEntityRefExpression expression) + { + VisitUnaryExpression(expression); + } + + public override void Visit(DbExceptExpression expression) + { + VisitBinary(expression); + } + + protected virtual void VisitBinary(DbBinaryExpression expression) + { + if (expression == null) throw new ArgumentException("expression"); + this.VisitExpression(expression.Left); + this.VisitExpression(expression.Right); + } + + public override void Visit(DbExpression expression) + { + if (expression == null) throw new ArgumentException("expression"); + throw new NotSupportedException("DbExpression"); + } + + public override void Visit(DbFilterExpression expression) + { + if (expression == null) throw new ArgumentException("expression"); + VisitExpressionBindingPre(expression.Input); + VisitExpression(expression.Predicate); + VisitExpressionBindingPost(expression.Input); + } + + public override void Visit(DbFunctionExpression expression) + { + if (expression == null) throw new ArgumentException("expression"); + VisitExpressionList(expression.Arguments); + //if (expression.IsLambda) + //{ + // VisitLambdaFunctionPre(expression.Function, expression.LambdaBody); + // VisitExpression(expression.LambdaBody); + // VisitLambdaFunctionPost(expression.Function, expression.LambdaBody); + //} + } + + public override void Visit(DbGroupByExpression expression) + { + if (expression == null) throw new ArgumentException("expression"); + VisitGroupExpressionBindingPre(expression.Input); + VisitExpressionList(expression.Keys); + VisitGroupExpressionBindingMid(expression.Input); + VisitAggregateList(expression.Aggregates); + VisitGroupExpressionBindingPost(expression.Input); + } + + public override void Visit(DbIntersectExpression expression) + { + VisitBinary(expression); + } + + public override void Visit(DbIsEmptyExpression expression) + { + VisitUnaryExpression(expression); + } + + public override void Visit(DbIsOfExpression expression) + { + VisitUnaryExpression(expression); + } + + public override void Visit(DbJoinExpression expression) + { + if (expression == null) throw new ArgumentException("expression"); + VisitExpressionBindingPre(expression.Left); + VisitExpressionBindingPre(expression.Right); + VisitExpression(expression.JoinCondition); + VisitExpressionBindingPost(expression.Left); + VisitExpressionBindingPost(expression.Right); + } + + public override void Visit(DbLikeExpression expression) + { + if (expression == null) throw new ArgumentException("expression"); + VisitExpression(expression.Argument); + VisitExpression(expression.Pattern); + VisitExpression(expression.Escape); + } + + public override void Visit(DbLimitExpression expression) + { + if (expression == null) throw new ArgumentException("expression"); + VisitExpression(expression.Argument); + VisitExpression(expression.Limit); + } + + public override void Visit(DbOfTypeExpression expression) + { + VisitUnaryExpression(expression); + } + + public override void Visit(DbParameterReferenceExpression expression) + { + if (expression == null) throw new ArgumentException("expression"); + } + + public override void Visit(DbProjectExpression expression) + { + if (expression == null) throw new ArgumentException("expression"); + VisitExpressionBindingPre(expression.Input); + VisitExpression(expression.Projection); + VisitExpressionBindingPost(expression.Input); + } + + public override void Visit(DbQuantifierExpression expression) + { + if (expression == null) throw new ArgumentException("expression"); + VisitExpressionBindingPre(expression.Input); + VisitExpression(expression.Predicate); + VisitExpressionBindingPost(expression.Input); + } + + public override void Visit(DbRefExpression expression) + { + VisitUnaryExpression(expression); + } + + public override void Visit(DbRefKeyExpression expression) + { + VisitUnaryExpression(expression); + } + + public override void Visit(DbRelationshipNavigationExpression expression) + { + if (expression == null) throw new ArgumentException("expression"); + VisitExpression(expression.NavigationSource); + } + + public override void Visit(DbSkipExpression expression) + { + if (expression == null) throw new ArgumentException("expression"); + VisitExpressionBindingPre(expression.Input); + foreach (DbSortClause clause in expression.SortOrder) + { + VisitExpression(clause.Expression); + } + VisitExpressionBindingPost(expression.Input); + VisitExpression(expression.Count); + } + + public override void Visit(DbSortExpression expression) + { + if (expression == null) throw new ArgumentException("expression"); + VisitExpressionBindingPre(expression.Input); + for (int i = 0; i < expression.SortOrder.Count; i++) + { + VisitExpression(expression.SortOrder[i].Expression); + } + VisitExpressionBindingPost(expression.Input); + } + + public override void Visit(DbTreatExpression expression) + { + VisitUnaryExpression(expression); + } + + public override void Visit(DbUnionAllExpression expression) + { + VisitBinary(expression); + } + + public override void Visit(DbVariableReferenceExpression expression) + { + if (expression == null) throw new ArgumentException("expression"); + } + + public virtual void VisitAggregate(DbAggregate aggregate) + { + if (aggregate == null) throw new ArgumentException("aggregate"); + VisitExpressionList(aggregate.Arguments); + } + + public virtual void VisitAggregateList(IList aggregates) + { + if (aggregates == null) throw new ArgumentException("aggregates"); + for (int i = 0; i < aggregates.Count; i++) + { + VisitAggregate(aggregates[i]); + } + } + + public virtual void VisitExpression(DbExpression expression) + { + if (expression == null) throw new ArgumentException("expression"); + expression.Accept(this); + } + + protected virtual void VisitExpressionBindingPost(DbExpressionBinding binding) + { + } + + protected virtual void VisitExpressionBindingPre(DbExpressionBinding binding) + { + if (binding == null) throw new ArgumentException("binding"); + VisitExpression(binding.Expression); + } + + public virtual void VisitExpressionList(IList expressionList) + { + if (expressionList == null) throw new ArgumentException("expressionList"); + for (int i = 0; i < expressionList.Count; i++) + { + VisitExpression(expressionList[i]); + } + } + + protected virtual void VisitGroupExpressionBindingMid(DbGroupExpressionBinding binding) + { + } + + protected virtual void VisitGroupExpressionBindingPost(DbGroupExpressionBinding binding) + { + } + + protected virtual void VisitGroupExpressionBindingPre(DbGroupExpressionBinding binding) + { + if (binding == null) throw new ArgumentException("binding"); + VisitExpression(binding.Expression); + } + + protected virtual void VisitLambdaFunctionPost(EdmFunction function, DbExpression body) + { + } + + protected virtual void VisitLambdaFunctionPre(EdmFunction function, DbExpression body) + { + if (function == null) throw new ArgumentException("function"); + if (body == null) throw new ArgumentException("body"); + } + + //internal virtual void VisitRelatedEntityReference(DbRelatedEntityRef relatedEntityRef) + //{ + // VisitExpression(relatedEntityRef.TargetEntityReference); + //} + + //internal virtual void VisitRelatedEntityReferenceList(IList relatedEntityReferences) + //{ + // for (int i = 0; i < relatedEntityReferences.Count; i++) + // { + // VisitRelatedEntityReference(relatedEntityReferences[i]); + // } + //} + + protected virtual void VisitUnaryExpression(DbUnaryExpression expression) + { + if (expression == null) throw new ArgumentException("expression"); + VisitExpression(expression.Argument); + } + #endregion + + public override void Visit(DbAndExpression expression) + { + VisitBinary(expression, " AND "); + } + + public override void Visit(DbOrExpression expression) + { + VisitBinary(expression, " OR "); + } + + public override void Visit(DbComparisonExpression expression) + { + Debug.Assert(expression.ExpressionKind == DbExpressionKind.Equals, + "only equals comparison expressions are produced in DML command trees in V1"); + + VisitBinary(expression, " = "); + + RegisterMemberValue(expression.Left, expression.Right); + } + + /// + /// Call this method to register a property value pair so the translator "remembers" + /// the values for members of the row being modified. These values can then be used + /// to form a predicate for server-generation (based on the key of the row) + /// + /// DbExpression containing the column reference (property expression). + /// DbExpression containing the value of the column. + internal void RegisterMemberValue(DbExpression propertyExpression, DbExpression value) + { + if (null != _memberValues) + { + // register the value for this property + Debug.Assert(propertyExpression.ExpressionKind == DbExpressionKind.Property, + "DML predicates and setters must be of the form property = value"); + + // get name of left property + EdmMember property = ((DbPropertyExpression)propertyExpression).Property; + + // don't track null values + if (value.ExpressionKind != DbExpressionKind.Null) + { + Debug.Assert(value.ExpressionKind == DbExpressionKind.Constant, + "value must either constant or null"); + // retrieve the last parameter added (which describes the parameter) + _memberValues[property] = _parameters[_parameters.Count - 1]; + } + } + } + + public override void Visit(DbIsNullExpression expression) + { + expression.Argument.Accept(this); + _commandText.Append(" IS NULL"); + } + + public override void Visit(DbNotExpression expression) + { + _commandText.Append("NOT ("); + expression.Accept(this); + _commandText.Append(")"); + } + + public override void Visit(DbConstantExpression expression) + { + SQLiteParameter parameter = CreateParameter(expression.Value, expression.ResultType); + _commandText.Append(parameter.ParameterName); + } + + public override void Visit(DbScanExpression expression) + { + string definingQuery = MetadataHelpers.TryGetValueForMetadataProperty(expression.Target, "DefiningQuery"); + if (definingQuery != null) + { + throw new NotSupportedException(String.Format("Unable to update the EntitySet '{0}' because it has a DefiningQuery and no <{1}> element exists in the element to support the current operation.", expression.Target.Name, _kind)); + } + _commandText.Append(SqlGenerator.GetTargetTSql(expression.Target)); + } + + public override void Visit(DbPropertyExpression expression) + { + _commandText.Append(GenerateMemberTSql(expression.Property)); + } + + public override void Visit(DbNullExpression expression) + { + _commandText.Append("NULL"); + } + + public override void Visit(DbNewInstanceExpression expression) + { + // assumes all arguments are self-describing (no need to use aliases + // because no renames are ever used in the projection) + bool first = true; + foreach (DbExpression argument in expression.Arguments) + { + if (first) { first = false; } + else { _commandText.Append(", "); } + argument.Accept(this); + } + } + + private void VisitBinary(DbBinaryExpression expression, string separator) + { + _commandText.Append("("); + expression.Left.Accept(this); + _commandText.Append(separator); + expression.Right.Accept(this); + _commandText.Append(")"); + } + } + } +} + Index: testce/testce.csproj ================================================================== --- testce/testce.csproj +++ testce/testce.csproj @@ -9,12 +9,12 @@ Properties test testce {4D628B5B-2FBC-4AA6-8C16-197242AEB884};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} PocketPC - b2c48bd2-963d-4549-9169-1fa021dce484 - 5.02 + 3C41C503-53EF-4c2a-8DD4-A8217CAD115E + 4.20 testce v2.0 @@ -21,11 +21,11 @@ %25CSIDL_PROGRAM_FILES%25 2.0 - Windows Mobile 6 Professional SDK + Pocket PC 2003 true @@ -61,10 +61,11 @@ False False + True False Index: testlinq/northwindEF.db ================================================================== --- testlinq/northwindEF.db +++ testlinq/northwindEF.db cannot compute difference between binary files Index: tools/install/InstallDesigner.cs ================================================================== --- tools/install/InstallDesigner.cs +++ tools/install/InstallDesigner.cs @@ -293,10 +293,25 @@ subkey.SetValue(null, path); } } } } + + for (int n = 0; n < compactFrameworks.Length; n++) + { + using (RegistryKey key = Registry.LocalMachine.OpenSubKey(String.Format("Software\\Microsoft\\.NETCompactFramework\\v3.5.0.0\\{0}\\AssemblyFoldersEx", compactFrameworks[n]), true)) + { + + if (key != null) + { + using (RegistryKey subkey = key.CreateSubKey("SQLite", RegistryKeyPermissionCheck.ReadWriteSubTree)) + { + subkey.SetValue(null, path); + } + } + } + } } for (int n = 0; n < 2; n++) { // Add factory support to the machine.config file. @@ -356,20 +371,24 @@ { try { Registry.LocalMachine.DeleteSubKey("Software\\Microsoft\\.NETFramework\\v2.0.50727\\AssemblyFoldersEx\\SQLite"); - for (int n = 0; n < compactFrameworks.Length; n++) - { - using (RegistryKey key = Registry.LocalMachine.OpenSubKey(String.Format("Software\\Microsoft\\.NETCompactFramework\\v2.0.0.0\\{0}\\DataProviders", compactFrameworks[n]), true)) - { - try - { - if (key != null) key.DeleteSubKey(standardDataProviderGuid.ToString("B")); - } - catch - { + string[] versions = { "v2.0.0.0", "v3.5.0.0" }; + for (int x = 0; x < versions.Length; x++) + { + for (int n = 0; n < compactFrameworks.Length; n++) + { + using (RegistryKey key = Registry.LocalMachine.OpenSubKey(String.Format("Software\\Microsoft\\.NETCompactFramework\\{1}\\{0}\\DataProviders", compactFrameworks[n], versions[x]), true)) + { + try + { + if (key != null) key.DeleteSubKey(standardDataProviderGuid.ToString("B")); + } + catch + { + } } } } for (int n = 0; n < compactFrameworks.Length; n++) @@ -473,12 +492,12 @@ { } finally { File.Delete(tempPath); - if (File.Exists(Path.GetFullPath("System.Data.SQLite.Linq.DLL")) == true) - AssemblyCache.InstallAssembly(Path.GetFullPath("System.Data.SQLite.Linq.DLL"), null, AssemblyCommitFlags.Default); + if (File.Exists(Path.GetFullPath("..\\System.Data.SQLite.Linq.DLL")) == true) + AssemblyCache.InstallAssembly(Path.GetFullPath("..\\System.Data.SQLite.Linq.DLL"), null, AssemblyCommitFlags.Default); AssemblyCache.InstallAssembly(Path.GetFullPath("SQLite.Designer.DLL"), null, AssemblyCommitFlags.Default); AssemblyCache.InstallAssembly(SQLiteLocation, null, AssemblyCommitFlags.Default); } } @@ -634,21 +653,26 @@ //} //catch //{ //} - for (int n = 0; n < compactFrameworks.Length; n++) - { - using (RegistryKey key = Registry.LocalMachine.OpenSubKey(String.Format("Software\\Microsoft\\.NETCompactFramework\\v2.0.0.0\\{0}\\DataProviders", compactFrameworks[n]), true)) - { - if (key != null) - { - using (RegistryKey subkey = key.CreateSubKey(standardDataProviderGuid.ToString("B"), RegistryKeyPermissionCheck.ReadWriteSubTree)) - { - subkey.SetValue(null, ".NET Framework Data Provider for SQLite"); - subkey.SetValue("InvariantName", "System.Data.SQLite"); - subkey.SetValue("RuntimeAssembly", "System.Data.SQLite.DLL"); + string[] versions = { "v2.0.0.0", "v3.5.0.0" }; + + for (int x = 0; x < versions.Length; x++) + { + for (int n = 0; n < compactFrameworks.Length; n++) + { + using (RegistryKey key = Registry.LocalMachine.CreateSubKey(String.Format("Software\\Microsoft\\.NETCompactFramework\\{1}\\{0}\\DataProviders", compactFrameworks[n], versions[x]))) + { + if (key != null) + { + using (RegistryKey subkey = key.CreateSubKey(standardDataProviderGuid.ToString("B"), RegistryKeyPermissionCheck.ReadWriteSubTree)) + { + subkey.SetValue(null, ".NET Framework Data Provider for SQLite"); + subkey.SetValue("InvariantName", "System.Data.SQLite"); + subkey.SetValue("RuntimeAssembly", "System.Data.SQLite.DLL"); + } } } } } Index: tools/install/Properties/Resources.Designer.cs ================================================================== --- tools/install/Properties/Resources.Designer.cs +++ tools/install/Properties/Resources.Designer.cs @@ -1,9 +1,9 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:2.0.50727.1433 +// Runtime Version:2.0.50727.3053 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // //------------------------------------------------------------------------------ Index: tools/install/Properties/Resources.resx ================================================================== --- tools/install/Properties/Resources.resx +++ tools/install/Properties/Resources.resx @@ -117,8 +117,8 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - ..\Resources\System.Data.SQLite.DLL;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + ..\Resources\System.Data.SQLite.dll;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Index: tools/install/install.csproj ================================================================== --- tools/install/install.csproj +++ tools/install/install.csproj @@ -1,10 +1,10 @@ Debug AnyCPU - 9.0.30428 + 9.0.30729 2.0 {71EED886-B5BF-488E-A4AA-1403E393D224} WinExe Properties install @@ -73,29 +73,27 @@ InstallDesigner.cs + + True + True + Resources.resx + Designer InstallDesigner.cs ResXFileCodeGenerator Resources.Designer.cs - Designer - - True - Resources.resx - True - - False .NET Framework 2.0 %28x86%29 @@ -112,10 +110,11 @@ false +