You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

1194 lines
34 KiB

  1. // Copyright 2016 Google LLC
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package bttest
  15. import (
  16. "context"
  17. "fmt"
  18. "math/rand"
  19. "strconv"
  20. "sync"
  21. "sync/atomic"
  22. "testing"
  23. "time"
  24. "github.com/golang/protobuf/proto"
  25. "github.com/google/go-cmp/cmp"
  26. "github.com/google/go-cmp/cmp/cmpopts"
  27. btapb "google.golang.org/genproto/googleapis/bigtable/admin/v2"
  28. btpb "google.golang.org/genproto/googleapis/bigtable/v2"
  29. "google.golang.org/grpc"
  30. )
  31. func TestConcurrentMutationsReadModifyAndGC(t *testing.T) {
  32. s := &server{
  33. tables: make(map[string]*table),
  34. }
  35. ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond)
  36. defer cancel()
  37. if _, err := s.CreateTable(
  38. ctx,
  39. &btapb.CreateTableRequest{Parent: "cluster", TableId: "t"}); err != nil {
  40. t.Fatal(err)
  41. }
  42. const name = `cluster/tables/t`
  43. tbl := s.tables[name]
  44. req := &btapb.ModifyColumnFamiliesRequest{
  45. Name: name,
  46. Modifications: []*btapb.ModifyColumnFamiliesRequest_Modification{{
  47. Id: "cf",
  48. Mod: &btapb.ModifyColumnFamiliesRequest_Modification_Create{Create: &btapb.ColumnFamily{}},
  49. }},
  50. }
  51. _, err := s.ModifyColumnFamilies(ctx, req)
  52. if err != nil {
  53. t.Fatal(err)
  54. }
  55. req = &btapb.ModifyColumnFamiliesRequest{
  56. Name: name,
  57. Modifications: []*btapb.ModifyColumnFamiliesRequest_Modification{{
  58. Id: "cf",
  59. Mod: &btapb.ModifyColumnFamiliesRequest_Modification_Update{Update: &btapb.ColumnFamily{
  60. GcRule: &btapb.GcRule{Rule: &btapb.GcRule_MaxNumVersions{MaxNumVersions: 1}},
  61. }},
  62. }},
  63. }
  64. if _, err := s.ModifyColumnFamilies(ctx, req); err != nil {
  65. t.Fatal(err)
  66. }
  67. var wg sync.WaitGroup
  68. var ts int64
  69. ms := func() []*btpb.Mutation {
  70. return []*btpb.Mutation{{
  71. Mutation: &btpb.Mutation_SetCell_{SetCell: &btpb.Mutation_SetCell{
  72. FamilyName: "cf",
  73. ColumnQualifier: []byte(`col`),
  74. TimestampMicros: atomic.AddInt64(&ts, 1000),
  75. }},
  76. }}
  77. }
  78. rmw := func() *btpb.ReadModifyWriteRowRequest {
  79. return &btpb.ReadModifyWriteRowRequest{
  80. TableName: name,
  81. RowKey: []byte(fmt.Sprint(rand.Intn(100))),
  82. Rules: []*btpb.ReadModifyWriteRule{{
  83. FamilyName: "cf",
  84. ColumnQualifier: []byte("col"),
  85. Rule: &btpb.ReadModifyWriteRule_IncrementAmount{IncrementAmount: 1},
  86. }},
  87. }
  88. }
  89. for i := 0; i < 100; i++ {
  90. wg.Add(1)
  91. go func() {
  92. defer wg.Done()
  93. for ctx.Err() == nil {
  94. req := &btpb.MutateRowRequest{
  95. TableName: name,
  96. RowKey: []byte(fmt.Sprint(rand.Intn(100))),
  97. Mutations: ms(),
  98. }
  99. if _, err := s.MutateRow(ctx, req); err != nil {
  100. panic(err) // can't use t.Fatal in goroutine
  101. }
  102. }
  103. }()
  104. wg.Add(1)
  105. go func() {
  106. defer wg.Done()
  107. for ctx.Err() == nil {
  108. _, _ = s.ReadModifyWriteRow(ctx, rmw())
  109. }
  110. }()
  111. wg.Add(1)
  112. go func() {
  113. defer wg.Done()
  114. tbl.gc()
  115. }()
  116. }
  117. done := make(chan struct{})
  118. go func() {
  119. wg.Wait()
  120. close(done)
  121. }()
  122. select {
  123. case <-done:
  124. case <-time.After(1 * time.Second):
  125. t.Error("Concurrent mutations and GCs haven't completed after 1s")
  126. }
  127. }
  128. func TestCreateTableWithFamily(t *testing.T) {
  129. // The Go client currently doesn't support creating a table with column families
  130. // in one operation but it is allowed by the API. This must still be supported by the
  131. // fake server so this test lives here instead of in the main bigtable
  132. // integration test.
  133. s := &server{
  134. tables: make(map[string]*table),
  135. }
  136. ctx := context.Background()
  137. newTbl := btapb.Table{
  138. ColumnFamilies: map[string]*btapb.ColumnFamily{
  139. "cf1": {GcRule: &btapb.GcRule{Rule: &btapb.GcRule_MaxNumVersions{MaxNumVersions: 123}}},
  140. "cf2": {GcRule: &btapb.GcRule{Rule: &btapb.GcRule_MaxNumVersions{MaxNumVersions: 456}}},
  141. },
  142. }
  143. cTbl, err := s.CreateTable(ctx, &btapb.CreateTableRequest{Parent: "cluster", TableId: "t", Table: &newTbl})
  144. if err != nil {
  145. t.Fatalf("Creating table: %v", err)
  146. }
  147. tbl, err := s.GetTable(ctx, &btapb.GetTableRequest{Name: cTbl.Name})
  148. if err != nil {
  149. t.Fatalf("Getting table: %v", err)
  150. }
  151. cf := tbl.ColumnFamilies["cf1"]
  152. if cf == nil {
  153. t.Fatalf("Missing col family cf1")
  154. }
  155. if got, want := cf.GcRule.GetMaxNumVersions(), int32(123); got != want {
  156. t.Errorf("Invalid MaxNumVersions: wanted:%d, got:%d", want, got)
  157. }
  158. cf = tbl.ColumnFamilies["cf2"]
  159. if cf == nil {
  160. t.Fatalf("Missing col family cf2")
  161. }
  162. if got, want := cf.GcRule.GetMaxNumVersions(), int32(456); got != want {
  163. t.Errorf("Invalid MaxNumVersions: wanted:%d, got:%d", want, got)
  164. }
  165. }
  166. type MockSampleRowKeysServer struct {
  167. responses []*btpb.SampleRowKeysResponse
  168. grpc.ServerStream
  169. }
  170. func (s *MockSampleRowKeysServer) Send(resp *btpb.SampleRowKeysResponse) error {
  171. s.responses = append(s.responses, resp)
  172. return nil
  173. }
  174. func TestSampleRowKeys(t *testing.T) {
  175. s := &server{
  176. tables: make(map[string]*table),
  177. }
  178. ctx := context.Background()
  179. newTbl := btapb.Table{
  180. ColumnFamilies: map[string]*btapb.ColumnFamily{
  181. "cf": {GcRule: &btapb.GcRule{Rule: &btapb.GcRule_MaxNumVersions{MaxNumVersions: 1}}},
  182. },
  183. }
  184. tbl, err := s.CreateTable(ctx, &btapb.CreateTableRequest{Parent: "cluster", TableId: "t", Table: &newTbl})
  185. if err != nil {
  186. t.Fatalf("Creating table: %v", err)
  187. }
  188. // Populate the table
  189. val := []byte("value")
  190. rowCount := 1000
  191. for i := 0; i < rowCount; i++ {
  192. req := &btpb.MutateRowRequest{
  193. TableName: tbl.Name,
  194. RowKey: []byte("row-" + strconv.Itoa(i)),
  195. Mutations: []*btpb.Mutation{{
  196. Mutation: &btpb.Mutation_SetCell_{SetCell: &btpb.Mutation_SetCell{
  197. FamilyName: "cf",
  198. ColumnQualifier: []byte("col"),
  199. TimestampMicros: 1000,
  200. Value: val,
  201. }},
  202. }},
  203. }
  204. if _, err := s.MutateRow(ctx, req); err != nil {
  205. t.Fatalf("Populating table: %v", err)
  206. }
  207. }
  208. mock := &MockSampleRowKeysServer{}
  209. if err := s.SampleRowKeys(&btpb.SampleRowKeysRequest{TableName: tbl.Name}, mock); err != nil {
  210. t.Errorf("SampleRowKeys error: %v", err)
  211. }
  212. if len(mock.responses) == 0 {
  213. t.Fatal("Response count: got 0, want > 0")
  214. }
  215. // Make sure the offset of the final response is the offset of the final row
  216. got := mock.responses[len(mock.responses)-1].OffsetBytes
  217. want := int64((rowCount - 1) * len(val))
  218. if got != want {
  219. t.Errorf("Invalid offset: got %d, want %d", got, want)
  220. }
  221. }
  222. func TestDropRowRange(t *testing.T) {
  223. s := &server{
  224. tables: make(map[string]*table),
  225. }
  226. ctx := context.Background()
  227. newTbl := btapb.Table{
  228. ColumnFamilies: map[string]*btapb.ColumnFamily{
  229. "cf": {GcRule: &btapb.GcRule{Rule: &btapb.GcRule_MaxNumVersions{MaxNumVersions: 1}}},
  230. },
  231. }
  232. tblInfo, err := s.CreateTable(ctx, &btapb.CreateTableRequest{Parent: "cluster", TableId: "t", Table: &newTbl})
  233. if err != nil {
  234. t.Fatalf("Creating table: %v", err)
  235. }
  236. tbl := s.tables[tblInfo.Name]
  237. // Populate the table
  238. prefixes := []string{"AAA", "BBB", "CCC", "DDD"}
  239. count := 3
  240. doWrite := func() {
  241. for _, prefix := range prefixes {
  242. for i := 0; i < count; i++ {
  243. req := &btpb.MutateRowRequest{
  244. TableName: tblInfo.Name,
  245. RowKey: []byte(prefix + strconv.Itoa(i)),
  246. Mutations: []*btpb.Mutation{{
  247. Mutation: &btpb.Mutation_SetCell_{SetCell: &btpb.Mutation_SetCell{
  248. FamilyName: "cf",
  249. ColumnQualifier: []byte("col"),
  250. TimestampMicros: 1000,
  251. Value: []byte{},
  252. }},
  253. }},
  254. }
  255. if _, err := s.MutateRow(ctx, req); err != nil {
  256. t.Fatalf("Populating table: %v", err)
  257. }
  258. }
  259. }
  260. }
  261. doWrite()
  262. tblSize := tbl.rows.Len()
  263. req := &btapb.DropRowRangeRequest{
  264. Name: tblInfo.Name,
  265. Target: &btapb.DropRowRangeRequest_RowKeyPrefix{RowKeyPrefix: []byte("AAA")},
  266. }
  267. if _, err = s.DropRowRange(ctx, req); err != nil {
  268. t.Fatalf("Dropping first range: %v", err)
  269. }
  270. got, want := tbl.rows.Len(), tblSize-count
  271. if got != want {
  272. t.Errorf("Row count after first drop: got %d (%v), want %d", got, tbl.rows, want)
  273. }
  274. req = &btapb.DropRowRangeRequest{
  275. Name: tblInfo.Name,
  276. Target: &btapb.DropRowRangeRequest_RowKeyPrefix{RowKeyPrefix: []byte("DDD")},
  277. }
  278. if _, err = s.DropRowRange(ctx, req); err != nil {
  279. t.Fatalf("Dropping second range: %v", err)
  280. }
  281. got, want = tbl.rows.Len(), tblSize-(2*count)
  282. if got != want {
  283. t.Errorf("Row count after second drop: got %d (%v), want %d", got, tbl.rows, want)
  284. }
  285. req = &btapb.DropRowRangeRequest{
  286. Name: tblInfo.Name,
  287. Target: &btapb.DropRowRangeRequest_RowKeyPrefix{RowKeyPrefix: []byte("XXX")},
  288. }
  289. if _, err = s.DropRowRange(ctx, req); err != nil {
  290. t.Fatalf("Dropping invalid range: %v", err)
  291. }
  292. got, want = tbl.rows.Len(), tblSize-(2*count)
  293. if got != want {
  294. t.Errorf("Row count after invalid drop: got %d (%v), want %d", got, tbl.rows, want)
  295. }
  296. req = &btapb.DropRowRangeRequest{
  297. Name: tblInfo.Name,
  298. Target: &btapb.DropRowRangeRequest_DeleteAllDataFromTable{DeleteAllDataFromTable: true},
  299. }
  300. if _, err = s.DropRowRange(ctx, req); err != nil {
  301. t.Fatalf("Dropping all data: %v", err)
  302. }
  303. got, want = tbl.rows.Len(), 0
  304. if got != want {
  305. t.Errorf("Row count after drop all: got %d, want %d", got, want)
  306. }
  307. // Test that we can write rows, delete some and then write them again.
  308. count = 1
  309. doWrite()
  310. req = &btapb.DropRowRangeRequest{
  311. Name: tblInfo.Name,
  312. Target: &btapb.DropRowRangeRequest_DeleteAllDataFromTable{DeleteAllDataFromTable: true},
  313. }
  314. if _, err = s.DropRowRange(ctx, req); err != nil {
  315. t.Fatalf("Dropping all data: %v", err)
  316. }
  317. got, want = tbl.rows.Len(), 0
  318. if got != want {
  319. t.Errorf("Row count after drop all: got %d, want %d", got, want)
  320. }
  321. doWrite()
  322. got, want = tbl.rows.Len(), len(prefixes)
  323. if got != want {
  324. t.Errorf("Row count after rewrite: got %d, want %d", got, want)
  325. }
  326. req = &btapb.DropRowRangeRequest{
  327. Name: tblInfo.Name,
  328. Target: &btapb.DropRowRangeRequest_RowKeyPrefix{RowKeyPrefix: []byte("BBB")},
  329. }
  330. if _, err = s.DropRowRange(ctx, req); err != nil {
  331. t.Fatalf("Dropping range: %v", err)
  332. }
  333. doWrite()
  334. got, want = tbl.rows.Len(), len(prefixes)
  335. if got != want {
  336. t.Errorf("Row count after drop range: got %d, want %d", got, want)
  337. }
  338. }
  339. type MockReadRowsServer struct {
  340. responses []*btpb.ReadRowsResponse
  341. grpc.ServerStream
  342. }
  343. func (s *MockReadRowsServer) Send(resp *btpb.ReadRowsResponse) error {
  344. s.responses = append(s.responses, resp)
  345. return nil
  346. }
  347. func TestReadRows(t *testing.T) {
  348. ctx := context.Background()
  349. s := &server{
  350. tables: make(map[string]*table),
  351. }
  352. newTbl := btapb.Table{
  353. ColumnFamilies: map[string]*btapb.ColumnFamily{
  354. "cf0": {GcRule: &btapb.GcRule{Rule: &btapb.GcRule_MaxNumVersions{MaxNumVersions: 1}}},
  355. },
  356. }
  357. tblInfo, err := s.CreateTable(ctx, &btapb.CreateTableRequest{Parent: "cluster", TableId: "t", Table: &newTbl})
  358. if err != nil {
  359. t.Fatalf("Creating table: %v", err)
  360. }
  361. mreq := &btpb.MutateRowRequest{
  362. TableName: tblInfo.Name,
  363. RowKey: []byte("row"),
  364. Mutations: []*btpb.Mutation{{
  365. Mutation: &btpb.Mutation_SetCell_{SetCell: &btpb.Mutation_SetCell{
  366. FamilyName: "cf0",
  367. ColumnQualifier: []byte("col"),
  368. TimestampMicros: 1000,
  369. Value: []byte{},
  370. }},
  371. }},
  372. }
  373. if _, err := s.MutateRow(ctx, mreq); err != nil {
  374. t.Fatalf("Populating table: %v", err)
  375. }
  376. for _, rowset := range []*btpb.RowSet{
  377. {RowKeys: [][]byte{[]byte("row")}},
  378. {RowRanges: []*btpb.RowRange{{StartKey: &btpb.RowRange_StartKeyClosed{StartKeyClosed: []byte("")}}}},
  379. {RowRanges: []*btpb.RowRange{{StartKey: &btpb.RowRange_StartKeyClosed{StartKeyClosed: []byte("r")}}}},
  380. {RowRanges: []*btpb.RowRange{{
  381. StartKey: &btpb.RowRange_StartKeyClosed{StartKeyClosed: []byte("")},
  382. EndKey: &btpb.RowRange_EndKeyOpen{EndKeyOpen: []byte("s")},
  383. }}},
  384. } {
  385. mock := &MockReadRowsServer{}
  386. req := &btpb.ReadRowsRequest{TableName: tblInfo.Name, Rows: rowset}
  387. if err = s.ReadRows(req, mock); err != nil {
  388. t.Fatalf("ReadRows error: %v", err)
  389. }
  390. if got, want := len(mock.responses), 1; got != want {
  391. t.Errorf("%+v: response count: got %d, want %d", rowset, got, want)
  392. }
  393. }
  394. }
  395. func TestReadRowsError(t *testing.T) {
  396. ctx := context.Background()
  397. s := &server{
  398. tables: make(map[string]*table),
  399. }
  400. newTbl := btapb.Table{
  401. ColumnFamilies: map[string]*btapb.ColumnFamily{
  402. "cf0": {GcRule: &btapb.GcRule{Rule: &btapb.GcRule_MaxNumVersions{MaxNumVersions: 1}}},
  403. },
  404. }
  405. tblInfo, err := s.CreateTable(ctx, &btapb.CreateTableRequest{Parent: "cluster", TableId: "t", Table: &newTbl})
  406. if err != nil {
  407. t.Fatalf("Creating table: %v", err)
  408. }
  409. mreq := &btpb.MutateRowRequest{
  410. TableName: tblInfo.Name,
  411. RowKey: []byte("row"),
  412. Mutations: []*btpb.Mutation{{
  413. Mutation: &btpb.Mutation_SetCell_{SetCell: &btpb.Mutation_SetCell{
  414. FamilyName: "cf0",
  415. ColumnQualifier: []byte("col"),
  416. TimestampMicros: 1000,
  417. Value: []byte{},
  418. }},
  419. }},
  420. }
  421. if _, err := s.MutateRow(ctx, mreq); err != nil {
  422. t.Fatalf("Populating table: %v", err)
  423. }
  424. mock := &MockReadRowsServer{}
  425. req := &btpb.ReadRowsRequest{TableName: tblInfo.Name, Filter: &btpb.RowFilter{
  426. Filter: &btpb.RowFilter_RowKeyRegexFilter{RowKeyRegexFilter: []byte("[")}}, // Invalid regex.
  427. }
  428. if err = s.ReadRows(req, mock); err == nil {
  429. t.Fatal("ReadRows got no error, want error")
  430. }
  431. }
  432. func TestReadRowsAfterDeletion(t *testing.T) {
  433. ctx := context.Background()
  434. s := &server{
  435. tables: make(map[string]*table),
  436. }
  437. newTbl := btapb.Table{
  438. ColumnFamilies: map[string]*btapb.ColumnFamily{
  439. "cf0": {},
  440. },
  441. }
  442. tblInfo, err := s.CreateTable(ctx, &btapb.CreateTableRequest{
  443. Parent: "cluster", TableId: "t", Table: &newTbl,
  444. })
  445. if err != nil {
  446. t.Fatalf("Creating table: %v", err)
  447. }
  448. populateTable(ctx, s)
  449. dreq := &btpb.MutateRowRequest{
  450. TableName: tblInfo.Name,
  451. RowKey: []byte("row"),
  452. Mutations: []*btpb.Mutation{{
  453. Mutation: &btpb.Mutation_DeleteFromRow_{
  454. DeleteFromRow: &btpb.Mutation_DeleteFromRow{},
  455. },
  456. }},
  457. }
  458. if _, err := s.MutateRow(ctx, dreq); err != nil {
  459. t.Fatalf("Deleting from table: %v", err)
  460. }
  461. mock := &MockReadRowsServer{}
  462. req := &btpb.ReadRowsRequest{TableName: tblInfo.Name}
  463. if err = s.ReadRows(req, mock); err != nil {
  464. t.Fatalf("ReadRows error: %v", err)
  465. }
  466. if got, want := len(mock.responses), 0; got != want {
  467. t.Errorf("response count: got %d, want %d", got, want)
  468. }
  469. }
  470. func TestReadRowsOrder(t *testing.T) {
  471. s := &server{
  472. tables: make(map[string]*table),
  473. }
  474. ctx := context.Background()
  475. newTbl := btapb.Table{
  476. ColumnFamilies: map[string]*btapb.ColumnFamily{
  477. "cf0": {GcRule: &btapb.GcRule{Rule: &btapb.GcRule_MaxNumVersions{MaxNumVersions: 1}}},
  478. },
  479. }
  480. tblInfo, err := s.CreateTable(ctx, &btapb.CreateTableRequest{Parent: "cluster", TableId: "t", Table: &newTbl})
  481. if err != nil {
  482. t.Fatalf("Creating table: %v", err)
  483. }
  484. count := 3
  485. mcf := func(i int) *btapb.ModifyColumnFamiliesRequest {
  486. return &btapb.ModifyColumnFamiliesRequest{
  487. Name: tblInfo.Name,
  488. Modifications: []*btapb.ModifyColumnFamiliesRequest_Modification{{
  489. Id: "cf" + strconv.Itoa(i),
  490. Mod: &btapb.ModifyColumnFamiliesRequest_Modification_Create{Create: &btapb.ColumnFamily{}},
  491. }},
  492. }
  493. }
  494. for i := 1; i <= count; i++ {
  495. _, err = s.ModifyColumnFamilies(ctx, mcf(i))
  496. if err != nil {
  497. t.Fatal(err)
  498. }
  499. }
  500. // Populate the table
  501. for fc := 0; fc < count; fc++ {
  502. for cc := count; cc > 0; cc-- {
  503. for tc := 0; tc < count; tc++ {
  504. req := &btpb.MutateRowRequest{
  505. TableName: tblInfo.Name,
  506. RowKey: []byte("row"),
  507. Mutations: []*btpb.Mutation{{
  508. Mutation: &btpb.Mutation_SetCell_{SetCell: &btpb.Mutation_SetCell{
  509. FamilyName: "cf" + strconv.Itoa(fc),
  510. ColumnQualifier: []byte("col" + strconv.Itoa(cc)),
  511. TimestampMicros: int64((tc + 1) * 1000),
  512. Value: []byte{},
  513. }},
  514. }},
  515. }
  516. if _, err := s.MutateRow(ctx, req); err != nil {
  517. t.Fatalf("Populating table: %v", err)
  518. }
  519. }
  520. }
  521. }
  522. req := &btpb.ReadRowsRequest{
  523. TableName: tblInfo.Name,
  524. Rows: &btpb.RowSet{RowKeys: [][]byte{[]byte("row")}},
  525. }
  526. mock := &MockReadRowsServer{}
  527. if err = s.ReadRows(req, mock); err != nil {
  528. t.Errorf("ReadRows error: %v", err)
  529. }
  530. if len(mock.responses) == 0 {
  531. t.Fatal("Response count: got 0, want > 0")
  532. }
  533. if len(mock.responses[0].Chunks) != 27 {
  534. t.Fatalf("Chunk count: got %d, want 27", len(mock.responses[0].Chunks))
  535. }
  536. testOrder := func(ms *MockReadRowsServer) {
  537. var prevFam, prevCol string
  538. var prevTime int64
  539. for _, cc := range ms.responses[0].Chunks {
  540. if prevFam == "" {
  541. prevFam = cc.FamilyName.Value
  542. prevCol = string(cc.Qualifier.Value)
  543. prevTime = cc.TimestampMicros
  544. continue
  545. }
  546. if cc.FamilyName.Value < prevFam {
  547. t.Errorf("Family order is not correct: got %s < %s", cc.FamilyName.Value, prevFam)
  548. } else if cc.FamilyName.Value == prevFam {
  549. if string(cc.Qualifier.Value) < prevCol {
  550. t.Errorf("Column order is not correct: got %s < %s", string(cc.Qualifier.Value), prevCol)
  551. } else if string(cc.Qualifier.Value) == prevCol {
  552. if cc.TimestampMicros > prevTime {
  553. t.Errorf("cell order is not correct: got %d > %d", cc.TimestampMicros, prevTime)
  554. }
  555. }
  556. }
  557. prevFam = cc.FamilyName.Value
  558. prevCol = string(cc.Qualifier.Value)
  559. prevTime = cc.TimestampMicros
  560. }
  561. }
  562. testOrder(mock)
  563. // Read with interleave filter
  564. inter := &btpb.RowFilter_Interleave{}
  565. fnr := &btpb.RowFilter{Filter: &btpb.RowFilter_FamilyNameRegexFilter{FamilyNameRegexFilter: "cf1"}}
  566. cqr := &btpb.RowFilter{Filter: &btpb.RowFilter_ColumnQualifierRegexFilter{ColumnQualifierRegexFilter: []byte("col2")}}
  567. inter.Filters = append(inter.Filters, fnr, cqr)
  568. req = &btpb.ReadRowsRequest{
  569. TableName: tblInfo.Name,
  570. Rows: &btpb.RowSet{RowKeys: [][]byte{[]byte("row")}},
  571. Filter: &btpb.RowFilter{
  572. Filter: &btpb.RowFilter_Interleave_{Interleave: inter},
  573. },
  574. }
  575. mock = &MockReadRowsServer{}
  576. if err = s.ReadRows(req, mock); err != nil {
  577. t.Errorf("ReadRows error: %v", err)
  578. }
  579. if len(mock.responses) == 0 {
  580. t.Fatal("Response count: got 0, want > 0")
  581. }
  582. if len(mock.responses[0].Chunks) != 18 {
  583. t.Fatalf("Chunk count: got %d, want 18", len(mock.responses[0].Chunks))
  584. }
  585. testOrder(mock)
  586. // Check order after ReadModifyWriteRow
  587. rmw := func(i int) *btpb.ReadModifyWriteRowRequest {
  588. return &btpb.ReadModifyWriteRowRequest{
  589. TableName: tblInfo.Name,
  590. RowKey: []byte("row"),
  591. Rules: []*btpb.ReadModifyWriteRule{{
  592. FamilyName: "cf3",
  593. ColumnQualifier: []byte("col" + strconv.Itoa(i)),
  594. Rule: &btpb.ReadModifyWriteRule_IncrementAmount{IncrementAmount: 1},
  595. }},
  596. }
  597. }
  598. for i := count; i > 0; i-- {
  599. if _, err := s.ReadModifyWriteRow(ctx, rmw(i)); err != nil {
  600. t.Fatal(err)
  601. }
  602. }
  603. req = &btpb.ReadRowsRequest{
  604. TableName: tblInfo.Name,
  605. Rows: &btpb.RowSet{RowKeys: [][]byte{[]byte("row")}},
  606. }
  607. mock = &MockReadRowsServer{}
  608. if err = s.ReadRows(req, mock); err != nil {
  609. t.Errorf("ReadRows error: %v", err)
  610. }
  611. if len(mock.responses) == 0 {
  612. t.Fatal("Response count: got 0, want > 0")
  613. }
  614. if len(mock.responses[0].Chunks) != 30 {
  615. t.Fatalf("Chunk count: got %d, want 30", len(mock.responses[0].Chunks))
  616. }
  617. testOrder(mock)
  618. }
  619. func TestReadRowsWithlabelTransformer(t *testing.T) {
  620. ctx := context.Background()
  621. s := &server{
  622. tables: make(map[string]*table),
  623. }
  624. newTbl := btapb.Table{
  625. ColumnFamilies: map[string]*btapb.ColumnFamily{
  626. "cf0": {GcRule: &btapb.GcRule{Rule: &btapb.GcRule_MaxNumVersions{MaxNumVersions: 1}}},
  627. },
  628. }
  629. tblInfo, err := s.CreateTable(ctx, &btapb.CreateTableRequest{Parent: "cluster", TableId: "t", Table: &newTbl})
  630. if err != nil {
  631. t.Fatalf("Creating table: %v", err)
  632. }
  633. mreq := &btpb.MutateRowRequest{
  634. TableName: tblInfo.Name,
  635. RowKey: []byte("row"),
  636. Mutations: []*btpb.Mutation{{
  637. Mutation: &btpb.Mutation_SetCell_{SetCell: &btpb.Mutation_SetCell{
  638. FamilyName: "cf0",
  639. ColumnQualifier: []byte("col"),
  640. TimestampMicros: 1000,
  641. Value: []byte{},
  642. }},
  643. }},
  644. }
  645. if _, err := s.MutateRow(ctx, mreq); err != nil {
  646. t.Fatalf("Populating table: %v", err)
  647. }
  648. mock := &MockReadRowsServer{}
  649. req := &btpb.ReadRowsRequest{
  650. TableName: tblInfo.Name,
  651. Filter: &btpb.RowFilter{
  652. Filter: &btpb.RowFilter_ApplyLabelTransformer{
  653. ApplyLabelTransformer: "label",
  654. },
  655. },
  656. }
  657. if err = s.ReadRows(req, mock); err != nil {
  658. t.Fatalf("ReadRows error: %v", err)
  659. }
  660. if got, want := len(mock.responses), 1; got != want {
  661. t.Fatalf("response count: got %d, want %d", got, want)
  662. }
  663. resp := mock.responses[0]
  664. if got, want := len(resp.Chunks), 1; got != want {
  665. t.Fatalf("chunks count: got %d, want %d", got, want)
  666. }
  667. chunk := resp.Chunks[0]
  668. if got, want := len(chunk.Labels), 1; got != want {
  669. t.Fatalf("labels count: got %d, want %d", got, want)
  670. }
  671. if got, want := chunk.Labels[0], "label"; got != want {
  672. t.Fatalf("label: got %s, want %s", got, want)
  673. }
  674. mock = &MockReadRowsServer{}
  675. req = &btpb.ReadRowsRequest{
  676. TableName: tblInfo.Name,
  677. Filter: &btpb.RowFilter{
  678. Filter: &btpb.RowFilter_ApplyLabelTransformer{
  679. ApplyLabelTransformer: "", // invalid label
  680. },
  681. },
  682. }
  683. if err = s.ReadRows(req, mock); err == nil {
  684. t.Fatal("ReadRows want invalid label error, got none")
  685. }
  686. }
  687. func TestCheckAndMutateRowWithoutPredicate(t *testing.T) {
  688. s := &server{
  689. tables: make(map[string]*table),
  690. }
  691. ctx := context.Background()
  692. newTbl := btapb.Table{
  693. ColumnFamilies: map[string]*btapb.ColumnFamily{
  694. "cf": {GcRule: &btapb.GcRule{Rule: &btapb.GcRule_MaxNumVersions{MaxNumVersions: 1}}},
  695. },
  696. }
  697. tbl, err := s.CreateTable(ctx, &btapb.CreateTableRequest{Parent: "cluster", TableId: "t", Table: &newTbl})
  698. if err != nil {
  699. t.Fatalf("Creating table: %v", err)
  700. }
  701. // Populate the table
  702. val := []byte("value")
  703. mrreq := &btpb.MutateRowRequest{
  704. TableName: tbl.Name,
  705. RowKey: []byte("row-present"),
  706. Mutations: []*btpb.Mutation{{
  707. Mutation: &btpb.Mutation_SetCell_{SetCell: &btpb.Mutation_SetCell{
  708. FamilyName: "cf",
  709. ColumnQualifier: []byte("col"),
  710. TimestampMicros: 1000,
  711. Value: val,
  712. }},
  713. }},
  714. }
  715. if _, err := s.MutateRow(ctx, mrreq); err != nil {
  716. t.Fatalf("Populating table: %v", err)
  717. }
  718. req := &btpb.CheckAndMutateRowRequest{
  719. TableName: tbl.Name,
  720. RowKey: []byte("row-not-present"),
  721. }
  722. if res, err := s.CheckAndMutateRow(ctx, req); err != nil {
  723. t.Errorf("CheckAndMutateRow error: %v", err)
  724. } else if got, want := res.PredicateMatched, false; got != want {
  725. t.Errorf("Invalid PredicateMatched value: got %t, want %t", got, want)
  726. }
  727. req = &btpb.CheckAndMutateRowRequest{
  728. TableName: tbl.Name,
  729. RowKey: []byte("row-present"),
  730. }
  731. if res, err := s.CheckAndMutateRow(ctx, req); err != nil {
  732. t.Errorf("CheckAndMutateRow error: %v", err)
  733. } else if got, want := res.PredicateMatched, true; got != want {
  734. t.Errorf("Invalid PredicateMatched value: got %t, want %t", got, want)
  735. }
  736. }
  737. func TestServer_ReadModifyWriteRow(t *testing.T) {
  738. s := &server{
  739. tables: make(map[string]*table),
  740. }
  741. ctx := context.Background()
  742. newTbl := btapb.Table{
  743. ColumnFamilies: map[string]*btapb.ColumnFamily{
  744. "cf": {GcRule: &btapb.GcRule{Rule: &btapb.GcRule_MaxNumVersions{MaxNumVersions: 1}}},
  745. },
  746. }
  747. tbl, err := s.CreateTable(ctx, &btapb.CreateTableRequest{Parent: "cluster", TableId: "t", Table: &newTbl})
  748. if err != nil {
  749. t.Fatalf("Creating table: %v", err)
  750. }
  751. req := &btpb.ReadModifyWriteRowRequest{
  752. TableName: tbl.Name,
  753. RowKey: []byte("row-key"),
  754. Rules: []*btpb.ReadModifyWriteRule{
  755. {
  756. FamilyName: "cf",
  757. ColumnQualifier: []byte("q1"),
  758. Rule: &btpb.ReadModifyWriteRule_AppendValue{
  759. AppendValue: []byte("a"),
  760. },
  761. },
  762. // multiple ops for same cell
  763. {
  764. FamilyName: "cf",
  765. ColumnQualifier: []byte("q1"),
  766. Rule: &btpb.ReadModifyWriteRule_AppendValue{
  767. AppendValue: []byte("b"),
  768. },
  769. },
  770. // different cell whose qualifier should sort before the prior rules
  771. {
  772. FamilyName: "cf",
  773. ColumnQualifier: []byte("q0"),
  774. Rule: &btpb.ReadModifyWriteRule_IncrementAmount{
  775. IncrementAmount: 1,
  776. },
  777. },
  778. },
  779. }
  780. got, err := s.ReadModifyWriteRow(ctx, req)
  781. if err != nil {
  782. t.Fatalf("ReadModifyWriteRow error: %v", err)
  783. }
  784. want := &btpb.ReadModifyWriteRowResponse{
  785. Row: &btpb.Row{
  786. Key: []byte("row-key"),
  787. Families: []*btpb.Family{{
  788. Name: "cf",
  789. Columns: []*btpb.Column{
  790. {
  791. Qualifier: []byte("q0"),
  792. Cells: []*btpb.Cell{{
  793. Value: []byte{0, 0, 0, 0, 0, 0, 0, 1},
  794. }},
  795. },
  796. {
  797. Qualifier: []byte("q1"),
  798. Cells: []*btpb.Cell{{
  799. Value: []byte("ab"),
  800. }},
  801. },
  802. },
  803. }},
  804. },
  805. }
  806. diff := cmp.Diff(got, want, cmpopts.IgnoreFields(btpb.Cell{}, "TimestampMicros"))
  807. if diff != "" {
  808. t.Errorf("unexpected response: %s", diff)
  809. }
  810. }
  811. // helper function to populate table data
  812. func populateTable(ctx context.Context, s *server) (*btapb.Table, error) {
  813. newTbl := btapb.Table{
  814. ColumnFamilies: map[string]*btapb.ColumnFamily{
  815. "cf0": {GcRule: &btapb.GcRule{Rule: &btapb.GcRule_MaxNumVersions{1}}},
  816. },
  817. }
  818. tblInfo, err := s.CreateTable(ctx, &btapb.CreateTableRequest{Parent: "cluster", TableId: "t", Table: &newTbl})
  819. if err != nil {
  820. return nil, err
  821. }
  822. count := 3
  823. mcf := func(i int) *btapb.ModifyColumnFamiliesRequest {
  824. return &btapb.ModifyColumnFamiliesRequest{
  825. Name: tblInfo.Name,
  826. Modifications: []*btapb.ModifyColumnFamiliesRequest_Modification{{
  827. Id: "cf" + strconv.Itoa(i),
  828. Mod: &btapb.ModifyColumnFamiliesRequest_Modification_Create{&btapb.ColumnFamily{}},
  829. }},
  830. }
  831. }
  832. for i := 1; i <= count; i++ {
  833. _, err = s.ModifyColumnFamilies(ctx, mcf(i))
  834. if err != nil {
  835. return nil, err
  836. }
  837. }
  838. // Populate the table
  839. for fc := 0; fc < count; fc++ {
  840. for cc := count; cc > 0; cc-- {
  841. for tc := 0; tc < count; tc++ {
  842. req := &btpb.MutateRowRequest{
  843. TableName: tblInfo.Name,
  844. RowKey: []byte("row"),
  845. Mutations: []*btpb.Mutation{{
  846. Mutation: &btpb.Mutation_SetCell_{&btpb.Mutation_SetCell{
  847. FamilyName: "cf" + strconv.Itoa(fc),
  848. ColumnQualifier: []byte("col" + strconv.Itoa(cc)),
  849. TimestampMicros: int64((tc + 1) * 1000),
  850. Value: []byte{},
  851. }},
  852. }},
  853. }
  854. if _, err := s.MutateRow(ctx, req); err != nil {
  855. return nil, err
  856. }
  857. }
  858. }
  859. }
  860. return tblInfo, nil
  861. }
  862. func TestFilters(t *testing.T) {
  863. tests := []struct {
  864. in *btpb.RowFilter
  865. out int
  866. }{
  867. {in: &btpb.RowFilter{Filter: &btpb.RowFilter_BlockAllFilter{true}}, out: 0},
  868. {in: &btpb.RowFilter{Filter: &btpb.RowFilter_BlockAllFilter{false}}, out: 1},
  869. {in: &btpb.RowFilter{Filter: &btpb.RowFilter_PassAllFilter{true}}, out: 1},
  870. {in: &btpb.RowFilter{Filter: &btpb.RowFilter_PassAllFilter{false}}, out: 0},
  871. }
  872. ctx := context.Background()
  873. s := &server{
  874. tables: make(map[string]*table),
  875. }
  876. tblInfo, err := populateTable(ctx, s)
  877. if err != nil {
  878. t.Fatal(err)
  879. }
  880. req := &btpb.ReadRowsRequest{
  881. TableName: tblInfo.Name,
  882. Rows: &btpb.RowSet{RowKeys: [][]byte{[]byte("row")}},
  883. }
  884. for _, tc := range tests {
  885. req.Filter = tc.in
  886. mock := &MockReadRowsServer{}
  887. if err = s.ReadRows(req, mock); err != nil {
  888. t.Errorf("ReadRows error: %v", err)
  889. continue
  890. }
  891. if len(mock.responses) != tc.out {
  892. t.Errorf("Response count: got %d, want %d", len(mock.responses), tc.out)
  893. continue
  894. }
  895. }
  896. }
  897. func Test_Mutation_DeleteFromColumn(t *testing.T) {
  898. ctx := context.Background()
  899. s := &server{
  900. tables: make(map[string]*table),
  901. }
  902. tblInfo, err := populateTable(ctx, s)
  903. if err != nil {
  904. t.Fatal(err)
  905. }
  906. tests := []struct {
  907. in *btpb.MutateRowRequest
  908. fail bool
  909. }{
  910. {in: &btpb.MutateRowRequest{
  911. TableName: tblInfo.Name,
  912. RowKey: []byte("row"),
  913. Mutations: []*btpb.Mutation{{
  914. Mutation: &btpb.Mutation_DeleteFromColumn_{DeleteFromColumn: &btpb.Mutation_DeleteFromColumn{
  915. FamilyName: "cf1",
  916. ColumnQualifier: []byte("col1"),
  917. TimeRange: &btpb.TimestampRange{
  918. StartTimestampMicros: 2000,
  919. EndTimestampMicros: 1000,
  920. },
  921. }},
  922. }},
  923. },
  924. fail: true,
  925. },
  926. {in: &btpb.MutateRowRequest{
  927. TableName: tblInfo.Name,
  928. RowKey: []byte("row"),
  929. Mutations: []*btpb.Mutation{{
  930. Mutation: &btpb.Mutation_DeleteFromColumn_{DeleteFromColumn: &btpb.Mutation_DeleteFromColumn{
  931. FamilyName: "cf2",
  932. ColumnQualifier: []byte("col2"),
  933. TimeRange: &btpb.TimestampRange{
  934. StartTimestampMicros: 1000,
  935. EndTimestampMicros: 2000,
  936. },
  937. }},
  938. }},
  939. },
  940. fail: false,
  941. },
  942. {in: &btpb.MutateRowRequest{
  943. TableName: tblInfo.Name,
  944. RowKey: []byte("row"),
  945. Mutations: []*btpb.Mutation{{
  946. Mutation: &btpb.Mutation_DeleteFromColumn_{DeleteFromColumn: &btpb.Mutation_DeleteFromColumn{
  947. FamilyName: "cf3",
  948. ColumnQualifier: []byte("col3"),
  949. TimeRange: &btpb.TimestampRange{
  950. StartTimestampMicros: 1000,
  951. EndTimestampMicros: 0,
  952. },
  953. }},
  954. }},
  955. },
  956. fail: false,
  957. },
  958. {in: &btpb.MutateRowRequest{
  959. TableName: tblInfo.Name,
  960. RowKey: []byte("row"),
  961. Mutations: []*btpb.Mutation{{
  962. Mutation: &btpb.Mutation_DeleteFromColumn_{DeleteFromColumn: &btpb.Mutation_DeleteFromColumn{
  963. FamilyName: "cf4",
  964. ColumnQualifier: []byte("col4"),
  965. TimeRange: &btpb.TimestampRange{
  966. StartTimestampMicros: 0,
  967. EndTimestampMicros: 1000,
  968. },
  969. }},
  970. }},
  971. },
  972. fail: true,
  973. },
  974. }
  975. for _, tst := range tests {
  976. _, err = s.MutateRow(ctx, tst.in)
  977. if err != nil && !tst.fail {
  978. t.Errorf("expected passed got failure for : %v \n with err: %v", tst.in, err)
  979. }
  980. if err == nil && tst.fail {
  981. t.Errorf("expected failure got passed for : %v", tst)
  982. }
  983. }
  984. }
  985. func TestFilterRow(t *testing.T) {
  986. row := &row{
  987. key: "row",
  988. families: map[string]*family{
  989. "fam": {
  990. name: "fam",
  991. cells: map[string][]cell{
  992. "col": {{ts: 100, value: []byte("val")}},
  993. },
  994. },
  995. },
  996. }
  997. for _, test := range []struct {
  998. filter *btpb.RowFilter
  999. want bool
  1000. }{
  1001. // The regexp-based filters perform whole-string, case-sensitive matches.
  1002. {&btpb.RowFilter{Filter: &btpb.RowFilter_RowKeyRegexFilter{[]byte("row")}}, true},
  1003. {&btpb.RowFilter{Filter: &btpb.RowFilter_RowKeyRegexFilter{[]byte("ro")}}, false},
  1004. {&btpb.RowFilter{Filter: &btpb.RowFilter_RowKeyRegexFilter{[]byte("ROW")}}, false},
  1005. {&btpb.RowFilter{Filter: &btpb.RowFilter_RowKeyRegexFilter{[]byte("moo")}}, false},
  1006. {&btpb.RowFilter{Filter: &btpb.RowFilter_FamilyNameRegexFilter{"fam"}}, true},
  1007. {&btpb.RowFilter{Filter: &btpb.RowFilter_FamilyNameRegexFilter{"f.*"}}, true},
  1008. {&btpb.RowFilter{Filter: &btpb.RowFilter_FamilyNameRegexFilter{"[fam]+"}}, true},
  1009. {&btpb.RowFilter{Filter: &btpb.RowFilter_FamilyNameRegexFilter{"fa"}}, false},
  1010. {&btpb.RowFilter{Filter: &btpb.RowFilter_FamilyNameRegexFilter{"FAM"}}, false},
  1011. {&btpb.RowFilter{Filter: &btpb.RowFilter_FamilyNameRegexFilter{"moo"}}, false},
  1012. {&btpb.RowFilter{Filter: &btpb.RowFilter_ColumnQualifierRegexFilter{[]byte("col")}}, true},
  1013. {&btpb.RowFilter{Filter: &btpb.RowFilter_ColumnQualifierRegexFilter{[]byte("co")}}, false},
  1014. {&btpb.RowFilter{Filter: &btpb.RowFilter_ColumnQualifierRegexFilter{[]byte("COL")}}, false},
  1015. {&btpb.RowFilter{Filter: &btpb.RowFilter_ColumnQualifierRegexFilter{[]byte("moo")}}, false},
  1016. {&btpb.RowFilter{Filter: &btpb.RowFilter_ValueRegexFilter{[]byte("val")}}, true},
  1017. {&btpb.RowFilter{Filter: &btpb.RowFilter_ValueRegexFilter{[]byte("va")}}, false},
  1018. {&btpb.RowFilter{Filter: &btpb.RowFilter_ValueRegexFilter{[]byte("VAL")}}, false},
  1019. {&btpb.RowFilter{Filter: &btpb.RowFilter_ValueRegexFilter{[]byte("moo")}}, false},
  1020. } {
  1021. got, _ := filterRow(test.filter, row.copy())
  1022. if got != test.want {
  1023. t.Errorf("%s: got %t, want %t", proto.CompactTextString(test.filter), got, test.want)
  1024. }
  1025. }
  1026. }
  1027. func TestFilterRowWithErrors(t *testing.T) {
  1028. row := &row{
  1029. key: "row",
  1030. families: map[string]*family{
  1031. "fam": {
  1032. name: "fam",
  1033. cells: map[string][]cell{
  1034. "col": {{ts: 100, value: []byte("val")}},
  1035. },
  1036. },
  1037. },
  1038. }
  1039. for _, test := range []struct {
  1040. badRegex *btpb.RowFilter
  1041. }{
  1042. {&btpb.RowFilter{Filter: &btpb.RowFilter_RowKeyRegexFilter{[]byte("[")}}},
  1043. {&btpb.RowFilter{Filter: &btpb.RowFilter_FamilyNameRegexFilter{"["}}},
  1044. {&btpb.RowFilter{Filter: &btpb.RowFilter_ColumnQualifierRegexFilter{[]byte("[")}}},
  1045. {&btpb.RowFilter{Filter: &btpb.RowFilter_ValueRegexFilter{[]byte("[")}}},
  1046. {&btpb.RowFilter{Filter: &btpb.RowFilter_Chain_{
  1047. Chain: &btpb.RowFilter_Chain{Filters: []*btpb.RowFilter{
  1048. {Filter: &btpb.RowFilter_ValueRegexFilter{[]byte("[")}}},
  1049. },
  1050. }}},
  1051. {&btpb.RowFilter{Filter: &btpb.RowFilter_Condition_{
  1052. Condition: &btpb.RowFilter_Condition{
  1053. PredicateFilter: &btpb.RowFilter{Filter: &btpb.RowFilter_ValueRegexFilter{[]byte("[")}},
  1054. },
  1055. }}},
  1056. {&btpb.RowFilter{Filter: &btpb.RowFilter_RowSampleFilter{0.0}}}, // 0.0 is invalid.
  1057. {&btpb.RowFilter{Filter: &btpb.RowFilter_RowSampleFilter{1.0}}}, // 1.0 is invalid.
  1058. } {
  1059. got, err := filterRow(test.badRegex, row.copy())
  1060. if got != false {
  1061. t.Errorf("%s: got true, want false", proto.CompactTextString(test.badRegex))
  1062. }
  1063. if err == nil {
  1064. t.Errorf("%s: got no error, want error", proto.CompactTextString(test.badRegex))
  1065. }
  1066. }
  1067. }
  1068. func TestFilterRowWithRowSampleFilter(t *testing.T) {
  1069. prev := randFloat
  1070. randFloat = func() float64 { return 0.5 }
  1071. defer func() { randFloat = prev }()
  1072. for _, test := range []struct {
  1073. p float64
  1074. want bool
  1075. }{
  1076. {0.1, false}, // Less than random float. Return no rows.
  1077. {0.5, false}, // Equal to random float. Return no rows.
  1078. {0.9, true}, // Greater than random float. Return all rows.
  1079. } {
  1080. got, err := filterRow(&btpb.RowFilter{Filter: &btpb.RowFilter_RowSampleFilter{test.p}}, &row{})
  1081. if err != nil {
  1082. t.Fatalf("%f: %v", test.p, err)
  1083. }
  1084. if got != test.want {
  1085. t.Errorf("%v: got %t, want %t", test.p, got, test.want)
  1086. }
  1087. }
  1088. }
  1089. func TestFilterRowWithBinaryColumnQualifier(t *testing.T) {
  1090. rs := []byte{128, 128}
  1091. row := &row{
  1092. key: string(rs),
  1093. families: map[string]*family{
  1094. "fam": {
  1095. name: "fam",
  1096. cells: map[string][]cell{
  1097. string(rs): {{ts: 100, value: []byte("val")}},
  1098. },
  1099. },
  1100. },
  1101. }
  1102. for _, test := range []struct {
  1103. filter []byte
  1104. want bool
  1105. }{
  1106. {[]byte{128, 128}, true}, // succeeds, exact match
  1107. {[]byte{128, 129}, false}, // fails
  1108. {[]byte{128}, false}, // fails, because the regexp must match the entire input
  1109. {[]byte{128, '*'}, true}, // succeeds: 0 or more 128s
  1110. {[]byte{'[', 127, 128, ']', '{', '2', '}'}, true}, // succeeds: exactly two of either 127 or 128
  1111. } {
  1112. got, _ := filterRow(&btpb.RowFilter{Filter: &btpb.RowFilter_ColumnQualifierRegexFilter{test.filter}}, row.copy())
  1113. if got != test.want {
  1114. t.Errorf("%v: got %t, want %t", test.filter, got, test.want)
  1115. }
  1116. }
  1117. }