-
Notifications
You must be signed in to change notification settings - Fork 74
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add cmd set #16
Add cmd set #16
Changes from all commits
be65547
bf71f9a
b0fcb75
61c63fd
5b4395f
ada791a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
package command | ||
|
||
// SAdd adds one or more elements to set. | ||
// SADD key elem [elem...] | ||
// For more information: https://redis.io/docs/latest/commands/sadd/ | ||
type SAdd struct { | ||
baseCmd | ||
key string | ||
elems []any | ||
} | ||
|
||
// parseSAdd parses SAdd command and creates *SAdd object. | ||
func parseSAdd(b baseCmd) (*SAdd, error) { | ||
cmd := &SAdd{baseCmd: b} | ||
if len(cmd.args) < 2 { | ||
return nil, ErrInvalidArgNum | ||
} | ||
cmd.key = string(cmd.args[0]) | ||
for _, arg := range cmd.args[1:] { | ||
cmd.elems = append(cmd.elems, arg) | ||
} | ||
return cmd, nil | ||
} | ||
|
||
func (cmd *SAdd) Run(w Writer, red Redka) (any, error) { | ||
n, err := red.Set().Add(cmd.key, cmd.elems...) | ||
if err != nil { | ||
w.WriteError(cmd.Error(err)) | ||
return nil, err | ||
} | ||
w.WriteInt(n) | ||
return n, nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package command | ||
|
||
import ( | ||
"reflect" | ||
"testing" | ||
) | ||
|
||
func TestParseSAdd(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
err error | ||
}{ | ||
{}, | ||
} | ||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
got, err := parseSAdd(tt.args.b) | ||
if (err != nil) != tt.wantErr { | ||
t.Errorf("parseSAdd() error = %v, wantErr %v", err, tt.wantErr) | ||
return | ||
} | ||
if !reflect.DeepEqual(got, tt.want) { | ||
t.Errorf("parseSAdd() got = %v, want %v", got, tt.want) | ||
} | ||
}) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package rset | ||
|
||
import ( | ||
"database/sql" | ||
"github.com/nalgeon/redka/internal/sqlx" | ||
) | ||
|
||
// DB is a database-backed set repository. | ||
// A set is a slice of elements associated with a key. | ||
// Use the set repository to work with individual sets | ||
type DB struct { | ||
*sqlx.DB[*Tx] | ||
} | ||
|
||
// New connects to the set repository. | ||
// Does not create the database schema. | ||
func New(db *sql.DB) *DB { | ||
d := sqlx.New(db, NewTx) | ||
return &DB{d} | ||
} | ||
|
||
func (d *DB) Add(key string, elems ...any) (int, error) { | ||
tx := NewTx(d.SQL) | ||
return tx.Add(key, elems...) | ||
Comment on lines
+23
to
+24
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
package rset | ||
|
||
import ( | ||
"database/sql" | ||
"github.com/nalgeon/redka/internal/core" | ||
"github.com/nalgeon/redka/internal/sqlx" | ||
"time" | ||
) | ||
|
||
const sqlAdd1 = ` | ||
insert into rkey (key, type, version, etime, mtime) | ||
values (:key, :type, :version, :etime, :mtime) | ||
on conflict (key) do update set | ||
version = version + 1, | ||
type = excluded.type, | ||
etime = excluded.etime, | ||
mtime = excluded.mtime | ||
;` | ||
|
||
const sqlAdd2 = `insert into rset (key_id, elem) | ||
values ((select id from rkey where key = :key), :value)` | ||
|
||
// Tx is a set repository transaction. | ||
type Tx struct { | ||
tx sqlx.Tx | ||
} | ||
|
||
func NewTx(tx sqlx.Tx) *Tx { | ||
return &Tx{tx} | ||
} | ||
|
||
// Add adds key elems to set. | ||
func (t *Tx) Add(key string, elems ...any) (int, error) { | ||
return t.add(key, elems...) | ||
|
||
} | ||
|
||
func (t *Tx) add(key string, elems ...any) (int, error) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is the need for this method? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I thought that we may need to reuse the same method for other commands, but I might be overengineering. I can remove it. |
||
now := time.Now() | ||
|
||
var args [][]any | ||
for _, elem := range elems { | ||
args = append(args, []any{ | ||
sql.Named("key", key), | ||
sql.Named("type", core.TypeSet), | ||
sql.Named("version", core.InitialVersion), | ||
sql.Named("elem", elem), | ||
sql.Named("etime", now), | ||
sql.Named("mtime", now.UnixMilli()), | ||
}) | ||
} | ||
|
||
for _, arg := range args { | ||
_, err := t.tx.Exec(sqlAdd1, arg) | ||
if err != nil { | ||
return 0, sqlx.TypedError(err) | ||
} | ||
_, err = t.tx.Exec(sqlAdd2, arg) | ||
if err != nil { | ||
return 0, err | ||
} | ||
} | ||
|
||
return len(args), nil | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's better not to rush things with the
command
package. Let's deal withrset
first.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't I implement the
internal/command
part?