database-sqlite.c 20.6 KB
Newer Older
1 2 3
/* See LICENSE file for license and copyright information */

#include <sqlite3.h>
4 5
#include <girara/utils.h>
#include <girara/datastructures.h>
6
#include <girara/input-history.h>
7
#include <string.h>
8

9
#include "database-sqlite.h"
10

11
static void zathura_database_interface_init(ZathuraDatabaseInterface* iface);
12
static void io_interface_init(GiraraInputHistoryIOInterface* iface);
13 14

G_DEFINE_TYPE_WITH_CODE(ZathuraSQLDatabase, zathura_sqldatabase, G_TYPE_OBJECT,
15 16
                        G_IMPLEMENT_INTERFACE(ZATHURA_TYPE_DATABASE, zathura_database_interface_init)
                        G_IMPLEMENT_INTERFACE(GIRARA_TYPE_INPUT_HISTORY_IO, io_interface_init))
17

18 19 20 21 22 23 24 25 26 27 28
static bool           check_column(sqlite3* session, const char* table, const char* col, bool* result);
static void           sqlite_finalize(GObject* object);
static bool           sqlite_add_bookmark(zathura_database_t* db, const char* file, zathura_bookmark_t* bookmark);
static bool           sqlite_remove_bookmark(zathura_database_t* db, const char* file, const char* id);
static girara_list_t* sqlite_load_bookmarks(zathura_database_t* db, const char* file);
static girara_list_t* sqlite_load_jumplist(zathura_database_t* db, const char* file);
static bool           sqlite_save_jumplist(zathura_database_t* db, const char* file, girara_list_t* jumplist);
static bool           sqlite_set_fileinfo(zathura_database_t* db, const char* file, zathura_fileinfo_t* file_info);
static bool           sqlite_get_fileinfo(zathura_database_t* db, const char* file, zathura_fileinfo_t* file_info);
static void           sqlite_set_property(GObject* object, guint prop_id, const GValue* value, GParamSpec* pspec);
static void           sqlite_io_append(GiraraInputHistoryIO* db, const char*);
29
static girara_list_t* sqlite_io_read(GiraraInputHistoryIO* db);
30 31

typedef struct zathura_sqldatabase_private_s {
32
  sqlite3* session;
33 34 35 36 37
} zathura_sqldatabase_private_t;

#define ZATHURA_SQLDATABASE_GET_PRIVATE(obj) \
  (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ZATHURA_TYPE_SQLDATABASE, zathura_sqldatabase_private_t))

Moritz Lipp's avatar
Moritz Lipp committed
38
enum {
39 40
  PROP_0,
  PROP_PATH
41 42
};

43 44 45 46
static void
zathura_database_interface_init(ZathuraDatabaseInterface* iface)
{
  /* initialize interface */
Moritz Lipp's avatar
Moritz Lipp committed
47
  iface->add_bookmark    = sqlite_add_bookmark;
48
  iface->remove_bookmark = sqlite_remove_bookmark;
Moritz Lipp's avatar
Moritz Lipp committed
49
  iface->load_bookmarks  = sqlite_load_bookmarks;
50 51
  iface->load_jumplist   = sqlite_load_jumplist;
  iface->save_jumplist   = sqlite_save_jumplist;
Moritz Lipp's avatar
Moritz Lipp committed
52 53
  iface->set_fileinfo    = sqlite_set_fileinfo;
  iface->get_fileinfo    = sqlite_get_fileinfo;
54 55
}

56 57 58 59 60 61 62 63
static void
io_interface_init(GiraraInputHistoryIOInterface* iface)
{
  /* initialize interface */
  iface->append = sqlite_io_append;
  iface->read = sqlite_io_read;
}

