Permalink
Browse files

initial commit

  • Loading branch information...
Jacob Vorreuter
Jacob Vorreuter committed Sep 6, 2009
1 parent 81f2f97 commit a43000ba9e2afcd8113239dd1049b88348f554c1
View
@@ -0,0 +1,26 @@
+LIBDIR=`erl -eval 'io:format("~s~n", [code:lib_dir()])' -s init stop -noshell`
+VERSION=0.1
+PKGNAME=emysql
+
+all: app
+ mkdir -p ebin
+ (cd src;$(MAKE))
+
+app:
+ sh ebin/$(PKGNAME).app.in $(VERSION)
+
+clean:
+ (cd src;$(MAKE) clean)
+ rm -rf ebin/*.app cover
+
+package: clean
+ @mkdir emysql-$(VERSION)/ && cp -rf ebin include Makefile priv README src support t $(PKGNAME)-$(VERSION)
+ @COPYFILE_DISABLE=true tar zcf $(PKGNAME)-$(VERSION).tgz $(PKGNAME)-$(VERSION)
+ @rm -rf $(PKGNAME)-$(VERSION)/
+
+install:
+ mkdir -p $(prefix)/$(LIBDIR)/$(PKGNAME)-$(VERSION)/{ebin,include}
+ for i in ebin/*.beam ebin/*.app include/*.hrl; do install $$i $(prefix)/$(LIBDIR)/$(PKGNAME)-$(VERSION)/$$i ; done
+
+test: all
+ prove t/*.t
View
@@ -0,0 +1,16 @@
+#!/bin/bash
+
+VERSION=${1}
+MODULES=`ls -1 src/*.erl | awk -F[/.] '{ print "\t\t" $2 }' | sed '$q;s/$/,/g'`
+
+cat > ebin/emysql.app << EOF
+{application, emysql, [
+ {description, "emysql"},
+ {vsn, "${VERSION}"},
+ {modules, [
+${MODULES}
+ ]},
+ {mod, {mysql, []}},
+ {registered, []},
+ {applications, [kernel, stdlib, crypto]}
+]}.
View
@@ -0,0 +1,104 @@
+%% Copyright (c) 2009
+%% Bill Warnecke <bill@rupture.com>
+%% Jacob Vorreuter <jacob.vorreuter@gmail.com>
+%%
+%% Permission is hereby granted, free of charge, to any person
+%% obtaining a copy of this software and associated documentation
+%% files (the "Software"), to deal in the Software without
+%% restriction, including without limitation the rights to use,
+%% copy, modify, merge, publish, distribute, sublicense, and/or sell
+%% copies of the Software, and to permit persons to whom the
+%% Software is furnished to do so, subject to the following
+%% conditions:
+%%
+%% The above copyright notice and this permission notice shall be
+%% included in all copies or substantial portions of the Software.
+%%
+%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+%% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+%% OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+%% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+%% HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+%% WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+%% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+%% OTHER DEALINGS IN THE SOFTWARE.
+-record(state, {pools=[], statements=[]}).
+-record(pool, {pool_id, size, user, password, host, port, database, encoding, connections=[]}).
+-record(connection, {pool_id, state=available, socket, version, thread_id, caps, language}).
+-record(greeting, {protocol_version, server_version, thread_id, salt1, salt2, caps, language, status, seq_num}).
+-record(field, {seq_num, catalog, db, table, org_table, name, org_name, type, default, charset_nr, length, flags, decimals}).
+-record(packet, {size, seq_num, data}).
+-record(mysql_ok_packet, {seq_num, affected_rows, insert_id, status, warning_count, msg}).
+-record(mysql_error_packet, {seq_num, code, state, msg}).
+-record(mysql_result_packet, {seq_num, field_list, rows, extra}).
+
+-define(TIMEOUT, 8000).
+-define(MAXPACKETBYTES, 1000000).
+-define(LONG_PASSWORD, 1).
+-define(LONG_FLAG, 4).
+-define(PROTOCOL_41, 512).
+-define(CLIENT_MULTI_STATEMENTS, 65536).
+-define(CLIENT_MULTI_RESULTS, 131072).
+-define(TRANSACTIONS, 8192).
+-define(SECURE_CONNECTION, 32768).
+-define(CONNECT_WITH_DB, 8).
+
+%% MYSQL COMMANDS
+-define(COM_SLEEP, 16#00).
+-define(COM_QUIT, 16#01).
+-define(COM_INIT_DB, 16#02).
+-define(COM_QUERY, 16#03).
+-define(COM_FIELD_LIST, 16#04).
+-define(COM_CREATE_DB, 16#05).
+-define(COM_DROP_DB, 16#06).
+-define(COM_REFRESH, 16#07).
+-define(COM_SHUTDOWN, 16#08).
+-define(COM_STATISTICS, 16#09).
+-define(COM_PROCESS_INFO, 16#0a).
+-define(COM_CONNECT, 16#0b).
+-define(COM_PROCESS_KILL, 16#0c).
+-define(COM_DEBUG, 16#0d).
+-define(COM_PING, 16#0e).
+-define(COM_TIME, 16#0f).
+-define(COM_DELAYED_INSERT, 16#10).
+-define(COM_CHANGE_USER, 16#11).
+-define(COM_BINLOG_DUMP, 16#12).
+-define(COM_TABLE_DUMP, 16#13).
+-define(COM_CONNECT_OUT, 16#14).
+-define(COM_REGISTER_SLAVE, 16#15).
+-define(COM_STMT_PREPARE, 16#16).
+-define(COM_STMT_EXECUTE, 16#17).
+-define(COM_STMT_SEND_LONG_DATA, 16#18).
+-define(COM_STMT_CLOSE, 16#19).
+-define(COM_STMT_RESET, 16#1a).
+-define(COM_SET_OPTION, 16#1b).
+-define(COM_STMT_FETCH, 16#1c).
+
+%% MYSQL TYPES
+-define(FIELD_TYPE_DECIMAL, 16#00).
+-define(FIELD_TYPE_TINY, 16#01).
+-define(FIELD_TYPE_SHORT, 16#02).
+-define(FIELD_TYPE_LONG, 16#03).
+-define(FIELD_TYPE_FLOAT, 16#04).
+-define(FIELD_TYPE_DOUBLE, 16#05).
+-define(FIELD_TYPE_NULL, 16#06).
+-define(FIELD_TYPE_TIMESTAMP, 16#07).
+-define(FIELD_TYPE_LONGLONG, 16#08).
+-define(FIELD_TYPE_INT24, 16#09).
+-define(FIELD_TYPE_DATE, 16#0a).
+-define(FIELD_TYPE_TIME, 16#0b).
+-define(FIELD_TYPE_DATETIME, 16#0c).
+-define(FIELD_TYPE_YEAR, 16#0d).
+-define(FIELD_TYPE_NEWDATE, 16#0e).
+-define(FIELD_TYPE_VARCHAR, 16#0f).
+-define(FIELD_TYPE_BIT, 16#10).
+-define(FIELD_TYPE_NEWDECIMAL, 16#f6).
+-define(FIELD_TYPE_ENUM, 16#f7).
+-define(FIELD_TYPE_SET, 16#f8).
+-define(FIELD_TYPE_TINY_BLOB, 16#f9).
+-define(FIELD_TYPE_MEDIUM_BLOB, 16#fa).
+-define(FIELD_TYPE_LONG_BLOB, 16#fb).
+-define(FIELD_TYPE_BLOB, 16#fc).
+-define(FIELD_TYPE_VAR_STRING, 16#fd).
+-define(FIELD_TYPE_STRING, 16#fe).
+-define(FIELD_TYPE_GEOMETRY, 16#ff).
View
@@ -0,0 +1,14 @@
+[{emysql, [
+ {default_timeout, 8000},
+ {pools, [
+ {test1, [
+ {size, 1},
+ {user, "test"},
+ {password, "test"},
+ {host, "localhost"},
+ {port, 3306},
+ {database, "testdatabase"},
+ {encoding, 'utf8'}
+ ]}
+ ]}
+]}].
View
@@ -0,0 +1,9 @@
+include ../support/include.mk
+
+all: $(EBIN_FILES)
+
+debug:
+ $(MAKE) DEBUG=-DDEBUG
+
+clean:
+ rm -rf $(EBIN_FILES)
View
@@ -0,0 +1,73 @@
+%% Copyright (c) 2009
+%% Bill Warnecke <bill@rupture.com>
+%% Jacob Vorreuter <jacob.vorreuter@gmail.com>
+%%
+%% Permission is hereby granted, free of charge, to any person
+%% obtaining a copy of this software and associated documentation
+%% files (the "Software"), to deal in the Software without
+%% restriction, including without limitation the rights to use,
+%% copy, modify, merge, publish, distribute, sublicense, and/or sell
+%% copies of the Software, and to permit persons to whom the
+%% Software is furnished to do so, subject to the following
+%% conditions:
+%%
+%% The above copyright notice and this permission notice shall be
+%% included in all copies or substantial portions of the Software.
+%%
+%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+%% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+%% OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+%% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+%% HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+%% WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+%% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+%% OTHER DEALINGS IN THE SOFTWARE.
+-module(mysql).
+-behaviour(application).
+
+-export([start/2, stop/1, init/1, modules/0]).
+-export([execute/2, execute/3]).
+
+start(_, _) ->
+ supervisor:start_link({local, ?MODULE}, ?MODULE, []).
+
+stop(_) -> ok.
+
+init(_) ->
+ {ok, {{one_for_one, 10, 10}, [
+ {mysql_conn_mgr, {mysql_conn_mgr, start_link, []}, permanent, 5000, worker, [mysql_conn_mgr]}
+ ]}}.
+
+modules() ->
+ {ok, Modules} = application_controller:get_key(emysql, modules),
+ Modules.
+
+execute(PoolId, Query) ->
+ execute(PoolId, Query, mysql_tcp:timeout()).
+
+execute(PoolId, Query, Timeout) ->
+ Connection = mysql_conn_mgr:lock_connection(PoolId),
+ monitor_work(Connection, Timeout, {mysql_conn, execute, [Connection, Query]}).
+
+monitor_work(Connection, Timeout, {M,F,A}) ->
+ Parent = self(),
+ Pid = spawn(
+ fun() ->
+ Parent ! {self(), apply(M, F, A)}
+ end),
+ Mref = erlang:monitor(process, Pid),
+ receive
+ {'DOWN', Mref, process, Pid, Reason} ->
+ mysql_conn_mgr:reset_connection(Connection),
+ exit(Reason);
+ {Pid, Result} ->
+ erlang:demonitor(Mref),
+ mysql_conn_mgr:unlock_connection(Connection),
+ receive {'DOWN', Mref, process, Pid, normal} -> ok after 0 -> ok end,
+ Result
+ after Timeout ->
+ erlang:demonitor(Mref),
+ mysql_conn_mgr:reset_connection(Connection),
+ exit(mysql_timeout)
+ end.
+
View
@@ -0,0 +1,97 @@
+%% Copyright (c) 2009
+%% Bill Warnecke <bill@rupture.com>
+%% Jacob Vorreuter <jacob.vorreuter@gmail.com>
+%%
+%% Permission is hereby granted, free of charge, to any person
+%% obtaining a copy of this software and associated documentation
+%% files (the "Software"), to deal in the Software without
+%% restriction, including without limitation the rights to use,
+%% copy, modify, merge, publish, distribute, sublicense, and/or sell
+%% copies of the Software, and to permit persons to whom the
+%% Software is furnished to do so, subject to the following
+%% conditions:
+%%
+%% The above copyright notice and this permission notice shall be
+%% included in all copies or substantial portions of the Software.
+%%
+%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+%% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+%% OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+%% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+%% HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+%% WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+%% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+%% OTHER DEALINGS IN THE SOFTWARE.
+-module(mysql_auth).
+-export([do_handshake/3]).
+-compile(export_all).
+
+-include("emysql.hrl").
+
+do_handshake(Sock, User, Password) ->
+ Greeting = recv_greeting(Sock),
+ case auth(Sock, Greeting#greeting.seq_num+1, User, Password, Greeting#greeting.salt1, Greeting#greeting.salt2) of
+ OK when is_record(OK, mysql_ok_packet) ->
+ ok;
+ Err when is_record(Err, mysql_error_packet) ->
+ exit({failed_to_authenticate, Err})
+ end,
+ Greeting.
+
+recv_greeting(Sock) ->
+ GreetingPacket = mysql_tcp:recv_packet(Sock),
+ case GreetingPacket#packet.data of
+ <<ProtocolVersion:8/integer, Rest1/binary>> ->
+ {ServerVersion, Rest2} = mysql_util:asciz(Rest1),
+ <<TreadID:32/little, Rest3/binary>> = Rest2,
+ {Salt, Rest4} = mysql_util:asciz(Rest3),
+ <<ServerCaps:16/little, Rest5/binary>> = Rest4,
+ <<ServerLanguage:8/little, ServerStatus:16/little, _:13/binary-unit:8, Rest6/binary>> = Rest5,
+ {Salt2, <<>>} = mysql_util:asciz(Rest6),
+ #greeting{
+ protocol_version = ProtocolVersion,
+ server_version = parse_server_version(ServerVersion),
+ thread_id = TreadID,
+ salt1 = Salt,
+ salt2 = Salt2,
+ caps = ServerCaps,
+ language = ServerLanguage,
+ status = ServerStatus,
+ seq_num = GreetingPacket#packet.seq_num
+ };
+ _ ->
+ exit(poorly_formatted_handshake_packet_protocol_version)
+ end.
+
+parse_server_version(Version) ->
+ [A,B,C] = string:tokens(Version, "."),
+ {list_to_integer(A), list_to_integer(B), list_to_integer(C)}.
+
+auth(Sock, SeqNum, User, Password, Salt1, Salt2) ->
+ ScrambleBuff = if
+ is_list(Password) orelse is_binary(Password) ->
+ password(Password, Salt1 ++ Salt2);
+ true ->
+ <<>>
+ end,
+ DBCaps = 0,
+ DatabaseB = <<>>,
+ Caps = ?LONG_PASSWORD bor ?LONG_FLAG bor ?TRANSACTIONS bor
+ ?CLIENT_MULTI_STATEMENTS bor ?CLIENT_MULTI_RESULTS bor
+ ?PROTOCOL_41 bor ?SECURE_CONNECTION bor DBCaps,
+ Maxsize = mysql_tcp:packet_size(),
+ UserB = list_to_binary(User),
+ PasswordL = size(ScrambleBuff),
+ Packet = <<Caps:32/little, Maxsize:32/little, 8:8, 0:23/integer-unit:8, UserB/binary, 0:8, PasswordL:8, ScrambleBuff/binary, DatabaseB/binary>>,
+ mysql_tcp:send_and_recv_packet(Sock, Packet, SeqNum).
+
+password(Password, Salt) ->
+ Stage1 = crypto:sha(Password),
+ Stage2 = crypto:sha(Stage1),
+ Res = crypto:sha_final(
+ crypto:sha_update(
+ crypto:sha_update(crypto:sha_init(), Salt),
+ Stage2
+ )
+ ),
+ mysql_util:bxor_binary(Res, Stage1).
View
@@ -0,0 +1,42 @@
+%% Copyright (c) 2009
+%% Bill Warnecke <bill@rupture.com>
+%% Jacob Vorreuter <jacob.vorreuter@gmail.com>
+%%
+%% Permission is hereby granted, free of charge, to any person
+%% obtaining a copy of this software and associated documentation
+%% files (the "Software"), to deal in the Software without
+%% restriction, including without limitation the rights to use,
+%% copy, modify, merge, publish, distribute, sublicense, and/or sell
+%% copies of the Software, and to permit persons to whom the
+%% Software is furnished to do so, subject to the following
+%% conditions:
+%%
+%% The above copyright notice and this permission notice shall be
+%% included in all copies or substantial portions of the Software.
+%%
+%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+%% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+%% OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+%% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+%% HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+%% WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+%% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+%% OTHER DEALINGS IN THE SOFTWARE.
+-module(mysql_conn).
+-export([set_database/2, set_encoding/2]).
+-export([execute/2]).
+
+-include("emysql.hrl").
+
+set_database(_, undefined) -> ok;
+set_database(Connection, Database) ->
+ Packet = <<?COM_QUERY, "use ", (iolist_to_binary(Database))/binary>>,
+ mysql_tcp:send_and_recv_packet(Connection#connection.socket, Packet, 0).
+
+set_encoding(Connection, Encoding) ->
+ Packet = <<?COM_QUERY, "set names '", (erlang:atom_to_binary(Encoding, utf8))/binary, "'">>,
+ mysql_tcp:send_and_recv_packet(Connection#connection.socket, Packet, 0).
+
+execute(Connection, Query) ->
+ Packet = <<?COM_QUERY, (iolist_to_binary(Query))/binary>>,
+ mysql_tcp:send_and_recv_packet(Connection#connection.socket, Packet, 0).
Oops, something went wrong.

0 comments on commit a43000b

Please sign in to comment.