aboutsummaryrefslogtreecommitdiffstats
path: root/lib/odbc/src
diff options
context:
space:
mode:
authorErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
committerErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
commit84adefa331c4159d432d22840663c38f155cd4c1 (patch)
treebff9a9c66adda4df2106dfd0e5c053ab182a12bd /lib/odbc/src
downloadotp-84adefa331c4159d432d22840663c38f155cd4c1.tar.gz
otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.bz2
otp-84adefa331c4159d432d22840663c38f155cd4c1.zip
The R13B03 release.OTP_R13B03
Diffstat (limited to 'lib/odbc/src')
-rw-r--r--lib/odbc/src/Makefile122
-rw-r--r--lib/odbc/src/odbc.app.src15
-rw-r--r--lib/odbc/src/odbc.appup.src1
-rw-r--r--lib/odbc/src/odbc.erl943
-rw-r--r--lib/odbc/src/odbc_app.erl36
-rw-r--r--lib/odbc/src/odbc_debug.erl43
-rw-r--r--lib/odbc/src/odbc_internal.hrl164
-rw-r--r--lib/odbc/src/odbc_sup.erl43
8 files changed, 1367 insertions, 0 deletions
diff --git a/lib/odbc/src/Makefile b/lib/odbc/src/Makefile
new file mode 100644
index 0000000000..b48dd768c8
--- /dev/null
+++ b/lib/odbc/src/Makefile
@@ -0,0 +1,122 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 1999-2009. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+#
+
+#
+include $(ERL_TOP)/make/target.mk
+
+ifeq ($(TYPE),debug)
+ERL_COMPILE_FLAGS += -Ddebug -W
+endif
+
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+# ----------------------------------------------------
+# Application version
+# ----------------------------------------------------
+include ../vsn.mk
+VSN=$(ODBC_VSN)
+APP_VSN = "odbc-$(VSN)"
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+RELSYSDIR = $(RELEASE_PATH)/lib/odbc-$(VSN)
+
+# ----------------------------------------------------
+# Target Specs
+# ----------------------------------------------------
+MODULES= \
+ odbc \
+ odbc_app \
+ odbc_sup
+
+HRLS = odbc_internal
+
+INCLUDE=../include
+ODBC_HRL=$(INCLUDE)/odbc.hrl
+
+EXT_HRL_FILES= $(ODBC_HRL)
+HRL_FILES = $(HRLS:%=%.hrl)
+ERL_FILES= $(MODULES:%=%.erl)
+
+APP_FILE = odbc.app
+APP_SRC = $(APP_FILE).src
+APP_TARGET = $(EBIN)/$(APP_FILE)
+
+APPUP_FILE = odbc.appup
+APPUP_SRC = $(APPUP_FILE).src
+APPUP_TARGET = $(EBIN)/$(APPUP_FILE)
+
+TARGET_FILES = $(MODULES:%=$(EBIN)/%.$(EMULATOR))
+
+# ----------------------------------------------------
+# ODBC FLAGS
+# ----------------------------------------------------
+ODBC_FLAGS = -D'SERVER_SOFTWARE="odbc/$(VSN)"' \
+
+# ----------------------------------------------------
+# FLAGS
+# ----------------------------------------------------
+ERL_COMPILE_FLAGS += -I$(INCLUDE) \
+ $(ODBC_FLAGS) \
+ +'{parse_transform,sys_pre_attributes}' \
+ +'{attribute,insert,app_vsn,$(APP_VSN)}'
+# ----------------------------------------------------
+# Targets
+# ----------------------------------------------------
+#debug:
+# @${MAKE} TYPE=debug opt
+
+debug opt: $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET)
+
+clean:
+ rm -f $(TARGET_FILES)
+ rm -f $(APP_TARGET)
+ rm -f $(APPUP_TARGET)
+ rm -f errs core *~
+# ---------------------------------------------------
+# Special Target
+# ---------------------------------------------------
+
+
+$(APP_TARGET): $(APP_SRC) ../vsn.mk
+ sed -e 's;%VSN%;$(VSN);' $< > $@
+$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
+ sed -e 's;%VSN%;$(VSN);' $< > $@
+
+docs:
+
+# ----------------------------------------------------
+# Release Target
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+
+release_spec: opt
+ $(INSTALL_DIR) $(RELSYSDIR)/src
+ $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(RELSYSDIR)/src
+ $(INSTALL_DIR) $(RELSYSDIR)/include
+ $(INSTALL_DATA) $(EXT_HRL_FILES) $(RELSYSDIR)/include
+ $(INSTALL_DIR) $(RELSYSDIR)/ebin
+ $(INSTALL_DATA) $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) \
+ $(RELSYSDIR)/ebin
+
+
+release_docs_spec:
+
+
diff --git a/lib/odbc/src/odbc.app.src b/lib/odbc/src/odbc.app.src
new file mode 100644
index 0000000000..5229b28c08
--- /dev/null
+++ b/lib/odbc/src/odbc.app.src
@@ -0,0 +1,15 @@
+{application, odbc,
+ [{description, "Erlang ODBC application"},
+ {vsn, "%VSN%"},
+ {modules, [
+ odbc,
+ odbc_app,
+ odbc_sup
+ ]},
+ {registered, [
+ odbc_sup
+ ]},
+ {applications, [kernel, stdlib]},
+ {env,[]},
+ {mod, {odbc_app, []}}]}.
+
diff --git a/lib/odbc/src/odbc.appup.src b/lib/odbc/src/odbc.appup.src
new file mode 100644
index 0000000000..e95e542ff5
--- /dev/null
+++ b/lib/odbc/src/odbc.appup.src
@@ -0,0 +1 @@
+{"%VSN%", [],[]}
diff --git a/lib/odbc/src/odbc.erl b/lib/odbc/src/odbc.erl
new file mode 100644
index 0000000000..8178accf6d
--- /dev/null
+++ b/lib/odbc/src/odbc.erl
@@ -0,0 +1,943 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1999-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%
+
+-module(odbc).
+
+-behaviour(gen_server).
+
+-include("odbc_internal.hrl").
+
+%% API --------------------------------------------------------------------
+
+-export([start/0, start/1, stop/0,
+ connect/2, disconnect/1, commit/2, commit/3, sql_query/2,
+ sql_query/3, select_count/2, select_count/3, first/1, first/2,
+ last/1, last/2, next/1, next/2, prev/1, prev/2, select/3,
+ select/4, param_query/3, param_query/4, describe_table/2,
+ describe_table/3]).
+
+%%-------------------------------------------------------------------------
+%% supervisor callbacks
+-export([start_link_sup/1]).
+
+%% gen_server callbacks
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
+ terminate/2, code_change/3]).
+
+%%--------------------------------------------------------------------------
+%% Internal state
+-record(state, {erlang_port, % The port to the c-program
+ reply_to, % gen_server From parameter
+ owner, % Pid of the connection owner
+ result_set = undefined, % exists | undefined
+ auto_commit_mode = on, % on | off
+ %% Indicates if first, last and "select absolut"
+ %% is supported by the odbc driver.
+ absolute_pos, % true | false
+ %% Indicates if prev and "select relative"
+ %% is supported by the odbc driver.
+ relative_pos, % true | false
+ scrollable_cursors, % on | off
+ %% connecting | connected | disconnecting
+ state = connecting,
+ %% For timeout handling
+ pending_request,
+ num_timeouts = 0,
+ listen_sockets,
+ sup_socket,
+ odbc_socket
+ }).
+
+%%--------------------------------------------------------------------------
+
+%%%=========================================================================
+%%% API
+%%%=========================================================================
+
+
+%%--------------------------------------------------------------------
+%% Function: start([, Type]) -> ok
+%%
+%% Type = permanent | transient | temporary
+%%
+%% Description: Starts the inets application. Default type
+%% is temporary. see application(3)
+%%--------------------------------------------------------------------
+start() ->
+ application:start(odbc).
+
+start(Type) ->
+ application:start(odbc, Type).
+
+%%--------------------------------------------------------------------
+%% Function: stop() -> ok
+%%
+%% Description: Stops the odbc application.
+%%--------------------------------------------------------------------
+stop() ->
+ application:stop(odbc).
+
+%%-------------------------------------------------------------------------
+%% connect(ConnectionStr, Options) -> {ok, ConnectionReferense} |
+%% {error, Reason}
+%% Description: Spawns an erlang control process that will open a port
+%% to a c-process that uses the ODBC API to open a connection
+%% to the database.
+%%-------------------------------------------------------------------------
+connect(ConnectionStr, Options) when is_list(ConnectionStr), is_list(Options) ->
+
+ %% Spawn the erlang control process.
+ try supervisor:start_child(odbc_sup, [[{client, self()}]]) of
+ {ok, Pid} ->
+ connect(Pid, ConnectionStr, Options);
+ {error, Reason} ->
+ {error, Reason}
+ catch
+ exit:{noproc, _} ->
+ {error, odbc_not_started}
+ end.
+
+%%--------------------------------------------------------------------------
+%% disconnect(ConnectionReferense) -> ok | {error, Reason}
+%%
+%% Description: Disconnects from the database and terminates both the erlang
+%% control process and the database handling c-process.
+%%--------------------------------------------------------------------------
+disconnect(ConnectionReference) when is_pid(ConnectionReference)->
+ ODBCCmd = [?CLOSE_CONNECTION],
+ case call(ConnectionReference, {disconnect, ODBCCmd}, 5000) of
+ {error, connection_closed} ->
+ %% If the connection has already been closed the effect of
+ %% disconnect has already been acomplished
+ ok;
+ %% Note a time out of this call will return ok, as disconnect
+ %% will always succeed, the time out is to make sure
+ %% the connection is killed brutaly if it will not be shut down
+ %% gracefully.
+ ok ->
+ ok;
+ %% However you may receive an error message as result if you try to
+ %% disconnect a connection started by another process.
+ Other ->
+ Other
+ end.
+
+%%--------------------------------------------------------------------------
+%% commit(ConnectionReference, CommitMode, <TimeOut>) -> ok | {error,Reason}
+%%
+%% Description: Commits or rollbacks a transaction. Needed on connections
+%% where automatic commit is turned off.
+%%--------------------------------------------------------------------------
+commit(ConnectionReference, CommitMode) ->
+ commit(ConnectionReference, CommitMode, ?DEFAULT_TIMEOUT).
+
+commit(ConnectionReference, commit, infinity)
+ when is_pid(ConnectionReference) ->
+ ODBCCmd = [?COMMIT_TRANSACTION, ?COMMIT],
+ call(ConnectionReference, {commit, ODBCCmd}, infinity);
+
+commit(ConnectionReference, commit, TimeOut)
+ when is_pid(ConnectionReference), is_integer(TimeOut), TimeOut > 0 ->
+ ODBCCmd = [?COMMIT_TRANSACTION, ?COMMIT],
+ call(ConnectionReference, {commit, ODBCCmd}, TimeOut);
+
+commit(ConnectionReference, rollback, infinity)
+ when is_pid(ConnectionReference) ->
+ ODBCCmd = [?COMMIT_TRANSACTION, ?ROLLBACK],
+ call(ConnectionReference, {commit, ODBCCmd}, infinity);
+
+commit(ConnectionReference, rollback, TimeOut)
+ when is_pid(ConnectionReference), is_integer(TimeOut), TimeOut > 0 ->
+ ODBCCmd = [?COMMIT_TRANSACTION, ?ROLLBACK],
+ call(ConnectionReference, {commit, ODBCCmd}, TimeOut).
+
+%%--------------------------------------------------------------------------
+%% sql_query(ConnectionReference, SQLQuery, <TimeOut>) -> {updated, NRows} |
+%% {selected, ColNames, Rows} | {error, Reason}
+%%
+%% Description: Executes a SQL query. If it is a SELECT query the
+%% result set is returned, otherwise the number of affected
+%% rows are returned.
+%%--------------------------------------------------------------------------
+sql_query(ConnectionReference, SQLQuery) ->
+ sql_query(ConnectionReference, SQLQuery, ?DEFAULT_TIMEOUT).
+
+sql_query(ConnectionReference, SQLQuery, infinity) when
+ is_pid(ConnectionReference), is_list(SQLQuery) ->
+ ODBCCmd = [?QUERY, SQLQuery],
+ call(ConnectionReference, {sql_query, ODBCCmd}, infinity);
+
+sql_query(ConnectionReference, SQLQuery, TimeOut)
+ when is_pid(ConnectionReference),is_list(SQLQuery),integer(TimeOut),TimeOut>0 ->
+ ODBCCmd = [?QUERY, SQLQuery],
+ call(ConnectionReference, {sql_query, ODBCCmd}, TimeOut).
+
+%%--------------------------------------------------------------------------
+%% select_count(ConnectionReference, SQLQuery, <TimeOut>) -> {ok, NrRows} |
+%% {error, Reason}
+%%
+%% Description: Executes a SQL SELECT query and associates the result set
+%% with the connection. A cursor is positioned before
+%% the first row in the result set and the number of
+%% rows in the result set is returned.
+%%--------------------------------------------------------------------------
+select_count(ConnectionReference, SQLQuery) ->
+ select_count(ConnectionReference, SQLQuery, ?DEFAULT_TIMEOUT).
+
+select_count(ConnectionReference, SQLQuery, infinity) when
+ is_pid(ConnectionReference), is_list(SQLQuery) ->
+ ODBCCmd = [?SELECT_COUNT, SQLQuery],
+ call(ConnectionReference, {select_count, ODBCCmd}, infinity);
+
+select_count(ConnectionReference, SQLQuery, TimeOut) when
+ is_pid(ConnectionReference), is_list(SQLQuery), is_integer(TimeOut), TimeOut > 0 ->
+ ODBCCmd = [?SELECT_COUNT, SQLQuery],
+ call(ConnectionReference, {select_count, ODBCCmd}, TimeOut).
+
+%%--------------------------------------------------------------------------
+%% first(ConnectionReference, <TimeOut>) -> {selected, ColNames, Rows} |
+%% {error, Reason}
+%%
+%% Description: Selects the first row in the current result set. The cursor
+%% : is positioned at this row.
+%%--------------------------------------------------------------------------
+first(ConnectionReference) ->
+ first(ConnectionReference, ?DEFAULT_TIMEOUT).
+
+first(ConnectionReference, infinity) when is_pid(ConnectionReference) ->
+ ODBCCmd = [?SELECT, ?SELECT_FIRST],
+ call(ConnectionReference, {select_cmd, absolute, ODBCCmd}, infinity);
+
+first(ConnectionReference, TimeOut)
+ when is_pid(ConnectionReference), is_integer(TimeOut), TimeOut > 0 ->
+ ODBCCmd = [?SELECT, ?SELECT_FIRST],
+ call(ConnectionReference, {select_cmd, absolute, ODBCCmd}, TimeOut).
+
+%%--------------------------------------------------------------------------
+%% last(ConnectionReference, <TimeOut>) -> {selected, ColNames, Rows} |
+%% {error, Reason}
+%%
+%% Description: Selects the last row in the current result set. The cursor
+%% : is positioned at this row.
+%%--------------------------------------------------------------------------
+last(ConnectionReference) ->
+ last(ConnectionReference, ?DEFAULT_TIMEOUT).
+
+last(ConnectionReference, infinity) when is_pid(ConnectionReference) ->
+ ODBCCmd = [?SELECT, ?SELECT_LAST],
+ call(ConnectionReference, {select_cmd, absolute, ODBCCmd}, infinity);
+
+last(ConnectionReference, TimeOut)
+ when is_pid(ConnectionReference), is_integer(TimeOut), TimeOut > 0 ->
+ ODBCCmd = [?SELECT, ?SELECT_LAST],
+ call(ConnectionReference, {select_cmd, absolute, ODBCCmd}, TimeOut).
+%%--------------------------------------------------------------------------
+%% next(ConnectionReference, <TimeOut>) -> {selected, ColNames, Rows} |
+%% {error, Reason}
+%%
+%% Description: Selects the next row relative the current cursor position
+%% : in the current result set. The cursor is positioned at
+%% : this row.
+%%--------------------------------------------------------------------------
+next(ConnectionReference) ->
+ next(ConnectionReference, ?DEFAULT_TIMEOUT).
+
+next(ConnectionReference, infinity) when is_pid(ConnectionReference) ->
+ ODBCCmd = [?SELECT, ?SELECT_NEXT],
+ call(ConnectionReference, {select_cmd, next, ODBCCmd}, infinity);
+
+next(ConnectionReference, TimeOut)
+ when is_pid(ConnectionReference), is_integer(TimeOut), TimeOut > 0 ->
+ ODBCCmd = [?SELECT, ?SELECT_NEXT],
+ call(ConnectionReference, {select_cmd, next, ODBCCmd}, TimeOut).
+
+%%--------------------------------------------------------------------------
+%% prev(ConnectionReference, <TimeOut>) -> {selected, ColNames, Rows} |
+%% {error, Reason}
+%%
+%% Description: Selects the previous row relative the current cursor
+%% : position in the current result set. The cursor is
+%% : positioned at this row.
+%%--------------------------------------------------------------------------
+prev(ConnectionReference) ->
+ prev(ConnectionReference, ?DEFAULT_TIMEOUT).
+
+prev(ConnectionReference, infinity) when is_pid(ConnectionReference) ->
+ ODBCCmd = [?SELECT, ?SELECT_PREV],
+ call(ConnectionReference, {select_cmd, relative, ODBCCmd}, infinity);
+
+prev(ConnectionReference, TimeOut)
+ when is_pid(ConnectionReference), is_integer(TimeOut), TimeOut > 0 ->
+ ODBCCmd = [?SELECT, ?SELECT_PREV],
+ call(ConnectionReference, {select_cmd, relative, ODBCCmd}, TimeOut).
+
+%%--------------------------------------------------------------------------
+%% select(ConnectionReference, <Timeout>) -> {selected, ColNames, Rows} |
+%% {error, Reason}
+%%
+%% Description: Selects <N> rows. If <Position> is next it is
+%% semanticly eqvivivalent of calling next/[1,2] <N>
+%% times. If <Position> is {relative, Pos} <Pos> will be
+%% used as an offset from the current cursor position to
+%% determine the first selected row. If <Position> is
+%% {absolute, Pos}, <Pos> will be the number of the first
+%% row selected. After this function has returned the
+%% cursor is positioned at the last selected row.
+%%--------------------------------------------------------------------------
+select(ConnectionReference, Position, N) ->
+ select(ConnectionReference, Position, N, ?DEFAULT_TIMEOUT).
+
+select(ConnectionReference, next, N, infinity)
+ when is_pid(ConnectionReference), is_integer(N), N > 0 ->
+ ODBCCmd = [?SELECT, ?SELECT_N_NEXT,
+ integer_to_list(?DUMMY_OFFSET), ";",
+ integer_to_list(N), ";"],
+ call(ConnectionReference, {select_cmd, next, ODBCCmd},
+ infinity);
+
+select(ConnectionReference, next, N, TimeOut)
+ when is_pid(ConnectionReference), is_integer(N), N > 0,
+ is_integer(TimeOut), TimeOut > 0 ->
+ ODBCCmd = [?SELECT, ?SELECT_N_NEXT,
+ integer_to_list(?DUMMY_OFFSET), ";",
+ integer_to_list(N), ";"],
+ call(ConnectionReference, {select_cmd, next, ODBCCmd},
+ TimeOut);
+
+select(ConnectionReference, {relative, Pos} , N, infinity)
+ when is_pid(ConnectionReference), is_integer(Pos), Pos > 0, is_integer(N), N > 0 ->
+ ODBCCmd = [?SELECT, ?SELECT_RELATIVE,
+ integer_to_list(Pos), ";", integer_to_list(N), ";"],
+ call(ConnectionReference, {select_cmd, relative, ODBCCmd},
+ infinity);
+
+select(ConnectionReference, {relative, Pos} , N, TimeOut)
+ when is_pid(ConnectionReference), is_integer(Pos), Pos >0, is_integer(N), N > 0,
+ is_integer(TimeOut), TimeOut > 0 ->
+ ODBCCmd = [?SELECT,?SELECT_RELATIVE,
+ integer_to_list(Pos), ";", integer_to_list(N), ";"],
+ call(ConnectionReference, {select_cmd, relative, ODBCCmd},
+ TimeOut);
+
+select(ConnectionReference, {absolute, Pos} , N, infinity)
+ when is_pid(ConnectionReference), is_integer(Pos), Pos > 0, is_integer(N), N > 0 ->
+ ODBCCmd = [?SELECT, ?SELECT_ABSOLUTE,
+ integer_to_list(Pos), ";", integer_to_list(N), ";"],
+ call(ConnectionReference, {select_cmd, absolute, ODBCCmd},
+ infinity);
+
+select(ConnectionReference, {absolute, Pos} , N, TimeOut)
+ when is_pid(ConnectionReference), is_integer(Pos), Pos > 0, is_integer(N), N > 0,
+ is_integer(TimeOut), TimeOut > 0 ->
+ ODBCCmd = [?SELECT, ?SELECT_ABSOLUTE,
+ integer_to_list(Pos), ";", integer_to_list(N), ";"],
+ call(ConnectionReference, {select_cmd, absolute, ODBCCmd},
+ TimeOut).
+%%--------------------------------------------------------------------------
+%% param_query(ConnectionReference, SQLQuery, Params, <TimeOut>) ->
+%% ok | {error, Reason}
+%%
+%% Description: Executes a parameterized update/delete/insert-query.
+%%--------------------------------------------------------------------------
+param_query(ConnectionReference, SQLQuery, Params) ->
+ param_query(ConnectionReference, SQLQuery, Params, ?DEFAULT_TIMEOUT).
+
+param_query(ConnectionReference, SQLQuery, Params, infinity)
+ when is_pid(ConnectionReference), is_list(SQLQuery), is_list(Params) ->
+ Values = param_values(Params),
+ NoRows = length(Values),
+ NewParams = lists:map(fun fix_params/1, Params),
+ ODBCCmd = [?PARAM_QUERY, term_to_binary({SQLQuery ++ [?STR_TERMINATOR],
+ NoRows, NewParams})],
+ call(ConnectionReference, {param_query, ODBCCmd}, infinity);
+
+param_query(ConnectionReference, SQLQuery, Params, TimeOut)
+ when is_pid(ConnectionReference), is_list(SQLQuery), is_list(Params),
+ is_integer(TimeOut), TimeOut > 0 ->
+ Values = param_values(Params),
+ NoRows = length(Values),
+ NewParams = lists:map(fun fix_params/1, Params),
+ ODBCCmd = [?PARAM_QUERY, term_to_binary({SQLQuery ++ [?STR_TERMINATOR],
+ NoRows, NewParams})],
+ call(ConnectionReference, {param_query, ODBCCmd}, TimeOut).
+
+%%--------------------------------------------------------------------------
+%% describe_table(ConnectionReference, Table, <TimeOut>) -> {ok, Desc}
+%%
+%% Desc - [{ColName, Datatype}]
+%% ColName - atom()
+%% Datatype - atom()
+%% Description: Queries the database to find out the datatypes of the
+%% table <Table>
+%%--------------------------------------------------------------------------
+describe_table(ConnectionReference, Table) ->
+ describe_table(ConnectionReference, Table, ?DEFAULT_TIMEOUT).
+
+describe_table(ConnectionReference, Table, infinity) when
+ is_pid(ConnectionReference), is_list(Table) ->
+ ODBCCmd = [?DESCRIBE, "SELECT * FROM " ++ Table],
+ call(ConnectionReference, {describe_table, ODBCCmd}, infinity);
+
+describe_table(ConnectionReference, Table, TimeOut)
+ when is_pid(ConnectionReference),is_list(Table),integer(TimeOut),TimeOut>0 ->
+ ODBCCmd = [?DESCRIBE, "SELECT * FROM " ++ Table],
+ call(ConnectionReference, {describe_table, ODBCCmd}, TimeOut).
+%%%=========================================================================
+%%% Start/stop
+%%%=========================================================================
+%%--------------------------------------------------------------------------
+%% start_link_sup(Args) -> {ok, Pid} | {error, Reason}
+%%
+%% Description: Callback function for the odbc supervisor. It is called
+%% : when connect/2 calls supervisor:start_child/2 to start an
+%% : instance of the erlang odbc control process.
+%%--------------------------------------------------------------------------
+start_link_sup(Args) ->
+ gen_server:start_link(?MODULE, Args, []).
+
+%%% Stop functionality is handled by disconnect/1
+
+%%%========================================================================
+%%% Callback functions from gen_server
+%%%========================================================================
+
+%%-------------------------------------------------------------------------
+%% init(Args) -> {ok, State} | {ok, State, Timeout} | {stop, Reason}
+%% Description: Initiates the erlang process that manages the connection
+%% and starts the port-program that use the odbc driver
+%% to communicate with the database.
+%%-------------------------------------------------------------------------
+init(Args) ->
+ process_flag(trap_exit, true),
+ {value, {client, ClientPid}} = lists:keysearch(client, 1, Args),
+
+ erlang:monitor(process, ClientPid),
+
+ Inet = case gen_tcp:listen(0, [inet6]) of
+ {ok, Dummyport} ->
+ gen_tcp:close(Dummyport),
+ inet6;
+ _ ->
+ inet
+ end,
+
+ {ok, ListenSocketSup} =
+ gen_tcp:listen(0, [Inet, binary, {packet, ?LENGTH_INDICATOR_SIZE},
+ {active, false}, {nodelay, true}]),
+ {ok, ListenSocketOdbc} =
+ gen_tcp:listen(0, [Inet, binary, {packet, ?LENGTH_INDICATOR_SIZE},
+ {active, false}, {nodelay, true}]),
+
+ %% Start the port program (a c program) that utilizes the odbc driver
+ case os:find_executable(?SERVERPROG, ?SERVERDIR) of
+ FileName when is_list(FileName)->
+ Port = open_port({spawn, FileName},
+ [{packet, ?LENGTH_INDICATOR_SIZE}, binary,
+ exit_status]),
+ State = #state{listen_sockets =
+ [ListenSocketSup, ListenSocketOdbc],
+ erlang_port = Port, owner = ClientPid},
+ {ok, State};
+ false ->
+ {stop, port_program_executable_not_found}
+ end.
+
+%%--------------------------------------------------------------------------
+%% handle_call(Request, From, State) -> {reply, Reply, State} |
+%% {reply, Reply, State, Timeout} |
+%% {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, Reply, State} |
+%% {stop, Reason, Reply, State}
+%% Description: Handle incoming requests. Only requests from the process
+%% that created the connection are allowed in order to preserve
+%% the semantics of result sets.
+%% Note: The order of the function clauses is significant.
+%%--------------------------------------------------------------------------
+handle_call({Client, Msg, Timeout}, From, State =
+ #state{owner = Client, reply_to = undefined}) ->
+ handle_msg(Msg, Timeout, State#state{reply_to = From});
+
+%% The client has caught the timeout and is sending a new request, but
+%% we must preserve a synchronous communication with the port. This
+%% request will be handled when we have received the answer to the
+%% timed out request and thrown it away, if it has not already been
+%% timed out itself in which case the request is thrown away.
+handle_call(Request = {Client, _, Timeout}, From,
+ State = #state{owner = Client, reply_to = skip,
+ num_timeouts = N}) when N < ?MAX_SEQ_TIMEOUTS ->
+ {noreply, State#state{pending_request = {Request, From}}, Timeout};
+
+%% The client has sent so many sequential requests that has timed out that
+%% there might be something radically wrong causing the ODBC-driver to
+%% hang. So we give up and close the connection.
+handle_call({Client, _, _}, From,
+ State = #state{owner = Client,
+ num_timeouts = N}) when N >= ?MAX_SEQ_TIMEOUTS ->
+ gen_server:reply(From, {error, connection_closed}),
+ {stop, too_many_sequential_timeouts, State#state{reply_to = undefined}};
+
+handle_call(_, _, State) ->
+ {reply, {error, process_not_owner_of_odbc_connection},
+ State#state{reply_to = undefined}}.
+
+%%--------------------------------------------------------------------------
+%% Func: handle_msg(Msg, Timeout, State) -> same as handle_call/3.
+%% Description: Sends requests to the port-program.
+%% Note: The order of the function clauses is significant.
+%%--------------------------------------------------------------------------
+handle_msg({connect, ODBCCmd, AutoCommitMode, SrollableCursors},
+ Timeout, State) ->
+
+ [ListenSocketSup, ListenSocketOdbc] = State#state.listen_sockets,
+
+ %% Inform c-client so it knows where to send answers
+ {ok, InetPortSup} = inet:port(ListenSocketSup),
+ {ok, InetPortOdbc} = inet:port(ListenSocketOdbc),
+
+ port_command(State#state.erlang_port,
+ [integer_to_list(InetPortSup), ";",
+ integer_to_list(InetPortOdbc) , ?STR_TERMINATOR]),
+
+ NewState = State#state{auto_commit_mode = AutoCommitMode,
+ scrollable_cursors = SrollableCursors},
+
+ case gen_tcp:accept(ListenSocketSup, 5000) of
+ {ok, SupSocket} ->
+ gen_tcp:close(ListenSocketSup),
+ case gen_tcp:accept(ListenSocketOdbc, 5000) of
+ {ok, OdbcSocket} ->
+ gen_tcp:close(ListenSocketOdbc),
+ odbc_send(OdbcSocket, ODBCCmd),
+ {noreply, NewState#state{odbc_socket = OdbcSocket,
+ sup_socket = SupSocket},
+ Timeout};
+ {error, Reason} ->
+ {stop, Reason, {error, connection_closed}, NewState}
+ end;
+ {error, Reason} ->
+ {stop, Reason, {error, connection_closed}, NewState}
+ end;
+
+handle_msg({disconnect, ODBCCmd}, Timeout, State) ->
+ odbc_send(State#state.odbc_socket, ODBCCmd),
+ {noreply, State#state{state = disconnecting}, Timeout};
+
+handle_msg({commit, _ODBCCmd}, Timeout,
+ State = #state{auto_commit_mode = on}) ->
+ {reply, {error, not_an_explicit_commit_connection},
+ State#state{reply_to = undefined}, Timeout};
+
+handle_msg({commit, ODBCCmd}, Timeout,
+ State = #state{auto_commit_mode = off}) ->
+ odbc_send(State#state.odbc_socket, ODBCCmd),
+ {noreply, State, Timeout};
+
+handle_msg({sql_query, ODBCCmd}, Timeout, State) ->
+ odbc_send(State#state.odbc_socket, ODBCCmd),
+ {noreply, State#state{result_set = undefined}, Timeout};
+
+handle_msg({param_query, ODBCCmd}, Timeout, State) ->
+ odbc_send(State#state.odbc_socket, ODBCCmd),
+ {noreply, State#state{result_set = undefined}, Timeout};
+
+handle_msg({describe_table, ODBCCmd}, Timeout, State) ->
+ odbc_send(State#state.odbc_socket, ODBCCmd),
+ {noreply, State#state{result_set = undefined}, Timeout};
+
+handle_msg({select_count, ODBCCmd}, Timeout, State) ->
+ odbc_send(State#state.odbc_socket, ODBCCmd),
+ {noreply, State#state{result_set = exists}, Timeout};
+
+handle_msg({select_cmd, absolute, ODBCCmd}, Timeout,
+ State = #state{result_set = exists, absolute_pos = true}) ->
+ odbc_send(State#state.odbc_socket, ODBCCmd),
+ {noreply, State, Timeout};
+
+handle_msg({select_cmd, relative, ODBCCmd}, Timeout,
+ State = #state{result_set = exists, relative_pos = true}) ->
+ odbc_send(State#state.odbc_socket, ODBCCmd),
+ {noreply, State, Timeout};
+
+handle_msg({select_cmd, next, ODBCCmd}, Timeout,
+ State = #state{result_set = exists}) ->
+ odbc_send(State#state.odbc_socket, ODBCCmd),
+ {noreply, State, Timeout};
+
+handle_msg({select_cmd, _Type, _ODBCCmd}, _Timeout,
+ State = #state{result_set = undefined}) ->
+ {reply, {error, result_set_does_not_exist},
+ State#state{reply_to = undefined}};
+
+handle_msg({select_cmd, _Type, _ODBCCmd}, _Timeout, State) ->
+ Reply = case State#state.scrollable_cursors of
+ on ->
+ {error, driver_does_not_support_function};
+ off ->
+ {error, scrollable_cursors_disabled}
+ end,
+
+ {reply, Reply, State#state{reply_to = undefined}};
+
+%---------------------------------------------------------------------------
+%% Catch all - This can oly happen if the application programmer writes
+%% really bad code that violates the API.
+handle_msg(Request, _Timeout, State) ->
+ {stop, {'API_violation_connection_colsed', Request},
+ {error, connection_closed}, State#state{reply_to = undefined}}.
+
+%%--------------------------------------------------------------------------
+%% handle_cast(Request, State) -> {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, State}
+%% Description: Handles cast messages.
+%% Note: The order of the function clauses is significant.
+%%-------------------------------------------------------------------------
+%% Catch all - This can only happen if the application programmer writes
+%% really bad code that violates the API.
+handle_cast(Msg, State) ->
+ {stop, {'API_violation_connection_colsed', Msg}, State}.
+
+%%--------------------------------------------------------------------------
+%% handle_info(Msg, State) -> {noreply, State} | {noreply, State, Timeout} |
+%% {stop, Reason, State}
+%% Description: Handles timouts, replys from the port-program and EXIT and
+%% down messages.
+%% Note: The order of the function clauses is significant.
+%%--------------------------------------------------------------------------
+handle_info({tcp, Socket, BinData}, State = #state{state = connecting,
+ reply_to = From,
+ odbc_socket = Socket}) ->
+ case binary_to_term(BinData) of
+ {ok, AbsolutSupport, RelativeSupport} ->
+ NewState = State#state{absolute_pos = AbsolutSupport,
+ relative_pos = RelativeSupport},
+ gen_server:reply(From, ok),
+ {noreply, NewState#state{state = connected,
+ reply_to = undefined}};
+ Error ->
+ gen_server:reply(From, Error),
+ {stop, normal, State#state{reply_to = undefined}}
+ end;
+
+
+handle_info({tcp, Socket, _},
+ State = #state{state = connected,
+ odbc_socket = Socket,
+ reply_to = skip,
+ pending_request = undefined}) ->
+ %% Disregard this message as it is a answer to a query that has timed
+ %% out.
+ {noreply, State#state{reply_to = undefined}};
+
+handle_info({tcp, Socket, _},
+ State = #state{state = connected, odbc_socket = Socket,
+ reply_to = skip}) ->
+
+ %% Disregard this message as it is a answer to a query that has timed
+ %% out and process the pending request.
+ {{_, Msg, Timeout}, From} = State#state.pending_request,
+ handle_msg(Msg, Timeout, State#state{pending_request=undefined,
+ reply_to = From});
+
+handle_info({tcp, Socket, BinData}, State = #state{state = connected,
+ reply_to = From,
+ odbc_socket = Socket}) ->
+ %% Send the reply from the database (received by the erlang control
+ %% process from the port program) to the waiting client.
+ gen_server:reply(From, BinData),
+ {noreply, State#state{reply_to = undefined,
+ num_timeouts = 0}};
+
+handle_info({tcp, Socket, BinData}, State = #state{state = disconnecting,
+ reply_to = From,
+ odbc_socket = Socket}) ->
+
+ %% The connection will always be closed
+ gen_server:reply(From, ok),
+
+ case binary_to_term(BinData) of
+ ok ->
+ ok;
+ {error, Reason} ->
+ Report =
+ io_lib:format("ODBC could not end connection "
+ "gracefully due to ~p~n", [Reason]),
+ error_logger:error_report(Report)
+ end,
+
+ {stop, normal, State#state{reply_to = undefined}};
+
+handle_info(timeout,
+ State = #state{state = disconnecting,
+ reply_to = From}) when From /= undefined ->
+ gen_server:reply(From, ok),
+ {stop, {timeout, "Port program is not responding to disconnect, "
+ "will be killed"}, State};
+
+handle_info(timeout,
+ State = #state{state = connecting,
+ reply_to = From}) when From /= undefined ->
+ gen_server:reply(From, timeout),
+ {stop, normal, State#state{reply_to = undefined}};
+
+handle_info(timeout,
+ State = #state{state = connected,
+ pending_request = undefined,
+ reply_to = From}) when From /= undefined ->
+ gen_server:reply(From, timeout),
+ {noreply, State#state{reply_to = skip,
+ num_timeouts = State#state.num_timeouts + 1}};
+
+handle_info(timeout, State =
+ #state{state = connected,
+ pending_request = {{_, {disconnect, _}, _},
+ PendingFrom}}) ->
+ gen_server:reply(PendingFrom, ok),
+ {stop, {timeout, "Port-program busy when trying to disconnect, "
+ "will be killed"},
+ State#state{pending_request = undefined, reply_to = undefined,
+ num_timeouts = State#state.num_timeouts + 1}};
+
+handle_info(timeout, State =
+ #state{state = connected,
+ pending_request = {_, PendingFrom}}) ->
+ gen_server:reply(PendingFrom, timeout),
+ %% The state variable reply_to should continue to have the value skip
+ {noreply, State#state{pending_request = undefined,
+ num_timeouts = State#state.num_timeouts + 1}};
+
+handle_info({Port, {exit_status, ?EXIT_SUCCESS}},
+ State = #state{erlang_port = Port, state = disconnecting}) ->
+ {noreply, State}; % Ignore as this is perfectly normal in this case
+
+handle_info({Port, {exit_status, Status}},
+ State = #state{erlang_port = Port}) ->
+ {stop, {port_exit, ?PORT_EXIT_REASON(Status)}, State};
+
+handle_info({'EXIT', Port, _}, State = #state{erlang_port = Port,
+ state = disconnecting}) ->
+ {noreply, State}; % Ignore as this is perfectly normal in this case
+
+handle_info({'EXIT', Port, Reason}, State = #state{erlang_port = Port}) ->
+ {stop, Reason, State};
+
+%%% If the owning process dies there is no reson to go on
+handle_info({'DOWN', _Ref, _Type, _Process, normal}, State) ->
+ {stop, normal, State#state{reply_to = undefined}};
+
+handle_info({'DOWN', _Ref, _Type, _Process, timeout}, State) ->
+ {stop, normal, State#state{reply_to = undefined}};
+
+handle_info({'DOWN', _Ref, _Type, Process, Reason}, State) ->
+ {stop, {stopped, {'EXIT', Process, Reason}},
+ State#state{reply_to = undefined}};
+
+%---------------------------------------------------------------------------
+%% Catch all - throws away unknown messages (This could happen by "accident"
+%% so we do not want to crash, but we make a log entry as it is an
+%% unwanted behaviour.)
+handle_info(Info, State) ->
+ Report = io_lib:format("ODBC: received unexpected info: ~p~n", [Info]),
+ error_logger:error_report(Report),
+ {noreply, State}.
+
+%%-------------------------------------------------------------------------
+%% terminate/2 and code_change/3
+%%--------------------------------------------------------------------------
+
+terminate({port_exit, _Reason}, State = #state{reply_to = undefined}) ->
+ %% Port program crashed
+ gen_tcp:close(State#state.odbc_socket),
+ gen_tcp:close(State#state.sup_socket),
+ ok;
+
+terminate(_Reason, State = #state{reply_to = undefined}) ->
+
+ catch gen_tcp:send(State#state.sup_socket,
+ [?SHUTDOWN, ?STR_TERMINATOR]),
+ catch gen_tcp:close(State#state.odbc_socket),
+ catch gen_tcp:close(State#state.sup_socket),
+ catch port_close(State#state.erlang_port),
+ ok;
+
+terminate(Reason, State = #state{reply_to = From}) ->
+ gen_server:reply(From, {error, connection_closed}),
+ terminate(Reason, State#state{reply_to = undefined}).
+
+%---------------------------------------------------------------------------
+code_change(_Vsn, State, _Extra) ->
+ {ok, State}.
+
+
+%%%========================================================================
+%%% Internal functions
+%%%========================================================================
+
+connect(ConnectionReferense, ConnectionStr, Options) ->
+ {C_AutoCommitMode, ERL_AutoCommitMode} =
+ connection_config(auto_commit, Options),
+ TimeOut = connection_config(timeout, Options),
+ {C_TraceDriver, _} = connection_config(trace_driver, Options),
+ {C_SrollableCursors, ERL_SrollableCursors} =
+ connection_config(scrollable_cursors, Options),
+ {C_TupleRow, _} =
+ connection_config(tuple_row, Options),
+ ODBCCmd =
+ [?OPEN_CONNECTION, C_AutoCommitMode, C_TraceDriver,
+ C_SrollableCursors, C_TupleRow, ConnectionStr],
+
+ %% Send request, to open a database connection, to the control process.
+ case call(ConnectionReferense,
+ {connect, ODBCCmd, ERL_AutoCommitMode, ERL_SrollableCursors},
+ TimeOut) of
+ ok ->
+ {ok, ConnectionReferense};
+ Error ->
+ Error
+ end.
+
+%%-------------------------------------------------------------------------
+odbc_send(Socket, Msg) -> %% Note currently all allowed messages are lists
+ NewMsg = Msg ++ [?STR_TERMINATOR],
+ ok = gen_tcp:send(Socket, NewMsg),
+ inet:setopts(Socket, [{active, once}]).
+
+%%--------------------------------------------------------------------------
+connection_config(Key, Options) ->
+ case lists:keysearch(Key, 1, Options) of
+ {value,{Key, on}} ->
+ {?ON, on};
+ {value,{Key, off}} ->
+ {?OFF, off};
+ {value,{Key, Value}} ->
+ Value;
+ _ ->
+ connection_default(Key)
+ end.
+
+%%--------------------------------------------------------------------------
+connection_default(auto_commit) ->
+ {?ON, on};
+
+connection_default(timeout) ->
+ ?DEFAULT_TIMEOUT;
+
+connection_default(tuple_row) ->
+ {?ON, on};
+
+connection_default(trace_driver) ->
+ {?OFF, off};
+
+connection_default(scrollable_cursors) ->
+ {?ON, on}.
+
+%%-------------------------------------------------------------------------
+call(ConnectionReference, Msg, Timeout) ->
+
+ Result = (catch gen_server:call(ConnectionReference,
+ {self(), Msg, Timeout}, infinity)),
+ case Result of
+ %% Normal case, the result from the port-program has directly
+ %% been forwarded to the client
+ Binary when binary(Binary) ->
+ decode(Binary);
+ timeout ->
+ exit(timeout);
+ {'EXIT', _} ->
+ {error, connection_closed};
+ %% At some occasions the erlang control process will have an
+ %% answer that was not directly received from the port-program.
+ Term ->
+ Term
+ end.
+
+%%-------------------------------------------------------------------------
+decode(Binary) ->
+ case binary_to_term(Binary) of
+ [ResultSet | []] ->
+ ResultSet;
+ param_badarg ->
+ exit({badarg, odbc, param_query, 'Params'});
+ MultipleResultSets_or_Other ->
+ MultipleResultSets_or_Other
+ end.
+
+%%-------------------------------------------------------------------------
+param_values(Params) ->
+ case Params of
+ [{_, Values} | _] ->
+ Values;
+ [{_, _, Values} | _] ->
+ Values
+ end.
+
+%%-------------------------------------------------------------------------
+fix_params({sql_integer, InOut, Values}) ->
+ {?USER_INT, fix_inout(InOut), [256 | Values]};
+fix_params({sql_smallint, InOut, Values}) ->
+ {?USER_SMALL_INT, fix_inout(InOut), [256 | Values]};
+fix_params({sql_tinyint, InOut, Values}) ->
+ {?USER_TINY_INT, fix_inout(InOut), [256 | Values]};
+fix_params({{sql_decimal, Precision, 0}, InOut,
+ Values}) when Precision >= 0, Precision =< 9 ->
+ {?USER_DECIMAL, Precision, 0, fix_inout(InOut), [256 | Values]};
+fix_params({{sql_decimal, Precision, Scale}, InOut, Values}) ->
+ {?USER_DECIMAL, Precision, Scale, fix_inout(InOut), Values};
+fix_params({{sql_numeric, Precision, 0}, InOut,
+ Values}) when Precision >= 0, Precision =< 9 ->
+ {?USER_NUMERIC, Precision, 0, fix_inout(InOut), [256 | Values]};
+fix_params({{sql_numeric, Precision, Scale}, InOut, Values}) ->
+ {?USER_NUMERIC, Precision, Scale, fix_inout(InOut), Values};
+fix_params({{sql_char, Max}, InOut, Values}) ->
+ NewValues =
+ case (catch
+ lists:map(fun(Str) -> Str ++ [?STR_TERMINATOR] end, Values)) of
+ Result ->
+ Result
+ end,
+ {?USER_CHAR, Max, fix_inout(InOut), NewValues};
+fix_params({{sql_varchar, Max}, InOut, Values}) ->
+ NewValues =
+ case (catch
+ lists:map(fun(Str) -> Str ++ [?STR_TERMINATOR] end, Values)) of
+ Result ->
+ Result
+ end,
+ {?USER_VARCHAR, Max, fix_inout(InOut), NewValues};
+fix_params({{sql_float, Precision}, InOut, Values}) ->
+ {?USER_FLOAT, Precision, fix_inout(InOut), Values};
+fix_params({sql_real, InOut, Values}) ->
+ {?USER_REAL, fix_inout(InOut), Values};
+fix_params({sql_double, InOut, Values}) ->
+ {?USER_DOUBLE, fix_inout(InOut), Values};
+fix_params({sql_bit, InOut, Values}) ->
+ {?USER_BOOLEAN, fix_inout(InOut), Values};
+%% default is IN %%%
+fix_params({Type, Values}) ->
+ fix_params({Type, in, Values}).
+
+fix_inout(in) ->
+ ?IN;
+fix_inout(out) ->
+ ?OUT;
+fix_inout(inout) ->
+ ?INOUT.
diff --git a/lib/odbc/src/odbc_app.erl b/lib/odbc/src/odbc_app.erl
new file mode 100644
index 0000000000..76d8ec1a8c
--- /dev/null
+++ b/lib/odbc/src/odbc_app.erl
@@ -0,0 +1,36 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1999-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+%%%----------------------------------------------------------------------
+%%% Purpose : The main application file of ODBC.
+%%%----------------------------------------------------------------------
+
+-module(odbc_app).
+
+-export([start/2, stop/1]).
+
+
+start(_Type, Name) ->
+ supervisor:start_link({local, odbc_sup}, odbc_sup, [Name]).
+
+
+stop([]) ->
+ ok.
+
diff --git a/lib/odbc/src/odbc_debug.erl b/lib/odbc/src/odbc_debug.erl
new file mode 100644
index 0000000000..a2e5e5dff3
--- /dev/null
+++ b/lib/odbc/src/odbc_debug.erl
@@ -0,0 +1,43 @@
+%%%-------------------------------------------------------------------
+%%% File : odbc_debug.erl
+%%% Author : Ingela Anderton Andin <[email protected]>
+%%% Description : Issuse standard tracing on an odbc connection process
+%%%
+%%% Created : 12 Dec 2003 by Ingela Anderton Andin <[email protected]>
+%%%-------------------------------------------------------------------
+-module(odbc_debug).
+
+-export([trace_odbc/2]).
+
+%%%========================================================================
+%%% Debug functions
+%%%========================================================================
+
+%%--------------------------------------------------------------------------
+%% trace_odbc(Process, OnOff, <Level>) -> ok
+%% Process - pid() | Name | {global, Name} | {Name, Node}
+%% OnOff - on | off
+%% Level - exported | all
+%% Description: Turns on tracing of messages sent and recived by
+%% the server <Process> and tracing on all, or all exported
+%% functions, according to level <Level>, in this module.
+%% Result will be printed on stdout.
+%%--------------------------------------------------------------------------
+trace_odbc(Process, OnOff) ->
+ trace_odbc(Process, OnOff, exported).
+
+trace_odbc(Process, on, exported) ->
+ dbg:tracer(),
+ dbg:tp(odbc, [{'_', [], [{return_trace}]}]),
+ dbg:p(Process, [call, m]),
+ ok;
+
+trace_odbc(Process, on, all) ->
+ dbg:tracer(),
+ dbg:tpl(odbc, [{'_', [], [{return_trace}]}]),
+ dbg:p(Process, [call, m]),
+ ok;
+
+trace_odbc(_Process, off, _Level) ->
+ dbg:stop(),
+ ok.
diff --git a/lib/odbc/src/odbc_internal.hrl b/lib/odbc/src/odbc_internal.hrl
new file mode 100644
index 0000000000..144e3cd176
--- /dev/null
+++ b/lib/odbc/src/odbc_internal.hrl
@@ -0,0 +1,164 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2002-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%
+%%
+
+%% Path to the c-program.
+-define(SERVERDIR, filename:nativename(
+ filename:join(code:priv_dir(odbc), "bin"))).
+
+%% Name of the C program
+-define(SERVERPROG, "odbcserver").
+
+%% Constats defining the command protocol between the erlang control
+%% process and the port program. These constants must also be defined
+%% in the same way in the port program.
+
+-define(OPEN_CONNECTION, 1).
+-define(CLOSE_CONNECTION, 2).
+-define(COMMIT_TRANSACTION, 3).
+-define(COMMIT, 4).
+-define(ROLLBACK, 5).
+-define(QUERY, 6).
+-define(SELECT_COUNT, 7).
+-define(SELECT_FIRST, 8).
+-define(SELECT_LAST, 9).
+-define(SELECT_NEXT, 10).
+-define(SELECT_PREV, 11).
+-define(SELECT, 12).
+-define(SELECT_RELATIVE, 13).
+-define(SELECT_ABSOLUTE, 14).
+-define(SELECT_N_NEXT, 15).
+-define(PARAM_QUERY, 16).
+-define(DESCRIBE, 17).
+-define(SHUTDOWN, 18).
+-define(LENGTH_INDICATOR_SIZE, 4).
+-define(INT_VALUE, 1).
+-define(STR_VALUE, 2).
+-define(ON, 1).
+-define(OFF, 2).
+-define(DUMMY_OFFSET, 0).
+
+
+%% Types of parameters given to param_query
+-define(USER_SMALL_INT, 1).
+-define(USER_INT, 2).
+-define(USER_DECIMAL, 3).
+-define(USER_NUMERIC, 4).
+-define(USER_CHAR, 5).
+-define(USER_VARCHAR, 6).
+-define(USER_FLOAT, 7).
+-define(USER_REAL, 8).
+-define(USER_DOUBLE, 9).
+-define(USER_BOOLEAN, 10).
+-define(USER_TINY_INT, 11).
+
+%% INPUT & OUTPUT TYPE
+-define(IN, 0).
+-define(OUT, 1).
+-define(INOUT, 2).
+
+%% EXIT CODES
+-define(EXIT_SUCCESS, 0). % As defined in c iso_stdlib
+-define(EXIT_FAILURE, 1). % As defined in c iso_stdlib
+-define(EXIT_ALLOC, 2).
+-define(EXIT_ENV, 3).
+-define(EXIT_CONNECTION, 4).
+-define(EXIT_FREE, 5).
+-define(EXIT_STDIN_HEADER, 6).
+-define(EXIT_STDIN_BODY, 7).
+-define(EXIT_BIN, 8).
+-define(EXIT_THREAD, 9).
+-define(EXIT_PARAM_ARRAY, 10).
+-define(EXIT_OLD_WINSOCK, 11).
+-define(EXIT_SOCKET_CONNECT, 12).
+-define(EXIT_SOCKET_SEND_HEADER, 13).
+-define(EXIT_SOCKET_SEND_BODY, 14).
+-define(EXIT_SOCKET_RECV_MSGSIZE,15).
+-define(EXIT_SOCKET_SEND_MSGSIZE,16).
+-define(EXIT_SOCKET_RECV_HEADER, 17).
+-define(EXIT_SOCKET_RECV_BODY, 18).
+-define(EXIT_COLS, 19).
+-define(EXIT_ROWS, 20).
+-define(EXIT_DESC, 21).
+-define(EXIT_BIND, 22).
+-define(EXIT_DRIVER_INFO, 23).
+
+%% Misc constants
+-define(DEFAULT_TIMEOUT, infinity).
+-define(STR_TERMINATOR, 0).
+-define(MAX_SEQ_TIMEOUTS, 10).
+
+%% Handling of C exit codes
+-define(ENCODE_EXIT_FUN,
+ (fun(?EXIT_SUCCESS) ->
+ normal_exit;
+ (?EXIT_FAILURE) ->
+ abnormal_exit;
+ (?EXIT_ALLOC) ->
+ memory_allocation_failed;
+ (?EXIT_ENV) ->
+ setting_of_environment_attributes_failed;
+ (?EXIT_CONNECTION) ->
+ setting_of_connection_attributes_faild;
+ (?EXIT_FREE) ->
+ freeing_of_memory_failed;
+ (?EXIT_STDIN_HEADER) ->
+ receiving_port_message_header_failed;
+ (?EXIT_STDIN_BODY) ->
+ receiving_port_message_body_failed;
+ (?EXIT_BIN) ->
+ retrieving_of_binary_data_failed;
+ (?EXIT_THREAD) ->
+ failed_to_create_thread;
+ (?EXIT_PARAM_ARRAY) ->
+ does_not_support_param_arrays;
+ (?EXIT_OLD_WINSOCK) ->
+ too_old_verion_of_winsock;
+ (?EXIT_SOCKET_CONNECT) ->
+ socket_connect_failed;
+ (?EXIT_SOCKET_SEND_HEADER) ->
+ socket_send_message_header_failed;
+ (?EXIT_SOCKET_SEND_BODY) ->
+ socket_send_message_body_failed;
+ (?EXIT_SOCKET_RECV_MSGSIZE) ->
+ socket_received_too_large_message;
+ (?EXIT_SOCKET_SEND_MSGSIZE) ->
+ too_large_message_in_socket_send;
+ (?EXIT_SOCKET_RECV_HEADER) ->
+ socket_receive_message_header_failed;
+ (?EXIT_SOCKET_RECV_BODY) ->
+ socket_receive_message_body_failed;
+ (?EXIT_COLS) ->
+ could_not_access_column_count;
+ (?EXIT_ROWS) ->
+ could_not_access_row_count;
+ (?EXIT_DESC) ->
+ could_not_access_table_description;
+ (?EXIT_BIND) ->
+ could_not_bind_data_buffers;
+ (?EXIT_DRIVER_INFO) ->
+ collecting_of_driver_information_faild;
+ (_) ->
+ killed
+ end)).
+
+-define(PORT_EXIT_REASON(EXIT_STATUS),
+ ?ENCODE_EXIT_FUN(EXIT_STATUS)).
diff --git a/lib/odbc/src/odbc_sup.erl b/lib/odbc/src/odbc_sup.erl
new file mode 100644
index 0000000000..66b3e4de7c
--- /dev/null
+++ b/lib/odbc/src/odbc_sup.erl
@@ -0,0 +1,43 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1999-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+-module(odbc_sup).
+
+-behaviour(supervisor).
+
+-export([init/1]).
+
+init([Name]) ->
+ RestartStrategy = simple_one_for_one,
+ MaxR = 0,
+ MaxT = 3600,
+ StartFunc = {odbc, start_link_sup, []},
+ Restart = temporary, % E.g. should not be restarted
+ Shutdown = 7000,
+ Modules = [odbc],
+ Type = worker,
+ ChildSpec = {Name, StartFunc, Restart, Shutdown, Type, Modules},
+ {ok, {{RestartStrategy, MaxR, MaxT}, [ChildSpec]}}.
+
+
+
+
+
+