Skip to content

Commit e3fa141

Browse files
committed
WL#10358: Implement table value constructors: VALUES
Implement the standard syntax for table value constructors (TVC). The SQL standard lets us use VALUES syntax, i.e. TVC, wherever a SELECT query specification may be used. This allows for statements such as VALUES (1, 'one'), (2, 'two'), which previously only could be emulated with UNIONS. Note: The current MySQL server has a conflicting non-standard feature: the VALUES() function of ON DUPLICATE KEY UPDATE update expressions, which is a showstopper for the standard TVC syntax. However, the standard also accepts a verbose form of TVC: VALUES ROW(), ROW() .., which is used instead. Also extend <query specification> to allow an <explicit table> clause as valid syntax. The syntax TABLE <table name> is equivalent to SELECT * FROM <table name>. The special syntax for INSERT statements, INSERT .. VALUES, remains as-is. In addition, INSERT statements with VALUES that use the new verbose syntax are rewritten to follow the same execution path as the old syntax. Reviewed by: Roy Lyseng <roy.lyseng@oracle.com>
1 parent 63c10d6 commit e3fa141

16 files changed

+637
-33
lines changed

mysql-test/suite/funcs_1/r/storedproc.result

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5605,7 +5605,7 @@ CREATE PROCEDURE sp1()
56055605
table:BEGIN
56065606
SELECT @x;
56075607
END//
5608-
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'table:BEGIN
5608+
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ':BEGIN
56095609
SELECT @x;
56105610
END' at line 2
56115611
DROP PROCEDURE IF EXISTS sp1;
@@ -5779,7 +5779,7 @@ CREATE PROCEDURE sp1()
57795779
values:BEGIN
57805780
SELECT @x;
57815781
END//
5782-
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'values:BEGIN
5782+
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ':BEGIN
57835783
SELECT @x;
57845784
END' at line 2
57855785
DROP PROCEDURE IF EXISTS sp1;

share/errmsg-utf8.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9203,6 +9203,12 @@ ER_CONSTRAINT_NOT_FOUND
92039203
ER_ALTER_CONSTRAINT_ENFORCEMENT_NOT_SUPPORTED
92049204
eng "Altering constraint enforcement is not supported for the constraint '%-.192s'. Enforcement state alter is not supported for the PRIMARY, UNIQUE and FOREIGN KEY type constraints."
92059205

9206+
ER_TABLE_VALUE_CONSTRUCTOR_MUST_HAVE_COLUMNS
9207+
eng "Each row of a VALUES clause must have at least one column, unless when used as source in an INSERT statement."
9208+
9209+
ER_TABLE_VALUE_CONSTRUCTOR_CANNOT_HAVE_DEFAULT
9210+
eng "A VALUES clause cannot use DEFAULT values, unless used as a source in an INSERT statement."
9211+
92069212
#
92079213
# End of 8.0 error messages (server-to-client).
92089214
# Do NOT add messages intended for the error log above!

sql/basic_row_iterators.h

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
#include "my_inttypes.h"
3939
#include "sql/mem_root_array.h"
4040
#include "sql/row_iterator.h"
41+
#include "sql/sql_list.h"
4142

4243
class Filesort_info;
4344
class Item;
@@ -517,4 +518,53 @@ class FollowTailIterator final : public TableRowIterator {
517518
ha_rows *m_stored_rows = nullptr;
518519
};
519520

