diff --git a/src/MySQL.jl b/src/MySQL.jl index 6ca2b3c..29b20bc 100644 --- a/src/MySQL.jl +++ b/src/MySQL.jl @@ -21,7 +21,7 @@ mutable struct Connection <: DBInterface.Connection db::String lastexecute::Any - function Connection(host::String, user::String, passwd::Union{String, Nothing}, db::String, port::Integer, unix_socket::String; kw...) + function Connection(host::AbstractString, user::AbstractString, passwd::Union{AbstractString, Nothing}, db::AbstractString, port::Integer, unix_socket::AbstractString; kw...) mysql = API.init() API.setoption(mysql, API.MYSQL_PLUGIN_DIR, API.PLUGIN_DIR) API.setoption(mysql, API.MYSQL_SET_CHARSET_NAME, "utf8mb4") @@ -105,38 +105,38 @@ function clientflags(; end function setoptions!(mysql; - init_command::Union{String, Nothing}=nothing, + init_command::Union{AbstractString, Nothing}=nothing, connect_timeout::Union{Integer, Nothing}=nothing, reconnect::Union{Bool, Nothing}=nothing, read_timeout::Union{Integer, Nothing}=nothing, write_timeout::Union{Integer, Nothing}=nothing, data_truncation::Union{Bool, Nothing}=nothing, - charset_dir::Union{String, Nothing}=nothing, - charset_name::Union{String, Nothing}=nothing, - bind::Union{String, Nothing}=nothing, + charset_dir::Union{AbstractString, Nothing}=nothing, + charset_name::Union{AbstractString, Nothing}=nothing, + bind::Union{AbstractString, Nothing}=nothing, max_allowed_packet::Union{Integer, Nothing}=nothing, net_buffer_length::Union{Integer, Nothing}=nothing, named_pipe::Union{Bool, Nothing}=nothing, protocol::Union{API.mysql_protocol_type, Nothing}=nothing, - ssl_key::Union{String, Nothing}=nothing, - ssl_cert::Union{String, Nothing}=nothing, - ssl_ca::Union{String, Nothing}=nothing, - ssl_capath::Union{String, Nothing}=nothing, - ssl_cipher::Union{String, Nothing}=nothing, - ssl_crl::Union{String, Nothing}=nothing, - ssl_crlpath::Union{String, Nothing}=nothing, - passphrase::Union{String, Nothing}=nothing, + ssl_key::Union{AbstractString, Nothing}=nothing, + ssl_cert::Union{AbstractString, Nothing}=nothing, + ssl_ca::Union{AbstractString, Nothing}=nothing, + ssl_capath::Union{AbstractString, Nothing}=nothing, + ssl_cipher::Union{AbstractString, Nothing}=nothing, + ssl_crl::Union{AbstractString, Nothing}=nothing, + ssl_crlpath::Union{AbstractString, Nothing}=nothing, + passphrase::Union{AbstractString, Nothing}=nothing, ssl_verify_server_cert::Union{Bool, Nothing}=nothing, ssl_enforce::Union{Bool, Nothing}=nothing, - default_auth::Union{String, Nothing}=nothing, - connection_handler::Union{String, Nothing}=nothing, - plugin_dir::Union{String, Nothing}=nothing, + default_auth::Union{AbstractString, Nothing}=nothing, + connection_handler::Union{AbstractString, Nothing}=nothing, + plugin_dir::Union{AbstractString, Nothing}=nothing, secure_auth::Union{Bool, Nothing}=nothing, - server_public_key::Union{String, Nothing}=nothing, + server_public_key::Union{AbstractString, Nothing}=nothing, read_default_file::Union{Bool, Nothing}=nothing, - option_file::Union{String, Nothing}=nothing, + option_file::Union{AbstractString, Nothing}=nothing, read_default_group::Union{Bool, Nothing}=nothing, - option_group::Union{String, Nothing}=nothing, + option_group::Union{AbstractString, Nothing}=nothing, kw... ) if init_command !== nothing @@ -239,12 +239,12 @@ function setoptions!(mysql; end """ - DBInterface.connect(MySQL.Connection, host::String, user::String, passwd::String; db::String="", port::Integer=3306, unix_socket::String=API.MYSQL_DEFAULT_SOCKET, client_flag=API.CLIENT_MULTI_STATEMENTS, opts = Dict()) + DBInterface.connect(MySQL.Connection, host::AbstractString, user::AbstractString, passwd::AbstractString; db::AbstractString="", port::Integer=3306, unix_socket::AbstractString=API.MYSQL_DEFAULT_SOCKET, client_flag=API.CLIENT_MULTI_STATEMENTS, opts = Dict()) Connect to a MySQL database with provided `host`, `user`, and `passwd` positional arguments. Supported keyword arguments include: - * `db::String=""`: attach to a database by default + * `db::AbstractString=""`: attach to a database by default * `port::Integer=3306`: connect to the database on a specific port - * `unix_socket::String`: specifies the socket or named pipe that should be used + * `unix_socket::AbstractString`: specifies the socket or named pipe that should be used * `found_rows::Bool=false`: Return the number of matched rows instead of number of changed rows * `no_schema::Bool=false`: Forbids the use of database.tablename.column syntax and forces the SQL parser to generate an error. * `compress::Bool=false`: Use compression protocol @@ -258,34 +258,34 @@ Connect to a MySQL database with provided `host`, `user`, and `passwd` positiona * `read_timeout::Integer`: Specifies the timeout in seconds for reading packets from the server. * `write_timeout::Integer`: Specifies the timeout in seconds for reading packets from the server. * `data_truncation::Bool`: Enable or disable reporting data truncation errors for prepared statements - * `charset_dir::String`: character set files directory - * `charset_name::String`: Specify the default character set for the connection - * `bind::String`: Specify the network interface from which to connect to the database, like `"192.168.8.3"` + * `charset_dir::AbstractString`: character set files directory + * `charset_name::AbstractString`: Specify the default character set for the connection + * `bind::AbstractString`: Specify the network interface from which to connect to the database, like `"192.168.8.3"` * `max_allowed_packet::Integer`: The maximum packet length to send to or receive from server. The default is 16MB, the maximum 1GB. * `net_buffer_length::Integer`: The buffer size for TCP/IP and socket communication. Default is 16KB. * `named_pipe::Bool`: For Windows operating systems only: Use named pipes for client/server communication. * `protocol::MySQL.API.mysql_protocol_type`: Specify the type of client/server protocol. Possible values are: `MySQL.API.MYSQL_PROTOCOL_TCP`, `MySQL.API.MYSQL_PROTOCOL_SOCKET`, `MySQL.API.MYSQL_PROTOCOL_PIPE`, `MySQL.API.MYSQL_PROTOCOL_MEMORY`. - * `ssl_key::String`: Defines a path to a private key file to use for TLS. This option requires that you use the absolute path, not a relative path. If the key is protected with a passphrase, the passphrase needs to be specified with `passphrase` keyword argument. - * `passphrase::String`: Specify a passphrase for a passphrase-protected private key, as configured by the `ssl_key` keyword argument. - * `ssl_cert::String`: Defines a path to the X509 certificate file to use for TLS. This option requires that you use the absolute path, not a relative path. - * `ssl_ca::String`: Defines a path to a PEM file that should contain one or more X509 certificates for trusted Certificate Authorities (CAs) to use for TLS. This option requires that you use the absolute path, not a relative path. - * `ssl_capath::String`: Defines a path to a directory that contains one or more PEM files that should each contain one X509 certificate for a trusted Certificate Authority (CA) to use for TLS. This option requires that you use the absolute path, not a relative path. The directory specified by this option needs to be run through the openssl rehash command. - * `ssl_cipher::String`: Defines a list of permitted ciphers or cipher suites to use for TLS, like `"DHE-RSA-AES256-SHA"` - * `ssl_crl::String`: Defines a path to a PEM file that should contain one or more revoked X509 certificates to use for TLS. This option requires that you use the absolute path, not a relative path. - * `ssl_crlpath::String`: Defines a path to a directory that contains one or more PEM files that should each contain one revoked X509 certificate to use for TLS. This option requires that you use the absolute path, not a relative path. The directory specified by this option needs to be run through the openssl rehash command. + * `ssl_key::AbstractString`: Defines a path to a private key file to use for TLS. This option requires that you use the absolute path, not a relative path. If the key is protected with a passphrase, the passphrase needs to be specified with `passphrase` keyword argument. + * `passphrase::AbstractString`: Specify a passphrase for a passphrase-protected private key, as configured by the `ssl_key` keyword argument. + * `ssl_cert::AbstractString`: Defines a path to the X509 certificate file to use for TLS. This option requires that you use the absolute path, not a relative path. + * `ssl_ca::AbstractString`: Defines a path to a PEM file that should contain one or more X509 certificates for trusted Certificate Authorities (CAs) to use for TLS. This option requires that you use the absolute path, not a relative path. + * `ssl_capath::AbstractString`: Defines a path to a directory that contains one or more PEM files that should each contain one X509 certificate for a trusted Certificate Authority (CA) to use for TLS. This option requires that you use the absolute path, not a relative path. The directory specified by this option needs to be run through the openssl rehash command. + * `ssl_cipher::AbstractString`: Defines a list of permitted ciphers or cipher suites to use for TLS, like `"DHE-RSA-AES256-SHA"` + * `ssl_crl::AbstractString`: Defines a path to a PEM file that should contain one or more revoked X509 certificates to use for TLS. This option requires that you use the absolute path, not a relative path. + * `ssl_crlpath::AbstractString`: Defines a path to a directory that contains one or more PEM files that should each contain one revoked X509 certificate to use for TLS. This option requires that you use the absolute path, not a relative path. The directory specified by this option needs to be run through the openssl rehash command. * `ssl_verify_server_cert::Bool`: Enables (or disables) server certificate verification. * `ssl_enforce::Bool`: Whether to force TLS - * `default_auth::String`: Default authentication client-side plugin to use. - * `connection_handler::String`: Specify the name of a connection handler plugin. - * `plugin_dir::String`: Specify the location of client plugins. The plugin directory can also be specified with the MARIADB_PLUGIN_DIR environment variable. + * `default_auth::AbstractString`: Default authentication client-side plugin to use. + * `connection_handler::AbstractString`: Specify the name of a connection handler plugin. + * `plugin_dir::AbstractString`: Specify the location of client plugins. The plugin directory can also be specified with the MARIADB_PLUGIN_DIR environment variable. * `secure_auth::Bool`: Refuse to connect to the server if the server uses the mysql_old_password authentication plugin. This mode is off by default, which is a difference in behavior compared to MySQL 5.6 and later, where it is on by default. - * `server_public_key::String`: Specifies the name of the file which contains the RSA public key of the database server. The format of this file must be in PEM format. This option is used by the caching_sha2_password client authentication plugin. + * `server_public_key::AbstractString`: Specifies the name of the file which contains the RSA public key of the database server. The format of this file must be in PEM format. This option is used by the caching_sha2_password client authentication plugin. * `read_default_file::Bool`: only the default option files are read - * `option_file::String`: the argument is interpreted as a path to a custom option file, and only that option file is read. + * `option_file::AbstractString`: the argument is interpreted as a path to a custom option file, and only that option file is read. * `read_default_group::Bool`: only the default option groups are read from specified option file(s) - * `option_group::String`: it is interpreted as a custom option group, and that custom option group is read in addition to the default option groups. + * `option_group::AbstractString`: it is interpreted as a custom option group, and that custom option group is read in addition to the default option groups. """ -DBInterface.connect(::Type{Connection}, host::String, user::String, passwd::Union{String, Nothing}=nothing; db::String="", port::Integer=3306, unix_socket::String=API.MYSQL_DEFAULT_SOCKET, kw...) = +DBInterface.connect(::Type{Connection}, host::AbstractString, user::AbstractString, passwd::Union{AbstractString, Nothing}=nothing; db::AbstractString="", port::Integer=3306, unix_socket::AbstractString=API.MYSQL_DEFAULT_SOCKET, kw...) = Connection(host, user, passwd, db, port, unix_socket; kw...) """ @@ -317,10 +317,10 @@ include("prepare.jl") include("load.jl") """ - MySQL.escape(conn::MySQL.Connection, str::String) -> String + MySQL.escape(conn::MySQL.Connection, str::AbstractString) -> String Escapes a string using `mysql_real_escape_string()`, returns the escaped string. """ -escape(conn::Connection, sql::String) = API.escapestring(conn.mysql, sql) +escape(conn::Connection, sql::AbstractString) = API.escapestring(conn.mysql, sql) end # module diff --git a/src/api/capi.jl b/src/api/capi.jl index 1c00cbd..ac8cc8b 100644 --- a/src/api/capi.jl +++ b/src/api/capi.jl @@ -108,7 +108,7 @@ ER_WRONG_DB_NAME The database name was too long. """=# -function changeuser(mysql::MYSQL, user::String, password::String, db::String) +function changeuser(mysql::MYSQL, user::AbstractString, password::AbstractString, db::AbstractString) return @checksuccess mysql mysql_change_user(mysql.ptr, user, password, isempty(db) ? C_NULL : db) end @@ -137,7 +137,7 @@ name: The plugin name. type: The plugin type. """=# -function findplugin(mysql::MYSQL, name::String, type::Integer) +function findplugin(mysql::MYSQL, name::AbstractString, type::Integer) return @checknull mysql mysql_client_find_plugin(mysql.ptr, name, type) end @@ -1034,7 +1034,7 @@ value: A pointer to the option value. Return Values Zero for success, 1 if an error occurred. If the plugin has an option handler, that handler should also return zero for success and 1 if an error occurred. """=# -function pluginoption(plugin::Ptr{Cvoid}, option::String, value) +function pluginoption(plugin::Ptr{Cvoid}, option::AbstractString, value) mysql_plugin_options(plugin, option, value) end @@ -1129,7 +1129,7 @@ If no value is found in an option file for a parameter, its default value is use Return Values A MYSQL* connection handler if the connection was successful, NULL if the connection was unsuccessful. For a successful connection, the return value is the same as the value of the first parameter. """=# -function connect(mysql::MYSQL, host::String, user::String, passwd::Union{String, Nothing}, db::String, port::Integer, unix_socket::String, client_flag) +function connect(mysql::MYSQL, host::AbstractString, user::AbstractString, passwd::Union{AbstractString, Nothing}, db::AbstractString, port::Integer, unix_socket::AbstractString, client_flag) @checknull mysql mysql_real_connect(mysql.ptr, host, user, passwd === nothing ? Ptr{UInt8}(C_NULL) : passwd, db, port, unix_socket, client_flag) return mysql end @@ -1145,7 +1145,7 @@ The string pointed to by from must be length bytes long. You must allocate the t If you must change the character set of the connection, use the mysql_set_character_set() function rather than executing a SET NAMES (or SET CHARACTER SET) statement. mysql_set_character_set() works like SET NAMES but also affects the character set used by mysql_real_escape_string(), which SET NAMES does not. """=# -function escapestring(mysql::MYSQL, str::String) +function escapestring(mysql::MYSQL, str::AbstractString) len = sizeof(str) to = Base.StringVector(len * 2 + 1) tolen = mysql_real_escape_string(mysql.ptr, to, str, len) @@ -1198,7 +1198,7 @@ if (mysql_real_query(&mysql,query,(unsigned int) (end - query))) } The my_stpcpy() function used in the example is included in the libmysqlclient library and works like strcpy() but returns a pointer to the terminating null of the first parameter. """=# -function escapestringquote(mysql::MYSQL, str::String, q::Char) +function escapestringquote(mysql::MYSQL, str::AbstractString, q::Char) len = sizeof(str) to = Base.StringVector(len * 2 + 1) tolen = mysql_real_escape_string_quote(mysql.ptr, to, str, len, q) @@ -1216,7 +1216,7 @@ If you want to know whether the statement returns a result set, you can use mysq Return Values Zero for success. Nonzero if an error occurred. """=# -function query(mysql::MYSQL, sql::String) +function query(mysql::MYSQL, sql::AbstractString) return @checksuccess mysql mysql_real_query(mysql.ptr, sql, sizeof(sql)) end @@ -1321,14 +1321,14 @@ Causes the database specified by db to become the default (current) database on mysql_select_db() fails unless the connected user can be authenticated as having permission to use the database or some object within it. """=# -function selectdb(mysql::MYSQL, db::String) +function selectdb(mysql::MYSQL, db::AbstractString) return @checksuccess mysql mysql_select_db(mysql.ptr, db) end #=""" This function is used to set the default character set for the current connection. The string csname specifies a valid character set name. The connection collation becomes the default collation of the character set. This function works like the SET NAMES statement, but also sets the value of mysql->charset, and thus affects the character set used by mysql_real_escape_string() """=# -function setcharacterset(mysql::MYSQL, csname::String) +function setcharacterset(mysql::MYSQL, csname::AbstractString) return @checksuccess mysql mysql_set_character_set(mysql.ptr, csname) end @@ -1445,7 +1445,7 @@ cipher: The list of permissible ciphers for SSL encryption. Return Values This function always returns 0. If SSL setup is incorrect, a subsequent mysql_real_connect() call returns an error when you attempt to connect. """=# -function sslset(mysql::MYSQL, key::String, cert::String, ca::String, capath::String, cipher::String) +function sslset(mysql::MYSQL, key::AbstractString, cert::AbstractString, ca::AbstractString, capath::AbstractString, cipher::AbstractString) return mysql_ssl_set(mysql.ptr, key, cert, ca, capath, cipher) end diff --git a/src/api/ccalls.jl b/src/api/ccalls.jl index 0464f0b..65f02ae 100644 --- a/src/api/ccalls.jl +++ b/src/api/ccalls.jl @@ -37,7 +37,7 @@ function mysql_autocommit(mysql::Ptr{Cvoid}, mode) end #bool mysql_change_user(MYSQL *mysql, const char *user, const char *password, const char *db) -function mysql_change_user(mysql::Ptr{Cvoid}, user::String, password::String, db) +function mysql_change_user(mysql::Ptr{Cvoid}, user::AbstractString, password::AbstractString, db) return @c(:mysql_change_user, Bool, (Ptr{Cvoid}, Ptr{UInt8}, Ptr{UInt8}, Ptr{UInt8}), @@ -53,7 +53,7 @@ function mysql_character_set_name(mysql::Ptr{Cvoid}) end #struct st_mysql_client_plugin *mysql_client_find_plugin(MYSQL *mysql, const char *name, int type) -function mysql_client_find_plugin(mysql::Ptr{Cvoid}, name::String, type::Int) +function mysql_client_find_plugin(mysql::Ptr{Cvoid}, name::AbstractString, type::Int) return @c(:mysql_client_find_plugin, Ptr{Cvoid}, (Ptr{Cvoid}, Ptr{UInt8}, Cint), @@ -477,7 +477,7 @@ function mysql_row_tell(result::Ptr{Cvoid}) end #int mysql_select_db(MYSQL *mysql, const char *db) -function mysql_select_db(mysql::Ptr{Cvoid}, db::String) +function mysql_select_db(mysql::Ptr{Cvoid}, db::AbstractString) return @c(:mysql_select_db, Cint, (Ptr{Cvoid}, Ptr{UInt8}), @@ -501,7 +501,7 @@ function mysql_session_track_get_next(mysql::Ptr{Cvoid}, type, data, len) end #int mysql_set_character_set(MYSQL *mysql, const char *csname) -function mysql_set_character_set(mysql::Ptr{Cvoid}, csname::String) +function mysql_set_character_set(mysql::Ptr{Cvoid}, csname::AbstractString) return @c(:mysql_set_character_set, Cint, (Ptr{Cvoid}, Ptr{UInt8}), @@ -806,4 +806,4 @@ function mysql_stmt_store_result(stmt::Ptr{Cvoid}) Cint, (Ptr{Cvoid},), stmt) -end \ No newline at end of file +end diff --git a/src/api/papi.jl b/src/api/papi.jl index 2f49f4b..9d6ebe2 100644 --- a/src/api/papi.jl +++ b/src/api/papi.jl @@ -314,7 +314,7 @@ The parameter markers must be bound to application variables using mysql_stmt_bi Metadata changes to tables or views referred to by prepared statements are detected and cause automatic repreparation of the statement when it is next executed. For more information, see Section 8.10.3, “Caching of Prepared Statements and Stored Programs”. """ -function prepare(stmt::MYSQL_STMT, sql::String) +function prepare(stmt::MYSQL_STMT, sql::AbstractString) return @checkstmtsuccess stmt mysql_stmt_prepare(stmt.ptr, sql, sizeof(sql)) end @@ -398,7 +398,7 @@ If you want to reset/forget the sent data, you can do it with mysql_stmt_reset() The max_allowed_packet system variable controls the maximum size of parameter values that can be sent with mysql_stmt_send_long_data(). """ -function sendlongdata(stmt::MYSQL_STMT, parameter_number, data::Union{String, Vector{UInt8}}) +function sendlongdata(stmt::MYSQL_STMT, parameter_number, data::Union{AbstractString, Vector{UInt8}}) return @checkstmtsuccess stmt mysql_stmt_send_long_data(stmt.ptr, parameter_number, data isa Vector ? pointer(data) : data, data isa Vector ? length(data) : sizeof(data)) end diff --git a/src/prepare.jl b/src/prepare.jl index eaa51c7..4016712 100644 --- a/src/prepare.jl +++ b/src/prepare.jl @@ -350,13 +350,14 @@ function bind!(helper, binds, i, x::Dates.TimeType) end val(x) = x +val(x::AbstractString) = String(x) val(x::API.Bit) = API.bitvalue(x) val(x::DecFP.DecimalFloatingPoint) = string(x) len(x::String) = sizeof(x) len(x::Vector{UInt8}) = length(x) -function bind!(helper, binds, i, x::Union{Vector{UInt8}, String, API.Bit, DecFP.DecimalFloatingPoint}) +function bind!(helper, binds, i, x::Union{Vector{UInt8}, AbstractString, API.Bit, DecFP.DecimalFloatingPoint}) ptr = pointer(binds, i) y = val(x) if !helper.typeset diff --git a/test/runtests.jl b/test/runtests.jl index b4fceda..f51d01c 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -7,6 +7,10 @@ DBInterface.close!(conn) conn = DBInterface.connect(MySQL.Connection, "mysql://127.0.0.1", "root"; port=3306) DBInterface.close!(conn) +# AbstractString as a connection parameter or an option +conn = DBInterface.connect(MySQL.Connection, SubString("127.0.0.1"), SubString("root"), SubString(""); port=3306, charset_name=SubString("utf8mb4")) +DBInterface.close!(conn) + # load host/user + options from file conn = DBInterface.connect(MySQL.Connection, "", ""; option_file=joinpath(dirname(pathof(MySQL)), "../test/", "my.ini")) @test isopen(conn) @@ -313,6 +317,20 @@ res = DBInterface.execute(conn, "select x from unsigned_float") |> columntable @test res.x == Vector{Float32}([1.1, 1.2]) # end issue #173 +# execute fails when the sql is an AbstractString, but not a String (#189) +DBInterface.execute(conn, SubString("select * from Employee")) +stmt = DBInterface.prepare(conn, SubString("select * from Employee")) +DBInterface.execute(stmt) + +# AbstractString as an input parameter +DBInterface.execute(conn, "DROP TABLE if exists abstract_string") +DBInterface.execute(conn, "CREATE TABLE abstract_string(str VARCHAR(255))") +stmt = DBInterface.prepare(conn, "INSERT INTO abstract_string (str) VALUES (?)") +DBInterface.execute(stmt, [SubString("foo")]) + +# escaping AbstractString +@test MySQL.escape(conn, SubString("'); DROP TABLE Employee; --")) == "\\'); DROP TABLE Employee; --" + # 156 res = DBInterface.execute(conn, "select * from Employee") DBInterface.close!(conn)