From 84b5c6c713b614fa84274eafa21f0aed7c57aaab Mon Sep 17 00:00:00 2001
From: Valentin Rothberg <vrothberg@redhat.com>
Date: Mon, 27 Feb 2023 13:06:52 +0100
Subject: [PATCH 1/9] sqlite: RewritePodConfig: update error message

Use the same error message as the boltdb backend.

[NO NEW TESTS NEEDED]

Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
---
 libpod/sqlite_state.go | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libpod/sqlite_state.go b/libpod/sqlite_state.go
index e03d7ad49b..a543523923 100644
--- a/libpod/sqlite_state.go
+++ b/libpod/sqlite_state.go
@@ -1186,7 +1186,7 @@ func (s *SQLiteState) RewritePodConfig(pod *Pod, newCfg *PodConfig) (defErr erro
 	}
 	if rows == 0 {
 		pod.valid = false
-		return define.ErrNoSuchPod
+		return fmt.Errorf("no pod with ID %s found in DB: %w", pod.ID(), define.ErrNoSuchPod)
 	}
 
 	if err := tx.Commit(); err != nil {

From e87014e444085a64411559f46ea38877938de8c4 Mon Sep 17 00:00:00 2001
From: Valentin Rothberg <vrothberg@redhat.com>
Date: Mon, 27 Feb 2023 16:54:13 +0100
Subject: [PATCH 2/9] sqlite: return correct error on pod-name conflict

I wasn't able to find a way to get error-checks working with the sqlite3
library with the time at hand.

[NO NEW TESTS NEEDED]

Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
---
 libpod/sqlite_state.go | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/libpod/sqlite_state.go b/libpod/sqlite_state.go
index a543523923..b86b648996 100644
--- a/libpod/sqlite_state.go
+++ b/libpod/sqlite_state.go
@@ -1470,6 +1470,19 @@ func (s *SQLiteState) AddPod(pod *Pod) (defErr error) {
 		}
 	}()
 
+	// TODO: explore whether there's a more idiomatic way to do error checks for the name.
+	// There is a sqlite3.ErrConstraintUnique error but I (vrothberg) couldn't find a way
+	// to work with the returned errors yet.
+	var check int
+	row := tx.QueryRow("SELECT 1 FROM PodConfig WHERE Name=?;", pod.Name())
+	if err := row.Scan(&check); err != nil {
+		if !errors.Is(err, sql.ErrNoRows) {
+			return fmt.Errorf("checking if pod name %s exists in database: %w", pod.ID(), err)
+		}
+	} else if check != 0 {
+		return fmt.Errorf("name \"%s\" is in use: %w", pod.Name(), define.ErrPodExists)
+	}
+
 	if _, err := tx.Exec("INSERT INTO IDNamespace VALUES (?);", pod.ID()); err != nil {
 		return fmt.Errorf("adding pod id to database: %w", err)
 	}

From 69ff04f7360f2a76a3255291c8bc00da62aea1dc Mon Sep 17 00:00:00 2001
From: Valentin Rothberg <vrothberg@redhat.com>
Date: Mon, 27 Feb 2023 16:55:18 +0100
Subject: [PATCH 3/9] sqlite: fix type rewriting container config

It's `UPDATE $NAME` not `UPDATE TABLE $NAME`.

[NO NEW TESTS NEEDED]

Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
---
 libpod/sqlite_state_internal.go | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libpod/sqlite_state_internal.go b/libpod/sqlite_state_internal.go
index 1128d01c77..358a15ec8c 100644
--- a/libpod/sqlite_state_internal.go
+++ b/libpod/sqlite_state_internal.go
@@ -318,7 +318,7 @@ func (s *SQLiteState) rewriteContainerConfig(ctr *Container, newCfg *ContainerCo
 		}
 	}()
 
-	results, err := tx.Exec("UPDATE TABLE ContainerConfig SET Name=?, JSON=? WHERE ID=?;", newCfg.Name, json, ctr.ID())
+	results, err := tx.Exec("UPDATE ContainerConfig SET Name=?, JSON=? WHERE ID=?;", newCfg.Name, json, ctr.ID())
 	if err != nil {
 		return fmt.Errorf("updating container config table with new configuration for container %s: %w", ctr.ID(), err)
 	}

