github.com/hdt3213/godis@v1.2.9/database/hash.go (about)

     1  package database
     2  
     3  import (
     4  	Dict "github.com/hdt3213/godis/datastruct/dict"
     5  	"github.com/hdt3213/godis/interface/database"
     6  	"github.com/hdt3213/godis/interface/redis"
     7  	"github.com/hdt3213/godis/lib/utils"
     8  	"github.com/hdt3213/godis/redis/protocol"
     9  	"github.com/shopspring/decimal"
    10  	"strconv"
    11  	"strings"
    12  )
    13  
    14  func (db *DB) getAsDict(key string) (Dict.Dict, protocol.ErrorReply) {
    15  	entity, exists := db.GetEntity(key)
    16  	if !exists {
    17  		return nil, nil
    18  	}
    19  	dict, ok := entity.Data.(Dict.Dict)
    20  	if !ok {
    21  		return nil, &protocol.WrongTypeErrReply{}
    22  	}
    23  	return dict, nil
    24  }
    25  
    26  func (db *DB) getOrInitDict(key string) (dict Dict.Dict, inited bool, errReply protocol.ErrorReply) {
    27  	dict, errReply = db.getAsDict(key)
    28  	if errReply != nil {
    29  		return nil, false, errReply
    30  	}
    31  	inited = false
    32  	if dict == nil {
    33  		dict = Dict.MakeSimple()
    34  		db.PutEntity(key, &database.DataEntity{
    35  			Data: dict,
    36  		})
    37  		inited = true
    38  	}
    39  	return dict, inited, nil
    40  }
    41  
    42  // execHSet sets field in hash table
    43  func execHSet(db *DB, args [][]byte) redis.Reply {
    44  	// parse args
    45  	key := string(args[0])
    46  	field := string(args[1])
    47  	value := args[2]
    48  
    49  	// get or init entity
    50  	dict, _, errReply := db.getOrInitDict(key)
    51  	if errReply != nil {
    52  		return errReply
    53  	}
    54  
    55  	result := dict.Put(field, value)
    56  	db.addAof(utils.ToCmdLine3("hset", args...))
    57  	return protocol.MakeIntReply(int64(result))
    58  }
    59  
    60  func undoHSet(db *DB, args [][]byte) []CmdLine {
    61  	key := string(args[0])
    62  	field := string(args[1])
    63  	return rollbackHashFields(db, key, field)
    64  }
    65  
    66  // execHSetNX sets field in hash table only if field not exists
    67  func execHSetNX(db *DB, args [][]byte) redis.Reply {
    68  	// parse args
    69  	key := string(args[0])
    70  	field := string(args[1])
    71  	value := args[2]
    72  
    73  	dict, _, errReply := db.getOrInitDict(key)
    74  	if errReply != nil {
    75  		return errReply
    76  	}
    77  
    78  	result := dict.PutIfAbsent(field, value)
    79  	if result > 0 {
    80  		db.addAof(utils.ToCmdLine3("hsetnx", args...))
    81  
    82  	}
    83  	return protocol.MakeIntReply(int64(result))
    84  }
    85  
    86  // execHGet gets field value of hash table
    87  func execHGet(db *DB, args [][]byte) redis.Reply {
    88  	// parse args
    89  	key := string(args[0])
    90  	field := string(args[1])
    91  
    92  	// get entity
    93  	dict, errReply := db.getAsDict(key)
    94  	if errReply != nil {
    95  		return errReply
    96  	}
    97  	if dict == nil {
    98  		return &protocol.NullBulkReply{}
    99  	}
   100  
   101  	raw, exists := dict.Get(field)
   102  	if !exists {
   103  		return &protocol.NullBulkReply{}
   104  	}
   105  	value, _ := raw.([]byte)
   106  	return protocol.MakeBulkReply(value)
   107  }
   108  
   109  // execHExists checks if a hash field exists
   110  func execHExists(db *DB, args [][]byte) redis.Reply {
   111  	// parse args
   112  	key := string(args[0])
   113  	field := string(args[1])
   114  
   115  	// get entity
   116  	dict, errReply := db.getAsDict(key)
   117  	if errReply != nil {
   118  		return errReply
   119  	}
   120  	if dict == nil {
   121  		return protocol.MakeIntReply(0)
   122  	}
   123  
   124  	_, exists := dict.Get(field)
   125  	if exists {
   126  		return protocol.MakeIntReply(1)
   127  	}
   128  	return protocol.MakeIntReply(0)
   129  }
   130  
   131  // execHDel deletes a hash field
   132  func execHDel(db *DB, args [][]byte) redis.Reply {
   133  	// parse args
   134  	key := string(args[0])
   135  	fields := make([]string, len(args)-1)
   136  	fieldArgs := args[1:]
   137  	for i, v := range fieldArgs {
   138  		fields[i] = string(v)
   139  	}
   140  
   141  	// get entity
   142  	dict, errReply := db.getAsDict(key)
   143  	if errReply != nil {
   144  		return errReply
   145  	}
   146  	if dict == nil {
   147  		return protocol.MakeIntReply(0)
   148  	}
   149  
   150  	deleted := 0
   151  	for _, field := range fields {
   152  		result := dict.Remove(field)
   153  		deleted += result
   154  	}
   155  	if dict.Len() == 0 {
   156  		db.Remove(key)
   157  	}
   158  	if deleted > 0 {
   159  		db.addAof(utils.ToCmdLine3("hdel", args...))
   160  	}
   161  
   162  	return protocol.MakeIntReply(int64(deleted))
   163  }
   164  
   165  func undoHDel(db *DB, args [][]byte) []CmdLine {
   166  	key := string(args[0])
   167  	fields := make([]string, len(args)-1)
   168  	fieldArgs := args[1:]
   169  	for i, v := range fieldArgs {
   170  		fields[i] = string(v)
   171  	}
   172  	return rollbackHashFields(db, key, fields...)
   173  }
   174  
   175  // execHLen gets number of fields in hash table
   176  func execHLen(db *DB, args [][]byte) redis.Reply {
   177  	// parse args
   178  	key := string(args[0])
   179  
   180  	dict, errReply := db.getAsDict(key)
   181  	if errReply != nil {
   182  		return errReply
   183  	}
   184  	if dict == nil {
   185  		return protocol.MakeIntReply(0)
   186  	}
   187  	return protocol.MakeIntReply(int64(dict.Len()))
   188  }
   189  
   190  // execHStrlen Returns the string length of the value associated with field in the hash stored at key.
   191  // If the key or the field do not exist, 0 is returned.
   192  func execHStrlen(db *DB, args [][]byte) redis.Reply {
   193  	key := string(args[0])
   194  	field := string(args[1])
   195  
   196  	dict, errReply := db.getAsDict(key)
   197  	if errReply != nil {
   198  		return errReply
   199  	}
   200  	if dict == nil {
   201  		return protocol.MakeIntReply(0)
   202  	}
   203  
   204  	raw, exists := dict.Get(field)
   205  	if exists {
   206  		value, _ := raw.([]byte)
   207  		return protocol.MakeIntReply(int64(len(value)))
   208  	}
   209  	return protocol.MakeIntReply(0)
   210  }
   211  
   212  // execHMSet sets multi fields in hash table
   213  func execHMSet(db *DB, args [][]byte) redis.Reply {
   214  	// parse args
   215  	if len(args)%2 != 1 {
   216  		return protocol.MakeSyntaxErrReply()
   217  	}
   218  	key := string(args[0])
   219  	size := (len(args) - 1) / 2
   220  	fields := make([]string, size)
   221  	values := make([][]byte, size)
   222  	for i := 0; i < size; i++ {
   223  		fields[i] = string(args[2*i+1])
   224  		values[i] = args[2*i+2]
   225  	}
   226  
   227  	// get or init entity
   228  	dict, _, errReply := db.getOrInitDict(key)
   229  	if errReply != nil {
   230  		return errReply
   231  	}
   232  
   233  	// put data
   234  	for i, field := range fields {
   235  		value := values[i]
   236  		dict.Put(field, value)
   237  	}
   238  	db.addAof(utils.ToCmdLine3("hmset", args...))
   239  	return &protocol.OkReply{}
   240  }
   241  
   242  func undoHMSet(db *DB, args [][]byte) []CmdLine {
   243  	key := string(args[0])
   244  	size := (len(args) - 1) / 2
   245  	fields := make([]string, size)
   246  	for i := 0; i < size; i++ {
   247  		fields[i] = string(args[2*i+1])
   248  	}
   249  	return rollbackHashFields(db, key, fields...)
   250  }
   251  
   252  // execHMGet gets multi fields in hash table
   253  func execHMGet(db *DB, args [][]byte) redis.Reply {
   254  	key := string(args[0])
   255  	size := len(args) - 1
   256  	fields := make([]string, size)
   257  	for i := 0; i < size; i++ {
   258  		fields[i] = string(args[i+1])
   259  	}
   260  
   261  	// get entity
   262  	result := make([][]byte, size)
   263  	dict, errReply := db.getAsDict(key)
   264  	if errReply != nil {
   265  		return errReply
   266  	}
   267  	if dict == nil {
   268  		return protocol.MakeMultiBulkReply(result)
   269  	}
   270  
   271  	for i, field := range fields {
   272  		value, ok := dict.Get(field)
   273  		if !ok {
   274  			result[i] = nil
   275  		} else {
   276  			bytes, _ := value.([]byte)
   277  			result[i] = bytes
   278  		}
   279  	}
   280  	return protocol.MakeMultiBulkReply(result)
   281  }
   282  
   283  // execHKeys gets all field names in hash table
   284  func execHKeys(db *DB, args [][]byte) redis.Reply {
   285  	key := string(args[0])
   286  
   287  	dict, errReply := db.getAsDict(key)
   288  	if errReply != nil {
   289  		return errReply
   290  	}
   291  	if dict == nil {
   292  		return &protocol.EmptyMultiBulkReply{}
   293  	}
   294  
   295  	fields := make([][]byte, dict.Len())
   296  	i := 0
   297  	dict.ForEach(func(key string, val interface{}) bool {
   298  		fields[i] = []byte(key)
   299  		i++
   300  		return true
   301  	})
   302  	return protocol.MakeMultiBulkReply(fields[:i])
   303  }
   304  
   305  // execHVals gets all field value in hash table
   306  func execHVals(db *DB, args [][]byte) redis.Reply {
   307  	key := string(args[0])
   308  
   309  	// get entity
   310  	dict, errReply := db.getAsDict(key)
   311  	if errReply != nil {
   312  		return errReply
   313  	}
   314  	if dict == nil {
   315  		return &protocol.EmptyMultiBulkReply{}
   316  	}
   317  
   318  	values := make([][]byte, dict.Len())
   319  	i := 0
   320  	dict.ForEach(func(key string, val interface{}) bool {
   321  		values[i], _ = val.([]byte)
   322  		i++
   323  		return true
   324  	})
   325  	return protocol.MakeMultiBulkReply(values[:i])
   326  }
   327  
   328  // execHGetAll gets all key-value entries in hash table
   329  func execHGetAll(db *DB, args [][]byte) redis.Reply {
   330  	key := string(args[0])
   331  
   332  	// get entity
   333  	dict, errReply := db.getAsDict(key)
   334  	if errReply != nil {
   335  		return errReply
   336  	}
   337  	if dict == nil {
   338  		return &protocol.EmptyMultiBulkReply{}
   339  	}
   340  
   341  	size := dict.Len()
   342  	result := make([][]byte, size*2)
   343  	i := 0
   344  	dict.ForEach(func(key string, val interface{}) bool {
   345  		result[i] = []byte(key)
   346  		i++
   347  		result[i], _ = val.([]byte)
   348  		i++
   349  		return true
   350  	})
   351  	return protocol.MakeMultiBulkReply(result[:i])
   352  }
   353  
   354  // execHIncrBy increments the integer value of a hash field by the given number
   355  func execHIncrBy(db *DB, args [][]byte) redis.Reply {
   356  	key := string(args[0])
   357  	field := string(args[1])
   358  	rawDelta := string(args[2])
   359  	delta, err := strconv.ParseInt(rawDelta, 10, 64)
   360  	if err != nil {
   361  		return protocol.MakeErrReply("ERR value is not an integer or out of range")
   362  	}
   363  
   364  	dict, _, errReply := db.getOrInitDict(key)
   365  	if errReply != nil {
   366  		return errReply
   367  	}
   368  
   369  	value, exists := dict.Get(field)
   370  	if !exists {
   371  		dict.Put(field, args[2])
   372  		db.addAof(utils.ToCmdLine3("hincrby", args...))
   373  		return protocol.MakeBulkReply(args[2])
   374  	}
   375  	val, err := strconv.ParseInt(string(value.([]byte)), 10, 64)
   376  	if err != nil {
   377  		return protocol.MakeErrReply("ERR hash value is not an integer")
   378  	}
   379  	val += delta
   380  	bytes := []byte(strconv.FormatInt(val, 10))
   381  	dict.Put(field, bytes)
   382  	db.addAof(utils.ToCmdLine3("hincrby", args...))
   383  	return protocol.MakeBulkReply(bytes)
   384  }
   385  
   386  func undoHIncr(db *DB, args [][]byte) []CmdLine {
   387  	key := string(args[0])
   388  	field := string(args[1])
   389  	return rollbackHashFields(db, key, field)
   390  }
   391  
   392  // execHIncrByFloat increments the float value of a hash field by the given number
   393  func execHIncrByFloat(db *DB, args [][]byte) redis.Reply {
   394  	key := string(args[0])
   395  	field := string(args[1])
   396  	rawDelta := string(args[2])
   397  	delta, err := decimal.NewFromString(rawDelta)
   398  	if err != nil {
   399  		return protocol.MakeErrReply("ERR value is not a valid float")
   400  	}
   401  
   402  	// get or init entity
   403  	dict, _, errReply := db.getOrInitDict(key)
   404  	if errReply != nil {
   405  		return errReply
   406  	}
   407  
   408  	value, exists := dict.Get(field)
   409  	if !exists {
   410  		dict.Put(field, args[2])
   411  		return protocol.MakeBulkReply(args[2])
   412  	}
   413  	val, err := decimal.NewFromString(string(value.([]byte)))
   414  	if err != nil {
   415  		return protocol.MakeErrReply("ERR hash value is not a float")
   416  	}
   417  	result := val.Add(delta)
   418  	resultBytes := []byte(result.String())
   419  	dict.Put(field, resultBytes)
   420  	db.addAof(utils.ToCmdLine3("hincrbyfloat", args...))
   421  	return protocol.MakeBulkReply(resultBytes)
   422  }
   423  
   424  // execHRandField return a random field(or field-value) from the hash value stored at key.
   425  func execHRandField(db *DB, args [][]byte) redis.Reply {
   426  	key := string(args[0])
   427  	count := 1
   428  	withvalues := 0
   429  
   430  	if len(args) > 3 {
   431  		return protocol.MakeErrReply("ERR wrong number of arguments for 'hrandfield' command")
   432  	}
   433  
   434  	if len(args) == 3 {
   435  		if strings.ToLower(string(args[2])) == "withvalues" {
   436  			withvalues = 1
   437  		} else {
   438  			return protocol.MakeSyntaxErrReply()
   439  		}
   440  	}
   441  
   442  	if len(args) >= 2 {
   443  		count64, err := strconv.ParseInt(string(args[1]), 10, 64)
   444  		if err != nil {
   445  			return protocol.MakeErrReply("ERR value is not an integer or out of range")
   446  		}
   447  		count = int(count64)
   448  	}
   449  
   450  	dict, errReply := db.getAsDict(key)
   451  	if errReply != nil {
   452  		return errReply
   453  	}
   454  	if dict == nil {
   455  		return &protocol.EmptyMultiBulkReply{}
   456  	}
   457  
   458  	if count > 0 {
   459  		fields := dict.RandomDistinctKeys(count)
   460  		Numfield := len(fields)
   461  		if withvalues == 0 {
   462  			result := make([][]byte, Numfield)
   463  			for i, v := range fields {
   464  				result[i] = []byte(v)
   465  			}
   466  			return protocol.MakeMultiBulkReply(result)
   467  		} else {
   468  			result := make([][]byte, 2*Numfield)
   469  			for i, v := range fields {
   470  				result[2*i] = []byte(v)
   471  				raw, _ := dict.Get(v)
   472  				result[2*i+1] = raw.([]byte)
   473  			}
   474  			return protocol.MakeMultiBulkReply(result)
   475  		}
   476  	} else if count < 0 {
   477  		fields := dict.RandomKeys(-count)
   478  		Numfield := len(fields)
   479  		if withvalues == 0 {
   480  			result := make([][]byte, Numfield)
   481  			for i, v := range fields {
   482  				result[i] = []byte(v)
   483  			}
   484  			return protocol.MakeMultiBulkReply(result)
   485  		} else {
   486  			result := make([][]byte, 2*Numfield)
   487  			for i, v := range fields {
   488  				result[2*i] = []byte(v)
   489  				raw, _ := dict.Get(v)
   490  				result[2*i+1] = raw.([]byte)
   491  			}
   492  			return protocol.MakeMultiBulkReply(result)
   493  		}
   494  	}
   495  
   496  	// 'count' is 0 will reach.
   497  	return &protocol.EmptyMultiBulkReply{}
   498  }
   499  
   500  func init() {
   501  	registerCommand("HSet", execHSet, writeFirstKey, undoHSet, 4, flagWrite).
   502  		attachCommandExtra([]string{redisFlagWrite, redisFlagDenyOOM, redisFlagFast}, 1, 1, 1)
   503  	registerCommand("HSetNX", execHSetNX, writeFirstKey, undoHSet, 4, flagWrite).
   504  		attachCommandExtra([]string{redisFlagWrite, redisFlagDenyOOM, redisFlagFast}, 1, 1, 1)
   505  	registerCommand("HGet", execHGet, readFirstKey, nil, 3, flagReadOnly).
   506  		attachCommandExtra([]string{redisFlagReadonly, redisFlagFast}, 1, 1, 1)
   507  	registerCommand("HExists", execHExists, readFirstKey, nil, 3, flagReadOnly).
   508  		attachCommandExtra([]string{redisFlagReadonly, redisFlagFast}, 1, 1, 1)
   509  	registerCommand("HDel", execHDel, writeFirstKey, undoHDel, -3, flagWrite).
   510  		attachCommandExtra([]string{redisFlagWrite, redisFlagFast}, 1, 1, 1)
   511  	registerCommand("HLen", execHLen, readFirstKey, nil, 2, flagReadOnly).
   512  		attachCommandExtra([]string{redisFlagReadonly, redisFlagFast}, 1, 1, 1)
   513  	registerCommand("HStrlen", execHStrlen, readFirstKey, nil, 3, flagReadOnly).
   514  		attachCommandExtra([]string{redisFlagReadonly, redisFlagFast}, 1, 1, 1)
   515  	registerCommand("HMSet", execHMSet, writeFirstKey, undoHMSet, -4, flagWrite).
   516  		attachCommandExtra([]string{redisFlagWrite, redisFlagDenyOOM, redisFlagFast}, 1, 1, 1)
   517  	registerCommand("HMGet", execHMGet, readFirstKey, nil, -3, flagReadOnly).
   518  		attachCommandExtra([]string{redisFlagReadonly, redisFlagFast}, 1, 1, 1)
   519  	registerCommand("HGet", execHGet, readFirstKey, nil, -3, flagReadOnly).
   520  		attachCommandExtra([]string{redisFlagReadonly, redisFlagFast}, 1, 1, 1)
   521  	registerCommand("HKeys", execHKeys, readFirstKey, nil, 2, flagReadOnly).
   522  		attachCommandExtra([]string{redisFlagReadonly, redisFlagSortForScript}, 1, 1, 1)
   523  	registerCommand("HVals", execHVals, readFirstKey, nil, 2, flagReadOnly).
   524  		attachCommandExtra([]string{redisFlagReadonly, redisFlagSortForScript}, 1, 1, 1)
   525  	registerCommand("HGetAll", execHGetAll, readFirstKey, nil, 2, flagReadOnly).
   526  		attachCommandExtra([]string{redisFlagReadonly, redisFlagRandom}, 1, 1, 1)
   527  	registerCommand("HIncrBy", execHIncrBy, writeFirstKey, undoHIncr, 4, flagWrite).
   528  		attachCommandExtra([]string{redisFlagWrite, redisFlagDenyOOM, redisFlagFast}, 1, 1, 1)
   529  	registerCommand("HIncrByFloat", execHIncrByFloat, writeFirstKey, undoHIncr, 4, flagWrite).
   530  		attachCommandExtra([]string{redisFlagWrite, redisFlagDenyOOM, redisFlagFast}, 1, 1, 1)
   531  	registerCommand("HRandField", execHRandField, readFirstKey, nil, -2, flagReadOnly).
   532  		attachCommandExtra([]string{redisFlagRandom, redisFlagReadonly}, 1, 1, 1)
   533  }