package tablestore import ( "bytes" "fmt" "github.com/aliyun/aliyun-tablestore-go-sdk/tablestore/otsprotocol" "github.com/golang/protobuf/proto" "io" "io/ioutil" "math" "net/http" "reflect" "sort" ) const ( maxTableNameLength = 100 maxPrimaryKeyLength = 255 maxPrimaryKeyNum = 4 maxMultiDeleteRows = 100 ) type ColumnType int32 const ( ColumnType_STRING ColumnType = 1 ColumnType_INTEGER ColumnType = 2 ColumnType_BOOLEAN ColumnType = 3 ColumnType_DOUBLE ColumnType = 4 ColumnType_BINARY ColumnType = 5 ) const ( Version = "1.0" ApiVersion = "2015-12-31" xOtsDateFormat = "2006-01-02T15:04:05.123Z" xOtsInstanceName = "x-ots-instancename" xOtsRequestId = "x-ots-requestid" ) type ColumnValue struct { Type ColumnType Value interface{} } func (cv *ColumnValue) writeCellValue(w io.Writer) { writeTag(w, TAG_CELL_VALUE) if cv == nil { writeRawLittleEndian32(w, 1) writeRawByte(w, VT_AUTO_INCREMENT) return } switch cv.Type { case ColumnType_STRING: v := cv.Value.(string) writeRawLittleEndian32(w, int32(LITTLE_ENDIAN_32_SIZE+1+len(v))) // length + type + value writeRawByte(w, VT_STRING) writeRawLittleEndian32(w, int32(len(v))) writeBytes(w, []byte(v)) case ColumnType_INTEGER: v := cv.Value.(int64) writeRawLittleEndian32(w, int32(LITTLE_ENDIAN_64_SIZE+1)) writeRawByte(w, VT_INTEGER) writeRawLittleEndian64(w, v) case ColumnType_BOOLEAN: v := cv.Value.(bool) writeRawLittleEndian32(w, 2) writeRawByte(w, VT_BOOLEAN) writeBoolean(w, v) case ColumnType_DOUBLE: v := cv.Value.(float64) writeRawLittleEndian32(w, LITTLE_ENDIAN_64_SIZE+1) writeRawByte(w, VT_DOUBLE) writeDouble(w, v) case ColumnType_BINARY: v := cv.Value.([]byte) writeRawLittleEndian32(w, int32(LITTLE_ENDIAN_32_SIZE+1+len(v))) // length + type + value writeRawByte(w, VT_BLOB) writeRawLittleEndian32(w, int32(len(v))) writeBytes(w, v) } } func (cv *ColumnValue) writeCellValueWithoutLengthPrefix() []byte { var b bytes.Buffer w := &b switch cv.Type { case ColumnType_STRING: v := cv.Value.(string) writeRawByte(w, VT_STRING) writeRawLittleEndian32(w, int32(len(v))) writeBytes(w, []byte(v)) case ColumnType_INTEGER: v := cv.Value.(int64) writeRawByte(w, VT_INTEGER) writeRawLittleEndian64(w, v) case ColumnType_BOOLEAN: v := cv.Value.(bool) writeRawByte(w, VT_BOOLEAN) writeBoolean(w, v) case ColumnType_DOUBLE: v := cv.Value.(float64) writeRawByte(w, VT_DOUBLE) writeDouble(w, v) case ColumnType_BINARY: v := cv.Value.([]byte) writeRawByte(w, VT_BLOB) writeRawLittleEndian32(w, int32(len(v))) writeBytes(w, v) } return b.Bytes() } func (cv *ColumnValue) getCheckSum(crc byte) byte { if cv == nil { return crc8Byte(crc, VT_AUTO_INCREMENT) } switch cv.Type { case ColumnType_STRING: v := cv.Value.(string) crc = crc8Byte(crc, VT_STRING) crc = crc8Int32(crc, int32(len(v))) crc = crc8Bytes(crc, []byte(v)) case ColumnType_INTEGER: v := cv.Value.(int64) crc = crc8Byte(crc, VT_INTEGER) crc = crc8Int64(crc, v) case ColumnType_BOOLEAN: v := cv.Value.(bool) crc = crc8Byte(crc, VT_BOOLEAN) if v { crc = crc8Byte(crc, 0x1) } else { crc = crc8Byte(crc, 0x0) } case ColumnType_DOUBLE: v := cv.Value.(float64) crc = crc8Byte(crc, VT_DOUBLE) crc = crc8Int64(crc, int64(math.Float64bits(v))) case ColumnType_BINARY: v := cv.Value.([]byte) crc = crc8Byte(crc, VT_BLOB) crc = crc8Int32(crc, int32(len(v))) crc = crc8Bytes(crc, v) } return crc } type Column struct { Name []byte Value ColumnValue Type byte Timestamp int64 HasType bool HasTimestamp bool IgnoreValue bool } func NewColumn(name []byte, value interface{}) *Column { v := &Column{} v.Name = name if value != nil { t := reflect.TypeOf(value) switch t.Kind() { case reflect.String: v.Value.Type = ColumnType_STRING case reflect.Int64: v.Value.Type = ColumnType_INTEGER case reflect.Bool: v.Value.Type = ColumnType_BOOLEAN case reflect.Float64: v.Value.Type = ColumnType_DOUBLE case reflect.Slice: v.Value.Type = ColumnType_BINARY default: panic(errInvalidInput) } v.Value.Value = value } return v } func (c *Column) toPlainBufferCell(ignoreValue bool) *PlainBufferCell { cell := &PlainBufferCell{} cell.cellName = c.Name cell.ignoreValue = ignoreValue if ignoreValue == false { cell.cellValue = &c.Value } if c.HasType { cell.hasCellType = c.HasType cell.cellType = byte(c.Type) } if c.HasTimestamp { cell.hasCellTimestamp = c.HasTimestamp cell.cellTimestamp = c.Timestamp } return cell } type PrimaryKeyColumnInner struct { Name []byte Type otsprotocol.PrimaryKeyType Value interface{} } func NewPrimaryKeyColumnINF_MAX(name []byte) *PrimaryKeyColumnInner { v := &PrimaryKeyColumnInner{} v.Name = name v.Type = 0 v.Value = "INF_MAX" return v } func NewPrimaryKeyColumnINF_MIN(name []byte) *PrimaryKeyColumnInner { v := &PrimaryKeyColumnInner{} v.Name = name v.Type = 0 v.Value = "INF_MIN" return v } func NewPrimaryKeyColumnAuto_Increment(name []byte) *PrimaryKeyColumnInner { v := &PrimaryKeyColumnInner{} v.Name = name v.Type = 0 v.Value = "AUTO_INCRMENT" return v } func NewPrimaryKeyColumn(name []byte, value interface{}, option PrimaryKeyOption) *PrimaryKeyColumnInner { if option == NONE { v := &PrimaryKeyColumnInner{} v.Name = name t := reflect.TypeOf(value) switch t.Kind() { case reflect.String: v.Type = otsprotocol.PrimaryKeyType_STRING case reflect.Int64: v.Type = otsprotocol.PrimaryKeyType_INTEGER case reflect.Slice: v.Type = otsprotocol.PrimaryKeyType_BINARY default: panic(errInvalidInput) } v.Value = value return v } else if option == AUTO_INCREMENT { return NewPrimaryKeyColumnAuto_Increment(name) } else if option == MIN { return NewPrimaryKeyColumnINF_MIN(name) } else { return NewPrimaryKeyColumnINF_MAX(name) } } func (pkc *PrimaryKeyColumnInner) toColumnValue() *ColumnValue { switch pkc.Type { case otsprotocol.PrimaryKeyType_INTEGER: return &ColumnValue{ColumnType_INTEGER, pkc.Value} case otsprotocol.PrimaryKeyType_STRING: return &ColumnValue{ColumnType_STRING, pkc.Value} case otsprotocol.PrimaryKeyType_BINARY: return &ColumnValue{ColumnType_BINARY, pkc.Value} } return nil } func (pkc *PrimaryKeyColumnInner) toPlainBufferCell() *PlainBufferCell { cell := &PlainBufferCell{} cell.cellName = pkc.Name cell.cellValue = pkc.toColumnValue() return cell } func (pkc *PrimaryKeyColumnInner) isInfMin() bool { if pkc.Type == 0 && pkc.Value.(string) == "INF_MIN" { return true } return false } func (pkc *PrimaryKeyColumnInner) isInfMax() bool { if pkc.Type == 0 && pkc.Value.(string) == "INF_MAX" { return true } return false } func (pkc *PrimaryKeyColumnInner) isAutoInc() bool { if pkc.Type == 0 && pkc.Value.(string) == "AUTO_INCRMENT" { return true } return false } func (pkc *PrimaryKeyColumnInner) getCheckSum(crc byte) byte { if pkc.isInfMin() { return crc8Byte(crc, VT_INF_MIN) } if pkc.isInfMax() { return crc8Byte(crc, VT_INF_MAX) } if pkc.isAutoInc() { return crc8Byte(crc, VT_AUTO_INCREMENT) } return pkc.toColumnValue().getCheckSum(crc) } func (pkc *PrimaryKeyColumnInner) writePrimaryKeyColumn(w io.Writer) { writeTag(w, TAG_CELL) writeCellName(w, []byte(pkc.Name)) if pkc.isInfMin() { writeTag(w, TAG_CELL_VALUE) writeRawLittleEndian32(w, 1) writeRawByte(w, VT_INF_MIN) return } if pkc.isInfMax() { writeTag(w, TAG_CELL_VALUE) writeRawLittleEndian32(w, 1) writeRawByte(w, VT_INF_MAX) return } if pkc.isAutoInc() { writeTag(w, TAG_CELL_VALUE) writeRawLittleEndian32(w, 1) writeRawByte(w, VT_AUTO_INCREMENT) return } pkc.toColumnValue().writeCellValue(w) } type PrimaryKey2 struct { primaryKey []*PrimaryKeyColumnInner } func (pk *PrimaryKey) Build(isDelete bool) []byte { var b bytes.Buffer writeHeader(&b) writeTag(&b, TAG_ROW_PK) rowChecksum := byte(0x0) var cellChecksum byte for _, column := range pk.PrimaryKeys { primaryKeyColumn := NewPrimaryKeyColumn([]byte(column.ColumnName), column.Value, column.PrimaryKeyOption) cellChecksum = crc8Bytes(byte(0x0), []byte(primaryKeyColumn.Name)) cellChecksum = primaryKeyColumn.getCheckSum(cellChecksum) rowChecksum = crc8Byte(rowChecksum, cellChecksum) primaryKeyColumn.writePrimaryKeyColumn(&b) writeTag(&b, TAG_CELL_CHECKSUM) writeRawByte(&b, cellChecksum) } // 没有deleteMarker, 要与0x0做crc. if isDelete { writeTag(&b, TAG_DELETE_ROW_MARKER) rowChecksum = crc8Byte(rowChecksum, byte(0x1)) } else { rowChecksum = crc8Byte(rowChecksum, byte(0x0)) } writeTag(&b, TAG_ROW_CHECKSUM) writeRawByte(&b, rowChecksum) return b.Bytes() } type RowPutChange struct { primaryKey []*PrimaryKeyColumnInner columnsToPut []*Column } type RowUpdateChange struct { primaryKey []*PrimaryKeyColumnInner columnsToUpdate []*Column } func (rpc *RowPutChange) Build() []byte { pkCells := make([]*PlainBufferCell, len(rpc.primaryKey)) for i, pkc := range rpc.primaryKey { pkCells[i] = pkc.toPlainBufferCell() } cells := make([]*PlainBufferCell, len(rpc.columnsToPut)) for i, c := range rpc.columnsToPut { cells[i] = c.toPlainBufferCell(false) } row := &PlainBufferRow{ primaryKey: pkCells, cells: cells} var b bytes.Buffer row.writeRowWithHeader(&b) return b.Bytes() } func (ruc *RowUpdateChange) Build() []byte { pkCells := make([]*PlainBufferCell, len(ruc.primaryKey)) for i, pkc := range ruc.primaryKey { pkCells[i] = pkc.toPlainBufferCell() } cells := make([]*PlainBufferCell, len(ruc.columnsToUpdate)) for i, c := range ruc.columnsToUpdate { cells[i] = c.toPlainBufferCell(c.IgnoreValue) } row := &PlainBufferRow{ primaryKey: pkCells, cells: cells} var b bytes.Buffer row.writeRowWithHeader(&b) return b.Bytes() } const ( MaxValue = "_get_range_max" MinValue = "_get_range_min" ) func (comparatorType *ComparatorType) ConvertToPbComparatorType() otsprotocol.ComparatorType { switch *comparatorType { case CT_EQUAL: return otsprotocol.ComparatorType_CT_EQUAL case CT_NOT_EQUAL: return otsprotocol.ComparatorType_CT_NOT_EQUAL case CT_GREATER_THAN: return otsprotocol.ComparatorType_CT_GREATER_THAN case CT_GREATER_EQUAL: return otsprotocol.ComparatorType_CT_GREATER_EQUAL case CT_LESS_THAN: return otsprotocol.ComparatorType_CT_LESS_THAN default: return otsprotocol.ComparatorType_CT_LESS_EQUAL } } func (columnType DefinedColumnType) ConvertToPbDefinedColumnType() otsprotocol.DefinedColumnType { switch columnType { case DefinedColumn_INTEGER: return otsprotocol.DefinedColumnType_DCT_INTEGER case DefinedColumn_DOUBLE: return otsprotocol.DefinedColumnType_DCT_DOUBLE case DefinedColumn_BOOLEAN: return otsprotocol.DefinedColumnType_DCT_BOOLEAN case DefinedColumn_STRING: return otsprotocol.DefinedColumnType_DCT_STRING default: return otsprotocol.DefinedColumnType_DCT_BLOB } } func (loType *LogicalOperator) ConvertToPbLoType() otsprotocol.LogicalOperator { switch *loType { case LO_NOT: return otsprotocol.LogicalOperator_LO_NOT case LO_AND: return otsprotocol.LogicalOperator_LO_AND default: return otsprotocol.LogicalOperator_LO_OR } } func ConvertToPbCastType(variantType VariantType) *otsprotocol.VariantType { switch variantType { case Variant_INTEGER: return otsprotocol.VariantType_VT_INTEGER.Enum() case Variant_DOUBLE: return otsprotocol.VariantType_VT_DOUBLE.Enum() case Variant_STRING: return otsprotocol.VariantType_VT_STRING.Enum() default: panic("invalid VariantType") } } func NewValueTransferRule(regex string, vt VariantType) *ValueTransferRule{ return &ValueTransferRule{Regex: regex, Cast_type: vt} } func NewSingleColumnValueRegexFilter(columnName string, comparator ComparatorType, rule *ValueTransferRule, value interface{}) *SingleColumnCondition { return &SingleColumnCondition{ColumnName: &columnName, Comparator: &comparator, ColumnValue: value, TransferRule: rule} } func NewSingleColumnValueFilter(condition *SingleColumnCondition) *otsprotocol.SingleColumnValueFilter { filter := new(otsprotocol.SingleColumnValueFilter) comparatorType := condition.Comparator.ConvertToPbComparatorType() filter.Comparator = &comparatorType filter.ColumnName = condition.ColumnName col := NewColumn([]byte(*condition.ColumnName), condition.ColumnValue) filter.ColumnValue = col.toPlainBufferCell(false).cellValue.writeCellValueWithoutLengthPrefix() filter.FilterIfMissing = proto.Bool(condition.FilterIfMissing) filter.LatestVersionOnly = proto.Bool(condition.LatestVersionOnly) if condition.TransferRule != nil { filter.ValueTransRule = &otsprotocol.ValueTransferRule{ Regex: proto.String(condition.TransferRule.Regex), CastType: ConvertToPbCastType(condition.TransferRule.Cast_type) } } return filter } func NewCompositeFilter(filters []ColumnFilter, lo LogicalOperator) *otsprotocol.CompositeColumnValueFilter { ccvfilter := new(otsprotocol.CompositeColumnValueFilter) combinator := lo.ConvertToPbLoType() ccvfilter.Combinator = &combinator for _, cf := range filters { filter := cf.ToFilter() ccvfilter.SubFilters = append(ccvfilter.SubFilters, filter) } return ccvfilter } func NewPaginationFilter(filter *PaginationFilter) *otsprotocol.ColumnPaginationFilter { pageFilter := new(otsprotocol.ColumnPaginationFilter) pageFilter.Offset = proto.Int32(filter.Offset) pageFilter.Limit = proto.Int32(filter.Limit) return pageFilter } func (otsClient *TableStoreClient) postReq(req *http.Request, url string) ([]byte, error, string) { resp, err := otsClient.httpClient.Do(req) if err != nil { return nil, err, "" } defer resp.Body.Close() reqId := getRequestId(resp) body, err := ioutil.ReadAll(resp.Body) if err != nil { return nil, err, reqId } if (resp.StatusCode >= 200 && resp.StatusCode < 300) == false { var retErr *OtsError perr := new(otsprotocol.Error) errUm := proto.Unmarshal(body, perr) if errUm != nil { retErr = rawHttpToOtsError(resp.StatusCode, body, reqId) } else { retErr = pbErrToOtsError(perr, reqId) } return nil, retErr, reqId } return body, nil, reqId } func rawHttpToOtsError(code int, body []byte, reqId string) *OtsError { oerr := &OtsError{ Message: string(body), RequestId: reqId, } if code >= 500 && code < 600 { oerr.Code = SERVER_UNAVAILABLE } else { oerr.Code = OTS_CLIENT_UNKNOWN } return oerr } func pbErrToOtsError(pbErr *otsprotocol.Error, reqId string) *OtsError { return &OtsError{ Code: pbErr.GetCode(), Message: pbErr.GetMessage(), RequestId: reqId, } } func getRequestId(response *http.Response) string { if response == nil || response.Header == nil { return "" } return response.Header.Get(xOtsRequestId) } func buildRowPutChange(primarykey *PrimaryKey, columns []AttributeColumn) *RowPutChange { row := new(RowPutChange) row.primaryKey = make([]*PrimaryKeyColumnInner, len(primarykey.PrimaryKeys)) for i, p := range primarykey.PrimaryKeys { row.primaryKey[i] = NewPrimaryKeyColumn([]byte(p.ColumnName), p.Value, p.PrimaryKeyOption) } row.columnsToPut = make([]*Column, len(columns)) for i, p := range columns { row.columnsToPut[i] = NewColumn([]byte(p.ColumnName), p.Value) if p.Timestamp != 0 { row.columnsToPut[i].HasTimestamp = true row.columnsToPut[i].Timestamp = p.Timestamp } } return row } func buildRowUpdateChange(primarykey *PrimaryKey, columns []ColumnToUpdate) *RowUpdateChange { row := new(RowUpdateChange) row.primaryKey = make([]*PrimaryKeyColumnInner, len(primarykey.PrimaryKeys)) for i, p := range primarykey.PrimaryKeys { row.primaryKey[i] = NewPrimaryKeyColumn([]byte(p.ColumnName), p.Value, p.PrimaryKeyOption) } row.columnsToUpdate = make([]*Column, len(columns)) for i, p := range columns { row.columnsToUpdate[i] = NewColumn([]byte(p.ColumnName), p.Value) row.columnsToUpdate[i].HasTimestamp = p.HasTimestamp row.columnsToUpdate[i].HasType = p.HasType row.columnsToUpdate[i].Type = p.Type row.columnsToUpdate[i].Timestamp = p.Timestamp row.columnsToUpdate[i].IgnoreValue = p.IgnoreValue } return row } func (condition *RowCondition) buildCondition() *otsprotocol.RowExistenceExpectation { switch condition.RowExistenceExpectation { case RowExistenceExpectation_IGNORE: return otsprotocol.RowExistenceExpectation_IGNORE.Enum() case RowExistenceExpectation_EXPECT_EXIST: return otsprotocol.RowExistenceExpectation_EXPECT_EXIST.Enum() case RowExistenceExpectation_EXPECT_NOT_EXIST: return otsprotocol.RowExistenceExpectation_EXPECT_NOT_EXIST.Enum() } panic(errInvalidInput) } // build primary key for create table, put row, delete row and update row // value only support int64,string,[]byte or you will get panic func buildPrimaryKey(primaryKeyName string, value interface{}) *PrimaryKeyColumn { // Todo: validate the input return &PrimaryKeyColumn{ColumnName: primaryKeyName, Value: value, PrimaryKeyOption: NONE} } // value only support int64,string,bool,float64,[]byte. other type will get panic func (rowchange *PutRowChange) AddColumn(columnName string, value interface{}) { // Todo: validate the input column := &AttributeColumn{ColumnName: columnName, Value: value} rowchange.Columns = append(rowchange.Columns, *column) } func (rowchange *PutRowChange) SetReturnPk() { rowchange.ReturnType = ReturnType(ReturnType_RT_PK) } func (rowchange *UpdateRowChange) SetReturnIncrementValue() { rowchange.ReturnType = ReturnType(ReturnType_RT_AFTER_MODIFY) } func (rowchange *UpdateRowChange) AppendIncrementColumnToReturn(name string) { rowchange.ColumnNamesToReturn = append(rowchange.ColumnNamesToReturn, name) } // value only support int64,string,bool,float64,[]byte. other type will get panic func (rowchange *PutRowChange) AddColumnWithTimestamp(columnName string, value interface{}, timestamp int64) { // Todo: validate the input column := &AttributeColumn{ColumnName: columnName, Value: value} column.Timestamp = timestamp rowchange.Columns = append(rowchange.Columns, *column) } func (pk *PrimaryKey) AddPrimaryKeyColumn(primaryKeyName string, value interface{}) { pk.PrimaryKeys = append(pk.PrimaryKeys, buildPrimaryKey(primaryKeyName, value)) } func (pk *PrimaryKey) AddPrimaryKeyColumnWithAutoIncrement(primaryKeyName string) { pk.PrimaryKeys = append(pk.PrimaryKeys, &PrimaryKeyColumn{ColumnName: primaryKeyName, PrimaryKeyOption: AUTO_INCREMENT}) } func (pk *PrimaryKey) AddPrimaryKeyColumnWithMinValue(primaryKeyName string) { pk.PrimaryKeys = append(pk.PrimaryKeys, &PrimaryKeyColumn{ColumnName: primaryKeyName, PrimaryKeyOption: MIN}) } // Only used for range query func (pk *PrimaryKey) AddPrimaryKeyColumnWithMaxValue(primaryKeyName string) { pk.PrimaryKeys = append(pk.PrimaryKeys, &PrimaryKeyColumn{ColumnName: primaryKeyName, PrimaryKeyOption: MAX}) } func (rowchange *PutRowChange) SetCondition(rowExistenceExpectation RowExistenceExpectation) { rowchange.Condition = &RowCondition{RowExistenceExpectation: rowExistenceExpectation} } func (rowchange *DeleteRowChange) SetCondition(rowExistenceExpectation RowExistenceExpectation) { rowchange.Condition = &RowCondition{RowExistenceExpectation: rowExistenceExpectation} } func (Criteria *SingleRowQueryCriteria) SetFilter(filter ColumnFilter) { Criteria.Filter = filter } func (Criteria *MultiRowQueryCriteria) SetFilter(filter ColumnFilter) { Criteria.Filter = filter } func NewSingleColumnCondition(columnName string, comparator ComparatorType, value interface{}) *SingleColumnCondition { return &SingleColumnCondition{ColumnName: &columnName, Comparator: &comparator, ColumnValue: value} } func NewCompositeColumnCondition(lo LogicalOperator) *CompositeColumnValueFilter { return &CompositeColumnValueFilter{Operator: lo} } func (rowchange *PutRowChange) SetColumnCondition(condition ColumnFilter) { rowchange.Condition.ColumnCondition = condition } func (rowchange *UpdateRowChange) SetCondition(rowExistenceExpectation RowExistenceExpectation) { rowchange.Condition = &RowCondition{RowExistenceExpectation: rowExistenceExpectation} } func (rowchange *UpdateRowChange) SetColumnCondition(condition ColumnFilter) { rowchange.Condition.ColumnCondition = condition } func (rowchange *DeleteRowChange) SetColumnCondition(condition ColumnFilter) { rowchange.Condition.ColumnCondition = condition } func (meta *TableMeta) AddPrimaryKeyColumn(name string, keyType PrimaryKeyType) { meta.SchemaEntry = append(meta.SchemaEntry, &PrimaryKeySchema{Name: &name, Type: &keyType}) } func (meta *TableMeta) AddPrimaryKeyColumnOption(name string, keyType PrimaryKeyType, keyOption PrimaryKeyOption) { meta.SchemaEntry = append(meta.SchemaEntry, &PrimaryKeySchema{Name: &name, Type: &keyType, Option: &keyOption}) } // value only support int64,string,bool,float64,[]byte. other type will get panic func (rowchange *UpdateRowChange) PutColumn(columnName string, value interface{}) { // Todo: validate the input column := &ColumnToUpdate{ColumnName: columnName, Value: value} rowchange.Columns = append(rowchange.Columns, *column) } func (rowchange *UpdateRowChange) DeleteColumn(columnName string) { // Todo: validate the input column := &ColumnToUpdate{ColumnName: columnName, Value: nil, Type: DELETE_ALL_VERSION, HasType: true, IgnoreValue: true} rowchange.Columns = append(rowchange.Columns, *column) } func (rowchange *UpdateRowChange) DeleteColumnWithTimestamp(columnName string, timestamp int64) { // Todo: validate the input column := &ColumnToUpdate{ColumnName: columnName, Value: nil, Type: DELETE_ONE_VERSION, HasType: true, HasTimestamp: true, Timestamp: timestamp, IgnoreValue: true} rowchange.Columns = append(rowchange.Columns, *column) } func (rowchange *UpdateRowChange) IncrementColumn(columnName string, value int64) { // Todo: validate the input column := &ColumnToUpdate{ColumnName: columnName, Value: value, Type: INCREMENT, HasType: true, IgnoreValue: false} rowchange.Columns = append(rowchange.Columns, *column) } func (rowchange *DeleteRowChange) Serialize() []byte { return rowchange.PrimaryKey.Build(true) } func (rowchange *PutRowChange) Serialize() []byte { row := buildRowPutChange(rowchange.PrimaryKey, rowchange.Columns) return row.Build() } func (rowchange *UpdateRowChange) Serialize() []byte { row := buildRowUpdateChange(rowchange.PrimaryKey, rowchange.Columns) return row.Build() } func (rowchange *DeleteRowChange) GetTableName() string { return rowchange.TableName } func (rowchange *PutRowChange) GetTableName() string { return rowchange.TableName } func (rowchange *UpdateRowChange) GetTableName() string { return rowchange.TableName } func (rowchange *DeleteRowChange) getOperationType() otsprotocol.OperationType { return otsprotocol.OperationType_DELETE } func (rowchange *PutRowChange) getOperationType() otsprotocol.OperationType { return otsprotocol.OperationType_PUT } func (rowchange *UpdateRowChange) getOperationType() otsprotocol.OperationType { return otsprotocol.OperationType_UPDATE } func (rowchange *DeleteRowChange) getCondition() *otsprotocol.Condition { condition := new(otsprotocol.Condition) condition.RowExistence = rowchange.Condition.buildCondition() if rowchange.Condition.ColumnCondition != nil { condition.ColumnCondition = rowchange.Condition.ColumnCondition.Serialize() } return condition } func (rowchange *UpdateRowChange) getCondition() *otsprotocol.Condition { condition := new(otsprotocol.Condition) condition.RowExistence = rowchange.Condition.buildCondition() if rowchange.Condition.ColumnCondition != nil { condition.ColumnCondition = rowchange.Condition.ColumnCondition.Serialize() } return condition } func (rowchange *PutRowChange) getCondition() *otsprotocol.Condition { condition := new(otsprotocol.Condition) condition.RowExistence = rowchange.Condition.buildCondition() if rowchange.Condition.ColumnCondition != nil { condition.ColumnCondition = rowchange.Condition.ColumnCondition.Serialize() } return condition } func (request *BatchWriteRowRequest) AddRowChange(change RowChange) { if request.RowChangesGroupByTable == nil { request.RowChangesGroupByTable = make(map[string][]RowChange) } request.RowChangesGroupByTable[change.GetTableName()] = append(request.RowChangesGroupByTable[change.GetTableName()], change) } func (direction Direction) ToDirection() otsprotocol.Direction { if direction == FORWARD { return otsprotocol.Direction_FORWARD } else { return otsprotocol.Direction_BACKWARD } } func (columnMap *ColumnMap) GetRange(start int, count int) ([]*AttributeColumn, error) { columns := []*AttributeColumn{} end := start + count if len(columnMap.columnsKey) < end { return nil, fmt.Errorf("invalid arugment") } for i := start; i < end; i++ { subColumns := columnMap.Columns[columnMap.columnsKey[i]] for _, column := range subColumns { columns = append(columns, column) } } return columns, nil } func (response *GetRowResponse) GetColumnMap() *ColumnMap { if response == nil { return nil } if response.columnMap != nil { return response.columnMap } else { response.columnMap = &ColumnMap{} response.columnMap.Columns = make(map[string][]*AttributeColumn) if len(response.Columns) == 0 { return response.columnMap } else { for _, column := range response.Columns { if _, ok := response.columnMap.Columns[column.ColumnName]; ok { response.columnMap.Columns[column.ColumnName] = append(response.columnMap.Columns[column.ColumnName], column) } else { response.columnMap.columnsKey = append(response.columnMap.columnsKey, column.ColumnName) value := []*AttributeColumn{} value = append(value, column) response.columnMap.Columns[column.ColumnName] = value } } sort.Strings(response.columnMap.columnsKey) return response.columnMap } } } func Assert(cond bool, msg string) { if !cond { panic(msg) } } func (meta *TableMeta) AddDefinedColumn(name string, definedType DefinedColumnType) { meta.DefinedColumns = append(meta.DefinedColumns, &DefinedColumnSchema{Name: name, ColumnType: definedType}) } func (meta *IndexMeta) AddDefinedColumn(name string) { meta.DefinedColumns = append(meta.DefinedColumns, name) } func (meta *IndexMeta) AddPrimaryKeyColumn(name string) { meta.Primarykey = append(meta.Primarykey, name) } func (request *CreateTableRequest) AddIndexMeta(meta *IndexMeta) { request.IndexMetas = append(request.IndexMetas, meta) } func (meta *IndexMeta) ConvertToPbIndexMeta() *otsprotocol.IndexMeta { return &otsprotocol.IndexMeta { Name: &meta.IndexName, PrimaryKey: meta.Primarykey, DefinedColumn: meta.DefinedColumns, IndexUpdateMode: otsprotocol.IndexUpdateMode_IUM_ASYNC_INDEX.Enum(), IndexType: otsprotocol.IndexType_IT_GLOBAL_INDEX.Enum(), } } func ConvertPbIndexTypeToIndexType(indexType *otsprotocol.IndexType) IndexType { switch *indexType { case otsprotocol.IndexType_IT_GLOBAL_INDEX: return IT_GLOBAL_INDEX default: return IT_LOCAL_INDEX } } func ConvertPbIndexMetaToIndexMeta(meta *otsprotocol.IndexMeta) *IndexMeta { indexmeta := &IndexMeta { IndexName: *meta.Name, IndexType: ConvertPbIndexTypeToIndexType(meta.IndexType), } for _, pk := range meta.PrimaryKey { indexmeta.Primarykey = append(indexmeta.Primarykey, pk) } for _, col := range meta.DefinedColumn { indexmeta.DefinedColumns = append(indexmeta.DefinedColumns, col) } return indexmeta }