Лексическая структура
Запрос на языке YQL представляет собой валидный UTF-8 текст, который состоит из команд (statement) разделенных символом точка с запятой (;
).
Последняя точка с запятой при этом может отсутствовать.
Каждая команда, в свою очередь, состоит из последовательности токенов допустимых для данной команды.
Токеном может быть ключевое слово, идентификатор, литерал и другие.
Токены разделяются пробельными символами (пробел, табуляция, перевод строки) либо комментариями. Комментарий не является частью команды и синтаксически эквивалентен пробельному символу.
Режимы совместимости синтаксиса
Поддерживаются два режима совместимости синтаксиса:
- Расширенный C++ (по умолчанию)
- ANSI SQL
Режим ANSI SQL включается с помощью специального комментария --!ansi-lexer
, который должен стоять в начале запроса.
Особенности интерпретации лексических элементов в разных режимах совместимости описаны ниже.
Комментарии
Поддерживаются следующие виды комментариев:
- Однострочные: начинается с последовательности символов
--
(два минуса подряд) и продолжается до конца строки - Многострочные: начинается с последовательности символов
/*
и заканчивается на последовательности символов*/
SELECT 1; -- A single-line comment
/*
Some multi-line comment
*/
В режиме совместимости синтаксиса C++ (по умолчанию) многострочный комментарий заканчивается на ближайшей последовательности символов */
.
В режиме совместимости синтаксиса ANSI SQL учитывается вложенность многострочных комментариев:
--!ansi_lexer
SELECT * FROM T; /* комментарий /* вложенный комментарий, без ansi_lexer будет ошибка */ */
Ключевые слова и идентификаторы
Ключевые слова – это токены, имеющее фиксированное значение в языке YQL. Примеры ключевых слов – SELECT
, INSERT
, FROM
, ACTION
и т.д. Ключевые слова регистронезависимы, то есть SELECT
и SeLEcT
эквивалентны.
Список ключевых слов не фиксирован – по мере развития языка он будет расширяться. Ключевое слово не может содержать цифры и начинаться или заканчиваться символом подчеркивания.
Идентификаторы – это токены, которые идентифицируют имена таблиц, колонок и других объектов в YQL. Идентификаторы в YQL всегда регистрозависимы.
Идентификатор может быть записан в теле программы без специального оформления, если он:
- Не является ключевым словом
- Начинается с латинской буквы или подчеркивания
- Последующими символами могут быть латинская буква, подчеркивание или цифра
SELECT my_column FROM my_table; -- my_column and my_table are identifiers
Для записи в теле запроса произвольного идентификатора он заключается в обратные кавычки (бэктики):
SELECT `column with space` from T;
SELECT * FROM `my_dir/my_table`
Идентификатор в обратных кавычках никогда не интерпретируется как ключевое слово:
SELECT `select` FROM T; -- select - имя колонки в таблице T
При использовании обратных кавычек применим стандартный C-эскейпинг:
SELECT 1 as `column with\n newline, \x0a newline and \` backtick `;
В режиме совместимости синтаксиса ANSI SQL произвольные идентификаторы также могут быть выделены заключением их в двойные кавычки. Для включения двойной кавычки в идентификатор в кавычках она должна быть удвоена:
--!ansi_lexer
SELECT 1 as "column with "" double quoute"; -- имя колонки будет: column with " double quoute
SQL хинты
SQL хинты – это специальные настройки, которые позволяют пользователю влиять на план выполнения запроса
(например, включать/выключать определенные оптимизации, форсировать стратегию JOIN-а и т.п.).
В отличие от PRAGMA, SQL хинты обладают локальным действием – они привязаны к определенной точке YQL запроса (обычно следуют после ключевого слова)
и влияют только на соответствующий statement или даже его часть.
SQL хинты представляют собой набор настроек "имя-список значений" и задаются внутри комментариев специального вида –
первым символом комментария с SQL хинтами должен быть +
:
--+ Name1(Value1 Value2 Value3) Name2(Value4) ...
Имя SQL хинта должно состоять из алфавтно-цифровых ASCII символов и начинаться с буквы. Регистр букв в имени хинта игнорируется.
После имени хинта в скобках задается произвольное количество значений, разделенных пробелами. В качестве значения может выступать произвольный набор символов.
Если в наборе символов значения имеется пробел или скобка, то необходимо использовать одинарные кавычки:
--+ foo('value with space and paren)')
--+ foo('value1' value2)
-- эквивалетно
--+ foo(value1 value2)
Одинарную кавычку внутри значения необходимо эскейпить путем дублирования:
--+ foo('value with single quote '' inside')
Неизвестные имена SQL хинтов (либо синтаксически некорректные хинты) никогда не вызывают ошибок – они просто игнорируется:
--+ foo(value1) bar(value2 baz(value3)
-- из-за пропущенной закрывающей скобки в bar эквивалетно
--+ foo(value1)
Такое поведение связано с нежеланием ломать написанные ранее валидные YQL запросы с комментариями, которые похожи на хинты.
При этом синтаксически корректные SQL хинты в неожиданном для YQL месте вызывают предупреждение:
-- в данный момент хинты после SELECT не поддерживаются
SELECT /*+ foo(123) */ 1; -- предупреждение 'Hint foo will not be used'
Хочется заметить, что SQL хинты – это именно подсказки оптимизатору, поэтому:
- хинты никогда не влияют на результат запроса
- по мере развития оптимизаторов в YQL вполне возможна ситуация, в которой хинт становится неактуальным и начнет игнорироваться (например, полностью поменялся алгоритм, который настраивался данным хинтом, либо оптимизатор настолько улучшился, что гарантированно выбирает оптимальное решение, поэтому какие-то ручные настройки будут скорее вредить)
Строковые литералы
Строковый литерал (константа) записывается как последовательность символов, заключенных в одинарные кавычки. Внутри строкового литерала можно использовать правила эскейпинга в стиле C:
SELECT 'string with\n newline, \x0a newline and \' backtick ';
В режиме совместимости синтаксиса С++ (по-умолчанию) разрешается использовать вместо одинарных кавычек двойные:
SELECT "string with\n newline, \x0a newline and \" backtick ";
В режиме совместимости синтаксиса ASNI SQL двойные кавычки используются для идентификаторов, а единственный вид эскепинга который действует для строковых литералов – это дублирование символа одиночной кавычки:
--!ansi_lexer
SELECT 'string with '' quote'; -- результат: string with ' quote
На основании строковых литералов могут быть получены литералы простых типов.
Многострочные строковые литералы
Многострочный строковой литерал записывается в виде произвольного набора символов между двойными собачками @@
:
$text = @@some
multiline
text@@;
SELECT LENGTH($text);
Если необходимо вставить в текст двойную собачку, ее необходимо удвоить:
$text = @@some
multiline with double at: @@@@
text@@;
SELECT $text;
Типизированные строковые литералы
-
Для строкового литерала, включая многострочный, по умолчанию используется тип
String
(см. также PRAGMA UnicodeLiterals). -
С помощью следующих суффиксов можно явно управлять типом литерала:
s
—String
;u
—Utf8
;y
—Yson
;j
—Json
.
Пример
SELECT "foo"u, '[1;2]'y, @@{"a":null}@@j;
Числовые литералы
-
Целочисленные литералы по умолчанию имеют тип
Int32
, если попадают в его диапазон, и в противном случае автоматически расширяются доInt64
. -
С помощью следующих суффиксов можно явно управлять типом литерала:
l
—Int64
;s
—Int16
;t
—Int8
.
-
Добавление суффикса
u
превращает тип в соответствующий беззнаковый:ul
—Uint64
;u
—Uint32
;us
—Uint16
;ut
—Uint8
.
-
Также для целочисленных литералов доступна запись в шестнадцатеричной, восьмеричной и двоичной форме с помощью префиксов
0x
,0o
и0b
, соответственно. Их можно произвольным образом комбинировать с описанными выше суффиксами. -
Литералы с плавающей точкой по умолчанию имеют тип
Double
, но с помощью суффиксаf
его можно сузить доFloat
.
SELECT
123l AS `Int64`,
0b01u AS `Uint32`,
0xfful AS `Uint64`,
0o7ut AS `Uint8`,
456s AS `Int16`,
1.2345f AS `Float`;
Литералы PostgreSQL
Строковые и числовые литералы Pg типов можно создавать с помощью специальных суффиксов (аналогично простым строковым и числовым литералам).
Целочисленные литералы
Суффикс | Тип | Комментарий |
---|---|---|
p |
PgInt4 |
32-битное знаковое целое (в PostgreSQL нет беззнаковых типов) |
ps |
PgInt2 |
16-битное знаковое целое |
pi |
PgInt4 |
|
pb |
PgInt8 |
64-битное знаковое цело |
pn |
PgNumeric |
знаковое целое произвольной точности (до 131072 цифр) |
Литералы с плавающей точкой
Суффикс | Тип | Комментарий |
---|---|---|
p |
PgFloat8 |
число с плавающей точкой (64 бит double) |
pf4 |
PgFloat4 |
число с плавающей точкой (32 бит float) |
pf8 |
PgFloat8 |
|
pn |
PgNumeric |
число с плавающей точкой произвольной точности (до 131072 цифр перед запятой, до 16383 цифр после запятой) |
Строковые литералы
Суффикс | Тип | Комментарий |
---|---|---|
p |
PgText |
текстовая строка |
pt |
PgText |
|
pv |
PgVarchar |
текстовая строка |
pb |
PgBytea |
бинарная строка |
Внимание
Значения строковых/числовых литералов (т.е. то что идет перед суффиксом) должны быть валидной строкой/числом с точки зрения YQL.
В частности, должны соблюдаться правила эскейпинга YQL, а не PostgreSQL.
Пример:
SELECT
1234p, -- pgint4
0x123pb, -- pgint8
"тест"pt, -- pgtext
123e-1000pn; -- pgnumeric
;
Литерал массива
Для построения литерала массива используется функция PgArray
:
SELECT
PgArray(1p, NULL ,2p) -- {1,NULL,2}, тип _int4
;
Конструктор литералов произвольного типа
Литералы всех типов (в том числе и Pg типов) могут создаваться с помощью конструктора литералов со следующей сигнатурой:
Имя_типа(<строковая константа>)
.
Напрмер:
DECLARE $foo AS String;
SELECT
PgInt4("1234"), -- то же что и 1234p
PgInt4(1234), -- в качестве аргумента можно использовать литеральные константы
PgInt4($foo), -- и declare параметры
PgBool(true),
PgInt8(1234),
PgDate("1932-01-07"),
;
Также поддерживается встроенная функция PgConst
со следующей сигнатурой: PgConst(<строковое значение>, <тип>)
.
Такой способ более удобен для кодогенерации.
Например:
SELECT
PgConst("1234", PgInt4), -- то же что и 1234p
PgConst("true", PgBool)
;
Для некоторых типов в функции PgConst
можно указать дополнительные модификаторы. Возможные модификаторы для типа pginterval
перечислены в документации PostgreSQL.
SELECT
PgConst(90, pginterval, "day"), -- 90 days
PgConst(13.45, pgnumeric, 10, 1); -- 13.5
;