New tests.
[mono.git] / mcs / class / System.Data.Linq / src / DbLinq / Data / Linq / Sugar / Implementation / QueryBuilder.cs
index 13762f2a6ddf3a3896082fd675675bdf07e66b72..62505027c726e57b1931480eae9edfa15a83b2de 100644 (file)
 \r
 using System;\r
 using System.Collections.Generic;\r
+using System.Diagnostics;\r
 using System.Linq;\r
 using System.Linq.Expressions;\r
+using System.Text.RegularExpressions;\r
 \r
-#if MONO_STRICT\r
-using System.Data.Linq.Sugar.ExpressionMutator;\r
-using System.Data.Linq.Sugar.Expressions;\r
-#else\r
 using DbLinq.Data.Linq.Sugar.ExpressionMutator;\r
 using DbLinq.Data.Linq.Sugar.Expressions;\r
-#endif\r
-using System.Text.RegularExpressions;\r
 using DbLinq.Factory;\r
 using DbLinq.Util;\r
-using System.Diagnostics;\r
 \r
-#if MONO_STRICT\r
-namespace System.Data.Linq.Sugar.Implementation\r
-#else\r
 namespace DbLinq.Data.Linq.Sugar.Implementation\r
-#endif\r
 {\r
     /// <summary>\r
     /// Full query builder, with cache management\r
@@ -98,7 +89,7 @@ namespace DbLinq.Data.Linq.Sugar.Implementation
         protected virtual IList<Expression> FindExpressionsByName(string name, BuilderContext builderContext)\r
         {\r
             var expressions = new List<Expression>();\r
-            expressions.AddRange(from t in builderContext.EnumerateAllTables() where t.Alias == name select (Expression)t);\r
+            expressions.AddRange((from t in builderContext.EnumerateAllTables() where t.Alias == name select (Expression)t).Distinct());\r
             expressions.AddRange(from c in builderContext.EnumerateScopeColumns() where c.Alias == name select (Expression)c);\r
             return expressions;\r
         }\r
@@ -126,7 +117,7 @@ namespace DbLinq.Data.Linq.Sugar.Implementation
         /// <param name="builderContext"></param>\r
         protected virtual void CheckTablesAlias(BuilderContext builderContext)\r
         {\r
-            var tables = builderContext.EnumerateAllTables().ToList();\r
+            var tables = builderContext.EnumerateAllTables().Distinct().ToList();\r
             // just to be nice: if we have only one table involved, there's no need to alias it\r
             if (tables.Count == 1)\r
             {\r
@@ -175,7 +166,7 @@ namespace DbLinq.Data.Linq.Sugar.Implementation
                     do\r
                     {\r
                         externalParameterExpression.Alias = MakeTableName(aliasBase, ++anonymousIndex, builderContext);\r
-                    } while (FindExpressionsByName(externalParameterExpression.Alias, builderContext).Count != 1);\r
+                    } while (FindParametersByName(externalParameterExpression.Alias, builderContext).Count > 1);\r
                 }\r
             }\r
         }\r
@@ -209,26 +200,7 @@ namespace DbLinq.Data.Linq.Sugar.Implementation
         /// <returns></returns>\r
         protected Expression BuildExpressionQuery(ExpressionChain expressions, Expression tableExpression, BuilderContext builderContext)\r
         {\r
-            var last = expressions.Last();\r
-            foreach (var expression in expressions)\r
-            {\r
-                if (expression == last)\r
-                    builderContext.IsExternalInExpressionChain = true;\r
-\r
-                // write full debug\r
-#if DEBUG && !MONO_STRICT\r
-                var log = builderContext.QueryContext.DataContext.Log;\r
-                if (log != null)\r
-                    log.WriteExpression(expression);\r
-#endif\r
-                // Convert linq Expressions to QueryOperationExpressions and QueryConstantExpressions \r
-                // Query expressions language identification\r
-                var currentExpression = ExpressionLanguageParser.Parse(expression, builderContext);\r
-                // Query expressions query identification \r
-                currentExpression = ExpressionDispatcher.Analyze(currentExpression, tableExpression, builderContext);\r
-\r
-                tableExpression = currentExpression;\r
-            }\r
+            tableExpression = ExpressionDispatcher.Analyze(expressions, tableExpression, builderContext);\r
             ExpressionDispatcher.BuildSelect(tableExpression, builderContext);\r
             return tableExpression;\r
         }\r
