From 2dd67031bae99fd150600ee90ff7ede1d9d12fb1 Mon Sep 17 00:00:00 2001
From: "H. Diedrich"
z+~dsS*KCaJHZ6*~quOC4C?~| l&%|mRWeE2>W-efCdXKR-i2D6ug1Bw@>TIhosTzZ The main Emysql module. Emysql is implemented as an Erlang
+ application. The term has a special meaning in Erlang, see
+ http://www.erlang.org/doc/design_principles/applications.html This module exports functions to:
+ Under this headline, Implementation, you will find details about the
+inner workings of Emysql. If you are new to Emysql, you can safely ignore
+them.
+As all executions, it uses the monitor_work/3 function to create a process to
+asynchronously handle the execution. add_pool(PoolId, Size, User, Password, Host, Port, Database, Encoding) -> any() decrement_pool_size(PoolId, Num) -> any() default_timeout() -> Timeout
+ Returns the default timeout in milliseconds. As set in emysql.app.src,
+or if not set, the value ?TIMEOUT as defined in include/emysql.hrl (8000ms). execute(PoolId, Query::Query | StmtName) -> Result | [Result]
+ Execute a query, prepared statement or a stored procedure. Same as
+
+Module emysql
+The main Emysql module.
+
+
+Description
Sample
+ -module(sample).
+ -export([run/0]).
+
+ run() ->
+
+ crypto:start(),
+ emysql:start(),
+
+ emysql:add_pool(hello_pool, 1,
+ "hello_username", "hello_password", "localhost", 3306,
+ "hello_database", utf8),
+
+ emysql:execute(hello_pool,
+ <<"INSERT INTO hello_table SET hello_text = 'Hello World!'">>),
+
+ emysql:prepare(my_stmt, <<"SELECT * from mytable WHERE id = ?">>),
+
+ Result = emysql:execute(mypoolname, my_stmt, [1]).
+
+ Implementation
+
+ start() -> application:start(emysql).
+ stop() -> application:stop(emysql).
+ modules() -> emysql_app:modules().
+ default_timeout() -> emysql_app:default_timeout().
+
+ execute() and prepare() are the bulk of the source
+ of this module. A lot gets repeated for default values in lesser arities.
+ The quintessential execute however is this, in execute/2:
+ execute(PoolId, Query, Args, Timeout)
+ when is_atom(PoolId) andalso (is_list(Query) orelse is_binary(Query)) andalso is_list(Args) andalso is_integer(Timeout) ->
+
+ Connection =
+ emysql_conn_mgr:wait_for_connection(PoolId),
+ monitor_work(Connection, Timeout, {emysql_conn, execute, [Connection, Query, Args]});
emysql_conn_mgr
and emysql_conn_mgr
.
+Function Index
+
+
+
+add_pool/8
+decrement_pool_size/2
+default_timeout/0 Returns the default timeout in milliseconds.
+execute/2 Execute a query, prepared statement or a stored procedure.
+execute/3 Execute a query, prepared statement or a stored procedure.
+execute/4 Execute a query, prepared statement or a stored procedure.
+execute/5 Execute a query, prepared statement or a stored procedure - but return immediately, returning the atom 'unavailable', when no connection in the pool is readily available without wait.
+increment_pool_size/2
+prepare/2 Prepare a statement.
+remove_pool/1
+start/0 Start the Emysql application.
+stop/0 Stop the Emysql application. Function Details
+
+add_pool/8
+decrement_pool_size/2
+default_timeout/0
+
+Implementation
+
+ src/emysql.app.src is a template for the emysql app file from which
+ ebin/emysql.app is created during building, by a sed command in 'Makefile'.
+ execute/2
+
+execute(PoolId, Query, [], default_timeout())
.
See also: execute/3, execute/4, execute/5, prepare/2.
+ +execute(PoolId, Query::Query | StmtName, Args::Args | Timeout) -> Result | [Result] +
Execute a query, prepared statement or a stored procedure.
+ +Same as execute(PoolId, Query, Args, default_timeout())
+ or execute(PoolId, Query, [], Timeout)
.
Timeout is the query timeout in milliseconds.
+ + The result is a list for stored procedure execution >= MySQL 4.1 + +See also: execute/2, execute/4, execute/5, prepare/2.
+ +execute(PoolId, Query::Query | StmtName, Args, Timeout) -> Result | [Result] +
Execute a query, prepared statement or a stored procedure.
+ +Connection = emysql_conn_mgr:wait_for_connection(PoolId), + monitor_work(Connection, Timeout, {emysql_conn, execute, [Connection, Query_or_StmtName, Args]}).
+Timeout is the query timeout in milliseconds.
+ + All other execute function eventually call this function. + +See also: execute/2, execute/3, execute/5, prepare/2.
+ +execute(PoolId, Query::Query | StmtName, Args, Timeout, X5::nonblocking) -> Result | [Result] +
Execute a query, prepared statement or a stored procedure - but return immediately, returning the atom 'unavailable', when no connection in the pool is readily available without wait.
+ +Timeout is the query timeout in milliseconds.
+ +{Connection, connection} = case emysql_conn_mgr:lock_connection(PoolId), + monitor_work(Connection, Timeout, {emysql_conn, execute, [Connection, Query_or_StmtName, Args]}).+ +
The result is a list for stored procedure execution >= MySQL 4.1
+ + All other execute function eventually call this function. + +See also: execute/2, execute/3, execute/4, prepare/2.
+ +increment_pool_size(PoolId, Num) -> any()
+prepare(StmtName, Statement) -> ok +
Prepare a statement.
+ +The atom given by parameter 'StmtName' is bound to the SQL string
+ 'Statement'. Calling execute(<Pool>, StmtName, <ParamList>)
executes the
+ statement with parameters from <ParamList>
.
This is not a mySQL prepared statement, but an implementation on the side of +Emysql.
+ +-module(sample). + -export([run/0]). + + run() -> + + application:start(sasl), + crypto:start(), + application:start(emysql), + + emysql:add_pool(hello_pool, 1, + "hello_username", "hello_password", "localhost", 3306, + "hello_database", utf8), + + emysql:execute(hello_pool, + <<"INSERT INTO hello_table SET hello_text = 'Hello World!'">>), + + emysql:prepare(hello_stmt, + <<"SELECT * from hello_table WHERE hello_text like ?">>), + + Result = emysql:execute(hello_pool, hello_stmt, ["Hello%"]), + + io:format("~n~s~n", [string:chars($-,72)]), + io:format("~p~n", [Result]), + + ok.+ Output: +
{result_packet,32, + [{field,2,<<"def">>,<<"hello_database">>, + <<"hello_table">>,<<"hello_table">>, + <<"hello_text">>,<<"hello_text">>,254,<<>>,33, + 60,0,0}], + [[<<"Hello World!">>]], + <<>>}+
Hands parameters over to emysql_statements:add/2:
+ emysql_statements:add(StmtName, Statement).
, which calls
+ handle_call({add, StmtName, Statement}, _From, State)
.
State#state{ + statements = gb_trees:enter(StmtName, {1, Statement}, + State#state.statements)+ Execution is called like this: +
execute(Connection, StmtName, Args) when is_atom(StmtName), is_list(Args) -> + prepare_statement(Connection, StmtName), + case set_params(Connection, 1, Args, undefined) of + OK when is_record(OK, ok_packet) -> + ParamNamesBin = list_to_binary(string:join([[$@ | integer_to_list(I)] || I <- lists:seq(1, length(Args))], ", ")), + StmtNameBin = atom_to_binary(StmtName, utf8), + Packet = <<?COM_QUERY, "EXECUTE ", StmtNameBin/binary, " USING ", ParamNamesBin/binary>>, + emysql_tcp:send_and_recv_packet(Connection#connection.socket, Packet, 0); + Error -> + Error + end.+ +
See also: emysql_conn:execute/3, emysql_statements:add/2, emysql_statements:handle/3.
+ +remove_pool(PoolId) -> any()
+start() -> ok
+Start the Emysql application.
+ +Simply calls application:start(emysql).
+If the application is not already loaded, the application controller will +first load it using application:load/1. It will check the value of the +applications key, to ensure that all applications that should be started +before this application are running. The application controller then +creates an application master for the application. The application master +is the group leader of all the processes in the application. The +application master starts the application by calling the application +callback function start/2 in the module, and with the start argument, +defined by the mod key in the .app file.
+ +application:start(Application) is the same as calling +application:start(Application, temporary). If a temporary application +terminates, this is reported but no other applications are terminated.
+ + See http://www.erlang.org/doc/design_principles/applications.html + + +stop() -> ok
+Stop the Emysql application.
+ +Simply calls application:stop(emysql).
+It is always possible to stop an application explicitly by calling +application:stop/1. Regardless of the mode, no other applications will be +affected.
+ + See http://www.erlang.org/doc/design_principles/applications.html + +Generated by EDoc, Feb 10 2011, 07:38:23.
+ + diff --git a/emysql_app.html b/emysql_app.html new file mode 100644 index 00000000..89b2b708 --- /dev/null +++ b/emysql_app.html @@ -0,0 +1,61 @@ + + + +Behaviours: application.
+ +default_timeout/0 | |
lock_timeout/0 | |
modules/0 | |
pools/0 | |
start/2 | |
stop/1 |
default_timeout() -> any()
+lock_timeout() -> any()
+modules() -> any()
+pools() -> any()
+start(Type, StartArgs) -> any()
+stop(State) -> any()
+Generated by EDoc, Feb 10 2011, 07:38:23.
+ + diff --git a/emysql_auth.html b/emysql_auth.html new file mode 100644 index 00000000..76d2195c --- /dev/null +++ b/emysql_auth.html @@ -0,0 +1,30 @@ + + + +do_handshake/3 |
do_handshake(Sock, User, Password) -> any()
+Generated by EDoc, Feb 10 2011, 07:38:23.
+ + diff --git a/emysql_conn.html b/emysql_conn.html new file mode 100644 index 00000000..3267af2f --- /dev/null +++ b/emysql_conn.html @@ -0,0 +1,84 @@ + + + +close_connection/1 | |
execute/3 | |
open_connection/1 | |
open_connections/1 | |
open_n_connections/2 | |
prepare/3 | |
reset_connection/2 | |
set_database/2 | |
set_encoding/2 | |
unprepare/2 |
close_connection(Conn) -> any()
+execute(Connection, Query, Args) -> any()
+open_connection(Pool) -> any()
+open_connections(Pool) -> any()
+open_n_connections(PoolId, N) -> any()
+prepare(Connection, Name, Statement) -> any()
+reset_connection(Pools, Conn) -> any()
+set_database(Connection, Database) -> any()
+set_encoding(Connection, Encoding) -> any()
+unprepare(Connection, Name) -> any()
+Generated by EDoc, Feb 10 2011, 07:38:23.
+ + diff --git a/emysql_conn_mgr.html b/emysql_conn_mgr.html new file mode 100644 index 00000000..c35040f4 --- /dev/null +++ b/emysql_conn_mgr.html @@ -0,0 +1,133 @@ + + + +Behaviours: gen_server.
+ +add_connections(PoolId, Conns) -> any()
+add_pool(Pool) -> any()
+code_change(OldVsn, State, Extra) -> any()
+find_pool(PoolId, Tail, OtherPools) -> any()
+handle_call(X1, From, State) -> any()
+handle_cast(Msg, State) -> any()
+handle_info(Info, State) -> any()
+init(X1) -> any()
+lock_connection(PoolId) -> any()
+pools() -> any()
+remove_connections(PoolId, Num) -> any()
+remove_pool(PoolId) -> any()
+replace_connection(OldConn, NewConn) -> any()
+start_link() -> any()
+terminate(Reason, State) -> any()
+unlock_connection(Connection) -> any()
+wait_for_connection(PoolId) -> any()
+waiting() -> any()
+Generated by EDoc, Feb 10 2011, 07:38:23.
+ + diff --git a/emysql_statements.html b/emysql_statements.html new file mode 100644 index 00000000..19450a8b --- /dev/null +++ b/emysql_statements.html @@ -0,0 +1,103 @@ + + + +Behaviours: gen_server.
+ +add/2 | |
all/0 | |
code_change/3 | |
fetch/1 | |
handle_call/3 | |
handle_cast/2 | |
handle_info/2 | |
init/1 | |
prepare/3 | |
remove/1 | |
start_link/0 | |
terminate/2 | |
version/2 |
add(StmtName, Statement) -> any()
+all() -> any()
+code_change(OldVsn, State, Extra) -> any()
+fetch(StmtName) -> any()
+handle_call(X1, From, State) -> any()
+handle_cast(Msg, State) -> any()
+handle_info(Info, State) -> any()
+init(X1) -> any()
+prepare(ConnId, StmtName, Version) -> any()
+remove(ConnId) -> any()
+start_link() -> any()
+terminate(Reason, State) -> any()
+version(ConnId, StmtName) -> any()
+Generated by EDoc, Feb 10 2011, 07:38:23.
+ + diff --git a/emysql_sup.html b/emysql_sup.html new file mode 100644 index 00000000..694395ef --- /dev/null +++ b/emysql_sup.html @@ -0,0 +1,37 @@ + + + +Behaviours: supervisor.
+ +init/1 | |
start_link/0 |
init(X1) -> any()
+start_link() -> any()
+Generated by EDoc, Feb 10 2011, 07:38:23.
+ + diff --git a/emysql_tcp.html b/emysql_tcp.html new file mode 100644 index 00000000..87775f08 --- /dev/null +++ b/emysql_tcp.html @@ -0,0 +1,36 @@ + + + +recv_packet/1 | |
send_and_recv_packet/3 |
recv_packet(Sock) -> any()
+send_and_recv_packet(Sock, Packet, SeqNum) -> any()
+Generated by EDoc, Feb 10 2011, 07:38:23.
+ + diff --git a/emysql_tracer.html b/emysql_tracer.html new file mode 100644 index 00000000..324c72cc --- /dev/null +++ b/emysql_tracer.html @@ -0,0 +1,30 @@ + + + +trace_module/1 |
trace_module(Fun) -> any()
+Generated by EDoc, Feb 10 2011, 07:38:23.
+ + diff --git a/emysql_util.html b/emysql_util.html new file mode 100644 index 00000000..abb5e63d --- /dev/null +++ b/emysql_util.html @@ -0,0 +1,125 @@ + + + +as_record/3 | |
as_record/4 | package row data as records. |
asciz/1 | |
bxor_binary/2 | |
dualmap/3 | |
encode/1 | Encode a value so that it can be included safely in a MySQL query. |
encode/2 | |
field_names/1 | |
hash/1 | |
length_coded_binary/1 | |
length_coded_string/1 | |
null_terminated_string/2 | |
quote/1 | |
rnd/3 |
as_record(Result, RecordName, Fields) -> any()
+as_record(Result, RecordName, Fields, Fun) -> Result +
+package row data as records
+ +RecordName is the name of the record to generate. +Fields are the field names to generate for each record.
+ +-module(fetch_example).
+ + fetch_foo() -> + Res = emysql:execute(pool1, "select * from foo"), + Res:as_record(foo, record_info(fields, foo)). + +asciz(Data) -> any()
+bxor_binary(B1, B2) -> any()
+dualmap(F, R1, R2) -> any()
+encode(Val::term()) -> string() | binary() | {error, Error}
+Encode a value so that it can be included safely in a MySQL query. +
+ +encode(Val, AsBinary) -> any()
+field_names(Result) -> any()
+hash(S) -> any()
+length_coded_binary(X1) -> any()
+length_coded_string(Bin) -> any()
+null_terminated_string(X1, Acc) -> any()
+quote(String) -> any()
+rnd(N, Seed1, Seed2) -> any()
+Generated by EDoc, Feb 10 2011, 07:38:23.
+ + diff --git a/emysql_worker.html b/emysql_worker.html new file mode 100644 index 00000000..80188a9e --- /dev/null +++ b/emysql_worker.html @@ -0,0 +1,49 @@ + + + +behaviour_info/1 | |
execute/2 | |
start/1 |
behaviour_info(X1) -> any()
+execute(To::Pid, Message) -> Result +
start(Module) -> Result +
Generated by EDoc, Feb 10 2011, 07:38:23.
+ + diff --git a/erlang.png b/erlang.png new file mode 100644 index 0000000000000000000000000000000000000000..987a618e2403af895bfaf8c2f929e3a4f3746659 GIT binary patch literal 2109 zcmV-D2*US?P)block +# ----------------------------------------------------------- +# tabs are consumed for 'navigation'. sed is Turing complete. +# inserted space is needed by edocs. +# There are tabs in this pattern. +/^ / { + # break ... on last line ('N' would exit) + $ b end_collect_with_last_line_hit + s/^ (.*)$/ \1/ + # do ... + : do_collect + # append next line + N + # break ... if we are now into the last line + # (or the test below will eat the tab away.) + $ b end_collect_with_last_line_hit + # does the current last line start with a tab, too? + s/(\n) (.*)$/\1 \2/ + # while: ... yes, then loop + t do_collect + # normal end of collect: got all indendet lines, plus one too many. + # ----------------------------------------------------------------- + b normal_course + # + # Run into file end while looping + # ------------------------------- + : end_collect_with_last_line_hit + # and does that last line start with a tab, too? + s/(\n) (.*)$/\1 \2/ + s/^ (.*)$/ \1/ + # yes, then we're done actually + t wrap_rest_and_done + # else, cut it off and such, as normal + # debug i\ + # debug normal + # + : normal_course + # ... ok, we have multiple lines, and we have one line too much, back it all up. + h + # Handle theblock to be (*): + # --------------------------------- + # cut off the last line, that doesn't belong and insert newlines + s/^(.*)(\n)(.*)$/\2\1\2/ + # wrap all in the docs code tags ```...''' + s/^(.*)$/```\1'''/ + # protect @ (for edoc related texts that explain @-tags). There is a tab in []. + s/([ \"\'\`]+@)/\1@/g + # send result to stdout + p + # Now make sure that that last line is not lost: + # ---------------------------------------------- + # get stored back + g + # this time discard all but the last line, which is processed further + s/^.*\n(.*)$/\1/ + # jump to end + b end_of_code_blocks_handling + # + # File End Remedy: wrap all to end and done. + # ------------------------------------------ + : wrap_rest_and_done + # debug i\ + # debug rest and done + # wrap all in the docs code tags ```...''' + s/^(.*)$/```\1'''/ + # protect @ (for edoc related texts that explain @-tags). There is a tab in []. + s/([ \"\'\`]+@)/\1@/g + b end + # +} + +:end_of_code_blocks_handling + + +# robust alternate for code blocks: each tabbed line +# -------------------------------------------------- +# If the above keeps being difficult, use this more robust +# version. The main difference is merely that it will tag each +# line separately. If you work with very small margins and +# paddings forin your css file, that might give just as +# nice a result as the above. There are tabs in this pattern. +# (Really, delete all of the above from '# code sample blocks ...' +# to # :end_of_code_blocks_handling) +# s/^ (.+)$/``` \1'''/ + +# edoc sugar +# ----------- +# won't help the markdown source but make the edoc prettier + +# footnote signs +# .............. +# superscript 1 +s/\(\*1\)/\¹/g +# superscript 2 +s/\(\*2\)/\²/g +# superscript 3 +s/\(\*3\)/\³/g +# dagger +s/\(\+\)/\/g +# double dagger +s/\(\+\+\)/\/g +# star +s/\(\*\)/\*/g +# double star +s/\(\*\*\)/\*\*/g +# triple star +s/\(\*\*\*\)/\*\*\*/g + +# special chars +# ............. +# middle dot +s/::/\·/g +# guillemot +s/<\«/g +s/>>/\»/g + +# copyright +# ......... +s/\(c)/\©/g +s/\(C)/\©/g +s/\(R)/\®/g +s/\(r)/\®/g +s/\(tm)/\/g +s/\(TM)/\/g + +# atone for markdown \_ +# --------------------- +s/\\_/_/g + + +# links +# ----- +# external links +s/\[([^]]+)\]\(([^)]+)\)/\1<\/a>/ + + +# references, '[..]:...'-style +# ---------------------------- +# take out markdown anchor relay tricks (see README) +# .................................................. +s/\[[^]]+\]:[ ]*#.*// + +# real urls (normal case) +# ....................... +s/(\[([^]]+)\]): +\[?(http[s]?:\/\/[^.>" ]+\.[^>" ]+)\]? * *("([^"]+)") * *$/\5:\3<\/a><\/li>/ +# check next line "..." description +/(\[([^]]+)\]): +\[?(http[s]?:\/\/[^.>" ]+\.[^>" ]+)\]? *$/ { + # get next line, if the current is not the last + $!N + # try two line spanning, or single (last) line + s/(\[([^]]+)\]): +\[?(http[s]?:\/\/[^.>" ]+\.[^>" ]+)\]? * *\n * *("([^"]*)") * *$/ \5:\3<\/a><\/li>/ + t double_line_url_done + # try one line only, rest to be saved + s/(\[([^]]+)\]): +\[?(http[s]?:\/\/[^.>" ]+\.[^>" ]+)\]? * *(\n)/ \3<\/a><\/li>\4/ + t double_line_url_done + # case of last line, single, no "..." description + s/(\[([^]]+)\]): +\[?(http[s]?:\/\/[^.>" ]+\.[^>" ]+)\]? * *$/ \3<\/a><\/li>/ + : double_line_url_done + # print out up to first \n, delete, start from top with the rest + P + D +} + +# email addresses +# ............... +s/(\[([^]]+)\]): +([^@>" ]+@[^.>" ]+\.[^>" ]+)>? * *("([^"]+)") * *$/ \5:\3<\/a><\/li>/ +# check next line "..." description +/(\[([^]]+)\]): +([^@>" ]+@[^.>" ]+\.[^>" ]+)>? * *("([^"]+)")? * *$/ { + # get next line, if the current is not the last + $!N + # try two line spanning, or single (last) line + s/(\[([^]]+)\]): +([^@>" ]+@[^.>" ]+\.[^>" ]+)>? * *\n * *("([^"]+)") * *$/ \5:\3<\/a><\/li>/ + t double_line_mail_done + # try one line only, rest to be saved + s/(\[([^]]+)\]): +([^@>" ]+@[^.>" ]+\.[^>" ]+)>? * *(\n)/ \3<\/a><\/li>\4/ + t double_line_mail_done + # case of last line, single, no "..." description + s/(\[([^]]+)\]): +([^@>" ]+@[^.>" ]+\.[^>" ]+)>? * *$/ \3<\/a><\/li>/ + : double_line_mail_done + # print out up to first \n, delete, start from top with the rest + P + D +} + + +# smart reference for the [x]: ... format, jumping right to the referenced page. +# ------------------------------------------------------------------------------ +# s/\[([^]]+)\]\[\]/\1<\/a>/g +# s/\[([^]]+)\]\[([^]]+)\]/\1<\/a>/g + + +# robust alternate reference for the [x]: ... format, jumping to footnote. +# ------------------------------------------------------------------------ +# If you don't like the 'smart' javascript tags, comment out the previous 'smart' +# reference patterns and uncomment these. The behavior then becomes, not to +# directly jump to a given url when clicked, but to the line where that link +# is listed that then can be clicked. So it's one click more, but it works. +s/\[([^]]+)\]\[\]/\1<\/a>/g +s/\[([^]]+)\]\[([^]]+)\]/\1<\/a>/g + + +# headlines by # +# -------------- +# h1 demoted to h2 as h1 is reserved in edoc, +# in passing weed out explicit anchors and formatting +s/^####([^<]+).*/====\1 ====/ +s/^###([^<]+).*/===\1 ===/ +s/^##([^<]+).*/==\1 ==/ +s/^#([^<]+).*/==\1 ==/ + + +# italics, bold +# ------------- +s/\*\*(([^*]+\*?)+)\*\*/\1<\/b>/g +s/\*([^*]+)\*/\1<\/em>/g + + +# bullet points +# ------------- +# edoc must see closing +s/^\*(.+)$/\1<\/li>/ + + +# emails, urls +# ------------ +s/<([^aA][^@>]+@[^.>]+.[^>]+)>/\1<\/a>/ +s/<(http[s]?:\/\/[^.>]+.[^>]+)>/\1<\/a>/ + + +# line breaks +# ----------- +s/ $/
/ + + +# single backticks +# ---------------- +# make code quotes +s/`([^`]+)`/\1<\/code>/g + + +# protect @ +# --------- +# leading space or tab indicates use as code sample for, well, edoc +# itself most likely, so escape it. +s/([ \"\'\`]+@)/\1@/g + +# protect & +# --------- +# still edoc won't understand code names like → +s//:::AMPERSAND:::#/g +s/&([a-z]{1,7});/:::AMPERSAND:::\1;/g +s/&/\&/g +s/:::AMPERSAND:::/\&/g + +# headlines by underline === or --- +# --------------------------------- +# demoted to h2 and h3, as h1 is reserved in edoc +{ + # don't check this for the last line ('N' would exit) + $ b skip_alt_headlines + # get next line + N + # contract === with previous to headline h2 + # and in passing weed out explicit anchors and formatting + s/^([^<]+)(<.*>)?[ ]*\n=+ *$/== \1 ==/ + # if substitution took place, goto ... + t substi + # contract --- with previous to headline h2 + # and in passing weed out explicit anchors and formatting + s/^([^<]+)(<.*>)?[ ]*\n-+ *$/=== \1 ===/ + # if substitution took place, goto ... + t substi + # no substitution: print the previous line and start with latest from top + # ----------------------------------------------------------------------- + # store the two lines we have now, one is the one formatting is done with + # the next is the fresh one we just pulled. + h + # cut off the last line, print the ready formatted one + P + D + # and this is the goto for successful headline substitutions above: + :substi +} + +:skip_alt_headlines + +:end + +# at the bottom, add JS for the 'smart' direct jump +# ------------------------------------------------- +# to a reference url in trailing '[]:...'-notation +$ a\ + + +# debugger stable +# --------------- +# i\ +# >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +# p +# i\ +# <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + +# ----------------------------------------------------------------- +# t,b: "In most cases, use of these commands indicates that you are +# probably better off programming in something like awk or Perl." +# sed manual: http://www.gnu.org/software/sed/manual/sed.html +# ----------------------------------------------------------------- +# 'powered by Eonblast' http://www.eonblast.com - all the new tech \ No newline at end of file diff --git a/modules-frame.html b/modules-frame.html new file mode 100644 index 00000000..ff6fa78f --- /dev/null +++ b/modules-frame.html @@ -0,0 +1,22 @@ + + + +
The emysql application + + + +Modules
++
+ + diff --git a/overview-summary.html b/overview-summary.html new file mode 100644 index 00000000..8030396d --- /dev/null +++ b/overview-summary.html @@ -0,0 +1,426 @@ + + + ++ emysql + emysql_app + emysql_auth + emysql_conn + emysql_conn_mgr + emysql_statements + emysql_sup + emysql_tcp + emysql_tracer + emysql_util emysql_worker Emysql - Erlang MySQL driver + + + + + +Emysql - Erlang MySQL driver +
+Version: 0.2 +
+Authors: Bill Warnecke, Jacob Vorreuter, Henning Diedrich.
+ +Emysql
+ +Erlang MySQL driver, based on a rewrite at Electronic Arts. Easy to use, Strong connection pooling, prepared statements & stored procedures. Optimized for a central node architecture and OLTP.
+ +While you can use mysql via ODBC, using a driver, like Emysql, should perform better. For samples and docs see below. Read the brief on choosing a package and about the history of the various MySQL drivers.
+ +This package is a direct continuation of the original emysql with fixes, updates, more documentation and samples. Emysql is a cleaner rewrite of erlang-mysql-driver, see History.
+ +
+ +Which fork/package should I use? Likely this one, but see Choosing.
+ +
+ Why are there so many? See History.
+ Who used this fork? Electronic Arts.
+ How do I ...? See Samples.
+ Hello ...? See Samples.Download: https://github.com/Eonblast/Emysql/archives/master
+ +
+ Docs: http://eonblast.github.com/Emysql/
+ Issues: https://github.com/Eonblast/Emysql/issues
+ Repository: https://github.com/Eonblast/Emysql
+ +Contents
+ +Choosing +Samples +Usage +History +Links +Todo +License + +
+ +Choosing
+ +Best
+In most cases, especially for high performance and stability, this package, Emysql, will be the best choice. It was rewritten from the ground up to overcome fundamental issues of 'erlang-mysql-driver'. It also has some usable docs meanwhile.
+ +Simple
+If you are looking for the plain necessities, you should use the ejabberd mysql driver. It is simple, battle tested and stable. There are comprehensive instructions in the source comments.
+ +Transaction
+For mnesia-style transactions, one of the multiple 'erlang-mysql-drivers' may suite you best. There are quite many branches of it out there, and they are based on the same project as the ejabberd driver. To learn more about out the differences between the drivers, see the mysql driver history.
+ +Getting Emysql
+ +$ git clone git://github.com/Eonblast/Emysql.git Emysql+ + +Samples
+ +Hello World
+ +This is a hello world program. Follow the three steps below to try it out. +-module(a_hello). + -export([run/0]). + + run() -> + + crypto:start(), + application:start(emysql), + + emysql:add_pool(hello_pool, 1, + "hello_username", "hello_password", "localhost", 3306, + "hello_database", utf8), + + emysql:execute(hello_pool, + <<"INSERT INTO hello_table SET hello_text = 'Hello World!'">>), + + Result = emysql:execute(hello_pool, + <<"select hello_text from hello_table">>), + + io:format("~n~p~n", [Result]).+ + +We'll be coming back to this source to make it run on your machine in a minute. But let's look at the basic building blocks first:
+ +Executing an SQL Statement
+ +emysql:execute(my_pool, <<"SELECT * from mytable">>).+ +For the exact spec, see below, Usage. Regarding the 'pool', also see below.
+ +Executing a Prepared Statement
+ +emysql:prepare(my_stmt, <<"SELECT * from mytable WHERE id = ?">>). + + emysql:execute(my_pool, my_stmt, [1]).+ +Executing Stored Procedures
+ +emysql:execute(my_pool, <<"create procedure my_sp() begin select * from mytable; end">>). + + emysql:execute(my_pool, <<"call my_sp();">>).+ +Result Record
+ +-record(result_packet, {seq_num, field_list, rows, extra}).+Converting Row Data To Records
+ +-record(foo, {bar, baz}).+ +Result = emysql:execute(pool1, <<"select bar, baz from foo">>). + Recs = emysql_util:as_record(Result, foo, record_info(fields, foo)). + Bars = [Foo#foo.bar || Foo <- Recs].+ +Adding a Connection to the Connection Pool
+ +Emysql uses a sophisticated connection pooling mechanism.
+ +emysql:add_pool(my_pool, 1, "myuser", "mypass", "myhost", 3306, "mydatabase", utf8).+ +Running Hello World
+ +Let's run the hello world sample from above:
+ +1. Build Emysql
+ +Build emysql.app, using make:
+ +$ cd Emysql + $ make+ + +2. Make a Sample Database
+ +For use in the above sample (and all of those below, too), create a local mysql database. You should have a mysql server installed and running: +$ mysql [-u<user> -p] + mysql> create database hello_database; + mysql> use hello_database; + mysql> create table hello_table (hello_text char(20)); + mysql> grant all privileges on hello_database.* to hello_username@localhost identified by 'hello_password';+ +3. Paste & Run Hello
+ +Be sure to have ./ebin in your Erlang path. The hello-world source as shown above already waits in the Emysql directory, as hello.erl. Just compile and run it:
+ +$ erlc hello.erl + $ erl -pa ./ebin -s hello run -s init stop -noshell+ +That's it.
+ +There are more sample programs:
+ +More Samples
+Sample programs are in ./samples.
+ +a_hello - Hello World +b_raw - Hello World, raw output +c_rows_as_records - Using Erlang records to access result rows +d_prepared_statement - Using prepared statements +e_stored_procedure - Using stored procedures + +To run the samples, create the database as listed above at localhost, and simply run the compile & run batches:
+ +$ cd samples + $ ./a_hello + $ ./b_raw + $ ./c_rows_as_records + $ ./d_prepared_statement + $ ./e_stored_procedure+or (after building emysql.app and the database, as explained above), start a_hello etc. manually along these lines:
+ +$ make + $ cd samples + $ erlc a_hello.erl + $ erl -pa ../ebin -s a_hello run -s init stop -noshell+ +Usage
+ +General Notes on using Emysql, including the actual specs:
+ +Starting an Application
+ +The Emysql driver is an Erlang gen-server, and, application.
+ +crypto:start(), + application:start(emysql).+ +Adding a Pool
+ +% emysql:add_pool(PoolName, PoolSize, Username, Password, Host, Port, Database, Encoding) -> + % ok | {error, pool_already_exists} + % PoolName = atom() + % PoolSize = integer() + % Username = string() + % Password = string() + % Host = string() + % Port = integer() + % Database = string() + % Encoding = atom() + + emysql:add_pool(mypoolname, 1, "username", "mypassword", "localhost", 3306, "mydatabase", utf8).+ +More Record Types
+ +-record(result_packet, {seq_num, field_list, rows, extra}). + + -record(ok_packet, {seq_num, affected_rows, insert_id, status, warning_count, msg}). + + -record(error_packet, {seq_num, code, msg}).+ +For other record types, see include/emysql.hrl.
+ +Executing SQL Statements
+ +% emysql:execute(PoolName, Statement) -> result_packet() | ok_packet() | error_packet() + % PoolName = atom() + % Statement = string() | binary() + + emysql:execute(mypoolname, <<"SELECT * from mytable">>). + # result_packet{field_list=[...], rows=[...]} + + emysql:execute(mypoolname, <<"UPDATE mytable SET bar = 'baz' WHERE id = 1">>). + # ok_packet{affected_rows=1}+ +Executing Prepared Statements
+ +% emysql:prepare(StmtName, Statement) -> ok + % StmtName = atom() + % Statement = binary() | string() + + emysql:prepare(my_stmt, <<"SELECT * from mytable WHERE id = ?">>). + # ok+ +% emysql:execute(PoolName, StmtName, Args) -> result_packet() | ok_packet() | error_packet() + % StmtName = atom() + % Args = [term()] + + emysql:execute(mypoolname, my_stmt, [1]). + #result_packet{field_list=[...], rows=[...]}+ +Executing Stored Procedures
+ +% emysql:execute(PoolName, StmtName, Args) -> result_packet() | ok_packet() | error_packet() + % StmtName = atom() + % Args = [term()] + + emysql:execute(hello_pool, + <<"create procedure sp_hello() begin select * from hello_table; end">>). + {ok_packet,1,0,0,2,0,[]} + + emysql:execute(hello_pool, <<"call sp_hello();">>). + [{result_packet,6, + [{field,2,<<"def">>,<<"hello_database">>,<<"hello_table">>, + <<"hello_table">>,<<"hello_text">>,<<"hello_text">>, + 254,<<>>,33,60,0,0}], + [[<<"Hello World!">>],[<<"Hello World!">>]], + <<>>}, + {ok_packet,7,0,0,34,0,[]}]+ +Note that you are getting back a list of results here.
+ +Converting Row Data To Records
+ +% emysql_util:as_record(ResultPacket, RecordName, Fields) -> Result + % ResultPacket = result_packet() + % RecordName = atom() (the name of the record to generate) + % Fields = [atom()] (the field names to generate for each record) + % Result = [record()] + + -module(fetch_example). + -record(foo, {bar, baz, bat}). + + fetch_foo() -> + Result = emysql:execute(pool1, <<"select bar, baz, bat from foo">>), + Recs = emysql_util:as_record(Result, foo, record_info(fields, foo)), + [begin + io:format("foo: ~p, ~p, ~p~n", [Foo#foo.bar, Foo#foo.baz, Foo#foo.bat]) + end || Foo <- Recs].+ + +History
+ +Open Source Erlang MySQL driver efforts are a fractured matter. You may find yourself digging in the source to find out about their relationship with each other - and which one to pick. Here is a brief history.
+ +Yxa: The first Erlang MySQL driver, in ~270 lines of code, seems to have been written between 2001 and 2004 by Magnus Ahltorp at the Swedish Royal Institute of Technology. It exposes low level, blocking functions to talk 4.0 protocol with a MySQL server. In 2005 Fredrik Thulin brought the driver into its current modular form to use it for the the SIP proxy Yxa while working at the Stockholm University. It has three process layers: a high level, round-robin connection pooling module; then, middle-man, single-connection, blocking processes that do the bit-level wrangling with the MySQL protocol. This module, mysql_conn, can also be used as a single-connection, stand-alone driver. And below this, there are small, protocol-agnostic receiver processes that handle the socket communication with the server, no matter the contents. Fredrik implemented gen-server behavior, added logging, error messages, upgraded authentication, and thoroughly commented the source. This mysql driver is working, complete and stable since at least 2007, it is available as part of Yxa 1.0 (hosted on github). It has no support for transactions or stored procedures. It is the basis for the following two packages. Its basic modular division and general functionality were not changed but only enhanced and it had originally been agreed upon that the Yxa branch should receive and merge the contributions of the later forks upstream. Unfortunately, that did not come to pass.
+ + +ejabberd: In 2006, a fork of the Yxa driver was created by Mickael Remond at Process One to become part of the successful instant messaging server ejabberd (also hosted at github). It can be assumed to be as stable as the Yxa branch, didn't change a byte in the lowest level, and only enhanced the higher levels. The difference to the original Yxa branch mainly consists of added inspection functions that help using the query results, and of an independent adoption to the MySQL 4.1 client-server protocol. The original Yxa branch has meanwhile adopted edoc comment format, which makes the sources look more different than they actually are. Find a Jan 2011 diff between Yxa and the ejabberd version here, and one ignoring comments here. These two branches could, be merged quiet easily, probably without any change in functionality.
+ +erlang-mysql-driver: The storied life of this branch began in 2006 when Yariv Sadan created a fork from the ejabberd branch, made it a standalone project, gave it the maximally generic name that stuck, and hosted it at Google Code. Before he moved on to work at Facebook, he added high-level handling of prepared statements and transactions, and at long last completed some loose ends with the connection pooling that had been known to be lagging since the Yxa version. There were changes both in the original Yxa and the ejabberd branch after the forking off that never made their way into this fork, but in all likelihood they must be minor. It is not always obvious if the changes in erlang-mysql-driver had reached their intended final form. The separation of the three process layers seems to have suffered and complicated enhancements as the highest layer module, mysql.erl, started to become entangled with the second, mysql_conn.erl. Maybe that had a part in why the upstream merge never happened. The main repository of this fork lay dormant since Oct '07 when in Feb '10, Dave Smith started making some updates and put them on github. The driver is now enjoying a couple of active forks that make a convincing case for the github Network graphs.
+ +A parallel fork from Yariv's branch, not entangled with Dave's tree, is the one by Nick Gerakines. It may technically be the strongest of the erlang-mysql-driver forks, with a lot of clean up work by some very smart guys put in. It is generally less well known. And much less forked. In the end, the branch was abandoned in favor of Emysql. In all branches, docs beyond the source comments remain lacking.
+ +Emysql was created from scratch in 2009, specifically to achieve better stability and throughput. It was proposed and written by Jacob Vorreuter at Electronic Arts and deployed at Shawn Fanning's Rupture.com, a social network site for gamers. Initially, Nick Gerakines, Jacob's boss at EA, rewrote large parts of erlang-mysql-server to clean it up. But some fundamental problems remained and when half a year in, they could still not overcome performance and stability issues, Nick gave Jacob green light to rewrite it from the ground up because they felt that, in Jacob's words, the Yxa branch had been touched by so many people that it had become more complicated than necessary. According to Jacob, Bill Warnecke helped in the early design and testing. They abandoned the separation into three process layers and pulled the socket handling and bit-parsing into one module, coupling the functionality in direct function calls. It looks like they borrowed some chore lines from Magnus but generally created a new, straight forward architecture focussed on providing a high performance node. Not only can Emysql have multiple connections, but multiple pools of multiple connections each, which makes for a strong central OLTP manager. Jacob says that Emysql is pretty stable and ran without issues in production at EA. Nick remembers: "The primary way we used it was to have a single Erlang node be the MySQL communication point and allow a number of connected nodes to connect through it to MySQL. We wanted very high throughput with many pids across a grid connecting to it and needed the ability to have a number of MySQL connections open for connection pooling." Rupture was killed in the consolidations of 2008. But Shawn could probably keep the money and we the fond memory of Napster and now, the glistening Emysql.
+ +Eonblast Emysql is a continuation fork of Jacob's work, including all his commits and adding docs, samples, fixes and extensions. Henning Diedrich, Vitaliy Batichko and Chris Rempel have contributed updates to this branch. Support for stored procedures has been added, but the fork is otherwise still very close to the original, which currently lays dormant.
+ +Fredrik, Nick and Jacob helped shedding light on the matter. Thank you very much! Errors and omissions are mine. Please let me know about typos and errors you may be spotting. Thanks.
+ + +Links and References
+ +emysql:http://github.com/JacobVorreuter/emysql +erlang-mysql-driver:http://github.com/dizzyd/erlang-mysql-driver +Royal Institure of Technology:http://www.kth.se/ +Yxa mysql driver:https://github.com/fredrikt/yxa/tree/master/src/mysql +Yxa Home:http://www.stacken.kth.se/project/yxa/index.html +Yxa repository at github:https://github.com/fredrikt/yxa +ejabberd mysql driver:http://svn.process-one.net/ejabberd-modules/mysql/trunk/ +Process One Home:https://support.process-one.net +ejabberd Home:http://www.process-one.net/en/ejabberd/ +ejabberd repository at github:https://github.com/processone/ejabberd/ +ejabberd MySQL 4.1. patch:https://support.process-one.net/doc/display/CONTRIBS/Yxa +Diff of Yxa and ejabberd mysql drivers:https://github.com/Eonblast/Emysql/tree/master/doc/diff-ejabberd-yxa.txt +Diff of Yxa and ejabberd mysql drivers ignoring comment changes:https://github.com/Eonblast/Emysql/tree/master/doc/diff-ejabberd-yxa-2.txt +original erlang-mysql-driver:http://code.google.com/p/erlang-mysql-driver/ +Dave Smith's erlang-mysql-driver at github, currently not maintained:http://github.com/dizzyd/erlang-mysql-driver +Fork graph of erlang-mysql-driver at github:https://github.com/dizzyd/erlang-mysql-driver/network +Earliest Yxa mysql driver:http://www.stacken.kth.se/projekt/yxa/mysql-0.1.tar.gz +MySQL protocol:http://forge.mysql.com/wiki/MySQL_Internals_ClientServer_Protocol +Stockholm University:http://www.su.se/english/ +Network graph of Nick's erlang-mysql-driver fork:https://github.com/ngerakines/erlang_mysql/network +Commits in Nick Gerakines' erlang-mysql-driver fork:https://github.com/ngerakines/erlang_mysql/commits/master +Nick Gerakines' erlang-mysql-driver fork:https://github.com/ngerakines/erlang_mysql +About Rupture:http://method.com/detail/CaseStudy/65 +Commits in the current Eonblast Emysql branch:https://github.com/Eonblast/Emysql/commits/master + +
+Magnus Ahltorp:ahltorp@nada.kth.se +Fredrik Thulin:ft@it.su.se +Mickael Remond:mickael.remond@process-one.net +Yariv Sadan:http://yarivsblog.blogspot.com +Dave Smith:dizzyd@dizzyd.com +Nick Gerakines:https://github.com/ngerakines +Jacob Vorreuter:https://github.com/JacobVorreuter +Bill Warnecke:bill@rupture.com +Henning Diedrich:hd2010@eonblast.com +Vitaliy Batichko:https://github.com/bva +Chris Rempel:https://github.com/csrl + +Eonblast Emysql Repository:https://github.com/Eonblast/Emysql +Emysql fixes:https://github.com/Eonblast/Emysql/issues/closed +Emysql updates:https://github.com/Eonblast/Emysql/commits/master +Emysql online docs:http://eonblast.github.com/Emysql/ + +Links
+ +Emysql on Github +Original Yxa mysql driver +ejabberd fork +'erlang-mysql-driver' +Dave Smith's erlang-mysql-driver fork +A maintained† erlang-mysql-driver fork +Another maintained† erlang-mysql-driver fork +MySQL Client Server Protocol +MySQL 5.5 Source + +†maintained at the time of writing, Feb 2011.
+ + + +TODO
+
decrementing pool size could close sockets that are in use +spawn individual conn_mgr gen_server processes for each pool +allow row results to be returned as binary + +License
+ +Copyright © 2009-2011 +Bill Warnecke bill@rupture.com, +Jacob Vorreuter jacob.vorreuter@gmail.com, +Henning Diedrich hd2010@eonblast.com, +Eonblast Corporation http://www.eonblast.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.
+ + + + + + + + + + + + + + +
+ +Generated by EDoc, Feb 10 2011, 07:38:24.
+ + diff --git a/overview.edoc b/overview.edoc new file mode 100644 index 00000000..116afbbb --- /dev/null +++ b/overview.edoc @@ -0,0 +1,7 @@ +@author Bill Warnecke, Jacob Vorreuter, Henning Diedrich + +@title Emysql - Erlang MySQL driver + +@version 0.2 + +@docfile "doc/readme.edoc" diff --git a/packages-frame.html b/packages-frame.html new file mode 100644 index 00000000..504e9860 --- /dev/null +++ b/packages-frame.html @@ -0,0 +1,11 @@ + + + +The emysql application + + + +Packages
++ + diff --git a/powered-by-mysql-125x64.png b/powered-by-mysql-125x64.png new file mode 100644 index 0000000000000000000000000000000000000000..86b3460d505d1be6baaab2a3f5d63d9ab01856d0 GIT binary patch literal 1443 zcmV;U1zh@xP)
M(?)`|aAv$;$A+e}903u+ZG)jv4@H zkN^MwZ=(B)0000PbVXQnQ*UN;cVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU$$w@>( zRA}DCnTfWeAPhiJaDCRgYpaF-|I0~ORFtW&)7HtE<3f4d5E2p`B0efL xC7aeB{ zuL3^HPo%?|%OP_G&0z(y6X}9*1h*Pm7SF8zdHTc)8Tn@9`bqqKo-UY-x_UPF#Quu^ z0=?k*))P57jXuGKx7q#w=6YYCYt!3-!1J@{*?y*Ey$yVqUNTl~nkuFu;RHHNe&^(F z+kb^_lFcUbpGV(K6#TwAiQY3;*^`|^uUszHa&`{AUXuJ;C0?K8^Oe&X*eUduyVqkk z`LEE4q_@-I !uu^fkTNze(yzh#w)OcM zS9zCd^KGUDI%*#VH@t>>9YU(^G&C1=U+8rAR~784K-wo8vN`2xI$m#RgGfXH6*L%e z;IQ#WKkz9$UV{Ks^v4l;H$HYW^ax&V!pGMr(0cT|f=pBIR=&*el+l(ex_`Z1{eaRS zYGAM%+))uK{82=(AfN#>k6}nr&l`}UAQa&^(?tUcqw#2PolRe~Fn`APXQ{4WON|fo z(FZ|OqT;@f)G-<(983N-2J{NbKnLsyH27z)djnlj*PvOM7?!wm)^lYxdxWvP(DV@} zehb3uh$}GqG9VhsfR#X@*k@0N$xE$-DdS0rHgMK+T`zxK(~ad}ELe3K1ip|qrtlyc zvO}aTPdAnay>YenUlS^4^#duVv*^a_kO@~Gqd@wX^t!o&_PWuxy$*kkTqk DJHEU{uN6~g0V0m*qVdvgxc@s{ zn4pttOviMb|3yl2mwF8E&?U7}G$wvU-@4lybm?r_;(o8tg-w@fB{`r&r(vPXahVFe z@(nsrn^-?Qq2C{IoDK_J?Ktzcv!=H%SRQv;h4=JMn<%faWJvEn@jYJXa@u^}at*yu zJD-t4HLugSXS_~lnFKsBo@1d=&2~l-m%A4~hR2FtX?P*l^s?tW(X+MWF3ss|-X~sh ziM=Ep(iL58v0PW+hOW 6qUa xVce>Mj__vN`DZ%Wd3;}rPsz*lzW(6J;tzqcVSM7`Lf-%Y002ovPDHLkV1iWD*RB8n literal 0 HcmV?d00001 diff --git a/stylesheet.css b/stylesheet.css new file mode 100644 index 00000000..e426a904 --- /dev/null +++ b/stylesheet.css @@ -0,0 +1,55 @@ +/* standard EDoc style sheet */ +body { + font-family: Verdana, Arial, Helvetica, sans-serif; + margin-left: .25in; + margin-right: .2in; + margin-top: 0.2in; + margin-bottom: 0.2in; + color: #000000; + background-color: #ffffff; +} +h1,h2 { + margin-left: -0.2in; +} +div.navbar { + background-color: #add8e6; + padding: 0.2em; +} +h2.indextitle { + padding: 0.4em; + background-color: #add8e6; +} +h3.function,h3.typedecl { + background-color: #add8e6; + padding-left: 1em; +} +div.spec { + margin-left: 2em; + background-color: #eeeeee; +} +a.module,a.package { + text-decoration:none +} +a.module:hover,a.package:hover { + background-color: #eeeeee; +} +ul.definitions { + list-style-type: none; +} +ul.index { + list-style-type: none; + background-color: #eeeeee; +} + +/* + * Minor style tweaks + */ +ul { + list-style-type: square; +} +table { + border-collapse: collapse; +} +td { + padding: 3 +}