mysql_execute_command(THD *thd)

LEX *lex = thd->lex;
switch (lex->sql_command) {
    case SQLCOM_CREATE_DB:      mysql_create_db();
    case SQLCOM_CREATE_TABLE:   mysql_create_table();
    case SQLCOM_INSERT:         mysql_insert();
    case SQLCOM_SELECT:         execute_sqlcom_select();
}

mysql_create_db(THD *thd, char *dbname, create_info, silent)

lock_schema_name(thd, dbname);
path = build_table_filename(path, path_length, dbname, tablename="", table_extension="", flags = 0);
// mysql_data_dir/db/
my_mkdir(path, 0777, ...);
// mysql_data_dir/db/db.opt
write_db_opt(thd, path, create_info);
    mysql_file_create(path);
    mysql_file_write("default-character-set=xxx\ndefault-collation=xxx\n");
if (!silent) {
    my_ok();
}

mysql_create_table(THD *thd, TABLE_LIST *create_table, create_info, alter_info)

/* 搞不清楚.frm文件的格式,编译一个debug版本的mysql nemiver一下.

mkdir -p mysql-debug/data

cd mysql-5.5.53
mkdir bld
cd bld
cmake ..
cmake-gui
修改install path, data path, with_debug, generate
make
make install

cd mysql-debug
./scripts/mysql_install_db --defaults-file=debug.cnf

cat debug.cnf
[client]
port = 3307
socket = /tmp/mysqld-debug.sock

[mysqld_safe]
socket = /tmp/mysqld-debug.sock

[mysqld]
user = heguangyu5
pid-file = /tmp/mysqld-debug.pid
socket = /tmp/mysqld-debug.sock
port = 3307
basedir = /home/heguangyu5/tmp/mysql-debug
datadir = /home/heguangyu5/tmp/mysql-debug/data

./bin/mysqld_safe --defaults-file=debug.cnf
./bin/mysqladmin --defaults-file=debug.cnf -uroot password '123456'
mysql -h127.0.0.1 -uroot -p123456 -P 3307
./bin/mysqladmin -h127.0.0.1 -uroot -p123456 -P 3307 shutdown

nemiver ./bin/mysqld \
    --defaults-file=debug.cnf \
    --plugin-dir=/home/heguangyu5/tmp/mysql-debug/lib/plugin \
    --log-error=/home/heguangyu5/tmp/mysql-debug/data/localhost.err

File -> Open Source File -> sql/sql_table.cc
*/

open_and_lock_tables(); // 在lxr里找不到open_and_lock_tables(..., prelocking_strategy)
                        // 这个函数定义在sql/sql_base.cc#5562
                        // open_tables(..., prelocking_strategy)定义在sql/sql_base.cc#4837
                        // 这个地方没有点其它的背景说明是搞不明白了.

mysql_create_table_no_lock(thd, create_table->db, create_table->table_name, ...);
    check_engine(); // 检查下对应的engine是否启用,以及创建表的名称和数据库的名称是否已占用.
                    // 比如mysql.user表就不能创建
    set_table_default_charset();

    // 调用engine的create()方法,create()方法返回一个handler,调用handler->init()方法
    handler *file = get_new_handler(handlerton* create_info->db_type);

    mysql_prepare_create_table();

    ha_table_exists_in_engine(thd, db, table_name);

    rea_create_table(); // create frm file
        mysql_create_frm();
        file->ha_create_handler_files();
        file->ha_create();

// storage是怎么加载运行的?
main()
    init_server_components()
        plugin_init()
            // for each mysql_mandatory_plugins(builtin plugins)
            // 执行cmake后在sql/sql_builtin.cc里可以找到myisam,csv
                register_builtin() // static HASH plugin_hash[MYSQL_MAX_PLUGIN_TYPE_NUM];
                plugin_initialize(); // only for MyISAM, CSV
                    // MyISAM: ha_initialize_handerton()
                        myisam_init(hton);
            // for each plugin_array // 上边只init了MyISAM和CSV,其它builtin plugin在这里init
                plugin_initialize();
                    plugin->init();


// 看看MyISAM是怎么实现create()方法和handler的ha_create_handler_files,ha_create的
myisam_hton->create = myisam_create_handler(); => new ha_myisam();
ha_myisam->init(); => cached_table_flags = HA_NULL_IN_KEY | HA_CAN_FULLTEXT | ...
ha_myisam->ha_create_handler_files(); // return FALSE
ha_myisam->ha_create(); => ha_myisam::create() => mi_create() 创建 .MYI .MYD 文件

mysql_insert()

open_and_lock_tables();
mysql_prepare_insert();
write_record();
    talbe->file->ha_write_row(); => ha_myisam::write_row(); => mi_write();

execute_sqlcom_select()

open_and_lock_tables();
handle_select();
    if (union) {
        mysql_union();
    } else {
        mysql_select();
    }

// mysql_union() 最终也是执行mysql_select()
mysql_select()
    if (select_lex->join != 0) {
        // union
        join = select_lex->join;
    } else {
        join = new JOIN();
    }
    join->optimize();
        make_join_readinfo()
            for (i = join->const_tables; i < join->tables; i++) {
                JOIN_TAB *tab = join->join_tab + i;
                tab.next_select = sub_select();
                pick_table_access_method(tab);
                    JT_CONST:   read_first_record = join_read_const;
                                read_record       = join_no_more_records;
                    JT_EQ_REF:  read_first_record = join_read_key;
                                read_record       = join_no_more_records;
                    JT_REF:     read_first_record = join_read_always_key;
                                read_record       = join_read_next_same;
                    // ...
            }
            join->join_tab[join->tables-1].next_select = 0;
    join->exec();
        do_select(curr_join, curr_fields_list, NULL, procedure);
            join->join_tab[join->table-1].next_select = end_send;
            join_tab = join->join_tab + join->const_tables;
            if (join->tables === join->const_tables) {
                // tables和const_tables什么区别?
                // 如果表里只有一条记录或者是通过主键或uniq key做的select,这些table称为const table
                if (!conds || conds->val_int()) {
                    // 如果有其它where条件, conds->val_int() 返回1表示match,返回0表示not match
                    end_send();
                }
            } else {
                sub_select();
                    (*join_tab->read_first_record)(join_tab);
                    evaluate_join_record(join, join_tab, error);
                        // 如果还有其它join,继续sub_select()
                        rc = (*join_tab->next_select)(join, join_tab+1, 0); => sub_select();
                        // 否则,返回 OK 或 NO_MORE_ROWS
                    while (rc == NESTED_LOOP_OK) {
                        read_record();
                        rc = evaluate_join_record();
                    }

                /*
                    sub_select()
                        read_first_record()
                        evaluate_join_record()
                            sub_select()
                                read_first_record()
                                evaluate_join_record()
                                    end_send()
                                while (OK) {
                                    read_record() => join_no_more_records(); => -1 // EQ_REF
                                    evaluate_join_record() => NO_MORE_ROWS
                                }
                                return OK;
                            return OK;
                        while (OK) {
                            read_record() => rr_sequential()
                            evaluate_join_record()
                                sub_select()
                                    read_first_record()
                                    evaluate_join_record()
                        }
                */
            }