add .gitignore
[ftsbench.git] / mysqldriver.c
1 /*
2  * Copyright (c) 2006 Teodor Sigaev <teodor@sigaev.ru>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *        notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *        notice, this list of conditions and the following disclaimer in the
12  *        documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the author nor the names of any co-contributors
14  *        may be used to endorse or promote products derived from this software
15  *        without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY CONTRIBUTORS ``AS IS'' AND ANY EXPRESS
18  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL CONTRIBUTORS BE LIABLE FOR ANY
21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
23  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
26  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
27  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <unistd.h>
33 #include <string.h>
34 #include <time.h>
35 #include <errno.h>
36 #include <sys/types.h>
37 #include <netinet/in.h>
38
39 #include <mysql.h>
40 #include "ftsbench.h"
41
42 typedef struct ftsMY {
43         ftsDB   db;
44         MYSQL   *conn;
45         int     flags;
46         MYSQL_STMT              *prepareStmt;
47         StringBuf       b;
48 } ftsMY;
49
50 static void
51 execQuery(ftsDB *adb, char **words, int flags) {
52         ftsMY *db = (ftsMY*)adb;
53         static MYSQL_BIND       datain, dataout;
54         static int intout;
55         static my_bool  isnull, my_error;
56         static unsigned long    length;
57         MYSQL_RES       *res;
58
59         if ( db->prepareStmt == NULL ) {
60                 db->prepareStmt = mysql_stmt_init(db->conn);
61                 if ( db->prepareStmt == NULL ) 
62                         fatal("mysql_stmt_init failed: %s\n", mysql_error(db->conn));
63
64 #define SEARCH_QUERY "SELECT count(*) FROM ftsbench WHERE MATCH(body) AGAINST ( ? IN BOOLEAN MODE);"
65                 if ( mysql_stmt_prepare( db->prepareStmt, SEARCH_QUERY, strlen(SEARCH_QUERY) ) != 0 ) 
66                         fatal("mysql_stmt_init failed: %s\n", mysql_error(db->conn));
67
68                 if ( mysql_stmt_param_count(db->prepareStmt) != 1 ) 
69                         fatal("mysql_stmt_param_count: invalid parameter count\n");
70
71                 memset(&datain, 0, sizeof(MYSQL_BIND));
72                 datain.buffer_type = MYSQL_TYPE_BLOB;
73                 datain.length = (unsigned long*)&(db->b.strlen);
74
75                 memset(&dataout, 0, sizeof(MYSQL_BIND));
76                 dataout.buffer_type= MYSQL_TYPE_LONG;
77                 dataout.buffer= (char *)&intout;
78                 dataout.is_null= &isnull;
79                 dataout.length= &length;
80                 dataout.error= &my_error;
81
82                 db->flags = flags;
83         }
84
85     db->b.strlen = 0;
86                  
87         while( *words ) {
88                 sb_addchar(&db->b, ' ');
89                 if ( db->flags & FLG_AND)
90                         sb_addchar(&db->b, '+');
91
92                 sb_add(&db->b, *words, -1);
93                 words++;
94         }
95
96         datain.buffer = db->b.str;
97         datain.buffer_length = db->b.strlen;
98
99         if ( mysql_stmt_bind_param(db->prepareStmt, &datain) ) 
100                 fatal("mysql_stmt_bind_param failed: %s\n", mysql_error(db->conn));
101
102         res = mysql_stmt_result_metadata(db->prepareStmt);
103         if ( !res ) 
104                 fatal("mysql_stmt_result_metadata failed: %s\n", mysql_error(db->conn));
105
106         if ( mysql_stmt_execute( db->prepareStmt ) ) 
107                 fatal("mysql_stmt_execute failed: %s\n", mysql_error(db->conn));
108
109         if (mysql_stmt_bind_result( db->prepareStmt, &dataout)) 
110                 fatal("mysql_stmt_bind_result failed: %s\n", mysql_error(db->conn));
111
112         if ( mysql_stmt_store_result( db->prepareStmt ) ) 
113                 fatal("mysql_stmt_store_result failed: %s\n", mysql_error(db->conn));
114
115         if ( !mysql_stmt_fetch( db->prepareStmt) ) {
116                 db->db.nres += intout;
117         } else {
118                 fatal("mysql_stmt_fetch returns void result\n");
119         }
120
121         mysql_free_result(res);
122
123         pthread_mutex_lock(&(db->db.nqueryMutex));
124         db->db.nquery ++;
125         pthread_mutex_unlock(&(db->db.nqueryMutex));
126 }
127
128 static void
129 startCreateScheme(ftsDB *adb, int flags) {
130         ftsMY *db = (ftsMY*)adb;
131
132         db->flags = flags;
133         if ( flags & FLG_FUNC )
134                 report("Flag 'func' is ignored by MySQL\n");
135
136         if ( flags & (FLG_GIN | FLG_GIST) )
137                 report("MySQL doesn't distinguish 'gin' and 'gist' flags\n");
138
139         if ( mysql_query(db->conn, "DROP TABLE IF EXISTS ftsbench CASCADE;")!= 0 ) 
140                 fatal("mysql_query failed: %s\n", mysql_error(db->conn));
141
142         if ( mysql_query(db->conn, "CREATE TABLE ftsbench (id int not null, body text) ENGINE MyISAM;")!= 0 ) 
143                 fatal("mysql_query failed: %s\n", mysql_error(db->conn));
144 }
145
146 static void
147 finishCreateScheme(ftsDB *adb) {
148         ftsMY *db = (ftsMY*)adb;
149
150         if  ( db->flags & (FLG_GIN | FLG_GIST) ) {
151                 report("(create index, ");
152
153                 if ( mysql_query(db->conn, "CREATE FULLTEXT INDEX fts ON ftsbench (body);")!= 0 ) 
154                         fatal("mysql_query failed: %s\n", mysql_error(db->conn));
155         } else 
156                 report("(");
157
158         report("optimize");
159         
160         if ( mysql_query(db->conn, "OPTIMIZE TABLE ftsbench;")!= 0 ) 
161                 fatal("mysql_query failed: %s\n", mysql_error(db->conn));
162
163         report(") ");
164 }
165
166 static void
167 InsertRow(ftsDB *adb, int id, char *txt) {
168         ftsMY *db = (ftsMY*)adb;
169         static MYSQL_BIND       data[2];
170         static unsigned long    txtlen;
171
172         if ( db->prepareStmt == NULL ) {
173                 db->prepareStmt = mysql_stmt_init(db->conn);
174                 if ( db->prepareStmt == NULL ) 
175                         fatal("mysql_stmt_init failed: %s\n", mysql_error(db->conn));
176
177 #define INSERT_QUERY "INSERT INTO ftsbench (id, body) VALUES ( ? , ? );"
178                 if ( mysql_stmt_prepare( db->prepareStmt, INSERT_QUERY, strlen(INSERT_QUERY) ) != 0 ) 
179                         fatal("mysql_stmt_init failed: %s\n", mysql_error(db->conn));
180
181                 if ( mysql_stmt_param_count(db->prepareStmt) != 2 ) 
182                         fatal("mysql_stmt_param_count: invalid parameter count\n");
183
184                 memset(data, 0, sizeof(data));
185                 data[0].buffer_type = MYSQL_TYPE_LONG;
186                 data[1].buffer_type = MYSQL_TYPE_BLOB;
187                 data[1].length = &txtlen;
188
189         }
190
191         data[0].buffer = &id;
192
193         txtlen = (unsigned long) strlen(txt);
194         data[1].buffer = txt;
195         data[1].buffer_length = txtlen;
196
197         if ( mysql_stmt_bind_param(db->prepareStmt, data) ) 
198                 fatal("mysql_stmt_bind_param failed: %s\n", mysql_error(db->conn));
199
200         if ( mysql_stmt_execute( db->prepareStmt ) ) 
201                 fatal("mysql_stmt_execute failed: %s\n", mysql_error(db->conn));
202 }
203
204 static void
205 Close(ftsDB* adb) {  
206     ftsMY *db = (ftsMY*)adb;
207
208         mysql_close(db->conn);
209 }
210
211 ftsDB* 
212 MYInit(char * connstr) {
213         ftsMY   *db = (ftsMY*)malloc(sizeof(ftsMY));
214
215         memset(db,0,sizeof(ftsMY));
216
217         db->conn = mysql_init(NULL);
218
219         if ( !mysql_real_connect(db->conn, NULL, NULL, NULL, connstr, 0, NULL, 0) ) 
220                 fatal("mysql_real_connect failed: %s\n", mysql_error(db->conn));
221
222         db->db.execQuery = execQuery;
223         db->db.startCreateScheme = startCreateScheme;
224         db->db.finishCreateScheme = finishCreateScheme;
225         db->db.InsertRow = InsertRow;
226         db->db.Close = Close;
227         
228         return (ftsDB*)db;
229 }