From a8d0290407a3e1c0a503075c0eaa57994a13346c Mon Sep 17 00:00:00 2001 From: Alexey Romanov Date: Tue, 12 Oct 2010 14:23:05 +0400 Subject: [PATCH] Functions to create tables with constraints --- src/sqlite3.erl | 37 +++++++++++++++++++++----- src/sqlite3_lib.erl | 63 +++++++++++++++++++++++++++++++++------------ 2 files changed, 77 insertions(+), 23 deletions(-) diff --git a/src/sqlite3.erl b/src/sqlite3.erl index ff6999d..cdb0015 100644 --- a/src/sqlite3.erl +++ b/src/sqlite3.erl @@ -17,7 +17,7 @@ -export([stop/0, close/1]). -export([sql_exec/1, sql_exec/2]). --export([create_table/2, create_table/3]). +-export([create_table/2, create_table/3, create_table/4]). -export([list_tables/0, list_tables/1, table_info/1, table_info/2]). -export([write/2, write/3]). -export([update/4, update/5]). @@ -167,9 +167,10 @@ create_table(Tbl, Options) -> ?MODULE:create_table(?MODULE, Tbl, Options). %%-------------------------------------------------------------------- -%% @spec create_table(Db :: atom(), Tbl :: atom(), TblInfo :: [{atom(), atom()}]) -> any() +%% @spec create_table(Db :: atom(), Tbl :: atom(), TblInfo) -> any() +%% TblInfo = [{atom(), atom()}] %% @doc -%% Creates the Tbl table in Db using TblInfo as the table structure. +%% Creates the Tbl table in Db using Columns as the table structure. %% The table structure is a list of {column name, column type} pairs. %% e.g. [{name, text}, {age, integer}] %% @@ -177,8 +178,25 @@ create_table(Tbl, Options) -> %% @end %%-------------------------------------------------------------------- -spec create_table(atom(), atom(), [{atom(), atom()}]) -> any(). -create_table(Db, Tbl, Options) -> - gen_server:call(Db, {create_table, Tbl, Options}). +create_table(Db, Tbl, Columns) -> + gen_server:call(Db, {create_table, Tbl, Columns}). + +%%-------------------------------------------------------------------- +%% @spec create_table(Db :: atom(), Tbl :: atom(), TblInfo, Constraints) -> any() +%% Columns = [{atom(), atom()}] +%% Constraints = [term()] +%% @doc +%% Creates the Tbl table in Db using Columns as the table structure and +%% Constraints as table constraints. +%% The table structure is a list of {column name, column type} pairs. +%% e.g. [{name, text}, {age, integer}] +%% +%% Returns the result of the create table call. +%% @end +%%-------------------------------------------------------------------- +-spec create_table(atom(), atom(), [{atom(), atom()}], [any()]) -> any(). +create_table(Db, Tbl, Columns, Constraints) -> + gen_server:call(Db, {create_table, Tbl, Columns, Constraints}). %%-------------------------------------------------------------------- %% @spec list_tables() -> [atom()] @@ -475,8 +493,13 @@ handle_call({create_function, FunctionName, Function}, _From, #state{port = Port % SQL Injection warning Reply = exec(Port, {create_function, FunctionName, Function}), {reply, Reply, State}; -handle_call({create_table, Tbl, Options}, _From, #state{port = Port} = State) -> - SQL = sqlite3_lib:create_table_sql(Tbl, Options), +handle_call({create_table, Tbl, Columns}, _From, #state{port = Port} = State) -> + SQL = sqlite3_lib:create_table_sql(Tbl, Columns), + Cmd = {sql_exec, SQL}, + Reply = exec(Port, Cmd), + {reply, Reply, State}; +handle_call({create_table, Tbl, Columns, Constraints}, _From, #state{port = Port} = State) -> + SQL = sqlite3_lib:create_table_sql(Tbl, Columns, Constraints), Cmd = {sql_exec, SQL}, Reply = exec(Port, Cmd), {reply, Reply, State}; diff --git a/src/sqlite3_lib.erl b/src/sqlite3_lib.erl index 7ed4ad8..47eaa76 100644 --- a/src/sqlite3_lib.erl +++ b/src/sqlite3_lib.erl @@ -13,19 +13,19 @@ -export([col_type_to_atom/1]). -export([value_to_sql/1, value_to_sql_unsafe/1, sql_to_value/1, escape/1]). -export([write_value_sql/1, write_col_sql/1]). --export([create_table_sql/2, write_sql/2, read_sql/3, delete_sql/3, drop_table/1]). --export([update_sql/4, update_set_sql/1]). --export([read_sql/4, read_cols_sql/1]). +-export([create_table_sql/2, create_table_sql/3, drop_table/1]). +-export([write_sql/2, update_sql/4, update_set_sql/1, delete_sql/3]). +-export([read_sql/3, read_sql/4, read_cols_sql/1]). %%==================================================================== %% API %%==================================================================== %%-------------------------------------------------------------------- -%% @spec col_type_to_string(Type :: atom()) -> string() +%% @spec col_type_to_string(Type :: atom() | string()) -> string() %% @doc Maps sqlite3 column type. %% @end %%-------------------------------------------------------------------- --spec col_type_to_string(atom()) -> string(). +-spec col_type_to_string(atom() | string()) -> string(). col_type_to_string(integer) -> "INTEGER"; col_type_to_string(text) -> @@ -33,7 +33,9 @@ col_type_to_string(text) -> col_type_to_string(double) -> "REAL"; col_type_to_string(real) -> - "REAL". + "REAL"; +col_type_to_string(String) when is_list(String) -> + String. %%-------------------------------------------------------------------- %% @spec col_type_to_atom(Type :: string()) -> atom() @@ -168,7 +170,7 @@ read_cols_sql(Columns) -> map_intersperse(fun atom_to_list/1, Columns, ", "). %%-------------------------------------------------------------------- -%% @spec create_table_sql(Tbl :: atom(), [{Column, Type}]) -> iolist() +%% @spec create_table_sql(Tbl :: atom(), ColumnData) -> iolist() %% Tbl = atom() %% ColumnData = {Column, Type} | {Column, Type, Constraints} %% Column = atom() @@ -179,15 +181,26 @@ read_cols_sql(Columns) -> %%-------------------------------------------------------------------- -spec create_table_sql(atom(), [{atom(), atom()} | {atom(), atom(), [any()]}]) -> iolist(). create_table_sql(Tbl, Columns) -> - ColumnNameTypeFun = - fun({Name0, Type0}) -> - [atom_to_list(Name0), " ", col_type_to_string(Type0)]; - ({Name0, Type0, Constraints}) -> - [atom_to_list(Name0), " ", col_type_to_string(Type0), - " " | map_intersperse(fun constraint_sql/1, Constraints, " ")] - end, ["CREATE TABLE ", atom_to_list(Tbl), " (", - map_intersperse(ColumnNameTypeFun, Columns, ", "), ");"]. + map_intersperse(fun column_sql_for_create_table/1, Columns, ", "), ");"]. + +%%-------------------------------------------------------------------- +%% @spec create_table_sql(Tbl :: atom(), ColumnData, TableConstraints) -> iolist() +%% Tbl = atom() +%% ColumnData = {Column, Type} | {Column, Type, Constraints} +%% Column = atom() +%% Type = atom() +%% Constraints = [any()] +%% TableConstraints = [any()] +%% @doc Generates a table create stmt in SQL. +%% @end +%%-------------------------------------------------------------------- +-spec create_table_sql(atom(), [{atom(), atom()} | {atom(), atom(), [any()]}], [any()]) -> iolist(). +create_table_sql(Tbl, Columns, TblConstraints) -> + ["CREATE TABLE ", atom_to_list(Tbl), " (", + map_intersperse(fun column_sql_for_create_table/1, Columns, ", "), ", ", + map_intersperse(fun table_constraint_sql/1, TblConstraints, ", "), + ");"]. %%-------------------------------------------------------------------- %% @spec update_sql(Tbl, Key, Value, Data) -> iolist() @@ -307,7 +320,13 @@ sql_string(StringWithEscapedQuotes) -> -spec sql_blob(string()) -> string(). sql_blob(Blob) -> Blob. --spec constraint_sql([any()]) -> iolist(). +column_sql_for_create_table({Name, Type}) -> + [atom_to_list(Name), " ", col_type_to_string(Type)]; +column_sql_for_create_table({Name, Type, Constraints}) -> + [atom_to_list(Name), " ", col_type_to_string(Type), + " " | map_intersperse(fun constraint_sql/1, Constraints, " ")]. + +-spec constraint_sql(any()) -> iolist(). constraint_sql(Constraint) -> case Constraint of primary_key -> "PRIMARY KEY"; @@ -317,6 +336,18 @@ constraint_sql(Constraint) -> {default, DefaultValue} -> ["DEFAULT ", value_to_sql(DefaultValue)] end. +-spec table_constraint_sql(any()) -> iolist(). +table_constraint_sql(TableConstraint) -> + case TableConstraint of + {primary_key, Columns} -> ["PRIMARY KEY(", map_intersperse(fun indexed_column_sql/1, Columns, ", "), ")"]; + {unique, Columns} -> ["UNIQUE(", map_intersperse(fun indexed_column_sql/1, Columns, ", "), ")"] + %% TODO: foreign key + end. + +indexed_column_sql({ColumnName, asc}) -> [atom_to_list(ColumnName), " ASC"]; +indexed_column_sql({ColumnName, desc}) -> [atom_to_list(ColumnName), " DESC"]; +indexed_column_sql(ColumnName) -> atom_to_list(ColumnName). + %%-------------------------------------------------------------------- %% @type sql_value() = number() | 'null' | iodata(). %%