← Back to team overview

maria-developers team mailing list archive

Re: INFORMATION_SCHEMA.SCHEMATA and INFORMATION_SCHEMA.SCHEMA_PRIVILEGES

 

On Tue, Feb 21, 2017 at 09:41:07PM +0100, Sergei Golubchik wrote:
> Hi, Witold!
> 
> On Feb 21, Witold Filipczyk wrote:
> > Hi,
> > I'm newbie, I have problem with INFORMATION_SCHEMA.
> > There is mariadb-10.0 with around 100000 databases. Disks are slow,
> > but most users have one database,
> > a few have more than one, but it is not a big number. Queries like
> > SHOW DATABASES or SELECT * FROM INFORMATION_SCHEMA.SCHEMATA
> > bring to knees whole server.
> > The idea to resolve it is:
> > in find_files(...) instead of the for loop execute query:
> > SELECT REPLACE(TABLE_SCHEMA, '\\', '') FROM INFORMATION_SCHEMA.SCHEMA_PRIVILEGES WHERE PRIVILEGE_TYPE = 'SELECT';
> > There are databases with names with _ in them and \_ in GRANTS.
> 
> That's a very interesting idea.
> 
> Basically, you're saying that if a user has no probal privileges and
> database privileges on only one database and no wildcards in the
> database name (*), then SELECT * FROM INFORMATION_SCHEMA.SCHEMATA essentially
> has an implicit WHERE schema_name="FOO" clause. Yes, that's certainly
> doable. It's even not very difficult to do, I'd think.
> 
> (*) this can be extended to privileges on few (more than one) databases
> and on wildcards in the database names, but let's start from something simple.
> 
> > This is theory, but I don't know how to do it. That's mean how to
> > freeze the current query, how to run new query, read results, and fill
> > files in the find_files function and resume the current query.
> 
> Kind of. Privileges are stored in memory in lists and hashes. You'd need
> to traverse them to see what databases a user has grants for.

I guess, I did not understand.
I did something like this:

/* sql_show.cc */
static find_files_result
find_files(THD *thd, Dynamic_array<LEX_STRING*> *files, LEX_STRING *db,
           const char *path, const LEX_STRING *wild)
{
  MY_DIR *dirp;
  Discovered_table_list tl(thd, files, wild);
  DBUG_ENTER("find_files");

  if (!(dirp = my_dir(path, MY_THREAD_SPECIFIC | (db ? 0 : MY_WANT_STAT))))
  {
    if (my_errno == ENOENT)
      my_error(ER_BAD_DB_ERROR, MYF(ME_BELL | ME_WAITTANG), db->str);
    else
      my_error(ER_CANT_READ_DIR, MYF(ME_BELL | ME_WAITTANG), path, my_errno);
    DBUG_RETURN(FIND_FILES_DIR);
  }

  if (!db)                                           /* Return databases */
  {
#ifndef NO_EMBEDDED_ACCESS_CHECKS
    char bufor[201];
    char *current;
    uint counter, i, n;
    ACL_DB *acl_db;
    char *curr_host= thd->security_ctx->priv_host_name();

    mysql_mutex_lock(&acl_cache->lock);

    for (counter=0 ; counter < acl_dbs.elements ; counter++)
    {
      const char *user, *host;

      acl_db=dynamic_element(&acl_dbs,counter,ACL_DB*);
      user= safe_str(acl_db->user);
      host= safe_str(acl_db->host.hostname);

      if ((strcmp(thd->security_ctx->priv_user, user) ||
         my_strcasecmp(system_charset_info, curr_host, host)))
        continue;

      if (is_in_ignore_db_dirs_list(acl_db->db))
        continue;

      for (i = 0, current = bufor; i < 200; ++i)
      {
        if (acl_db->db[i] == '\\') continue;
        if (acl_db->db[i] == '\0') break;
        *current++ = acl_db->db[i];
      }
      *current = '\0';

      if (tl.add_file(bufor))
        goto err;
    }
    mysql_mutex_unlock(&acl_cache->lock);
    tl.sort();
#endif
  }
  else
  {
    if (ha_discover_table_names(thd, db, dirp, &tl, false))
      goto err2;
  }
  DBUG_PRINT("info",("found: %zu files", files->elements()));
  my_dirend(dirp);

  DBUG_RETURN(FIND_FILES_OK);

err:
  mysql_mutex_unlock(&acl_cache->lock);
err2:
  my_dirend(dirp);
  DBUG_RETURN(FIND_FILES_OOM);
}

And turned on INFORMATION_SCHEMA in phpmyadmin and it is slow, very slow.
What I'm doing wrong here?
How to speed up?

