diff --git a/calexium_lib/database/alter_table.anubis b/calexium_lib/database/alter_table.anubis new file mode 100644 index 0000000..8dba8a6 --- /dev/null +++ b/calexium_lib/database/alter_table.anubis @@ -0,0 +1,306 @@ + + *Project* Anubis + + *Title* SQLite Alter table function + + *Copyright* Copyright (c) Cédric Ricard 2007. + + + + *Author* Cédric Ricard + + *Created* 2007 02 17 + *Satus* Released + *Compatibility* 1.7.10 + +read tools/basis.anubis +read data_base/db_tools.anubis +read data_base/alter_table.anubis +read system/logger.anubis +read system/string.anubis + + + +public define Maybe(One) + alter_table + ( + Database db, + String tableName, + String newTableQuery, + Logger log + ). + + + + + + --- That's all for the public part ! -------------------------------------------------- + +define String + to_String + ( + DbEntityType type + ) = + if type is + { + table then "table", + view then "view", + index then "index", + trigger then "trigger" + }. + +define One + print_list + ( + List(String) l + ) = + if l is + { + [] then print(" <<<\n"), + [h . t] then print(h + ", "); print_list(t) + }. + +define Maybe(One) + my_sql_query + ( + Database db, + String sql, + Logger log + ) = + if sql_query(db, sql) is + { + error(sql_error) then logError(log, "SQL ERROR: " + sql_error.text + "\n ...executing query [" + sql + "]"); failure, + ok(_,_,_) then success(unique) + }. + +define Maybe(String) + get_original_query + ( + Database db, + String dbName, // alias for attached database, 'main' for main database. + DbEntityType object_type, + String tableName, + Logger log + ) = + if sql_query(db, "SELECT sql FROM " + dbName + ".sqlite_master where (type='"+to_String(object_type)+"') and tbl_name='" + tableName + "'") is + { + error(sql_error) then logError(log, "alter_table/get_original_query ERROR '" + sql_error.text + "'"); failure, + ok(_,table_cursor,_) then + if table_cursor(unique) is + { + error(sql_error) then logError(log, "alter_table/get_original_query ERROR '" + sql_error.text + "'"); failure, + no_more_row then failure, //can't find the table in sqlite_master, because the table is new + row(current) then success(text(current)(0)) + } + }. + +define Bool + has_rows + ( + Database db, + String tableName, + Logger log + ) = + if sql_query(db, "SELECT count(*) FROM " + tableName) is + { + error(sql_error) then logError(log, "has_rows query ERROR '" + sql_error.text + "'"); false, + ok(_,table_cursor,_) then + if table_cursor(unique) is + { + error(sql_error) then logError(log, "has_row run ERROR '" + sql_error.text + "'"); false, + no_more_row then false, + row(current) then db_integer(current)(0) /= 0 + } + }. + +define List(String) + alter_table_extract_columns_list_sub + ( + Int -> SQLite3Datum row, + Int colIndex + ) = + if row(colIndex) is text(name) then [name . alter_table_extract_columns_list_sub(row, colIndex+1)] + else []. + +define List(String) + alter_table_extract_columns_list + ( + SQLite3HeadersOrRow -> SQLite3Row table_cursor, + Logger log + ) = + if table_cursor(headers) is + { + error(sql_error) then logError(log, "alter_table_extract_columns_list ERROR '" + sql_error.text + "'"); [], + no_more_row then [], + row(headers) then alter_table_extract_columns_list_sub(headers, 0) + }. + +define String + alter_table_extract_column_string + ( + One -> DbRow table_cursor, + List(String) oldColumns, + Logger log + ) = + if table_cursor(unique) is + { + error(sql_error) then logError(log, "alter_table_extract_columns_list ERROR '" + sql_error.text + "'"); "", + no_more_row then /*logDebug(log, "Extract columns answers 'no more row'");*/ "", + row(row) then + if row(1) is db_text(name) then + with test = (String name2) |-> logDebug(log, "name: " + name + " / name2: " + name2); if name = name2 then true else false, + colString = alter_table_extract_column_string(table_cursor, oldColumns, log), + if find_element(oldColumns, test) is + { + failure then colString, + success(_) then logDebug(log, "found: " + name); + if is_empty(colString) then name else name + ", " + colString + } + else "" + }. + +// false = different, true = same +define Bool + compare_creation_queries + ( + String tableName, + String query1, + String query2, + ) = + if find(tableName, query1, 0) is + { + failure then false, + success(p1) then + if find(tableName, query2, 0) is + { + failure then false, + success(p2) then + if sub_string(query1, p1, length(query1) - p1) is + { + failure then false, + success(s1) then + if sub_string(query2, p2, length(query2) - p2) is + { + failure then false, + success(s2) then s1 = s2 + } + } + } + }. + +public define Maybe(One) + alter_table + ( + Database db, + String dbName, // alias for attached database, 'main' for main database. + String tableName, + String newTableQuery, + Logger log + ) = + with fullTableName = dbName + "." + tableName, + if get_original_query(db, dbName, table, tableName, log) is + { + failure then + logInfo(log, "Creating table '" + fullTableName + "'... "); + if sql_query(db, newTableQuery) is error(sql_error) + then logError(log, "ERROR : " + sql_error.text); failure + else logInfo(log, " --> ok."); success(unique), + success(originalTableQuery) then + if compare_creation_queries(tableName, originalTableQuery, newTableQuery) = false then + logInfo(log, "Updating table '" + fullTableName + "' on database... "); + println(originalTableQuery); + println(newTableQuery); + if has_rows(db, fullTableName, log) is + { + false then + if my_sql_query(db, "DROP TABLE " + fullTableName, log) is failure + then failure + else if my_sql_query(db, newTableQuery, log ) is + { + failure then failure, + success(_) then logInfo(log, " --> ok."); success(unique) + }, + true then + with tempNameTable = tableName + "_temp", + forget(sql_query(db, "DROP TABLE " + tempNameTable)); + if my_sql_query(db, "CREATE TEMP TABLE " + tempNameTable + " AS SELECT * from " + fullTableName, log) is failure then failure else + if my_sql_query(db, "DROP TABLE " + fullTableName, log) is failure then failure else + if my_sql_query(db, newTableQuery , log) is failure then failure else + if sql_query(db, "SELECT * FROM " + tempNameTable) is + { + error(sql_error) then logError(log, "alter_table: select * tempTable ERROR '" + sql_error.text + "'"); failure, + ok(get_cols,table_cursor2,_) then + with oldColumns = get_cols(unique), + //print_list(oldColumns); + if sql_query(db, "PRAGMA " + dbName + ".table_info(" + tableName + ")") is + { + error(sql_error) then logError(log, "alter_table: pragma table_info() ERROR '" + sql_error.text + "'"); failure, + ok(_,table_cursor3,_) then + with newColumnsStr = alter_table_extract_column_string(table_cursor3, oldColumns, log), + sql = "INSERT INTO " + fullTableName + " (" + newColumnsStr + ") SELECT " + newColumnsStr + " FROM " + tempNameTable, + if sql_query(db, sql) is + { + error(sql_error) then logError(log, "alter_table ERROR '" + sql_error.text + "'\n\tquery = [" + sql + "]"); failure, + ok(_,_,_) then logInfo(log, " --> ok."); success(unique) + } + } + } + } + else + success(unique) + }. + +public define Maybe(One) + alter_table + ( + Database db, + String tableName, + String newTableQuery, + Logger log + ) = + alter_table(db, "main", tableName, newTableQuery, log). + + +public define Maybe(One) + alter_view + ( + Database db, + String dbName, // alias for attached database, 'main' for main database. + String viewName, + String newViewQuery, + Logger log + ) = + with fullViewName = dbName + "." + viewName, + if get_original_query(db, dbName, view, viewName, log) is + { + failure then + logInfo(log, "Creating view '" + fullViewName + "'... "); + if sql_query(db, newViewQuery) is error(sql_error) + then logError(log, "ERROR : " + sql_error.text); failure + else logInfo(log, " --> ok."); success(unique), + success(originalViewQuery) then + if compare_creation_queries(viewName, originalViewQuery, newViewQuery) = false then + logInfo(log, "Updating view '" + fullViewName + "' on database... "); + println(originalViewQuery); + println(newViewQuery); + if my_sql_query(db, "DROP VIEW " + fullViewName, log) is failure + then failure + else if my_sql_query(db, newViewQuery, log ) is + { + failure then failure, + success(_) then logInfo(log, " --> ok."); success(unique) + } + else + success(unique) + }. + +public define Maybe(One) + alter_view + ( + Database db, + String viewName, + String newViewQuery, + Logger log + ) = + alter_view(db, "main", viewName, newViewQuery, log). + diff --git a/calexium_lib/database/migration.anubis b/calexium_lib/database/migration.anubis index f89bc19..f770208 100644 --- a/calexium_lib/database/migration.anubis +++ b/calexium_lib/database/migration.anubis @@ -10,10 +10,10 @@ read tools/basis.anubis read system/files.anubis read system/logger.anubis read system/string.anubis -read data_base/alter_table.anubis read data_base/db_tools.anubis read calexium_lib/database/db_utils.anubis +read calexium_lib/database/alter_table.anubis read calexium_lib/net_services_protocols/logger_service.anubis read mf_constants.anubis @@ -60,14 +60,6 @@ public define Int }, compare(split(version1, '.'), split(version2, '.')). -public define One - update_version_settings - ( - Database db, - String version - )= - update_settings(db, "MF_version", version). - public define Maybe(One) backup_database ( @@ -88,7 +80,7 @@ public define Maybe(One) // } // else false, - if should_copy then + //if should_copy then with dir_save = db_path + dir_save_database, if (Maybe(String))make_directory(dir_save) is { @@ -119,7 +111,7 @@ public define Maybe(One) }, copy_db([main_db_name . other_db_names]) } - else success(unique) + //else success(unique) . @@ -144,7 +136,8 @@ define Maybe(One) String current_version, String db_ver, List(Migration) migrations, - Maybe(Migration) need_to_create_table // success in case of new DB or when no migration have been done + Maybe(Migration) need_to_create_table, // success in case of new DB or when no migration have been done + (Database, String) -> One update_db_version, ) = if migrations is { @@ -152,13 +145,13 @@ define Maybe(One) if need_to_create_table is { failure then - update_version_settings(db, current_version); + update_db_version(db, current_version); success(unique), success(last) then if last is migration(_, _, create_tables, create_indexes) then if create_tables(db, logger) is success(_) then if create_indexes(db, logger) is success(_) then - update_version_settings(db, current_version); + update_db_version(db, current_version); success(unique) else failure @@ -172,9 +165,9 @@ define Maybe(One) if migrate_to(db, logger) is failure then failure else - do_migration_loop(db, logger, current_version, version, t, failure) + do_migration_loop(db, logger, current_version, version, t, failure, update_db_version) else - do_migration_loop(db, logger, current_version, db_ver, t, success(h)), + do_migration_loop(db, logger, current_version, db_ver, t, success(h), update_db_version), }. @@ -185,17 +178,17 @@ public define Maybe(One) Database db, Logger logger, String current_version, - (Database) |-> String get_db_version, - (Database, String) |-> One update_db_version, + (Database) -> String get_db_version, + (Database, String) -> One update_db_version, List(Migration) all_migrations, )= if is_empty_database(db) then // new empty database logInfo(logger, "No database found. Creating a new one..."); - do_migration_loop(db, logger, current_version, "", [], last(all_migrations)) + do_migration_loop(db, logger, current_version, "", [], last(all_migrations), update_db_version) else ( - with db_version = get_version_settings(db), - do_migration_loop(db, logger, current_version, db_version, all_migrations, failure) + with db_version = get_db_version(db), + do_migration_loop(db, logger, current_version, db_version, all_migrations, failure, update_db_version) ). public define Maybe(One) diff --git a/calexium_lib/database/migration_common.anubis b/calexium_lib/database/migration_common.anubis index b2f1c0f..c9d2d3e 100644 --- a/calexium_lib/database/migration_common.anubis +++ b/calexium_lib/database/migration_common.anubis @@ -9,8 +9,8 @@ read tools/basis.anubis read system/logger.anubis read data_base/db_tools.anubis -read data_base/alter_table.anubis -read data_base/sqlite_foreign_key.anubis +read alter_table.anubis +read sqlite_foreign_key.anubis read calexium_lib/database/db_utils.anubis read calexium_lib/net_services_protocols/logger_service.anubis @@ -31,7 +31,7 @@ public define Bool String dbName, String table_name )= - if sql_query_timeout(db, "SELECT * FROM " + dbName + ".sqlite_master WHERE type ='table' AND name = @table_name", [bind_String("@table_name", table_name)], "is_table_exists("+table_name+")") is + if sql_query_timeout(db, "SELECT * FROM " + dbName + ".sqlite_master WHERE type ='table' AND name = :table_name", [db_bind("table_name", db_text(table_name))], "is_table_exists("+table_name+")") is { error(_) then false, //error in the SQL request ok(_, cursor, _) then diff --git a/calexium_lib/database/sqlite_foreign_key.anubis b/calexium_lib/database/sqlite_foreign_key.anubis new file mode 100644 index 0000000..d26c0cf --- /dev/null +++ b/calexium_lib/database/sqlite_foreign_key.anubis @@ -0,0 +1,149 @@ + + *Project* Anubis + + *Title* SQLite Foreign Key functions + + *Copyright* Copyright (c) Cédric Ricard 2007. + + + + *Author* Cédric Ricard + + *Created* 2007 12 21 + *Satus* Released + *Compatibility* 1.8.4 + +read tools/basis.anubis +read data_base/sqlite.anubis +read data_base/db_tools.anubis +read system/logger.anubis + + + +define Maybe(One) + my_sql_query + ( + Database db, + String sql, + Logger log + ) = + if sql_query(db, sql) is + { + error(sql_error) then logError(log, "SQL ERROR: " + sql_error.text + "\n ...executing query [" + sql + "]"); failure, + ok(_,_,_) then success(unique) + }. + +public define Maybe(One) + alter_trigger + ( + Database db, + String trigger_name, + String sql, + Logger log + ) = + if sql_query(db, "SELECT sql FROM sqlite_master WHERE type = 'trigger' AND name = " + db_make_sql_string(trigger_name)) is + { + error(sql_error) then logError(log, "SQL ERROR: " + sql_error.text + "\n ...looking for trigger named [" + trigger_name + "]"); failure, + ok(_,cursor,_) then + if cursor(unique) is + { + error(sql_error) then logError(log, "alter_trigger ERROR '" + sql_error.text + "'"); failure, + no_more_row then my_sql_query(db, sql, log), + row(current) then + with old_sql = text(current)(0), + if old_sql /= sql then + logDebug(log, "Old trigger was:\n" + old_sql + "\n"); + logDebug(log, "New trigger is:\n" + sql + "\n"); + logInfo(log, "Updating trigger '" + trigger_name + "'..."); + forget(my_sql_query(db, "DROP TRIGGER [" + trigger_name + "]", log)); + if my_sql_query(db, sql, log) is success(_) then logInfo (log, " --> ok."); success(unique) + else logError(log, " --> error!"); failure + else + success(unique) + } + }. + + +public type FK_Null: + null, + not_null. + +public type FK_Cascade: + delete_in_cascade, + block_if_children. + +public define Maybe(One) + make_foreign_key + ( + Database db, + String table_name, + String field_name, + String foreign_table_name, + String foreign_field_name, + FK_Null fk_null, + FK_Cascade fk_cascade, + Logger log + ) = + with constrait_name = table_name + "__" + field_name + "__" + foreign_table_name, + sql_create = + "CREATE TRIGGER [fki__" + constrait_name + "] \n" + + "BEFORE INSERT ON [" + table_name + "] \n" + + "FOR EACH ROW BEGIN \n" + + " SELECT RAISE(ROLLBACK, 'insert on table \"" + table_name + "\" violates foreign key constraint \"fki__" + constrait_name + "\"') \n" + + " WHERE " + (if fk_null is null then " NEW.[" + field_name + "] IS NOT NULL AND \n" else "\n") + + " (SELECT [" + foreign_field_name + "] FROM [" + foreign_table_name + "] WHERE [" + foreign_field_name + "] = NEW.[" + field_name + "]) IS NULL; \n" + + "END", + sql_update = + "CREATE TRIGGER [fku__" + constrait_name + "] \n" + + "BEFORE UPDATE ON [" + table_name + "] \n" + + "FOR EACH ROW BEGIN \n" + + " SELECT RAISE(ROLLBACK, 'update on table \"" + table_name + "\" violates foreign key constraint \"fku__" + constrait_name + "\"') \n" + + " WHERE " + (if fk_null is null then " NEW.[" + field_name + "] IS NOT NULL AND \n" else "\n") + + " (SELECT [" + foreign_field_name + "] FROM [" + foreign_table_name + "] WHERE [" + foreign_field_name + "] = NEW.[" + field_name + "]) IS NULL; \n" + + "END", + sql_delete = + "CREATE TRIGGER [fkd__" + constrait_name + "] \n" + + "BEFORE DELETE ON [" + foreign_table_name + "] \n" + + "FOR EACH ROW BEGIN \n" + + if fk_cascade is { + delete_in_cascade then + " DELETE from [" + table_name + "] WHERE [" + field_name + "] = OLD.[" + foreign_field_name + "];\n", + block_if_children then + " SELECT RAISE(ROLLBACK, 'delete on table \"" + foreign_table_name + "\" violates foreign key constraint \"fku__" + constrait_name + "\"') \n" + + " WHERE (SELECT [" + field_name + "] FROM [" + table_name + "] WHERE [" + field_name + "] = OLD.[" + foreign_field_name + "]) IS NOT NULL; \n" + } + + "END", + if alter_trigger(db, "fki__" + constrait_name, sql_create, log) is failure + then failure + else if alter_trigger(db, "fku__" + constrait_name, sql_update, log) is failure + then failure + else if alter_trigger(db, "fkd__" + constrait_name, sql_delete, log) is failure + then failure + else + success(unique). + + + +public define Maybe(One) + make_foreign_key + ( + Database db, + String table_name, + String field_name, + String foreign_table_name, + String foreign_field_name, + Bool field_can_be_null, + Bool delete, //delete_in_cascade ? + Logger log + ) = + make_foreign_key + ( + db, + table_name, + field_name, + foreign_table_name, + foreign_field_name, + if field_can_be_null then null else not_null, + if delete then delete_in_cascade else block_if_children, + log + ). -- libgit2 0.21.4