[Перевод] database/sql биндинги для YDB в Go
YQL — это SQL-диалект, специфичный для базы данных YDB. YQL требует заранее объявлять имена и типы параметров запроса. Это обеспечивает высокую производительность и корректное поведение. В синтаксисе 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. Пробуйте и возвращайтесь с обратной связью!
Если у вас возникнут какие-либо трудности или вопросы, пожалуйста, не стесняйтесь обращаться к нам через: