diff --git a/libpod/sqlite_state.go b/libpod/sqlite_state.go index ea42f8e3e8..72fb83f62f 100644 --- a/libpod/sqlite_state.go +++ b/libpod/sqlite_state.go @@ -39,13 +39,16 @@ const ( sqliteOptionForeignKeys = "&_foreign_keys=1" // Make sure that transactions happen exclusively. sqliteOptionTXLock = "&_txlock=exclusive" + // Enforce case sensitivity for LIKE + sqliteOptionCaseSensitiveLike = "&_cslike=TRUE" // Assembled sqlite options used when opening the database. sqliteOptions = "db.sql?" + sqliteOptionLocation + sqliteOptionSynchronous + sqliteOptionForeignKeys + - sqliteOptionTXLock + sqliteOptionTXLock + + sqliteOptionCaseSensitiveLike ) // NewSqliteState creates a new SQLite-backed state database. @@ -2210,7 +2213,9 @@ func (s *SQLiteState) LookupVolume(name string) (*Volume, error) { return nil, define.ErrDBClosed } - rows, err := s.conn.Query("SELECT Name, JSON FROM VolumeConfig WHERE Name LIKE ? ORDER BY LENGTH(Name) ASC;", name+"%") + escaper := strings.NewReplacer("\\", "\\\\", "_", "\\_", "%", "\\%") + queryString := escaper.Replace(name) + "%" + rows, err := s.conn.Query("SELECT Name, JSON FROM VolumeConfig WHERE Name LIKE ? ESCAPE '\\' ORDER BY LENGTH(Name) ASC;", queryString) if err != nil { return nil, fmt.Errorf("querying database for volume %s: %w", name, err) } diff --git a/test/e2e/volume_rm_test.go b/test/e2e/volume_rm_test.go index 7333ce1a47..28d45dc37a 100644 --- a/test/e2e/volume_rm_test.go +++ b/test/e2e/volume_rm_test.go @@ -114,4 +114,14 @@ var _ = Describe("Podman volume rm", func() { Expect(session).Should(ExitCleanly()) Expect(len(session.OutputToStringArray())).To(BeNumerically(">=", 2)) }) + + It("podman volume rm by unique partial name - case & underscore insensitive", func() { + volNames := []string{"test_volume", "test-volume", "test", "Test"} + for _, name := range volNames { + podmanTest.PodmanExitCleanly("volume", "create", name) + } + + podmanTest.PodmanExitCleanly("volume", "rm", volNames[0]) + podmanTest.PodmanExitCleanly("volume", "rm", volNames[2]) + }) })