mariadb/plugin/feedback/utils.cc
Alexander Barkov 133446828c MDEV-27009 Add UCA-14.0.0 collations
- Added one neutral and 22 tailored (language specific) collations based on
  Unicode Collation Algorithm version 14.0.0.

  Collations were added for Unicode character sets
  utf8mb3, utf8mb4, ucs2, utf16, utf32.

  Every tailoring was added with four accent and case
  sensitivity flag combinations, e.g:

  * utf8mb4_uca1400_swedish_as_cs
  * utf8mb4_uca1400_swedish_as_ci
  * utf8mb4_uca1400_swedish_ai_cs
  * utf8mb4_uca1400_swedish_ai_ci

  and their _nopad_ variants:

  * utf8mb4_uca1400_swedish_nopad_as_cs
  * utf8mb4_uca1400_swedish_nopad_as_ci
  * utf8mb4_uca1400_swedish_nopad_ai_cs
  * utf8mb4_uca1400_swedish_nopad_ai_ci

- Introducing a conception of contextually typed named collations:

  CREATE DATABASE db1 CHARACTER SET utf8mb4;
  CREATE TABLE db1.t1 (a CHAR(10) COLLATE uca1400_as_ci);

  The idea is that there is no a need to specify the character set prefix
  in the new collation names. It's enough to type just the suffix
  "uca1400_as_ci". The character set is taken from the context.

  In the above example script the context character set is utf8mb4.
  So the CREATE TABLE will make a column with the collation
  utf8mb4_uca1400_as_ci.

  Short collations names can be used in any parts of the SQL syntax
  where the COLLATE clause is understood.

- New collations are displayed only one time
  (without character set combinations) by these statements:

     SELECT * FROM INFORMATION_SCHEMA.COLLATIONS;
     SHOW COLLATION;

  For example, all these collations:
  - utf8mb3_uca1400_swedish_as_ci
  - utf8mb4_uca1400_swedish_as_ci
  - ucs2_uca1400_swedish_as_ci
  - utf16_uca1400_swedish_as_ci
  - utf32_uca1400_swedish_as_ci
  have just one entry in INFORMATION_SCHEMA.COLLATIONS and SHOW COLLATION,
  with COLLATION_NAME equal to "uca1400_swedish_as_ci", which is the suffix
  without the character set name:

SELECT COLLATION_NAME FROM INFORMATION_SCHEMA.COLLATIONS
WHERE COLLATION_NAME LIKE '%uca1400_swedish_as_ci';

+-----------------------+
| COLLATION_NAME        |
+-----------------------+
| uca1400_swedish_as_ci |
+-----------------------+

  Note, the behaviour of old collations did not change.
  Non-unicode collations (e.g. latin1_swedish_ci) and
  old UCA-4.0.0 collations (e.g. utf8mb4_unicode_ci)
  are still displayed with the character set prefix, as before.

- The structure of the table INFORMATION_SCHEMA.COLLATIONS was changed.

  The NOT NULL constraint was removed from these columns:
  - CHARACTER_SET_NAME
  - ID
  - IS_DEFAULT
  and from the corresponding columns in SHOW COLLATION.

  For example:

SELECT COLLATION_NAME, CHARACTER_SET_NAME, ID, IS_DEFAULT
FROM INFORMATION_SCHEMA.COLLATIONS
WHERE COLLATION_NAME LIKE '%uca1400_swedish_as_ci';
+-----------------------+--------------------+------+------------+
| COLLATION_NAME        | CHARACTER_SET_NAME | ID   | IS_DEFAULT |
+-----------------------+--------------------+------+------------+
| uca1400_swedish_as_ci | NULL               | NULL | NULL       |
+-----------------------+--------------------+------+------------+

  The NULL value in these columns now means that the collation
  is applicable to multiple character sets.
  The behavioir of old collations did not change.
  Make sure your client programs can handle NULL values in these columns.

