8785481999-07-01Henrik Grubbström (Grubba) /* * Some SQL utility functions. * They are kept here to avoid circular references. * * Henrik Grubbström 1999-07-01 */
a580e12000-09-27Fredrik Hübinette (Hubbe) #pike __REAL_VERSION__
a20af62000-09-26Fredrik Hübinette (Hubbe) 
36d2b82001-09-06Martin Nilsson //! Some SQL utility functions
8785481999-07-01Henrik Grubbström (Grubba) 
36d2b82001-09-06Martin Nilsson //! Quote a string so that it can safely be put in a query. //! //! @param s //! String to quote.
8785481999-07-01Henrik Grubbström (Grubba) string quote(string s) {
18096f2003-08-22Martin Nilsson  return replace(s, "\'", "\'\'");
8785481999-07-01Henrik Grubbström (Grubba) }
36d2b82001-09-06Martin Nilsson //! Throw an error in case an unimplemented function is called.
485cb11999-07-01Henrik Grubbström (Grubba) void fallback()
8785481999-07-01Henrik Grubbström (Grubba) {
5d16962003-04-22Martin Nilsson  error( "Function not supported in this database." );
8785481999-07-01Henrik Grubbström (Grubba) }
6458a72000-04-29Francesco Chemolli 
d6beb92012-09-19Martin Stjernholm //! Wrapper to handle zero. //! //! @seealso //! @[zero] protected class ZeroWrapper { //! @returns //! Returns the following: //! @string //! @value "NULL" //! If @[fmt] is @expr{'s'@}. //! @value "ZeroWrapper()" //! If @[fmt] is @expr{'O'@}. //! @endstring //! Otherwise it formats a @expr{0@} (zero). protected string _sprintf(int fmt, mapping(string:mixed) params) { if (fmt == 's') return "NULL"; if (fmt == 'O') return "ZeroWrapper()"; return sprintf(sprintf("%%*%c", fmt), params, 0); } } //! Instance of @[ZeroWrapper] used by @[handle_extraargs()]. protected ZeroWrapper zero = ZeroWrapper(); protected class NullArg { protected string _sprintf (int fmt) {return fmt == 'O' ? "NullArg()" : "NULL";} } protected NullArg null_arg = NullArg(); //! Handle @[sprintf]-based quoted arguments //! //! @param query //! The query as sent to one of the query functions. //! //! @param extraargs //! The arguments following the query. //!
db5c782012-09-19Martin Stjernholm //! @param bindings //! Optional bindings mapping to which additional bindings will be //! added. It's returned as the second element in the return value. //! A new mapping is used if this isn't specified. //!
d6beb92012-09-19Martin Stjernholm //! @returns //! Returns an array with two elements: //! @array //! @elem string 0 //! The query altered to use bindings-syntax. //! @elem mapping(string|int:mixed) 1
db5c782012-09-19Martin Stjernholm //! A bindings mapping. Zero if no bindings were added.
d6beb92012-09-19Martin Stjernholm //! @endarray array(string|mapping(string|int:mixed))
db5c782012-09-19Martin Stjernholm  handle_extraargs(string query, array(mixed) extraargs, void|mapping(string|int:mixed) bindings) {
d6beb92012-09-19Martin Stjernholm  array(mixed) args=allocate(sizeof(extraargs));
db5c782012-09-19Martin Stjernholm  if (!bindings) bindings = ([]);
d6beb92012-09-19Martin Stjernholm 
db5c782012-09-19Martin Stjernholm  int a, new_bindings;
d6beb92012-09-19Martin Stjernholm  foreach(extraargs; int j; mixed s) { if (stringp(s) || multisetp(s)) {
db5c782012-09-19Martin Stjernholm  string bind_name; do { bind_name = ":arg"+(a++);
65340d2014-08-15Martin Nilsson  } while (has_index (bindings, bind_name));
db5c782012-09-19Martin Stjernholm  args[j]=bind_name; bindings[bind_name] = s; new_bindings = 1;
d6beb92012-09-19Martin Stjernholm  continue; } if (intp(s) || floatp(s)) { args[j] = s || zero; continue; } if (objectp (s) && s->is_val_null) { args[j] = null_arg; continue; } error("Wrong type to query argument %d: %O\n", j + 1, s); }
db5c782012-09-19Martin Stjernholm  return ({sprintf(query,@args), new_bindings && bindings});
d6beb92012-09-19Martin Stjernholm }
580be42001-12-04Martin Nilsson //! Build a raw SQL query, given the cooked query and the variable bindings
36d2b82001-09-06Martin Nilsson //! It's meant to be used as an emulation engine for those drivers not //! providing such a behaviour directly (i.e. Oracle). //! The raw query can contain some variables (identified by prefixing
580be42001-12-04Martin Nilsson //! a colon to a name or a number (i.e. ":var" or ":2"). They will be
36d2b82001-09-06Martin Nilsson //! replaced by the corresponding value in the mapping. //! //! @param query
580be42001-12-04Martin Nilsson //! The query.
36d2b82001-09-06Martin Nilsson //! //! @param bindings //! Optional mapping containing the variable bindings. Make sure that //! no confusion is possible in the query. If necessary, change the //! variables' names.
4903d52000-04-29Francesco Chemolli string emulate_bindings(string query, mapping(string|int:mixed)|void bindings, void|object driver)
6458a72000-04-29Francesco Chemolli { array(string)k, v; if (!bindings) return query;
580be42001-12-04Martin Nilsson  function my_quote=(driver&&driver->quote?driver->quote:quote);
981f832005-04-10Martin Nilsson  v=map(values(bindings), lambda(mixed m) {
65340d2014-08-15Martin Nilsson  if(undefinedp(m))
0e09b42012-06-10Martin Stjernholm  return "NULL"; if (objectp (m) && m->is_val_null) // Note: Could need bug compatibility here - in some cases // we might be passed a null object that can be cast to // "", and before this it would be. This is an observed // compat issue in comment #7 in [bug 5900]. return "NULL";
39adc62008-06-30Stephen R. van den Berg  if(multisetp(m)) return sizeof(m) ? indices(m)[0] : "";
a9b54e2008-06-30Stephen R. van den Berg  return "'"+(intp(m)?(string)m:my_quote((string)m))+"'";
981f832005-04-10Martin Nilsson  });
580be42001-12-04Martin Nilsson  // Throws if mapping key is empty string.
981f832005-04-10Martin Nilsson  k=map(indices(bindings),lambda(string s){ return ( (stringp(s)&&s[0]==':') ? s : ":"+s); });
6458a72000-04-29Francesco Chemolli  return replace(query,k,v); }
e796fc2006-08-09Henrik Grubbström (Grubba)  //! Result object wrapper performing utf8 decoding of all fields. class UnicodeWrapper (
02a56d2006-12-30Martin Nilsson  // The wrapped result object.
9eaf1d2008-06-28Martin Nilsson  protected object master_result
e796fc2006-08-09Henrik Grubbström (Grubba)  ) { //! Returns the number of rows in the result. int num_rows() { return master_result->num_rows(); } //! Returns the number of fields in the result. int num_fields() { return master_result->num_fields(); } //! Returns @expr{1@} if there are no more rows in the result. int(0..1) eof() { return master_result->eof(); } //! Cached @[fetch_fields()] result.
9eaf1d2008-06-28Martin Nilsson  protected array(int|mapping(string:mixed)) field_info;
e796fc2006-08-09Henrik Grubbström (Grubba)  //! Returns Information about the fields in the result. //! //! The following fields are converted from UTF8 if present: //! @mapping //! @member string "name" //! The name of the field. Always present. //! @member string "table" //! The table the field is from. Not present from all databases. //! @member string "default" //! The default value for the column. Not available from all databases. //! @endmapping array(int|mapping(string:mixed)) fetch_fields() { if (!field_info) { field_info = master_result->fetch_fields(); foreach(field_info, int|mapping(string:mixed) field) { if (mappingp(field)) {
9c9a912018-05-24Henrik Grubbström (Grubba)  field->name = utf8_to_string(field->name, 2);
e796fc2006-08-09Henrik Grubbström (Grubba)  if (field->table) {
9c9a912018-05-24Henrik Grubbström (Grubba)  field->table = utf8_to_string(field->table, 2);
e796fc2006-08-09Henrik Grubbström (Grubba)  } if (field->default) {
9c9a912018-05-24Henrik Grubbström (Grubba)  field->default = utf8_to_string(field->default, 2);
e796fc2006-08-09Henrik Grubbström (Grubba)  } } } } return field_info; } //! Skip ahead the specified number of rows. void seek(int rows) { master_result->seek(rows); } //! Fetch the next row from the result. //! //! All strings in the result are decoded from UTF8. int|array(string) fetch_row() { int|array(string) row = master_result->fetch_row(); if (!arrayp(row)) return row;
cd16882006-08-11Henrik Grubbström (Grubba)  array(int|mapping(string:mixed)) field_info = fetch_fields();
e796fc2006-08-09Henrik Grubbström (Grubba)  foreach(row; int i; string|int val) {
8877692006-11-17Martin Stjernholm  if (stringp(val)) {
9c9a912018-05-24Henrik Grubbström (Grubba)  row[i] = utf8_to_string(val, 2);
8877692006-11-17Martin Stjernholm  } } return row; }
79ad902009-08-26Henrik Grubbström (Grubba)  //! JSON is always utf8 default, do nothing. int|string fetch_json_result() { return master_result->fetch_json_result(); }
8877692006-11-17Martin Stjernholm }
45ba202012-09-19Martin Stjernholm #if constant (___Mysql.mysql.HAVE_MYSQL_FIELD_CHARSETNR)
77ac702006-11-27Martin Stjernholm 
8877692006-11-17Martin Stjernholm class MySQLUnicodeWrapper //! Result wrapper for MySQL that performs UTF-8 decoding of all //! nonbinary fields. Useful if the result charset of the connection //! has been set to UTF-8. //! //! @note //! There's normally no need to use this class directly. It's used
9a3d002011-01-09Henrik Grubbström (Grubba) //! automatically when @[mysql.set_unicode_decode_mode] is activated.
8877692006-11-17Martin Stjernholm { inherit UnicodeWrapper; int|array(string) fetch_row() { int|array(string) row = master_result->fetch_row(); if (!arrayp(row)) return row; array(int|mapping(string:mixed)) field_info = fetch_fields(); foreach(row; int i; string|int val) { if (stringp(val) && field_info[i]->charsetnr != 63) {
9c9a912018-05-24Henrik Grubbström (Grubba)  row[i] = utf8_to_string(val, 2);
e796fc2006-08-09Henrik Grubbström (Grubba)  } } return row; } }
77ac702006-11-27Martin Stjernholm  #else class MySQLBrokenUnicodeWrapper
9bcbd22007-05-03Martin Stjernholm // This one is used to get a buggy unicode support when compiled with // an old MySQL client lib that doesn't have the charsetnr property in // the field info. It looks at the binary flag instead, which is set // for binary fields but might also be set for text fields (e.g. with // a definition like "VARCHAR(255) BINARY").
77ac702006-11-27Martin Stjernholm // // I.e. the effect of using this one is that text fields with the
9bcbd22007-05-03Martin Stjernholm // binary flag won't be correctly decoded in unicode decode mode. // // This has to be enabled either by passing "broken-unicode" as // charset to Sql.mysql.create or Sql.mysql.set_charset, by calling // Sql.mysql.set_unicode_decode_mode(-1), or by defining the // environment variable PIKE_BROKEN_MYSQL_UNICODE_MODE. That will // cause this buggy variant to be used if and only if the MySQL client // lib doesn't support the charsetnr property.
77ac702006-11-27Martin Stjernholm { inherit UnicodeWrapper; int|array(string) fetch_row() { int|array(string) row = master_result->fetch_row(); if (!arrayp(row)) return row; array(int|mapping(string:mixed)) field_info = fetch_fields(); foreach(row; int i; string|int val) { if (stringp(val) && field_info[i]->flags && !field_info[i]->flags->binary) {
9c9a912018-05-24Henrik Grubbström (Grubba)  row[i] = utf8_to_string(val, 2);
77ac702006-11-27Martin Stjernholm  } } return row; } }
8877692006-11-17Martin Stjernholm #endif