Commit c2d9d9ff authored by unknown's avatar unknown

IN subselect erged with current 4.1 (pointer conversion)


sql/item.cc:
  Auto merged
sql/item_subselect.cc:
  Auto merged
sql/item_sum.cc:
  Auto merged
sql/sql_yacc.yy:
  Auto merged
sql/sql_lex.cc:
  Merged
sql/sql_lex.h:
  Merged
parents efc131e9 002803d9
...@@ -99,6 +99,17 @@ select * from t3 where not exists (select * from t2 where t2.b=t3.a); ...@@ -99,6 +99,17 @@ select * from t3 where not exists (select * from t2 where t2.b=t3.a);
a a
6 6
3 3
select * from t3 where a in (select b from t2);
a
7
select * from t3 where a not in (select b from t2);
a
6
3
select * from t3 where a in (select a,b from t2);
Subselect returns more than 1 field
select * from t3 where a in (select * from t2);
Subselect returns more than 1 field
insert into t4 values (12,7),(1,7),(10,9),(9,6),(7,6),(3,9); insert into t4 values (12,7),(1,7),(10,9),(9,6),(7,6),(3,9);
select b,max(a) as ma from t4 group by b having b < (select max(t2.a) select b,max(a) as ma from t4 group by b having b < (select max(t2.a)
from t2 where t2.b=t4.b); from t2 where t2.b=t4.b);
...@@ -219,4 +230,15 @@ id select_type table type possible_keys key key_len ref rows Extra ...@@ -219,4 +230,15 @@ id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY searchconthardwarefr3 index NULL topic 3 NULL 2 Using index 1 PRIMARY searchconthardwarefr3 index NULL topic 3 NULL 2 Using index
2 SUBSELECT No tables used 2 SUBSELECT No tables used
3 UNION No tables used 3 UNION No tables used
SELECT 1 IN (SELECT 1 FROM searchconthardwarefr3 HAVING a);
Unknown column 'a' in 'having clause'
SELECT * from searchconthardwarefr3 where topic IN (SELECT topic FROM searchconthardwarefr3 GROUP BY date);
topic date pseudo
40143 2002-08-03 joce
43506 2002-10-02 joce
SELECT * from searchconthardwarefr3 where topic IN (SELECT topic FROM searchconthardwarefr3 GROUP BY date HAVING topic < 4100);
topic date pseudo
43506 2002-10-02 joce
SELECT * from searchconthardwarefr3 where topic IN (SELECT SUM(topic) FROM searchconthardwarefr3);
topic date pseudo
drop table searchconthardwarefr3; drop table searchconthardwarefr3;
...@@ -34,6 +34,12 @@ select b,(select avg(t2.a+(select min(t3.a) from t3 where t3.a >= t4.a)) from t2 ...@@ -34,6 +34,12 @@ select b,(select avg(t2.a+(select min(t3.a) from t3 where t3.a >= t4.a)) from t2
explain select b,(select avg(t2.a+(select min(t3.a) from t3 where t3.a >= t4.a)) from t2) from t4; explain select b,(select avg(t2.a+(select min(t3.a) from t3 where t3.a >= t4.a)) from t2) from t4;
select * from t3 where exists (select * from t2 where t2.b=t3.a); select * from t3 where exists (select * from t2 where t2.b=t3.a);
select * from t3 where not exists (select * from t2 where t2.b=t3.a); select * from t3 where not exists (select * from t2 where t2.b=t3.a);
select * from t3 where a in (select b from t2);
select * from t3 where a not in (select b from t2);
-- error 1239
select * from t3 where a in (select a,b from t2);
-- error 1239
select * from t3 where a in (select * from t2);
insert into t4 values (12,7),(1,7),(10,9),(9,6),(7,6),(3,9); insert into t4 values (12,7),(1,7),(10,9),(9,6),(7,6),(3,9);
select b,max(a) as ma from t4 group by b having b < (select max(t2.a) select b,max(a) as ma from t4 group by b having b < (select max(t2.a)
from t2 where t2.b=t4.b); from t2 where t2.b=t4.b);
...@@ -114,4 +120,9 @@ SELECT 1 FROM searchconthardwarefr3 WHERE 1=(SELECT 1 UNION SELECT 1) UNION ALL ...@@ -114,4 +120,9 @@ SELECT 1 FROM searchconthardwarefr3 WHERE 1=(SELECT 1 UNION SELECT 1) UNION ALL
-- error 1240 -- error 1240
SELECT 1 FROM searchconthardwarefr3 WHERE 1=(SELECT 1 UNION ALL SELECT 1) UNION SELECT 1; SELECT 1 FROM searchconthardwarefr3 WHERE 1=(SELECT 1 UNION ALL SELECT 1) UNION SELECT 1;
EXPLAIN SELECT 1 FROM searchconthardwarefr3 WHERE 1=(SELECT 1 UNION SELECT 1); EXPLAIN SELECT 1 FROM searchconthardwarefr3 WHERE 1=(SELECT 1 UNION SELECT 1);
-- error 1054
SELECT 1 IN (SELECT 1 FROM searchconthardwarefr3 HAVING a);
SELECT * from searchconthardwarefr3 where topic IN (SELECT topic FROM searchconthardwarefr3 GROUP BY date);
SELECT * from searchconthardwarefr3 where topic IN (SELECT topic FROM searchconthardwarefr3 GROUP BY date HAVING topic < 4100);
SELECT * from searchconthardwarefr3 where topic IN (SELECT SUM(topic) FROM searchconthardwarefr3);
drop table searchconthardwarefr3; drop table searchconthardwarefr3;
\ No newline at end of file
...@@ -424,6 +424,53 @@ bool Item::fix_fields(THD *thd, ...@@ -424,6 +424,53 @@ bool Item::fix_fields(THD *thd,
return 0; return 0;
} }
bool Item_outer_select_context_saver::fix_fields(THD *thd,
struct st_table_list *list,
Item ** ref)
{
DBUG_ENTER("Item_outer_select_context_saver::fix_fields");
bool res= item->fix_fields(thd,
0, // do not show current subselect fields
&item);
*ref= item;
DBUG_RETURN(res);
}
bool Item_asterisk_remover::fix_fields(THD *thd,
struct st_table_list *list,
Item ** ref)
{
DBUG_ENTER("Item_asterisk_remover::fix_fields");
bool res;
if (item)
if (item->type() == Item::FIELD_ITEM &&
((Item_field*) item)->field_name[0] == '*')
{
List<Item> fields;
fields.push_back(item);
List_iterator<Item> it(fields);
it++;
uint elem=fields.elements;
if (insert_fields(thd, list, ((Item_field*) item)->db_name,
((Item_field*) item)->table_name, &it))
res= -1;
else
if (fields.elements > 1)
{
my_message(ER_SUBSELECT_NO_1_COL, ER(ER_SUBSELECT_NO_1_COL), MYF(0));
res= -1;
}
}
else
res= item->fix_fields(thd, list, &item);
else
res= -1;
*ref= item;
DBUG_RETURN(res);
}
bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref)
{ {
if (!field) // If field is not checked if (!field) // If field is not checked
......
...@@ -90,6 +90,53 @@ class Item { ...@@ -90,6 +90,53 @@ class Item {
}; };
/*
Wrapper base class
*/
class Item_wrapper :public Item
{
protected:
Item *item;
public:
/*
Following methods should not be used, because fix_fields exclude this
item (it assign '*ref' with field 'item' in derived classes)
*/
enum Type type() const { return item->type(); }
double val() { return item->val(); }
longlong val_int() { return item->val_int(); }
String* val_str(String* s) { return item->val_str(s); }
void make_field(Send_field* f) { item->make_field(f); }
};
/*
Save context of name resolution for Item, used in subselect transformer.
*/
class Item_outer_select_context_saver :public Item_wrapper
{
public:
Item_outer_select_context_saver(Item *i)
{
item= i;
}
bool fix_fields(THD *, struct st_table_list *, Item ** ref);
};
/*
To resolve '*' field moved to condition
*/
class Item_asterisk_remover :public Item_wrapper
{
public:
Item_asterisk_remover(Item *i)
{
item= i;
}
bool fix_fields(THD *, struct st_table_list *, Item ** ref);
};
class st_select_lex; class st_select_lex;
class Item_ident :public Item class Item_ident :public Item
{ {
......
...@@ -44,12 +44,13 @@ Item_subselect::Item_subselect(): ...@@ -44,12 +44,13 @@ Item_subselect::Item_subselect():
} }
void Item_subselect::init(THD *thd, st_select_lex *select_lex, void Item_subselect::init(THD *thd, st_select_lex *select_lex,
select_subselect *result) select_subselect *result, Item *left_expr)
{ {
DBUG_ENTER("Item_subselect::init"); DBUG_ENTER("Item_subselect::init");
DBUG_PRINT("subs", ("select_lex 0x%xl", (long) select_lex)); DBUG_PRINT("subs", ("select_lex 0x%xl", (ulong) select_lex));
select_transformer(select_lex, left_expr);
if (select_lex->next_select()) if (select_lex->next_select())
engine= new subselect_union_engine(thd, select_lex->master_unit(), result, engine= new subselect_union_engine(thd, select_lex->master_unit(), result,
this); this);
...@@ -65,6 +66,14 @@ Item_subselect::~Item_subselect() ...@@ -65,6 +66,14 @@ Item_subselect::~Item_subselect()
delete engine; delete engine;
} }
void Item_subselect::select_transformer(st_select_lex *select_lex,
Item *left_expr)
{
DBUG_ENTER("Item_subselect::select_transformer");
DBUG_VOID_RETURN;
}
void Item_subselect::make_field (Send_field *tmp_field) void Item_subselect::make_field (Send_field *tmp_field)
{ {
if (null_value) if (null_value)
...@@ -109,9 +118,11 @@ Item_singleval_subselect::Item_singleval_subselect(THD *thd, ...@@ -109,9 +118,11 @@ Item_singleval_subselect::Item_singleval_subselect(THD *thd,
st_select_lex *select_lex): st_select_lex *select_lex):
Item_subselect() Item_subselect()
{ {
init(thd, select_lex, new select_singleval_subselect(this)); DBUG_ENTER("Item_singleval_subselect::Item_singleval_subselect");
init(thd, select_lex, new select_singleval_subselect(this), 0);
max_columns= 1; max_columns= 1;
maybe_null= 1; maybe_null= 1;
DBUG_VOID_RETURN;
} }
void Item_singleval_subselect::fix_length_and_dec() void Item_singleval_subselect::fix_length_and_dec()
...@@ -156,17 +167,37 @@ String *Item_singleval_subselect::val_str (String *str) ...@@ -156,17 +167,37 @@ String *Item_singleval_subselect::val_str (String *str)
} }
Item_exists_subselect::Item_exists_subselect(THD *thd, Item_exists_subselect::Item_exists_subselect(THD *thd,
st_select_lex *select_lex): st_select_lex *select_lex,
Item *left_expr):
Item_subselect() Item_subselect()
{ {
init(thd, select_lex, new select_exists_subselect(this)); DBUG_ENTER("Item_exists_subselect::Item_exists_subselect");
init(thd, select_lex, new select_exists_subselect(this), left_expr);
max_columns= UINT_MAX;
null_value= 0; //can't be NULL
maybe_null= 0; //can't be NULL
value= 0;
// We need only 1 row to determinate existence
select_lex->master_unit()->global_parameters->select_limit= 1;
DBUG_VOID_RETURN;
}
Item_in_subselect::Item_in_subselect(THD *thd, Item * left_expr,
st_select_lex *select_lex):
Item_exists_subselect()
{
DBUG_ENTER("Item_in_subselect::Item_in_subselect");
init(thd, select_lex, new select_exists_subselect(this), left_expr);
max_columns= UINT_MAX; max_columns= UINT_MAX;
null_value= 0; //can't be NULL null_value= 0; //can't be NULL
maybe_null= 0; //can't be NULL maybe_null= 0; //can't be NULL
value= 0; value= 0;
select_lex->select_limit= 1; // we need only 1 row to determinate existence // We need only 1 row to determinate existence
select_lex->master_unit()->global_parameters->select_limit= 1;
DBUG_VOID_RETURN;
} }
void Item_exists_subselect::fix_length_and_dec() void Item_exists_subselect::fix_length_and_dec()
{ {
max_length= 1; max_length= 1;
...@@ -204,6 +235,52 @@ String *Item_exists_subselect::val_str(String *str) ...@@ -204,6 +235,52 @@ String *Item_exists_subselect::val_str(String *str)
return str; return str;
} }
Item_in_subselect::Item_in_subselect(Item_in_subselect *item):
Item_exists_subselect(item)
{
}
void Item_in_subselect::select_transformer(st_select_lex *select_lex,
Item *left_expr)
{
DBUG_ENTER("Item_in_subselect::select_transformer");
for(SELECT_LEX * sl= select_lex; sl; sl= sl->next_select())
{
Item *item;
if (sl->item_list.elements > 1)
{
my_message(ER_SUBSELECT_NO_1_COL, ER(ER_SUBSELECT_NO_1_COL), MYF(0));
item= 0; // Item_asterisk_remover mast fail
}
else
item= (Item*) sl->item_list.pop();
left_expr= new Item_outer_select_context_saver(left_expr);
if (sl->having || sl->with_sum_func || sl->group_list.first)
{
sl->item_list.push_back(item);
item= new Item_ref(sl->item_list.head_ref(), 0, "<result>");
if (sl->having)
sl->having= new Item_cond_and(sl->having,
new Item_func_eq(item, left_expr));
else
sl->having= new Item_func_eq(item, left_expr);
}
else
{
sl->item_list.empty();
sl->item_list.push_back(new Item_int(1));
item= new Item_asterisk_remover(item);
if (sl->where)
sl->where= new Item_cond_and(sl->where,
new Item_func_eq(item, left_expr));
else
sl->where= new Item_func_eq(item, left_expr);
}
}
DBUG_VOID_RETURN;
}
subselect_single_select_engine::subselect_single_select_engine(THD *thd, subselect_single_select_engine::subselect_single_select_engine(THD *thd,
st_select_lex *select, st_select_lex *select,
......
...@@ -55,13 +55,16 @@ class Item_subselect :public Item ...@@ -55,13 +55,16 @@ class Item_subselect :public Item
pointer in constructor initialization list, but we need pass pointer pointer in constructor initialization list, but we need pass pointer
to subselect Item class to select_subselect classes constructor. to subselect Item class to select_subselect classes constructor.
*/ */
void init (THD *thd, st_select_lex *select_lex, select_subselect *result); virtual void init (THD *thd, st_select_lex *select_lex,
select_subselect *result,
Item *left_expr= 0);
~Item_subselect(); ~Item_subselect();
virtual void assign_null() virtual void assign_null()
{ {
null_value= 1; null_value= 1;
} }
virtual void select_transformer(st_select_lex *select_lex, Item *left_expr);
bool assigned() { return value_assigned; } bool assigned() { return value_assigned; }
void assigned(bool a) { value_assigned= a; } void assigned(bool a) { value_assigned= a; }
enum Type type() const; enum Type type() const;
...@@ -74,7 +77,6 @@ class Item_subselect :public Item ...@@ -74,7 +77,6 @@ class Item_subselect :public Item
friend class select_subselect; friend class select_subselect;
}; };
/* single value subselect */ /* single value subselect */
class Item_singleval_subselect :public Item_subselect class Item_singleval_subselect :public Item_subselect
...@@ -127,12 +129,15 @@ class Item_exists_subselect :public Item_subselect ...@@ -127,12 +129,15 @@ class Item_exists_subselect :public Item_subselect
longlong value; /* value of this item (boolean: exists/not-exists) */ longlong value; /* value of this item (boolean: exists/not-exists) */
public: public:
Item_exists_subselect(THD *thd, st_select_lex *select_lex); Item_exists_subselect(THD *thd, st_select_lex *select_lex,
Item *left_expr= 0);
Item_exists_subselect(Item_exists_subselect *item): Item_exists_subselect(Item_exists_subselect *item):
Item_subselect(item) Item_subselect(item)
{ {
value= item->value; value= item->value;
} }
Item_exists_subselect(): Item_subselect() {}
virtual void assign_null() virtual void assign_null()
{ {
value= 0; value= 0;
...@@ -147,6 +152,16 @@ class Item_exists_subselect :public Item_subselect ...@@ -147,6 +152,16 @@ class Item_exists_subselect :public Item_subselect
friend class select_exists_subselect; friend class select_exists_subselect;
}; };
/* IN subselect */
class Item_in_subselect :public Item_exists_subselect
{
public:
Item_in_subselect(THD *thd, Item * left_expr, st_select_lex *select_lex);
Item_in_subselect(Item_in_subselect *item);
virtual void select_transformer(st_select_lex *select_lex, Item *left_exp);
};
class subselect_engine class subselect_engine
{ {
protected: protected:
......
...@@ -23,7 +23,6 @@ ...@@ -23,7 +23,6 @@
#include "mysql_priv.h" #include "mysql_priv.h"
Item_sum::Item_sum(List<Item> &list) Item_sum::Item_sum(List<Item> &list)
{ {
arg_count=list.elements; arg_count=list.elements;
...@@ -38,10 +37,14 @@ Item_sum::Item_sum(List<Item> &list) ...@@ -38,10 +37,14 @@ Item_sum::Item_sum(List<Item> &list)
args[i++]= item; args[i++]= item;
} }
} }
with_sum_func=1; mark_as_sum_func();
list.empty(); // Fields are used list.empty(); // Fields are used
} }
void Item_sum::mark_as_sum_func()
{
current_thd->lex.select->with_sum_func= with_sum_func= 1;
}
void Item_sum::make_field(Send_field *tmp_field) void Item_sum::make_field(Send_field *tmp_field)
{ {
......
...@@ -34,23 +34,28 @@ class Item_sum :public Item_result_field ...@@ -34,23 +34,28 @@ class Item_sum :public Item_result_field
uint arg_count; uint arg_count;
bool quick_group; /* If incremental update of fields */ bool quick_group; /* If incremental update of fields */
Item_sum() : arg_count(0),quick_group(1) { with_sum_func=1; } Item_sum() : arg_count(0),quick_group(1)
{
mark_as_sum_func();
}
Item_sum(Item *a) :quick_group(1) Item_sum(Item *a) :quick_group(1)
{ {
arg_count=1; arg_count=1;
args=tmp_args; args=tmp_args;
args[0]=a; args[0]=a;
with_sum_func = 1; mark_as_sum_func();
} }
Item_sum( Item *a, Item *b ) :quick_group(1) Item_sum( Item *a, Item *b ) :quick_group(1)
{ {
arg_count=2; arg_count=2;
args=tmp_args; args=tmp_args;
args[0]=a; args[1]=b; args[0]=a; args[1]=b;
with_sum_func=1; mark_as_sum_func();
} }
Item_sum(List<Item> &list); Item_sum(List<Item> &list);
~Item_sum() { result_field=0; } ~Item_sum() { result_field=0; }
inline void mark_as_sum_func();
enum Type type() const { return SUM_FUNC_ITEM; } enum Type type() const { return SUM_FUNC_ITEM; }
virtual enum Sumfunctype sum_func () const=0; virtual enum Sumfunctype sum_func () const=0;
virtual void reset()=0; virtual void reset()=0;
......
...@@ -936,6 +936,7 @@ void st_select_lex_node::init_select() ...@@ -936,6 +936,7 @@ void st_select_lex_node::init_select()
order_list.next= (byte**) &order_list.first; order_list.next= (byte**) &order_list.first;
select_limit= HA_POS_ERROR; select_limit= HA_POS_ERROR;
offset_limit= 0; offset_limit= 0;
with_sum_func= 0;
create_refs= dependent= 0; create_refs= dependent= 0;
} }
......
...@@ -200,6 +200,7 @@ class st_select_lex_node { ...@@ -200,6 +200,7 @@ class st_select_lex_node {
List<List_item> expr_list; List<List_item> expr_list;
List<List_item> when_list; /* WHEN clause (expression) */ List<List_item> when_list; /* WHEN clause (expression) */
ha_rows select_limit, offset_limit; /* LIMIT clause parameters */ ha_rows select_limit, offset_limit; /* LIMIT clause parameters */
bool with_sum_func;
bool create_refs; bool create_refs;
bool dependent; /* dependent from outer select subselect */ bool dependent; /* dependent from outer select subselect */
......
...@@ -70,6 +70,7 @@ inline Item *or_or_concat(Item* A, Item* B) ...@@ -70,6 +70,7 @@ inline Item *or_or_concat(Item* A, Item* B)
enum Item_udftype udf_type; enum Item_udftype udf_type;
CHARSET_INFO *charset; CHARSET_INFO *charset;
interval_type interval; interval_type interval;
st_select_lex *select_lex;
} }
%{ %{
...@@ -530,7 +531,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); ...@@ -530,7 +531,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
%type <lex_str> %type <lex_str>
IDENT TEXT_STRING REAL_NUM FLOAT_NUM NUM LONG_NUM HEX_NUM LEX_HOSTNAME IDENT TEXT_STRING REAL_NUM FLOAT_NUM NUM LONG_NUM HEX_NUM LEX_HOSTNAME
ULONGLONG_NUM field_ident select_alias ident ident_or_text UNDERSCORE_CHARSET ULONGLONG_NUM field_ident select_alias ident ident_or_text
UNDERSCORE_CHARSET
%type <lex_str_ptr> %type <lex_str_ptr>
opt_table_alias opt_table_alias
...@@ -612,6 +614,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); ...@@ -612,6 +614,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
%type <variable> internal_variable_name %type <variable> internal_variable_name
%type <select_lex> in_subselect in_subselect_init
%type <NONE> %type <NONE>
query verb_clause create change select do drop insert replace insert2 query verb_clause create change select do drop insert replace insert2
insert_values update delete truncate rename insert_values update delete truncate rename
...@@ -1725,6 +1729,12 @@ expr_expr: ...@@ -1725,6 +1729,12 @@ expr_expr:
{ $$= new Item_func_in($1,*$4); } { $$= new Item_func_in($1,*$4); }
| expr NOT IN_SYM '(' expr_list ')' | expr NOT IN_SYM '(' expr_list ')'
{ $$= new Item_func_not(new Item_func_in($1,*$5)); } { $$= new Item_func_not(new Item_func_in($1,*$5)); }
| expr IN_SYM in_subselect
{ $$= new Item_in_subselect(current_thd, $1, $3); }
| expr NOT IN_SYM in_subselect
{
$$= new Item_func_not(new Item_in_subselect(current_thd, $1, $4));
}
| expr BETWEEN_SYM no_and_expr AND expr | expr BETWEEN_SYM no_and_expr AND expr
{ $$= new Item_func_between($1,$3,$5); } { $$= new Item_func_between($1,$3,$5); }
| expr NOT BETWEEN_SYM no_and_expr AND expr | expr NOT BETWEEN_SYM no_and_expr AND expr
...@@ -1808,6 +1818,12 @@ no_and_expr: ...@@ -1808,6 +1818,12 @@ no_and_expr:
{ $$= new Item_func_in($1,*$4); } { $$= new Item_func_in($1,*$4); }
| no_and_expr NOT IN_SYM '(' expr_list ')' | no_and_expr NOT IN_SYM '(' expr_list ')'
{ $$= new Item_func_not(new Item_func_in($1,*$5)); } { $$= new Item_func_not(new Item_func_in($1,*$5)); }
| no_and_expr IN_SYM in_subselect
{ $$= new Item_in_subselect(current_thd, $1, $3); }
| no_and_expr NOT IN_SYM in_subselect
{
$$= new Item_func_not(new Item_in_subselect(current_thd, $1, $4));
}
| no_and_expr BETWEEN_SYM no_and_expr AND expr | no_and_expr BETWEEN_SYM no_and_expr AND expr
{ $$= new Item_func_between($1,$3,$5); } { $$= new Item_func_between($1,$3,$5); }
| no_and_expr NOT BETWEEN_SYM no_and_expr AND expr | no_and_expr NOT BETWEEN_SYM no_and_expr AND expr
...@@ -4232,6 +4248,19 @@ exists_subselect_init: ...@@ -4232,6 +4248,19 @@ exists_subselect_init:
first_select()); first_select());
}; };
in_subselect:
subselect_start in_subselect_init
subselect_end
{
$$= $2;
};
in_subselect_init:
select_init
{
$$= Lex->select->master_unit()->first_select();
};
subselect_start: subselect_start:
'(' '('
{ {
......
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