- The structure of the table
  INFORMATION_SCHEMA.COLLATION_CHARACTER_SET_APPLICABILITY was changed.

  Three new NOT NULL columns were added:
  - FULL_COLLATION_NAME
  - ID
  - IS_DEFAULT

  New collations have multiple entries in COLLATION_CHARACTER_SET_APPLICABILITY.
  The column COLLATION_NAME contains the collation name without the character
  set prefix. The column FULL_COLLATION_NAME contains the collation name with
  the character set prefix.

  Old collations have full collation name in both FULL_COLLATION_NAME and
  COLLATION_NAME.

SELECT COLLATION_NAME, FULL_COLLATION_NAME, CHARACTER_SET_NAME, ID, IS_DEFAULT
FROM INFORMATION_SCHEMA.COLLATION_CHARACTER_SET_APPLICABILITY
WHERE FULL_COLLATION_NAME RLIKE '^(utf8mb4|latin1).*swedish.*ci$';
+-----------------------------+-------------------------------------+--------------------+------+------------+
| COLLATION_NAME              | FULL_COLLATION_NAME                 | CHARACTER_SET_NAME | ID   | IS_DEFAULT |
+-----------------------------+-------------------------------------+--------------------+------+------------+
| latin1_swedish_ci           | latin1_swedish_ci                   | latin1             |    8 | Yes        |
| latin1_swedish_nopad_ci     | latin1_swedish_nopad_ci             | latin1             | 1032 |            |
| utf8mb4_swedish_ci          | utf8mb4_swedish_ci                  | utf8mb4            |  232 |            |
| uca1400_swedish_ai_ci       | utf8mb4_uca1400_swedish_ai_ci       | utf8mb4            | 2368 |            |
| uca1400_swedish_as_ci       | utf8mb4_uca1400_swedish_as_ci       | utf8mb4            | 2370 |            |
| uca1400_swedish_nopad_ai_ci | utf8mb4_uca1400_swedish_nopad_ai_ci | utf8mb4            | 2372 |            |
| uca1400_swedish_nopad_as_ci | utf8mb4_uca1400_swedish_nopad_as_ci | utf8mb4            | 2374 |            |
+-----------------------------+-------------------------------------+--------------------+------+------------+

- Other INFORMATION_SCHEMA queries:

  SELECT COLLATION_NAME FROM INFORMATION_SCHEMA.COLUMNS;
  SELECT COLLATION_NAME FROM INFORMATION_SCHEMA.PARAMETERS;
  SELECT TABLE_COLLATION FROM INFORMATION_SCHEMA.TABLES;
  SELECT DEFAULT_COLLATION_NAME FROM INFORMATION_SCHEMA.SCHEMATA;
  SELECT COLLATION_NAME FROM INFORMATION_SCHEMA.ROUTINES;
  SELECT COLLATION_CONNECTION FROM INFORMATION_SCHEMA.EVENTS;
  SELECT DATABASE_COLLATION FROM INFORMATION_SCHEMA.EVENTS;
  SELECT COLLATION_CONNECTION FROM INFORMATION_SCHEMA.ROUTINES;
  SELECT DATABASE_COLLATION FROM INFORMATION_SCHEMA.ROUTINES;
  SELECT COLLATION_CONNECTION FROM INFORMATION_SCHEMA.TRIGGERS;
  SELECT DATABASE_COLLATION FROM INFORMATION_SCHEMA.TRIGGERS;
  SELECT COLLATION_CONNECTION FROM INFORMATION_SCHEMA.VIEWS;

  display full collation names, including character sets prefix,
  for all collations, including new collations.

  Corresponding SHOW commands also display full collation names
  in collation related columns:

  SHOW CREATE TABLE t1;
  SHOW CREATE DATABASE db1;
  SHOW TABLE STATUS;
  SHOW CREATE FUNCTION f1;
  SHOW CREATE PROCEDURE p1;
  SHOW CREATE EVENT ev1;
  SHOW CREATE TRIGGER tr1;
  SHOW CREATE VIEW;

  These INFORMATION_SCHEMA queries and SHOW statements may change in
  the future, to display show collation names.
2022-08-10 15:04:24 +02:00

442 lines
12 KiB
C++

