1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as
6 // published by the Free Software Foundation, either version 3 of the
7 // License, or (at your option) any later version.
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU Affero General Public License for more details.
14 // You should have received a copy of the GNU Affero General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
19 #include "mysql_wrapper.h"
23 using namespace NLMISC
;
24 using namespace NLNET
;
26 CVariable
<bool> MSWStrictMode("msw", "MSWStrictMode", "Set the strict mode on SQL request", true, 0, true);
27 CVariable
<uint32
> MSWRequestDuration("msw", "MSWRequestDuration", "Measure the duration of SQL request", 0, 1000);
28 CVariable
<bool> MSWAutoReconnect("msw", "MSWAutoReconnect", "MYSQL_OPT_RECONNECT", true, 0, true);
33 std::string
encodeDate(NLMISC::TTime date
)
35 time_t baseTime
= time_t(date
);
36 // convert UTC time_t into local time, including daillight adjustment
37 struct tm
*converted
= localtime(&baseTime
);
39 if (converted
== NULL
)
41 nldebug("Failed to convert %dl into a time structure", date
);
42 return "0000-00-00 00:00:00";
46 str
= NLMISC::toString("%4u-%02u-%02u %u:%02u:%02u",
47 converted
->tm_year
+1900,
57 const std::string
&escapeString(const std::string
&str
, CConnection
&dbCnx
)
60 // reserve space in the buffer according to the mysql documentation
61 buffer
.resize(str
.size()*2+1);
62 unsigned long resultSize
= mysql_escape_string((char*)buffer
.data(), str
.data(), (unsigned long)str
.size());
64 // now, resize to the real size
65 buffer
.resize(resultSize
);
71 void CConnection::addOption(mysql_option option
, const char *value
)
73 _Options
[option
] = value
;
76 void CConnection::clearOption(mysql_option option
)
78 _Options
.erase(option
);
82 bool CConnection::connect(const TParsedCommandLine
&dbInfo
)
84 const TParsedCommandLine
*dbHost
= dbInfo
.getParam("host");
85 const TParsedCommandLine
*dbPort
= dbInfo
.getParam("port");
86 const TParsedCommandLine
*dbUser
= dbInfo
.getParam("user");
87 const TParsedCommandLine
*dbPassword
= dbInfo
.getParam("password");
88 const TParsedCommandLine
*dbBase
= dbInfo
.getParam("base");
94 nlwarning("LS : Invalid database configuration");
97 nlstopex(("SQL Strict mode, the above error is fatal"));
102 if (!dbPort
|| !NLMISC::fromString(dbPort
->ParamValue
, port
)) // parse port
104 port
= 0; // or use default
107 // connect to the database
108 return connect(dbHost
->ParamValue
, dbUser
->ParamValue
, dbPassword
->ParamValue
, dbBase
->ParamValue
, port
);
111 bool CConnection::connect(const std::string
&hostName
, const std::string
&userName
, const std::string
&password
, const std::string
&defaultDatabase
, uint port
)
113 // store the connection info
114 _ConnHostName
= hostName
;
115 _ConnUserName
= userName
;
116 _ConnPassword
= password
;
117 _ConnDefaultDatabase
= defaultDatabase
;
120 nlassert(!_Connected
);
122 if (MSWAutoReconnect
)
124 addOption(MYSQL_OPT_RECONNECT
, "1");
130 bool CConnection::_connect()
134 if (_MysqlContext
!= NULL
)
136 mysql_close(_MysqlContext
);
140 _MysqlContext
= mysql_init(NULL
);
143 TOptions::iterator
first(_Options
.begin()), last(_Options
.end());
144 for (; first
!= last
; ++first
)
146 const mysql_option
&option
= first
->first
;
147 const char *value
= first
->second
;
148 mysql_options(_MysqlContext
, option
, value
);
151 MYSQL
*res
= mysql_real_connect(_MysqlContext
,
152 _ConnHostName
.c_str(),
153 _ConnUserName
.c_str(),
154 _ConnPassword
.c_str(),
155 _ConnDefaultDatabase
.c_str(),
162 nlwarning("Error during connection to database '%s:%s@%s:%d' :", _ConnUserName
.c_str(), _ConnDefaultDatabase
.c_str(), _ConnHostName
.c_str(), _ConnPort
);
163 nlwarning("Mysql_real_connect error :%s ", mysql_error(_MysqlContext
));
164 // the connection failed !
165 mysql_close(_MysqlContext
);
166 _MysqlContext
= NULL
;
169 nlstopex(("SQL Strict mode, the above error is fatal"));
179 void CConnection::closeConn()
181 nlassert(_Connected
);
183 // the connection failed !
184 mysql_close(_MysqlContext
);
187 _MysqlContext
= NULL
;
190 bool CConnection::query(const std::string
&queryString
)
192 H_AUTO(CConnection_query
);
193 nlassert(_Connected
);
195 nldebug("Executing query : [%s%s", queryString
.substr(0, 100).c_str(), 100<queryString
.size() ? "]" : "");
196 for (uint i
=100; i
<queryString
.size(); i
+=100)
197 nldebug("\t%s%s", queryString
.substr(i
, 100).c_str(), i
+100>queryString
.size() ? "]" : "");
199 TTime startDate
= CTime::getLocalTime();
201 int result
= mysql_real_query(_MysqlContext
, queryString
.c_str(), (unsigned long)queryString
.size());
204 // in all case, we try to reconnect
205 int merrno
= mysql_errno(_MysqlContext
);
206 //if (result == CR_SERVER_GONE_ERROR)
208 // reconnect and retry the request
209 nlinfo("%p Mysql error errno:%d result:%d : %s, try to reconnect...", _MysqlContext
, merrno
, result
, mysql_error(_MysqlContext
));
211 result
= mysql_real_query(_MysqlContext
, queryString
.c_str(), (unsigned long)queryString
.size());
214 nlwarning("Failed to reopen closed connection to send query '%s'", queryString
.c_str());
217 nlstopex(("SQL Strict mode, the above error is fatal"));
219 TTime endDate
= CTime::getLocalTime();
220 MSWRequestDuration
= uint32(endDate
- startDate
);
227 nlwarning("Mysql error errno:%d result:%d : %s", merrno
, result
, mysql_error(_MysqlContext
));
228 nlwarning(" in query '%s':", queryString
.c_str());
231 nlstopex(("SQL Strict mode, the above error is fatal"));
233 TTime endDate
= CTime::getLocalTime();
234 MSWRequestDuration
= uint32(endDate
- startDate
);
239 TTime endDate
= CTime::getLocalTime();
240 MSWRequestDuration
= uint32(endDate
- startDate
);
246 CUniquePtr
<CStoreResult
> CConnection::storeResult()
248 H_AUTO(CConnection_storeResult
);
249 MYSQL_RES
*res
= mysql_store_result(_MysqlContext
);
251 CUniquePtr
<CStoreResult
> sr(new CStoreResult(res
));
256 CUniquePtr
<CUseResult
> CConnection::useResult()
258 H_AUTO(CConnection_useResult
);
259 MYSQL_RES
*res
= mysql_use_result(_MysqlContext
);
261 CUniquePtr
<CUseResult
> sr(new CUseResult(res
));
268 void CResultBase::getDateField(uint32 fieldIndex
, uint32
&time
)
270 const char *str
= getRawField(fieldIndex
);
272 // read from a DATE_TIME field
273 MYSQL_FIELD
*field
= mysql_fetch_field_direct(_Result
, fieldIndex
);
274 nlassert(field
!= NULL
);
277 case FIELD_TYPE_DATETIME
:
279 // format is 'YYYY-MM-DD HH:MM:SS'
280 uint y
, m
, d
, h
, mn
, s
;
281 uint nbScanned
= sscanf(str
, "%u-%u-%u %u:%u:%u", &y
, &m
, &d
, &h
, &mn
, &s
);
282 nlassert(nbScanned
== 6);
284 myTm
.tm_year
= y
-1900;
291 myTm
.tm_isdst
= -1; // let the C runtime determine daylight adjustment
294 // Convert the local date into a UTC unix time
295 uint32 t
= (uint32
)nl_mktime(&myTm
);
300 case FIELD_TYPE_DATE
:
302 // format is 'YYYY-MM-DD'
304 uint nbScanned
= sscanf(str
, "%u-%u-%u", &y
, &m
, &d
);
305 nlassert(nbScanned
== 3);
307 myTm
.tm_year
= y
-1900;
314 myTm
.tm_isdst
= -1; // let the C runtime determine daylight adjustment
317 // Convert the local date into a UTC unix time
318 uint32 t
= (uint32
)nl_mktime(&myTm
);
323 case FIELD_TYPE_TIME
:
325 // format is 'HH:MM:SS'
327 uint nbScanned
= sscanf(str
, "%u:%u:%u", &h
, &m
, &s
);
328 nlassert(nbScanned
== 3);
337 myTm
.tm_isdst
= -1; // let the C runtime determine daylight adjustment
340 // Convert the local date into a UTC unix time
341 uint32 t
= (uint32
)nl_mktime(&myTm
);
346 case FIELD_TYPE_YEAR
:
350 uint nbScanned
= sscanf(str
, "%u", &y
);
351 nlassert(nbScanned
== 1);
353 myTm
.tm_year
= y
-1900;
360 myTm
.tm_isdst
= -1; // let the C runtime determine daylight adjustment
363 // Convert the local date into a UTC unix time
364 uint32 t
= (uint32
)nl_mktime(&myTm
);
371 // any other case, consider the field as a second unix time (seconds since 1970)
372 // format is 'SSSSSSSSSS..'
374 getField(fieldIndex
, t
);
385 // allowed transition table
386 bool AllowedTransition
[os_nb_state
][os_nb_state
] =
388 // transient clean dirty removed released
389 /*transient*/ { true, true, true, false, false },
390 /*clean*/ { false, true, true, true, true },
391 /*dirty*/ { false, true, true, true, false },
392 /*removed*/ { false, false, false, false, false },
393 /*released*/ { false, false, false, false, false },
396 NLMISC_SAFE_SINGLETON_IMPL(CPersistentCache
);
398 NLMISC_CATEGORISED_DYNVARIABLE("nope", bool, NOPEAllowReleaseDirtyObject
, "Set to true to allow dirty object to be removed from cache (this mean some modified data are lost)")
401 *pointer
= AllowedTransition
[os_dirty
][os_released
];
403 AllowedTransition
[os_dirty
][os_released
] = (*pointer
);