@@ -292,9 +264,37 @@ namespace DbLinq.Data.Linq.Sugar.Implementation
                 var scopeExpression = builderContext.SelectExpressions[scopeExpressionIndex];\r
 \r
                 // where clauses\r
+                List<int> whereToRemove = new List<int>(); // List of where clausole evaluating TRUE (could be ignored and so removed)\r
+                bool falseWhere = false; // true when the full where evaluate to FALSE\r
                 for (int whereIndex = 0; whereIndex < scopeExpression.Where.Count; whereIndex++)\r
                 {\r
-                    scopeExpression.Where[whereIndex] = processor(scopeExpression.Where[whereIndex], builderContext);\r
+                    Expression whereClausole = processor(scopeExpression.Where[whereIndex], builderContext);\r
+                    ConstantExpression constantWhereClausole = whereClausole as ConstantExpression;\r
+                    if (constantWhereClausole != null)\r
+                    {\r
+                        if (constantWhereClausole.Value.Equals(false))\r
+                        {\r
+                            falseWhere = true;\r
+                            break;\r
+                        }\r
+                        else if (constantWhereClausole.Value.Equals(true))\r
+                        {\r
+                            whereToRemove.Add(whereIndex);\r
+                            continue;\r
+                        }\r
+                    }\r
+                    scopeExpression.Where[whereIndex] = whereClausole;\r
+                }\r
+                if (scopeExpression.Where.Count > 0)\r
+                {\r
+                    if (falseWhere)\r
+                    {\r
+                        scopeExpression.Where.Clear();\r
+                        scopeExpression.Where.Add(Expression.Equal(Expression.Constant(true), Expression.Constant(false)));\r
+                    }\r
+                    else\r
+                        foreach (int whereIndex in whereToRemove)\r
+                            scopeExpression.Where.RemoveAt(whereIndex);\r
                 }\r
 \r
                 // limit clauses\r
@@ -353,29 +353,25 @@ namespace DbLinq.Data.Linq.Sugar.Implementation
         protected virtual SelectQuery GetFromSelectCache(ExpressionChain expressions)\r
         {\r
             var cache = QueryCache;\r
-            lock (cache)\r
-                return cache.GetFromSelectCache(expressions);\r
+            return cache.GetFromSelectCache(expressions);\r
         }\r
 \r
         protected virtual void SetInSelectCache(ExpressionChain expressions, SelectQuery sqlSelectQuery)\r
         {\r
             var cache = QueryCache;\r
-            lock (cache)\r
-                cache.SetInSelectCache(expressions, sqlSelectQuery);\r
+            cache.SetInSelectCache(expressions, sqlSelectQuery);\r
         }\r
 \r
         protected virtual Delegate GetFromTableReaderCache(Type tableType, IList<string> columns)\r
         {\r
             var cache = QueryCache;\r
-            lock (cache)\r
-                return cache.GetFromTableReaderCache(tableType, columns);\r
+            return cache.GetFromTableReaderCache(tableType, columns);\r
         }\r
 \r
         protected virtual void SetInTableReaderCache(Type tableType, IList<string> columns, Delegate tableReader)\r
         {\r
             var cache = queryCache;\r
-            lock (cache)\r
-                cache.SetInTableReaderCache(tableType, columns, tableReader);\r
+            cache.SetInTableReaderCache(tableType, columns, tableReader);\r
         }\r
 \r
         /// <summary>\r
