?
本文檔使用 PHP中文網(wǎng)手冊 發(fā)布
目錄
對于MySQL 5.1,MySQL AB公司引入了插件式存儲引擎體系結(jié)構(gòu),這樣,就能創(chuàng)建新的存儲引擎,并將它們添加到正在運行的MySQL服務(wù)器上,而不必重新編譯服務(wù)器本身。
該體系結(jié)構(gòu)簡化了新存儲引擎的開發(fā)和部署。
本章的意圖是作為指南,用于幫助你為新的插件式存儲引擎體系結(jié)構(gòu)開發(fā)存儲引擎。
關(guān)于MySQL插件式存儲引擎體系結(jié)構(gòu)的更多信息,請參見第14章:插件式存儲引擎體系結(jié)構(gòu)。每個存儲引擎均是1個繼承類,每個類實例作為處理程序而被引用。
針對需要與特殊表一起工作的每個線程,處理程序是在1個處理程序的基礎(chǔ)上實例化的。例如,如果3個連接全都在相同的表上工作,需要創(chuàng)建3個處理程序?qū)嵗?/p>
一旦創(chuàng)建了處理程序?qū)嵗?span>MySQL服務(wù)器將向處理程序發(fā)送命令,以便執(zhí)行數(shù)據(jù)存儲和檢索任務(wù),如打開表、操縱行和管理索引等。
能夠以累進方式創(chuàng)建定制存儲引擎:開發(fā)人員能夠以只讀存儲引擎啟動,隨后添加對INSERT、UPDATE和DELETE操作的支持,甚至能夠增加對索引功能、事務(wù)和其他高級操作的支持。
實施新存儲引擎的最簡單方法是,通過拷貝和更改EXAMPLE存儲引擎開始。在MySQL 5.1源碼樹的sql/examples/目錄下可找到文件ha_example.cc和ha_example.h。關(guān)于如何獲得5.1源碼樹的說明,請參見2.8.3節(jié),“從開發(fā)源碼樹安裝”。
復(fù)制文件時,將名稱從ha_example.cc和ha_example.h更改為與存儲引擎相適應(yīng)的名稱,如ha_foo.cc和ha_foo.h。
拷貝并重命名了這些文件后,必須更換所有的EXAMPLE示例,以及具有存儲引擎名稱的示例。如果你熟悉sed,也能自動完成這些步驟:
sed s/EXAMPLE/FOO/g ha_example.h | sed s/example/foo/g ha_foo.h
sed s/EXAMPLE/FOO/g ha_example.cc | sed s/example/foo/g ha_foo.cc
handlerton(“單個處理程序”的簡稱)定義了存儲引擎,并包含指向函數(shù)的函數(shù)指針,它以整體方式作用在引擎上,而函數(shù)工作在單獨的處理程序?qū)嵗?。在這類函數(shù)的一些示例中,包含用于處理注釋和回滾的事務(wù)函數(shù)。
下面給出了一個來自EXAMPLE存儲引擎的示例:
handlerton example_hton= {
? "EXAMPLE",
? SHOW_OPTION_YES,
? "Example storage engine",
??DB_TYPE_EXAMPLE_DB,
? NULL,???
? 0,??????
? 0,??????
? NULL,???
? NULL,???
? NULL,???
? NULL,???
? NULL,???
? NULL,???
? NULL,???
? NULL,???
? NULL,???
? NULL,???
? NULL,???
? NULL,???
? NULL,???
? example_create_handler,???
? NULL,???
? NULL,???
? NULL,???
? NULL,???
? NULL,???
? NULL,???
? NULL,???
? NULL,???
? HTON_CAN_RECREATE
};
下面給出了來自handler.h的handlerton定義:
typedef struct
? {
??? const char *name;
??? SHOW_COMP_OPTION state;
??? const char *comment;
??? enum db_type db_type;
??? bool (*init)();
??? uint slot;
??? uint savepoint_offset;
??? int? (*close_connection)(THD *thd);
??? int? (*savepoint_set)(THD *thd, void *sv);
??? int? (*savepoint_rollback)(THD *thd, void *sv);
??? int? (*savepoint_release)(THD *thd, void *sv);
??? int? (*commit)(THD *thd, bool all);
?? ?int? (*rollback)(THD *thd, bool all);
??? int? (*prepare)(THD *thd, bool all);
??? int? (*recover)(XID *xid_list, uint len);
??? int? (*commit_by_xid)(XID *xid);
??? int? (*rollback_by_xid)(XID *xid);
??? void *(*create_cursor_read_view)();
??? void (*set_cursor_read_view)(void *);
??? void (*close_cursor_read_view)(void *);
??? handler *(*create)(TABLE *table);
??? void (*drop_database)(char* path);
??? int (*panic)(enum ha_panic_function flag);
??? int (*release_temporary_latches)(THD *thd);
??? int (*update_statistics)();
??? int (*start_consistent_snapshot)(THD *thd);
??? bool (*flush_logs)();
??? bool (*show_status)(THD *thd, stat_print_fn *print, enum ha_stat_type stat);
??? int (*repl_report_sent_binlog)(THD *thd, char *log_file_name, my_off_t end_offset);
??? uint32 flags;???????????????????????????????
??} handlerton;?
共有30個handlerton元素,但只有少量元素是強制性的(明確地講是前4個元素和第21個元素)。
1.??? 存儲引擎的名稱。這是創(chuàng)建表時將使用的名稱(CREATE TABLE ... ENGINE = FOO;)。
2.??? 確定使用SHOW STORAGE ENGINES命令時是否列出存儲引擎。
3.??? 存儲引擎注釋,對使用SHOW STORAGE ENGINES命令時顯示的存儲引擎的描述。
4.??? 在MySQL服務(wù)器內(nèi)唯一識別存儲引擎的整數(shù)。內(nèi)置存儲引擎使用的常數(shù)定義在handler.h文件中。作為創(chuàng)建常數(shù)的可選方法,可使用大于25的整數(shù)。
5.??? 指向存儲引擎初始化程序的指針。僅當(dāng)啟動服務(wù)器時才調(diào)用該函數(shù),以便在實例化處理程序之前,存儲引擎類能執(zhí)行必要的內(nèi)務(wù)操作。
6.??? 插槽。保存每連接的信息時,每個存儲引擎在thd中有自己的內(nèi)存區(qū)域(實際上為指針)。它是作為thd->ha_data[foo_hton.slot]訪問的。插槽編號在調(diào)用foo_init()后由MySQL初始化。
7.??? 保存點偏移。為了保存每個savepoint數(shù)據(jù),為存儲引擎提供了請求的大?。ǖ湫颓闆r下為0)。
必須以靜態(tài)方式初始化savepoint偏移,使其具有所有的內(nèi)存大小,以便保存每個savepoint的信息。在foo_init之后,它被更改為savepoint存儲區(qū)域的偏移,存儲引擎不需要使用它。
8.??? 由事務(wù)性存儲引擎使用,清理其存儲段內(nèi)分配的內(nèi)存,和/或回滾任何未完成的事務(wù)。
9.??? 由事務(wù)性存儲引擎選擇性使用,創(chuàng)建savepoint(保存點),并將其保存到提供的內(nèi)存中。
10.指向處理程序rollback_to_savepoint()函數(shù)的函數(shù)指針。它用于在事務(wù)期間返回savepoint。僅對支持保存點的存儲引擎才會填充它。
11.指向處理程序release_savepoint()函數(shù)的函數(shù)指針。它用于在事務(wù)期間釋放保存點的資源。僅對支持保存點的存儲引擎才會填充它。
12.指向處理程序commit()函數(shù)的函數(shù)指針。它用于提交事務(wù)。僅對支持事務(wù)的存儲引擎才會填充它。
13.指向處理程序rollback()函數(shù)的函數(shù)指針。它用于回滾交易。僅對支持事務(wù)的存儲引擎才會填充它。
14.XA事務(wù)性存儲引擎所需。為提交操作準(zhǔn)備事務(wù)。將XID與事務(wù)關(guān)聯(lián)起來。
15.XA事務(wù)性存儲引擎所需?;謴?fù)由XID標(biāo)識的事務(wù)。
16.XA事務(wù)性存儲引擎所需。提交由XID標(biāo)識的事務(wù)。
17.XA事務(wù)性存儲引擎所需。回滾由XID標(biāo)識的事務(wù)。
18.與服務(wù)器端光標(biāo)一起使用,尚未實施。
19.與服務(wù)器端光標(biāo)一起使用,尚未實施。
20.與服務(wù)器端光標(biāo)一起使用,尚未實施。
21.MANDATORY:構(gòu)造并返回處理程序?qū)嵗?/p>
22.撤銷方案時,如果存儲引擎需要執(zhí)行特殊步驟時使用(如在使用表空間的存儲引擎中使用)。
23.清理在服務(wù)器關(guān)閉和崩潰時調(diào)用的函數(shù)。
24.InnoDB特殊函數(shù)。
25.在啟動SHOW STATUS時調(diào)用InnoDB特殊函數(shù)。
26.調(diào)用InnoDB特殊函數(shù)以開始連續(xù)讀取。
27.調(diào)用它,指明應(yīng)將日志刷新為可靠的存儲。
28.在存儲引擎上提供可被人員讀取的狀態(tài)信息。
29.InnoDB特殊函數(shù)用于復(fù)制。
30.Handlerton標(biāo)志,通常與ALTER TABLE相關(guān)。可能的值定義于sql/handler.h文件中,并在此列出;
31.?????? #define HTON_NO_FLAGS???????????????? 0
32.?????? #define HTON_CLOSE_CURSORS_AT_COMMIT (1 << 0)
33.?????? #define HTON_ALTER_NOT_SUPPORTED ????(1 << 1)
34.?????? #define HTON_CAN_RECREATE??????????? (1 << 2)
35.?????? #define HTON_FLUSH_AFTER_RENAME????? (1 << 3)
36.?????? #define HTON_NOT_USER_SELECTABLE???? (1 << 4)
HTON_ALTER_NOT_SUPPORTED由FEDERATED存儲引擎使用,用以指明存儲引擎不接受AFTER TABLE語句。
HTON_FLUSH_AFTER_RENAME指明,重命名表后 ,必須調(diào)用FLUSH LOGS。
HTON_NOT_USER_SELECTABLE指明存儲引擎不能由用戶選擇,而是用作系統(tǒng)存儲引擎,如用于二進制日志的偽存儲引擎。
調(diào)用存儲引擎的第1個方法是調(diào)用新的處理程序?qū)嵗?/p>
在存儲引擎源文件中定義handlerton之前,必須定義用于函數(shù)實例化的函數(shù)題頭。下面給出了1個來自CSV引擎的示例:
static handler* tina_create_handler(TABLE *table);
正如你所見到的那樣,函數(shù)接受指向處理程序準(zhǔn)備管理的表的指針,并返回處理程序?qū)ο蟆?/p>
定義了函數(shù)題頭后,用第21個handlerton元素中的函數(shù)指針命名函數(shù),指明函數(shù)負(fù)責(zé)生成新的處理程序?qū)嵗?/p>
下面給出了MyISAM存儲引擎的實例化函數(shù)示例:
static handler *myisam_create_handler(TABLE *table)
? {
? ??return new ha_myisam(table);
? }
該調(diào)用隨后與存儲引擎的構(gòu)造程序一起工作。下面給出了來自FEDERATED存儲引擎的1個示例:
ha_federated::ha_federated(TABLE *table_arg)
? :handler(&federated_hton, table_arg),
? mysql(0), stored_result(0), scan_flag(0),
? ref_length(sizeof(MYSQL_ROW_OFFSET)), current_position(0)
? {}
下面給出了來自EXAMPLE存儲引擎的另一個示例:
ha_example::ha_example(TABLE *table_arg)
? :handler(&example_hton, table_arg)
? {}?
FEDERATED示例中的附加元素是處理程序的額外初始化要素。所要求的最低實施是EXAMPLE示例中顯示的handler()初始化。
就給定的表、數(shù)據(jù)和索引,要求存儲引擎為MySQL服務(wù)器提供存儲引擎所使用的擴展列表。
擴展應(yīng)采用以Null終結(jié)的字符串?dāng)?shù)組形式。下面給出了CSV引擎使用的數(shù)組:
static const char *ha_tina_exts[] = {
? ".CSV",
? NullS
};
調(diào)用bas_ext()函數(shù)時返回該數(shù)組。
const char **ha_tina::bas_ext() const
{
? return ha_tina_exts;
}
通過提供擴展信息,你還能忽略DROP TABLE功能的實施,這是因為,通過關(guān)閉表并用你指定的擴展刪除所有文件,MySQL服務(wù)器能實現(xiàn)該功能。
一旦實例化了處理程序,所需的第1個操作很可能是創(chuàng)建表。
你的存儲引擎必須實現(xiàn)create()虛擬函數(shù):
virtual int create(const char *name, TABLE *form, HA_CREATE_INFO *info)=0;
該函數(shù)應(yīng)創(chuàng)建所有必須的文件,然后關(guān)閉表。MySQL服務(wù)器將調(diào)用隨后需打開的表。
*name參數(shù)是表的名稱。*form參數(shù)是st_table結(jié)構(gòu),該結(jié)構(gòu)定義了表并與MySQL服務(wù)器已創(chuàng)建的tablename.frm文件的內(nèi)容匹配。在大多數(shù)情況下,存儲引擎不需要更改tablename.frm文件,也沒有支持該操作的預(yù)置功能。
*info參數(shù)是包含CREATE TABLE語句用于創(chuàng)建表所需信息的結(jié)構(gòu)。該結(jié)構(gòu)定義于handler.h文件中,并為了便于參考列于下面:
typedef struct st_ha_create_information
? {
??? CHARSET_INFO *table_charset, *default_table_charset;
??? LEX_STRING connect_string;
??? const char *comment,*password;
??? const char *data_file_name, *index_file_name;
??? const char *alias;
??? ulonglong max_rows,min_rows;
??? ulonglong auto_increment_value;
??? ulong table_options;
??? ulong avg_row_length;
??? ulong raid_chunksize;
??? ulong used_fields;
??? SQL_LIST merge_list;
??? enum db_type db_type;
??? enum row_type row_type;
??? uint null_bits;??????????????????????
??? uint options;???????????????????????????? ?????????????????
??? uint raid_type,raid_chunks;
??? uint merge_insert_method;
??? uint extra_size;?????????????????????
??? bool table_existed;?????????????????????? ?????????????
??? bool frm_only;???????????????????????
??? bool varchar;????????????????????????
? } HA_CREATE_INFO;
基本的存儲引擎能忽略*form和*info的內(nèi)容,這是因為,真正所需的是創(chuàng)建存儲引擎所使用的數(shù)據(jù)文件,以及對數(shù)據(jù)文件的可能初始化操作(假定存儲文件是基于文件的)。
下面給出了來自CSV存儲引擎的實施示例:
int ha_tina::create(const char *name, TABLE *table_arg,
? HA_CREATE_INFO *create_info)
? {
??? char name_buff[FN_REFLEN];
??? File create_file;
??? DBUG_ENTER("ha_tina::create");
?
????if ((create_file= my_create(fn_format(name_buff, name, "", ".CSV",
????????? MY_REPLACE_EXT|MY_UNPACK_FILENAME),0,
????????? O_RDWR | O_TRUNC,MYF(MY_WME))) < 0)
??? DBUG_RETURN(-1);
?
????my_close(create_file,MYF(0));
????DBUG_RETURN(0);
? }
在前面的例子中,CSV引擎未引用*table_arg或*create_info參數(shù),而是簡單地創(chuàng)建了所需的數(shù)據(jù)文件,關(guān)閉它們,并返回。
my_create和my_close函數(shù)是定義于src/include/my_sys.h文件中的助手函數(shù)。
在表上執(zhí)行任何讀或?qū)懖僮髦埃?span>MySQL服務(wù)器將調(diào)用open()方法打開表數(shù)據(jù)和索引文件(如果存在的話)。
int open(const char *name, int mode, int test_if_locked);
第1個參數(shù)是要打開的表的名稱。第2個參數(shù)確定了要打開的文件或準(zhǔn)備執(zhí)行的操作。它們的值定義于handler.h中,并為了方便起見列在下面:
#define HA_OPEN_KEYFILE?????????????? ? 1
#define HA_OPEN_RNDFILE?????????????? ? 2
#define HA_GET_INDEX?????????? ??? 4
#define HA_GET_INFO??????????? ????? 8 ???
#define HA_READ_ONLY?????????? ??? 16? ?
#define HA_TRY_READ_ONLY?????? ? 32???
#define HA_WAIT_IF_LOCKED????? ? 64??? ?
#define HA_ABORT_IF_LOCKED???? 128???? ?
#define HA_BLOCK_LOCK????????? ??? 256 ?
#define HA_OPEN_TEMPORARY????? ? 512
最后一個選項規(guī)定了是否要在打開表之前檢查表上的鎖定。
在典型情況下,存儲引擎需要實施某種形式的共享訪問控制,以防止在多線程環(huán)境下的文件損壞。關(guān)于如何實施文件鎖定的示例,請參見sql/examples/ha_tina.cc的get_share()和free_share()方法。
最基本的存儲引擎能實現(xiàn)只讀表掃描功能。這類引擎可用于支持SQL日志查詢、以及在MySQL之外填充的其他數(shù)據(jù)文件。
本節(jié)介紹的方法實施提供了創(chuàng)建更高級存儲引擎的基礎(chǔ)。
下面給出了在CSV引擎的9行表掃描過程中進行的方法調(diào)用:
ha_tina::store_lock ha_tina::external_lock ha_tina::info ha_tina::rnd_init ha_tina::extra - ENUM HA_EXTRA_CACHE Cache record in HA_rrnd() ha_tina::rnd_next ha_tina::rnd_next ha_tina::rnd_next ha_tina::rnd_next ha_tina::rnd_next ha_tina::rnd_next ha_tina::rnd_next ha_tina::rnd_next ha_tina::rnd_next ha_tina::extra - ENUM HA_EXTRA_NO_CACHE End cacheing of records (def) ha_tina::external_lock ha_tina::extra - ENUM HA_EXTRA_RESET Reset database to after open
在執(zhí)行任何讀取或?qū)懖僮髦?,調(diào)用store_lock()函數(shù)。
將鎖定添加到表鎖定處理程序之前(請參見thr_lock.c),mysqld將用請求的鎖調(diào)用存儲鎖定。目前,存儲鎖定能將寫鎖定更改為讀鎖定(或其他鎖定),忽略鎖定(如果不打算使用MySQL鎖定的話),或為很多表添加鎖定(就像使用MERGE處理程序時作的那樣)。
例如,Berkeley DB能將所有的WRITE鎖定更改為TL_WRITE_ALLOW_WRITE(表示我們正在執(zhí)行WRITES,但我們?nèi)栽试S其他人員進行操作)。
釋放鎖定時,也將調(diào)用store_lock(),在這種情況下,通常不需做任何事。
在某些特殊情況下,MySQL可能會發(fā)送對TL_IGNORE的請求。這意味著我們正在請求與上次相同的鎖定,這也應(yīng)被忽略(當(dāng)我們打開了表的某一部分時,如果其他人執(zhí)行了表刷新操作,就會出現(xiàn)該情況,此時,mysqld將關(guān)閉并再次打開表,然后獲取與上次相同的鎖定)。我們打算在將來刪除該特性。
可能的鎖定類型定義于includes/thr_lock.h中,并列在下面:
enum thr_lock_type
{
???????? TL_IGNORE=-1,
????????????? ?????TL_UNLOCK,??????????????? ????????????
????????????? ?????TL_READ,????????????????? ??????????????
????????????? ?????TL_READ_WITH_SHARED_LOCKS,?
?????????TL_READ_HIGH_PRIORITY,?????
???????? TL_READ_NO_INSERT, ????????? ?????
???????? TL_WRITE_ALLOW_WRITE, ?????????????? ???
???????? TL_WRITE_ALLOW_READ,? ??????
???????? TL_WRITE_CONCURRENT_INSERT,
???????? TL_WRITE_DELAYED,? ????????? ?????
???????? TL_WRITE_LOW_PRIORITY,?????? ????
???????? TL_WRITE,????????? ????????? ?????
???????? TL_WRITE_ONLY??????????????
};?
實際的鎖定處理因鎖定實施的不同而不同,你可以選擇某些請求的鎖定類型或不選擇任何鎖定類型,并根據(jù)情況恰當(dāng)?shù)卮肽阕约旱姆椒?。下面給出了1個CSV存儲引擎實施示例:
THR_LOCK_DATA **ha_tina::store_lock(THD *thd,
???????????????????????????????????? THR_LOCK_DATA **to,
???????????????????????????????????? enum thr_lock_type lock_type)
{
?? if (lock_type != TL_IGNORE && lock.type == TL_UNLOCK)
???? lock.type=lock_type;
?? *to++= &lock;
?? return to;
}?
external_lock() 函數(shù)是在事務(wù)開始時調(diào)用的,或發(fā)出LOCK TABLES語句時調(diào)用的,用于事務(wù)性存儲引擎。
在sql/ha_innodb.cc和sql/ha_berkeley.cc文件中,可找到使用external_lock()的示例,但大多數(shù)存儲引擎簡單地返回0,就像EXAMPLE存儲引擎那樣:
int ha_example::external_lock(THD *thd, int lock_type)
{
?? DBUG_ENTER("ha_example::external_lock");
?? DBUG_RETURN(0);
}
在任何表掃描之前調(diào)用的函數(shù)是rnd_init()函數(shù)。函數(shù)rnd_init()用于為表掃描作準(zhǔn)備,將計數(shù)器和指針復(fù)位為表的開始狀態(tài)。
下述示例來自CSV存儲引擎:
int ha_tina::rnd_init(bool scan) { DBUG_ENTER("ha_tina::rnd_init"); current_position= next_position= 0; records= 0; chain_ptr= chain; DBUG_RETURN(0); }
優(yōu)化程序所需的信息不是通過返回值給定的,你需填充存儲引擎類的特定屬性,當(dāng)info()調(diào)用返回后,優(yōu)化程序?qū)⒆x取存儲引擎類。
除了供優(yōu)化程序使用外,在調(diào)用info()函數(shù)期間,很多值集合還將用于SHOW TABLE STATUS語句。
在sql/handler.h中列出了完整的公共屬性,下面給出了一些常見的屬性:
ulonglong data_file_length;??????????
ulonglong max_data_file_length;??????
ulonglong index_file_length;
ulonglong max_index_file_length;
ulonglong delete_length;?????????????
ulonglong auto_increment_value;
ha_rows records;?????????????????????
ha_rows deleted;?????????????????????
ulong raid_chunksize;
ulong mean_rec_length;????????
time_t create_time;??????????????????
time_t check_time;
time_t update_time;?
對于表掃描,最重要的屬性是“records”,它指明了表中的記錄數(shù)。當(dāng)存儲引擎指明表中有0或1行時,或有2行以上時,在這兩種情況下,優(yōu)化程序的執(zhí)行方式不同。因此,當(dāng)你在執(zhí)行表掃描之前不清楚表中有多少行時,應(yīng)返回大于等于2的值,這很重要(例如,數(shù)據(jù)是在外部填充的)。
執(zhí)行某些操作之前,應(yīng)調(diào)用extra()函數(shù),以便為存儲引擎就如何執(zhí)行特定操作予以提示。
額外調(diào)用中的提示實施不是強制性的,大多數(shù)存儲引擎均返回0:
int ha_tina::extra(enum ha_extra_function operation) { DBUG_ENTER("ha_tina::extra"); DBUG_RETURN(0); }
完成表的初始化操作后,MySQL服務(wù)器將調(diào)用處理程序的rnd_next()函數(shù),每兩個掃描行調(diào)用1次,直至滿足了服務(wù)器的搜索條件或到達文件結(jié)尾為止,在后一種情況下,處理程序?qū)⒎祷?span>HA_ERR_END_OF_FILE。
rnd_next()函數(shù)有一個名為*buf的單字節(jié)數(shù)組參數(shù)。對于*buf參數(shù),必須按內(nèi)部MySQL格式用表行的內(nèi)容填充它。
服務(wù)器采用了三種數(shù)據(jù)格式:固定長度行,可變長度行,以及具有BLOB指針的可變長度行。對于每種格式,各列將按照它們由CREATE TABLE語句定義的順序顯示(表定義保存在.frm文件中,優(yōu)化程序和處理程序均能從相同的源,即TABLE結(jié)構(gòu),訪問表的元數(shù)據(jù))。
每種格式以每列1比特的"NULL bitmap"開始。對于含6個列的表,其bitmap為1字節(jié),對于含9~16列的表,其bitmap為2字節(jié),依此類推。要想指明特定的值是NULL,應(yīng)將該列NULL位設(shè)置為1。
當(dāng)NULL bitmap逐個進入列后,每列將具有MySQL手冊的“MySQL數(shù)據(jù)類型”一節(jié)中指定的大小。在服務(wù)器中,列的數(shù)據(jù)類型定義在sql/field.cc文件中。對于固定長度行格式,列將簡單地逐個放置。對于可變長度行,VARCHAR列將被編碼為1字節(jié)長,后跟字符串。對于具有BLOB列的可變長度行,每個blob由兩部分表示:首先是表示BLOB實際大小的整數(shù),然后是指向內(nèi)存中BLOB的指針。
在任何表處理程序中從rnd_next()開始,可找到行轉(zhuǎn)換(或“包裝”)的示例。例如,在ha_tina.cc中,find_current_row()內(nèi)的代碼給出了使用TABLE結(jié)構(gòu)(由表指向的)和字符串對象(命名緩沖)包裝字符數(shù)據(jù)(來自CSV文件)的方法。將行寫回磁盤需要反向轉(zhuǎn)換,從內(nèi)部格式解包。
下述示例來自CSV存儲引擎:
int ha_tina::rnd_next(byte *buf)
{
? ?DBUG_ENTER("ha_tina::rnd_next");
???statistic_increment(table->in_use->status_var.ha_read_rnd_next_count, &LOCK_status);
???current_position= next_position;
?? if (!share->mapped_file)
???? DBUG_RETURN(HA_ERR_END_OF_FILE);
?? if (HA_ERR_END_OF_FILE == find_current_row(buf) )
???? DBUG_RETURN(HA_ERR_END_OF_FILE);
???records++;
?? DBUG_RETURN(0);
}?
對于從內(nèi)部行格式到CSV行格式的轉(zhuǎn)換,它是在find_current_row()函數(shù)中執(zhí)行的。
int ha_tina::find_current_row(byte *buf)
{
?? byte *mapped_ptr= (byte *)share->mapped_file + current_position;
?? byte *end_ptr;
?? DBUG_ENTER("ha_tina::find_current_row");
???
?? if ((end_ptr=? find_eoln(share->mapped_file, current_position,
??????????????????????????? share->file_stat.st_size)) == 0)
???? DBUG_RETURN(HA_ERR_END_OF_FILE);
???for (Field **field=table->field ; *field ; field++)
?? {
???? buffer.length(0);
???? mapped_ptr++; // Increment past the first quote
???? for(;mapped_ptr != end_ptr; mapped_ptr++)
???? {
?????? // Need to convert line feeds!
?????? if (*mapped_ptr == '"' &&
?????????? (((mapped_ptr[1] == ',') && (mapped_ptr[2] == '"')) ||
??????????? (mapped_ptr == end_ptr -1 )))
?????? {
???????? mapped_ptr += 2; // Move past the , and the "
???????? break;
??? ???}
?????? if (*mapped_ptr == '\\' && mapped_ptr != (end_ptr - 1))
?????? {
???????? mapped_ptr++;
???????? if (*mapped_ptr == 'r')
?????????? buffer.append('\r');
???????? else if (*mapped_ptr == 'n' )
?????????? buffer.append('\n');
???????? else if ((*mapped_ptr == '\\') || (*mapped_ptr == '"'))
?????????? buffer.append(*mapped_ptr);
???????? else?
???????? {
?????????? buffer.append('\\');
?????????? buffer.append(*mapped_ptr);
???????? }
?? ????}
?????? else
???????? buffer.append(*mapped_ptr);
???? }
???? (*field)->store(buffer.ptr(), buffer.length(), system_charset_info);
?? }
?? next_position= (end_ptr - share->mapped_file)+1;
??
?? memset(buf, 0, table->s->null_bytes);
???DBUG_RETURN(0);
}?
對于使用共享訪問方法的存儲引擎(如CSV引擎和其他示例引擎中顯示的方法),必須將它們自己從共享結(jié)構(gòu)中刪除:
int ha_tina::close(void)
{
?? DBUG_ENTER("ha_tina::close");
?? DBUG_RETURN(free_share(share));
}?
對于使用其自己共享管理系統(tǒng)的存儲引擎,應(yīng)使用任何所需的方法,在它們的處理程序中,從已打開表的共享區(qū)刪除處理程序?qū)嵗?/p>
所有的INSERT操作均是通過write_row()函數(shù)予以處理的:
int ha_foo::write_row(byte *buf)?
*buf參數(shù)包含將要插入的行,采用內(nèi)部MySQL格式。基本的存儲引擎將簡單地前進到數(shù)據(jù)文件末尾,并直接在末尾處添加緩沖的內(nèi)容,這樣就能使行讀取變得簡單,這是因為,你可以讀取行并將其直接傳遞到rnd_next()函數(shù)的緩沖參數(shù)中。
寫入行的進程與讀取行的進程相反:從MySQL內(nèi)部行格式獲取數(shù)據(jù),并將其寫入數(shù)據(jù)文件。下述示例來自CSV存儲引擎:
int ha_tina::write_row(byte * buf)
{
?? int size;
?? DBUG_ENTER("ha_tina::write_row");
???statistic_increment(table->in_use->status_var.ha_write_count, &LOCK_status);
???if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT)
???? table->timestamp_field->set_time();
???size= encode_quote(buf);
???if (my_write(share->data_file, buffer.ptr(), size, MYF(MY_WME | MY_NABP)))
???? DBUG_RETURN(-1);
?
?? if (get_mmap(share, 0) > 0)
???? DBUG_RETURN(-1);
?? DBUG_RETURN(0);
}
前述示例中的兩條注釋包括,更新關(guān)于寫入操作的表統(tǒng)計,以及在寫入行之前設(shè)置時間戳。
通過執(zhí)行表掃描操作,在找到與UPDATE語句的WHERE子句匹配的行后,MySQL服務(wù)器將執(zhí)行UPDATE語句,然后調(diào)用update_row()函數(shù):
int ha_foo::update_row(const byte *old_data, byte *new_data)
*old_data參數(shù)包含更新前位于行中的數(shù)據(jù),而*new_data參數(shù)包含行的新內(nèi)容(采用MySQL內(nèi)部行格式)。
更新的執(zhí)行取決于行格式和存儲實施方式。某些存儲引擎將替換恰當(dāng)位置的數(shù)據(jù),而其他實施方案則會刪除已有的行,并在數(shù)據(jù)文件末尾添加新行。
非事務(wù)性存儲引擎通常會忽略*old_data參數(shù)的內(nèi)容,僅處理*new_data緩沖。事務(wù)性存儲引擎可能需要比較緩沖,以確定在上次回滾中出現(xiàn)了什么變化。
如果正在更新的表中包含時間戳列,對時間戳的更新將由update_row()調(diào)用管理。下述示例來自CSV引擎:
int ha_tina::update_row(const byte * old_data, byte * new_data)
{
?? int size;
?? DBUG_ENTER("ha_tina::update_row");
???statistic_increment(table->in_use->status_var.ha_read_rnd_next_count,
????????????? ??????&LOCK_status);
???if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_UPDATE)
???? table->timestamp_field->set_time();
???size= encode_quote(new_data);
???if (chain_append())
???? DBUG_RETURN(-1);
???if (my_write(share->data_file, buffer.ptr(), size, MYF(MY_WME | MY_NABP)))
???? DBUG_RETURN(-1);
?? DBUG_RETURN(0);
}
請注意上例中的時間戳設(shè)置。
MySQL服務(wù)器采用了與INSERT語句相同的方法來執(zhí)行DELETE語句:服務(wù)器使用rnd_next()函數(shù)跳到要刪除的行,然后調(diào)用delete_row()函數(shù)刪除行。
int ha_foo::delete_row(const byte *buf)
*buf參數(shù)包含要刪除行的內(nèi)容。對于大多數(shù)存儲引擎,該參數(shù)可被忽略,但事務(wù)性存儲引擎可能需要保存刪除的數(shù)據(jù),以供回滾操作使用。
下述示例來自CSV存儲引擎:
int ha_tina::delete_row(const byte * buf)
{
?? DBUG_ENTER("ha_tina::delete_row");
?? statistic_increment(table->in_use->status_var.ha_delete_count,
?????????????????????? &LOCK_status);
???if (chain_append())
???? DBUG_RETURN(-1);
?
???--records;
???DBUG_RETURN(0);
}
前述示例的步驟是更新delete_count統(tǒng)計,并記錄計數(shù)。
定義存儲引擎所使用的文件擴展。
virtual const char ** bas_ext ( | ); | ? |
? | ; |
這是bas_ext方法。調(diào)用它,可為MySQL服務(wù)器提供存儲引擎所使用的文件擴展列表。該列表將返回以Null終結(jié)的字符串?dāng)?shù)組。
通過提供擴展列表,在很多情況下,存儲引擎能省略delete_table()函數(shù),這是因為MySQL服務(wù)器將關(guān)閉所有對表的引用,并使用指定的擴展刪除所有文件。
該函數(shù)無參數(shù)。
返回值是存儲引擎擴展的以Null終結(jié)的字符串?dāng)?shù)組。下面給出了CSV引擎的示例:
static const char *ha_tina_exts[] = { ".CSV", NullS };
static const char *ha_tina_exts[] = { ".CSV", NullS }; const char **ha_tina::bas_ext() const { return ha_tina_exts; }
static const char *ha_example_exts[] = { NullS }; const char **ha_example::bas_ext() const { return ha_example_exts; }
關(guān)閉打開的表。
virtual int close ( | void); | ? |
? | void ; |
這是close方法。
關(guān)閉表。這是釋放任何已分配資源的恰當(dāng)時機。
從sql_base.cc、sql_select.cc和table.cc調(diào)用它。在sql_select.cc中,它僅用于關(guān)閉臨時表,或在將臨時表轉(zhuǎn)換為myisam表的過程中關(guān)閉表。關(guān)于sql_base.cc,請查看close_data_tables()。
void
無返回值。
取自CSV引擎的示例:
int ha_example::close(void) { DBUG_ENTER("ha_example::close"); DBUG_RETURN(free_share(share)); }
創(chuàng)建新表。
virtual int create ( | name, | ? |
? | form, | ? |
? | info); | ? |
const char *? | name ; |
TABLE *? | form ; |
HA_CREATE_INFO *? | info ; |
這是create方法。
調(diào)用create()以創(chuàng)建表。變量名稱為表的名稱。調(diào)用create()時,不需要打開表。此外,由于已創(chuàng)建了.frm文件,不推薦調(diào)整create_info。
由ha_create_table()從handle.cc中調(diào)用。
name
form
info
無返回值。
CSV搜索引擎示例:
int ha_tina::create(const char *name, TABLE *table_arg, HA_CREATE_INFO *create_info) { char name_buff[FN_REFLEN]; File create_file; DBUG_ENTER("ha_tina::create"); if ((create_file= my_create(fn_format(name_buff, name, "", ".CSV", MY_REPLACE_EXT|MY_UNPACK_FILENAME),0, O_RDWR | O_TRUNC,MYF(MY_WME))) < 0) DBUG_RETURN(-1); my_close(create_file,MYF(0)); DBUG_RETURN(0); }
刪除行。
virtual int delete_row ( | buf); | ? |
const byte *? | buf ; |
這是delete_row方法。
Buf包含刪除行的副本。調(diào)用了當(dāng)前行后,服務(wù)器將立刻調(diào)用它(通過前一個rnd_next()或索引調(diào)用)。如果存在指向上一行的指針,或能夠訪問 主鍵,刪除操作將更為容易。請記住,服務(wù)器不保證連續(xù)刪除。可以使用ORDER BY子句。
在sql_acl.cc和sql_udf.cc中調(diào)用,以管理內(nèi)部的表信息。在sql_delete.cc、sql_insert.cc和sql_select.cc中調(diào)用。在sql_select中,它用于刪除副本,而在插入操作中,它用于REPLACE調(diào)用。
buf
無返回值。
{ return HA_ERR_WRONG_COMMAND; }
用來自bas_ext()的擴展刪除所有文件。
virtual int delete_table ( | name); | ? |
const char *? | name ; |
這是delete_table方法。
用于刪除表。調(diào)用delete_table()時,所有已打開的對該表的引用均將被關(guān)閉(并釋放全局共享的引用)。變量名稱為表名。此時,需要刪除任何已創(chuàng)建的文件。
如果未實施它,將從handler.cc調(diào)用默認(rèn)的delete_table(),并用bas_ext()返回的文件擴展刪除所有文件。假定處理程序返回的擴展比文件實際使用的多。
由delete_table和ha_create_table()從handler.cc調(diào)用。如果為存儲引擎指定了table_flag HA_DROP_BEFORE_CREATE,僅在創(chuàng)建過程中使用。
name: 表的基本名稱
·???????? 如果成功地從base_ext刪除了至少1個文件而且未出現(xiàn)除ENOENT之外的錯誤,返回0。
·???????? #: Error
為事務(wù)處理表鎖定。
virtual int external_lock ( | thd, | ? |
? | lock_type); | ? |
THD *? | thd ; |
int? | lock_type ; |
這是external_lock方法。
在lock.cc中“用于mysql的鎖定函數(shù)”一節(jié),給出了關(guān)于該議題的額外注釋,值的一讀。
在表上創(chuàng)建鎖定。如果實施了能處理事務(wù)的存儲引擎,請查看ha_berkely.cc,以了解如何執(zhí)行該操作的方法。否則,應(yīng)考慮在此調(diào)用flock()。
由lock_external()和unlock_external()從lock.cc中調(diào)用。也能由copy_data_between_tables()從sql_table.cc中調(diào)用。
thd
lock_type
無返回值。
{ return 0; }
將提示從服務(wù)器傳遞給存儲引擎。
virtual int extra ( | operation); | ? |
enum ha_extra_function? | operation ; |
這是extra方法。
無論何時,當(dāng)服務(wù)器希望將提示發(fā)送到存儲引擎時,將調(diào)用extra()。MyISAM引擎實現(xiàn)了大多數(shù)提示。ha_innodb.cc給出了最詳盡的提示列表。
operation
無返回值。
默認(rèn)情況下,存儲引擎傾向于不實施任何提示。
{ return 0; }
提示存儲引擎通報統(tǒng)計信息。
virtual void info ( | uint); | ? |
? | uint ; |
這是info方法。
::info()用于將信息返回給優(yōu)化程序。目前,該表處理程序未實施實際需要的大多數(shù)字段。SHOW也能利用該數(shù)據(jù)。注意,或許你打算在你的代碼中包含下述內(nèi)容“if (records > 2) records = 2”。原因在于,服務(wù)器僅優(yōu)化具有一條記錄的情形。如果在表掃描過程中,你不清楚記錄的數(shù)目,最好將記錄數(shù)設(shè)為2,以便能夠返回盡可能多的所需記錄。除了記錄外,你或許還希望設(shè)置其他變量,包括:刪除的記錄,data_file_length,index_file_length,delete_length,check_time。更多信息,請參見handler.h中的公共變量。
在下述文件中調(diào)用:filesort.cc ha_heap.cc item_sum.cc opt_sum.cc sql_delete.cc sql_delete.cc sql_derived.cc sql_select.cc sql_select.cc sql_select.cc sql_select.cc sql_select.cc sql_show.cc sql_show.cc sql_show.cc sql_show.cc sql_table.cc sql_union.cc sql_update.cc
uint
無返回值。
該示例取自CSV存儲引擎:
void ha_tina::info(uint flag) { DBUG_ENTER("ha_tina::info"); if (records < 2) records= 2; DBUG_VOID_RETURN; }
打開表。
virtual int open ( | name, | ? |
? | mode, | ? |
? | test_if_locked); | ? |
const char *? | name ; |
int? | mode ; |
uint? | test_if_locked ; |
這是open方法。
用于打開表。名稱是文件的名稱。在需要打開表時打開它。例如,當(dāng)請求在表上執(zhí)行選擇操作時(對于每一請求,表未打開并被關(guān)閉,對其進行高速緩沖處理)。
由handler::ha_open()從handler.cc中調(diào)用。通過調(diào)用ha_open(),然后調(diào)用處理程序相關(guān)的open(),服務(wù)器打開所有表。
對于處理程序?qū)ο?,將作為初始化的一部分并在將其用于正常查詢之前打開它(并非總在元數(shù)據(jù)變化之前)。如果打開了對象,在刪除之前還將關(guān)閉它。
這是open方法。調(diào)用open以打開數(shù)據(jù)庫表。
第1個參數(shù)是要打開的表的名稱。第2個參數(shù)決定了要打開的文件或?qū)⒁獔?zhí)行的操作。這類值定義于handler.h中,為了方便起見在此列出:
??????? #define HA_OPEN_KEYFILE?????????????? ? 1
??????? #define HA_OPEN_RNDFILE?????????????? ? 2
??????? #define HA_GET_INDEX????????? ??? 4
??????? #define HA_GET_INFO?????????? ????? 8 ???
??????? #define HA_READ_ONLY????????? ??? 16? ?
??????? #define HA_TRY_READ_ONLY????? ? 32???
??????? #define HA_WAIT_IF_LOCKED???? ? 64??? ?
??????? #define HA_ABORT_IF_LOCKED??? 128???? ?
??????? #define HA_BLOCK_LOCK???????? ??? 256 ?
??????? #define HA_OPEN_TEMPORARY???? ? 512
?????
最后的選項規(guī)定了在打開表之前是否應(yīng)檢查表上的鎖定。
典型情況下,存儲引擎需要實現(xiàn)某種形式的共享訪問控制,以防止多線程環(huán)境下的文件損壞。關(guān)于如何實現(xiàn)文件鎖定的示例,請參見sql/examples/ha_tina.cc的get_share()和free_share()方法。
name
mode
test_if_locked
無返回值。
該示例取自CSV存儲引擎:
int ha_tina::open(const char *name, int mode, uint test_if_locked) { DBUG_ENTER("ha_tina::open"); if (!(share= get_share(name, table))) DBUG_RETURN(1); thr_lock_data_init(&share->lock,&lock,NULL); ref_length=sizeof(off_t); DBUG_RETURN(0); }
為表掃描功能初始化處理程序。
virtual int rnd_init ( | scan); | ? |
bool? | scan ; |
這是rnd_init方法。
當(dāng)系統(tǒng)希望存儲引擎執(zhí)行表掃描時,將調(diào)用rnd_init()。
與index_init()不同,rnd_init()可以調(diào)用兩次,兩次調(diào)用之間不使用rnd_end()(僅當(dāng)scan=1時才有意義)。隨后,第2次調(diào)用應(yīng)準(zhǔn)備好新的表掃描。例如,如果rnd_init分配了光標(biāo),第2次調(diào)用應(yīng)將光標(biāo)定位于表的開始部分,不需要撤銷分配并再次分配。
從下述文件調(diào)用:filesort.cc, records.cc, sql_handler.cc, sql_select.cc, sql_table.cc, 和sql_update.cc。
scan
無返回值。
該示例取自CSV存儲引擎:
int ha_tina::rnd_init(bool scan) { DBUG_ENTER("ha_tina::rnd_init"); current_position= next_position= 0; records= 0; chain_ptr= chain; DBUG_RETURN(0); }
從表中讀取下一行,并將其返回服務(wù)器。
virtual int rnd_next ( | buf); | ? |
byte *? | buf ; |
這是rnd_next方法。
對于表掃描的每一行調(diào)用它。耗盡記錄時,應(yīng)返回HA_ERR_END_OF_FILE。用行信息填充buff。表的字段結(jié)構(gòu)是以服務(wù)器能理解的方式將數(shù)據(jù)保存到buf中的鍵。
從下述文件調(diào)用:filesort.cc, records.cc, sql_handler.cc, sql_select.cc, sql_table.cc, 和sql_update.cc。
buf
無返回值。
下述示例取自ARCHIVE存儲引擎:
int ha_archive::rnd_next(byte *buf) { int rc; DBUG_ENTER("ha_archive::rnd_next"); if (share->crashed) DBUG_RETURN(HA_ERR_CRASHED_ON_USAGE); if (!scan_rows) DBUG_RETURN(HA_ERR_END_OF_FILE); scan_rows--; statistic_increment(table->in_use->status_var.ha_read_rnd_next_count, &LOCK_status); current_position= gztell(archive); rc= get_row(archive, buf); if (rc != HA_ERR_END_OF_FILE) records++; DBUG_RETURN(rc); }
創(chuàng)建和釋放表鎖定。
virtual THR_LOCK_DATA ** store_lock ( | thd, | ? |
? | to, | ? |
? | lock_type); | ? |
THD *? | thd ; |
THR_LOCK_DATA **? | to ; |
enum thr_lock_type? | lock_type ; |
這是store_lock方法。
下面介紹了關(guān)于handler::store_lock()的概念:
該語句決定了在表上需要何種鎖定。對于updates/deletes/inserts,我們得到WRITE鎖定;對于SELECT...,我們得到讀鎖定。
將鎖定添加到表鎖定處理程序之前(請參見thr_lock.c),mysqld將用請求的鎖定調(diào)用存儲鎖定。目前,存儲鎖定能將寫鎖定更改為讀鎖定(或某些其他鎖定),忽略鎖定(如果不打算使用MySQL表鎖定),或為很多表添加鎖定(就像使用MERGE處理程序時那樣)。
例如,Berkeley DB能夠?qū)⑺械?span>WRITE鎖定更改為TL_WRITE_ALLOW_WRITE(表明正在執(zhí)行WRITES操作,但我們?nèi)栽试S其他人執(zhí)行操作)。
釋放鎖定時,也將調(diào)用store_lock()。在這種情況下,通常不需要作任何事。
在某些特殊情況下,MySQL可能會發(fā)送對TL_IGNORE的請求。這意味著我們正在請求與上次相同的鎖定,這也應(yīng)被忽略(當(dāng)我們打開了表的某一部分時,如果其他人執(zhí)行了表刷新操作,就會出現(xiàn)該情況,此時,mysqld將關(guān)閉并再次打開表,然后獲取與上次相同的鎖定)。我們打算在將來刪除該特性。
由get_lock_data()從lock.cc中調(diào)用。
thd
to
lock_type
無返回值。
下述示例取自ARCHIVE存儲引擎:
THR_LOCK_DATA **ha_archive::store_lock(THD *thd, THR_LOCK_DATA **to, enum thr_lock_type lock_type) { if (lock_type == TL_WRITE_DELAYED) delayed_insert= TRUE; else delayed_insert= FALSE; if (lock_type != TL_IGNORE && lock.type == TL_UNLOCK) { if ((lock_type >= TL_WRITE_CONCURRENT_INSERT && lock_type <= TL_WRITE) && !thd->in_lock_tables && !thd->tablespace_op) lock_type = TL_WRITE_ALLOW_WRITE; if (lock_type == TL_READ_NO_INSERT && !thd->in_lock_tables) lock_type = TL_READ; lock.type=lock_type; } *to++= &lock; return to; }
更新已有行的內(nèi)容。
virtual int update_row ( | old_data, | ? |
? | new_data); | ? |
const byte *? | old_data ; |
byte *? | new_data ; |
這是update_row方法。
old_data將保存前一行的記錄,而new_data將保存最新的數(shù)據(jù)。
如果使用了ORDER BY子句,服務(wù)器能夠根據(jù)排序執(zhí)行更新操作。不保證連續(xù)排序。
目前,new_data不會擁有已更新的auto_increament記錄,或已更新的時間戳字段。你可以通過下述方式(例如)完成該操作:if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_UPDATE) table->timestamp_field->set_time(); if (table->next_number_field && record == table->record[0]) update_auto_increment();
從sql_select.cc, sql_acl.cc, sql_update.cc和sql_insert.cc調(diào)用。
old_data
new_data
無返回值。
{ return HA_ERR_WRONG_COMMAND; }
為表添加新行。
virtual int write_row ( | buf); | ? |
byte *? | buf ; |
這是write_row方法。
write_row()用于插入行。目前,如果出現(xiàn)大量加載,不會給出任何extra()提示。buf是數(shù)據(jù)的字節(jié)數(shù)組,大小為table->s->reclength。
可以使用字段信息從本地字節(jié)數(shù)組類型提取數(shù)據(jù)。例如:
for (Field **field=table->field ; *field ; field++) { ... }
BLOB必須特殊處理:
???
for (ptr= table->s->blob_field, end= ptr + table->s->blob_fields ; ptr != end ; ptr++)
??{
????????char *data_ptr;
????????uint32 size= ((Field_blob*)table->field[*ptr])->get_length();
??????? ((Field_blob*)table->field[*ptr])->get_ptr(&data_ptr);
????????...
??}
關(guān)于以字符串形式提取所有數(shù)據(jù)的示例,請參見ha_tina.cc。在ha_berkeley.cc中,對于ha_berkeley自己的本地存儲類型,給出了一個通過“包裝功能”完整保存它的例子。
請參見update_row()關(guān)于auto_increments和時間戳的注釋。該情形也適用于write_row()。
從item_sum.cc、item_sum.cc、sql_acl.cc、sql_insert.cc、sql_insert.cc、sql_select.cc、sql_table.cc、sql_udf.cc、以及sql_update.cc調(diào)用。
數(shù)據(jù)的buf字節(jié)數(shù)組
無返回值。
{ return HA_ERR_WRONG_COMMAND; }
這是MySQL參考手冊的翻譯版本,關(guān)于MySQL參考手冊,請訪問dev.mysql.com。 原始參考手冊為英文版,與英文版參考手冊相比,本翻譯版可能不是最新的。