diff --git a/WebContent/WEB-INF/lib/questdb-jdk8-6.0.1.jar b/WebContent/WEB-INF/lib/questdb-jdk8-6.0.1.jar
new file mode 100644
index 0000000000..9ef91e861d
Binary files /dev/null and b/WebContent/WEB-INF/lib/questdb-jdk8-6.0.1.jar differ
diff --git a/build.xml b/build.xml
index 07f1c09d44..e69e30344e 100644
--- a/build.xml
+++ b/build.xml
@@ -249,6 +249,9 @@
haltonfailure="yes">
+
+
@@ -660,6 +663,7 @@
+
diff --git a/scadalts-ui/src/locales/de.json b/scadalts-ui/src/locales/de.json
index 55e27a1e3b..2ca2eebcf5 100644
--- a/scadalts-ui/src/locales/de.json
+++ b/scadalts-ui/src/locales/de.json
@@ -337,6 +337,7 @@
"systemsettings.notification.save.systemevent": "System Event Types Settings saved!",
"systemsettings.notification.save.systeminfo": "Systeminfo-Einstellungen gespeichert!",
"systemsettings.notification.send.email": "E-Mail wurde gesendet an ",
+ "systemsettings.notification.save.dbquery": "DbQuery Einstellungen gespeichert!",
"systemsettings.scadaconf.aclServer": "Zugriffskontrolllistenserver",
"systemsettings.scadaconf.apiReplaceAlertOnView": "Ersetzen Sie die angezeigte Warnung",
"systemsettings.scadaconf.cacheEnable": "Event cache",
@@ -356,6 +357,8 @@
"systemsettings.scadaconf.useCacheDataSourcePointsWhenSystemIsReady": "Verwenden Sie Cache-Datenquellenpunkte, wenn das System bereit ist",
"systemsettings.smsdomain.label": "SMS-Gateway-Serveradresse",
"systemsettings.smsdomain.title": "SMS Domain",
+ "systemsettings.dbquery.title": "DBQuery-Einstellungen",
+ "systemsettings.dbquery.readEnabled": "Lesen aktivieren",
"systemsettings.title": "Systemeinstellungen",
"systemsettings.tooltip.derbydatabase": "Die Derby-Datenbank ist in dieser ScadaLTS-Version nicht verfügbar!",
"systemsettings.tooltip.purgedata": "Löschen Sie die Zustimmung zum historischen Datenpunkt",
diff --git a/scadalts-ui/src/locales/en.json b/scadalts-ui/src/locales/en.json
index f8f5e54568..fb26a7d725 100644
--- a/scadalts-ui/src/locales/en.json
+++ b/scadalts-ui/src/locales/en.json
@@ -787,6 +787,7 @@
"systemsettings.notification.save.systemevent": "System Event Types settings saved!",
"systemsettings.notification.save.systeminfo": "System info settings saved!",
"systemsettings.notification.send.email": "Email has been sent to ",
+ "systemsettings.notification.save.dbquery": "DbQuery settings saved!",
"systemsettings.scadaconf.aclServer": "Access Control List server",
"systemsettings.scadaconf.apiReplaceAlertOnView": "Replace alert on view",
"systemsettings.scadaconf.cacheEnable": "Event cache",
@@ -806,6 +807,8 @@
"systemsettings.scadaconf.useCacheDataSourcePointsWhenSystemIsReady": "Use cache data sources points when the system is ready",
"systemsettings.smsdomain.label": "SMS-Gateway server address",
"systemsettings.smsdomain.title": "SMS Domain",
+ "systemsettings.dbquery.title": "DBQuery settings",
+ "systemsettings.dbquery.readEnabled": "Enable read",
"systemsettings.title": "System settings",
"systemsettings.tooltip.derbydatabase": "Derby database is unavailable in this ScadaLTS version!",
"systemsettings.tooltip.purgedata": "Delete all historical datapoint values",
diff --git a/scadalts-ui/src/store/systemSettings/index.js b/scadalts-ui/src/store/systemSettings/index.js
index 559e932f4e..c5fd3b85ac 100644
--- a/scadalts-ui/src/store/systemSettings/index.js
+++ b/scadalts-ui/src/store/systemSettings/index.js
@@ -23,6 +23,7 @@ const storeSystemSettings = {
schemaVersion: undefined,
scadaConfig: undefined,
defaultLoggingType: undefined,
+ dbQuerySettings: undefined,
},
mutations: {
setDatabaseType(state, databaseType) {
@@ -71,6 +72,9 @@ const storeSystemSettings = {
setDefaultLoggingType(state, defaultLoggingType) {
state.defaultLoggingType = defaultLoggingType;
},
+ setDbQuerySettings(state, dbQuerySettings) {
+ state.dbQuerySettings = dbQuerySettings;
+ },
},
actions: {
getDatabaseType({ commit, dispatch }) {
@@ -272,6 +276,20 @@ const storeSystemSettings = {
});
},
+ getDbQuerySettings({ commit, dispatch }) {
+ return dispatch('requestGet', '/systemSettings/getDbQuerySettings').then((r) => {
+ commit('setDbQuerySettings', r);
+ return r;
+ });
+ },
+
+ saveDbQuerySettings({ state, dispatch }) {
+ return dispatch('requestPost', {
+ url: '/systemSettings/saveDbQuerySettings',
+ data: state.dbQuerySettings,
+ });
+ },
+
purgeData(context) {
return new Promise((resolve, reject) => {
axios
diff --git a/scadalts-ui/src/views/System/SystemSettings/DbQuerySettingsComponent.vue b/scadalts-ui/src/views/System/SystemSettings/DbQuerySettingsComponent.vue
new file mode 100644
index 0000000000..e497d49a12
--- /dev/null
+++ b/scadalts-ui/src/views/System/SystemSettings/DbQuerySettingsComponent.vue
@@ -0,0 +1,130 @@
+
+
+
+
+ {{ $t('systemsettings.dbquery.title') }}
+ *
+
+
+
+
+
+
+
+
+
+
+
+ {{ response.message }}
+
+
+
+
+
diff --git a/scadalts-ui/src/views/System/SystemSettings/index.vue b/scadalts-ui/src/views/System/SystemSettings/index.vue
index f1475381e8..b078aa5726 100644
--- a/scadalts-ui/src/views/System/SystemSettings/index.vue
+++ b/scadalts-ui/src/views/System/SystemSettings/index.vue
@@ -67,6 +67,11 @@
+
+
@@ -281,6 +286,7 @@ import SmsDomainSettingsComponent from './SmsDomainSettingsComponent';
import ScadaConfigurationComponent from './ScadaConfigurationComponent';
import AmChartSettingsComponent from './AmChartSettingsComponent';
import DataBaseInfoComponent from './DataBaseInfoComponent';
+import DbQuerySettingsComponent from './DbQuerySettingsComponent';
export default {
el: '#systemsettings',
@@ -298,6 +304,7 @@ export default {
ScadaConfigurationComponent,
AmChartSettingsComponent,
DataBaseInfoComponent,
+ DbQuerySettingsComponent,
},
filters: {
blank: function (value) {
@@ -347,7 +354,7 @@ export default {
setTimeout(() => {
let el = document.getElementById(element);
if(!!el) {
- el.scrollIntoView();
+ el.scrollIntoView();
}
}, 1000);
}
diff --git a/src/com/serotonin/mango/Common.java b/src/com/serotonin/mango/Common.java
index 7289022818..7ed90c8a96 100644
--- a/src/com/serotonin/mango/Common.java
+++ b/src/com/serotonin/mango/Common.java
@@ -117,6 +117,7 @@ public interface ContextKeys {
String HTTP_RECEIVER_MULTICASTER = "HTTP_RECEIVER_MULTICASTER";
String DOCUMENTATION_MANIFEST = "DOCUMENTATION_MANIFEST";
String DATA_POINTS_NAME_ID_MAPPING = "DATAPOINTS_NAME_ID_MAPPING";
+ String DATABASE_QUERY_ACCESS = "DATABASE_QUERY_ACCESS";
}
public interface TimePeriods {
diff --git a/src/com/serotonin/mango/MangoContextListener.java b/src/com/serotonin/mango/MangoContextListener.java
index f839cb0a37..b007665bab 100644
--- a/src/com/serotonin/mango/MangoContextListener.java
+++ b/src/com/serotonin/mango/MangoContextListener.java
@@ -414,13 +414,31 @@ private void databaseInitialize(ServletContext ctx) {
DatabaseAccess databaseAccess = DatabaseAccess
.createDatabaseAccess(ctx);
ctx.setAttribute(Common.ContextKeys.DATABASE_ACCESS, databaseAccess);
- databaseAccess.initialize();
+ boolean dbQueryEnabled = Common.getEnvironmentProfile().getBoolean("dbquery.enabled",
+ false);
+ if(dbQueryEnabled) {
+ DatabaseAccess databaseQueryAccess = DatabaseAccess
+ .createDatabaseAccess(ctx, "dbquery.");
+ ctx.setAttribute(Common.ContextKeys.DATABASE_QUERY_ACCESS, databaseQueryAccess);
+
+ databaseAccess.initOnlyDatasourceJdbc();
+ databaseQueryAccess.initOnlyDatasourceJdbc();
+
+ databaseAccess.initialize();
+ databaseQueryAccess.initialize();
+ } else {
+ databaseAccess.initialize();
+ }
}
private void databaseTerminate(ContextWrapper ctx) {
DatabaseAccess databaseAccess = ctx.getDatabaseAccess();
if (databaseAccess != null)
databaseAccess.terminate();
+
+ DatabaseAccess databaseQueryAccess = ctx.getDatabaseQueryAccess();
+ if(databaseQueryAccess != null)
+ databaseQueryAccess.terminate();
}
//
diff --git a/src/com/serotonin/mango/db/BasePooledAccess.java b/src/com/serotonin/mango/db/BasePooledAccess.java
index 8d923c1faa..720bb52d48 100644
--- a/src/com/serotonin/mango/db/BasePooledAccess.java
+++ b/src/com/serotonin/mango/db/BasePooledAccess.java
@@ -50,18 +50,23 @@ public BasePooledAccess(ServletContext ctx)
super(ctx);
}
+ public BasePooledAccess(ServletContext ctx, String dbPrefix)
+ {
+ super(ctx, dbPrefix);
+ }
+
@SuppressWarnings("deprecation")
@Override
protected void initializeImpl(String propertyPrefix, String dataSourceName)
{
log.info("Initializing pooled connection manager");
- if(Common.getEnvironmentProfile().getString(propertyPrefix + "db.datasource", "false").equals("true"))
+ if(Common.getEnvironmentProfile().getString(propertyPrefix + getDbPrefix() + "datasource", "false").equals("true"))
{
try
{
- log.info("Looking for Datasource: " + Common.getEnvironmentProfile().getString(propertyPrefix + "db.datasourceName"));
- dataSource = (DataSource) new InitialContext().lookup(Common.getEnvironmentProfile().getString(propertyPrefix + "db.datasourceName"));
+ log.info("Looking for Datasource: " + Common.getEnvironmentProfile().getString(propertyPrefix + getDbPrefix() + "datasourceName"));
+ dataSource = (DataSource) new InitialContext().lookup(Common.getEnvironmentProfile().getString(propertyPrefix + getDbPrefix() + "datasourceName"));
Connection conn = dataSource.getConnection();
log.info("DataSource meta: " + conn.getMetaData().getDatabaseProductName() + " " + conn.getMetaData().getDatabaseProductVersion());
dataSourceFound = true;
@@ -84,15 +89,15 @@ protected void initializeImpl(String propertyPrefix)
dataSource = new BasicDataSource();
((BasicDataSource) dataSource).setDriverClassName(getDriverClassName());
((BasicDataSource) dataSource).setUrl(getUrl(propertyPrefix));
- ((BasicDataSource) dataSource).setUsername(Common.getEnvironmentProfile().getString(propertyPrefix + "db.username"));
+ ((BasicDataSource) dataSource).setUsername(Common.getEnvironmentProfile().getString(propertyPrefix + getDbPrefix() + "username"));
((BasicDataSource) dataSource).setPassword(getDatabasePassword(propertyPrefix));
- ((BasicDataSource) dataSource).setMaxActive(Common.getEnvironmentProfile().getInt(propertyPrefix + "db.pool.maxActive", 10));
- ((BasicDataSource) dataSource).setMaxIdle(Common.getEnvironmentProfile().getInt(propertyPrefix + "db.pool.maxIdle", 10));
+ ((BasicDataSource) dataSource).setMaxActive(Common.getEnvironmentProfile().getInt(propertyPrefix + getDbPrefix() + "pool.maxActive", 10));
+ ((BasicDataSource) dataSource).setMaxIdle(Common.getEnvironmentProfile().getInt(propertyPrefix + getDbPrefix() + "pool.maxIdle", 10));
}
protected String getUrl(String propertyPrefix)
{
- return Common.getEnvironmentProfile().getString(propertyPrefix + "db.url");
+ return Common.getEnvironmentProfile().getString(propertyPrefix + getDbPrefix() + "url");
}
abstract protected String getDriverClassName();
diff --git a/src/com/serotonin/mango/db/DatabaseAccess.java b/src/com/serotonin/mango/db/DatabaseAccess.java
index 7bac36ff90..fc843df5b4 100644
--- a/src/com/serotonin/mango/db/DatabaseAccess.java
+++ b/src/com/serotonin/mango/db/DatabaseAccess.java
@@ -24,9 +24,12 @@
import javax.servlet.ServletContext;
import javax.sql.DataSource;
+import net.bull.javamelody.internal.common.LOG;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.flywaydb.core.Flyway;
import org.scada_lts.dao.SystemSettingsDAO;
+import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.CannotGetJdbcConnectionException;
import org.springframework.jdbc.datasource.DataSourceUtils;
@@ -49,33 +52,81 @@ public enum DatabaseType {
DatabaseAccess getImpl(ServletContext ctx) {
return new DerbyAccess(ctx);
}
+
+ @Override
+ DatabaseAccess getImpl(ServletContext ctx, String dbPrefix) {
+ return new DerbyAccess(ctx, dbPrefix);
+ }
},
MSSQL {
@Override
DatabaseAccess getImpl(ServletContext ctx) {
return new MSSQLAccess(ctx);
}
+
+ @Override
+ DatabaseAccess getImpl(ServletContext ctx, String dbPrefix) {
+ return new MSSQLAccess(ctx, dbPrefix);
+ }
},
MYSQL {
@Override
DatabaseAccess getImpl(ServletContext ctx) {
return new MySQLAccess(ctx);
}
+
+ @Override
+ DatabaseAccess getImpl(ServletContext ctx, String dbPrefix) {
+ return new MySQLAccess(ctx, dbPrefix);
+ }
},
POSTGRES {
@Override
DatabaseAccess getImpl(ServletContext ctx) {
return new PostgreSQLAccess(ctx);
}
+
+ @Override
+ DatabaseAccess getImpl(ServletContext ctx, String dbPrefix) {
+ return new PostgreSQLAccess(ctx, dbPrefix);
+ }
},
ORACLE11G {
@Override
DatabaseAccess getImpl(ServletContext ctx) {
return new Oracle11GAccess(ctx);
}
- };
+
+ @Override
+ DatabaseAccess getImpl(ServletContext ctx, String dbPrefix) {
+ return new Oracle11GAccess(ctx, dbPrefix);
+ }
+ },
+ QUESTDB {
+ @Override
+ DatabaseAccess getImpl(ServletContext ctx) {
+ return new QuestDbAccess(ctx);
+ }
+
+ @Override
+ DatabaseAccess getImpl(ServletContext ctx, String dbPrefix) {
+ return new QuestDbAccess(ctx, dbPrefix);
+ }
+ },
+ NONE {
+ @Override
+ DatabaseAccess getImpl(ServletContext ctx) {
+ return null;
+ }
+
+ @Override
+ DatabaseAccess getImpl(ServletContext ctx, String dbPrefix) {
+ return null;
+ }
+ };
abstract DatabaseAccess getImpl(ServletContext ctx);
+ abstract DatabaseAccess getImpl(ServletContext ctx, String dbPrefix);
}
public static DatabaseAccess createDatabaseAccess(ServletContext ctx) {
@@ -90,26 +141,38 @@ public static DatabaseAccess createDatabaseAccess(ServletContext ctx) {
return dt.getImpl(ctx);
}
+ public static DatabaseAccess createDatabaseAccess(ServletContext ctx, String dbPrefix) {
+
+ String type = Common.getEnvironmentProfile().getString(dbPrefix + "type",
+ "derby");
+ DatabaseType dt = DatabaseType.valueOf(type.toUpperCase());
+
+ if (dt == null)
+ throw new IllegalArgumentException("Unknown database type: " + type);
+
+ return dt.getImpl(ctx, dbPrefix);
+ }
+
public static DatabaseAccess getDatabaseAccess() {
return Common.ctx.getDatabaseAccess();
}
protected final ServletContext ctx;
+ private final String dbPrefix;
protected DatabaseAccess(ServletContext ctx) {
this.ctx = ctx;
+ this.dbPrefix = "db.";
+ }
+
+ public DatabaseAccess(ServletContext ctx, String dbPrefix) {
+ this.ctx = ctx;
+ this.dbPrefix = dbPrefix;
}
public void initialize() {
- if (Common.getEnvironmentProfile().getString("db.datasource", "false")
- .equals("true")) {
- initializeImpl(
- "",
- Common.getEnvironmentProfile().getString(
- "db.datasourceName"));
- } else {
- initializeImpl("");
- }
+ if(getDataSource() == null)
+ initOnlyDatasourceJdbc();
ExtendedJdbcTemplate ejt = new ExtendedJdbcTemplate();
ejt.setDataSource(getDataSource());
@@ -120,7 +183,7 @@ public void initialize() {
String convertTypeStr = null;
try {
convertTypeStr = Common.getEnvironmentProfile().getString(
- "convert.db.type");
+ "convert." + dbPrefix + ".type");
} catch (MissingResourceException e) {
// no op
}
@@ -171,7 +234,7 @@ public void initialize() {
// matches
// // the application version.
if (Common.getEnvironmentProfile()
- .getString("db.upgrade.check", "false").equals("true")) {
+ .getString(dbPrefix + "upgrade.check", "false").equals("true")) {
DBUpgrade.checkUpgrade();
}
}
@@ -184,6 +247,18 @@ public void initialize() {
postInitialize(ejt);
}
+ public void initOnlyDatasourceJdbc() {
+ if (Common.getEnvironmentProfile().getString(dbPrefix + "datasource", "false")
+ .equals("true")) {
+ initializeImpl(
+ "",
+ Common.getEnvironmentProfile().getString(
+ dbPrefix + "datasourceName"));
+ } else {
+ initializeImpl("");
+ }
+ }
+
abstract public DatabaseType getType();
abstract public void terminate();
@@ -206,7 +281,73 @@ protected void postInitialize(
// no op - override as necessary
}
- abstract protected boolean newDatabaseCheck(ExtendedJdbcTemplate ejt);
+ protected boolean newDatabaseCheck(ExtendedJdbcTemplate ejt) {
+ boolean shemaExist = true;
+ boolean baseLineNotExist = false;
+
+ String tableToCheck = Common.getEnvironmentProfile().getString(getDbPrefix() + "table.tocheck", "users");
+ try {
+ ejt.execute("select count(*) from " + tableToCheck);
+ LOG.info("schemaExist:"+shemaExist);
+ } catch (DataAccessException e) {
+ shemaExist = false;
+ LOG.info("schemaExist:"+shemaExist);
+ }
+
+ try {
+ ejt.execute("select count(*) from schema_version");
+ LOG.info("BaseLineNotExist:"+baseLineNotExist);
+ } catch (DataAccessException e) {
+ baseLineNotExist = true;
+ LOG.info("BaseLineNotExist:"+baseLineNotExist);
+ }
+
+ String migrationPackage = Common.getEnvironmentProfile().getString(getDbPrefix() + "migration.package");
+ try {
+ Flyway flyway = null;
+
+ if (shemaExist) {
+ // old shema without flayway
+ if (baseLineNotExist) {
+ flyway = Flyway.configure()
+ .baselineOnMigrate(true)
+ .dataSource(getDataSource())
+ .locations(migrationPackage)
+ .table("schema_version")
+ .load();
+
+ flyway.baseline();
+ flyway.migrate();
+ }
+ } else {
+ //shema not exist
+ if (baseLineNotExist) {
+ flyway = Flyway.configure()
+ .baselineOnMigrate(true)
+ .dataSource(getDataSource())
+ .locations(migrationPackage)
+ .table("schema_version")
+ .load();
+ //flayway.baseline();
+ flyway.migrate();
+ }
+ }
+ if (flyway == null) {
+ flyway = Flyway.configure()
+ .dataSource(getDataSource())
+ .locations(migrationPackage)
+ .table("schema_version")
+ .load();
+ }
+ //flyway.repair();
+ flyway.migrate();
+ } catch (Exception e) {
+ log.error(e.getMessage(), e);
+ //Need stop scada
+ }
+
+ return false;
+ }
abstract public void runScript(String[] script, final OutputStream out)
throws Exception;
@@ -237,7 +378,7 @@ public void doInConnection(ConnectionCallbackVoid callback) {
public String getDatabasePassword(String propertyPrefix) {
String input = Common.getEnvironmentProfile().getString(
- propertyPrefix + "db.password");
+ propertyPrefix + getDbPrefix() + "password");
return new DatabaseAccessUtils().decrypt(input);
}
@@ -262,4 +403,8 @@ public PreparedStatement prepareStatement(Connection connection,
return connection.prepareStatement(sql, 1);
}
+
+ public String getDbPrefix() {
+ return dbPrefix;
+ }
}
diff --git a/src/com/serotonin/mango/db/DerbyAccess.java b/src/com/serotonin/mango/db/DerbyAccess.java
index d09a12c689..edb050c1f7 100644
--- a/src/com/serotonin/mango/db/DerbyAccess.java
+++ b/src/com/serotonin/mango/db/DerbyAccess.java
@@ -62,6 +62,10 @@ public DerbyAccess(ServletContext ctx) {
super(ctx);
}
+ public DerbyAccess(ServletContext ctx, String dbPrefix) {
+ super(ctx, dbPrefix);
+ }
+
@Override
public DatabaseType getType() {
return DatabaseType.DERBY;
diff --git a/src/com/serotonin/mango/db/MSSQLAccess.java b/src/com/serotonin/mango/db/MSSQLAccess.java
index 93fba4fce9..97e01104b1 100644
--- a/src/com/serotonin/mango/db/MSSQLAccess.java
+++ b/src/com/serotonin/mango/db/MSSQLAccess.java
@@ -31,6 +31,10 @@ public MSSQLAccess(ServletContext ctx) {
super(ctx);
}
+ public MSSQLAccess(ServletContext ctx, String dbPrefix) {
+ super(ctx, dbPrefix);
+ }
+
@Override
public DatabaseType getType() {
return DatabaseType.MSSQL;
diff --git a/src/com/serotonin/mango/db/MySQLAccess.java b/src/com/serotonin/mango/db/MySQLAccess.java
index 8c0b331385..c6d2b9032b 100644
--- a/src/com/serotonin/mango/db/MySQLAccess.java
+++ b/src/com/serotonin/mango/db/MySQLAccess.java
@@ -37,6 +37,10 @@ public MySQLAccess(ServletContext ctx) {
super(ctx);
}
+ public MySQLAccess(ServletContext ctx, String dbPrefix) {
+ super(ctx, dbPrefix);
+ }
+
@Override
protected void initializeImpl(String propertyPrefix) {
super.initializeImpl(propertyPrefix);
@@ -115,6 +119,7 @@ protected boolean newDatabaseCheck(ExtendedJdbcTemplate ejt) {
LOG.info("BaseLineNotExist:"+baseLineNotExist);
}
+ String migrationPackage = Common.getEnvironmentProfile().getString(getDbPrefix() + "migration.package");
try {
Flyway flyway = null;
@@ -124,7 +129,7 @@ protected boolean newDatabaseCheck(ExtendedJdbcTemplate ejt) {
flyway = Flyway.configure()
.baselineOnMigrate(true)
.dataSource(getDataSource())
- .locations("org.scada_lts.dao.migration.mysql")
+ .locations(migrationPackage)
.table("schema_version")
.load();
@@ -137,7 +142,7 @@ protected boolean newDatabaseCheck(ExtendedJdbcTemplate ejt) {
flyway = Flyway.configure()
.baselineOnMigrate(true)
.dataSource(getDataSource())
- .locations("org.scada_lts.dao.migration.mysql")
+ .locations(migrationPackage)
.table("schema_version")
.load();
//flayway.baseline();
@@ -147,7 +152,7 @@ protected boolean newDatabaseCheck(ExtendedJdbcTemplate ejt) {
if (flyway == null) {
flyway = Flyway.configure()
.dataSource(getDataSource())
- .locations("org.scada_lts.dao.migration.mysql")
+ .locations(migrationPackage)
.table("schema_version")
.load();
}
diff --git a/src/com/serotonin/mango/db/Oracle11GAccess.java b/src/com/serotonin/mango/db/Oracle11GAccess.java
index 4574f92f2c..6dbaf6f26c 100644
--- a/src/com/serotonin/mango/db/Oracle11GAccess.java
+++ b/src/com/serotonin/mango/db/Oracle11GAccess.java
@@ -55,6 +55,10 @@ public Oracle11GAccess(ServletContext ctx) {
super(ctx);
}
+ public Oracle11GAccess(ServletContext ctx, String dbPrefix) {
+ super(ctx, dbPrefix);
+ }
+
@Override
protected void initializeImpl(String propertyPrefix) {
super.initializeImpl(propertyPrefix);
@@ -92,9 +96,9 @@ protected void createSchema(String scriptFile) {
}
try {
Connection con = DriverManager.getConnection(Common
- .getEnvironmentProfile().getString("db.url"), Common
- .getEnvironmentProfile().getString("db.username"), Common
- .getEnvironmentProfile().getString("db.password"));
+ .getEnvironmentProfile().getString(getDbPrefix() + "url"), Common
+ .getEnvironmentProfile().getString(getDbPrefix() + "username"), Common
+ .getEnvironmentProfile().getString(getDbPrefix() + "password"));
// Initialize object for ScripRunner
ScriptRunner sr = new ScriptRunner(con, false, false);
diff --git a/src/com/serotonin/mango/db/PostgreSQLAccess.java b/src/com/serotonin/mango/db/PostgreSQLAccess.java
index 1015c206b0..33cd6b12b7 100644
--- a/src/com/serotonin/mango/db/PostgreSQLAccess.java
+++ b/src/com/serotonin/mango/db/PostgreSQLAccess.java
@@ -28,16 +28,25 @@
import javax.servlet.ServletContext;
import org.apache.commons.dbcp.BasicDataSource;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
import org.springframework.dao.DataAccessException;
import com.serotonin.db.spring.ExtendedJdbcTemplate;
import com.serotonin.mango.Common;
public class PostgreSQLAccess extends BasePooledAccess {
+
+ private static final Log LOG = LogFactory.getLog(PostgreSQLAccess.class);
+
public PostgreSQLAccess(ServletContext ctx) {
super(ctx);
}
+ public PostgreSQLAccess(ServletContext ctx, String dbPrefix) {
+ super(ctx, dbPrefix);
+ }
+
@Override
protected void initializeImpl(String propertyPrefix) {
super.initializeImpl(propertyPrefix);
@@ -74,7 +83,7 @@ public DatabaseAccess.DatabaseType getType() {
protected String getDriverClassName() {
return "org.postgresql.Driver";
}
-
+/*
@Override
protected boolean newDatabaseCheck(ExtendedJdbcTemplate ejt) {
try {
@@ -99,10 +108,10 @@ protected boolean newDatabaseCheck(ExtendedJdbcTemplate ejt) {
Common.getEnvironmentProfile().getString("db.password"));
createSchema("/WEB-INF/db/createTables-postgresql.sql");
conn.close();
- return true;
+ return true;
}
} catch (SQLException ex) {
- //Logger.getLogger(PostgreSQLAccess.class.getName()).log(Level.SEVERE, null, ex);
+ //Logger.getLogger(PostgreSQLAccess.class.getName()).log(Level.SEVERE, null, ex);
} catch (ClassNotFoundException ex) {
Logger.getLogger(PostgreSQLAccess.class.getName()).log(Level.SEVERE, null, ex);
}
@@ -119,7 +128,7 @@ protected boolean newDatabaseCheck(ExtendedJdbcTemplate ejt) {
}
return false;
}
-
+*/
@Override
public double applyBounds(double value) {
if (Double.isNaN(value))
diff --git a/src/com/serotonin/mango/db/QuestDbAccess.java b/src/com/serotonin/mango/db/QuestDbAccess.java
new file mode 100644
index 0000000000..21263de09f
--- /dev/null
+++ b/src/com/serotonin/mango/db/QuestDbAccess.java
@@ -0,0 +1,32 @@
+package com.serotonin.mango.db;
+
+import com.serotonin.db.spring.ExtendedJdbcTemplate;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.scada_lts.dao.migration.query.questdb.V2_8_0_0__CreatePointValuesDenormalized;
+
+import javax.servlet.ServletContext;
+
+public class QuestDbAccess extends PostgreSQLAccess {
+
+ private static final Log LOG = LogFactory.getLog(QuestDbAccess.class);
+
+ public QuestDbAccess(ServletContext ctx) {
+ super(ctx);
+ }
+
+ public QuestDbAccess(ServletContext ctx, String dbPrefix) {
+ super(ctx, dbPrefix);
+ }
+
+ @Override
+ protected boolean newDatabaseCheck(ExtendedJdbcTemplate ejt) {
+ try {
+ new V2_8_0_0__CreatePointValuesDenormalized().migrate(null);
+ } catch (Exception e) {
+ LOG.error(e.getMessage(), e);
+ }
+ return false;
+ }
+}
diff --git a/src/com/serotonin/mango/db/dao/PointValueDao.java b/src/com/serotonin/mango/db/dao/PointValueDao.java
index 6a0b4e0721..e6f1c55e7d 100644
--- a/src/com/serotonin/mango/db/dao/PointValueDao.java
+++ b/src/com/serotonin/mango/db/dao/PointValueDao.java
@@ -56,7 +56,7 @@ public PointValueTime savePointValueSync(int pointId,
PointValueTime pointValue, SetPointSource source) {
long id = savePointValueImpl(pointId, pointValue, source, false);
- PointValueTime savedPointValue;
+ /*PointValueTime savedPointValue;
int retries = 5;
while (true) {
try {
@@ -67,9 +67,9 @@ public PointValueTime savePointValueSync(int pointId,
throw e;
retries--;
}
- }
+ }*/
- return savedPointValue;
+ return pointValue;
}
/**
diff --git a/src/com/serotonin/mango/web/ContextWrapper.java b/src/com/serotonin/mango/web/ContextWrapper.java
index 0bc9f837e2..e16264f818 100644
--- a/src/com/serotonin/mango/web/ContextWrapper.java
+++ b/src/com/serotonin/mango/web/ContextWrapper.java
@@ -55,6 +55,11 @@ public DatabaseAccess getDatabaseAccess() {
.getAttribute(Common.ContextKeys.DATABASE_ACCESS);
}
+ public DatabaseAccess getDatabaseQueryAccess() {
+ return (DatabaseAccess) ctx
+ .getAttribute(Common.ContextKeys.DATABASE_QUERY_ACCESS);
+ }
+
@SuppressWarnings("unchecked")
public List getImageSets() {
return (List) ctx.getAttribute(Common.ContextKeys.IMAGE_SETS);
diff --git a/src/org/scada_lts/dao/DAO.java b/src/org/scada_lts/dao/DAO.java
index 0d17782b56..b12e359573 100644
--- a/src/org/scada_lts/dao/DAO.java
+++ b/src/org/scada_lts/dao/DAO.java
@@ -23,6 +23,7 @@
import javax.sql.DataSource;
+import com.serotonin.mango.db.DatabaseAccess;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.jdbc.core.JdbcTemplate;
@@ -41,8 +42,9 @@ public class DAO {
private static final Log LOG = LogFactory.getLog(DAO.class);
private NamedParameterJdbcTemplate namedParamJdbcTemplate;
- private JdbcTemplate jdbcTemplate;
+ private JdbcTemplate jdbcTemplate;
private static DAO instance;
+ private static DAO query;
private boolean test =false;
private DAO() {
@@ -55,7 +57,13 @@ private DAO() {
LOG.error(e);
}
}
-
+
+ private DAO(NamedParameterJdbcTemplate namedParamJdbcTemplate, JdbcTemplate jdbcTemplate) {
+ LOG.trace("Create DAO");
+ this.namedParamJdbcTemplate = namedParamJdbcTemplate;
+ this.jdbcTemplate = jdbcTemplate;
+ }
+
/**
* Get id.
* @return Method queryForObject() can also return "null"
@@ -70,6 +78,24 @@ public static DAO getInstance() {
}
return instance;
}
+
+ public static DAO query() {
+ if (query == null) {
+ boolean dbQueryEnabled = Common.getEnvironmentProfile().getBoolean("dbquery.enabled", false);
+ if(dbQueryEnabled) {
+ DatabaseAccess databaseAccess = Common.ctx.getDatabaseQueryAccess();
+ if (databaseAccess == null) {
+ throw new IllegalStateException("dbquery.enabled is true, you must definition datasource for dbquery or disabled in env.properties");
+ } else {
+ DataSource ds = databaseAccess.getDataSource();
+ NamedParameterJdbcTemplate namedParamJdbcTemplate = new NamedParameterJdbcTemplate(ds);
+ JdbcTemplate jdbcTemplate = new JdbcTemplate(ds);
+ query = new DAO(namedParamJdbcTemplate, jdbcTemplate);
+ }
+ }
+ }
+ return query;
+ }
/**
* Method utility needed because of mango compatibility.
@@ -97,7 +123,7 @@ public static String boolToChar(boolean b) {
public JdbcTemplate getJdbcTemp() {
return jdbcTemplate;
}
-
+
/**
* Set jdbcTemplate
* @see TestDAO
diff --git a/src/org/scada_lts/dao/SystemSettingsDAO.java b/src/org/scada_lts/dao/SystemSettingsDAO.java
index e041129873..162947f4fb 100644
--- a/src/org/scada_lts/dao/SystemSettingsDAO.java
+++ b/src/org/scada_lts/dao/SystemSettingsDAO.java
@@ -27,6 +27,7 @@
import org.scada_lts.utils.ApplicationBeans;
import org.scada_lts.utils.ColorUtils;
import org.scada_lts.web.mvc.api.AggregateSettings;
+import org.scada_lts.web.mvc.api.DbQuerySettings;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
@@ -122,6 +123,9 @@ public class SystemSettingsDAO {
public static final String AGGREGATION_VALUES_LIMIT = "aggregationValuesLimit";
public static final String AGGREGATION_LIMIT_FACTOR = "aggregationLimitFactor";
+ //QueryDB
+ public static final String DB_QUERY_READ_ENABLED = "dbQueryReadEnabled";
+
private static final String DELETE_WATCH_LISTS = "delete from watchLists";
private static final String DELETE_MANGO_VIEWS = "delete from mangoViews";
private static final String DELETE_POINT_EVENT_DETECTORS = "delete from pointEventDetectors";
@@ -374,6 +378,9 @@ public String getDatabaseSchemaVersion(String key, String defaultValue) {
DEFAULT_VALUES.put(AGGREGATION_LIMIT_FACTOR, String.valueOf(aggregateSettings.getLimitFactor()));
DEFAULT_VALUES.put(AGGREGATION_VALUES_LIMIT, aggregateSettings.getValuesLimit());
+ DbQuerySettings dbQuerySettings = DbQuerySettings.fromEnvProperties();
+ DEFAULT_VALUES.put(DB_QUERY_READ_ENABLED, dbQuerySettings.isReadEnabled());
+
DEFAULT_VALUES.put(VALUES_LIMIT_FOR_PURGE, 100);
}
diff --git a/src/org/scada_lts/dao/migration/mysql/V2_8_0_0__CreateViewPointValuesDenormalized.java b/src/org/scada_lts/dao/migration/mysql/V2_8_0_0__CreateViewPointValuesDenormalized.java
new file mode 100644
index 0000000000..6f7e91ec48
--- /dev/null
+++ b/src/org/scada_lts/dao/migration/mysql/V2_8_0_0__CreateViewPointValuesDenormalized.java
@@ -0,0 +1,41 @@
+package org.scada_lts.dao.migration.mysql;
+
+import org.flywaydb.core.api.migration.BaseJavaMigration;
+import org.flywaydb.core.api.migration.Context;
+import org.scada_lts.dao.DAO;
+import org.springframework.jdbc.core.JdbcTemplate;
+
+
+public class V2_8_0_0__CreateViewPointValuesDenormalized extends BaseJavaMigration {
+
+
+ @Override
+ public void migrate(Context context) throws Exception {
+ final JdbcTemplate mysqlJdbcTmp = DAO.getInstance().getJdbcTemp();
+ createViewForPointValues(mysqlJdbcTmp);
+ }
+
+
+ public void createViewForPointValues(JdbcTemplate jdbcTmp) {
+
+ String createViewSQL = "CREATE OR REPLACE VIEW pointValuesDenormalized AS " +
+ "SELECT " +
+ "pv.dataPointId, " +
+ "pvm.pointValue, " +
+ "pv.dataType, " +
+ "CONCAT(DATE(FROM_UNIXTIME(pv.ts * 0.001)), \"T\", TIME(FROM_UNIXTIME(pv.ts * 0.001)), \"Z\") AS timestamp, " +
+ "pv.ts, " +
+ "(SELECT REPLACE(JSON_OBJECT('sourceType', pva.sourceType, " +
+ " 'sourceId', pva.sourceId, 'username', us.username), '\"', '')) AS metaData " +
+ "FROM " +
+ " (SELECT id, CONCAT_WS('', IF(dataType IN (4,5), null, pointValue), pva.textPointValueShort, pva.textPointValueLong) AS pointValue " +
+ "FROM pointValues pv LEFT JOIN pointValueAnnotations pva ON pv.id = pva.pointValueId) AS pvm, pointValues pv " +
+ "LEFT JOIN pointValueAnnotations pva ON pv.id = pva.pointValueId " +
+ "LEFT JOIN users us ON pva.sourceId = us.id " +
+ "WHERE pvm.id = pv.id;";
+
+ jdbcTmp.execute(createViewSQL);
+ }
+
+
+}
\ No newline at end of file
diff --git a/src/org/scada_lts/dao/migration/query/mysql/V2_8_0_0__CreatePointValuesDenormalized.java b/src/org/scada_lts/dao/migration/query/mysql/V2_8_0_0__CreatePointValuesDenormalized.java
new file mode 100644
index 0000000000..06f29bd568
--- /dev/null
+++ b/src/org/scada_lts/dao/migration/query/mysql/V2_8_0_0__CreatePointValuesDenormalized.java
@@ -0,0 +1,48 @@
+/*
+ * (c) 2016 Abil'I.T. http://abilit.eu/
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+package org.scada_lts.dao.migration.query.mysql;
+
+import org.flywaydb.core.api.migration.BaseJavaMigration;
+import org.flywaydb.core.api.migration.Context;
+import org.scada_lts.dao.DAO;
+import org.springframework.jdbc.core.JdbcTemplate;
+
+public class V2_8_0_0__CreatePointValuesDenormalized extends BaseJavaMigration {
+
+ @Override
+ public void migrate(Context context) throws Exception {
+
+ final JdbcTemplate jdbcTemplate = DAO.query().getJdbcTemp();
+
+ final String pointValueAnnotations = ""
+ + "create table IF NOT EXISTS pointValuesDenormalized ("
+ + "dataPointId int,"
+ + "dataType int,"
+ + "pointValue double,"
+ + "ts bigint,"
+ + "textPointValueShort varchar(128),"
+ + "textPointValueLong longtext,"
+ + "sourceType smallint,"
+ + "sourceId int,"
+ + "username varchar(40)) ENGINE=InnoDB;";
+
+ jdbcTemplate.execute(pointValueAnnotations);
+
+ }
+
+}
diff --git a/src/org/scada_lts/dao/migration/query/postgres/V2_8_0_0__CreatePointValuesDenormalized.java b/src/org/scada_lts/dao/migration/query/postgres/V2_8_0_0__CreatePointValuesDenormalized.java
new file mode 100644
index 0000000000..cba4c805c7
--- /dev/null
+++ b/src/org/scada_lts/dao/migration/query/postgres/V2_8_0_0__CreatePointValuesDenormalized.java
@@ -0,0 +1,48 @@
+/*
+ * (c) 2016 Abil'I.T. http://abilit.eu/
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+package org.scada_lts.dao.migration.query.postgres;
+
+
+import org.flywaydb.core.api.migration.BaseJavaMigration;
+import org.flywaydb.core.api.migration.Context;
+import org.scada_lts.dao.DAO;
+import org.springframework.jdbc.core.JdbcTemplate;
+
+public class V2_8_0_0__CreatePointValuesDenormalized extends BaseJavaMigration {
+
+ @Override
+ public void migrate(Context context) throws Exception {
+
+ final JdbcTemplate jdbcTemplate = DAO.query().getJdbcTemp();
+
+ final String pointValueAnnotations = ""
+ + "create table IF NOT EXISTS pointValuesDenormalized ("
+ + "dataPointId int,"
+ + "dataType int,"
+ + "pointValue DOUBLE PRECISION,"
+ + "ts bigint,"
+ + "textPointValueShort varchar(128),"
+ + "textPointValueLong text,"
+ + "sourceType smallint,"
+ + "sourceId int,"
+ + "username varchar(40));";
+
+ jdbcTemplate.execute(pointValueAnnotations);
+
+ }
+}
diff --git a/src/org/scada_lts/dao/migration/query/questdb/V2_8_0_0__CreatePointValuesDenormalized.java b/src/org/scada_lts/dao/migration/query/questdb/V2_8_0_0__CreatePointValuesDenormalized.java
new file mode 100644
index 0000000000..03f9df0158
--- /dev/null
+++ b/src/org/scada_lts/dao/migration/query/questdb/V2_8_0_0__CreatePointValuesDenormalized.java
@@ -0,0 +1,335 @@
+/*
+ * (c) 2016 Abil'I.T. http://abilit.eu/
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+package org.scada_lts.dao.migration.query.questdb;
+
+import com.serotonin.mango.Common;
+import org.apache.commons.httpclient.HttpClient;
+import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
+import org.apache.commons.httpclient.methods.PostMethod;
+import org.apache.commons.httpclient.methods.multipart.FilePart;
+import org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity;
+import org.apache.commons.httpclient.methods.multipart.Part;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.flywaydb.core.api.migration.BaseJavaMigration;
+import org.flywaydb.core.api.migration.Context;
+import org.scada_lts.dao.DAO;
+import org.springframework.jdbc.core.JdbcOperations;
+
+import java.io.*;
+import java.net.URL;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class V2_8_0_0__CreatePointValuesDenormalized extends BaseJavaMigration {
+
+
+ private static final Log LOG = LogFactory.getLog(V2_8_0_0__CreatePointValuesDenormalized.class);
+
+ @Override
+ public void migrate(Context context) throws Exception {
+
+ final JdbcOperations questdb = DAO.query().getJdbcTemp();
+ final JdbcOperations mysql = DAO.getInstance().getJdbcTemp();
+
+ URL resource = V2_8_0_0__CreatePointValuesDenormalized.class.getClassLoader().getResource("questdb-schema.json");
+ File schema = Paths.get(Objects.requireNonNull(resource).toURI()).toFile();
+ int limit = Common.getEnvironmentProfile().getInt("db.values.export.limit", 1001);
+ boolean overwrite = Common.getEnvironmentProfile().getBoolean("dbquery.import.overwrite", false);
+ boolean valuesImportEnabled = Common.getEnvironmentProfile().getBoolean("dbquery.values.import.enabled", true);
+
+ List dataPoints = mysql.queryForList("select dataPointId from pointValues group by dataPointId order by dataPointId", Integer.class);
+
+ if(overwrite) {
+ try {
+ List tables = questdb.queryForList("SHOW TABLES;", String.class);
+
+ for(String table: tables) {
+ if(!table.equalsIgnoreCase("telemetry")
+ && !table.equalsIgnoreCase("telemetry_config")
+ && table.contains("pointValues")) {
+ String dropQuery = "DROP TABLE " + table + ";";
+ questdb.execute(dropQuery);
+ }
+ }
+
+ } catch (Exception ex) {
+ LOG.warn(ex.getMessage());
+ }
+ }
+
+ if(valuesImportEnabled) {
+
+ MigrationSettings migrationSettings = MigrationSettings.builder()
+ .toRead(mysql)
+ .overwrite(false)
+ .schema(schema)
+ .build();
+
+ for (int dataPoint : dataPoints) {
+ LOG.info("Datapoint: " + dataPoint);
+ for (int i = 0; migrationNext(PaginationParams.params(i, limit, dataPoint), migrationSettings); i += limit) {
+ }
+ }
+ }
+/*
+ final String createEmptyTableQuery = "CREATE TABLE IF NOT EXISTS pointValuesDenormalized (" +
+ "dataPointId INT, " +
+ "dataType INT, " +
+ "pointValue DOUBLE, " +
+ "ts LONG, " +
+ "timestamp TIMESTAMP, " +
+ "textPointValueShort SYMBOL, " +
+ "textPointValueLong SYMBOL, " +
+ "sourceType INT, " +
+ "sourceId INT, " +
+ "username SYMBOL) timestamp(timestamp) PARTITION BY DAY;";
+
+ questdb.execute(createEmptyTableQuery);*/
+
+ }
+
+ private static boolean migrationNext(PaginationParams paginationParams,
+ MigrationSettings migrationSettings) {
+ if(migrationSettings.getSchema() == null
+ || !migrationSettings.getSchema().exists()
+ || paginationParams.getOffset() < 0
+ || paginationParams.getLimit() <= 0
+ || migrationSettings.getToRead() == null) {
+ LOG.warn(migrationSettings);
+ return false;
+ }
+ AtomicBoolean exportedToCsv = new AtomicBoolean(false);
+ AtomicInteger rowsImported = new AtomicInteger(0);
+ createAccessDir(migrationSettings.getToRead())
+ .flatMap(dir -> exportToCsv(dir, paginationParams, migrationSettings.getToRead()))
+ .ifPresent(csv -> {
+ exportedToCsv.set(true);
+ try {
+ if (csv.length() > 0) {
+ int imported = importToQuestDb(csv, migrationSettings, paginationParams.getDataPointId());
+ rowsImported.set(imported);
+ }
+ } finally {
+ delete(csv);
+ }
+ });
+ LOG.info("last offset: " + (paginationParams.getOffset() + rowsImported.get()));
+ return exportedToCsv.get() && rowsImported.get() == paginationParams.getLimit();
+ }
+
+
+ private static Optional createAccessDir(JdbcOperations jdbcOperations) {
+ try {
+ String secureFilePrivQuery = "SHOW VARIABLES LIKE \"secure_file_priv\"";
+ String secureFilePriv = jdbcOperations.query(secureFilePrivQuery, (resultSet, i) -> resultSet.getString("Value")).get(0);
+ if(secureFilePriv == null || "NULL".equalsIgnoreCase(secureFilePriv)) {
+ LOG.warn("The secure_file_priv constant in the mysql database has an empty null value set, this blocked the database from being exported to a file, set a value for this constant other than null:\n" +
+ "1) Setting the value to empty will make it possible to export to any location;\n" +
+ "2) Setting this parameter to a specific path will force the use of that particular path;\n" +
+ "\n" +
+ "The change requires a mysql server restart. Applies to mysql version 5.7.");
+ return Optional.empty();
+ }
+ String tempDir = System.getProperty("java.io.tmpdir");
+ if((tempDir != null && tempDir.equals(secureFilePriv)) || secureFilePriv.isEmpty()) {
+ File file = File.createTempFile("pointValues", ".csv");
+ delete(file);
+ return Optional.of(changePath(file));
+ }
+ File file = new File(secureFilePriv + File.separator + "pointValues" + System.currentTimeMillis() + ".csv");
+ return Optional.of(changePath(file));
+ } catch (Exception e) {
+ LOG.error(e.getMessage(), e);
+ return Optional.empty();
+ }
+ }
+
+ private static String changePath(File file) {
+ if(file.getAbsolutePath().contains("\\")) {
+ return file.getAbsolutePath().replace("\\", "\\\\");
+ }
+ return file.getAbsolutePath();
+ }
+
+ public static Optional exportToCsv(String csv,
+ PaginationParams paginationParams,
+ JdbcOperations jdbcOperations) {
+ String query = "SELECT * FROM (" +
+ " SELECT 'timestamp', 'ts', 'pointValue', 'metaData', 'dataType' UNION ALL" +
+ " (" +
+ " SELECT timestamp, ts, pointValue, metaData, dataType " +
+ " FROM pointValuesDenormalized WHERE dataPointId = " + paginationParams.getDataPointId() + " LIMIT " + paginationParams.getLimit() + " OFFSET " + paginationParams.getOffset() + " " +
+ " )" +
+ ") result INTO OUTFILE '" + csv + "' FIELDS TERMINATED BY '\t' ENCLOSED BY '' LINES TERMINATED BY '\r\n';";
+
+ try {
+ jdbcOperations.execute(query);
+ LOG.info("MySql export finished: OK");
+ return Optional.of(new File(csv));
+ } catch (Exception ex) {
+ LOG.info("MySql export finished: ERROR");
+ LOG.error(ex.getMessage(), ex);
+ return Optional.empty();
+ }
+ }
+
+ public static int importToQuestDb(File csv, MigrationSettings migrationSettings, int dataPointId) {
+ try {
+ PostMethod postMethod = new PostMethod("http://localhost:9000/imp?name=pointValues" + dataPointId + "×tamp=timestamp&partitionBy=MONTH&overwrite=" + migrationSettings.isOverwrite());
+ MultipartRequestEntity entity = new MultipartRequestEntity(new Part[]{new FilePart("schema", migrationSettings.getSchema()), new FilePart("data", csv)}, postMethod.getParams());
+ postMethod.setRequestEntity(entity);
+ MultiThreadedHttpConnectionManager manager = new MultiThreadedHttpConnectionManager();
+ HttpClient httpClient = new HttpClient(manager);
+ httpClient.executeMethod(postMethod);
+ String response = postMethod.getResponseBodyAsString();
+ LOG.info("QuestDb import finished: \r\n" + response);
+ return parseRowsImported(response);
+ } catch (Exception ex) {
+ LOG.error(ex.getMessage(), ex);
+ return -3;
+ }
+ }
+
+ private static int parseRowsImported(String response) {
+ String[] lines = response.split("\r\n");
+ for(String line: lines) {
+ if(line.contains("Rows imported")) {
+ LOG.info(line);
+ String[] cols = line.split("([|])");
+ try {
+ return Integer.parseInt(cols[2].trim());
+ } catch (Exception ex) {
+ return -1;
+ }
+ }
+ }
+ return -2;
+ }
+
+ public static void delete(File csv) {
+ try {
+ Files.delete(csv.toPath());
+ } catch (Exception e) {
+ LOG.error(e.getMessage(), e);
+ }
+ }
+
+ private static class PaginationParams {
+ int offset;
+ int limit;
+ int dataPointId;
+
+ private PaginationParams(int offset, int limit, int dataPointId) {
+ this.offset = offset;
+ this.limit = limit;
+ this.dataPointId = dataPointId;
+ }
+
+ public static PaginationParams params(int offset, int limit, int dataPointId) {
+ return new PaginationParams(offset, limit, dataPointId);
+ }
+
+ public int getOffset() {
+ return offset;
+ }
+
+ public int getLimit() {
+ return limit;
+ }
+
+ public int getDataPointId() {
+ return dataPointId;
+ }
+
+ @Override
+ public String toString() {
+ return "PaginationParams{" +
+ "offset=" + offset +
+ ", limit=" + limit +
+ '}';
+ }
+ }
+
+ private static class MigrationSettings {
+ File schema;
+ JdbcOperations toRead;
+ boolean overwrite;
+
+ private MigrationSettings(File schema, JdbcOperations toRead, boolean overwrite) {
+ this.schema = schema;
+ this.toRead = toRead;
+ this.overwrite = overwrite;
+ }
+
+ public static MigrationSettings.Builder builder() {
+ return new MigrationSettings.Builder();
+ }
+
+ private static class Builder {
+ File schema;
+ JdbcOperations toRead;
+ boolean overwrite;
+
+ public Builder schema(File schema) {
+ this.schema = schema;
+ return this;
+ }
+
+ public Builder toRead(JdbcOperations toRead) {
+ this.toRead = toRead;
+ return this;
+ }
+
+ public Builder overwrite(boolean overwrite) {
+ this.overwrite = overwrite;
+ return this;
+ }
+
+ public MigrationSettings build() {
+ return new MigrationSettings(schema, toRead, overwrite);
+ }
+ }
+
+ public File getSchema() {
+ return schema;
+ }
+
+ public JdbcOperations getToRead() {
+ return toRead;
+ }
+
+ public boolean isOverwrite() {
+ return overwrite;
+ }
+
+ @Override
+ public String toString() {
+ return "MigrationSettings{" +
+ "schema=" + schema +
+ ", jdbcOperations=" + toRead.getClass().getSimpleName() +
+ ", overwrite=" + overwrite +
+ '}';
+ }
+ }
+}
diff --git a/src/org/scada_lts/dao/model/point/PointValueAdnnotation.java b/src/org/scada_lts/dao/model/point/PointValueAdnnotation.java
index 205aaaeb18..b0365e1ad6 100644
--- a/src/org/scada_lts/dao/model/point/PointValueAdnnotation.java
+++ b/src/org/scada_lts/dao/model/point/PointValueAdnnotation.java
@@ -17,7 +17,9 @@
*/
package org.scada_lts.dao.model.point;
-/**
+import com.serotonin.mango.rt.dataImage.SetPointSource;
+
+/**
* Bean
*
* @author grzegorz bylica Abil'I.T. development team, sdt@abilit.eu
@@ -45,6 +47,29 @@ public PointValueAdnnotation(long id, String shortString, String longString, Int
this.sourceType = sourceType;
this.sourceId = sourceId;
}
+
+ public static PointValueAdnnotation newInstance(String svalue, SetPointSource source, long pointValueId) {
+ Integer sourceType = null, sourceId = null;
+ if (source != null) {
+ sourceType = source.getSetPointSourceType();
+ sourceId = source.getSetPointSourceId();
+ } else {
+ sourceType = SetPointSource.Types.UNKNOWN;
+ sourceId = 1;
+ }
+
+ String shortString = null;
+ String longString = null;
+ if (svalue != null) {
+ if (svalue.length() > 128)
+ longString = svalue;
+ else
+ shortString = svalue;
+ }
+ return new PointValueAdnnotation(pointValueId, shortString, longString, sourceType, sourceId);
+ }
+
+
public String getChangeOwner() {
return changeOwner;
}
diff --git a/src/org/scada_lts/dao/pointvalues/DataPointSimpleValue.java b/src/org/scada_lts/dao/pointvalues/DataPointSimpleValue.java
new file mode 100644
index 0000000000..25197b406b
--- /dev/null
+++ b/src/org/scada_lts/dao/pointvalues/DataPointSimpleValue.java
@@ -0,0 +1,17 @@
+package org.scada_lts.dao.pointvalues;
+
+public class DataPointSimpleValue {
+ int pointId;
+ double value;
+ long timestamp;
+
+ DataPointSimpleValue(int pointId, double value, long timestamp) {
+ this.pointId = pointId;
+ this.value = value;
+ this.timestamp = timestamp;
+ }
+
+ public long getTimestamp() {
+ return timestamp;
+ }
+}
\ No newline at end of file
diff --git a/src/org/scada_lts/dao/pointvalues/IAmChartDAO.java b/src/org/scada_lts/dao/pointvalues/IAmChartDAO.java
new file mode 100644
index 0000000000..0489869357
--- /dev/null
+++ b/src/org/scada_lts/dao/pointvalues/IAmChartDAO.java
@@ -0,0 +1,14 @@
+package org.scada_lts.dao.pointvalues;
+
+import com.serotonin.mango.vo.DataPointVO;
+
+import java.util.List;
+import java.util.Map;
+
+public interface IAmChartDAO {
+ List