mirror of
https://github.com/postgres/postgres.git
synced 2025-06-17 17:02:08 +03:00
Teach map_partition_varattnos to handle whole-row expressions.
Otherwise, partitioned tables with RETURNING expressions or subject to a WITH CHECK OPTION do not work properly. Amit Langote, reviewed by Amit Khandekar and Etsuro Fujita. A few comment changes by me. Discussion: http://postgr.es/m/9a39df80-871e-6212-0684-f93c83be4097@lab.ntt.co.jp
This commit is contained in:
@ -898,16 +898,20 @@ get_qual_from_partbound(Relation rel, Relation parent,
|
|||||||
* We must allow for cases where physical attnos of a partition can be
|
* We must allow for cases where physical attnos of a partition can be
|
||||||
* different from the parent's.
|
* different from the parent's.
|
||||||
*
|
*
|
||||||
|
* If found_whole_row is not NULL, *found_whole_row returns whether a
|
||||||
|
* whole-row variable was found in the input expression.
|
||||||
|
*
|
||||||
* Note: this will work on any node tree, so really the argument and result
|
* Note: this will work on any node tree, so really the argument and result
|
||||||
* should be declared "Node *". But a substantial majority of the callers
|
* should be declared "Node *". But a substantial majority of the callers
|
||||||
* are working on Lists, so it's less messy to do the casts internally.
|
* are working on Lists, so it's less messy to do the casts internally.
|
||||||
*/
|
*/
|
||||||
List *
|
List *
|
||||||
map_partition_varattnos(List *expr, int target_varno,
|
map_partition_varattnos(List *expr, int target_varno,
|
||||||
Relation partrel, Relation parent)
|
Relation partrel, Relation parent,
|
||||||
|
bool *found_whole_row)
|
||||||
{
|
{
|
||||||
AttrNumber *part_attnos;
|
AttrNumber *part_attnos;
|
||||||
bool found_whole_row;
|
bool my_found_whole_row;
|
||||||
|
|
||||||
if (expr == NIL)
|
if (expr == NIL)
|
||||||
return NIL;
|
return NIL;
|
||||||
@ -919,10 +923,10 @@ map_partition_varattnos(List *expr, int target_varno,
|
|||||||
target_varno, 0,
|
target_varno, 0,
|
||||||
part_attnos,
|
part_attnos,
|
||||||
RelationGetDescr(parent)->natts,
|
RelationGetDescr(parent)->natts,
|
||||||
&found_whole_row);
|
RelationGetForm(partrel)->reltype,
|
||||||
/* There can never be a whole-row reference here */
|
&my_found_whole_row);
|
||||||
if (found_whole_row)
|
if (found_whole_row)
|
||||||
elog(ERROR, "unexpected whole-row reference found in partition key");
|
*found_whole_row = my_found_whole_row;
|
||||||
|
|
||||||
return expr;
|
return expr;
|
||||||
}
|
}
|
||||||
@ -1783,6 +1787,7 @@ generate_partition_qual(Relation rel)
|
|||||||
List *my_qual = NIL,
|
List *my_qual = NIL,
|
||||||
*result = NIL;
|
*result = NIL;
|
||||||
Relation parent;
|
Relation parent;
|
||||||
|
bool found_whole_row;
|
||||||
|
|
||||||
/* Guard against stack overflow due to overly deep partition tree */
|
/* Guard against stack overflow due to overly deep partition tree */
|
||||||
check_stack_depth();
|
check_stack_depth();
|
||||||
@ -1825,7 +1830,11 @@ generate_partition_qual(Relation rel)
|
|||||||
* in it to bear this relation's attnos. It's safe to assume varno = 1
|
* in it to bear this relation's attnos. It's safe to assume varno = 1
|
||||||
* here.
|
* here.
|
||||||
*/
|
*/
|
||||||
result = map_partition_varattnos(result, 1, rel, parent);
|
result = map_partition_varattnos(result, 1, rel, parent,
|
||||||
|
&found_whole_row);
|
||||||
|
/* There can never be a whole-row reference here */
|
||||||
|
if (found_whole_row)
|
||||||
|
elog(ERROR, "unexpected whole-row reference found in partition key");
|
||||||
|
|
||||||
/* Save a copy in the relcache */
|
/* Save a copy in the relcache */
|
||||||
oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
|
oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
|
||||||
|
@ -1989,7 +1989,7 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
|
|||||||
expr = map_variable_attnos(stringToNode(check[i].ccbin),
|
expr = map_variable_attnos(stringToNode(check[i].ccbin),
|
||||||
1, 0,
|
1, 0,
|
||||||
newattno, tupleDesc->natts,
|
newattno, tupleDesc->natts,
|
||||||
&found_whole_row);
|
InvalidOid, &found_whole_row);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* For the moment we have to reject whole-row variables. We
|
* For the moment we have to reject whole-row variables. We
|
||||||
@ -8874,7 +8874,7 @@ ATPrepAlterColumnType(List **wqueue,
|
|||||||
map_variable_attnos(def->cooked_default,
|
map_variable_attnos(def->cooked_default,
|
||||||
1, 0,
|
1, 0,
|
||||||
attmap, RelationGetDescr(rel)->natts,
|
attmap, RelationGetDescr(rel)->natts,
|
||||||
&found_whole_row);
|
InvalidOid, &found_whole_row);
|
||||||
if (found_whole_row)
|
if (found_whole_row)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
@ -13713,6 +13713,7 @@ ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd)
|
|||||||
Oid part_relid = lfirst_oid(lc);
|
Oid part_relid = lfirst_oid(lc);
|
||||||
Relation part_rel;
|
Relation part_rel;
|
||||||
Expr *constr;
|
Expr *constr;
|
||||||
|
bool found_whole_row;
|
||||||
|
|
||||||
/* Lock already taken */
|
/* Lock already taken */
|
||||||
if (part_relid != RelationGetRelid(attachRel))
|
if (part_relid != RelationGetRelid(attachRel))
|
||||||
@ -13738,7 +13739,12 @@ ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd)
|
|||||||
constr = linitial(partConstraint);
|
constr = linitial(partConstraint);
|
||||||
tab->partition_constraint = (Expr *)
|
tab->partition_constraint = (Expr *)
|
||||||
map_partition_varattnos((List *) constr, 1,
|
map_partition_varattnos((List *) constr, 1,
|
||||||
part_rel, rel);
|
part_rel, rel,
|
||||||
|
&found_whole_row);
|
||||||
|
/* There can never be a whole-row reference here */
|
||||||
|
if (found_whole_row)
|
||||||
|
elog(ERROR, "unexpected whole-row reference found in partition key");
|
||||||
|
|
||||||
/* keep our lock until commit */
|
/* keep our lock until commit */
|
||||||
if (part_rel != attachRel)
|
if (part_rel != attachRel)
|
||||||
heap_close(part_rel, NoLock);
|
heap_close(part_rel, NoLock);
|
||||||
|
@ -1996,7 +1996,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
|
|||||||
/* varno = node->nominalRelation */
|
/* varno = node->nominalRelation */
|
||||||
mapped_wcoList = map_partition_varattnos(wcoList,
|
mapped_wcoList = map_partition_varattnos(wcoList,
|
||||||
node->nominalRelation,
|
node->nominalRelation,
|
||||||
partrel, rel);
|
partrel, rel, NULL);
|
||||||
foreach(ll, mapped_wcoList)
|
foreach(ll, mapped_wcoList)
|
||||||
{
|
{
|
||||||
WithCheckOption *wco = castNode(WithCheckOption, lfirst(ll));
|
WithCheckOption *wco = castNode(WithCheckOption, lfirst(ll));
|
||||||
@ -2069,7 +2069,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
|
|||||||
/* varno = node->nominalRelation */
|
/* varno = node->nominalRelation */
|
||||||
rlist = map_partition_varattnos(returningList,
|
rlist = map_partition_varattnos(returningList,
|
||||||
node->nominalRelation,
|
node->nominalRelation,
|
||||||
partrel, rel);
|
partrel, rel, NULL);
|
||||||
resultRelInfo->ri_projectReturning =
|
resultRelInfo->ri_projectReturning =
|
||||||
ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps,
|
ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps,
|
||||||
resultRelInfo->ri_RelationDesc->rd_att);
|
resultRelInfo->ri_RelationDesc->rd_att);
|
||||||
|
@ -1107,7 +1107,7 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
|
|||||||
ccbin_node = map_variable_attnos(stringToNode(ccbin),
|
ccbin_node = map_variable_attnos(stringToNode(ccbin),
|
||||||
1, 0,
|
1, 0,
|
||||||
attmap, tupleDesc->natts,
|
attmap, tupleDesc->natts,
|
||||||
&found_whole_row);
|
InvalidOid, &found_whole_row);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We reject whole-row variables because the whole point of LIKE
|
* We reject whole-row variables because the whole point of LIKE
|
||||||
@ -1463,7 +1463,7 @@ generateClonedIndexStmt(CreateStmtContext *cxt, Relation source_idx,
|
|||||||
indexkey = map_variable_attnos(indexkey,
|
indexkey = map_variable_attnos(indexkey,
|
||||||
1, 0,
|
1, 0,
|
||||||
attmap, attmap_length,
|
attmap, attmap_length,
|
||||||
&found_whole_row);
|
InvalidOid, &found_whole_row);
|
||||||
|
|
||||||
/* As in transformTableLikeClause, reject whole-row variables */
|
/* As in transformTableLikeClause, reject whole-row variables */
|
||||||
if (found_whole_row)
|
if (found_whole_row)
|
||||||
@ -1539,7 +1539,7 @@ generateClonedIndexStmt(CreateStmtContext *cxt, Relation source_idx,
|
|||||||
pred_tree = map_variable_attnos(pred_tree,
|
pred_tree = map_variable_attnos(pred_tree,
|
||||||
1, 0,
|
1, 0,
|
||||||
attmap, attmap_length,
|
attmap, attmap_length,
|
||||||
&found_whole_row);
|
InvalidOid, &found_whole_row);
|
||||||
|
|
||||||
/* As in transformTableLikeClause, reject whole-row variables */
|
/* As in transformTableLikeClause, reject whole-row variables */
|
||||||
if (found_whole_row)
|
if (found_whole_row)
|
||||||
|
@ -1203,14 +1203,12 @@ replace_rte_variables_mutator(Node *node,
|
|||||||
* appear in the expression.
|
* appear in the expression.
|
||||||
*
|
*
|
||||||
* If the expression tree contains a whole-row Var for the target RTE,
|
* If the expression tree contains a whole-row Var for the target RTE,
|
||||||
* the Var is not changed but *found_whole_row is returned as TRUE.
|
* *found_whole_row is returned as TRUE. In addition, if to_rowtype is
|
||||||
* For most callers this is an error condition, but we leave it to the caller
|
* not InvalidOid, we modify the Var's vartype and insert a ConvertRowTypeExpr
|
||||||
* to report the error so that useful context can be provided. (In some
|
* to map back to the orignal rowtype. Callers that don't provide to_rowtype
|
||||||
* usages it would be appropriate to modify the Var's vartype and insert a
|
* should report an error if *found_row_type is true; we don't do that here
|
||||||
* ConvertRowtypeExpr node to map back to the original vartype. We might
|
* because we don't know exactly what wording for the error message would
|
||||||
* someday extend this function's API to support that. For now, the only
|
* be most appropriate. The caller will be aware of the context.
|
||||||
* concession to that future need is that this function is a tree mutator
|
|
||||||
* not just a walker.)
|
|
||||||
*
|
*
|
||||||
* This could be built using replace_rte_variables and a callback function,
|
* This could be built using replace_rte_variables and a callback function,
|
||||||
* but since we don't ever need to insert sublinks, replace_rte_variables is
|
* but since we don't ever need to insert sublinks, replace_rte_variables is
|
||||||
@ -1223,6 +1221,8 @@ typedef struct
|
|||||||
int sublevels_up; /* (current) nesting depth */
|
int sublevels_up; /* (current) nesting depth */
|
||||||
const AttrNumber *attno_map; /* map array for user attnos */
|
const AttrNumber *attno_map; /* map array for user attnos */
|
||||||
int map_length; /* number of entries in attno_map[] */
|
int map_length; /* number of entries in attno_map[] */
|
||||||
|
/* Target type when converting whole-row vars */
|
||||||
|
Oid to_rowtype;
|
||||||
bool *found_whole_row; /* output flag */
|
bool *found_whole_row; /* output flag */
|
||||||
} map_variable_attnos_context;
|
} map_variable_attnos_context;
|
||||||
|
|
||||||
@ -1257,6 +1257,34 @@ map_variable_attnos_mutator(Node *node,
|
|||||||
{
|
{
|
||||||
/* whole-row variable, warn caller */
|
/* whole-row variable, warn caller */
|
||||||
*(context->found_whole_row) = true;
|
*(context->found_whole_row) = true;
|
||||||
|
|
||||||
|
/* If the callers expects us to convert the same, do so. */
|
||||||
|
if (OidIsValid(context->to_rowtype))
|
||||||
|
{
|
||||||
|
/* No support for RECORDOID. */
|
||||||
|
Assert(var->vartype != RECORDOID);
|
||||||
|
|
||||||
|
/* Don't convert unless necessary. */
|
||||||
|
if (context->to_rowtype != var->vartype)
|
||||||
|
{
|
||||||
|
ConvertRowtypeExpr *r;
|
||||||
|
|
||||||
|
/* Var itself is converted to the requested type. */
|
||||||
|
newvar->vartype = context->to_rowtype;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* And a conversion node on top to convert back to the
|
||||||
|
* original type.
|
||||||
|
*/
|
||||||
|
r = makeNode(ConvertRowtypeExpr);
|
||||||
|
r->arg = (Expr *) newvar;
|
||||||
|
r->resulttype = var->vartype;
|
||||||
|
r->convertformat = COERCE_IMPLICIT_CAST;
|
||||||
|
r->location = -1;
|
||||||
|
|
||||||
|
return (Node *) r;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return (Node *) newvar;
|
return (Node *) newvar;
|
||||||
}
|
}
|
||||||
@ -1283,7 +1311,7 @@ Node *
|
|||||||
map_variable_attnos(Node *node,
|
map_variable_attnos(Node *node,
|
||||||
int target_varno, int sublevels_up,
|
int target_varno, int sublevels_up,
|
||||||
const AttrNumber *attno_map, int map_length,
|
const AttrNumber *attno_map, int map_length,
|
||||||
bool *found_whole_row)
|
Oid to_rowtype, bool *found_whole_row)
|
||||||
{
|
{
|
||||||
map_variable_attnos_context context;
|
map_variable_attnos_context context;
|
||||||
|
|
||||||
@ -1291,6 +1319,7 @@ map_variable_attnos(Node *node,
|
|||||||
context.sublevels_up = sublevels_up;
|
context.sublevels_up = sublevels_up;
|
||||||
context.attno_map = attno_map;
|
context.attno_map = attno_map;
|
||||||
context.map_length = map_length;
|
context.map_length = map_length;
|
||||||
|
context.to_rowtype = to_rowtype;
|
||||||
context.found_whole_row = found_whole_row;
|
context.found_whole_row = found_whole_row;
|
||||||
|
|
||||||
*found_whole_row = false;
|
*found_whole_row = false;
|
||||||
|
@ -80,7 +80,8 @@ extern Oid get_partition_parent(Oid relid);
|
|||||||
extern List *get_qual_from_partbound(Relation rel, Relation parent,
|
extern List *get_qual_from_partbound(Relation rel, Relation parent,
|
||||||
PartitionBoundSpec *spec);
|
PartitionBoundSpec *spec);
|
||||||
extern List *map_partition_varattnos(List *expr, int target_varno,
|
extern List *map_partition_varattnos(List *expr, int target_varno,
|
||||||
Relation partrel, Relation parent);
|
Relation partrel, Relation parent,
|
||||||
|
bool *found_whole_row);
|
||||||
extern List *RelationGetPartitionQual(Relation rel);
|
extern List *RelationGetPartitionQual(Relation rel);
|
||||||
extern Expr *get_partition_qual_relid(Oid relid);
|
extern Expr *get_partition_qual_relid(Oid relid);
|
||||||
|
|
||||||
|
@ -72,7 +72,7 @@ extern Node *replace_rte_variables_mutator(Node *node,
|
|||||||
extern Node *map_variable_attnos(Node *node,
|
extern Node *map_variable_attnos(Node *node,
|
||||||
int target_varno, int sublevels_up,
|
int target_varno, int sublevels_up,
|
||||||
const AttrNumber *attno_map, int map_length,
|
const AttrNumber *attno_map, int map_length,
|
||||||
bool *found_whole_row);
|
Oid to_rowtype, bool *found_whole_row);
|
||||||
|
|
||||||
extern Node *ReplaceVarsFromTargetList(Node *node,
|
extern Node *ReplaceVarsFromTargetList(Node *node,
|
||||||
int target_varno, int sublevels_up,
|
int target_varno, int sublevels_up,
|
||||||
|
@ -659,3 +659,24 @@ select tableoid::regclass, * from mcrparted order by a, b;
|
|||||||
(11 rows)
|
(11 rows)
|
||||||
|
|
||||||
drop table mcrparted;
|
drop table mcrparted;
|
||||||
|
-- check that wholerow vars in the RETURNING list work with partitioned tables
|
||||||
|
create table returningwrtest (a int) partition by list (a);
|
||||||
|
create table returningwrtest1 partition of returningwrtest for values in (1);
|
||||||
|
insert into returningwrtest values (1) returning returningwrtest;
|
||||||
|
returningwrtest
|
||||||
|
-----------------
|
||||||
|
(1)
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- check also that the wholerow vars in RETURNING list are converted as needed
|
||||||
|
alter table returningwrtest add b text;
|
||||||
|
create table returningwrtest2 (b text, c int, a int);
|
||||||
|
alter table returningwrtest2 drop c;
|
||||||
|
alter table returningwrtest attach partition returningwrtest2 for values in (2);
|
||||||
|
insert into returningwrtest values (2, 'foo') returning returningwrtest;
|
||||||
|
returningwrtest
|
||||||
|
-----------------
|
||||||
|
(2,foo)
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
drop table returningwrtest;
|
||||||
|
@ -2428,3 +2428,29 @@ ERROR: new row violates check option for view "ptv_wco"
|
|||||||
DETAIL: Failing row contains (1, 2, null).
|
DETAIL: Failing row contains (1, 2, null).
|
||||||
drop view ptv, ptv_wco;
|
drop view ptv, ptv_wco;
|
||||||
drop table pt, pt1, pt11;
|
drop table pt, pt1, pt11;
|
||||||
|
-- check that wholerow vars appearing in WITH CHECK OPTION constraint expressions
|
||||||
|
-- work fine with partitioned tables
|
||||||
|
create table wcowrtest (a int) partition by list (a);
|
||||||
|
create table wcowrtest1 partition of wcowrtest for values in (1);
|
||||||
|
create view wcowrtest_v as select * from wcowrtest where wcowrtest = '(2)'::wcowrtest with check option;
|
||||||
|
insert into wcowrtest_v values (1);
|
||||||
|
ERROR: new row violates check option for view "wcowrtest_v"
|
||||||
|
DETAIL: Failing row contains (1).
|
||||||
|
alter table wcowrtest add b text;
|
||||||
|
create table wcowrtest2 (b text, c int, a int);
|
||||||
|
alter table wcowrtest2 drop c;
|
||||||
|
alter table wcowrtest attach partition wcowrtest2 for values in (2);
|
||||||
|
create table sometable (a int, b text);
|
||||||
|
insert into sometable values (1, 'a'), (2, 'b');
|
||||||
|
create view wcowrtest_v2 as
|
||||||
|
select *
|
||||||
|
from wcowrtest r
|
||||||
|
where r in (select s from sometable s where r.a = s.a)
|
||||||
|
with check option;
|
||||||
|
-- WITH CHECK qual will be processed with wcowrtest2's
|
||||||
|
-- rowtype after tuple-routing
|
||||||
|
insert into wcowrtest_v2 values (2, 'no such row in sometable');
|
||||||
|
ERROR: new row violates check option for view "wcowrtest_v2"
|
||||||
|
DETAIL: Failing row contains (2, no such row in sometable).
|
||||||
|
drop view wcowrtest_v, wcowrtest_v2;
|
||||||
|
drop table wcowrtest, sometable;
|
||||||
|
@ -399,3 +399,16 @@ insert into mcrparted values ('aaa', 0), ('b', 0), ('bz', 10), ('c', -10),
|
|||||||
('commons', 0), ('d', -10), ('e', 0);
|
('commons', 0), ('d', -10), ('e', 0);
|
||||||
select tableoid::regclass, * from mcrparted order by a, b;
|
select tableoid::regclass, * from mcrparted order by a, b;
|
||||||
drop table mcrparted;
|
drop table mcrparted;
|
||||||
|
|
||||||
|
-- check that wholerow vars in the RETURNING list work with partitioned tables
|
||||||
|
create table returningwrtest (a int) partition by list (a);
|
||||||
|
create table returningwrtest1 partition of returningwrtest for values in (1);
|
||||||
|
insert into returningwrtest values (1) returning returningwrtest;
|
||||||
|
|
||||||
|
-- check also that the wholerow vars in RETURNING list are converted as needed
|
||||||
|
alter table returningwrtest add b text;
|
||||||
|
create table returningwrtest2 (b text, c int, a int);
|
||||||
|
alter table returningwrtest2 drop c;
|
||||||
|
alter table returningwrtest attach partition returningwrtest2 for values in (2);
|
||||||
|
insert into returningwrtest values (2, 'foo') returning returningwrtest;
|
||||||
|
drop table returningwrtest;
|
||||||
|
@ -1141,3 +1141,30 @@ create view ptv_wco as select * from pt where a = 0 with check option;
|
|||||||
insert into ptv_wco values (1, 2);
|
insert into ptv_wco values (1, 2);
|
||||||
drop view ptv, ptv_wco;
|
drop view ptv, ptv_wco;
|
||||||
drop table pt, pt1, pt11;
|
drop table pt, pt1, pt11;
|
||||||
|
|
||||||
|
-- check that wholerow vars appearing in WITH CHECK OPTION constraint expressions
|
||||||
|
-- work fine with partitioned tables
|
||||||
|
create table wcowrtest (a int) partition by list (a);
|
||||||
|
create table wcowrtest1 partition of wcowrtest for values in (1);
|
||||||
|
create view wcowrtest_v as select * from wcowrtest where wcowrtest = '(2)'::wcowrtest with check option;
|
||||||
|
insert into wcowrtest_v values (1);
|
||||||
|
|
||||||
|
alter table wcowrtest add b text;
|
||||||
|
create table wcowrtest2 (b text, c int, a int);
|
||||||
|
alter table wcowrtest2 drop c;
|
||||||
|
alter table wcowrtest attach partition wcowrtest2 for values in (2);
|
||||||
|
|
||||||
|
create table sometable (a int, b text);
|
||||||
|
insert into sometable values (1, 'a'), (2, 'b');
|
||||||
|
create view wcowrtest_v2 as
|
||||||
|
select *
|
||||||
|
from wcowrtest r
|
||||||
|
where r in (select s from sometable s where r.a = s.a)
|
||||||
|
with check option;
|
||||||
|
|
||||||
|
-- WITH CHECK qual will be processed with wcowrtest2's
|
||||||
|
-- rowtype after tuple-routing
|
||||||
|
insert into wcowrtest_v2 values (2, 'no such row in sometable');
|
||||||
|
|
||||||
|
drop view wcowrtest_v, wcowrtest_v2;
|
||||||
|
drop table wcowrtest, sometable;
|
||||||
|
Reference in New Issue
Block a user