@@ -48,38 +48,193 @@ var halfSizeString = string(makeTestBytes(2000, 2))
4848// We expect a tuple to never store this value out-of-band.
4949var tinyString = string (makeTestBytes (10 , 3 ))
5050
51- func TestAdaptiveEncoding (t * testing.T ) {
52- columnType := "text"
51+ // A 4000 byte file starting with ascii byte 1 and then consisting of all zeros.
52+ // This is larger than default target tuple size for outlining adaptive types.
53+ // We expect a tuple to always store this value out-of-band
54+ var fullSizeVarbit = string (makeTestBytes (4000 , '1' ))
55+
56+ // A 2000 byte file starting with ascii byte 1 and then consisting of all zeros.
57+ // This is over half of the default target tuple size for outlining adaptive types.
58+ // We expect a tuple to be able to store this value inline once, but not twice.
59+ var halfSizeVarbit = string (makeTestBytes (2000 , '1' ))
60+
61+ // A 10 byte file starting with ascii byte 1 and then consisting of 10 zero bytes.
62+ // This is file is smaller than an address hash.
63+ // We expect a tuple to never store this value out-of-band.
64+ var tinyVarbit = string (makeTestBytes (10 , '1' ))
65+
66+ func TestAdaptiveEncodingText (t * testing.T ) {
5367 fullSizeOutOfLineRepr := fullSizeString
68+ columnTypes := []string {"varchar" , "text" }
69+ for _ , columnType := range columnTypes {
70+ t .Run (columnType , func (t * testing.T ) {
71+ RunScripts (t , []ScriptTest {
72+ {
73+ Name : "Adaptive Encoding With One Column" ,
74+ SetUpScript : setup.SetupScript {
75+ fmt .Sprintf (`create table blobt (i char(1) primary key, b %s);` , columnType ),
76+ fmt .Sprintf (`create table blobt2 (i char(2) primary key, b1 %s, b2 %s);` , columnType , columnType ),
77+ `insert into blobt values
78+ ('F', LOAD_FILE('testdata/fullSize')),
79+ ('H', LOAD_FILE('testdata/halfSize')),
80+ ('T', LOAD_FILE('testdata/tinyFile'))` ,
81+ },
82+ Assertions : []ScriptTestAssertion {
83+ {
84+ Query : "select b from blobt where i = 'F'" ,
85+ Expected : []sql.Row {{fullSizeString }},
86+ },
87+ {
88+ // Files that can fit within a tuple are stored inline.
89+ Query : "select b from blobt where i = 'H'" ,
90+ Expected : []sql.Row {{halfSizeString }},
91+ },
92+ {
93+ // An inlined adaptive column can be used in a filter.
94+ Query : "select i from blobt where b = LOAD_FILE('testdata/fullSize')" ,
95+ Expected : []sql.Row {{"F" }},
96+ },
97+ {
98+ // An out-of-line adaptive column can be used in a filter.
99+ Query : "select i from blobt where b = LOAD_FILE('testdata/halfSize')" ,
100+ Expected : []sql.Row {{"H" }},
101+ },
102+ },
103+ },
104+ {
105+ Name : "Adaptive Encoding With Two Columns" ,
106+ SetUpScript : setup.SetupScript {
107+ fmt .Sprintf (`create table blobt2 (i char(2) primary key, b1 %s, b2 %s);` , columnType , columnType ),
108+ `insert into blobt2 values
109+ ('FF', LOAD_FILE('testdata/fullSize'), LOAD_FILE('testdata/fullSize')),
110+ ('HF', LOAD_FILE('testdata/halfSize'), LOAD_FILE('testdata/fullSize')),
111+ ('TF', LOAD_FILE('testdata/tinyFile'), LOAD_FILE('testdata/fullSize')),
112+ ('FH', LOAD_FILE('testdata/fullSize'), LOAD_FILE('testdata/halfSize')),
113+ ('HH', LOAD_FILE('testdata/halfSize'), LOAD_FILE('testdata/halfSize')),
114+ ('TH', LOAD_FILE('testdata/tinyFile'), LOAD_FILE('testdata/halfSize')),
115+ ('FT', LOAD_FILE('testdata/fullSize'), LOAD_FILE('testdata/tinyFile')),
116+ ('HT', LOAD_FILE('testdata/halfSize'), LOAD_FILE('testdata/tinyFile')),
117+ ('TT', LOAD_FILE('testdata/tinyFile'), LOAD_FILE('testdata/tinyFile'))` ,
118+ },
119+ Assertions : []ScriptTestAssertion {
120+ {
121+ // When a tuple with multiple adaptive columns is too large, columns are moved out-of-band from left to right.
122+ // However, strings smaller than the address size (20 bytes) are never stored out-of-band.
123+ Query : "select i, b1, b2 from blobt2" ,
124+ Expected : []sql.Row {
125+ {"FF" , fullSizeString , fullSizeString },
126+ {"HF" , halfSizeString , fullSizeString },
127+ {"TF" , tinyString , fullSizeString },
128+ {"FH" , fullSizeString , halfSizeString },
129+ {"HH" , halfSizeString , halfSizeString },
130+ {"TH" , tinyString , halfSizeString },
131+ {"FT" , fullSizeString , tinyString },
132+ {"HT" , halfSizeString , tinyString },
133+ {"TT" , tinyString , tinyString },
134+ },
135+ },
136+ {
137+ // An adaptive column can be used in a filter when it doesn't have the same encoding in all rows.
138+ Query : "select i from blobt2 where b1 = LOAD_FILE('testdata/halfSize')" ,
139+ Expected : []sql.Row {{"HF" }, {"HH" }, {"HT" }},
140+ },
141+ {
142+ // An adaptive column can be used in a filter when it doesn't have the same encoding in all rows.
143+ Query : "select i from blobt2 where b2 = LOAD_FILE('testdata/halfSize')" ,
144+ Expected : []sql.Row {{"FH" }, {"HH" }, {"TH" }},
145+ },
146+ {
147+ // Test creating an index on an adaptive encoding column, matching against out-of-band values
148+ Query : "CREATE INDEX bidx ON blobt2 (b1)" ,
149+ },
150+ {
151+ Query : "select i, b1 FROM blobt2 WHERE b1 LIKE '\x01 %'" ,
152+ Expected : []sql.Row {
153+ {"FF" , fullSizeOutOfLineRepr },
154+ {"FH" , fullSizeOutOfLineRepr },
155+ {"FT" , fullSizeOutOfLineRepr },
156+ },
157+ },
158+ {
159+ // Test creating an index on an adaptive encoding column, matching against inline values
160+ Query : "CREATE INDEX bidx2 ON blobt2 (b2)" ,
161+ },
162+ {
163+ Query : "select i, b2 FROM blobt2 WHERE b2 LIKE '\x02 %'" ,
164+ Expected : []sql.Row {
165+ {"FH" , halfSizeString },
166+ {"HH" , halfSizeString },
167+ {"TH" , halfSizeString },
168+ },
169+ },
170+ {
171+ // Tuples containing adaptive columns should be independent of how the tuple was created.
172+ // And adaptive values are always outlined starting from the left.
173+ // This means that in a table with two adaptive columns where both columns were previously stored out-of line,
174+ // Decreasing the size of the second column may allow both columns to be stored inline.
175+ Query : "UPDATE blobt2 SET b2 = LOAD_FILE('testdata/tinyFile') WHERE i = 'HH'" ,
176+ },
177+ {
178+ Query : "select i, b1, b2 from blobt2 where i = 'HH'" ,
179+ Expected : []sql.Row {{"HH" , halfSizeString , tinyString }},
180+ },
181+ {
182+ // Similar to the above, dropping a column can change whether the other column is inlined.
183+ Query : "ALTER TABLE blobt2 DROP COLUMN b2" ,
184+ },
185+ {
186+ Query : "select i, b1 from blobt2" ,
187+ Expected : []sql.Row {
188+ {"FF" , fullSizeString },
189+ {"HF" , halfSizeString },
190+ {"TF" , tinyString },
191+ {"FH" , fullSizeString },
192+ {"HH" , halfSizeString },
193+ {"TH" , tinyString },
194+ {"FT" , fullSizeString },
195+ {"HT" , halfSizeString },
196+ {"TT" , tinyString },
197+ },
198+ },
199+ },
200+ },
201+ })
202+ })
203+ }
204+ }
205+
206+ func TestAdaptiveEncodingVarbit (t * testing.T ) {
207+ columnType := "varbit"
208+ fullSizeOutOfLineRepr := fullSizeVarbit
54209 RunScripts (t , []ScriptTest {
55210 {
56211 Name : "Adaptive Encoding With One Column" ,
57212 SetUpScript : setup.SetupScript {
58213 fmt .Sprintf (`create table blobt (i char(1) primary key, b %s);` , columnType ),
59214 fmt .Sprintf (`create table blobt2 (i char(2) primary key, b1 %s, b2 %s);` , columnType , columnType ),
60215 `insert into blobt values
61- ('F', LOAD_FILE('testdata/fullSize ')),
62- ('H', LOAD_FILE('testdata/halfSize ')),
63- ('T', LOAD_FILE('testdata/tinyFile '))` ,
216+ ('F', LOAD_FILE('testdata/fullSizeVarbit ')),
217+ ('H', LOAD_FILE('testdata/halfSizeVarbit ')),
218+ ('T', LOAD_FILE('testdata/tinyFileVarbit '))` ,
64219 },
65220 Assertions : []ScriptTestAssertion {
66221 {
67222 Query : "select b from blobt where i = 'F'" ,
68- Expected : []sql.Row {{fullSizeString }},
223+ Expected : []sql.Row {{fullSizeVarbit }},
69224 },
70225 {
71226 // Files that can fit within a tuple are stored inline.
72227 Query : "select b from blobt where i = 'H'" ,
73- Expected : []sql.Row {{halfSizeString }},
228+ Expected : []sql.Row {{halfSizeVarbit }},
74229 },
75230 {
76231 // An inlined adaptive column can be used in a filter.
77- Query : "select i from blobt where b = LOAD_FILE('testdata/fullSize ')" ,
232+ Query : "select i from blobt where b = LOAD_FILE('testdata/fullSizeVarbit ')" ,
78233 Expected : []sql.Row {{"F" }},
79234 },
80235 {
81236 // An out-of-line adaptive column can be used in a filter.
82- Query : "select i from blobt where b = LOAD_FILE('testdata/halfSize ')" ,
237+ Query : "select i from blobt where b = LOAD_FILE('testdata/halfSizeVarbit ')" ,
83238 Expected : []sql.Row {{"H" }},
84239 },
85240 },
@@ -105,15 +260,15 @@ func TestAdaptiveEncoding(t *testing.T) {
105260 // However, strings smaller than the address size (20 bytes) are never stored out-of-band.
106261 Query : "select i, b1, b2 from blobt2" ,
107262 Expected : []sql.Row {
108- {"FF" , fullSizeString , fullSizeString },
109- {"HF" , halfSizeString , fullSizeString },
110- {"TF" , tinyString , fullSizeString },
111- {"FH" , fullSizeString , halfSizeString },
112- {"HH" , halfSizeString , halfSizeString },
113- {"TH" , tinyString , halfSizeString },
114- {"FT" , fullSizeString , tinyString },
115- {"HT" , halfSizeString , tinyString },
116- {"TT" , tinyString , tinyString },
263+ {"FF" , fullSizeVarbit , fullSizeVarbit },
264+ {"HF" , halfSizeVarbit , fullSizeVarbit },
265+ {"TF" , tinyVarbit , fullSizeVarbit },
266+ {"FH" , fullSizeVarbit , halfSizeVarbit },
267+ {"HH" , halfSizeVarbit , halfSizeVarbit },
268+ {"TH" , tinyVarbit , halfSizeVarbit },
269+ {"FT" , fullSizeVarbit , tinyVarbit },
270+ {"HT" , halfSizeVarbit , tinyVarbit },
271+ {"TT" , tinyVarbit , tinyVarbit },
117272 },
118273 },
119274 {
@@ -145,9 +300,9 @@ func TestAdaptiveEncoding(t *testing.T) {
145300 {
146301 Query : "select i, b2 FROM blobt2 WHERE b2 LIKE '\x02 %'" ,
147302 Expected : []sql.Row {
148- {"FH" , halfSizeString },
149- {"HH" , halfSizeString },
150- {"TH" , halfSizeString },
303+ {"FH" , halfSizeVarbit },
304+ {"HH" , halfSizeVarbit },
305+ {"TH" , halfSizeVarbit },
151306 },
152307 },
153308 {
@@ -159,7 +314,7 @@ func TestAdaptiveEncoding(t *testing.T) {
159314 },
160315 {
161316 Query : "select i, b1, b2 from blobt2 where i = 'HH'" ,
162- Expected : []sql.Row {{"HH" , halfSizeString , tinyString }},
317+ Expected : []sql.Row {{"HH" , halfSizeVarbit , tinyVarbit }},
163318 },
164319 {
165320 // Similar to the above, dropping a column can change whether the other column is inlined.
@@ -168,15 +323,15 @@ func TestAdaptiveEncoding(t *testing.T) {
168323 {
169324 Query : "select i, b1 from blobt2" ,
170325 Expected : []sql.Row {
171- {"FF" , fullSizeString },
172- {"HF" , halfSizeString },
173- {"TF" , tinyString },
174- {"FH" , fullSizeString },
175- {"HH" , halfSizeString },
176- {"TH" , tinyString },
177- {"FT" , fullSizeString },
178- {"HT" , halfSizeString },
179- {"TT" , tinyString },
326+ {"FF" , fullSizeVarbit },
327+ {"HF" , halfSizeVarbit },
328+ {"TF" , tinyVarbit },
329+ {"FH" , fullSizeVarbit },
330+ {"HH" , halfSizeVarbit },
331+ {"TH" , tinyVarbit },
332+ {"FT" , fullSizeVarbit },
333+ {"HT" , halfSizeVarbit },
334+ {"TT" , tinyVarbit },
180335 },
181336 },
182337 },
0 commit comments