/* $Id$ */ /* * Copyright (c) 2016 Kristaps Dzonsons * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include "ksql.h" #include "extern.h" enum stmt { STMT_BOOK, STMT_BOOKS, STMT_BOOKS_AUTHORS, STMT__MAX }; static const char *const stmts[STMT__MAX] = { /* STMT_BOOK */ "SELECT books.id AS id,title,series_index,name as series_name,series.id AS series_id FROM books LEFT OUTER JOIN books_series_link ON books.id = books_series_link.book LEFT OUTER JOIN series ON series.id = books_series_link.series WHERE books.id = ?", /* STMT_BOOKS */ "SELECT books.id AS id,title,series_index,name as series_name,series.id AS series_id FROM books LEFT OUTER JOIN books_series_link ON books.id = books_series_link.book LEFT OUTER JOIN series ON series.id = books_series_link.series ORDER BY id LIMIT ? OFFSET ?", /* STMT_BOOKS_AUTHORS */ "SELECT authors.id, authors.name, books_authors_link.book as book FROM authors, books_authors_link WHERE books_authors_link.author = authors.id AND books_authors_link.book IN ( SELECT id FROM books ORDER BY id LIMIT ? OFFSET ?)", }; /* * Linked list authors of book. */ struct bookauth { Author *a; BookAdv *b; struct bookauth *p; }; static void db_series_unfill(Series *p) { if (NULL == p) return; free(p->name); } void db_series_free(Series *p) { db_series_unfill(p); free(p); } static void db_book_unfill(Book *p) { if (NULL == p) return; free(p->title); db_series_unfill(&p->s); } static void db_author_unfill(Author *p) { if (NULL == p) return; free(p->name); } void db_book_free(Book *p) { db_book_unfill(p); free(p); } void db_book_adv_free(BookAdv *p) { db_book_unfill(&p->b); for (size_t i = 0; i < p->asize; i++) { db_author_unfill(p->a[i]); } free(p); } static void db_book_fill(Book *book, struct ksqlstmt *stmt) { book->id = ksql_stmt_int(stmt, 0); book->title = kstrdup(ksql_stmt_str(stmt, 1)); if ( ksql_stmt_isnull(stmt, 4) ) { book->s.id = -1; } else { book->s_idx = ksql_stmt_double(stmt, 2); book->s.name = kstrdup(ksql_stmt_str(stmt, 3)); book->s.id = ksql_stmt_int(stmt, 4); } } int db_books_load(struct kreq *r, BookAdv **books, int limit, int offset) { if (limit < 0 || offset < 0) { return 0; } struct ksqlstmt *stmt; struct bookauth *p = NULL; struct bookauth *item = NULL; int i, count = 0; int64_t bid; ksql_stmt_alloc(r->arg, &stmt, stmts[STMT_BOOKS], STMT_BOOKS); ksql_bind_int(stmt, 0, limit); ksql_bind_int(stmt, 1, offset); while (KSQL_ROW == ksql_stmt_step(stmt)) { books[count] = kcalloc(1, sizeof(BookAdv)); db_book_fill(&books[count]->b,stmt); books[count]->asize = 0; count++; } ksql_stmt_free(stmt); ksql_stmt_alloc(r->arg, &stmt, stmts[STMT_BOOKS_AUTHORS], STMT_BOOKS_AUTHORS); ksql_bind_int(stmt, 0, limit); ksql_bind_int(stmt, 1, offset); while (KSQL_ROW == ksql_stmt_step(stmt)) { // add author to list, link related book and increment asize item = kcalloc(1, sizeof(struct bookauth)); item->a = kcalloc(1, sizeof(Author)); item->a->id = ksql_stmt_int(stmt, 0); item->a->name = kstrdup(ksql_stmt_str(stmt, 1)); bid = ksql_stmt_int(stmt, 2); for (i = 0; i < count && NULL == item->b; i++) { if (books[i]->b.id == bid) { item->b = books[i]; item->b->asize++; } } item->p = p; p = item; } // assign authors to books while (NULL != p) { if (NULL != p->b && NULL == p->b->a && p->b->asize > 0) { p->b->a = kcalloc(p->b->asize, sizeof(Author)); p->b->asize = 0; // use as counter for insert } p->b->a[p->b->asize] = p->a;// add author p->b->asize++; item = p; p = p->p; free(item); } ksql_stmt_free(stmt); return count; } Book * db_book_load(struct kreq *r, int64_t id) { struct ksqlstmt *stmt; Book *book; ksql_stmt_alloc(r->arg, &stmt, stmts[STMT_BOOK], STMT_BOOK); ksql_bind_int(stmt, 0, id); if (KSQL_ROW != ksql_stmt_step(stmt)) { ksql_stmt_free(stmt); return(NULL); } book = kcalloc(1, sizeof(Book)); db_book_fill(book, stmt); ksql_stmt_free(stmt); return book; } /* * Open the database and stash the resulting handle in the d */ int db_open(struct kreq *r, const char *file) { struct ksqlcfg cfg; struct ksql *sql; /* Configure normal database except with foreign keys. */ memset(&cfg, 0, sizeof(struct ksqlcfg)); cfg.flags = KSQL_EXIT_ON_ERR | KSQL_FOREIGN_KEYS | KSQL_SAFE_EXIT; cfg.err = ksqlitemsg; cfg.dberr = ksqlitedbmsg; /* Allocate database. */ if (NULL == (sql = ksql_alloc(&cfg))) return(0); ksql_open(sql, file); r->arg = sql; return(1); } /* * Close the database stashed in the kreq's argument. */ void db_close(struct kreq *r) { ksql_free(r->arg); r->arg = NULL; }