diff --git a/src/com/michaldabski/msqlite/DataTypes.java b/src/com/michaldabski/msqlite/DataTypes.java index fed5f98..b260f5c 100644 --- a/src/com/michaldabski/msqlite/DataTypes.java +++ b/src/com/michaldabski/msqlite/DataTypes.java @@ -5,6 +5,7 @@ public class DataTypes { // Class field types public final static int + TYPE_NA = -1, TYPE_OTHER = 100, TYPE_STRING = 101, TYPE_COLLECTION = 102, diff --git a/src/com/michaldabski/msqlite/MSQLiteOpenHelper.java b/src/com/michaldabski/msqlite/MSQLiteOpenHelper.java index a30e663..12affbf 100644 --- a/src/com/michaldabski/msqlite/MSQLiteOpenHelper.java +++ b/src/com/michaldabski/msqlite/MSQLiteOpenHelper.java @@ -2,10 +2,8 @@ import java.util.ArrayList; import java.util.Collection; -import java.util.HashMap; import java.util.HashSet; import java.util.List; -import java.util.Map; import android.annotation.TargetApi; import android.content.ContentValues; @@ -16,7 +14,6 @@ import android.database.sqlite.SQLiteDatabase.CursorFactory; import android.database.sqlite.SQLiteOpenHelper; import android.os.Build; -import android.text.TextUtils; import android.util.Log; import com.michaldabski.msqlite.models.Table; @@ -46,30 +43,32 @@ public MSQLiteOpenHelper(Context context, String name, CursorFactory factory, // Static methods public static void createTable(SQLiteDatabase database, Class type, boolean ifNotExist) { - database - .execSQL(new CreateTable(type).setIF_NOT_EXIST(ifNotExist).build()); + createTable(database, new Table(type), ifNotExist); } - private static Map getCreateTableStatements(SQLiteDatabase database) + public static void createTable(SQLiteDatabase database, Table table, boolean ifNotExist) { - final int COLUMN_NAME = 0, - COLUMN_SQL = 1; - Cursor c = database.rawQuery("SELECT name, sql FROM SQLITE_MASTER WHERE type=?", new String [] {"table"}); - Map result = new HashMap(c.getCount()); - while (c.moveToNext()) - { - result.put(c.getString(COLUMN_NAME), c.getString(COLUMN_SQL)); - } - c.close(); - return result; + database + .execSQL(new CreateTable(table).setIF_NOT_EXIST(ifNotExist).build()); } private static void upgradeTable(SQLiteDatabase database, Table table) { - Cursor cur = database.rawQuery("PRAGMA table_info("+table.getName()+");", null); - // TODO: Compare columns - // [cid, name, type, notnull, dflt_value, pk] - Log.i(table.getName(), "table upgraded"); + Cursor cursor = database.rawQuery("PRAGMA table_info("+table.getName()+");", null); + if (cursor.getCount() == 0) + { + createTable(database, table, false); + Log.i("DatabaseUpgrade", "table created: "+table.getName()); + } + else + { + Table currentDatabaseTable = Table.fromCursor(table.getName(), cursor); + for (String sql : Table.upgradeTable(currentDatabaseTable, table)) + { + database.execSQL(sql); + Log.i("DatabaseUpgrade", "table altered. Query: "+sql); + } + } } public void upgradeDatabase() @@ -125,23 +124,7 @@ public void onCreate(SQLiteDatabase db) @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - for (Class c : classes) - { - dropTable(db, c, true); - createTable(db, c, false); - } - // TODO detect differences between tables and modify table instead of re-creating - } - - @Override - public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) - { - for (Class c : classes) - { - dropTable(db, c, true); - createTable(db, c, false); - } - // TODO detect differences between tables and modify table instead of re-creating + upgradeDatabase(db); } /** diff --git a/src/com/michaldabski/msqlite/models/Column.java b/src/com/michaldabski/msqlite/models/Column.java index 5bdb318..e1afda7 100644 --- a/src/com/michaldabski/msqlite/models/Column.java +++ b/src/com/michaldabski/msqlite/models/Column.java @@ -1,10 +1,8 @@ package com.michaldabski.msqlite.models; -import java.io.NotSerializableException; -import java.io.Serializable; import java.lang.reflect.Field; -import java.util.Locale; +import android.database.Cursor; import android.util.Log; import com.michaldabski.msqlite.Annotations.ColumnName; @@ -30,25 +28,48 @@ public class Column dataType, // table.field uniqueName; - - protected final Class fieldClass; private final int fieldType; - public Column(Field field) + public static Column fromCursor(Cursor cursor, Table table) + { + final int FIELD_NAME = 1, + FIELD_TYPE = 2, + FIELD_NOT_NULL = 3, + FIELD_DEF_VALUE = 4, + FIELD_PK = 5; + String name = cursor.getString(FIELD_NAME); + Column column = new Column(null, cursor.getInt(FIELD_NOT_NULL)==1, cursor.getInt(FIELD_PK)==1, name, + cursor.getString(FIELD_TYPE), table.getName()+"."+name, DataTypes.TYPE_NA); + + return column; + } + + private Column(Field field, boolean nOT_NULL, boolean pRIMARY_KEY, + String name, String dataType, String uniqueName, + int fieldType) + { + this.field = field; + NOT_NULL = nOT_NULL; + PRIMARY_KEY = pRIMARY_KEY; + this.name = name; + this.dataType = dataType; + this.uniqueName = uniqueName; + this.fieldType = fieldType; + } + + public Column(Field field, Table table) { this.field =field; if (field.isAnnotationPresent(ColumnName.class)) this.name = field.getAnnotation(ColumnName.class).value(); else this.name = field.getName(); - this.fieldClass = field.getType(); - - this.uniqueName = String.format("%s.%s", field.getDeclaringClass().getSimpleName(), name); + this.uniqueName = String.format("%s.%s", table.getName(), name); - fieldType = DataTypes.getFieldType(fieldClass); + fieldType = DataTypes.getFieldType(field.getType()); if (fieldType == DataTypes.TYPE_OTHER) { - Log.w("MSQLite", fieldClass.getSimpleName()+" is not supported as a table field yet. Consider making this field transient."); + Log.w("MSQLite", field.getType().getSimpleName()+" is not supported as a table field yet. Consider making this field transient."); dataType = null; return; } @@ -171,6 +192,11 @@ public int getFieldType() return fieldType; } + public boolean isPRIMARY_KEY() + { + return PRIMARY_KEY; + } + public void setValueFromString(Object object, String value) throws IllegalArgumentException { try diff --git a/src/com/michaldabski/msqlite/models/Table.java b/src/com/michaldabski/msqlite/models/Table.java index 23d3fd7..24155ee 100644 --- a/src/com/michaldabski/msqlite/models/Table.java +++ b/src/com/michaldabski/msqlite/models/Table.java @@ -29,6 +29,59 @@ public class Table protected final List columns; protected final List primaryKeys; + /** + * Create table instance from cursor + * @param cursor PRAGMA table_info(TABLE_NAME); + * @return table model based on data from the cursor + */ + public static Table fromCursor(String tableName, Cursor cursor) + { + Table result = new Table(tableName, new ArrayList(cursor.getCount())); + while (cursor.moveToNext()) + { + Column column = Column.fromCursor(cursor, result); + result.columns.add(column); + if (column.isPRIMARY_KEY()) + result.primaryKeys.add(column); + } + return result; + } + + /** + * Create upgrade statements according to sqlite rules: + * http://www.sqlite.org/lang_altertable.html + * only new columns are added. + * @return alter statements to alter upgradeFrom table into this table + */ + public List upgradeTable(Table upgradeFrom) + { + List result = new ArrayList(); + + // add new columns + for (Column column : columns) + { + if (upgradeFrom.columns.contains(column) == false) + { + StringBuilder resultBuilder = new StringBuilder("Alter table `").append(this.name) + .append("` ADD COLUMN ").append(column.getBuilder()).append(';'); + result.add(resultBuilder.toString()); + } + } + return result; + } + + public static List upgradeTable(Table currentTable, Table newTable) + { + return newTable.upgradeTable(currentTable); + } + + private Table(String name, List columns) + { + this.name = name; + this.columns = columns; + this.primaryKeys = new ArrayList(1); + } + /** * Create Table from java class definition * @@ -57,7 +110,7 @@ public Table(Class type) for (Field field : fields) { if ((field.getModifiers() & (Modifier.TRANSIENT | Modifier.STATIC | Modifier.FINAL)) > 0) continue; - Column column = new Column(field); + Column column = new Column(field, this); if (column.getDataType() == null) continue; columns.add(column); if (column.PRIMARY_KEY) primaryKeys.add(column); diff --git a/src/com/michaldabski/msqlite/queries/CreateTable.java b/src/com/michaldabski/msqlite/queries/CreateTable.java index 65fcc20..4bd5d26 100644 --- a/src/com/michaldabski/msqlite/queries/CreateTable.java +++ b/src/com/michaldabski/msqlite/queries/CreateTable.java @@ -11,11 +11,16 @@ public class CreateTable extends QueryBuilder { private boolean IF_NOT_EXISTS = false; - + public CreateTable(Class type) { super(type); } + + public CreateTable(Table table) + { + super(table); + } public CreateTable setIF_NOT_EXIST(boolean iF_NOT_EXIST) { diff --git a/src/com/michaldabski/msqlite/queries/QueryBuilder.java b/src/com/michaldabski/msqlite/queries/QueryBuilder.java index a17fa53..c613c54 100644 --- a/src/com/michaldabski/msqlite/queries/QueryBuilder.java +++ b/src/com/michaldabski/msqlite/queries/QueryBuilder.java @@ -5,17 +5,23 @@ public abstract class QueryBuilder { - protected Class type; + protected Table table; protected String condition = "1"; + @Deprecated public QueryBuilder(Class type) { - this.type = type; + this.table = new Table(type); + } + + public QueryBuilder(Table table) + { + this.table = table; } public Table getTable() { - return new Table(type); + return table; } // public abstract Result execute(Class type);