From 3baa9e72ca1cc55c8a8fb870190861ae56d9a8a1 Mon Sep 17 00:00:00 2001
From: Mark Beamer Jr <markbeamerjr@gmail.com>
Date: Thu, 10 Jan 2019 20:11:42 -0500
Subject: [PATCH 1/8] Updated to use the latest lbry.go changes

---
 boilingcore/imports.go      | 50 ++++++++++++++++++-------------------
 boilingcore/imports_test.go | 10 ++++----
 queries/helpers_test.go     |  2 +-
 queries/query.go            |  2 +-
 randomize/randomize.go      |  2 +-
 randomize/randomize_test.go |  2 +-
 6 files changed, 34 insertions(+), 34 deletions(-)

diff --git a/boilingcore/imports.go b/boilingcore/imports.go
index 9a5c656..b5979e3 100644
--- a/boilingcore/imports.go
+++ b/boilingcore/imports.go
@@ -170,8 +170,8 @@ func newImporter() importer {
 			`"time"`,
 		},
 		thirdParty: importList{
-			`"github.com/lbryio/lbry.go/errors"`,
-			`"github.com/lbryio/lbry.go/null"`,
+			`"github.com/lbryio/lbry.go/extras/errors"`,
+			`"github.com/lbryio/lbry.go/extras/null"`,
 			`"github.com/lbryio/sqlboiler/boil"`,
 			`"github.com/lbryio/sqlboiler/queries"`,
 			`"github.com/lbryio/sqlboiler/queries/qm"`,
@@ -185,7 +185,7 @@ func newImporter() importer {
 				`"fmt"`,
 			},
 			thirdParty: importList{
-				`"github.com/lbryio/lbry.go/errors"`,
+				`"github.com/lbryio/lbry.go/extras/errors"`,
 				`"github.com/lbryio/sqlboiler/boil"`,
 				`"github.com/lbryio/sqlboiler/queries"`,
 				`"github.com/lbryio/sqlboiler/queries/qm"`,
@@ -194,7 +194,7 @@ func newImporter() importer {
 		},
 		"boil_types": {
 			thirdParty: importList{
-				`"github.com/lbryio/lbry.go/errors"`,
+				`"github.com/lbryio/lbry.go/extras/errors"`,
 				`"github.com/lbryio/sqlboiler/strmangle"`,
 			},
 		},
@@ -227,7 +227,7 @@ func newImporter() importer {
 			},
 			thirdParty: importList{
 				`"github.com/kat-co/vala"`,
-				`"github.com/lbryio/lbry.go/errors"`,
+				`"github.com/lbryio/lbry.go/extras/errors"`,
 				`"github.com/lbryio/sqlboiler/boil"`,
 				`"github.com/spf13/viper"`,
 			},
@@ -265,7 +265,7 @@ func newImporter() importer {
 				`"strings"`,
 			},
 			thirdParty: importList{
-				`"github.com/lbryio/lbry.go/errors"`,
+				`"github.com/lbryio/lbry.go/extras/errors"`,
 				`"github.com/lbryio/sqlboiler/bdb/drivers"`,
 				`"github.com/lbryio/sqlboiler/randomize"`,
 				`_ "github.com/lib/pq"`,
@@ -285,7 +285,7 @@ func newImporter() importer {
 			},
 			thirdParty: importList{
 				`_ "github.com/go-sql-driver/mysql"`,
-				`"github.com/lbryio/lbry.go/errors"`,
+				`"github.com/lbryio/lbry.go/extras/errors"`,
 				`"github.com/lbryio/sqlboiler/bdb/drivers"`,
 				`"github.com/lbryio/sqlboiler/randomize"`,
 				`"github.com/spf13/viper"`,
@@ -302,7 +302,7 @@ func newImporter() importer {
 			},
 			thirdParty: importList{
 				`_ "github.com/denisenkom/go-mssqldb"`,
-				`"github.com/lbryio/lbry.go/errors"`,
+				`"github.com/lbryio/lbry.go/extras/errors"`,
 				`"github.com/lbryio/sqlboiler/bdb/drivers"`,
 				`"github.com/lbryio/sqlboiler/randomize"`,
 				`"github.com/spf13/viper"`,
@@ -315,55 +315,55 @@ func newImporter() importer {
 	// TranslateColumnType to see the type assignments.
 	imp.BasedOnType = mapImports{
 		"null.Float32": {
-			thirdParty: importList{`"github.com/lbryio/lbry.go/null"`},
+			thirdParty: importList{`"github.com/lbryio/lbry.go/extras/null"`},
 		},
 		"null.Float64": {
-			thirdParty: importList{`"github.com/lbryio/lbry.go/null"`},
+			thirdParty: importList{`"github.com/lbryio/lbry.go/extras/null"`},
 		},
 		"null.Int": {
-			thirdParty: importList{`"github.com/lbryio/lbry.go/null"`},
+			thirdParty: importList{`"github.com/lbryio/lbry.go/extras/null"`},
 		},
 		"null.Int8": {
-			thirdParty: importList{`"github.com/lbryio/lbry.go/null"`},
+			thirdParty: importList{`"github.com/lbryio/lbry.go/extras/null"`},
 		},
 		"null.Int16": {
-			thirdParty: importList{`"github.com/lbryio/lbry.go/null"`},
+			thirdParty: importList{`"github.com/lbryio/lbry.go/extras/null"`},
 		},
 		"null.Int32": {
-			thirdParty: importList{`"github.com/lbryio/lbry.go/null"`},
+			thirdParty: importList{`"github.com/lbryio/lbry.go/extras/null"`},
 		},
 		"null.Int64": {
-			thirdParty: importList{`"github.com/lbryio/lbry.go/null"`},
+			thirdParty: importList{`"github.com/lbryio/lbry.go/extras/null"`},
 		},
 		"null.Uint": {
-			thirdParty: importList{`"github.com/lbryio/lbry.go/null"`},
+			thirdParty: importList{`"github.com/lbryio/lbry.go/extras/null"`},
 		},
 		"null.Uint8": {
-			thirdParty: importList{`"github.com/lbryio/lbry.go/null"`},
+			thirdParty: importList{`"github.com/lbryio/lbry.go/extras/null"`},
 		},
 		"null.Uint16": {
-			thirdParty: importList{`"github.com/lbryio/lbry.go/null"`},
+			thirdParty: importList{`"github.com/lbryio/lbry.go/extras/null"`},
 		},
 		"null.Uint32": {
-			thirdParty: importList{`"github.com/lbryio/lbry.go/null"`},
+			thirdParty: importList{`"github.com/lbryio/lbry.go/extras/null"`},
 		},
 		"null.Uint64": {
-			thirdParty: importList{`"github.com/lbryio/lbry.go/null"`},
+			thirdParty: importList{`"github.com/lbryio/lbry.go/extras/null"`},
 		},
 		"null.String": {
-			thirdParty: importList{`"github.com/lbryio/lbry.go/null"`},
+			thirdParty: importList{`"github.com/lbryio/lbry.go/extras/null"`},
 		},
 		"null.Bool": {
-			thirdParty: importList{`"github.com/lbryio/lbry.go/null"`},
+			thirdParty: importList{`"github.com/lbryio/lbry.go/extras/null"`},
 		},
 		"null.Time": {
-			thirdParty: importList{`"github.com/lbryio/lbry.go/null"`},
+			thirdParty: importList{`"github.com/lbryio/lbry.go/extras/null"`},
 		},
 		"null.JSON": {
-			thirdParty: importList{`"github.com/lbryio/lbry.go/null"`},
+			thirdParty: importList{`"github.com/lbryio/lbry.go/extras/null"`},
 		},
 		"null.Bytes": {
-			thirdParty: importList{`"github.com/lbryio/lbry.go/null"`},
+			thirdParty: importList{`"github.com/lbryio/lbry.go/extras/null"`},
 		},
 		"time.Time": {
 			standard: importList{`"time"`},
diff --git a/boilingcore/imports_test.go b/boilingcore/imports_test.go
index 693bc10..6d22956 100644
--- a/boilingcore/imports_test.go
+++ b/boilingcore/imports_test.go
@@ -246,7 +246,7 @@ func TestCombineTypeImports(t *testing.T) {
 		},
 		thirdParty: importList{
 			`"github.com/lbryio/sqlboiler/boil"`,
-			`"github.com/lbryio/lbry.go/null"`,
+			`"github.com/lbryio/lbry.go/extras/null"`,
 		},
 	}
 
@@ -281,7 +281,7 @@ func TestCombineTypeImports(t *testing.T) {
 		},
 		thirdParty: importList{
 			`"github.com/lbryio/sqlboiler/boil"`,
-			`"github.com/lbryio/lbry.go/null"`,
+			`"github.com/lbryio/lbry.go/extras/null"`,
 		},
 	}
 
@@ -297,7 +297,7 @@ func TestCombineImports(t *testing.T) {
 
 	a := imports{
 		standard:   importList{"fmt"},
-		thirdParty: importList{"github.com/lbryio/sqlboiler", "github.com/lbryio/lbry.go/null"},
+		thirdParty: importList{"github.com/lbryio/sqlboiler", "github.com/lbryio/lbry.go/extras/null"},
 	}
 	b := imports{
 		standard:   importList{"os"},
@@ -309,8 +309,8 @@ func TestCombineImports(t *testing.T) {
 	if c.standard[0] != "fmt" && c.standard[1] != "os" {
 		t.Errorf("Wanted: fmt, os got: %#v", c.standard)
 	}
-	if c.thirdParty[0] != "github.com/lbryio/sqlboiler" && c.thirdParty[1] != "github.com/lbryio/lbry.go/null" {
-		t.Errorf("Wanted: github.com/lbryio/sqlboiler, github.com/lbryio/lbry.go/null got: %#v", c.thirdParty)
+	if c.thirdParty[0] != "github.com/lbryio/sqlboiler" && c.thirdParty[1] != "github.com/lbryio/lbry.go/extras/null" {
+		t.Errorf("Wanted: github.com/lbryio/sqlboiler, github.com/lbryio/lbry.go/extras/null got: %#v", c.thirdParty)
 	}
 }
 
diff --git a/queries/helpers_test.go b/queries/helpers_test.go
index b6b757d..5a1802a 100644
--- a/queries/helpers_test.go
+++ b/queries/helpers_test.go
@@ -5,7 +5,7 @@ import (
 	"testing"
 	"time"
 
-	null "github.com/lbryio/lbry.go/null"
+	null "github.com/lbryio/lbry.go/extras/null"
 )
 
 type testObj struct {
diff --git a/queries/query.go b/queries/query.go
index fdd54b4..14f2d51 100644
--- a/queries/query.go
+++ b/queries/query.go
@@ -4,7 +4,7 @@ import (
 	"database/sql"
 	"fmt"
 
-	"github.com/lbryio/lbry.go/errors"
+	"github.com/lbryio/lbry.go/extras/errors"
 	"github.com/lbryio/sqlboiler/boil"
 )
 
diff --git a/randomize/randomize.go b/randomize/randomize.go
index 54c88bf..65b2a2b 100644
--- a/randomize/randomize.go
+++ b/randomize/randomize.go
@@ -14,7 +14,7 @@ import (
 	"sync/atomic"
 	"time"
 
-	null "github.com/lbryio/lbry.go/null"
+	null "github.com/lbryio/lbry.go/extras/null"
 
 	"github.com/pkg/errors"
 	"github.com/satori/go.uuid"
diff --git a/randomize/randomize_test.go b/randomize/randomize_test.go
index bc7a382..45ddf16 100644
--- a/randomize/randomize_test.go
+++ b/randomize/randomize_test.go
@@ -5,7 +5,7 @@ import (
 	"testing"
 	"time"
 
-	null "github.com/lbryio/lbry.go/null"
+	null "github.com/lbryio/lbry.go/extras/null"
 )
 
 func TestRandomizeStruct(t *testing.T) {

From eea3d349a773e4f5bcfb9a194b78674e5b92fdd5 Mon Sep 17 00:00:00 2001
From: Mark Beamer Jr <markbeamerjr@gmail.com>
Date: Mon, 3 Sep 2018 00:51:55 -0400
Subject: [PATCH 2/8] added support for 1 to 1 relations and added support for
 n unique keys for conflict resolution during merging. split out into two
 functions.

---
 templates/singleton/boil_queries.tpl | 118 +++++++++++++++++++--------
 1 file changed, 83 insertions(+), 35 deletions(-)

diff --git a/templates/singleton/boil_queries.tpl b/templates/singleton/boil_queries.tpl
index 51ed108..a881cda 100644
--- a/templates/singleton/boil_queries.tpl
+++ b/templates/singleton/boil_queries.tpl
@@ -27,11 +27,15 @@ func mergeModels(tx boil.Executor, primaryID uint64, secondaryID uint64, foreign
 	var err error
 
 	for _, conflict := range conflictingKeys {
-		err = deleteConflictsBeforeMerge(tx, conflict, primaryID, secondaryID)
-		if err != nil {
-			return err
-		}
-	}
+        if len(conflict.columns) == 1 && conflict.columns[0] == conflict.objectIdColumn {
+            err = deleteOneToOneConflictsBeforeMerge(tx, conflict, primaryID, secondaryID)
+        } else {
+            err = deleteOneToManyConflictsBeforeMerge(tx, conflict, primaryID, secondaryID)
+        }
+        if err != nil {
+            return err
+        }
+     }
 
 	for _, fk := range foreignKeys {
 		// TODO: use NewQuery here, not plain sql
@@ -48,53 +52,97 @@ func mergeModels(tx boil.Executor, primaryID uint64, secondaryID uint64, foreign
 	return checkMerge(tx, foreignKeys)
 }
 
-func deleteConflictsBeforeMerge(tx boil.Executor, conflict conflictingUniqueKey, primaryID uint64, secondaryID uint64) error {
-	conflictingColumns := strmangle.SetComplement(conflict.columns, []string{conflict.objectIdColumn})
-
-	if len(conflictingColumns) < 1 {
-		return nil
-	} else if len(conflictingColumns) > 1 {
-		return errors.Err("this doesnt work for unique keys with more than two columns (yet)")
-	}
-
+func deleteOneToOneConflictsBeforeMerge(tx boil.Executor, conflict conflictingUniqueKey, primaryID uint64, secondaryID uint64) error {
 	query := fmt.Sprintf(
-		"SELECT %s FROM %s WHERE %s IN (%s) GROUP BY %s HAVING count(distinct %s) > 1",
-		conflictingColumns[0], conflict.table, conflict.objectIdColumn,
+		"SELECT COUNT(*) FROM %s WHERE %s IN (%s)",
+		conflict.table, conflict.objectIdColumn,
 		strmangle.Placeholders(dialect.IndexPlaceholders, 2, 1, 1),
-		conflictingColumns[0], conflict.objectIdColumn,
 	)
 
-	rows, err := tx.Query(query, primaryID, secondaryID)
-	defer rows.Close()
+	var count int
+	err := tx.QueryRow(query, primaryID, secondaryID).Scan(&count)
 	if err != nil {
 		return errors.Err(err)
 	}
 
-	args := []interface{}{secondaryID}
-	for rows.Next() {
-		var value string
-		err = rows.Scan(&value)
-		if err != nil {
-			return errors.Err(err)
-		}
-		args = append(args, value)
-	}
-
-	// if no rows found, no need to delete anything
-	if len(args) < 2 {
-		return nil
+	if count > 2 {
+		return errors.Err("it should not be possible to have more than two rows here")
+	} else if count != 2 {
+		return nil // no conflicting rows
 	}
 
 	query = fmt.Sprintf(
-		"DELETE FROM %s WHERE %s = %s AND %s IN (%s)",
+		"DELETE FROM %s WHERE %s = %s",
 		conflict.table, conflict.objectIdColumn, strmangle.Placeholders(dialect.IndexPlaceholders, 1, 1, 1),
-		conflictingColumns[0], strmangle.Placeholders(dialect.IndexPlaceholders, len(args)-1, 2, 1),
 	)
 
-	_, err = tx.Exec(query, args...)
+	_, err = tx.Exec(query, secondaryID)
+	return errors.Err(err)
+}
+
+func deleteOneToManyConflictsBeforeMerge(tx boil.Executor, conflict conflictingUniqueKey, primaryID uint64, secondaryID uint64) error {
+	conflictingColumns := strmangle.SetComplement(conflict.columns, []string{conflict.objectIdColumn})
+
+	query := fmt.Sprintf(
+		"SELECT %s FROM %s WHERE %s IN (%s) GROUP BY %s HAVING count(distinct %s) > 1",
+		strings.Join(conflictingColumns, ","), conflict.table, conflict.objectIdColumn,
+		strmangle.Placeholders(dialect.IndexPlaceholders, 2, 1, 1),
+		strings.Join(conflictingColumns, ","), conflict.objectIdColumn,
+	)
+
+	//The selectParams should be the ObjectIDs to search for regarding the conflict.
+	rows, err := tx.Query(query, primaryID, secondaryID)
 	if err != nil {
 		return errors.Err(err)
 	}
+
+	//Since we don't don't know if advance how many columns the query returns, we have dynamically assign them to be
+	// used in the delete query.
+	colNames, err := rows.Columns()
+	if err != nil {
+		log.Fatal(err)
+	}
+	//Each row result of the query needs to be removed for being a conflicting row. Store each row's keys in an array.
+	var rowsToRemove = [][]interface{}(nil)
+	for rows.Next() {
+		//Set pointers for dynamic scan
+		iColPtrs := make([]interface{}, len(colNames))
+		for i := 0; i < len(colNames); i++ {
+			s := string("")
+			iColPtrs[i] = &s
+		}
+		//Dynamically scan n columns
+		err = rows.Scan(iColPtrs...)
+		if err != nil {
+			return errors.Err(err)
+		}
+		//Grab scanned values for query arguments
+		iCol := make([]interface{}, len(colNames))
+		for i, col := range iColPtrs {
+			x := col.(*string)
+			iCol[i] = *x
+		}
+		rowsToRemove = append(rowsToRemove, iCol)
+	}
+	defer rows.Close()
+
+	//This query will adjust dynamically depending on the number of conflicting keys, adding AND expressions for each
+	// key to ensure the right conflicting rows are deleted.
+	query = fmt.Sprintf(
+		"DELETE FROM %s %s",
+		conflict.table,
+		"WHERE "+strings.Join(conflict.columns, " = ? AND ")+" = ?",
+	)
+
+	//There could be multiple conflicting rows between ObjectIDs. In the SELECT query we grab each row and their column
+	// keys to be deleted here in a loop.
+	for _, rowToDelete := range rowsToRemove {
+		rowToDelete = append(rowToDelete, secondaryID)
+		_, err = tx.Exec(query, rowToDelete...)
+		if err != nil {
+			return errors.Err(err)
+		}
+	}
 	return nil
 }
 

From 29172e976b0923c864899a362c4aefe974a168d7 Mon Sep 17 00:00:00 2001
From: Mark Beamer Jr <markbeamerjr@gmail.com>
Date: Fri, 7 Sep 2018 20:51:50 -0400
Subject: [PATCH 3/8] changed from Fatal to Error - copy paste mistake.

---
 templates/singleton/boil_queries.tpl | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/templates/singleton/boil_queries.tpl b/templates/singleton/boil_queries.tpl
index a881cda..3e9ebb8 100644
--- a/templates/singleton/boil_queries.tpl
+++ b/templates/singleton/boil_queries.tpl
@@ -100,7 +100,7 @@ func deleteOneToManyConflictsBeforeMerge(tx boil.Executor, conflict conflictingU
 	// used in the delete query.
 	colNames, err := rows.Columns()
 	if err != nil {
-		log.Fatal(err)
+		return errors.Err(err)
 	}
 	//Each row result of the query needs to be removed for being a conflicting row. Store each row's keys in an array.
 	var rowsToRemove = [][]interface{}(nil)

From f892107dad46b36f3487ffe336d53064c8a053d8 Mon Sep 17 00:00:00 2001
From: Mark Beamer Jr <markbeamerjr@gmail.com>
Date: Thu, 7 Feb 2019 20:24:07 -0500
Subject: [PATCH 4/8] missing import

---
 boilingcore/imports.go | 1 +
 1 file changed, 1 insertion(+)

diff --git a/boilingcore/imports.go b/boilingcore/imports.go
index b5979e3..d37c84b 100644
--- a/boilingcore/imports.go
+++ b/boilingcore/imports.go
@@ -183,6 +183,7 @@ func newImporter() importer {
 		"boil_queries": imports{
 			standard: importList{
 				`"fmt"`,
+				`"strings"`,
 			},
 			thirdParty: importList{
 				`"github.com/lbryio/lbry.go/extras/errors"`,

From 4e1b83ab3942cc6747e04065aef330b0d9dff56d Mon Sep 17 00:00:00 2001
From: Mark Beamer Jr <markbeamerjr@gmail.com>
Date: Sat, 29 Jun 2019 11:30:23 -0400
Subject: [PATCH 5/8] Add force index query mod for select query.

Fix eager loading casting for nested levels.
---
 queries/eager_load.go     | 14 ++++++++++++--
 queries/qm/query_mods.go  |  7 +++++++
 queries/query.go          |  6 ++++++
 queries/query_builders.go |  8 +++++++-
 4 files changed, 32 insertions(+), 3 deletions(-)

diff --git a/queries/eager_load.go b/queries/eager_load.go
index 081aac4..962d722 100644
--- a/queries/eager_load.go
+++ b/queries/eager_load.go
@@ -206,9 +206,16 @@ func (l loadRelationshipState) loadRelationshipsRecurse(depth int, obj reflect.V
 	}
 
 	bkind := kindStruct
-	if reflect.Indirect(loadedObject).Kind() != reflect.Struct {
+	if derefed := reflect.Indirect(loadedObject); derefed.Kind() != reflect.Struct {
 		bkind = kindPtrSliceStruct
-		loadedObject = loadedObject.Addr()
+
+		// Convert away any helper slice types
+		// elemType is *elem (from []*elem or helperSliceType)
+		// sliceType is *[]*elem
+		elemType := derefed.Type().Elem()
+		sliceType := reflect.PtrTo(reflect.SliceOf(elemType))
+
+		loadedObject = loadedObject.Addr().Convert(sliceType)
 	}
 	return l.loadRelationships(depth+1, loadedObject.Interface(), bkind)
 }
@@ -241,6 +248,9 @@ func collectLoaded(key string, loadingFrom reflect.Value) (reflect.Value, bindKi
 	if loadedType.Elem().Kind() == reflect.Struct {
 		bkind = kindStruct
 		loadedType = reflect.SliceOf(loadedType)
+	} else {
+		// Ensure that we get rid of all the helper "XSlice" types
+		loadedType = reflect.SliceOf(loadedType.Elem())
 	}
 
 	collection := reflect.MakeSlice(loadedType, 0, 0)
diff --git a/queries/qm/query_mods.go b/queries/qm/query_mods.go
index 89d31d4..6fa6e2f 100644
--- a/queries/qm/query_mods.go
+++ b/queries/qm/query_mods.go
@@ -123,6 +123,13 @@ func From(from string) QueryMod {
 	}
 }
 
+func ForceIndex( index string) QueryMod {
+	return func(q *queries.Query) {
+		queries.SetForceIndex(q, index)
+	}
+}
+
+
 // Limit the number of returned rows
 func Limit(limit int) QueryMod {
 	return func(q *queries.Query) {
diff --git a/queries/query.go b/queries/query.go
index 14f2d51..5b0ffbc 100644
--- a/queries/query.go
+++ b/queries/query.go
@@ -30,6 +30,7 @@ type Query struct {
 	selectCols []string
 	count      bool
 	from       []string
+	forceindex string
 	joins      []join
 	where      []where
 	in         []in
@@ -263,6 +264,11 @@ func SetLastWhereAsOr(q *Query) {
 	q.where[len(q.where)-1].orSeparator = true
 }
 
+// SetForceIndex sets the index to be used by the query
+func SetForceIndex(q *Query, index string){
+	q.forceindex = index
+}
+
 // SetLastInAsOr sets the or separator for the tail "IN" in the slice
 func SetLastInAsOr(q *Query) {
 	if len(q.in) == 0 {
diff --git a/queries/query_builders.go b/queries/query_builders.go
index f884f91..e682877 100644
--- a/queries/query_builders.go
+++ b/queries/query_builders.go
@@ -76,7 +76,13 @@ func buildSelectQuery(q *Query) (*bytes.Buffer, []interface{}) {
 		buf.WriteByte(')')
 	}
 
-	fmt.Fprintf(buf, " FROM %s", strings.Join(strmangle.IdentQuoteSlice(q.dialect.LQ, q.dialect.RQ, q.from), ", "))
+	if len(q.forceindex) > 0 {
+		fmt.Fprintf(buf, " FROM %s FORCE INDEX (%s)", strings.Join(strmangle.IdentQuoteSlice(q.dialect.LQ, q.dialect.RQ, q.from), ", "),q.forceindex)
+
+	}else{
+		fmt.Fprintf(buf, " FROM %s", strings.Join(strmangle.IdentQuoteSlice(q.dialect.LQ, q.dialect.RQ, q.from), ", "))
+
+	}
 
 	if len(q.joins) > 0 {
 		argsLen := len(args)

From c01b1828391ba95ef3366817aad739e264f46441 Mon Sep 17 00:00:00 2001
From: Mark Beamer Jr <markbeamerjr@gmail.com>
Date: Mon, 1 Jul 2019 23:46:37 -0400
Subject: [PATCH 6/8] Allow null query mods for dynamic queries

---
 queries/qm/query_mods.go | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/queries/qm/query_mods.go b/queries/qm/query_mods.go
index 6fa6e2f..42e881e 100644
--- a/queries/qm/query_mods.go
+++ b/queries/qm/query_mods.go
@@ -8,7 +8,9 @@ type QueryMod func(q *queries.Query)
 // Apply the query mods to the Query object
 func Apply(q *queries.Query, mods ...QueryMod) {
 	for _, mod := range mods {
-		mod(q)
+		if mod != nil {
+			mod(q)
+		}
 	}
 }
 
@@ -123,13 +125,12 @@ func From(from string) QueryMod {
 	}
 }
 
-func ForceIndex( index string) QueryMod {
+func ForceIndex(index string) QueryMod {
 	return func(q *queries.Query) {
 		queries.SetForceIndex(q, index)
 	}
 }
 
-
 // Limit the number of returned rows
 func Limit(limit int) QueryMod {
 	return func(q *queries.Query) {

From 256a6d4225b70fd17caba555c5b8f035e33db8c9 Mon Sep 17 00:00:00 2001
From: Niko Storni <niko@lbry.io>
Date: Mon, 10 Feb 2020 14:49:11 +0100
Subject: [PATCH 7/8] update lbry.go library

---
 boilingcore/imports.go      | 50 ++++++++++++++++++-------------------
 boilingcore/imports_test.go | 10 ++++----
 queries/helpers_test.go     |  2 +-
 queries/query.go            |  2 +-
 randomize/randomize.go      |  2 +-
 randomize/randomize_test.go |  2 +-
 6 files changed, 34 insertions(+), 34 deletions(-)

diff --git a/boilingcore/imports.go b/boilingcore/imports.go
index d37c84b..e20480b 100644
--- a/boilingcore/imports.go
+++ b/boilingcore/imports.go
@@ -170,8 +170,8 @@ func newImporter() importer {
 			`"time"`,
 		},
 		thirdParty: importList{
-			`"github.com/lbryio/lbry.go/extras/errors"`,
-			`"github.com/lbryio/lbry.go/extras/null"`,
+			`"github.com/lbryio/lbry.go/v2/extras/errors"`,
+			`"github.com/lbryio/lbry.go/v2/extras/null"`,
 			`"github.com/lbryio/sqlboiler/boil"`,
 			`"github.com/lbryio/sqlboiler/queries"`,
 			`"github.com/lbryio/sqlboiler/queries/qm"`,
@@ -186,7 +186,7 @@ func newImporter() importer {
 				`"strings"`,
 			},
 			thirdParty: importList{
-				`"github.com/lbryio/lbry.go/extras/errors"`,
+				`"github.com/lbryio/lbry.go/v2/extras/errors"`,
 				`"github.com/lbryio/sqlboiler/boil"`,
 				`"github.com/lbryio/sqlboiler/queries"`,
 				`"github.com/lbryio/sqlboiler/queries/qm"`,
@@ -195,7 +195,7 @@ func newImporter() importer {
 		},
 		"boil_types": {
 			thirdParty: importList{
-				`"github.com/lbryio/lbry.go/extras/errors"`,
+				`"github.com/lbryio/lbry.go/v2/extras/errors"`,
 				`"github.com/lbryio/sqlboiler/strmangle"`,
 			},
 		},
@@ -228,7 +228,7 @@ func newImporter() importer {
 			},
 			thirdParty: importList{
 				`"github.com/kat-co/vala"`,
-				`"github.com/lbryio/lbry.go/extras/errors"`,
+				`"github.com/lbryio/lbry.go/v2/extras/errors"`,
 				`"github.com/lbryio/sqlboiler/boil"`,
 				`"github.com/spf13/viper"`,
 			},
@@ -266,7 +266,7 @@ func newImporter() importer {
 				`"strings"`,
 			},
 			thirdParty: importList{
-				`"github.com/lbryio/lbry.go/extras/errors"`,
+				`"github.com/lbryio/lbry.go/v2/extras/errors"`,
 				`"github.com/lbryio/sqlboiler/bdb/drivers"`,
 				`"github.com/lbryio/sqlboiler/randomize"`,
 				`_ "github.com/lib/pq"`,
@@ -286,7 +286,7 @@ func newImporter() importer {
 			},
 			thirdParty: importList{
 				`_ "github.com/go-sql-driver/mysql"`,
-				`"github.com/lbryio/lbry.go/extras/errors"`,
+				`"github.com/lbryio/lbry.go/v2/extras/errors"`,
 				`"github.com/lbryio/sqlboiler/bdb/drivers"`,
 				`"github.com/lbryio/sqlboiler/randomize"`,
 				`"github.com/spf13/viper"`,
@@ -303,7 +303,7 @@ func newImporter() importer {
 			},
 			thirdParty: importList{
 				`_ "github.com/denisenkom/go-mssqldb"`,
-				`"github.com/lbryio/lbry.go/extras/errors"`,
+				`"github.com/lbryio/lbry.go/v2/extras/errors"`,
 				`"github.com/lbryio/sqlboiler/bdb/drivers"`,
 				`"github.com/lbryio/sqlboiler/randomize"`,
 				`"github.com/spf13/viper"`,
@@ -316,55 +316,55 @@ func newImporter() importer {
 	// TranslateColumnType to see the type assignments.
 	imp.BasedOnType = mapImports{
 		"null.Float32": {
-			thirdParty: importList{`"github.com/lbryio/lbry.go/extras/null"`},
+			thirdParty: importList{`"github.com/lbryio/lbry.go/v2/extras/null"`},
 		},
 		"null.Float64": {
-			thirdParty: importList{`"github.com/lbryio/lbry.go/extras/null"`},
+			thirdParty: importList{`"github.com/lbryio/lbry.go/v2/extras/null"`},
 		},
 		"null.Int": {
-			thirdParty: importList{`"github.com/lbryio/lbry.go/extras/null"`},
+			thirdParty: importList{`"github.com/lbryio/lbry.go/v2/extras/null"`},
 		},
 		"null.Int8": {
-			thirdParty: importList{`"github.com/lbryio/lbry.go/extras/null"`},
+			thirdParty: importList{`"github.com/lbryio/lbry.go/v2/extras/null"`},
 		},
 		"null.Int16": {
-			thirdParty: importList{`"github.com/lbryio/lbry.go/extras/null"`},
+			thirdParty: importList{`"github.com/lbryio/lbry.go/v2/extras/null"`},
 		},
 		"null.Int32": {
-			thirdParty: importList{`"github.com/lbryio/lbry.go/extras/null"`},
+			thirdParty: importList{`"github.com/lbryio/lbry.go/v2/extras/null"`},
 		},
 		"null.Int64": {
-			thirdParty: importList{`"github.com/lbryio/lbry.go/extras/null"`},
+			thirdParty: importList{`"github.com/lbryio/lbry.go/v2/extras/null"`},
 		},
 		"null.Uint": {
-			thirdParty: importList{`"github.com/lbryio/lbry.go/extras/null"`},
+			thirdParty: importList{`"github.com/lbryio/lbry.go/v2/extras/null"`},
 		},
 		"null.Uint8": {
-			thirdParty: importList{`"github.com/lbryio/lbry.go/extras/null"`},
+			thirdParty: importList{`"github.com/lbryio/lbry.go/v2/extras/null"`},
 		},
 		"null.Uint16": {
-			thirdParty: importList{`"github.com/lbryio/lbry.go/extras/null"`},
+			thirdParty: importList{`"github.com/lbryio/lbry.go/v2/extras/null"`},
 		},
 		"null.Uint32": {
-			thirdParty: importList{`"github.com/lbryio/lbry.go/extras/null"`},
+			thirdParty: importList{`"github.com/lbryio/lbry.go/v2/extras/null"`},
 		},
 		"null.Uint64": {
-			thirdParty: importList{`"github.com/lbryio/lbry.go/extras/null"`},
+			thirdParty: importList{`"github.com/lbryio/lbry.go/v2/extras/null"`},
 		},
 		"null.String": {
-			thirdParty: importList{`"github.com/lbryio/lbry.go/extras/null"`},
+			thirdParty: importList{`"github.com/lbryio/lbry.go/v2/extras/null"`},
 		},
 		"null.Bool": {
-			thirdParty: importList{`"github.com/lbryio/lbry.go/extras/null"`},
+			thirdParty: importList{`"github.com/lbryio/lbry.go/v2/extras/null"`},
 		},
 		"null.Time": {
-			thirdParty: importList{`"github.com/lbryio/lbry.go/extras/null"`},
+			thirdParty: importList{`"github.com/lbryio/lbry.go/v2/extras/null"`},
 		},
 		"null.JSON": {
-			thirdParty: importList{`"github.com/lbryio/lbry.go/extras/null"`},
+			thirdParty: importList{`"github.com/lbryio/lbry.go/v2/extras/null"`},
 		},
 		"null.Bytes": {
-			thirdParty: importList{`"github.com/lbryio/lbry.go/extras/null"`},
+			thirdParty: importList{`"github.com/lbryio/lbry.go/v2/extras/null"`},
 		},
 		"time.Time": {
 			standard: importList{`"time"`},
diff --git a/boilingcore/imports_test.go b/boilingcore/imports_test.go
index 6d22956..d0043c0 100644
--- a/boilingcore/imports_test.go
+++ b/boilingcore/imports_test.go
@@ -246,7 +246,7 @@ func TestCombineTypeImports(t *testing.T) {
 		},
 		thirdParty: importList{
 			`"github.com/lbryio/sqlboiler/boil"`,
-			`"github.com/lbryio/lbry.go/extras/null"`,
+			`"github.com/lbryio/lbry.go/v2/extras/null"`,
 		},
 	}
 
@@ -281,7 +281,7 @@ func TestCombineTypeImports(t *testing.T) {
 		},
 		thirdParty: importList{
 			`"github.com/lbryio/sqlboiler/boil"`,
-			`"github.com/lbryio/lbry.go/extras/null"`,
+			`"github.com/lbryio/lbry.go/v2/extras/null"`,
 		},
 	}
 
@@ -297,7 +297,7 @@ func TestCombineImports(t *testing.T) {
 
 	a := imports{
 		standard:   importList{"fmt"},
-		thirdParty: importList{"github.com/lbryio/sqlboiler", "github.com/lbryio/lbry.go/extras/null"},
+		thirdParty: importList{"github.com/lbryio/sqlboiler", "github.com/lbryio/lbry.go/v2/extras/null"},
 	}
 	b := imports{
 		standard:   importList{"os"},
@@ -309,8 +309,8 @@ func TestCombineImports(t *testing.T) {
 	if c.standard[0] != "fmt" && c.standard[1] != "os" {
 		t.Errorf("Wanted: fmt, os got: %#v", c.standard)
 	}
-	if c.thirdParty[0] != "github.com/lbryio/sqlboiler" && c.thirdParty[1] != "github.com/lbryio/lbry.go/extras/null" {
-		t.Errorf("Wanted: github.com/lbryio/sqlboiler, github.com/lbryio/lbry.go/extras/null got: %#v", c.thirdParty)
+	if c.thirdParty[0] != "github.com/lbryio/sqlboiler" && c.thirdParty[1] != "github.com/lbryio/lbry.go/v2/extras/null" {
+		t.Errorf("Wanted: github.com/lbryio/sqlboiler, github.com/lbryio/lbry.go/v2/extras/null got: %#v", c.thirdParty)
 	}
 }
 
diff --git a/queries/helpers_test.go b/queries/helpers_test.go
index 5a1802a..fd093e3 100644
--- a/queries/helpers_test.go
+++ b/queries/helpers_test.go
@@ -5,7 +5,7 @@ import (
 	"testing"
 	"time"
 
-	null "github.com/lbryio/lbry.go/extras/null"
+	null "github.com/lbryio/lbry.go/v2/extras/null"
 )
 
 type testObj struct {
diff --git a/queries/query.go b/queries/query.go
index 5b0ffbc..e3a6470 100644
--- a/queries/query.go
+++ b/queries/query.go
@@ -4,7 +4,7 @@ import (
 	"database/sql"
 	"fmt"
 
-	"github.com/lbryio/lbry.go/extras/errors"
+	"github.com/lbryio/lbry.go/v2/extras/errors"
 	"github.com/lbryio/sqlboiler/boil"
 )
 
diff --git a/randomize/randomize.go b/randomize/randomize.go
index 65b2a2b..30592bf 100644
--- a/randomize/randomize.go
+++ b/randomize/randomize.go
@@ -14,7 +14,7 @@ import (
 	"sync/atomic"
 	"time"
 
-	null "github.com/lbryio/lbry.go/extras/null"
+	null "github.com/lbryio/lbry.go/v2/extras/null"
 
 	"github.com/pkg/errors"
 	"github.com/satori/go.uuid"
diff --git a/randomize/randomize_test.go b/randomize/randomize_test.go
index 45ddf16..71afb55 100644
--- a/randomize/randomize_test.go
+++ b/randomize/randomize_test.go
@@ -5,7 +5,7 @@ import (
 	"testing"
 	"time"
 
-	null "github.com/lbryio/lbry.go/extras/null"
+	null "github.com/lbryio/lbry.go/v2/extras/null"
 )
 
 func TestRandomizeStruct(t *testing.T) {

From 3db4f30f564e65e40f5467f76692501c21489a46 Mon Sep 17 00:00:00 2001
From: Mark Beamer Jr <markbeamerjr@gmail.com>
Date: Wed, 19 Aug 2020 23:43:39 -0400
Subject: [PATCH 8/8] Fix multi-depth eager loading of relationships. If a
 relationship is nil, do not add it to the collection for checking the next
 depth level.

---
 queries/eager_load.go | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/queries/eager_load.go b/queries/eager_load.go
index 962d722..8992290 100644
--- a/queries/eager_load.go
+++ b/queries/eager_load.go
@@ -5,9 +5,9 @@ import (
 	"reflect"
 	"strings"
 
-	"github.com/pkg/errors"
 	"github.com/lbryio/sqlboiler/boil"
 	"github.com/lbryio/sqlboiler/strmangle"
+	"github.com/pkg/errors"
 )
 
 type loadRelationshipState struct {
@@ -259,9 +259,13 @@ func collectLoaded(key string, loadingFrom reflect.Value) (reflect.Value, bindKi
 	for {
 		switch bkind {
 		case kindStruct:
-			collection = reflect.Append(collection, loadedObject)
+			if !loadedObject.IsNil() {
+				collection = reflect.Append(collection, loadedObject)
+			}
 		case kindPtrSliceStruct:
-			collection = reflect.AppendSlice(collection, loadedObject)
+			if !loadedObject.IsNil() {
+				collection = reflect.AppendSlice(collection, loadedObject)
+			}
 		}
 
 		i++