diff --git a/data/test/vtexplain/multi-output/selectsharded-output.txt b/data/test/vtexplain/multi-output/selectsharded-output.txt index 9d1d878b4f5..6f458bdd8b5 100644 --- a/data/test/vtexplain/multi-output/selectsharded-output.txt +++ b/data/test/vtexplain/multi-output/selectsharded-output.txt @@ -118,3 +118,11 @@ select name from user where exists (select id from t1) /* non-correlated subquer 2 ks_sharded/c0-: select name from user where 1 limit 10001 /* non-correlated subquery as EXISTS */ ---------------------------------------------------------------------- +select * from name_info order by info /* select * and order by varchar column */ + +1 ks_sharded/-40: select name, info, weight_string(info) from name_info order by info asc limit 10001 /* select * and order by varchar column */ +1 ks_sharded/40-80: select name, info, weight_string(info) from name_info order by info asc limit 10001 /* select * and order by varchar column */ +1 ks_sharded/80-c0: select name, info, weight_string(info) from name_info order by info asc limit 10001 /* select * and order by varchar column */ +1 ks_sharded/c0-: select name, info, weight_string(info) from name_info order by info asc limit 10001 /* select * and order by varchar column */ + +---------------------------------------------------------------------- diff --git a/data/test/vtexplain/selectsharded-queries.sql b/data/test/vtexplain/selectsharded-queries.sql index ce6148c5613..a51570ca833 100644 --- a/data/test/vtexplain/selectsharded-queries.sql +++ b/data/test/vtexplain/selectsharded-queries.sql @@ -18,3 +18,5 @@ select name from user where id = (select id from t1) /* non-correlated subquery select name from user where id in (select id from t1) /* non-correlated subquery in IN clause */; select name from user where id not in (select id from t1) /* non-correlated subquery in NOT IN clause */; select name from user where exists (select id from t1) /* non-correlated subquery as EXISTS */; + +select * from name_info order by info /* select * and order by varchar column */ diff --git a/data/test/vtexplain/test-vschema.json b/data/test/vtexplain/test-vschema.json index 6c92078f5ce..0ad8a36fc41 100644 --- a/data/test/vtexplain/test-vschema.json +++ b/data/test/vtexplain/test-vschema.json @@ -1,13 +1,13 @@ { "ks_unsharded": { - "Sharded": false, - "Tables": { + "sharded": false, + "tables": { "t1": {}, "table_not_in_schema": {} } }, "ks_sharded": { - "Sharded": true, + "sharded": true, "vindexes": { "music_user_map": { "type": "lookup_hash_unique", @@ -81,7 +81,18 @@ "column": "name", "name": "md5" } - ] + ], + "columns": [ + { + "name": "name", + "type": "VARCHAR" + }, + { + "name": "info", + "type": "VARCHAR" + } + ], + "column_list_authoritative": true } } } diff --git a/data/test/vtgate/postprocess_cases.txt b/data/test/vtgate/postprocess_cases.txt index d4a7cee8d01..4f21d74d498 100644 --- a/data/test/vtgate/postprocess_cases.txt +++ b/data/test/vtgate/postprocess_cases.txt @@ -168,6 +168,49 @@ } } +# ORDER BY works for select * from authoritative table +"select * from authoritative order by user_id" +{ + "Original": "select * from authoritative order by user_id", + "Instructions": { + "Opcode": "SelectScatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "select user_id, col1, col2 from authoritative order by user_id asc", + "FieldQuery": "select user_id, col1, col2 from authoritative where 1 != 1", + "OrderBy": [ + { + "Col": 0, + "Desc": false + } + ] + } +} + +# ORDER BY works for select * from authoritative table +"select * from authoritative order by col1" +{ + "Original": "select * from authoritative order by col1", + "Instructions": { + "Opcode": "SelectScatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "select user_id, col1, col2, weight_string(col1) from authoritative order by col1 asc", + "FieldQuery": "select user_id, col1, col2, weight_string(col1) from authoritative where 1 != 1", + "OrderBy": [ + { + "Col": 3, + "Desc": false + } + ], + "TruncateColumnCount": 3 + } +} + # ORDER BY on scatter with text column "select a, textcol1, b from user order by a, textcol1, b" { diff --git a/data/test/vtgate/schema_test.json b/data/test/vtgate/schema_test.json index 0369446dcb5..01b19f5c2c2 100644 --- a/data/test/vtgate/schema_test.json +++ b/data/test/vtgate/schema_test.json @@ -128,6 +128,27 @@ } ] }, + "authoritative": { + "column_vindexes": [ + { + "column": "user_id", + "name": "user_index" + } + ], + "columns": [ + { + "name": "user_id" + }, + { + "name": "col1", + "type": "VARCHAR" + }, + { + "name": "col2" + } + ], + "column_list_authoritative": true + }, "multicolvin": { "column_vindexes": [ { diff --git a/data/test/vtgate/select_cases.txt b/data/test/vtgate/select_cases.txt index 36a83fa03dd..3380f73e25a 100644 --- a/data/test/vtgate/select_cases.txt +++ b/data/test/vtgate/select_cases.txt @@ -183,9 +183,9 @@ } # fully qualified '*' expression for simple route -"select user.user.* from user" +"select user.user.* from user.user" { - "Original": "select user.user.* from user", + "Original": "select user.user.* from user.user", "Instructions": { "Opcode": "SelectScatter", "Keyspace": { @@ -197,9 +197,84 @@ } } -# invalid keyspace for '*' expression for simple route -"select a.user.* from user" -"cannot resolve a.user.* to keyspace user" +# select * from authoritative table +"select * from authoritative" +{ + "Original": "select * from authoritative", + "Instructions": { + "Opcode": "SelectScatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "select user_id, col1, col2 from authoritative", + "FieldQuery": "select user_id, col1, col2 from authoritative where 1 != 1" + } +} + +# select * from join of authoritative tables +"select * from authoritative a join authoritative b on a.user_id=b.user_id" +{ + "Original": "select * from authoritative a join authoritative b on a.user_id=b.user_id", + "Instructions": { + "Opcode": "SelectScatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "select a.user_id as user_id, a.col1 as col1, a.col2 as col2, b.user_id as user_id, b.col1 as col1, b.col2 as col2 from authoritative as a join authoritative as b on a.user_id = b.user_id", + "FieldQuery": "select a.user_id as user_id, a.col1 as col1, a.col2 as col2, b.user_id as user_id, b.col1 as col1, b.col2 as col2 from authoritative as a join authoritative as b on a.user_id = b.user_id where 1 != 1" + } +} + +# test table lookup failure for authoritative code path +"select a.* from authoritative" +"table a not found" + +# select * from qualified authoritative table +"select a.* from authoritative a" +{ + "Original": "select a.* from authoritative a", + "Instructions": { + "Opcode": "SelectScatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "select a.user_id, a.col1, a.col2 from authoritative as a", + "FieldQuery": "select a.user_id, a.col1, a.col2 from authoritative as a where 1 != 1" + } +} + +# select * from intermixing of authoritative table with non-authoritative results in no expansion +"select * from authoritative join user on authoritative.user_id=user.id" +{ + "Original": "select * from authoritative join user on authoritative.user_id=user.id", + "Instructions": { + "Opcode": "SelectScatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "select * from authoritative join user on authoritative.user_id = user.id", + "FieldQuery": "select * from authoritative join user on authoritative.user_id = user.id where 1 != 1" + } +} + +# select authoritative.* with intermixing still expands +"select user.id, a.*, user.col1 from authoritative a join user on a.user_id=user.id" +{ + "Original": "select user.id, a.*, user.col1 from authoritative a join user on a.user_id=user.id", + "Instructions": { + "Opcode": "SelectScatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "select user.id, a.user_id, a.col1, a.col2, user.col1 from authoritative as a join user on a.user_id = user.id", + "FieldQuery": "select user.id, a.user_id, a.col1, a.col2, user.col1 from authoritative as a join user on a.user_id = user.id where 1 != 1" + } +} # auto-resolve anonymous columns for simple route "select col from user join user_extra on user.id = user_extra.user_id" @@ -620,6 +695,7 @@ "FieldQuery": "select * from user where 1 != 1" } } + # sharded limit offset "select user_id from music order by user_id limit 10, 20" { @@ -842,9 +918,50 @@ } } +# select * from subquery expands specific columns +"select * from (select user.id id1, user_extra.id id2 from user join user_extra) as t" +{ + "Original": "select * from (select user.id id1, user_extra.id id2 from user join user_extra) as t", + "Instructions": { + "Cols": [ + 0, + 1 + ], + "Subquery": { + "Opcode": "Join", + "Left": { + "Opcode": "SelectScatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "select user.id as id1 from user", + "FieldQuery": "select user.id as id1 from user where 1 != 1" + }, + "Right": { + "Opcode": "SelectScatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "select user_extra.id as id2 from user_extra", + "FieldQuery": "select user_extra.id as id2 from user_extra where 1 != 1" + }, + "Cols": [ + -1, + 1 + ] + } + } +} + +# duplicate columns not allowed in subquery +"select * from (select user.id, user_extra.id from user join user_extra) as t" +"duplicate column names in subquery: id" + # non-existent symbol in cross-shard subquery "select t.col from (select user.id from user join user_extra) as t" -"symbol t.col is referencing a non-existent column of the subquery" +"symbol t.col not found in table or subquery" # union of information_schema "select * from information_schema.a union select * from information_schema.b" diff --git a/data/test/vtgate/unsupported_cases.txt b/data/test/vtgate/unsupported_cases.txt index 34c55f58baf..09dafacc88b 100644 --- a/data/test/vtgate/unsupported_cases.txt +++ b/data/test/vtgate/unsupported_cases.txt @@ -425,10 +425,10 @@ "select keyspace_id from user_index where 1 = id" "unsupported: where clause for vindex function must be of the form id = (lhs is not a column)" -"select keyspace_id from user_index where none = 1" +"select keyspace_id from user_index where keyspace_id = 1" "unsupported: where clause for vindex function must be of the form id = (lhs is not id)" -"select keyspace_id from user_index where id = a" +"select keyspace_id from user_index where id = id+1" "unsupported: where clause for vindex function must be of the form id = (rhs is not a value)" "select keyspace_id from user_index" diff --git a/data/test/vtgate/vindex_func_cases.txt b/data/test/vtgate/vindex_func_cases.txt index d8058321a48..7ba9beae062 100644 --- a/data/test/vtgate/vindex_func_cases.txt +++ b/data/test/vtgate/vindex_func_cases.txt @@ -33,6 +33,41 @@ } } +# vindex func select * +"select * from user_index where id = :id" +{ + "Original": "select * from user_index where id = :id", + "Instructions": { + "Opcode": "VindexMap", + "Fields": [ + { + "name": "id", + "type": 10262 + }, + { + "name": "keyspace_id", + "type": 10262 + }, + { + "name": "range_start", + "type": 10262 + }, + { + "name": "range_end", + "type": 10262 + } + ], + "Cols": [ + 0, + 1, + 2, + 3 + ], + "Vindex": "user_index", + "Value": ":id" + } +} + # vindex func read with id repeated "select id, keyspace_id, id from user_index where id = :id" { @@ -320,4 +355,4 @@ } "select none from user_index where id = :id" -"unrecognized column none for vindex: user_index" +"symbol none not found in table or subquery" diff --git a/go/vt/proto/vschema/vschema.pb.go b/go/vt/proto/vschema/vschema.pb.go index 938a617d117..da4101a3090 100644 --- a/go/vt/proto/vschema/vschema.pb.go +++ b/go/vt/proto/vschema/vschema.pb.go @@ -34,7 +34,7 @@ func (m *Keyspace) Reset() { *m = Keyspace{} } func (m *Keyspace) String() string { return proto.CompactTextString(m) } func (*Keyspace) ProtoMessage() {} func (*Keyspace) Descriptor() ([]byte, []int) { - return fileDescriptor_vschema_f0f3bc4bc6c5c748, []int{0} + return fileDescriptor_vschema_5ecfaf46981fe072, []int{0} } func (m *Keyspace) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_Keyspace.Unmarshal(m, b) @@ -99,7 +99,7 @@ func (m *Vindex) Reset() { *m = Vindex{} } func (m *Vindex) String() string { return proto.CompactTextString(m) } func (*Vindex) ProtoMessage() {} func (*Vindex) Descriptor() ([]byte, []int) { - return fileDescriptor_vschema_f0f3bc4bc6c5c748, []int{1} + return fileDescriptor_vschema_5ecfaf46981fe072, []int{1} } func (m *Vindex) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_Vindex.Unmarshal(m, b) @@ -156,17 +156,21 @@ type Table struct { // shard, as dictated by the keyspace id. // The keyspace id is represened in hex form // like in keyranges. - Pinned string `protobuf:"bytes,5,opt,name=pinned" json:"pinned,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + Pinned string `protobuf:"bytes,5,opt,name=pinned" json:"pinned,omitempty"` + // column_list_authoritative is set to true if columns is + // an authoritative list for the table. This allows + // us to expand 'select *' expressions. + ColumnListAuthoritative bool `protobuf:"varint,6,opt,name=column_list_authoritative,json=columnListAuthoritative" json:"column_list_authoritative,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *Table) Reset() { *m = Table{} } func (m *Table) String() string { return proto.CompactTextString(m) } func (*Table) ProtoMessage() {} func (*Table) Descriptor() ([]byte, []int) { - return fileDescriptor_vschema_f0f3bc4bc6c5c748, []int{2} + return fileDescriptor_vschema_5ecfaf46981fe072, []int{2} } func (m *Table) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_Table.Unmarshal(m, b) @@ -221,6 +225,13 @@ func (m *Table) GetPinned() string { return "" } +func (m *Table) GetColumnListAuthoritative() bool { + if m != nil { + return m.ColumnListAuthoritative + } + return false +} + // ColumnVindex is used to associate a column to a vindex. type ColumnVindex struct { // Legacy implemenation, moving forward all vindexes should define a list of columns. @@ -238,7 +249,7 @@ func (m *ColumnVindex) Reset() { *m = ColumnVindex{} } func (m *ColumnVindex) String() string { return proto.CompactTextString(m) } func (*ColumnVindex) ProtoMessage() {} func (*ColumnVindex) Descriptor() ([]byte, []int) { - return fileDescriptor_vschema_f0f3bc4bc6c5c748, []int{3} + return fileDescriptor_vschema_5ecfaf46981fe072, []int{3} } func (m *ColumnVindex) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_ColumnVindex.Unmarshal(m, b) @@ -293,7 +304,7 @@ func (m *AutoIncrement) Reset() { *m = AutoIncrement{} } func (m *AutoIncrement) String() string { return proto.CompactTextString(m) } func (*AutoIncrement) ProtoMessage() {} func (*AutoIncrement) Descriptor() ([]byte, []int) { - return fileDescriptor_vschema_f0f3bc4bc6c5c748, []int{4} + return fileDescriptor_vschema_5ecfaf46981fe072, []int{4} } func (m *AutoIncrement) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_AutoIncrement.Unmarshal(m, b) @@ -340,7 +351,7 @@ func (m *Column) Reset() { *m = Column{} } func (m *Column) String() string { return proto.CompactTextString(m) } func (*Column) ProtoMessage() {} func (*Column) Descriptor() ([]byte, []int) { - return fileDescriptor_vschema_f0f3bc4bc6c5c748, []int{5} + return fileDescriptor_vschema_5ecfaf46981fe072, []int{5} } func (m *Column) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_Column.Unmarshal(m, b) @@ -387,7 +398,7 @@ func (m *SrvVSchema) Reset() { *m = SrvVSchema{} } func (m *SrvVSchema) String() string { return proto.CompactTextString(m) } func (*SrvVSchema) ProtoMessage() {} func (*SrvVSchema) Descriptor() ([]byte, []int) { - return fileDescriptor_vschema_f0f3bc4bc6c5c748, []int{6} + return fileDescriptor_vschema_5ecfaf46981fe072, []int{6} } func (m *SrvVSchema) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_SrvVSchema.Unmarshal(m, b) @@ -428,42 +439,44 @@ func init() { proto.RegisterMapType((map[string]*Keyspace)(nil), "vschema.SrvVSchema.KeyspacesEntry") } -func init() { proto.RegisterFile("vschema.proto", fileDescriptor_vschema_f0f3bc4bc6c5c748) } - -var fileDescriptor_vschema_f0f3bc4bc6c5c748 = []byte{ - // 530 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x54, 0xcd, 0x6e, 0xd3, 0x40, - 0x10, 0x96, 0x93, 0xc6, 0x49, 0xc6, 0x24, 0x81, 0x55, 0xa9, 0x2c, 0x23, 0xd4, 0xc8, 0x2a, 0x10, - 0x2e, 0x8e, 0x94, 0x0a, 0x89, 0x1f, 0x15, 0x01, 0x11, 0x87, 0x0a, 0x24, 0x90, 0x1b, 0xf5, 0xc0, - 0xa5, 0xda, 0x3a, 0x23, 0x6a, 0x35, 0x5e, 0xbb, 0x5e, 0xdb, 0xe0, 0xa7, 0x41, 0xe2, 0x0d, 0x78, - 0x20, 0xde, 0x05, 0x79, 0x77, 0xed, 0xae, 0xd3, 0x70, 0xdb, 0xcf, 0x33, 0xdf, 0x37, 0xdf, 0xce, - 0xce, 0x18, 0x46, 0x05, 0x0f, 0xae, 0x30, 0xa2, 0x5e, 0x92, 0xc6, 0x59, 0x4c, 0xfa, 0x0a, 0x3a, - 0xd6, 0x4d, 0x8e, 0x69, 0x29, 0xbf, 0xba, 0x7f, 0x3a, 0x30, 0xf8, 0x84, 0x25, 0x4f, 0x68, 0x80, - 0xc4, 0x86, 0x3e, 0xbf, 0xa2, 0xe9, 0x1a, 0xd7, 0xb6, 0x31, 0x35, 0x66, 0x03, 0xbf, 0x86, 0xe4, - 0x0d, 0x0c, 0x8a, 0x90, 0xad, 0xf1, 0x27, 0x72, 0xbb, 0x33, 0xed, 0xce, 0xac, 0xc5, 0xa1, 0x57, - 0xcb, 0xd7, 0x74, 0xef, 0x5c, 0x65, 0x7c, 0x64, 0x59, 0x5a, 0xfa, 0x0d, 0x81, 0xbc, 0x00, 0x33, - 0xa3, 0x97, 0x1b, 0xe4, 0x76, 0x57, 0x50, 0x1f, 0xdf, 0xa5, 0xae, 0x44, 0x5c, 0x12, 0x55, 0xb2, - 0xf3, 0x19, 0x46, 0x2d, 0x45, 0x72, 0x1f, 0xba, 0xd7, 0x58, 0x0a, 0x6b, 0x43, 0xbf, 0x3a, 0x92, - 0x27, 0xd0, 0x2b, 0xe8, 0x26, 0x47, 0xbb, 0x33, 0x35, 0x66, 0xd6, 0x62, 0xd2, 0x08, 0x4b, 0xa2, - 0x2f, 0xa3, 0xaf, 0x3b, 0x2f, 0x0d, 0xe7, 0x14, 0x2c, 0xad, 0xc8, 0x0e, 0xad, 0xa3, 0xb6, 0xd6, - 0xb8, 0xd1, 0x12, 0x34, 0x4d, 0xca, 0xfd, 0x6d, 0x80, 0x29, 0x0b, 0x10, 0x02, 0x7b, 0x59, 0x99, - 0xa0, 0xd2, 0x11, 0x67, 0x72, 0x0c, 0x66, 0x42, 0x53, 0x1a, 0xd5, 0x9d, 0x7a, 0xb4, 0xe5, 0xca, - 0xfb, 0x2a, 0xa2, 0xea, 0xb2, 0x32, 0x95, 0xec, 0x43, 0x2f, 0xfe, 0xc1, 0x30, 0xb5, 0xbb, 0x42, - 0x49, 0x02, 0xe7, 0x15, 0x58, 0x5a, 0xf2, 0x0e, 0xd3, 0xfb, 0xba, 0xe9, 0xa1, 0x6e, 0xf2, 0xaf, - 0x01, 0x3d, 0xe1, 0x7c, 0xa7, 0xc7, 0xb7, 0x30, 0x09, 0xe2, 0x4d, 0x1e, 0xb1, 0x8b, 0xad, 0x67, - 0x7d, 0xd8, 0x98, 0x5d, 0x8a, 0xb8, 0x6a, 0xe4, 0x38, 0xd0, 0x10, 0x72, 0x72, 0x02, 0x63, 0x9a, - 0x67, 0xf1, 0x45, 0xc8, 0x82, 0x14, 0x23, 0x64, 0x99, 0xf0, 0x6d, 0x2d, 0x0e, 0x1a, 0xfa, 0xfb, - 0x3c, 0x8b, 0x4f, 0xeb, 0xa8, 0x3f, 0xa2, 0x3a, 0x24, 0xcf, 0xa1, 0x2f, 0x05, 0xb9, 0xbd, 0x27, - 0xca, 0x4e, 0xb6, 0xca, 0xfa, 0x75, 0x9c, 0x1c, 0x80, 0x99, 0x84, 0x8c, 0xe1, 0xda, 0xee, 0x09, - 0xff, 0x0a, 0xb9, 0x2b, 0xb8, 0xa7, 0x3b, 0xac, 0xf2, 0x24, 0x45, 0xdd, 0x53, 0xa1, 0xea, 0xf6, - 0x8c, 0x46, 0x75, 0x83, 0xc4, 0xb9, 0x9a, 0xf3, 0xba, 0x7c, 0x35, 0x91, 0xc3, 0xa6, 0x9a, 0xbb, - 0x84, 0x51, 0xcb, 0xf8, 0x7f, 0x65, 0x1d, 0x18, 0x70, 0xbc, 0xc9, 0x91, 0x05, 0xb5, 0x74, 0x83, - 0xdd, 0x13, 0x30, 0x97, 0xed, 0xe2, 0x86, 0x56, 0xfc, 0x50, 0x3d, 0x47, 0xc5, 0x1a, 0x2f, 0x2c, - 0x4f, 0x6e, 0xe3, 0xaa, 0x4c, 0x50, 0xbe, 0x8d, 0xfb, 0xcb, 0x00, 0x38, 0x4b, 0x8b, 0xf3, 0x33, - 0xd1, 0x10, 0xf2, 0x0e, 0x86, 0xd7, 0x6a, 0x4d, 0xb8, 0x6d, 0x88, 0x6e, 0xb9, 0x4d, 0xb7, 0x6e, - 0xf3, 0x9a, 0x5d, 0x52, 0x83, 0x75, 0x4b, 0x72, 0xbe, 0xc0, 0xb8, 0x1d, 0xdc, 0x31, 0x48, 0xcf, - 0xda, 0xd3, 0xff, 0xe0, 0xce, 0x8a, 0x6a, 0xb3, 0xf5, 0xe1, 0xe9, 0xb7, 0xa3, 0x22, 0xcc, 0x90, - 0x73, 0x2f, 0x8c, 0xe7, 0xf2, 0x34, 0xff, 0x1e, 0xcf, 0x8b, 0x6c, 0x2e, 0x7e, 0x2a, 0x73, 0xc5, - 0xbd, 0x34, 0x05, 0x3c, 0xfe, 0x17, 0x00, 0x00, 0xff, 0xff, 0x31, 0x37, 0xa1, 0xa9, 0x8a, 0x04, +func init() { proto.RegisterFile("vschema.proto", fileDescriptor_vschema_5ecfaf46981fe072) } + +var fileDescriptor_vschema_5ecfaf46981fe072 = []byte{ + // 562 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x54, 0x41, 0x6f, 0xd3, 0x4c, + 0x10, 0x95, 0x93, 0xc6, 0x4d, 0xc6, 0x5f, 0xd2, 0x8f, 0x55, 0x29, 0xc6, 0x08, 0x35, 0xb2, 0x0a, + 0x84, 0x8b, 0x23, 0xa5, 0x42, 0x82, 0xa2, 0x22, 0x4a, 0xc4, 0xa1, 0xa2, 0x12, 0xc8, 0x8d, 0x7a, + 0xe0, 0x12, 0x6d, 0x9d, 0x11, 0xb1, 0x9a, 0xd8, 0xae, 0x77, 0x6d, 0xf0, 0x4f, 0xe1, 0x84, 0xc4, + 0x3f, 0xe0, 0x1f, 0x22, 0xef, 0xae, 0xdd, 0x75, 0x1a, 0x6e, 0xfb, 0x3c, 0xf3, 0xde, 0xbc, 0x9d, + 0x9d, 0x31, 0xf4, 0x73, 0x16, 0x2c, 0x71, 0x4d, 0xbd, 0x24, 0x8d, 0x79, 0x4c, 0x76, 0x15, 0x74, + 0xac, 0xdb, 0x0c, 0xd3, 0x42, 0x7e, 0x75, 0xff, 0xb4, 0xa0, 0xfb, 0x09, 0x0b, 0x96, 0xd0, 0x00, + 0x89, 0x0d, 0xbb, 0x6c, 0x49, 0xd3, 0x05, 0x2e, 0x6c, 0x63, 0x68, 0x8c, 0xba, 0x7e, 0x05, 0xc9, + 0x5b, 0xe8, 0xe6, 0x61, 0xb4, 0xc0, 0x1f, 0xc8, 0xec, 0xd6, 0xb0, 0x3d, 0xb2, 0x26, 0x87, 0x5e, + 0x25, 0x5f, 0xd1, 0xbd, 0x2b, 0x95, 0xf1, 0x31, 0xe2, 0x69, 0xe1, 0xd7, 0x04, 0xf2, 0x0a, 0x4c, + 0x4e, 0xaf, 0x57, 0xc8, 0xec, 0xb6, 0xa0, 0x3e, 0xbd, 0x4f, 0x9d, 0x89, 0xb8, 0x24, 0xaa, 0x64, + 0xe7, 0x02, 0xfa, 0x0d, 0x45, 0xf2, 0x3f, 0xb4, 0x6f, 0xb0, 0x10, 0xd6, 0x7a, 0x7e, 0x79, 0x24, + 0xcf, 0xa0, 0x93, 0xd3, 0x55, 0x86, 0x76, 0x6b, 0x68, 0x8c, 0xac, 0xc9, 0x5e, 0x2d, 0x2c, 0x89, + 0xbe, 0x8c, 0x9e, 0xb4, 0x5e, 0x1b, 0xce, 0x39, 0x58, 0x5a, 0x91, 0x2d, 0x5a, 0x47, 0x4d, 0xad, + 0x41, 0xad, 0x25, 0x68, 0x9a, 0x94, 0xfb, 0xdb, 0x00, 0x53, 0x16, 0x20, 0x04, 0x76, 0x78, 0x91, + 0xa0, 0xd2, 0x11, 0x67, 0x72, 0x0c, 0x66, 0x42, 0x53, 0xba, 0xae, 0x3a, 0xf5, 0x64, 0xc3, 0x95, + 0xf7, 0x45, 0x44, 0xd5, 0x65, 0x65, 0x2a, 0xd9, 0x87, 0x4e, 0xfc, 0x3d, 0xc2, 0xd4, 0x6e, 0x0b, + 0x25, 0x09, 0x9c, 0x37, 0x60, 0x69, 0xc9, 0x5b, 0x4c, 0xef, 0xeb, 0xa6, 0x7b, 0xba, 0xc9, 0x9f, + 0x2d, 0xe8, 0x08, 0xe7, 0x5b, 0x3d, 0xbe, 0x83, 0xbd, 0x20, 0x5e, 0x65, 0xeb, 0x68, 0xbe, 0xf1, + 0xac, 0x0f, 0x6b, 0xb3, 0x53, 0x11, 0x57, 0x8d, 0x1c, 0x04, 0x1a, 0x42, 0x46, 0x4e, 0x61, 0x40, + 0x33, 0x1e, 0xcf, 0xc3, 0x28, 0x48, 0x71, 0x8d, 0x11, 0x17, 0xbe, 0xad, 0xc9, 0x41, 0x4d, 0x3f, + 0xcb, 0x78, 0x7c, 0x5e, 0x45, 0xfd, 0x3e, 0xd5, 0x21, 0x79, 0x09, 0xbb, 0x52, 0x90, 0xd9, 0x3b, + 0xa2, 0xec, 0xde, 0x46, 0x59, 0xbf, 0x8a, 0x93, 0x03, 0x30, 0x93, 0x30, 0x8a, 0x70, 0x61, 0x77, + 0x84, 0x7f, 0x85, 0xc8, 0x09, 0x3c, 0x56, 0x37, 0x58, 0x85, 0x8c, 0xcf, 0x69, 0xc6, 0x97, 0x71, + 0x1a, 0x72, 0xca, 0xc3, 0x1c, 0x6d, 0x53, 0x4c, 0xef, 0x23, 0x99, 0x70, 0x11, 0x32, 0x7e, 0xa6, + 0x87, 0xdd, 0x19, 0xfc, 0xa7, 0xdf, 0xae, 0xac, 0x21, 0x53, 0x55, 0x8f, 0x14, 0x2a, 0x3b, 0x17, + 0xd1, 0x75, 0xd5, 0x5c, 0x71, 0x2e, 0x77, 0xa4, 0xb2, 0x5e, 0x4e, 0x73, 0xaf, 0x76, 0xea, 0x4e, + 0xa1, 0xdf, 0xb8, 0xf4, 0x3f, 0x65, 0x1d, 0xe8, 0x32, 0xbc, 0xcd, 0x30, 0x0a, 0x2a, 0xe9, 0x1a, + 0xbb, 0xa7, 0x60, 0x4e, 0x9b, 0xc5, 0x0d, 0xad, 0xf8, 0xa1, 0x7a, 0xca, 0x92, 0x35, 0x98, 0x58, + 0x9e, 0xdc, 0xe4, 0x59, 0x91, 0xa0, 0x7c, 0x57, 0xf7, 0x97, 0x01, 0x70, 0x99, 0xe6, 0x57, 0x97, + 0xa2, 0x99, 0xe4, 0x3d, 0xf4, 0x6e, 0xd4, 0x8a, 0x31, 0xdb, 0x10, 0x9d, 0x76, 0xeb, 0x4e, 0xdf, + 0xe5, 0xd5, 0x7b, 0xa8, 0x86, 0xf2, 0x8e, 0xe4, 0x7c, 0x86, 0x41, 0x33, 0xb8, 0x65, 0x08, 0x5f, + 0x34, 0x37, 0xe7, 0xc1, 0xbd, 0xf5, 0xd6, 0xe6, 0xf2, 0xc3, 0xf3, 0xaf, 0x47, 0x79, 0xc8, 0x91, + 0x31, 0x2f, 0x8c, 0xc7, 0xf2, 0x34, 0xfe, 0x16, 0x8f, 0x73, 0x3e, 0x16, 0x3f, 0xa4, 0xb1, 0xe2, + 0x5e, 0x9b, 0x02, 0x1e, 0xff, 0x0d, 0x00, 0x00, 0xff, 0xff, 0x41, 0x18, 0x64, 0xd4, 0xc6, 0x04, 0x00, 0x00, } diff --git a/go/vt/vtexplain/vtexplain_vtgate.go b/go/vt/vtexplain/vtexplain_vtgate.go index 4e90acf00f2..4ea5b73abd8 100644 --- a/go/vt/vtexplain/vtexplain_vtgate.go +++ b/go/vt/vtexplain/vtexplain_vtgate.go @@ -20,11 +20,11 @@ limitations under the License. package vtexplain import ( - "encoding/json" "fmt" "golang.org/x/net/context" + "vitess.io/vitess/go/json2" "vitess.io/vitess/go/vt/discovery" "vitess.io/vitess/go/vt/key" "vitess.io/vitess/go/vt/log" @@ -89,11 +89,15 @@ func buildTopology(opts *Options, vschemaStr string, numShardsPerKeyspace int) e explainTopo.Lock.Lock() defer explainTopo.Lock.Unlock() - explainTopo.Keyspaces = make(map[string]*vschemapb.Keyspace) - err := json.Unmarshal([]byte(vschemaStr), &explainTopo.Keyspaces) + // We have to use proto's custom json loader so it can + // handle string->enum conversion correctly. + var srvVSchema vschemapb.SrvVSchema + wrappedStr := fmt.Sprintf(`{"keyspaces": %s}`, vschemaStr) + err := json2.Unmarshal([]byte(wrappedStr), &srvVSchema) if err != nil { return err } + explainTopo.Keyspaces = srvVSchema.Keyspaces explainTopo.TabletConns = make(map[string]*explainTablet) for ks, vschema := range explainTopo.Keyspaces { diff --git a/go/vt/vtgate/planbuilder/from.go b/go/vt/vtgate/planbuilder/from.go index a0efd6a7d72..ed1c761dc68 100644 --- a/go/vt/vtgate/planbuilder/from.go +++ b/go/vt/vtgate/planbuilder/from.go @@ -93,8 +93,9 @@ func (pb *primitiveBuilder) processAliasedTable(tableExpr *sqlparser.AliasedTabl subroute, ok := spb.bldr.(*route) if !ok { - pb.bldr, pb.st = newSubquery(tableExpr.As, spb.bldr) - return nil + var err error + pb.bldr, pb.st, err = newSubquery(tableExpr.As, spb.bldr) + return err } // Since a route is more versatile than a subquery, we diff --git a/go/vt/vtgate/planbuilder/select.go b/go/vt/vtgate/planbuilder/select.go index 17490eed290..8814f328b1b 100644 --- a/go/vt/vtgate/planbuilder/select.go +++ b/go/vt/vtgate/planbuilder/select.go @@ -182,8 +182,8 @@ func (pb *primitiveBuilder) pushSelectExprs(sel *sqlparser.Select, grouper group // pusheSelectRoutes is a convenience function that pushes all the select // expressions and returns the list of resultColumns generated for it. func (pb *primitiveBuilder) pushSelectRoutes(selectExprs sqlparser.SelectExprs) ([]*resultColumn, error) { - resultColumns := make([]*resultColumn, len(selectExprs)) - for i, node := range selectExprs { + resultColumns := make([]*resultColumn, 0, len(selectExprs)) + for _, node := range selectExprs { switch node := node.(type) { case *sqlparser.AliasedExpr: pullouts, origin, expr, err := pb.findOrigin(node.Expr) @@ -191,12 +191,22 @@ func (pb *primitiveBuilder) pushSelectRoutes(selectExprs sqlparser.SelectExprs) return nil, err } node.Expr = expr - resultColumns[i], _, err = pb.bldr.PushSelect(node, origin) + rc, _, err := pb.bldr.PushSelect(node, origin) if err != nil { return nil, err } + resultColumns = append(resultColumns, rc) pb.addPullouts(pullouts) case *sqlparser.StarExpr: + var expanded bool + var err error + resultColumns, expanded, err = pb.expandStar(resultColumns, node) + if err != nil { + return nil, err + } + if expanded { + continue + } // We'll allow select * for simple routes. rb, ok := pb.bldr.(*route) if !ok { @@ -210,7 +220,7 @@ func (pb *primitiveBuilder) pushSelectRoutes(selectExprs sqlparser.SelectExprs) } } } - resultColumns[i] = rb.PushAnonymous(node) + resultColumns = append(resultColumns, rb.PushAnonymous(node)) case sqlparser.Nextval: rb, ok := pb.bldr.(*route) if !ok { @@ -220,7 +230,7 @@ func (pb *primitiveBuilder) pushSelectRoutes(selectExprs sqlparser.SelectExprs) if err := rb.SetOpcode(engine.SelectNext); err != nil { return nil, err } - resultColumns[i] = rb.PushAnonymous(node) + resultColumns = append(resultColumns, rb.PushAnonymous(node)) default: panic(fmt.Sprintf("BUG: unexpceted select expression type: %T", node)) } @@ -228,6 +238,90 @@ func (pb *primitiveBuilder) pushSelectRoutes(selectExprs sqlparser.SelectExprs) return resultColumns, nil } +// expandStar expands a StarExpr and pushes the expanded +// expressions down if the tables have authoritative column lists. +// If not, it returns false. +// This function breaks the abstraction a bit: it directly sets the +// the Metadata for newly created expressions. In all other cases, +// the Metadata is set through a symtab Find. +func (pb *primitiveBuilder) expandStar(inrcs []*resultColumn, expr *sqlparser.StarExpr) (outrcs []*resultColumn, expanded bool, err error) { + tables := pb.st.AllTables() + if tables == nil { + // no table metadata available. + return inrcs, false, nil + } + if expr.TableName.IsEmpty() { + for _, t := range tables { + // All tables must have authoritative column lists. + if !t.isAuthoritative { + return inrcs, false, nil + } + } + singleTable := false + if len(tables) == 1 { + singleTable = true + } + for _, t := range tables { + for _, col := range t.columnNames { + var expr *sqlparser.AliasedExpr + if singleTable { + // If there's only one table, we use unqualifed column names. + expr = &sqlparser.AliasedExpr{ + Expr: &sqlparser.ColName{ + Metadata: t.columns[col.Lowered()], + Name: col, + }, + } + } else { + // If a and b have id as their column, then + // select * from a join b should result in + // select a.id as id, b.id as id from a join b. + expr = &sqlparser.AliasedExpr{ + Expr: &sqlparser.ColName{ + Metadata: t.columns[col.Lowered()], + Name: col, + Qualifier: t.alias, + }, + As: col, + } + } + rc, _, err := pb.bldr.PushSelect(expr, t.origin) + if err != nil { + // Unreachable because PushSelect won't fail on ColName. + return inrcs, false, err + } + inrcs = append(inrcs, rc) + } + } + return inrcs, true, nil + } + + // Expression qualified with table name. + t, err := pb.st.FindTable(expr.TableName) + if err != nil { + return inrcs, false, err + } + if !t.isAuthoritative { + return inrcs, false, nil + } + for _, col := range t.columnNames { + expr := &sqlparser.AliasedExpr{ + Expr: &sqlparser.ColName{ + Metadata: t.columns[col.Lowered()], + Name: col, + Qualifier: expr.TableName, + }, + } + rc, _, err := pb.bldr.PushSelect(expr, t.origin) + if err != nil { + // Unreachable because PushSelect won't fail on ColName. + return inrcs, false, err + } + inrcs = append(inrcs, rc) + } + return inrcs, true, nil +} + // queryTimeout returns DirectiveQueryTimeout value if set, otherwise returns 0. func queryTimeout(d sqlparser.CommentDirectives) int { if d == nil { diff --git a/go/vt/vtgate/planbuilder/subquery.go b/go/vt/vtgate/planbuilder/subquery.go index e69892dbea8..f9b4a968965 100644 --- a/go/vt/vtgate/planbuilder/subquery.go +++ b/go/vt/vtgate/planbuilder/subquery.go @@ -18,6 +18,7 @@ package planbuilder import ( "errors" + "fmt" "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vtgate/engine" @@ -42,7 +43,7 @@ type subquery struct { } // newSubquery builds a new subquery. -func newSubquery(alias sqlparser.TableIdent, bldr builder) (*subquery, *symtab) { +func newSubquery(alias sqlparser.TableIdent, bldr builder) (*subquery, *symtab, error) { sq := &subquery{ order: bldr.Order() + 1, input: bldr, @@ -56,20 +57,17 @@ func newSubquery(alias sqlparser.TableIdent, bldr builder) (*subquery, *symtab) } // Create column symbols based on the result column names. - cols := make(map[string]*column) - for i, rc := range bldr.ResultColumns() { - cols[rc.alias.Lowered()] = &column{ - origin: sq, - colnum: i, + for _, rc := range bldr.ResultColumns() { + if _, ok := t.columns[rc.alias.Lowered()]; ok { + return nil, nil, fmt.Errorf("duplicate column names in subquery: %s", sqlparser.String(rc.alias)) } + t.addColumn(rc.alias, &column{origin: sq}) } - - // Populate the table with those columns and add it to symtab. - t.columns = cols + t.isAuthoritative = true st := newSymtab() // AddTable will not fail because symtab is empty. _ = st.AddTable(t) - return sq, st + return sq, st, nil } // Order satisfies the builder interface. diff --git a/go/vt/vtgate/planbuilder/symtab.go b/go/vt/vtgate/planbuilder/symtab.go index 440782b7696..ac4caae56dd 100644 --- a/go/vt/vtgate/planbuilder/symtab.go +++ b/go/vt/vtgate/planbuilder/symtab.go @@ -28,6 +28,8 @@ import ( querypb "vitess.io/vitess/go/vt/proto/query" ) +var errNoTable = errors.New("no table info") + // symtab represents the symbol table for a SELECT statement // or a subquery. The symtab evolves over time. // As a query is analyzed, multiple independent @@ -48,7 +50,8 @@ import ( // which is later used to determine if the subquery can be // merged with an outer route. type symtab struct { - tables map[sqlparser.TableName]*table + tables map[sqlparser.TableName]*table + tableNames []sqlparser.TableName // uniqueColumns has the column name as key // and points at the columns that tables contains. @@ -91,18 +94,18 @@ func newSymtabWithRoute(rb *route) *symtab { // and adds it to symtab. func (st *symtab) AddVindexTable(alias sqlparser.TableName, vindexTable *vindexes.Table, rb *route) error { t := &table{ - alias: alias, - columns: make(map[string]*column), - origin: rb, - vindexTable: vindexTable, + alias: alias, + origin: rb, + vindexTable: vindexTable, + isAuthoritative: vindexTable.ColumnListAuthoritative, } for _, col := range vindexTable.Columns { - t.columns[col.Name.Lowered()] = &column{ + t.addColumn(col.Name, &column{ origin: rb, st: st, typ: col.Type, - } + }) } for _, cv := range vindexTable.ColumnVindexes { @@ -117,21 +120,21 @@ func (st *symtab) AddVindexTable(alias sqlparser.TableName, vindexTable *vindexe col.Vindex = vindex continue } - t.columns[lowered] = &column{ + t.addColumn(cvcol, &column{ origin: rb, st: st, Vindex: vindex, - } + }) } } if ai := vindexTable.AutoIncrement; ai != nil { lowered := ai.Column.Lowered() if _, ok := t.columns[lowered]; !ok { - t.columns[lowered] = &column{ + t.addColumn(ai.Column, &column{ origin: rb, st: st, - } + }) } } return st.AddTable(t) @@ -144,6 +147,11 @@ func (st *symtab) AddVindexTable(alias sqlparser.TableName, vindexTable *vindexe // At this point, only tables and uniqueColumns are set. // All other fields are ignored. func (st *symtab) Merge(newsyms *symtab) error { + if st.tableNames == nil || newsyms.tableNames == nil { + // If any side of symtab has anonymous tables, + // we treat the merged symtab as having anonymous tables. + return nil + } for _, t := range newsyms.tables { if err := st.AddTable(t); err != nil { return err @@ -161,6 +169,7 @@ func (st *symtab) AddTable(t *table) error { return fmt.Errorf("duplicate symbol: %s", sqlparser.String(t.alias)) } st.tables[t.alias] = t + st.tableNames = append(st.tableNames, t.alias) // update the uniqueColumns list, and eliminate // duplicate symbols if found. @@ -178,6 +187,37 @@ func (st *symtab) AddTable(t *table) error { return nil } +// AllTables returns an ordered list of all current tables. +func (st *symtab) AllTables() []*table { + if len(st.tableNames) == 0 { + return nil + } + tables := make([]*table, 0, len(st.tableNames)) + for _, tname := range st.tableNames { + tables = append(tables, st.tables[tname]) + } + return tables +} + +// FindTable finds a table in symtab. This function is specifically used +// for expanding 'select a.*' constructs. If you're in a subquery, +// you're most likely referring to a table in the local 'from' clause. +// For this reason, the search is only performed in the current scope. +// This may be a deviation from the formal definition of SQL, but there +// are currently no use cases that require the full support. +func (st *symtab) FindTable(tname sqlparser.TableName) (*table, error) { + if st.tableNames == nil { + // Unreachable because current code path checks for this condition + // before invoking this function. + return nil, errNoTable + } + t, ok := st.tables[tname] + if !ok { + return nil, fmt.Errorf("table %v not found", sqlparser.String(tname)) + } + return t, nil +} + // ClearVindexes removes the Column Vindexes from the aliases signifying // that they cannot be used to make routing improvements. This is // called if a primitive is in the RHS of a LEFT JOIN. @@ -330,14 +370,14 @@ func (st *symtab) searchTables(col *sqlparser.ColName) (*column, error) { c, ok := t.columns[col.Name.Lowered()] if !ok { // We know all the column names of a subquery. Might as well return an error if it's not found. - if _, ok := t.origin.(*subquery); ok { - return nil, fmt.Errorf("symbol %s is referencing a non-existent column of the subquery", sqlparser.String(col)) + if t.isAuthoritative { + return nil, fmt.Errorf("symbol %s not found in table or subquery", sqlparser.String(col)) } c = &column{ origin: t.origin, st: st, } - t.columns[col.Name.Lowered()] = c + t.addColumn(col.Name, c) } return c, nil } @@ -400,10 +440,25 @@ func (st *symtab) ResolveSymbols(node sqlparser.SQLNode) error { // It represents a table alias in a FROM clause. It points // to the builder that represents it. type table struct { - alias sqlparser.TableName - columns map[string]*column - origin builder - vindexTable *vindexes.Table + alias sqlparser.TableName + columns map[string]*column + columnNames []sqlparser.ColIdent + isAuthoritative bool + origin builder + vindexTable *vindexes.Table +} + +func (t *table) addColumn(alias sqlparser.ColIdent, c *column) { + if t.columns == nil { + t.columns = make(map[string]*column) + } + lowered := alias.Lowered() + // Dups are allowed, but first one wins if referenced. + if _, ok := t.columns[lowered]; !ok { + c.colnum = len(t.columnNames) + t.columns[lowered] = c + } + t.columnNames = append(t.columnNames, alias) } // column represents a unique symbol in the query that other diff --git a/go/vt/vtgate/planbuilder/vindex_func.go b/go/vt/vtgate/planbuilder/vindex_func.go index 88adcf3768d..91d0885539c 100644 --- a/go/vt/vtgate/planbuilder/vindex_func.go +++ b/go/vt/vtgate/planbuilder/vindex_func.go @@ -55,16 +55,11 @@ func newVindexFunc(alias sqlparser.TableName, vindex vindexes.Vindex) (*vindexFu } // Column names are hard-coded to id, keyspace_id - t.columns = map[string]*column{ - "id": { - origin: vf, - colnum: 0, - }, - "keyspace_id": { - origin: vf, - colnum: 1, - }, - } + t.addColumn(sqlparser.NewColIdent("id"), &column{origin: vf}) + t.addColumn(sqlparser.NewColIdent("keyspace_id"), &column{origin: vf}) + t.addColumn(sqlparser.NewColIdent("range_start"), &column{origin: vf}) + t.addColumn(sqlparser.NewColIdent("range_end"), &column{origin: vf}) + t.isAuthoritative = true st := newSymtab() // AddTable will not fail because symtab is empty. @@ -151,18 +146,7 @@ func (vf *vindexFunc) PushSelect(expr *sqlparser.AliasedExpr, _ builder) (rc *re Name: rc.alias.String(), Type: querypb.Type_VARBINARY, }) - switch { - case col.Name.EqualString("id"): - vf.eVindexFunc.Cols = append(vf.eVindexFunc.Cols, 0) - case col.Name.EqualString("keyspace_id"): - vf.eVindexFunc.Cols = append(vf.eVindexFunc.Cols, 1) - case col.Name.EqualString("range_start"): - vf.eVindexFunc.Cols = append(vf.eVindexFunc.Cols, 2) - case col.Name.EqualString("range_end"): - vf.eVindexFunc.Cols = append(vf.eVindexFunc.Cols, 3) - default: - return nil, 0, fmt.Errorf("unrecognized column %s for vindex: %s", col.Name, vf.eVindexFunc.Vindex) - } + vf.eVindexFunc.Cols = append(vf.eVindexFunc.Cols, col.Metadata.(*column).colnum) return rc, len(vf.resultColumns) - 1, nil } diff --git a/go/vt/vtgate/vindexes/vschema.go b/go/vt/vtgate/vindexes/vschema.go index 7619b77d405..db3767c638a 100644 --- a/go/vt/vtgate/vindexes/vschema.go +++ b/go/vt/vtgate/vindexes/vschema.go @@ -41,15 +41,16 @@ type VSchema struct { // Table represents a table in VSchema. type Table struct { - IsSequence bool `json:"is_sequence,omitempty"` - Name sqlparser.TableIdent `json:"name"` - Keyspace *Keyspace `json:"-"` - ColumnVindexes []*ColumnVindex `json:"column_vindexes,omitempty"` - Ordered []*ColumnVindex `json:"ordered,omitempty"` - Owned []*ColumnVindex `json:"owned,omitempty"` - AutoIncrement *AutoIncrement `json:"auto_increment,omitempty"` - Columns []Column `json:"columns,omitempty"` - Pinned []byte `json:"pinned,omitempty"` + IsSequence bool `json:"is_sequence,omitempty"` + Name sqlparser.TableIdent `json:"name"` + Keyspace *Keyspace `json:"-"` + ColumnVindexes []*ColumnVindex `json:"column_vindexes,omitempty"` + Ordered []*ColumnVindex `json:"ordered,omitempty"` + Owned []*ColumnVindex `json:"owned,omitempty"` + AutoIncrement *AutoIncrement `json:"auto_increment,omitempty"` + Columns []Column `json:"columns,omitempty"` + Pinned []byte `json:"pinned,omitempty"` + ColumnListAuthoritative bool `json:"column_list_authoritative,omitempty"` } // Keyspace contains the keyspcae info for each Table. @@ -192,8 +193,9 @@ func buildTables(source *vschemapb.SrvVSchema, vschema *VSchema) error { } for tname, table := range ks.Tables { t := &Table{ - Name: sqlparser.NewTableIdent(tname), - Keyspace: keyspace, + Name: sqlparser.NewTableIdent(tname), + Keyspace: keyspace, + ColumnListAuthoritative: table.ColumnListAuthoritative, } if _, ok := vschema.uniqueTables[tname]; ok { vschema.uniqueTables[tname] = nil diff --git a/go/vt/vtgate/vindexes/vschema_test.go b/go/vt/vtgate/vindexes/vschema_test.go index 133922bd058..e48cef99e90 100644 --- a/go/vt/vtgate/vindexes/vschema_test.go +++ b/go/vt/vtgate/vindexes/vschema_test.go @@ -214,6 +214,71 @@ func TestVSchemaColumns(t *testing.T) { } } +func TestVSchemaColumnListAuthoritative(t *testing.T) { + good := vschemapb.SrvVSchema{ + Keyspaces: map[string]*vschemapb.Keyspace{ + "unsharded": { + Tables: map[string]*vschemapb.Table{ + "t1": { + Columns: []*vschemapb.Column{{ + Name: "c1", + }, { + Name: "c2", + Type: sqltypes.VarChar, + }}, + ColumnListAuthoritative: true, + }, + }, + }, + }, + } + got, err := BuildVSchema(&good) + if err != nil { + t.Error(err) + } + ks := &Keyspace{ + Name: "unsharded", + } + t1 := &Table{ + Name: sqlparser.NewTableIdent("t1"), + Keyspace: ks, + Columns: []Column{{ + Name: sqlparser.NewColIdent("c1"), + Type: sqltypes.Null, + }, { + Name: sqlparser.NewColIdent("c2"), + Type: sqltypes.VarChar, + }}, + ColumnListAuthoritative: true, + } + dual := &Table{ + Name: sqlparser.NewTableIdent("dual"), + Keyspace: ks, + } + want := &VSchema{ + uniqueTables: map[string]*Table{ + "t1": t1, + "dual": dual, + }, + uniqueVindexes: map[string]Vindex{}, + Keyspaces: map[string]*KeyspaceSchema{ + "unsharded": { + Keyspace: ks, + Tables: map[string]*Table{ + "t1": t1, + "dual": dual, + }, + Vindexes: map[string]Vindex{}, + }, + }, + } + if !reflect.DeepEqual(got, want) { + gotb, _ := json.Marshal(got) + wantb, _ := json.Marshal(want) + t.Errorf("BuildVSchema:\n%s, want\n%s", gotb, wantb) + } +} + func TestVSchemaColumnsFail(t *testing.T) { good := vschemapb.SrvVSchema{ Keyspaces: map[string]*vschemapb.Keyspace{ diff --git a/proto/vschema.proto b/proto/vschema.proto index 81276a767b5..bdf7e9834e9 100644 --- a/proto/vschema.proto +++ b/proto/vschema.proto @@ -64,7 +64,11 @@ message Table { // shard, as dictated by the keyspace id. // The keyspace id is represened in hex form // like in keyranges. - string pinned =5; + string pinned = 5; + // column_list_authoritative is set to true if columns is + // an authoritative list for the table. This allows + // us to expand 'select *' expressions. + bool column_list_authoritative = 6; } // ColumnVindex is used to associate a column to a vindex. diff --git a/py/vtproto/vschema_pb2.py b/py/vtproto/vschema_pb2.py index 81c5b4064e8..878d1d08dc9 100644 --- a/py/vtproto/vschema_pb2.py +++ b/py/vtproto/vschema_pb2.py @@ -20,7 +20,7 @@ name='vschema.proto', package='vschema', syntax='proto3', - serialized_pb=_b('\n\rvschema.proto\x12\x07vschema\x1a\x0bquery.proto\"\xfe\x01\n\x08Keyspace\x12\x0f\n\x07sharded\x18\x01 \x01(\x08\x12\x31\n\x08vindexes\x18\x02 \x03(\x0b\x32\x1f.vschema.Keyspace.VindexesEntry\x12-\n\x06tables\x18\x03 \x03(\x0b\x32\x1d.vschema.Keyspace.TablesEntry\x1a@\n\rVindexesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x1e\n\x05value\x18\x02 \x01(\x0b\x32\x0f.vschema.Vindex:\x02\x38\x01\x1a=\n\x0bTablesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x1d\n\x05value\x18\x02 \x01(\x0b\x32\x0e.vschema.Table:\x02\x38\x01\"\x81\x01\n\x06Vindex\x12\x0c\n\x04type\x18\x01 \x01(\t\x12+\n\x06params\x18\x02 \x03(\x0b\x32\x1b.vschema.Vindex.ParamsEntry\x12\r\n\x05owner\x18\x03 \x01(\t\x1a-\n\x0bParamsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\xa7\x01\n\x05Table\x12\x0c\n\x04type\x18\x01 \x01(\t\x12.\n\x0f\x63olumn_vindexes\x18\x02 \x03(\x0b\x32\x15.vschema.ColumnVindex\x12.\n\x0e\x61uto_increment\x18\x03 \x01(\x0b\x32\x16.vschema.AutoIncrement\x12 \n\x07\x63olumns\x18\x04 \x03(\x0b\x32\x0f.vschema.Column\x12\x0e\n\x06pinned\x18\x05 \x01(\t\"=\n\x0c\x43olumnVindex\x12\x0e\n\x06\x63olumn\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x0f\n\x07\x63olumns\x18\x03 \x03(\t\"1\n\rAutoIncrement\x12\x0e\n\x06\x63olumn\x18\x01 \x01(\t\x12\x10\n\x08sequence\x18\x02 \x01(\t\"1\n\x06\x43olumn\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x19\n\x04type\x18\x02 \x01(\x0e\x32\x0b.query.Type\"\x88\x01\n\nSrvVSchema\x12\x35\n\tkeyspaces\x18\x01 \x03(\x0b\x32\".vschema.SrvVSchema.KeyspacesEntry\x1a\x43\n\x0eKeyspacesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12 \n\x05value\x18\x02 \x01(\x0b\x32\x11.vschema.Keyspace:\x02\x38\x01\x42&Z$vitess.io/vitess/go/vt/proto/vschemab\x06proto3') + serialized_pb=_b('\n\rvschema.proto\x12\x07vschema\x1a\x0bquery.proto\"\xfe\x01\n\x08Keyspace\x12\x0f\n\x07sharded\x18\x01 \x01(\x08\x12\x31\n\x08vindexes\x18\x02 \x03(\x0b\x32\x1f.vschema.Keyspace.VindexesEntry\x12-\n\x06tables\x18\x03 \x03(\x0b\x32\x1d.vschema.Keyspace.TablesEntry\x1a@\n\rVindexesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x1e\n\x05value\x18\x02 \x01(\x0b\x32\x0f.vschema.Vindex:\x02\x38\x01\x1a=\n\x0bTablesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x1d\n\x05value\x18\x02 \x01(\x0b\x32\x0e.vschema.Table:\x02\x38\x01\"\x81\x01\n\x06Vindex\x12\x0c\n\x04type\x18\x01 \x01(\t\x12+\n\x06params\x18\x02 \x03(\x0b\x32\x1b.vschema.Vindex.ParamsEntry\x12\r\n\x05owner\x18\x03 \x01(\t\x1a-\n\x0bParamsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\xca\x01\n\x05Table\x12\x0c\n\x04type\x18\x01 \x01(\t\x12.\n\x0f\x63olumn_vindexes\x18\x02 \x03(\x0b\x32\x15.vschema.ColumnVindex\x12.\n\x0e\x61uto_increment\x18\x03 \x01(\x0b\x32\x16.vschema.AutoIncrement\x12 \n\x07\x63olumns\x18\x04 \x03(\x0b\x32\x0f.vschema.Column\x12\x0e\n\x06pinned\x18\x05 \x01(\t\x12!\n\x19\x63olumn_list_authoritative\x18\x06 \x01(\x08\"=\n\x0c\x43olumnVindex\x12\x0e\n\x06\x63olumn\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x0f\n\x07\x63olumns\x18\x03 \x03(\t\"1\n\rAutoIncrement\x12\x0e\n\x06\x63olumn\x18\x01 \x01(\t\x12\x10\n\x08sequence\x18\x02 \x01(\t\"1\n\x06\x43olumn\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x19\n\x04type\x18\x02 \x01(\x0e\x32\x0b.query.Type\"\x88\x01\n\nSrvVSchema\x12\x35\n\tkeyspaces\x18\x01 \x03(\x0b\x32\".vschema.SrvVSchema.KeyspacesEntry\x1a\x43\n\x0eKeyspacesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12 \n\x05value\x18\x02 \x01(\x0b\x32\x11.vschema.Keyspace:\x02\x38\x01\x42&Z$vitess.io/vitess/go/vt/proto/vschemab\x06proto3') , dependencies=[query__pb2.DESCRIPTOR,]) @@ -270,6 +270,13 @@ message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='column_list_authoritative', full_name='vschema.Table.column_list_authoritative', index=5, + number=6, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), ], extensions=[ ], @@ -283,7 +290,7 @@ oneofs=[ ], serialized_start=429, - serialized_end=596, + serialized_end=631, ) @@ -327,8 +334,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=598, - serialized_end=659, + serialized_start=633, + serialized_end=694, ) @@ -365,8 +372,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=661, - serialized_end=710, + serialized_start=696, + serialized_end=745, ) @@ -403,8 +410,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=712, - serialized_end=761, + serialized_start=747, + serialized_end=796, ) @@ -441,8 +448,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=833, - serialized_end=900, + serialized_start=868, + serialized_end=935, ) _SRVVSCHEMA = _descriptor.Descriptor( @@ -471,8 +478,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=764, - serialized_end=900, + serialized_start=799, + serialized_end=935, ) _KEYSPACE_VINDEXESENTRY.fields_by_name['value'].message_type = _VINDEX