Skip to content

Commit

Permalink
Fix case insensitive LIKE for postgres
Browse files Browse the repository at this point in the history
LIKE in SQLITE is case insensetive by default, where its SENSETIVE in postgres.
Postgres has ILIKE which is insensitive, we should use that
  • Loading branch information
NodudeWasTaken committed Oct 26, 2024
1 parent 49cea00 commit 2c0c951
Show file tree
Hide file tree
Showing 14 changed files with 48 additions and 42 deletions.
14 changes: 7 additions & 7 deletions pkg/sqlite/criterion_handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,9 @@ func stringCriterionHandler(c *models.StringCriterionInput, column string) crite
case models.CriterionModifierExcludes:
f.whereClauses = append(f.whereClauses, getStringSearchClause([]string{column}, c.Value, true))
case models.CriterionModifierEquals:
f.addWhere(column+" LIKE ?", c.Value)
f.addWhere(column+" "+getDBLike()+" ?", c.Value)
case models.CriterionModifierNotEquals:
f.addWhere(column+" NOT LIKE ?", c.Value)
f.addWhere(column+" NOT "+getDBLike()+" ?", c.Value)
case models.CriterionModifierMatchesRegex:
if _, err := regexp.Compile(c.Value); err != nil {
f.setError(err)
Expand Down Expand Up @@ -86,9 +86,9 @@ func uuidCriterionHandler(c *models.StringCriterionInput, column string) criteri
case models.CriterionModifierExcludes:
f.whereClauses = append(f.whereClauses, getStringSearchClause([]string{columnCast}, c.Value, true))
case models.CriterionModifierEquals:
f.addWhere(columnCast+" LIKE ?", c.Value)
f.addWhere(columnCast+" "+getDBLike()+" ?", c.Value)
case models.CriterionModifierNotEquals:
f.addWhere(columnCast+" NOT LIKE ?", c.Value)
f.addWhere(columnCast+" NOT "+getDBLike()+" ?", c.Value)
case models.CriterionModifierMatchesRegex:
if _, err := regexp.Compile(c.Value); err != nil {
f.setError(err)
Expand Down Expand Up @@ -191,7 +191,7 @@ func getPathSearchClause(pathColumn, basenameColumn, p string, addWildcards, not
}

filepathColumn := fmt.Sprintf("%s || '%s' || %s", pathColumn, string(filepath.Separator), basenameColumn)
ret := makeClause(fmt.Sprintf("%s LIKE ?", filepathColumn), p)
ret := makeClause(fmt.Sprintf("%s "+getDBLike()+" ?", filepathColumn), p)

if not {
ret = ret.not()
Expand Down Expand Up @@ -589,7 +589,7 @@ func (m *stringListCriterionHandlerBuilder) handler(criterion *models.StringCrit
// excludes all of the provided values
// need to use actual join table name for this
// <primaryTable>.id NOT IN (select <joinTable>.<primaryFK> from <joinTable> where <joinTable>.<foreignFK> in <values>)
whereClause := utils.StrFormat("{primaryTable}.id NOT IN (SELECT {joinTable}.{primaryFK} from {joinTable} where {joinTable}.{stringColumn} LIKE ?)",
whereClause := utils.StrFormat("{primaryTable}.id NOT IN (SELECT {joinTable}.{primaryFK} from {joinTable} where {joinTable}.{stringColumn} "+getDBLike()+" ?)",
utils.StrFormatMap{
"primaryTable": m.primaryTable,
"joinTable": m.joinTable,
Expand Down Expand Up @@ -730,7 +730,7 @@ WHERE id in {inBinding}
{unionClause})
`, withClauseMap)

query := fmt.Sprintf("WITH RECURSIVE %s SELECT 'VALUES' || "+DBGroupConcat("'(' || root_id || ', ' || item_id || ')'")+" AS val FROM items", withClause)
query := fmt.Sprintf("WITH RECURSIVE %s SELECT 'VALUES' || "+getDBGroupConcat("'(' || root_id || ', ' || item_id || ')'")+" AS val FROM items", withClause)

var valuesClause sql.NullString
err := dbWrapper.Get(ctx, &valuesClause, query, args...)
Expand Down
11 changes: 10 additions & 1 deletion pkg/sqlite/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,16 @@ func getDBMinFunc() string {
}
}

func DBGroupConcat(columnName string) string {
func getDBLike() string {
switch dbWrapper.dbType {
case PostgresBackend:
return "ILIKE"
default:
return "LIKE"
}
}

func getDBGroupConcat(columnName string) string {
switch dbWrapper.dbType {
case PostgresBackend:
return "STRING_AGG(" + columnName + "::TEXT, ',')"
Expand Down
6 changes: 3 additions & 3 deletions pkg/sqlite/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -622,8 +622,8 @@ func (qb *FileStore) FindAllByPath(ctx context.Context, p string) ([]models.File

if strings.Contains(basename, "%") || strings.Contains(dirName, "%") {
q = q.Where(
folderTable.Col("path").Like(dirName),
table.Col("basename").Like(basename),
folderTable.Col("path").ILike(dirName),
table.Col("basename").ILike(basename),
)
} else {
q = q.Where(
Expand All @@ -647,7 +647,7 @@ func (qb *FileStore) allInPaths(q *goqu.SelectDataset, p []string) *goqu.SelectD
for _, pp := range p {
ppWildcard := pp + string(filepath.Separator) + "%"

conds = append(conds, folderTable.Col("path").Eq(pp), folderTable.Col("path").Like(ppWildcard))
conds = append(conds, folderTable.Col("path").Eq(pp), folderTable.Col("path").ILike(ppWildcard))
}

return q.Where(
Expand Down
12 changes: 6 additions & 6 deletions pkg/sqlite/filter_internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -471,7 +471,7 @@ func TestStringCriterionHandlerIncludes(t *testing.T) {
}, column))

assert.Len(f.whereClauses, 1)
assert.Equal(fmt.Sprintf("(%[1]s LIKE ? OR %[1]s LIKE ?)", column), f.whereClauses[0].sql)
assert.Equal(fmt.Sprintf("(%[1]s "+getDBLike()+" ? OR %[1]s "+getDBLike()+" ?)", column), f.whereClauses[0].sql)
assert.Len(f.whereClauses[0].args, 2)
assert.Equal("%two%", f.whereClauses[0].args[0])
assert.Equal("%words%", f.whereClauses[0].args[1])
Expand All @@ -483,7 +483,7 @@ func TestStringCriterionHandlerIncludes(t *testing.T) {
}, column))

assert.Len(f.whereClauses, 1)
assert.Equal(fmt.Sprintf("(%[1]s LIKE ?)", column), f.whereClauses[0].sql)
assert.Equal(fmt.Sprintf("(%[1]s "+getDBLike()+" ?)", column), f.whereClauses[0].sql)
assert.Len(f.whereClauses[0].args, 1)
assert.Equal("%two words%", f.whereClauses[0].args[0])
}
Expand All @@ -502,7 +502,7 @@ func TestStringCriterionHandlerExcludes(t *testing.T) {
}, column))

assert.Len(f.whereClauses, 1)
assert.Equal(fmt.Sprintf("(%[1]s NOT LIKE ? AND %[1]s NOT LIKE ?)", column), f.whereClauses[0].sql)
assert.Equal(fmt.Sprintf("(%[1]s NOT "+getDBLike()+" ? AND %[1]s NOT "+getDBLike()+" ?)", column), f.whereClauses[0].sql)
assert.Len(f.whereClauses[0].args, 2)
assert.Equal("%two%", f.whereClauses[0].args[0])
assert.Equal("%words%", f.whereClauses[0].args[1])
Expand All @@ -514,7 +514,7 @@ func TestStringCriterionHandlerExcludes(t *testing.T) {
}, column))

assert.Len(f.whereClauses, 1)
assert.Equal(fmt.Sprintf("(%[1]s NOT LIKE ?)", column), f.whereClauses[0].sql)
assert.Equal(fmt.Sprintf("(%[1]s NOT "+getDBLike()+" ?)", column), f.whereClauses[0].sql)
assert.Len(f.whereClauses[0].args, 1)
assert.Equal("%two words%", f.whereClauses[0].args[0])
}
Expand All @@ -532,7 +532,7 @@ func TestStringCriterionHandlerEquals(t *testing.T) {
}, column))

assert.Len(f.whereClauses, 1)
assert.Equal(fmt.Sprintf("%[1]s LIKE ?", column), f.whereClauses[0].sql)
assert.Equal(fmt.Sprintf("%[1]s "+getDBLike()+" ?", column), f.whereClauses[0].sql)
assert.Len(f.whereClauses[0].args, 1)
assert.Equal(value1, f.whereClauses[0].args[0])
}
Expand All @@ -550,7 +550,7 @@ func TestStringCriterionHandlerNotEquals(t *testing.T) {
}, column))

assert.Len(f.whereClauses, 1)
assert.Equal(fmt.Sprintf("%[1]s NOT LIKE ?", column), f.whereClauses[0].sql)
assert.Equal(fmt.Sprintf("%[1]s NOT "+getDBLike()+" ?", column), f.whereClauses[0].sql)
assert.Len(f.whereClauses[0].args, 1)
assert.Equal(value1, f.whereClauses[0].args[0])
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/sqlite/folder.go
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ func (qb *FolderStore) allInPaths(q *goqu.SelectDataset, p []string) *goqu.Selec
for _, pp := range p {
ppWildcard := pp + string(filepath.Separator) + "%"

conds = append(conds, table.Col("path").Eq(pp), table.Col("path").Like(ppWildcard))
conds = append(conds, table.Col("path").Eq(pp), table.Col("path").ILike(ppWildcard))
}

return q.Where(
Expand Down
4 changes: 2 additions & 2 deletions pkg/sqlite/gallery_filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,13 +201,13 @@ func (qb *galleryFilterHandler) pathCriterionHandler(c *models.StringCriterionIn
case models.CriterionModifierEquals:
addWildcards = false
clause := getPathSearchClause(pathColumn, basenameColumn, c.Value, addWildcards, not)
clause2 := makeClause(folderPathColumn+" LIKE ?", c.Value)
clause2 := makeClause(folderPathColumn+" "+getDBLike()+" ?", c.Value)
f.whereClauses = append(f.whereClauses, orClauses(clause, clause2))
case models.CriterionModifierNotEquals:
addWildcards = false
not = true
clause := getPathSearchClause(pathColumn, basenameColumn, c.Value, addWildcards, not)
clause2 := makeClause(folderPathColumn+" NOT LIKE ?", c.Value)
clause2 := makeClause(folderPathColumn+" NOT "+getDBLike()+" ?", c.Value)
f.whereClauses = append(f.whereClauses, orClauses(clause, clause2))
case models.CriterionModifierMatchesRegex:
if _, err := regexp.Compile(c.Value); err != nil {
Expand Down
4 changes: 2 additions & 2 deletions pkg/sqlite/performer.go
Original file line number Diff line number Diff line change
Expand Up @@ -568,9 +568,9 @@ func (qb *PerformerStore) QueryForAutoTag(ctx context.Context, words []string) (
var whereClauses []exp.Expression

for _, w := range words {
whereClauses = append(whereClauses, table.Col("name").Like(w+"%"))
whereClauses = append(whereClauses, table.Col("name").ILike(w+"%"))
// TODO - see above
// whereClauses = append(whereClauses, performersAliasesJoinTable.Col("alias").Like(w+"%"))
// whereClauses = append(whereClauses, performersAliasesJoinTable.Col("alias").ILike(w+"%"))
}

sq = sq.Where(
Expand Down
9 changes: 3 additions & 6 deletions pkg/sqlite/performer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1155,12 +1155,9 @@ func TestPerformerQueryForAutoTag(t *testing.T) {
t.Errorf("Error finding performers: %s", err.Error())
}

if assert.Len(t, performers, 2) {
assert.Equal(t, strings.ToLower(performerNames[performerIdx1WithScene]), strings.ToLower(performers[0].Name))
assert.Equal(t, strings.ToLower(performerNames[performerIdx1WithScene]), strings.ToLower(performers[1].Name))
} else {
t.Errorf("Skipping performer comparison as atleast 1 is missing")
}
assert.Len(t, performers, 2)
assert.Equal(t, strings.ToLower(performerNames[performerIdx1WithScene]), strings.ToLower(performers[0].Name))
assert.Equal(t, strings.ToLower(performerNames[performerIdx1WithScene]), strings.ToLower(performers[1].Name))

return nil
})
Expand Down
6 changes: 3 additions & 3 deletions pkg/sqlite/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ func (qb *queryBuilder) parseQueryString(columns []string, q string) {
var clauses []string

for _, column := range columns {
clauses = append(clauses, column+" LIKE ?")
clauses = append(clauses, column+" "+getDBLike()+" ?")
qb.addArg(like(t))
}

Expand All @@ -199,7 +199,7 @@ func (qb *queryBuilder) parseQueryString(columns []string, q string) {

for _, t := range specs.MustNot {
for _, column := range columns {
qb.addWhere(coalesce(column) + " NOT LIKE ?")
qb.addWhere(coalesce(column) + " NOT " + getDBLike() + " ?")
qb.addArg(like(t))
}
}
Expand All @@ -209,7 +209,7 @@ func (qb *queryBuilder) parseQueryString(columns []string, q string) {

for _, column := range columns {
for _, v := range set {
clauses = append(clauses, column+" LIKE ?")
clauses = append(clauses, column+" "+getDBLike()+" ?")
qb.addArg(like(v))
}
}
Expand Down
8 changes: 4 additions & 4 deletions pkg/sqlite/scene.go
Original file line number Diff line number Diff line change
Expand Up @@ -710,8 +710,8 @@ func (qb *SceneStore) FindByPath(ctx context.Context, p string) ([]*models.Scene
foldersTable,
goqu.On(foldersTable.Col(idColumn).Eq(filesTable.Col("parent_folder_id"))),
).Select(scenesFilesJoinTable.Col(sceneIDColumn)).Where(
foldersTable.Col("path").Like(dir),
filesTable.Col("basename").Like(basename),
foldersTable.Col("path").ILike(dir),
filesTable.Col("basename").ILike(basename),
)

ret, err := qb.findBySubquery(ctx, sq)
Expand Down Expand Up @@ -890,7 +890,7 @@ func (qb *SceneStore) Wall(ctx context.Context, q *string) ([]*models.Scene, err
}

table := qb.table()
qq := qb.selectDataset().Prepared(true).Where(table.Col("details").Like("%" + s + "%")).Order(goqu.L("RANDOM()").Asc()).Limit(80)
qq := qb.selectDataset().Prepared(true).Where(table.Col("details").ILike("%" + s + "%")).Order(goqu.L("RANDOM()").Asc()).Limit(80)
return qb.getMany(ctx, qq)
}

Expand Down Expand Up @@ -1356,7 +1356,7 @@ func (qb *SceneStore) FindDuplicates(ctx context.Context, distance int, duration
if distance == 0 {
var ids []string

dbfix_findExactDuplicateQuery := fmt.Sprintf(findExactDuplicateQuery, DBGroupConcat("DISTINCT scene_id"))
dbfix_findExactDuplicateQuery := fmt.Sprintf(findExactDuplicateQuery, getDBGroupConcat("DISTINCT scene_id"))
if err := dbWrapper.Select(ctx, &ids, dbfix_findExactDuplicateQuery, durationDiff); err != nil {
return nil, err
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/sqlite/scene_filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -389,7 +389,7 @@ func (qb *sceneFilterHandler) captionCriterionHandler(captions *models.StringCri
excludeClause := `scenes.id NOT IN (
SELECT scenes_files.scene_id from scenes_files
INNER JOIN video_captions on video_captions.file_id = scenes_files.file_id
WHERE video_captions.language_code LIKE ?
WHERE video_captions.language_code " + getDBLike() + " ?
)`
f.addWhere(excludeClause, criterion.Value)

Expand Down
4 changes: 2 additions & 2 deletions pkg/sqlite/scene_marker.go
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ func (qb *SceneMarkerStore) CountByTagID(ctx context.Context, tagID int) (int, e
func (qb *SceneMarkerStore) GetMarkerStrings(ctx context.Context, q *string, sort *string) ([]*models.MarkerStringsResultType, error) {
query := "SELECT count(*) as `count`, scene_markers.id as id, scene_markers.title as title FROM scene_markers"
if q != nil {
query += " WHERE title LIKE '%" + *q + "%'"
query += " WHERE title " + getDBLike() + " '%" + *q + "%'"
}
query += " GROUP BY title"
if sort != nil && *sort == "count" {
Expand All @@ -285,7 +285,7 @@ func (qb *SceneMarkerStore) Wall(ctx context.Context, q *string) ([]*models.Scen
}

table := qb.table()
qq := qb.selectDataset().Prepared(true).Where(table.Col("title").Like("%" + s + "%")).Order(goqu.L("RANDOM()").Asc()).Limit(80)
qq := qb.selectDataset().Prepared(true).Where(table.Col("title").ILike("%" + s + "%")).Order(goqu.L("RANDOM()").Asc()).Limit(80)
return qb.getMany(ctx, qq)
}

Expand Down
4 changes: 2 additions & 2 deletions pkg/sqlite/sql.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,14 +164,14 @@ func getStringSearchClause(columns []string, q string, not bool) sqlClause {
// Search for any word
for _, word := range queryWords {
for _, column := range columns {
likeClauses = append(likeClauses, column+notStr+" LIKE ?")
likeClauses = append(likeClauses, column+notStr+" "+getDBLike()+" ?")
args = append(args, "%"+word+"%")
}
}
} else {
// Search the exact query
for _, column := range columns {
likeClauses = append(likeClauses, column+notStr+" LIKE ?")
likeClauses = append(likeClauses, column+notStr+" "+getDBLike()+" ?")
args = append(args, "%"+trimmedQuery+"%")
}
}
Expand Down
4 changes: 2 additions & 2 deletions pkg/sqlite/studio.go
Original file line number Diff line number Diff line change
Expand Up @@ -495,8 +495,8 @@ func (qb *StudioStore) QueryForAutoTag(ctx context.Context, words []string) ([]*
var whereClauses []exp.Expression

for _, w := range words {
whereClauses = append(whereClauses, table.Col(studioNameColumn).Like(w+"%"))
whereClauses = append(whereClauses, studiosAliasesJoinTable.Col("alias").Like(w+"%"))
whereClauses = append(whereClauses, table.Col(studioNameColumn).ILike(w+"%"))
whereClauses = append(whereClauses, studiosAliasesJoinTable.Col("alias").ILike(w+"%"))
}

sq = sq.Where(
Expand Down

0 comments on commit 2c0c951

Please sign in to comment.