From 01359457c44a468f2dbaa3613e9a8158b46dac4f Mon Sep 17 00:00:00 2001
From: Valentin Rothberg <vrothberg@redhat.com>
Date: Tue, 28 Feb 2023 09:55:11 +0100
Subject: [PATCH 4/9] sqlite: LookupVolume: wrap error

Wrap the error with the message expexted by the system tests.

[NO NEW TESTS NEEDED]

Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
---
 libpod/sqlite_state.go | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libpod/sqlite_state.go b/libpod/sqlite_state.go
index b86b648996..2ae010c08d 100644
--- a/libpod/sqlite_state.go
+++ b/libpod/sqlite_state.go
@@ -1477,7 +1477,7 @@ func (s *SQLiteState) AddPod(pod *Pod) (defErr error) {
 	row := tx.QueryRow("SELECT 1 FROM PodConfig WHERE Name=?;", pod.Name())
 	if err := row.Scan(&check); err != nil {
 		if !errors.Is(err, sql.ErrNoRows) {
-			return fmt.Errorf("checking if pod name %s exists in database: %w", pod.ID(), err)
+			return fmt.Errorf("checking if pod name %s exists in database: %w", pod.Name(), err)
 		}
 	} else if check != 0 {
 		return fmt.Errorf("name \"%s\" is in use: %w", pod.Name(), define.ErrPodExists)
@@ -2064,7 +2064,7 @@ func (s *SQLiteState) LookupVolume(name string) (*Volume, error) {
 		foundResult = true
 	}
 	if !foundResult {
-		return nil, define.ErrNoSuchVolume
+		return nil, fmt.Errorf("no volume with name %q found: %w", name, define.ErrNoSuchVolume)
 	}
 
 	vol := new(Volume)

From df88f546b69a8c2c524b73d86f51ec65bb3390f4 Mon Sep 17 00:00:00 2001
From: Valentin Rothberg <vrothberg@redhat.com>
Date: Tue, 28 Feb 2023 10:43:37 +0100
Subject: [PATCH 5/9] sqlite: LookupVolume: fix partial name match

A partial name match is tricky as we want it to be fast but also make
sure there's only one partial match iff there's no full one.

[NO NEW TESTS NEEDED] as it fixes a system test.

Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
---
 libpod/sqlite_state.go | 15 ++++++++-------
 1 file changed, 8 insertions(+), 7 deletions(-)

diff --git a/libpod/sqlite_state.go b/libpod/sqlite_state.go
index 2ae010c08d..499d1b966f 100644
--- a/libpod/sqlite_state.go
+++ b/libpod/sqlite_state.go
@@ -2046,24 +2046,25 @@ func (s *SQLiteState) LookupVolume(name string) (*Volume, error) {
 		return nil, define.ErrDBClosed
 	}
 
-	rows, err := s.conn.Query("SELECT JSON FROM VolumeConfig WHERE Name LIKE ?;", name+"%")
+	rows, err := s.conn.Query("SELECT Name, JSON FROM VolumeConfig WHERE Name LIKE ? ORDER BY LENGTH(Name) ASC;", name+"%")
 	if err != nil {
 		return nil, fmt.Errorf("querying database for volume %s: %w", name, err)
 	}
 	defer rows.Close()
 
-	var configJSON string
-	foundResult := false
+	var foundName, configJSON string
 	for rows.Next() {
-		if foundResult {
+		if foundName != "" {
 			return nil, fmt.Errorf("more than one result for volume name %s: %w", name, define.ErrVolumeExists)
 		}
-		if err := rows.Scan(&configJSON); err != nil {
+		if err := rows.Scan(&foundName, &configJSON); err != nil {
 			return nil, fmt.Errorf("retrieving volume %s config from database: %w", name, err)
 		}
-		foundResult = true
+		if foundName == name {
+			break
+		}
 	}
-	if !foundResult {
+	if foundName == "" {
 		return nil, fmt.Errorf("no volume with name %q found: %w", name, define.ErrNoSuchVolume)
 	}
 

From 86d12520e95e0b23b7fc272c6e1b66f4bd48ef92 Mon Sep 17 00:00:00 2001
From: Valentin Rothberg <vrothberg@redhat.com>
Date: Tue, 28 Feb 2023 11:03:52 +0100
Subject: [PATCH 6/9] sqlite: implement RewriteVolumeConfig

[NO NEW TESTS NEEDED]

Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
---
 libpod/sqlite_state.go | 70 ++++++++++++++++++++++--------------------
 1 file changed, 37 insertions(+), 33 deletions(-)

diff --git a/libpod/sqlite_state.go b/libpod/sqlite_state.go
index 499d1b966f..4dfc2415f0 100644
--- a/libpod/sqlite_state.go
+++ b/libpod/sqlite_state.go
@@ -1199,8 +1199,7 @@ func (s *SQLiteState) RewritePodConfig(pod *Pod, newCfg *PodConfig) (defErr erro
 // RewriteVolumeConfig rewrites a volume's configuration.
 // WARNING: This function is DANGEROUS. Do not use without reading the full
 // comment on this function in state.go.
-// TODO TODO TODO
-func (s *SQLiteState) RewriteVolumeConfig(volume *Volume, newCfg *VolumeConfig) error {
+func (s *SQLiteState) RewriteVolumeConfig(volume *Volume, newCfg *VolumeConfig) (defErr error) {
 	if !s.valid {
 		return define.ErrDBClosed
 	}
@@ -1209,38 +1208,43 @@ func (s *SQLiteState) RewriteVolumeConfig(volume *Volume, newCfg *VolumeConfig)
 		return define.ErrVolumeRemoved
 	}
 
+	json, err := json.Marshal(newCfg)
+	if err != nil {
+		return fmt.Errorf("error marshalling volume %s new config JSON: %w", volume.Name(), err)
+	}
+
+	tx, err := s.conn.Begin()
+	if err != nil {
+		return fmt.Errorf("beginning transaction to rewrite volume %s config: %w", volume.Name(), err)
+	}
+	defer func() {
+		if defErr != nil {
+			if err := tx.Rollback(); err != nil {
+				logrus.Errorf("Rolling back transaction to rewrite volume %s config: %v", volume.Name(), err)
+			}
+		}
+	}()
+
+	results, err := tx.Exec("UPDATE VolumeConfig SET Name=?, JSON=? WHERE ID=?;", newCfg.Name, json, volume.Name())
+	if err != nil {
+		return fmt.Errorf("updating volume config table with new configuration for volume %s: %w", volume.Name(), err)
+	}
+	rows, err := results.RowsAffected()
+	if err != nil {
+		return fmt.Errorf("retrieving volume %s config rewrite rows affected: %w", volume.Name(), err)
+	}
+	if rows == 0 {
+		volume.valid = false
+		return fmt.Errorf("no volume with name %q found in DB: %w", volume.Name(), define.ErrNoSuchVolume)
+	}
+
+	if err := tx.Commit(); err != nil {
+		return fmt.Errorf("committing transaction to rewrite volume %s config: %w", volume.Name(), err)
+	}
+
+	return nil
+
 	return define.ErrNotImplemented
-
-	// newCfgJSON, err := json.Marshal(newCfg)
-	// if err != nil {
-	// 	return fmt.Errorf("marshalling new configuration JSON for volume %q: %w", volume.Name(), err)
-	// }
-
-	// db, err := s.getDBCon()
-	// if err != nil {
-	// 	return err
-	// }
-	// defer s.deferredCloseDBCon(db)
-
-	// err = db.Update(func(tx *bolt.Tx) error {
-	// 	volBkt, err := getVolBucket(tx)
-	// 	if err != nil {
-	// 		return err
-	// 	}
-
-	// 	volDB := volBkt.Bucket([]byte(volume.Name()))
-	// 	if volDB == nil {
-	// 		volume.valid = false
-	// 		return fmt.Errorf("no volume with name %q found in DB: %w", volume.Name(), define.ErrNoSuchVolume)
-	// 	}
-
-	// 	if err := volDB.Put(configKey, newCfgJSON); err != nil {
-	// 		return fmt.Errorf("updating volume %q config JSON: %w", volume.Name(), err)
-	// 	}
-
-	// 	return nil
-	// })
-	// return err
 }
 
 // Pod retrieves a pod given its full ID

From 2342d1a314f3c930dd36dbb5ad628cd7bf42d2a0 Mon Sep 17 00:00:00 2001
From: Valentin Rothberg <vrothberg@redhat.com>
Date: Tue, 28 Feb 2023 13:21:06 +0100
Subject: [PATCH 7/9] sqlite: addContainer: add named volume only once

There's a unique constraint in the table, so we shouldn't add the same
volume more than once to the same container.

[NO NEW TESTS NEEDED] as it fixes an existing one.

Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
---
 libpod/sqlite_state_internal.go | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/libpod/sqlite_state_internal.go b/libpod/sqlite_state_internal.go
index 358a15ec8c..b13cc8b62e 100644
--- a/libpod/sqlite_state_internal.go
+++ b/libpod/sqlite_state_internal.go
@@ -399,9 +399,13 @@ func (s *SQLiteState) addContainer(ctr *Container) (defErr error) {
 			return fmt.Errorf("adding container dependency %s to database: %w", dep, err)
 		}
 	}
+	volMap := make(map[string]bool)
 	for _, vol := range ctr.config.NamedVolumes {
-		if _, err := tx.Exec("INSERT INTO ContainerVolume VALUES (?, ?);", ctr.ID(), vol.Name); err != nil {
-			return fmt.Errorf("adding container volume %s to database: %w", vol.Name, err)
+		if _, ok := volMap[vol.Name]; !ok {
+			if _, err := tx.Exec("INSERT INTO ContainerVolume VALUES (?, ?);", ctr.ID(), vol.Name); err != nil {
+				return fmt.Errorf("adding container volume %s to database: %w", vol.Name, err)
+			}
+			volMap[vol.Name] = true
 		}
 	}
 

From 38acab832df2a4e5ea24461a64b6b16f5a4d056f Mon Sep 17 00:00:00 2001
From: Valentin Rothberg <vrothberg@redhat.com>
Date: Tue, 28 Feb 2023 16:24:53 +0100
Subject: [PATCH 8/9] sqlite: remove dead code

Found by golangci-lint.

Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
---
 libpod/sqlite_state.go | 2 --
 1 file changed, 2 deletions(-)

diff --git a/libpod/sqlite_state.go b/libpod/sqlite_state.go
index 4dfc2415f0..881731f828 100644
--- a/libpod/sqlite_state.go
+++ b/libpod/sqlite_state.go
@@ -1243,8 +1243,6 @@ func (s *SQLiteState) RewriteVolumeConfig(volume *Volume, newCfg *VolumeConfig)
 	}
 
 	return nil
-
-	return define.ErrNotImplemented
 }
 
 // Pod retrieves a pod given its full ID

From 2c67ff5d401c7853448ee15383400e8e4b389559 Mon Sep 17 00:00:00 2001
From: Valentin Rothberg <vrothberg@redhat.com>
Date: Wed, 1 Mar 2023 11:30:20 +0100
Subject: [PATCH 9/9] sqlite: add container short ID to network aliases

Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
---
 libpod/sqlite_state_internal.go | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/libpod/sqlite_state_internal.go b/libpod/sqlite_state_internal.go
index b13cc8b62e..bc8b966c9e 100644
--- a/libpod/sqlite_state_internal.go
+++ b/libpod/sqlite_state_internal.go
@@ -339,6 +339,12 @@ func (s *SQLiteState) rewriteContainerConfig(ctr *Container, newCfg *ContainerCo
 }
 
 func (s *SQLiteState) addContainer(ctr *Container) (defErr error) {
+	for net := range ctr.config.Networks {
+		opts := ctr.config.Networks[net]
+		opts.Aliases = append(opts.Aliases, ctr.config.ID[:12])
+		ctr.config.Networks[net] = opts
+	}
+
 	configJSON, err := json.Marshal(ctr.config)
 	if err != nil {
 		return fmt.Errorf("marshalling container config json: %w", err)
@@ -503,6 +509,9 @@ func (s *SQLiteState) networkModify(ctr *Container, network string, opts types.P
 	}
 
 	if !disconnect {
+		if newCfg.Networks == nil {
+			newCfg.Networks = make(map[string]types.PerNetworkOptions)
+		}
 		newCfg.Networks[network] = opts
 	} else {
 		delete(newCfg.Networks, network)