64 65 66 67 68 69 70 71
static void
zathura_sqldatabase_class_init(ZathuraSQLDatabaseClass* class)
{
  /* add private members */
  g_type_class_add_private(class, sizeof(zathura_sqldatabase_private_t));

  /* override methods */
  GObjectClass* object_class = G_OBJECT_CLASS(class);
Moritz Lipp's avatar
Moritz Lipp committed
72
  object_class->finalize     = sqlite_finalize;
73 74 75
  object_class->set_property = sqlite_set_property;

  g_object_class_install_property(object_class, PROP_PATH,
Sebastian Ramacher's avatar
Sebastian Ramacher committed
76 77
    g_param_spec_string("path", "path", "path to the database", NULL,
      G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
78 79 80 81 82 83 84 85 86
}

static void
zathura_sqldatabase_init(ZathuraSQLDatabase* db)
{
  zathura_sqldatabase_private_t* priv = ZATHURA_SQLDATABASE_GET_PRIVATE(db);
  priv->session = NULL;
}

87
zathura_database_t*
88
zathura_sqldatabase_new(const char* path)
Pavel Borzenkov's avatar
Pavel Borzenkov committed
89
{
90 91 92 93 94
  g_return_val_if_fail(path != NULL && strlen(path) != 0, NULL);

  zathura_database_t* db = g_object_new(ZATHURA_TYPE_SQLDATABASE, "path", path, NULL);
  zathura_sqldatabase_private_t* priv = ZATHURA_SQLDATABASE_GET_PRIVATE(db);
  if (priv->session == NULL) {
Sebastian Ramacher's avatar
Sebastian Ramacher committed
95
    g_object_unref(G_OBJECT(db));
96
    return NULL;
Moritz Lipp's avatar
Moritz Lipp committed
97
  }
Moritz Lipp's avatar
Moritz Lipp committed
98

99 100
  return db;
}
Moritz Lipp's avatar
Moritz Lipp committed
101

102 103 104 105 106 107 108
static void
sqlite_finalize(GObject* object)
{
  ZathuraSQLDatabase* db = ZATHURA_SQLDATABASE(object);
  zathura_sqldatabase_private_t* priv = ZATHURA_SQLDATABASE_GET_PRIVATE(db);
  if (priv->session) {
    sqlite3_close(priv->session);
109 110
  }

111 112 113 114 115 116 117
  G_OBJECT_CLASS(zathura_sqldatabase_parent_class)->finalize(object);
}

static void
sqlite_db_init(ZathuraSQLDatabase* db, const char* path)
{
  zathura_sqldatabase_private_t* priv = ZATHURA_SQLDATABASE_GET_PRIVATE(db);
118

119
  /* create bookmarks table */
120 121
  static const char SQL_BOOKMARK_INIT[] =
    "CREATE TABLE IF NOT EXISTS bookmarks ("
Moritz Lipp's avatar
Moritz Lipp committed
122 123 124
    "file TEXT,"
    "id TEXT,"
    "page INTEGER,"
125 126
    "hadj_ratio FLOAT,"
    "vadj_ratio FLOAT,"
Moritz Lipp's avatar
Moritz Lipp committed
127
    "PRIMARY KEY(file, id));";
128

129 130 131 132 133 134 135 136 137
  static const char SQL_JUMPLIST_INIT[] =
    "CREATE TABLE IF NOT EXISTS jumplist ("
    "id INTEGER PRIMARY KEY AUTOINCREMENT,"
    "file TEXT,"
    "page INTEGER,"
    "hadj_ratio FLOAT,"
    "vadj_ratio FLOAT"
    ");";

138
  /* create fileinfo table */
139 140
  static const char SQL_FILEINFO_INIT[] =
    "CREATE TABLE IF NOT EXISTS fileinfo ("
Moritz Lipp's avatar
Moritz Lipp committed
141 142 143 144 145 146 147 148 149 150
    "file TEXT PRIMARY KEY,"
    "page INTEGER,"
    "offset INTEGER,"
    "scale FLOAT,"
    "rotation INTEGER,"
    "pages_per_row INTEGER,"
    "first_page_column INTEGER,"
    "position_x FLOAT,"
    "position_y FLOAT"
    ");";
151

152 153 154 155 156 157 158 159
  /* create history table */
  static const char SQL_HISTORY_INIT[] =
    "CREATE TABLE IF NOT EXISTS history ("
    "time TIMESTAMP,"
    "line TEXT,"
    "PRIMARY KEY(line));";

  /* update fileinfo table (part 1) */
160 161 162 163 164
  static const char SQL_FILEINFO_ALTER[] =
    "ALTER TABLE fileinfo ADD COLUMN pages_per_row INTEGER;"
    "ALTER TABLE fileinfo ADD COLUMN position_x FLOAT;"
    "ALTER TABLE fileinfo ADD COLUMN position_y FLOAT;";

165
  /* update fileinfo table (part 2) */
166 167 168
  static const char SQL_FILEINFO_ALTER2[] =
    "ALTER TABLE fileinfo ADD COLUMN first_page_column INTEGER;";

169
  /* update bookmark table */
170 171 172 173
  static const char SQL_BOOKMARK_ALTER[] =
    "ALTER TABLE bookmarks ADD COLUMN hadj_ratio FLOAT;"
    "ALTER TABLE bookmarks ADD COLUMN vadj_ratio FLOAT;";

174 175
  sqlite3* session = NULL;
  if (sqlite3_open(path, &session) != SQLITE_OK) {
176
    girara_error("Could not open database: %s\n", path);
177
    return;
178 179
  }

180
  /* create tables if they don't exist */
181
  if (sqlite3_exec(session, SQL_BOOKMARK_INIT, NULL, 0, NULL) != SQLITE_OK) {
182
    girara_error("Failed to initialize database: %s\n", path);
183 184
    sqlite3_close(session);
    return;
185 186
  }

187 188 189 190 191 192
  if (sqlite3_exec(session, SQL_JUMPLIST_INIT, NULL, 0, NULL) != SQLITE_OK) {
    girara_error("Failed to initialize database: %s\n", path);
    sqlite3_close(session);
    return;
  }

193
  if (sqlite3_exec(session, SQL_FILEINFO_INIT, NULL, 0, NULL) != SQLITE_OK) {
194
    girara_error("Failed to initialize database: %s\n", path);
195 196
    sqlite3_close(session);
    return;
197 198
  }

199 200 201 202 203 204
  if (sqlite3_exec(session, SQL_HISTORY_INIT, NULL, 0, NULL) != SQLITE_OK) {
    girara_error("Failed to initialize database: %s\n", path);
    sqlite3_close(session);
    return;
  }

205
  /* check existing tables for missing columns */
206 207 208 209 210
  bool res1, res2, ret1, ret2;

  ret1 = check_column(session, "fileinfo", "pages_per_row", &res1);

  if (ret1 == true && res1 == false) {
211 212 213 214 215 216
    girara_debug("old database table layout detected; updating ...");
    if (sqlite3_exec(session, SQL_FILEINFO_ALTER, NULL, 0, NULL) != SQLITE_OK) {
      girara_warning("failed to update database table layout");
    }
  }

217 218 219
  ret1 = check_column(session, "fileinfo", "first_page_column", &res1);

  if (ret1 == true && res1 == false) {
220 221 222 223 224 225
    girara_debug("old database table layout detected; updating ...");
    if (sqlite3_exec(session, SQL_FILEINFO_ALTER2, NULL, 0, NULL) != SQLITE_OK) {
      girara_warning("failed to update database table layout");
    }
  }

226 227 228 229
  ret1 = check_column(session, "bookmarks", "hadj_ratio", &res1);
  ret2 = check_column(session, "bookmarks", "vadj_ratio", &res2);

  if (ret1 == true && ret2 == true && res1 == false && res2 == false) {
230 231 232 233 234 235
    girara_debug("old database table layout detected; updating ...");
    if (sqlite3_exec(session, SQL_BOOKMARK_ALTER, NULL, 0, NULL) != SQLITE_OK) {
      girara_warning("failed to update database table layout");
    }
  }

236
  priv->session = session;
237 238
}

239 240
static void
sqlite_set_property(GObject* object, guint prop_id, const GValue* value, GParamSpec* pspec)
Pavel Borzenkov's avatar
Pavel Borzenkov committed
241
{
242 243 244 245 246 247 248 249 250 251
  ZathuraSQLDatabase* db = ZATHURA_SQLDATABASE(object);
  zathura_sqldatabase_private_t* priv = ZATHURA_SQLDATABASE_GET_PRIVATE(db);

  switch (prop_id) {
    case PROP_PATH:
      g_return_if_fail(priv->session == NULL);
      sqlite_db_init(db, g_value_get_string(value));
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
252
  }
253 254 255 256 257 258 259 260 261 262 263 264
}

static sqlite3_stmt*
prepare_statement(sqlite3* session, const char* statement)
{
  if (session == NULL || statement == NULL) {
    return NULL;
  }

  const char* pz_tail   = NULL;
  sqlite3_stmt* pp_stmt = NULL;

265
  if (sqlite3_prepare_v2(session, statement, -1, &pp_stmt, &pz_tail) != SQLITE_OK) {
266 267 268 269 270 271 272 273 274 275 276 277
    girara_error("Failed to prepare query: %s", statement);
    sqlite3_finalize(pp_stmt);
    return NULL;
  } else if (pz_tail && *pz_tail != '\0') {
    girara_error("Unused portion of statement: %s", pz_tail);
    sqlite3_finalize(pp_stmt);
    return NULL;
  }

  return pp_stmt;
}

278 279 280
static bool
check_column(sqlite3* session, const char* table, const char* col, bool* res)
{
281 282 283 284 285 286
  /* we can't actually bind the argument with sqlite3_bind_text because
   * sqlite3_prepare_v2 fails with "PRAGMA table_info(?);" */
  char* query = sqlite3_mprintf("PRAGMA table_info(%Q);", table);
  if (query == NULL) {
    return false;
  }
287

288
  sqlite3_stmt* stmt = prepare_statement(session, query);
289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306
  if (stmt == NULL) {
    return false;
  }

  *res = false;

  while (sqlite3_step(stmt) == SQLITE_ROW) {
    if (strcmp((const char*) sqlite3_column_text(stmt, 1), col) == 0) {
      *res = true;
      break;
    }
  }

  if (*res == false) {
    girara_debug("column %s in table %s is NOT found", col, table);
  }

  sqlite3_finalize(stmt);
307
  sqlite3_free(query);
308 309 310 311

  return true;
}

312 313
static bool
sqlite_add_bookmark(zathura_database_t* db, const char* file,
Moritz Lipp's avatar
Moritz Lipp committed
314
                    zathura_bookmark_t* bookmark)
315
{
316
  zathura_sqldatabase_private_t* priv = ZATHURA_SQLDATABASE_GET_PRIVATE(db);
317 318

  static const char SQL_BOOKMARK_ADD[] =
319
    "REPLACE INTO bookmarks (file, id, page, hadj_ratio, vadj_ratio) VALUES (?, ?, ?, ?, ?);";
320

321
  sqlite3_stmt* stmt = prepare_statement(priv->session, SQL_BOOKMARK_ADD);
322 323 324 325 326 327
  if (stmt == NULL) {
    return false;
  }

  if (sqlite3_bind_text(stmt, 1, file, -1, NULL) != SQLITE_OK ||
      sqlite3_bind_text(stmt, 2, bookmark->id, -1, NULL) != SQLITE_OK ||
328 329 330 331
      sqlite3_bind_int(stmt, 3, bookmark->page) != SQLITE_OK ||
      sqlite3_bind_double(stmt, 4, bookmark->x) != SQLITE_OK ||
      sqlite3_bind_double(stmt, 5, bookmark->y) != SQLITE_OK) {

332 333 334 335 336 337 338
    sqlite3_finalize(stmt);
    girara_error("Failed to bind arguments.");
    return false;
  }

  int res = sqlite3_step(stmt);
  sqlite3_finalize(stmt);
Moritz Lipp's avatar
Moritz Lipp committed
339 340

  return (res == SQLITE_DONE) ? true : false;
341 342
}

343 344
static bool
sqlite_remove_bookmark(zathura_database_t* db, const char* file, const char*
Moritz Lipp's avatar
Moritz Lipp committed
345
                       id)
346
{
347
  zathura_sqldatabase_private_t* priv = ZATHURA_SQLDATABASE_GET_PRIVATE(db);
348 349

  static const char SQL_BOOKMARK_ADD[] =
350
    "DELETE FROM bookmarks WHERE file = ? AND id = ?;";
351

352
  sqlite3_stmt* stmt = prepare_statement(priv->session, SQL_BOOKMARK_ADD);
353 354 355 356 357 358 359 360 361 362 363 364 365
  if (stmt == NULL) {
    return false;
  }

  if (sqlite3_bind_text(stmt, 1, file, -1, NULL) != SQLITE_OK ||
      sqlite3_bind_text(stmt, 2, id, -1, NULL) != SQLITE_OK) {
    sqlite3_finalize(stmt);
    girara_error("Failed to bind arguments.");
    return false;
  }

  int res = sqlite3_step(stmt);
  sqlite3_finalize(stmt);
Moritz Lipp's avatar
Moritz Lipp committed
366 367

  return (res == SQLITE_DONE) ? true : false;
368 369
}

370 371
static girara_list_t*
sqlite_load_bookmarks(zathura_database_t* db, const char* file)
372
{
373
  zathura_sqldatabase_private_t* priv = ZATHURA_SQLDATABASE_GET_PRIVATE(db);
374 375

  static const char SQL_BOOKMARK_SELECT[] =
376
    "SELECT id, page, hadj_ratio, vadj_ratio FROM bookmarks WHERE file = ?;";
377

378
  sqlite3_stmt* stmt = prepare_statement(priv->session, SQL_BOOKMARK_SELECT);
379 380 381 382 383 384 385 386 387 388
  if (stmt == NULL) {
    return NULL;
  }

  if (sqlite3_bind_text(stmt, 1, file, -1, NULL) != SQLITE_OK) {
    sqlite3_finalize(stmt);
    girara_error("Failed to bind arguments.");
    return NULL;
  }

389
  girara_list_t* result = girara_sorted_list_new2((girara_compare_function_t) zathura_bookmarks_compare,
Moritz Lipp's avatar
Moritz Lipp committed
390
                          (girara_free_function_t) zathura_bookmark_free);
391 392 393 394
  if (result != NULL) {
    sqlite3_finalize(stmt);
    return NULL;
  }
Moritz Lipp's avatar
Moritz Lipp committed
395

396
  while (sqlite3_step(stmt) == SQLITE_ROW) {
397 398 399 400
    zathura_bookmark_t* bookmark = g_try_malloc0(sizeof(zathura_bookmark_t));
    if (bookmark == NULL) {
      continue;
    }
Moritz Lipp's avatar
Moritz Lipp committed
401 402

    bookmark->id   = g_strdup((const char*) sqlite3_column_text(stmt, 0));
403
    bookmark->page = sqlite3_column_int(stmt, 1);
404 405
    bookmark->x    = sqlite3_column_double(stmt, 2);
    bookmark->y    = sqlite3_column_double(stmt, 3);
406 407 408 409 410

    if (bookmark->page > 1) {
      bookmark->x = bookmark->x == 0.0 ? DBL_MIN : bookmark->x;
      bookmark->y = bookmark->y == 0.0 ? DBL_MIN : bookmark->y;
    }
411

412 413
    girara_list_append(result, bookmark);
  }
Moritz Lipp's avatar
Moritz Lipp committed
414

415
  sqlite3_finalize(stmt);
Moritz Lipp's avatar
Moritz Lipp committed
416

417 418 419
  return result;
}

420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530
static bool
sqlite_save_jumplist(zathura_database_t* db, const char* file, girara_list_t* jumplist)
{
  g_return_val_if_fail(db != NULL && file != NULL && jumplist != NULL, false);

  zathura_sqldatabase_private_t* priv = ZATHURA_SQLDATABASE_GET_PRIVATE(db);
  static const char SQL_INSERT_JUMP[] = "INSERT INTO jumplist (file, page, hadj_ratio, vadj_ratio) VALUES (?, ?, ?, ?);";
  static const char SQL_REMOVE_JUMPLIST[] = "DELETE FROM jumplist WHERE file = ?;";
  sqlite3_stmt* stmt = NULL;
  int res = 0;

  if (sqlite3_exec(priv->session, "BEGIN;", NULL, 0, NULL) != SQLITE_OK) {
    return false;
  }

  stmt = prepare_statement(priv->session, SQL_REMOVE_JUMPLIST);

  if (stmt == NULL) {
    sqlite3_exec(priv->session, "ROLLBACK;", NULL, 0, NULL);
    return false;
  }

  if (sqlite3_bind_text(stmt, 1, file, -1, NULL) != SQLITE_OK) {
    sqlite3_finalize(stmt);
    sqlite3_exec(priv->session, "ROLLBACK;", NULL, 0, NULL);
    girara_error("Failed to bind arguments.");
    return false;
  }

  res = sqlite3_step(stmt);
  sqlite3_finalize(stmt);

  if (res != SQLITE_DONE) {
    sqlite3_exec(priv->session, "ROLLBACK;", NULL, 0, NULL);
    return false;
  }

  if (girara_list_size(jumplist) == 0) {
    sqlite3_exec(priv->session, "COMMIT;", NULL, 0, NULL);
    return true;
  }

  girara_list_iterator_t* cur = girara_list_iterator(jumplist);
  bool status = true;

  while (true) {
    zathura_jump_t* jump = girara_list_iterator_data(cur);

    stmt = prepare_statement(priv->session, SQL_INSERT_JUMP);

    if (stmt == NULL) {
      status = false;
      break;
    }

    if (sqlite3_bind_text(stmt, 1, file, -1, NULL) != SQLITE_OK ||
        sqlite3_bind_int(stmt, 2, jump->page)      != SQLITE_OK ||
        sqlite3_bind_double(stmt, 3, jump->x)      != SQLITE_OK ||
        sqlite3_bind_double(stmt, 4, jump->y)      != SQLITE_OK) {

      sqlite3_finalize(stmt);
      girara_error("Failed to bind arguments.");
      status = false;
      break;
    }

    res = sqlite3_step(stmt);
    sqlite3_finalize(stmt);

    if (res != SQLITE_DONE) {
      status = false;
      break;
    }

    if (girara_list_iterator_has_next(cur) == true) {
      girara_list_iterator_next(cur);
    } else {
      break;
    }
  }

  if (status == false) {
    sqlite3_exec(priv->session, "ROLLBACK;", NULL, 0, NULL);
    return false;
  } else {
    sqlite3_exec(priv->session, "COMMIT;", NULL, 0, NULL);
    return true;
  }
}

static girara_list_t*
sqlite_load_jumplist(zathura_database_t* db, const char* file)
{
  g_return_val_if_fail(db != NULL && file != NULL, NULL);

  zathura_sqldatabase_private_t* priv = ZATHURA_SQLDATABASE_GET_PRIVATE(db);
  static const char SQL_GET_JUMPLIST[] = "SELECT page, hadj_ratio, vadj_ratio FROM jumplist WHERE file = ? ORDER BY id ASC;";
  sqlite3_stmt* stmt = prepare_statement(priv->session, SQL_GET_JUMPLIST);

  if (stmt == NULL) {
    return NULL;
  }

  if (sqlite3_bind_text(stmt, 1, file, -1, NULL) != SQLITE_OK) {
    sqlite3_finalize(stmt);
    girara_error("Failed to bind arguments.");

    return NULL;
  }

  girara_list_t* jumplist = girara_list_new2(g_free);
531 532 533 534
  if (jumplist == NULL) {
    sqlite3_finalize(stmt);
    return NULL;
  }
535

536
  int res = 0;
537
  while ((res = sqlite3_step(stmt)) == SQLITE_ROW) {
538 539 540 541
    zathura_jump_t* jump = g_try_malloc0(sizeof(zathura_jump_t));
    if (jump == NULL) {
      continue;
    }
542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559

    jump->page = sqlite3_column_int(stmt, 0);
    jump->x    = sqlite3_column_double(stmt, 1);
    jump->y    = sqlite3_column_double(stmt, 2);
    girara_list_append(jumplist, jump);
  }

  sqlite3_finalize(stmt);

  if (res != SQLITE_DONE) {
    girara_list_free(jumplist);

    return NULL;
  }

  return jumplist;
}

560
static bool
561
sqlite_set_fileinfo(zathura_database_t* db, const char* file,
Moritz Lipp's avatar
Moritz Lipp committed
562
                    zathura_fileinfo_t* file_info)
563
{
564
  if (db == NULL || file == NULL || file_info == NULL) {
Moritz Lipp's avatar
Moritz Lipp committed
565 566
    return false;
  }
567

568
  zathura_sqldatabase_private_t* priv = ZATHURA_SQLDATABASE_GET_PRIVATE(db);
569 570

  static const char SQL_FILEINFO_SET[] =
571
    "REPLACE INTO fileinfo (file, page, offset, scale, rotation, pages_per_row, first_page_column, position_x, position_y) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?);";
572

573
  sqlite3_stmt* stmt = prepare_statement(priv->session, SQL_FILEINFO_SET);
574 575 576 577
  if (stmt == NULL) {
    return false;
  }

578 579 580 581 582 583 584 585 586
  if (sqlite3_bind_text(stmt,   1, file, -1, NULL)               != SQLITE_OK ||
      sqlite3_bind_int(stmt,    2, file_info->current_page)      != SQLITE_OK ||
      sqlite3_bind_int(stmt,    3, file_info->page_offset)       != SQLITE_OK ||
      sqlite3_bind_double(stmt, 4, file_info->scale)             != SQLITE_OK ||
      sqlite3_bind_int(stmt,    5, file_info->rotation)          != SQLITE_OK ||
      sqlite3_bind_int(stmt,    6, file_info->pages_per_row)     != SQLITE_OK ||
      sqlite3_bind_int(stmt,    7, file_info->first_page_column) != SQLITE_OK ||
      sqlite3_bind_double(stmt, 8, file_info->position_x)        != SQLITE_OK ||
      sqlite3_bind_double(stmt, 9, file_info->position_y)        != SQLITE_OK) {
587 588 589 590 591 592 593
    sqlite3_finalize(stmt);
    girara_error("Failed to bind arguments.");
    return false;
  }

  int res = sqlite3_step(stmt);
  sqlite3_finalize(stmt);
Moritz Lipp's avatar
Moritz Lipp committed
594 595

  return (res == SQLITE_DONE) ? true : false;
596
}
597

598
static bool
599
sqlite_get_fileinfo(zathura_database_t* db, const char* file,
Moritz Lipp's avatar
Moritz Lipp committed
600
                    zathura_fileinfo_t* file_info)
601
{
Moritz Lipp's avatar
Moritz Lipp committed
602 603 604
  if (db == NULL || file == NULL || file_info == NULL) {
    return false;
  }
605

606
  zathura_sqldatabase_private_t* priv = ZATHURA_SQLDATABASE_GET_PRIVATE(db);
607 608

  static const char SQL_FILEINFO_GET[] =
609
    "SELECT page, offset, scale, rotation, pages_per_row, first_page_column, position_x, position_y FROM fileinfo WHERE file = ?;";
610

611
  sqlite3_stmt* stmt = prepare_statement(priv->session, SQL_FILEINFO_GET);
612
  if (stmt == NULL) {
Moritz Lipp's avatar
Moritz Lipp committed
613
    return false;
614 615
  }

Sebastian Ramacher's avatar
Sebastian Ramacher committed
616
  if (sqlite3_bind_text(stmt, 1, file, -1, NULL) != SQLITE_OK) {
617 618 619 620 621 622 623 624 625 626 627
    sqlite3_finalize(stmt);
    girara_error("Failed to bind arguments.");
    return false;
  }

  if (sqlite3_step(stmt) != SQLITE_ROW) {
    sqlite3_finalize(stmt);
    girara_info("No info for file %s available.", file);
    return false;
  }

628 629 630 631 632 633 634 635
  file_info->current_page      = sqlite3_column_int(stmt, 0);
  file_info->page_offset       = sqlite3_column_int(stmt, 1);
  file_info->scale             = sqlite3_column_double(stmt, 2);
  file_info->rotation          = sqlite3_column_int(stmt, 3);
  file_info->pages_per_row     = sqlite3_column_int(stmt, 4);
  file_info->first_page_column = sqlite3_column_int(stmt, 5);
  file_info->position_x        = sqlite3_column_double(stmt, 6);
  file_info->position_y        = sqlite3_column_double(stmt, 7);
636

637
  sqlite3_finalize(stmt);
Moritz Lipp's avatar
Moritz Lipp committed
638

639 640
  return true;
}
641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688

static void
sqlite_io_append(GiraraInputHistoryIO* db, const char* input)
{
  static const char SQL_HISTORY_SET[] =
    "REPLACE INTO history (line, time) VALUES (?, DATETIME('now'));";

  zathura_sqldatabase_private_t* priv = ZATHURA_SQLDATABASE_GET_PRIVATE(db);
  sqlite3_stmt* stmt = prepare_statement(priv->session, SQL_HISTORY_SET);
  if (stmt == NULL) {
    return;
  }

  if (sqlite3_bind_text(stmt, 1, input, -1, NULL) != SQLITE_OK) {
    sqlite3_finalize(stmt);
    girara_error("Failed to bind arguments.");
    return;
  }

  sqlite3_step(stmt);
  sqlite3_finalize(stmt);
}

static girara_list_t*
sqlite_io_read(GiraraInputHistoryIO* db)
{
  static const char SQL_HISTORY_GET[] =
    "SELECT line FROM history ORDER BY time";

  zathura_sqldatabase_private_t* priv = ZATHURA_SQLDATABASE_GET_PRIVATE(db);
  sqlite3_stmt* stmt = prepare_statement(priv->session, SQL_HISTORY_GET);
  if (stmt == NULL) {
    return NULL;
  }

  girara_list_t* list = girara_list_new2((girara_free_function_t) g_free);
  if (list == NULL) {
    sqlite3_finalize(stmt);
    return NULL;
  }

  while (sqlite3_step(stmt) == SQLITE_ROW) {
    girara_list_append(list, g_strdup((const char*) sqlite3_column_text(stmt, 0)));
  }

  sqlite3_finalize(stmt);
  return list;
}