© Jesper Wisborg Krogh 2020
J. W. KroghMySQL 8 Query Performance Tuninghttps://doi.org/10.1007/978-1-4842-5584-1_12

12. MySQL Shell

Jesper Wisborg Krogh1 
(1)
Hornsby, NSW, Australia
 

MySQL Shell is the second-generation command-line client which stands out compared to the traditional mysql command-line client by supporting the X Protocol as well as the Python and JavaScript languages. It also comes with several utilities and is highly extensible. This makes it a great tool for not only day-to-day tasks but also when investigating performance issues.

This chapter starts out with an overview of what the MySQL Shell offers including the built-in help and the rich prompt. The second part of the chapter covers how you can extend the functionality of MySQL Shell through the use of external code modules, the reporting infrastructure, and plugins.

Overview

The first MySQL Shell release with the status of general availability was in 2017, so it is still a very new tool in the MySQL toolbox. Yet, it already has a large array of features well beyond what the traditional mysql command-line client has. These features are not limited to those required to use MySQL Shell as part of the MySQL InnoDB Cluster solution; there are also several features that are useful for day-to-day database administration tasks and performance optimization.

An advantage of MySQL Shell over the mysql command-line client is that the MySQL Shell editor behaves the same on Linux and Microsoft Windows, so if you work on both platforms, you get a consistent user experience. This means that Ctrl+D exists the shell both on Linux, macOS, and Microsoft Windows, Ctrl+W deletes the previous word, and so forth.

Tip

Charles Bell (the technical reviewer of this book and a MySQL developer) has written the book Introducing MySQL Shell (Apress) with a comprehensive introduction to MySQL Shell: www.apress.com/gp/book/9781484250822. Additionally, the author of this book has published several blogs about MySQL Shell. See https://mysql.wisborg.dk/mysql-shell-blogs/.

This section will look at installing MySQL Shell, invoking it, and some of the basic features. It is however not possible to go into details with all features of MySQL Shell. You are encouraged to consult the online manual at https://dev.mysql.com/doc/mysql-shell/en for more information as you are using MySQL Shell.

Installing MySQL Shell

MySQL Shell is installed in the same way as other MySQL products (except for MySQL Enterprise Monitor). You can download it from https://dev.mysql.com/downloads/shell/; and it is available for Microsoft Windows, Linux, and macOS and as a source code. For Microsoft Windows, you can also install it through MySQL Installer.

If you install MySQL Shell using a native package format and MySQL Installer for Microsoft Windows, the installation instructions are the same as for MySQL Workbench except for the names. Please see the previous chapter for details.

You can also install MySQL Shell using a ZIP archive on Microsoft Windows or a TAR archive on Linux and macOS. If you choose that option, you simply unpack the downloaded file, and you are done.

Invoking MySQL Shell

MySQL Shell is invoked using the mysqlsh (or mysqlsh.exe on Microsoft Windows) binary which is in the bin directory of the installation directory. When you install MySQL Shell using a native package, the binary will be in your PATH environment variable, so the operating system can find it without you explicitly providing the path.

This means that the simplest way to start MySQL Shell is just to execute mysqlsh:
shell> mysqlsh
MySQL Shell 8.0.18
Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its affiliates.
Other names may be trademarks of their respective owners.
Type 'help' or '?' for help; 'quit' to exit.
MySQL JS>

The prompt will look different than in this output as the default prompt cannot be fully represented in plain text. Unlike the mysql command line, MySQL Shell does not require a connection to be present, and by default none is created.

Creating Connections

There are several ways to create a connection for MySQL Shell including from the command line and from inside MySQL Shell.

If you add any connection-related argument when invoking mysqlsh, then MySQL Shell will create a connection as part of starting up. Any connection options that are not specified will use their default values. For example, to connect as the root MySQL user to a MySQL instance on the local host using the default port (and on Linux and macOS socket) values, you just need to specify the --user argument:
shell> mysqlsh --user=root
Please provide the password for 'root@localhost': ********
Save password for 'root@localhost'? [Y]es/[N]o/Ne[v]er (default No): yes
MySQL Shell 8.0.18
Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its affiliates.
Other names may be trademarks of their respective owners.
Type 'help' or '?' for help; 'quit' to exit.
Creating a session to 'root@localhost'
Fetching schema names for autocompletion... Press ^C to stop.
Your MySQL connection id is 39581 (X protocol)
Server version: 8.0.18 MySQL Community Server - GPL
No default schema selected; type use <schema> to set one.
MySQL  localhost:33060+ ssl  JS >

The first time you connect, you will be asked to enter the password for the account. If MySQL Shell finds the mysql_config_editor command in the path or you are on Microsoft Windows where MySQL Shell can use the Windows keyring service, MySQL Shell will offer to save the password for you, so you do not need to enter it in the future.

Alternatively, you can use a URI to specify the connection options, for example:
shell> mysqlsh root@localhost:3306?schema=world

After MySQL Shell has started up, notice how the prompt has changed. MySQL Shell features an adaptive prompt that changes to reflect the status of your connection. The default prompt includes the port number you are connected to. If you connect to MySQL Server 8, then the default port used is 33060 instead of port 3306 as MySQL Shell by default uses the X Protocol when the server supports it rather than the traditional MySQL protocol. That is the reason the port number is not what you may expect.

You can also create (or change) the connection from within MySQL Shell. You can even have multiple connections, so you can work on two or more instances concurrently. There are several ways to create a session, including those listed in Table 12-1. The table also includes how to set and retrieve a global session. The Language Commands column shows the commands or methods to invoke depending on the language mode in use.
Table 12-1

Various ways to create and work with a connection

Method

Language Commands

Description

Global session

All modes:

connect (or c for short)

Creates a global session (the default session). This is the equivalent of a connection in the mysql command-line client.

General session

JavaScript:

mysqlx.getSession()

Python:

mysqlx.get_session()

Returns the session so it can be assigned to a variable. Can be used both for a connection using the X Protocol and the classic protocol.

Classic session

JavaScript:

mysql.getClassicSession()

Python:

mysql.get_classic_session()

Similar to a general session, but always returns a classic session.

Set global session

JavaScript:

shell.setSession()

Python:

shell.set_session()

Sets the global session from a variable containing a session.

Get global session

JavaScript:

shell.getSession()

Python:

shell.get_session()

Returns the global session, so it can be assigned to a variable.

Reconnect

All modes:

econnect

Reconnects using the same arguments as for the existing global connection.

