All patches
[idzebra-moved-to-github.git] / index / mod_indexplugin_mysql.c
1 /* This file is part of the Zebra server.
2    Copyright (C) 1994-2009 Index Data
3
4 Zebra is free software; you can redistribute it and/or modify it under
5 the terms of the GNU General Public License as published by the Free
6 Software Foundation; either version 2, or (at your option) any later
7 version.
8
9 Zebra is distributed in the hope that it will be useful, but WITHOUT ANY
10 WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17
18 */
19
20 #include "indexplugin.h"
21 #include <mysql/mysql.h>
22 #include <stdint.h>
23
24 MYSQL mCon;
25
26 static int mysqlConnect(const char *username, const char *password, const char *database, const char *hostname)
27 {
28     mysql_init(&mCon);
29
30     /* Set the default encoding to utf-8 so that zebra 
31        doesn't gribe that the XML conflicts with it's encoding */
32     mysql_options(&mCon, MYSQL_SET_CHARSET_NAME, "utf8");
33
34     mysql_options(&mCon, MYSQL_READ_DEFAULT_GROUP, "indexplugin_mysql");
35     if (!mysql_real_connect(&mCon, hostname, username, password, database, 0, NULL, 0))
36     {
37         yaz_log(YLOG_FATAL, "Failed to connect to database: %s\n", mysql_error(&mCon));
38         return ZEBRA_FAIL;
39     }
40     else
41     {
42         yaz_log(YLOG_LOG, "Connected to Mysql Database");
43     }
44         
45     return ZEBRA_OK;
46 }
47
48
49 static int repositoryExtract(ZebraHandle zh, const char *driverCommand, enum zebra_recctrl_action_t action)
50 {
51     /* this doesn't really need to be initialised */
52     int ret = ZEBRA_FAIL;
53
54     //nasty parsing method
55     char *sqlQuery = strchr(driverCommand, ':');
56     if(sqlQuery) *(sqlQuery ++) = NULL;
57     else
58     {
59         yaz_log(YLOG_LOG, "No MySQL Query given, falling back on config default");
60         sqlQuery = res_get_named(zh->session_res, "indexplugin.mysql_defaultsql", driverCommand);
61     }
62
63     yaz_log(YLOG_LOG, "Database configuration selected: %s", driverCommand);
64
65     //Get our connection specific info from the config
66     //TODO: make the "test" bit configurable by command line
67     const char *username = res_get_named(zh->session_res, "indexplugin.mysql_username", driverCommand);
68     const char *password = res_get_named(zh->session_res, "indexplugin.mysql_password", driverCommand);
69     const char *hostname = res_get_named(zh->session_res, "indexplugin.mysql_hostname", driverCommand);
70     const char *database = res_get_named(zh->session_res, "indexplugin.mysql_database", driverCommand);
71
72     const char *idfield = res_get_named(zh->session_res, "indexplugin.mysql_idfield", driverCommand);
73     const char *datafield = res_get_named(zh->session_res, "indexplugin.mysql_datafield", driverCommand);
74
75     if(!username) 
76     {
77         yaz_log(YLOG_FATAL, "Database configuration incomplete or missing");
78         return ZEBRA_FAIL;
79     }
80
81     if(!sqlQuery)
82     {
83         yaz_log(YLOG_FATAL, "No valid MySQL query");
84         return ZEBRA_FAIL;
85     }
86
87     enum
88     {
89         IDFIELD,
90         DATAFIELD
91     };
92     //This is a rudimentary way of binding fields, it's nasty
93     uint8_t fieldBind[2] = {0xFF, 0xFF};
94
95     yaz_log(YLOG_LOG, "MySQL Query: %s", sqlQuery);
96
97     if ((ret = mysqlConnect(username, password, database, hostname)) == ZEBRA_OK)
98     {
99         const char *mQuery = sqlQuery;
100         if (mysql_real_query(&mCon, mQuery, strlen(mQuery)) == 0)
101         {
102             MYSQL_RES *result = NULL;
103             if ((result = mysql_store_result(&mCon)))
104             {
105                 //Check for the binding fields
106                 MYSQL_FIELD *field;
107                 int i = 0;
108                 while(field = mysql_fetch_field(result))
109                 {
110                     if(strcmp(field->name, idfield) == 0) fieldBind[IDFIELD] = i;
111                     if(strcmp(field->name, datafield) == 0) fieldBind[DATAFIELD] = i;
112                     i ++;
113                 }
114
115                 //Test the binding fields
116                 if(fieldBind[IDFIELD] == 0xFF || fieldBind[DATAFIELD] == 0xFF)
117                 {
118                     yaz_log(YLOG_FATAL, "Query did not reveal all/any binding columns");
119                     ret = ZEBRA_FAIL;
120                 }
121                 else
122                 {
123                     yaz_log(YLOG_LOG, "Successfully found all binding columns");
124                         
125                     unsigned int num_fields;
126                     num_fields = mysql_num_fields(result);
127
128                     MYSQL_ROW row;
129                     while ((row = mysql_fetch_row(result)))
130                     {
131                         unsigned long *lengths;
132                         lengths = mysql_fetch_lengths(result);
133
134                         //This is the critical line, that actually indexes your data
135                         //Args: Zebra Handle, Data, Data length, Action, FileName(Unique identifier)
136                         zebraIndexBuffer(zh, row[fieldBind[DATAFIELD]], lengths[fieldBind[DATAFIELD]], action, row[fieldBind[IDFIELD]]);
137                     }
138                 }
139                 mysql_free_result(result);
140             }
141         }
142         else
143         {
144             yaz_log(YLOG_FATAL, "Failed to run query: %s", mysql_error(&mCon));
145             ret = ZEBRA_FAIL;
146         }
147     }
148
149     /* Drop our MYSQL connection as we don't need it anymore
150        and deallocate anything allocated */
151     mysql_close(&mCon);
152
153     return ret;
154 }
155
156 void indexPluginRegister(void)
157 {
158     /* register our function that gets called while indexing a document */
159     addDriverFunction(repositoryExtract);
160 }
161 /*
162  * Local variables:
163  * c-basic-offset: 4
164  * c-file-style: "Stroustrup"
165  * indent-tabs-mode: nil
166  * End:
167  * vim: shiftwidth=4 tabstop=8 expandtab
168  */
169