commit 1757ba50d6a03e1cd0b294925bf94d8bf7f3c568
parent 5348e19880454920c387b3c1191716898cc5e79a
Author: lash <dev@holbrook.no>
Date: Sat, 9 Nov 2024 00:16:02 +0000
Implement postgres dumper
Diffstat:
4 files changed, 128 insertions(+), 3 deletions(-)
diff --git a/db/postgres/dump.go b/db/postgres/dump.go
@@ -1,12 +1,44 @@
package postgres
import (
+ "fmt"
"context"
- "errors"
"git.defalsify.org/vise.git/db"
)
func(pdb *pgDb) Dump(ctx context.Context, key []byte) (*db.Dumper, error) {
- return nil, errors.New("unimplemented")
+ tx, err := pdb.conn.BeginTx(ctx, defaultTxOptions)
+ if err != nil {
+ return nil, err
+ }
+
+ query := fmt.Sprintf("SELECT key, value FROM %s.kv_vise WHERE key >= $1 AND key < $2", pdb.schema)
+ rs, err := tx.Query(ctx, query, key, key[0])
+ if err != nil {
+ tx.Rollback(ctx)
+ return nil, err
+ }
+ defer rs.Close()
+
+ if rs.Next() {
+ r := rs.RawValues()
+ tx.Commit(ctx)
+ //tx.Rollback(ctx)
+ pdb.it = rs
+ pdb.itBase = key
+ return db.NewDumper(pdb.dumpFunc).WithFirst(r[0], r[1]), nil
+ }
+
+ return nil, db.NewErrNotFound(key)
+}
+
+func(pdb *pgDb) dumpFunc(ctx context.Context) ([]byte, []byte) {
+ if !pdb.it.Next() {
+ pdb.it = nil
+ pdb.itBase = nil
+ return nil, nil
+ }
+ r := pdb.it.RawValues()
+ return r[0], r[1]
}
diff --git a/db/postgres/dump_test.go b/db/postgres/dump_test.go
@@ -0,0 +1,91 @@
+package postgres
+
+import (
+ "bytes"
+ "context"
+ "encoding/base64"
+ "strings"
+ "testing"
+
+ pgxmock "github.com/pashagolub/pgxmock/v4"
+ "github.com/jackc/pgx/v5/pgtype"
+ "github.com/jackc/pgx/v5/pgconn"
+
+ "git.defalsify.org/vise.git/db"
+)
+
+func TestDumpPg(t *testing.T) {
+ ses := "xyzzy"
+
+ mock, err := pgxmock.NewPool()
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer mock.Close()
+
+ store := NewPgDb().WithConnection(mock).WithSchema("vvise")
+ store.SetPrefix(db.DATATYPE_USERDATA)
+ store.SetSession(ses)
+ ctx := context.Background()
+
+// store.SetPrefix(db.DATATYPE_USERDATA)
+// err = store.Put(ctx, []byte("bar"), []byte("inky"))
+// if err != nil {
+// t.Fatal(err)
+// }
+// err = store.Put(ctx, []byte("foobar"), []byte("pinky"))
+// if err != nil {
+// t.Fatal(err)
+// }
+// err = store.Put(ctx, []byte("foobarbaz"), []byte("blinky"))
+// if err != nil {
+// t.Fatal(err)
+// }
+// err = store.Put(ctx, []byte("xyzzy"), []byte("clyde"))
+// if err != nil {
+// t.Fatal(err)
+// }
+
+ typMap := pgtype.NewMap()
+ k := []byte("foo")
+ mockVfd := pgconn.FieldDescription{
+ Name: "value",
+ DataTypeOID: pgtype.ByteaOID,
+ Format: typMap.FormatCodeForOID(pgtype.ByteaOID),
+ }
+ mockKfd := pgconn.FieldDescription{
+ Name: "key",
+ DataTypeOID: pgtype.ByteaOID,
+ Format: typMap.FormatCodeForOID(pgtype.ByteaOID),
+ }
+ rows := pgxmock.NewRowsWithColumnDefinition(mockKfd, mockVfd)
+ //rows = rows.AddRow([]byte("bar"), []byte("inky"))
+ rows = rows.AddRow([]byte("foobar"), []byte("pinky"))
+ rows = rows.AddRow([]byte("foobarbaz"), []byte("blinky"))
+ //rows = rows.AddRow([]byte("xyzzy"), []byte("clyde"))
+
+ mock.ExpectBegin()
+ mock.ExpectQuery("SELECT key, value FROM vvise.kv_vise").WithArgs(k, k[0]).WillReturnRows(rows)
+ mock.ExpectCommit()
+
+ o, err := store.Dump(ctx, []byte("foo"))
+ if err != nil {
+ t.Fatal(err)
+ }
+ k, _ = o.Next(ctx)
+ br, err := base64.StdEncoding.DecodeString(strings.Trim(string(k), "\""))
+ if !bytes.Equal(br, []byte("foobar")) {
+ t.Fatalf("expected key 'foobar', got %s", br)
+ }
+
+ k, _ = o.Next(ctx)
+ br, err = base64.StdEncoding.DecodeString(strings.Trim(string(k), "\""))
+ if !bytes.Equal(br, []byte("foobarbaz")) {
+ t.Fatalf("expected key 'foobarbaz', got %s", br)
+ }
+
+ k, _ = o.Next(ctx)
+ if k != nil {
+ t.Fatalf("expected nil")
+ }
+}
diff --git a/db/postgres/pg.go b/db/postgres/pg.go
@@ -26,6 +26,8 @@ type pgDb struct {
schema string
prefix uint8
prepd bool
+ it pgx.Rows
+ itBase []byte
}
// NewpgDb creates a new Postgres backed Db implementation.
diff --git a/db/postgres/pg_test.go b/db/postgres/pg_test.go
@@ -55,7 +55,7 @@ func TestPutGetPg(t *testing.T) {
typMap := pgtype.NewMap()
k := []byte("foo")
- ks := append([]byte{db.DATATYPE_USERDATA}, []byte("xyzzy.foo")...)
+ ks := append([]byte{db.DATATYPE_USERDATA}, []byte("foo")...)
v := []byte("bar")
resInsert := pgxmock.NewResult("UPDATE", 1)
mock.ExpectBegin()