521+
/**
522+
TableValueConstructor is the iterator for the table value constructor case of
523+
a query_primary (i.e. queries of the form VALUES row_list; e.g. VALUES ROW(1,
524+
10), ROW(2, 20)).
525+
526+
The iterator is passed the field list of its parent JOIN object, which may
527+
contain Item_values_column objects that are created during
528+
SELECT_LEX::prepare_values(). This is required so that Read() can replace the
529+
currently selected row by simply changing the references of Item_values_column
530+
objects to the next row.
531+
532+
The iterator must output multiple rows without being materialized, and does
533+
not scan any tables. The indirection of Item_values_column is required, as the
534+
executor outputs what is contained in join->fields (either directly, or
535+
indirectly through ConvertItemsToCopy), and is thus responsible for ensuring
536+
that join->fields contains the correct next row.
537+
*/
538+
class TableValueConstructorIterator final : public RowIterator {
539+
public:
540+
TableValueConstructorIterator(THD *thd, ha_rows *examined_rows,
541+
const List<List<Item>> &row_value_list,
542+
List<Item> *join_fields);
543+
544+
bool Init() override;
545+
int Read() override;
546+
547+
std::vector<std::string> DebugString() const override {
548+
return {"Rows fetched before execution"};
549+
}
550+
551+
void SetNullRowFlag(bool) override { DBUG_ASSERT(false); }
552+
553+
void UnlockRow() override {}
554+
555+
private:
556+
ha_rows *const m_examined_rows{nullptr};
557+
558+
/// Contains the row values that are part of a VALUES clause. Read() will
559+
/// modify contained Item objects during execution by calls to is_null() and
560+
/// the required val function to extract its value.
561+
const List<List<Item>> &m_row_value_list;
562+
List_STL_Iterator<const List<Item>> m_row_it;
563+
564+
/// References to the row we currently want to output. When multiple rows must
565+
/// be output, this contains Item_values_column objects. In this case, each
566+
/// call to Read() will replace its current reference with the next row.
567+
List<Item> *const m_output_refs;
568+
};
569+
520570
#endif // SQL_BASIC_ROW_ITERATORS_H_

sql/item.cc

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9519,6 +9519,111 @@ bool Item_type_holder::get_time(MYSQL_TIME *) {
95199519
return true;
95209520
}
95219521

