Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 9fa75f1

Browse files
committed
Revert "Built-in support of local table"
This reverts commit 3e4fed0.
1 parent 15a9b2e commit 9fa75f1

File tree

23 files changed

+53
-119
lines changed

23 files changed

+53
-119
lines changed

contrib/mmts/multimaster--1.0.sql

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,9 +112,9 @@ CREATE FUNCTION mtm.referee_poll(xid bigint) RETURNS bigint
112112
AS 'MODULE_PATHNAME','mtm_referee_poll'
113113
LANGUAGE C;
114114

115-
CREATE LOCAL TABLE IF NOT EXISTS mtm.local_tables(rel_schema name, rel_name name, primary key(rel_schema, rel_name));
115+
CREATE TABLE IF NOT EXISTS mtm.local_tables(rel_schema name, rel_name name, primary key(rel_schema, rel_name));
116116

117-
CREATE LOCAL TABLE mtm.referee_decision(key text primary key not null, node_id int);
117+
CREATE TABLE mtm.referee_decision(key text primary key not null, node_id int);
118118

119119
CREATE OR REPLACE FUNCTION mtm.alter_sequences() RETURNS boolean AS
120120
$$

contrib/mmts/multimaster.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3860,7 +3860,12 @@ MtmReplicationRowFilterHook(struct PGLogicalRowFilterArgs* args)
38603860
{
38613861
bool isDistributed;
38623862

3863-
if (args->changed_rel->rd_islocal)
3863+
/*
3864+
* We have several built-in local tables that shouldn't be replicated.
3865+
* It is hard to insert them into MtmLocalTables properly on extension
3866+
* creation so we just list them here.
3867+
*/
3868+
if (strcmp(args->changed_rel->rd_rel->relname.data, "referee_decision") == 0)
38643869
return false;
38653870

38663871
/*

contrib/mmts/pglogical_apply.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1040,6 +1040,12 @@ process_remote_insert(StringInfo s, Relation rel)
10401040
if (ActiveSnapshotSet())
10411041
PopActiveSnapshot();
10421042

1043+
if (strcmp(RelationGetRelationName(rel), MULTIMASTER_LOCAL_TABLES_TABLE) == 0 &&
1044+
strcmp(get_namespace_name(RelationGetNamespace(rel)), MULTIMASTER_SCHEMA_NAME) == 0)
1045+
{
1046+
MtmMakeTableLocal((char*)DatumGetPointer(new_tuple.values[0]), (char*)DatumGetPointer(new_tuple.values[1]));
1047+
}
1048+
10431049
ExecResetTupleTable(estate->es_tupleTable, true);
10441050
FreeExecutorState(estate);
10451051

src/backend/access/heap/heapam.c

Lines changed: 1 addition & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,6 @@
8080
/* GUC variable */
8181
bool synchronize_seqscans = true;
8282

83-
HeapTrigger* pg_heap_trigger;
8483

8584
static HeapScanDesc heap_beginscan_internal(Relation relation,
8685
Snapshot snapshot,
@@ -2565,10 +2564,7 @@ heap_insert(Relation relation, HeapTuple tup, CommandId cid,
25652564
if (vmbuffer != InvalidBuffer)
25662565
ReleaseBuffer(vmbuffer);
25672566

2568-
if (pg_heap_trigger)
2569-
pg_heap_trigger->on_insert(relation, heaptup);
2570-
2571-
/*
2567+
/*
25722568
* If tuple is cachable, mark it for invalidation from the caches in case
25732569
* we abort. Note it is OK to do this after releasing the buffer, because
25742570
* the heaptup data structure is all in local memory, not in the shared
@@ -3420,15 +3416,6 @@ heap_multi_insert(Relation relation, HeapTuple *tuples, int ntuples,
34203416
if (vmbuffer != InvalidBuffer)
34213417
ReleaseBuffer(vmbuffer);
34223418

3423-
if (pg_heap_trigger)
3424-
{
3425-
for (i = 0; i < nthispage; i++)
3426-
{
3427-
HeapTuple heaptup = heaptuples[ndone + i];
3428-
pg_heap_trigger->on_insert(relation, heaptup);
3429-
}
3430-
}
3431-
34323419
ndone += nthispage;
34333420
}
34343421

@@ -3896,9 +3883,6 @@ heap_delete(Relation relation, ItemPointer tid,
38963883

38973884
LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
38983885

3899-
if (pg_heap_trigger)
3900-
pg_heap_trigger->on_delete(relation, old_key_tuple);
3901-
39023886
if (vmbuffer != InvalidBuffer)
39033887
ReleaseBuffer(vmbuffer);
39043888

@@ -4879,9 +4863,6 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
48794863
LockBuffer(newbuf, BUFFER_LOCK_UNLOCK);
48804864
LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
48814865

4882-
if (pg_heap_trigger)
4883-
pg_heap_trigger->on_update(relation, use_hot_update ? NULL : &oldtup, heaptup);
4884-
48854866
/*
48864867
* Mark old tuple for invalidation from system caches at next command
48874868
* boundary, and mark the new tuple for invalidation in case we abort. We

src/backend/catalog/catalog.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -421,7 +421,6 @@ GetNewRelFileNode(Oid reltablespace, Relation pg_class, char relpersistence)
421421
case RELPERSISTENCE_UNLOGGED:
422422
case RELPERSISTENCE_CONSTANT:
423423
case RELPERSISTENCE_PERMANENT:
424-
case RELPERSISTENCE_LOCAL:
425424
backend = InvalidBackendId;
426425
break;
427426
default:

src/backend/catalog/namespace.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -631,7 +631,6 @@ RangeVarAdjustRelationPersistence(RangeVar *newRelation, Oid nspid)
631631
}
632632
break;
633633
case RELPERSISTENCE_PERMANENT:
634-
case RELPERSISTENCE_LOCAL:
635634
if (isTempOrTempToastNamespace(nspid))
636635
newRelation->relpersistence = RELPERSISTENCE_TEMP;
637636
else if (isAnyTempNamespace(nspid))

src/backend/catalog/pg_publication.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ is_publishable_class(Oid relid, Form_pg_class reltuple)
101101
{
102102
return reltuple->relkind == RELKIND_RELATION &&
103103
!IsCatalogClass(relid, reltuple) &&
104-
(reltuple->relpersistence == RELPERSISTENCE_PERMANENT || reltuple->relpersistence == RELPERSISTENCE_LOCAL) &&
104+
reltuple->relpersistence == RELPERSISTENCE_PERMANENT &&
105105
relid >= FirstNormalObjectId;
106106
}
107107

src/backend/catalog/storage.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,6 @@ RelationCreateStorage(RelFileNode rnode, char relpersistence)
9494
break;
9595
case RELPERSISTENCE_PERMANENT:
9696
case RELPERSISTENCE_CONSTANT:
97-
case RELPERSISTENCE_LOCAL:
9897
backend = InvalidBackendId;
9998
needs_wal = true;
10099
break;

src/backend/commands/cluster.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1566,7 +1566,7 @@ finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap,
15661566
*/
15671567
if (newrelpersistence == RELPERSISTENCE_UNLOGGED)
15681568
reindex_flags |= REINDEX_REL_FORCE_INDEXES_UNLOGGED;
1569-
else if (newrelpersistence != RELPERSISTENCE_TEMP)
1569+
else if (newrelpersistence == RELPERSISTENCE_PERMANENT)
15701570
reindex_flags |= REINDEX_REL_FORCE_INDEXES_PERMANENT;
15711571
else if (newrelpersistence == RELPERSISTENCE_CONSTANT)
15721572
{

src/backend/commands/copy.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -809,7 +809,7 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt,
809809
Assert(rel);
810810

811811
/* check read-only transaction and parallel mode */
812-
if (XactReadOnly && !rel->rd_istemp)
812+
if (XactReadOnly && !rel->rd_islocaltemp)
813813
PreventCommandIfReadOnly("COPY FROM");
814814
PreventCommandIfParallelMode("COPY FROM");
815815

src/backend/commands/sequence.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -599,7 +599,7 @@ nextval_internal(Oid relid, bool check_permissions)
599599
RelationGetRelationName(seqrel))));
600600

601601
/* read-only transactions may only modify temp sequences */
602-
if (!seqrel->rd_istemp)
602+
if (!seqrel->rd_islocaltemp)
603603
PreventCommandIfReadOnly("nextval()");
604604

605605
/*
@@ -935,7 +935,7 @@ AdjustSequence(Oid relid, int64 next)
935935
}
936936

937937
/* read-only transactions may only modify temp sequences */
938-
if (!seqrel->rd_istemp)
938+
if (!seqrel->rd_islocaltemp)
939939
PreventCommandIfReadOnly("setval()");
940940

941941
/*
@@ -1067,7 +1067,7 @@ do_setval(Oid relid, int64 next, bool iscalled)
10671067
ReleaseSysCache(pgstuple);
10681068

10691069
/* read-only transactions may only modify temp sequences */
1070-
if (!seqrel->rd_istemp)
1070+
if (!seqrel->rd_islocaltemp)
10711071
PreventCommandIfReadOnly("setval()");
10721072

10731073
/*

src/backend/commands/tablecmds.c

Lines changed: 20 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1827,7 +1827,7 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
18271827

18281828
/* If existing rel is temp, it must belong to this session */
18291829
if (relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
1830-
!relation->rd_istemp)
1830+
!relation->rd_islocaltemp)
18311831
ereport(ERROR,
18321832
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
18331833
errmsg(!is_partition
@@ -3395,7 +3395,6 @@ AlterTableGetLockLevel(List *cmds)
33953395
cmd_lockmode = ShareUpdateExclusiveLock;
33963396
break;
33973397

3398-
case AT_SetLocal:
33993398
case AT_SetLogged:
34003399
case AT_SetUnLogged:
34013400
case AT_SetConstant:
@@ -3676,17 +3675,6 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
36763675
tab->newrelpersistence = RELPERSISTENCE_CONSTANT;
36773676
pass = AT_PASS_MISC;
36783677
break;
3679-
case AT_SetLocal: /* SET LOCAL */
3680-
ATSimplePermissions(rel, ATT_TABLE);
3681-
tab->chgPersistence = ATPrepChangePersistence(rel, RELPERSISTENCE_LOCAL);
3682-
/* force rewrite if necessary; see comment in ATRewriteTables */
3683-
if (tab->chgPersistence)
3684-
{
3685-
tab->rewrite |= AT_REWRITE_ALTER_PERSISTENCE;
3686-
tab->newrelpersistence = RELPERSISTENCE_LOCAL;
3687-
}
3688-
pass = AT_PASS_MISC;
3689-
break;
36903678
case AT_AddOids: /* SET WITH OIDS */
36913679
ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
36923680
if (!rel->rd_rel->relhasoids || recursing)
@@ -4017,7 +4005,6 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
40174005
case AT_DropCluster: /* SET WITHOUT CLUSTER */
40184006
ATExecDropCluster(rel, lockmode);
40194007
break;
4020-
case AT_SetLocal: /* SET LOCAL */
40214008
case AT_SetLogged: /* SET LOGGED */
40224009
case AT_SetUnLogged: /* SET UNLOGGED */
40234010
break;
@@ -7190,17 +7177,14 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
71907177
*/
71917178
switch (rel->rd_rel->relpersistence)
71927179
{
7193-
case RELPERSISTENCE_LOCAL:
71947180
case RELPERSISTENCE_PERMANENT:
7195-
if (pkrel->rd_rel->relpersistence != RELPERSISTENCE_PERMANENT
7196-
&& pkrel->rd_rel->relpersistence != RELPERSISTENCE_LOCAL)
7181+
if (pkrel->rd_rel->relpersistence != RELPERSISTENCE_PERMANENT)
71977182
ereport(ERROR,
71987183
(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
71997184
errmsg("constraints on permanent tables may reference only permanent tables")));
72007185
break;
72017186
case RELPERSISTENCE_UNLOGGED:
72027187
if (pkrel->rd_rel->relpersistence != RELPERSISTENCE_PERMANENT
7203-
&& pkrel->rd_rel->relpersistence != RELPERSISTENCE_LOCAL
72047188
&& pkrel->rd_rel->relpersistence != RELPERSISTENCE_UNLOGGED)
72057189
ereport(ERROR,
72067190
(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
@@ -7211,7 +7195,7 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
72117195
ereport(ERROR,
72127196
(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
72137197
errmsg("constraints on temporary tables may reference only temporary tables")));
7214-
if (!pkrel->rd_istemp || !rel->rd_istemp)
7198+
if (!pkrel->rd_islocaltemp || !rel->rd_islocaltemp)
72157199
ereport(ERROR,
72167200
(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
72177201
errmsg("constraints on temporary tables must involve temporary tables of this session")));
@@ -10788,7 +10772,6 @@ ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
1078810772
* init fork of an unlogged relation.
1078910773
*/
1079010774
if (rel->rd_rel->relpersistence == RELPERSISTENCE_PERMANENT ||
10791-
rel->rd_rel->relpersistence == RELPERSISTENCE_LOCAL ||
1079210775
(rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED &&
1079310776
forkNum == INIT_FORKNUM))
1079410777
log_smgrcreate(&newrnode, forkNum);
@@ -11037,7 +11020,7 @@ copy_relation_data(SMgrRelation src, SMgrRelation dst,
1103711020
* enabled AND it's a permanent relation.
1103811021
*/
1103911022
use_wal = XLogIsNeeded() &&
11040-
(relpersistence == RELPERSISTENCE_PERMANENT || relpersistence == RELPERSISTENCE_LOCAL || copying_initfork);
11023+
(relpersistence == RELPERSISTENCE_PERMANENT || copying_initfork);
1104111024

1104211025
nblocks = smgrnblocks(src, forkNum);
1104311026

@@ -11096,7 +11079,7 @@ copy_relation_data(SMgrRelation src, SMgrRelation dst,
1109611079
* wouldn't replay our earlier WAL entries. If we do not fsync those pages
1109711080
* here, they might still not be on disk when the crash occurs.
1109811081
*/
11099-
if (relpersistence == RELPERSISTENCE_PERMANENT || relpersistence == RELPERSISTENCE_LOCAL || copying_initfork)
11082+
if (relpersistence == RELPERSISTENCE_PERMANENT || copying_initfork)
1110011083
smgrimmedsync(dst, forkNum);
1110111084
}
1110211085

@@ -11183,14 +11166,14 @@ ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
1118311166

1118411167
/* If parent rel is temp, it must belong to this session */
1118511168
if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
11186-
!parent_rel->rd_istemp)
11169+
!parent_rel->rd_islocaltemp)
1118711170
ereport(ERROR,
1118811171
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
1118911172
errmsg("cannot inherit from temporary relation of another session")));
1119011173

1119111174
/* Ditto for the child */
1119211175
if (child_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
11193-
!child_rel->rd_istemp)
11176+
!child_rel->rd_islocaltemp)
1119411177
ereport(ERROR,
1119511178
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
1119611179
errmsg("cannot inherit to temporary relation of another session")));
@@ -12542,7 +12525,15 @@ ATPrepChangePersistence(Relation rel, char newrelpersistence)
1254212525
HeapTuple tuple;
1254312526
SysScanDesc scan;
1254412527
ScanKeyData skey[1];
12545-
bool toLogged = newrelpersistence != RELPERSISTENCE_UNLOGGED;
12528+
bool toLogged = false;
12529+
12530+
/*
12531+
* When we track constraints, constant tables behaves just like
12532+
* permanent ones.
12533+
*/
12534+
if (newrelpersistence == RELPERSISTENCE_PERMANENT
12535+
|| newrelpersistence == RELPERSISTENCE_CONSTANT)
12536+
toLogged = true;
1254612537

1254712538
/* Nothing to do */
1254812539
if (rel->rd_rel->relpersistence == newrelpersistence)
@@ -12629,8 +12620,7 @@ ATPrepChangePersistence(Relation rel, char newrelpersistence)
1262912620

1263012621
if (newrelpersistence == RELPERSISTENCE_PERMANENT)
1263112622
{
12632-
if (foreignrel->rd_rel->relpersistence != RELPERSISTENCE_PERMANENT &&
12633-
foreignrel->rd_rel->relpersistence != RELPERSISTENCE_LOCAL)
12623+
if (foreignrel->rd_rel->relpersistence != RELPERSISTENCE_PERMANENT)
1263412624
ereport(ERROR,
1263512625
(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
1263612626
errmsg("could not change table \"%s\" to logged because it references unlogged table \"%s\"",
@@ -12640,8 +12630,7 @@ ATPrepChangePersistence(Relation rel, char newrelpersistence)
1264012630
}
1264112631
if (newrelpersistence == RELPERSISTENCE_UNLOGGED)
1264212632
{
12643-
if (foreignrel->rd_rel->relpersistence == RELPERSISTENCE_PERMANENT ||
12644-
foreignrel->rd_rel->relpersistence == RELPERSISTENCE_LOCAL)
12633+
if (foreignrel->rd_rel->relpersistence == RELPERSISTENCE_PERMANENT)
1264512634
ereport(ERROR,
1264612635
(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
1264712636
errmsg("could not change table \"%s\" to unlogged because it references logged table \"%s\"",
@@ -13761,14 +13750,14 @@ ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd)
1376113750

1376213751
/* If the parent is temp, it must belong to this session */
1376313752
if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
13764-
!rel->rd_istemp)
13753+
!rel->rd_islocaltemp)
1376513754
ereport(ERROR,
1376613755
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
1376713756
errmsg("cannot attach as partition of temporary relation of another session")));
1376813757

1376913758
/* Ditto for the partition */
1377013759
if (attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
13771-
!attachrel->rd_istemp)
13760+
!attachrel->rd_islocaltemp)
1377213761
ereport(ERROR,
1377313762
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
1377413763
errmsg("cannot attach temporary relation of another session as partition")));

src/backend/commands/view.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -553,7 +553,7 @@ DefineView(ViewStmt *stmt, const char *queryString,
553553
* schema name.
554554
*/
555555
view = copyObject(stmt->view); /* don't corrupt original command */
556-
if ((view->relpersistence == RELPERSISTENCE_PERMANENT || view->relpersistence == RELPERSISTENCE_LOCAL)
556+
if (view->relpersistence == RELPERSISTENCE_PERMANENT
557557
&& isQueryUsingTempRelation(viewParse))
558558
{
559559
view->relpersistence = RELPERSISTENCE_TEMP;

src/backend/parser/gram.y

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2301,13 +2301,6 @@ alter_table_cmd:
23012301
n->name = NULL;
23022302
$$ = (Node *)n;
23032303
}
2304-
/* ALTER TABLE <name> SET LOCAL */
2305-
| SET LOCAL
2306-
{
2307-
AlterTableCmd *n = makeNode(AlterTableCmd);
2308-
n->subtype = AT_SetLocal;
2309-
$$ = (Node *)n;
2310-
}
23112304
/* ALTER TABLE <name> SET LOGGED */
23122305
| SET LOGGED
23132306
{
@@ -3405,7 +3398,6 @@ OptTemp: TEMPORARY { $$ = RELPERSISTENCE_TEMP; }
34053398
parser_errposition(@1)));
34063399
$$ = RELPERSISTENCE_TEMP;
34073400
}
3408-
| LOCAL { $$ = RELPERSISTENCE_LOCAL; }
34093401
| UNLOGGED { $$ = RELPERSISTENCE_UNLOGGED; }
34103402
| CONSTANT { $$ = RELPERSISTENCE_CONSTANT; }
34113403
| /*EMPTY*/ { $$ = RELPERSISTENCE_PERMANENT; }

src/backend/storage/buffer/bufmgr.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1523,7 +1523,7 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum,
15231523
buf_state &= ~(BM_VALID | BM_DIRTY | BM_JUST_DIRTIED |
15241524
BM_CHECKPOINT_NEEDED | BM_IO_ERROR | BM_PERMANENT |
15251525
BUF_USAGECOUNT_MASK);
1526-
if (relpersistence == RELPERSISTENCE_PERMANENT || relpersistence == RELPERSISTENCE_LOCAL || forkNum == INIT_FORKNUM)
1526+
if (relpersistence == RELPERSISTENCE_PERMANENT || forkNum == INIT_FORKNUM)
15271527
buf_state |= BM_TAG_VALID | BM_PERMANENT | BUF_USAGECOUNT_ONE;
15281528
else
15291529
buf_state |= BM_TAG_VALID | BUF_USAGECOUNT_ONE;

src/backend/utils/adt/dbsize.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1036,7 +1036,6 @@ pg_relation_filepath(PG_FUNCTION_ARGS)
10361036
case RELPERSISTENCE_UNLOGGED:
10371037
case RELPERSISTENCE_CONSTANT:
10381038
case RELPERSISTENCE_PERMANENT:
1039-
case RELPERSISTENCE_LOCAL:
10401039
backend = InvalidBackendId;
10411040
break;
10421041
case RELPERSISTENCE_TEMP:

0 commit comments

Comments
 (0)