Commit 3a75900b authored by unknown's avatar unknown

merge 5.5->10.0-base

parents 8f0be1ad dfed4478
...@@ -3041,6 +3041,10 @@ void open_file(const char *name) ...@@ -3041,6 +3041,10 @@ void open_file(const char *name)
5.try in basedir 5.try in basedir
*/ */
#ifdef __WIN__
fix_win_paths(curname, sizeof(curname));
#endif
bool in_overlay= opt_overlay_dir && bool in_overlay= opt_overlay_dir &&
!strncmp(curname, opt_overlay_dir, overlay_dir_len); !strncmp(curname, opt_overlay_dir, overlay_dir_len);
bool in_suiteir= opt_overlay_dir && !in_overlay && bool in_suiteir= opt_overlay_dir && !in_overlay &&
......
...@@ -42,3 +42,47 @@ select * from t1 where a in ('4828532208463511553'); ...@@ -42,3 +42,47 @@ select * from t1 where a in ('4828532208463511553');
a a
4828532208463511553 4828532208463511553
drop table t1; drop table t1;
#End of 4.1 tests
#
# MDEV-5103: server crashed on singular Item_equal
#
CREATE TABLE `t1` (
`tipo` enum('p','r') NOT NULL DEFAULT 'r',
`arquivo_id` bigint(20) unsigned NOT NULL DEFAULT '0',
`arquivo_md5` char(32) NOT NULL,
`conteudo` longblob NOT NULL,
`usuario` varchar(15) NOT NULL,
`datahora_gmt` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
`tipo_arquivo` varchar(255) NOT NULL,
`nome_arquivo` varchar(255) NOT NULL,
`tamanho_arquivo` bigint(20) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`tipo`,`arquivo_id`),
UNIQUE KEY `tipo` (`tipo`,`arquivo_md5`)
) ENGINE=Aria DEFAULT CHARSET=latin1 PAGE_CHECKSUM=1;
INSERT INTO `t1` (`tipo`, `arquivo_id`, `arquivo_md5`, `conteudo`, `usuario`, `datahora_gmt`, `tipo_arquivo`, `nome_arquivo`, `tamanho_arquivo`) VALUES
('r', 1, 'ad18832202b199728921807033a8a515', '', 'rspadim', '2013-10-05 13:55:50', '001_cbr643', 'CBR6431677410201314132.ret', 21306);
CREATE TABLE `t2` (
`tipo` enum('p','r') NOT NULL DEFAULT 'p',
`arquivo_id` bigint(20) NOT NULL DEFAULT '0',
`usuario` varchar(25) NOT NULL,
`datahora` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
`erros` longblob NOT NULL,
`importados` bigint(20) unsigned NOT NULL DEFAULT '0',
`n_importados` bigint(20) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`tipo`,`arquivo_id`,`datahora`)
) ENGINE=Aria DEFAULT CHARSET=latin1 PAGE_CHECKSUM=1;
INSERT INTO `t2` (`tipo`, `arquivo_id`, `usuario`, `datahora`, `erros`, `importados`, `n_importados`) VALUES
('r', 1, 'rspadim', '2013-10-05 14:25:30', '', 32, 0);
SELECT
arquivo_id,usuario,datahora_gmt,tipo_arquivo,nome_arquivo,tamanho_arquivo
FROM t1 AS a
WHERE datahora_gmt>='0000-00-00 00:00:00' AND
datahora_gmt<='2013-10-07 02:59:59' AND tipo='r' AND
(tipo_arquivo,arquivo_id) NOT IN
(SELECT tipo_arquivo,arquivo_id
FROM t2
WHERE (tipo_arquivo,arquivo_id)=(a.tipo_arquivo,a.arquivo_id))
ORDER BY arquivo_id DESC;
arquivo_id usuario datahora_gmt tipo_arquivo nome_arquivo tamanho_arquivo
drop table t2, t1;
#End of 5.3 tests
...@@ -4921,6 +4921,37 @@ q 1 q ...@@ -4921,6 +4921,37 @@ q 1 q
q 1 q q 1 q
drop view v1; drop view v1;
drop table t1,t2; drop table t1,t2;
#
# MDEV-5153: Server crashes in Item_ref::fix_fields on 2nd execution
# of PS with LEFT JOIN and MERGE view or SELECT SQ
#
CREATE TABLE t1 (i1 INT, c1 VARCHAR(6)) ENGINE=MyISAM;
INSERT INTO t1 VALUES (1,'foo'),(2,'bar');
CREATE TABLE t2 (c2 VARCHAR(6)) ENGINE=MyISAM;
INSERT INTO t2 VALUES ('foobar'),('qux');
CREATE ALGORITHM=MERGE VIEW v1 AS SELECT * FROM t1 WHERE ( c1 ) IN ( SELECT c2 FROM t2 ) AND i1 <= 2 ;
PREPARE stmt FROM 'SELECT * FROM t1 LEFT JOIN v1 ON (v1.i1 = t1.i1)';
EXECUTE stmt;
i1 c1 i1 c1
1 foo NULL NULL
2 bar NULL NULL
EXECUTE stmt;
i1 c1 i1 c1
1 foo NULL NULL
2 bar NULL NULL
drop view v1;
CREATE ALGORITHM=MERGE VIEW v1 AS SELECT * FROM t1 WHERE ( c1, c1 ) IN ( SELECT c2, c2 FROM t2 ) AND i1 <= 2 ;
EXECUTE stmt;
i1 c1 i1 c1
1 foo NULL NULL
2 bar NULL NULL
EXECUTE stmt;
i1 c1 i1 c1
1 foo NULL NULL
2 bar NULL NULL
deallocate prepare stmt;
drop view v1;
drop table t1,t2;
# ----------------------------------------------------------------- # -----------------------------------------------------------------
# -- End of 5.3 tests. # -- End of 5.3 tests.
# ----------------------------------------------------------------- # -----------------------------------------------------------------
......
select 1; select 1;
1 1
1 1
select 2;
2
2
...@@ -5,4 +5,8 @@ ...@@ -5,4 +5,8 @@
# three times - once for the parent suite, and once for each overlay. # three times - once for the parent suite, and once for each overlay.
# even if the test files are not overlayed. # even if the test files are not overlayed.
# #
# overlay.inc is overridden in mtr2-myisam, and there is an rdiff file.
#
select 1; select 1;
source overlay.inc;
...@@ -43,4 +43,54 @@ select * from t1 where a = '4828532208463511553'; ...@@ -43,4 +43,54 @@ select * from t1 where a = '4828532208463511553';
select * from t1 where a in ('4828532208463511553'); select * from t1 where a in ('4828532208463511553');
drop table t1; drop table t1;
# End of 4.1 tests --echo #End of 4.1 tests
--echo #
--echo # MDEV-5103: server crashed on singular Item_equal
--echo #
CREATE TABLE `t1` (
`tipo` enum('p','r') NOT NULL DEFAULT 'r',
`arquivo_id` bigint(20) unsigned NOT NULL DEFAULT '0',
`arquivo_md5` char(32) NOT NULL,
`conteudo` longblob NOT NULL,
`usuario` varchar(15) NOT NULL,
`datahora_gmt` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
`tipo_arquivo` varchar(255) NOT NULL,
`nome_arquivo` varchar(255) NOT NULL,
`tamanho_arquivo` bigint(20) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`tipo`,`arquivo_id`),
UNIQUE KEY `tipo` (`tipo`,`arquivo_md5`)
) ENGINE=Aria DEFAULT CHARSET=latin1 PAGE_CHECKSUM=1;
INSERT INTO `t1` (`tipo`, `arquivo_id`, `arquivo_md5`, `conteudo`, `usuario`, `datahora_gmt`, `tipo_arquivo`, `nome_arquivo`, `tamanho_arquivo`) VALUES
('r', 1, 'ad18832202b199728921807033a8a515', '', 'rspadim', '2013-10-05 13:55:50', '001_cbr643', 'CBR6431677410201314132.ret', 21306);
CREATE TABLE `t2` (
`tipo` enum('p','r') NOT NULL DEFAULT 'p',
`arquivo_id` bigint(20) NOT NULL DEFAULT '0',
`usuario` varchar(25) NOT NULL,
`datahora` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
`erros` longblob NOT NULL,
`importados` bigint(20) unsigned NOT NULL DEFAULT '0',
`n_importados` bigint(20) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`tipo`,`arquivo_id`,`datahora`)
) ENGINE=Aria DEFAULT CHARSET=latin1 PAGE_CHECKSUM=1;
INSERT INTO `t2` (`tipo`, `arquivo_id`, `usuario`, `datahora`, `erros`, `importados`, `n_importados`) VALUES
('r', 1, 'rspadim', '2013-10-05 14:25:30', '', 32, 0);
SELECT
arquivo_id,usuario,datahora_gmt,tipo_arquivo,nome_arquivo,tamanho_arquivo
FROM t1 AS a
WHERE datahora_gmt>='0000-00-00 00:00:00' AND
datahora_gmt<='2013-10-07 02:59:59' AND tipo='r' AND
(tipo_arquivo,arquivo_id) NOT IN
(SELECT tipo_arquivo,arquivo_id
FROM t2
WHERE (tipo_arquivo,arquivo_id)=(a.tipo_arquivo,a.arquivo_id))
ORDER BY arquivo_id DESC;
drop table t2, t1;
--echo #End of 5.3 tests
...@@ -4845,6 +4845,34 @@ SELECT * FROM t2 LEFT JOIN v1 ON ( c=b AND a IN ( 1,6 ) ); ...@@ -4845,6 +4845,34 @@ SELECT * FROM t2 LEFT JOIN v1 ON ( c=b AND a IN ( 1,6 ) );
drop view v1; drop view v1;
drop table t1,t2; drop table t1,t2;
--echo #
--echo # MDEV-5153: Server crashes in Item_ref::fix_fields on 2nd execution
--echo # of PS with LEFT JOIN and MERGE view or SELECT SQ
--echo #
CREATE TABLE t1 (i1 INT, c1 VARCHAR(6)) ENGINE=MyISAM;
INSERT INTO t1 VALUES (1,'foo'),(2,'bar');
CREATE TABLE t2 (c2 VARCHAR(6)) ENGINE=MyISAM;
INSERT INTO t2 VALUES ('foobar'),('qux');
CREATE ALGORITHM=MERGE VIEW v1 AS SELECT * FROM t1 WHERE ( c1 ) IN ( SELECT c2 FROM t2 ) AND i1 <= 2 ;
PREPARE stmt FROM 'SELECT * FROM t1 LEFT JOIN v1 ON (v1.i1 = t1.i1)';
EXECUTE stmt;
EXECUTE stmt;
drop view v1;
CREATE ALGORITHM=MERGE VIEW v1 AS SELECT * FROM t1 WHERE ( c1, c1 ) IN ( SELECT c2, c2 FROM t2 ) AND i1 <= 2 ;
EXECUTE stmt;
EXECUTE stmt;
deallocate prepare stmt;
drop view v1;
drop table t1,t2;
--echo # ----------------------------------------------------------------- --echo # -----------------------------------------------------------------
--echo # -- End of 5.3 tests. --echo # -- End of 5.3 tests.
--echo # ----------------------------------------------------------------- --echo # -----------------------------------------------------------------
......
...@@ -1438,9 +1438,11 @@ bool Item_in_optimizer::eval_not_null_tables(uchar *opt_arg) ...@@ -1438,9 +1438,11 @@ bool Item_in_optimizer::eval_not_null_tables(uchar *opt_arg)
bool Item_in_optimizer::fix_left(THD *thd) bool Item_in_optimizer::fix_left(THD *thd)
{ {
DBUG_ENTER("Item_in_optimizer::fix_left");
if ((!args[0]->fixed && args[0]->fix_fields(thd, args)) || if ((!args[0]->fixed && args[0]->fix_fields(thd, args)) ||
(!cache && !(cache= Item_cache::get_cache(args[0])))) (!cache && !(cache= Item_cache::get_cache(args[0]))))
return 1; DBUG_RETURN(1);
DBUG_PRINT("info", ("actual fix fields"));
cache->setup(args[0]); cache->setup(args[0]);
if (cache->cols() == 1) if (cache->cols() == 1)
...@@ -1466,11 +1468,15 @@ bool Item_in_optimizer::fix_left(THD *thd) ...@@ -1466,11 +1468,15 @@ bool Item_in_optimizer::fix_left(THD *thd)
{ {
my_error(ER_NOT_SUPPORTED_YET, MYF(0), my_error(ER_NOT_SUPPORTED_YET, MYF(0),
"SUBQUERY in ROW in left expression of IN/ALL/ANY"); "SUBQUERY in ROW in left expression of IN/ALL/ANY");
return 1; DBUG_RETURN(1);
} }
Item *element=args[0]->element_index(i); Item *element=args[0]->element_index(i);
if (element->used_tables() || !element->const_item()) if (element->used_tables() || !element->const_item())
((Item_cache *)cache->element_index(i))->set_used_tables(OUTER_REF_TABLE_BIT); {
((Item_cache *)cache->element_index(i))->
set_used_tables(OUTER_REF_TABLE_BIT);
cache->set_used_tables(OUTER_REF_TABLE_BIT);
}
else else
((Item_cache *)cache->element_index(i))->set_used_tables(0); ((Item_cache *)cache->element_index(i))->set_used_tables(0);
} }
...@@ -1491,7 +1497,7 @@ bool Item_in_optimizer::fix_left(THD *thd) ...@@ -1491,7 +1497,7 @@ bool Item_in_optimizer::fix_left(THD *thd)
with_sum_func= with_sum_func || args[1]->with_sum_func; with_sum_func= with_sum_func || args[1]->with_sum_func;
const_item_cache= const_item_cache && args[1]->const_item(); const_item_cache= const_item_cache && args[1]->const_item();
} }
return 0; DBUG_RETURN(0);
} }
...@@ -5794,6 +5800,12 @@ void Item_equal::add_const(Item *c, Item *f) ...@@ -5794,6 +5800,12 @@ void Item_equal::add_const(Item *c, Item *f)
func->quick_fix_field(); func->quick_fix_field();
cond_false= !func->val_int(); cond_false= !func->val_int();
} }
/*
TODO: also support the case where Item_equal becomes singular with
this->is_cond_true()=1. When I attempted to mark the item as constant,
the optimizer attempted to remove it, however it is still referenced from
COND_EQUAL and I got a crash.
*/
if (cond_false) if (cond_false)
const_item_cache= 1; const_item_cache= 1;
} }
...@@ -5998,7 +6010,8 @@ void Item_equal::merge_into_list(List<Item_equal> *list, ...@@ -5998,7 +6010,8 @@ void Item_equal::merge_into_list(List<Item_equal> *list,
void Item_equal::sort(Item_field_cmpfunc compare, void *arg) void Item_equal::sort(Item_field_cmpfunc compare, void *arg)
{ {
bubble_sort<Item>(&equal_items, compare, arg); if (equal_items.elements > 1)
bubble_sort<Item>(&equal_items, compare, arg);
} }
...@@ -6126,6 +6139,12 @@ bool Item_equal::fix_fields(THD *thd, Item **ref) ...@@ -6126,6 +6139,12 @@ bool Item_equal::fix_fields(THD *thd, Item **ref)
void Item_equal::update_used_tables() void Item_equal::update_used_tables()
{ {
not_null_tables_cache= used_tables_cache= 0; not_null_tables_cache= used_tables_cache= 0;
/*
TODO: also support the case where Item_equal becomes singular with
this->is_cond_true()=1. When I attempted to mark the item as constant,
the optimizer attempted to remove it, however it is still referenced from
COND_EQUAL and I got a crash.
*/
if ((const_item_cache= cond_false)) if ((const_item_cache= cond_false))
return; return;
Item_equal_fields_iterator it(*this); Item_equal_fields_iterator it(*this);
...@@ -6175,6 +6194,8 @@ longlong Item_equal::val_int() ...@@ -6175,6 +6194,8 @@ longlong Item_equal::val_int()
{ {
if (cond_false) if (cond_false)
return 0; return 0;
if (is_cond_true())
return 1;
Item *item= get_const(); Item *item= get_const();
Item_equal_fields_iterator it(*this); Item_equal_fields_iterator it(*this);
if (!item) if (!item)
...@@ -6199,6 +6220,11 @@ longlong Item_equal::val_int() ...@@ -6199,6 +6220,11 @@ longlong Item_equal::val_int()
void Item_equal::fix_length_and_dec() void Item_equal::fix_length_and_dec()
{ {
Item *item= get_first(NO_PARTICULAR_TAB, NULL); Item *item= get_first(NO_PARTICULAR_TAB, NULL);
if (!item)
{
DBUG_ASSERT(is_cond_true()); // it should be the only constant
item= equal_items.head();
}
eval_item= cmp_item::get_comparator(item->cmp_type(), item, eval_item= cmp_item::get_comparator(item->cmp_type(), item,
item->collation.collation); item->collation.collation);
} }
......
...@@ -1826,6 +1826,7 @@ class Item_equal: public Item_bool_func ...@@ -1826,6 +1826,7 @@ class Item_equal: public Item_bool_func
Item_equal(Item_equal *item_equal); Item_equal(Item_equal *item_equal);
/* Currently the const item is always the first in the list of equal items */ /* Currently the const item is always the first in the list of equal items */
inline Item* get_const() { return with_const ? equal_items.head() : NULL; } inline Item* get_const() { return with_const ? equal_items.head() : NULL; }
inline bool is_cond_true() { return equal_items.elements == 1; }
void add_const(Item *c, Item *f = NULL); void add_const(Item *c, Item *f = NULL);
/** Add a non-constant item to the multiple equality */ /** Add a non-constant item to the multiple equality */
void add(Item *f) { equal_items.push_back(f); } void add(Item *f) { equal_items.push_back(f); }
......
...@@ -2277,11 +2277,11 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join, ...@@ -2277,11 +2277,11 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join,
DBUG_RETURN(true); DBUG_RETURN(true);
Item *item_eq= Item *item_eq=
new Item_func_eq(new new Item_func_eq(new
Item_ref(&select_lex->context, Item_direct_ref(&select_lex->context,
(*optimizer->get_cache())-> (*optimizer->get_cache())->
addr(i), addr(i),
(char *)"<no matter>", (char *)"<no matter>",
(char *)in_left_expr_name), (char *)in_left_expr_name),
new new
Item_ref(&select_lex->context, Item_ref(&select_lex->context,
select_lex->ref_pointer_array + i, select_lex->ref_pointer_array + i,
...@@ -3000,7 +3000,7 @@ bool Item_exists_subselect::exists2in_processor(uchar *opt_arg) ...@@ -3000,7 +3000,7 @@ bool Item_exists_subselect::exists2in_processor(uchar *opt_arg)
bool bool
Item_in_subselect::select_in_like_transformer(JOIN *join) Item_in_subselect::select_in_like_transformer(JOIN *join)
{ {
Query_arena *arena, backup; Query_arena *arena= 0, backup;
SELECT_LEX *current= thd->lex->current_select; SELECT_LEX *current= thd->lex->current_select;
const char *save_where= thd->where; const char *save_where= thd->where;
bool trans_res= true; bool trans_res= true;
...@@ -3022,9 +3022,6 @@ Item_in_subselect::select_in_like_transformer(JOIN *join) ...@@ -3022,9 +3022,6 @@ Item_in_subselect::select_in_like_transformer(JOIN *join)
} }
} }
if (changed)
DBUG_RETURN(false);
thd->where= "IN/ALL/ANY subquery"; thd->where= "IN/ALL/ANY subquery";
/* /*
...@@ -3035,25 +3032,29 @@ Item_in_subselect::select_in_like_transformer(JOIN *join) ...@@ -3035,25 +3032,29 @@ Item_in_subselect::select_in_like_transformer(JOIN *join)
note: we won't need Item_in_optimizer when handling degenerate cases note: we won't need Item_in_optimizer when handling degenerate cases
like "... IN (SELECT 1)" like "... IN (SELECT 1)"
*/ */
arena= thd->activate_stmt_arena_if_needed(&backup);
if (!optimizer) if (!optimizer)
{ {
arena= thd->activate_stmt_arena_if_needed(&backup);
result= (!(optimizer= new Item_in_optimizer(left_expr, this))); result= (!(optimizer= new Item_in_optimizer(left_expr, this)));
if (arena)
thd->restore_active_arena(arena, &backup);
if (result) if (result)
goto err; goto out;
} }
thd->lex->current_select= current->return_after_parsing(); thd->lex->current_select= current->return_after_parsing();
result= (!left_expr->fixed && result= optimizer->fix_left(thd);
left_expr->fix_fields(thd, optimizer->arguments()));
/* fix_fields can change reference to left_expr, we need reassign it */ /* fix_fields can change reference to left_expr, we need reassign it */
left_expr= optimizer->arguments()[0]; left_expr= optimizer->arguments()[0];
thd->lex->current_select= current; thd->lex->current_select= current;
if (changed)
{
trans_res= false;
goto out;
}
if (result) if (result)
goto err; goto out;
/* /*
Both transformers call fix_fields() only for Items created inside them, Both transformers call fix_fields() only for Items created inside them,
...@@ -3062,7 +3063,6 @@ Item_in_subselect::select_in_like_transformer(JOIN *join) ...@@ -3062,7 +3063,6 @@ Item_in_subselect::select_in_like_transformer(JOIN *join)
of Item, we have to call fix_fields() for it only with original arena to of Item, we have to call fix_fields() for it only with original arena to
avoid memory leack) avoid memory leack)
*/ */
arena= thd->activate_stmt_arena_if_needed(&backup);
if (left_expr->cols() == 1) if (left_expr->cols() == 1)
trans_res= single_value_transformer(join); trans_res= single_value_transformer(join);
else else
...@@ -3077,9 +3077,9 @@ Item_in_subselect::select_in_like_transformer(JOIN *join) ...@@ -3077,9 +3077,9 @@ Item_in_subselect::select_in_like_transformer(JOIN *join)
} }
trans_res= row_value_transformer(join); trans_res= row_value_transformer(join);
} }
out:
if (arena) if (arena)
thd->restore_active_arena(arena, &backup); thd->restore_active_arena(arena, &backup);
err:
thd->where= save_where; thd->where= save_where;
DBUG_RETURN(trans_res); DBUG_RETURN(trans_res);
} }
......
...@@ -1272,11 +1272,11 @@ void get_delayed_table_estimates(TABLE *table, ...@@ -1272,11 +1272,11 @@ void get_delayed_table_estimates(TABLE *table,
@brief Replaces an expression destructively inside the expression tree of @brief Replaces an expression destructively inside the expression tree of
the WHERE clase. the WHERE clase.
@note Because of current requirements for semijoin flattening, we do not @note We substitute AND/OR structure because it was copied by
need to recurse here, hence this function will only examine the top-level copy_andor_structure and some changes could be done in the copy but
AND conditions. (see JOIN::prepare, comment starting with "Check if the should be left permanent, also there could be several layers of AND over
subquery predicate can be executed via materialization". AND and OR over OR because ::fix_field() possibly is not called.
@param join The top-level query. @param join The top-level query.
@param old_cond The expression to be replaced. @param old_cond The expression to be replaced.
@param new_cond The expression to be substituted. @param new_cond The expression to be substituted.
...@@ -1304,13 +1304,20 @@ static bool replace_where_subcondition(JOIN *join, Item **expr, ...@@ -1304,13 +1304,20 @@ static bool replace_where_subcondition(JOIN *join, Item **expr,
Item *item; Item *item;
while ((item= li++)) while ((item= li++))
{ {
if (item == old_cond) if (item == old_cond)
{ {
li.replace(new_cond); li.replace(new_cond);
if (do_fix_fields) if (do_fix_fields)
new_cond->fix_fields(join->thd, li.ref()); new_cond->fix_fields(join->thd, li.ref());
return FALSE; return FALSE;
} }
else if (item->type() == Item::COND_ITEM)
{
DBUG_ASSERT(!(*expr)->fixed);
replace_where_subcondition(join, li.ref(),
old_cond, new_cond,
do_fix_fields);
}
} }
} }
/* /*
......
--- suite/mtr2/single.result 2013-11-10 03:58:37.000000000 +0400
+++ suite/mtr2/single.reject 2013-11-10 03:59:08.000000000 +0400
@@ -1,6 +1,6 @@
select 1;
1
1
-select 2;
-2
-2
+select 3;
+3
+3
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment