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