datus-doris 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- datus_doris-0.1.0/.gitignore +143 -0
- datus_doris-0.1.0/PKG-INFO +154 -0
- datus_doris-0.1.0/README.md +128 -0
- datus_doris-0.1.0/datus_doris/__init__.py +21 -0
- datus_doris-0.1.0/datus_doris/config.py +27 -0
- datus_doris-0.1.0/datus_doris/connector.py +808 -0
- datus_doris-0.1.0/datus_doris/tpch_data.py +89 -0
- datus_doris-0.1.0/docker-compose.yml +94 -0
- datus_doris-0.1.0/pyproject.toml +78 -0
- datus_doris-0.1.0/scripts/init_tpch_data.py +165 -0
- datus_doris-0.1.0/scripts/test.sh +75 -0
- datus_doris-0.1.0/scripts/wait_for_doris.py +197 -0
- datus_doris-0.1.0/tests/__init__.py +0 -0
- datus_doris-0.1.0/tests/conftest.py +15 -0
- datus_doris-0.1.0/tests/integration/__init__.py +3 -0
- datus_doris-0.1.0/tests/integration/conftest.py +242 -0
- datus_doris-0.1.0/tests/integration/test_catalog_operations.py +65 -0
- datus_doris-0.1.0/tests/integration/test_connection.py +23 -0
- datus_doris-0.1.0/tests/integration/test_contract.py +89 -0
- datus_doris-0.1.0/tests/integration/test_materialized_views.py +34 -0
- datus_doris-0.1.0/tests/integration/test_metadata_retrieval.py +89 -0
- datus_doris-0.1.0/tests/integration/test_sql_execution.py +159 -0
- datus_doris-0.1.0/tests/integration/test_tpch.py +136 -0
- datus_doris-0.1.0/tests/unit/__init__.py +0 -0
- datus_doris-0.1.0/tests/unit/test_config.py +81 -0
- datus_doris-0.1.0/tests/unit/test_connector_unit.py +302 -0
- datus_doris-0.1.0/tests/unit/test_migration_mixin.py +183 -0
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
pip-wheel-metadata/
|
|
24
|
+
share/python-wheels/
|
|
25
|
+
*.egg-info/
|
|
26
|
+
.installed.cfg
|
|
27
|
+
*.egg
|
|
28
|
+
MANIFEST
|
|
29
|
+
|
|
30
|
+
# PyInstaller
|
|
31
|
+
*.manifest
|
|
32
|
+
*.spec
|
|
33
|
+
|
|
34
|
+
# Installer logs
|
|
35
|
+
pip-log.txt
|
|
36
|
+
pip-delete-this-directory.txt
|
|
37
|
+
|
|
38
|
+
# Unit test / coverage reports
|
|
39
|
+
htmlcov/
|
|
40
|
+
.tox/
|
|
41
|
+
.nox/
|
|
42
|
+
.coverage
|
|
43
|
+
.coverage.*
|
|
44
|
+
.cache
|
|
45
|
+
nosetests.xml
|
|
46
|
+
coverage.xml
|
|
47
|
+
*.cover
|
|
48
|
+
*.py,cover
|
|
49
|
+
.hypothesis/
|
|
50
|
+
.pytest_cache/
|
|
51
|
+
|
|
52
|
+
# Translations
|
|
53
|
+
*.mo
|
|
54
|
+
*.pot
|
|
55
|
+
|
|
56
|
+
# Django stuff:
|
|
57
|
+
*.log
|
|
58
|
+
local_settings.py
|
|
59
|
+
db.sqlite3
|
|
60
|
+
db.sqlite3-journal
|
|
61
|
+
|
|
62
|
+
# Flask stuff:
|
|
63
|
+
instance/
|
|
64
|
+
.webassets-cache
|
|
65
|
+
|
|
66
|
+
# Scrapy stuff:
|
|
67
|
+
.scrapy
|
|
68
|
+
|
|
69
|
+
# Sphinx documentation
|
|
70
|
+
docs/_build/
|
|
71
|
+
|
|
72
|
+
# PyBuilder
|
|
73
|
+
target/
|
|
74
|
+
|
|
75
|
+
# Jupyter Notebook
|
|
76
|
+
.ipynb_checkpoints
|
|
77
|
+
|
|
78
|
+
# IPython
|
|
79
|
+
profile_default/
|
|
80
|
+
ipython_config.py
|
|
81
|
+
|
|
82
|
+
# pyenv
|
|
83
|
+
.python-version
|
|
84
|
+
|
|
85
|
+
# pipenv
|
|
86
|
+
Pipfile.lock
|
|
87
|
+
|
|
88
|
+
# uv
|
|
89
|
+
uv.lock
|
|
90
|
+
|
|
91
|
+
# PEP 582
|
|
92
|
+
__pypackages__/
|
|
93
|
+
|
|
94
|
+
# Celery stuff
|
|
95
|
+
celerybeat-schedule
|
|
96
|
+
celerybeat.pid
|
|
97
|
+
|
|
98
|
+
# SageMath parsed files
|
|
99
|
+
*.sage.py
|
|
100
|
+
|
|
101
|
+
# Environments
|
|
102
|
+
.env
|
|
103
|
+
.venv
|
|
104
|
+
env/
|
|
105
|
+
venv/
|
|
106
|
+
ENV/
|
|
107
|
+
env.bak/
|
|
108
|
+
venv.bak/
|
|
109
|
+
|
|
110
|
+
# Spyder project settings
|
|
111
|
+
.spyderproject
|
|
112
|
+
.spyproject
|
|
113
|
+
|
|
114
|
+
# Rope project settings
|
|
115
|
+
.ropeproject
|
|
116
|
+
|
|
117
|
+
# mkdocs documentation
|
|
118
|
+
/site
|
|
119
|
+
|
|
120
|
+
# mypy
|
|
121
|
+
.mypy_cache/
|
|
122
|
+
.dmypy.json
|
|
123
|
+
dmypy.json
|
|
124
|
+
|
|
125
|
+
# Pyre type checker
|
|
126
|
+
.pyre/
|
|
127
|
+
|
|
128
|
+
# IDEs
|
|
129
|
+
.vscode/
|
|
130
|
+
.idea/
|
|
131
|
+
*.swp
|
|
132
|
+
*.swo
|
|
133
|
+
*~
|
|
134
|
+
|
|
135
|
+
# OS
|
|
136
|
+
.DS_Store
|
|
137
|
+
Thumbs.db
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
.omc
|
|
141
|
+
|
|
142
|
+
uv.toml
|
|
143
|
+
*/uv.toml
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: datus-doris
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Doris database adapter for Datus
|
|
5
|
+
Project-URL: Homepage, https://github.com/Datus-ai/datus-db-adapters
|
|
6
|
+
Project-URL: Repository, https://github.com/Datus-ai/datus-db-adapters
|
|
7
|
+
Project-URL: Issues, https://github.com/Datus-ai/datus-db-adapters/issues
|
|
8
|
+
Author-email: DatusAI <support@datus.ai>
|
|
9
|
+
License: Apache-2.0
|
|
10
|
+
Keywords: adapter,database,datus,doris
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Requires-Python: >=3.12
|
|
17
|
+
Requires-Dist: datus-db-core>=0.1.5
|
|
18
|
+
Requires-Dist: datus-mysql>=0.1.8
|
|
19
|
+
Requires-Dist: datus-sqlalchemy>=0.1.7
|
|
20
|
+
Requires-Dist: pydantic>=2.0.0
|
|
21
|
+
Requires-Dist: pymysql>=1.1.1
|
|
22
|
+
Provides-Extra: test
|
|
23
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == 'test'
|
|
24
|
+
Requires-Dist: pytest>=7.0.0; extra == 'test'
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
|
|
27
|
+
# datus-doris
|
|
28
|
+
|
|
29
|
+
Apache Doris database adapter for Datus.
|
|
30
|
+
|
|
31
|
+
## Features
|
|
32
|
+
|
|
33
|
+
- MySQL-protocol connection and SQL execution
|
|
34
|
+
- Multi-catalog discovery and context switching
|
|
35
|
+
- Catalog-aware table, view, schema, and sample-row metadata
|
|
36
|
+
- Asynchronous materialized-view discovery and DDL retrieval
|
|
37
|
+
- Three-part identifiers: `catalog.database.table`
|
|
38
|
+
- Doris migration capability, table-layout, and type-mapping guidance
|
|
39
|
+
- List, CSV, Pandas, and Arrow result formats inherited from `datus-mysql`
|
|
40
|
+
|
|
41
|
+
## Installation
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
pip install datus-doris
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
The package installs `datus-db-core` and `datus-mysql` as dependencies and
|
|
48
|
+
registers the `doris` adapter through the `datus.adapters` entry-point group.
|
|
49
|
+
|
|
50
|
+
## Configuration
|
|
51
|
+
|
|
52
|
+
```yaml
|
|
53
|
+
database:
|
|
54
|
+
type: doris
|
|
55
|
+
host: localhost
|
|
56
|
+
port: 9030
|
|
57
|
+
username: root
|
|
58
|
+
password: ""
|
|
59
|
+
catalog: internal
|
|
60
|
+
database: analytics
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
The built-in catalog is `internal`. External catalogs, including Hive Metastore
|
|
64
|
+
catalogs, can be selected through the same connector API.
|
|
65
|
+
|
|
66
|
+
## Python API
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
from datus_doris import DorisConfig, DorisConnector
|
|
70
|
+
|
|
71
|
+
config = DorisConfig(
|
|
72
|
+
host="localhost",
|
|
73
|
+
port=9030,
|
|
74
|
+
username="root",
|
|
75
|
+
password="",
|
|
76
|
+
catalog="internal",
|
|
77
|
+
database="analytics",
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
with DorisConnector(config) as connector:
|
|
81
|
+
catalogs = connector.get_catalogs()
|
|
82
|
+
tables = connector.get_tables(database_name="analytics")
|
|
83
|
+
views = connector.get_views(database_name="analytics")
|
|
84
|
+
materialized_views = connector.get_materialized_views_with_ddl(
|
|
85
|
+
database_name="analytics"
|
|
86
|
+
)
|
|
87
|
+
result = connector.execute_query("SELECT 1")
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Catalog context can be switched explicitly:
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
connector.switch_catalog("hive_catalog")
|
|
94
|
+
databases = connector.get_databases()
|
|
95
|
+
connector.switch_catalog("internal")
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Fully qualified identifiers are rendered as Doris three-part names:
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
connector.full_name(
|
|
102
|
+
catalog_name="internal",
|
|
103
|
+
database_name="analytics",
|
|
104
|
+
table_name="events",
|
|
105
|
+
)
|
|
106
|
+
# `internal`.`analytics`.`events`
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Development and testing
|
|
110
|
+
|
|
111
|
+
Unit tests do not require a database:
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
ci/run-unit-tests.sh datus-doris
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
The repository integration runner starts Apache Doris FE/BE 4.0.7 and Hive
|
|
118
|
+
Metastore 4.0.1, waits for an alive backend and a successful OLAP DDL probe,
|
|
119
|
+
runs the complete integration suite, and removes the test services afterward:
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
ci/run-integration-tests.sh doris
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
To run the package-local workflow manually:
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
cd datus-doris
|
|
129
|
+
docker compose up -d --wait
|
|
130
|
+
./scripts/test.sh integration
|
|
131
|
+
docker compose down -v
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
The integration suite covers catalog operations, Hive external catalogs,
|
|
135
|
+
tables, views, asynchronous materialized views, DDL/DML, sample rows, TPC-H
|
|
136
|
+
queries, and list/CSV/Pandas/Arrow output.
|
|
137
|
+
|
|
138
|
+
### Environment variables
|
|
139
|
+
|
|
140
|
+
| Variable | Default | Description |
|
|
141
|
+
| --- | --- | --- |
|
|
142
|
+
| `DORIS_HOST` | `localhost` | FE query host |
|
|
143
|
+
| `DORIS_PORT` | `9030` | MySQL protocol port |
|
|
144
|
+
| `DORIS_USER` | `root` | Username |
|
|
145
|
+
| `DORIS_PASSWORD` | empty | Password |
|
|
146
|
+
| `DORIS_CATALOG` | `internal` | Initial catalog |
|
|
147
|
+
| `DORIS_DATABASE` | `test` | Test database |
|
|
148
|
+
| `DORIS_FE_JAVA_XMS` | `1024m` | Initial FE JVM heap for the Docker test environment |
|
|
149
|
+
| `DORIS_FE_JAVA_XMX` | `2048m` | Maximum FE JVM heap for the Docker test environment |
|
|
150
|
+
| `HIVE_METASTORE_URI` | `thrift://hive-metastore:9083` | Hive catalog metastore URI |
|
|
151
|
+
|
|
152
|
+
## License
|
|
153
|
+
|
|
154
|
+
Apache License 2.0
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# datus-doris
|
|
2
|
+
|
|
3
|
+
Apache Doris database adapter for Datus.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- MySQL-protocol connection and SQL execution
|
|
8
|
+
- Multi-catalog discovery and context switching
|
|
9
|
+
- Catalog-aware table, view, schema, and sample-row metadata
|
|
10
|
+
- Asynchronous materialized-view discovery and DDL retrieval
|
|
11
|
+
- Three-part identifiers: `catalog.database.table`
|
|
12
|
+
- Doris migration capability, table-layout, and type-mapping guidance
|
|
13
|
+
- List, CSV, Pandas, and Arrow result formats inherited from `datus-mysql`
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pip install datus-doris
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
The package installs `datus-db-core` and `datus-mysql` as dependencies and
|
|
22
|
+
registers the `doris` adapter through the `datus.adapters` entry-point group.
|
|
23
|
+
|
|
24
|
+
## Configuration
|
|
25
|
+
|
|
26
|
+
```yaml
|
|
27
|
+
database:
|
|
28
|
+
type: doris
|
|
29
|
+
host: localhost
|
|
30
|
+
port: 9030
|
|
31
|
+
username: root
|
|
32
|
+
password: ""
|
|
33
|
+
catalog: internal
|
|
34
|
+
database: analytics
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
The built-in catalog is `internal`. External catalogs, including Hive Metastore
|
|
38
|
+
catalogs, can be selected through the same connector API.
|
|
39
|
+
|
|
40
|
+
## Python API
|
|
41
|
+
|
|
42
|
+
```python
|
|
43
|
+
from datus_doris import DorisConfig, DorisConnector
|
|
44
|
+
|
|
45
|
+
config = DorisConfig(
|
|
46
|
+
host="localhost",
|
|
47
|
+
port=9030,
|
|
48
|
+
username="root",
|
|
49
|
+
password="",
|
|
50
|
+
catalog="internal",
|
|
51
|
+
database="analytics",
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
with DorisConnector(config) as connector:
|
|
55
|
+
catalogs = connector.get_catalogs()
|
|
56
|
+
tables = connector.get_tables(database_name="analytics")
|
|
57
|
+
views = connector.get_views(database_name="analytics")
|
|
58
|
+
materialized_views = connector.get_materialized_views_with_ddl(
|
|
59
|
+
database_name="analytics"
|
|
60
|
+
)
|
|
61
|
+
result = connector.execute_query("SELECT 1")
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Catalog context can be switched explicitly:
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
connector.switch_catalog("hive_catalog")
|
|
68
|
+
databases = connector.get_databases()
|
|
69
|
+
connector.switch_catalog("internal")
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Fully qualified identifiers are rendered as Doris three-part names:
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
connector.full_name(
|
|
76
|
+
catalog_name="internal",
|
|
77
|
+
database_name="analytics",
|
|
78
|
+
table_name="events",
|
|
79
|
+
)
|
|
80
|
+
# `internal`.`analytics`.`events`
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Development and testing
|
|
84
|
+
|
|
85
|
+
Unit tests do not require a database:
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
ci/run-unit-tests.sh datus-doris
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
The repository integration runner starts Apache Doris FE/BE 4.0.7 and Hive
|
|
92
|
+
Metastore 4.0.1, waits for an alive backend and a successful OLAP DDL probe,
|
|
93
|
+
runs the complete integration suite, and removes the test services afterward:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
ci/run-integration-tests.sh doris
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
To run the package-local workflow manually:
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
cd datus-doris
|
|
103
|
+
docker compose up -d --wait
|
|
104
|
+
./scripts/test.sh integration
|
|
105
|
+
docker compose down -v
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
The integration suite covers catalog operations, Hive external catalogs,
|
|
109
|
+
tables, views, asynchronous materialized views, DDL/DML, sample rows, TPC-H
|
|
110
|
+
queries, and list/CSV/Pandas/Arrow output.
|
|
111
|
+
|
|
112
|
+
### Environment variables
|
|
113
|
+
|
|
114
|
+
| Variable | Default | Description |
|
|
115
|
+
| --- | --- | --- |
|
|
116
|
+
| `DORIS_HOST` | `localhost` | FE query host |
|
|
117
|
+
| `DORIS_PORT` | `9030` | MySQL protocol port |
|
|
118
|
+
| `DORIS_USER` | `root` | Username |
|
|
119
|
+
| `DORIS_PASSWORD` | empty | Password |
|
|
120
|
+
| `DORIS_CATALOG` | `internal` | Initial catalog |
|
|
121
|
+
| `DORIS_DATABASE` | `test` | Test database |
|
|
122
|
+
| `DORIS_FE_JAVA_XMS` | `1024m` | Initial FE JVM heap for the Docker test environment |
|
|
123
|
+
| `DORIS_FE_JAVA_XMX` | `2048m` | Maximum FE JVM heap for the Docker test environment |
|
|
124
|
+
| `HIVE_METASTORE_URI` | `thrift://hive-metastore:9083` | Hive catalog metastore URI |
|
|
125
|
+
|
|
126
|
+
## License
|
|
127
|
+
|
|
128
|
+
Apache License 2.0
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Copyright 2025-present DatusAI, Inc.
|
|
2
|
+
# Licensed under the Apache License, Version 2.0.
|
|
3
|
+
# See http://www.apache.org/licenses/LICENSE-2.0 for details.
|
|
4
|
+
|
|
5
|
+
from .config import DorisConfig
|
|
6
|
+
from .connector import DorisConnector
|
|
7
|
+
|
|
8
|
+
__version__ = "0.1.0"
|
|
9
|
+
__all__ = ["DorisConnector", "DorisConfig", "register"]
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def register():
|
|
13
|
+
"""Register Doris connector with Datus registry."""
|
|
14
|
+
from datus_db_core import connector_registry
|
|
15
|
+
|
|
16
|
+
connector_registry.register(
|
|
17
|
+
"doris",
|
|
18
|
+
DorisConnector,
|
|
19
|
+
config_class=DorisConfig,
|
|
20
|
+
capabilities={"catalog", "database"},
|
|
21
|
+
)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Copyright 2025-present DatusAI, Inc.
|
|
2
|
+
# Licensed under the Apache License, Version 2.0.
|
|
3
|
+
# See http://www.apache.org/licenses/LICENSE-2.0 for details.
|
|
4
|
+
|
|
5
|
+
from typing import Optional
|
|
6
|
+
|
|
7
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class DorisConfig(BaseModel):
|
|
11
|
+
"""Doris-specific configuration."""
|
|
12
|
+
|
|
13
|
+
model_config = ConfigDict(extra="forbid")
|
|
14
|
+
|
|
15
|
+
host: str = Field(default="127.0.0.1", description="Doris server host")
|
|
16
|
+
port: int = Field(default=9030, description="Doris server port")
|
|
17
|
+
username: str = Field(..., description="Doris username")
|
|
18
|
+
password: str = Field(
|
|
19
|
+
default="",
|
|
20
|
+
description="Doris password",
|
|
21
|
+
json_schema_extra={"input_type": "password"},
|
|
22
|
+
)
|
|
23
|
+
catalog: str = Field(default="internal", description="Default catalog name")
|
|
24
|
+
database: Optional[str] = Field(default=None, description="Default database name")
|
|
25
|
+
charset: str = Field(default="utf8mb4", description="Character set to use")
|
|
26
|
+
autocommit: bool = Field(default=True, description="Enable autocommit mode")
|
|
27
|
+
timeout_seconds: int = Field(default=30, description="Connection timeout in seconds")
|