All the commands and methods to create a session support a URI in the format [scheme://][user[:password]@]<host[:port]|socket>[/schema][?option=value&option=value...]. The methods also support supplying the options in a dictionary. If you do not include the password and MySQL Shell does not have a stored password for the account, you will be prompted to enter the password interactively (unlike the traditional command-line client, MySQL Shell can prompt for information during the execution of a command). For example, to connect as the myuser user to localhost
MySQL JS> connect myuser@localhost
Creating a session to 'myuser@localhost'
Please provide the password for 'myuser@localhost': *******

The language mode has been mentioned a few times. The next subsection will look at how you work with it.

Language Modes

One of the biggest features of MySQL Shell is that you are not restricted to executing SQL statements. You have the full power of both JavaScript and Python at your disposal – and of course SQL statements. This makes MySQL Shell very powerful for automating tasks.

You work in one language mode at a time, though it is possible to execute queries through the API in both JavaScript and Python. Table 12-2 summarizes how you can choose the language mode you want to use from the command line and from within MySQL Shell.
Table 12-2

Choosing the MySQL Shell language mode

Mode

Command-Line

MySQL Shell

JavaScript

--js

js

Python

--py

py

SQL

--sql

sql

The default mode is JavaScript. The prompt reflects which language mode you are in, so you always know which mode you are using.

Tip

In MySQL Shell 8.0.16 and later, you can in the Python and JavaScript modes prefix a command with sql which makes MySQL Shell execute the command as an SQL statement.

One thing to be aware of that also was hinted when listing how to create connections is that MySQL Shell like the X DevAPI tries to keep the naming convention normally used for the language. This means that in JavaScript mode, the functions and methods use camel case (e.g., getSession()), whereas in Python mode snake case (get_session()) is used. If you use the built-in help, the help will reflect the names used for the language mode you are in.

Built-in Help

It can be hard to keep on top of all of the features of MySQL Shell and how to use them. Fortunately, there is an extensive built-in help feature that lets you get information on the features without having to go back to the online manual each time.

If you execute mysqlsh with the --help argument , you will get information about all the supported command-line arguments. After starting a shell, you can also get help about the commands, objects, and methods. The topmost help is obtained using the h, ? or help command which is available from all language modes. This lists the commands and the global objects and how to get further help.

The second level of help is for the commands and global objects. You can specify the name of a command of a global object with one of the help commands to get more information about the command or object. For example:
mysql-js> h connect
NAME
      connect - Connects the shell to a MySQL server and assigns the global session.
SYNTAX
      connect [<TYPE>] <URI>
      c [<TYPE>] <URI>
DESCRIPTION
...
The final level of help is for the features of the global objects. The global objects and modules of global objects all have a help() method that provides help for the object or module. The help() method can also take the name of a method of the module or object as a string which will return help for that method. Some examples are (the output is omitted as it is quite verbose – it is recommended to try the commands for yourself to see the help text that is returned):
MySQL JS> h shell
MySQL JS> shell.help()
MySQL JS> shell.help('reconnect')
MySQL JS> shell.reports.help()
MySQL JS> shell.reports.help('query')

The two first commands retrieve the same help text. It is worth familiarizing yourself with the help feature as it can greatly improve how efficiently you can work with MySQL Shell.

The context awareness of the help goes further than to detect whether a global object exists and whether a method name follows the JavaScript or Python convention. Consider a request for help about “select.” There are several possibilities of what you mean by that. It can be one of the select() methods in the X DevAPI, or you may think of the SELECT SQL statement. If you request the help in the SQL mode, MySQL Shell assumes you mean the SQL statement. However, in the Python and JavaScript modes, you will be asked which one you mean:
MySQL Py> h select
Found several entries matching select
The following topics were found at the SQL Syntax category:
- SQL Syntax/SELECT
The following topics were found at the X DevAPI category:
- mysqlx.Table.select
- mysqlx.TableSelect.select
For help on a specific topic use: ? <topic>
e.g.: ? SQL Syntax/SELECT

The reason MySQL Shell can provide the help for the SELECT statement in the SQL mode without considering the X DevAPI is that the X DevAPI methods can only be accessed from Python and JavaScript. On the other hand, all three meanings of “select” make sense in the Python and JavaScript modes.

As noted earlier, there exist several global objects. What are those?

Built-in Global Objects

MySQL Shell uses global objects to group features. Much of the functionality that makes MySQL Shell so powerful can be found in the global objects. As you will see in the “Plugins” section, it is also possible to add your own global objects.

The built-in global objects include
  • db: When a default schema has been set, db holds the X DevAPI schema object for the default schema. X DevAPI table objects can be found as properties of the db object (unless the table or view name is the same as an existing property). The session object can also be obtained from the db object.

  • dba: For administrating MySQL InnoDB Cluster.

  • mysql: For connecting to MySQL using the classic MySQL protocol.

  • mysqlx: For working with MySQL X Protocol sessions.

  • session: For working with the current global session (connection to a MySQL instance).

  • shell: Various general-purpose methods and properties.

  • util: Various utilities such as the upgrade checker, importing JSON data, and importing data in CSV files to relational tables.

That concludes the general overview of MySQL Shell. Next, you will learn more about the prompt and how to customize it.

The Prompt

One of the features setting MySQL Shell apart from the traditional command-line client is the rich prompt which not only makes it easy to see which host and schema you are working with but also can add information such as whether you are connected to a production instance, whether SSL is used, and custom fields.

Built-in Prompts

The MySQL Shell installation comes with several predefined prompt templates that you can choose from. The default is to use a prompt that provides information about the connection and supports 256 colors, but there are also simpler prompts.

The location of the prompt definition templates depends on how you installed MySQL Shell. Examples of the location include
  • ZIP and TAR archives: The share/mysqlsh/prompt directory in the archive.

  • RPM on Oracle Linux 7: /usr/share/mysqlsh/prompt/

  • MySQL Installer on Microsoft Windows: C:Program FilesMySQLMySQL Shell 8.0sharemysqlshprompt

The prompt definitions are JSON files with the definitions that are included as of MySQL Shell 8.0.18 being
  • prompt_16.json: A colored prompt limited to use 16/8 color ANSI colors and attributes.

  • prompt_256.json: The prompt uses 256 indexed colors. This is the one that is used by default.

  • prompt_256inv.json: Like the prompt_256.json, but with an “invisible” background color (it just uses the same as for the terminal) and with different foreground colors.

  • prompt_256pl.json: Same as prompt_256.json but with extra symbols. This requires a Powerline patched font such as the one that is installed with the Powerline project. This will add a padlock with the prompt when you use SSL to connect to MySQL and use “arrow” separators. An example of installing the Powerline font is shown later.

  • prompt_256pl+aw.json: Same as prompt_256pl.json but with “awesome symbols.” This additionally requires the awesome symbols to be included in the Powerline font. An example of installing the awesome symbols is shown later.

  • prompt_classic.json: This is a very basic prompt that just shows mysql-js>, mysql-py>, or mysql-sql> based on the mode in use.

  • prompt_dbl_256.json: A two-line version of the prompt_256.json prompt.

  • prompt_dbl_256pl.json: A two-line version of the prompt_256pl.json prompt.

  • prompt_dbl_256pl+aw.json: A two-line version of the prompt_256pl+aw.json prompt.

  • prompt_nocolor.json: Gives the full prompt information, but completely without colors. An example of a prompt is MySQL [localhost+ ssl/world] JS>.

The two-line templates are particularly useful if your shell window is of limited width as they will put the information on one line and allow you to type your command on the next without having it preceded by the full prompt.

There are two ways to specify which prompt you want to use. MySQL Shell first looks for the file prompt.json in the user’s MySQL Shell directory. The default location depends on your operating system:
  • Linux and macOS: ~/.mysqlsh/prompt.json – that is in the .mysqlsh directory in the user’s home directory.

  • Microsoft Windows: %AppData%MySQLmysqlshprompt.json – that is in AppDataRoamingMySQLmysqlsh directory from the user’s home directory.

You can change the directory by setting the MYSQLSH_HOME environment variable. If you prefer a different prompt than the default, you can copy that definition into the directory and name the file prompt.json.

The other way to specify the location of the prompt definition is to set the MYSQLSH_PROMPT_THEME environment variable, for example, on Microsoft Windows using the command prompt:
C:> set MYSQLSH_PROMPT_THEME=C:Program FilesMySQLMySQL Shell 8.0sharemysqlshpromptprompt_256inv.json
In PowerShell the syntax is a little different:
PS> $env:MYSQLSH_PROMPT_THEME = "C:Program FilesMySQLMySQL Shell 8.0sharemysqlshpromptprompt_256inv.json";
On Linux and Unix:
shell$ export MYSQLSH_PROMPT_THEME=/usr/share/mysqlsh/prompt/prompt_256inv.json

This can be useful if you temporarily want to use a different prompt than your usual prompt.

As it has already been hinted, there are several parts to most of the prompt definitions. The easiest way is to take a look at an example of the prompt such as the default (prompt_256.json) prompt shown in Figure 12-1.
../images/484666_1_En_12_Chapter/484666_1_En_12_Fig1_HTML.jpg
Figure 12-1

The default MySQL Shell prompt

There are several parts to the prompt. First, it says PRODUCTION on a red background which is to warn you that you are connected to a production instance. Whether an instance is considered a production instance is based on whether the hostname you are connected to is included in the PRODUCTION_SERVERS environment variable. The second element is the MySQL string which does not have any special meaning.

Third is the host and port you are connected to, whether you use the X Protocol, and whether SSL is used. In this case, there is a + after the port number which indicates that the X Protocol is in use. The fourth element is the default schema.

The fifth and last element (not counting the > at the end) is the language mode. It will show SQL, Py, or JS depending on whether you have enabled the SQL, Python, or JavaScript mode, respectively. The background color of this element also changes with the language. SQL uses orange, Python blue, and JavaScript yellow.

In general, you will not see all elements of the prompt as MySQL Shell only includes those that are relevant. For example, the default schema is only included when you have set a default schema, and the connection information is only present when you are connected to an instance.

As you work with MySQL Shell, you may realize that you would like to make some changes to the prompt definition. Let’s look at how you can do that.

Custom Prompt Definition

The prompt definitions are JSON files, and there is nothing that prevents you from editing a definition to change according to your preferences. The best way to do this is to copy the template that is closest to what you want and then make your changes.

Tip

The best source for help to create your own prompt definition is the README.prompt file that is located in the same directory as the template files.

Instead of going through the specification in details, it is easier to look at the prompt_256.json template and discuss some parts of it. Listing 12-1 shows the end of the file is where the elements of the prompt are defined.
  "segments": [
    {
      "classes": ["disconnected%host%", "%is_production%"]
    },
    {
      "text": " My",
      "bg": 254,
      "fg": 23
    },
    {
      "separator": "",
      "text": "SQL ",
      "bg": 254,
      "fg": 166
    },
    {
      "classes": ["disconnected%host%", "%ssl%host%session%"],
      "shrink": "truncate_on_dot",
      "bg": 237,
      "fg": 15,
      "weight": 10,
      "padding" : 1
    },
    {
      "classes": ["noschema%schema%", "schema"],
      "bg": 242,
      "fg": 15,
      "shrink": "ellipsize",
      "weight": -1,
      "padding" : 1
    },
    {
      "classes": ["%Mode%"],
      "text": "%Mode%",
      "padding" : 1
    }
  ]
Listing 12-1

The definition of the elements of the prompt

There are a few things that are interesting to note here. First, notice that there is an object with the classes disconnected%host% and %is_production%. The names within the percentage signs are variables defined in the same file or that come from MySQL Shell itself (it has variables such as the host and port). For example, is_production is defined as
  "variables" : {
    "is_production": {
        "match" : {
            "pattern": "*;%host%;*",
            "value": ";%env:PRODUCTION_SERVERS%;"
        },
        "if_true" : "production",
        "if_false" : ""
    },

So a host is considered to be a production instance if it is included in the environment variable PRODUCTION_SERVERS.

The second thing to note about the list of elements is that there are some special fields such as shrink which can be used to define how the text is kept relatively short. For example, the host element uses truncate_on_dot, so only the part before the first dot in the hostname is displayed if the full hostname is too long. Alternatively ellipsize can be used to add … after the truncated value.

Third, the background and foreground colors are defined using the bg and fg elements, respectively. This allows you to completely customize the prompt to your liking with respect to colors. The color can be specified in one of the following ways:
  • By Name: There are a few colors that are known by name: black, red, green, yellow, blue, magenta, cyan, and white.

  • By Index: A value between 0 and 255 (both inclusive) where 0 is black, 63 light blue, 127 magenta, 193 yellow, and 255 white.

  • By RGB: Use a value in the #rrggbb format. This requires that the terminal supports TrueColor colors.

One group of built-in variables that deserve an example are the ones that in some way depend on the environment or the MySQL instance you are connected to. These are
  • %env:varname%: This uses an environment variable. The way it is determined whether you are connected to a production server is an example of using an environment variable.

  • %sysvar:varname%: This uses the value of a global system variable from MySQL, that is, the value returned by SELECT @@global.varname.

  • %sessvar:varname%: Similar to the previous but using a session system variable.

  • %status:varname%: This uses the value of a global status variable from MySQL, that is, the value returned by SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME = 'varname'.

  • %status:varname%: Similar to the previous, but using a session status variable.

If you, for example, want to include the MySQL version of the instance you are connected to in the prompt, you can add an element like
    {
      "separator": "",
      "text": "%sysvar:version%",
      "bg": 250,
      "fg": 166
    },

You are encouraged to play around with the definition until you get a color scheme and the elements that work best for you. An alternative way to improve the prompt on Linux is to install the Powerline and Awesome fonts.

Powerline and Awesome Fonts

If you feel that the normal MySQL Shell prompts are too square and you use MySQL Shell on Linux, you can consider using one of the templates that rely on the Powerline and the Awesome fonts. The fonts are not installed by default.

This example will show you how to do a minimal installation of the Powerline fonts1 and install the Awesome font using the patching-strategy branch of gabrielelana’s awesome-terminal-fonts project on GitHub.2

Tip

Another option is the Fantasque Awesome Powerline fonts (https://github.com/ztomer/fantasque_awesome_powerline) which include both the Powerline and Awesome fonts. These fonts look a little different from those installed in this example. Choose those that you prefer.

You install the Awesome fonts by cloning the GitHub repository and change to the patching-strategy branch. Then it is a matter of copying the required files to .local/share/fonts/ under the home directory and rebuilding the font information cache files. The steps are shown in Listing 12-2. The output is also available in listing_12_2.txt in this book’s GitHub repository to make it easier to copy the commands.
shell$ git clone https://github.com/gabrielelana/awesome-terminal-fonts.git
Cloning into 'awesome-terminal-fonts'...
remote: Enumerating objects: 329, done.
remote: Total 329 (delta 0), reused 0 (delta 0), pack-reused 329
Receiving objects: 100% (329/329), 2.77 MiB | 941.00 KiB/s, done.
Resolving deltas: 100% (186/186), done.
shell$ cd awesome-terminal-fonts
shell$ git checkout patching-strategy
Branch patching-strategy set up to track remote branch patching-strategy from origin.
Switched to a new branch 'patching-strategy'
shell$ mkdir -p ~/.local/share/fonts/
shell$ cp patched/SourceCodePro+Powerline+Awesome+Regular.* ~/.local/share/fonts/
shell$ fc-cache -fv ~/.local/share/fonts/
/home/myuser/.local/share/fonts: caching, new cache contents: 1 fonts, 0 dirs
/usr/lib/fontconfig/cache: not cleaning unwritable cache directory
/home/myuser/.cache/fontconfig: cleaning cache directory
/home/myuser/.fontconfig: not cleaning non-existent cache directory
/usr/bin/fc-cache-64: succeeded
Listing 12-2

Installing the Awesome fonts

This requires that you have git installed. The next part is to install the Powerline fonts which is shown in Listing 12-3. The output is also available in listing_12_3.txt in this book’s GitHub repository to make it easier to copy the commands.
shell$ wget --directory-prefix="${HOME}/.local/share/fonts" https://github.com/powerline/powerline/raw/develop/font/PowerlineSymbols.otf
...
2019-08-25 14:38:41 (5.48 MB/s) - '/home/myuser/.local/share/fonts/PowerlineSymbols.otf' saved [2264/2264]
shell$ fc-cache -vf ~/.local/share/fonts/
/home/myuser/.local/share/fonts: caching, new cache contents: 2 fonts, 0 dirs
/usr/lib/fontconfig/cache: not cleaning unwritable cache directory
/home/myuser/.cache/fontconfig: cleaning cache directory
/home/myuser/.fontconfig: not cleaning non-existent cache directory
/usr/bin/fc-cache-64: succeeded
shell$ wget --directory-prefix="${HOME}/.config/fontconfig/conf.d" https://github.com/powerline/powerline/raw/develop/font/10-powerline-symbols.conf
...
2019-08-25 14:39:11 (3.61 MB/s) - '/home/myuser/.config/fontconfig/conf.d/10-powerline-symbols.conf' saved [2713/2713]
Listing 12-3

Installing the Powerline font

This does not do a full installation of the Powerline font, but it is all that is required if you just want to use the Powerline font with MySQL Shell. The two wget commands download the font and configuration files, and the fc-cache command rebuilds the font information cache files. You will need to restart Linux for the changes to take effect.

Once the restart has completed, you can copy one of the pl+aw templates to become your new prompt, for example:
shell$ cp /usr/share/mysqlsh/prompt/prompt_dbl_256pl+aw.json ~/.mysqlsh/prompt.json
The resulting prompt can be seen in Figure 12-2.
../images/484666_1_En_12_Chapter/484666_1_En_12_Fig2_HTML.jpg
Figure 12-2

The double-line Powerline + Awesome fonts prompt

This example also shows how the prompt changes as you change the language mode and set a default schema. Regarding support for multiple modules, then that is largely why MySQL Shell is such a powerful tool, so the next section will look at how you can use external modules in MySQL Shell.

Using External Modules

The support for JavaScript and Python makes it easy to perform tasks in MySQL Shell. You are not limited to the core functionality but can also import both standard modules and your own custom modules. This section will start out with the basics of using external modules (as opposed to the built-in MySQL Shell modules). The next section will go into the reporting infrastructure, and after that plugins will be covered.

Note

The discussion in this book focuses on Python. If you prefer JavaScript, the usage is very similar. One main difference is that Python uses snake case (e.g., import_table()), whereas JavaScript uses camel case (importTable()). See also https://dev.mysql.com/doc/mysql-shell/en/mysql-shell-code-execution.html for general information about code execution in MySQL Shell.

You use Python modules in MySQL Shell in the same way as when you use the interactive Python interpreter, for example:
mysql-py> import sys
mysql-py> print(sys.version)
3.7.4 (default, Sep 13 2019, 06:53:53) [MSC v.1900 64 bit (AMD64)]
mysql-py> import uuid
mysql-py> print(uuid.uuid1())
fd37319e-c70d-11e9-a265-b0359feab2bb

The exact output depends on the version of MySQL Shell and the platform you use it on.

Note

MySQL Shell 8.0.17 and earlier provide Python 2.7, whereas MySQL Shell 8.0.18 and later come with Python 3.7.

The MySQL Shell interpreter allows you to import all the usual modules included with Python. If you want to import your own modules, you will need to adjust the search path. You can do this directly in the interactive session, for example:
mysql-py> sys.path.append('C:MySQLShellPython')

Modifying the path this way is fine for a one-off use of a module; however, it is inconvenient if you have created a module you will be using on a regular basis.

When MySQL Shell starts, it reads two configuration files, one for Python and one for JavaScript. For Python the file is mysqlshrc.py and for JavaScript mysqlshrc.js. MySQL Shell searches in four places for the files. On Microsoft Windows, the paths are in the order they are searched:
  1. 1.

    %PROGRAMDATA%MySQLmysqlsh

     
  2. 2.

    %MYSQLSH_HOME%sharedmysqlsh

     
  3. 3.

    <mysqlsh binary path>

     
  4. 4.

    %APPDATA%MySQLmysqlsh

     
On Linux and Unix:
  1. 1.

    /etc/mysql/mysqlsh/

     
  2. 2.

    $MYSQLSH_HOME/shared/mysqlsh/

     
  3. 3.

    <mysqlsh binary path>/

     
  4. 4.

    $HOME/.mysqlsh/

     

All four paths are always searched, and if the file is found in multiple locations, each file will be executed. This means that the last found file takes precedence if the files affect the same variables. If you make changes meant for you personally, the best place to make the changes is in the fourth location. The path in step 4 can be overridden with the MYSQLSH_USER_CONFIG_HOME environment variable.

If you add modules that you want to use on a regular basis, you can modify the search path in the mysqlshrc.py file. That way, you can import the module as any other Python module.

Tip

A great example of the power of the support for external modules is the MySQL Shell port of Innotop (https://github.com/lefred/mysql-shell-innotop). It also reveals two of the limitations. Because the reporting part of Innotop is implemented using the curses library, it does not work on Microsoft Windows, and because the implementation uses Python, it requires that you execute Innotop in the Python language mode. The reporting infrastructure and plugins discussed later in the chapter avoid these limitations.

As a simple example, consider a very simple module which has a function to roll a virtual dice and return a value between one and six:
import random
def dice():
    return random.randint(1, 6)
The example is also available from the file example.py in this book’s GitHub repository. If you save the file to the directory C:MySQLShellPython, add the following code to the mysqlshrc.py file (adjust the path in the sys.path.append() line according to where you save the file):
import sys
sys.path.append('C:MySQLShellPython')
The next time you start MySQL Shell, you can use the module, for example (since the dice() function returns a random value, your output will vary):
mysql-py> import example
mysql-py> example.dice()
5
mysql-py> example.dice()
3

This is the simplest way of extending MySQL Shell. Another way is to add reports to the reporting infrastructure.

Reporting Infrastructure

Starting with MySQL Shell 8.0.16, there is a reporting infrastructure available that you can use with both built-in reports and your own custom reports. This is a very powerful way to use MySQL Shell to monitor a MySQL instance and collect information when you encounter performance problems.

Tip

Since the reporting infrastructure is still very new, it is recommended to check each new release for new built-in reports.

This section will start out showing how you get help about the available reports and then discuss how to execute the reports and finally how to add your own reports.

Report Information and Help

The built-in help of MySQL Shell also extends to the reports, so you can easily obtain help of how to use the reports. You can start out using the show command without any arguments to get a list of available reports. If you add a report name as an argument together with the --help option , you get detailed help for that report. Listing 12-4 shows an example of both uses.
mysql-py> show
Available reports: query, thread, threads.
mysql-py> show query --help
NAME
      query - Executes the SQL statement given as arguments.
SYNTAX
      show query [OPTIONS] [ARGS]
      watch query [OPTIONS] [ARGS]
DESCRIPTION
      Options:
      --help, -h  Display this help and exit.
      --vertical, -E
                  Display records vertically.
      Arguments:
      This report accepts 1-* arguments.
Listing 12-4

Obtaining a list of reports and help for the query report

The output of the show command shows there are three reports available. These are the built-in reports as of version 8.0.18. The second command returns the help for the query report which shows it takes one or more arguments and has two options: --help for returning the help text and --vertical or -E to return the query result in the vertical format.

The built-in reports are
  • query: Execute the query provided as an argument.

  • thread: Return information about the current connection.

  • threads: Return information about all connections for the current user, foreground threads, or background threads.

Another thing that you should notice in the help output is that it lists two ways to execute the report. You can either use the show command that was also used to generate the help, or you can use the watch command. You can get more help about each command using the usual built-in help:
mysql-py> h show
mysql-py> h watch

The help output is rather verbose, so it has been omitted here. Instead, the next subsection will discuss how to use the two commands.

Executing Reports

There are two different ways to execute a report. You can either ask for the report to be executed a single time, or you can request the report to be executed over and over at a fixed interval.

There are two commands available to execute a report:
  • show: Execute the report a single time.

  • watch: Keep executing the report at the interval provided like the watch command on Linux.

Both commands can be used from either of the language modes. The show command does not have any arguments of its own (but a report may add arguments specific to it). The watch command has two options that specify when and how to output the report. These options are
  • --interval=float, -i float: The number of seconds to wait between each execution of the report. The value must be in the range 0.1–86400 (one day) seconds. The default is 2 seconds.

  • --nocls: Do not clear the screen when outputting the result of the report. This appends the new result below the previous ones and allows you to see the history of the report results until the oldest scroll out of view.

When you execute a report with the watch command, you stop the execution with Ctrl+C.

As an example of executing a report, consider the query report which you give a query that will be executed. If you want the result to be returned in a vertical format, you can use the --vertical argument. Listing 12-5 shows an example of the result of first executing the report fetching active queries from the sys.session view with the show command and then with the watch command refreshing every 5 seconds and without clearing the screen. To ensure that there is some data returned, you can, for example, execute the query SELECT SLEEP(60) in a second connection.
mysql-sql> show query --vertical SELECT conn_id, current_statement AS stmt, statement_latency AS latency FROM sys.session WHERE command = 'Query' AND conn_id <> CONNECTION_ID()
*************************** 1. row ***************************
conn_id: 34979
   stmt: SELECT SLEEP(60)
latency: 32.62 s
mysql-sql> watch query --interval=5 --nocls --vertical SELECT conn_id, current_statement AS stmt, statement_latency AS latency FROM sys.session WHERE command = 'Query' AND conn_id <> CONNECTION_ID()
*************************** 1. row ***************************
conn_id: 34979
   stmt: SELECT SLEEP(60)
latency: 43.02 s
*************************** 1. row ***************************
conn_id: 34979
   stmt: SELECT SLEEP(60)
latency: 48.09 s
*************************** 1. row ***************************
conn_id: 34979
   stmt: SELECT SLEEP(60)
latency: 53.15 s
*************************** 1. row ***************************
conn_id: 34979
   stmt: SELECT SLEEP(60)
latency: 58.22 s
Report returned no data.
Listing 12-5

Using the query report

If you execute the same commands, your output will depend on which statements are executing in other threads at the time the report is run. The query used for the report adds a condition that the connection id must be different from the one for the connection generating the report. The show command with the query report has little value on its own as you could just as well execute the query. It is more useful with other reports and to check the query before using it with watch command.

The watch command is more interesting as it allows you keep getting the result updated. In the example, the report runs five times before it is stopped. The four first times, there is another connection executing a query, and the fifth time the report generates no data. Notice that more than five seconds are added to the statement latency for the query between successive executions. That is because the 5 seconds is the time MySQL Shell waits from the result of one iteration has been displayed until it starts executing the query again. So the overall time between two outputs is the interval plus the query execution time plus the time it takes to process the result.

The reporting infrastructure not only allows you to use the built-in reports. You can also add your own reports.

Adding Your Own Reports

The real power of the reporting infrastructure is that it is easily extendable, so both the MySQL development team and you can add more reports. While you can use the support for external modules to add reports as it is done with Innotop, that approach requires you to implement the reporting infrastructure yourself, and you must use the language mode of the modules to execute the report. When you use the reporting infrastructure, that is all handled for you, and the report is available for all language modes.

Note

The report code in this section is not meant to be executed in a MySQL Shell session (and it will cause errors if you copy and paste it as the code stands because blank lines inside a block are used by MySQL Shell in interactive mode to exit the block). Instead, the code must be saved to a file that is loaded when invoking MySQL Shell. Instructions how to install the code follow at the end of the example.

A good way to discuss how to create your own reports is to create a simple report and discuss the various parts that make it up. Listing 12-6 shows the code necessary to create a report that queries the sys.session view . The code is also available from the file listing_12_6.py in this book’s GitHub repository. Where to save the code so it becomes available as a report in MySQL Shell will be discussed later.
'''Defines the report "sessions" that queries the sys.x$session view
for active queries. There is support for specifying what to order by
and in which direction, and the maximum number of rows to include in
the result.'''
SORT_ALLOWED = {
    'thread': 'thd_id',
    'connection': 'conn_id',
    'user': 'user',
    'db': 'db',
    'latency': 'statement_latency',
    'memory': 'current_memory',
}
def sessions(session, args, options):
    '''Defines the report itself. The session argument is the MySQL
    Shell session object, args are unnamed arguments, and options
    are the named options.'''
    sys = session.get_schema('sys')
    session_view = sys.get_table('x$session')
    query = session_view.select(
        'thd_id', 'conn_id', 'user', 'db',
        'sys.format_statement(current_statement) AS statement',
        'sys.format_time(statement_latency) AS latency',
        'format_bytes(current_memory) AS memory')
    # Set what to sort the rows by (--sort)
    try:
        order_by = options['sort']
    except KeyError:
        order_by = 'latency'
    if order_by in ('latency', 'memory'):
        direction = 'DESC'
    else:
        direction = 'ASC'
    query.order_by('{0} {1}'.format(SORT_ALLOWED[order_by], direction))
    # If ordering by latency, ignore those statements with a NULL latency
    # (they are not active)
    if order_by == 'latency':
        query.where('statement_latency IS NOT NULL')
    # Set the maximum number of rows to retrieve is --limit is set.
    try:
        limit = options['limit']
    except KeyError:
        limit = 0
    if limit > 0:
        query.limit(limit)
    result = query.execute()
    report = [result.get_column_names()]
    for row in result.fetch_all():
        report.append(list(row))
    return {'report': report}
Listing 12-6

Report querying the sys.session view

The code first defines a dictionary with the supported values for sorting the result. This will be used later in the code both inside the sessions() function and when registering the report. The sessions() function is where the report is created. The function takes three arguments:
  • session: This is a MySQL Shell Session object (what defines the connection to the MySQL instance).

  • args: A list with the unnamed arguments passed to the report. This is what is used for the query report where you just specify the query without adding an argument name before the query.

  • options: A dictionary with the named arguments for the report.

The sessions report uses named options, so the args argument is not used.

The next eight lines use the X DevAPI to define the base query. First, the schema object for the sys schema is obtained from the session. Then the sessions view is obtained from the schema object (you use get_table() both to fetch a view and a table). Finally, a select query is created with the arguments specifying which columns that should be retrieved and which aliases to use for the columns.

Next, the --sort argument is handled which is available as the sort key in the options dictionary. If the key does not exist, the report falls back to sorting by latency. The sort order is defined as descending in case the output is sorted according to the latency or memory usage; otherwise, the sort order is ascending. The order_by() method is used to add the sorting information to the query. Additionally, when sorting by latency, only sessions where the latency is not NULL are included.

The --limit argument is handled in a similar fashion, and a value of 0 is taken to mean all matching sessions. Finally, the query is executed. The report is generated as a list with the first item being the column headers and the rest the rows in the result. The report returns a dictionary with the report list in the report item.

This report returns the result formatted as a list. There are two other formats as well. Overall, the following result formats are supported:
  • List Type: The result is returned as a list with the first item the headers and the remaining the rows in the order they should be displayed. The headers and the rows are themselves lists.

  • Report Type: The result is a list with a single item. MySQL Shell uses YAML to display the result.

  • Print Type: The result is printed directly to the screen as is.

All that remains is to register the report. That is done using the register_report() method of the shell object as shown in Listing 12-7 (this is also included in the file listing_12-6.py).
# Make the report available in MySQL Shell.
shell.register_report(
    'sessions',
    'list',
    sessions,
    {
        'brief': 'Shows which sessions exist.',
        'details': ['You need the SELECT privilege on sys.session view and ' +
                    'the underlying tables and functions used by it.'],
        'options': [
            {
                'name': 'limit',
                'brief': 'The maximum number of rows to return.',
                'shortcut': 'l',
                'type': 'integer'
            },
            {
                'name': 'sort',
                'brief': 'The field to sort by.',
                'shortcut': 's',
                'type': 'string',
                'values': list(SORT_ALLOWED.keys())
            }
        ],
        'argc': '0'
    }
)
Listing 12-7

Registering the sessions report

The register_report() method takes four arguments that define the report and provides the help information returned by MySQL Shell’s built-in help feature. The arguments are
  • name: The name of the report. You can choose the name relatively freely as long as it is a single word and it is unique for all reports.

  • type: The result format: 'list', 'report', or 'print'.

  • report: The object of the function generating the report, in this case sessions.

  • description: An optional argument that describes the report. If you provide the description, you use a dictionary as described shortly.

The description is the most complex of the arguments. It consists of a dictionary with the following keys (all items are optional):
  • brief: A short description of the report.

  • details: A detailed description of the report provided as a list of strings.

  • options: The named arguments as a list of dictionaries.

  • argc: The number of unnamed arguments. You specify that either as an exact number like in this example, an asterisk (*) for any number of arguments, a range with exact numbers (like '1-3'), or a range with a minimum number of arguments ('3-*').

The options element is used to define the named arguments of the report. Each dictionary object of the list must include the name of the argument, and there is support for several optional arguments to provide more information about the argument. Table 12-3 lists the dictionary keys along with their default values and a description. The name key is required; the rest are optional.
Table 12-3

The dictionary keys used to define the report argument

Key

Default Value

Description

name

 

The argument name which is used with double dashes (e.g., --sort) when invoking the report.

brief

 

A short description of the argument.

details

 

A detailed description of the argument provided as a list of strings.

shortcut

 

A single alphanumeric character that can be used to access the argument.

type

string

The argument type. Supported values at the time of writing are string, bool, integer, and float. When a Boolean is chosen, the argument works as a switch defaulting to False.

required

False

Whether the argument is mandatory.

values

 

The allowed values for a string argument. If values are not provided, all values are supported. This is what is used in the example to restrict the allowed sorting options.

The typical way you import a report is to save the report definition and registration code in the init.d directory under the user configuration path which defaults to %AppData%MySQLmysqlsh on Microsoft Windows and $HOME/.mysqlsh/ on Linux and Unix (the same as the fourth path searched for the configuration files). All scripts with the filename extension .py will be executed as a Python script (and .js for JavaScript) when starting up MySQL Shell.

Tip

If there are errors in the scripts, information about the problems will be logged to the MySQL Shell log which is stored in the file mysqlsh.log in the user configuration path.

If you copy the listing_12_6.py file into this directory and restart MySQL Shell (make sure you connect using the MySQL X port – by default port 33060), you can use the sessions report as shown in Listing 12-8. The result of the report varies, so you will not see the same if you execute the report.
mysql-py> show
Available reports: query, sessions, thread, threads.
mysql-py> show sessions --help
NAME
      sessions - Shows which sessions exist.
SYNTAX
      show sessions [OPTIONS]
      watch sessions [OPTIONS]
DESCRIPTION
      You need the SELECT privilege on sys.session view and the underlying
      tables and functions used by it.
      Options:
      --help, -h  Display this help and exit.
      --vertical, -E
                  Display records vertically.
      --limit=integer, -l
                  The maximum number of rows to return.
      --sort=string, -s
                  The field to sort by. Allowed values: thread, connection,
                  user, db, latency, memory.
mysql-py> show sessions --vertical
*************************** 1. row ***************************
   thd_id: 81
  conn_id: 36
     user: mysqlx/worker
       db: NULL
statement: SELECT `thd_id`,`conn_id`,`use ... ER BY `statement_latency` DESC
  latency: 40.81 ms
   memory: 1.02 MiB
mysql-py> js
Switching to JavaScript mode...
mysql-js> show sessions --vertical
*************************** 1. row ***************************
   thd_id: 81
  conn_id: 36
     user: mysqlx/worker
       db: NULL
statement: SELECT `thd_id`,`conn_id`,`use ... ER BY `statement_latency` DESC
  latency: 71.40 ms
   memory: 1.02 MiB
mysql-js> sql
Switching to SQL mode... Commands end with ;
mysql-sql> show sessions --vertical
*************************** 1. row ***************************
   thd_id: 81
  conn_id: 36
     user: mysqlx/worker
       db: NULL
statement: SELECT `thd_id`,`conn_id`,`use ... ER BY `statement_latency` DESC
  latency: 44.80 ms
   memory: 1.02 MiB
Listing 12-8

Using the sessions report

The new sessions report shows up in the same way as the built-in reports, and you have the same features as for the built-in report, for example, support for displaying the result in the vertical output. The reason the vertical output is supported is because the report returns the result as a list, so MySQL Shell handles the formatting. Notice also how the report can be used in all three language modes even though it was written in Python.

There is an alternative way to import the report. Instead of saving the file to the init.d directory, you can include the report as part of a plugin.

Plugins

MySQL Shell added support for plugins in version 8.0.17. A plugin consists of one or more code modules which can include reports, utilities, or anything else that may be of use to you and that can be executed as Python or JavaScript code. This is the most powerful way to extend MySQL Shell. The price is that it is also relatively complex, but the benefit is that it is easier to share and import a package of features. Another benefit of plugins is that not only can reports be executed from any language mode; the rest of your code can also be used from both Python and JavaScript.

Tip

For all the details of adding plugins including the description of the parameters for the methods used to create plugin objects and register them, see https://dev.mysql.com/doc/mysql-shell/en/mysql-shell-plugins.html. There is also an example plugin available at Mike Zinner’s (MySQL development manager whose team includes the MySQL Shell developers) GitHub repository that is worth studying: https://github.com/mzinner/mysql-shell-ex.

You create a plugin by adding a directory with the name of the plugin to the plugins directory under the user configuration path which defaults to %AppData%MySQLmysqlsh on Microsoft Windows and $HOME/.mysqlsh/ on Linux and Unix (the same as the fourth path searched for the configuration files). The plugin can consist of any number of files and directories, but all files must use the same programming language.

Note

All code within a plugin must use the same programming language. If you need to use both Python and JavaScript, you must split the code into two different plugins.

An example plugin called myext is included in the directory Chapter_12/myext in this book’s GitHub repository. It includes the directories and files depicted in Figure 12-3. The light-colored (yellow) rectangles with rounded corners represent directories, and the darker (red) document shapes are a list of files in a directory.

Note

The example plugin is very basic with the aim at demonstrating how the plugin infrastructure works. If you use plugins in productions, make sure to add appropriate validation and error handling to your code.

../images/484666_1_En_12_Chapter/484666_1_En_12_Fig3_HTML.png
Figure 12-3

The directories and files for the myext plugin

You can view the structure of the plugin like Python packages and modules. Two important things to be aware of are that there must be a __init__.py file in each directory and when the plugin is imported, only the init.py files (init.js for a JavaScript module) are executed. This means that you must include the code that is necessary to register the public parts of the plugin in init.py files. In this example plugin, all the __init__.py files are empty.

Note

Plugins are not meant to be created interactively. Make sure you save the code to files and at the end restart MySQL Shell to import the plugin. More details are given in the remainder of this section.

The sessions.py file in the reports directory is the same as the sessions report that was generated in Listing 12-6 except that the registration of the report is done in reports/init.py and the report is renamed to sessions_myext to avoid two reports with the same name.

The utils directory includes a module with the get_columns() function that is used by the describe() function in tools/example.py. The get_columns() function is also registered as util.get_columns() in utils/init.py. Listing 12-9 shows the get_columns() function from utils/util.py.
'''Define utility functions for the plugin.'''
def get_columns(table):
    '''Create query against information_schema.COLUMNS to obtain
    meta data for the columns.'''
    session = table.get_session()
    i_s = session.get_schema("information_schema")
    i_s_columns = i_s.get_table("COLUMNS")
    query = i_s_columns.select(
        "COLUMN_NAME AS Field",
        "COLUMN_TYPE AS Type",
        "IS_NULLABLE AS `Null`",
        "COLUMN_KEY AS Key",
        "COLUMN_DEFAULT AS Default",
        "EXTRA AS Extra"
    )
    query = query.where("TABLE_SCHEMA = :schema AND TABLE_NAME = :table")
    query = query.order_by("ORDINAL_POSITION")
    query = query.bind("schema", table.schema.name)
    query = query.bind("table", table.name)
    result = query.execute()
    return result
Listing 12-9

The get_columns() function from utils/util.py

The function takes a table object and uses the X DevAPI to construct a query against the information_schema.COLUMNS view. Notice how the function obtains the session and schema through the table object. At the end, the result object of executing the query is returned.

Listing 12-10 shows how to register the get_columns() function , so it is available as util.get_columns() in the myext plugin. The registration happens in utils/init.py.
'''Import the utilities into the plugin.'''
import mysqlsh
from myext.utils import util
shell = mysqlsh.globals.shell
# Get the global object (the myext plugin)
try:
    # See if myext has already been registered
    global_obj = mysqlsh.globals.myext
except AttributeError:
    # Register myext
    global_obj = shell.create_extension_object()
    description = {
        'brief': 'Various MySQL Shell extensions.',
        'details': [
            'More detailed help. To be added later.'
        ]
    }
    shell.register_global('myext', global_obj, description)
# Get the utils extension
try:
    plugin_obj = global_obj.utils
except IndexError:
    # The utils extension does not exist yet, so register it
    plugin_obj = shell.create_extension_object()
    description = {
        'brief': 'Utilities.',
        'details': ['Various utilities.']
    }
    shell.add_extension_object_member(global_obj, "util", plugin_obj,
                                      description)
definition = {
    'brief': 'Describe a table.',
    'details': ['Show information about the columns of a table.'],
    'parameters': [
        {
            'name': 'table',
            'type': 'object',
            'class': 'Table',
            'required': True,
            'brief': 'The table to get the columns for.',
            'details': ['A table object for the table.']
        }
    ]
}
try:
    shell.add_extension_object_member(plugin_obj, 'get_columns',
                                      util.get_columns, definition)
except SystemError as e:
    shell.log("ERROR", "Failed to register myext util.get_columns ({0})."
              .format(str(e).rstrip()))
Listing 12-10

Registering the get_columns() function as util.get_columns()

The first important observation is that the mysqlsh module is imported. Both the shell and the session objects can be obtained through the mysqlsh module, so this is an important module when working with extensions in MySQL Shell. Notice also how the util module is imported. It is always required to use full paths starting from the plugin name to import plugin modules.

In order to register the function, first, it is checked whether the myext plugin already exists in mysqlsh.globals. If not, it is created with shell.create_extension_object() and registered using the shell.register_global() method . This dance is necessary as there are multiple init.py files and you should not rely on the order they are executed.

Next, the utils module is registered in a similar fashion using the shell.create_extension_object() and shell.add_extension_object_member() methods . There is potential for ending up duplicating code and performing similar steps a lot if you have a large plugin, so you can consider creating utility functions to avoid repeating yourself.

Finally, the function itself is registered using the shell.add_extension_object_member() method . Since the table argument takes an object, it is possible to specify the type of object that is required.

For the registration of the module and function, there is no requirement that the name in the code and the registered name are the same. The registration of the report in reports/init.py includes an example of changing the name, if you are interested. However, in most cases, it is the preferred way to keep the names the same to make it easier to find the code behind a feature.

The tools/example.py file adds two functions that are both registered. There is the dice() function from earlier as well as the describe() function that uses get_columns() to get the column information. The part of the code relevant for the describe() function is shown in Listing 12-11.
import mysqlsh
from myext.utils import util
def describe(schema_name, table_name):
    shell = mysqlsh.globals.shell
    session = shell.get_session()
    schema = session.get_schema(schema_name)
    table = schema.get_table(table_name)
    columns = util.get_columns(table)
    shell.dump_rows(columns)
Listing 12-11

The describe() function in tools/example.py

The most important thing to notice is that the shell object is obtained as mysqlsh.globals.shell and from there the session, schema, and table objects can be obtained. The shell.dump_rows() method is used to generate the output of the result. The method takes a result object and optionally the format (defaults to the table format). In the process of outputting the result, the result object is consumed.

You are now ready to try the plugin. You need to copy the whole myext directory into the plugins directory and restart MySQL Shell. Listing 12-12 shows the global objects from the help content.

Tip

If MySQL Shell encounters errors importing the plugin, a line like WARNING: Found errors loading plugins, for more details look at the log at: C:UsersmyuserAppDataRoamingMySQLmysqlshmysqlsh.log will be generated when starting MySQL Shell.

mysql-py> h
...
GLOBAL OBJECTS
The following modules and objects are ready for use when the shell starts:
 - dba     Used for InnoDB cluster administration.
 - myext   Various MySQL Shell extensions.
 - mysql   Support for connecting to MySQL servers using the classic MySQL
           protocol.
 - mysqlx  Used to work with X Protocol sessions using the MySQL X DevAPI.
 - session Represents the currently open MySQL session.
 - shell   Gives access to general purpose functions and properties.
 - util    Global object that groups miscellaneous tools like upgrade checker
           and JSON import.
For additional information on these global objects use: <object>.help()
Listing 12-12

The global objects in the help content

Notice how the myext plugin shows up as a global object. You can use the myext plugin just as any of the built-in global objects. This includes obtaining help for the subparts of the plugin such as shown in Listing 12-13 for myext.tools .
mysql-py> myext.tools.help()
NAME
      tools - Tools.
SYNTAX
      myext.tools
DESCRIPTION
      Various tools including describe() and dice().
FUNCTIONS
      describe(schema_name, table_name)
            Describe a table.
      dice()
            Roll a dice
      help([member])
            Provides help about this object and it's members
Listing 12-13

Obtaining help for myext.tools

As a last example, consider how the describe() and get_columns() methods can be used. Listing 12-14 uses both methods for the world.city table in the Python language mode.
mysql-py> myext.tools.describe('world', 'city')
+-------------+----------+------+-----+---------+----------------+
| Field       | Type     | Null | Key | Default | Extra          |
+-------------+----------+------+-----+---------+----------------+
| ID          | int(11)  | NO   | PRI | NULL    | auto_increment |
| Name        | char(35) | NO   |     |         |                |
| CountryCode | char(3)  | NO   | MUL |         |                |
| District    | char(20) | NO   |     |         |                |
| Population  | int(11)  | NO   |     | 0       |                |
+-------------+----------+------+-----+---------+----------------+
mysql-py> use world
Default schema `world` accessible through db.
mysql-py> result = myext.util.get_columns(db.city)
mysql-py> shell.dump_rows(result, 'json/pretty')
{
    "Field": "ID",
    "Type": "int(11)",
    "Null": "NO",
    "Key": "PRI",
    "Default": null,
    "Extra": "auto_increment"
}
{
    "Field": "Name",
    "Type": "char(35)",
    "Null": "NO",
    "Key": "",
    "Default": "",
    "Extra": ""
}
{
    "Field": "CountryCode",
    "Type": "char(3)",
    "Null": "NO",
    "Key": "MUL",
    "Default": "",
    "Extra": ""
}
{
    "Field": "District",
    "Type": "char(20)",
    "Null": "NO",
    "Key": "",
    "Default": "",
    "Extra": ""
}
{
    "Field": "Population",
    "Type": "int(11)",
    "Null": "NO",
    "Key": "",
    "Default": "0",
    "Extra": ""
}
5
Listing 12-14

Using the describe() and get_columns() methods in Python

First, the describe() method is used. The schema and table are provided as strings using their names, and the result is printed as a table. Then, the current schema is set to the world schema which allows you to access the tables as properties of the db object. The shell.dump_rows() method is then used to print the result as pretty printed JSON.

Tip

Because MySQL Shell detects if you use a method interactively, if you do not assign the result of get_columns() to a variable, MySQL Shell will output it directly to the console.

That concludes the discussion of MySQL Shell. If you do not already take advantage of the features it provides, you are encouraged to start using it.

Summary

This chapter has introduced MySQL Shell. It started out with an overview of how to install and use MySQL Shell, including working with connections; the SQL, Python, and JavaScript language modes; the built-in help; and the global objects. The rest of the chapter covered customization of MySQL Shell, reports, and extending MySQL.

The MySQL Shell prompt is more than a static label. It adapts according to the connection and default schema, and you can customize it to include information such as the version of MySQL you are connected to, and you can change the theme that is used.

The power of MySQL Shell comes from the built-in complex features and its support for creating complex methods. The simplest way to extend the features is to use external modules for JavaScript or Python. You can also use the reporting infrastructure including creating your own custom reports. Finally, MySQL Shell 8.0.17 and later have support for plugins which you can use to add your features in a global object. The reporting infrastructure and plugins have the advantage that the features you add become language independent.

All examples that use a command-line interface in the remainder of the book are created with MySQL Shell unless noted otherwise. To minimize the space used, the prompt has been replaced with just mysql> unless the language mode is important in which case the language mode is included, for example, mysql-py> for Python mode.

That concludes the discussion about tools for performance turning. Part IV covers schema considerations and the query optimizer with the next chapter discussing data types.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.221.11.62