44f60a795581e7547ea5164799fb7962aefab5e0
[vuplus_xbmc] / xbmc / utils / DatabaseUtils.cpp
1 /*
2  *      Copyright (C) 2012-2013 Team XBMC
3  *      http://xbmc.org
4  *
5  *  This Program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This Program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, see
17  *  <http://www.gnu.org/licenses/>.
18  *
19  */
20
21 #include <sstream>
22
23 #include "DatabaseUtils.h"
24 #include "dbwrappers/dataset.h"
25 #include "music/MusicDatabase.h"
26 #include "utils/log.h"
27 #include "utils/Variant.h"
28 #include "video/VideoDatabase.h"
29
30 std::string DatabaseUtils::MediaTypeToString(MediaType mediaType)
31 {
32   switch (mediaType)
33   {
34   case MediaTypeMusic:
35     return "music";
36   case MediaTypeArtist:
37     return "artist";
38   case MediaTypeAlbum:
39     return "album";
40   case MediaTypeSong:
41     return "song";
42   case MediaTypeVideo:
43     return "video";
44   case MediaTypeVideoCollection:
45     return "set";
46   case MediaTypeMusicVideo:
47     return "musicvideo";
48   case MediaTypeMovie:
49     return "movie";
50   case MediaTypeTvShow:
51     return "tvshow";
52   case MediaTypeEpisode:
53     return "episode";
54   default:
55     break;
56   }
57
58   return "";
59 }
60
61 MediaType DatabaseUtils::MediaTypeFromString(const std::string &strMediaType)
62 {
63   if (strMediaType.compare("music") == 0)
64     return MediaTypeMusic;
65   else if (strMediaType.compare("artist") == 0 || strMediaType.compare("artists") == 0)
66     return MediaTypeArtist;
67   else if (strMediaType.compare("album") == 0 || strMediaType.compare("albums") == 0)
68     return MediaTypeAlbum;
69   else if (strMediaType.compare("song") == 0 || strMediaType.compare("songs") == 0)
70     return MediaTypeSong;
71   else if (strMediaType.compare("video") == 0 || strMediaType.compare("videos") == 0)
72     return MediaTypeVideo;
73   else if (strMediaType.compare("set") == 0 || strMediaType.compare("sets") == 0)
74     return MediaTypeVideoCollection;
75   else if (strMediaType.compare("musicvideo") == 0 || strMediaType.compare("musicvideos") == 0)
76     return MediaTypeMusicVideo;
77   else if (strMediaType.compare("movie") == 0 || strMediaType.compare("movies") == 0)
78     return MediaTypeMovie;
79   else if (strMediaType.compare("tvshow") == 0 || strMediaType.compare("tvshows") == 0)
80     return MediaTypeTvShow;
81   else if (strMediaType.compare("episode") == 0 || strMediaType.compare("episodes") == 0)
82     return MediaTypeEpisode;
83
84   return MediaTypeNone;
85 }
86
87 MediaType DatabaseUtils::MediaTypeFromVideoContentType(int videoContentType)
88 {
89   VIDEODB_CONTENT_TYPE type = (VIDEODB_CONTENT_TYPE)videoContentType;
90   switch (type)
91   {
92     case VIDEODB_CONTENT_MOVIES:
93       return MediaTypeMovie;
94
95     case VIDEODB_CONTENT_MOVIE_SETS:
96       return MediaTypeVideoCollection;
97
98     case VIDEODB_CONTENT_TVSHOWS:
99       return MediaTypeTvShow;
100
101     case VIDEODB_CONTENT_EPISODES:
102       return MediaTypeEpisode;
103
104     case VIDEODB_CONTENT_MUSICVIDEOS:
105       return MediaTypeMusicVideo;
106
107     default:
108       break;
109   }
110
111   return MediaTypeNone;
112 }
113
114 std::string DatabaseUtils::GetField(Field field, MediaType mediaType, DatabaseQueryPart queryPart)
115 {
116   if (field == FieldNone || mediaType == MediaTypeNone)
117     return "";
118
119   if (mediaType == MediaTypeAlbum)
120   {
121     if (field == FieldId) return "albumview.idAlbum";
122     else if (field == FieldAlbum) return "albumview.strAlbum";
123     else if (field == FieldArtist || field == FieldAlbumArtist) return "albumview.strArtists";
124     else if (field == FieldGenre) return "albumview.strGenre";
125     else if (field == FieldYear) return "albumview.iYear";
126     else if (field == FieldMoods) return "albumview.strMoods";
127     else if (field == FieldStyles) return "albumview.strStyles";
128     else if (field == FieldThemes) return "albumview.strThemes";
129     else if (field == FieldReview) return "albumview.strReview";
130     else if (field == FieldMusicLabel) return "albumview.strLabel";
131     else if (field == FieldAlbumType) return "albumview.strType";
132     else if (field == FieldRating) return "albumview.iRating";
133     else if (field == FieldDateAdded && queryPart == DatabaseQueryPartOrderBy) return "albumview.idalbum";    // only used for order clauses
134     else if (field == FieldPlaycount) return "albumview.iTimesPlayed";
135   }
136   else if (mediaType == MediaTypeSong)
137   {
138     if (field == FieldId) return "songview.idSong";
139     else if (field == FieldTitle) return "songview.strTitle";
140     else if (field == FieldTrackNumber) return "songview.iTrack";
141     else if (field == FieldTime) return "songview.iDuration";
142     else if (field == FieldYear) return "songview.iYear";
143     else if (field == FieldFilename) return "songview.strFilename";
144     else if (field == FieldPlaycount) return "songview.iTimesPlayed";
145     else if (field == FieldStartOffset) return "songview.iStartOffset";
146     else if (field == FieldEndOffset) return "songview.iEndOffset";
147     else if (field == FieldLastPlayed) return "songview.lastPlayed";
148     else if (field == FieldRating) return "songview.rating";
149     else if (field == FieldComment) return "songview.comment";
150     else if (field == FieldAlbum) return "songview.strAlbum";
151     else if (field == FieldPath) return "songview.strPath";
152     else if (field == FieldArtist || field == FieldAlbumArtist) return "songview.strArtists";
153     else if (field == FieldGenre) return "songview.strGenre";
154     else if (field == FieldDateAdded && queryPart == DatabaseQueryPartOrderBy) return "songview.idSong";     // only used for order clauses
155   }
156   else if (mediaType == MediaTypeArtist)
157   {
158     if (field == FieldId) return "artistview.idArtist";
159     else if (field == FieldArtist) return "artistview.strArtist";
160     else if (field == FieldGenre) return "artistview.strGenres";
161     else if (field == FieldMoods) return "artistview.strMoods";
162     else if (field == FieldStyles) return "artistview.strStyles";
163     else if (field == FieldInstruments) return "artistview.strInstruments";
164     else if (field == FieldBiography) return "artistview.strBiography";
165     else if (field == FieldBorn) return "artistview.strBorn";
166     else if (field == FieldBandFormed) return "artistview.strFormed";
167     else if (field == FieldDisbanded) return "artistview.strDisbanded";
168     else if (field == FieldDied) return "artistview.strDied";
169   }
170   else if (mediaType == MediaTypeMusicVideo)
171   {
172     CStdString result;
173     if (field == FieldId) return "musicvideoview.idMVideo";
174     else if (field == FieldTitle) result.Format("musicvideoview.c%02d",VIDEODB_ID_MUSICVIDEO_TITLE);
175     else if (field == FieldTime) result.Format("musicvideoview.c%02d", VIDEODB_ID_MUSICVIDEO_RUNTIME);
176     else if (field == FieldDirector) result.Format("musicvideoview.c%02d", VIDEODB_ID_MUSICVIDEO_DIRECTOR);
177     else if (field == FieldStudio) result.Format("musicvideoview.c%02d", VIDEODB_ID_MUSICVIDEO_STUDIOS);
178     else if (field == FieldYear) result.Format("musicvideoview.c%02d",VIDEODB_ID_MUSICVIDEO_YEAR);
179     else if (field == FieldPlot) result.Format("musicvideoview.c%02d", VIDEODB_ID_MUSICVIDEO_PLOT);
180     else if (field == FieldAlbum) result.Format("musicvideoview.c%02d",VIDEODB_ID_MUSICVIDEO_ALBUM);
181     else if (field == FieldArtist) result.Format("musicvideoview.c%02d", VIDEODB_ID_MUSICVIDEO_ARTIST);
182     else if (field == FieldGenre) result.Format("musicvideoview.c%02d", VIDEODB_ID_MUSICVIDEO_GENRE);
183     else if (field == FieldTrackNumber) result.Format("musicvideoview.c%02d", VIDEODB_ID_MUSICVIDEO_TRACK);
184     else if (field == FieldFilename) return "musicvideoview.strFilename";
185     else if (field == FieldPath) return "musicvideoview.strPath";
186     else if (field == FieldPlaycount) return "musicvideoview.playCount";
187     else if (field == FieldLastPlayed) return "musicvideoview.lastPlayed";
188     else if (field == FieldDateAdded) return "musicvideoview.dateAdded";
189
190     if (!result.empty())
191       return result;
192   }
193   else if (mediaType == MediaTypeMovie)
194   {
195     CStdString result;
196     if (field == FieldId) return "movieview.idMovie";
197     else if (field == FieldTitle)
198     {
199       // We need some extra logic to get the title value if sorttitle isn't set
200       if (queryPart == DatabaseQueryPartOrderBy)
201         result.Format("CASE WHEN length(movieview.c%02d) > 0 THEN movieview.c%02d ELSE movieview.c%02d END", VIDEODB_ID_SORTTITLE, VIDEODB_ID_SORTTITLE, VIDEODB_ID_TITLE);
202       else
203         result.Format("movieview.c%02d", VIDEODB_ID_TITLE);
204     }
205     else if (field == FieldPlot) result.Format("movieview.c%02d", VIDEODB_ID_PLOT);
206     else if (field == FieldPlotOutline) result.Format("movieview.c%02d", VIDEODB_ID_PLOTOUTLINE);
207     else if (field == FieldTagline) result.Format("movieview.c%02d", VIDEODB_ID_TAGLINE);
208     else if (field == FieldVotes) result.Format("movieview.c%02d", VIDEODB_ID_VOTES);
209     else if (field == FieldRating)
210     {
211       if (queryPart == DatabaseQueryPartOrderBy)
212         result.Format("CAST(movieview.c%02d as DECIMAL(5,3))", VIDEODB_ID_RATING);
213       else
214         result.Format("movieview.c%02d", VIDEODB_ID_RATING);
215     }
216     else if (field == FieldWriter) result.Format("movieview.c%02d", VIDEODB_ID_CREDITS);
217     else if (field == FieldYear) result.Format("movieview.c%02d", VIDEODB_ID_YEAR);
218     else if (field == FieldSortTitle) result.Format("movieview.c%02d", VIDEODB_ID_SORTTITLE);
219     else if (field == FieldTime) result.Format("movieview.c%02d", VIDEODB_ID_RUNTIME);
220     else if (field == FieldMPAA) result.Format("movieview.c%02d", VIDEODB_ID_MPAA);
221     else if (field == FieldTop250) result.Format("movieview.c%02d", VIDEODB_ID_TOP250);
222     else if (field == FieldSet) return "movieview.strSet";
223     else if (field == FieldGenre) result.Format("movieview.c%02d", VIDEODB_ID_GENRE);
224     else if (field == FieldDirector) result.Format("movieview.c%02d", VIDEODB_ID_DIRECTOR);
225     else if (field == FieldStudio) result.Format("movieview.c%02d", VIDEODB_ID_STUDIOS);
226     else if (field == FieldTrailer) result.Format("movieview.c%02d", VIDEODB_ID_TRAILER);
227     else if (field == FieldCountry) result.Format("movieview.c%02d", VIDEODB_ID_COUNTRY);
228     else if (field == FieldFilename) return "movieview.strFilename";
229     else if (field == FieldPath) return "movieview.strPath";
230     else if (field == FieldPlaycount) return "movieview.playCount";
231     else if (field == FieldLastPlayed) return "movieview.lastPlayed";
232     else if (field == FieldDateAdded) return "movieview.dateAdded";
233
234     if (!result.empty())
235       return result;
236   }
237   else if (mediaType == MediaTypeTvShow)
238   {
239     CStdString result;
240     if (field == FieldId) return "tvshowview.idShow";
241     else if (field == FieldTitle)
242     {
243       // We need some extra logic to get the title value if sorttitle isn't set
244       if (queryPart == DatabaseQueryPartOrderBy)
245         result.Format("CASE WHEN length(tvshowview.c%02d) > 0 THEN tvshowview.c%02d ELSE tvshowview.c%02d END", VIDEODB_ID_TV_SORTTITLE, VIDEODB_ID_TV_SORTTITLE, VIDEODB_ID_TV_TITLE);
246       else
247         result.Format("tvshowview.c%02d", VIDEODB_ID_TV_TITLE);
248     }
249     else if (field == FieldPlot) result.Format("tvshowview.c%02d", VIDEODB_ID_TV_PLOT);
250     else if (field == FieldTvShowStatus) result.Format("tvshowview.c%02d", VIDEODB_ID_TV_STATUS);
251     else if (field == FieldVotes) result.Format("tvshowview.c%02d", VIDEODB_ID_TV_VOTES);
252     else if (field == FieldRating) result.Format("tvshowview.c%02d", VIDEODB_ID_TV_RATING);
253     else if (field == FieldYear) result.Format("tvshowview.c%02d", VIDEODB_ID_TV_PREMIERED);
254     else if (field == FieldGenre) result.Format("tvshowview.c%02d", VIDEODB_ID_TV_GENRE);
255     else if (field == FieldMPAA) result.Format("tvshowview.c%02d", VIDEODB_ID_TV_MPAA);
256     else if (field == FieldStudio) result.Format("tvshowview.c%02d", VIDEODB_ID_TV_STUDIOS);
257     else if (field == FieldSortTitle) result.Format("tvshowview.c%02d", VIDEODB_ID_TV_SORTTITLE);
258     else if (field == FieldPath) return "tvshowview.strPath";
259     else if (field == FieldDateAdded) return "tvshowview.dateAdded";
260     else if (field == FieldLastPlayed) return "tvshowview.lastPlayed";
261     else if (field == FieldSeason) return "tvshowview.totalSeasons";
262     else if (field == FieldNumberOfEpisodes) return "tvshowview.totalCount";
263     else if (field == FieldNumberOfWatchedEpisodes) return "tvshowview.watchedcount";
264
265     if (!result.empty())
266       return result;
267   }
268   else if (mediaType == MediaTypeEpisode)
269   {
270     CStdString result;
271     if (field == FieldId) return "episodeview.idEpisode";
272     else if (field == FieldTitle) result.Format("episodeview.c%02d", VIDEODB_ID_EPISODE_TITLE);
273     else if (field == FieldPlot) result.Format("episodeview.c%02d", VIDEODB_ID_EPISODE_PLOT);
274     else if (field == FieldVotes) result.Format("episodeview.c%02d", VIDEODB_ID_EPISODE_VOTES);
275     else if (field == FieldRating) result.Format("episodeview.c%02d", VIDEODB_ID_EPISODE_RATING);
276     else if (field == FieldWriter) result.Format("episodeview.c%02d", VIDEODB_ID_EPISODE_CREDITS);
277     else if (field == FieldAirDate) result.Format("episodeview.c%02d", VIDEODB_ID_EPISODE_AIRED);
278     else if (field == FieldTime) result.Format("episodeview.c%02d", VIDEODB_ID_EPISODE_RUNTIME);
279     else if (field == FieldDirector) result.Format("episodeview.c%02d", VIDEODB_ID_EPISODE_DIRECTOR);
280     else if (field == FieldSeason) result.Format("episodeview.c%02d", VIDEODB_ID_EPISODE_SEASON);
281     else if (field == FieldEpisodeNumber) result.Format("episodeview.c%02d", VIDEODB_ID_EPISODE_EPISODE);
282     else if (field == FieldUniqueId) result.Format("episodeview.c%02d", VIDEODB_ID_EPISODE_UNIQUEID);
283     else if (field == FieldEpisodeNumberSpecialSort) result.Format("episodeview.c%02d", VIDEODB_ID_EPISODE_SORTEPISODE);
284     else if (field == FieldSeasonSpecialSort) result.Format("episodeview.c%02d", VIDEODB_ID_EPISODE_SORTSEASON);
285     else if (field == FieldFilename) return "episodeview.strFilename";
286     else if (field == FieldPath) return "episodeview.strPath";
287     else if (field == FieldPlaycount) return "episodeview.playCount";
288     else if (field == FieldLastPlayed) return "episodeview.lastPlayed";
289     else if (field == FieldDateAdded) return "episodeview.dateAdded";
290     else if (field == FieldTvShowTitle) return "episodeview.strTitle";
291     else if (field == FieldYear) return "episodeview.premiered";
292     else if (field == FieldMPAA) return "episodeview.mpaa";
293     else if (field == FieldStudio) return "episodeview.strStudio";
294
295     if (!result.empty())
296       return result;
297   }
298
299   if (field == FieldRandom && queryPart == DatabaseQueryPartOrderBy)
300     return "RANDOM()";
301
302   return "";
303 }
304
305 int DatabaseUtils::GetField(Field field, MediaType mediaType)
306 {
307   if (field == FieldNone || mediaType == MediaTypeNone)
308     return -1;
309
310   return GetField(field, mediaType, false);
311 }
312
313 int DatabaseUtils::GetFieldIndex(Field field, MediaType mediaType)
314 {
315   if (field == FieldNone || mediaType == MediaTypeNone)
316     return -1;
317
318   return GetField(field, mediaType, true);
319 }
320
321 bool DatabaseUtils::GetSelectFields(const Fields &fields, MediaType mediaType, FieldList &selectFields)
322 {
323   if (mediaType == MediaTypeNone || fields.empty())
324     return false;
325
326   Fields sortFields = fields;
327
328   // add necessary fields to create the label
329   if (mediaType == MediaTypeSong || mediaType == MediaTypeVideo || mediaType == MediaTypeVideoCollection ||
330       mediaType == MediaTypeMusicVideo || mediaType == MediaTypeMovie || mediaType == MediaTypeTvShow || mediaType == MediaTypeEpisode)
331     sortFields.insert(FieldTitle);
332   if (mediaType == MediaTypeEpisode)
333   {
334     sortFields.insert(FieldSeason);
335     sortFields.insert(FieldEpisodeNumber);
336   }
337   else if (mediaType == MediaTypeAlbum)
338     sortFields.insert(FieldAlbum);
339   else if (mediaType == MediaTypeSong)
340     sortFields.insert(FieldTrackNumber);
341   else if (mediaType == MediaTypeArtist)
342     sortFields.insert(FieldArtist);
343
344   selectFields.clear();
345   for (Fields::const_iterator it = sortFields.begin(); it != sortFields.end(); it++)
346   {
347     // ignore FieldLabel because it needs special handling (see further up)
348     if (*it == FieldLabel)
349       continue;
350
351     if (GetField(*it, mediaType, DatabaseQueryPartSelect).empty())
352     {
353       CLog::Log(LOGDEBUG, "DatabaseUtils::GetSortFieldList: unknown field %d", *it);
354       continue;
355     }
356     selectFields.push_back(*it);
357   }
358
359   return !selectFields.empty();
360 }
361
362 bool DatabaseUtils::GetFieldValue(const dbiplus::field_value &fieldValue, CVariant &variantValue)
363 {
364   if (fieldValue.get_isNull())
365   {
366     variantValue = CVariant::ConstNullVariant;
367     return true;
368   }
369
370   switch (fieldValue.get_fType())
371   {
372   case dbiplus::ft_String:
373   case dbiplus::ft_WideString:
374   case dbiplus::ft_Object:
375     variantValue = fieldValue.get_asString();
376     return true;
377   case dbiplus::ft_Char:
378   case dbiplus::ft_WChar:
379     variantValue = fieldValue.get_asChar();
380     return true;
381   case dbiplus::ft_Boolean:
382     variantValue = fieldValue.get_asBool();
383     return true;
384   case dbiplus::ft_Short:
385     variantValue = fieldValue.get_asShort();
386     return true;
387   case dbiplus::ft_UShort:
388     variantValue = fieldValue.get_asShort();
389     return true;
390   case dbiplus::ft_Int:
391     variantValue = fieldValue.get_asInt();
392     return true;
393   case dbiplus::ft_UInt:
394     variantValue = fieldValue.get_asUInt();
395     return true;
396   case dbiplus::ft_Float:
397     variantValue = fieldValue.get_asFloat();
398     return true;
399   case dbiplus::ft_Double:
400   case dbiplus::ft_LongDouble:
401     variantValue = fieldValue.get_asDouble();
402     return true;
403   case dbiplus::ft_Int64:
404     variantValue = fieldValue.get_asInt64();
405     return true;
406   }
407
408   return false;
409 }
410
411 bool DatabaseUtils::GetDatabaseResults(MediaType mediaType, const FieldList &fields, const std::auto_ptr<dbiplus::Dataset> &dataset, DatabaseResults &results)
412 {
413   if (dataset->num_rows() == 0)
414     return true;
415
416   const dbiplus::result_set &resultSet = dataset->get_result_set();
417   unsigned int offset = results.size();
418
419   if (fields.empty())
420   {
421     DatabaseResult result;
422     for (unsigned int index = 0; index < resultSet.records.size(); index++)
423     {
424       result[FieldRow] = index + offset;
425       results.push_back(result);
426     }
427
428     return true;
429   }
430
431   if (resultSet.record_header.size() < fields.size())
432     return false;
433
434   std::vector<int> fieldIndexLookup;
435   fieldIndexLookup.reserve(fields.size());
436   for (FieldList::const_iterator it = fields.begin(); it != fields.end(); it++)
437     fieldIndexLookup.push_back(GetFieldIndex(*it, mediaType));
438
439   results.reserve(resultSet.records.size() + offset);
440   for (unsigned int index = 0; index < resultSet.records.size(); index++)
441   {
442     DatabaseResult result;
443     result[FieldRow] = index + offset;
444
445     unsigned int lookupIndex = 0;
446     for (FieldList::const_iterator it = fields.begin(); it != fields.end(); it++)
447     {
448       int fieldIndex = fieldIndexLookup[lookupIndex++];
449       if (fieldIndex < 0)
450         return false;
451
452       std::pair<Field, CVariant> value;
453       value.first = *it;
454       if (!GetFieldValue(resultSet.records[index]->at(fieldIndex), value.second))
455         CLog::Log(LOGWARNING, "GetDatabaseResults: unable to retrieve value of field %s", resultSet.record_header[fieldIndex].name.c_str());
456
457       if (value.first == FieldYear &&
458          (mediaType == MediaTypeTvShow || mediaType == MediaTypeEpisode))
459       {
460         CDateTime dateTime;
461         dateTime.SetFromDBDate(value.second.asString());
462         if (dateTime.IsValid())
463         {
464           value.second.clear();
465           value.second = dateTime.GetYear();
466         }
467       }
468
469       result.insert(value);
470     }
471
472     result[FieldMediaType] = mediaType;
473     switch (mediaType)
474     {
475     case MediaTypeMovie:
476     case MediaTypeVideoCollection:
477     case MediaTypeTvShow:
478     case MediaTypeMusicVideo:
479       result[FieldLabel] = result.at(FieldTitle).asString();
480       break;
481       
482     case MediaTypeEpisode:
483     {
484       std::ostringstream label;
485       label << (int)(result.at(FieldSeason).asInteger() * 100 + result.at(FieldEpisodeNumber).asInteger());
486       label << ". ";
487       label << result.at(FieldTitle).asString();
488       result[FieldLabel] = label.str();
489       break;
490     }
491
492     case MediaTypeAlbum:
493       result[FieldLabel] = result.at(FieldAlbum).asString();
494       break;
495
496     case MediaTypeSong:
497     {
498       std::ostringstream label;
499       label << (int)result.at(FieldTrackNumber).asInteger();
500       label << ". ";
501       label << result.at(FieldTitle).asString();
502       result[FieldLabel] = label.str();
503       break;
504     }
505
506     case MediaTypeArtist:
507       result[FieldLabel] = result.at(FieldArtist).asString();
508       break;
509
510     default:
511       break;
512     }
513
514     results.push_back(result);
515   }
516
517   return true;
518 }
519
520 std::string DatabaseUtils::BuildLimitClause(int end, int start /* = 0 */)
521 {
522   std::ostringstream sql;
523   sql << " LIMIT ";
524   if (start > 0)
525   {
526     if (end > 0)
527     {
528       end = end - start;
529       if (end < 0)
530         end = 0;
531     }
532
533     sql << start << "," << end;
534   }
535   else
536     sql << end;
537
538   return sql.str();
539 }
540
541 int DatabaseUtils::GetField(Field field, MediaType mediaType, bool asIndex)
542 {
543   if (field == FieldNone || mediaType == MediaTypeNone)
544     return -1;
545
546   int index = -1;
547
548   if (mediaType == MediaTypeAlbum)
549   {
550     if (field == FieldId) return CMusicDatabase::album_idAlbum;
551     else if (field == FieldAlbum) return CMusicDatabase::album_strAlbum;
552     else if (field == FieldArtist || field == FieldAlbumArtist) return CMusicDatabase::album_strArtists;
553     else if (field == FieldGenre) return CMusicDatabase::album_strGenres;
554     else if (field == FieldYear) return CMusicDatabase::album_iYear;
555     else if (field == FieldMoods) return CMusicDatabase::album_strMoods;
556     else if (field == FieldStyles) return CMusicDatabase::album_strStyles;
557     else if (field == FieldThemes) return CMusicDatabase::album_strThemes;
558     else if (field == FieldReview) return CMusicDatabase::album_strReview;
559     else if (field == FieldMusicLabel) return CMusicDatabase::album_strLabel;
560     else if (field == FieldAlbumType) return CMusicDatabase::album_strType;
561     else if (field == FieldRating) return CMusicDatabase::album_iRating;
562     else if (field == FieldPlaycount) return CMusicDatabase::album_iTimesPlayed;
563   }
564   else if (mediaType == MediaTypeSong)
565   {
566     if (field == FieldId) return CMusicDatabase::song_idSong;
567     else if (field == FieldTitle) return CMusicDatabase::song_strTitle;
568     else if (field == FieldTrackNumber) return CMusicDatabase::song_iTrack;
569     else if (field == FieldTime) return CMusicDatabase::song_iDuration;
570     else if (field == FieldYear) return CMusicDatabase::song_iYear;
571     else if (field == FieldFilename) return CMusicDatabase::song_strFileName;
572     else if (field == FieldPlaycount) return CMusicDatabase::song_iTimesPlayed;
573     else if (field == FieldStartOffset) return CMusicDatabase::song_iStartOffset;
574     else if (field == FieldEndOffset) return CMusicDatabase::song_iEndOffset;
575     else if (field == FieldLastPlayed) return CMusicDatabase::song_lastplayed;
576     else if (field == FieldRating) return CMusicDatabase::song_rating;
577     else if (field == FieldComment) return CMusicDatabase::song_comment;
578     else if (field == FieldAlbum) return CMusicDatabase::song_strAlbum;
579     else if (field == FieldPath) return CMusicDatabase::song_strPath;
580     else if (field == FieldGenre) return CMusicDatabase::song_strGenres;
581     else if (field == FieldArtist || field == FieldAlbumArtist) return CMusicDatabase::song_strArtists;
582   }
583   else if (mediaType == MediaTypeArtist)
584   {
585     if (field == FieldId) return CMusicDatabase::artist_idArtist;
586     else if (field == FieldArtist) return CMusicDatabase::artist_strArtist;
587     else if (field == FieldGenre) return CMusicDatabase::artist_strGenres;
588     else if (field == FieldMoods) return CMusicDatabase::artist_strMoods;
589     else if (field == FieldStyles) return CMusicDatabase::artist_strStyles;
590     else if (field == FieldInstruments) return CMusicDatabase::artist_strInstruments;
591     else if (field == FieldBiography) return CMusicDatabase::artist_strBiography;
592     else if (field == FieldBorn) return CMusicDatabase::artist_strBorn;
593     else if (field == FieldBandFormed) return CMusicDatabase::artist_strFormed;
594     else if (field == FieldDisbanded) return CMusicDatabase::artist_strDisbanded;
595     else if (field == FieldDied) return CMusicDatabase::artist_strDied;
596   }
597   else if (mediaType == MediaTypeMusicVideo)
598   {
599     if (field == FieldId) return 0;
600     else if (field == FieldTitle) index = VIDEODB_ID_MUSICVIDEO_TITLE;
601     else if (field == FieldTime) index =  VIDEODB_ID_MUSICVIDEO_RUNTIME;
602     else if (field == FieldDirector) index =  VIDEODB_ID_MUSICVIDEO_DIRECTOR;
603     else if (field == FieldStudio) index =  VIDEODB_ID_MUSICVIDEO_STUDIOS;
604     else if (field == FieldYear) index = VIDEODB_ID_MUSICVIDEO_YEAR;
605     else if (field == FieldPlot) index =  VIDEODB_ID_MUSICVIDEO_PLOT;
606     else if (field == FieldAlbum) index = VIDEODB_ID_MUSICVIDEO_ALBUM;
607     else if (field == FieldArtist) index =  VIDEODB_ID_MUSICVIDEO_ARTIST;
608     else if (field == FieldGenre) index =  VIDEODB_ID_MUSICVIDEO_GENRE;
609     else if (field == FieldTrackNumber) index =  VIDEODB_ID_MUSICVIDEO_TRACK;
610     else if (field == FieldFilename) return VIDEODB_DETAILS_MUSICVIDEO_FILE;
611     else if (field == FieldPath) return VIDEODB_DETAILS_MUSICVIDEO_PATH;
612     else if (field == FieldPlaycount) return VIDEODB_DETAILS_MUSICVIDEO_PLAYCOUNT;
613     else if (field == FieldLastPlayed) return VIDEODB_DETAILS_MUSICVIDEO_LASTPLAYED;
614     else if (field == FieldDateAdded) return VIDEODB_DETAILS_MUSICVIDEO_DATEADDED;
615
616     if (index < 0)
617       return index;
618
619     if (asIndex)
620     {
621       // see VideoDatabase.h
622       // the first field is the item's ID and the second is the item's file ID
623       index += 2;
624     }
625   }
626   else if (mediaType == MediaTypeMovie)
627   {
628     if (field == FieldId) return 0;
629     else if (field == FieldTitle) index = VIDEODB_ID_TITLE;
630     else if (field == FieldSortTitle) index = VIDEODB_ID_SORTTITLE;
631     else if (field == FieldPlot) index = VIDEODB_ID_PLOT;
632     else if (field == FieldPlotOutline) index = VIDEODB_ID_PLOTOUTLINE;
633     else if (field == FieldTagline) index = VIDEODB_ID_TAGLINE;
634     else if (field == FieldVotes) index = VIDEODB_ID_VOTES;
635     else if (field == FieldRating) index = VIDEODB_ID_RATING;
636     else if (field == FieldWriter) index = VIDEODB_ID_CREDITS;
637     else if (field == FieldYear) index = VIDEODB_ID_YEAR;
638     else if (field == FieldTime) index = VIDEODB_ID_RUNTIME;
639     else if (field == FieldMPAA) index = VIDEODB_ID_MPAA;
640     else if (field == FieldTop250) index = VIDEODB_ID_TOP250;
641     else if (field == FieldSet) return VIDEODB_DETAILS_MOVIE_SET_NAME;
642     else if (field == FieldGenre) index = VIDEODB_ID_GENRE;
643     else if (field == FieldDirector) index = VIDEODB_ID_DIRECTOR;
644     else if (field == FieldStudio) index = VIDEODB_ID_STUDIOS;
645     else if (field == FieldTrailer) index = VIDEODB_ID_TRAILER;
646     else if (field == FieldCountry) index = VIDEODB_ID_COUNTRY;
647     else if (field == FieldFilename) index = VIDEODB_DETAILS_MOVIE_FILE;
648     else if (field == FieldPath) return VIDEODB_DETAILS_MOVIE_PATH;
649     else if (field == FieldPlaycount) return VIDEODB_DETAILS_MOVIE_PLAYCOUNT;
650     else if (field == FieldLastPlayed) return VIDEODB_DETAILS_MOVIE_LASTPLAYED;
651     else if (field == FieldDateAdded) return VIDEODB_DETAILS_MOVIE_DATEADDED;
652
653     if (index < 0)
654       return index;
655
656     if (asIndex)
657     {
658       // see VideoDatabase.h
659       // the first field is the item's ID and the second is the item's file ID
660       index += 2;
661     }
662   }
663   else if (mediaType == MediaTypeTvShow)
664   {
665     if (field == FieldId) return 0;
666     else if (field == FieldTitle) index = VIDEODB_ID_TV_TITLE;
667     else if (field == FieldSortTitle) index = VIDEODB_ID_TV_SORTTITLE;
668     else if (field == FieldPlot) index = VIDEODB_ID_TV_PLOT;
669     else if (field == FieldTvShowStatus) index = VIDEODB_ID_TV_STATUS;
670     else if (field == FieldVotes) index = VIDEODB_ID_TV_VOTES;
671     else if (field == FieldRating) index = VIDEODB_ID_TV_RATING;
672     else if (field == FieldYear) index = VIDEODB_ID_TV_PREMIERED;
673     else if (field == FieldGenre) index = VIDEODB_ID_TV_GENRE;
674     else if (field == FieldMPAA) index = VIDEODB_ID_TV_MPAA;
675     else if (field == FieldStudio) index = VIDEODB_ID_TV_STUDIOS;
676     else if (field == FieldPath) return VIDEODB_DETAILS_TVSHOW_PATH;
677     else if (field == FieldDateAdded) return VIDEODB_DETAILS_TVSHOW_DATEADDED;
678     else if (field == FieldLastPlayed) return VIDEODB_DETAILS_TVSHOW_LASTPLAYED;
679     else if (field == FieldNumberOfEpisodes) return VIDEODB_DETAILS_TVSHOW_NUM_EPISODES;
680     else if (field == FieldNumberOfWatchedEpisodes) return VIDEODB_DETAILS_TVSHOW_NUM_WATCHED;
681     else if (field == FieldSeason) return VIDEODB_DETAILS_TVSHOW_NUM_SEASONS;
682
683     if (index < 0)
684       return index;
685
686     if (asIndex)
687     {
688       // see VideoDatabase.h
689       // the first field is the item's ID
690       index += 1;
691     }
692   }
693   else if (mediaType == MediaTypeEpisode)
694   {
695     if (field == FieldId) return 0;
696     else if (field == FieldTitle) index = VIDEODB_ID_EPISODE_TITLE;
697     else if (field == FieldPlot) index = VIDEODB_ID_EPISODE_PLOT;
698     else if (field == FieldVotes) index = VIDEODB_ID_EPISODE_VOTES;
699     else if (field == FieldRating) index = VIDEODB_ID_EPISODE_RATING;
700     else if (field == FieldWriter) index = VIDEODB_ID_EPISODE_CREDITS;
701     else if (field == FieldAirDate) index = VIDEODB_ID_EPISODE_AIRED;
702     else if (field == FieldTime) index = VIDEODB_ID_EPISODE_RUNTIME;
703     else if (field == FieldDirector) index = VIDEODB_ID_EPISODE_DIRECTOR;
704     else if (field == FieldSeason) index = VIDEODB_ID_EPISODE_SEASON;
705     else if (field == FieldEpisodeNumber) index = VIDEODB_ID_EPISODE_EPISODE;
706     else if (field == FieldUniqueId) index = VIDEODB_ID_EPISODE_UNIQUEID;
707     else if (field == FieldEpisodeNumberSpecialSort) index = VIDEODB_ID_EPISODE_SORTEPISODE;
708     else if (field == FieldSeasonSpecialSort) index = VIDEODB_ID_EPISODE_SORTSEASON;
709     else if (field == FieldFilename) return VIDEODB_DETAILS_EPISODE_FILE;
710     else if (field == FieldPath) return VIDEODB_DETAILS_EPISODE_PATH;
711     else if (field == FieldPlaycount) return VIDEODB_DETAILS_EPISODE_PLAYCOUNT;
712     else if (field == FieldLastPlayed) return VIDEODB_DETAILS_EPISODE_LASTPLAYED;
713     else if (field == FieldDateAdded) return VIDEODB_DETAILS_EPISODE_DATEADDED;
714     else if (field == FieldTvShowTitle) return VIDEODB_DETAILS_EPISODE_TVSHOW_NAME;
715     else if (field == FieldStudio) return VIDEODB_DETAILS_EPISODE_TVSHOW_STUDIO;
716     else if (field == FieldYear) return VIDEODB_DETAILS_EPISODE_TVSHOW_AIRED;
717     else if (field == FieldMPAA) return VIDEODB_DETAILS_EPISODE_TVSHOW_MPAA;
718
719     if (index < 0)
720       return index;
721
722     if (asIndex)
723     {
724       // see VideoDatabase.h
725       // the first field is the item's ID and the second is the item's file ID
726       index += 2;
727     }
728   }
729
730   return index;
731 }