11 // ---------------------------------------------------------------------------
14 Age string // recommended age, e.g. "beginner", "junior", "ya" (Young Adult), "adult"
15 AuthorGrouping string // unique rendering of the author's name, used for internal grouping
16 AuthorReading string // reading order of author's name, e.g. "Charles Dickens"
17 AuthorSort string // sort order of author's name, e.g. "Dickens, Charles"
18 DDC string // Dewey Decimal Classification
19 Description string // Back cover / inside flap blurb, describing the book
20 Genre string // e.g. "adventure", "historical", "mystery", "romance", "sf" (Science Fiction)
21 LCC string // Library of Congress Classification
27 // ---------------------------------------------------------------------------
30 Author, Series, Title Field = "aut", "ser", "tit"
33 func (f Field) String() string {
37 // ---------------------------------------------------------------------------
38 type SearchTerm struct {
43 var g_db *sql.DB = nil
44 var g_mutex = &sync.Mutex{}
46 // ============================================================================
53 func getDb() (*sql.DB) {
56 defer g_mutex.Unlock()
59 g_db = openDb(config.user, config.pass, config.dbName)
66 func nsVal(ns sql.NullString) string {
73 func openDb(user, pass, dbName string) (*sql.DB) {
74 db, err := sql.Open("postgres","user=" + user + " password=" + pass + " dbname=" + dbName + " sslmode=disable")
76 report("Error: DB arguments incorrect?", err)
82 report("Error: could not connect to DB.", err)
90 func queryBooksByIds(ids []int) []Book {
91 query := `SELECT s.age,a.grouping,a.reading,a.sort,c.ddc,b.description,s.genre,c.lcc,s.descr,b.title,b.volume
93 INNER JOIN Books b ON a.id=b.author
94 LEFT OUTER JOIN Classifications c ON c.id=b.classification
95 LEFT OUTER JOIN Series s ON s.id=b.series
98 ps, err := getDb().Prepare(query)
100 report("Error: failed to prepare statement: " + query, err)
106 for _, id := range ids {
112 res := make([]Book, count)
115 for _, id := range ids {
120 row := ps.QueryRow(id)
122 var age, grouping, reading, sort, ddc, description, genre, lcc, name, title, volume sql.NullString
124 err = row.Scan(&age, &grouping, &reading, &sort, &ddc, &description, &genre, &lcc, &name, &title, &volume)
126 report("Error: Failed to read book:" + strconv.Itoa(id) + ":", err)
131 b.AuthorGrouping = nsVal(grouping)
132 b.AuthorReading = nsVal(reading)
133 b.AuthorSort = nsVal(sort)
135 b.Description = nsVal(description)
136 b.Genre = nsVal(genre)
138 b.SeriesName = nsVal(name)
139 b.Title = nsVal(title)
140 b.Volume = nsVal(volume)
147 if count < len(res) {
154 func queryIds(criteria []SearchTerm) []int {
155 query := "SELECT b.id FROM Books b" +
156 " INNER JOIN Authors a ON a.id=b.author" +
157 " LEFT OUTER JOIN Series s ON s.id=b.series"
159 args := make([]interface{}, len(criteria))
161 for i, criterion := range criteria {
167 switch criterion.Attribute {
169 query += " a.grouping LIKE $" + strconv.Itoa(i + 1)
171 query += " s.descr LIKE $" + strconv.Itoa(i + 1)
173 query += " b.title LIKE $" + strconv.Itoa(i + 1)
175 report("Error: unrecognized search field in queryIds(): " + criterion.Attribute.String(), nil)
178 args[i] = criterion.Text
183 ps, err := getDb().Prepare(query)
185 report("Failed to Prepare query: " + query, err)
191 rows, err = ps.Query(args...)
193 report("Failed to execute query: " + query, err)
198 for rows.Next(); rows.Next(); {
201 res = append(res, id)
207 func report(msg string, err error) {
208 fmt.Println("Error: " + msg, err)