@ -586,7 +586,7 @@ namespace Emby.Server.Implementations.Data
/// <exception cref="ArgumentNullException">
/// <paramref name="items"/> or <paramref name="cancellationToken"/> is <c>null</c>.
/// </exception>
public void SaveItems ( I Enumerable < BaseItem > items , CancellationToken cancellationToken )
public void SaveItems ( I ReadOnlyList < BaseItem > items , CancellationToken cancellationToken )
{
ArgumentNullException . ThrowIfNull ( items ) ;
@ -594,9 +594,11 @@ namespace Emby.Server.Implementations.Data
CheckDisposed ( ) ;
var tuples = new List < ( BaseItem , List < Guid > , BaseItem , string , List < string > ) > ( ) ;
foreach ( var item in items )
var itemsLen = items . Count ;
var tuples = new ValueTuple < BaseItem , List < Guid > , BaseItem , string , List < string > > [ itemsLen ] ;
for ( int i = 0 ; i < itemsLen ; i + + )
{
var item = items [ i ] ;
var ancestorIds = item . SupportsAncestors ?
item . GetAncestorIds ( ) . Distinct ( ) . ToList ( ) :
null ;
@ -606,7 +608,7 @@ namespace Emby.Server.Implementations.Data
var userdataKey = item . GetUserDataKeys ( ) . FirstOrDefault ( ) ;
var inheritedTags = item . GetInheritedTags ( ) ;
tuples .Add ( ( item , ancestorIds , topParent , userdataKey , inheritedTags ) ) ;
tuples [i ] = ( item , ancestorIds , topParent , userdataKey , inheritedTags ) ;
}
using ( var connection = GetConnection ( ) )
@ -3202,7 +3204,8 @@ namespace Emby.Server.Implementations.Data
return IsAlphaNumeric ( value ) ;
}
private List < string > GetWhereClauses ( InternalItemsQuery query , IStatement statement )
#nullable enable
private List < string > GetWhereClauses ( InternalItemsQuery query , IStatement ? statement )
{
if ( query . IsResumable ? ? false )
{
@ -3677,7 +3680,6 @@ namespace Emby.Server.Implementations.Data
if ( statement is not null )
{
nameContains = FixUnicodeChars ( nameContains ) ;
statement . TryBind ( "@NameContains" , "%" + GetCleanValue ( nameContains ) + "%" ) ;
}
}
@ -3803,13 +3805,8 @@ namespace Emby.Server.Implementations.Data
foreach ( var artistId in query . ArtistIds )
{
var paramName = "@ArtistIds" + index ;
clauses . Add ( "(guid in (select itemid from ItemValues where CleanValue = (select CleanName from TypedBaseItems where guid=" + paramName + ") and Type<=1))" ) ;
if ( statement is not null )
{
statement . TryBind ( paramName , artistId ) ;
}
statement ? . TryBind ( paramName , artistId ) ;
index + + ;
}
@ -3824,13 +3821,8 @@ namespace Emby.Server.Implementations.Data
foreach ( var artistId in query . AlbumArtistIds )
{
var paramName = "@ArtistIds" + index ;
clauses . Add ( "(guid in (select itemid from ItemValues where CleanValue = (select CleanName from TypedBaseItems where guid=" + paramName + ") and Type=1))" ) ;
if ( statement is not null )
{
statement . TryBind ( paramName , artistId ) ;
}
statement ? . TryBind ( paramName , artistId ) ;
index + + ;
}
@ -3845,13 +3837,8 @@ namespace Emby.Server.Implementations.Data
foreach ( var artistId in query . ContributingArtistIds )
{
var paramName = "@ArtistIds" + index ;
clauses . Add ( "((select CleanName from TypedBaseItems where guid=" + paramName + ") in (select CleanValue from ItemValues where ItemId=Guid and Type=0) AND (select CleanName from TypedBaseItems where guid=" + paramName + ") not in (select CleanValue from ItemValues where ItemId=Guid and Type=1))" ) ;
if ( statement is not null )
{
statement . TryBind ( paramName , artistId ) ;
}
statement ? . TryBind ( paramName , artistId ) ;
index + + ;
}
@ -3866,13 +3853,8 @@ namespace Emby.Server.Implementations.Data
foreach ( var albumId in query . AlbumIds )
{
var paramName = "@AlbumIds" + index ;
clauses . Add ( "Album in (select Name from typedbaseitems where guid=" + paramName + ")" ) ;
if ( statement is not null )
{
statement . TryBind ( paramName , albumId ) ;
}
statement ? . TryBind ( paramName , albumId ) ;
index + + ;
}
@ -3887,13 +3869,8 @@ namespace Emby.Server.Implementations.Data
foreach ( var artistId in query . ExcludeArtistIds )
{
var paramName = "@ExcludeArtistId" + index ;
clauses . Add ( "(guid not in (select itemid from ItemValues where CleanValue = (select CleanName from TypedBaseItems where guid=" + paramName + ") and Type<=1))" ) ;
if ( statement is not null )
{
statement . TryBind ( paramName , artistId ) ;
}
statement ? . TryBind ( paramName , artistId ) ;
index + + ;
}
@ -3908,13 +3885,8 @@ namespace Emby.Server.Implementations.Data
foreach ( var genreId in query . GenreIds )
{
var paramName = "@GenreId" + index ;
clauses . Add ( "(guid in (select itemid from ItemValues where CleanValue = (select CleanName from TypedBaseItems where guid=" + paramName + ") and Type=2))" ) ;
if ( statement is not null )
{
statement . TryBind ( paramName , genreId ) ;
}
statement ? . TryBind ( paramName , genreId ) ;
index + + ;
}
@ -3929,11 +3901,7 @@ namespace Emby.Server.Implementations.Data
foreach ( var item in query . Genres )
{
clauses . Add ( "@Genre" + index + " in (select CleanValue from ItemValues where ItemId=Guid and Type=2)" ) ;
if ( statement is not null )
{
statement . TryBind ( "@Genre" + index , GetCleanValue ( item ) ) ;
}
statement ? . TryBind ( "@Genre" + index , GetCleanValue ( item ) ) ;
index + + ;
}
@ -3948,11 +3916,7 @@ namespace Emby.Server.Implementations.Data
foreach ( var item in tags )
{
clauses . Add ( "@Tag" + index + " in (select CleanValue from ItemValues where ItemId=Guid and Type=4)" ) ;
if ( statement is not null )
{
statement . TryBind ( "@Tag" + index , GetCleanValue ( item ) ) ;
}
statement ? . TryBind ( "@Tag" + index , GetCleanValue ( item ) ) ;
index + + ;
}
@ -3967,11 +3931,7 @@ namespace Emby.Server.Implementations.Data
foreach ( var item in excludeTags )
{
clauses . Add ( "@ExcludeTag" + index + " not in (select CleanValue from ItemValues where ItemId=Guid and Type=4)" ) ;
if ( statement is not null )
{
statement . TryBind ( "@ExcludeTag" + index , GetCleanValue ( item ) ) ;
}
statement ? . TryBind ( "@ExcludeTag" + index , GetCleanValue ( item ) ) ;
index + + ;
}
@ -3986,14 +3946,8 @@ namespace Emby.Server.Implementations.Data
foreach ( var studioId in query . StudioIds )
{
var paramName = "@StudioId" + index ;
clauses . Add ( "(guid in (select itemid from ItemValues where CleanValue = (select CleanName from TypedBaseItems where guid=" + paramName + ") and Type=3))" ) ;
if ( statement is not null )
{
statement . TryBind ( paramName , studioId ) ;
}
statement ? . TryBind ( paramName , studioId ) ;
index + + ;
}
@ -4008,11 +3962,7 @@ namespace Emby.Server.Implementations.Data
foreach ( var item in query . OfficialRatings )
{
clauses . Add ( "OfficialRating=@OfficialRating" + index ) ;
if ( statement is not null )
{
statement . TryBind ( "@OfficialRating" + index , item ) ;
}
statement ? . TryBind ( "@OfficialRating" + index , item ) ;
index + + ;
}
@ -4020,34 +3970,96 @@ namespace Emby.Server.Implementations.Data
whereClauses . Add ( clause ) ;
}
if ( query . MinParentalRating . HasValue )
var ratingClauseBuilder = new StringBuilder ( "(" ) ;
if ( query . HasParentalRating ? ? false )
{
whereClauses. Add ( "InheritedParentalRatingValue>=@MinParentalRating ") ;
if ( statement is not null )
ratingClauseBuilder. Append ( "InheritedParentalRatingValue not null ") ;
if ( query. MinParentalRating . HasValue )
{
statement . TryBind ( "@MinParentalRating" , query . MinParentalRating . Value ) ;
ratingClauseBuilder . Append ( " AND InheritedParentalRatingValue >= @MinParentalRating" ) ;
statement ? . TryBind ( "@MinParentalRating" , query . MinParentalRating . Value ) ;
}
}
if ( query . MaxParentalRating . HasValue )
if ( query . MaxParentalRating . HasValue )
{
ratingClauseBuilder . Append ( " AND InheritedParentalRatingValue <= @MaxParentalRating" ) ;
statement ? . TryBind ( "@MaxParentalRating" , query . MaxParentalRating . Value ) ;
}
}
else if ( query . BlockUnratedItems . Length > 0 )
{
whereClauses . Add ( "InheritedParentalRatingValue<=@MaxParentalRating" ) ;
var paramName = "@UnratedType" ;
var index = 0 ;
string blockedUnratedItems = string . Join ( ',' , query . BlockUnratedItems . Select ( _ = > paramName + index + + ) ) ;
ratingClauseBuilder . Append ( "(InheritedParentalRatingValue is null AND UnratedType not in (" + blockedUnratedItems + "))" ) ;
if ( statement is not null )
{
statement . TryBind ( "@MaxParentalRating" , query . MaxParentalRating . Value ) ;
for ( var ind = 0 ; ind < query . BlockUnratedItems . Length ; ind + + )
{
statement . TryBind ( paramName + ind , query . BlockUnratedItems [ ind ] . ToString ( ) ) ;
}
}
}
if ( query . HasParentalRating . HasValue )
{
if ( query . HasParentalRating . Value )
if ( query . MinParentalRating . HasValue | | query . MaxParentalRating . HasValue )
{
whereClauses. Add ( "InheritedParentalRatingValue > 0 ") ;
ratingClauseBuilder . Append ( " OR (" ) ;
}
else
if ( query . MinParentalRating . HasValue )
{
ratingClauseBuilder . Append ( "InheritedParentalRatingValue >= @MinParentalRating" ) ;
statement ? . TryBind ( "@MinParentalRating" , query . MinParentalRating . Value ) ;
}
if ( query . MaxParentalRating . HasValue )
{
if ( query . MinParentalRating . HasValue )
{
ratingClauseBuilder . Append ( " AND " ) ;
}
ratingClauseBuilder . Append ( "InheritedParentalRatingValue <= @MaxParentalRating" ) ;
statement ? . TryBind ( "@MaxParentalRating" , query . MaxParentalRating . Value ) ;
}
if ( query . MinParentalRating . HasValue | | query . MaxParentalRating . HasValue )
{
ratingClauseBuilder . Append ( ")" ) ;
}
if ( ! ( query . MinParentalRating . HasValue | | query . MaxParentalRating . HasValue ) )
{
ratingClauseBuilder . Append ( " OR InheritedParentalRatingValue not null" ) ;
}
}
else if ( query . MinParentalRating . HasValue )
{
ratingClauseBuilder . Append ( "InheritedParentalRatingValue is null OR (InheritedParentalRatingValue >= @MinParentalRating" ) ;
statement ? . TryBind ( "@MinParentalRating" , query . MinParentalRating . Value ) ;
if ( query . MaxParentalRating . HasValue )
{
whereClauses . Add ( "InheritedParentalRatingValue = 0" ) ;
ratingClauseBuilder . Append ( " AND InheritedParentalRatingValue <= @MaxParentalRating" ) ;
statement ? . TryBind ( "@MaxParentalRating" , query . MaxParentalRating . Value ) ;
}
ratingClauseBuilder . Append ( ")" ) ;
}
else if ( query . MaxParentalRating . HasValue )
{
ratingClauseBuilder . Append ( "InheritedParentalRatingValue is null OR InheritedParentalRatingValue <= @MaxParentalRating" ) ;
statement ? . TryBind ( "@MaxParentalRating" , query . MaxParentalRating . Value ) ;
}
else if ( ! query . HasParentalRating ? ? false )
{
ratingClauseBuilder . Append ( "InheritedParentalRatingValue is null" ) ;
}
var ratingClauseString = ratingClauseBuilder . ToString ( ) ;
if ( ! string . Equals ( ratingClauseString , "(" , StringComparison . OrdinalIgnoreCase ) )
{
whereClauses . Add ( ratingClauseString + ")" ) ;
}
if ( query . HasOfficialRating . HasValue )
@ -4089,37 +4101,25 @@ namespace Emby.Server.Implementations.Data
if ( ! string . IsNullOrWhiteSpace ( query . HasNoAudioTrackWithLanguage ) )
{
whereClauses . Add ( "((select language from MediaStreams where MediaStreams.ItemId=A.Guid and MediaStreams.StreamType='Audio' and MediaStreams.Language=@HasNoAudioTrackWithLanguage limit 1) is null)" ) ;
if ( statement is not null )
{
statement . TryBind ( "@HasNoAudioTrackWithLanguage" , query . HasNoAudioTrackWithLanguage ) ;
}
statement ? . TryBind ( "@HasNoAudioTrackWithLanguage" , query . HasNoAudioTrackWithLanguage ) ;
}
if ( ! string . IsNullOrWhiteSpace ( query . HasNoInternalSubtitleTrackWithLanguage ) )
{
whereClauses . Add ( "((select language from MediaStreams where MediaStreams.ItemId=A.Guid and MediaStreams.StreamType='Subtitle' and MediaStreams.IsExternal=0 and MediaStreams.Language=@HasNoInternalSubtitleTrackWithLanguage limit 1) is null)" ) ;
if ( statement is not null )
{
statement . TryBind ( "@HasNoInternalSubtitleTrackWithLanguage" , query . HasNoInternalSubtitleTrackWithLanguage ) ;
}
statement ? . TryBind ( "@HasNoInternalSubtitleTrackWithLanguage" , query . HasNoInternalSubtitleTrackWithLanguage ) ;
}
if ( ! string . IsNullOrWhiteSpace ( query . HasNoExternalSubtitleTrackWithLanguage ) )
{
whereClauses . Add ( "((select language from MediaStreams where MediaStreams.ItemId=A.Guid and MediaStreams.StreamType='Subtitle' and MediaStreams.IsExternal=1 and MediaStreams.Language=@HasNoExternalSubtitleTrackWithLanguage limit 1) is null)" ) ;
if ( statement is not null )
{
statement . TryBind ( "@HasNoExternalSubtitleTrackWithLanguage" , query . HasNoExternalSubtitleTrackWithLanguage ) ;
}
statement ? . TryBind ( "@HasNoExternalSubtitleTrackWithLanguage" , query . HasNoExternalSubtitleTrackWithLanguage ) ;
}
if ( ! string . IsNullOrWhiteSpace ( query . HasNoSubtitleTrackWithLanguage ) )
{
whereClauses . Add ( "((select language from MediaStreams where MediaStreams.ItemId=A.Guid and MediaStreams.StreamType='Subtitle' and MediaStreams.Language=@HasNoSubtitleTrackWithLanguage limit 1) is null)" ) ;
if ( statement is not null )
{
statement . TryBind ( "@HasNoSubtitleTrackWithLanguage" , query . HasNoSubtitleTrackWithLanguage ) ;
}
statement ? . TryBind ( "@HasNoSubtitleTrackWithLanguage" , query . HasNoSubtitleTrackWithLanguage ) ;
}
if ( query . HasSubtitles . HasValue )
@ -4169,15 +4169,11 @@ namespace Emby.Server.Implementations.Data
if ( query . Years . Length = = 1 )
{
whereClauses . Add ( "ProductionYear=@Years" ) ;
if ( statement is not null )
{
statement . TryBind ( "@Years" , query . Years [ 0 ] . ToString ( CultureInfo . InvariantCulture ) ) ;
}
statement ? . TryBind ( "@Years" , query . Years [ 0 ] . ToString ( CultureInfo . InvariantCulture ) ) ;
}
else if ( query . Years . Length > 1 )
{
var val = string . Join ( ',' , query . Years ) ;
whereClauses . Add ( "ProductionYear in (" + val + ")" ) ;
}
@ -4185,10 +4181,7 @@ namespace Emby.Server.Implementations.Data
if ( isVirtualItem . HasValue )
{
whereClauses . Add ( "IsVirtualItem=@IsVirtualItem" ) ;
if ( statement is not null )
{
statement . TryBind ( "@IsVirtualItem" , isVirtualItem . Value ) ;
}
statement ? . TryBind ( "@IsVirtualItem" , isVirtualItem . Value ) ;
}
if ( query . IsSpecialSeason . HasValue )
@ -4219,31 +4212,22 @@ namespace Emby.Server.Implementations.Data
if ( queryMediaTypes . Length = = 1 )
{
whereClauses . Add ( "MediaType=@MediaTypes" ) ;
if ( statement is not null )
{
statement . TryBind ( "@MediaTypes" , queryMediaTypes [ 0 ] ) ;
}
statement ? . TryBind ( "@MediaTypes" , queryMediaTypes [ 0 ] ) ;
}
else if ( queryMediaTypes . Length > 1 )
{
var val = string . Join ( ',' , queryMediaTypes . Select ( i = > "'" + i + "'" ) ) ;
whereClauses . Add ( "MediaType in (" + val + ")" ) ;
}
if ( query . ItemIds . Length > 0 )
{
var includeIds = new List < string > ( ) ;
var index = 0 ;
foreach ( var id in query . ItemIds )
{
includeIds . Add ( "Guid = @IncludeId" + index ) ;
if ( statement is not null )
{
statement . TryBind ( "@IncludeId" + index , id ) ;
}
statement ? . TryBind ( "@IncludeId" + index , id ) ;
index + + ;
}
@ -4253,16 +4237,11 @@ namespace Emby.Server.Implementations.Data
if ( query . ExcludeItemIds . Length > 0 )
{
var excludeIds = new List < string > ( ) ;
var index = 0 ;
foreach ( var id in query . ExcludeItemIds )
{
excludeIds . Add ( "Guid <> @ExcludeId" + index ) ;
if ( statement is not null )
{
statement . TryBind ( "@ExcludeId" + index , id ) ;
}
statement ? . TryBind ( "@ExcludeId" + index , id ) ;
index + + ;
}
@ -4283,11 +4262,7 @@ namespace Emby.Server.Implementations.Data
var paramName = "@ExcludeProviderId" + index ;
excludeIds . Add ( "(ProviderIds is null or ProviderIds not like " + paramName + ")" ) ;
if ( statement is not null )
{
statement . TryBind ( paramName , "%" + pair . Key + "=" + pair . Value + "%" ) ;
}
statement ? . TryBind ( paramName , "%" + pair . Key + "=" + pair . Value + "%" ) ;
index + + ;
break ;
@ -4312,7 +4287,7 @@ namespace Emby.Server.Implementations.Data
}
// TODO this seems to be an idea for a better schema where ProviderIds are their own table
// bu u t this is not implemented
// bu t this is not implemented
// hasProviderIds.Add("(COALESCE((select value from ProviderIds where ItemId=Guid and Name = '" + pair.Key + "'), '') <> " + paramName + ")");
// TODO this is a really BAD way to do it since the pair:
@ -4326,11 +4301,7 @@ namespace Emby.Server.Implementations.Data
hasProviderIds . Add ( "ProviderIds like " + paramName ) ;
// this replaces the placeholder with a value, here: %key=val%
if ( statement is not null )
{
statement . TryBind ( paramName , "%" + pair . Key + "=" + pair . Value + "%" ) ;
}
statement ? . TryBind ( paramName , "%" + pair . Key + "=" + pair . Value + "%" ) ;
index + + ;
break ;
@ -4407,11 +4378,7 @@ namespace Emby.Server.Implementations.Data
if ( query . AncestorIds . Length = = 1 )
{
whereClauses . Add ( "Guid in (select itemId from AncestorIds where AncestorId=@AncestorId)" ) ;
if ( statement is not null )
{
statement . TryBind ( "@AncestorId" , query . AncestorIds [ 0 ] ) ;
}
statement ? . TryBind ( "@AncestorId" , query . AncestorIds [ 0 ] ) ;
}
if ( query . AncestorIds . Length > 1 )
@ -4424,39 +4391,13 @@ namespace Emby.Server.Implementations.Data
{
var inClause = "select guid from TypedBaseItems where PresentationUniqueKey=@AncestorWithPresentationUniqueKey" ;
whereClauses . Add ( string . Format ( CultureInfo . InvariantCulture , "Guid in (select itemId from AncestorIds where AncestorId in ({0}))" , inClause ) ) ;
if ( statement is not null )
{
statement . TryBind ( "@AncestorWithPresentationUniqueKey" , query . AncestorWithPresentationUniqueKey ) ;
}
statement ? . TryBind ( "@AncestorWithPresentationUniqueKey" , query . AncestorWithPresentationUniqueKey ) ;
}
if ( ! string . IsNullOrWhiteSpace ( query . SeriesPresentationUniqueKey ) )
{
whereClauses . Add ( "SeriesPresentationUniqueKey=@SeriesPresentationUniqueKey" ) ;
if ( statement is not null )
{
statement . TryBind ( "@SeriesPresentationUniqueKey" , query . SeriesPresentationUniqueKey ) ;
}
}
if ( query . BlockUnratedItems . Length = = 1 )
{
whereClauses . Add ( "(InheritedParentalRatingValue > 0 or UnratedType <> @UnratedType)" ) ;
if ( statement is not null )
{
statement . TryBind ( "@UnratedType" , query . BlockUnratedItems [ 0 ] . ToString ( ) ) ;
}
}
if ( query . BlockUnratedItems . Length > 1 )
{
var inClause = string . Join ( ',' , query . BlockUnratedItems . Select ( i = > "'" + i . ToString ( ) + "'" ) ) ;
whereClauses . Add (
string . Format (
CultureInfo . InvariantCulture ,
"(InheritedParentalRatingValue > 0 or UnratedType not in ({0}))" ,
inClause ) ) ;
statement ? . TryBind ( "@SeriesPresentationUniqueKey" , query . SeriesPresentationUniqueKey ) ;
}
if ( query . ExcludeInheritedTags . Length > 0 )
@ -4605,6 +4546,7 @@ namespace Emby.Server.Implementations.Data
return whereClauses ;
}
#nullable disable
/// <summary>
/// Formats a where clause for the specified provider.