From 1ba45dbe091c4f53316ed5331c069143ece6dd96 Mon Sep 17 00:00:00 2001
From: viktorstrate <viktorstrate@gmail.com>
Date: Sun, 14 Feb 2021 10:11:10 +0100
Subject: [PATCH] Fix SQL errors for Timeline view,

when using Postgres or Sqlite. Closes #194
---
 .gitignore                        |  1 +
 api/database/helpers.go           | 48 +++++++++++++++++++++++++++++++
 api/graphql/resolvers/timeline.go | 20 +++++++++----
 3 files changed, 64 insertions(+), 5 deletions(-)
 create mode 100644 api/database/helpers.go

diff --git a/.gitignore b/.gitignore
index 3418fec..34bbc3a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,6 +4,7 @@ cache/
 media_cache/
 /photos_path
 photoview.db
+photoview.db-journal
 
 .env
 
diff --git a/api/database/helpers.go b/api/database/helpers.go
new file mode 100644
index 0000000..60cc736
--- /dev/null
+++ b/api/database/helpers.go
@@ -0,0 +1,48 @@
+package database
+
+import (
+	"fmt"
+	"log"
+
+	"gorm.io/gorm"
+)
+
+// DateComponent a component of a date (day, month, year)
+type DateComponent string
+
+const (
+	DateCompYear  DateComponent = "YEAR"
+	DateCompMonth DateComponent = "MONTH"
+	DateCompDay   DateComponent = "DAY"
+)
+
+// DateExtract is a helper function that is used to generate the proper SQL syntax
+// for extracting date components (day, month, year) for different database backends.
+func DateExtract(db *gorm.DB, component DateComponent, attribute string) string {
+
+	var result string
+
+	switch db.Dialector.Name() {
+	case "mysql", "postgress":
+		result = fmt.Sprintf("EXTRACT(%s FROM %s)", component, attribute)
+		break
+	case "sqlite":
+
+		var sqliteFormatted string
+		switch component {
+		case DateCompYear:
+			sqliteFormatted = "%Y"
+		case DateCompMonth:
+			sqliteFormatted = "%m"
+		case DateCompDay:
+			sqliteFormatted = "%d"
+		}
+
+		result = fmt.Sprintf("CAST(strftime('%s', %s) AS INTEGER)", sqliteFormatted, attribute)
+		break
+	default:
+		log.Panicf("unsupported database backend: %s", db.Dialector.Name())
+	}
+
+	return result
+}
diff --git a/api/graphql/resolvers/timeline.go b/api/graphql/resolvers/timeline.go
index e9002fe..65cdd79 100644
--- a/api/graphql/resolvers/timeline.go
+++ b/api/graphql/resolvers/timeline.go
@@ -2,8 +2,10 @@ package resolvers
 
 import (
 	"context"
+	"fmt"
 	"time"
 
+	"github.com/photoview/photoview/api/database"
 	"github.com/photoview/photoview/api/graphql/auth"
 	"github.com/photoview/photoview/api/graphql/models"
 	"gorm.io/gorm"
@@ -21,9 +23,9 @@ func (r *queryResolver) MyTimeline(ctx context.Context, paginate *models.Paginat
 		// album_id, year, month, day
 		daysQuery := tx.Select(
 			"albums.id AS album_id",
-			"YEAR(media.date_shot) AS year",
-			"MONTH(media.date_shot) AS month",
-			"DAY(media.date_shot) AS day",
+			fmt.Sprintf("%s AS year", database.DateExtract(tx, database.DateCompYear, "media.date_shot")),
+			fmt.Sprintf("%s AS month", database.DateExtract(tx, database.DateCompMonth, "media.date_shot")),
+			fmt.Sprintf("%s AS day", database.DateExtract(tx, database.DateCompDay, "media.date_shot")),
 		).
 			Table("media").
 			Joins("JOIN albums ON media.album_id = albums.id").
@@ -43,7 +45,12 @@ func (r *queryResolver) MyTimeline(ctx context.Context, paginate *models.Paginat
 			}
 		}
 
-		rows, err := daysQuery.Group("albums.id, YEAR(media.date_shot), MONTH(media.date_shot), DAY(media.date_shot)").
+		rows, err := daysQuery.Group(
+			fmt.Sprintf("albums.id, %s, %s, %s",
+				database.DateExtract(tx, database.DateCompYear, "media.date_shot"),
+				database.DateExtract(tx, database.DateCompMonth, "media.date_shot"),
+				database.DateExtract(tx, database.DateCompDay, "media.date_shot")),
+		).
 			Order("media.date_shot DESC").
 			Rows()
 
@@ -81,7 +88,10 @@ func (r *queryResolver) MyTimeline(ctx context.Context, paginate *models.Paginat
 			// Fill media
 			var groupMedia []*models.Media
 			mediaQuery := tx.Model(&models.Media{}).
-				Where("album_id = ? AND YEAR(date_shot) = ? AND MONTH(date_shot) = ? AND DAY(date_shot) = ?", group.albumID, group.year, group.month, group.day).
+				Where("album_id = ?", group.albumID).
+				Where(fmt.Sprintf("%s = ?", database.DateExtract(tx, database.DateCompYear, "media.date_shot")), group.year).
+				Where(fmt.Sprintf("%s = ?", database.DateExtract(tx, database.DateCompMonth, "media.date_shot")), group.month).
+				Where(fmt.Sprintf("%s = ?", database.DateExtract(tx, database.DateCompDay, "media.date_shot")), group.day).
 				Order("date_shot DESC")
 
 			if onlyFavorites != nil && *onlyFavorites == true {
