database/sql биндинги для YDB в Go

от автора

YQL — это SQL-диалект, специфичный для базы данных YDBYQL требует заранее объявлять имена и типы параметров запроса. Это обеспечивает высокую производительность и корректное поведение. В синтаксисе YQL параметры необходимо перечислять явно с помощью инструкции DECLARE. И этот нюанс YDB может быть неожиданным для пользователей традиционных баз данных.

Кроме того, поскольку таблицы YDB находятся в структуре, подобной виртуальной файловой системе, их имена могут быть довольно длинными. Существует PRAGMA TablePathPrefix, которая может охватить остальную часть запроса внутри заданного префикса, упрощая имена таблиц. Например, запрос к таблице «/local/path/to/tables/seasons» может выглядеть следующим образом:

DECLARE $title AS Text; DECLARE $views AS Uint64; SELECT season_id FROM `/local/path/to/tables/seasons` WHERE title LIKE $title AND views > $views;

Используя инструкцию PRAGMA, вы можете упростить префиксную часть в названии всех таблиц, участвующих в YQL-запросе:

PRAGMA TablePathPrefix(“/local/path/to/tables/”); DECLARE $title AS Text; DECLARE $views AS Uint64; SELECT season_id  FROM seasons  WHERE title LIKE $title AND views > $views;

Также YQL поддерживает только именованные параметры запроса. Это означает, что вы не можете использовать привычные нумерованные (с использованием плейсхолдеров типа «$1”“$2”“$3” и т.п.) или позиционные (с использованием плейсхолдеров “?”) параметры запроса. Это может стать неожиданностью для пользователей, привыкших к синтаксису PostgreSQL или MySQL (или другой СУБД).

Как YDB Go SDK может помочь вам упростить такие запросы?

database/sql драйвер для YDB (часть YDB Go SDK) поддерживает биндинги (обогащение) запросов для:

  • указания PRAGMA TablePathPrefix

  • авто-выведения DECLARE для типов параметров

  • нумерованных или позиционных параметров запросов

Биндинги запросов могут быть явно включены на этапе инициализации драйвера с помощью опций коннектора или параметра строки подключения. По умолчанию database/sql драйвер для YDB не изменяет запросы.

Следующий пример (без включения биндингов запросов) демонстрирует работу с типами YDB явным образом:

package main   import (   "context"   "database/sql"    "github.com/ydb-platform/ydb-go-sdk/v3"   "github.com/ydb-platform/ydb-go-sdk/v3/table"   "github.com/ydb-platform/ydb-go-sdk/v3/table/types" )  func main() {   db := sql.Open("ydb", "grpc://localhost:2136/local")   defer db.Close()   row := db.QueryRowContext(context.TODO(), `     PRAGMA TablePathPrefix("/local/path/to/my/folder");     DECLARE $p0 AS Int32;     DECLARE $p1 AS Utf8;     SELECT $p0, $p1`,      sql.Named("$p0", 42),      table.ValueParam("$p1", types.TextValue("my string")),   )   ... }

Как вы можете видеть, в этом примере также требовался импорт пакетов ydb-go-sdk и непосредственная работа с ними.

С включенными биндингами тот же результат может быть достигнут намного проще:

  • через параметры строку подключения:

package main   import (   "context"   "database/sql"    // anonymous import for registering driver   _ "github.com/ydb-platform/ydb-go-sdk/v3"  )  func main() {   var (     ctx = context.TODO()     db = sql.Open("ydb", "grpc://localhost:2136/local?"+            "go_auto_bind="+               "table_path_prefix(/local/path/to/my/folder),"+               "declare,"+               "positional"     )   )   // cleanup resources on exit from func   defer db.Close()   // query with positional args   row := db.QueryRowContext(ctx, `SELECT ?, ?`, 42, "my string")   ... }
  • через опции коннектора:

package main   import (   "context"   "database/sql"    "github.com/ydb-platform/ydb-go-sdk/v3"  )  func main() {   var (     ctx = context.TODO()     nativeDriver = ydb.MustOpen(ctx, "grpc://localhost:2136/local")     db = sql.OpenDB(ydb.MustConnector(nativeDriver,       // bind pragma TablePathPrefix       ydb.WithTablePathPrefix("/local/path/to/my/folder"),       // bind parameters declare       ydb.WithAutoDeclare(),       // bind positional args       ydb.WithPositionalArgs(),     ))   )   // cleanup resources on exit from func   defer nativeDriver.Close(ctx)    defer db.Close()   // query with positional args   row := db.QueryRowContext(ctx, `SELECT ?, ?`, 42, "my string")   ... }

В обоих случаях исходный простой запрос

SELECT ?, ?

на стороне драйвера будет обогащен до следующего:

-- bind TablePathPrefix  PRAGMA TablePathPrefix("/local/path/to/my/folder");  -- bind declares DECLARE $p0 AS Int32; DECLARE $p1 AS Utf8;  -- origin query with positional args replacement SELECT $p0, $p1

Этот обогащенный запрос будет отправлен в YDB вместо исходного.

Порядок объявления привязок (через параметры строки подключения или через опции коннектора) определяет порядок, в котором биндинги отрабатывают на стороне драйвера.

Дополнительные примеры включения биндингов запросов смотрите в документации ydb-go-sdk:

1) обогащение запроса прагмой TablePathPrefix:

2) авто-выведение типов параметров (DECLARE):

3) биндинг позиционных параметров:

4) биндинг нумерованных параметров:

Для глубокого понимания биндингов запросов смотрите также unit-тесты в ydb-go-sdk

Вы можете написать свои собственные unit-тесты для проверки корректности работы биндингов над вашими запросами следующим образом:

import (   "testing"    "github.com/stretchr/testify/require"    "github.com/ydb-platform/ydb-go-sdk/v3"   "github.com/ydb-platform/ydb-go-sdk/v3/table"   "github.com/ydb-platform/ydb-go-sdk/v3/testutil" )  func TestBinding(t *testing.T) {    binder := testutil.QueryBind(     // bind pragma TablePathPrefix     ydb.WithTablePathPrefix("/local/path/to/my/folder"),     // bind parameters declare     ydb.WithAutoDeclare(),     // auto-replace positional args     ydb.WithPositionalArgs(),   )   query, params, err := binder.RewriteQuery(     "SELECT ?, ?, ?",      1,      uint64(2),      "3",   )   require.NoError(t, err)   require.Equal(t, `-- bind TablePathPrefix PRAGMA TablePathPrefix(“/local/path/to/my/folder”);  -- bind declares DECLARE $p0 AS Int32; DECLARE $p1 AS Uint64; DECLARE $p2 AS Utf8;  -- origin query with positional args replacement SELECT $p0, $p1, $p2`,      query,   )   require.Equal(t,      table.NewQueryParameters(       table.ValueParam("$p0", types.Int32Value(1)),       table.ValueParam("$p1", types.Uint64Value(2)),       table.ValueParam("$p2", types.TextValue("3")),     ),      params,   ) }

Биндинги запросов в database/sql драйвере для YDB доступны начиная с версии v3.44.0 ydb-go-sdk. Пробуйте и возвращайтесь с обратной связью!

Если у вас возникнут какие-либо трудности или вопросы, пожалуйста, не стесняйтесь обращаться к нам через:


ссылка на оригинал статьи https://habr.com/ru/articles/747490/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *