diff --git a/core/chaincode/shim/chaincode.go b/core/chaincode/shim/chaincode.go index 19db11afd78..4c4a51ae943 100644 --- a/core/chaincode/shim/chaincode.go +++ b/core/chaincode/shim/chaincode.go @@ -420,8 +420,11 @@ var ( // CreateTable creates a new table given the table name and column definitions func (stub *ChaincodeStub) CreateTable(name string, columnDefinitions []*ColumnDefinition) error { + return createTableInternal(stub, name, columnDefinitions) +} - _, err := stub.getTable(name) +func createTableInternal(stub ChaincodeStubInterface, name string, columnDefinitions []*ColumnDefinition) error { + _, err := getTable(stub, name) if err == nil { return fmt.Errorf("CreateTable operation failed. Table %s already exists.", name) } @@ -490,11 +493,15 @@ func (stub *ChaincodeStub) CreateTable(name string, columnDefinitions []*ColumnD // GetTable returns the table for the specified table name or ErrTableNotFound // if the table does not exist. func (stub *ChaincodeStub) GetTable(tableName string) (*Table, error) { - return stub.getTable(tableName) + return getTable(stub, tableName) } // DeleteTable deletes an entire table and all associated rows. func (stub *ChaincodeStub) DeleteTable(tableName string) error { + return deleteTableInternal(stub, tableName) +} + +func deleteTableInternal(stub ChaincodeStubInterface, tableName string) error { tableNameKey, err := getTableNameKey(tableName) if err != nil { return err @@ -527,7 +534,7 @@ func (stub *ChaincodeStub) DeleteTable(tableName string) error { // false and a TableNotFoundError if the specified table name does not exist. // false and an error if there is an unexpected error condition. func (stub *ChaincodeStub) InsertRow(tableName string, row Row) (bool, error) { - return stub.insertRowInternal(tableName, row, false) + return insertRowInternal(stub, tableName, row, false) } // ReplaceRow updates the row in the specified table. @@ -537,11 +544,15 @@ func (stub *ChaincodeStub) InsertRow(tableName string, row Row) (bool, error) { // flase and a TableNotFoundError if the specified table name does not exist. // false and an error if there is an unexpected error condition. func (stub *ChaincodeStub) ReplaceRow(tableName string, row Row) (bool, error) { - return stub.insertRowInternal(tableName, row, true) + return insertRowInternal(stub, tableName, row, true) } // GetRow fetches a row from the specified table for the given key. func (stub *ChaincodeStub) GetRow(tableName string, key []Column) (Row, error) { + return getRowInternal(stub, tableName, key) +} + +func getRowInternal(stub ChaincodeStubInterface, tableName string, key []Column) (Row, error) { var row Row @@ -571,13 +582,17 @@ func (stub *ChaincodeStub) GetRow(tableName string, key []Column) (Row, error) { // also be called with A only to return all rows that have A and any value // for C and D as their key. func (stub *ChaincodeStub) GetRows(tableName string, key []Column) (<-chan Row, error) { + return getRowsInternal(stub, tableName, key) +} + +func getRowsInternal(stub ChaincodeStubInterface, tableName string, key []Column) (<-chan Row, error) { keyString, err := buildKeyString(tableName, key) if err != nil { return nil, err } - table, err := stub.getTable(tableName) + table, err := getTable(stub, tableName) if err != nil { return nil, err } @@ -630,6 +645,10 @@ func (stub *ChaincodeStub) GetRows(tableName string, key []Column) (<-chan Row, // DeleteRow deletes the row for the given key from the specified table. func (stub *ChaincodeStub) DeleteRow(tableName string, key []Column) error { + return deleteRowInternal(stub, tableName, key) +} + +func deleteRowInternal(stub ChaincodeStubInterface, tableName string, key []Column) error { keyString, err := buildKeyString(tableName, key) if err != nil { @@ -682,7 +701,7 @@ func (stub *ChaincodeStub) GetTxTimestamp() (*gp.Timestamp, error) { return stub.securityContext.TxTimestamp, nil } -func (stub *ChaincodeStub) getTable(tableName string) (*Table, error) { +func getTable(stub ChaincodeStubInterface, tableName string) (*Table, error) { tableName, err := getTableNameKey(tableName) if err != nil { @@ -808,7 +827,7 @@ func getKeyAndVerifyRow(table Table, row Row) ([]Column, error) { return keys, nil } -func (stub *ChaincodeStub) isRowPresent(tableName string, key []Column) (bool, error) { +func isRowPresent(stub ChaincodeStubInterface, tableName string, key []Column) (bool, error) { keyString, err := buildKeyString(tableName, key) if err != nil { return false, err @@ -829,9 +848,9 @@ func (stub *ChaincodeStub) isRowPresent(tableName string, key []Column) (bool, e // false and no error if a row already exists for the given key. // false and a TableNotFoundError if the specified table name does not exist. // false and an error if there is an unexpected error condition. -func (stub *ChaincodeStub) insertRowInternal(tableName string, row Row, update bool) (bool, error) { +func insertRowInternal(stub ChaincodeStubInterface, tableName string, row Row, update bool) (bool, error) { - table, err := stub.getTable(tableName) + table, err := getTable(stub, tableName) if err != nil { return false, err } @@ -841,7 +860,7 @@ func (stub *ChaincodeStub) insertRowInternal(tableName string, row Row, update b return false, err } - present, err := stub.isRowPresent(tableName, key) + present, err := isRowPresent(stub, tableName, key) if err != nil { return false, err } diff --git a/core/chaincode/shim/mockstub.go b/core/chaincode/shim/mockstub.go index 5fdd1c4d073..e72c61febc6 100644 --- a/core/chaincode/shim/mockstub.go +++ b/core/chaincode/shim/mockstub.go @@ -196,45 +196,60 @@ func (stub *MockStub) RangeQueryState(startKey, endKey string) (StateRangeQueryI return NewMockStateRangeQueryIterator(stub, startKey, endKey), nil } -// Not implemented +// CreateTable creates a new table given the table name and column definitions func (stub *MockStub) CreateTable(name string, columnDefinitions []*ColumnDefinition) error { - return nil + return createTableInternal(stub, name, columnDefinitions) } -// Not implemented +// GetTable returns the table for the specified table name or ErrTableNotFound +// if the table does not exist. func (stub *MockStub) GetTable(tableName string) (*Table, error) { - return nil, nil + return getTable(stub, tableName) } -// Not implemented +// DeleteTable deletes an entire table and all associated rows. func (stub *MockStub) DeleteTable(tableName string) error { - return nil + return deleteTableInternal(stub, tableName) } -// Not implemented +// InsertRow inserts a new row into the specified table. +// Returns - +// true and no error if the row is successfully inserted. +// false and no error if a row already exists for the given key. +// false and a TableNotFoundError if the specified table name does not exist. +// false and an error if there is an unexpected error condition. func (stub *MockStub) InsertRow(tableName string, row Row) (bool, error) { - return false, nil + return insertRowInternal(stub, tableName, row, false) } -// Not implemented +// ReplaceRow updates the row in the specified table. +// Returns - +// true and no error if the row is successfully updated. +// false and no error if a row does not exist the given key. +// flase and a TableNotFoundError if the specified table name does not exist. +// false and an error if there is an unexpected error condition. func (stub *MockStub) ReplaceRow(tableName string, row Row) (bool, error) { - return false, nil + return insertRowInternal(stub, tableName, row, true) } -// Not implemented +// GetRow fetches a row from the specified table for the given key. func (stub *MockStub) GetRow(tableName string, key []Column) (Row, error) { - var r Row - return r, nil + return getRowInternal(stub, tableName, key) } -// Not implemented +// GetRows returns multiple rows based on a partial key. For example, given table +// | A | B | C | D | +// where A, C and D are keys, GetRows can be called with [A, C] to return +// all rows that have A, C and any value for D as their key. GetRows could +// also be called with A only to return all rows that have A and any value +// for C and D as their key. func (stub *MockStub) GetRows(tableName string, key []Column) (<-chan Row, error) { - return nil, nil + return getRowsInternal(stub, tableName, key) } -// Not implemented +// DeleteRow deletes the row for the given key from the specified table. func (stub *MockStub) DeleteRow(tableName string, key []Column) error { - return nil + return deleteRowInternal(stub, tableName, key) } // Invokes a peered chaincode. diff --git a/core/chaincode/shim/mockstub_test.go b/core/chaincode/shim/mockstub_test.go index 39611c65780..d9ded212691 100644 --- a/core/chaincode/shim/mockstub_test.go +++ b/core/chaincode/shim/mockstub_test.go @@ -17,10 +17,88 @@ limitations under the License. package shim import ( + "errors" "fmt" "testing" ) +func createTable(stub ChaincodeStubInterface) error { + // Create table one + var columnDefsTableOne []*ColumnDefinition + columnOneTableOneDef := ColumnDefinition{Name: "colOneTableOne", + Type: ColumnDefinition_STRING, Key: true} + columnTwoTableOneDef := ColumnDefinition{Name: "colTwoTableOne", + Type: ColumnDefinition_INT32, Key: false} + columnThreeTableOneDef := ColumnDefinition{Name: "colThreeTableOne", + Type: ColumnDefinition_INT32, Key: false} + columnDefsTableOne = append(columnDefsTableOne, &columnOneTableOneDef) + columnDefsTableOne = append(columnDefsTableOne, &columnTwoTableOneDef) + columnDefsTableOne = append(columnDefsTableOne, &columnThreeTableOneDef) + return stub.CreateTable("tableOne", columnDefsTableOne) +} + +func insertRow(stub ChaincodeStubInterface, col1Val string, col2Val int32, col3Val int32) error { + var columns []*Column + col1 := Column{Value: &Column_String_{String_: col1Val}} + col2 := Column{Value: &Column_Int32{Int32: col2Val}} + col3 := Column{Value: &Column_Int32{Int32: col3Val}} + columns = append(columns, &col1) + columns = append(columns, &col2) + columns = append(columns, &col3) + + row := Row{Columns: columns} + ok, err := stub.InsertRow("tableOne", row) + if err != nil { + return fmt.Errorf("insertTableOne operation failed. %s", err) + } + if !ok { + return errors.New("insertTableOne operation failed. Row with given key already exists") + } + return nil +} + +func getRow(stub ChaincodeStubInterface, col1Val string) (Row, error) { + var columns []Column + col1 := Column{Value: &Column_String_{String_: col1Val}} + columns = append(columns, col1) + + row, err := stub.GetRow("tableOne", columns) + if err != nil { + return row, fmt.Errorf("getRowTableOne operation failed. %s", err) + } + + return row, nil +} + +func getRows(stub ChaincodeStubInterface, col1Val string) ([]Row, error) { + var columns []Column + + col1 := Column{Value: &Column_String_{String_: col1Val}} + columns = append(columns, col1) + + rowChannel, err := stub.GetRows("tableOne", columns) + if err != nil { + return nil, fmt.Errorf("getRows operation failed. %s", err) + } + + var rows []Row + for { + select { + case row, ok := <-rowChannel: + if !ok { + rowChannel = nil + } else { + rows = append(rows, row) + } + } + if rowChannel == nil { + break + } + } + + return rows, nil +} + func TestMockStateRangeQueryIterator(t *testing.T) { stub := NewMockStub("rangeTest", nil) stub.MockTransactionStart("init") @@ -50,3 +128,49 @@ func TestMockStateRangeQueryIterator(t *testing.T) { } } } + +func TestMockTable(t *testing.T) { + stub := NewMockStub("CreateTable", nil) + stub.MockTransactionStart("init") + + //create a table + if err := createTable(stub); err != nil { + t.FailNow() + } + + type rowType struct { + col1 string + col2 int32 + col3 int32 + } + + //add some rows + rows := []rowType{{"one", 1, 11}, {"two", 2, 22}, {"three", 3, 33}} + for _, r := range rows { + if err := insertRow(stub, r.col1, r.col2, r.col3); err != nil { + t.FailNow() + } + } + + //get one row + if r, err := getRow(stub, "one"); err != nil { + t.FailNow() + } else if len(r.Columns) != 3 || r.Columns[0].GetString_() != "one" || r.Columns[1].GetInt32() != 1 || r.Columns[2].GetInt32() != 11 { + t.FailNow() + } + + /** we know GetRows is buggy and need to be fixed. Enable this test + * when it is + //get all rows + if rs,err := getRows(stub,"one"); err != nil { + fmt.Printf("getRows err %s\n", err) + t.FailNow() + } else if len(rs) != 1 { + fmt.Printf("getRows returned len %d(expected 1)\n", len(rs)) + t.FailNow() + } else if len(rs[0].Columns) != 3 || rs[0].Columns[0].GetString_() != "one" || rs[0].Columns[1].GetInt32() != 1 || rs[0].Columns[2].GetInt32() != 11 { + fmt.Printf("getRows invaid row %v\n", rs[0]) + t.FailNow() + } + ***/ +}