diff -Nru mariadb-10.0-10.0.29.info/sql/sql_acl.cc mariadb-10.0-10.0.29/sql/sql_acl.cc
--- mariadb-10.0-10.0.29.info/sql/sql_acl.cc	2017-03-05 10:36:10.603234750 +0100
+++ mariadb-10.0-10.0.29/sql/sql_acl.cc	2017-03-05 10:38:17.155530562 +0100
@@ -213,11 +213,6 @@
 
 /* Classes */
 
-struct acl_host_and_ip
-{
-  char *hostname;
-  long ip, ip_mask;                      // Used with masked ip:s
-};
 
 #ifndef NO_EMBEDDED_ACCESS_CHECKS
 static bool compare_hostname(const acl_host_and_ip *, const char *, const char *);
@@ -225,12 +220,6 @@
 #define compare_hostname(X,Y,Z) 0
 #endif
 
-class ACL_ACCESS {
-public:
-  ulong sort;
-  ulong access;
-};
-
 /* ACL_HOST is used if no host is specified */
 
 class ACL_HOST :public ACL_ACCESS
@@ -335,14 +324,6 @@
 
 };
 
-class ACL_DB :public ACL_ACCESS
-{
-public:
-  acl_host_and_ip host;
-  char *user,*db;
-  ulong initial_access; /* access bits present in the table */
-};
-
 #ifndef DBUG_OFF
 /* status variables, only visible in SHOW STATUS after -#d,role_merge_stats */
 ulong role_global_merges= 0, role_db_merges= 0, role_table_merges= 0,
@@ -588,15 +569,6 @@
 
 #define FIRST_NON_YN_FIELD 26
 
-class acl_entry :public hash_filo_element
-{
-public:
-  ulong access;
-  uint16 length;
-  char key[1];					// Key will be stored here
-};
-
-
 static uchar* acl_entry_get_key(acl_entry *entry, size_t *length,
                                 my_bool not_used __attribute__((unused)))
 {
@@ -714,7 +686,8 @@
 /* Flag to mark that on_node was already called for this role */
 #define ROLE_OPENED             (1L << 3)
 
-static DYNAMIC_ARRAY acl_hosts, acl_users, acl_dbs, acl_proxy_users;
+DYNAMIC_ARRAY acl_dbs;
+static DYNAMIC_ARRAY acl_hosts, acl_users, acl_proxy_users;
 static HASH acl_roles;
 /*
   An hash containing mappings user <--> role
@@ -728,7 +701,7 @@
 static bool allow_all_hosts=1;
 static HASH acl_check_hosts, column_priv_hash, proc_priv_hash, func_priv_hash;
 static DYNAMIC_ARRAY acl_wild_hosts;
-static Hash_filo<acl_entry> *acl_cache;
+Hash_filo<acl_entry> *acl_cache;
 static uint grant_version=0; /* Version of priv tables. incremented by acl_load */
 static ulong get_access(TABLE *form,uint fieldnr, uint *next_field=0);
 static bool check_is_role(TABLE *form);
diff -Nru mariadb-10.0-10.0.29.info/sql/sql_acl.h mariadb-10.0-10.0.29/sql/sql_acl.h
--- mariadb-10.0-10.0.29.info/sql/sql_acl.h	2017-03-05 10:35:42.863169909 +0100
+++ mariadb-10.0-10.0.29/sql/sql_acl.h	2017-03-05 10:38:21.203540024 +0100
@@ -19,6 +19,7 @@
 #include "my_global.h"                          /* NO_EMBEDDED_ACCESS_CHECKS */
 #include "violite.h"                            /* SSL_type */
 #include "sql_class.h"                          /* LEX_COLUMN */
+#include "hash_filo.h"
 
 #define SELECT_ACL      (1L << 0)
 #define INSERT_ACL      (1L << 1)
@@ -193,6 +194,36 @@
                           : ER_ACCESS_DENIED_ERROR;
 }
 
+class acl_entry :public hash_filo_element
+{
+public:
+  ulong access;
+  uint16 length;
+  char key[1];					// Key will be stored here
+};
+
+class ACL_ACCESS {
+public:
+  ulong sort;
+  ulong access;
+};
+
+struct acl_host_and_ip
+{
+  char *hostname;
+  long ip, ip_mask;                      // Used with masked ip:s
+};
+
+class ACL_DB :public ACL_ACCESS
+{
+public:
+  acl_host_and_ip host;
+  char *user,*db;
+  ulong initial_access; /* access bits present in the table */
+};
+
+extern DYNAMIC_ARRAY acl_dbs;
+extern Hash_filo<acl_entry> *acl_cache;
 
 /* prototypes */
 
