diff --git a/examples/database/mysql.v b/examples/database/mysql.v index 88e099b660..97e0888583 100644 --- a/examples/database/mysql.v +++ b/examples/database/mysql.v @@ -13,6 +13,5 @@ fn main() { for row in res.rows() { println(row.vals.join(', ')) } - res.free() conn.close() } diff --git a/vlib/mysql/_cdefs.c.v b/vlib/mysql/_cdefs.c.v index 5367c3cc49..c3baa13458 100644 --- a/vlib/mysql/_cdefs.c.v +++ b/vlib/mysql/_cdefs.c.v @@ -17,8 +17,8 @@ struct C.MYSQL_FIELD { db byteptr // Database for table catalog byteptr // Catalog for table def byteptr // Default value (set by mysql_list_fields) - length int // Width of column (create length) - max_length int // Max width for selected set + length int // Width of column (create length) + max_length int // Max width for selected set name_length u32 org_name_length u32 table_length u32 diff --git a/vlib/mysql/_cdefs_nix.c.v b/vlib/mysql/_cdefs_nix.c.v index 40d8963748..445aaa4dcc 100644 --- a/vlib/mysql/_cdefs_nix.c.v +++ b/vlib/mysql/_cdefs_nix.c.v @@ -2,4 +2,3 @@ module mysql #pkgconfig mysqlclient #include # Please install the mysqlclient development headers - diff --git a/vlib/mysql/mysql.v b/vlib/mysql/mysql.v index 04d6c2d459..3b29d8d408 100644 --- a/vlib/mysql/mysql.v +++ b/vlib/mysql/mysql.v @@ -1,22 +1,21 @@ module mysql - // Values for the capabilities flag bitmask used by the MySQL protocol. // See more on https://dev.mysql.com/doc/dev/mysql-server/latest/group__group__cs__capabilities__flags.html#details pub enum ConnectionFlag { // CAN_HANDLE_EXPIRED_PASSWORDS = C.CAN_HANDLE_EXPIRED_PASSWORDS - client_compress = C.CLIENT_COMPRESS - client_found_rows = C.CLIENT_FOUND_ROWS - client_ignore_sigpipe = C.CLIENT_IGNORE_SIGPIPE - client_ignore_space = C.CLIENT_IGNORE_SPACE - client_interactive = C.CLIENT_INTERACTIVE - client_local_files = C.CLIENT_LOCAL_FILES - client_multi_results = C.CLIENT_MULTI_RESULTS + client_compress = C.CLIENT_COMPRESS + client_found_rows = C.CLIENT_FOUND_ROWS + client_ignore_sigpipe = C.CLIENT_IGNORE_SIGPIPE + client_ignore_space = C.CLIENT_IGNORE_SPACE + client_interactive = C.CLIENT_INTERACTIVE + client_local_files = C.CLIENT_LOCAL_FILES + client_multi_results = C.CLIENT_MULTI_RESULTS client_multi_statements = C.CLIENT_MULTI_STATEMENTS - client_no_schema = C.CLIENT_NO_SCHEMA - client_odbc = C.CLIENT_ODBC + client_no_schema = C.CLIENT_NO_SCHEMA + client_odbc = C.CLIENT_ODBC // client_optional_resultset_metadata = C.CLIENT_OPTIONAL_RESULTSET_METADATA - client_ssl = C.CLIENT_SSL + client_ssl = C.CLIENT_SSL client_remember_options = C.CLIENT_REMEMBER_OPTIONS } @@ -26,14 +25,14 @@ mut: conn &C.MYSQL = C.mysql_init(0) pub mut: host string = '127.0.0.1' - port u32 = 3306 + port u32 = 3306 username string password string dbname string flag ConnectionFlag } -// connect connects to a MySQL server. +// connect - create a new connection to the MySQL server. pub fn (mut conn Connection) connect() ?bool { instance := C.mysql_init(conn.conn) conn.conn = C.mysql_real_connect(conn.conn, conn.host.str, conn.username.str, conn.password.str, @@ -44,9 +43,10 @@ pub fn (mut conn Connection) connect() ?bool { return true } -// query executes an SQL query. -// `query()` cannot be used for statements that contain binary data; you must use `real_query()` instead. -pub fn (conn Connection) query(q string) ?Result { +// query - make an SQL query and receive the results. +// `query()` cannot be used for statements that contain binary data; +// Use `real_query()` instead. +pub fn (mut conn Connection) query(q string) ?Result { if C.mysql_query(conn.conn, q.str) != 0 { return error_with_code(get_error_msg(conn.conn), get_errno(conn.conn)) } @@ -54,11 +54,12 @@ pub fn (conn Connection) query(q string) ?Result { return Result{res} } -// real_query executes an SQL query. Same as `query ()`, -// But `real_query ()` can be used for statements containing binary data. -// (Binary data may contain the \0 character, which `query()` interprets as the end of the statement string). -// In addition, `real_query()` is faster than `query()`. -pub fn (conn Connection) real_query(q string) ?Result { +// real_query - make an SQL query and receive the results. +// `real_query()` can be used for statements containing binary data. +// (Binary data may contain the `\0` character, which `query()` +// interprets as the end of the statement string). In addition, +// `real_query()` is faster than `query()`. +pub fn (mut conn Connection) real_query(q string) ?Result { if C.mysql_real_query(conn.conn, q.str, q.len) != 0 { return error_with_code(get_error_msg(conn.conn), get_errno(conn.conn)) } @@ -66,18 +67,18 @@ pub fn (conn Connection) real_query(q string) ?Result { return Result{res} } -// select_db selects the default database for database queries. -pub fn (conn Connection) select_db(dbname string) ?bool { +// select_db - change the default database for database queries. +pub fn (mut conn Connection) select_db(dbname string) ?bool { if C.mysql_select_db(conn.conn, dbname.str) != 0 { return error_with_code(get_error_msg(conn.conn), get_errno(conn.conn)) } return true } -// change_user changes the user of the specified database connection. -// if desired, the empty string value can be passed to the `dbname` parameter -// resulting in only changing the user and not selecting a database. -pub fn (conn Connection) change_user(username string, password string, dbname string) ?bool { +// change_user - change the mysql user for the connection. +// Passing an empty string for the `dbname` parameter, resultsg in only changing +// the user and not changing the default database for the connection. +pub fn (mut conn Connection) change_user(username string, password string, dbname string) ?bool { mut ret := true if dbname != '' { ret = C.mysql_change_user(conn.conn, username.str, password.str, dbname.str) @@ -90,20 +91,24 @@ pub fn (conn Connection) change_user(username string, password string, dbname st return ret } -// affected_rows returns the number of rows changed/deleted/inserted -// by the last UPDATE, DELETE, or INSERT query. -pub fn (conn Connection) affected_rows() u64 { +// affected_rows - return the number of rows changed/deleted/inserted +// by the last `UPDATE`, `DELETE`, or `INSERT` query. +pub fn (conn &Connection) affected_rows() u64 { return C.mysql_affected_rows(conn.conn) } -// autocommit turns on or off auto-committing database modifications. -pub fn (conn Connection) autocommit(mode bool) { +// autocommit - turns on/off the auto-committing mode for the connection. +// When it is on, then each query is commited right away. +pub fn (mut conn Connection) autocommit(mode bool) { C.mysql_autocommit(conn.conn, mode) } -// tables returns list of tables that match the `wildcard` parameter. -// If empty string is passed, will return all tables. -pub fn (conn Connection) tables(wildcard string) ?[]string { +// tables - returns a list of the names of the tables in the current database, +// that match the simple regular expression specified by the `wildcard` parameter. +// The `wildcard` parameter may contain the wildcard characters `%` or `_`. +// If an empty string is passed, it will return all tables. +// Calling `tables()` is similar to executing query `SHOW TABLES [LIKE wildcard]`. +pub fn (conn &Connection) tables(wildcard string) ?[]string { cres := C.mysql_list_tables(conn.conn, wildcard.str) if isnil(cres) { return error_with_code(get_error_msg(conn.conn), get_errno(conn.conn)) @@ -113,27 +118,28 @@ pub fn (conn Connection) tables(wildcard string) ?[]string { for row in res.rows() { tables << row.vals[0] } - res.free() return tables } -// escape_string creates a legal SQL string for use in an SQL statement. -pub fn (conn Connection) escape_string(s string) string { +// escape_string - creates a legal SQL string for use in an SQL statement. +// The `s` argument is encoded to produce an escaped SQL string, +// taking into account the current character set of the connection. +pub fn (conn &Connection) escape_string(s string) string { to := malloc(2 * s.len + 1) C.mysql_real_escape_string_quote(conn.conn, to, s.str, s.len, `\'`) - return unsafe {to.vstring()} + return unsafe { to.vstring() } } -// set_option is used to set extra connect options and affect behavior for a connection. -// This function may be called multiple times to set several options. -// To retrieve option values, use `get_option()`. -pub fn (conn Connection) set_option(option_type int, val voidptr) { +// set_option - sets extra connect options that affect the behavior of +// a connection. This function may be called multiple times to set several +// options. To retrieve the current values for an option, use `get_option()`. +pub fn (mut conn Connection) set_option(option_type int, val voidptr) { C.mysql_options(conn.conn, option_type, val) } -// get_option returns the current value of an option settable `set_option`. -// The value should be treated as read only. -pub fn (conn Connection) get_option(option_type int) ?voidptr { +// get_option - return the value of an option, settable by `set_option`. +// https://dev.mysql.com/doc/c-api/5.7/en/mysql-get-option.html +pub fn (conn &Connection) get_option(option_type int) ?voidptr { ret := voidptr(0) if C.mysql_get_option(conn.conn, option_type, &ret) != 0 { return error_with_code(get_error_msg(conn.conn), get_errno(conn.conn)) @@ -141,79 +147,86 @@ pub fn (conn Connection) get_option(option_type int) ?voidptr { return ret } -// refresh flushes tables or caches, or resets replication server information. -// The connected user must have the `RELOAD` privilege. -pub fn (conn Connection) refresh(options u32) ?bool { +// refresh - flush the tables or caches, or resets replication server +// information. The connected user must have the `RELOAD` privilege. +pub fn (mut conn Connection) refresh(options u32) ?bool { if C.mysql_refresh(conn.conn, options) != 0 { return error_with_code(get_error_msg(conn.conn), get_errno(conn.conn)) } return true } -// reset resets the connection to clear the session state. -pub fn (conn Connection) reset() ?bool { +// reset - resets the connection, and clear the session state. +pub fn (mut conn Connection) reset() ?bool { if C.mysql_reset_connection(conn.conn) != 0 { return error_with_code(get_error_msg(conn.conn), get_errno(conn.conn)) } return true } -// ping pings a server connection, or tries to reconnect if the connection has gone down. -pub fn (conn Connection) ping() ?bool { +// ping - pings a server connection, or tries to reconnect if the connection +// has gone down. +pub fn (mut conn Connection) ping() ?bool { if C.mysql_ping(conn.conn) != 0 { return error_with_code(get_error_msg(conn.conn), get_errno(conn.conn)) } return true } -// close closes a previously opened database connection. -pub fn (conn &Connection) close() { +// close - closes the connection. +pub fn (mut conn Connection) close() { C.mysql_close(conn.conn) } -// -------------------------- MYSQL INFO & VERSION -------------------------- -// info returns information about the most recently executed query. -pub fn (conn Connection) info() string { +// info - returns information about the most recently executed query. +// See more on https://dev.mysql.com/doc/c-api/8.0/en/mysql-info.html +pub fn (conn &Connection) info() string { return resolve_nil_str(C.mysql_info(conn.conn)) } -// get_host_info returns a string describing the connection. -pub fn (conn Connection) get_host_info() string { - return unsafe {C.mysql_get_host_info(conn.conn).vstring()} +// get_host_info - returns a string describing the type of connection in use, +// including the server host name. +pub fn (conn &Connection) get_host_info() string { + return unsafe { C.mysql_get_host_info(conn.conn).vstring() } } -// get_server_info returns the server version number as a string. -pub fn (conn Connection) get_server_info() string { - return unsafe {C.mysql_get_server_info(conn.conn).vstring()} +// get_server_info - returns a string representing the MySQL server version. +// For example, `8.0.24`. +pub fn (conn &Connection) get_server_info() string { + return unsafe { C.mysql_get_server_info(conn.conn).vstring() } } -// get_server_version returns the server version number as an integer. -pub fn (conn Connection) get_server_version() u64 { +// get_server_version - returns an integer, representing the MySQL server +// version. The value has the format `XYYZZ` where `X` is the major version, +// `YY` is the release level (or minor version), and `ZZ` is the sub-version +// within the release level. For example, `8.0.24` is returned as `80024`. +pub fn (conn &Connection) get_server_version() u64 { return C.mysql_get_server_version(conn.conn) } -// --------------------------------- CLIENT --------------------------------- -// get_client_info returns client version information as a string. -pub fn get_client_info() string { - return unsafe {C.mysql_get_client_info().vstring()} -} - -// get_client_version returns client version information as an integer. -pub fn get_client_version() u64 { - return C.mysql_get_client_version() -} - -// ------------------------------- MYSQL DEBUG ------------------------------ -// dump_debug_info causes the server to write debug information to the log -pub fn (conn Connection) dump_debug_info() ?bool { +// dump_debug_info - instructs the server to write debugging information +// to the error log. The connected user must have the `SUPER` privilege. +pub fn (mut conn Connection) dump_debug_info() ?bool { if C.mysql_dump_debug_info(conn.conn) != 0 { return error_with_code(get_error_msg(conn.conn), get_errno(conn.conn)) } return true } -// debug does a `DBUG_PUSH` with the given string. -// See https://dev.mysql.com/doc/refman/5.7/en/mysql-debug.html +// get_client_info - returns client version information as a string. +pub fn get_client_info() string { + return unsafe { C.mysql_get_client_info().vstring() } +} + +// get_client_version - returns the client version information as an integer. +pub fn get_client_version() u64 { + return C.mysql_get_client_version() +} + +// debug - does a `DBUG_PUSH` with the given string. +// `debug()` uses the Fred Fish debug library. +// To use this function, you must compile the client library to support debugging. +// See https://dev.mysql.com/doc/c-api/8.0/en/mysql-debug.html pub fn debug(debug string) { C.mysql_debug(debug.str) } diff --git a/vlib/mysql/result.v b/vlib/mysql/result.v index 10cbe11b95..3fc6bf66ac 100644 --- a/vlib/mysql/result.v +++ b/vlib/mysql/result.v @@ -9,86 +9,6 @@ pub mut: vals []string } -// fetch_row fetches the next row from the result set. -pub fn (r Result) fetch_row() &byteptr { - return C.mysql_fetch_row(r.result) -} - -// n_rows returns the number of rows in the result set. -pub fn (r Result) n_rows() u64 { - return C.mysql_num_rows(r.result) -} - -// n_fields returns the number of columns in a result set. -pub fn (r Result) n_fields() int { - return C.mysql_num_fields(r.result) -} - -// rows returns rows with `array` of columns. -pub fn (r Result) rows() []Row { - mut rows := []Row{} - nr_cols := r.n_fields() - for rr := r.fetch_row(); rr; rr = r.fetch_row() { - mut row := Row{} - for i in 0 .. nr_cols { - if unsafe {rr[i] == 0} { - row.vals << '' - } else { - row.vals << mystring(unsafe {byteptr(rr[i])}) - } - } - rows << row - } - return rows -} - -// Returns rows with `map` of columns instead `array` of columns. -pub fn (r Result) maps() []map[string]string { - mut array_map := []map[string]string{} - rows := r.rows() - fields := r.fields() - for i in 0 .. rows.len { - mut map_val := map[string]string{} - for j in 0 .. fields.len { - map_val[fields[j].name] = rows[i].vals[j] - } - array_map << map_val - } - return array_map -} - -// fields returns an array of all field structures. -pub fn (r Result) fields() []Field { - mut fields := []Field{} - nr_cols := r.n_fields() - orig_fields := C.mysql_fetch_fields(r.result) - for i in 0 .. nr_cols { - unsafe {fields << Field{ - name: mystring(orig_fields[i].name) - org_name: mystring(orig_fields[i].org_name) - table: mystring(orig_fields[i].table) - org_table: mystring(orig_fields[i].org_table) - db: mystring(orig_fields[i].db) - catalog: mystring(orig_fields[i].catalog) - def: resolve_nil_str(orig_fields[i].def) - length: orig_fields.length - max_length: orig_fields.max_length - name_length: orig_fields.name_length - org_name_length: orig_fields.org_name_length - table_length: orig_fields.table_length - org_table_length: orig_fields.org_table_length - db_length: orig_fields.db_length - catalog_length: orig_fields.catalog_length - def_length: orig_fields.def_length - flags: orig_fields.flags - decimals: orig_fields.decimals - charsetnr: orig_fields.charsetnr - type_: FieldType(orig_fields.@type) - }} - } - return fields -} - pub struct Field { name string org_name string @@ -112,6 +32,93 @@ pub struct Field { type_ FieldType } +// fetch_row - fetches the next row from a result. +pub fn (r Result) fetch_row() &byteptr { + return C.mysql_fetch_row(r.result) +} + +// n_rows - returns the number of rows from a result. +pub fn (r Result) n_rows() u64 { + return C.mysql_num_rows(r.result) +} + +// n_fields - returns the number of columns from a result. +pub fn (r Result) n_fields() int { + return C.mysql_num_fields(r.result) +} + +// rows - returns array of rows, each containing an array of values, +// one for each column. +pub fn (r Result) rows() []Row { + mut rows := []Row{} + nr_cols := r.n_fields() + for rr := r.fetch_row(); rr; rr = r.fetch_row() { + mut row := Row{} + for i in 0 .. nr_cols { + if unsafe { rr[i] == 0 } { + row.vals << '' + } else { + row.vals << mystring(unsafe { byteptr(rr[i]) }) + } + } + rows << row + } + return rows +} + +// maps - returns an array of maps, each containing a set of +// field name: field value pairs. +pub fn (r Result) maps() []map[string]string { + mut array_map := []map[string]string{} + rows := r.rows() + fields := r.fields() + for i in 0 .. rows.len { + mut map_val := map[string]string{} + for j in 0 .. fields.len { + map_val[fields[j].name] = rows[i].vals[j] + } + array_map << map_val + } + return array_map +} + +// fields - returns an array of fields/columns. +// The definitions apply primarily for columns of results, +// such as those produced by `SELECT` statements. +pub fn (r Result) fields() []Field { + mut fields := []Field{} + nr_cols := r.n_fields() + orig_fields := C.mysql_fetch_fields(r.result) + for i in 0 .. nr_cols { + unsafe { + fields << Field{ + name: mystring(orig_fields[i].name) + org_name: mystring(orig_fields[i].org_name) + table: mystring(orig_fields[i].table) + org_table: mystring(orig_fields[i].org_table) + db: mystring(orig_fields[i].db) + catalog: mystring(orig_fields[i].catalog) + def: resolve_nil_str(orig_fields[i].def) + length: orig_fields.length + max_length: orig_fields.max_length + name_length: orig_fields.name_length + org_name_length: orig_fields.org_name_length + table_length: orig_fields.table_length + org_table_length: orig_fields.org_table_length + db_length: orig_fields.db_length + catalog_length: orig_fields.catalog_length + def_length: orig_fields.def_length + flags: orig_fields.flags + decimals: orig_fields.decimals + charsetnr: orig_fields.charsetnr + type_: FieldType(orig_fields.@type) + } + } + } + return fields +} + +// str - serializes the field pub fn (f Field) str() string { return ' { @@ -139,7 +146,8 @@ pub fn (f Field) str() string { ' } -// free frees memory used by a result set -pub fn (r Result) free() { +// free - frees the memory used by a result +[unsafe] +pub fn (r &Result) free() { C.mysql_free_result(r.result) } diff --git a/vlib/mysql/utils.v b/vlib/mysql/utils.v index 5b0a4dd1b1..30fa66cca6 100644 --- a/vlib/mysql/utils.v +++ b/vlib/mysql/utils.v @@ -1,21 +1,21 @@ module mysql -// get_error_msg returns error message from MySQL instance. +// get_error_msg - returns error message from MySQL instance. fn get_error_msg(conn &C.MYSQL) string { - return unsafe {C.mysql_error(conn).vstring()} + return unsafe { C.mysql_error(conn).vstring() } } -// get_errno returns error number from MySQL instance. +// get_errno - returns error number from MySQL instance. fn get_errno(conn &C.MYSQL) int { return C.mysql_errno(conn) } -// resolve_nil_str returns empty string if passed value is a nil pointer. +// resolve_nil_str - returns an empty string if passed value is a nil pointer. fn resolve_nil_str(ptr byteptr) string { if isnil(ptr) { return '' } - return unsafe {ptr.vstring()} + return unsafe { ptr.vstring() } } [inline]