Skip to content

Commit 12c31fb

Browse files
authored
Merge branch 'master' into 31504
2 parents 25f749d + aff62f6 commit 12c31fb

File tree

220 files changed

+1226
-1380
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

220 files changed

+1226
-1380
lines changed

RELEASE-NOTES.md

+7-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
1. SQL Parser: Support parsing Doris STRRIGHT - [#33393](https://github.com/apache/shardingsphere/pull/33393)
2121
1. SQL Parser: Support parsing MySQL by adding non-reserved keywords in BaseRule.g4 file according to MySQL 8.4 doc - [#33846](https://github.com/apache/shardingsphere/pull/33846)
2222
1. SQL Parser: Support parsing Doris EXTRACT\_URL\_PARAMETER - [#33571](https://github.com/apache/shardingsphere/pull/33571)
23+
1. SQL Parser: Enhance create view, alter view, drop view sql parser - [#34283](https://github.com/apache/shardingsphere/pull/34283)
2324
1. SQL Binder: Add sql bind logic for create table statement - [#34074](https://github.com/apache/shardingsphere/pull/34074)
2425
1. SQL Binder: Support create index statement sql bind - [#34112](https://github.com/apache/shardingsphere/pull/34112)
2526
1. SQL Parser: Support MySQL update with statement parse - [#34126](https://github.com/apache/shardingsphere/pull/34126)
@@ -32,6 +33,10 @@
3233
1. SQL Binder: Support truncate table sql bind and add test case - [#34162](https://github.com/apache/shardingsphere/pull/34162)
3334
1. SQL Binder: Support create view, alter view, drop view sql bind logic - [#34167](https://github.com/apache/shardingsphere/pull/34167)
3435
1. SQL Binder: Support load data and load xml sql bind and add test case - [#34177](https://github.com/apache/shardingsphere/pull/34177)
36+
1. SQL Binder: Support optimize table sql bind and add test case - [#34242](https://github.com/apache/shardingsphere/pull/34242)
37+
1. SQL Binder: Support show create table, show columns, show index statement bind - [#34271](https://github.com/apache/shardingsphere/pull/34271)
38+
1. SQL Binder: Support deny user sql bind and add test case - [#34279](https://github.com/apache/shardingsphere/pull/34279)
39+
1. SQL Binder: Support with segment bind check with UniqueCommonTableExpressionException - [#34163](https://github.com/apache/shardingsphere/pull/34163)
3540
1. Storage: Support setting `hive_conf_list`, `hive_var_list` and `sess_var_list` for jdbcURL when connecting to HiveServer2 - [#33749](https://github.com/apache/shardingsphere/pull/33749)
3641
1. Storage: Support connecting to HiveServer2 through database connection pools other than HikariCP - [#33762](https://github.com/apache/shardingsphere/pull/33762)
3742
1. Storage: Partial support for connecting to embedded ClickHouse `chDB` - [#33786](https://github.com/apache/shardingsphere/pull/33786)
@@ -54,7 +59,7 @@
5459
1. SQL Binder: Support show create table, show columns, show index statement bind - [#34271](https://github.com/apache/shardingsphere/pull/34271)
5560
1. SQL Parser: Enhance create view, alter view, drop view sql parser - [#34283](https://github.com/apache/shardingsphere/pull/34283)
5661
1. SQL Binder: Support deny user sql bind and add test case - [#34279](https://github.com/apache/shardingsphere/pull/34279)
57-
1. SQL Binder: Support with segment bind check with UniqueCommonTableExpressionException - [#34163](https://github.com/apache/shardingsphere/pull/34163)\
62+
1. SQL Binder: Support with segment bind check with UniqueCommonTableExpressionException - [#34163](https://github.com/apache/shardingsphere/pull/34163)
5863
1. SQL Parser: Support parsing Doris RECOVER - [#31504](https://github.com/apache/shardingsphere/pull/31504)
5964

6065
### Bug Fixes
@@ -72,6 +77,7 @@
7277
1. Proxy: Fixes BatchUpdateException when execute INSERT INTO ON DUPLICATE KEY UPDATE in proxy adapter - [#33796](https://github.com/apache/shardingsphere/pull/33796)
7378
1. Proxy: Fixes "ALL PRIVILEGES ON `DB`.*" is not recognized during SELECT privilege verification for MySQL - [#34037](https://github.com/apache/shardingsphere/pull/34037)
7479
1. Proxy: Fixes MySQL longblob wrong column type returned by proxy protocol - [#34121](https://github.com/apache/shardingsphere/pull/34121)
80+
1. Proxy: Fixes MySQL proxy error if insert SQL contains more parameters not in insert values syntax - [#34287](https://github.com/apache/shardingsphere/pull/34287)
7581
1. Sharding: Remove ShardingRouteAlgorithmException check logic temporarily to support different actual table name configuration - [#33367](https://github.com/apache/shardingsphere/pull/33367)
7682
1. Sharding: Fixes SQL COUNT with GROUP BY to prevent incorrect row returns - [#33380](https://github.com/apache/shardingsphere/pull/33380)
7783
1. Sharding: Fixes avg, sum, min, max function return empty data when no query result return - [#33449](https://github.com/apache/shardingsphere/pull/33449)

agent/plugins/metrics/core/src/test/java/org/apache/shardingsphere/agent/plugin/metrics/core/exporter/impl/proxy/ProxyMetaDataInfoExporterTest.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
import org.apache.shardingsphere.infra.metadata.database.resource.ResourceMetaData;
2929
import org.apache.shardingsphere.infra.metadata.database.resource.unit.StorageUnit;
3030
import org.apache.shardingsphere.infra.spi.type.typed.TypedSPILoader;
31-
import org.apache.shardingsphere.metadata.persist.MetaDataPersistService;
31+
import org.apache.shardingsphere.mode.metadata.persist.MetaDataPersistService;
3232
import org.apache.shardingsphere.mode.manager.ContextManager;
3333
import org.apache.shardingsphere.mode.metadata.MetaDataContexts;
3434
import org.apache.shardingsphere.mode.metadata.MetaDataContextsFactory;
@@ -82,7 +82,7 @@ private ContextManager mockContextManager() {
8282
when(database.getProtocolType()).thenReturn(TypedSPILoader.getService(DatabaseType.class, "FIXTURE"));
8383
ShardingSphereMetaData metaData = mock(ShardingSphereMetaData.class);
8484
when(metaData.getAllDatabases()).thenReturn(Collections.singleton(database));
85-
MetaDataContexts metaDataContexts = MetaDataContextsFactory.create(mock(MetaDataPersistService.class), metaData);
85+
MetaDataContexts metaDataContexts = new MetaDataContexts(metaData, MetaDataContextsFactory.createStatistics(mock(MetaDataPersistService.class), metaData));
8686
ContextManager result = mock(ContextManager.class, RETURNS_DEEP_STUBS);
8787
when(result.getMetaDataContexts()).thenReturn(metaDataContexts);
8888
return result;

agent/plugins/metrics/core/src/test/java/org/apache/shardingsphere/agent/plugin/metrics/core/exporter/impl/proxy/ProxyStateExporterTest.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
import org.apache.shardingsphere.agent.plugin.metrics.core.fixture.collector.MetricsCollectorFixture;
2525
import org.apache.shardingsphere.infra.metadata.ShardingSphereMetaData;
2626
import org.apache.shardingsphere.infra.state.instance.InstanceStateContext;
27-
import org.apache.shardingsphere.metadata.persist.MetaDataPersistService;
27+
import org.apache.shardingsphere.mode.metadata.persist.MetaDataPersistService;
2828
import org.apache.shardingsphere.mode.manager.ContextManager;
2929
import org.apache.shardingsphere.mode.metadata.MetaDataContexts;
3030
import org.apache.shardingsphere.mode.metadata.MetaDataContextsFactory;
@@ -73,7 +73,8 @@ void assertExportWithContextManager() {
7373
}
7474

7575
private ContextManager mockContextManager() {
76-
MetaDataContexts metaDataContexts = MetaDataContextsFactory.create(mock(MetaDataPersistService.class), new ShardingSphereMetaData());
76+
ShardingSphereMetaData metaData = new ShardingSphereMetaData();
77+
MetaDataContexts metaDataContexts = new MetaDataContexts(metaData, MetaDataContextsFactory.createStatistics(mock(MetaDataPersistService.class), metaData));
7778
ContextManager result = mock(ContextManager.class, RETURNS_DEEP_STUBS);
7879
when(result.getMetaDataContexts()).thenReturn(metaDataContexts);
7980
return result;

agent/plugins/metrics/type/prometheus/src/test/java/org/apache/shardingsphere/agent/plugin/metrics/prometheus/PrometheusPluginLifecycleServiceTest.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
import org.apache.shardingsphere.infra.lock.LockContext;
2626
import org.apache.shardingsphere.infra.metadata.ShardingSphereMetaData;
2727
import org.apache.shardingsphere.infra.util.eventbus.EventBusContext;
28-
import org.apache.shardingsphere.metadata.persist.MetaDataPersistService;
28+
import org.apache.shardingsphere.mode.metadata.persist.MetaDataPersistService;
2929
import org.apache.shardingsphere.mode.manager.ContextManager;
3030
import org.apache.shardingsphere.mode.manager.standalone.workerid.StandaloneWorkerIdGenerator;
3131
import org.apache.shardingsphere.mode.metadata.MetaDataContexts;
@@ -70,7 +70,8 @@ void assertStart() throws IOException {
7070
}
7171

7272
private ContextManager mockContextManager() {
73-
MetaDataContexts metaDataContexts = MetaDataContextsFactory.create(mock(MetaDataPersistService.class), new ShardingSphereMetaData());
73+
ShardingSphereMetaData metaData = new ShardingSphereMetaData();
74+
MetaDataContexts metaDataContexts = new MetaDataContexts(metaData, MetaDataContextsFactory.createStatistics(mock(MetaDataPersistService.class), metaData));
7475
ComputeNodeInstanceContext computeNodeInstanceContext = new ComputeNodeInstanceContext(
7576
new ComputeNodeInstance(mock(InstanceMetaData.class)), new ModeConfiguration("Standalone", null), new EventBusContext());
7677
computeNodeInstanceContext.init(new StandaloneWorkerIdGenerator(), mock(LockContext.class));

features/readwrite-splitting/core/pom.xml

-5
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,6 @@
3838
<artifactId>shardingsphere-infra-route</artifactId>
3939
<version>${project.version}</version>
4040
</dependency>
41-
<dependency>
42-
<groupId>org.apache.shardingsphere</groupId>
43-
<artifactId>shardingsphere-metadata-core</artifactId>
44-
<version>${project.version}</version>
45-
</dependency>
4641
<dependency>
4742
<groupId>org.apache.shardingsphere</groupId>
4843
<artifactId>shardingsphere-mode-core</artifactId>
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
* limitations under the License.
1616
*/
1717

18-
package org.apache.shardingsphere.readwritesplitting.cluster;
18+
package org.apache.shardingsphere.readwritesplitting.deliver;
1919

2020
import lombok.Getter;
2121
import lombok.RequiredArgsConstructor;
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,19 @@
1515
* limitations under the License.
1616
*/
1717

18-
package org.apache.shardingsphere.readwritesplitting.cluster;
18+
package org.apache.shardingsphere.readwritesplitting.deliver;
1919

2020
import com.google.common.eventbus.Subscribe;
2121
import lombok.Setter;
22-
import org.apache.shardingsphere.metadata.persist.node.QualifiedDataSourceNodePath;
22+
import org.apache.shardingsphere.mode.metadata.persist.node.QualifiedDataSourceNodePath;
2323
import org.apache.shardingsphere.mode.deliver.DeliverEventSubscriber;
2424
import org.apache.shardingsphere.mode.spi.PersistRepository;
2525

2626
/**
27-
* Readwrite-splitting qualified data source deleted subscriber.
27+
* Readwrite-splitting qualified data source changed subscriber.
2828
*/
2929
@Setter
30-
public final class ReadwriteSplittingQualifiedDataSourceDeletedSubscriber implements DeliverEventSubscriber {
30+
public final class ReadwriteSplittingQualifiedDataSourceChangedSubscriber implements DeliverEventSubscriber {
3131

3232
private PersistRepository repository;
3333

features/readwrite-splitting/core/src/main/java/org/apache/shardingsphere/readwritesplitting/rule/attribute/ReadwriteSplittingStaticDataSourceRuleAttribute.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
import org.apache.shardingsphere.infra.metadata.database.schema.QualifiedDataSource;
2424
import org.apache.shardingsphere.infra.rule.attribute.datasource.StaticDataSourceRuleAttribute;
2525
import org.apache.shardingsphere.infra.state.datasource.DataSourceState;
26-
import org.apache.shardingsphere.readwritesplitting.cluster.QualifiedDataSourceDeletedEvent;
26+
import org.apache.shardingsphere.readwritesplitting.deliver.QualifiedDataSourceDeletedEvent;
2727
import org.apache.shardingsphere.readwritesplitting.exception.logic.ReadwriteSplittingDataSourceRuleNotFoundException;
2828
import org.apache.shardingsphere.readwritesplitting.rule.ReadwriteSplittingDataSourceGroupRule;
2929

features/readwrite-splitting/core/src/main/resources/META-INF/services/org.apache.shardingsphere.mode.deliver.DeliverEventSubscriber

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,4 @@
1515
# limitations under the License.
1616
#
1717

18-
org.apache.shardingsphere.readwritesplitting.cluster.ReadwriteSplittingQualifiedDataSourceDeletedSubscriber
18+
org.apache.shardingsphere.readwritesplitting.deliver.ReadwriteSplittingQualifiedDataSourceChangedSubscriber
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
* limitations under the License.
1616
*/
1717

18-
package org.apache.shardingsphere.readwritesplitting.cluster;
18+
package org.apache.shardingsphere.readwritesplitting.deliver;
1919

2020
import org.apache.shardingsphere.infra.metadata.database.schema.QualifiedDataSource;
2121
import org.apache.shardingsphere.mode.spi.PersistRepository;
@@ -28,16 +28,16 @@
2828
import static org.mockito.Mockito.verify;
2929

3030
@ExtendWith(MockitoExtension.class)
31-
class ReadwriteSplittingQualifiedDataSourceDeletedSubscriberTest {
31+
class ReadwriteSplittingQualifiedDataSourceChangedSubscriberTest {
3232

33-
private ReadwriteSplittingQualifiedDataSourceDeletedSubscriber subscriber;
33+
private ReadwriteSplittingQualifiedDataSourceChangedSubscriber subscriber;
3434

3535
@Mock
3636
private PersistRepository repository;
3737

3838
@BeforeEach
3939
void setUp() {
40-
subscriber = new ReadwriteSplittingQualifiedDataSourceDeletedSubscriber();
40+
subscriber = new ReadwriteSplittingQualifiedDataSourceChangedSubscriber();
4141
subscriber.setRepository(repository);
4242
}
4343

features/readwrite-splitting/core/src/test/java/org/apache/shardingsphere/readwritesplitting/rule/attribute/ReadwriteSplittingStaticDataSourceRuleAttributeTest.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
import org.apache.shardingsphere.infra.instance.ComputeNodeInstanceContext;
2121
import org.apache.shardingsphere.infra.metadata.database.schema.QualifiedDataSource;
2222
import org.apache.shardingsphere.infra.state.datasource.DataSourceState;
23-
import org.apache.shardingsphere.readwritesplitting.cluster.QualifiedDataSourceDeletedEvent;
23+
import org.apache.shardingsphere.readwritesplitting.deliver.QualifiedDataSourceDeletedEvent;
2424
import org.apache.shardingsphere.readwritesplitting.exception.logic.ReadwriteSplittingDataSourceRuleNotFoundException;
2525
import org.apache.shardingsphere.readwritesplitting.rule.ReadwriteSplittingDataSourceGroupRule;
2626
import org.junit.jupiter.api.Test;

infra/common/src/main/java/org/apache/shardingsphere/infra/instance/ComputeNodeData.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@
2727
@Getter
2828
public final class ComputeNodeData {
2929

30+
private final String databaseName;
31+
3032
private final String attribute;
3133

3234
private final String version;
33-
34-
private final String databaseName;
3535
}

infra/common/src/main/java/org/apache/shardingsphere/infra/instance/metadata/InstanceMetaDataFactory.java

+7-6
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
import lombok.AccessLevel;
2121
import lombok.NoArgsConstructor;
22+
import org.apache.shardingsphere.infra.instance.ComputeNodeData;
2223
import org.apache.shardingsphere.infra.instance.metadata.jdbc.JDBCInstanceMetaData;
2324
import org.apache.shardingsphere.infra.instance.metadata.proxy.ProxyInstanceMetaData;
2425

@@ -32,13 +33,13 @@ public final class InstanceMetaDataFactory {
3233
* Create instance meta data.
3334
*
3435
* @param instanceId instance ID
35-
* @param instanceType instance type
36-
* @param attributes attributes
37-
* @param version version
38-
* @param databaseName database name
36+
* @param instanceType instance type
37+
* @param computeNodeData compute node data
3938
* @return created instance meta data
4039
*/
41-
public static InstanceMetaData create(final String instanceId, final InstanceType instanceType, final String attributes, final String version, final String databaseName) {
42-
return InstanceType.JDBC == instanceType ? new JDBCInstanceMetaData(instanceId, attributes, version, databaseName) : new ProxyInstanceMetaData(instanceId, attributes, version);
40+
public static InstanceMetaData create(final String instanceId, final InstanceType instanceType, final ComputeNodeData computeNodeData) {
41+
return InstanceType.JDBC == instanceType
42+
? new JDBCInstanceMetaData(instanceId, computeNodeData.getAttribute(), computeNodeData.getVersion(), computeNodeData.getDatabaseName())
43+
: new ProxyInstanceMetaData(instanceId, computeNodeData.getAttribute(), computeNodeData.getVersion());
4344
}
4445
}

infra/common/src/main/java/org/apache/shardingsphere/infra/instance/yaml/YamlComputeNodeData.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@
2828
@Setter
2929
public final class YamlComputeNodeData implements YamlConfiguration {
3030

31+
private String databaseName;
32+
3133
private String attribute;
3234

3335
private String version;
34-
35-
private String databaseName;
3636
}

infra/common/src/main/java/org/apache/shardingsphere/infra/instance/yaml/YamlComputeNodeDataSwapper.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,14 @@ public final class YamlComputeNodeDataSwapper implements YamlConfigurationSwappe
2828
@Override
2929
public YamlComputeNodeData swapToYamlConfiguration(final ComputeNodeData data) {
3030
YamlComputeNodeData result = new YamlComputeNodeData();
31+
result.setDatabaseName(data.getDatabaseName());
3132
result.setAttribute(data.getAttribute());
3233
result.setVersion(data.getVersion());
33-
result.setDatabaseName(data.getDatabaseName());
3434
return result;
3535
}
3636

3737
@Override
3838
public ComputeNodeData swapToObject(final YamlComputeNodeData yamlConfig) {
39-
return new ComputeNodeData(yamlConfig.getAttribute(), yamlConfig.getVersion(), yamlConfig.getDatabaseName());
39+
return new ComputeNodeData(yamlConfig.getDatabaseName(), yamlConfig.getAttribute(), yamlConfig.getVersion());
4040
}
4141
}

infra/common/src/main/java/org/apache/shardingsphere/infra/metadata/ShardingSphereMetaData.java

+18-1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535

3636
import java.util.Collection;
3737
import java.util.Collections;
38+
import java.util.LinkedList;
3839
import java.util.Map;
3940
import java.util.Optional;
4041
import java.util.Properties;
@@ -45,7 +46,7 @@
4546
* ShardingSphere meta data.
4647
*/
4748
@Getter
48-
public final class ShardingSphereMetaData {
49+
public final class ShardingSphereMetaData implements AutoCloseable {
4950

5051
@Getter(AccessLevel.NONE)
5152
private final Map<ShardingSphereIdentifier, ShardingSphereDatabase> databases;
@@ -143,4 +144,20 @@ private void cleanResources(final ShardingSphereDatabase database) {
143144
Optional.ofNullable(database.getResourceMetaData())
144145
.ifPresent(optional -> optional.getStorageUnits().values().forEach(each -> new DataSourcePoolDestroyer(each.getDataSource()).asyncDestroy()));
145146
}
147+
148+
@SneakyThrows(Exception.class)
149+
@Override
150+
public void close() {
151+
for (ShardingSphereRule each : getAllRules()) {
152+
if (each instanceof AutoCloseable) {
153+
((AutoCloseable) each).close();
154+
}
155+
}
156+
}
157+
158+
private Collection<ShardingSphereRule> getAllRules() {
159+
Collection<ShardingSphereRule> result = new LinkedList<>(globalRuleMetaData.getRules());
160+
getAllDatabases().stream().map(each -> each.getRuleMetaData().getRules()).forEach(result::addAll);
161+
return result;
162+
}
146163
}

infra/common/src/test/java/org/apache/shardingsphere/infra/instance/metadata/InstanceMetaDataFactoryTest.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
package org.apache.shardingsphere.infra.instance.metadata;
1919

20+
import org.apache.shardingsphere.infra.instance.ComputeNodeData;
2021
import org.apache.shardingsphere.infra.instance.metadata.proxy.ProxyInstanceMetaData;
2122
import org.junit.jupiter.api.Test;
2223

@@ -28,7 +29,7 @@ class InstanceMetaDataFactoryTest {
2829

2930
@Test
3031
void assertCreateJDBCInstanceMetaDataWithInstanceId() {
31-
InstanceMetaData actual = InstanceMetaDataFactory.create("foo_id", InstanceType.JDBC, "127.0.0.1", "foo_version", "foo_db");
32+
InstanceMetaData actual = InstanceMetaDataFactory.create("foo_id", InstanceType.JDBC, new ComputeNodeData("foo_db", "127.0.0.1", "foo_version"));
3233
assertThat(actual.getId(), is("foo_id"));
3334
assertNotNull(actual.getIp());
3435
assertThat(actual.getAttributes(), is("127.0.0.1"));
@@ -38,7 +39,7 @@ void assertCreateJDBCInstanceMetaDataWithInstanceId() {
3839

3940
@Test
4041
void assertCreateProxyInstanceMetaDataWithInstanceId() {
41-
ProxyInstanceMetaData actual = (ProxyInstanceMetaData) InstanceMetaDataFactory.create("foo_id", InstanceType.PROXY, "127.0.0.1@3307", "foo_version", "foo_db");
42+
ProxyInstanceMetaData actual = (ProxyInstanceMetaData) InstanceMetaDataFactory.create("foo_id", InstanceType.PROXY, new ComputeNodeData("foo_db", "127.0.0.1@3307", "foo_version"));
4243
assertThat(actual.getId(), is("foo_id"));
4344
assertThat(actual.getIp(), is("127.0.0.1"));
4445
assertThat(actual.getPort(), is(3307));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"resources":{
3+
"includes":[{
4+
"condition":{"typeReachable":"com.github.dockerjava.zerodep.shaded.org.apache.hc.core5.util.VersionInfo"},
5+
"pattern":"\\Qcom/github/dockerjava/zerodep/shaded/org/apache/hc/client5/version.properties\\E"
6+
}]},
7+
"bundles":[]
8+
}

0 commit comments

Comments
 (0)