make beauty README
[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                         fprintf(stderr,"mysql_stmt_init failed: %s\n", mysql_error(db->conn));
63                         exit(1);
64                 }
65
66 #define SEARCH_QUERY "SELECT count(*) FROM ftsbench WHERE MATCH(body) AGAINST ( ? IN BOOLEAN MODE);"
67                 if ( mysql_stmt_prepare( db->prepareStmt, SEARCH_QUERY, strlen(SEARCH_QUERY) ) != 0 ) {
68                         fprintf(stderr,"mysql_stmt_init failed: %s\n", mysql_error(db->conn));
69                         exit(1);
70                 }
71
72                 if ( mysql_stmt_param_count(db->prepareStmt) != 1 ) {
73                         fprintf(stderr,"mysql_stmt_param_count: invalid parameter count\n");
74                         exit(1);
75                 }
76
77                 memset(&datain, 0, sizeof(MYSQL_BIND));
78                 datain.buffer_type = MYSQL_TYPE_BLOB;
79                 datain.length = (unsigned long*)&(db->b.strlen);
80
81                 memset(&dataout, 0, sizeof(MYSQL_BIND));
82                 dataout.buffer_type= MYSQL_TYPE_LONG;
83                 dataout.buffer= (char *)&intout;
84                 dataout.is_null= &isnull;
85                 dataout.length= &length;
86                 dataout.error= &my_error;
87
88                 db->flags = flags;
89         }
90
91     db->b.strlen = 0;
92                  
93         while( *words ) {
94                 if ( db->flags & FLG_OR)
95                         sb_add(&db->b, " ", 1);
96                 else
97                         sb_add(&db->b, " +", 2);
98
99                 sb_add(&db->b, *words, -1);
100                 words++;
101         }
102
103         datain.buffer = db->b.str;
104         datain.buffer_length = db->b.strlen;
105
106         if ( mysql_stmt_bind_param(db->prepareStmt, &datain) ) {
107                 fprintf(stderr,"mysql_stmt_bind_param failed: %s\n", mysql_error(db->conn));
108                 exit(1);
109         }
110
111         res = mysql_stmt_result_metadata(db->prepareStmt);
112         if ( !res ) { 
113                 fprintf(stderr,"mysql_stmt_result_metadata failed: %s\n", mysql_error(db->conn));
114                 exit(1);
115         }
116
117         if ( mysql_stmt_execute( db->prepareStmt ) ) {
118                 fprintf(stderr,"mysql_stmt_execute failed: %s\n", mysql_error(db->conn));
119                 exit(1);
120         }
121
122         if (mysql_stmt_bind_result( db->prepareStmt, &dataout)) {
123                 fprintf(stderr,"mysql_stmt_bind_result failed: %s\n", mysql_error(db->conn));
124                 exit(1);
125         }
126
127         if ( mysql_stmt_store_result( db->prepareStmt ) ) {
128                 fprintf(stderr,"mysql_stmt_store_result failed: %s\n", mysql_error(db->conn));
129                 exit(1);
130         }
131
132         if ( !mysql_stmt_fetch( db->prepareStmt) ) {
133                 db->db.nres += intout;
134         } else {
135                 fprintf(stderr,"mysql_stmt_fetch returns void result\n");
136                 exit(1);
137         }
138
139         mysql_free_result(res);
140
141         pthread_mutex_lock(&(db->db.nqueryMutex));
142         db->db.nquery ++;
143         pthread_mutex_unlock(&(db->db.nqueryMutex));
144 }
145
146 static void
147 startCreateScheme(ftsDB *adb, int flags) {
148         ftsMY *db = (ftsMY*)adb;
149
150         db->flags = flags;
151         if ( flags & FLG_FUNC )
152                 report("Flag 'func' is ignored by MySQL\n");
153
154         if ( (flags & (FLG_GIN | FLG_GIST)) && (db->flags & FLG_SQL) == 0 )
155                 report("MySQL doesn't distinguish 'gin' and 'gist' flags\n");
156
157         if ( mysql_query(db->conn, "DROP TABLE IF EXISTS ftsbench CASCADE;")!= 0 ) {
158                 fprintf(stderr,"mysql_query failed: %s\n", mysql_error(db->conn));
159                 exit(1);
160         }
161
162         if ( mysql_query(db->conn, "CREATE TABLE ftsbench (id int not null, body text) ENGINE MyISAM;")!= 0 ) {
163                 fprintf(stderr,"mysql_query failed: %s\n", mysql_error(db->conn));
164                 exit(1);
165         }
166 }
167
168 static void
169 finishCreateScheme(ftsDB *adb) {
170         ftsMY *db = (ftsMY*)adb;
171
172         if  ( db->flags & (FLG_GIN | FLG_GIST) ) {
173                 report("(create index, ");
174
175                 if ( mysql_query(db->conn, "CREATE FULLTEXT INDEX fts ON ftsbench (body);")!= 0 ) {
176                         fprintf(stderr,"mysql_query failed: %s\n", mysql_error(db->conn));
177                         exit(1);
178                 }
179         } else 
180                 report("(");
181
182         report("optimize");
183         
184         if ( mysql_query(db->conn, "OPTIMIZE TABLE ftsbench;")!= 0 ) {
185                 fprintf(stderr,"mysql_query failed: %s\n", mysql_error(db->conn));
186                 exit(1);
187         }
188
189         report(") ");
190 }
191
192 static void
193 InsertRow(ftsDB *adb, int id, char *txt) {
194         ftsMY *db = (ftsMY*)adb;
195         static MYSQL_BIND       data[2];
196         static unsigned long    txtlen;
197
198         if ( db->prepareStmt == NULL ) {
199                 db->prepareStmt = mysql_stmt_init(db->conn);
200                 if ( db->prepareStmt == NULL ) {
201                         fprintf(stderr,"mysql_stmt_init failed: %s\n", mysql_error(db->conn));
202                         exit(1);
203                 }
204
205 #define INSERT_QUERY "INSERT INTO ftsbench (id, body) VALUES ( ? , ? );"
206                 if ( mysql_stmt_prepare( db->prepareStmt, INSERT_QUERY, strlen(INSERT_QUERY) ) != 0 ) {
207                         fprintf(stderr,"mysql_stmt_init failed: %s\n", mysql_error(db->conn));
208                         exit(1);
209                 }
210
211                 if ( mysql_stmt_param_count(db->prepareStmt) != 2 ) {
212                         fprintf(stderr,"mysql_stmt_param_count: invalid parameter count\n");
213                         exit(1);
214                 }
215
216                 memset(data, 0, sizeof(data));
217                 data[0].buffer_type = MYSQL_TYPE_LONG;
218                 data[1].buffer_type = MYSQL_TYPE_BLOB;
219                 data[1].length = &txtlen;
220
221         }
222
223         data[0].buffer = &id;
224
225         txtlen = (unsigned long) strlen(txt);
226         data[1].buffer = txt;
227         data[1].buffer_length = txtlen;
228
229         if ( mysql_stmt_bind_param(db->prepareStmt, data) ) {
230                 fprintf(stderr,"mysql_stmt_bind_param failed: %s\n", mysql_error(db->conn));
231                 exit(1);
232         }
233
234         if ( mysql_stmt_execute( db->prepareStmt ) ) {
235                 fprintf(stderr,"mysql_stmt_execute failed: %s\n", mysql_error(db->conn));
236                 exit(1);
237         }
238 }
239
240 static void
241 Close(ftsDB* adb) {  
242     ftsMY *db = (ftsMY*)adb;
243
244         mysql_close(db->conn);
245 }
246
247 ftsDB* 
248 MYInit(char * connstr) {
249         ftsMY   *db = (ftsMY*)malloc(sizeof(ftsMY));
250
251         memset(db,0,sizeof(ftsMY));
252
253         db->conn = mysql_init(NULL);
254
255         if ( !mysql_real_connect(db->conn, NULL, NULL, NULL, connstr, 0, NULL, 0) ) {
256                 fprintf(stderr,"mysql_real_connect failed: %s\n", mysql_error(db->conn));
257                 exit(1);
258         }
259
260         db->db.execQuery = execQuery;
261         db->db.startCreateScheme = startCreateScheme;
262         db->db.finishCreateScheme = finishCreateScheme;
263         db->db.InsertRow = InsertRow;
264         db->db.Close = Close;
265         
266         return (ftsDB*)db;
267 }