~ubuntu-branches/ubuntu/saucy/ibus-pinyin/saucy-proposed

« back to all changes in this revision

Viewing changes to src/Database.cc

  • Committer: Bazaar Package Importer
  • Author(s): LI Daobing, Asias He
  • Date: 2010-09-08 21:38:54 UTC
  • mfrom: (1.2.8 upstream)
  • Revision ID: james.westby@ubuntu.com-20100908213854-q4wlx8zlcyqxvelz
Tags: 1.3.11-1
[ Asias He ]
New upstream release.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* vim:set et ts=4 sts=4:
2
 
 *
3
 
 * ibus-pinyin - The Chinese PinYin engine for IBus
4
 
 *
5
 
 * Copyright (c) 2008-2010 Peng Huang <shawn.p.huang@gmail.com>
6
 
 *
7
 
 * This program is free software; you can redistribute it and/or modify
8
 
 * it under the terms of the GNU General Public License as published by
9
 
 * the Free Software Foundation; either version 2, or (at your option)
10
 
 * any later version.
11
 
 *
12
 
 * This program is distributed in the hope that it will be useful,
13
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 
 * GNU General Public License for more details.
16
 
 *
17
 
 * You should have received a copy of the GNU General Public License
18
 
 * along with this program; if not, write to the Free Software
19
 
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20
 
 */
21
 
#include "Database.h"
22
 
#include <sqlite3.h>
23
 
#include "Util.h"
24
 
#include "PinyinArray.h"
25
 
 
26
 
namespace PY {
27
 
 
28
 
#define DB_CACHE_SIZE       "5000"
29
 
#define DB_INDEX_SIZE       (3)
30
 
/* define columns */
31
 
#define DB_COLUMN_USER_FREQ (0)
32
 
#define DB_COLUMN_PHRASE    (1)
33
 
#define DB_COLUMN_FREQ      (2)
34
 
#define DB_COLUMN_S0        (3)
35
 
 
36
 
#define DB_PREFETCH_LEN     (6)
37
 
 
38
 
boost::scoped_ptr<Database> Database::m_instance;
39
 
 
40
 
class Conditions : public std::vector<std::string> {
41
 
public:
42
 
    Conditions (void) : std::vector<std::string> (1) {}
43
 
 
44
 
    void double_ (void) {
45
 
        gint i = size ();
46
 
        do {
47
 
            push_back (at (--i));
48
 
        } while (i > 0);
49
 
    }
50
 
 
51
 
    void triple (void) {
52
 
        gint i = size ();
53
 
        do {
54
 
            const std::string & value = std::vector<std::string>::at (--i);
55
 
            push_back (value);
56
 
            push_back (value);
57
 
        } while (i > 0);
58
 
    }
59
 
 
60
 
    void appendVPrintf (gint begin, gint end, const gchar *fmt, va_list args) {
61
 
        gchar str[64];
62
 
        g_vsnprintf (str, sizeof(str), fmt, args);
63
 
        for (gint i = begin; i < end; i++) {
64
 
            at (i) += str;
65
 
        }
66
 
    }
67
 
 
68
 
    void appendPrintf (gint begin, gint end, const gchar *fmt, ...) {
69
 
        va_list args;
70
 
        va_start (args, fmt);
71
 
        appendVPrintf (begin, end, fmt, args);
72
 
        va_end (args);
73
 
    }
74
 
};
75
 
 
76
 
class SQLStmt {
77
 
public:
78
 
    SQLStmt (sqlite3 *db)
79
 
        : m_db (db), m_stmt (NULL) {
80
 
        g_assert (m_db != NULL);
81
 
    }
82
 
 
83
 
    ~SQLStmt () {
84
 
        if (m_stmt != NULL) {
85
 
            if (sqlite3_finalize (m_stmt) != SQLITE_OK) {
86
 
                g_warning ("destroy sqlite stmt failed!");
87
 
            }
88
 
        }
89
 
    }
90
 
 
91
 
    gboolean prepare (const String &sql) {
92
 
        if (sqlite3_prepare (m_db,
93
 
                             sql.c_str (),
94
 
                             sql.size (),
95
 
                             &m_stmt,
96
 
                             NULL) != SQLITE_OK) {
97
 
            g_warning ("parse sql failed!\n %s", sql.c_str ());
98
 
            return FALSE;
99
 
        }
100
 
 
101
 
        return TRUE;
102
 
    }
103
 
 
104
 