9522+
type_conversion_status Item_values_column::save_in_field_inner(
9523+
Field *to, bool no_conversions) {
9524+
type_conversion_status res;
9525+
res = m_value_ref->save_in_field(to, no_conversions);
9526+
null_value = m_value_ref->null_value;
9527+
return res;
9528+
}
9529+
9530+
Item_values_column::Item_values_column(THD *thd, Item *ref) : super(thd, ref) {
9531+
fixed = true;
9532+
}
9533+
9534+
/* purecov: begin deadcode */
9535+
9536+
bool Item_values_column::eq(const Item *item, bool binary_cmp) const {
9537+
DBUG_ASSERT(false);
9538+
const Item *it = const_cast<Item *>(item)->real_item();
9539+
return m_value_ref && m_value_ref->eq(it, binary_cmp);
9540+
}
9541+
9542+
/* purecov: end */
9543+
9544+
double Item_values_column::val_real() {
9545+
DBUG_ASSERT(fixed);
9546+
double tmp = m_value_ref->val_real();
9547+
null_value = m_value_ref->null_value;
9548+
return tmp;
9549+
}
9550+
9551+
longlong Item_values_column::val_int() {
9552+
DBUG_ASSERT(fixed);
9553+
longlong tmp = m_value_ref->val_int();
9554+
null_value = m_value_ref->null_value;
9555+
return tmp;
9556+
}
9557+
9558+
/* purecov: begin deadcode */
9559+
9560+
my_decimal *Item_values_column::val_decimal(my_decimal *decimal_value) {
9561+
DBUG_ASSERT(false);
9562+
DBUG_ASSERT(fixed);
9563+
my_decimal *val = m_value_ref->val_decimal(decimal_value);
9564+
null_value = m_value_ref->null_value;
9565+
return val;
9566+
}
9567+
9568+
bool Item_values_column::val_bool() {
9569+
DBUG_ASSERT(false);
9570+
DBUG_ASSERT(fixed);
9571+
bool tmp = m_value_ref->val_bool();
9572+
null_value = m_value_ref->null_value;
9573+
return tmp;
9574+
}
9575+
9576+
bool Item_values_column::val_json(Json_wrapper *result) {
9577+
DBUG_ASSERT(false);
9578+
DBUG_ASSERT(fixed);
9579+
bool ok = m_value_ref->val_json(result);
9580+
null_value = m_value_ref->null_value;
9581+
return ok;
9582+
}
9583+
9584+
/* purecov: end */
9585+
9586+
String *Item_values_column::val_str(String *tmp) {
9587+
DBUG_ASSERT(fixed);
9588+
tmp = m_value_ref->val_str(tmp);
9589+
null_value = m_value_ref->null_value;
9590+
return tmp;
9591+
}
9592+
9593+
bool Item_values_column::is_null() {
9594+
DBUG_ASSERT(fixed);
9595+
/*
9596+
Item_values_column is dualistic in nature: It represents both a set
9597+
of values, and, during evaluation, an individual value in this set.
9598+
This assert will ensure that we only check nullability of individual
9599+
values, since a set of values is never NULL. Note that setting
9600+
RAND_TABLE_BIT in the constructor prevents this function from being called
9601+
during resolving.
9602+
*/
9603+
DBUG_ASSERT(m_value_ref != nullptr);
9604+
bool tmp = m_value_ref->is_null();
9605+
null_value = m_value_ref->null_value;
9606+
return tmp;
9607+
}
9608+
9609+
bool Item_values_column::get_date(MYSQL_TIME *ltime,
9610+
my_time_flags_t fuzzydate) {
9611+
DBUG_ASSERT(fixed);
9612+
bool result = m_value_ref->get_date(ltime, fuzzydate);
9613+
null_value = m_value_ref->null_value;
9614+
return result;
9615+
}
9616+
9617+
bool Item_values_column::get_time(MYSQL_TIME *ltime) {
9618+
DBUG_ASSERT(fixed);
9619+
DBUG_ASSERT(m_value_ref != nullptr);
9620+
return m_value_ref->get_time(ltime);
9621+
}
9622+
9623+
void Item_values_column::add_used_tables(Item *value) {
9624+
m_aggregated_used_tables |= value->used_tables();
9625+
}
9626+
95229627
void Item_result_field::cleanup() {
95239628
DBUG_TRACE;
95249629
Item::cleanup();

sql/item.h

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -728,7 +728,8 @@ class Item : public Parse_tree_node {
728728
XPATH_NODESET_CMP,
729729
VIEW_FIXER_ITEM,
730730
FIELD_BIT_ITEM,
731-
NULL_RESULT_ITEM
731+
NULL_RESULT_ITEM,
732+
VALUES_COLUMN_ITEM
732733
};
733734

734735
enum cond_result { COND_UNDEF, COND_OK, COND_TRUE, COND_FALSE };
@@ -6124,6 +6125,57 @@ class Item_type_holder final : public Item_aggregate_type {
61246125
bool get_time(MYSQL_TIME *) override;
61256126
};
61266127

6128+
/**
6129+
Reference item that encapsulates both the type and the contained items of a
6130+
single column of a VALUES ROW query expression.
6131+
6132+
During execution, the item that will be output for the current iteration is
6133+
contained in m_value_ref. The type of the column and the referenced item may
6134+
differ in cases where a column of a VALUES clause contains different types
6135+
across different rows, and must therefore do type conversions to their common
6136+
denominator (e.g. a column containing both 10 and "10", of which the types
6137+
will be aggregated into VARCHAR).
6138+
6139+
See the class comment for TableValueConstructorIterator for info on how
6140+
Item_values_column is used as an indirection to iterate over the rows of a
6141+
table value constructor (i.e. VALUES ROW expressions).
6142+
*/
6143+
class Item_values_column final : public Item_aggregate_type {
6144+
typedef Item_aggregate_type super;
6145+
6146+
private:
6147+
Item *m_value_ref{nullptr};
6148+
/*
6149+
Even if a table value constructor contains only constant values, we
6150+
still need to identify individual rows within it. Set RAND_TABLE_BIT
6151+
to ensure that all rows are scanned, and that the whole VALUES clause
6152+
is never substituted with a const value or row.
6153+
*/
6154+
table_map m_aggregated_used_tables{RAND_TABLE_BIT};
6155+
6156+
type_conversion_status save_in_field_inner(Field *field,
6157+
bool no_conversions) override;
6158+
6159+
public:
6160+
Item_values_column(THD *thd, Item *ref);
6161+
6162+
bool eq(const Item *item, bool binary_cmp) const override;
6163+
double val_real() override;
6164+
longlong val_int() override;
6165+
my_decimal *val_decimal(my_decimal *) override;
6166+
bool val_bool() override;
6167+
String *val_str(String *tmp) override;
6168+
bool val_json(Json_wrapper *result) override;
6169+
bool is_null() override;
6170+
bool get_date(MYSQL_TIME *ltime, my_time_flags_t fuzzydate) override;
6171+
bool get_time(MYSQL_TIME *ltime) override;
6172+
6173+
enum Type type() const override { return VALUES_COLUMN_ITEM; }
6174+
void set_value(Item *new_value) { m_value_ref = new_value; }
6175+
table_map used_tables() const override { return m_aggregated_used_tables; }
6176+
void add_used_tables(Item *value);
6177+
};
6178+
61276179
/// A class that represents a constant JSON value.
61286180
class Item_json final : public Item_basic_constant {
61296181
unique_ptr_destroy_only<Json_wrapper> m_value;

sql/item_cmpfunc.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5727,7 +5727,7 @@ void Item_is_not_null_test::update_used_tables() {
57275727
set_accum_properties(args[0]);
57285728
used_tables_cache |= args[0]->used_tables();
57295729

5730-
if (used_tables_cache == initial_pseudo_tables)
5730+
if (used_tables_cache == initial_pseudo_tables && args[0]->const_item())
57315731
/* Remember if the value is always NULL or never NULL */
57325732
cached_value = !args[0]->is_null();
57335733
}

sql/item_subselect.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1966,7 +1966,7 @@ Item_in_subselect::single_value_in_to_exists_transformer(THD *thd,
19661966
*/
19671967
Item *orig_item = select->item_list.head()->real_item();
19681968

1969-
if (select->table_list.elements || select->where_cond()) {
1969+
if (!select->source_table_is_one_row() || select->where_cond()) {
19701970
bool tmp;
19711971
Item_bool_func *item = func->create(m_injected_left_expr, orig_item);
19721972
/*

sql/parse_tree_nodes.cc

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -811,6 +811,33 @@ Sql_cmd *PT_insert::make_cmd(THD *thd) {
811811

812812
Parse_context pc(thd, lex->current_select());
813813

814+
// Currently there are two syntaxes (old and new, respectively) for INSERT
815+
// .. VALUES statements:
816+
//
817+
// - INSERT .. VALUES (), () ..
818+
// - INSERT .. VALUES ROW(), ROW() ..
819+
//
820+
// The latter is a table value constructor, i.e. it has an subquery
821+
// expression, while the former is the standard VALUES syntax. When the
822+
// non-standard VALUES() function (primarily used in ON DUPLICATE KEY UPDATE
823+
// update expressions) is deprecated in the future, the old syntax can be used
824+
// as a table value constructor as well.
825+
//
826+
// However, until such a change is made, we convert INSERT statements with
827+
// table value constructors into PT_insert objects that are equal to the old
828+
// syntax, as to enforce consistency by making sure they both follow the same
829+
// execution path.
830+
//
831+
// Note that this removes the constness of both row_value_list and
832+
// insert_query_expression, which should both be restored when deprecating
833+
// VALUES as mentioned above.
834+
if (has_select() && insert_query_expression->is_table_value_constructor()) {
835+
row_value_list = insert_query_expression->get_row_value_list();
836+
DBUG_ASSERT(row_value_list != nullptr);
837+
838+
insert_query_expression = nullptr;
839+
}
840+
814841
if (is_replace) {
815842
lex->sql_command = has_select() ? SQLCOM_REPLACE_SELECT : SQLCOM_REPLACE;
816843
lex->duplicates = DUP_REPLACE;
@@ -1026,6 +1053,21 @@ bool PT_query_specification::contextualize(Parse_context *pc) {
10261053
return false;
10271054
}
10281055

1056+
bool PT_table_value_constructor::contextualize(Parse_context *pc) {
1057+
if (row_value_list->contextualize(pc)) return true;
1058+
1059+
pc->select->is_table_value_constructor = true;
1060+
pc->select->row_value_list = &row_value_list->get_many_values();
1061+
1062+
// Some queries, such as CREATE TABLE with SELECT, require item_list to
1063+
// contain items to call SELECT_LEX::prepare.
1064+
for (Item &item : *pc->select->row_value_list->head()) {
1065+
pc->select->item_list.push_back(&item);
1066+
}
1067+
1068+
return false;
1069+
}
1070+
10291071
bool PT_query_expression::contextualize_order_and_limit(Parse_context *pc) {
10301072
/*
10311073
Quick reject test. We don't need to do anything if there are no limit

0 commit comments

Comments
 (0)