diff --git a/go/test/endtoend/vtgate/vindex_bindvars/main_test.go b/go/test/endtoend/vtgate/vindex_bindvars/main_test.go index 195ea4a6862..83e20d9aa31 100644 --- a/go/test/endtoend/vtgate/vindex_bindvars/main_test.go +++ b/go/test/endtoend/vtgate/vindex_bindvars/main_test.go @@ -42,6 +42,9 @@ var ( field BIGINT NOT NULL, field2 BIGINT, field3 BIGINT, + field4 BIGINT, + field5 BIGINT, + field6 BIGINT, PRIMARY KEY (id) ) ENGINE=Innodb; @@ -63,6 +66,24 @@ CREATE TABLE lookup3 ( UNIQUE KEY (field3) ) ENGINE=Innodb; +CREATE TABLE lookup4 ( + field4 BIGINT NOT NULL, + keyspace_id binary(8), + UNIQUE KEY (field4) +) ENGINE=Innodb; + +CREATE TABLE lookup5 ( + field5 BIGINT NOT NULL, + keyspace_id binary(8), + UNIQUE KEY (field5) +) ENGINE=Innodb; + +CREATE TABLE lookup6 ( + field6 BIGINT NOT NULL, + keyspace_id binary(8), + UNIQUE KEY (field6) +) ENGINE=Innodb; + CREATE TABLE thex ( id VARBINARY(64) NOT NULL, field BIGINT NOT NULL, @@ -118,6 +139,36 @@ CREATE TABLE thex ( "to": "keyspace_id" }, "owner": "t1" + }, + "lookup4": { + "type": "lookup", + "params": { + "from": "field4", + "read_lock": "exclusive", + "table": "lookup4", + "to": "keyspace_id" + }, + "owner": "t1" + }, + "lookup5": { + "type": "lookup", + "params": { + "from": "field5", + "read_lock": "shared", + "table": "lookup5", + "to": "keyspace_id" + }, + "owner": "t1" + }, + "lookup6": { + "type": "lookup", + "params": { + "from": "field6", + "read_lock": "none", + "table": "lookup6", + "to": "keyspace_id" + }, + "owner": "t1" } }, "tables": { @@ -138,6 +189,18 @@ CREATE TABLE thex ( { "column": "field3", "name": "lookup3" + }, + { + "column": "field4", + "name": "lookup4" + }, + { + "column": "field5", + "name": "lookup5" + }, + { + "column": "field6", + "name": "lookup6" } ] }, @@ -165,6 +228,30 @@ CREATE TABLE thex ( } ] }, + "lookup4": { + "column_vindexes": [ + { + "column": "field4", + "name": "binary_md5_vdx" + } + ] + }, + "lookup5": { + "column_vindexes": [ + { + "column": "field5", + "name": "binary_md5_vdx" + } + ] + }, + "lookup6": { + "column_vindexes": [ + { + "column": "field6", + "name": "binary_md5_vdx" + } + ] + }, "thex": { "column_vindexes": [ { @@ -245,51 +332,51 @@ func TestVindexBindVarOverlap(t *testing.T) { require.Nil(t, err) defer conn.Close() - utils.Exec(t, conn, "INSERT INTO t1 (id, field, field2, field3) VALUES "+ - "(0,1,2,3), "+ - "(1,2,3,4), "+ - "(2,3,4,5), "+ - "(3,4,5,6), "+ - "(4,5,6,7), "+ - "(5,6,7,8), "+ - "(6,7,8,9), "+ - "(7,8,9,10), "+ - "(8,9,10,11), "+ - "(9,10,11,12), "+ - "(10,11,12,13), "+ - "(11,12,13,14), "+ - "(12,13,14,15), "+ - "(13,14,15,16), "+ - "(14,15,16,17), "+ - "(15,16,17,18), "+ - "(16,17,18,19), "+ - "(17,18,19,20), "+ - "(18,19,20,21), "+ - "(19,20,21,22), "+ - "(20,21,22,23)") - result := utils.Exec(t, conn, "select id, field, field2, field3 from t1 order by id") + utils.Exec(t, conn, "INSERT INTO t1 (id, field, field2, field3, field4, field5, field6) VALUES "+ + "(0,1,2,3,4,5,6), "+ + "(1,2,3,4,5,6,7), "+ + "(2,3,4,5,6,7,8), "+ + "(3,4,5,6,7,8,9), "+ + "(4,5,6,7,8,9,10), "+ + "(5,6,7,8,9,10,11), "+ + "(6,7,8,9,10,11,12), "+ + "(7,8,9,10,11,12,13), "+ + "(8,9,10,11,12,13,14), "+ + "(9,10,11,12,13,14,15), "+ + "(10,11,12,13,14,15,16), "+ + "(11,12,13,14,15,16,17), "+ + "(12,13,14,15,16,17,18), "+ + "(13,14,15,16,17,18,19), "+ + "(14,15,16,17,18,19,20), "+ + "(15,16,17,18,19,20,21), "+ + "(16,17,18,19,20,21,22), "+ + "(17,18,19,20,21,22,23), "+ + "(18,19,20,21,22,23,24), "+ + "(19,20,21,22,23,24,25), "+ + "(20,21,22,23,24,25,26)") + result := utils.Exec(t, conn, "select id, field, field2, field3, field4, field5, field6 from t1 order by id") expected := - "[[INT64(0) INT64(1) INT64(2) INT64(3)] " + - "[INT64(1) INT64(2) INT64(3) INT64(4)] " + - "[INT64(2) INT64(3) INT64(4) INT64(5)] " + - "[INT64(3) INT64(4) INT64(5) INT64(6)] " + - "[INT64(4) INT64(5) INT64(6) INT64(7)] " + - "[INT64(5) INT64(6) INT64(7) INT64(8)] " + - "[INT64(6) INT64(7) INT64(8) INT64(9)] " + - "[INT64(7) INT64(8) INT64(9) INT64(10)] " + - "[INT64(8) INT64(9) INT64(10) INT64(11)] " + - "[INT64(9) INT64(10) INT64(11) INT64(12)] " + - "[INT64(10) INT64(11) INT64(12) INT64(13)] " + - "[INT64(11) INT64(12) INT64(13) INT64(14)] " + - "[INT64(12) INT64(13) INT64(14) INT64(15)] " + - "[INT64(13) INT64(14) INT64(15) INT64(16)] " + - "[INT64(14) INT64(15) INT64(16) INT64(17)] " + - "[INT64(15) INT64(16) INT64(17) INT64(18)] " + - "[INT64(16) INT64(17) INT64(18) INT64(19)] " + - "[INT64(17) INT64(18) INT64(19) INT64(20)] " + - "[INT64(18) INT64(19) INT64(20) INT64(21)] " + - "[INT64(19) INT64(20) INT64(21) INT64(22)] " + - "[INT64(20) INT64(21) INT64(22) INT64(23)]]" + "[[INT64(0) INT64(1) INT64(2) INT64(3) INT64(4) INT64(5) INT64(6)] " + + "[INT64(1) INT64(2) INT64(3) INT64(4) INT64(5) INT64(6) INT64(7)] " + + "[INT64(2) INT64(3) INT64(4) INT64(5) INT64(6) INT64(7) INT64(8)] " + + "[INT64(3) INT64(4) INT64(5) INT64(6) INT64(7) INT64(8) INT64(9)] " + + "[INT64(4) INT64(5) INT64(6) INT64(7) INT64(8) INT64(9) INT64(10)] " + + "[INT64(5) INT64(6) INT64(7) INT64(8) INT64(9) INT64(10) INT64(11)] " + + "[INT64(6) INT64(7) INT64(8) INT64(9) INT64(10) INT64(11) INT64(12)] " + + "[INT64(7) INT64(8) INT64(9) INT64(10) INT64(11) INT64(12) INT64(13)] " + + "[INT64(8) INT64(9) INT64(10) INT64(11) INT64(12) INT64(13) INT64(14)] " + + "[INT64(9) INT64(10) INT64(11) INT64(12) INT64(13) INT64(14) INT64(15)] " + + "[INT64(10) INT64(11) INT64(12) INT64(13) INT64(14) INT64(15) INT64(16)] " + + "[INT64(11) INT64(12) INT64(13) INT64(14) INT64(15) INT64(16) INT64(17)] " + + "[INT64(12) INT64(13) INT64(14) INT64(15) INT64(16) INT64(17) INT64(18)] " + + "[INT64(13) INT64(14) INT64(15) INT64(16) INT64(17) INT64(18) INT64(19)] " + + "[INT64(14) INT64(15) INT64(16) INT64(17) INT64(18) INT64(19) INT64(20)] " + + "[INT64(15) INT64(16) INT64(17) INT64(18) INT64(19) INT64(20) INT64(21)] " + + "[INT64(16) INT64(17) INT64(18) INT64(19) INT64(20) INT64(21) INT64(22)] " + + "[INT64(17) INT64(18) INT64(19) INT64(20) INT64(21) INT64(22) INT64(23)] " + + "[INT64(18) INT64(19) INT64(20) INT64(21) INT64(22) INT64(23) INT64(24)] " + + "[INT64(19) INT64(20) INT64(21) INT64(22) INT64(23) INT64(24) INT64(25)] " + + "[INT64(20) INT64(21) INT64(22) INT64(23) INT64(24) INT64(25) INT64(26)]]" assert.Equal(t, expected, fmt.Sprintf("%v", result.Rows)) } diff --git a/go/vt/vtexplain/testdata/test-schema.sql b/go/vt/vtexplain/testdata/test-schema.sql index 716f141e472..06da14c669a 100644 --- a/go/vt/vtexplain/testdata/test-schema.sql +++ b/go/vt/vtexplain/testdata/test-schema.sql @@ -112,6 +112,24 @@ CREATE TABLE orders_id_lookup ( primary key(id) ); +CREATE TABLE orders_id_lookup_exclusive_read_lock ( + id int NOT NULL, + keyspace_id varbinary(128), + primary key(id) +); + +CREATE TABLE orders_id_lookup_shared_read_lock ( + id int NOT NULL, + keyspace_id varbinary(128), + primary key(id) +); + +CREATE TABLE orders_id_lookup_no_read_lock ( + id int NOT NULL, + keyspace_id varbinary(128), + primary key(id) +); + CREATE TABLE orders_id_lookup_no_verify ( id int NOT NULL, keyspace_id varbinary(128), diff --git a/go/vt/vtexplain/testdata/test-vschema.json b/go/vt/vtexplain/testdata/test-vschema.json index ec25beaec50..5d288121507 100644 --- a/go/vt/vtexplain/testdata/test-vschema.json +++ b/go/vt/vtexplain/testdata/test-vschema.json @@ -19,6 +19,36 @@ }, "owner": "orders" }, + "orders_id_vdx_exclusive_read_lock": { + "type": "lookup_unique", + "params": { + "table": "orders_id_lookup_exclusive_read_lock", + "from": "id", + "to": "keyspace_id", + "read_lock": "exclusive" + }, + "owner": "orders" + }, + "orders_id_vdx_shared_read_lock": { + "type": "lookup_unique", + "params": { + "table": "orders_id_lookup_shared_read_lock", + "from": "id", + "to": "keyspace_id", + "read_lock": "shared" + }, + "owner": "orders" + }, + "orders_id_vdx_no_read_lock": { + "type": "lookup_unique", + "params": { + "table": "orders_id_lookup_no_read_lock", + "from": "id", + "to": "keyspace_id", + "read_lock": "none" + }, + "owner": "orders" + }, "orders_id_vdx_no_verify": { "type": "lookup_unique", "params": { @@ -175,6 +205,14 @@ } ] }, + "orders_id_lookup_no_read_lock": { + "column_vindexes": [ + { + "column": "id", + "name": "hash" + } + ] + }, "orders_id_lookup_no_verify": { "column_vindexes": [ { diff --git a/go/vt/vtgate/executor_dml_test.go b/go/vt/vtgate/executor_dml_test.go index 634684936c2..18ba691cb8f 100644 --- a/go/vt/vtgate/executor_dml_test.go +++ b/go/vt/vtgate/executor_dml_test.go @@ -166,8 +166,11 @@ func TestUpdateFromSubQuery(t *testing.T) { func TestUpdateEqualWithNoVerifyAndWriteOnlyLookupUniqueVindexes(t *testing.T) { res := []*sqltypes.Result{sqltypes.MakeTestResult( - sqltypes.MakeTestFields("id|wo_lu_col|nv_lu_col|lu_col|t2_lu_vdx", "int64|int64|int64|int64|int64"), - "1|2|2|1|0", + sqltypes.MakeTestFields( + "id|wo_lu_col|erl_lu_col|srl_lu_col|nrl_lu_col|nv_lu_col|lu_col|t2_lu_vdx", + "int64|int64|int64|int64|int64|int64|int64|int64", + ), + "1|2|2|2|2|2|1|0", )} executor, sbc1, sbc2, sbcLookup := createCustomExecutorSetValues(executorVSchema, res) @@ -175,7 +178,7 @@ func TestUpdateEqualWithNoVerifyAndWriteOnlyLookupUniqueVindexes(t *testing.T) { require.NoError(t, err) wantQueries := []*querypb.BoundQuery{ { - Sql: "select id, wo_lu_col, nv_lu_col, lu_col, lu_col = 5 from t2_lookup where wo_lu_col = 2 for update", + Sql: "select id, wo_lu_col, erl_lu_col, srl_lu_col, nrl_lu_col, nv_lu_col, lu_col, lu_col = 5 from t2_lookup where wo_lu_col = 2 for update", BindVariables: map[string]*querypb.BindVariable{}, }, { Sql: "update t2_lookup set lu_col = 5 where wo_lu_col = 2", @@ -199,7 +202,264 @@ func TestUpdateEqualWithNoVerifyAndWriteOnlyLookupUniqueVindexes(t *testing.T) { "lu_col_0": sqltypes.Int64BindVariable(5), }, } - lookWant := []*querypb.BoundQuery{bq1, bq2, bq1, bq2, bq1, bq2, bq1, bq2, bq1, bq2, bq1, bq2, bq1, bq2, bq1, bq2} + lookWant := []*querypb.BoundQuery{ + bq1, bq2, + bq1, bq2, + bq1, bq2, + bq1, bq2, + bq1, bq2, + bq1, bq2, + bq1, bq2, + bq1, bq2, + } + assertQueries(t, sbcLookup, lookWant) +} + +func TestUpdateInTransactionLookupDefaultReadLock(t *testing.T) { + res := []*sqltypes.Result{sqltypes.MakeTestResult( + sqltypes.MakeTestFields( + "id|wo_lu_col|erl_lu_col|srl_lu_col|nrl_lu_col|nv_lu_col|lu_col|t2_lu_vdx", + "int64|int64|int64|int64|int64|int64|int64|int64", + ), + "1|2|2|2|2|2|1|0", + )} + executor, sbc1, sbc2, sbcLookup := createCustomExecutorSetValues(executorVSchema, res) + + safeSession := NewSafeSession(&vtgatepb.Session{InTransaction: true}) + _, err := executorExecSession( + executor, + "update t2_lookup set lu_col = 5 where nv_lu_col = 2", + nil, + safeSession.Session, + ) + + require.NoError(t, err) + wantQueries := []*querypb.BoundQuery{ + { + Sql: "select id, wo_lu_col, erl_lu_col, srl_lu_col, nrl_lu_col, nv_lu_col, lu_col, lu_col = 5 from t2_lookup where nv_lu_col = 2 and lu_col = 1 for update", + BindVariables: map[string]*querypb.BindVariable{}, + }, { + Sql: "update t2_lookup set lu_col = 5 where nv_lu_col = 2", + BindVariables: map[string]*querypb.BindVariable{}, + }, + } + + assertQueries(t, sbc1, wantQueries) + assertQueries(t, sbc2, wantQueries) + + vars, _ := sqltypes.BuildBindVariable([]any{ + sqltypes.NewInt64(2), + }) + bq1 := &querypb.BoundQuery{ + Sql: "select nv_lu_col, keyspace_id from nv_lu_idx where nv_lu_col in ::nv_lu_col for update", + BindVariables: map[string]*querypb.BindVariable{ + "nv_lu_col": vars, + }, + } + bq2 := &querypb.BoundQuery{ + Sql: "insert into lu_idx(lu_col, keyspace_id) values (:lu_col_0, :keyspace_id_0)", + BindVariables: map[string]*querypb.BindVariable{ + "keyspace_id_0": sqltypes.Uint64BindVariable(1), + "lu_col_0": sqltypes.Int64BindVariable(5), + }, + } + lookWant := []*querypb.BoundQuery{ + bq1, bq2, + bq1, bq2, + bq1, bq2, + bq1, bq2, + bq1, bq2, + bq1, bq2, + bq1, bq2, + bq1, bq2, + } + + assertQueries(t, sbcLookup, lookWant) +} + +func TestUpdateInTransactionLookupExclusiveReadLock(t *testing.T) { + res := []*sqltypes.Result{sqltypes.MakeTestResult( + sqltypes.MakeTestFields( + "id|wo_lu_col|erl_lu_col|srl_lu_col|nrl_lu_col|nv_lu_col|lu_col|t2_lu_vdx", + "int64|int64|int64|int64|int64|int64|int64|int64", + ), + "1|2|2|2|2|2|1|0", + )} + executor, sbc1, sbc2, sbcLookup := createCustomExecutorSetValues(executorVSchema, res) + + safeSession := NewSafeSession(&vtgatepb.Session{InTransaction: true}) + _, err := executorExecSession( + executor, + "update t2_lookup set lu_col = 5 where erl_lu_col = 2", + nil, + safeSession.Session, + ) + + require.NoError(t, err) + wantQueries := []*querypb.BoundQuery{ + { + Sql: "select id, wo_lu_col, erl_lu_col, srl_lu_col, nrl_lu_col, nv_lu_col, lu_col, lu_col = 5 from t2_lookup where nv_lu_col = 2 and lu_col = 1 for update", + BindVariables: map[string]*querypb.BindVariable{}, + }, { + Sql: "update t2_lookup set lu_col = 5 where erl_lu_col = 2", + BindVariables: map[string]*querypb.BindVariable{}, + }, + } + + assertQueries(t, sbc1, wantQueries) + assertQueries(t, sbc2, wantQueries) + + vars, _ := sqltypes.BuildBindVariable([]any{ + sqltypes.NewInt64(2), + }) + bq1 := &querypb.BoundQuery{ + Sql: "select erl_lu_col, keyspace_id from erl_lu_idx where erl_lu_col in ::erl_lu_col for update", + BindVariables: map[string]*querypb.BindVariable{ + "erl_lu_col": vars, + }, + } + bq2 := &querypb.BoundQuery{ + Sql: "insert into lu_idx(lu_col, keyspace_id) values (:lu_col_0, :keyspace_id_0)", + BindVariables: map[string]*querypb.BindVariable{ + "keyspace_id_0": sqltypes.Uint64BindVariable(1), + "lu_col_0": sqltypes.Int64BindVariable(5), + }, + } + lookWant := []*querypb.BoundQuery{ + bq1, bq2, + bq1, bq2, + bq1, bq2, + bq1, bq2, + bq1, bq2, + bq1, bq2, + bq1, bq2, + bq1, bq2, + } + + assertQueries(t, sbcLookup, lookWant) +} + +func TestUpdateInTransactionLookupSharedReadLock(t *testing.T) { + res := []*sqltypes.Result{sqltypes.MakeTestResult( + sqltypes.MakeTestFields( + "id|wo_lu_col|erl_lu_col|srl_lu_col|nrl_lu_col|nv_lu_col|lu_col|t2_lu_vdx", + "int64|int64|int64|int64|int64|int64|int64|int64", + ), + "1|2|2|2|2|2|1|0", + )} + executor, sbc1, sbc2, sbcLookup := createCustomExecutorSetValues(executorVSchema, res) + + safeSession := NewSafeSession(&vtgatepb.Session{InTransaction: true}) + _, err := executorExecSession( + executor, + "update t2_lookup set lu_col = 5 where srl_lu_col = 2", + nil, + safeSession.Session, + ) + + require.NoError(t, err) + wantQueries := []*querypb.BoundQuery{ + { + Sql: "select id, wo_lu_col, erl_lu_col, srl_lu_col, nrl_lu_col, nv_lu_col, lu_col, lu_col = 5 from t2_lookup where nv_lu_col = 2 and lu_col = 1 for update", + BindVariables: map[string]*querypb.BindVariable{}, + }, { + Sql: "update t2_lookup set lu_col = 5 where srl_lu_col = 2", + BindVariables: map[string]*querypb.BindVariable{}, + }, + } + + assertQueries(t, sbc1, wantQueries) + assertQueries(t, sbc2, wantQueries) + + vars, _ := sqltypes.BuildBindVariable([]any{ + sqltypes.NewInt64(2), + }) + bq1 := &querypb.BoundQuery{ + Sql: "select srl_lu_col, keyspace_id from srl_lu_idx where srl_lu_col in ::srl_lu_col lock in share mode", + BindVariables: map[string]*querypb.BindVariable{ + "srl_lu_col": vars, + }, + } + bq2 := &querypb.BoundQuery{ + Sql: "insert into lu_idx(lu_col, keyspace_id) values (:lu_col_0, :keyspace_id_0)", + BindVariables: map[string]*querypb.BindVariable{ + "keyspace_id_0": sqltypes.Uint64BindVariable(1), + "lu_col_0": sqltypes.Int64BindVariable(5), + }, + } + lookWant := []*querypb.BoundQuery{ + bq1, bq2, + bq1, bq2, + bq1, bq2, + bq1, bq2, + bq1, bq2, + bq1, bq2, + bq1, bq2, + bq1, bq2, + } + + assertQueries(t, sbcLookup, lookWant) +} + +func TestUpdateInTransactionLookupNoReadLock(t *testing.T) { + res := []*sqltypes.Result{sqltypes.MakeTestResult( + sqltypes.MakeTestFields( + "id|wo_lu_col|erl_lu_col|srl_lu_col|nrl_lu_col|nv_lu_col|lu_col|t2_lu_vdx", + "int64|int64|int64|int64|int64|int64|int64|int64", + ), + "1|2|2|2|2|2|1|0", + )} + executor, sbc1, sbc2, sbcLookup := createCustomExecutorSetValues(executorVSchema, res) + + safeSession := NewSafeSession(&vtgatepb.Session{InTransaction: true}) + _, err := executorExecSession( + executor, + "update t2_lookup set lu_col = 5 where nrl_lu_col = 2", + nil, + safeSession.Session, + ) + + require.NoError(t, err) + wantQueries := []*querypb.BoundQuery{ + { + Sql: "select id, wo_lu_col, erl_lu_col, srl_lu_col, nrl_lu_col, nv_lu_col, lu_col, lu_col = 5 from t2_lookup where nrl_lu_col = 2 and lu_col = 1 for update", + BindVariables: map[string]*querypb.BindVariable{}, + }, { + Sql: "update t2_lookup set lu_col = 5 where nrl_lu_col = 2", + BindVariables: map[string]*querypb.BindVariable{}, + }, + } + + assertQueries(t, sbc1, wantQueries) + assertQueries(t, sbc2, wantQueries) + + vars, _ := sqltypes.BuildBindVariable([]any{ + sqltypes.NewInt64(2), + }) + bq1 := &querypb.BoundQuery{ + Sql: "select nrl_lu_col, keyspace_id from nrl_lu_idx where nrl_lu_col in ::nrl_lu_col", + BindVariables: map[string]*querypb.BindVariable{ + "nrl_lu_col": vars, + }, + } + bq2 := &querypb.BoundQuery{ + Sql: "insert into lu_idx(lu_col, keyspace_id) values (:lu_col_0, :keyspace_id_0)", + BindVariables: map[string]*querypb.BindVariable{ + "keyspace_id_0": sqltypes.Uint64BindVariable(1), + "lu_col_0": sqltypes.Int64BindVariable(5), + }, + } + lookWant := []*querypb.BoundQuery{ + bq1, bq2, + bq1, bq2, + bq1, bq2, + bq1, bq2, + bq1, bq2, + bq1, bq2, + bq1, bq2, + bq1, bq2, + } + assertQueries(t, sbcLookup, lookWant) } @@ -513,15 +773,18 @@ func TestUpdateEqualWithMultipleLookupVindex(t *testing.T) { )}) sbc1.SetResults([]*sqltypes.Result{sqltypes.MakeTestResult( - sqltypes.MakeTestFields("id|wo_lu_col|nv_lu_col|lu_col|t2_lu_vdx", "int64|int64|int64|int64|int64"), - "1|2|2|1|0", + sqltypes.MakeTestFields( + "id|wo_lu_col|erl_lu_col|srl_lu_col|nrl_lu_col|nv_lu_col|lu_col|t2_lu_vdx", + "int64|int64|int64|int64|int64|int64|int64|int64", + ), + "1|2|2|2|2|2|1|0", )}) _, err := executorExec(executor, "update t2_lookup set lu_col = 5 where wo_lu_col = 2 and lu_col = 1", nil) require.NoError(t, err) wantQueries := []*querypb.BoundQuery{ { - Sql: "select id, wo_lu_col, nv_lu_col, lu_col, lu_col = 5 from t2_lookup where wo_lu_col = 2 and lu_col = 1 for update", + Sql: "select id, wo_lu_col, erl_lu_col, srl_lu_col, nrl_lu_col, nv_lu_col, lu_col, lu_col = 5 from t2_lookup where wo_lu_col = 2 and lu_col = 1 for update", BindVariables: map[string]*querypb.BindVariable{}, }, { Sql: "update t2_lookup set lu_col = 5 where wo_lu_col = 2 and lu_col = 1", @@ -564,16 +827,19 @@ func TestUpdateUseHigherCostVindexIfBackfilling(t *testing.T) { )}) sbc1.SetResults([]*sqltypes.Result{sqltypes.MakeTestResult( - sqltypes.MakeTestFields("id|wo_lu_col|nv_lu_col|lu_col|t2_lu_vdx", "int64|int64|int64|int64|int64"), - "1|2|2|1|0", - "1|2|2|2|0", + sqltypes.MakeTestFields( + "id|wo_lu_col|erl_lu_col|srl_lu_col|nrl_lu_col|nv_lu_col|lu_col|t2_lu_vdx", + "int64|int64|int64|int64|int64|int64|int64|int64", + ), + "1|2|2|2|2|2|1|0", + "1|2|2|2|2|2|2|0", )}) _, err := executorExec(executor, "update t2_lookup set lu_col = 5 where wo_lu_col = 2 and lu_col in (1, 2)", nil) require.NoError(t, err) wantQueries := []*querypb.BoundQuery{ { - Sql: "select id, wo_lu_col, nv_lu_col, lu_col, lu_col = 5 from t2_lookup where wo_lu_col = 2 and lu_col in (1, 2) for update", + Sql: "select id, wo_lu_col, erl_lu_col, srl_lu_col, nrl_lu_col, nv_lu_col, lu_col, lu_col = 5 from t2_lookup where wo_lu_col = 2 and lu_col in (1, 2) for update", BindVariables: map[string]*querypb.BindVariable{}, }, { Sql: "update t2_lookup set lu_col = 5 where wo_lu_col = 2 and lu_col in (1, 2)", @@ -621,8 +887,11 @@ func TestUpdateUseHigherCostVindexIfBackfilling(t *testing.T) { func TestDeleteEqualWithNoVerifyAndWriteOnlyLookupUniqueVindex(t *testing.T) { res := []*sqltypes.Result{sqltypes.MakeTestResult( - sqltypes.MakeTestFields("id|wo_lu_col|nv_lu_col|lu_col", "int64|int64|int64|int64"), - "1|1|1|1", + sqltypes.MakeTestFields( + "id|wo_lu_col|erl_lu_col|srl_lu_col|nrl_lu_col|nv_lu_col|lu_col", + "int64|int64|int64|int64|int64|int64|int64", + ), + "1|1|1|1|1|1|1", )} executor, sbc1, sbc2, sbcLookup := createCustomExecutorSetValues(executorVSchema, res) @@ -630,7 +899,7 @@ func TestDeleteEqualWithNoVerifyAndWriteOnlyLookupUniqueVindex(t *testing.T) { require.NoError(t, err) wantQueries := []*querypb.BoundQuery{ { - Sql: "select id, wo_lu_col, nv_lu_col, lu_col from t2_lookup where wo_lu_col = 1 for update", + Sql: "select id, wo_lu_col, erl_lu_col, srl_lu_col, nrl_lu_col, nv_lu_col, lu_col from t2_lookup where wo_lu_col = 1 for update", BindVariables: map[string]*querypb.BindVariable{}, }, { Sql: "delete from t2_lookup where wo_lu_col = 1", @@ -645,20 +914,50 @@ func TestDeleteEqualWithNoVerifyAndWriteOnlyLookupUniqueVindex(t *testing.T) { }, } bq2 := &querypb.BoundQuery{ + Sql: "delete from erl_lu_idx where erl_lu_col = :erl_lu_col and keyspace_id = :keyspace_id", + BindVariables: map[string]*querypb.BindVariable{ + "keyspace_id": {Type: querypb.Type_VARBINARY, Value: []byte("\x16k@\xb4J\xbaK\xd6")}, + "erl_lu_col": sqltypes.Int64BindVariable(1), + }, + } + bq3 := &querypb.BoundQuery{ + Sql: "delete from srl_lu_idx where srl_lu_col = :srl_lu_col and keyspace_id = :keyspace_id", + BindVariables: map[string]*querypb.BindVariable{ + "keyspace_id": {Type: querypb.Type_VARBINARY, Value: []byte("\x16k@\xb4J\xbaK\xd6")}, + "srl_lu_col": sqltypes.Int64BindVariable(1), + }, + } + bq4 := &querypb.BoundQuery{ + Sql: "delete from nrl_lu_idx where nrl_lu_col = :nrl_lu_col and keyspace_id = :keyspace_id", + BindVariables: map[string]*querypb.BindVariable{ + "keyspace_id": {Type: querypb.Type_VARBINARY, Value: []byte("\x16k@\xb4J\xbaK\xd6")}, + "nrl_lu_col": sqltypes.Int64BindVariable(1), + }, + } + bq5 := &querypb.BoundQuery{ Sql: "delete from nv_lu_idx where nv_lu_col = :nv_lu_col and keyspace_id = :keyspace_id", BindVariables: map[string]*querypb.BindVariable{ "keyspace_id": {Type: querypb.Type_VARBINARY, Value: []byte("\x16k@\xb4J\xbaK\xd6")}, "nv_lu_col": sqltypes.Int64BindVariable(1), }, } - bq3 := &querypb.BoundQuery{ + bq6 := &querypb.BoundQuery{ Sql: "delete from lu_idx where lu_col = :lu_col and keyspace_id = :keyspace_id", BindVariables: map[string]*querypb.BindVariable{ "keyspace_id": sqltypes.Uint64BindVariable(1), "lu_col": sqltypes.Int64BindVariable(1), }, } - lookWant := []*querypb.BoundQuery{bq1, bq2, bq3, bq1, bq2, bq3, bq1, bq2, bq3, bq1, bq2, bq3, bq1, bq2, bq3, bq1, bq2, bq3, bq1, bq2, bq3, bq1, bq2, bq3} + lookWant := []*querypb.BoundQuery{ + bq1, bq2, bq3, bq4, bq5, bq6, + bq1, bq2, bq3, bq4, bq5, bq6, + bq1, bq2, bq3, bq4, bq5, bq6, + bq1, bq2, bq3, bq4, bq5, bq6, + bq1, bq2, bq3, bq4, bq5, bq6, + bq1, bq2, bq3, bq4, bq5, bq6, + bq1, bq2, bq3, bq4, bq5, bq6, + bq1, bq2, bq3, bq4, bq5, bq6, + } assertQueries(t, sbcLookup, lookWant) assertQueries(t, sbc1, wantQueries) assertQueries(t, sbc2, wantQueries) @@ -673,15 +972,18 @@ func TestDeleteEqualWithMultipleLookupVindex(t *testing.T) { )}) sbc1.SetResults([]*sqltypes.Result{sqltypes.MakeTestResult( - sqltypes.MakeTestFields("id|wo_lu_col|nv_lu_col|lu_col", "int64|int64|int64|int64"), - "1|1|1|1", + sqltypes.MakeTestFields( + "id|wo_lu_col|erl_lu_col|srl_lu_col|nrl_lu_col|nv_lu_col|lu_col", + "int64|int64|int64|int64|int64|int64|int64", + ), + "1|1|1|1|1|1|1", )}) _, err := executorExec(executor, "delete from t2_lookup where wo_lu_col = 1 and lu_col = 1", nil) require.NoError(t, err) wantQueries := []*querypb.BoundQuery{ { - Sql: "select id, wo_lu_col, nv_lu_col, lu_col from t2_lookup where wo_lu_col = 1 and lu_col = 1 for update", + Sql: "select id, wo_lu_col, erl_lu_col, srl_lu_col, nrl_lu_col, nv_lu_col, lu_col from t2_lookup where wo_lu_col = 1 and lu_col = 1 for update", BindVariables: map[string]*querypb.BindVariable{}, }, { Sql: "delete from t2_lookup where wo_lu_col = 1 and lu_col = 1", @@ -702,6 +1004,24 @@ func TestDeleteEqualWithMultipleLookupVindex(t *testing.T) { "keyspace_id": {Type: querypb.Type_VARBINARY, Value: []byte("\x16k@\xb4J\xbaK\xd6")}, "wo_lu_col": sqltypes.Int64BindVariable(1), }, + }, { + Sql: "delete from erl_lu_idx where erl_lu_col = :erl_lu_col and keyspace_id = :keyspace_id", + BindVariables: map[string]*querypb.BindVariable{ + "keyspace_id": {Type: querypb.Type_VARBINARY, Value: []byte("\x16k@\xb4J\xbaK\xd6")}, + "erl_lu_col": sqltypes.Int64BindVariable(1), + }, + }, { + Sql: "delete from srl_lu_idx where srl_lu_col = :srl_lu_col and keyspace_id = :keyspace_id", + BindVariables: map[string]*querypb.BindVariable{ + "keyspace_id": {Type: querypb.Type_VARBINARY, Value: []byte("\x16k@\xb4J\xbaK\xd6")}, + "srl_lu_col": sqltypes.Int64BindVariable(1), + }, + }, { + Sql: "delete from nrl_lu_idx where nrl_lu_col = :nrl_lu_col and keyspace_id = :keyspace_id", + BindVariables: map[string]*querypb.BindVariable{ + "keyspace_id": {Type: querypb.Type_VARBINARY, Value: []byte("\x16k@\xb4J\xbaK\xd6")}, + "nrl_lu_col": sqltypes.Int64BindVariable(1), + }, }, { Sql: "delete from nv_lu_idx where nv_lu_col = :nv_lu_col and keyspace_id = :keyspace_id", BindVariables: map[string]*querypb.BindVariable{ @@ -731,16 +1051,19 @@ func TestDeleteUseHigherCostVindexIfBackfilling(t *testing.T) { )}) sbc1.SetResults([]*sqltypes.Result{sqltypes.MakeTestResult( - sqltypes.MakeTestFields("id|wo_lu_col|nv_lu_col|lu_col", "int64|int64|int64|int64"), - "1|1|1|1", - "1|1|1|2", + sqltypes.MakeTestFields( + "id|wo_lu_col|erl_lu_col|srl_lu_col|nrl_lu_col|nv_lu_col|lu_col", + "int64|int64|int64|int64|int64|int64|int64", + ), + "1|1|1|1|1|1|1", + "1|1|1|1|1|1|2", )}) _, err := executorExec(executor, "delete from t2_lookup where wo_lu_col = 1 and lu_col in (1, 2)", nil) require.NoError(t, err) wantQueries := []*querypb.BoundQuery{ { - Sql: "select id, wo_lu_col, nv_lu_col, lu_col from t2_lookup where wo_lu_col = 1 and lu_col in (1, 2) for update", + Sql: "select id, wo_lu_col, erl_lu_col, srl_lu_col, nrl_lu_col, nv_lu_col, lu_col from t2_lookup where wo_lu_col = 1 and lu_col in (1, 2) for update", BindVariables: map[string]*querypb.BindVariable{}, }, { Sql: "delete from t2_lookup where wo_lu_col = 1 and lu_col in (1, 2)", @@ -762,6 +1085,24 @@ func TestDeleteUseHigherCostVindexIfBackfilling(t *testing.T) { "keyspace_id": {Type: querypb.Type_VARBINARY, Value: []byte("\x16k@\xb4J\xbaK\xd6")}, "wo_lu_col": sqltypes.Int64BindVariable(1), }, + }, { + Sql: "delete from erl_lu_idx where erl_lu_col = :erl_lu_col and keyspace_id = :keyspace_id", + BindVariables: map[string]*querypb.BindVariable{ + "keyspace_id": {Type: querypb.Type_VARBINARY, Value: []byte("\x16k@\xb4J\xbaK\xd6")}, + "erl_lu_col": sqltypes.Int64BindVariable(1), + }, + }, { + Sql: "delete from srl_lu_idx where srl_lu_col = :srl_lu_col and keyspace_id = :keyspace_id", + BindVariables: map[string]*querypb.BindVariable{ + "keyspace_id": {Type: querypb.Type_VARBINARY, Value: []byte("\x16k@\xb4J\xbaK\xd6")}, + "srl_lu_col": sqltypes.Int64BindVariable(1), + }, + }, { + Sql: "delete from nrl_lu_idx where nrl_lu_col = :nrl_lu_col and keyspace_id = :keyspace_id", + BindVariables: map[string]*querypb.BindVariable{ + "keyspace_id": {Type: querypb.Type_VARBINARY, Value: []byte("\x16k@\xb4J\xbaK\xd6")}, + "nrl_lu_col": sqltypes.Int64BindVariable(1), + }, }, { Sql: "delete from nv_lu_idx where nv_lu_col = :nv_lu_col and keyspace_id = :keyspace_id", BindVariables: map[string]*querypb.BindVariable{ @@ -780,6 +1121,24 @@ func TestDeleteUseHigherCostVindexIfBackfilling(t *testing.T) { "keyspace_id": {Type: querypb.Type_VARBINARY, Value: []byte("\x16k@\xb4J\xbaK\xd6")}, "wo_lu_col": sqltypes.Int64BindVariable(1), }, + }, { + Sql: "delete from erl_lu_idx where erl_lu_col = :erl_lu_col and keyspace_id = :keyspace_id", + BindVariables: map[string]*querypb.BindVariable{ + "keyspace_id": {Type: querypb.Type_VARBINARY, Value: []byte("\x16k@\xb4J\xbaK\xd6")}, + "erl_lu_col": sqltypes.Int64BindVariable(1), + }, + }, { + Sql: "delete from srl_lu_idx where srl_lu_col = :srl_lu_col and keyspace_id = :keyspace_id", + BindVariables: map[string]*querypb.BindVariable{ + "keyspace_id": {Type: querypb.Type_VARBINARY, Value: []byte("\x16k@\xb4J\xbaK\xd6")}, + "srl_lu_col": sqltypes.Int64BindVariable(1), + }, + }, { + Sql: "delete from nrl_lu_idx where nrl_lu_col = :nrl_lu_col and keyspace_id = :keyspace_id", + BindVariables: map[string]*querypb.BindVariable{ + "keyspace_id": {Type: querypb.Type_VARBINARY, Value: []byte("\x16k@\xb4J\xbaK\xd6")}, + "nrl_lu_col": sqltypes.Int64BindVariable(1), + }, }, { Sql: "delete from nv_lu_idx where nv_lu_col = :nv_lu_col and keyspace_id = :keyspace_id", BindVariables: map[string]*querypb.BindVariable{ diff --git a/go/vt/vtgate/executor_framework_test.go b/go/vt/vtgate/executor_framework_test.go index d302a6451c7..0b821822df8 100644 --- a/go/vt/vtgate/executor_framework_test.go +++ b/go/vt/vtgate/executor_framework_test.go @@ -122,6 +122,36 @@ var executorVSchema = ` "write_only": "true" }, "owner": "t2_lookup" + }, + "t2_erl_lu_vdx": { + "type": "lookup_unique", + "params": { + "table": "TestUnsharded.erl_lu_idx", + "from": "erl_lu_col", + "to": "keyspace_id", + "read_lock": "exclusive" + }, + "owner": "t2_lookup" + }, + "t2_srl_lu_vdx": { + "type": "lookup_unique", + "params": { + "table": "TestUnsharded.srl_lu_idx", + "from": "srl_lu_col", + "to": "keyspace_id", + "read_lock": "shared" + }, + "owner": "t2_lookup" + }, + "t2_nrl_lu_vdx": { + "type": "lookup_unique", + "params": { + "table": "TestUnsharded.nrl_lu_idx", + "from": "nrl_lu_col", + "to": "keyspace_id", + "read_lock": "none" + }, + "owner": "t2_lookup" }, "t2_nv_lu_vdx": { "type": "lookup_unique", @@ -311,8 +341,20 @@ var executorVSchema = ` "name": "hash_index" }, { - "column": "wo_lu_col", - "name": "t2_wo_lu_vdx" + "column": "wo_lu_col", + "name": "t2_wo_lu_vdx" + }, + { + "column": "erl_lu_col", + "name": "t2_erl_lu_vdx" + }, + { + "column": "srl_lu_col", + "name": "t2_srl_lu_vdx" + }, + { + "column": "nrl_lu_col", + "name": "t2_nrl_lu_vdx" }, { "column": "nv_lu_col", @@ -364,6 +406,9 @@ var unshardedVSchema = ` } }, "wo_lu_idx": {}, + "erl_lu_idx": {}, + "srl_lu_idx": {}, + "nrl_lu_idx": {}, "nv_lu_idx": {}, "lu_idx": {}, "simple": {} diff --git a/go/vt/vtgate/executor_test.go b/go/vt/vtgate/executor_test.go index c52a878afa7..3e5100999f1 100644 --- a/go/vt/vtgate/executor_test.go +++ b/go/vt/vtgate/executor_test.go @@ -865,8 +865,11 @@ func TestExecutorShow(t *testing.T) { buildVarCharRow("TestExecutor", "name_user_map", "lookup_hash", "from=name; table=name_user_map; to=user_id", "user"), buildVarCharRow("TestExecutor", "regional_vdx", "region_experimental", "region_bytes=1", ""), buildVarCharRow("TestExecutor", "t1_lkp_vdx", "consistent_lookup_unique", "from=unq_col; table=t1_lkp_idx; to=keyspace_id", "t1"), + buildVarCharRow("TestExecutor", "t2_erl_lu_vdx", "lookup_unique", "from=erl_lu_col; read_lock=exclusive; table=TestUnsharded.erl_lu_idx; to=keyspace_id", "t2_lookup"), buildVarCharRow("TestExecutor", "t2_lu_vdx", "lookup_hash_unique", "from=lu_col; table=TestUnsharded.lu_idx; to=keyspace_id", "t2_lookup"), + buildVarCharRow("TestExecutor", "t2_nrl_lu_vdx", "lookup_unique", "from=nrl_lu_col; read_lock=none; table=TestUnsharded.nrl_lu_idx; to=keyspace_id", "t2_lookup"), buildVarCharRow("TestExecutor", "t2_nv_lu_vdx", "lookup_unique", "from=nv_lu_col; no_verify=true; table=TestUnsharded.nv_lu_idx; to=keyspace_id", "t2_lookup"), + buildVarCharRow("TestExecutor", "t2_srl_lu_vdx", "lookup_unique", "from=srl_lu_col; read_lock=shared; table=TestUnsharded.srl_lu_idx; to=keyspace_id", "t2_lookup"), buildVarCharRow("TestExecutor", "t2_wo_lu_vdx", "lookup_unique", "from=wo_lu_col; table=TestUnsharded.wo_lu_idx; to=keyspace_id; write_only=true", "t2_lookup"), buildVarCharRow("TestMultiCol", "multicol_vdx", "multicol", "column_bytes=1,3,4; column_count=3; column_vindex=hash,binary,unicode_loose_xxhash", ""), }, @@ -998,14 +1001,17 @@ func TestExecutorShow(t *testing.T) { Fields: buildVarCharFields("Tables"), Rows: [][]sqltypes.Value{ buildVarCharRow("dual"), + buildVarCharRow("erl_lu_idx"), buildVarCharRow("ins_lookup"), buildVarCharRow("lu_idx"), buildVarCharRow("main1"), buildVarCharRow("music_user_map"), buildVarCharRow("name_lastname_keyspace_id_map"), buildVarCharRow("name_user_map"), + buildVarCharRow("nrl_lu_idx"), buildVarCharRow("nv_lu_idx"), buildVarCharRow("simple"), + buildVarCharRow("srl_lu_idx"), buildVarCharRow("user_msgs"), buildVarCharRow("user_seq"), buildVarCharRow("wo_lu_idx"), diff --git a/go/vt/vtgate/vindexes/cached_size.go b/go/vt/vtgate/vindexes/cached_size.go index 9581a411b4b..341ffa27d9f 100644 --- a/go/vt/vtgate/vindexes/cached_size.go +++ b/go/vt/vtgate/vindexes/cached_size.go @@ -180,7 +180,7 @@ func (cached *LookupHash) CachedSize(alloc bool) int64 { } size := int64(0) if alloc { - size += int64(144) + size += int64(176) } // field name string size += hack.RuntimeAllocSize(int64(len(cached.name))) @@ -194,7 +194,7 @@ func (cached *LookupHashUnique) CachedSize(alloc bool) int64 { } size := int64(0) if alloc { - size += int64(144) + size += int64(176) } // field name string size += hack.RuntimeAllocSize(int64(len(cached.name))) @@ -208,7 +208,7 @@ func (cached *LookupNonUnique) CachedSize(alloc bool) int64 { } size := int64(0) if alloc { - size += int64(144) + size += int64(176) } // field name string size += hack.RuntimeAllocSize(int64(len(cached.name))) @@ -222,7 +222,7 @@ func (cached *LookupUnicodeLooseMD5Hash) CachedSize(alloc bool) int64 { } size := int64(0) if alloc { - size += int64(144) + size += int64(176) } // field name string size += hack.RuntimeAllocSize(int64(len(cached.name))) @@ -236,7 +236,7 @@ func (cached *LookupUnicodeLooseMD5HashUnique) CachedSize(alloc bool) int64 { } size := int64(0) if alloc { - size += int64(144) + size += int64(176) } // field name string size += hack.RuntimeAllocSize(int64(len(cached.name))) @@ -250,7 +250,7 @@ func (cached *LookupUnique) CachedSize(alloc bool) int64 { } size := int64(0) if alloc { - size += int64(144) + size += int64(176) } // field name string size += hack.RuntimeAllocSize(int64(len(cached.name))) @@ -492,7 +492,7 @@ func (cached *clCommon) CachedSize(alloc bool) int64 { } size := int64(0) if alloc { - size += int64(256) + size += int64(288) } // field name string size += hack.RuntimeAllocSize(int64(len(cached.name))) @@ -525,7 +525,7 @@ func (cached *lookupInternal) CachedSize(alloc bool) int64 { } size := int64(0) if alloc { - size += int64(112) + size += int64(144) } // field Table string size += hack.RuntimeAllocSize(int64(len(cached.Table))) @@ -538,8 +538,12 @@ func (cached *lookupInternal) CachedSize(alloc bool) int64 { } // field To string size += hack.RuntimeAllocSize(int64(len(cached.To))) + // field ReadLock string + size += hack.RuntimeAllocSize(int64(len(cached.ReadLock))) // field sel string size += hack.RuntimeAllocSize(int64(len(cached.sel))) + // field selTxDml string + size += hack.RuntimeAllocSize(int64(len(cached.selTxDml))) // field ver string size += hack.RuntimeAllocSize(int64(len(cached.ver))) // field del string diff --git a/go/vt/vtgate/vindexes/lookup_internal.go b/go/vt/vtgate/vindexes/lookup_internal.go index e0bd06196c1..e1390fca752 100644 --- a/go/vt/vtgate/vindexes/lookup_internal.go +++ b/go/vt/vtgate/vindexes/lookup_internal.go @@ -33,17 +33,31 @@ import ( vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" ) +var ( + readLockExclusive = "exclusive" + readLockShared = "shared" + readLockNone = "none" + readLockDefault = readLockExclusive + + readLockExprs map[string]string = map[string]string{ + readLockExclusive: "for update", + readLockShared: "lock in share mode", + readLockNone: "", + } +) + // lookupInternal implements the functions for the Lookup vindexes. type lookupInternal struct { - Table string `json:"table"` - FromColumns []string `json:"from_columns"` - To string `json:"to"` - Autocommit bool `json:"autocommit,omitempty"` - MultiShardAutocommit bool `json:"multi_shard_autocommit,omitempty"` - Upsert bool `json:"upsert,omitempty"` - IgnoreNulls bool `json:"ignore_nulls,omitempty"` - BatchLookup bool `json:"batch_lookup,omitempty"` - sel, ver, del string // sel: map query, ver: verify query, del: delete query + Table string `json:"table"` + FromColumns []string `json:"from_columns"` + To string `json:"to"` + Autocommit bool `json:"autocommit,omitempty"` + MultiShardAutocommit bool `json:"multi_shard_autocommit,omitempty"` + Upsert bool `json:"upsert,omitempty"` + IgnoreNulls bool `json:"ignore_nulls,omitempty"` + BatchLookup bool `json:"batch_lookup,omitempty"` + ReadLock string `json:"read_lock,omitempty"` + sel, selTxDml, ver, del string // sel: map query, ver: verify query, del: delete query } func (lkp *lookupInternal) Init(lookupQueryParams map[string]string, autocommit, upsert, multiShardAutocommit bool) error { @@ -64,6 +78,12 @@ func (lkp *lookupInternal) Init(lookupQueryParams map[string]string, autocommit, if err != nil { return err } + if readLock, ok := lookupQueryParams["read_lock"]; ok { + if _, valid := readLockExprs[readLock]; !valid { + return fmt.Errorf("invalid read_lock value: %s", readLock) + } + lkp.ReadLock = readLock + } lkp.Autocommit = autocommit lkp.Upsert = upsert @@ -76,6 +96,15 @@ func (lkp *lookupInternal) Init(lookupQueryParams map[string]string, autocommit, // as part of face 2 of https://github.com/vitessio/vitess/issues/3481 // For now multi column behaves as a single column for Map and Verify operations lkp.sel = fmt.Sprintf("select %s, %s from %s where %s in ::%s", lkp.FromColumns[0], lkp.To, lkp.Table, lkp.FromColumns[0], lkp.FromColumns[0]) + if lkp.ReadLock != readLockNone { + lockExpr, ok := readLockExprs[lkp.ReadLock] + if !ok { + lockExpr = readLockExprs[readLockDefault] + } + lkp.selTxDml = fmt.Sprintf("%s %s", lkp.sel, lockExpr) + } else { + lkp.selTxDml = lkp.sel + } lkp.ver = fmt.Sprintf("select %s from %s where %s = :%s and %s = :%s", lkp.FromColumns[0], lkp.Table, lkp.FromColumns[0], lkp.FromColumns[0], lkp.To, lkp.To) lkp.del = lkp.initDelStmt() return nil @@ -90,9 +119,11 @@ func (lkp *lookupInternal) Lookup(ctx context.Context, vcursor VCursor, ids []sq if lkp.Autocommit { co = vtgatepb.CommitOrder_AUTOCOMMIT } - sel := lkp.sel + var sel string if vcursor.InTransactionAndIsDML() { - sel = sel + " for update" + sel = lkp.selTxDml + } else { + sel = lkp.sel } if ids[0].IsIntegral() || lkp.BatchLookup { // for integral types, batch query all ids and then map them back to the input order