Skip to content

Commit afb1a66

Browse files
Fix the lifetimeof encoder for system temp tables
1 parent b00a94c commit afb1a66

3 files changed

Lines changed: 45 additions & 5 deletions

File tree

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
CREATE TABLE t1 (val vsql_complex.COMPLEX);
2+
INSERT INTO t1 VALUES ('(1.0, 2.0)'), ('(3.0, 4.0)');
3+
# String literal unioned with COMPLEX column: encoder runs on tmp table field
4+
SELECT '(5.0, 6.0)' AS val UNION SELECT val FROM t1 ORDER BY val;
5+
val
6+
(1,2)
7+
(3,4)
8+
(5,6)
9+
DROP TABLE t1;
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Regression test: encoding a string literal into a UNION result tmp table's
2+
# COMPLEX field must not allocate the TypeEncoder on TABLE::mem_root.
3+
#
4+
# A UNION where one arm is a string literal and the other is a COMPLEX column
5+
# forces GetTypeEncoderFor() to run on the UNION result tmp table's Field.
6+
# With the bug, close_tmp_table() asserts table->mem_root.allocated_size() == 0
7+
# in debug builds because the encoder was allocated there.
8+
9+
--source include/villagesql/install_complex_extension.inc
10+
11+
CREATE TABLE t1 (val vsql_complex.COMPLEX);
12+
INSERT INTO t1 VALUES ('(1.0, 2.0)'), ('(3.0, 4.0)');
13+
14+
--echo # String literal unioned with COMPLEX column: encoder runs on tmp table field
15+
SELECT '(5.0, 6.0)' AS val UNION SELECT val FROM t1 ORDER BY val;
16+
17+
DROP TABLE t1;
18+
19+
--source include/villagesql/uninstall_complex_extension.inc

villagesql/types/util.cc

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -238,14 +238,26 @@ bool HandleCustomColumnsForTableRename(THD &thd, const char *old_db,
238238
return false;
239239
}
240240

241-
// Lazily allocate a TypeEncoder for field from TABLE::mem_root. The encoder
242-
// is reused for all subsequent encodes on the same Field within the TABLE's
243-
// lifetime.
241+
// Lazily allocate a TypeEncoder for field, reused for all subsequent encodes
242+
// within the table's lifetime.
243+
//
244+
// For any kind of tmp table, close_tmp_table() asserts TABLE::mem_root is
245+
// empty, so we use TABLE_SHARE::mem_root instead. This covers UNION result
246+
// tables and any other internal tmp table that may hold custom type fields
247+
// (e.g. when a string literal is unioned with a COMPLEX column and must be
248+
// encoded into the result tmp table's field).
249+
// free_tmp_table() frees the share's mem_root, cleaning up the encoder.
250+
//
251+
// For regular tables (NO_TMP_TABLE), TABLE::mem_root has the same lifetime as
252+
// the Field clone that caches the encoder pointer, so both are freed together
253+
// when the TABLE is evicted from the table open cache.
244254
static TypeEncoder *GetTypeEncoderFor(Field *field) {
245255
TypeEncoder *encoder = field->get_type_encoder();
246256
if (encoder == nullptr) {
247-
encoder = new (&field->table->mem_root)
248-
TypeEncoder(field->get_type_context(), field->table->mem_root);
257+
MEM_ROOT &mem_root = (field->table->s->tmp_table == NO_TMP_TABLE)
258+
? field->table->mem_root
259+
: field->table->s->mem_root;
260+
encoder = new (&mem_root) TypeEncoder(field->get_type_context(), mem_root);
249261
if (encoder == nullptr) {
250262
my_error(ER_OUTOFMEMORY, MYF(ME_FATALERROR), sizeof(TypeEncoder));
251263
return nullptr;

0 commit comments

Comments
 (0)