/* Copyright (C) 2010 Sergei Golubchik and Monty Program Ab
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
#include "feedback.h"
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#if defined (_WIN32)
#define HAVE_SYS_UTSNAME_H
#ifndef VER_SUITE_WH_SERVER
#define VER_SUITE_WH_SERVER 0x00008000
#endif
struct utsname {
char sysname[16]; // Name of this implementation of the operating system.
char nodename[16]; // Name of this node within the communications
// network to which this node is attached, if any.
char release[16]; // Current release level of this implementation.
char version[256]; // Current version level of this release.
char machine[16]; // Name of the hardware type on which the system is running.
};
/* Get commonly used name for Windows version */
static const char *get_os_version_name(OSVERSIONINFOEX *ver)
{
DWORD major = ver->dwMajorVersion;
DWORD minor = ver->dwMinorVersion;
if (major == 10 && minor == 0)
{
return (ver->wProductType == VER_NT_WORKSTATION) ?
"Windows 10" : "Windows Server 2016";
}
if (major == 6 && minor == 3)
{
return (ver->wProductType == VER_NT_WORKSTATION)?
"Windows 8.1":"Windows Server 2012 R2";
}
if (major == 6 && minor == 2)
{
return (ver->wProductType == VER_NT_WORKSTATION)?
"Windows 8":"Windows Server 2012";
}
if (major == 6 && minor == 1)
{
return (ver->wProductType == VER_NT_WORKSTATION)?
"Windows 7":"Windows Server 2008 R2";
}
if (major == 6 && minor == 0)
{
return (ver->wProductType == VER_NT_WORKSTATION)?
"Windows Vista":"Windows Server 2008";
}
if (major == 5 && minor == 2)
{
if (GetSystemMetrics(SM_SERVERR2) != 0)
return "Windows Server 2003 R2";
if (ver->wSuiteMask & VER_SUITE_WH_SERVER)
return "Windows Home Server";
SYSTEM_INFO sysinfo;
GetSystemInfo(&sysinfo);
if (ver->wProductType == VER_NT_WORKSTATION &&
sysinfo.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_AMD64)
return "Windows XP Professional x64 Edition";
return "Windows Server 2003";
}
if (major == 5 && minor == 1)
return "Windows XP";
if (major == 5 && minor == 0)
return "Windows 2000";
return "";
}
static int uname(struct utsname *buf)
{
OSVERSIONINFOEX ver;
ver.dwOSVersionInfoSize = (DWORD)sizeof(ver);
/* GetVersionEx got deprecated, we need it anyway, so disable deprecation warnings. */
#ifdef _MSC_VER
#pragma warning (disable : 4996)
#endif
#ifdef __clang__
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
#endif
if (!GetVersionEx((OSVERSIONINFO *)&ver))
return -1;
buf->nodename[0]= 0;
strcpy(buf->sysname, "Windows");
sprintf(buf->release, "%d.%d", (int)ver.dwMajorVersion, (int)ver.dwMinorVersion);
const char *version_str= get_os_version_name(&ver);
if(version_str && version_str[0])
sprintf(buf->version, "%s %s",version_str, ver.szCSDVersion);
else
{
/* Fallback for unknown versions, e.g "Windows <major_ver>.<minor_ver>" */
sprintf(buf->version, "Windows %d.%d%s",
(int)ver.dwMajorVersion, (int)ver.dwMinorVersion,
(ver.wProductType == VER_NT_WORKSTATION ? "" : " Server"));
}
#ifdef _WIN64
strcpy(buf->machine, "x64");
#else
BOOL isX64;
if (IsWow64Process(GetCurrentProcess(), &isX64) && isX64)
strcpy(buf->machine, "x64");
else
strcpy(buf->machine,"x86");
#endif
return 0;
}
#elif defined(HAVE_SYS_UTSNAME_H)
#include <sys/utsname.h>
#endif
#ifdef HAVE_SYS_UTSNAME_H
static bool have_ubuf= false;
static struct utsname ubuf;
#endif
#ifdef TARGET_OS_LINUX
#include <glob.h>
static bool have_distribution= false;
static char distribution[256];
static const char *masks[]= {
"/etc/*-version", "/etc/*-release",
"/etc/*_version", "/etc/*_release"
};
#endif
bool schema_table_store_record(THD *thd, TABLE *table);
namespace feedback {
/*
convenience macros for inserting rows into I_S table.
*/
#define INSERT2(NAME,LEN,VALUE) \
do { \
table->field[0]->store(NAME, (uint) LEN, system_charset_info); \
table->field[1]->store VALUE; \
if (schema_table_store_record(thd, table)) \
return 1; \
} while (0)
#define INSERT1(NAME,VALUE) \
do { \
table->field[0]->store(NAME, (uint) sizeof(NAME)-1, system_charset_info); \
table->field[1]->store VALUE; \
if (schema_table_store_record(thd, table)) \
return 1; \
} while (0)
static const bool UNSIGNED= true; ///< used below when inserting integers
/**
callback for fill_plugins()
*/
static my_bool show_plugins(THD *thd, plugin_ref plugin, void *arg)
{
TABLE *table= (TABLE*) arg;
char name[NAME_LEN*2];
size_t name_len;
char version[20];
size_t version_len;
name_len= my_snprintf(name, sizeof(name), "%s version",
plugin_name(plugin)->str);
version_len= my_snprintf(version, sizeof(version), "%d.%d",
(plugin_decl(plugin)->version) >> 8,
(plugin_decl(plugin)->version) & 0xff);
INSERT2(name, name_len,
(version, (uint)version_len, system_charset_info));
name_len= my_snprintf(name, sizeof(name), "%s used",
plugin_name(plugin)->str);
INSERT2(name, name_len, (plugin_ref_to_int(plugin)->locks_total, UNSIGNED));
return 0;
}
/**
inserts all plugins, their versions, and usage counters
*/
int fill_plugin_version(THD *thd, TABLE_LIST *tables)
{
return plugin_foreach_with_mask(thd, show_plugins, MYSQL_ANY_PLUGIN,
~PLUGIN_IS_FREED, tables->table);
}
#if defined(_SC_PAGE_SIZE) && !defined(_SC_PAGESIZE)
#define _SC_PAGESIZE _SC_PAGE_SIZE
#endif
/**
return the amount of physical memory
*/
static ulonglong my_getphysmem()
{
#ifdef _WIN32
MEMORYSTATUSEX memstatus;
memstatus.dwLength= sizeof(memstatus);
GlobalMemoryStatusEx(&memstatus);
return memstatus.ullTotalPhys;
#else
ulonglong pages= 0;
#ifdef _SC_PHYS_PAGES
pages= sysconf(_SC_PHYS_PAGES);
#endif
#ifdef _SC_PAGESIZE
return pages * sysconf(_SC_PAGESIZE);
#else
return pages * my_getpagesize();
#endif
#endif
}
/* get the number of (online) CPUs */
int my_getncpus()
{
#ifdef _SC_NPROCESSORS_ONLN
return sysconf(_SC_NPROCESSORS_ONLN);
#elif defined(_WIN32)
SYSTEM_INFO sysinfo;
GetSystemInfo(&sysinfo);
return sysinfo.dwNumberOfProcessors;
#else
return 0;
#endif
}
/**
Find the version of the kernel and the linux distribution
*/
int prepare_linux_info()
{
#ifdef HAVE_SYS_UTSNAME_H
have_ubuf= (uname(&ubuf) != -1);
#endif
#ifdef TARGET_OS_LINUX
/*
let's try to find what linux distribution it is
we read *[-_]{release,version} file in /etc.
Either it will be /etc/lsb-release, such as
==> /etc/lsb-release <==
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=8.04
DISTRIB_CODENAME=hardy
DISTRIB_DESCRIPTION="Ubuntu 8.04.4 LTS"
Or a one-liner with the description (/etc/SuSE-release has more
than one line, but the description is the first, so it can be
treated as a one-liner).
We'll read lsb-release first, and if it's not found will search
for other files (*-version *-release *_version *_release)
*/
int fd;
have_distribution= false;
if ((fd= my_open("/etc/lsb-release", O_RDONLY, MYF(0))) != -1)
{
/* Cool, LSB-compliant distribution! */
size_t len= my_read(fd, (uchar*)distribution, sizeof(distribution)-1, MYF(0));
my_close(fd, MYF(0));
if (len != (size_t)-1)
{
distribution[len]= 0; // safety
char *found= strstr(distribution, "DISTRIB_DESCRIPTION=");
if (found)
{
have_distribution= true;
char *end= strstr(found, "\n");
if (end == NULL)
end= distribution + len;
found+= 20;
if (*found == '"' && end[-1] == '"')
{
found++;
end--;
}
*end= 0;
char *to= strmov(distribution, "lsb: ");
memmove(to, found, end - found + 1);
}
}
}
/* if not an LSB-compliant distribution */
for (uint i= 0; !have_distribution && i < array_elements(masks); i++)
{
glob_t found;
if (glob(masks[i], GLOB_NOSORT, NULL, &found) == 0)
{
int fd;
if ((fd= my_open(found.gl_pathv[0], O_RDONLY, MYF(0))) != -1)
{
/*
+5 and -8 below cut the file name part out of the
full pathname that corresponds to the mask as above.
*/
char *to= strmov(distribution, found.gl_pathv[0] + 5) - 8;
*to++= ':';
*to++= ' ';
size_t to_len= distribution + sizeof(distribution) - 1 - to;
size_t len= my_read(fd, (uchar*)to, to_len, MYF(0));
my_close(fd, MYF(0));
if (len != (size_t)-1)
{
to[len]= 0; // safety
char *end= strstr(to, "\n");
if (end)
*end= 0;
have_distribution= true;
}
}
}
globfree(&found);
}
#endif
return 0;
}
/**
Add the linux distribution and the kernel version
*/
int fill_linux_info(THD *thd, TABLE_LIST *tables)
{
#if defined(HAVE_SYS_UTSNAME_H) || defined(TARGET_OS_LINUX)
TABLE *table= tables->table;
CHARSET_INFO *cs= system_charset_info;
#endif
#ifdef HAVE_SYS_UTSNAME_H
if (have_ubuf)
{
INSERT1("Uname_sysname", (ubuf.sysname, (uint) strlen(ubuf.sysname), cs));
INSERT1("Uname_release", (ubuf.release, (uint) strlen(ubuf.release), cs));
INSERT1("Uname_version", (ubuf.version, (uint) strlen(ubuf.version), cs));
INSERT1("Uname_machine", (ubuf.machine, (uint) strlen(ubuf.machine), cs));
}
#endif
#ifdef TARGET_OS_LINUX
if (have_distribution)
INSERT1("Uname_distribution", (distribution, strlen(distribution), cs));
#endif
return 0;
}
/**
Adds varios bits of information to the I_S.FEEDBACK
*/
int fill_misc_data(THD *thd, TABLE_LIST *tables)
{
TABLE *table= tables->table;
INSERT1("Cpu_count", (my_getncpus(), UNSIGNED));
INSERT1("Mem_total", (my_getphysmem(), UNSIGNED));
INSERT1("Now", (thd->query_start(), UNSIGNED));
return 0;
}
int fill_collation_statistics(THD *thd, TABLE_LIST *tables)
{
TABLE *table= tables->table;
for (uint id= 1; id < MY_ALL_CHARSETS_SIZE; id++)
{
ulonglong count;
if (my_collation_is_known_id(id) &&
(count= my_collation_statistics_get_use_count(id)))
{
char name[MY_CS_COLLATION_NAME_SIZE + 32];
size_t namelen= my_snprintf(name, sizeof(name),
"Collation used %s",
get_charset_name(id));
INSERT2(name, namelen, (count, UNSIGNED));
}
}
return 0;
};
/**
calculates the server unique identifier
UID is a base64 encoded SHA1 hash of the MAC address of one of
the interfaces, and the tcp port that the server is listening on
*/
int calculate_server_uid(char *dest)
{
uchar rawbuf[2 + 6];
uchar shabuf[MY_SHA1_HASH_SIZE];
int2store(rawbuf, mysqld_port);
if (my_gethwaddr(rawbuf + 2))
{
sql_print_error("feedback plugin: failed to retrieve the MAC address");
return 1;
}
my_sha1((uint8*) shabuf, (char*) rawbuf, sizeof(rawbuf));
assert(my_base64_needed_encoded_length(sizeof(shabuf)) <= SERVER_UID_SIZE);
my_base64_encode(shabuf, sizeof(shabuf), dest);
return 0;
}
} // namespace feedback