diff -Nru mariadb-10.0-10.0.29.info/sql/sql_show.cc mariadb-10.0-10.0.29/sql/sql_show.cc
--- mariadb-10.0-10.0.29.info/sql/sql_show.cc	2017-03-05 10:36:32.415285735 +0100
+++ mariadb-10.0-10.0.29/sql/sql_show.cc	2017-03-05 10:38:25.451549954 +0100
@@ -59,6 +59,8 @@
 #include "debug_sync.h"
 #include "keycaches.h"
 
+#include "hash_filo.h"
+
 #ifdef WITH_PARTITION_STORAGE_ENGINE
 #include "ha_partition.h"
 #endif
@@ -811,6 +813,14 @@
   FIND_FILES_DIR
 };
 
+static char *safe_str(char *str)
+{ return str ? str : const_cast<char*>(""); }
+
+static const char *safe_str(const char *str)
+{ return str ? str : ""; }
+
+
+
 /*
   find_files() - find files in a given directory.
 
@@ -849,53 +859,62 @@
 
   if (!db)                                           /* Return databases */
   {
-    for (uint i=0; i < (uint) dirp->number_of_files; i++)
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+    char bufor[201];
+    char *current;
+    uint counter, i, n;
+    ACL_DB *acl_db;
+    char *curr_host= thd->security_ctx->priv_host_name();
+
+    mysql_mutex_lock(&acl_cache->lock);
+
+    for (counter=0 ; counter < acl_dbs.elements ; counter++)
     {
-      FILEINFO *file= dirp->dir_entry+i;
-#ifdef USE_SYMDIR
-      char *ext;
-      char buff[FN_REFLEN];
-      if (my_use_symdir && !strcmp(ext=fn_ext(file->name), ".sym"))
-      {
-        /* Only show the sym file if it points to a directory */
-        char *end;
-        *ext=0;                                 /* Remove extension */
-        unpack_dirname(buff, file->name);
-        end= strend(buff);
-        if (end != buff && end[-1] == FN_LIBCHAR)
-          end[-1]= 0;				// Remove end FN_LIBCHAR
-        if (!mysql_file_stat(key_file_misc, buff, file->mystat, MYF(0)))
-               continue;
-       }
-#endif
-      if (!MY_S_ISDIR(file->mystat->st_mode))
+      const char *user, *host;
+
+      acl_db=dynamic_element(&acl_dbs,counter,ACL_DB*);
+      user= safe_str(acl_db->user);
+      host= safe_str(acl_db->host.hostname);
+
+      if ((strcmp(thd->security_ctx->priv_user, user) ||
+         my_strcasecmp(system_charset_info, curr_host, host)))
         continue;
 
-      if (is_in_ignore_db_dirs_list(file->name))
+      if (is_in_ignore_db_dirs_list(acl_db->db))
         continue;
 
-      if (tl.add_file(file->name))
+      for (i = 0, current = bufor; i < 200; ++i)
+      {
+        if (acl_db->db[i] == '\\') continue;
+        if (acl_db->db[i] == '\0') break;
+        *current++ = acl_db->db[i];
+      }
+      *current = '\0';
+
+      if (tl.add_file(bufor))
         goto err;
     }
+    mysql_mutex_unlock(&acl_cache->lock);
     tl.sort();
+#endif
   }
   else
   {
     if (ha_discover_table_names(thd, db, dirp, &tl, false))
-      goto err;
+      goto err2;
   }
-
   DBUG_PRINT("info",("found: %zu files", files->elements()));
   my_dirend(dirp);
 
   DBUG_RETURN(FIND_FILES_OK);
 
 err:
+  mysql_mutex_unlock(&acl_cache->lock);
+err2:
   my_dirend(dirp);
   DBUG_RETURN(FIND_FILES_OOM);
 }
 
-
 /**
    An Internal_error_handler that suppresses errors regarding views'
    underlying tables that occur during privilege checking within SHOW CREATE
diff -Nru mariadb-10.0-10.0.29.info/sql/sql_show.h mariadb-10.0-10.0.29/sql/sql_show.h
--- mariadb-10.0-10.0.29.info/sql/sql_show.h	2017-03-05 10:36:00.199210431 +0100
+++ mariadb-10.0-10.0.29/sql/sql_show.h	2017-03-05 10:38:07.759508600 +0100
@@ -35,6 +35,8 @@
 struct TABLE;
 typedef struct system_status_var STATUS_VAR;
 
+class ACL_DB;
+
 /* Used by handlers to store things in schema tables */
 #define IS_FILES_FILE_ID              0
 #define IS_FILES_FILE_NAME            1

Follow ups

References