From 685c5daf361459256c77b07bf31fb62223e14e16 Mon Sep 17 00:00:00 2001 From: Leonardo Galli Date: Sun, 20 Aug 2017 22:12:02 +0200 Subject: [PATCH] Fixed: Rename movie not working (#1970) (Fixes #1908) --- .../Parameters/ParameterChainMethods.cs | 22 +++++-- src/Marr.Data/QGen/Dialects/Dialect.cs | 8 +++ src/Marr.Data/QGen/WhereBuilder.cs | 62 ++++++++++++++----- .../Extensions/IEnumerableExtensions.cs | 4 ++ .../Datastore/BasicRepository.cs | 5 +- .../MediaFiles/RenameMovieFileService.cs | 25 +++++--- 6 files changed, 96 insertions(+), 30 deletions(-) diff --git a/src/Marr.Data/Parameters/ParameterChainMethods.cs b/src/Marr.Data/Parameters/ParameterChainMethods.cs index c1ceef248..e00535ff7 100644 --- a/src/Marr.Data/Parameters/ParameterChainMethods.cs +++ b/src/Marr.Data/Parameters/ParameterChainMethods.cs @@ -14,8 +14,10 @@ You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ using System; +using System.Collections.Generic; using System.Data; using System.Data.Common; +using System.Runtime.InteropServices.ComTypes; using Marr.Data.Converters; namespace Marr.Data.Parameters @@ -42,15 +44,25 @@ namespace Marr.Data.Parameters Type valueType = value.GetType(); // Check for a registered IConverter - IConverter converter = MapRepository.Instance.GetConverter(valueType); - if (converter != null) + //If we have a list of ints, we ignore the converter since we want to do an in statement! + var list = value as List; + if (list != null) { - Parameter.Value = converter.ToDB(value); + Parameter.Value = $"{string.Join(",", list)}"; } else { - Parameter.Value = value; - } + IConverter converter = MapRepository.Instance.GetConverter(valueType); + if (converter != null) + { + Parameter.Value = converter.ToDB(value); + } + else + { + Parameter.Value = value; + } + } + //// Determine the correct DbType based on the passed in value type //IDbTypeBuilder typeBuilder = MapRepository.Instance.DbTypeBuilder; diff --git a/src/Marr.Data/QGen/Dialects/Dialect.cs b/src/Marr.Data/QGen/Dialects/Dialect.cs index 195cabc1f..915866dfe 100644 --- a/src/Marr.Data/QGen/Dialects/Dialect.cs +++ b/src/Marr.Data/QGen/Dialects/Dialect.cs @@ -68,5 +68,13 @@ namespace Marr.Data.QGen.Dialects { get { return "({0} LIKE '%' + {1} + '%')"; } } + + public virtual string InFormat + { + get + { + return "({0} in ({1}))"; + } + } } } diff --git a/src/Marr.Data/QGen/WhereBuilder.cs b/src/Marr.Data/QGen/WhereBuilder.cs index 64992d542..0a7c217f7 100644 --- a/src/Marr.Data/QGen/WhereBuilder.cs +++ b/src/Marr.Data/QGen/WhereBuilder.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Text; using System.Linq.Expressions; using System.Data.Common; @@ -92,6 +93,10 @@ namespace Marr.Data.QGen case "EndsWith": Write_EndsWith(expression); break; + + case "In": + Write_In(expression); + break; default: string msg = string.Format("'{0}' expressions are not yet implemented in the where clause expression tree parser.", method); @@ -140,31 +145,47 @@ namespace Marr.Data.QGen return expression; } - private object GetRightValue(Expression rightExpression) + private object GetRightValue(Expression expression) { object rightValue = null; - - var right = rightExpression as ConstantExpression; - if (right == null) // Value is not directly passed in as a constant + + var simpleConstExp = expression as ConstantExpression; + if (simpleConstExp == null) // Value is not directly passed in as a constant { - var rightMemberExp = (rightExpression as MemberExpression); - var parentMemberExpression = rightMemberExp.Expression as MemberExpression; - if (parentMemberExpression != null) // Value is passed in as a property on a parent entity + MemberExpression memberExp = expression as MemberExpression; + ConstantExpression constExp = null; + + // Value may be nested in multiple levels of objects/properties, so traverse the MemberExpressions + // until a ConstantExpression property value is found, and then unwind the stack to get the value. + var memberNames = new Stack(); + + while (memberExp != null) { - string entityName = (rightMemberExp.Expression as MemberExpression).Member.Name; - var container = ((rightMemberExp.Expression as MemberExpression).Expression as ConstantExpression).Value; - var entity = _repos.ReflectionStrategy.GetFieldValue(container, entityName); - rightValue = _repos.ReflectionStrategy.GetFieldValue(entity, rightMemberExp.Member.Name); + memberNames.Push(memberExp.Member.Name); + + // Function calls are not supported - user needs to simplify their Where expression. + var methodExp = memberExp.Expression as MethodCallExpression; + if (methodExp != null) + { + var errMsg = string.Format("Function calls are not supported by the Where clause expression parser. Please evaluate your function call, '{0}', manually and then use the resulting paremeter value in your Where expression.", methodExp.Method.Name); + throw new NotSupportedException(errMsg); + } + + constExp = memberExp.Expression as ConstantExpression; + memberExp = memberExp.Expression as MemberExpression; } - else // Value is passed in as a variable + + object entity = constExp.Value; + while (memberNames.Count > 0) { - var parent = (rightMemberExp.Expression as ConstantExpression).Value; - rightValue = _repos.ReflectionStrategy.GetFieldValue(parent, rightMemberExp.Member.Name); + string entityName = memberNames.Pop(); + entity = _repos.ReflectionStrategy.GetFieldValue(entity, entityName); } + rightValue = entity; } else // Value is passed in directly as a constant { - rightValue = right.Value; + rightValue = simpleConstExp.Value; } return rightValue; @@ -238,6 +259,17 @@ namespace Marr.Data.QGen _sb.AppendFormat(_dialect.ContainsFormat, fqColumn, paramName); } + private void Write_In(MethodCallExpression body) + { + object value = GetRightValue(body.Arguments[1]); + //string paramName = string.Concat(_paramPrefix, "P", _command.Parameters.Count.ToString()); + //var parameter = new ParameterChainMethods(_command, paramName, value).Parameter; + + MemberExpression memberExp = (body.Arguments[0] as MemberExpression); + string fqColumn = GetFullyQualifiedColumnName(memberExp.Member, memberExp.Expression.Type); + _sb.AppendFormat(_dialect.InFormat, fqColumn, string.Join(",", value as List)); + } + private void Write_StartsWith(MethodCallExpression body) { // Add parameter to Command.Parameters diff --git a/src/NzbDrone.Common/Extensions/IEnumerableExtensions.cs b/src/NzbDrone.Common/Extensions/IEnumerableExtensions.cs index b6fca0ea2..7a7efd12c 100644 --- a/src/NzbDrone.Common/Extensions/IEnumerableExtensions.cs +++ b/src/NzbDrone.Common/Extensions/IEnumerableExtensions.cs @@ -105,5 +105,9 @@ namespace NzbDrone.Common.Extensions yield return buffer.Dequeue(); } } + public static bool In(this T source, List list) + { + return list.Contains(source); + } } } \ No newline at end of file diff --git a/src/NzbDrone.Core/Datastore/BasicRepository.cs b/src/NzbDrone.Core/Datastore/BasicRepository.cs index 88a78536e..7c3ff7642 100644 --- a/src/NzbDrone.Core/Datastore/BasicRepository.cs +++ b/src/NzbDrone.Core/Datastore/BasicRepository.cs @@ -83,11 +83,12 @@ namespace NzbDrone.Core.Datastore { var idList = ids.ToList(); var query = string.Format("Id IN ({0})", string.Join(",", idList)); - var result = Query.Where(query).ToList(); + var result = Query.Where(m => m.Id.In(idList)).ToList(); + //var result = Query.Where(query).ToList(); if (result.Count != idList.Count()) { - throw new ApplicationException("Expected query to return {0} rows but returned {1}".Inject(idList.Count(), result.Count)); + throw new ApplicationException("Expected query to return {0} rows but returned {1}.".Inject(idList.Count(), result.Count)); } return result; diff --git a/src/NzbDrone.Core/MediaFiles/RenameMovieFileService.cs b/src/NzbDrone.Core/MediaFiles/RenameMovieFileService.cs index a1a24a626..626c8929f 100644 --- a/src/NzbDrone.Core/MediaFiles/RenameMovieFileService.cs +++ b/src/NzbDrone.Core/MediaFiles/RenameMovieFileService.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Data.SQLite; using System.Linq; using System.Text; using System.IO; @@ -198,14 +199,22 @@ namespace NzbDrone.Core.MediaFiles public void Execute(RenameMovieFolderCommand message) { - _logger.Debug("Renaming movie folder for selected movie if necessary"); - var moviesToRename = _movieService.GetMovies(message.MovieIds); - foreach(var movie in moviesToRename) - { - var movieFiles = _mediaFileService.GetFilesByMovie(movie.Id); - _logger.ProgressInfo("Renaming movie folder for {0}", movie.Title); - RenameMoviePath(movie); - } + try + { + _logger.Debug("Renaming movie folder for selected movie if necessary"); + var moviesToRename = _movieService.GetMovies(message.MovieIds); + foreach(var movie in moviesToRename) + { + var movieFiles = _mediaFileService.GetFilesByMovie(movie.Id); + _logger.ProgressInfo("Renaming movie folder for {0}", movie.Title); + RenameMoviePath(movie); + } + } + catch (SQLiteException ex) + { + _logger.Warn(ex, "wtf: {0}, {1}", ex.ResultCode, ex.Data); + } + } } }