/* $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" #define STMT_PAGE " LIMIT ? OFFSET ?" #define STMT_AUTHORS0 "SELECT authors.id, authors.name, count(book) as count FROM authors, books_authors_link "\ "WHERE authors.id = books_authors_link.author GROUP BY author " #define STMT_AUTHORS_SEARCH "SELECT id, name FROM authors WHERE " #define STMT_SEARCH_TERM " sort like ? " #define STMT_BOOL_AND " AND " #define STMT_BOOL_OR " OR " #define STMT_SEARCH_ORDER " ORDER BY sort" enum stmt { STMT_AUTHORS_ID_ASC, STMT_AUTHORS_ID_DESC, STMT_AUTHORS_NAME_ASC, STMT_AUTHORS_NAME_DESC, STMT_AUTHOR_BOOKS, STMT_AUTHORS_AUTHORS, STMT_AUTHOR, STMT__MAX }; static const char *const stmts[STMT__MAX] = { /* STMT_AUTHORS_ID_ASC */ STMT_AUTHORS0 "ORDER BY authors.id " STMT_PAGE, /* STMT_AUTHORS_ID_DESC */ STMT_AUTHORS0 "ORDER BY authors.id DESC " STMT_PAGE, /* STMT_AUTHORS_NAME_ASC */ STMT_AUTHORS0 "ORDER BY authors.sort " STMT_PAGE, /* STMT_AUTHORS_NAME_DESC */ STMT_AUTHORS0 "ORDER BY authors.sort DESC " STMT_PAGE, /* STMT_AUTHOR_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 \ LEFT OUTER JOIN books_authors_link ON books.id = books_authors_link.book \ WHERE books_authors_link.author = ? ORDER BY id", /* STMT_AUTHORS_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 books.id FROM books LEFT OUTER JOIN books_authors_link \ ON books.id = books_authors_link.book WHERE books_authors_link.author = ? ORDER BY books.id)", /* STMT_AUTHOR */ "SELECT name FROM authors WHERE id = ?", }; void db_author_unfill(Author *p) { if (NULL == p) return; free(p->name); } void db_author_adv_free(AuthorAdv *p) { db_author_unfill(&p->a); free(p); } void db_author_free(Author *p) { db_author_unfill(p); free(p); } static int query_authors(const char* sort, const char *order) { int query = STMT_AUTHORS_ID_ASC; if (NULL != sort && 0 == strcmp(sort, "name")) { query = STMT_AUTHORS_NAME_ASC; if (NULL != order && 0 == strcmp(order, "desc")) query++; } else { if (NULL == order || 0 == strcmp(order, "desc")) query++; } return query; } AuthorFull * db_author_load(struct kreq *r, int64_t id) { struct ksqlstmt *stmt; struct bookauth *item = NULL; struct bookauth *p = NULL; int books = 0; BookAdv *b; AuthorFull *a = kcalloc(1, sizeof(AuthorFull)); a->a.id = id; ksql_stmt_alloc(r->arg, &stmt, stmts[STMT_AUTHOR_BOOKS], STMT_AUTHOR_BOOKS); ksql_bind_int(stmt, 0, id); while (KSQL_ROW == ksql_stmt_step(stmt)) { books++; b = kcalloc(1, sizeof(BookAdv)); b->asize = 0; b->tsize = 0; db_book_fill(&b->b, stmt); item = kcalloc(1, sizeof(struct bookauth)); item->b = b; item->a = &a->a; item->p = p; p = item; } ksql_stmt_free(stmt); a->bsize = books; if (books>0) { a->books = kcalloc(books, sizeof(BookAdv)); while (NULL != p && books > 0) { a->books[--books] = p->b; item = p; p = p->p; free(item); } ksql_stmt_alloc(r->arg, &stmt, stmts[STMT_AUTHORS_AUTHORS], STMT_AUTHORS_AUTHORS); ksql_bind_int(stmt, 0, id); p = db_load_books_authors(a->books, a->bsize, stmt); ksql_stmt_free(stmt); db_assign_book_authors(p); // get name from 1st book for (size_t i=0; ibooks[0]->asize; i++) { if (a->a.id == a->books[0]->a[i]->id) a->a.name = a->books[0]->a[i]->name; } } else { // no books, get name ksql_stmt_alloc(r->arg, &stmt, stmts[STMT_AUTHOR], STMT_AUTHOR); ksql_bind_int(stmt, 0, id); if (KSQL_ROW == ksql_stmt_step(stmt)) a->a.name = kstrdup(ksql_stmt_str(stmt, 0)); else a = NULL; ksql_stmt_free(stmt); } return a; } int db_authors_load(struct kreq *r, AuthorAdv **authors, const char *sort, const char *order, int limit, int offset) { if (limit < 0 || offset < 0) { return 0; } struct ksqlstmt *stmt; int count = 0; int query = query_authors(sort, order); ksql_stmt_alloc(r->arg, &stmt, stmts[query], query); ksql_bind_int(stmt, 0, limit); ksql_bind_int(stmt, 1, offset); while (KSQL_ROW == ksql_stmt_step(stmt)) { authors[count] = kcalloc(1, sizeof(AuthorAdv)); authors[count]->a.id = ksql_stmt_int(stmt, 0); authors[count]->a.name = kstrdup(ksql_stmt_str(stmt, 1)); authors[count]->count = ksql_stmt_int(stmt, 2); count++; } ksql_stmt_free(stmt); return count; } int db_authors_search(struct kreq *r, int limit, Author** authors, const char **terms, int num, int all) { if (limit < 0) { return 0; } struct ksqlstmt *stmt; int i, count = 0; size_t sqlsz = sizeof(STMT_AUTHORS_SEARCH) + num * sizeof(STMT_SEARCH_TERM) + (num-1) * sizeof(STMT_BOOL_AND) + sizeof(STMT_SEARCH_ORDER); char *sql = kcalloc(sqlsz, sizeof(char)); strlcat(sql, STMT_AUTHORS_SEARCH, sqlsz); for (i=0; iarg, &stmt, sql, 0); for (i=0; iid = ksql_stmt_int(stmt, 0); authors[count]->name = kstrdup(ksql_stmt_str(stmt, 1)); } count++; } ksql_stmt_free(stmt); return count; }