f6bbe21997-03-06Henrik Grubbström (Grubba) /*
485f3b2007-05-03Martin Stjernholm  * $Id: mysql.pike,v 1.32 2007/05/03 13:57:35 mast Exp $
f6bbe21997-03-06Henrik Grubbström (Grubba)  * * Glue for the Mysql-module */
36d2b82001-09-06Martin Nilsson //! Implements the glue needed to access the Mysql-module from the generic //! SQL module.
4182211998-03-20Henrik Grubbström (Grubba) 
a580e12000-09-27Fredrik Hübinette (Hubbe) #pike __REAL_VERSION__
a20af62000-09-26Fredrik Hübinette (Hubbe) 
6f7b202004-04-16Henrik Grubbström (Grubba) #if constant(Mysql.mysql)
53675a1998-10-17Henrik Grubbström (Grubba) 
f6bbe21997-03-06Henrik Grubbström (Grubba) inherit Mysql.mysql;
2f9cc31998-03-19Henrik Grubbström (Grubba) 
98cdce2006-08-15Henrik Grubbström (Grubba) #define UNICODE_DECODE_MODE 1 // Unicode decode mode #define LATIN1_UNICODE_ENCODE_MODE 2 // Unicode encode mode with latin1 charset #define UTF8_UNICODE_ENCODE_MODE 4 // Unicode encode mode with utf8 charset
dd12882006-09-15Martin Stjernholm #ifdef MYSQL_CHARSET_DEBUG #define CH_DEBUG(X...) werror("Sql.mysql: " + X) #else #define CH_DEBUG(X...) #endif
485f3b2007-05-03Martin Stjernholm #ifndef (Mysql.mysql.HAVE_MYSQL_FIELD_CHARSETNR) // Recognition constant to tell that the unicode decode mode would use // the buggy MySQLBrokenUnicodeWrapper if it would be enabled through // any of the undocumented methods. constant unicode_decode_mode_is_broken = 1; #endif
dd12882006-09-15Martin Stjernholm // Set to the above if the connection is requested to be in one of the // unicode modes. latin1 unicode encode mode is enabled by default; it // should be compatible with earlier pike versions.
98cdce2006-08-15Henrik Grubbström (Grubba) static int utf8_mode; // The charset, either "latin1" or "utf8", currently assigned to // character_set_client when unicode encode mode is enabled. Zero when // the connection charset has been set to something else than "latin1" // or "unicode". static string send_charset; static void update_unicode_encode_mode_from_charset (string charset) { switch (charset) { // Lowercase assumed. case "latin1": utf8_mode |= LATIN1_UNICODE_ENCODE_MODE; utf8_mode &= ~UTF8_UNICODE_ENCODE_MODE; send_charset = "latin1";
dd12882006-09-15Martin Stjernholm  CH_DEBUG ("Entering latin1 encode mode.\n");
98cdce2006-08-15Henrik Grubbström (Grubba)  break; case "unicode": utf8_mode |= UTF8_UNICODE_ENCODE_MODE; utf8_mode &= ~LATIN1_UNICODE_ENCODE_MODE; send_charset = "utf8";
dd12882006-09-15Martin Stjernholm  CH_DEBUG ("Entering unicode encode mode.\n");
98cdce2006-08-15Henrik Grubbström (Grubba)  break; default: // Wrong charset - the mode can't be used. utf8_mode |= LATIN1_UNICODE_ENCODE_MODE|UTF8_UNICODE_ENCODE_MODE; send_charset = 0;
dd12882006-09-15Martin Stjernholm  CH_DEBUG ("Not entering latin1/unicode encode mode " "due to incompatible charset %O.\n", charset);
98cdce2006-08-15Henrik Grubbström (Grubba)  break; } } int(0..1) set_unicode_encode_mode (int enable) //! Enables or disables unicode encode mode. //! //! In this mode, if the server supports UTF-8 and the connection //! charset is @expr{latin1@} (the default) or @expr{unicode@} then //! @[big_query] handles wide unicode queries. Enabled by default. //! //! Unicode encode mode works as follows: Eight bit strings are sent //! as @expr{latin1@} and wide strings are sent using @expr{utf8@}. //! @[big_query] sends @expr{SET character_set_client@} statements as //! necessary to update the charset on the server side. If the server //! doesn't support that then it fails, but the wide string query //! would fail anyway. //! //! To make this transparent, string literals with introducers (e.g. //! @expr{_binary 'foo'@}) are excluded from the UTF-8 encoding. This //! means that @[big_query] needs to do some superficial parsing of //! the query when it is a wide string. //! //! @returns //! @int //! @value 1 //! Unicode encode mode is enabled. //! @value 0 //! Unicode encode mode couldn't be enabled because an //! incompatible connection charset is set. You need to do //! @expr{@[set_charset]("latin1")@} or //! @expr{@[set_charset]("unicode")@} to enable it. //! @endint //! //! @note //! Note that this mode doesn't affect the MySQL system variable //! @expr{character_set_connection@}, i.e. it will still be set to //! @expr{latin1@} by default which means server functions like //! @expr{UPPER()@} won't handle non-@expr{latin1@} characters //! correctly in all cases. //! //! To fix that, do @expr{@[set_charset]("unicode")@}. That will //! allow unicode encode mode to work while @expr{utf8@} is fully //! enabled at the server side. //! //! Tip: If you enable @expr{utf8@} on the server side, you need to //! send raw binary strings as @expr{_binary'...'@}. Otherwise they //! will get UTF-8 encoded by the server. //! //! @note //! When unicode encode mode is enabled and the connection charset //! is @expr{latin1@}, the charset accepted by @[big_query] is not //! quite Unicode since @expr{latin1@} is based on @expr{cp1252@}. //! The differences are in the range @expr{0x80..0x9f@} where
485f3b2007-05-03Martin Stjernholm //! Unicode has control chars.
98cdce2006-08-15Henrik Grubbström (Grubba) //! //! This small discrepancy is not present when the connection //! charset is @expr{unicode@}. //! //! @seealso //! @[set_unicode_decode_mode], @[set_charset] { if (enable) update_unicode_encode_mode_from_charset (lower_case (get_charset())); else { utf8_mode &= ~(LATIN1_UNICODE_ENCODE_MODE|UTF8_UNICODE_ENCODE_MODE); send_charset = 0;
dd12882006-09-15Martin Stjernholm  CH_DEBUG("Disabling unicode encode mode.\n");
98cdce2006-08-15Henrik Grubbström (Grubba)  } return !!send_charset; } int get_unicode_encode_mode() //! Returns nonzero if unicode encode mode is enabled, zero otherwise. //! //! @seealso //! @[set_unicode_encode_mode] { return !!send_charset; } void set_unicode_decode_mode (int enable) //! Enable or disable unicode decode mode. //! //! In this mode, if the server supports UTF-8 then non-binary text
ffe6dc2006-11-17Martin Stjernholm //! strings in results are automatically decoded to (possibly wide) //! unicode strings. Not enabled by default.
98cdce2006-08-15Henrik Grubbström (Grubba) //! //! The statement "@expr{SET character_set_results = utf8@}" is sent //! to the server to enable the mode. When the mode is disabled, //! "@expr{SET character_set_results = xxx@}" is sent, where //! @expr{xxx@} is the connection charset that @[get_charset] returns. //! //! @param enable //! Nonzero enables this feature, zero disables it. //! //! @throws //! Throws an exception if the server doesn't support this, i.e. if //! the statement above fails. The MySQL system variable //! @expr{character_set_results@} was added in MySQL 4.1.1. //!
485f3b2007-05-03Martin Stjernholm //! An error is also thrown if Pike has been compiled with a MySQL //! client library older than 4.1.0, which lack the necessary //! support for this.
98cdce2006-08-15Henrik Grubbström (Grubba) //! //! @seealso //! @[set_unicode_encode_mode] {
55a42c2006-12-05Henrik Grubbström (Grubba) #if !constant (Mysql.mysql.HAVE_MYSQL_FIELD_CHARSETNR)
485f3b2007-05-03Martin Stjernholm  // Undocumented feature for old mysql libs. See // MySQLBrokenUnicodeWrapper for details. if (!(<0, -1>)[enable] && !getenv("PIKE_BROKEN_MYSQL_UNICODE_MODE")) { predef::error ("Unicode decode mode not supported - " "compiled with MySQL client library < 4.1.0.\n");
55a42c2006-12-05Henrik Grubbström (Grubba)  } #endif
485f3b2007-05-03Martin Stjernholm 
98cdce2006-08-15Henrik Grubbström (Grubba)  if (enable) {
dd12882006-09-15Martin Stjernholm  CH_DEBUG("Enabling unicode decode mode.\n");
98cdce2006-08-15Henrik Grubbström (Grubba)  ::big_query ("SET character_set_results = utf8"); utf8_mode |= UNICODE_DECODE_MODE; } else {
dd12882006-09-15Martin Stjernholm  CH_DEBUG("Disabling unicode decode mode.\n");
98cdce2006-08-15Henrik Grubbström (Grubba)  ::big_query ("SET character_set_results = " + get_charset()); utf8_mode &= ~UNICODE_DECODE_MODE; } }
2c4b2c2006-11-27Martin Stjernholm 
98cdce2006-08-15Henrik Grubbström (Grubba) int get_unicode_decode_mode() //! Returns nonzero if unicode decode mode is enabled, zero otherwise. //! //! @seealso //! @[set_unicode_decode_mode] { return utf8_mode & UNICODE_DECODE_MODE; } void set_charset (string charset) //! Changes the connection charset. Works similar to sending the query //! @expr{SET NAMES @[charset]@} but also records the charset on the //! client side so that various client functions work correctly. //! //! @[charset] is a MySQL charset name or the special value //! @expr{"unicode"@} (see below). You can use @expr{SHOW CHARACTER //! SET@} to get a list of valid charsets. //! //! Specifying @expr{"unicode"@} as charset is the same as //! @expr{"utf8"@} except that unicode encode and decode modes are //! enabled too. Briefly, this means that you can send queries as //! unencoded unicode strings and will get back non-binary text //! results as unencoded unicode strings. See //! @[set_unicode_encode_mode] and @[set_unicode_decode_mode] for //! further details. //! //! @throws //! Throws an exception if the server doesn't support this, i.e. if //! the statement @expr{SET NAMES@} fails. Support for it was added //! in MySQL 4.1.0. //! //! @note //! If @[charset] is @expr{"latin1"@} and unicode encode mode is //! enabled (the default) then @[big_query] can send wide unicode //! queries transparently if the server supports UTF-8. See //! @[set_unicode_encode_mode]. //! //! @note //! If unicode decode mode is already enabled (see //! @[set_unicode_decode_mode]) then this function won't affect the //! result charset (i.e. the MySQL system variable //! @expr{character_set_results@}). //! //! Actually, a query @expr{SET character_set_results = utf8@} will //! be sent immediately after setting the charset as above if //! unicode decode mode is enabled and @[charset] isn't //! @expr{"utf8"@}. //! //! @note //! You should always use either this function or the //! @expr{"mysql_charset_name"@} option to @[create] to set the //! connection charset, or more specifically the charset that the //! server expects queries to have (i.e. the MySQL system variable //! @expr{character_set_client@}). Otherwise @[big_query] might not //! work correctly. //! //! Afterwards you may change the system variable //! @expr{character_set_connection@}, and also //! @expr{character_set_results@} if unicode decode mode isn't //! enabled. //! //! @note //! The MySQL @expr{latin1@} charset is close to Windows //! @expr{cp1252@}. The difference from ISO-8859-1 is a bunch of //! printable chars in the range @expr{0x80..0x9f@} (which contains //! control chars in ISO-8859-1). For instance, the euro currency //! sign is @expr{0x80@}. //! //! You can use the @expr{mysql-latin1@} encoding in the //! @[Locale.Charset] module to do conversions, or just use the //! special @expr{"unicode"@} charset instead. //! //! @seealso //! @[get_charset], @[set_unicode_encode_mode], @[set_unicode_decode_mode] { charset = lower_case (charset);
dd12882006-09-15Martin Stjernholm  CH_DEBUG("Setting charset to %O.\n", charset);
485f3b2007-05-03Martin Stjernholm  int broken_unicode = charset == "broken-unicode"; if (broken_unicode) charset = "unicode";
98cdce2006-08-15Henrik Grubbström (Grubba)  ::set_charset (charset == "unicode" ? "utf8" : charset); if (charset == "unicode" || utf8_mode & (LATIN1_UNICODE_ENCODE_MODE|UTF8_UNICODE_ENCODE_MODE)) update_unicode_encode_mode_from_charset (charset);
ffe6dc2006-11-17Martin Stjernholm  if (charset == "unicode") { #if constant (Mysql.mysql.HAVE_MYSQL_FIELD_CHARSETNR)
98cdce2006-08-15Henrik Grubbström (Grubba)  utf8_mode |= UNICODE_DECODE_MODE;
ffe6dc2006-11-17Martin Stjernholm #else
485f3b2007-05-03Martin Stjernholm  if (broken_unicode || getenv ("PIKE_BROKEN_MYSQL_UNICODE_MODE")) // Undocumented feature for old mysql libs. See // MySQLBrokenUnicodeWrapper for details.
2c4b2c2006-11-27Martin Stjernholm  utf8_mode |= UNICODE_DECODE_MODE; else predef::error ("Unicode decode mode not supported - " "compiled with MySQL client library < 4.1.0.\n");
ffe6dc2006-11-17Martin Stjernholm #endif }
98cdce2006-08-15Henrik Grubbström (Grubba)  else if (utf8_mode & UNICODE_DECODE_MODE && charset != "utf8") // This setting has been overridden by ::set_charset, so we need // to reinstate it. ::big_query ("SET character_set_results = utf8"); } string get_charset() //! Returns the MySQL name for the current connection charset. //! //! Returns @expr{"unicode"@} if unicode encode mode is enabled and //! UTF-8 is used on the server side (i.e. in //! @expr{character_set_connection@}). //! //! @note //! In servers with full charset support (i.e. MySQL 4.1.0 or //! later), this corresponds to the MySQL system variable //! @expr{character_set_client@} (with one exception - see next //! note) and thus controls the charset in which queries are sent. //! The charset used for text strings in results might be something //! else (and typically is if unicode decode mode is enabled; see //! @[set_unicode_decode_mode]). //! //! @note //! If the returned charset is @expr{latin1@} or @expr{unicode@} and //! unicode encode mode is enabled (the default) then //! @expr{character_set_client@} in the server might be either //! @expr{latin1@} or @expr{utf8@}, depending on the last sent //! query. See @[set_unicode_encode_mode] for more info. //! //! @seealso //! @[set_charset] { if (utf8_mode & UTF8_UNICODE_ENCODE_MODE && send_charset)
485f3b2007-05-03Martin Stjernholm  // We don't try to be symmetric with set_charset when the // broken-unicode kludge is in use. That since this reflects the // setting on the encode side only.
98cdce2006-08-15Henrik Grubbström (Grubba)  return "unicode"; return ::get_charset(); }
4c14422002-01-02Per Hedbor #if constant( Mysql.mysql.MYSQL_NO_ADD_DROP_DB ) // Documented in the C-file. void create_db( string db ) { ::big_query( "CREATE DATABASE "+db ); } void drop_db( string db ) { ::big_query( "DROP DATABASE "+db ); } #endif
36d2b82001-09-06Martin Nilsson //! Quote a string so that it can safely be put in a query. //! //! @param s //! String to quote.
6279f61998-03-19Henrik Grubbström (Grubba) string quote(string s)
2f9cc31998-03-19Henrik Grubbström (Grubba) {
18096f2003-08-22Martin Nilsson  return replace(s,
2f9cc31998-03-19Henrik Grubbström (Grubba)  ({ "\\", "\"", "\0", "\'", "\n", "\r" }),
18096f2003-08-22Martin Nilsson  ({ "\\\\", "\\\"", "\\0", "\\\'", "\\n", "\\r" }));
2f9cc31998-03-19Henrik Grubbström (Grubba) }
93f5441998-07-03Martin Stjernholm 
98cdce2006-08-15Henrik Grubbström (Grubba) string latin1_to_utf8 (string s) //! Converts a string in MySQL @expr{latin1@} format to UTF-8. { return string_to_utf8 (replace (s, ([
ef17a42006-09-18Martin Stjernholm  "\x80": "\x20AC", /*"\x81": "\x0081",*/ "\x82": "\x201A", "\x83": "\x0192", "\x84": "\x201E", "\x85": "\x2026", "\x86": "\x2020", "\x87": "\x2021", "\x88": "\x02C6", "\x89": "\x2030", "\x8a": "\x0160", "\x8b": "\x2039", "\x8c": "\x0152", /*"\x8d": "\x008D",*/ "\x8e": "\x017D", /*"\x8f": "\x008F",*/ /*"\x90": "\x0090",*/ "\x91": "\x2018", "\x92": "\x2019", "\x93": "\x201C", "\x94": "\x201D", "\x95": "\x2022", "\x96": "\x2013", "\x97": "\x2014", "\x98": "\x02DC", "\x99": "\x2122", "\x9a": "\x0161", "\x9b": "\x203A", "\x9c": "\x0153", /*"\x9d": "\x009D",*/ "\x9e": "\x017E", "\x9f": "\x0178",
98cdce2006-08-15Henrik Grubbström (Grubba)  ]))); } string utf8_encode_query (string q, function(string:string) encode_fn) //! Encodes the appropriate sections of the query with @[encode_fn]. //! Everything except strings prefixed by an introducer (i.e. //! @expr{_something@} or @expr{N@}) is encoded. { // We need to find the segments that shouldn't be encoded. string e = ""; while (1) { sscanf(q, "%[^\'\"]%s", string prefix, string suffix); e += encode_fn (prefix); if (suffix == "") break; string quote = suffix[..0]; int start = 1; int end; while ((end = search(suffix, quote, start)) >= 0) { if (suffix[end-1] == '\\') { // Count the number of preceding back-slashes. // if odd, continue searching after the quote. int i; for (i = 2; i < end; i++) { if (suffix[end - i] != '\\') break; } if (!(i & 1)) { start = end+1; continue; } } if (sizeof(suffix) == end+1) break; if (suffix[end+1] == quote[0]) { // Quote quoted by doubling. start = end+2; continue; } break; } #define IS_IDENTIFIER_CHAR(chr) (Unicode.is_wordchar (chr) || \ (<'_', '$'>)[chr]) int intpos = -1; // Optimize the use of _binary. if (has_suffix (prefix, "_binary")) intpos = sizeof (prefix) - sizeof ("_binary"); else if (has_suffix (prefix, "_binary ")) intpos = sizeof (prefix) - sizeof ("_binary "); else { // Find the white-space suffix of the prefix. int i = sizeof(prefix); while (i--) { if (!(< ' ', '\n', '\r', '\t' >)[prefix[i]]) break; } if (i >= 0) { if ((<'n', 'N'>)[prefix[i]]) // Probably got a national charset string. intpos = i; else { // The following assumes all possible charset names contain // only [a-zA-Z0-9_$] and are max 32 chars (from // MY_CS_NAME_SIZE in m_ctype.h). sscanf (reverse (prefix[i - 33..i]), "%[a-zA-Z0-9_$]%s", string rev_intro, string rest); if (sizeof (rev_intro) && rev_intro[-1] == '_' && sizeof (rest)) intpos = i - sizeof (rev_intro) + 1; } } } int got_introducer; if (intpos == 0) // The prefix begins with the introducer. got_introducer = 1; else if (intpos > 0) { // Check that the introducer sequence we found isn't a suffix of // some longer keyword or identifier. int prechar = prefix[intpos - 1]; if (!IS_IDENTIFIER_CHAR (prechar)) got_introducer = 1; } if (got_introducer) { string s = suffix[..end]; if (String.width (s) > 8) { string encoding = prefix[intpos..]; if (has_prefix (encoding, "_")) sscanf (encoding[1..], "%[a-zA-Z0-9]", encoding); else encoding = "utf8"; // Gotta be "N". s = s[1..sizeof (s) - 2]; if (sizeof (s) > 40) s = sprintf ("%O...", s[..37]); else s = sprintf ("%O", s); predef::error ("A string in the query should be %s encoded " "but it is wide: %s\n", encoding, s); } e += s; } else { e += encode_fn (suffix[..end]); } q = suffix[end+1..]; } return e; }
93f5441998-07-03Martin Stjernholm // The following time conversion functions assumes the SQL server // handles time in this local timezone. They map the special zero // time/date spec to 0. private constant timezone = localtime (0)->timezone;
36d2b82001-09-06Martin Nilsson //! Converts a system time value to an appropriately formatted time //! spec for the database. //! //! @param time //! Time to encode. //! //! @param date //! If nonzero then time is taken as a "full" unix time spec //! (where the date part is ignored), otherwise it's converted as a //! seconds-since-midnight value.
93f5441998-07-03Martin Stjernholm string encode_time (int time, void|int date) { if (date) { if (!time) return "000000"; mapping(string:int) ct = localtime (time); return sprintf ("%02d%02d%02d", ct->hour, ct->min, ct->sec); } else return sprintf ("%02d%02d%02d", time / 3600 % 24, time / 60 % 60, time % 60); }
36d2b82001-09-06Martin Nilsson //! Converts a system time value to an appropriately formatted //! date-only spec for the database. //! //! @param time //! Time to encode.
93f5441998-07-03Martin Stjernholm string encode_date (int time) { if (!time) return "00000000"; mapping(string:int) ct = localtime (time); return sprintf ("%04d%02d%02d", ct->year + 1900, ct->mon + 1, ct->mday); }
36d2b82001-09-06Martin Nilsson //! Converts a system time value to an appropriately formatted //! date and time spec for the database. //! //! @param time //! Time to encode.
93f5441998-07-03Martin Stjernholm string encode_datetime (int time) { if (!time) return "00000000000000"; mapping(string:int) ct = localtime (time); return sprintf ("%04d%02d%02d%02d%02d%02d", ct->year + 1900, ct->mon + 1, ct->mday, ct->hour, ct->min, ct->sec); }
36d2b82001-09-06Martin Nilsson //! Converts a database time spec to a system time value. //! //! @param timestr //! Time spec to decode. //! //! @param date //! Take the date part from this system time value. If zero, a //! seconds-since-midnight value is returned.
93f5441998-07-03Martin Stjernholm int decode_time (string timestr, void|int date) { int hour = 0, min = 0, sec = 0; if (sscanf (timestr, "%d:%d:%d", hour, min, sec) <= 1) sscanf (timestr, "%2d%2d%2d", hour, min, sec); if (date && (hour || min || sec)) { mapping(string:int) ct = localtime (date); return mktime (sec, min, hour, ct->mday, ct->mon, ct->year, ct->isdst, ct->timezone); } else return (hour * 60 + min) * 60 + sec; }
36d2b82001-09-06Martin Nilsson //! Converts a database date-only spec to a system time value. //! Assumes 4-digit years. //! //! @param datestr //! Date spec to decode.
93f5441998-07-03Martin Stjernholm int decode_date (string datestr) { int year = 0, mon = 0, mday = 0, n; n = sscanf (datestr, "%d-%d-%d", year, mon, mday); if (n <= 1) n = sscanf (datestr, "%4d%2d%2d", year, mon, mday); if (year || mon || mday) return mktime (0, 0, 0, n == 3 ? mday : 1, n >= 2 && mon - 1, year - 1900, -1, timezone); else return 0; }
36d2b82001-09-06Martin Nilsson //! Converts a database date and time spec to a system time value. //! Can decode strings missing the time part. //! //! @param datestr //! Date and time spec to decode.
93f5441998-07-03Martin Stjernholm int decode_datetime (string timestr) { array(string) a = timestr / " "; if (sizeof (a) == 2) return decode_date (a[0]) + decode_time (a[1]); else { int n = sizeof (timestr); if (n >= 12) return decode_date (timestr[..n-7]) + decode_time (timestr[n-6..n-1]); else return decode_date (timestr); } }
53675a1998-10-17Henrik Grubbström (Grubba) 
98cdce2006-08-15Henrik Grubbström (Grubba) Mysql.mysql_result big_query (string query, mapping(string|int:mixed)|void bindings, void|string charset)
6458a72000-04-29Francesco Chemolli {
98cdce2006-08-15Henrik Grubbström (Grubba)  if (bindings) query = .sql_util.emulate_bindings(query,bindings,this); string restore_charset; if (charset) { restore_charset = send_charset || get_charset();
824d4d2006-08-22Henrik Grubbström (Grubba)  if (charset != restore_charset) {
dd12882006-09-15Martin Stjernholm  CH_DEBUG ("Switching charset from %O to %O (due to charset arg).\n", restore_charset, charset);
98cdce2006-08-15Henrik Grubbström (Grubba)  ::big_query ("SET character_set_client=" + charset);
dd12882006-09-15Martin Stjernholm  /* Can't be changed automatically - has side effects. /mast */ /* ::big_query("SET character_set_connection=" + charset); */
824d4d2006-08-22Henrik Grubbström (Grubba)  } else
98cdce2006-08-15Henrik Grubbström (Grubba)  restore_charset = 0; } else if (send_charset) { string new_send_charset;
6458a72000-04-29Francesco Chemolli 
98cdce2006-08-15Henrik Grubbström (Grubba)  if (utf8_mode & LATIN1_UNICODE_ENCODE_MODE) { if (String.width (query) == 8) new_send_charset = "latin1"; else {
dd12882006-09-15Martin Stjernholm  CH_DEBUG ("Converting (mysql-)latin1 query to utf8.\n");
98cdce2006-08-15Henrik Grubbström (Grubba)  query = utf8_encode_query (query, latin1_to_utf8); new_send_charset = "utf8"; } } else { /* utf8_mode & UTF8_UNICODE_ENCODE_MODE */ if (_can_send_as_latin1 (query)) new_send_charset = "latin1"; else {
dd12882006-09-15Martin Stjernholm  CH_DEBUG ("Converting query to utf8.\n");
98cdce2006-08-15Henrik Grubbström (Grubba)  query = utf8_encode_query (query, string_to_utf8); new_send_charset = "utf8"; } } if (new_send_charset != send_charset) {
dd12882006-09-15Martin Stjernholm  CH_DEBUG ("Switching charset from %O to %O.\n", send_charset, new_send_charset); if (mixed err = catch { ::big_query ("SET character_set_client=" + new_send_charset); /* Can't be changed automatically - has side effects. /mast */ /* ::big_query("SET character_set_connection=" + new_send_charset); */ }) {
a1ff6c2006-08-15Henrik Grubbström (Grubba)  if (new_send_charset == "utf8")
98cdce2006-08-15Henrik Grubbström (Grubba)  predef::error ("The query is a wide string " "and the MySQL server doesn't support UTF-8: %s\n", describe_error (err));
dd12882006-09-15Martin Stjernholm  else
f430ea2006-09-15Martin Stjernholm  throw (err);
98cdce2006-08-15Henrik Grubbström (Grubba)  } send_charset = new_send_charset; } }
dd12882006-09-15Martin Stjernholm  CH_DEBUG ("Sending query with charset %O: %O.\n", charset || send_charset, query);
98cdce2006-08-15Henrik Grubbström (Grubba)  int|object res = ::big_query(query); if (restore_charset) { if (send_charset && (<"latin1", "utf8">)[charset]) send_charset = charset;
824d4d2006-08-22Henrik Grubbström (Grubba)  else {
dd12882006-09-15Martin Stjernholm  CH_DEBUG ("Restoring charset %O.\n", restore_charset); ::big_query ("SET character_set_client=" + restore_charset); /* Can't be changed automatically - has side effects. /mast */ /* ::big_query("SET character_set_connection=" + restore_charset); */
824d4d2006-08-22Henrik Grubbström (Grubba)  }
98cdce2006-08-15Henrik Grubbström (Grubba)  } if (!objectp(res)) return res; if (utf8_mode & UNICODE_DECODE_MODE) {
ffe6dc2006-11-17Martin Stjernholm  CH_DEBUG ("Using MySQLUnicodeWrapper for result.\n");
2c4b2c2006-11-27Martin Stjernholm #if constant (Mysql.mysql.HAVE_MYSQL_FIELD_CHARSETNR)
ffe6dc2006-11-17Martin Stjernholm  return .sql_util.MySQLUnicodeWrapper(res);
2c4b2c2006-11-27Martin Stjernholm #else return .sql_util.MySQLBrokenUnicodeWrapper (res);
ffe6dc2006-11-17Martin Stjernholm #endif
2c4b2c2006-11-27Martin Stjernholm  }
98cdce2006-08-15Henrik Grubbström (Grubba)  return res; }
d7bd0e2003-12-31Martin Nilsson 
0e5be12002-01-17Martin Nilsson int(0..1) is_keyword( string name ) //! Return 1 if the argument @[name] is a mysql keyword. {
98cdce2006-08-15Henrik Grubbström (Grubba)  // FIXME: Document which version of MySQL this is up-to-date with.
0e5be12002-01-17Martin Nilsson  return (< "action", "add", "aggregate", "all", "alter", "after", "and", "as", "asc", "avg", "avg_row_length", "auto_increment", "between", "bigint", "bit", "binary", "blob", "bool", "both", "by", "cascade", "case", "char", "character", "change", "check", "checksum", "column", "columns", "comment", "constraint", "create", "cross", "current_date", "current_time", "current_timestamp", "data", "database", "databases", "date", "datetime", "day", "day_hour", "day_minute", "day_second", "dayofmonth", "dayofweek", "dayofyear", "dec", "decimal", "default", "delayed", "delay_key_write", "delete", "desc", "describe", "distinct", "distinctrow", "double", "drop", "end", "else", "escape", "escaped", "enclosed", "enum", "explain", "exists", "fields", "file", "first", "float", "float4", "float8", "flush", "foreign", "from", "for", "full", "function", "global", "grant", "grants", "group", "having", "heap", "high_priority", "hour", "hour_minute", "hour_second", "hosts", "identified", "ignore", "in", "index", "infile", "inner", "insert", "insert_id", "int", "integer", "interval", "int1", "int2", "int3", "int4", "int8", "into", "if", "is", "isam", "join", "key", "keys", "kill", "last_insert_id", "leading", "left", "length", "like", "lines", "limit", "load", "local", "lock", "logs", "long", "longblob", "longtext", "low_priority", "max", "max_rows", "match", "mediumblob", "mediumtext", "mediumint", "middleint", "min_rows", "minute", "minute_second", "modify", "month", "monthname", "myisam", "natural", "numeric", "no", "not", "null", "on", "optimize", "option", "optionally", "or", "order", "outer", "outfile", "pack_keys", "partial", "password", "precision", "primary", "procedure", "process", "processlist", "privileges", "read", "real", "references", "reload", "regexp", "rename", "replace", "restrict", "returns", "revoke", "rlike", "row", "rows", "second", "select", "set", "show", "shutdown", "smallint", "soname", "sql_big_tables", "sql_big_selects", "sql_low_priority_updates", "sql_log_off", "sql_log_update", "sql_select_limit", "sql_small_result", "sql_big_result", "sql_warnings", "straight_join", "starting", "status", "string", "table", "tables", "temporary", "terminated", "text", "then", "time", "timestamp", "tinyblob", "tinytext", "tinyint", "trailing", "to", "type", "use", "using", "unique", "unlock", "unsigned", "update", "usage", "values", "varchar", "variables", "varying", "varbinary", "with", "write", "when", "where", "year", "year_month", "zerofill", >)[ lower_case(name) ]; }
98cdce2006-08-15Henrik Grubbström (Grubba) static void create(string|void host, string|void database, string|void user, string|void password, mapping(string:string|int)|void options) { if (options) {
485f3b2007-05-03Martin Stjernholm  string charset = options->mysql_charset_name ? lower_case (options->mysql_charset_name) : "latin1"; int broken_unicode = charset == "broken-unicode"; if (broken_unicode) charset = "unicode";
98cdce2006-08-15Henrik Grubbström (Grubba)  if (charset == "unicode") options->mysql_charset_name = "utf8"; ::create(host||"", database||"", user||"", password||"", options); update_unicode_encode_mode_from_charset (lower_case (charset));
2c4b2c2006-11-27Martin Stjernholm #if !constant (Mysql.mysql.HAVE_MYSQL_FIELD_CHARSETNR)
485f3b2007-05-03Martin Stjernholm  // Undocumented feature for old mysql libs. See // MySQLBrokenUnicodeWrapper for details. if (broken_unicode || getenv ("PIKE_BROKEN_MYSQL_UNICODE_MODE")) {
2c4b2c2006-11-27Martin Stjernholm #endif if (charset == "unicode") utf8_mode |= UNICODE_DECODE_MODE; else if (options->unicode_decode_mode) set_unicode_decode_mode (1); #if !constant (Mysql.mysql.HAVE_MYSQL_FIELD_CHARSETNR) } else if (charset == "unicode" || options->unicode_decode_mode) predef::error ("Unicode decode mode not supported - " "compiled with MySQL client library < 4.1.0.\n");
ffe6dc2006-11-17Martin Stjernholm #endif
98cdce2006-08-15Henrik Grubbström (Grubba)  } else { ::create(host||"", database||"", user||"", password||""); update_unicode_encode_mode_from_charset ("latin1"); } }
ffaf452004-04-14Martin Nilsson #else constant this_program_does_not_exist=1;
53675a1998-10-17Henrik Grubbström (Grubba) #endif /* constant(Mysql.mysql) */