55.10. Сводка изменений по сравнению с протоколом версии 2.0
В этом разделе представлен краткий список изменений к сведению разработчиков, желающих модернизировать существующие клиентские библиотеки до протокола 3.0.
В начальном стартовом пакете вместо фиксированного формата применяется гибкий формат списка строк. Заметьте, что теперь сеансовые значения по умолчанию для параметров времени выполнения можно задать непосредственно в стартовом пакете. (Вообще, это можно было делать и раньше, используя поле options
, но из-за ограниченного размера options
и невозможности задавать значения с пробелами, это вариант был не очень безопасным.)
Во всех сообщениях непосредственно за байтом типа сообщения следует счётчик длины (за исключением стартовых пакетов, в которых нет байта типа). Также заметьте, что байт типа теперь есть в сообщении PasswordMessage.
Сообщения ErrorResponse и NoticeResponse ('E
' и 'N
') могут содержать несколько полей, из которых клиентский код может собрать сообщение об ошибке желаемого уровня детализации. Заметьте, что текст отдельных полей обычно не завершается новой строкой, тогда как в старом протоколе одиночная строка всегда завершалась так.
Сообщение ReadyForQuery ('Z
') включает индикатор статуса транзакции.
Различие между типами данных BinaryRow и DataRow ушло; один тип сообщений DataRow позволяет возвращать данные во всех форматах. Заметьте, что формат DataRow был изменён для упрощения его разбора. Также изменилось представление двоичных значений: оно больше не привязано к внутреннему представлению сервера.
В протоколе появился новый раздел «расширенный протокол запросов», в котором добавлены типы сообщений для команд Parse, Bind, Execute, Describe, Close, Flush и Sync, а также типы серверных сообщений ParseComplete, BindComplete, PortalSuspended, ParameterDescription, NoData и CloseComplete. Существующие клиенты могут не подстраиваться под этот раздел протокола, но если они задействует его, это позволит улучшить производительность или функциональность.
Данные COPY
теперь внедряются в сообщения CopyData и CopyDone. Есть чётко определённый способ восстановить работу в случае ошибок в процессе COPY
. Специальная последняя строка «\.
» больше не нужна, она не передаётся при выполнении COPY OUT
. (Она по-прежнему воспринимается как завершающая последовательность в потоке COPY IN
, но это считается устаревшим способом завершения, и в конце концов он будет исключён.) Поддерживается COPY
в двоичном режиме. Сообщения CopyInResponse и CopyOutResponse включают поля, определяющие число столбцов и формат каждого столбца.
Изменилась структура сообщений FunctionCall и FunctionCallResponse. Сообщение FunctionCall теперь позволяет передавать функциям аргументы NULL. Ещё в нём могут передаваться параметры и получаться результаты в текстовом или двоичном формате. Не осталось повода считать сообщение FunctionCall потенциально небезопасным, так как оно не даёт прямого доступа к внутренней презентации данных на сервере.
Сервер отправляет сообщения ParameterStatus ('S
') при попытке подключения для всех параметров, которые он считает интересными для клиентской библиотеки. Как следствие, при любом изменении активного значения одного из этих параметров также выдаётся сообщение ParameterStatus.
Сообщение RowDescription ('T
') содержит поля с OID таблицы и номером столбца для каждого столбца описываемой строки. В нём также передаётся код формата для каждого столбца.
Сервер более не выдаёт сообщение CursorResponse ('P
').
В сообщении NotificationResponse ('A
') добавилось ещё одно строковое поле, в котором может передаваться строка «сообщения» от отправителя события NOTIFY
.
Раньше сообщение EmptyQueryResponse ('I
') включало пустой строковый параметр; теперь он ликвидирован.
7.6. LIMIT
and OFFSET
LIMIT
and OFFSET
allow you to retrieve just a portion of the rows that are generated by the rest of the query:
SELECTselect_list
FROMtable_expression
[ ORDER BY ... ] [ LIMIT {number
| ALL } ] [ OFFSETnumber
]
If a limit count is given, no more than that many rows will be returned (but possibly fewer, if the query itself yields fewer rows). LIMIT ALL
is the same as omitting the LIMIT
clause, as is LIMIT
with a NULL argument.
OFFSET
says to skip that many rows before beginning to return rows. OFFSET 0
is the same as omitting the OFFSET
clause, as is OFFSET
with a NULL argument.
If both OFFSET
and LIMIT
appear, then OFFSET
rows are skipped before starting to count the LIMIT
rows that are returned.
When using LIMIT
, it is important to use an ORDER BY
clause that constrains the result rows into a unique order. Otherwise you will get an unpredictable subset of the query's rows. You might be asking for the tenth through twentieth rows, but tenth through twentieth in what ordering? The ordering is unknown, unless you specified ORDER BY
.
The query optimizer takes LIMIT
into account when generating query plans, so you are very likely to get different plans (yielding different row orders) depending on what you give for LIMIT
and OFFSET
. Thus, using different LIMIT
/OFFSET
values to select different subsets of a query result will give inconsistent results unless you enforce a predictable result ordering with ORDER BY
. This is not a bug; it is an inherent consequence of the fact that SQL does not promise to deliver the results of a query in any particular order unless ORDER BY
is used to constrain the order.
The rows skipped by an OFFSET
clause still have to be computed inside the server; therefore a large OFFSET
might be inefficient.