From a70bce58478cf7bd43cc0ddd01373853c1ec15e2 Mon Sep 17 00:00:00 2001 From: KirillKurdyukov Date: Sun, 20 Sep 2026 22:48:49 +0300 Subject: [PATCH 1/3] Test YDB default write mode --- .../ydb/trino/TestYdbDefaultWriteMode.java | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 ydb-trino-adapter/src/test/java/tech/ydb/trino/TestYdbDefaultWriteMode.java diff --git a/ydb-trino-adapter/src/test/java/tech/ydb/trino/TestYdbDefaultWriteMode.java b/ydb-trino-adapter/src/test/java/tech/ydb/trino/TestYdbDefaultWriteMode.java new file mode 100644 index 00000000..46e2eb5b --- /dev/null +++ b/ydb-trino-adapter/src/test/java/tech/ydb/trino/TestYdbDefaultWriteMode.java @@ -0,0 +1,75 @@ +package tech.ydb.trino; + +import io.trino.testing.AbstractTestQueryFramework; +import io.trino.testing.QueryRunner; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import tech.ydb.test.junit5.YdbHelperExtension; + +import static org.assertj.core.api.Assertions.assertThat; + +public class TestYdbDefaultWriteMode extends AbstractTestQueryFramework { + @RegisterExtension + static final YdbHelperExtension ydb = new YdbHelperExtension(); + + @Override + protected QueryRunner createQueryRunner() throws Exception { + return YdbQueryRunner.builder(ydb) + .addConnectorProperty("insert.non-transactional-insert.enabled", "false") + .useProductionClient() + .build(); + } + + @Test + public void testCreateTableAsSelect() { + String tableName = "default_mode_ctas"; + String duplicateTableName = "default_mode_ctas_duplicate"; + try { + assertUpdate("CREATE TABLE " + tableName + " (id, payload) WITH (primary_key = ARRAY['id']) " + + "AS VALUES (BIGINT '1', 'a'), (BIGINT '2', 'b')", 2); + assertQuery("SELECT * FROM " + tableName, + "VALUES (CAST(1 AS BIGINT), 'a'), (CAST(2 AS BIGINT), 'b')"); + + var tablesBefore = computeActual("SHOW TABLES").getOnlyColumnAsSet(); + assertThat(query("CREATE TABLE " + duplicateTableName + " (id, payload) " + + "WITH (primary_key = ARRAY['id']) AS VALUES (BIGINT '1', 'a'), (BIGINT '1', 'b')")) + .failure(); + assertThat(computeActual("SHOW TABLES").getOnlyColumnAsSet()).isEqualTo(tablesBefore); + assertThat(getQueryRunner().tableExists(getSession(), duplicateTableName)).isFalse(); + } + finally { + assertUpdate("DROP TABLE IF EXISTS " + tableName); + assertUpdate("DROP TABLE IF EXISTS " + duplicateTableName); + } + } + + @Test + public void testInsert() { + String tableName = "default_mode_insert"; + try { + assertUpdate("CREATE TABLE " + tableName + " (id, payload) WITH (primary_key = ARRAY['id']) " + + "AS VALUES (BIGINT '1', 'before')", 1); + var tablesBefore = computeActual("SHOW TABLES").getOnlyColumnAsSet(); + + try { + assertUpdate("INSERT INTO " + tableName + " VALUES (BIGINT '2', 'after')", 1); + assertQuery("SELECT * FROM " + tableName, + "VALUES (CAST(1 AS BIGINT), 'before'), (CAST(2 AS BIGINT), 'after')"); + } + catch (RuntimeException | AssertionError failure) { + try { + assertQuery("SELECT * FROM " + tableName, "VALUES (CAST(1 AS BIGINT), 'before')"); + assertThat(computeActual("SHOW TABLES").getOnlyColumnAsSet()).isEqualTo(tablesBefore); + } + catch (RuntimeException | AssertionError verificationFailure) { + failure.addSuppressed(verificationFailure); + } + throw failure; + } + assertThat(computeActual("SHOW TABLES").getOnlyColumnAsSet()).isEqualTo(tablesBefore); + } + finally { + assertUpdate("DROP TABLE IF EXISTS " + tableName); + } + } +} From fe237cf77784a4f366d120fa026dbd8f627b3f1c Mon Sep 17 00:00:00 2001 From: KirillKurdyukov Date: Tue, 29 Sep 2026 15:59:09 +0300 Subject: [PATCH 2/3] Document default YDB insert staging limitation --- ydb-trino-adapter/README.md | 14 +++++++--- .../ydb/trino/TestYdbDefaultWriteMode.java | 27 ++++++++----------- 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/ydb-trino-adapter/README.md b/ydb-trino-adapter/README.md index e4aad3c5..67b8a0f3 100644 --- a/ydb-trino-adapter/README.md +++ b/ydb-trino-adapter/README.md @@ -60,9 +60,17 @@ AS SELECT tenant, event_id, payload FROM events; ``` Имена ключей в CTAS относятся к выходным столбцам запроса. Отсутствующий, -пустой, повторяющийся или неизвестный `primary_key` отклоняется. Текущая -integration-проверка CTAS использует `insert.non-transactional-insert.enabled=true`; -transactional staging этой проверкой не подтверждается. +пустой, повторяющийся или неизвестный `primary_key` отклоняется. CTAS адаптера +работает при обоих значениях `insert.non-transactional-insert.enabled`. + +При значении `false` (по умолчанию в Trino 483) обычный `INSERT` не работает: +Trino пытается создать промежуточную таблицу запросом `CREATE TABLE tmp AS SELECT +... WHERE 0 = 1`, который YDB отклоняет на `AS`. Эта форма не соответствует +[синтаксису YQL CTAS](https://ydb.tech/docs/en/yql/reference/syntax/create_table/as_select): +он требует первичный ключ и поддерживается для колоночных таблиц. Для `INSERT` +задайте `insert.non-transactional-insert.enabled=true` в свойствах каталога. +Тогда Trino пишет непосредственно в целевую таблицу; при сбое запись может +остаться частичной. ## Текст и байты diff --git a/ydb-trino-adapter/src/test/java/tech/ydb/trino/TestYdbDefaultWriteMode.java b/ydb-trino-adapter/src/test/java/tech/ydb/trino/TestYdbDefaultWriteMode.java index 46e2eb5b..ca0ef63d 100644 --- a/ydb-trino-adapter/src/test/java/tech/ydb/trino/TestYdbDefaultWriteMode.java +++ b/ydb-trino-adapter/src/test/java/tech/ydb/trino/TestYdbDefaultWriteMode.java @@ -44,28 +44,23 @@ public void testCreateTableAsSelect() { } @Test - public void testInsert() { + public void testInsertRequiresUnsupportedStagingTable() { String tableName = "default_mode_insert"; try { assertUpdate("CREATE TABLE " + tableName + " (id, payload) WITH (primary_key = ARRAY['id']) " + "AS VALUES (BIGINT '1', 'before')", 1); var tablesBefore = computeActual("SHOW TABLES").getOnlyColumnAsSet(); - try { - assertUpdate("INSERT INTO " + tableName + " VALUES (BIGINT '2', 'after')", 1); - assertQuery("SELECT * FROM " + tableName, - "VALUES (CAST(1 AS BIGINT), 'before'), (CAST(2 AS BIGINT), 'after')"); - } - catch (RuntimeException | AssertionError failure) { - try { - assertQuery("SELECT * FROM " + tableName, "VALUES (CAST(1 AS BIGINT), 'before')"); - assertThat(computeActual("SHOW TABLES").getOnlyColumnAsSet()).isEqualTo(tablesBefore); - } - catch (RuntimeException | AssertionError verificationFailure) { - failure.addSuppressed(verificationFailure); - } - throw failure; - } + assertThat(query("INSERT INTO " + tableName + " VALUES (BIGINT '2', 'after')")) + .failure() + .hasMessageMatching("(?s)Cannot call 'SCHEME_QUERY >>\\s*CREATE TABLE `tmp_trino_[^`]+` " + + "AS SELECT `id`, `payload` FROM `default_mode_insert` WHERE 0 = 1' " + + "with Status\\{code = GENERIC_ERROR\\(code=400080\\), issues = " + + "\\[\\d+:\\d+ - \\d+:\\d+: missing '\\(' at 'AS' \\(S_ERROR\\), " + + "\\d+:\\d+ - \\d+:\\d+: mismatched input 'SELECT' expecting " + + "\\{',', '\\)'\\} \\(S_ERROR\\)\\]\\}"); + + assertQuery("SELECT * FROM " + tableName, "VALUES (CAST(1 AS BIGINT), 'before')"); assertThat(computeActual("SHOW TABLES").getOnlyColumnAsSet()).isEqualTo(tablesBefore); } finally { From 23238991856156897f33107258e8d549c9cb7364 Mon Sep 17 00:00:00 2001 From: KirillKurdyukov Date: Tue, 29 Sep 2026 16:40:10 +0300 Subject: [PATCH 3/3] Remove default insert diagnostic test --- .../ydb/trino/TestYdbDefaultWriteMode.java | 25 ------------------- 1 file changed, 25 deletions(-) diff --git a/ydb-trino-adapter/src/test/java/tech/ydb/trino/TestYdbDefaultWriteMode.java b/ydb-trino-adapter/src/test/java/tech/ydb/trino/TestYdbDefaultWriteMode.java index ca0ef63d..45f0651a 100644 --- a/ydb-trino-adapter/src/test/java/tech/ydb/trino/TestYdbDefaultWriteMode.java +++ b/ydb-trino-adapter/src/test/java/tech/ydb/trino/TestYdbDefaultWriteMode.java @@ -42,29 +42,4 @@ public void testCreateTableAsSelect() { assertUpdate("DROP TABLE IF EXISTS " + duplicateTableName); } } - - @Test - public void testInsertRequiresUnsupportedStagingTable() { - String tableName = "default_mode_insert"; - try { - assertUpdate("CREATE TABLE " + tableName + " (id, payload) WITH (primary_key = ARRAY['id']) " + - "AS VALUES (BIGINT '1', 'before')", 1); - var tablesBefore = computeActual("SHOW TABLES").getOnlyColumnAsSet(); - - assertThat(query("INSERT INTO " + tableName + " VALUES (BIGINT '2', 'after')")) - .failure() - .hasMessageMatching("(?s)Cannot call 'SCHEME_QUERY >>\\s*CREATE TABLE `tmp_trino_[^`]+` " + - "AS SELECT `id`, `payload` FROM `default_mode_insert` WHERE 0 = 1' " + - "with Status\\{code = GENERIC_ERROR\\(code=400080\\), issues = " + - "\\[\\d+:\\d+ - \\d+:\\d+: missing '\\(' at 'AS' \\(S_ERROR\\), " + - "\\d+:\\d+ - \\d+:\\d+: mismatched input 'SELECT' expecting " + - "\\{',', '\\)'\\} \\(S_ERROR\\)\\]\\}"); - - assertQuery("SELECT * FROM " + tableName, "VALUES (CAST(1 AS BIGINT), 'before')"); - assertThat(computeActual("SHOW TABLES").getOnlyColumnAsSet()).isEqualTo(tablesBefore); - } - finally { - assertUpdate("DROP TABLE IF EXISTS " + tableName); - } - } }