@@ -386,35 +382,47 @@ namespace DbLinq.Data.Linq.Sugar.Implementation
         /// <returns></returns>\r
         public SelectQuery GetSelectQuery(ExpressionChain expressions, QueryContext queryContext)\r
         {\r
-            var query = GetFromSelectCache(expressions);\r
+            SelectQuery query = null;\r
+            if (queryContext.DataContext.QueryCacheEnabled)\r
+            {\r
+                query = GetFromSelectCache(expressions);\r
+            }\r
             if (query == null)\r
             {\r
-                var timer = new Stopwatch();\r
-                timer.Start();\r
+                Profiler.At("START: GetSelectQuery(), building Expression query");\r
                 var expressionsQuery = BuildExpressionQuery(expressions, queryContext);\r
-                timer.Stop();\r
-                long expressionBuildTime = timer.ElapsedMilliseconds;\r
+                Profiler.At("END: GetSelectQuery(), building Expression query");\r
 \r
-                timer.Reset();\r
-                timer.Start();\r
+                Profiler.At("START: GetSelectQuery(), building Sql query");\r
                 query = BuildSqlQuery(expressionsQuery, queryContext);\r
-                timer.Stop();\r
-                long sqlBuildTime = timer.ElapsedMilliseconds;\r
+                Profiler.At("END: GetSelectQuery(), building Sql query");\r
 \r
-#if DEBUG && !MONO_STRICT\r
-                // generation time statistics\r
-                var log = queryContext.DataContext.Log;\r
-                if (log != null)\r
+                if (queryContext.DataContext.QueryCacheEnabled)\r
                 {\r
-                    log.WriteLine("Select Expression build: {0}ms", expressionBuildTime);\r
-                    log.WriteLine("Select SQL build:        {0}ms", sqlBuildTime);\r
+                    SetInSelectCache(expressions, query);\r
                 }\r
-#endif\r
-                SetInSelectCache(expressions, query);\r
+            }\r
+            else if (query.InputParameters.Count > 0)\r
+            {\r
+                Profiler.At("START: GetSelectQuery(), building Expression parameters of cached query");\r
+                var parameters = BuildExpressionParameters(expressions, queryContext);\r
+                query = new SelectQuery(queryContext.DataContext, query.Sql, parameters, query.RowObjectCreator, query.ExecuteMethodName);\r
+                Profiler.At("END: GetSelectQuery(), building Expression parameters of cached query");\r
             }\r
             return query;\r
         }\r
 \r
+        IList<InputParameterExpression> BuildExpressionParameters(ExpressionChain expressions, QueryContext queryContext)\r
+        {\r
+            var builderContext = new BuilderContext(queryContext);\r
+            var previousExpression = ExpressionDispatcher.CreateTableExpression(expressions.Expressions[0], builderContext);\r
+            previousExpression = BuildExpressionQuery(expressions, previousExpression, builderContext);\r
+            BuildOffsetsAndLimits(builderContext);\r
+            // then prepare Parts for SQL translation\r
+            PrepareSqlOperands(builderContext);\r
+            return builderContext.ExpressionQuery.Parameters;\r
+        }\r
+\r
         /// <summary>\r
         /// Returns a Delegate to create a row for a given IDataRecord\r
         /// The Delegate is Func&lt;IDataRecord,MappingContext,"tableType">\r
@@ -425,13 +433,20 @@ namespace DbLinq.Data.Linq.Sugar.Implementation
         /// <returns></returns>\r
         public virtual Delegate GetTableReader(Type tableType, IList<string> parameters, QueryContext queryContext)\r
         {\r
-            var reader = GetFromTableReaderCache(tableType, parameters);\r
+            Delegate reader = null;\r
+            if (queryContext.DataContext.QueryCacheEnabled)\r
+            {\r
+                reader = GetFromTableReaderCache(tableType, parameters);\r
+            }\r
             if (reader == null)\r
             {\r
                 var lambda = ExpressionDispatcher.BuildTableReader(tableType, parameters,\r
                                                                    new BuilderContext(queryContext));\r
                 reader = lambda.Compile();\r
-                SetInTableReaderCache(tableType, parameters, reader);\r
+                if (queryContext.DataContext.QueryCacheEnabled)\r
+                {\r
+                    SetInTableReaderCache(tableType, parameters, reader);\r
+                }\r
             }\r
             return reader;\r
         }\r