com.abubusoft.kripton.android.sqlite.SQLiteSchemaVerifierHelper Maven / Gradle / Ivy
/*******************************************************************************
* Copyright 2015, 2017 Francesco Benincasa ([email protected]).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*******************************************************************************/
package com.abubusoft.kripton.android.sqlite;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.tree.ParseTreeWalker;
import com.abubusoft.kripton.android.Logger;
import com.abubusoft.kripton.android.commons.IOUtils;
import com.abubusoft.kripton.android.sqlite.SQLiteUpdateTaskHelper.OnResultListener;
import com.abubusoft.kripton.android.sqlite.SQLiteUpdateTaskHelper.QueryType;
import com.abubusoft.kripton.common.StringUtils;
import com.abubusoft.kripton.exception.KriptonRuntimeException;
import com.abubusoft.kripton.processor.sqlite.grammars.jsql.JqlBaseListener;
import com.abubusoft.kripton.processor.sqlite.grammars.jsql.JqlLexer;
import com.abubusoft.kripton.processor.sqlite.grammars.jsql.JqlParser;
import com.abubusoft.kripton.processor.sqlite.grammars.jsql.JqlParser.Sql_stmtContext;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
/**
* @author Francesco Benincasa ([email protected])
*
*/
public abstract class SQLiteSchemaVerifierHelper {
/**
* Delete database file
* @param context
*/
public static void clearDatabase(Context context) {
File dbFile=context.getDatabasePath(SQLiteUpdateTestDatabase.MIGRATION_TEST);
Logger.info("Clear database file %s", dbFile.getAbsolutePath());
if (!dbFile.delete()) {
Logger.warn("Can not delete database " + dbFile.getAbsolutePath());
}
}
private static void query(SQLiteDatabase db, String conditions, QueryType type, OnResultListener listener) {
SQLiteUpdateTaskHelper.query(db, conditions, type, listener);
}
/**
* Drop all entity of particular type (table or index). If prefix is
* specified, the drop operation is applied only to entity with prefix.
*
* @param db
* @param type
* @param prefix
*/
private static void drop(SQLiteDatabase db, final QueryType type, String prefix) {
String dropSQL = StringUtils.hasText(prefix) ? "name like '" + prefix + "' || '%'" : null;
query(db, dropSQL, type, new OnResultListener() {
@Override
public void onRow(SQLiteDatabase db, String name, String sql) {
String drop = "drop " + type.toString().toLowerCase() + " " + name;
Logger.info(drop);
db.execSQL(drop);
}
});
}
/**
* Retrieve all table as a Map of
*
* @param db
* @return
*/
public static Map getAllTables(SQLiteDatabase db) {
return SQLiteUpdateTaskHelper.getAllTables(db);
}
/**
* Add to all schema's table a specifix prefix
*
* @param db
* @param prefix
*/
public static void renameAllTablesWithPrefix(SQLiteDatabase db, final String prefix) {
Logger.info("MASSIVE TABLE RENAME OPERATION: ADD PREFIX " + prefix);
query(db, null, QueryType.TABLE, new OnResultListener() {
@Override
public void onRow(SQLiteDatabase db, String name, String sql) {
sql = String.format("ALTER TABLE %s RENAME TO %s%s;", name, prefix, name);
Logger.info(sql);
db.execSQL(sql);
}
});
}
/**
* Drop all table with specific prefix
*
* @param db
* @param prefix
*/
public static void dropTablesWithPrefix(SQLiteDatabase db, String prefix) {
Logger.info("MASSIVE TABLE DROP OPERATION%s", StringUtils.ifNotEmptyAppend(prefix, " WITH PREFIX "));
drop(db, QueryType.TABLE, prefix);
}
public static void dropTablesAndIndices(SQLiteDatabase db) {
drop(db, QueryType.INDEX, null);
drop(db, QueryType.TABLE, null);
}
/**
*
* Retrieve all indexes as a Map of
*
* @param db
* @return
*/
public static Map getAllIndexes(SQLiteDatabase db) {
return SQLiteUpdateTaskHelper.getAllIndexes(db);
}
public static void executeSQL(final SQLiteDatabase database, Context context, int rawResourceId) {
String[] c = IOUtils.readTextFile(context, rawResourceId).split(";");
List commands = Arrays.asList(c);
executeSQL(database, commands);
}
public static List readSQLFromFile(String fileName) {
try {
return readSQLFromFile(new FileInputStream(fileName));
} catch (FileNotFoundException e) {
e.printStackTrace();
return null;
}
}
public static List readSQLFromFile(InputStream fileInputStream) {
String content = IOUtils.readText(fileInputStream);
// remove comments
content = content.replaceAll("\\/\\*.*\\*\\/", "");
content = content.replaceAll("--.*\n", "");
content = content.replaceAll("\n", "");
String[] c = content.split(";");
List commands = new ArrayList<>();
for (String i : c) {
if (StringUtils.hasText(i)) {
commands.add(i.trim());
}
}
return commands;
}
public static void executeSQL(final SQLiteDatabase database, InputStream fileInputStream) {
List commands = readSQLFromFile(fileInputStream);
executeSQL(database, commands);
}
public static void executeSQL(final SQLiteDatabase database, List commands) {
for (String command : commands) {
executeSQL(database, command);
}
// commands.forEach(command -> {
// executeSQL(database, command);
// });
}
public static void executeSQL(final SQLiteDatabase database, String command) {
// remove comments
command = command.replaceAll("\\/\\*.*\\*\\/", "");
command = command.replaceAll("--.*$", "");
if (command.trim().length() > 0) {
Logger.info(command);
database.execSQL(command);
}
}
public static void verifySchema(SQLiteDatabase database, InputStream inputStream) {
List ddl = extractCommands(database, inputStream);
verifySchemaInternal(database, ddl);
}
public static void verifySchema(H dataSource, InputStream inputStream) {
verifySchema(dataSource.openWritableDatabase(), inputStream);
}
public static void verifySchema(SQLiteDatabase database, Context context, int rawId) {
verifySchema(database, context.getResources().openRawResource(rawId));
}
static List extractCommands(SQLiteDatabase database, InputStream inputStream) {
final List result = new ArrayList<>();
final String input = IOUtils.readText(inputStream);
JqlLexer lexer = new JqlLexer(CharStreams.fromString(input));
CommonTokenStream tokens = new CommonTokenStream(lexer);
JqlParser parser = new JqlParser(tokens);
ParserRuleContext parseContext = parser.parse();
ParseTreeWalker walk = new ParseTreeWalker();
walk.walk(new JqlBaseListener() {
@Override
public void enterSql_stmt(Sql_stmtContext ctx) {
int start = ctx.getStart().getStartIndex();
int stop = ctx.getStop().getStopIndex() + 1;
if (start == stop)
return;
result.add(input.substring(start, stop));
}
}, parseContext);
return result;
}
public static void verifySchema(H dataSource, Context context, int rawId) {
verifySchema(dataSource.openWritableDatabase(), context, rawId);
}
static void verifySchemaInternal(SQLiteDatabase database, List expectedSQL) {
Set actualSql = new HashSet();
actualSql.addAll(SQLiteSchemaVerifierHelper.getAllTables(database).values());
actualSql.addAll(SQLiteSchemaVerifierHelper.getAllIndexes(database).values());
if (actualSql.size() != expectedSQL.size()) {
Logger.error(
"SCHEMA COMPARATOR RESULT: ERROR - Number of tables and indexes between aspected and actual schemas are different");
for (String item1 : actualSql) {
Logger.info("actual: " + item1);
}
for (String item1 : expectedSQL) {
Logger.info("expected: " + item1);
}
throw new KriptonRuntimeException(
"Number of tables and indexes between aspected and actual schemas are different");
}
for (String item : expectedSQL) {
if (!actualSql.contains(item)) {
Logger.error("SCHEMA COMPARATOR RESULT: ERROR - Actual and expected schemas are NOT the same");
for (String item1 : actualSql) {
Logger.info("actual: " + item1);
}
for (String item1 : expectedSQL) {
Logger.info("expected: " + item1);
}
throw new KriptonRuntimeException("not found element: " + item);
}
}
Logger.info("SCHEMA COMPARATOR RESULT: OK - Actual and expected schemas are the same!");
database.close();
}
/**
* Force a schema update for a datasource. Note that no DDL was execute
* untill the database was opened.
*
* @param dataSource
* @param version
* to upgrade.
*/
public static void forceSchemaUpdate(E dataSource, int version) {
dataSource.forceClose();
dataSource.version = version;
dataSource.database = null;
dataSource.sqliteHelper = null;
dataSource.openWritableDatabase();
}
public static void clearDatabase(E dataSource) {
dataSource.openWritableDatabase();
File file = new File(dataSource.database.getPath(), dataSource.name);
if (dataSource.isOpen()) {
dataSource.forceClose();
dataSource.close();
}
Logger.info("Clear database file %s", file.getAbsolutePath());
if (!file.delete()) {
Logger.warn("Can not delete database " + file.getAbsolutePath());
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy