Для SQL пикодаты была реализована более продвинутая система типов. В новой системе были переработаны
ошибки и был реализован механизм вывода типов параметров.

Вывод типов параметров важен с точки зрения совместимости с PostgreSQL; он позволяет исполнять
через pgproto существенно больше запросов без правок. Раньше в параметризованных запросах было
необходимо указывать типы явно. Технически, клиентский драйвер может сам или с помощью
разрабочика указать типы при подготовке запроса, но многие драйверы полагаются на вывод
типов. В подобных случаях типы параметров приходилось указывать в SQL через CAST параметра
к нужному типу. С новой системой для большинства запросов в этом пропадает необходимость, но
возможность указать типы явно через CAST сохранилась. Теперь типы параметров определяются
согласно контексту. Например, в выражении `int_col = $1` типом параметра будет `int`.

Для большинства выражений определить типы параметров можно согласно следующим принципам:
- В процессе вывода типы распространяются по запросу слева направо. В результате обхода
  каждого выражения типы обновляются и переиспользуются при анализе последующих выражений.
- Для простых выражений тип параметра определяется согласно типу соседнего
  выражения или типу ожидаемого аргумента функции.
- В случае отсутствия контекста, параметр приобретает тип по умолчанию — `text`.

Подобные правила существуют в [PostgreSQL](https://www.postgresql.org/docs/17/typeconv.html).

Примеры:

1. `SELECT 1 + $1` -- параметр имеет тип `int` согласно типу соседнего операнда.
2. `SELECT upper($1)` -- параметр имеет `text`, так как функция upper принимает аргумент типа `text`.
3. `SELECT COALESCE(int_col, $1) FROM t` -- параметр имеет тип `int` согласно типу колонки.
4. `SELECT $1` -- тип параметра `text`, так контекст для вывода типа отсутствует.
5. `SELECT $1::int` -- тип параметра `int`, так как каст явно задает тип параметра.
6. `SELECT $1, $2 FROM t WHERE $1 = 1.5` -- параметры имеют типы `numeric` и `text`.
   Тип параметра `$1` определяется из фильтра, это является примером нарушения правила слева направо.
   В проекции для `$1` тип будет уже определен, а для `$2` будет применен тип по умолчанию.
7. `SELECT $1 + 1, $1 + 1.5` -- тип параметра `int`, он определяется согласно левому выражению и переиспользуется в правом.

Поддерживаются и более сложные сценарии, например, в запросе `SELECT 1 + ($1 + $2)`
параметры будут иметь тип `int`, хотя параметры сами являются соседями друг друга и
не задают тип. На самом деле, информация о типах распространяется по всему выражению,
а не только на соседей. Поэтому целочисленный литерал заставляет систему выбрать такую
перегрузку для оператора `+`, чтобы результат имел тип `int`, и согласно этой перегрузке
определяются типы параметров.

Это позволяет добиться более стабильного вывода; например, в выражениях `1 + $1 + 1.5` и
`1.5 + $1 + 1` тип параметра будет всегда `numeric` и не зависит от ассоциативности операторов.
Более мощный (т.е. вмещающий больше значений) тип в контексте распространяется на типы параметров.

Контекст для вывода типов может задаваться не только выражениями. Например,
при вставке строк в таблицу типы колонок могут определять типы параметров.

Примеры:

1. `UPDATE t SET int_col = $1 + $2` -- параметры будут иметь тип `int`, так как колонка имеет тип `int`.
2. `INSERT INTO t (int_col, datetime_col) VALUES ($1, $2)` -- параметры будут иметь тип `int` и `datetime` согласно типам колонок.

Типы параметров не всегда можно однозначно определить. Система
**не пытается разрешать неоднозначности**, это должен делать пользователь.
Обобщенный рецепт — использовать касты, явно фиксирующие типы параметров.


Например, `SELECT $1::int = $1::double` -- касты диктуют различные типы параметров,
в результате происходит ошибка, предлагающая рецепт решения с помощью явного
указания типа через CAST:

```
inconsistent types int and double deduced for parameter $1, consider using transitive type casts through a common type, e.g. $1::int::double and $1::int.
```

Исправленный запрос выглядит так: `SELECT $1::int::double = $1::int`,
тип параметра явно задан через каст.

Еще одно нововведение — строковые литералы могут неявно приводиться согласно контексту.
Самый простой пример: в `SELECT 1 + '1'` строка станет целочисленным литералом,
делая запрос валидным. Это позволяет избегать явных кастов, в том числе для
не скалярных типов, для которых нет естественного представления в sql.

Например, при сравнении с колонкой типа `datetime` или вставке значение можно задать в виде строки:

```sql
INSERT INTO t (datetime_col) VALUES ('2023-07-07T12:34:56Z')
```

будет эквивалентно

```sql
INSERT INTO t (datetime_col) VALUES ('2023-07-07T12:34:56Z'::datetime)
```

Если значение не является валидным представлением более конкретного типа, то происходит ошибка парсинга:

```sql
SELECT 1 + 'not a number'
-- failed to parse 'not a number' as a value of type unsigned, consider using explicit type casts
```

Новая система способна определять множество ошибок, связанных с типами, на этапе парсинга.
Например, раньше сравнение значений несовместимых типов `unsigned` и `text` приводило к ошибке исполнения:

```
sbroad: failed to create tarantool: Tarantool(BoxError { code: 171, message: Some("Type mismatch: can not convert string(''kek'') to number"), error_type: Some("ClientError"), errno: None, file: Some("./src/box/sql/mem.c"), line: Some(2784), fields: {}, cause: None })
```

Теперь ошибка стала более дружелюбной:

```
sbroad: could not resolve operator overload for <(unsigned, text)
```

Примеры:

1. `SELECT int_col + text_col` -- "could not resolve operator overload for +(int, text)"
2. `SELECT COALESCE(int_col, text_col)` -- "COALESCE types int and text cannot be matched"
3. `VALUES (1, 2), (2, false)` -- "VALUES types unsigned and bool cannot be matched"
4. `SELECT CASE WHEN true THEN 1 ELSE false END` -- "CASE/THEN types unsigned and bool cannot be matched"
5. `UPDATE t SET numeric_col = false` -- "column "numeric_col" is of type numeric, but expression is of type bool"
6. `INSERT INTO t (int_col, numeric_col) VALUES (false, 1)` -- "INSERT column at position 1 is of type int, but expression is of type bool"
