Встраиваемая база данных из С
Встраиваемая база данных из С
Встраиваемая СУБД — библиотека, которая работает внутри вашей программы, без отдельного сервера. Классический пример — SQLite: один файл app.db на диске, вызовы функций из C вместо сетевого протокола.
Общие понятия SQL (таблицы, SELECT, INSERT) — в разделе SQL. Здесь — как это выглядит в коде на С.
Термины API SQLite
| Имя | Роль |
|---|---|
sqlite3 | «соединение» с базой (один файл) |
sqlite3_stmt | подготовленный запрос с плейсхолдерами ? |
sqlite3_step | выполнить шаг (одна строка результата или завершение) |
sqlite3_bind_* | подставить значение вместо ? |
sqlite3_column_* | прочитать столбец текущей строки |
SQLITE_OK, SQLITE_ROW, SQLITE_DONE | коды результата |
Модель «библиотека в процессе»
В отличие от клиент-серверной PostgreSQL или MySQL:
- нет отдельного демона;
- нет сетевого порта;
- запросы выполняются вызовами функций API;
- файл
.db— обычный файл в файловой системе.
Приложение линкуется с библиотекой (часто libsqlite3) и включает заголовок с объявлениями API.
Жизненный цикл соединения
Типичная последовательность:
- Открыть базу: указатель на соединение (
sqlite3 *db). - Выполнить SQL — создание таблиц, вставка, выборка.
- Закрыть соединение — освободить ресурсы.
#include <sqlite3.h>
int rc = sqlite3_open("app.db", &db);
if (rc != SQLITE_OK) {
fprintf(stderr, "open failed: %s\n", sqlite3_errmsg(db));
return 1;
}
/* ... работа ... */
sqlite3_close(db);
sqlite3_open создаёт файл, если его нет (при успешных правах на каталог). Ошибки — кодами SQLITE_* и текстом sqlite3_errmsg(db).
Разбор проверки:
int rc = sqlite3_open("app.db", &db);
if (rc != SQLITE_OK) {
/* db может быть NULL или содержать сообщение */
fprintf(stderr, "%s\n", sqlite3_errmsg(db));
sqlite3_close(db);
return 1;
}
Любой путь, где open не удался, должен вызывать sqlite3_close, чтобы не утекали дескрипторы.
Выполнение SQL
Простой путь — одна строка SQL и callback для каждой строки результата (sqlite3_exec). Подходит для CREATE TABLE, миграций, редких административных команд.
Подготовленные запросы (sqlite3_prepare_v2 + sqlite3_step) — основной путь для повторяющихся операций:
const char *sql = "INSERT INTO users (name, age) VALUES (?, ?);";
sqlite3_stmt *stmt;
if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) != SQLITE_OK)
goto fail;
sqlite3_bind_text(stmt, 1, "Alice", -1, SQLITE_TRANSIENT);
sqlite3_bind_int(stmt, 2, 30);
if (sqlite3_step(stmt) != SQLITE_DONE)
goto fail;
sqlite3_finalize(stmt);
Плейсхолдеры ? или :name защищают от SQL-инъекций и переиспользуют план запроса.
Цикл чтения:
while (sqlite3_step(stmt) == SQLITE_ROW) {
const char *name = (const char *)sqlite3_column_text(stmt, 0);
int age = sqlite3_column_int(stmt, 1);
}
Что возвращает sqlite3_step:
| Код | Значение |
|---|---|
SQLITE_ROW | есть очередная строка результата — читайте column_* |
SQLITE_DONE | запрос завершён (для INSERT/UPDATE без строк) |
| другое | ошибка — смотрите sqlite3_errmsg |
Индексы столбцов в column_* начинаются с 0. Строка из column_text действительна до следующего step на этом stmt — для долгого хранения скопируйте в свой буфер.
Плейсхолдеры: sqlite3_bind_text(stmt, 1, "Alice", -1, SQLITE_TRANSIENT) — номер 1 соответствует первому ? в SQL. Четвёртый аргумент -1 значит «длина по \0». SQLITE_TRANSIENT — SQLite скопирует строку сам.
Транзакции
По умолчанию SQLite может автоматически фиксировать каждую операцию. Для пакетной вставки оборачивают в транзакцию:
BEGIN;
/* множество INSERT */
COMMIT;
При сбое — ROLLBACK. В API это те же строки SQL через exec или отдельные функции транзакций. Пакет в одной транзакции на порядки быстрее тысяч одиночных commit.
Типичные ошибки
| Проблема | Причина |
|---|---|
database is locked | другой поток/процесс держит запись; нужен таймаут или очередь |
утечка stmt | забыли sqlite3_finalize |
| гонка при потоках | одно соединение на поток или режим serialized + мьютекс |
| несовпадение типов bind | bind_int и bind_text для столбца |
См. Многопоточность на С: разделяемое соединение без синхронизации недопустимо.
Схема и миграции
Таблицы создают один раз при старте или версионируют схему:
CREATE TABLE IF NOT EXISTS settings (
key TEXT PRIMARY KEY,
value TEXT NOT NULL
);
Номер версии схемы хранят в таблице pragma user_version или отдельной таблице миграций; при обновлении приложения выполняют ALTER / пересоздание по скрипту.
Когда встраиваемая БД уместна
Подходит: локальное хранилище приложения, прототип, офлайн-режим, встраиваемые устройства, тестовые стенды.
Слабее: высокая конкурентная запись с множества сетевых клиентов, сложная репликация, тяжёлая аналитика на кластере — там серверные СУБД из обзора баз данных.
Связь с идиомами С
- Проверять каждый код возврата API.
- Освобождать
stmtна всех путях (очистка сgoto). - Не хранить указатели на
column_textпосле следующегоstepбез копирования строки.
См. также: Файловый ввод-вывод, Справочник.
См. также
Другие статьи этого же раздела в боковом меню (как на странице «О разделе»). История языка C - происхождение, ключевые идеи и влияние на развитие операционных систем и компиляторов. Язык С — это процедурный, компилируемый язык программирования, созданный в начале 1970-х годов Деннисом Ритчи в Bell Labs. Программирование на языке С требует понимания не только самого языка, но и всей совокупности программ, задействованных в процессе превращения исходного текста в исполняемый файл. Программа на языке С не выполняется напрямую процессором. Исходный текст проходит несколько этапов обработки, прежде чем превратится в машинный код, который может быть запущен операционной системой. Язык программирования С существует не как набор случайных правил, а как строго определённая спецификация, зафиксированная в международных стандартах. Как исполняемый файл на С раскладывается по областям памяти — код, данные, BSS, куча и стек — и что это даёт при отладке. Архитектура программ на C - организация модулей, процесс компиляции и взаимосвязь компонентов системы. Язык программирования С занимает особое место в истории и практике разработки программного обеспечения. Типизация, набор правил определения типа данных значений языка. Язык программирования С предоставляет механизм создания составных типов данных, позволяющих объединять разнородные элементы под единым именем. Этот механизм называется структурой. Как на С организовать функции, владение ресурсами, коды ошибок и очистку без исключений и сборщика мусора. Реализация ассоциативного массива на С — хеш-функция, коллизии, цепочки, открытая адресация и изменение размера.История языка С
Основы языка С
Инструментальная цепочка компиляции С
Преобразование исходного кода в исполняемый файл
Стандарты языка С
Память процесса и сегменты
Архитектура программ на С
Компиляторы и среды разработки для С
Типы данных в С
Структуры и объединения
Идиомы кода и обработка ошибок
Хеш-таблица на С