RussianLDP Рейтинг@Mail.ru
WebMoney: 
WMZ Z294115950220 
WMR R409981405661 
WME E134003968233 
Visa 
4274 3200 2453 6495 

Small. Fast. Reliable.
Choose any three.
Встроенный SQLite printf()

1. Обзор

SQLite содержит свое собственное внедрение режима форматирования последовательности "printf()", доступное через следующие интерфейсы:

  • format() → SQL-функция, возвращающая отформатированную строку
  • sqlite3_mprintf() → Сохраняет отформатированную строку в памяти от sqlite3_malloc64().
  • sqlite3_snprintf() → Сохраняет отформатированную строку в статическом буфере
  • sqlite3_str_appendf() → Добавляет форматированный текст к динамической последовательности
  • sqlite3_vmprintf() & rarr; Varargs-версия sqlite3_mprintf()
  • sqlite3_vsnprintf() → Varargs-версия sqlite3_snprintf()
  • sqlite3_str_vappendf() → Varargs-версия sqlite3_str_appendf()

То же самое основное средство форматирования последовательности также используется внутренне SQLite.

1.1. Преимущества

Почему SQLite имеет собственный частный встроенный printf()? Почему бы не использовать printf() от стандартной библиотеки для C? Несколько причин:

  1. При помощи его собственного встроенного внедрения SQLite гарантирует, что вывод будет тем же самым на всех платформах и во всех LOCALE. Это важно для последовательности и для тестирования. Это было бы проблематично, если бы одна машина дала ответ "5.25e+08", а другая "5.250e+008". Оба ответа правильны, но лучше, когда SQLite всегда дает тот же самый ответ.

  2. Мы не знаем ни о каком способе пользоваться стандартной библиотекой printf() C, чтобы осуществить SQL-функцию format() в SQLite. Встроенный printf() может быть легко адаптирован к этой задаче, как бы то ни было.

  3. printf() в SQLite поддерживает новые нестандартные типы замены (%q, %Q, %w и %z) и увеличенное поведение замены (%s и %z), которые полезны внутренне для SQLite и для запросов, используя SQLite. Стандартная библиотека printf() не может обычно расширяться таким образом.

  4. Через sqlite3_mprintf() и sqlite3_vmprintf() встроенный printf() поддерживает способность отдать череду произвольных длин в буфер памяти, полученный из sqlite3_malloc64(). Это более безопасно и менее подвержено ошибкам, чем попытка предварительно вычислить верхнее ограничение размера на последовательность результата, ассигновать соответственно размерный буфер, а затем вызвать snprintf().

  5. SQLite-printf() поддерживает новый флаг "alternate-form-2". Он изменяет обработку преобразований с плавающей запятой тонкими способами так, чтобы вывод всегда был SQL-совместимым текстовым представлением числа с плавающей запятой, этого невозможно достигнуть со стандартной библиотекой printf(). Для замен последовательности alternate-form-2 заставляет ширину и точность быть измеренной в знаках вместо байтов, что упрощает обработку последовательностей, содержащих мультибайтные знаки UTF8.

  6. У встроенного SQLite есть варианты времени компиляции, такие как SQLITE_PRINTF_PRECISION_LIMIT, которые обеспечивают защиту против атак denial-of-service для применений, которые выставляют функциональность printf() пользователям, которым не доверяют.

  7. Использование встроенного printf() означает, что у SQLite есть меньше зависимости от серверной среды, делая его более портативным.

1.2. Недостатки

Честно, наличие встроенного внедрения printf() также идет с некоторыми недостатками.

  1. Встроенный printf() использует дополнительное кодовое пространство (приблизительно 7800 байтов на GCC 5.4 с -Os).

  2. С плавающей запятой к текстовой конверсионной подфункции для встроенного printf() ограничивается в точности 16 значительными цифрами или 26 значительными цифрами, если использован "!" alternate-form-2. Каждый IEEE 754 double может быть представлен точно как десятичное значение, но для многих double точное десятичное представление требует больше, чем 16 или 26 значительных цифр. SQLite printf() отдает только первые 16 или 26 значительных цифр, потому что это может быть сделано эффективно и потому что 16 десятичных цифр достаточно, чтобы отличить каждое возможнле значение double. Используйте десятичное расширение, чтобы получить точный десятичный эквивалент double для редких случаев, где это требуется.

  3. Порядок буферного указателя и параметров размера буфера во встроенном snprintf() полностью изменен от порядка, используемого во внедрениях стандартной библиотеки.

  4. Встроенный printf() не обращается с позиционными модификаторами ссылки posix, которые позволяют порядку аргументов printf() отличаться от порядка %-подстановок. Во встроенном printf() порядок аргументов должен точно соответствовать порядку %-подстановок.

Несмотря на недостатки, разработчики полагают, что иметь встроенный printf() в SQLite это полезно.

2. Форматирование деталей