    gboolean step (void) {
105
 
        switch (sqlite3_step (m_stmt)) {
106
 
        case SQLITE_ROW:
107
 
            return TRUE;
108
 
        case SQLITE_DONE:
109
 
            return FALSE;
110
 
        default:
111
 
            g_warning ("sqlites step error!");
112
 
            return FALSE;
113
 
        }
114
 
    }
115
 
 
116
 
    const gchar *columnText (guint col) {
117
 
        return (const gchar *) sqlite3_column_text (m_stmt, col);
118
 
    }
119
 
 
120
 
    gint columnInt (guint col) {
121
 
        return sqlite3_column_int (m_stmt, col);
122
 
    }
123
 
 
124
 
private:
125
 
    sqlite3 *m_db;
126
 
    sqlite3_stmt *m_stmt;
127
 
};
128
 
 
129
 
Query::Query (const PinyinArray    & pinyin,
130
 
              guint                  pinyin_begin,
131
 
              guint                  pinyin_len,
132
 
              guint                  option)
133
 
    : m_pinyin (pinyin),
134
 
      m_pinyin_begin (pinyin_begin),
135
 
      m_pinyin_len (pinyin_len),
136
 
      m_option (option)
137
 
{
138
 
    g_assert (m_pinyin.size () >= pinyin_begin + pinyin_len);
139
 
}
140
 
 
141
 
Query::~Query (void)
142
 
{
143
 
}
144
 
 
145
 
gint
146
 
Query::fill (PhraseArray &phrases, gint count)
147
 
{
148
 
    gint row = 0;
149
 
 
150
 
    while (m_pinyin_len > 0) {
151
 
        if (G_LIKELY (m_stmt.get () == NULL)) {
152
 
            m_stmt = Database::instance ().query (m_pinyin, m_pinyin_begin, m_pinyin_len, -1, m_option);
153
 
            g_assert (m_stmt.get () != NULL);
154
 
        }
155
 
 
156
 
        while (m_stmt->step ()) {
157
 
            Phrase phrase;
158
 
 
159
 
            g_strlcpy (phrase.phrase,
160
 
                       m_stmt->columnText (DB_COLUMN_PHRASE),
161
 
                       sizeof (phrase.phrase));
162
 
            phrase.freq = m_stmt->columnInt (DB_COLUMN_FREQ);
163
 
            phrase.user_freq = m_stmt->columnInt (DB_COLUMN_USER_FREQ);
164
 
            phrase.len = m_pinyin_len;
165
 
 
166
 
            for (guint i = 0, column = DB_COLUMN_S0; i < m_pinyin_len; i++) {
167
 
                phrase.pinyin_id[i].sheng = m_stmt->columnInt (column++);
168
 
                phrase.pinyin_id[i].yun = m_stmt->columnInt (column++);
169
 
            }
170
 
 
171
 
            phrases.push_back (phrase);
172
 
            row ++;
173
 
            if (G_UNLIKELY (row == count)) {
174
 
                return row;
175
 
            }
176
 
        }
177
 
 
178
 
        m_stmt.reset ();
179
 
        m_pinyin_len --;
180
 
    }
181
 
 
182
 
    return row;
183
 
}
184
 
 
185
 
Database::Database (void)
186
 
