From 2059bfd7fd324ff9389806ef893f31e647c5604e Mon Sep 17 00:00:00 2001 From: Alexey Romanov Date: Thu, 28 Oct 2010 10:14:04 +0400 Subject: [PATCH] Support for BLOBs --- include/sqlite3.hrl | 2 +- src/sqlite3_lib.erl | 44 ++++++++++++++++++++++++++++++------------- test/sqlite3_test.erl | 10 ++++++++++ 3 files changed, 42 insertions(+), 14 deletions(-) diff --git a/include/sqlite3.hrl b/include/sqlite3.hrl index 3fd668b..24b637d 100644 --- a/include/sqlite3.hrl +++ b/include/sqlite3.hrl @@ -20,4 +20,4 @@ -endif. -define(NULL_ATOM, null). --type(sql_value() :: number() | ?NULL_ATOM | iodata()). +-type(sql_value() :: number() | ?NULL_ATOM | iodata() | {blob, binary()}). diff --git a/src/sqlite3_lib.erl b/src/sqlite3_lib.erl index 7457d2e..1e2e521 100644 --- a/src/sqlite3_lib.erl +++ b/src/sqlite3_lib.erl @@ -11,7 +11,7 @@ %% API -export([col_type_to_atom/1]). --export([value_to_sql/1, value_to_sql_unsafe/1, sql_to_value/1, escape/1]). +-export([value_to_sql/1, value_to_sql_unsafe/1, sql_to_value/1, escape/1, bin_to_hex/1]). -export([write_value_sql/1, write_col_sql/1]). -export([create_table_sql/2, create_table_sql/3, drop_table_sql/1]). -export([write_sql/2, update_sql/4, update_set_sql/1, delete_sql/3]). @@ -34,6 +34,8 @@ col_type_to_string(double) -> "REAL"; col_type_to_string(real) -> "REAL"; +col_type_to_string(blob) -> + "BLOB"; col_type_to_string(String) when is_list(String) -> String. @@ -48,7 +50,9 @@ col_type_to_atom("INTEGER") -> col_type_to_atom("TEXT") -> text; col_type_to_atom("REAL") -> - double. + double; +col_type_to_atom("BLOB") -> + blob. %%-------------------------------------------------------------------- %% @spec value_to_sql_unsafe(Value :: sql_value()) -> iolist() @@ -65,11 +69,12 @@ col_type_to_atom("REAL") -> %%-------------------------------------------------------------------- -spec value_to_sql_unsafe(sql_value()) -> iolist(). value_to_sql_unsafe(X) -> - if - is_integer(X) -> integer_to_list(X); - is_float(X) -> float_to_list(X); - X == ?NULL_ATOM -> "NULL"; - true -> [$', X, $'] %% assumes no $' inside strings! + case X of + _ when is_integer(X) -> integer_to_list(X); + _ when is_float(X) -> float_to_list(X); + ?NULL_ATOM -> "NULL"; + {blob, Blob} -> ["x'", bin_to_hex(Blob), $']; + _ -> [$', X, $'] %% assumes no $' inside strings! end. %%-------------------------------------------------------------------- @@ -84,14 +89,14 @@ value_to_sql_unsafe(X) -> %%-------------------------------------------------------------------- -spec value_to_sql(sql_value()) -> iolist(). value_to_sql(X) -> - if - is_integer(X) -> integer_to_list(X); - is_float(X) -> float_to_list(X); - X == ?NULL_ATOM -> "NULL"; - true -> [$', escape(X), $'] + case X of + _ when is_integer(X) -> integer_to_list(X); + _ when is_float(X) -> float_to_list(X); + ?NULL_ATOM -> "NULL"; + {blob, Blob} -> ["x'", bin_to_hex(Blob), $']; + _ -> [$', escape(X), $'] end. - %%-------------------------------------------------------------------- %% @spec sql_to_value(String :: string()) -> sql_value() %% @doc @@ -144,6 +149,16 @@ write_col_sql(Cols) -> -spec escape(iodata()) -> iodata(). escape(IoData) -> re:replace(IoData, "'", "''", [global]). +%%-------------------------------------------------------------------- +%% @spec bin_to_hex(Binary :: binary()) -> binary() +%% +%% @doc Converts a plain binary to its hexadecimal encoding, to be +%% passed as a blob literal. +%% @end +%%-------------------------------------------------------------------- +-spec bin_to_hex(iodata()) -> binary(). +bin_to_hex(Binary) -> << <<(half_byte_to_hex(X)):8>> || <> <= Binary>>. + %%-------------------------------------------------------------------- %% @spec update_set_sql([{Column :: atom(), Value :: sql_value()}]) -> iolist() %% @doc @@ -322,6 +337,9 @@ map_intersperse(_Fun, [], _Sep) -> []; map_intersperse(Fun, [Elem], _Sep) -> [Fun(Elem)]; map_intersperse(Fun, [Head | Tail], Sep) -> [Fun(Head), Sep | map_intersperse(Fun, Tail, Sep)]. +half_byte_to_hex(X) when X < 10 -> $0 + X; +half_byte_to_hex(X) -> $a + X - 10. + -spec sql_number(string()) -> number() | {error, not_a_number}. sql_number(NumberStr) -> case string:to_integer(NumberStr) of diff --git a/test/sqlite3_test.erl b/test/sqlite3_test.erl index 24a3ffe..9ca7e1c 100644 --- a/test/sqlite3_test.erl +++ b/test/sqlite3_test.erl @@ -79,6 +79,16 @@ basic_functionality_test() -> sqlite3:drop_table(ct, user)), sqlite3:close(ct). +blob_test() -> + sqlite3:open(ct), + drop_table_if_exists(ct, blobs), + sqlite3:create_table(ct, blobs, [{blob_col, blob}]), + sqlite3:write(ct, blobs, [{blob_col, {blob, <<0,255,1,2>>}}]), + ?assertEqual( + [{columns, ["blob_col"]}, {rows, [{<<0,255,1,2>>}]}], + sqlite3:read_all(ct, blobs)), + sqlite3:close(ct). + select_many_records_test() -> sqlite3:open(ct), drop_table_if_exists(ct, many_records),