Использование SQLLine для работы с Phoenix
Секция SQLLine описывает способ подключения к Phoenix с помощью SQLLine. Основные операции с данными, предоставляемые Phoenix при работе в среде SQLLine, приведены ниже. Они основаны на примере, приведенном в разделе Модель данных HBase. Мы используем этот пример для демонстрации работы как в HBase, так и в Phoenix — для понимания сходства и различия между ними.
Полный список этих команд и их описание приведены в документации Phoenix.
ПРИМЕЧАНИЕ
Перед тем как начать выполнять данные команды, рекомендуем ознакомиться с моделью данных HBase и базовыми операциями с данными в HBase. |
Шаг 1. Создание таблицы
Большинство синтаксических конструкций Phoenix соотвествуют SQL. Для создания таблицы используйте ключевые слова CREATE TABLE, затем введите название таблицы в двойных кавычках и описание колонок с указанием их типов в круглых скобках. Если необходимо распределить колонки по разным семействам в HBase, используйте комбинированное название для каждой колонки, состоящее из разделенных точкой названия семейства колонок и квалификатора колонки, как продемонстрировано в примере ниже. Название семейства колонок по умолчанию — 0
. После описания колонок можно указать общие параметры таблицы.
Следующая команда создает таблицу articles
с первичным ключом (primary key) row_id
, который будет использован в HBase в качестве ключа строки (row key). Кроме того, команда определяет две колонки (author
и header
) внутри семейства basic
и четыре колонки (arch
, concepts
, tutorials
и ref
) в семействе tags
. Она также устанавливает, что колонки могу хранить до пяти версий значений.
РЕКОМЕНДАЦИЯ
Заключайте в двойные кавычки |
CREATE TABLE "articles" ("row_id" VARCHAR PRIMARY KEY, "basic"."author" VARCHAR, "basic"."header" VARCHAR, "tags"."arch" BOOLEAN, "tags"."concepts" BOOLEAN, "tags"."tutorials" BOOLEAN, "tags"."ref" BOOLEAN) VERSIONS=5;
Вывод на экран:
No rows affected (1.313 seconds)
Шаг 2. Получение информации о таблице
Для проверки наличия таблицы и получения ее описания можно использовать одну из следующих команд в SQLLine:
-
!tables — возвращает список всех таблиц в Phoenix:
!tables
Вывод на экран:
+------------+--------------+-------------+---------------+----------+------------+----------------------------+-----------------+--------------+-----------------+---------------+---------------+-----------------+------------+-------------+----------------+----------------------+--------------------+-----------------------+ | TABLE_CAT | TABLE_SCHEM | TABLE_NAME | TABLE_TYPE | REMARKS | TYPE_NAME | SELF_REFERENCING_COL_NAME | REF_GENERATION | INDEX_STATE | IMMUTABLE_ROWS | SALT_BUCKETS | MULTI_TENANT | VIEW_STATEMENT | VIEW_TYPE | INDEX_TYPE | TRANSACTIONAL | IS_NAMESPACE_MAPPED | GUIDE_POSTS_WIDTH | TRANSACTION_PROVIDER | +------------+--------------+-------------+---------------+----------+------------+----------------------------+-----------------+--------------+-----------------+---------------+---------------+-----------------+------------+-------------+----------------+----------------------+--------------------+-----------------------+ | | SYSTEM | CATALOG | SYSTEM TABLE | | | | | | false | null | false | | | | false | false | null | | | | SYSTEM | FUNCTION | SYSTEM TABLE | | | | | | false | null | false | | | | false | false | null | | | | SYSTEM | LOG | SYSTEM TABLE | | | | | | true | 32 | false | | | | false | false | null | | | | SYSTEM | SEQUENCE | SYSTEM TABLE | | | | | | false | null | false | | | | false | false | null | | | | SYSTEM | STATS | SYSTEM TABLE | | | | | | false | null | false | | | | false | false | null | | | | | articles | TABLE | | | | | | false | null | false | | | | false | false | null | | +------------+--------------+-------------+---------------+----------+------------+----------------------------+-----------------+--------------+-----------------+---------------+---------------+-----------------+------------+-------------+----------------+----------------------+--------------------+-----------------------+
-
!describe — возвращает описание указанной таблицы, содержащее названия всех колонок, типов данных и другие атрибуты:
!describe "articles"
Вывод на экран:
+------------+--------------+-------------+--------------+------------+------------+--------------+----------------+-----------------+-----------------+-----------+----------+-------------+----------+ | TABLE_CAT | TABLE_SCHEM | TABLE_NAME | COLUMN_NAME | DATA_TYPE | TYPE_NAME | COLUMN_SIZE | BUFFER_LENGTH | DECIMAL_DIGITS | NUM_PREC_RADIX | NULLABLE | REMARKS | COLUMN_DEF | SQL_DATA | +------------+--------------+-------------+--------------+------------+------------+--------------+----------------+-----------------+-----------------+-----------+----------+-------------+----------+ | | | articles | row_id | 12 | VARCHAR | null | null | null | null | 0 | | | null | | | | articles | author | 12 | VARCHAR | null | null | null | null | 1 | | | null | | | | articles | header | 12 | VARCHAR | null | null | null | null | 1 | | | null | | | | articles | arch | 16 | BOOLEAN | null | null | null | null | 1 | | | null | | | | articles | concepts | 16 | BOOLEAN | null | null | null | null | 1 | | | null | | | | articles | tutorials | 16 | BOOLEAN | null | null | null | null | 1 | | | null | | | | articles | ref | 16 | BOOLEAN | null | null | null | null | 1 | | | null | +------------+--------------+-------------+--------------+------------+------------+--------------+----------------+-----------------+-----------------+-----------+----------+-------------+----------+
Если вывод команды не умещается в окне консоли по ширине, завершите сеанс SQLLine (используя команду !quit
) и выполните следующую команду для расширения поля вывода:
$ stty cols 400
Другой способ заключается в переводе SQLLine в режим построчного вывода с помощью следующей команды (для возврата в прежний режим используйте аргумент horizontal
):
!outputformat vertical
Кроме использования команд в среде SQLLine, можно проверить создание таблицы с помощью команд exists
и describe
в консоли Hbase shell. Проверьте наличие таблицы и заданных в ней семейств колонок:
exists 'articles'
Вывод на экран:
Table articles does exist Took 0.0453 seconds => true
describe 'articles'
Вывод на экран:
Table articles is ENABLED articles, {TABLE_ATTRIBUTES => {coprocessor$1 => '|org.apache.phoenix.coprocessor.ScanRegionObserver|805306366|', coprocessor$2 => '|org.apache.phoenix.coprocessor.UngroupedAggregateRegionObserver|805306366|', coprocessor$3 => '|org.apache.phoenix.coprocessor.GroupedAggregateRegionObserver|805306366|', coprocessor$4 => '|org.apache.phoenix.coprocessor.ServerCachingEndpointImpl|805306366|', coprocessor$5 => '|org.apache.phoenix.hbase.index.Indexer|805306366|index.builder=org.apache.phoenix.index.PhoenixIndexBuilder,org.apache.hadoop.hbase.index.codec.class=org.apache.phoenix.index.PhoenixIndexCodec'} COLUMN FAMILIES DESCRIPTION {NAME => 'basic', VERSIONS => '5', EVICT_BLOCKS_ON_CLOSE => 'false', NEW_VERSION_BEHAVIOR => 'false', KEEP_DELETED_CELLS => 'FALSE', CACHE_DATA_ON_WRITE => 'false', DATA_BLOCK_ENCODING => 'FAST_DIFF', TTL => 'FOREVER', MIN_VERSIONS => '0', REPLICATION_SCOPE => '0', BLOOMFILTER => 'NONE', CACHE_INDEX_ON_WRITE => 'false', IN_MEMORY => 'false', CACHE_BLOOMS_ON_WRITE => 'false', PREFETCH_BLOCKS_ON_OPEN => 'false', COMPRESSION => 'NONE', BLOCKCACHE => 'true', BLOCKSIZE => '65536'} {NAME => 'tags', VERSIONS => '5', EVICT_BLOCKS_ON_CLOSE => 'false', NEW_VERSION_BEHAVIOR => 'false', KEEP_DELETED_CELLS => 'FALSE', CACHE_DATA_ON_WRITE => 'false', DATA_BLOCK_ENCODING => 'FAST_DIFF', TTL => 'FOREVER', MIN_VERSIONS => '0', REPLICATION_SCOPE => '0', BLOOMFILTER => 'NONE', CACHE_INDEX_ON_WRITE => 'false', IN_MEMORY => 'false', CACHE_BLOOMS_ON_WRITE => 'false', PREFETCH_BLOCKS_ON_OPEN => 'false', COMPRESSION => 'NONE', BLOCKCACHE => 'true', BLOCKSIZE => '65536'} 2 row(s) Took 0.0356 seconds
Шаг 3. Добавление данных в таблицу
Для ввода новых данных в созданную таблицу используйте команду UPSERT. Она объединяет две команды SQL: INSERT
и UPDATE
. Если строка с указанным ключом уже есть, данные в ней будут обновлены, в противном случае вставится новая строка.
Следующая команда добавляет новые данные в строку с ключом article1
. Если названия колонок не указаны в круглых скобках сразу после названия таблицы, то приведенные значения появятся в колонках согласно порядку их объявления при создании таблицы. Значение для последней колонки не задано, поэтому она не будет сохранена в HBase.
РЕКОМЕНДАЦИЯ
Значения для колонок типа |
UPSERT INTO "articles" VALUES('article1', 'Test author', 'Test article', true, true, true);
Следующая команда добавляет новые данные для строки article2
. Названия колонок определены явно. HBase не сохранит пропущенные колонки в таблице.
UPSERT INTO "articles"("row_id", "basic"."author", "basic"."header", "tags"."ref") VALUES('article2', 'Test author2', 'Test article2', true);
Пример вывода на экран для каждой из команд:
1 row affected (0.018 seconds)
Шаг 4. Выбор данных из таблицы
Для выбора всех данных из таблицы используйте команду SELECT:
SELECT * FROM "articles";
Вывод на экран:
+-----------+---------------+----------------+-------+-----------+------------+-------+ | row_id | author | header | arch | concepts | tutorials | ref | +-----------+---------------+----------------+-------+-----------+------------+-------+ | article1 | Test author | Test article | true | true | true | | | article2 | Test author2 | Test article2 | | | | true | +-----------+---------------+----------------+-------+-----------+------------+-------+ 2 rows selected (0.06 seconds)
Можно применять различные конструкции для фильтрации, сортировки и группирования выходных данных: WHERE
, GROUP BY
, ORDER BY
и другие. Можно также использовать подзапросы.
Следующий запрос выбирает данные по значению первичного ключа:
SELECT * FROM "articles" WHERE "row_id" = 'article1';
Вывод на экран:
+-----------+--------------+---------------+-------+-----------+------------+------+ | row_id | author | header | arch | concepts | tutorials | ref | +-----------+--------------+---------------+-------+-----------+------------+------+ | article1 | Test author | Test article | true | true | true | | +-----------+--------------+---------------+-------+-----------+------------+------+ 1 row selected (0.038 seconds)
Можно также проверить содержимое таблицы с помощью команды scan
в консоли HBase shell:
scan 'articles'
На экране вы увидите добавленные значения. Кроме колонок, определенных пользователем, Phoenix также добавляет одну служебную колонку для каждой строки — в нашем примере с кодированным ключом \x00\x00\x00\x00
и кодированным значением x
. Эта колонка необходима для повышения производительности операций сканирования; не изменяйте ее.
РЕКОМЕНДАЦИЯ
Обратите внимание на то, что Phoenix кодирует данные. Поэтому не рекомендуется использовать HBase API для работы с этими данными, поскольку их необходимо декодировать. Вместо этого используйте Phoenix. |
ROW COLUMN+CELL article1 column=basic:\x00\x00\x00\x00, timestamp=1638285442663, value=x article1 column=basic:\x80\x0B, timestamp=1638285442663, value=Test author article1 column=basic:\x80\x0C, timestamp=1638285442663, value=Test article article1 column=tags:\x80\x0D, timestamp=1638285442663, value=\x01 article1 column=tags:\x80\x0E, timestamp=1638285442663, value=\x01 article1 column=tags:\x80\x0F, timestamp=1638285442663, value=\x01 article2 column=basic:\x00\x00\x00\x00, timestamp=1638285450913, value=x article2 column=basic:\x80\x0B, timestamp=1638285450913, value=Test author2 article2 column=basic:\x80\x0C, timestamp=1638285450913, value=Test article2 article2 column=tags:\x80\x10, timestamp=1638285450913, value=\x01 2 row(s) Took 0.0214 seconds
Шаг 5. Обновление данных в таблице
Для обновления данных в таблице применяйте ту же команду, что и для вставки новых данных, то есть UPSERT. Следующая команда обновляет значение колонки header
в строке с ключом article1
:
UPSERT INTO "articles"("row_id", "basic"."header") VALUES('article1', 'Test article. Version 2');
Вывод на экран:
1 row affected (0.015 seconds)
Для просмотра обновленных данных выполните команду SELECT
:
SELECT * FROM "articles";
Вывод на экран:
+-----------+---------------+--------------------------+-------+-----------+------------+-------+ | row_id | author | header | arch | concepts | tutorials | ref | +-----------+---------------+--------------------------+-------+-----------+------------+-------+ | article1 | Test author | Test article. Version 2 | true | true | true | | | article2 | Test author2 | Test article2 | | | | true | +-----------+---------------+--------------------------+-------+-----------+------------+-------+ 2 rows selected (0.041 seconds)
Проверьте это также в консоли HBase shell с помощью следующей команды:
scan 'articles'
Результат подтверждает предыдущие выводы:
ROW COLUMN+CELL article1 column=basic:\x00\x00\x00\x00, timestamp=1638285553403, value=x article1 column=basic:\x80\x0B, timestamp=1638285442663, value=Test author article1 column=basic:\x80\x0C, timestamp=1638285553403, value=Test article. Version 2 article1 column=tags:\x80\x0D, timestamp=1638285442663, value=\x01 article1 column=tags:\x80\x0E, timestamp=1638285442663, value=\x01 article1 column=tags:\x80\x0F, timestamp=1638285442663, value=\x01 article2 column=basic:\x00\x00\x00\x00, timestamp=1638285450913, value=x article2 column=basic:\x80\x0B, timestamp=1638285450913, value=Test author2 article2 column=basic:\x80\x0C, timestamp=1638285450913, value=Test article2 article2 column=tags:\x80\x10, timestamp=1638285450913, value=\x01 2 row(s) Took 0.0125 seconds
Напомним, что при создании таблицы мы потребовали хранение до пяти версий значений. Поэтому HBase сохраняет не только последнее значение, но и предыдущие тоже. Чтобы увидеть это, выполните в консоли HBase shell команду scan
с аргументом TIMERANGE
:
scan 'articles', {TIMERANGE => [1638285442663, 1638285450914]}
Вывод содержит версии значений в указанном диапазоне временных меток:
ROW COLUMN+CELL article1 column=basic:\x00\x00\x00\x00, timestamp=1638285442663, value=x article1 column=basic:\x80\x0B, timestamp=1638285442663, value=Test author article1 column=basic:\x80\x0C, timestamp=1638285442663, value=Test article article1 column=tags:\x80\x0D, timestamp=1638285442663, value=\x01 article1 column=tags:\x80\x0E, timestamp=1638285442663, value=\x01 article1 column=tags:\x80\x0F, timestamp=1638285442663, value=\x01 article2 column=basic:\x00\x00\x00\x00, timestamp=1638285450913, value=x article2 column=basic:\x80\x0B, timestamp=1638285450913, value=Test author2 article2 column=basic:\x80\x0C, timestamp=1638285450913, value=Test article2 article2 column=tags:\x80\x10, timestamp=1638285450913, value=\x01 2 row(s) Took 0.0112 seconds
Шаг 6. Удаление данных из таблицы
Для удаления данных из таблицы используйте команду DELETE. Следующая команда удаляет все строки таблицы, в которых значение колонки header
равно Test article. Version 2
.
DELETE FROM "articles" WHERE "basic"."header" = 'Test article. Version 2';
Вывод на экран:
1 row affected (0.018 seconds)
ВНИМАНИЕ
В отличие от HBase, в Phoenix команда |
С помощью команды SELECT
убедитесь, что в таблице осталась только одна строка:
SELECT * FROM "articles";
Вывод подтверждает, что первая строка удалена:
+-----------+---------------+----------------+-------+-----------+-------------+ | row_id | author | header | arch | concepts | tutorials | +-----------+---------------+----------------+-------+-----------+-------------+ | article2 | Test author2 | Test article2 | | | | +-----------+---------------+----------------+-------+-----------+-------------+ 1 row selected (0.042 seconds)
HBase shell показывает аналогичный результат:
scan 'articles'
Вывод на экран:
ROW COLUMN+CELL article2 column=basic:\x00\x00\x00\x00, timestamp=1638283068620, value=x article2 column=basic:\x80\x0B, timestamp=1638283068620, value=Test author2 article2 column=basic:\x80\x0C, timestamp=1638283068620, value=Test article2 article2 column=tags:\x80\x10, timestamp=1638283068620, value=\x01 1 row(s) Took 0.0101 seconds
Следующая команда удаляет строку с заданным значением главного ключа:
DELETE FROM "articles" WHERE "row_id" = 'article2';
После этой операции таблица стала пустой:
SELECT * FROM "articles";
Вывод на экран:
+---------+---------+---------+-------+-----------+------------+------+ | row_id | author | header | arch | concepts | tutorials | ref | +---------+---------+---------+-------+-----------+------------+------+ +---------+---------+---------+-------+-----------+------------+------+ No rows selected (0.027 seconds)
Шаг 7. Изменение структуры таблицы
Для изменения структуры таблицы используйте команду ALTER TABLE. Следующий запрос добавляет семейство колонок temp
и колонку review
внутри этого семейства:
ALTER TABLE "articles" ADD "temp"."review" VARCHAR VERSIONS=3;
Вывод на экран:
No rows affected (6.604 seconds)
Проверьте обновленную структуру с помощью команды !describe
в SQLLine:
!describe "articles"
Вывод на экран содержит информацию о новой колонке review
:
+------------+--------------+-------------+--------------+------------+------------+--------------+----------------+-----------------+-----------------+-----------+----------+-------------+----------+ | TABLE_CAT | TABLE_SCHEM | TABLE_NAME | COLUMN_NAME | DATA_TYPE | TYPE_NAME | COLUMN_SIZE | BUFFER_LENGTH | DECIMAL_DIGITS | NUM_PREC_RADIX | NULLABLE | REMARKS | COLUMN_DEF | SQL_DATA | +------------+--------------+-------------+--------------+------------+------------+--------------+----------------+-----------------+-----------------+-----------+----------+-------------+----------+ | | | articles | row_id | 12 | VARCHAR | null | null | null | null | 0 | | | null | | | | articles | author | 12 | VARCHAR | null | null | null | null | 1 | | | null | | | | articles | header | 12 | VARCHAR | null | null | null | null | 1 | | | null | | | | articles | arch | 16 | BOOLEAN | null | null | null | null | 1 | | | null | | | | articles | concepts | 16 | BOOLEAN | null | null | null | null | 1 | | | null | | | | articles | tutorials | 16 | BOOLEAN | null | null | null | null | 1 | | | null | | | | articles | ref | 16 | BOOLEAN | null | null | null | null | 1 | | | null | | | | articles | review | 12 | VARCHAR | null | null | null | null | 1 | | | null | +------------+--------------+-------------+--------------+------------+------------+--------------+----------------+-----------------+-----------------+-----------+----------+-------------+----------+
Для проверки добавления нового семейства колонок используйте команду describe
в HBase shell:
describe 'articles'
Вывод на экран:
Table articles is ENABLED articles, {TABLE_ATTRIBUTES => {coprocessor$1 => '|org.apache.phoenix.coprocessor.ScanRegionObserver|805306366|', coprocesso r$2 => '|org.apache.phoenix.coprocessor.UngroupedAggregateRegionObserver|805306366|', coprocessor$3 => '|org.apache.phoenix. coprocessor.GroupedAggregateRegionObserver|805306366|', coprocessor$4 => '|org.apache.phoenix.coprocessor.ServerCachingEndpo intImpl|805306366|', coprocessor$5 => '|org.apache.phoenix.hbase.index.Indexer|805306366|index.builder=org.apache.phoenix.in dex.PhoenixIndexBuilder,org.apache.hadoop.hbase.index.codec.class=org.apache.phoenix.index.PhoenixIndexCodec'} COLUMN FAMILIES DESCRIPTION {NAME => 'basic', VERSIONS => '5', EVICT_BLOCKS_ON_CLOSE => 'false', NEW_VERSION_BEHAVIOR => 'false', KEEP_DELETED_CELLS => 'FALSE', CACHE_DATA_ON_WRITE => 'false', DATA_BLOCK_ENCODING => 'FAST_DIFF', TTL => 'FOREVER', MIN_VERSIONS => '0', REPLICAT ION_SCOPE => '0', BLOOMFILTER => 'NONE', CACHE_INDEX_ON_WRITE => 'false', IN_MEMORY => 'false', CACHE_BLOOMS_ON_WRITE => 'fa lse', PREFETCH_BLOCKS_ON_OPEN => 'false', COMPRESSION => 'NONE', BLOCKCACHE => 'true', BLOCKSIZE => '65536'} {NAME => 'tags', VERSIONS => '5', EVICT_BLOCKS_ON_CLOSE => 'false', NEW_VERSION_BEHAVIOR => 'false', KEEP_DELETED_CELLS => ' FALSE', CACHE_DATA_ON_WRITE => 'false', DATA_BLOCK_ENCODING => 'FAST_DIFF', TTL => 'FOREVER', MIN_VERSIONS => '0', REPLICATI ON_SCOPE => '0', BLOOMFILTER => 'NONE', CACHE_INDEX_ON_WRITE => 'false', IN_MEMORY => 'false', CACHE_BLOOMS_ON_WRITE => 'fal se', PREFETCH_BLOCKS_ON_OPEN => 'false', COMPRESSION => 'NONE', BLOCKCACHE => 'true', BLOCKSIZE => '65536'} {NAME => 'temp', VERSIONS => '3', EVICT_BLOCKS_ON_CLOSE => 'false', NEW_VERSION_BEHAVIOR => 'false', KEEP_DELETED_CELLS => ' FALSE', CACHE_DATA_ON_WRITE => 'false', DATA_BLOCK_ENCODING => 'FAST_DIFF', TTL => 'FOREVER', MIN_VERSIONS => '0', REPLICATI ON_SCOPE => '0', BLOOMFILTER => 'NONE', CACHE_INDEX_ON_WRITE => 'false', IN_MEMORY => 'false', CACHE_BLOOMS_ON_WRITE => 'fal se', PREFETCH_BLOCKS_ON_OPEN => 'false', COMPRESSION => 'NONE', BLOCKCACHE => 'true', BLOCKSIZE => '65536'} 3 row(s) Took 0.0222 seconds
Шаг 8. Удаление таблицы
Для окончательного удаления таблицы используйте команду DROP TABLE:
DROP TABLE "articles";
Вывод на экран:
No rows affected (1.246 seconds)
Теперь команда !tables
покажет, что таблица articles
более не существует:
+------------+--------------+-------------+---------------+----------+------------+----------------------------+-----------------+--------------+-----------------+---------------+---------------+-----------------+------------+-------------+----------------+----------------------+--------------------+-----------------------+ | TABLE_CAT | TABLE_SCHEM | TABLE_NAME | TABLE_TYPE | REMARKS | TYPE_NAME | SELF_REFERENCING_COL_NAME | REF_GENERATION | INDEX_STATE | IMMUTABLE_ROWS | SALT_BUCKETS | MULTI_TENANT | VIEW_STATEMENT | VIEW_TYPE | INDEX_TYPE | TRANSACTIONAL | IS_NAMESPACE_MAPPED | GUIDE_POSTS_WIDTH | TRANSACTION_PROVIDER | +------------+--------------+-------------+---------------+----------+------------+----------------------------+-----------------+--------------+-----------------+---------------+---------------+-----------------+------------+-------------+----------------+----------------------+--------------------+-----------------------+ | | SYSTEM | CATALOG | SYSTEM TABLE | | | | | | false | null | false | | | | false | false | null | | | | SYSTEM | FUNCTION | SYSTEM TABLE | | | | | | false | null | false | | | | false | false | null | | | | SYSTEM | LOG | SYSTEM TABLE | | | | | | true | 32 | false | | | | false | false | null | | | | SYSTEM | SEQUENCE | SYSTEM TABLE | | | | | | false | null | false | | | | false | false | null | | | | SYSTEM | STATS | SYSTEM TABLE | | | | | | false | null | false | | | | false | false | null | | +------------+--------------+-------------+---------------+----------+------------+----------------------------+-----------------+--------------+-----------------+---------------+---------------+-----------------+------------+-------------+----------------+----------------------+--------------------+-----------------------+