Строка формата для printf() является шаблоном для произведенной последовательности. Замены сделаны каждый раз, когда символ "%" появляется в строке формата. "%" сопровождается одним или более дополнительными знаками, которые описывают замену. У каждой замены есть следующий формат:

%[flags][width][.precision][length]type

Все замены начинаются с единственного "%" и заканчиваются единственным символом текста. Другие элементы замены дополнительные.

Чтобы включать единственный символ "%" в вывод, поместите два последовательных знака "%" в шаблон.

2.1. Типы замены

Следующая диаграмма показывает типы замены, поддержанные SQLite:

Тип заменыЗначение
%Два символа "%" в строке подряд переведены в единственный "%" в выводе, не заменяя никакими значениями.
d, i Аргумент это целое число со знаком, которое показано в десятичном числе.
u Аргумент это целое без знака, которое показано в десятичном числе.
f Аргумент это double, которое показано в десятичном числе.
e, E Аргумент это double, которое показано в экспоненциальном представлении. Символ экспоненты 'e' или 'E' в зависимости от типа.
g, G Аргумент это double, которое показано в нормальной десятичной записи или (если экспонента не близка к нолю) в экспоненциальном представлении.
x, X Аргумент это double, которое показано в шестнадцатеричном виде. Шестнадцатеричные строчные буквы используются для %x, верхний регистр используется для %X.
oАргумент это целое число, которое показано в октальном виде.
s, z Аргумент это законченная нолем последовательность, которая показана, или нулевой указатель, который рассматривают как пустую строку. Для типа %z в интерфейсе языка C sqlite3_free() вызван после того, как это было скопировано в вывод. Подстановки %s и %z идентичны для SQL printf(), параметр NULL рассматривают как пустую строку.
Подстановка %s универсальна среди функций printf, но %z и безопасная обработка нулевых указателей это улучшения SQLite, не найденные в другом printf().
c Для интерфейсов языка C аргумент это целое число, которое интерпретируется как символ. Для функции format() аргумент это строка, из которой первый символ извлечен и показан.
p Аргумент это указатель, который показан как шестнадцатеричный адрес. Так как у языка SQL нет понятия указателя, %p для функции format() работает как %x.
n Аргумент это указатель на целое число. Ничто не показано для этого типа замены. Вместо этого целое число, на которое указывает аргумент, переписано количеством знаков в произведенной последовательности, которые следуют из всех символов формата налево от %n.
q, Q Аргумент это законченная нолем последовательность. Последовательность печатается со всеми одинарными кавычками, удвоенными так, чтобы последовательность могла безопасно появиться в строковом литерале SQL. Тип замены %Q также помещает одинарные кавычки на оба конца последовательности, которой заменяют.
Если аргумент %Q нулевой указатель, вывод неэкранированный "NULL". Другими словами, нулевой указатель производит NULL SQL, и ненулевой указатель производит действительный строковый литерал SQL. Если аргумент %q нулевой указатель, никакой вывод не произведен. Таким образом нулевой указатель к %q совпадает с пустой строкой.
Для этих замен точность это число байтов или знаков, взятых от аргумента, а не число байтов или знаков, которые написаны в вывод.
%q и %Q улучшения SQLite, не найденные в большей части других printf().
w Эта замена работает как %q за исключением того, что это удваивает все знаки двойной кавычки (") вместо одинарных кавычек, делая результат подходящим для использования с именем идентификатора в двойных кавычках в SQL-операторе.
Замена %w это улучшение SQLite, не найденное в большей части других printf().

2.2. Дополнительная область длины

Длина значения аргумента может быть определена одним или более символами, которые появляются только до символа типа замены. В SQLite длина имеет значение только для целых типов. Длина проигнорирована для SQL-функции format(), которая всегда использует 64-битные значения. Следующая таблица показывает спецификаторы длины, позволенные SQLite:

Спецификатор длиныСмысл
(умолчание) "int" или "unsigned int". 32 бита на всех современных системах.
l"long int" или "long unsigned int". 32 бита на всех современных системах.
ll "long long int", "long long unsigned", "sqlite3_int64" или "sqlite3_uint64". Это 64-битные целые числа на всех современных системах.

Только "ll" когда-либо имеет значение для SQLite. И это имеет значение, только используя интерфейсы языка C.

2.3. Дополнительная область ширины

Область ширины определяет минимальную ширину значения, которое заменяют, в выводе. Если последовательность или число, которое написано в вывод, короче, чем ширина, то значение дополнено. Дополнение по умолчанию слева (значение выровнено по правому знаку). Если использован флаг "-", то дополнение справа, и значение выровнено по левому краю.

Ширина измерена в байтах по умолчанию. Однако, если указан флаг "!", ширина задана в знаках. Это имеет значение только для мультибайтных знаков utf-8.

Если ширина это единственный символ "*" вместо числа, то фактическое значение ширины прочитано как целое число из списка аргументов. Если прочитанное значение отрицательное, то абсолютное значение используется для ширины, и оно выровнено налево, как будто применен флаг "-".

Если значение, которым заменяют, больше, чем ширина, то полное значение добавляется к выводу. Другими словами, ширина это минимальная ширина значения в выводе.

2.4. Дополнительная область точности

Область точности, если это присутствует, должна следовать за шириной, отделенная единственной ".". Если нет никакой ширины, то ".", которая вводит точность, следует немедленно за любым флагом (если есть) или начальным "%".

Для замен последовательности (%s, %z, %q, %Q или %w) точность это число байт или символов, используемых от аргумента. Число это байты по умолчанию, но является знаками, если указать "!". Если нет никакой точности, то заменяют всей последовательностью. Примеры: "%.3s" заменяет первыми 3 байтами последовательности аргумента. "%!.3s" заменяет первыми тремя знаками последовательности аргумента.

Для замен целого числа (%d, %i, %x, %X, %o и %p) точность определяет минимальное количество цифр, чтобы показать. Начальные нули добавляются при необходимости, чтобы расширить вывод до минимального количества цифр.

Для замен с плавающей запятой (%e, %E, %f, %g, %G) точность определяет количество цифр, чтобы показать справа от десятичной точки.

Для замены символа (%c) точность N больше, чем 1 предписывает повторить символ N раз. Это нестандартное расширение SQLite.

Если точность это единственный символ "*" вместо числа, то фактическое значение точности прочитано как целое число из списка аргументов.

2.5. Область флагов вариантов

Флаги состоят из ноля или большего количества знаков, которые немедленно следуют за "%", который вводит замену. Различные флаги и их значения следующие:

ФлагСмысл
- Выровнять влево в выводе. По уомлчанию выравнивание вправо. Если ширина ноль или меньше, чем длина значения, которым заменяют, то нет никакого дополнения и флаг "-" ничего не делает.
+ Для signed числовых замен включить знак "+" перед положительными числами. Знак "-" всегда появляется перед отрицательными числами независимо от параметров настройки флага.
(пробел) Для signed числовых замен подставить предварительный пробел перед положительными числами.
0 (опция нулевого дополнения) Предварить символами "0" числовые замены по мере необходимости, чтобы расширить значение до указанной ширины. Если область ширины опущена, то этот флаг не работает. Бесконечность и NaN (Not-A-Number), которые значения с плавающей точкой, обычно представляются как "Inf" и "NaN", соответственно, но с дополняющим нолями выбором они представлены как "9.0e+999" и "null". Другими словами, с дополняющим нолями выбором бесконечность с плавающей запятой и NaN представлены как литералы SQL и JSON.
# Это "alternate-form-1". Для %g и %G это заставляет конечные нули быть удаленными. Этот флаг вынуждает десятичную точку появиться для всех замен с плавающей запятой. Для %o, %x и %X alternate-form-1 предписывает предварить значение соответственно символами "0", "0x" или "0X".
, Выбор запятой заставляет сепараторы запятой быть добавленными к выводу числовых замен (%d, %f и т.п.) перед каждой третьей цифрой налево от десятичной точки. Никакие запятые не добавляются для цифр направо от десятичной точки. Это может помочь людям более легко различить величину больших целочисленных значений. Например, значение 2147483647 было бы представлено как "2147483647" с использованием "%d", но как "2,147,483,647" с "%,d". Этот флаг нестандартное расширение.
! Это "alternate-form-2. Для замен последовательности этот флаг предписывает понимать ширину и точность с точки зрения знаков, а не байтов. Для замен с плавающей точкой флаг alternate-form-2 увеличивает максимальное число значащих цифр с 16 до 26, вызывает показ десятичной точки и заставляет по крайней мере одну цифру появляться после десятичной точки.
alternate-form-2 это нестандартное расширение, которое не появляется ни в каком другом printf().

3. Внедрение и история

Основной движок форматирования последовательности это функция sqlite3VXPrintf() в исходном файле printf.c. Все различные интерфейсы вызывают (иногда косвенно) эту основную функцию. sqlite3VXPrintf() началась как код, написанный первым автором SQLite (Hipp), когда он был аспирантом в Университете Дюка в конце 1980-х. Hipp сохранял этот printf() в его личном комплекте инструментов, пока он не начал работать над SQLite в 2000. Код был включен в исходное дерево SQLite 2000-10-08 в SQLite version 1.0.9.

Fossil Version Control System использует свой собственный printf(), который получен из ранней версии SQLite printf(), но те два внедрения с тех пор отличались.

Функция sqlite3_snprintf() имеет свой буферный указатель и аргументы размера буфера, полностью измененные от того, что найдено в стандартной библиотеке для C snprintf(). Это вызвано тем, что не было никакого snprintf() в стандартной библиотеке для C, когда Hipp сначала осуществлял свою версию, и он выбрал другой порядок, чем проектировщики стандартной библиотеки для C.