    : m_db (NULL)
187
 
{
188
 
    open ();
189
 
}
190
 
 
191
 
Database::~Database (void)
192
 
{
193
 
    if (m_db) {
194
 
        if (sqlite3_close (m_db) != SQLITE_OK) {
195
 
            g_warning ("close sqlite database failed!");
196
 
        }
197
 
    }
198
 
}
199
 
 
200
 
inline gboolean
201
 
Database::executeSQL (const gchar *sql)
202
 
{
203
 
    gchar *errmsg;
204
 
    if (sqlite3_exec (m_db, sql, NULL, NULL, &errmsg) != SQLITE_OK) {
205
 
        g_warning ("%s: %s", errmsg, sql);
206
 
        sqlite3_free (errmsg);
207
 
        return FALSE;
208
 
    }
209
 
    return TRUE;
210
 
}
211
 
 
212
 
gboolean
213
 
Database::open (void)
214
 
{
215
 
    gboolean retval;
216
 
 
217
 
#if (SQLITE_VERSION_NUMBER >= 3006000)
218
 
    sqlite3_initialize ();
219
 
#endif
220
 
 
221
 
    static const gchar * maindb [] = {
222
 
        PKGDATADIR"/db/local.db",
223
 
        PKGDATADIR"/db/open-phrase.db",
224
 
        PKGDATADIR"/db/android.db",
225
 
        "main.db",
226
 
    };
227
 
 
228
 
    guint i;
229
 
    for (i = 0; i < G_N_ELEMENTS (maindb); i++) {
230
 
        if (!g_file_test(maindb[i], G_FILE_TEST_IS_REGULAR))
231
 
            continue;
232
 
        if (sqlite3_open_v2 (maindb[i], &m_db,
233
 
            SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL) == SQLITE_OK) {
234
 
            g_message ("Use database %s", maindb[i]);
235
 
            break;
236
 
        }
237
 
    }
238
 
 
239
 
    if (i == G_N_ELEMENTS (maindb)) {
240
 
        g_warning ("can not open main database");
241
 
        goto _failed;
242
 
    }
243
 
 
244
 
    m_sql.clear ();
245
 
 
246
 
#if 1
247
 
    /* Set synchronous=OFF, write user database will become much faster.
248
 
     * It will cause user database corrupted, if the operatering system
249
 
     * crashes or computer loses power.
250
 
     * */
251
 
    m_sql << "PRAGMA synchronous=NORMAL;\n";
252
 
#endif
253
 
 
254
 
    /* Set the cache size for better performance */
255
 
    m_sql << "PRAGMA cache_size=" DB_CACHE_SIZE ";\n";
256
 
 
257
 
    /* Using memory for temp store */
258
 
    m_sql << "PRAGMA temp_store=MEMORY;\n";
259
 
 
260
 
    /* Set journal mode */
261
 
    m_sql << "PRAGMA journal_mode=PERSIST;\n";
262
 
 
263
 
    /* Using EXCLUSIVE locking mode on databases
264
 
     * for better performance */
265
 
    m_sql << "PRAGMA locking_mode=EXCLUSIVE;\n";
266
 
    if (!executeSQL (m_sql))
267
 
        goto _failed;
268
 
 
269
 
    /* Attach user database */
270
 
    m_buffer = g_get_user_cache_dir ();
271
 
    m_buffer << G_DIR_SEPARATOR_S << "ibus"
272
 
             << G_DIR_SEPARATOR_S << "pinyin";
273
 
    g_mkdir_with_parents (m_buffer, 0750);
274
 
    m_buffer << G_DIR_SEPARATOR_S << "user-1.3.db";
275
 
    retval = openUserDB (m_buffer);
276
 
    if (!retval) {
277
 
        g_warning ("Can not open user database %s", m_buffer.c_str ());
278
 
        if (!openUserDB (":memory:"))
279
 
            goto _failed;
280
 
    }
281
 
 
282
 
    /* prefetch some tables */
283
 
    // prefetch ();
284
 
 
285
 
    return TRUE;
286
 
 
287
 
_failed:
288
 
    if (m_db) {
289
 
        sqlite3_close (m_db);
290
 
        m_db = NULL;
291
 
    }
292
 
    return FALSE;
293
 
}
294
 
 
295
 
gboolean
296
 
Database::openUserDB (const gchar *userdb)
297
 
{
298
 
    m_sql.printf ("ATTACH DATABASE \"%s\" AS userdb;", userdb);
299
 
    if (!executeSQL (m_sql))
300
 
        return FALSE;
301
 
 
302
 
    m_sql = "BEGIN TRANSACTION;\n";
303
 
    /* create desc table*/
304
 
    m_sql << "CREATE TABLE IF NOT EXISTS userdb.desc (name PRIMARY KEY, value TEXT);\n";
305
 
    m_sql << "INSERT OR IGNORE INTO userdb.desc VALUES " << "('version', '1.2.0');\n"
306
 
          << "INSERT OR IGNORE INTO userdb.desc VALUES " << "('uuid', '" << UUID () << "');\n"
307
 
          << "INSERT OR IGNORE INTO userdb.desc VALUES " << "('hostname', '" << Hostname () << "');\n"
308
 
          << "INSERT OR IGNORE INTO userdb.desc VALUES " << "('username', '" << Env ("USERNAME") << "');\n"
309
 
          << "INSERT OR IGNORE INTO userdb.desc VALUES " << "('create-time', datetime());\n"
310
 
          << "INSERT OR IGNORE INTO userdb.desc VALUES " << "('attach-time', datetime());\n";
311
 
 
312
 
    /* create phrase tables */
313
 
    for (guint i = 0; i < MAX_PHRASE_LEN; i++) {
314
 
        m_sql.appendPrintf ("CREATE TABLE IF NOT EXISTS userdb.py_phrase_%d (user_freq, phrase TEXT, freq INTEGER ", i);
315
 
        for (guint j = 0; j <= i; j++)
316
 
            m_sql.appendPrintf (",s%d INTEGER, y%d INTEGER", j, j);
317
 
        m_sql << ");\n";
318
 
    }
319
 
 
320
 
    /* create index */
321
 
    m_sql << "CREATE UNIQUE INDEX IF NOT EXISTS " << "userdb.index_0_0 ON py_phrase_0(s0,y0,phrase);\n";
322
 
    m_sql << "CREATE UNIQUE INDEX IF NOT EXISTS " << "userdb.index_1_0 ON py_phrase_1(s0,y0,s1,y1,phrase);\n";
323
 
    m_sql << "CREATE INDEX IF NOT EXISTS " << "userdb.index_1_1 ON py_phrase_1(s0,s1,y1);\n";
324
 
    for (guint i = 2; i < MAX_PHRASE_LEN; i++) {
325
 
        m_sql << "CREATE UNIQUE INDEX IF NOT EXISTS " << "userdb.index_" << i << "_0 ON py_phrase_" << i
326
 
              << "(s0,y0";
327
 
        for (guint j = 1; j <= i; j++)
328
 
            m_sql << ",s" << j << ",y" << j;
329
 
        m_sql << ",phrase);\n";
330
 
        m_sql << "CREATE INDEX IF NOT EXISTS " << "userdb.index_" << i << "_1 ON py_phrase_" << i << "(s0,s1,s2,y2);\n";
331
 
    }
332
 
    m_sql << "COMMIT;";
333
 
 
334
 
    if (!executeSQL (m_sql))
335
 
        goto _failed;
336
 
 
337
 
    m_sql  = "UPDATE userdb.desc SET value=datetime() WHERE name='attach-time';";
338
 
 
339
 
    if (!executeSQL (m_sql))
340
 
        goto _failed;
341
 
 
342
 
    return TRUE;
343
 
 
344
 
_failed:
345
 
    m_sql = "DETACH DATABASE userdb;";
346
 
    executeSQL (m_sql);
347
 
    return FALSE;
348
 
}
349
 
 
350
 
void
351
 
Database::prefetch (void)
352
 
{
353
 
    m_sql.clear ();
354
 
    for (guint i = 0; i < DB_PREFETCH_LEN; i++)
355
 
        m_sql << "SELECT * FROM py_phrase_" << i << ";\n";
356
 
 
357
 
    // g_debug ("prefetching ...");
358
 
    executeSQL (m_sql);
359
 
    // g_debug ("done");
360
 
}
361
 
 
362
 
inline static gboolean
363
 
pinyin_option_check_sheng (guint option, gint id, gint fid)
364
 
{
365
 
    switch ((id << 16) | fid) {
366
 
    case (PINYIN_ID_C << 16) | PINYIN_ID_CH:
367
 
        return (option & PINYIN_FUZZY_C_CH);
368
 
    case (PINYIN_ID_CH << 16) | PINYIN_ID_C:
369
 
        return (option & PINYIN_FUZZY_CH_C);
370
 
    case (PINYIN_ID_Z << 16) | PINYIN_ID_ZH:
371
 
        return (option & PINYIN_FUZZY_Z_ZH);
372
 
    case (PINYIN_ID_ZH << 16) | PINYIN_ID_Z:
373
 
        return (option & PINYIN_FUZZY_ZH_Z);
374
 
    case (PINYIN_ID_S << 16) | PINYIN_ID_SH:
375
 
        return (option & PINYIN_FUZZY_S_SH);
376
 
    case (PINYIN_ID_SH << 16) | PINYIN_ID_S:
377
 
        return (option & PINYIN_FUZZY_SH_S);
378
 
    case (PINYIN_ID_L << 16) | PINYIN_ID_N:
379
 
        return (option & PINYIN_FUZZY_L_N);
380
 
    case (PINYIN_ID_N << 16) | PINYIN_ID_L:
381
 
        return (option & PINYIN_FUZZY_N_L);
382
 
    case (PINYIN_ID_F << 16) | PINYIN_ID_H:
383
 
        return (option & PINYIN_FUZZY_F_H);
384
 
    case (PINYIN_ID_H << 16) | PINYIN_ID_F:
385
 
        return (option & PINYIN_FUZZY_H_F);
386
 
    case (PINYIN_ID_L << 16) | PINYIN_ID_R:
387
 
        return (option & PINYIN_FUZZY_L_R);
388
 
    case (PINYIN_ID_R << 16) | PINYIN_ID_L:
389
 
        return (option & PINYIN_FUZZY_R_L);
390
 
    case (PINYIN_ID_K << 16) | PINYIN_ID_G:
391
 
        return (option & PINYIN_FUZZY_K_G);
392
 
    case (PINYIN_ID_G << 16) | PINYIN_ID_K:
393
 
        return (option & PINYIN_FUZZY_G_K);
394
 
    default: return FALSE;
395
 
    }
396
 
}
397
 
 
398
 
inline static gboolean
399
 
pinyin_option_check_yun (guint option, gint id, gint fid)
400
 
{
401
 
    switch ((id << 16) | fid) {
402
 
    case (PINYIN_ID_AN << 16) | PINYIN_ID_ANG:
403
 
        return (option & PINYIN_FUZZY_AN_ANG);
404
 
    case (PINYIN_ID_ANG << 16) | PINYIN_ID_AN:
405
 
        return (option & PINYIN_FUZZY_ANG_AN);
406
 
    case (PINYIN_ID_EN << 16) | PINYIN_ID_ENG:
407
 
        return (option & PINYIN_FUZZY_EN_ENG);
408
 
    case (PINYIN_ID_ENG << 16) | PINYIN_ID_EN:
409
 
        return (option & PINYIN_FUZZY_ENG_EN);
410
 
    case (PINYIN_ID_IN << 16) | PINYIN_ID_ING:
411
 
        return (option & PINYIN_FUZZY_IN_ING);
412
 
    case (PINYIN_ID_ING << 16) | PINYIN_ID_IN:
413
 
        return (option & PINYIN_FUZZY_ING_IN);
414
 
    case (PINYIN_ID_IAN << 16) | PINYIN_ID_IANG:
415
 
        return (option & PINYIN_FUZZY_IAN_IANG);
416
 
    case (PINYIN_ID_IANG << 16) | PINYIN_ID_IAN:
417
 
        return (option & PINYIN_FUZZY_IANG_IAN);
418
 
    case (PINYIN_ID_UAN << 16) | PINYIN_ID_UANG:
419
 
        return (option & PINYIN_FUZZY_UAN_UANG);
420
 
    case (PINYIN_ID_UANG << 16) | PINYIN_ID_UAN:
421
 
        return (option & PINYIN_FUZZY_UANG_UAN);
422
 
    default: return FALSE;
423
 
    }
424
 
}
425
 
 
426
 
SQLStmtPtr
427
 
Database::query (const PinyinArray &pinyin,
428
 
                 guint              pinyin_begin,
429
 
                 guint              pinyin_len,
430
 
                 gint               m,
431
 
                 guint              option)
432
 
{
433
 
    g_assert (pinyin_begin < pinyin.size ());
434
 
    g_assert (pinyin_len <= pinyin.size () - pinyin_begin);
435
 
    g_assert (pinyin_len <= MAX_PHRASE_LEN);
436
 
 
437
 
    /* prepare sql */
438
 
    Conditions conditions;
439
 
 
440
 
    for (guint i = 0; i < pinyin_len; i++) {
441
 
        const Pinyin *p;
442
 
        gboolean fs1, fs2;
443
 
        p = pinyin[i + pinyin_begin];
444
 
 
445
 
        fs1 = pinyin_option_check_sheng (option, p->pinyin_id[0].sheng, p->pinyin_id[1].sheng);
446
 
        fs2 = pinyin_option_check_sheng (option, p->pinyin_id[0].sheng, p->pinyin_id[2].sheng);
447
 
 
448
 
        if (G_LIKELY (i > 0))
449
 
            conditions.appendPrintf (0, conditions.size (),
450
 
                                       " AND ");
451
 
 
452
 
        if (G_UNLIKELY (fs1 || fs2)) {
453
 
            if (G_LIKELY (i < DB_INDEX_SIZE)) {
454
 
                if (fs1 && fs2 == 0) {
455
 
                    conditions.double_ ();
456
 
                    conditions.appendPrintf (0, conditions.size ()  >> 1,
457
 
                                               "s%d=%d", i, p->pinyin_id[0].sheng);
458
 
                    conditions.appendPrintf (conditions.size () >> 1, conditions.size (),
459
 
                                               "s%d=%d", i, p->pinyin_id[1].sheng);
460
 
                }
461
 
                else if (fs1 == 0 && fs2) {
462
 
                    conditions.double_ ();
463
 
                    conditions.appendPrintf (0, conditions.size ()  >> 1,
464
 
                                               "s%d=%d", i, p->pinyin_id[0].sheng);
465
 
                    conditions.appendPrintf (conditions.size () >> 1, conditions.size (),
466
 
                                               "s%d=%d", i, p->pinyin_id[2].sheng);
467
 
                }
468
 
                else {
469
 
                    gint len = conditions.size ();
470
 
                    conditions.triple ();
471
 
                    conditions.appendPrintf (0, len,
472
 
                                               "s%d=%d", i, p->pinyin_id[0].sheng);
473
 
                    conditions.appendPrintf (len, len << 1,
474
 
                                               "s%d=%d", i, p->pinyin_id[1].sheng);
475
 
                    conditions.appendPrintf (len << 1, conditions.size (),
476
 
                                               "s%d=%d", i, p->pinyin_id[2].sheng);
477
 
                }
478
 
            }
479
 
            else {
480
 
                if (fs1 && fs2 == 0) {
481
 
                    conditions.appendPrintf (0, conditions.size (),
482
 
                                               "s%d IN (%d,%d)", i, p->pinyin_id[0].sheng, p->pinyin_id[1].sheng);
483
 
                }
484
 
                else if (fs1 == 0 && fs2) {
485
 
                    conditions.appendPrintf (0, conditions.size (),
486
 
                                               "s%d IN (%d,%d)", i, p->pinyin_id[0].sheng, p->pinyin_id[2].sheng);
487
 
                }
488
 
                else {
489
 
                    conditions.appendPrintf (0, conditions.size (),
490
 
                                               "s%d IN (%d,%d,%d)", i, p->pinyin_id[0].sheng, p->pinyin_id[1].sheng, p->pinyin_id[2].sheng);
491
 
                }
492
 
            }
493
 
        }
494
 
        else {
495
 
            conditions.appendPrintf (0, conditions.size (),
496
 
                                       "s%d=%d", i, p->pinyin_id[0].sheng);
497
 
        }
498
 
 
499
 
        if (p->pinyin_id[0].yun != PINYIN_ID_ZERO) {
500
 
            if (pinyin_option_check_yun (option, p->pinyin_id[0].yun, p->pinyin_id[1].yun)) {
501
 
                if (G_LIKELY (i < DB_INDEX_SIZE)) {
502
 
                    conditions.double_ ();
503
 
                    conditions.appendPrintf (0, conditions.size ()  >> 1,
504
 
                                               " AND y%d=%d", i, p->pinyin_id[0].yun);
505
 
                    conditions.appendPrintf (conditions.size () >> 1, conditions.size (),
506
 
                                               " and y%d=%d", i, p->pinyin_id[1].yun);
507
 
                }
508
 
                else {
509
 
                    conditions.appendPrintf (0, conditions.size (),
510
 
                                               " AND y%d IN (%d,%d)", i, p->pinyin_id[0].yun, p->pinyin_id[1].yun);
511
 
                }
512
 
            }
513
 
            else {
514
 
                conditions.appendPrintf (0, conditions.size (),
515
 
                                           " AND y%d=%d", i, p->pinyin_id[0].yun);
516
 
            }
517
 
        }
518
 
    }
519
 
 
520
 
 
521
 
    m_buffer.clear ();
522
 
    for (guint i = 0; i < conditions.size (); i++) {
523
 
        if (G_UNLIKELY (i == 0))
524
 
            m_buffer << "  (" << conditions[i] << ")\n";
525
 
        else
526
 
            m_buffer << "  OR (" << conditions[i] << ")\n";
527
 
    }
528
 
 
529
 
    m_sql.clear ();
530
 
    gint id = pinyin_len - 1;
531
 
    m_sql << "SELECT * FROM ("
532
 
                "SELECT 0 AS user_freq, * FROM main.py_phrase_" << id << " WHERE " << m_buffer << " UNION ALL "
533
 
                "SELECT * FROM userdb.py_phrase_" << id << " WHERE " << m_buffer << ") "
534
 
                    "GROUP BY phrase ORDER BY user_freq DESC, freq DESC";
535
 
    if (m > 0)
536
 
        m_sql << " LIMIT " << m;
537
 
#if 0
538
 
    g_debug ("sql =\n%s", m_sql.c_str ());
539
 
#endif
540
 
 
541
 
    /* query database */
542
 
    SQLStmtPtr stmt (new SQLStmt (m_db));
543
 
 
544
 
    if (!stmt->prepare (m_sql)) {
545
 
        stmt.reset ();
546
 
    }
547
 
 
548
 
    return stmt;
549
 
}
550
 
 
551
 
inline void
552
 
Database::phraseWhereSql (const Phrase & p, String & sql)
553
 
{
554
 
    sql << " WHERE";
555
 
    sql << " s0=" << p.pinyin_id[0].sheng
556
 
        << " AND y0=" << p.pinyin_id[0].yun;
557
 
    for (guint i = 1; i < p.len; i++) {
558
 
        sql << " AND s" << i << '=' << p.pinyin_id[i].sheng
559
 
            << " AND y" << i << '=' << p.pinyin_id[i].yun;
560
 
    }
561
 
    sql << " AND phrase=\"" << p.phrase << "\"";
562
 
 
563
 
}
564
 
 
565
 
inline void
566
 
Database::phraseSql (const Phrase & p, String & sql)
567
 
{
568
 
    sql << "INSERT OR IGNORE INTO userdb.py_phrase_" << p.len - 1
569
 
        << " VALUES(" << 0                  /* user_freq */
570
 
        << ",\"" << p.phrase << '"'         /* phrase */
571
 
        << ','   << p.freq;                 /* freq */
572
 
 
573
 
    for (guint i = 0; i < p.len; i++) {
574
 
        sql << ',' << p.pinyin_id[i].sheng << ',' << p.pinyin_id[i].yun;
575
 
    }
576
 
 
577
 
    sql << ");\n";
578
 
 
579
 
    sql << "UPDATE userdb.py_phrase_" << p.len - 1
580
 
        << " SET user_freq=user_freq+1";
581
 
 
582
 
    phraseWhereSql (p, sql);
583
 
    sql << ";\n";
584
 
}
585
 
 
586
 
void
587
 
Database::commit (const PhraseArray  &phrases)
588
 
{
589
 
    Phrase phrase = {""};
590
 
 
591
 
    m_sql = "BEGIN TRANSACTION;\n";
592
 
    for (guint i = 0; i < phrases.size (); i++) {
593
 
        phrase += phrases[i];
594
 
        phraseSql (phrases[i], m_sql);
595
 
    }
596
 
    if (phrases.size () > 1)
597
 
        phraseSql (phrase, m_sql);
598
 
    m_sql << "COMMIT;\n";
599
 
 
600
 
    executeSQL (m_sql);
601
 
}
602
 
 
603
 
void
604
 
Database::remove (const Phrase & phrase)
605
 
{
606
 
    m_sql = "BEGIN TRANSACTION;\n";
607
 
    m_sql << "DELETE FROM userdb.py_phrase_" << phrase.len - 1;
608
 
    phraseWhereSql (phrase, m_sql);
609
 
    m_sql << ";\n";
610
 
    m_sql << "COMMIT;\n";
611
 
 
612
 
    executeSQL (m_sql);
613
 
}
614
 
 
615
 
void
616
 
Database::init (void)
617
 
{
618
 
    if (m_instance == NULL) {
619
 
        m_instance.reset (new Database ());
620
 
    }
621
 
}
622
 
 
623
 
};