+++ /dev/null
-/**
- * Copyright (c) 2010-2022 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.persistence.jdbc;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-
-/**
- * This class represents a checked item/table relation.
- *
- * @author Jacob Laursen - Initial contribution
- */
-@NonNullByDefault
-public class ItemTableCheckEntry {
- private String itemName;
- private String tableName;
- private ItemTableCheckEntryStatus status;
-
- public ItemTableCheckEntry(String itemName, String tableName, ItemTableCheckEntryStatus status) {
- this.itemName = itemName;
- this.tableName = tableName;
- this.status = status;
- }
-
- public String getItemName() {
- return itemName;
- }
-
- public String getTableName() {
- return tableName;
- }
-
- public ItemTableCheckEntryStatus getStatus() {
- return status;
- }
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.persistence.jdbc;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-
-/**
- * This class represents status for an {@link ItemTableCheckEntry}.
- *
- * @author Jacob Laursen - Initial contribution
- */
-@NonNullByDefault
-public enum ItemTableCheckEntryStatus {
- /**
- * Table is consistent.
- */
- VALID {
- @Override
- public String toString() {
- return "Valid";
- }
- },
- /**
- * Table has no corresponding item.
- */
- ITEM_MISSING {
- @Override
- public String toString() {
- return "Item missing";
- }
- },
- /**
- * Referenced table does not exist.
- */
- TABLE_MISSING {
- @Override
- public String toString() {
- return "Table missing";
- }
- },
- /**
- * Referenced table does not exist nor has corresponding item.
- */
- ITEM_AND_TABLE_MISSING {
- @Override
- public String toString() {
- return "Item and table missing";
- }
- },
- /**
- * Mapping for table does not exist in index.
- */
- ORPHAN_TABLE {
- @Override
- public String toString() {
- return "Orphan table";
- }
- }
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.persistence.jdbc.console;
-
-import java.util.Arrays;
-import java.util.Comparator;
-import java.util.List;
-import java.util.stream.Stream;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.knowm.yank.exceptions.YankSQLException;
-import org.openhab.core.io.console.Console;
-import org.openhab.core.io.console.ConsoleCommandCompleter;
-import org.openhab.core.io.console.StringsCompleter;
-import org.openhab.core.io.console.extensions.AbstractConsoleCommandExtension;
-import org.openhab.core.io.console.extensions.ConsoleCommandExtension;
-import org.openhab.core.persistence.PersistenceService;
-import org.openhab.core.persistence.PersistenceServiceRegistry;
-import org.openhab.persistence.jdbc.ItemTableCheckEntry;
-import org.openhab.persistence.jdbc.ItemTableCheckEntryStatus;
-import org.openhab.persistence.jdbc.internal.JdbcPersistenceService;
-import org.openhab.persistence.jdbc.internal.JdbcPersistenceServiceConstants;
-import org.osgi.service.component.annotations.Activate;
-import org.osgi.service.component.annotations.Component;
-import org.osgi.service.component.annotations.Reference;
-
-/**
- * The {@link JdbcCommandExtension} is responsible for handling console commands
- *
- * @author Jacob Laursen - Initial contribution
- */
-@NonNullByDefault
-@Component(service = ConsoleCommandExtension.class)
-public class JdbcCommandExtension extends AbstractConsoleCommandExtension implements ConsoleCommandCompleter {
-
- private static final String CMD_TABLES = "tables";
- private static final String SUBCMD_TABLES_LIST = "list";
- private static final String SUBCMD_TABLES_CLEAN = "clean";
- private static final String PARAMETER_ALL = "all";
- private static final String PARAMETER_FORCE = "force";
- private static final StringsCompleter CMD_COMPLETER = new StringsCompleter(List.of(CMD_TABLES), false);
- private static final StringsCompleter SUBCMD_TABLES_COMPLETER = new StringsCompleter(
- List.of(SUBCMD_TABLES_LIST, SUBCMD_TABLES_CLEAN), false);
-
- private final PersistenceServiceRegistry persistenceServiceRegistry;
-
- @Activate
- public JdbcCommandExtension(final @Reference PersistenceServiceRegistry persistenceServiceRegistry) {
- super(JdbcPersistenceServiceConstants.SERVICE_ID, "Interact with the JDBC persistence service.");
- this.persistenceServiceRegistry = persistenceServiceRegistry;
- }
-
- @Override
- public void execute(String[] args, Console console) {
- if (args.length < 2 || args.length > 4 || !CMD_TABLES.equals(args[0])) {
- printUsage(console);
- return;
- }
- JdbcPersistenceService persistenceService = getPersistenceService();
- if (persistenceService == null) {
- return;
- }
- try {
- if (!execute(persistenceService, args, console)) {
- printUsage(console);
- return;
- }
- } catch (YankSQLException e) {
- console.println(e.toString());
- }
- }
-
- private @Nullable JdbcPersistenceService getPersistenceService() {
- for (PersistenceService persistenceService : persistenceServiceRegistry.getAll()) {
- if (persistenceService instanceof JdbcPersistenceService) {
- return (JdbcPersistenceService) persistenceService;
- }
- }
- return null;
- }
-
- private boolean execute(JdbcPersistenceService persistenceService, String[] args, Console console) {
- if (SUBCMD_TABLES_LIST.equalsIgnoreCase(args[1])) {
- listTables(persistenceService, console, args.length == 3 && PARAMETER_ALL.equalsIgnoreCase(args[2]));
- return true;
- } else if (SUBCMD_TABLES_CLEAN.equalsIgnoreCase(args[1])) {
- if (args.length == 3) {
- cleanupItem(persistenceService, console, args[2], false);
- return true;
- } else if (args.length == 4 && PARAMETER_FORCE.equalsIgnoreCase(args[3])) {
- cleanupItem(persistenceService, console, args[2], true);
- return true;
- } else {
- cleanupTables(persistenceService, console);
- return true;
- }
- }
- return false;
- }
-
- private void listTables(JdbcPersistenceService persistenceService, Console console, Boolean all) {
- List<ItemTableCheckEntry> entries = persistenceService.getCheckedEntries();
- if (!all) {
- entries.removeIf(t -> t.getStatus() == ItemTableCheckEntryStatus.VALID);
- }
- entries.sort(Comparator.comparing(ItemTableCheckEntry::getTableName));
- // FIXME: NoSuchElement when empty table - because of get()
- int itemNameMaxLength = Math
- .max(entries.stream().map(t -> t.getItemName().length()).max(Integer::compare).get(), 4);
- int tableNameMaxLength = Math
- .max(entries.stream().map(t -> t.getTableName().length()).max(Integer::compare).get(), 5);
- int statusMaxLength = Stream.of(ItemTableCheckEntryStatus.values()).map(t -> t.toString().length())
- .max(Integer::compare).get();
- console.println(String.format(
- "%1$-" + (tableNameMaxLength + 2) + "sRow Count %2$-" + (itemNameMaxLength + 2) + "s%3$s", "Table",
- "Item", "Status"));
- console.println("-".repeat(tableNameMaxLength) + " " + "--------- " + "-".repeat(itemNameMaxLength) + " "
- + "-".repeat(statusMaxLength));
- for (ItemTableCheckEntry entry : entries) {
- String tableName = entry.getTableName();
- ItemTableCheckEntryStatus status = entry.getStatus();
- long rowCount = status == ItemTableCheckEntryStatus.VALID
- || status == ItemTableCheckEntryStatus.ITEM_MISSING ? persistenceService.getRowCount(tableName) : 0;
- console.println(String.format(
- "%1$-" + (tableNameMaxLength + 2) + "s%2$9d %3$-" + (itemNameMaxLength + 2) + "s%4$s", tableName,
- rowCount, entry.getItemName(), status));
- }
- }
-
- private void cleanupTables(JdbcPersistenceService persistenceService, Console console) {
- console.println("Cleaning up all inconsistent items...");
- List<ItemTableCheckEntry> entries = persistenceService.getCheckedEntries();
- entries.removeIf(t -> t.getStatus() == ItemTableCheckEntryStatus.VALID || t.getItemName().isEmpty());
- for (ItemTableCheckEntry entry : entries) {
- console.print(entry.getItemName() + " -> ");
- if (persistenceService.cleanupItem(entry)) {
- console.println("done.");
- } else {
- console.println("skipped/failed.");
- }
- }
- }
-
- private void cleanupItem(JdbcPersistenceService persistenceService, Console console, String itemName,
- boolean force) {
- console.print("Cleaning up item " + itemName + "... ");
- if (persistenceService.cleanupItem(itemName, force)) {
- console.println("done.");
- } else {
- console.println("skipped/failed.");
- }
- }
-
- @Override
- public List<String> getUsages() {
- return Arrays.asList(
- buildCommandUsage(CMD_TABLES + " " + SUBCMD_TABLES_LIST + " [" + PARAMETER_ALL + "]",
- "list tables (all = include valid)"),
- buildCommandUsage(
- CMD_TABLES + " " + SUBCMD_TABLES_CLEAN + " [<itemName>]" + " [" + PARAMETER_FORCE + "]",
- "clean inconsistent items (remove from index and drop tables)"));
- }
-
- @Override
- public @Nullable ConsoleCommandCompleter getCompleter() {
- return this;
- }
-
- @Override
- public boolean complete(String[] args, int cursorArgumentIndex, int cursorPosition, List<String> candidates) {
- if (cursorArgumentIndex <= 0) {
- return CMD_COMPLETER.complete(args, cursorArgumentIndex, cursorPosition, candidates);
- } else if (cursorArgumentIndex == 1) {
- if (CMD_TABLES.equalsIgnoreCase(args[0])) {
- return SUBCMD_TABLES_COMPLETER.complete(args, cursorArgumentIndex, cursorPosition, candidates);
- }
- } else if (cursorArgumentIndex == 2) {
- if (CMD_TABLES.equalsIgnoreCase(args[0])) {
- if (SUBCMD_TABLES_CLEAN.equalsIgnoreCase(args[1])) {
- JdbcPersistenceService persistenceService = getPersistenceService();
- if (persistenceService != null) {
- return new StringsCompleter(persistenceService.getItemNames(), true).complete(args,
- cursorArgumentIndex, cursorPosition, candidates);
- }
- } else if (SUBCMD_TABLES_LIST.equalsIgnoreCase(args[1])) {
- new StringsCompleter(List.of(PARAMETER_ALL), false).complete(args, cursorArgumentIndex,
- cursorPosition, candidates);
- }
- }
- }
- return false;
- }
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.persistence.jdbc.db;
-
-import java.math.BigDecimal;
-import java.time.Instant;
-import java.time.LocalDateTime;
-import java.time.ZoneId;
-import java.time.ZonedDateTime;
-import java.time.format.DateTimeFormatter;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Properties;
-import java.util.stream.Collectors;
-
-import javax.measure.Quantity;
-import javax.measure.Unit;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.knowm.yank.Yank;
-import org.openhab.core.items.GroupItem;
-import org.openhab.core.items.Item;
-import org.openhab.core.library.items.ColorItem;
-import org.openhab.core.library.items.ContactItem;
-import org.openhab.core.library.items.DateTimeItem;
-import org.openhab.core.library.items.DimmerItem;
-import org.openhab.core.library.items.ImageItem;
-import org.openhab.core.library.items.NumberItem;
-import org.openhab.core.library.items.PlayerItem;
-import org.openhab.core.library.items.RollershutterItem;
-import org.openhab.core.library.items.SwitchItem;
-import org.openhab.core.library.types.DateTimeType;
-import org.openhab.core.library.types.DecimalType;
-import org.openhab.core.library.types.HSBType;
-import org.openhab.core.library.types.PercentType;
-import org.openhab.core.library.types.QuantityType;
-import org.openhab.core.library.types.RawType;
-import org.openhab.core.library.unit.Units;
-import org.openhab.core.persistence.FilterCriteria;
-import org.openhab.core.persistence.FilterCriteria.Ordering;
-import org.openhab.core.persistence.HistoricItem;
-import org.openhab.core.types.State;
-import org.openhab.core.types.TypeParser;
-import org.openhab.persistence.jdbc.dto.ItemVO;
-import org.openhab.persistence.jdbc.dto.ItemsVO;
-import org.openhab.persistence.jdbc.dto.JdbcHistoricItem;
-import org.openhab.persistence.jdbc.utils.DbMetaData;
-import org.openhab.persistence.jdbc.utils.StringUtilsExt;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Default Database Configuration class.
- *
- * @author Helmut Lehmeyer - Initial contribution
- */
-@NonNullByDefault
-public class JdbcBaseDAO {
- private final Logger logger = LoggerFactory.getLogger(JdbcBaseDAO.class);
-
- public final Properties databaseProps = new Properties();
- protected String urlSuffix = "";
- public final Map<String, String> sqlTypes = new HashMap<>();
-
- // Get Database Meta data
- protected @Nullable DbMetaData dbMeta;
-
- protected String sqlPingDB = "SELECT 1";
- protected String sqlGetDB = "SELECT DATABASE()";
- protected String sqlIfTableExists = "SHOW TABLES LIKE '#searchTable#'";
- protected String sqlCreateNewEntryInItemsTable = "INSERT INTO #itemsManageTable# (ItemName) VALUES ('#itemname#')";
- protected String sqlCreateItemsTableIfNot = "CREATE TABLE IF NOT EXISTS #itemsManageTable# (ItemId INT NOT NULL AUTO_INCREMENT,#colname# #coltype# NOT NULL,PRIMARY KEY (ItemId))";
- protected String sqlDropItemsTableIfExists = "DROP TABLE IF EXISTS #itemsManageTable#";
- protected String sqlDropTable = "DROP TABLE #tableName#";
- protected String sqlDeleteItemsEntry = "DELETE FROM #itemsManageTable# WHERE ItemName='#itemname#'";
- protected String sqlGetItemIDTableNames = "SELECT ItemId, ItemName FROM #itemsManageTable#";
- protected String sqlGetItemTables = "SELECT table_name FROM information_schema.tables WHERE table_type='BASE TABLE' AND table_schema='#jdbcUriDatabaseName#' AND NOT table_name='#itemsManageTable#'";
- protected String sqlCreateItemTable = "CREATE TABLE IF NOT EXISTS #tableName# (time #tablePrimaryKey# NOT NULL, value #dbType#, PRIMARY KEY(time))";
- protected String sqlInsertItemValue = "INSERT INTO #tableName# (TIME, VALUE) VALUES( #tablePrimaryValue#, ? ) ON DUPLICATE KEY UPDATE VALUE= ?";
- protected String sqlGetRowCount = "SELECT COUNT(*) FROM #tableName#";
-
- /********
- * INIT *
- ********/
- public JdbcBaseDAO() {
- initSqlTypes();
- initDbProps();
- }
-
- /**
- * ## Get high precision by fractal seconds, examples ##
- *
- * mysql > 5.5 + mariadb > 5.2:
- * DROP TABLE FractionalSeconds;
- * CREATE TABLE FractionalSeconds (time TIMESTAMP(3), value TIMESTAMP(3));
- * INSERT INTO FractionalSeconds (time, value) VALUES( NOW(3), '1999-01-09 20:11:11.126' );
- * SELECT time FROM FractionalSeconds ORDER BY time DESC LIMIT 1;
- *
- * mysql <= 5.5 + mariadb <= 5.2: !!! NO high precision and fractal seconds !!!
- * DROP TABLE FractionalSeconds;
- * CREATE TABLE FractionalSeconds (time TIMESTAMP, value TIMESTAMP);
- * INSERT INTO FractionalSeconds (time, value) VALUES( NOW(), '1999-01-09 20:11:11.126' );
- * SELECT time FROM FractionalSeconds ORDER BY time DESC LIMIT 1;
- *
- * derby:
- * DROP TABLE FractionalSeconds;
- * CREATE TABLE FractionalSeconds (time TIMESTAMP, value TIMESTAMP);
- * INSERT INTO FractionalSeconds (time, value) VALUES( CURRENT_TIMESTAMP, '1999-01-09 20:11:11.126' );
- * SELECT time, value FROM FractionalSeconds;
- *
- * H2 + postgreSQL + hsqldb:
- * DROP TABLE FractionalSeconds;
- * CREATE TABLE FractionalSeconds (time TIMESTAMP, value TIMESTAMP);
- * INSERT INTO FractionalSeconds (time, value) VALUES( NOW(), '1999-01-09 20:11:11.126' );
- * SELECT time, value FROM FractionalSeconds;
- *
- * Sqlite:
- * DROP TABLE FractionalSeconds;
- * CREATE TABLE FractionalSeconds (time TIMESTAMP, value TIMESTAMP);
- * INSERT INTO FractionalSeconds (time, value) VALUES( strftime('%Y-%m-%d %H:%M:%f' , 'now' , 'localtime'),
- * '1999-01-09 20:11:11.124' );
- * SELECT time FROM FractionalSeconds ORDER BY time DESC LIMIT 1;
- *
- */
-
- /**
- * INFO: http://www.java2s.com/Code/Java/Database-SQL-JDBC/StandardSQLDataTypeswithTheirJavaEquivalents.htm
- */
- private void initSqlTypes() {
- logger.debug("JDBC::initSqlTypes: Initialize the type array");
- sqlTypes.put("CALLITEM", "VARCHAR(200)");
- sqlTypes.put("COLORITEM", "VARCHAR(70)");
- sqlTypes.put("CONTACTITEM", "VARCHAR(6)");
- sqlTypes.put("DATETIMEITEM", "TIMESTAMP");
- sqlTypes.put("DIMMERITEM", "TINYINT");
- sqlTypes.put("IMAGEITEM", "VARCHAR(65500)");// jdbc max 21845
- sqlTypes.put("LOCATIONITEM", "VARCHAR(50)");
- sqlTypes.put("NUMBERITEM", "DOUBLE");
- sqlTypes.put("PLAYERITEM", "VARCHAR(20)");
- sqlTypes.put("ROLLERSHUTTERITEM", "TINYINT");
- sqlTypes.put("STRINGITEM", "VARCHAR(65500)");// jdbc max 21845
- sqlTypes.put("SWITCHITEM", "VARCHAR(6)");
- sqlTypes.put("tablePrimaryKey", "TIMESTAMP");
- sqlTypes.put("tablePrimaryValue", "NOW()");
- }
-
- /**
- * INFO: https://github.com/brettwooldridge/HikariCP
- *
- * driverClassName (used with jdbcUrl):
- * Derby: org.apache.derby.jdbc.EmbeddedDriver
- * H2: org.h2.Driver
- * HSQLDB: org.hsqldb.jdbcDriver
- * Jaybird: org.firebirdsql.jdbc.FBDriver
- * MariaDB: org.mariadb.jdbc.Driver
- * MySQL: com.mysql.cj.jdbc.Driver
- * MaxDB: com.sap.dbtech.jdbc.DriverSapDB
- * PostgreSQL: org.postgresql.Driver
- * SyBase: com.sybase.jdbc3.jdbc.SybDriver
- * SqLite: org.sqlite.JDBC
- *
- * dataSourceClassName (for alternative Configuration):
- * Derby: org.apache.derby.jdbc.ClientDataSource
- * H2: org.h2.jdbcx.JdbcDataSource
- * HSQLDB: org.hsqldb.jdbc.JDBCDataSource
- * Jaybird: org.firebirdsql.pool.FBSimpleDataSource
- * MariaDB, MySQL: org.mariadb.jdbc.MySQLDataSource
- * MaxDB: com.sap.dbtech.jdbc.DriverSapDB
- * PostgreSQL: org.postgresql.ds.PGSimpleDataSource
- * SyBase: com.sybase.jdbc4.jdbc.SybDataSource
- * SqLite: org.sqlite.SQLiteDataSource
- *
- * HikariPool - configuration Example:
- * allowPoolSuspension.............false
- * autoCommit......................true
- * catalog.........................
- * connectionInitSql...............
- * connectionTestQuery.............
- * connectionTimeout...............30000
- * dataSource......................
- * dataSourceClassName.............
- * dataSourceJNDI..................
- * dataSourceProperties............{password=<masked>}
- * driverClassName.................
- * healthCheckProperties...........{}
- * healthCheckRegistry.............
- * idleTimeout.....................600000
- * initializationFailFast..........true
- * isolateInternalQueries..........false
- * jdbc4ConnectionTest.............false
- * jdbcUrl.........................jdbc:mysql://192.168.0.1:3306/test
- * leakDetectionThreshold..........0
- * maxLifetime.....................1800000
- * maximumPoolSize.................10
- * metricRegistry..................
- * metricsTrackerFactory...........
- * minimumIdle.....................10
- * password........................<masked>
- * poolName........................HikariPool-0
- * readOnly........................false
- * registerMbeans..................false
- * scheduledExecutorService........
- * threadFactory...................
- * transactionIsolation............
- * username........................xxxx
- * validationTimeout...............5000
- */
- private void initDbProps() {
- // databaseProps.setProperty("dataSource.url", "jdbc:mysql://192.168.0.1:3306/test");
- // databaseProps.setProperty("dataSource.user", "test");
- // databaseProps.setProperty("dataSource.password", "test");
-
- // Most relevant Performance values
- // maximumPoolSize to 20, minimumIdle to 5, and idleTimeout to 2 minutes.
- // databaseProps.setProperty("maximumPoolSize", ""+maximumPoolSize);
- // databaseProps.setProperty("minimumIdle", ""+minimumIdle);
- // databaseProps.setProperty("idleTimeout", ""+idleTimeout);
- // databaseProps.setProperty("connectionTimeout",""+connectionTimeout);
- // databaseProps.setProperty("idleTimeout", ""+idleTimeout);
- // databaseProps.setProperty("maxLifetime", ""+maxLifetime);
- // databaseProps.setProperty("validationTimeout",""+validationTimeout);
- }
-
- public void initAfterFirstDbConnection() {
- logger.debug("JDBC::initAfterFirstDbConnection: Initializing step, after db is connected.");
- // Initialize sqlTypes, depending on DB version for example
- dbMeta = new DbMetaData();// get DB information
- }
-
- public Properties getConnectionProperties() {
- return new Properties(this.databaseProps);
- }
-
- /**************
- * ITEMS DAOs *
- **************/
- public @Nullable Integer doPingDB() {
- final @Nullable Integer result = Yank.queryScalar(sqlPingDB, Integer.class, null);
- return result;
- }
-
- public @Nullable String doGetDB() {
- final @Nullable String result = Yank.queryScalar(sqlGetDB, String.class, null);
- return result;
- }
-
- public boolean doIfTableExists(ItemsVO vo) {
- String sql = StringUtilsExt.replaceArrayMerge(sqlIfTableExists, new String[] { "#searchTable#" },
- new String[] { vo.getItemsManageTable() });
- logger.debug("JDBC::doIfTableExists sql={}", sql);
- final @Nullable String result = Yank.queryScalar(sql, String.class, null);
- return Objects.nonNull(result);
- }
-
- public boolean doIfTableExists(String tableName) {
- String sql = StringUtilsExt.replaceArrayMerge(sqlIfTableExists, new String[] { "#searchTable#" },
- new String[] { tableName });
- logger.debug("JDBC::doIfTableExists sql={}", sql);
- final @Nullable String result = Yank.queryScalar(sql, String.class, null);
- return Objects.nonNull(result);
- }
-
- public Long doCreateNewEntryInItemsTable(ItemsVO vo) {
- String sql = StringUtilsExt.replaceArrayMerge(sqlCreateNewEntryInItemsTable,
- new String[] { "#itemsManageTable#", "#itemname#" },
- new String[] { vo.getItemsManageTable(), vo.getItemName() });
- logger.debug("JDBC::doCreateNewEntryInItemsTable sql={}", sql);
- return Yank.insert(sql, null);
- }
-
- public ItemsVO doCreateItemsTableIfNot(ItemsVO vo) {
- String sql = StringUtilsExt.replaceArrayMerge(sqlCreateItemsTableIfNot,
- new String[] { "#itemsManageTable#", "#colname#", "#coltype#" },
- new String[] { vo.getItemsManageTable(), vo.getColname(), vo.getColtype() });
- logger.debug("JDBC::doCreateItemsTableIfNot sql={}", sql);
- Yank.execute(sql, null);
- return vo;
- }
-
- public ItemsVO doDropItemsTableIfExists(ItemsVO vo) {
- String sql = StringUtilsExt.replaceArrayMerge(sqlDropItemsTableIfExists, new String[] { "#itemsManageTable#" },
- new String[] { vo.getItemsManageTable() });
- logger.debug("JDBC::doDropItemsTableIfExists sql={}", sql);
- Yank.execute(sql, null);
- return vo;
- }
-
- public void doDropTable(String tableName) {
- String sql = StringUtilsExt.replaceArrayMerge(sqlDropTable, new String[] { "#tableName#" },
- new String[] { tableName });
- logger.debug("JDBC::doDropTable sql={}", sql);
- Yank.execute(sql, null);
- }
-
- public void doDeleteItemsEntry(ItemsVO vo) {
- String sql = StringUtilsExt.replaceArrayMerge(sqlDeleteItemsEntry,
- new String[] { "#itemsManageTable#", "#itemname#" },
- new String[] { vo.getItemsManageTable(), vo.getItemName() });
- logger.debug("JDBC::doDeleteItemsEntry sql={}", sql);
- Yank.execute(sql, null);
- }
-
- public List<ItemsVO> doGetItemIDTableNames(ItemsVO vo) {
- String sql = StringUtilsExt.replaceArrayMerge(sqlGetItemIDTableNames, new String[] { "#itemsManageTable#" },
- new String[] { vo.getItemsManageTable() });
- logger.debug("JDBC::doGetItemIDTableNames sql={}", sql);
- return Yank.queryBeanList(sql, ItemsVO.class, null);
- }
-
- public List<ItemsVO> doGetItemTables(ItemsVO vo) {
- String sql = StringUtilsExt.replaceArrayMerge(sqlGetItemTables,
- new String[] { "#jdbcUriDatabaseName#", "#itemsManageTable#" },
- new String[] { vo.getJdbcUriDatabaseName(), vo.getItemsManageTable() });
- logger.debug("JDBC::doGetItemTables sql={}", sql);
- return Yank.queryBeanList(sql, ItemsVO.class, null);
- }
-
- /*************
- * ITEM DAOs *
- *************/
- public void doUpdateItemTableNames(List<ItemVO> vol) {
- logger.debug("JDBC::doUpdateItemTableNames vol.size = {}", vol.size());
- for (ItemVO itemTable : vol) {
- String sql = updateItemTableNamesProvider(itemTable);
- Yank.execute(sql, null);
- }
- }
-
- public void doCreateItemTable(ItemVO vo) {
- String sql = StringUtilsExt.replaceArrayMerge(sqlCreateItemTable,
- new String[] { "#tableName#", "#dbType#", "#tablePrimaryKey#" },
- new String[] { vo.getTableName(), vo.getDbType(), sqlTypes.get("tablePrimaryKey") });
- logger.debug("JDBC::doCreateItemTable sql={}", sql);
- Yank.execute(sql, null);
- }
-
- public void doStoreItemValue(Item item, State itemState, ItemVO vo) {
- ItemVO storedVO = storeItemValueProvider(item, itemState, vo);
- String sql = StringUtilsExt.replaceArrayMerge(sqlInsertItemValue,
- new String[] { "#tableName#", "#tablePrimaryValue#" },
- new String[] { storedVO.getTableName(), sqlTypes.get("tablePrimaryValue") });
- Object[] params = { storedVO.getValue(), storedVO.getValue() };
- logger.debug("JDBC::doStoreItemValue sql={} value='{}'", sql, storedVO.getValue());
- Yank.execute(sql, params);
- }
-
- public void doStoreItemValue(Item item, State itemState, ItemVO vo, ZonedDateTime date) {
- ItemVO storedVO = storeItemValueProvider(item, itemState, vo);
- String sql = StringUtilsExt.replaceArrayMerge(sqlInsertItemValue,
- new String[] { "#tableName#", "#tablePrimaryValue#" }, new String[] { storedVO.getTableName(), "?" });
- java.sql.Timestamp timestamp = new java.sql.Timestamp(date.toInstant().toEpochMilli());
- Object[] params = { timestamp, storedVO.getValue(), storedVO.getValue() };
- logger.debug("JDBC::doStoreItemValue sql={} timestamp={} value='{}'", sql, timestamp, storedVO.getValue());
- Yank.execute(sql, params);
- }
-
- public List<HistoricItem> doGetHistItemFilterQuery(Item item, FilterCriteria filter, int numberDecimalcount,
- String table, String name, ZoneId timeZone) {
- String sql = histItemFilterQueryProvider(filter, numberDecimalcount, table, name, timeZone);
- logger.debug("JDBC::doGetHistItemFilterQuery sql={}", sql);
- List<Object[]> m = Yank.queryObjectArrays(sql, null);
- if (m == null) {
- logger.debug("JDBC::doGetHistItemFilterQuery Query failed. Returning an empty list.");
- return List.of();
- }
- // we already retrieve the unit here once as it is a very costly operation
- String itemName = item.getName();
- Unit<? extends Quantity<?>> unit = item instanceof NumberItem ? ((NumberItem) item).getUnit() : null;
- return m.stream()
- .map(o -> new JdbcHistoricItem(itemName, objectAsState(item, unit, o[1]), objectAsZonedDateTime(o[0])))
- .collect(Collectors.<HistoricItem> toList());
- }
-
- public void doDeleteItemValues(FilterCriteria filter, String table, ZoneId timeZone) {
- String sql = histItemFilterDeleteProvider(filter, table, timeZone);
- logger.debug("JDBC::doDeleteItemValues sql={}", sql);
- Yank.execute(sql, null);
- }
-
- public long doGetRowCount(String tableName) {
- final String sql = StringUtilsExt.replaceArrayMerge(sqlGetRowCount, new String[] { "#tableName#" },
- new String[] { tableName });
- logger.debug("JDBC::doGetRowCount sql={}", sql);
- final @Nullable Long result = Yank.queryScalar(sql, Long.class, null);
- return Objects.requireNonNullElse(result, 0L);
- }
-
- /*************
- * Providers *
- *************/
- static final DateTimeFormatter JDBC_DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
-
- protected String histItemFilterQueryProvider(FilterCriteria filter, int numberDecimalcount, String table,
- String simpleName, ZoneId timeZone) {
- logger.debug(
- "JDBC::getHistItemFilterQueryProvider filter = {}, numberDecimalcount = {}, table = {}, simpleName = {}",
- filter, numberDecimalcount, table, simpleName);
-
- String filterString = resolveTimeFilter(filter, timeZone);
- filterString += (filter.getOrdering() == Ordering.ASCENDING) ? " ORDER BY time ASC" : " ORDER BY time DESC";
- if (filter.getPageSize() != Integer.MAX_VALUE) {
- filterString += " LIMIT " + filter.getPageNumber() * filter.getPageSize() + "," + filter.getPageSize();
- }
- // SELECT time, ROUND(value,3) FROM number_item_0114 ORDER BY time DESC LIMIT 0,1
- // rounding HALF UP
- String queryString = "NUMBERITEM".equalsIgnoreCase(simpleName) && numberDecimalcount > -1
- ? "SELECT time, ROUND(value," + numberDecimalcount + ") FROM " + table
- : "SELECT time, value FROM " + table;
- if (!filterString.isEmpty()) {
- queryString += filterString;
- }
- logger.debug("JDBC::query queryString = {}", queryString);
- return queryString;
- }
-
- protected String histItemFilterDeleteProvider(FilterCriteria filter, String table, ZoneId timeZone) {
- logger.debug("JDBC::histItemFilterDeleteProvider filter = {}, table = {}", filter, table);
-
- String filterString = resolveTimeFilter(filter, timeZone);
- String deleteString = filterString.isEmpty() ? "TRUNCATE TABLE " + table
- : "DELETE FROM " + table + filterString;
- logger.debug("JDBC::delete deleteString = {}", deleteString);
- return deleteString;
- }
-
- protected String resolveTimeFilter(FilterCriteria filter, ZoneId timeZone) {
- String filterString = "";
- if (filter.getBeginDate() != null) {
- filterString += filterString.isEmpty() ? " WHERE" : " AND";
- filterString += " TIME>'" + JDBC_DATE_FORMAT.format(filter.getBeginDate().withZoneSameInstant(timeZone))
- + "'";
- }
- if (filter.getEndDate() != null) {
- filterString += filterString.isEmpty() ? " WHERE" : " AND";
- filterString += " TIME<'" + JDBC_DATE_FORMAT.format(filter.getEndDate().withZoneSameInstant(timeZone))
- + "'";
- }
- return filterString;
- }
-
- private String updateItemTableNamesProvider(ItemVO itemTable) {
- String queryString = "ALTER TABLE " + itemTable.getTableName() + " RENAME TO " + itemTable.getNewTableName();
- logger.debug("JDBC::query queryString = {}", queryString);
- return queryString;
- }
-
- protected ItemVO storeItemValueProvider(Item item, State itemState, ItemVO vo) {
- String itemType = getItemType(item);
-
- logger.debug("JDBC::storeItemValueProvider: item '{}' as Type '{}' in '{}' with state '{}'", item.getName(),
- itemType, vo.getTableName(), itemState);
-
- // insertItemValue
- logger.debug("JDBC::storeItemValueProvider: itemState: '{}'", itemState);
- /*
- * !!ATTENTION!!
- *
- * 1. DimmerItem.getStateAs(PercentType.class).toString() always
- * returns 0
- * RollershutterItem.getStateAs(PercentType.class).toString() works
- * as expected
- *
- * 2. (item instanceof ColorItem) == (item instanceof DimmerItem) =
- * true Therefore for instance tests ColorItem always has to be
- * tested before DimmerItem
- *
- * !!ATTENTION!!
- */
- switch (itemType) {
- case "COLORITEM":
- vo.setValueTypes(getSqlTypes().get(itemType), java.lang.String.class);
- vo.setValue(itemState.toString());
- break;
- case "NUMBERITEM":
- State convertedState = itemState;
- if (item instanceof NumberItem && itemState instanceof QuantityType) {
- Unit<? extends Quantity<?>> unit = ((NumberItem) item).getUnit();
- if (unit != null && !Units.ONE.equals(unit)) {
- convertedState = ((QuantityType<?>) itemState).toUnit(unit);
- if (convertedState == null) {
- logger.warn(
- "JDBC::storeItemValueProvider: Failed to convert state '{}' to unit '{}'. Please check your item definition for correctness.",
- itemState, unit);
- convertedState = itemState;
- }
- }
- }
- String it = getSqlTypes().get(itemType);
- if (it == null) {
- logger.warn("JDBC::storeItemValueProvider: No SQL type defined for item type {}", itemType);
- } else if (it.toUpperCase().contains("DOUBLE")) {
- vo.setValueTypes(it, java.lang.Double.class);
- double value = ((Number) convertedState).doubleValue();
- logger.debug("JDBC::storeItemValueProvider: newVal.doubleValue: '{}'", value);
- vo.setValue(value);
- } else if (it.toUpperCase().contains("DECIMAL") || it.toUpperCase().contains("NUMERIC")) {
- vo.setValueTypes(it, java.math.BigDecimal.class);
- BigDecimal value = BigDecimal.valueOf(((Number) convertedState).doubleValue());
- logger.debug("JDBC::storeItemValueProvider: newVal.toBigDecimal: '{}'", value);
- vo.setValue(value);
- } else if (it.toUpperCase().contains("INT")) {
- vo.setValueTypes(it, java.lang.Integer.class);
- int value = ((Number) convertedState).intValue();
- logger.debug("JDBC::storeItemValueProvider: newVal.intValue: '{}'", value);
- vo.setValue(value);
- } else {// fall back to String
- vo.setValueTypes(it, java.lang.String.class);
- logger.warn("JDBC::storeItemValueProvider: itemState: '{}'", convertedState);
- vo.setValue(convertedState.toString());
- }
- break;
- case "ROLLERSHUTTERITEM":
- case "DIMMERITEM":
- vo.setValueTypes(getSqlTypes().get(itemType), java.lang.Integer.class);
- int value = ((DecimalType) itemState).intValue();
- logger.debug("JDBC::storeItemValueProvider: newVal.intValue: '{}'", value);
- vo.setValue(value);
- break;
- case "DATETIMEITEM":
- vo.setValueTypes(getSqlTypes().get(itemType), java.sql.Timestamp.class);
- java.sql.Timestamp d = new java.sql.Timestamp(
- ((DateTimeType) itemState).getZonedDateTime().toInstant().toEpochMilli());
- logger.debug("JDBC::storeItemValueProvider: DateTimeItem: '{}'", d);
- vo.setValue(d);
- break;
- case "IMAGEITEM":
- vo.setValueTypes(getSqlTypes().get(itemType), java.lang.String.class);
- String encodedString = item.getState().toFullString();
- logger.debug("JDBC::storeItemValueProvider: ImageItem: '{}'", encodedString);
- vo.setValue(encodedString);
- break;
- default:
- // All other items should return the best format by default
- vo.setValueTypes(getSqlTypes().get(itemType), java.lang.String.class);
- logger.debug("JDBC::storeItemValueProvider: other: itemState: '{}'", itemState);
- vo.setValue(itemState.toString());
- break;
- }
- return vo;
- }
-
- /*****************
- * H E L P E R S *
- *****************/
- protected State objectAsState(Item item, @Nullable Unit<? extends Quantity<?>> unit, Object v) {
- logger.debug(
- "JDBC::ItemResultHandler::handleResult getState value = '{}', unit = '{}', getClass = '{}', clazz = '{}'",
- v, unit, v.getClass(), v.getClass().getSimpleName());
- if (item instanceof NumberItem) {
- String it = getSqlTypes().get("NUMBERITEM");
- if (it == null) {
- throw new UnsupportedOperationException("No SQL type defined for item type NUMBERITEM");
- }
- if (it.toUpperCase().contains("DOUBLE")) {
- return unit == null ? new DecimalType(((Number) v).doubleValue())
- : QuantityType.valueOf(((Number) v).doubleValue(), unit);
- } else if (it.toUpperCase().contains("DECIMAL") || it.toUpperCase().contains("NUMERIC")) {
- return unit == null ? new DecimalType((BigDecimal) v)
- : QuantityType.valueOf(((BigDecimal) v).doubleValue(), unit);
- } else if (it.toUpperCase().contains("INT")) {
- return unit == null ? new DecimalType(objectAsInteger(v))
- : QuantityType.valueOf(((Integer) v).doubleValue(), unit);
- }
- return unit == null ? DecimalType.valueOf(objectAsString(v)) : QuantityType.valueOf(objectAsString(v));
- } else if (item instanceof DateTimeItem) {
- return new DateTimeType(objectAsZonedDateTime(v));
- } else if (item instanceof ColorItem) {
- return HSBType.valueOf(objectAsString(v));
- } else if (item instanceof DimmerItem || item instanceof RollershutterItem) {
- return new PercentType(objectAsInteger(v));
- } else if (item instanceof ImageItem) {
- return RawType.valueOf(objectAsString(v));
- } else if (item instanceof ContactItem || item instanceof PlayerItem || item instanceof SwitchItem) {
- State state = TypeParser.parseState(item.getAcceptedDataTypes(), ((String) v).toString().trim());
- if (state == null) {
- throw new UnsupportedOperationException("Unable to parse state for item " + item.toString());
- }
- return state;
- } else {
- State state = TypeParser.parseState(item.getAcceptedDataTypes(), ((String) v).toString());
- if (state == null) {
- throw new UnsupportedOperationException("Unable to parse state for item " + item.toString());
- }
- return state;
- }
- }
-
- protected ZonedDateTime objectAsZonedDateTime(Object v) {
- if (v instanceof Long) {
- return ZonedDateTime.ofInstant(Instant.ofEpochMilli(((Number) v).longValue()), ZoneId.systemDefault());
- } else if (v instanceof java.sql.Date) {
- return ZonedDateTime.ofInstant(Instant.ofEpochMilli(((java.sql.Date) v).getTime()), ZoneId.systemDefault());
- } else if (v instanceof LocalDateTime) {
- return ((LocalDateTime) v).atZone(ZoneId.systemDefault());
- } else if (v instanceof Instant) {
- return ((Instant) v).atZone(ZoneId.systemDefault());
- } else if (v instanceof java.sql.Timestamp) {
- return ((java.sql.Timestamp) v).toInstant().atZone(ZoneId.systemDefault());
- } else if (v instanceof java.lang.String) {
- return ZonedDateTime.ofInstant(java.sql.Timestamp.valueOf(v.toString()).toInstant(),
- ZoneId.systemDefault());
- }
- throw new UnsupportedOperationException("Date of type " + v.getClass().getName() + " is not supported");
- }
-
- protected Integer objectAsInteger(Object v) {
- if (v instanceof Byte) {
- return ((Byte) v).intValue();
- }
- return ((Integer) v).intValue();
- }
-
- protected String objectAsString(Object v) {
- if (v instanceof byte[]) {
- return new String((byte[]) v);
- }
- return ((String) v).toString();
- }
-
- public String getItemType(Item i) {
- Item item = i;
- String def = "STRINGITEM";
- if (i instanceof GroupItem) {
- item = ((GroupItem) i).getBaseItem();
- if (item == null) {
- // if GroupItem:<ItemType> is not defined in *.items using StringType
- logger.debug(
- "JDBC::getItemType: Cannot detect ItemType for {} because the GroupItems' base type isn't set in *.items File.",
- i.getName());
- Iterator<Item> iterator = ((GroupItem) i).getMembers().iterator();
- if (!iterator.hasNext()) {
- logger.debug(
- "JDBC::getItemType: No Child-Members of GroupItem {}, use ItemType for STRINGITEM as Fallback",
- i.getName());
- return def;
- }
- item = iterator.next();
- }
- }
- String itemType = item.getClass().getSimpleName().toUpperCase();
- logger.debug("JDBC::getItemType: Try to use ItemType {} for Item {}", itemType, i.getName());
- if (sqlTypes.get(itemType) == null) {
- logger.warn(
- "JDBC::getItemType: No sqlType found for ItemType {}, use ItemType for STRINGITEM as Fallback for {}",
- itemType, i.getName());
- return def;
- }
- return itemType;
- }
-
- /******************************
- * public Getters and Setters *
- ******************************/
- public Map<String, String> getSqlTypes() {
- return sqlTypes;
- }
-
- public String getDataType(Item item) {
- String dataType = sqlTypes.get(getItemType(item));
- if (dataType == null) {
- throw new UnsupportedOperationException("No data type found for " + getItemType(item));
- }
- return dataType;
- }
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.persistence.jdbc.db;
-
-import java.time.ZoneId;
-import java.util.List;
-import java.util.Objects;
-import java.util.stream.Collectors;
-
-import javax.measure.Quantity;
-import javax.measure.Unit;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.knowm.yank.Yank;
-import org.openhab.core.items.Item;
-import org.openhab.core.library.items.NumberItem;
-import org.openhab.core.persistence.FilterCriteria;
-import org.openhab.core.persistence.FilterCriteria.Ordering;
-import org.openhab.core.persistence.HistoricItem;
-import org.openhab.core.types.State;
-import org.openhab.persistence.jdbc.dto.ItemVO;
-import org.openhab.persistence.jdbc.dto.ItemsVO;
-import org.openhab.persistence.jdbc.dto.JdbcHistoricItem;
-import org.openhab.persistence.jdbc.utils.StringUtilsExt;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Extended Database Configuration class. Class represents
- * the extended database-specific configuration. Overrides and supplements the
- * default settings from JdbcBaseDAO. Enter only the differences to JdbcBaseDAO here.
- *
- * @author Helmut Lehmeyer - Initial contribution
- */
-@NonNullByDefault
-public class JdbcDerbyDAO extends JdbcBaseDAO {
- private static final String DRIVER_CLASS_NAME = org.apache.derby.jdbc.EmbeddedDriver.class.getName();
- @SuppressWarnings("unused")
- private static final String DATA_SOURCE_CLASS_NAME = org.apache.derby.jdbc.EmbeddedDataSource.class.getName();
-
- private final Logger logger = LoggerFactory.getLogger(JdbcDerbyDAO.class);
-
- /********
- * INIT *
- ********/
- public JdbcDerbyDAO() {
- initSqlTypes();
- initDbProps();
- initSqlQueries();
- }
-
- private void initSqlQueries() {
- logger.debug("JDBC::initSqlQueries: '{}'", this.getClass().getSimpleName());
- sqlPingDB = "values 1";
- sqlGetDB = "VALUES SYSCS_UTIL.SYSCS_GET_DATABASE_PROPERTY( 'DataDictionaryVersion' )"; // returns version
- sqlIfTableExists = "SELECT * FROM SYS.SYSTABLES WHERE TABLENAME='#searchTable#'";
- sqlCreateItemsTableIfNot = "CREATE TABLE #itemsManageTable# ( ItemId INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 1, INCREMENT BY 1), #colname# #coltype# NOT NULL)";
- sqlCreateItemTable = "CREATE TABLE #tableName# (time #tablePrimaryKey# NOT NULL, value #dbType#, PRIMARY KEY(time))";
- // Prevent error against duplicate time value (seldom): No powerful Merge found:
- // http://www.codeproject.com/Questions/162627/how-to-insert-new-record-in-my-table-if-not-exists
- sqlInsertItemValue = "INSERT INTO #tableName# (TIME, VALUE) VALUES( #tablePrimaryValue#, CAST( ? as #dbType#) )";
- }
-
- private void initSqlTypes() {
- sqlTypes.put("DATETIMEITEM", "TIMESTAMP");
- sqlTypes.put("DIMMERITEM", "SMALLINT");
- sqlTypes.put("IMAGEITEM", "VARCHAR(32000)");
- sqlTypes.put("ROLLERSHUTTERITEM", "SMALLINT");
- sqlTypes.put("STRINGITEM", "VARCHAR(32000)");
- sqlTypes.put("tablePrimaryValue", "CURRENT_TIMESTAMP");
- logger.debug("JDBC::initSqlTypes: Initialized the type array sqlTypes={}", sqlTypes.values());
- }
-
- /**
- * INFO: https://github.com/brettwooldridge/HikariCP
- */
- private void initDbProps() {
- // Properties for HikariCP
- // Use driverClassName
- databaseProps.setProperty("driverClassName", DRIVER_CLASS_NAME);
- // OR dataSourceClassName
- // databaseProps.setProperty("dataSourceClassName", DATA_SOURCE_CLASS_NAME);
- databaseProps.setProperty("maximumPoolSize", "1");
- databaseProps.setProperty("minimumIdle", "1");
- }
-
- @Override
- public void initAfterFirstDbConnection() {
- logger.debug("JDBC::initAfterFirstDbConnection: Initializing step, after db is connected.");
- // Initialize sqlTypes, depending on DB version for example
- // derby does not like this... dbMeta = new DbMetaData();// get DB information
- }
-
- /**************
- * ITEMS DAOs *
- **************/
- @Override
- public @Nullable Integer doPingDB() {
- return Yank.queryScalar(sqlPingDB, Integer.class, null);
- }
-
- @Override
- public boolean doIfTableExists(ItemsVO vo) {
- String sql = StringUtilsExt.replaceArrayMerge(sqlIfTableExists, new String[] { "#searchTable#" },
- new String[] { vo.getItemsManageTable().toUpperCase() });
- logger.debug("JDBC::doIfTableExists sql={}", sql);
- final @Nullable String result = Yank.queryScalar(sql, String.class, null);
- return Objects.nonNull(result);
- }
-
- @Override
- public Long doCreateNewEntryInItemsTable(ItemsVO vo) {
- String sql = StringUtilsExt.replaceArrayMerge(sqlCreateNewEntryInItemsTable,
- new String[] { "#itemsManageTable#", "#itemname#" },
- new String[] { vo.getItemsManageTable().toUpperCase(), vo.getItemName() });
- logger.debug("JDBC::doCreateNewEntryInItemsTable sql={}", sql);
- return Yank.insert(sql, null);
- }
-
- @Override
- public ItemsVO doCreateItemsTableIfNot(ItemsVO vo) {
- // boolean tableExists = Yank.queryScalar(SQL_IF_TABLE_EXISTS.replace("#searchTable#",
- // vo.getItemsManageTable().toUpperCase()), String.class, null) == null;
- boolean tableExists = doIfTableExists(vo);
- if (!tableExists) {
- String sql = StringUtilsExt.replaceArrayMerge(sqlCreateItemsTableIfNot,
- new String[] { "#itemsManageTable#", "#colname#", "#coltype#" },
- new String[] { vo.getItemsManageTable().toUpperCase(), vo.getColname(), vo.getColtype() });
- logger.debug("JDBC::doCreateItemsTableIfNot tableExists={} therefore sql={}", tableExists, sql);
- Yank.execute(sql, null);
- } else {
- logger.debug("JDBC::doCreateItemsTableIfNot tableExists={}, did not CREATE TABLE", tableExists);
- }
- return vo;
- }
-
- /*************
- * ITEM DAOs *
- *************/
- @Override
- public void doCreateItemTable(ItemVO vo) {
- String sql = StringUtilsExt.replaceArrayMerge(sqlCreateItemTable,
- new String[] { "#tableName#", "#dbType#", "#tablePrimaryKey#" },
- new String[] { vo.getTableName(), vo.getDbType(), sqlTypes.get("tablePrimaryKey") });
- Yank.execute(sql, null);
- }
-
- @Override
- public void doStoreItemValue(Item item, State itemState, ItemVO vo) {
- ItemVO storedVO = storeItemValueProvider(item, itemState, vo);
- String sql = StringUtilsExt.replaceArrayMerge(sqlInsertItemValue,
- new String[] { "#tableName#", "#dbType#", "#tablePrimaryValue#" },
- new String[] { storedVO.getTableName().toUpperCase(), storedVO.getDbType(),
- sqlTypes.get("tablePrimaryValue") });
- Object[] params = { storedVO.getValue() };
- logger.debug("JDBC::doStoreItemValue sql={} value='{}'", sql, storedVO.getValue());
- Yank.execute(sql, params);
- }
-
- @Override
- public List<HistoricItem> doGetHistItemFilterQuery(Item item, FilterCriteria filter, int numberDecimalcount,
- String table, String name, ZoneId timeZone) {
- String sql = histItemFilterQueryProvider(filter, numberDecimalcount, table, name, timeZone);
- List<Object[]> m = Yank.queryObjectArrays(sql, null);
- logger.debug("JDBC::doGetHistItemFilterQuery got Array length={}", m.size());
- // we already retrieve the unit here once as it is a very costly operation
- String itemName = item.getName();
- Unit<? extends Quantity<?>> unit = item instanceof NumberItem ? ((NumberItem) item).getUnit() : null;
- return m.stream().map(o -> {
- logger.debug("JDBC::doGetHistItemFilterQuery 0='{}' 1='{}'", o[0], o[1]);
- return new JdbcHistoricItem(itemName, objectAsState(item, unit, o[1]), objectAsZonedDateTime(o[0]));
- }).collect(Collectors.<HistoricItem> toList());
- }
-
- /****************************
- * SQL generation Providers *
- ****************************/
-
- @Override
- protected String histItemFilterQueryProvider(FilterCriteria filter, int numberDecimalcount, String table,
- String simpleName, ZoneId timeZone) {
- logger.debug(
- "JDBC::getHistItemFilterQueryProvider filter = {}, numberDecimalcount = {}, table = {}, simpleName = {}",
- StringUtilsExt.filterToString(filter), numberDecimalcount, table, simpleName);
-
- String filterString = "";
- if (filter.getBeginDate() != null) {
- filterString += filterString.isEmpty() ? " WHERE" : " AND";
- filterString += " TIME>'" + JDBC_DATE_FORMAT.format(filter.getBeginDate().withZoneSameInstant(timeZone))
- + "'";
- }
- if (filter.getEndDate() != null) {
- filterString += filterString.isEmpty() ? " WHERE" : " AND";
- filterString += " TIME<'" + JDBC_DATE_FORMAT.format(filter.getEndDate().withZoneSameInstant(timeZone))
- + "'";
- }
- filterString += (filter.getOrdering() == Ordering.ASCENDING) ? " ORDER BY time ASC" : " ORDER BY time DESC";
- if (filter.getPageSize() != 0x7fffffff) {
- // TODO: TESTING!!!
- // filterString += " LIMIT " + filter.getPageNumber() *
- // filter.getPageSize() + "," + filter.getPageSize();
- // SELECT time, value FROM ohscriptfiles_sw_ace_paths_0001 ORDER BY
- // time DESC OFFSET 1 ROWS FETCH NEXT 0 ROWS ONLY
- // filterString += " OFFSET " + filter.getPageSize() +" ROWS FETCH
- // FIRST||NEXT " + filter.getPageNumber() * filter.getPageSize() + "
- // ROWS ONLY";
- filterString += " OFFSET " + filter.getPageSize() + " ROWS FETCH FIRST "
- + (filter.getPageNumber() * filter.getPageSize() + 1) + " ROWS ONLY";
- }
-
- // http://www.seemoredata.com/en/showthread.php?132-Round-function-in-Apache-Derby
- // simulated round function in Derby: CAST(value 0.0005 AS DECIMAL(15,3))
- // simulated round function in Derby: "CAST(value 0.0005 AS DECIMAL(15,"+numberDecimalcount+"))"
-
- String queryString = "SELECT time,";
- if ("NUMBERITEM".equalsIgnoreCase(simpleName) && numberDecimalcount > -1) {
- // rounding HALF UP
- queryString += "CAST(value 0.";
- for (int i = 0; i < numberDecimalcount; i++) {
- queryString += "0";
- }
- queryString += "5 AS DECIMAL(31," + numberDecimalcount + "))"; // 31 is DECIMAL max precision
- // https://db.apache.org/derby/docs/10.0/manuals/develop/develop151.html
- } else {
- queryString += " value FROM " + table.toUpperCase();
- }
-
- if (!filterString.isEmpty()) {
- queryString += filterString;
- }
- logger.debug("JDBC::query queryString = {}", queryString);
- return queryString;
- }
-
- /*****************
- * H E L P E R S *
- *****************/
-
- /******************************
- * public Getters and Setters *
- ******************************/
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.persistence.jdbc.db;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.knowm.yank.Yank;
-import org.openhab.core.items.Item;
-import org.openhab.core.types.State;
-import org.openhab.persistence.jdbc.dto.ItemVO;
-import org.openhab.persistence.jdbc.utils.StringUtilsExt;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Extended Database Configuration class. Class represents
- * the extended database-specific configuration. Overrides and supplements the
- * default settings from JdbcBaseDAO. Enter only the differences to JdbcBaseDAO here.
- *
- * @author Helmut Lehmeyer - Initial contribution
- */
-@NonNullByDefault
-public class JdbcH2DAO extends JdbcBaseDAO {
- private static final String DRIVER_CLASS_NAME = org.h2.Driver.class.getName();
- @SuppressWarnings("unused")
- private static final String DATA_SOURCE_CLASS_NAME = org.h2.jdbcx.JdbcDataSource.class.getName();
-
- private final Logger logger = LoggerFactory.getLogger(JdbcH2DAO.class);
-
- /********
- * INIT *
- ********/
- public JdbcH2DAO() {
- initSqlQueries();
- initSqlTypes();
- initDbProps();
- }
-
- private void initSqlQueries() {
- logger.debug("JDBC::initSqlQueries: '{}'", this.getClass().getSimpleName());
- sqlIfTableExists = "SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME='#searchTable#'";
- // SQL_INSERT_ITEM_VALUE = "INSERT INTO #tableName# (TIME, VALUE) VALUES( NOW(), CAST( ? as #dbType#) )";
- // http://stackoverflow.com/questions/19768051/h2-sql-database-insert-if-the-record-does-not-exist
- sqlInsertItemValue = "MERGE INTO #tableName# (TIME, VALUE) VALUES( #tablePrimaryValue#, CAST( ? as #dbType#) )";
- }
-
- /**
- * INFO: http://www.java2s.com/Code/Java/Database-SQL-JDBC/StandardSQLDataTypeswithTheirJavaEquivalents.htm
- */
- private void initSqlTypes() {
- }
-
- /**
- * INFO: https://github.com/brettwooldridge/HikariCP
- */
- private void initDbProps() {
- // Properties for HikariCP
- databaseProps.setProperty("driverClassName", DRIVER_CLASS_NAME);
- // driverClassName OR BETTER USE dataSourceClassName
- // databaseProps.setProperty("dataSourceClassName", DATA_SOURCE_CLASS_NAME);
- }
-
- /**************
- * ITEMS DAOs *
- **************/
-
- /*************
- * ITEM DAOs *
- *************/
- @Override
- public void doStoreItemValue(Item item, State itemState, ItemVO vo) {
- ItemVO storedVO = storeItemValueProvider(item, itemState, vo);
- String sql = StringUtilsExt.replaceArrayMerge(sqlInsertItemValue,
- new String[] { "#tableName#", "#dbType#", "#tablePrimaryValue#" },
- new String[] { storedVO.getTableName(), storedVO.getDbType(), sqlTypes.get("tablePrimaryValue") });
- Object[] params = { storedVO.getValue() };
- logger.debug("JDBC::doStoreItemValue sql={} value='{}'", sql, storedVO.getValue());
- Yank.execute(sql, params);
- }
-
- /****************************
- * SQL generation Providers *
- ****************************/
-
- /*****************
- * H E L P E R S *
- *****************/
-
- /******************************
- * public Getters and Setters *
- ******************************/
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.persistence.jdbc.db;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.knowm.yank.Yank;
-import org.openhab.core.items.Item;
-import org.openhab.core.types.State;
-import org.openhab.persistence.jdbc.dto.ItemVO;
-import org.openhab.persistence.jdbc.dto.ItemsVO;
-import org.openhab.persistence.jdbc.utils.StringUtilsExt;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Extended Database Configuration class. Class represents
- * the extended database-specific configuration. Overrides and supplements the
- * default settings from JdbcBaseDAO. Enter only the differences to JdbcBaseDAO here.
- *
- * @author Helmut Lehmeyer - Initial contribution
- */
-@NonNullByDefault
-public class JdbcHsqldbDAO extends JdbcBaseDAO {
- private static final String DRIVER_CLASS_NAME = org.hsqldb.jdbcDriver.class.getName();
- @SuppressWarnings("unused")
- private static final String DATA_SOURCE_CLASS_NAME = org.hsqldb.jdbc.JDBCDataSource.class.getName();
-
- private final Logger logger = LoggerFactory.getLogger(JdbcHsqldbDAO.class);
-
- /********
- * INIT *
- ********/
- public JdbcHsqldbDAO() {
- initSqlQueries();
- initSqlTypes();
- initDbProps();
- }
-
- private void initSqlQueries() {
- logger.debug("JDBC::initSqlQueries: '{}'", this.getClass().getSimpleName());
- // http://hsqldb.org/doc/guide/builtinfunctions-chapt.html
- sqlPingDB = "SELECT 1 FROM INFORMATION_SCHEMA.SYSTEM_USERS";
- sqlGetDB = "SELECT DATABASE () FROM INFORMATION_SCHEMA.SYSTEM_USERS";
- sqlIfTableExists = "SELECT * FROM INFORMATION_SCHEMA.SYSTEM_TABLES WHERE TABLE_NAME='#searchTable#'";
- sqlCreateItemsTableIfNot = "CREATE TABLE IF NOT EXISTS #itemsManageTable# ( ItemId INT GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1) NOT NULL, #colname# #coltype# NOT NULL)";
- sqlCreateNewEntryInItemsTable = "INSERT INTO #itemsManageTable# (ItemName) VALUES ('#itemname#')";
- // Prevent error against duplicate time value
- // http://hsqldb.org/doc/guide/dataaccess-chapt.html#dac_merge_statement
- // SQL_INSERT_ITEM_VALUE = "INSERT INTO #tableName# (TIME, VALUE) VALUES( NOW(), CAST( ? as #dbType#) )";
- sqlInsertItemValue = "MERGE INTO #tableName# "
- + "USING (VALUES #tablePrimaryValue#, CAST( ? as #dbType#)) temp (TIME, VALUE) ON (#tableName#.TIME=temp.TIME) "
- + "WHEN NOT MATCHED THEN INSERT (TIME, VALUE) VALUES (temp.TIME, temp.VALUE)";
- }
-
- /**
- * INFO: http://www.java2s.com/Code/Java/Database-SQL-JDBC/StandardSQLDataTypeswithTheirJavaEquivalents.htm
- */
- private void initSqlTypes() {
- }
-
- /**
- * INFO: https://github.com/brettwooldridge/HikariCP
- */
- private void initDbProps() {
- // Properties for HikariCP
- databaseProps.setProperty("driverClassName", DRIVER_CLASS_NAME);
- // driverClassName OR BETTER USE dataSourceClassName
- // databaseProps.setProperty("dataSourceClassName", DATA_SOURCE_CLASS_NAME);
- }
-
- /**************
- * ITEMS DAOs *
- **************/
- @Override
- public @Nullable Integer doPingDB() {
- return Yank.queryScalar(sqlPingDB, Integer.class, null);
- }
-
- @Override
- public ItemsVO doCreateItemsTableIfNot(ItemsVO vo) {
- String sql = StringUtilsExt.replaceArrayMerge(sqlCreateItemsTableIfNot,
- new String[] { "#itemsManageTable#", "#colname#", "#coltype#", "#itemsManageTable#" },
- new String[] { vo.getItemsManageTable(), vo.getColname(), vo.getColtype(), vo.getItemsManageTable() });
- logger.debug("JDBC::doCreateItemsTableIfNot sql={}", sql);
- Yank.execute(sql, null);
- return vo;
- }
-
- @Override
- public Long doCreateNewEntryInItemsTable(ItemsVO vo) {
- String sql = StringUtilsExt.replaceArrayMerge(sqlCreateNewEntryInItemsTable,
- new String[] { "#itemsManageTable#", "#itemname#" },
- new String[] { vo.getItemsManageTable(), vo.getItemName() });
- logger.debug("JDBC::doCreateNewEntryInItemsTable sql={}", sql);
- return Yank.insert(sql, null);
- }
-
- /*************
- * ITEM DAOs *
- *************/
- @Override
- public void doStoreItemValue(Item item, State itemState, ItemVO vo) {
- ItemVO storedVO = storeItemValueProvider(item, itemState, vo);
- String sql = StringUtilsExt.replaceArrayMerge(sqlInsertItemValue,
- new String[] { "#tableName#", "#dbType#", "#tableName#", "#tablePrimaryValue#" },
- new String[] { storedVO.getTableName(), storedVO.getDbType(), storedVO.getTableName(),
- sqlTypes.get("tablePrimaryValue") });
- Object[] params = { storedVO.getValue() };
- logger.debug("JDBC::doStoreItemValue sql={} value='{}'", sql, storedVO.getValue());
- Yank.execute(sql, params);
- }
-
- /****************************
- * SQL generation Providers *
- ****************************/
-
- /*****************
- * H E L P E R S *
- *****************/
-
- /******************************
- * public Getters and Setters *
- ******************************/
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.persistence.jdbc.db;
-
-import java.util.Objects;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.knowm.yank.Yank;
-import org.openhab.persistence.jdbc.utils.DbMetaData;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Extended Database Configuration class. Class represents
- * the extended database-specific configuration. Overrides and supplements the
- * default settings from JdbcBaseDAO. Enter only the differences to JdbcBaseDAO here.
- *
- * @author Helmut Lehmeyer - Initial contribution
- */
-@NonNullByDefault
-public class JdbcMariadbDAO extends JdbcBaseDAO {
- private static final String DRIVER_CLASS_NAME = org.mariadb.jdbc.Driver.class.getName();
- @SuppressWarnings("unused")
- private static final String DATA_SOURCE_CLASS_NAME = org.mariadb.jdbc.MariaDbDataSource.class.getName();
-
- private final Logger logger = LoggerFactory.getLogger(JdbcMariadbDAO.class);
-
- /********
- * INIT *
- ********/
- public JdbcMariadbDAO() {
- initSqlTypes();
- initDbProps();
- initSqlQueries();
- }
-
- private void initSqlQueries() {
- logger.debug("JDBC::initSqlQueries: '{}'", this.getClass().getSimpleName());
- }
-
- /**
- * INFO: http://www.java2s.com/Code/Java/Database-SQL-JDBC/StandardSQLDataTypeswithTheirJavaEquivalents.htm
- */
- private void initSqlTypes() {
- logger.debug("JDBC::initSqlTypes: Initialize the type array");
- sqlTypes.put("IMAGEITEM", "VARCHAR(16255)");
- sqlTypes.put("STRINGITEM", "VARCHAR(16255)"); // MariaDB using utf-8 max = 16383, using 16383-128 = 16255
- }
-
- /**
- * INFO: https://github.com/brettwooldridge/HikariCP
- */
- private void initDbProps() {
- // Performancetuning
- databaseProps.setProperty("dataSource.cachePrepStmts", "true");
- databaseProps.setProperty("dataSource.prepStmtCacheSize", "250");
- databaseProps.setProperty("dataSource.prepStmtCacheSqlLimit", "2048");
- databaseProps.setProperty("dataSource.jdbcCompliantTruncation", "false");// jdbc standard max varchar max length
- // of 21845
-
- // Properties for HikariCP
- // Use driverClassName
- databaseProps.setProperty("driverClassName", DRIVER_CLASS_NAME);
- // driverClassName OR BETTER USE dataSourceClassName
- // databaseProps.setProperty("dataSourceClassName", DATA_SOURCE_CLASS_NAME);
- databaseProps.setProperty("maximumPoolSize", "3");
- databaseProps.setProperty("minimumIdle", "2");
- }
-
- @Override
- public void initAfterFirstDbConnection() {
- logger.debug("JDBC::initAfterFirstDbConnection: Initializing step, after db is connected.");
- DbMetaData dbMeta = new DbMetaData();
- this.dbMeta = dbMeta;
- // Initialize sqlTypes, depending on DB version for example
- if (dbMeta.isDbVersionGreater(5, 1)) {
- sqlTypes.put("DATETIMEITEM", "TIMESTAMP(3)");
- sqlTypes.put("tablePrimaryKey", "TIMESTAMP(3)");
- sqlTypes.put("tablePrimaryValue", "NOW(3)");
- }
- }
-
- /**************
- * ITEMS DAOs *
- **************/
- @Override
- public @Nullable Integer doPingDB() {
- final @Nullable Long result = Yank.queryScalar(sqlPingDB, Long.class, null);
- return Objects.nonNull(result) ? result.intValue() : null;
- }
-
- /*************
- * ITEM DAOs *
- *************/
-
- /****************************
- * SQL generation Providers *
- ****************************/
-
- /*****************
- * H E L P E R S *
- *****************/
-
- /******************************
- * public Getters and Setters *
- ******************************/
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.persistence.jdbc.db;
-
-import java.util.Objects;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.knowm.yank.Yank;
-import org.openhab.persistence.jdbc.utils.DbMetaData;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Extended Database Configuration class. Class represents
- * the extended database-specific configuration. Overrides and supplements the
- * default settings from JdbcBaseDAO. Enter only the differences to JdbcBaseDAO here.
- *
- * since driver version >= 6.0 sometimes timezone conversation is needed: ?serverTimezone=UTC
- * example: dbProps.setProperty("jdbcUrl", "jdbc:mysql://192.168.0.181:3306/ItemTypeTest3?serverTimezone=UTC");//mysql
- * 5.7
- *
- * @author Helmut Lehmeyer - Initial contribution
- */
-@NonNullByDefault
-public class JdbcMysqlDAO extends JdbcBaseDAO {
- private static final String DRIVER_CLASS_NAME = com.mysql.cj.jdbc.Driver.class.getName();
- @SuppressWarnings("unused")
- private static final String DATA_SOURCE_CLASS_NAME = com.mysql.cj.jdbc.MysqlDataSource.class.getName();
-
- private final Logger logger = LoggerFactory.getLogger(JdbcMysqlDAO.class);
-
- /********
- * INIT *
- ********/
- public JdbcMysqlDAO() {
- initSqlTypes();
- initDbProps();
- initSqlQueries();
- }
-
- private void initSqlQueries() {
- logger.debug("JDBC::initSqlQueries: '{}'", this.getClass().getSimpleName());
- }
-
- /**
- * INFO: http://www.java2s.com/Code/Java/Database-SQL-JDBC/StandardSQLDataTypeswithTheirJavaEquivalents.htm
- */
- private void initSqlTypes() {
- logger.debug("JDBC::initSqlTypes: Initialize the type array");
- sqlTypes.put("STRINGITEM", "VARCHAR(21717)");// mysql using utf-8 max 65535/3 = 21845, using 21845-128 = 21717
- }
-
- /**
- * INFO: https://github.com/brettwooldridge/HikariCP
- */
- private void initDbProps() {
- // Performancetuning
- databaseProps.setProperty("dataSource.cachePrepStmts", "true");
- databaseProps.setProperty("dataSource.prepStmtCacheSize", "250");
- databaseProps.setProperty("dataSource.prepStmtCacheSqlLimit", "2048");
- databaseProps.setProperty("dataSource.jdbcCompliantTruncation", "false");// jdbc standard max varchar max length
- // of 21845
-
- // Properties for HikariCP
- // Use driverClassName
- databaseProps.setProperty("driverClassName", DRIVER_CLASS_NAME);
- // OR dataSourceClassName
- // databaseProps.setProperty("dataSourceClassName", DATA_SOURCE_CLASS_NAME);
- databaseProps.setProperty("maximumPoolSize", "3");
- databaseProps.setProperty("minimumIdle", "2");
- }
-
- @Override
- public void initAfterFirstDbConnection() {
- logger.debug("JDBC::initAfterFirstDbConnection: Initializing step, after db is connected.");
- DbMetaData dbMeta = new DbMetaData();
- this.dbMeta = dbMeta;
- // Initialize sqlTypes, depending on DB version for example
- if (dbMeta.isDbVersionGreater(5, 5)) {
- sqlTypes.put("DATETIMEITEM", "TIMESTAMP(3)");
- sqlTypes.put("tablePrimaryKey", "TIMESTAMP(3)");
- sqlTypes.put("tablePrimaryValue", "NOW(3)");
- }
- }
-
- /**************
- * ITEMS DAOs *
- **************/
- @Override
- public @Nullable Integer doPingDB() {
- final @Nullable Long result = Yank.queryScalar(sqlPingDB, Long.class, null);
- return Objects.nonNull(result) ? result.intValue() : null;
- }
-
- /*************
- * ITEM DAOs *
- *************/
-
- /****************************
- * SQL generation Providers *
- ****************************/
-
- /*****************
- * H E L P E R S *
- *****************/
-
- /******************************
- * public Getters and Setters *
- ******************************/
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.persistence.jdbc.db;
-
-import java.time.ZoneId;
-import java.util.List;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.knowm.yank.Yank;
-import org.openhab.core.items.Item;
-import org.openhab.core.persistence.FilterCriteria;
-import org.openhab.core.persistence.FilterCriteria.Ordering;
-import org.openhab.core.types.State;
-import org.openhab.persistence.jdbc.dto.ItemVO;
-import org.openhab.persistence.jdbc.dto.ItemsVO;
-import org.openhab.persistence.jdbc.utils.StringUtilsExt;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Extended Database Configuration class. Class represents
- * the extended database-specific configuration. Overrides and supplements the
- * default settings from JdbcBaseDAO. Enter only the differences to JdbcBaseDAO here.
- *
- * @author Helmut Lehmeyer - Initial contribution
- */
-@NonNullByDefault
-public class JdbcPostgresqlDAO extends JdbcBaseDAO {
- private static final String DRIVER_CLASS_NAME = org.postgresql.Driver.class.getName();
- @SuppressWarnings("unused")
- private static final String DATA_SOURCE_CLASS_NAME = org.postgresql.ds.PGSimpleDataSource.class.getName();
-
- private final Logger logger = LoggerFactory.getLogger(JdbcPostgresqlDAO.class);
-
- /********
- * INIT *
- ********/
- public JdbcPostgresqlDAO() {
- initSqlQueries();
- initSqlTypes();
- initDbProps();
- }
-
- private void initSqlQueries() {
- logger.debug("JDBC::initSqlQueries: '{}'", this.getClass().getSimpleName());
- // System Information Functions: https://www.postgresql.org/docs/9.2/static/functions-info.html
- sqlGetDB = "SELECT CURRENT_DATABASE()";
- sqlIfTableExists = "SELECT * FROM PG_TABLES WHERE TABLENAME='#searchTable#'";
- sqlCreateItemsTableIfNot = "CREATE TABLE IF NOT EXISTS #itemsManageTable# (itemid SERIAL NOT NULL, #colname# #coltype# NOT NULL, CONSTRAINT #itemsManageTable#_pkey PRIMARY KEY (itemid))";
- sqlCreateNewEntryInItemsTable = "INSERT INTO items (itemname) SELECT itemname FROM #itemsManageTable# UNION VALUES ('#itemname#') EXCEPT SELECT itemname FROM items";
- sqlGetItemTables = "SELECT table_name FROM information_schema.tables WHERE table_type='BASE TABLE' AND table_schema=(SELECT table_schema "
- + "FROM information_schema.tables WHERE table_type='BASE TABLE' AND table_name='#itemsManageTable#') AND NOT table_name='#itemsManageTable#'";
- // http://stackoverflow.com/questions/17267417/how-do-i-do-an-upsert-merge-insert-on-duplicate-update-in-postgresql
- // for later use, PostgreSql > 9.5 to prevent PRIMARY key violation use:
- // SQL_INSERT_ITEM_VALUE = "INSERT INTO #tableName# (TIME, VALUE) VALUES( NOW(), CAST( ? as #dbType#) ) ON
- // CONFLICT DO NOTHING";
- sqlInsertItemValue = "INSERT INTO #tableName# (TIME, VALUE) VALUES( #tablePrimaryValue#, CAST( ? as #dbType#) )";
- }
-
- /**
- * INFO: http://www.java2s.com/Code/Java/Database-SQL-JDBC/StandardSQLDataTypeswithTheirJavaEquivalents.htm
- */
- private void initSqlTypes() {
- // Initialize the type array
- sqlTypes.put("CALLITEM", "VARCHAR");
- sqlTypes.put("COLORITEM", "VARCHAR");
- sqlTypes.put("CONTACTITEM", "VARCHAR");
- sqlTypes.put("DATETIMEITEM", "TIMESTAMP");
- sqlTypes.put("DIMMERITEM", "SMALLINT");
- sqlTypes.put("IMAGEITEM", "VARCHAR");
- sqlTypes.put("LOCATIONITEM", "VARCHAR");
- sqlTypes.put("NUMBERITEM", "DOUBLE PRECISION");
- sqlTypes.put("PLAYERITEM", "VARCHAR");
- sqlTypes.put("ROLLERSHUTTERITEM", "SMALLINT");
- sqlTypes.put("STRINGITEM", "VARCHAR");
- sqlTypes.put("SWITCHITEM", "VARCHAR");
- logger.debug("JDBC::initSqlTypes: Initialized the type array sqlTypes={}", sqlTypes.values());
- }
-
- /**
- * INFO: https://github.com/brettwooldridge/HikariCP
- */
- private void initDbProps() {
- // Performance:
- // databaseProps.setProperty("dataSource.cachePrepStmts", "true");
- // databaseProps.setProperty("dataSource.prepStmtCacheSize", "250");
- // databaseProps.setProperty("dataSource.prepStmtCacheSqlLimit", "2048");
-
- // Properties for HikariCP
- databaseProps.setProperty("driverClassName", DRIVER_CLASS_NAME);
- // driverClassName OR BETTER USE dataSourceClassName
- // databaseProps.setProperty("dataSourceClassName", DATA_SOURCE_CLASS_NAME);
- // databaseProps.setProperty("maximumPoolSize", "3");
- // databaseProps.setProperty("minimumIdle", "2");
- }
-
- /**************
- * ITEMS DAOs *
- **************/
- @Override
- public ItemsVO doCreateItemsTableIfNot(ItemsVO vo) {
- String sql = StringUtilsExt.replaceArrayMerge(sqlCreateItemsTableIfNot,
- new String[] { "#itemsManageTable#", "#colname#", "#coltype#", "#itemsManageTable#" },
- new String[] { vo.getItemsManageTable(), vo.getColname(), vo.getColtype(), vo.getItemsManageTable() });
- logger.debug("JDBC::doCreateItemsTableIfNot sql={}", sql);
- Yank.execute(sql, null);
- return vo;
- }
-
- @Override
- public Long doCreateNewEntryInItemsTable(ItemsVO vo) {
- String sql = StringUtilsExt.replaceArrayMerge(sqlCreateNewEntryInItemsTable,
- new String[] { "#itemsManageTable#", "#itemname#" },
- new String[] { vo.getItemsManageTable(), vo.getItemName() });
- logger.debug("JDBC::doCreateNewEntryInItemsTable sql={}", sql);
- return Yank.insert(sql, null);
- }
-
- @Override
- public List<ItemsVO> doGetItemTables(ItemsVO vo) {
- String sql = StringUtilsExt.replaceArrayMerge(this.sqlGetItemTables,
- new String[] { "#itemsManageTable#", "#itemsManageTable#" },
- new String[] { vo.getItemsManageTable(), vo.getItemsManageTable() });
- this.logger.debug("JDBC::doGetItemTables sql={}", sql);
- return Yank.queryBeanList(sql, ItemsVO.class, null);
- }
-
- /*************
- * ITEM DAOs *
- *************/
- @Override
- public void doStoreItemValue(Item item, State itemState, ItemVO vo) {
- ItemVO storedVO = storeItemValueProvider(item, itemState, vo);
- String sql = StringUtilsExt.replaceArrayMerge(sqlInsertItemValue,
- new String[] { "#tableName#", "#dbType#", "#tablePrimaryValue#" },
- new String[] { storedVO.getTableName(), storedVO.getDbType(), sqlTypes.get("tablePrimaryValue") });
- Object[] params = { storedVO.getValue() };
- logger.debug("JDBC::doStoreItemValue sql={} value='{}'", sql, storedVO.getValue());
- Yank.execute(sql, params);
- }
-
- /****************************
- * SQL generation Providers *
- ****************************/
-
- @Override
- protected String histItemFilterQueryProvider(FilterCriteria filter, int numberDecimalcount, String table,
- String simpleName, ZoneId timeZone) {
- logger.debug(
- "JDBC::getHistItemFilterQueryProvider filter = {}, numberDecimalcount = {}, table = {}, simpleName = {}",
- filter.toString(), numberDecimalcount, table, simpleName);
-
- String filterString = "";
- if (filter.getBeginDate() != null) {
- filterString += filterString.isEmpty() ? " WHERE" : " AND";
- filterString += " TIME>'" + JDBC_DATE_FORMAT.format(filter.getBeginDate().withZoneSameInstant(timeZone))
- + "'";
- }
- if (filter.getEndDate() != null) {
- filterString += filterString.isEmpty() ? " WHERE" : " AND";
- filterString += " TIME<'" + JDBC_DATE_FORMAT.format(filter.getEndDate().withZoneSameInstant(timeZone))
- + "'";
- }
- filterString += (filter.getOrdering() == Ordering.ASCENDING) ? " ORDER BY time ASC" : " ORDER BY time DESC";
- if (filter.getPageSize() != 0x7fffffff) {
- // see:
- // http://www.jooq.org/doc/3.5/manual/sql-building/sql-statements/select-statement/limit-clause/
- filterString += " OFFSET " + filter.getPageNumber() * filter.getPageSize() + " LIMIT "
- + filter.getPageSize();
- }
- String queryString = "NUMBERITEM".equalsIgnoreCase(simpleName) && numberDecimalcount > -1
- ? "SELECT time, ROUND(CAST (value AS numeric)," + numberDecimalcount + ") FROM " + table
- : "SELECT time, value FROM " + table;
- if (!filterString.isEmpty()) {
- queryString += filterString;
- }
- logger.debug("JDBC::query queryString = {}", queryString);
- return queryString;
- }
-
- /*****************
- * H E L P E R S *
- *****************/
-
- /******************************
- * public Getters and Setters *
- ******************************/
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.persistence.jdbc.db;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.knowm.yank.Yank;
-import org.openhab.core.items.Item;
-import org.openhab.core.types.State;
-import org.openhab.persistence.jdbc.dto.ItemVO;
-import org.openhab.persistence.jdbc.dto.ItemsVO;
-import org.openhab.persistence.jdbc.utils.StringUtilsExt;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Extended Database Configuration class. Class represents
- * the extended database-specific configuration. Overrides and supplements the
- * default settings from JdbcBaseDAO. Enter only the differences to JdbcBaseDAO here.
- *
- * @author Helmut Lehmeyer - Initial contribution
- */
-@NonNullByDefault
-public class JdbcSqliteDAO extends JdbcBaseDAO {
- private static final String DRIVER_CLASS_NAME = org.sqlite.JDBC.class.getName();
- @SuppressWarnings("unused")
- private static final String DATA_SOURCE_CLASS_NAME = org.sqlite.SQLiteDataSource.class.getName();
-
- private final Logger logger = LoggerFactory.getLogger(JdbcSqliteDAO.class);
-
- /********
- * INIT *
- ********/
- public JdbcSqliteDAO() {
- initSqlQueries();
- initSqlTypes();
- initDbProps();
- }
-
- private void initSqlQueries() {
- logger.debug("JDBC::initSqlQueries: '{}'", this.getClass().getSimpleName());
- sqlGetDB = "PRAGMA DATABASE_LIST"; // "SELECT SQLITE_VERSION()"; // "PRAGMA DATABASE_LIST"->db Path/Name
- // "PRAGMA SCHEMA_VERSION";
- sqlIfTableExists = "SELECT name FROM sqlite_master WHERE type='table' AND name='#searchTable#'";
- sqlCreateItemsTableIfNot = "CREATE TABLE IF NOT EXISTS #itemsManageTable# (ItemId INTEGER PRIMARY KEY AUTOINCREMENT, #colname# #coltype# NOT NULL)";
- sqlInsertItemValue = "INSERT OR IGNORE INTO #tableName# (TIME, VALUE) VALUES( #tablePrimaryValue#, CAST( ? as #dbType#) )";
- }
-
- /**
- * INFO: http://www.java2s.com/Code/Java/Database-SQL-JDBC/StandardSQLDataTypeswithTheirJavaEquivalents.htm
- */
- private void initSqlTypes() {
- logger.debug("JDBC::initSqlTypes: Initialize the type array");
- sqlTypes.put("tablePrimaryValue", "strftime('%Y-%m-%d %H:%M:%f' , 'now' , 'localtime')");
- }
-
- /**
- * INFO: https://github.com/brettwooldridge/HikariCP
- */
- private void initDbProps() {
- // Properties for HikariCP
- databaseProps.setProperty("driverClassName", DRIVER_CLASS_NAME);
- // driverClassName OR BETTER USE dataSourceClassName
- // databaseProps.setProperty("dataSourceClassName", DATA_SOURCE_CLASS_NAME);
- }
-
- /**************
- * ITEMS DAOs *
- **************/
-
- @Override
- public @Nullable String doGetDB() {
- return Yank.queryColumn(sqlGetDB, "file", String.class, null).get(0);
- }
-
- @Override
- public ItemsVO doCreateItemsTableIfNot(ItemsVO vo) {
- String sql = StringUtilsExt.replaceArrayMerge(sqlCreateItemsTableIfNot,
- new String[] { "#itemsManageTable#", "#colname#", "#coltype#" },
- new String[] { vo.getItemsManageTable(), vo.getColname(), vo.getColtype() });
- logger.debug("JDBC::doCreateItemsTableIfNot sql={}", sql);
- Yank.execute(sql, null);
- return vo;
- }
-
- /*************
- * ITEM DAOs *
- *************/
- @Override
- public void doStoreItemValue(Item item, State itemState, ItemVO vo) {
- ItemVO storedVO = storeItemValueProvider(item, itemState, vo);
- String sql = StringUtilsExt.replaceArrayMerge(sqlInsertItemValue,
- new String[] { "#tableName#", "#dbType#", "#tablePrimaryValue#" },
- new String[] { storedVO.getTableName(), storedVO.getDbType(), sqlTypes.get("tablePrimaryValue") });
- Object[] params = { storedVO.getValue() };
- logger.debug("JDBC::doStoreItemValue sql={} value='{}'", sql, storedVO.getValue());
- Yank.execute(sql, params);
- }
-
- /****************************
- * SQL generation Providers *
- ****************************/
-
- /*****************
- * H E L P E R S *
- *****************/
-
- /******************************
- * public Getters and Setters *
- ******************************/
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.persistence.jdbc.db;
-
-import java.util.Properties;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.knowm.yank.Yank;
-import org.openhab.persistence.jdbc.dto.ItemVO;
-import org.openhab.persistence.jdbc.utils.StringUtilsExt;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Extended Database Configuration class. Class represents the extended database-specific configuration. Overrides and
- * supplements the default settings from JdbcBaseDAO and JdbcPostgresqlDAO.
- *
- * @author Riccardo Nimser-Joseph - Initial contribution
- * @author Dan Cunningham - Fixes and refactoring
- */
-@NonNullByDefault
-public class JdbcTimescaledbDAO extends JdbcPostgresqlDAO {
- private final Logger logger = LoggerFactory.getLogger(JdbcTimescaledbDAO.class);
-
- private final String sqlCreateHypertable = "SELECT created from create_hypertable('#tableName#', 'time')";
-
- @Override
- public Properties getConnectionProperties() {
- Properties properties = (Properties) this.databaseProps.clone();
- // Adjust the jdbc url since the service name 'timescaledb' is only used to differentiate the DAOs
- if (properties.containsKey("jdbcUrl")) {
- properties.put("jdbcUrl", properties.getProperty("jdbcUrl").replace("timescaledb", "postgresql"));
- }
- return properties;
- }
-
- @Override
- public void doCreateItemTable(ItemVO vo) {
- super.doCreateItemTable(vo);
- String sql = StringUtilsExt.replaceArrayMerge(this.sqlCreateHypertable, new String[] { "#tableName#" },
- new String[] { vo.getTableName() });
- this.logger.debug("JDBC::doCreateItemTable sql={}", sql);
- Yank.queryScalar(sql, Boolean.class, null);
- }
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.persistence.jdbc.dto;
-
-import java.io.Serializable;
-import java.util.Date;
-import java.util.Objects;
-
-import org.eclipse.jdt.annotation.Nullable;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Represents the Item-data on the part of MyBatis/database.
- *
- * @author Helmut Lehmeyer - Initial contribution
- */
-public class ItemVO implements Serializable {
- private final Logger logger = LoggerFactory.getLogger(ItemVO.class);
-
- private static final long serialVersionUID = 1871441039821454890L;
-
- private String tableName;
- private @Nullable String newTableName;
- private String dbType;
- private String jdbcType;
- private String itemType;
- private Class<?> javaType;
- private Date time;
- private Object value;
-
- public ItemVO(String tableName, @Nullable String newTableName) {
- logger.debug("JDBC:ItemVO tableName={}; newTableName={}; ", tableName, newTableName);
- this.tableName = tableName;
- this.newTableName = newTableName;
- }
-
- public ItemVO() {
- }
-
- public void setValueTypes(String dbType, Class<?> javaType) {
- logger.debug("JDBC:ItemVO setValueTypes dbType={}; javaType={};", dbType, javaType);
- this.dbType = dbType;
- this.javaType = javaType;
- }
-
- public String getTableName() {
- return tableName;
- }
-
- public void setTableName(String tableName) {
- this.tableName = tableName;
- }
-
- public @Nullable String getNewTableName() {
- return newTableName;
- }
-
- public void setNewTableName(String newTableName) {
- this.newTableName = newTableName;
- }
-
- public String getDbType() {
- return dbType;
- }
-
- public void setDbType(String dbType) {
- this.dbType = dbType;
- }
-
- public String getJdbcType() {
- return jdbcType;
- }
-
- public void setJdbcType(String jdbcType) {
- this.jdbcType = jdbcType;
- }
-
- public String getItemType() {
- return itemType;
- }
-
- public void setItemType(String itemType) {
- this.itemType = itemType;
- }
-
- public String getJavaType() {
- return javaType.getName();
- }
-
- public void setJavaType(Class<?> javaType) {
- this.javaType = javaType;
- }
-
- public Date getTime() {
- return time;
- }
-
- public void setTime(Date time) {
- this.time = time;
- }
-
- public Object getValue() {
- return value;
- }
-
- public void setValue(Object value) {
- this.value = value;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- }
- if (obj == null) {
- return false;
- }
- if (getClass() != obj.getClass()) {
- return false;
- }
- ItemVO other = (ItemVO) obj;
- if (value == null) {
- if (other.value != null) {
- return false;
- }
- } else if (!value.equals(other.value)) {
- return false;
- }
- return Objects.equals(time, other.time);
- }
-
- @Override
- public String toString() {
- return new StringBuilder("ItemVO [tableName=").append(tableName).append(", newTableName=").append(newTableName)
- .append(", dbType=").append(dbType).append(", javaType=").append(javaType).append(", time=")
- .append(time).append(", value=").append(value).append("]").toString();
- }
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.persistence.jdbc.dto;
-
-import java.io.Serializable;
-import java.util.Objects;
-
-/**
- * Represents the table naming data.
- *
- * @author Helmut Lehmeyer - Initial contribution
- */
-public class ItemsVO implements Serializable {
-
- private static final long serialVersionUID = 2871961811177601520L;
-
- private static final String STR_FILTER = "[^a-zA-Z0-9]";
-
- private String coltype = "VARCHAR(500)";
- private String colname = "itemname";
- private String itemsManageTable = "items";
- private int itemId;
- private String itemName;
- private String tableName;
- private String jdbcUriDatabaseName;
-
- public String getColtype() {
- return coltype;
- }
-
- public void setColtype(String coltype) {
- this.coltype = coltype.replaceAll(STR_FILTER, "");
- }
-
- public String getColname() {
- return colname;
- }
-
- public void setColname(String colname) {
- this.colname = colname.replaceAll(STR_FILTER, "");
- }
-
- public String getItemsManageTable() {
- return itemsManageTable;
- }
-
- public void setItemsManageTable(String itemsManageTable) {
- this.itemsManageTable = itemsManageTable.replaceAll(STR_FILTER, "");
- }
-
- public int getItemId() {
- return itemId;
- }
-
- public void setItemId(int itemId) {
- this.itemId = itemId;
- }
-
- public String getItemName() {
- return itemName;
- }
-
- public void setItemName(String itemName) {
- this.itemName = itemName;
- }
-
- public String getTableName() {
- return tableName;
- }
-
- public void setTableName(String tableName) {
- this.tableName = tableName;
- }
-
- public String getJdbcUriDatabaseName() {
- return jdbcUriDatabaseName;
- }
-
- public void setJdbcUriDatabaseName(String jdbcUriDatabaseName) {
- this.jdbcUriDatabaseName = jdbcUriDatabaseName;
- }
-
- /*
- * (non-Javadoc)
- *
- * @see java.lang.Object#hashCode()
- */
- @Override
- public int hashCode() {
- return Objects.hash(itemName, itemId);
- }
-
- /*
- * (non-Javadoc)
- *
- * @see java.lang.Object#equals(java.lang.Object)
- */
- @Override
- public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- }
- if (obj == null) {
- return false;
- }
- if (getClass() != obj.getClass()) {
- return false;
- }
- ItemsVO other = (ItemsVO) obj;
- if (itemName == null) {
- if (other.itemName != null) {
- return false;
- }
- } else if (!itemName.equals(other.itemName)) {
- return false;
- }
- return itemId == other.itemId;
- }
-
- @Override
- public String toString() {
- StringBuilder builder = new StringBuilder();
- builder.append("ItemsVO [coltype=");
- builder.append(coltype);
- builder.append(", colname=");
- builder.append(colname);
- builder.append(", itemsManageTable=");
- builder.append(itemsManageTable);
- builder.append(", itemid=");
- builder.append(itemId);
- builder.append(", itemname=");
- builder.append(itemName);
- builder.append(", table_name=");
- builder.append(tableName);
- builder.append("]");
- return builder.toString();
- }
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.persistence.jdbc.dto;
-
-import java.time.ZonedDateTime;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.openhab.core.persistence.HistoricItem;
-import org.openhab.core.types.State;
-
-/**
- * Represents the data on the part of openHAB.
- *
- * @author Helmut Lehmeyer - Initial contribution
- */
-@NonNullByDefault
-public class JdbcHistoricItem implements HistoricItem {
-
- private final String name;
- private final State state;
- private final ZonedDateTime timestamp;
-
- public JdbcHistoricItem(String name, State state, ZonedDateTime timestamp) {
- this.name = name;
- this.state = state;
- this.timestamp = timestamp;
- }
-
- @Override
- public String getName() {
- return name;
- }
-
- @Override
- public State getState() {
- return state;
- }
-
- @Override
- public ZonedDateTime getTimestamp() {
- return timestamp;
- }
-
- @Override
- public String toString() {
- StringBuilder builder = new StringBuilder();
- builder.append("JdbcItem [name=");
- builder.append(name);
- builder.append(", state=");
- builder.append(state);
- builder.append(", timestamp=");
- builder.append(timestamp);
- builder.append("]");
- return builder.toString();
- }
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.persistence.jdbc.dto;
-
-import java.util.Date;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.core.persistence.PersistenceItemInfo;
-
-/**
- * Represents the item info for openHAB.
- *
- * @author Christoph Weitkamp - Initial contribution
- */
-@NonNullByDefault
-public class JdbcPersistenceItemInfo implements PersistenceItemInfo {
-
- private final String name;
- private final @Nullable Integer count;
- private final @Nullable Date earliest;
- private final @Nullable Date latest;
-
- public JdbcPersistenceItemInfo(String name) {
- this(name, null, null, null);
- }
-
- public JdbcPersistenceItemInfo(String name, @Nullable Integer count, @Nullable Date earliest,
- @Nullable Date latest) {
- this.name = name;
- this.count = count;
- this.earliest = earliest;
- this.latest = latest;
- }
-
- @Override
- public String getName() {
- return name;
- }
-
- @Override
- public @Nullable Integer getCount() {
- return count;
- }
-
- @Override
- public @Nullable Date getEarliest() {
- return earliest;
- }
-
- @Override
- public @Nullable Date getLatest() {
- return latest;
- }
-}
--- /dev/null
+/**
+ * Copyright (c) 2010-2022 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.persistence.jdbc.internal;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * This class represents a checked item/table relation.
+ *
+ * @author Jacob Laursen - Initial contribution
+ */
+@NonNullByDefault
+public class ItemTableCheckEntry {
+ private String itemName;
+ private String tableName;
+ private ItemTableCheckEntryStatus status;
+
+ public ItemTableCheckEntry(String itemName, String tableName, ItemTableCheckEntryStatus status) {
+ this.itemName = itemName;
+ this.tableName = tableName;
+ this.status = status;
+ }
+
+ public String getItemName() {
+ return itemName;
+ }
+
+ public String getTableName() {
+ return tableName;
+ }
+
+ public ItemTableCheckEntryStatus getStatus() {
+ return status;
+ }
+}
--- /dev/null
+/**
+ * Copyright (c) 2010-2022 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.persistence.jdbc.internal;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * This class represents status for an {@link ItemTableCheckEntry}.
+ *
+ * @author Jacob Laursen - Initial contribution
+ */
+@NonNullByDefault
+public enum ItemTableCheckEntryStatus {
+ /**
+ * Table is consistent.
+ */
+ VALID {
+ @Override
+ public String toString() {
+ return "Valid";
+ }
+ },
+ /**
+ * Table has no corresponding item.
+ */
+ ITEM_MISSING {
+ @Override
+ public String toString() {
+ return "Item missing";
+ }
+ },
+ /**
+ * Referenced table does not exist.
+ */
+ TABLE_MISSING {
+ @Override
+ public String toString() {
+ return "Table missing";
+ }
+ },
+ /**
+ * Referenced table does not exist nor has corresponding item.
+ */
+ ITEM_AND_TABLE_MISSING {
+ @Override
+ public String toString() {
+ return "Item and table missing";
+ }
+ },
+ /**
+ * Mapping for table does not exist in index.
+ */
+ ORPHAN_TABLE {
+ @Override
+ public String toString() {
+ return "Orphan table";
+ }
+ }
+}
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.persistence.jdbc.db.JdbcBaseDAO;
-import org.openhab.persistence.jdbc.utils.MovingAverage;
-import org.openhab.persistence.jdbc.utils.StringUtilsExt;
+import org.openhab.persistence.jdbc.internal.db.JdbcBaseDAO;
+import org.openhab.persistence.jdbc.internal.utils.MovingAverage;
+import org.openhab.persistence.jdbc.internal.utils.StringUtilsExt;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
private final Logger logger = LoggerFactory.getLogger(JdbcConfiguration.class);
private static final Pattern EXTRACT_CONFIG_PATTERN = Pattern.compile("^(.*?)\\.([0-9.a-zA-Z]+)$");
- private static final String DB_DAO_PACKAGE = "org.openhab.persistence.jdbc.db.Jdbc";
+ private static final String DB_DAO_PACKAGE = "org.openhab.persistence.jdbc.internal.db.Jdbc";
private Map<Object, Object> configuration;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.knowm.yank.Yank;
-import org.knowm.yank.exceptions.YankSQLException;
import org.openhab.core.i18n.TimeZoneProvider;
import org.openhab.core.items.Item;
import org.openhab.core.persistence.FilterCriteria;
import org.openhab.core.persistence.HistoricItem;
import org.openhab.core.persistence.PersistenceItemInfo;
import org.openhab.core.types.State;
-import org.openhab.persistence.jdbc.dto.ItemVO;
-import org.openhab.persistence.jdbc.dto.ItemsVO;
-import org.openhab.persistence.jdbc.dto.JdbcPersistenceItemInfo;
+import org.openhab.persistence.jdbc.internal.dto.ItemVO;
+import org.openhab.persistence.jdbc.internal.dto.ItemsVO;
+import org.openhab.persistence.jdbc.internal.dto.JdbcPersistenceItemInfo;
+import org.openhab.persistence.jdbc.internal.exceptions.JdbcSQLException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
this.timeZoneProvider = timeZoneProvider;
}
- /*****************
+ /****************
* MAPPER ITEMS *
- *****************/
- private boolean pingDB() {
+ ****************/
+ private boolean pingDB() throws JdbcSQLException {
logger.debug("JDBC::pingDB");
boolean ret = false;
long timerStart = System.currentTimeMillis();
return ret;
}
- private boolean ifItemsTableExists() {
+ private boolean ifItemsTableExists() throws JdbcSQLException {
logger.debug("JDBC::ifItemsTableExists");
long timerStart = System.currentTimeMillis();
boolean res = conf.getDBDAO().doIfTableExists(new ItemsVO());
return res;
}
- protected boolean ifTableExists(String tableName) {
+ protected boolean ifTableExists(String tableName) throws JdbcSQLException {
logger.debug("JDBC::ifTableExists");
long timerStart = System.currentTimeMillis();
boolean res = conf.getDBDAO().doIfTableExists(tableName);
return res;
}
- private ItemsVO createNewEntryInItemsTable(ItemsVO vo) {
+ private ItemsVO createNewEntryInItemsTable(ItemsVO vo) throws JdbcSQLException {
logger.debug("JDBC::createNewEntryInItemsTable");
long timerStart = System.currentTimeMillis();
Long i = conf.getDBDAO().doCreateNewEntryInItemsTable(vo);
return vo;
}
- private boolean createItemsTableIfNot(ItemsVO vo) {
+ private boolean createItemsTableIfNot(ItemsVO vo) throws JdbcSQLException {
logger.debug("JDBC::createItemsTableIfNot");
long timerStart = System.currentTimeMillis();
conf.getDBDAO().doCreateItemsTableIfNot(vo);
return true;
}
- private boolean dropItemsTableIfExists(ItemsVO vo) {
+ private boolean dropItemsTableIfExists(ItemsVO vo) throws JdbcSQLException {
logger.debug("JDBC::dropItemsTableIfExists");
long timerStart = System.currentTimeMillis();
conf.getDBDAO().doDropItemsTableIfExists(vo);
return true;
}
- protected void dropTable(String tableName) {
+ protected void dropTable(String tableName) throws JdbcSQLException {
logger.debug("JDBC::dropTable");
long timerStart = System.currentTimeMillis();
conf.getDBDAO().doDropTable(tableName);
logTime("doDropTable", timerStart, System.currentTimeMillis());
}
- protected ItemsVO deleteItemsEntry(ItemsVO vo) {
+ protected ItemsVO deleteItemsEntry(ItemsVO vo) throws JdbcSQLException {
logger.debug("JDBC::deleteItemsEntry");
long timerStart = System.currentTimeMillis();
conf.getDBDAO().doDeleteItemsEntry(vo);
return vo;
}
- private List<ItemsVO> getItemIDTableNames() {
+ private List<ItemsVO> getItemIDTableNames() throws JdbcSQLException {
logger.debug("JDBC::getItemIDTableNames");
long timerStart = System.currentTimeMillis();
List<ItemsVO> vo = conf.getDBDAO().doGetItemIDTableNames(new ItemsVO());
return vo;
}
- protected List<ItemsVO> getItemTables() {
+ protected List<ItemsVO> getItemTables() throws JdbcSQLException {
logger.debug("JDBC::getItemTables");
long timerStart = System.currentTimeMillis();
ItemsVO vo = new ItemsVO();
/****************
* MAPPERS ITEM *
****************/
- private void updateItemTableNames(List<ItemVO> vol) {
+ private void updateItemTableNames(List<ItemVO> vol) throws JdbcSQLException {
logger.debug("JDBC::updateItemTableNames");
long timerStart = System.currentTimeMillis();
conf.getDBDAO().doUpdateItemTableNames(vol);
logTime("updateItemTableNames", timerStart, System.currentTimeMillis());
}
- private ItemVO createItemTable(ItemVO vo) {
+ private ItemVO createItemTable(ItemVO vo) throws JdbcSQLException {
logger.debug("JDBC::createItemTable");
long timerStart = System.currentTimeMillis();
conf.getDBDAO().doCreateItemTable(vo);
return vo;
}
- protected Item storeItemValue(Item item, State itemState, @Nullable ZonedDateTime date) {
+ protected void storeItemValue(Item item, State itemState, @Nullable ZonedDateTime date) throws JdbcSQLException {
logger.debug("JDBC::storeItemValue: item={} state={} date={}", item, itemState, date);
String tableName = getTable(item);
long timerStart = System.currentTimeMillis();
}
logTime("storeItemValue", timerStart, System.currentTimeMillis());
errCnt = 0;
- return item;
}
- public long getRowCount(String tableName) {
+ public long getRowCount(String tableName) throws JdbcSQLException {
return conf.getDBDAO().doGetRowCount(tableName);
}
protected List<HistoricItem> getHistItemFilterQuery(FilterCriteria filter, int numberDecimalcount, String table,
- Item item) {
+ Item item) throws JdbcSQLException {
logger.debug(
"JDBC::getHistItemFilterQuery filter='{}' numberDecimalcount='{}' table='{}' item='{}' itemName='{}'",
true, numberDecimalcount, table, item, item.getName());
return result;
}
- protected void deleteItemValues(FilterCriteria filter, String table) {
+ protected void deleteItemValues(FilterCriteria filter, String table) throws JdbcSQLException {
logger.debug("JDBC::deleteItemValues filter='{}' table='{}' itemName='{}'", true, table, filter.getItemName());
long timerStart = System.currentTimeMillis();
conf.getDBDAO().doDeleteItemValues(filter, table, timeZoneProvider.getTimeZone());
logger.debug("JDBC::checkDBAcessability, second try connection: {}", p);
return (p && !(conf.getErrReconnectThreshold() > 0 && errCnt <= conf.getErrReconnectThreshold()));
}
- } catch (YankSQLException e) {
+ } catch (JdbcSQLException e) {
logger.warn("Unable to ping database", e);
return false;
}
/**************************
* DATABASE TABLEHANDLING *
**************************/
- protected void checkDBSchema() {
+ protected void checkDBSchema() throws JdbcSQLException {
if (!conf.getTableUseRealCaseSensitiveItemNames()) {
createItemsTableIfNot(new ItemsVO());
}
populateItemNameToTableNameMap();
}
- private void populateItemNameToTableNameMap() {
+ private void populateItemNameToTableNameMap() throws JdbcSQLException {
itemNameToTableNameMap.clear();
if (conf.getTableUseRealCaseSensitiveItemNames()) {
for (String itemName : getItemTables().stream().map(t -> t.getTableName()).collect(Collectors.toList())) {
}
}
- protected String getTable(Item item) {
+ protected String getTable(Item item) throws JdbcSQLException {
int itemId = 0;
ItemsVO isvo;
ItemVO ivo;
return tableName;
}
- private void formatTableNames() {
+ private void formatTableNames() throws JdbcSQLException {
boolean tmpinit = initialized;
if (tmpinit) {
initialized = false;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
-import org.knowm.yank.exceptions.YankSQLException;
import org.openhab.core.config.core.ConfigurableService;
import org.openhab.core.i18n.TimeZoneProvider;
import org.openhab.core.items.GroupItem;
import org.openhab.core.persistence.strategy.PersistenceStrategy;
import org.openhab.core.types.State;
import org.openhab.core.types.UnDefType;
-import org.openhab.persistence.jdbc.ItemTableCheckEntry;
-import org.openhab.persistence.jdbc.ItemTableCheckEntryStatus;
-import org.openhab.persistence.jdbc.dto.ItemsVO;
+import org.openhab.persistence.jdbc.internal.dto.ItemsVO;
+import org.openhab.persistence.jdbc.internal.exceptions.JdbcSQLException;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.service.component.annotations.Activate;
logger.debug("JDBC: Stored item '{}' as '{}' in SQL database at {} in {} ms.", item.getName(), state,
new Date(), System.currentTimeMillis() - timerStart);
}
- } catch (YankSQLException e) {
+ } catch (JdbcSQLException e) {
logger.warn("JDBC::store: Unable to store item", e);
}
}
// Success
errCnt = 0;
return items;
- } catch (YankSQLException e) {
+ } catch (JdbcSQLException e) {
logger.warn("JDBC::query: Unable to query item", e);
return List.of();
}
checkDBSchema();
// connection has been established ... initialization completed!
initialized = true;
- } catch (YankSQLException e) {
+ } catch (JdbcSQLException e) {
logger.error("Failed to check database schema", e);
initialized = false;
}
System.currentTimeMillis() - timerStart);
}
return true;
- } catch (YankSQLException e) {
+ } catch (JdbcSQLException e) {
logger.debug("JDBC::remove: Unable to remove values for item", e);
return false;
}
*
* @return list of {@link ItemTableCheckEntry}
*/
- public List<ItemTableCheckEntry> getCheckedEntries() {
+ public List<ItemTableCheckEntry> getCheckedEntries() throws JdbcSQLException {
List<ItemTableCheckEntry> entries = new ArrayList<>();
if (!checkDBAccessability()) {
* @param itemName Name of item to clean
* @param force If true, non-empty tables will be dropped too
* @return true if item was cleaned up
+ * @throws JdbcSQLException
*/
- public boolean cleanupItem(String itemName, boolean force) {
+ public boolean cleanupItem(String itemName, boolean force) throws JdbcSQLException {
String tableName = itemNameToTableNameMap.get(itemName);
if (tableName == null) {
return false;
*
* @param entry
* @return true if item was cleaned up
+ * @throws JdbcSQLException
*/
- public boolean cleanupItem(ItemTableCheckEntry entry) {
+ public boolean cleanupItem(ItemTableCheckEntry entry) throws JdbcSQLException {
return cleanupItem(entry, false);
}
- private boolean cleanupItem(ItemTableCheckEntry entry, boolean force) {
+ private boolean cleanupItem(ItemTableCheckEntry entry, boolean force) throws JdbcSQLException {
if (!checkDBAccessability()) {
logger.warn("JDBC::cleanupItem: database not connected");
return false;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.items.ItemUtil;
-import org.openhab.persistence.jdbc.dto.ItemVO;
+import org.openhab.persistence.jdbc.internal.dto.ItemVO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
--- /dev/null
+/**
+ * Copyright (c) 2010-2022 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.persistence.jdbc.internal.console;
+
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
+import java.util.stream.Stream;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.core.io.console.Console;
+import org.openhab.core.io.console.ConsoleCommandCompleter;
+import org.openhab.core.io.console.StringsCompleter;
+import org.openhab.core.io.console.extensions.AbstractConsoleCommandExtension;
+import org.openhab.core.io.console.extensions.ConsoleCommandExtension;
+import org.openhab.core.persistence.PersistenceService;
+import org.openhab.core.persistence.PersistenceServiceRegistry;
+import org.openhab.persistence.jdbc.internal.ItemTableCheckEntry;
+import org.openhab.persistence.jdbc.internal.ItemTableCheckEntryStatus;
+import org.openhab.persistence.jdbc.internal.JdbcPersistenceService;
+import org.openhab.persistence.jdbc.internal.JdbcPersistenceServiceConstants;
+import org.openhab.persistence.jdbc.internal.exceptions.JdbcSQLException;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+
+/**
+ * The {@link JdbcCommandExtension} is responsible for handling console commands
+ *
+ * @author Jacob Laursen - Initial contribution
+ */
+@NonNullByDefault
+@Component(service = ConsoleCommandExtension.class)
+public class JdbcCommandExtension extends AbstractConsoleCommandExtension implements ConsoleCommandCompleter {
+
+ private static final String CMD_TABLES = "tables";
+ private static final String SUBCMD_TABLES_LIST = "list";
+ private static final String SUBCMD_TABLES_CLEAN = "clean";
+ private static final String PARAMETER_ALL = "all";
+ private static final String PARAMETER_FORCE = "force";
+ private static final StringsCompleter CMD_COMPLETER = new StringsCompleter(List.of(CMD_TABLES), false);
+ private static final StringsCompleter SUBCMD_TABLES_COMPLETER = new StringsCompleter(
+ List.of(SUBCMD_TABLES_LIST, SUBCMD_TABLES_CLEAN), false);
+
+ private final PersistenceServiceRegistry persistenceServiceRegistry;
+
+ @Activate
+ public JdbcCommandExtension(final @Reference PersistenceServiceRegistry persistenceServiceRegistry) {
+ super(JdbcPersistenceServiceConstants.SERVICE_ID, "Interact with the JDBC persistence service.");
+ this.persistenceServiceRegistry = persistenceServiceRegistry;
+ }
+
+ @Override
+ public void execute(String[] args, Console console) {
+ if (args.length < 2 || args.length > 4 || !CMD_TABLES.equals(args[0])) {
+ printUsage(console);
+ return;
+ }
+ JdbcPersistenceService persistenceService = getPersistenceService();
+ if (persistenceService == null) {
+ return;
+ }
+ try {
+ if (!execute(persistenceService, args, console)) {
+ printUsage(console);
+ return;
+ }
+ } catch (JdbcSQLException e) {
+ console.println(e.toString());
+ }
+ }
+
+ private @Nullable JdbcPersistenceService getPersistenceService() {
+ for (PersistenceService persistenceService : persistenceServiceRegistry.getAll()) {
+ if (persistenceService instanceof JdbcPersistenceService) {
+ return (JdbcPersistenceService) persistenceService;
+ }
+ }
+ return null;
+ }
+
+ private boolean execute(JdbcPersistenceService persistenceService, String[] args, Console console)
+ throws JdbcSQLException {
+ if (SUBCMD_TABLES_LIST.equalsIgnoreCase(args[1])) {
+ listTables(persistenceService, console, args.length == 3 && PARAMETER_ALL.equalsIgnoreCase(args[2]));
+ return true;
+ } else if (SUBCMD_TABLES_CLEAN.equalsIgnoreCase(args[1])) {
+ if (args.length == 3) {
+ cleanupItem(persistenceService, console, args[2], false);
+ return true;
+ } else if (args.length == 4 && PARAMETER_FORCE.equalsIgnoreCase(args[3])) {
+ cleanupItem(persistenceService, console, args[2], true);
+ return true;
+ } else {
+ cleanupTables(persistenceService, console);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void listTables(JdbcPersistenceService persistenceService, Console console, Boolean all)
+ throws JdbcSQLException {
+ List<ItemTableCheckEntry> entries = persistenceService.getCheckedEntries();
+ if (!all) {
+ entries.removeIf(t -> t.getStatus() == ItemTableCheckEntryStatus.VALID);
+ }
+ entries.sort(Comparator.comparing(ItemTableCheckEntry::getTableName));
+ // FIXME: NoSuchElement when empty table - because of get()
+ int itemNameMaxLength = Math
+ .max(entries.stream().map(t -> t.getItemName().length()).max(Integer::compare).get(), 4);
+ int tableNameMaxLength = Math
+ .max(entries.stream().map(t -> t.getTableName().length()).max(Integer::compare).get(), 5);
+ int statusMaxLength = Stream.of(ItemTableCheckEntryStatus.values()).map(t -> t.toString().length())
+ .max(Integer::compare).get();
+ console.println(String.format(
+ "%1$-" + (tableNameMaxLength + 2) + "sRow Count %2$-" + (itemNameMaxLength + 2) + "s%3$s", "Table",
+ "Item", "Status"));
+ console.println("-".repeat(tableNameMaxLength) + " " + "--------- " + "-".repeat(itemNameMaxLength) + " "
+ + "-".repeat(statusMaxLength));
+ for (ItemTableCheckEntry entry : entries) {
+ String tableName = entry.getTableName();
+ ItemTableCheckEntryStatus status = entry.getStatus();
+ long rowCount = status == ItemTableCheckEntryStatus.VALID
+ || status == ItemTableCheckEntryStatus.ITEM_MISSING ? persistenceService.getRowCount(tableName) : 0;
+ console.println(String.format(
+ "%1$-" + (tableNameMaxLength + 2) + "s%2$9d %3$-" + (itemNameMaxLength + 2) + "s%4$s", tableName,
+ rowCount, entry.getItemName(), status));
+ }
+ }
+
+ private void cleanupTables(JdbcPersistenceService persistenceService, Console console) throws JdbcSQLException {
+ console.println("Cleaning up all inconsistent items...");
+ List<ItemTableCheckEntry> entries = persistenceService.getCheckedEntries();
+ entries.removeIf(t -> t.getStatus() == ItemTableCheckEntryStatus.VALID || t.getItemName().isEmpty());
+ for (ItemTableCheckEntry entry : entries) {
+ console.print(entry.getItemName() + " -> ");
+ if (persistenceService.cleanupItem(entry)) {
+ console.println("done.");
+ } else {
+ console.println("skipped/failed.");
+ }
+ }
+ }
+
+ private void cleanupItem(JdbcPersistenceService persistenceService, Console console, String itemName, boolean force)
+ throws JdbcSQLException {
+ console.print("Cleaning up item " + itemName + "... ");
+ if (persistenceService.cleanupItem(itemName, force)) {
+ console.println("done.");
+ } else {
+ console.println("skipped/failed.");
+ }
+ }
+
+ @Override
+ public List<String> getUsages() {
+ return Arrays.asList(
+ buildCommandUsage(CMD_TABLES + " " + SUBCMD_TABLES_LIST + " [" + PARAMETER_ALL + "]",
+ "list tables (all = include valid)"),
+ buildCommandUsage(
+ CMD_TABLES + " " + SUBCMD_TABLES_CLEAN + " [<itemName>]" + " [" + PARAMETER_FORCE + "]",
+ "clean inconsistent items (remove from index and drop tables)"));
+ }
+
+ @Override
+ public @Nullable ConsoleCommandCompleter getCompleter() {
+ return this;
+ }
+
+ @Override
+ public boolean complete(String[] args, int cursorArgumentIndex, int cursorPosition, List<String> candidates) {
+ if (cursorArgumentIndex <= 0) {
+ return CMD_COMPLETER.complete(args, cursorArgumentIndex, cursorPosition, candidates);
+ } else if (cursorArgumentIndex == 1) {
+ if (CMD_TABLES.equalsIgnoreCase(args[0])) {
+ return SUBCMD_TABLES_COMPLETER.complete(args, cursorArgumentIndex, cursorPosition, candidates);
+ }
+ } else if (cursorArgumentIndex == 2) {
+ if (CMD_TABLES.equalsIgnoreCase(args[0])) {
+ if (SUBCMD_TABLES_CLEAN.equalsIgnoreCase(args[1])) {
+ JdbcPersistenceService persistenceService = getPersistenceService();
+ if (persistenceService != null) {
+ return new StringsCompleter(persistenceService.getItemNames(), true).complete(args,
+ cursorArgumentIndex, cursorPosition, candidates);
+ }
+ } else if (SUBCMD_TABLES_LIST.equalsIgnoreCase(args[1])) {
+ new StringsCompleter(List.of(PARAMETER_ALL), false).complete(args, cursorArgumentIndex,
+ cursorPosition, candidates);
+ }
+ }
+ }
+ return false;
+ }
+}
--- /dev/null
+/**
+ * Copyright (c) 2010-2022 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.persistence.jdbc.internal.db;
+
+import java.math.BigDecimal;
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Properties;
+import java.util.stream.Collectors;
+
+import javax.measure.Quantity;
+import javax.measure.Unit;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.knowm.yank.Yank;
+import org.knowm.yank.exceptions.YankSQLException;
+import org.openhab.core.items.GroupItem;
+import org.openhab.core.items.Item;
+import org.openhab.core.library.items.ColorItem;
+import org.openhab.core.library.items.ContactItem;
+import org.openhab.core.library.items.DateTimeItem;
+import org.openhab.core.library.items.DimmerItem;
+import org.openhab.core.library.items.ImageItem;
+import org.openhab.core.library.items.NumberItem;
+import org.openhab.core.library.items.PlayerItem;
+import org.openhab.core.library.items.RollershutterItem;
+import org.openhab.core.library.items.SwitchItem;
+import org.openhab.core.library.types.DateTimeType;
+import org.openhab.core.library.types.DecimalType;
+import org.openhab.core.library.types.HSBType;
+import org.openhab.core.library.types.PercentType;
+import org.openhab.core.library.types.QuantityType;
+import org.openhab.core.library.types.RawType;
+import org.openhab.core.library.unit.Units;
+import org.openhab.core.persistence.FilterCriteria;
+import org.openhab.core.persistence.FilterCriteria.Ordering;
+import org.openhab.core.persistence.HistoricItem;
+import org.openhab.core.types.State;
+import org.openhab.core.types.TypeParser;
+import org.openhab.persistence.jdbc.internal.dto.ItemVO;
+import org.openhab.persistence.jdbc.internal.dto.ItemsVO;
+import org.openhab.persistence.jdbc.internal.dto.JdbcHistoricItem;
+import org.openhab.persistence.jdbc.internal.exceptions.JdbcSQLException;
+import org.openhab.persistence.jdbc.internal.utils.DbMetaData;
+import org.openhab.persistence.jdbc.internal.utils.StringUtilsExt;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Default Database Configuration class.
+ *
+ * @author Helmut Lehmeyer - Initial contribution
+ */
+@NonNullByDefault
+public class JdbcBaseDAO {
+ private final Logger logger = LoggerFactory.getLogger(JdbcBaseDAO.class);
+
+ public final Properties databaseProps = new Properties();
+ protected String urlSuffix = "";
+ public final Map<String, String> sqlTypes = new HashMap<>();
+
+ // Get Database Meta data
+ protected @Nullable DbMetaData dbMeta;
+
+ protected String sqlPingDB = "SELECT 1";
+ protected String sqlGetDB = "SELECT DATABASE()";
+ protected String sqlIfTableExists = "SHOW TABLES LIKE '#searchTable#'";
+ protected String sqlCreateNewEntryInItemsTable = "INSERT INTO #itemsManageTable# (ItemName) VALUES ('#itemname#')";
+ protected String sqlCreateItemsTableIfNot = "CREATE TABLE IF NOT EXISTS #itemsManageTable# (ItemId INT NOT NULL AUTO_INCREMENT,#colname# #coltype# NOT NULL,PRIMARY KEY (ItemId))";
+ protected String sqlDropItemsTableIfExists = "DROP TABLE IF EXISTS #itemsManageTable#";
+ protected String sqlDropTable = "DROP TABLE #tableName#";
+ protected String sqlDeleteItemsEntry = "DELETE FROM #itemsManageTable# WHERE ItemName='#itemname#'";
+ protected String sqlGetItemIDTableNames = "SELECT ItemId, ItemName FROM #itemsManageTable#";
+ protected String sqlGetItemTables = "SELECT table_name FROM information_schema.tables WHERE table_type='BASE TABLE' AND table_schema='#jdbcUriDatabaseName#' AND NOT table_name='#itemsManageTable#'";
+ protected String sqlCreateItemTable = "CREATE TABLE IF NOT EXISTS #tableName# (time #tablePrimaryKey# NOT NULL, value #dbType#, PRIMARY KEY(time))";
+ protected String sqlInsertItemValue = "INSERT INTO #tableName# (TIME, VALUE) VALUES( #tablePrimaryValue#, ? ) ON DUPLICATE KEY UPDATE VALUE= ?";
+ protected String sqlGetRowCount = "SELECT COUNT(*) FROM #tableName#";
+
+ /********
+ * INIT *
+ ********/
+ public JdbcBaseDAO() {
+ initSqlTypes();
+ initDbProps();
+ }
+
+ /**
+ * ## Get high precision by fractal seconds, examples ##
+ *
+ * mysql > 5.5 + mariadb > 5.2:
+ * DROP TABLE FractionalSeconds;
+ * CREATE TABLE FractionalSeconds (time TIMESTAMP(3), value TIMESTAMP(3));
+ * INSERT INTO FractionalSeconds (time, value) VALUES( NOW(3), '1999-01-09 20:11:11.126' );
+ * SELECT time FROM FractionalSeconds ORDER BY time DESC LIMIT 1;
+ *
+ * mysql <= 5.5 + mariadb <= 5.2: !!! NO high precision and fractal seconds !!!
+ * DROP TABLE FractionalSeconds;
+ * CREATE TABLE FractionalSeconds (time TIMESTAMP, value TIMESTAMP);
+ * INSERT INTO FractionalSeconds (time, value) VALUES( NOW(), '1999-01-09 20:11:11.126' );
+ * SELECT time FROM FractionalSeconds ORDER BY time DESC LIMIT 1;
+ *
+ * derby:
+ * DROP TABLE FractionalSeconds;
+ * CREATE TABLE FractionalSeconds (time TIMESTAMP, value TIMESTAMP);
+ * INSERT INTO FractionalSeconds (time, value) VALUES( CURRENT_TIMESTAMP, '1999-01-09 20:11:11.126' );
+ * SELECT time, value FROM FractionalSeconds;
+ *
+ * H2 + postgreSQL + hsqldb:
+ * DROP TABLE FractionalSeconds;
+ * CREATE TABLE FractionalSeconds (time TIMESTAMP, value TIMESTAMP);
+ * INSERT INTO FractionalSeconds (time, value) VALUES( NOW(), '1999-01-09 20:11:11.126' );
+ * SELECT time, value FROM FractionalSeconds;
+ *
+ * Sqlite:
+ * DROP TABLE FractionalSeconds;
+ * CREATE TABLE FractionalSeconds (time TIMESTAMP, value TIMESTAMP);
+ * INSERT INTO FractionalSeconds (time, value) VALUES( strftime('%Y-%m-%d %H:%M:%f' , 'now' , 'localtime'),
+ * '1999-01-09 20:11:11.124' );
+ * SELECT time FROM FractionalSeconds ORDER BY time DESC LIMIT 1;
+ *
+ */
+
+ /**
+ * INFO: http://www.java2s.com/Code/Java/Database-SQL-JDBC/StandardSQLDataTypeswithTheirJavaEquivalents.htm
+ */
+ private void initSqlTypes() {
+ logger.debug("JDBC::initSqlTypes: Initialize the type array");
+ sqlTypes.put("CALLITEM", "VARCHAR(200)");
+ sqlTypes.put("COLORITEM", "VARCHAR(70)");
+ sqlTypes.put("CONTACTITEM", "VARCHAR(6)");
+ sqlTypes.put("DATETIMEITEM", "TIMESTAMP");
+ sqlTypes.put("DIMMERITEM", "TINYINT");
+ sqlTypes.put("IMAGEITEM", "VARCHAR(65500)");// jdbc max 21845
+ sqlTypes.put("LOCATIONITEM", "VARCHAR(50)");
+ sqlTypes.put("NUMBERITEM", "DOUBLE");
+ sqlTypes.put("PLAYERITEM", "VARCHAR(20)");
+ sqlTypes.put("ROLLERSHUTTERITEM", "TINYINT");
+ sqlTypes.put("STRINGITEM", "VARCHAR(65500)");// jdbc max 21845
+ sqlTypes.put("SWITCHITEM", "VARCHAR(6)");
+ sqlTypes.put("tablePrimaryKey", "TIMESTAMP");
+ sqlTypes.put("tablePrimaryValue", "NOW()");
+ }
+
+ /**
+ * INFO: https://github.com/brettwooldridge/HikariCP
+ *
+ * driverClassName (used with jdbcUrl):
+ * Derby: org.apache.derby.jdbc.EmbeddedDriver
+ * H2: org.h2.Driver
+ * HSQLDB: org.hsqldb.jdbcDriver
+ * Jaybird: org.firebirdsql.jdbc.FBDriver
+ * MariaDB: org.mariadb.jdbc.Driver
+ * MySQL: com.mysql.cj.jdbc.Driver
+ * MaxDB: com.sap.dbtech.jdbc.DriverSapDB
+ * PostgreSQL: org.postgresql.Driver
+ * SyBase: com.sybase.jdbc3.jdbc.SybDriver
+ * SqLite: org.sqlite.JDBC
+ *
+ * dataSourceClassName (for alternative Configuration):
+ * Derby: org.apache.derby.jdbc.ClientDataSource
+ * H2: org.h2.jdbcx.JdbcDataSource
+ * HSQLDB: org.hsqldb.jdbc.JDBCDataSource
+ * Jaybird: org.firebirdsql.pool.FBSimpleDataSource
+ * MariaDB, MySQL: org.mariadb.jdbc.MySQLDataSource
+ * MaxDB: com.sap.dbtech.jdbc.DriverSapDB
+ * PostgreSQL: org.postgresql.ds.PGSimpleDataSource
+ * SyBase: com.sybase.jdbc4.jdbc.SybDataSource
+ * SqLite: org.sqlite.SQLiteDataSource
+ *
+ * HikariPool - configuration Example:
+ * allowPoolSuspension.............false
+ * autoCommit......................true
+ * catalog.........................
+ * connectionInitSql...............
+ * connectionTestQuery.............
+ * connectionTimeout...............30000
+ * dataSource......................
+ * dataSourceClassName.............
+ * dataSourceJNDI..................
+ * dataSourceProperties............{password=<masked>}
+ * driverClassName.................
+ * healthCheckProperties...........{}
+ * healthCheckRegistry.............
+ * idleTimeout.....................600000
+ * initializationFailFast..........true
+ * isolateInternalQueries..........false
+ * jdbc4ConnectionTest.............false
+ * jdbcUrl.........................jdbc:mysql://192.168.0.1:3306/test
+ * leakDetectionThreshold..........0
+ * maxLifetime.....................1800000
+ * maximumPoolSize.................10
+ * metricRegistry..................
+ * metricsTrackerFactory...........
+ * minimumIdle.....................10
+ * password........................<masked>
+ * poolName........................HikariPool-0
+ * readOnly........................false
+ * registerMbeans..................false
+ * scheduledExecutorService........
+ * threadFactory...................
+ * transactionIsolation............
+ * username........................xxxx
+ * validationTimeout...............5000
+ */
+ private void initDbProps() {
+ // databaseProps.setProperty("dataSource.url", "jdbc:mysql://192.168.0.1:3306/test");
+ // databaseProps.setProperty("dataSource.user", "test");
+ // databaseProps.setProperty("dataSource.password", "test");
+
+ // Most relevant Performance values
+ // maximumPoolSize to 20, minimumIdle to 5, and idleTimeout to 2 minutes.
+ // databaseProps.setProperty("maximumPoolSize", ""+maximumPoolSize);
+ // databaseProps.setProperty("minimumIdle", ""+minimumIdle);
+ // databaseProps.setProperty("idleTimeout", ""+idleTimeout);
+ // databaseProps.setProperty("connectionTimeout",""+connectionTimeout);
+ // databaseProps.setProperty("idleTimeout", ""+idleTimeout);
+ // databaseProps.setProperty("maxLifetime", ""+maxLifetime);
+ // databaseProps.setProperty("validationTimeout",""+validationTimeout);
+ }
+
+ public void initAfterFirstDbConnection() {
+ logger.debug("JDBC::initAfterFirstDbConnection: Initializing step, after db is connected.");
+ // Initialize sqlTypes, depending on DB version for example
+ dbMeta = new DbMetaData();// get DB information
+ }
+
+ public Properties getConnectionProperties() {
+ return new Properties(this.databaseProps);
+ }
+
+ /**************
+ * ITEMS DAOs *
+ **************/
+ public @Nullable Integer doPingDB() throws JdbcSQLException {
+ try {
+ final @Nullable Integer result = Yank.queryScalar(sqlPingDB, Integer.class, null);
+ return result;
+ } catch (YankSQLException e) {
+ throw new JdbcSQLException(e);
+ }
+ }
+
+ public @Nullable String doGetDB() throws JdbcSQLException {
+ try {
+ final @Nullable String result = Yank.queryScalar(sqlGetDB, String.class, null);
+ return result;
+ } catch (YankSQLException e) {
+ throw new JdbcSQLException(e);
+ }
+ }
+
+ public boolean doIfTableExists(ItemsVO vo) throws JdbcSQLException {
+ String sql = StringUtilsExt.replaceArrayMerge(sqlIfTableExists, new String[] { "#searchTable#" },
+ new String[] { vo.getItemsManageTable() });
+ logger.debug("JDBC::doIfTableExists sql={}", sql);
+ try {
+ final @Nullable String result = Yank.queryScalar(sql, String.class, null);
+ return Objects.nonNull(result);
+ } catch (YankSQLException e) {
+ throw new JdbcSQLException(e);
+ }
+ }
+
+ public boolean doIfTableExists(String tableName) throws JdbcSQLException {
+ String sql = StringUtilsExt.replaceArrayMerge(sqlIfTableExists, new String[] { "#searchTable#" },
+ new String[] { tableName });
+ logger.debug("JDBC::doIfTableExists sql={}", sql);
+ try {
+ final @Nullable String result = Yank.queryScalar(sql, String.class, null);
+ return Objects.nonNull(result);
+ } catch (YankSQLException e) {
+ throw new JdbcSQLException(e);
+ }
+ }
+
+ public Long doCreateNewEntryInItemsTable(ItemsVO vo) throws JdbcSQLException {
+ String sql = StringUtilsExt.replaceArrayMerge(sqlCreateNewEntryInItemsTable,
+ new String[] { "#itemsManageTable#", "#itemname#" },
+ new String[] { vo.getItemsManageTable(), vo.getItemName() });
+ logger.debug("JDBC::doCreateNewEntryInItemsTable sql={}", sql);
+ try {
+ return Yank.insert(sql, null);
+ } catch (YankSQLException e) {
+ throw new JdbcSQLException(e);
+ }
+ }
+
+ public ItemsVO doCreateItemsTableIfNot(ItemsVO vo) throws JdbcSQLException {
+ String sql = StringUtilsExt.replaceArrayMerge(sqlCreateItemsTableIfNot,
+ new String[] { "#itemsManageTable#", "#colname#", "#coltype#" },
+ new String[] { vo.getItemsManageTable(), vo.getColname(), vo.getColtype() });
+ logger.debug("JDBC::doCreateItemsTableIfNot sql={}", sql);
+ try {
+ Yank.execute(sql, null);
+ } catch (YankSQLException e) {
+ throw new JdbcSQLException(e);
+ }
+ return vo;
+ }
+
+ public ItemsVO doDropItemsTableIfExists(ItemsVO vo) throws JdbcSQLException {
+ String sql = StringUtilsExt.replaceArrayMerge(sqlDropItemsTableIfExists, new String[] { "#itemsManageTable#" },
+ new String[] { vo.getItemsManageTable() });
+ logger.debug("JDBC::doDropItemsTableIfExists sql={}", sql);
+ try {
+ Yank.execute(sql, null);
+ } catch (YankSQLException e) {
+ throw new JdbcSQLException(e);
+ }
+ return vo;
+ }
+
+ public void doDropTable(String tableName) throws JdbcSQLException {
+ String sql = StringUtilsExt.replaceArrayMerge(sqlDropTable, new String[] { "#tableName#" },
+ new String[] { tableName });
+ logger.debug("JDBC::doDropTable sql={}", sql);
+ try {
+ Yank.execute(sql, null);
+ } catch (YankSQLException e) {
+ throw new JdbcSQLException(e);
+ }
+ }
+
+ public void doDeleteItemsEntry(ItemsVO vo) throws JdbcSQLException {
+ String sql = StringUtilsExt.replaceArrayMerge(sqlDeleteItemsEntry,
+ new String[] { "#itemsManageTable#", "#itemname#" },
+ new String[] { vo.getItemsManageTable(), vo.getItemName() });
+ logger.debug("JDBC::doDeleteItemsEntry sql={}", sql);
+ try {
+ Yank.execute(sql, null);
+ } catch (YankSQLException e) {
+ throw new JdbcSQLException(e);
+ }
+ }
+
+ public List<ItemsVO> doGetItemIDTableNames(ItemsVO vo) throws JdbcSQLException {
+ String sql = StringUtilsExt.replaceArrayMerge(sqlGetItemIDTableNames, new String[] { "#itemsManageTable#" },
+ new String[] { vo.getItemsManageTable() });
+ logger.debug("JDBC::doGetItemIDTableNames sql={}", sql);
+ try {
+ return Yank.queryBeanList(sql, ItemsVO.class, null);
+ } catch (YankSQLException e) {
+ throw new JdbcSQLException(e);
+ }
+ }
+
+ public List<ItemsVO> doGetItemTables(ItemsVO vo) throws JdbcSQLException {
+ String sql = StringUtilsExt.replaceArrayMerge(sqlGetItemTables,
+ new String[] { "#jdbcUriDatabaseName#", "#itemsManageTable#" },
+ new String[] { vo.getJdbcUriDatabaseName(), vo.getItemsManageTable() });
+ logger.debug("JDBC::doGetItemTables sql={}", sql);
+ try {
+ return Yank.queryBeanList(sql, ItemsVO.class, null);
+ } catch (YankSQLException e) {
+ throw new JdbcSQLException(e);
+ }
+ }
+
+ /*************
+ * ITEM DAOs *
+ *************/
+ public void doUpdateItemTableNames(List<ItemVO> vol) throws JdbcSQLException {
+ logger.debug("JDBC::doUpdateItemTableNames vol.size = {}", vol.size());
+ for (ItemVO itemTable : vol) {
+ String sql = updateItemTableNamesProvider(itemTable);
+ try {
+ Yank.execute(sql, null);
+ } catch (YankSQLException e) {
+ throw new JdbcSQLException(e);
+ }
+ }
+ }
+
+ public void doCreateItemTable(ItemVO vo) throws JdbcSQLException {
+ String sql = StringUtilsExt.replaceArrayMerge(sqlCreateItemTable,
+ new String[] { "#tableName#", "#dbType#", "#tablePrimaryKey#" },
+ new String[] { vo.getTableName(), vo.getDbType(), sqlTypes.get("tablePrimaryKey") });
+ logger.debug("JDBC::doCreateItemTable sql={}", sql);
+ try {
+ Yank.execute(sql, null);
+ } catch (YankSQLException e) {
+ throw new JdbcSQLException(e);
+ }
+ }
+
+ public void doStoreItemValue(Item item, State itemState, ItemVO vo) throws JdbcSQLException {
+ ItemVO storedVO = storeItemValueProvider(item, itemState, vo);
+ String sql = StringUtilsExt.replaceArrayMerge(sqlInsertItemValue,
+ new String[] { "#tableName#", "#tablePrimaryValue#" },
+ new String[] { storedVO.getTableName(), sqlTypes.get("tablePrimaryValue") });
+ Object[] params = { storedVO.getValue(), storedVO.getValue() };
+ logger.debug("JDBC::doStoreItemValue sql={} value='{}'", sql, storedVO.getValue());
+ try {
+ Yank.execute(sql, params);
+ } catch (YankSQLException e) {
+ throw new JdbcSQLException(e);
+ }
+ }
+
+ public void doStoreItemValue(Item item, State itemState, ItemVO vo, ZonedDateTime date) throws JdbcSQLException {
+ ItemVO storedVO = storeItemValueProvider(item, itemState, vo);
+ String sql = StringUtilsExt.replaceArrayMerge(sqlInsertItemValue,
+ new String[] { "#tableName#", "#tablePrimaryValue#" }, new String[] { storedVO.getTableName(), "?" });
+ java.sql.Timestamp timestamp = new java.sql.Timestamp(date.toInstant().toEpochMilli());
+ Object[] params = { timestamp, storedVO.getValue(), storedVO.getValue() };
+ logger.debug("JDBC::doStoreItemValue sql={} timestamp={} value='{}'", sql, timestamp, storedVO.getValue());
+ try {
+ Yank.execute(sql, params);
+ } catch (YankSQLException e) {
+ throw new JdbcSQLException(e);
+ }
+ }
+
+ public List<HistoricItem> doGetHistItemFilterQuery(Item item, FilterCriteria filter, int numberDecimalcount,
+ String table, String name, ZoneId timeZone) throws JdbcSQLException {
+ String sql = histItemFilterQueryProvider(filter, numberDecimalcount, table, name, timeZone);
+ logger.debug("JDBC::doGetHistItemFilterQuery sql={}", sql);
+ List<Object[]> m;
+ try {
+ m = Yank.queryObjectArrays(sql, null);
+ } catch (YankSQLException e) {
+ throw new JdbcSQLException(e);
+ }
+ if (m == null) {
+ logger.debug("JDBC::doGetHistItemFilterQuery Query failed. Returning an empty list.");
+ return List.of();
+ }
+ // we already retrieve the unit here once as it is a very costly operation
+ String itemName = item.getName();
+ Unit<? extends Quantity<?>> unit = item instanceof NumberItem ? ((NumberItem) item).getUnit() : null;
+ return m.stream()
+ .map(o -> new JdbcHistoricItem(itemName, objectAsState(item, unit, o[1]), objectAsZonedDateTime(o[0])))
+ .collect(Collectors.<HistoricItem> toList());
+ }
+
+ public void doDeleteItemValues(FilterCriteria filter, String table, ZoneId timeZone) throws JdbcSQLException {
+ String sql = histItemFilterDeleteProvider(filter, table, timeZone);
+ logger.debug("JDBC::doDeleteItemValues sql={}", sql);
+ try {
+ Yank.execute(sql, null);
+ } catch (YankSQLException e) {
+ throw new JdbcSQLException(e);
+ }
+ }
+
+ public long doGetRowCount(String tableName) throws JdbcSQLException {
+ final String sql = StringUtilsExt.replaceArrayMerge(sqlGetRowCount, new String[] { "#tableName#" },
+ new String[] { tableName });
+ logger.debug("JDBC::doGetRowCount sql={}", sql);
+ try {
+ final @Nullable Long result = Yank.queryScalar(sql, Long.class, null);
+ return Objects.requireNonNullElse(result, 0L);
+ } catch (YankSQLException e) {
+ throw new JdbcSQLException(e);
+ }
+ }
+
+ /*************
+ * Providers *
+ *************/
+ static final DateTimeFormatter JDBC_DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+
+ protected String histItemFilterQueryProvider(FilterCriteria filter, int numberDecimalcount, String table,
+ String simpleName, ZoneId timeZone) {
+ logger.debug(
+ "JDBC::getHistItemFilterQueryProvider filter = {}, numberDecimalcount = {}, table = {}, simpleName = {}",
+ filter, numberDecimalcount, table, simpleName);
+
+ String filterString = resolveTimeFilter(filter, timeZone);
+ filterString += (filter.getOrdering() == Ordering.ASCENDING) ? " ORDER BY time ASC" : " ORDER BY time DESC";
+ if (filter.getPageSize() != Integer.MAX_VALUE) {
+ filterString += " LIMIT " + filter.getPageNumber() * filter.getPageSize() + "," + filter.getPageSize();
+ }
+ // SELECT time, ROUND(value,3) FROM number_item_0114 ORDER BY time DESC LIMIT 0,1
+ // rounding HALF UP
+ String queryString = "NUMBERITEM".equalsIgnoreCase(simpleName) && numberDecimalcount > -1
+ ? "SELECT time, ROUND(value," + numberDecimalcount + ") FROM " + table
+ : "SELECT time, value FROM " + table;
+ if (!filterString.isEmpty()) {
+ queryString += filterString;
+ }
+ logger.debug("JDBC::query queryString = {}", queryString);
+ return queryString;
+ }
+
+ protected String histItemFilterDeleteProvider(FilterCriteria filter, String table, ZoneId timeZone) {
+ logger.debug("JDBC::histItemFilterDeleteProvider filter = {}, table = {}", filter, table);
+
+ String filterString = resolveTimeFilter(filter, timeZone);
+ String deleteString = filterString.isEmpty() ? "TRUNCATE TABLE " + table
+ : "DELETE FROM " + table + filterString;
+ logger.debug("JDBC::delete deleteString = {}", deleteString);
+ return deleteString;
+ }
+
+ protected String resolveTimeFilter(FilterCriteria filter, ZoneId timeZone) {
+ String filterString = "";
+ if (filter.getBeginDate() != null) {
+ filterString += filterString.isEmpty() ? " WHERE" : " AND";
+ filterString += " TIME>'" + JDBC_DATE_FORMAT.format(filter.getBeginDate().withZoneSameInstant(timeZone))
+ + "'";
+ }
+ if (filter.getEndDate() != null) {
+ filterString += filterString.isEmpty() ? " WHERE" : " AND";
+ filterString += " TIME<'" + JDBC_DATE_FORMAT.format(filter.getEndDate().withZoneSameInstant(timeZone))
+ + "'";
+ }
+ return filterString;
+ }
+
+ private String updateItemTableNamesProvider(ItemVO itemTable) {
+ String queryString = "ALTER TABLE " + itemTable.getTableName() + " RENAME TO " + itemTable.getNewTableName();
+ logger.debug("JDBC::query queryString = {}", queryString);
+ return queryString;
+ }
+
+ protected ItemVO storeItemValueProvider(Item item, State itemState, ItemVO vo) {
+ String itemType = getItemType(item);
+
+ logger.debug("JDBC::storeItemValueProvider: item '{}' as Type '{}' in '{}' with state '{}'", item.getName(),
+ itemType, vo.getTableName(), itemState);
+
+ // insertItemValue
+ logger.debug("JDBC::storeItemValueProvider: itemState: '{}'", itemState);
+ /*
+ * !!ATTENTION!!
+ *
+ * 1. DimmerItem.getStateAs(PercentType.class).toString() always
+ * returns 0
+ * RollershutterItem.getStateAs(PercentType.class).toString() works
+ * as expected
+ *
+ * 2. (item instanceof ColorItem) == (item instanceof DimmerItem) =
+ * true Therefore for instance tests ColorItem always has to be
+ * tested before DimmerItem
+ *
+ * !!ATTENTION!!
+ */
+ switch (itemType) {
+ case "COLORITEM":
+ vo.setValueTypes(getSqlTypes().get(itemType), java.lang.String.class);
+ vo.setValue(itemState.toString());
+ break;
+ case "NUMBERITEM":
+ State convertedState = itemState;
+ if (item instanceof NumberItem && itemState instanceof QuantityType) {
+ Unit<? extends Quantity<?>> unit = ((NumberItem) item).getUnit();
+ if (unit != null && !Units.ONE.equals(unit)) {
+ convertedState = ((QuantityType<?>) itemState).toUnit(unit);
+ if (convertedState == null) {
+ logger.warn(
+ "JDBC::storeItemValueProvider: Failed to convert state '{}' to unit '{}'. Please check your item definition for correctness.",
+ itemState, unit);
+ convertedState = itemState;
+ }
+ }
+ }
+ String it = getSqlTypes().get(itemType);
+ if (it == null) {
+ logger.warn("JDBC::storeItemValueProvider: No SQL type defined for item type {}", itemType);
+ } else if (it.toUpperCase().contains("DOUBLE")) {
+ vo.setValueTypes(it, java.lang.Double.class);
+ double value = ((Number) convertedState).doubleValue();
+ logger.debug("JDBC::storeItemValueProvider: newVal.doubleValue: '{}'", value);
+ vo.setValue(value);
+ } else if (it.toUpperCase().contains("DECIMAL") || it.toUpperCase().contains("NUMERIC")) {
+ vo.setValueTypes(it, java.math.BigDecimal.class);
+ BigDecimal value = BigDecimal.valueOf(((Number) convertedState).doubleValue());
+ logger.debug("JDBC::storeItemValueProvider: newVal.toBigDecimal: '{}'", value);
+ vo.setValue(value);
+ } else if (it.toUpperCase().contains("INT")) {
+ vo.setValueTypes(it, java.lang.Integer.class);
+ int value = ((Number) convertedState).intValue();
+ logger.debug("JDBC::storeItemValueProvider: newVal.intValue: '{}'", value);
+ vo.setValue(value);
+ } else {// fall back to String
+ vo.setValueTypes(it, java.lang.String.class);
+ logger.warn("JDBC::storeItemValueProvider: itemState: '{}'", convertedState);
+ vo.setValue(convertedState.toString());
+ }
+ break;
+ case "ROLLERSHUTTERITEM":
+ case "DIMMERITEM":
+ vo.setValueTypes(getSqlTypes().get(itemType), java.lang.Integer.class);
+ int value = ((DecimalType) itemState).intValue();
+ logger.debug("JDBC::storeItemValueProvider: newVal.intValue: '{}'", value);
+ vo.setValue(value);
+ break;
+ case "DATETIMEITEM":
+ vo.setValueTypes(getSqlTypes().get(itemType), java.sql.Timestamp.class);
+ java.sql.Timestamp d = new java.sql.Timestamp(
+ ((DateTimeType) itemState).getZonedDateTime().toInstant().toEpochMilli());
+ logger.debug("JDBC::storeItemValueProvider: DateTimeItem: '{}'", d);
+ vo.setValue(d);
+ break;
+ case "IMAGEITEM":
+ vo.setValueTypes(getSqlTypes().get(itemType), java.lang.String.class);
+ String encodedString = item.getState().toFullString();
+ logger.debug("JDBC::storeItemValueProvider: ImageItem: '{}'", encodedString);
+ vo.setValue(encodedString);
+ break;
+ default:
+ // All other items should return the best format by default
+ vo.setValueTypes(getSqlTypes().get(itemType), java.lang.String.class);
+ logger.debug("JDBC::storeItemValueProvider: other: itemState: '{}'", itemState);
+ vo.setValue(itemState.toString());
+ break;
+ }
+ return vo;
+ }
+
+ /*****************
+ * H E L P E R S *
+ *****************/
+ protected State objectAsState(Item item, @Nullable Unit<? extends Quantity<?>> unit, Object v) {
+ logger.debug(
+ "JDBC::ItemResultHandler::handleResult getState value = '{}', unit = '{}', getClass = '{}', clazz = '{}'",
+ v, unit, v.getClass(), v.getClass().getSimpleName());
+ if (item instanceof NumberItem) {
+ String it = getSqlTypes().get("NUMBERITEM");
+ if (it == null) {
+ throw new UnsupportedOperationException("No SQL type defined for item type NUMBERITEM");
+ }
+ if (it.toUpperCase().contains("DOUBLE")) {
+ return unit == null ? new DecimalType(((Number) v).doubleValue())
+ : QuantityType.valueOf(((Number) v).doubleValue(), unit);
+ } else if (it.toUpperCase().contains("DECIMAL") || it.toUpperCase().contains("NUMERIC")) {
+ return unit == null ? new DecimalType((BigDecimal) v)
+ : QuantityType.valueOf(((BigDecimal) v).doubleValue(), unit);
+ } else if (it.toUpperCase().contains("INT")) {
+ return unit == null ? new DecimalType(objectAsInteger(v))
+ : QuantityType.valueOf(((Integer) v).doubleValue(), unit);
+ }
+ return unit == null ? DecimalType.valueOf(objectAsString(v)) : QuantityType.valueOf(objectAsString(v));
+ } else if (item instanceof DateTimeItem) {
+ return new DateTimeType(objectAsZonedDateTime(v));
+ } else if (item instanceof ColorItem) {
+ return HSBType.valueOf(objectAsString(v));
+ } else if (item instanceof DimmerItem || item instanceof RollershutterItem) {
+ return new PercentType(objectAsInteger(v));
+ } else if (item instanceof ImageItem) {
+ return RawType.valueOf(objectAsString(v));
+ } else if (item instanceof ContactItem || item instanceof PlayerItem || item instanceof SwitchItem) {
+ State state = TypeParser.parseState(item.getAcceptedDataTypes(), ((String) v).toString().trim());
+ if (state == null) {
+ throw new UnsupportedOperationException("Unable to parse state for item " + item.toString());
+ }
+ return state;
+ } else {
+ State state = TypeParser.parseState(item.getAcceptedDataTypes(), ((String) v).toString());
+ if (state == null) {
+ throw new UnsupportedOperationException("Unable to parse state for item " + item.toString());
+ }
+ return state;
+ }
+ }
+
+ protected ZonedDateTime objectAsZonedDateTime(Object v) {
+ if (v instanceof Long) {
+ return ZonedDateTime.ofInstant(Instant.ofEpochMilli(((Number) v).longValue()), ZoneId.systemDefault());
+ } else if (v instanceof java.sql.Date) {
+ return ZonedDateTime.ofInstant(Instant.ofEpochMilli(((java.sql.Date) v).getTime()), ZoneId.systemDefault());
+ } else if (v instanceof LocalDateTime) {
+ return ((LocalDateTime) v).atZone(ZoneId.systemDefault());
+ } else if (v instanceof Instant) {
+ return ((Instant) v).atZone(ZoneId.systemDefault());
+ } else if (v instanceof java.sql.Timestamp) {
+ return ((java.sql.Timestamp) v).toInstant().atZone(ZoneId.systemDefault());
+ } else if (v instanceof java.lang.String) {
+ return ZonedDateTime.ofInstant(java.sql.Timestamp.valueOf(v.toString()).toInstant(),
+ ZoneId.systemDefault());
+ }
+ throw new UnsupportedOperationException("Date of type " + v.getClass().getName() + " is not supported");
+ }
+
+ protected Integer objectAsInteger(Object v) {
+ if (v instanceof Byte) {
+ return ((Byte) v).intValue();
+ }
+ return ((Integer) v).intValue();
+ }
+
+ protected String objectAsString(Object v) {
+ if (v instanceof byte[]) {
+ return new String((byte[]) v);
+ }
+ return ((String) v).toString();
+ }
+
+ public String getItemType(Item i) {
+ Item item = i;
+ String def = "STRINGITEM";
+ if (i instanceof GroupItem) {
+ item = ((GroupItem) i).getBaseItem();
+ if (item == null) {
+ // if GroupItem:<ItemType> is not defined in *.items using StringType
+ logger.debug(
+ "JDBC::getItemType: Cannot detect ItemType for {} because the GroupItems' base type isn't set in *.items File.",
+ i.getName());
+ Iterator<Item> iterator = ((GroupItem) i).getMembers().iterator();
+ if (!iterator.hasNext()) {
+ logger.debug(
+ "JDBC::getItemType: No Child-Members of GroupItem {}, use ItemType for STRINGITEM as Fallback",
+ i.getName());
+ return def;
+ }
+ item = iterator.next();
+ }
+ }
+ String itemType = item.getClass().getSimpleName().toUpperCase();
+ logger.debug("JDBC::getItemType: Try to use ItemType {} for Item {}", itemType, i.getName());
+ if (sqlTypes.get(itemType) == null) {
+ logger.warn(
+ "JDBC::getItemType: No sqlType found for ItemType {}, use ItemType for STRINGITEM as Fallback for {}",
+ itemType, i.getName());
+ return def;
+ }
+ return itemType;
+ }
+
+ /******************************
+ * public Getters and Setters *
+ ******************************/
+ public Map<String, String> getSqlTypes() {
+ return sqlTypes;
+ }
+
+ public String getDataType(Item item) {
+ String dataType = sqlTypes.get(getItemType(item));
+ if (dataType == null) {
+ throw new UnsupportedOperationException("No data type found for " + getItemType(item));
+ }
+ return dataType;
+ }
+}
--- /dev/null
+/**
+ * Copyright (c) 2010-2022 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.persistence.jdbc.internal.db;
+
+import java.time.ZoneId;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+import javax.measure.Quantity;
+import javax.measure.Unit;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.knowm.yank.Yank;
+import org.knowm.yank.exceptions.YankSQLException;
+import org.openhab.core.items.Item;
+import org.openhab.core.library.items.NumberItem;
+import org.openhab.core.persistence.FilterCriteria;
+import org.openhab.core.persistence.FilterCriteria.Ordering;
+import org.openhab.core.persistence.HistoricItem;
+import org.openhab.core.types.State;
+import org.openhab.persistence.jdbc.internal.dto.ItemVO;
+import org.openhab.persistence.jdbc.internal.dto.ItemsVO;
+import org.openhab.persistence.jdbc.internal.dto.JdbcHistoricItem;
+import org.openhab.persistence.jdbc.internal.exceptions.JdbcSQLException;
+import org.openhab.persistence.jdbc.internal.utils.StringUtilsExt;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Extended Database Configuration class. Class represents
+ * the extended database-specific configuration. Overrides and supplements the
+ * default settings from JdbcBaseDAO. Enter only the differences to JdbcBaseDAO here.
+ *
+ * @author Helmut Lehmeyer - Initial contribution
+ */
+@NonNullByDefault
+public class JdbcDerbyDAO extends JdbcBaseDAO {
+ private static final String DRIVER_CLASS_NAME = org.apache.derby.jdbc.EmbeddedDriver.class.getName();
+ @SuppressWarnings("unused")
+ private static final String DATA_SOURCE_CLASS_NAME = org.apache.derby.jdbc.EmbeddedDataSource.class.getName();
+
+ private final Logger logger = LoggerFactory.getLogger(JdbcDerbyDAO.class);
+
+ /********
+ * INIT *
+ ********/
+ public JdbcDerbyDAO() {
+ initSqlTypes();
+ initDbProps();
+ initSqlQueries();
+ }
+
+ private void initSqlQueries() {
+ logger.debug("JDBC::initSqlQueries: '{}'", this.getClass().getSimpleName());
+ sqlPingDB = "values 1";
+ sqlGetDB = "VALUES SYSCS_UTIL.SYSCS_GET_DATABASE_PROPERTY( 'DataDictionaryVersion' )"; // returns version
+ sqlIfTableExists = "SELECT * FROM SYS.SYSTABLES WHERE TABLENAME='#searchTable#'";
+ sqlCreateItemsTableIfNot = "CREATE TABLE #itemsManageTable# ( ItemId INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 1, INCREMENT BY 1), #colname# #coltype# NOT NULL)";
+ sqlCreateItemTable = "CREATE TABLE #tableName# (time #tablePrimaryKey# NOT NULL, value #dbType#, PRIMARY KEY(time))";
+ // Prevent error against duplicate time value (seldom): No powerful Merge found:
+ // http://www.codeproject.com/Questions/162627/how-to-insert-new-record-in-my-table-if-not-exists
+ sqlInsertItemValue = "INSERT INTO #tableName# (TIME, VALUE) VALUES( #tablePrimaryValue#, CAST( ? as #dbType#) )";
+ }
+
+ private void initSqlTypes() {
+ sqlTypes.put("DATETIMEITEM", "TIMESTAMP");
+ sqlTypes.put("DIMMERITEM", "SMALLINT");
+ sqlTypes.put("IMAGEITEM", "VARCHAR(32000)");
+ sqlTypes.put("ROLLERSHUTTERITEM", "SMALLINT");
+ sqlTypes.put("STRINGITEM", "VARCHAR(32000)");
+ sqlTypes.put("tablePrimaryValue", "CURRENT_TIMESTAMP");
+ logger.debug("JDBC::initSqlTypes: Initialized the type array sqlTypes={}", sqlTypes.values());
+ }
+
+ /**
+ * INFO: https://github.com/brettwooldridge/HikariCP
+ */
+ private void initDbProps() {
+ // Properties for HikariCP
+ // Use driverClassName
+ databaseProps.setProperty("driverClassName", DRIVER_CLASS_NAME);
+ // OR dataSourceClassName
+ // databaseProps.setProperty("dataSourceClassName", DATA_SOURCE_CLASS_NAME);
+ databaseProps.setProperty("maximumPoolSize", "1");
+ databaseProps.setProperty("minimumIdle", "1");
+ }
+
+ @Override
+ public void initAfterFirstDbConnection() {
+ logger.debug("JDBC::initAfterFirstDbConnection: Initializing step, after db is connected.");
+ // Initialize sqlTypes, depending on DB version for example
+ // derby does not like this... dbMeta = new DbMetaData();// get DB information
+ }
+
+ /**************
+ * ITEMS DAOs *
+ **************/
+ @Override
+ public @Nullable Integer doPingDB() throws JdbcSQLException {
+ try {
+ return Yank.queryScalar(sqlPingDB, Integer.class, null);
+ } catch (YankSQLException e) {
+ throw new JdbcSQLException(e);
+ }
+ }
+
+ @Override
+ public boolean doIfTableExists(ItemsVO vo) throws JdbcSQLException {
+ String sql = StringUtilsExt.replaceArrayMerge(sqlIfTableExists, new String[] { "#searchTable#" },
+ new String[] { vo.getItemsManageTable().toUpperCase() });
+ logger.debug("JDBC::doIfTableExists sql={}", sql);
+ try {
+ final @Nullable String result = Yank.queryScalar(sql, String.class, null);
+ return Objects.nonNull(result);
+ } catch (YankSQLException e) {
+ throw new JdbcSQLException(e);
+ }
+ }
+
+ @Override
+ public Long doCreateNewEntryInItemsTable(ItemsVO vo) throws JdbcSQLException {
+ String sql = StringUtilsExt.replaceArrayMerge(sqlCreateNewEntryInItemsTable,
+ new String[] { "#itemsManageTable#", "#itemname#" },
+ new String[] { vo.getItemsManageTable().toUpperCase(), vo.getItemName() });
+ logger.debug("JDBC::doCreateNewEntryInItemsTable sql={}", sql);
+ try {
+ return Yank.insert(sql, null);
+ } catch (YankSQLException e) {
+ throw new JdbcSQLException(e);
+ }
+ }
+
+ @Override
+ public ItemsVO doCreateItemsTableIfNot(ItemsVO vo) throws JdbcSQLException {
+ boolean tableExists = doIfTableExists(vo);
+ if (!tableExists) {
+ String sql = StringUtilsExt.replaceArrayMerge(sqlCreateItemsTableIfNot,
+ new String[] { "#itemsManageTable#", "#colname#", "#coltype#" },
+ new String[] { vo.getItemsManageTable().toUpperCase(), vo.getColname(), vo.getColtype() });
+ logger.debug("JDBC::doCreateItemsTableIfNot tableExists={} therefore sql={}", tableExists, sql);
+ try {
+ Yank.execute(sql, null);
+ } catch (YankSQLException e) {
+ throw new JdbcSQLException(e);
+ }
+ } else {
+ logger.debug("JDBC::doCreateItemsTableIfNot tableExists={}, did not CREATE TABLE", tableExists);
+ }
+ return vo;
+ }
+
+ /*************
+ * ITEM DAOs *
+ *************/
+ @Override
+ public void doCreateItemTable(ItemVO vo) throws JdbcSQLException {
+ String sql = StringUtilsExt.replaceArrayMerge(sqlCreateItemTable,
+ new String[] { "#tableName#", "#dbType#", "#tablePrimaryKey#" },
+ new String[] { vo.getTableName(), vo.getDbType(), sqlTypes.get("tablePrimaryKey") });
+ try {
+ Yank.execute(sql, null);
+ } catch (YankSQLException e) {
+ throw new JdbcSQLException(e);
+ }
+ }
+
+ @Override
+ public void doStoreItemValue(Item item, State itemState, ItemVO vo) throws JdbcSQLException {
+ ItemVO storedVO = storeItemValueProvider(item, itemState, vo);
+ String sql = StringUtilsExt.replaceArrayMerge(sqlInsertItemValue,
+ new String[] { "#tableName#", "#dbType#", "#tablePrimaryValue#" },
+ new String[] { storedVO.getTableName().toUpperCase(), storedVO.getDbType(),
+ sqlTypes.get("tablePrimaryValue") });
+ Object[] params = { storedVO.getValue() };
+ logger.debug("JDBC::doStoreItemValue sql={} value='{}'", sql, storedVO.getValue());
+ try {
+ Yank.execute(sql, params);
+ } catch (YankSQLException e) {
+ throw new JdbcSQLException(e);
+ }
+ }
+
+ @Override
+ public List<HistoricItem> doGetHistItemFilterQuery(Item item, FilterCriteria filter, int numberDecimalcount,
+ String table, String name, ZoneId timeZone) throws JdbcSQLException {
+ String sql = histItemFilterQueryProvider(filter, numberDecimalcount, table, name, timeZone);
+ List<Object[]> m;
+ try {
+ m = Yank.queryObjectArrays(sql, null);
+ } catch (YankSQLException e) {
+ throw new JdbcSQLException(e);
+ }
+ logger.debug("JDBC::doGetHistItemFilterQuery got Array length={}", m.size());
+ // we already retrieve the unit here once as it is a very costly operation
+ String itemName = item.getName();
+ Unit<? extends Quantity<?>> unit = item instanceof NumberItem ? ((NumberItem) item).getUnit() : null;
+ return m.stream().map(o -> {
+ logger.debug("JDBC::doGetHistItemFilterQuery 0='{}' 1='{}'", o[0], o[1]);
+ return new JdbcHistoricItem(itemName, objectAsState(item, unit, o[1]), objectAsZonedDateTime(o[0]));
+ }).collect(Collectors.<HistoricItem> toList());
+ }
+
+ /****************************
+ * SQL generation Providers *
+ ****************************/
+
+ @Override
+ protected String histItemFilterQueryProvider(FilterCriteria filter, int numberDecimalcount, String table,
+ String simpleName, ZoneId timeZone) {
+ logger.debug(
+ "JDBC::getHistItemFilterQueryProvider filter = {}, numberDecimalcount = {}, table = {}, simpleName = {}",
+ StringUtilsExt.filterToString(filter), numberDecimalcount, table, simpleName);
+
+ String filterString = "";
+ if (filter.getBeginDate() != null) {
+ filterString += filterString.isEmpty() ? " WHERE" : " AND";
+ filterString += " TIME>'" + JDBC_DATE_FORMAT.format(filter.getBeginDate().withZoneSameInstant(timeZone))
+ + "'";
+ }
+ if (filter.getEndDate() != null) {
+ filterString += filterString.isEmpty() ? " WHERE" : " AND";
+ filterString += " TIME<'" + JDBC_DATE_FORMAT.format(filter.getEndDate().withZoneSameInstant(timeZone))
+ + "'";
+ }
+ filterString += (filter.getOrdering() == Ordering.ASCENDING) ? " ORDER BY time ASC" : " ORDER BY time DESC";
+ if (filter.getPageSize() != 0x7fffffff) {
+ // TODO: TESTING!!!
+ // filterString += " LIMIT " + filter.getPageNumber() *
+ // filter.getPageSize() + "," + filter.getPageSize();
+ // SELECT time, value FROM ohscriptfiles_sw_ace_paths_0001 ORDER BY
+ // time DESC OFFSET 1 ROWS FETCH NEXT 0 ROWS ONLY
+ // filterString += " OFFSET " + filter.getPageSize() +" ROWS FETCH
+ // FIRST||NEXT " + filter.getPageNumber() * filter.getPageSize() + "
+ // ROWS ONLY";
+ filterString += " OFFSET " + filter.getPageSize() + " ROWS FETCH FIRST "
+ + (filter.getPageNumber() * filter.getPageSize() + 1) + " ROWS ONLY";
+ }
+
+ // http://www.seemoredata.com/en/showthread.php?132-Round-function-in-Apache-Derby
+ // simulated round function in Derby: CAST(value 0.0005 AS DECIMAL(15,3))
+ // simulated round function in Derby: "CAST(value 0.0005 AS DECIMAL(15,"+numberDecimalcount+"))"
+
+ String queryString = "SELECT time,";
+ if ("NUMBERITEM".equalsIgnoreCase(simpleName) && numberDecimalcount > -1) {
+ // rounding HALF UP
+ queryString += "CAST(value 0.";
+ for (int i = 0; i < numberDecimalcount; i++) {
+ queryString += "0";
+ }
+ queryString += "5 AS DECIMAL(31," + numberDecimalcount + "))"; // 31 is DECIMAL max precision
+ // https://db.apache.org/derby/docs/10.0/manuals/develop/develop151.html
+ } else {
+ queryString += " value FROM " + table.toUpperCase();
+ }
+
+ if (!filterString.isEmpty()) {
+ queryString += filterString;
+ }
+ logger.debug("JDBC::query queryString = {}", queryString);
+ return queryString;
+ }
+
+ /*****************
+ * H E L P E R S *
+ *****************/
+
+ /******************************
+ * public Getters and Setters *
+ ******************************/
+}
--- /dev/null
+/**
+ * Copyright (c) 2010-2022 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.persistence.jdbc.internal.db;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.knowm.yank.Yank;
+import org.knowm.yank.exceptions.YankSQLException;
+import org.openhab.core.items.Item;
+import org.openhab.core.types.State;
+import org.openhab.persistence.jdbc.internal.dto.ItemVO;
+import org.openhab.persistence.jdbc.internal.exceptions.JdbcSQLException;
+import org.openhab.persistence.jdbc.internal.utils.StringUtilsExt;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Extended Database Configuration class. Class represents
+ * the extended database-specific configuration. Overrides and supplements the
+ * default settings from JdbcBaseDAO. Enter only the differences to JdbcBaseDAO here.
+ *
+ * @author Helmut Lehmeyer - Initial contribution
+ */
+@NonNullByDefault
+public class JdbcH2DAO extends JdbcBaseDAO {
+ private static final String DRIVER_CLASS_NAME = org.h2.Driver.class.getName();
+ @SuppressWarnings("unused")
+ private static final String DATA_SOURCE_CLASS_NAME = org.h2.jdbcx.JdbcDataSource.class.getName();
+
+ private final Logger logger = LoggerFactory.getLogger(JdbcH2DAO.class);
+
+ /********
+ * INIT *
+ ********/
+ public JdbcH2DAO() {
+ initSqlQueries();
+ initSqlTypes();
+ initDbProps();
+ }
+
+ private void initSqlQueries() {
+ logger.debug("JDBC::initSqlQueries: '{}'", this.getClass().getSimpleName());
+ sqlIfTableExists = "SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME='#searchTable#'";
+ // SQL_INSERT_ITEM_VALUE = "INSERT INTO #tableName# (TIME, VALUE) VALUES( NOW(), CAST( ? as #dbType#) )";
+ // http://stackoverflow.com/questions/19768051/h2-sql-database-insert-if-the-record-does-not-exist
+ sqlInsertItemValue = "MERGE INTO #tableName# (TIME, VALUE) VALUES( #tablePrimaryValue#, CAST( ? as #dbType#) )";
+ }
+
+ /**
+ * INFO: http://www.java2s.com/Code/Java/Database-SQL-JDBC/StandardSQLDataTypeswithTheirJavaEquivalents.htm
+ */
+ private void initSqlTypes() {
+ }
+
+ /**
+ * INFO: https://github.com/brettwooldridge/HikariCP
+ */
+ private void initDbProps() {
+ // Properties for HikariCP
+ databaseProps.setProperty("driverClassName", DRIVER_CLASS_NAME);
+ // driverClassName OR BETTER USE dataSourceClassName
+ // databaseProps.setProperty("dataSourceClassName", DATA_SOURCE_CLASS_NAME);
+ }
+
+ /**************
+ * ITEMS DAOs *
+ **************/
+
+ /*************
+ * ITEM DAOs *
+ *************/
+ @Override
+ public void doStoreItemValue(Item item, State itemState, ItemVO vo) throws JdbcSQLException {
+ ItemVO storedVO = storeItemValueProvider(item, itemState, vo);
+ String sql = StringUtilsExt.replaceArrayMerge(sqlInsertItemValue,
+ new String[] { "#tableName#", "#dbType#", "#tablePrimaryValue#" },
+ new String[] { storedVO.getTableName(), storedVO.getDbType(), sqlTypes.get("tablePrimaryValue") });
+ Object[] params = { storedVO.getValue() };
+ logger.debug("JDBC::doStoreItemValue sql={} value='{}'", sql, storedVO.getValue());
+ try {
+ Yank.execute(sql, params);
+ } catch (YankSQLException e) {
+ throw new JdbcSQLException(e);
+ }
+ }
+
+ /****************************
+ * SQL generation Providers *
+ ****************************/
+
+ /*****************
+ * H E L P E R S *
+ *****************/
+
+ /******************************
+ * public Getters and Setters *
+ ******************************/
+}
--- /dev/null
+/**
+ * Copyright (c) 2010-2022 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.persistence.jdbc.internal.db;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.knowm.yank.Yank;
+import org.knowm.yank.exceptions.YankSQLException;
+import org.openhab.core.items.Item;
+import org.openhab.core.types.State;
+import org.openhab.persistence.jdbc.internal.dto.ItemVO;
+import org.openhab.persistence.jdbc.internal.dto.ItemsVO;
+import org.openhab.persistence.jdbc.internal.exceptions.JdbcSQLException;
+import org.openhab.persistence.jdbc.internal.utils.StringUtilsExt;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Extended Database Configuration class. Class represents
+ * the extended database-specific configuration. Overrides and supplements the
+ * default settings from JdbcBaseDAO. Enter only the differences to JdbcBaseDAO here.
+ *
+ * @author Helmut Lehmeyer - Initial contribution
+ */
+@NonNullByDefault
+public class JdbcHsqldbDAO extends JdbcBaseDAO {
+ private static final String DRIVER_CLASS_NAME = org.hsqldb.jdbcDriver.class.getName();
+ @SuppressWarnings("unused")
+ private static final String DATA_SOURCE_CLASS_NAME = org.hsqldb.jdbc.JDBCDataSource.class.getName();
+
+ private final Logger logger = LoggerFactory.getLogger(JdbcHsqldbDAO.class);
+
+ /********
+ * INIT *
+ ********/
+ public JdbcHsqldbDAO() {
+ initSqlQueries();
+ initSqlTypes();
+ initDbProps();
+ }
+
+ private void initSqlQueries() {
+ logger.debug("JDBC::initSqlQueries: '{}'", this.getClass().getSimpleName());
+ // http://hsqldb.org/doc/guide/builtinfunctions-chapt.html
+ sqlPingDB = "SELECT 1 FROM INFORMATION_SCHEMA.SYSTEM_USERS";
+ sqlGetDB = "SELECT DATABASE () FROM INFORMATION_SCHEMA.SYSTEM_USERS";
+ sqlIfTableExists = "SELECT * FROM INFORMATION_SCHEMA.SYSTEM_TABLES WHERE TABLE_NAME='#searchTable#'";
+ sqlCreateItemsTableIfNot = "CREATE TABLE IF NOT EXISTS #itemsManageTable# ( ItemId INT GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1) NOT NULL, #colname# #coltype# NOT NULL)";
+ sqlCreateNewEntryInItemsTable = "INSERT INTO #itemsManageTable# (ItemName) VALUES ('#itemname#')";
+ // Prevent error against duplicate time value
+ // http://hsqldb.org/doc/guide/dataaccess-chapt.html#dac_merge_statement
+ // SQL_INSERT_ITEM_VALUE = "INSERT INTO #tableName# (TIME, VALUE) VALUES( NOW(), CAST( ? as #dbType#) )";
+ sqlInsertItemValue = "MERGE INTO #tableName# "
+ + "USING (VALUES #tablePrimaryValue#, CAST( ? as #dbType#)) temp (TIME, VALUE) ON (#tableName#.TIME=temp.TIME) "
+ + "WHEN NOT MATCHED THEN INSERT (TIME, VALUE) VALUES (temp.TIME, temp.VALUE)";
+ }
+
+ /**
+ * INFO: http://www.java2s.com/Code/Java/Database-SQL-JDBC/StandardSQLDataTypeswithTheirJavaEquivalents.htm
+ */
+ private void initSqlTypes() {
+ }
+
+ /**
+ * INFO: https://github.com/brettwooldridge/HikariCP
+ */
+ private void initDbProps() {
+ // Properties for HikariCP
+ databaseProps.setProperty("driverClassName", DRIVER_CLASS_NAME);
+ // driverClassName OR BETTER USE dataSourceClassName
+ // databaseProps.setProperty("dataSourceClassName", DATA_SOURCE_CLASS_NAME);
+ }
+
+ /**************
+ * ITEMS DAOs *
+ **************/
+ @Override
+ public @Nullable Integer doPingDB() throws JdbcSQLException {
+ try {
+ return Yank.queryScalar(sqlPingDB, Integer.class, null);
+ } catch (YankSQLException e) {
+ throw new JdbcSQLException(e);
+ }
+ }
+
+ @Override
+ public ItemsVO doCreateItemsTableIfNot(ItemsVO vo) throws JdbcSQLException {
+ String sql = StringUtilsExt.replaceArrayMerge(sqlCreateItemsTableIfNot,
+ new String[] { "#itemsManageTable#", "#colname#", "#coltype#", "#itemsManageTable#" },
+ new String[] { vo.getItemsManageTable(), vo.getColname(), vo.getColtype(), vo.getItemsManageTable() });
+ logger.debug("JDBC::doCreateItemsTableIfNot sql={}", sql);
+ try {
+ Yank.execute(sql, null);
+ } catch (YankSQLException e) {
+ throw new JdbcSQLException(e);
+ }
+ return vo;
+ }
+
+ @Override
+ public Long doCreateNewEntryInItemsTable(ItemsVO vo) throws JdbcSQLException {
+ String sql = StringUtilsExt.replaceArrayMerge(sqlCreateNewEntryInItemsTable,
+ new String[] { "#itemsManageTable#", "#itemname#" },
+ new String[] { vo.getItemsManageTable(), vo.getItemName() });
+ logger.debug("JDBC::doCreateNewEntryInItemsTable sql={}", sql);
+ try {
+ return Yank.insert(sql, null);
+ } catch (YankSQLException e) {
+ throw new JdbcSQLException(e);
+ }
+ }
+
+ /*************
+ * ITEM DAOs *
+ *************/
+ @Override
+ public void doStoreItemValue(Item item, State itemState, ItemVO vo) throws JdbcSQLException {
+ ItemVO storedVO = storeItemValueProvider(item, itemState, vo);
+ String sql = StringUtilsExt.replaceArrayMerge(sqlInsertItemValue,
+ new String[] { "#tableName#", "#dbType#", "#tableName#", "#tablePrimaryValue#" },
+ new String[] { storedVO.getTableName(), storedVO.getDbType(), storedVO.getTableName(),
+ sqlTypes.get("tablePrimaryValue") });
+ Object[] params = { storedVO.getValue() };
+ logger.debug("JDBC::doStoreItemValue sql={} value='{}'", sql, storedVO.getValue());
+ try {
+ Yank.execute(sql, params);
+ } catch (YankSQLException e) {
+ throw new JdbcSQLException(e);
+ }
+ }
+
+ /****************************
+ * SQL generation Providers *
+ ****************************/
+
+ /*****************
+ * H E L P E R S *
+ *****************/
+
+ /******************************
+ * public Getters and Setters *
+ ******************************/
+}
--- /dev/null
+/**
+ * Copyright (c) 2010-2022 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.persistence.jdbc.internal.db;
+
+import java.util.Objects;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.knowm.yank.Yank;
+import org.knowm.yank.exceptions.YankSQLException;
+import org.openhab.persistence.jdbc.internal.exceptions.JdbcSQLException;
+import org.openhab.persistence.jdbc.internal.utils.DbMetaData;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Extended Database Configuration class. Class represents
+ * the extended database-specific configuration. Overrides and supplements the
+ * default settings from JdbcBaseDAO. Enter only the differences to JdbcBaseDAO here.
+ *
+ * @author Helmut Lehmeyer - Initial contribution
+ */
+@NonNullByDefault
+public class JdbcMariadbDAO extends JdbcBaseDAO {
+ private static final String DRIVER_CLASS_NAME = org.mariadb.jdbc.Driver.class.getName();
+ @SuppressWarnings("unused")
+ private static final String DATA_SOURCE_CLASS_NAME = org.mariadb.jdbc.MariaDbDataSource.class.getName();
+
+ private final Logger logger = LoggerFactory.getLogger(JdbcMariadbDAO.class);
+
+ /********
+ * INIT *
+ ********/
+ public JdbcMariadbDAO() {
+ initSqlTypes();
+ initDbProps();
+ initSqlQueries();
+ }
+
+ private void initSqlQueries() {
+ logger.debug("JDBC::initSqlQueries: '{}'", this.getClass().getSimpleName());
+ }
+
+ /**
+ * INFO: http://www.java2s.com/Code/Java/Database-SQL-JDBC/StandardSQLDataTypeswithTheirJavaEquivalents.htm
+ */
+ private void initSqlTypes() {
+ logger.debug("JDBC::initSqlTypes: Initialize the type array");
+ sqlTypes.put("IMAGEITEM", "VARCHAR(16255)");
+ sqlTypes.put("STRINGITEM", "VARCHAR(16255)"); // MariaDB using utf-8 max = 16383, using 16383-128 = 16255
+ }
+
+ /**
+ * INFO: https://github.com/brettwooldridge/HikariCP
+ */
+ private void initDbProps() {
+ // Performancetuning
+ databaseProps.setProperty("dataSource.cachePrepStmts", "true");
+ databaseProps.setProperty("dataSource.prepStmtCacheSize", "250");
+ databaseProps.setProperty("dataSource.prepStmtCacheSqlLimit", "2048");
+ databaseProps.setProperty("dataSource.jdbcCompliantTruncation", "false");// jdbc standard max varchar max length
+ // of 21845
+
+ // Properties for HikariCP
+ // Use driverClassName
+ databaseProps.setProperty("driverClassName", DRIVER_CLASS_NAME);
+ // driverClassName OR BETTER USE dataSourceClassName
+ // databaseProps.setProperty("dataSourceClassName", DATA_SOURCE_CLASS_NAME);
+ databaseProps.setProperty("maximumPoolSize", "3");
+ databaseProps.setProperty("minimumIdle", "2");
+ }
+
+ @Override
+ public void initAfterFirstDbConnection() {
+ logger.debug("JDBC::initAfterFirstDbConnection: Initializing step, after db is connected.");
+ DbMetaData dbMeta = new DbMetaData();
+ this.dbMeta = dbMeta;
+ // Initialize sqlTypes, depending on DB version for example
+ if (dbMeta.isDbVersionGreater(5, 1)) {
+ sqlTypes.put("DATETIMEITEM", "TIMESTAMP(3)");
+ sqlTypes.put("tablePrimaryKey", "TIMESTAMP(3)");
+ sqlTypes.put("tablePrimaryValue", "NOW(3)");
+ }
+ }
+
+ /**************
+ * ITEMS DAOs *
+ **************/
+ @Override
+ public @Nullable Integer doPingDB() throws JdbcSQLException {
+ try {
+ final @Nullable Long result = Yank.queryScalar(sqlPingDB, Long.class, null);
+ return Objects.nonNull(result) ? result.intValue() : null;
+ } catch (YankSQLException e) {
+ throw new JdbcSQLException(e);
+ }
+ }
+
+ /*************
+ * ITEM DAOs *
+ *************/
+
+ /****************************
+ * SQL generation Providers *
+ ****************************/
+
+ /*****************
+ * H E L P E R S *
+ *****************/
+
+ /******************************
+ * public Getters and Setters *
+ ******************************/
+}
--- /dev/null
+/**
+ * Copyright (c) 2010-2022 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.persistence.jdbc.internal.db;
+
+import java.util.Objects;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.knowm.yank.Yank;
+import org.knowm.yank.exceptions.YankSQLException;
+import org.openhab.persistence.jdbc.internal.exceptions.JdbcSQLException;
+import org.openhab.persistence.jdbc.internal.utils.DbMetaData;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Extended Database Configuration class. Class represents
+ * the extended database-specific configuration. Overrides and supplements the
+ * default settings from JdbcBaseDAO. Enter only the differences to JdbcBaseDAO here.
+ *
+ * since driver version >= 6.0 sometimes timezone conversation is needed: ?serverTimezone=UTC
+ * example: dbProps.setProperty("jdbcUrl", "jdbc:mysql://192.168.0.181:3306/ItemTypeTest3?serverTimezone=UTC");//mysql
+ * 5.7
+ *
+ * @author Helmut Lehmeyer - Initial contribution
+ */
+@NonNullByDefault
+public class JdbcMysqlDAO extends JdbcBaseDAO {
+ private static final String DRIVER_CLASS_NAME = com.mysql.cj.jdbc.Driver.class.getName();
+ @SuppressWarnings("unused")
+ private static final String DATA_SOURCE_CLASS_NAME = com.mysql.cj.jdbc.MysqlDataSource.class.getName();
+
+ private final Logger logger = LoggerFactory.getLogger(JdbcMysqlDAO.class);
+
+ /********
+ * INIT *
+ ********/
+ public JdbcMysqlDAO() {
+ initSqlTypes();
+ initDbProps();
+ initSqlQueries();
+ }
+
+ private void initSqlQueries() {
+ logger.debug("JDBC::initSqlQueries: '{}'", this.getClass().getSimpleName());
+ }
+
+ /**
+ * INFO: http://www.java2s.com/Code/Java/Database-SQL-JDBC/StandardSQLDataTypeswithTheirJavaEquivalents.htm
+ */
+ private void initSqlTypes() {
+ logger.debug("JDBC::initSqlTypes: Initialize the type array");
+ sqlTypes.put("STRINGITEM", "VARCHAR(21717)");// mysql using utf-8 max 65535/3 = 21845, using 21845-128 = 21717
+ }
+
+ /**
+ * INFO: https://github.com/brettwooldridge/HikariCP
+ */
+ private void initDbProps() {
+ // Performancetuning
+ databaseProps.setProperty("dataSource.cachePrepStmts", "true");
+ databaseProps.setProperty("dataSource.prepStmtCacheSize", "250");
+ databaseProps.setProperty("dataSource.prepStmtCacheSqlLimit", "2048");
+ databaseProps.setProperty("dataSource.jdbcCompliantTruncation", "false");// jdbc standard max varchar max length
+ // of 21845
+
+ // Properties for HikariCP
+ // Use driverClassName
+ databaseProps.setProperty("driverClassName", DRIVER_CLASS_NAME);
+ // OR dataSourceClassName
+ // databaseProps.setProperty("dataSourceClassName", DATA_SOURCE_CLASS_NAME);
+ databaseProps.setProperty("maximumPoolSize", "3");
+ databaseProps.setProperty("minimumIdle", "2");
+ }
+
+ @Override
+ public void initAfterFirstDbConnection() {
+ logger.debug("JDBC::initAfterFirstDbConnection: Initializing step, after db is connected.");
+ DbMetaData dbMeta = new DbMetaData();
+ this.dbMeta = dbMeta;
+ // Initialize sqlTypes, depending on DB version for example
+ if (dbMeta.isDbVersionGreater(5, 5)) {
+ sqlTypes.put("DATETIMEITEM", "TIMESTAMP(3)");
+ sqlTypes.put("tablePrimaryKey", "TIMESTAMP(3)");
+ sqlTypes.put("tablePrimaryValue", "NOW(3)");
+ }
+ }
+
+ /**************
+ * ITEMS DAOs *
+ **************/
+ @Override
+ public @Nullable Integer doPingDB() throws JdbcSQLException {
+ try {
+ final @Nullable Long result = Yank.queryScalar(sqlPingDB, Long.class, null);
+ return Objects.nonNull(result) ? result.intValue() : null;
+ } catch (YankSQLException e) {
+ throw new JdbcSQLException(e);
+ }
+ }
+
+ /*************
+ * ITEM DAOs *
+ *************/
+
+ /****************************
+ * SQL generation Providers *
+ ****************************/
+
+ /*****************
+ * H E L P E R S *
+ *****************/
+
+ /******************************
+ * public Getters and Setters *
+ ******************************/
+}
--- /dev/null
+/**
+ * Copyright (c) 2010-2022 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.persistence.jdbc.internal.db;
+
+import java.time.ZoneId;
+import java.util.List;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.knowm.yank.Yank;
+import org.knowm.yank.exceptions.YankSQLException;
+import org.openhab.core.items.Item;
+import org.openhab.core.persistence.FilterCriteria;
+import org.openhab.core.persistence.FilterCriteria.Ordering;
+import org.openhab.core.types.State;
+import org.openhab.persistence.jdbc.internal.dto.ItemVO;
+import org.openhab.persistence.jdbc.internal.dto.ItemsVO;
+import org.openhab.persistence.jdbc.internal.exceptions.JdbcSQLException;
+import org.openhab.persistence.jdbc.internal.utils.StringUtilsExt;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Extended Database Configuration class. Class represents
+ * the extended database-specific configuration. Overrides and supplements the
+ * default settings from JdbcBaseDAO. Enter only the differences to JdbcBaseDAO here.
+ *
+ * @author Helmut Lehmeyer - Initial contribution
+ */
+@NonNullByDefault
+public class JdbcPostgresqlDAO extends JdbcBaseDAO {
+ private static final String DRIVER_CLASS_NAME = org.postgresql.Driver.class.getName();
+ @SuppressWarnings("unused")
+ private static final String DATA_SOURCE_CLASS_NAME = org.postgresql.ds.PGSimpleDataSource.class.getName();
+
+ private final Logger logger = LoggerFactory.getLogger(JdbcPostgresqlDAO.class);
+
+ /********
+ * INIT *
+ ********/
+ public JdbcPostgresqlDAO() {
+ initSqlQueries();
+ initSqlTypes();
+ initDbProps();
+ }
+
+ private void initSqlQueries() {
+ logger.debug("JDBC::initSqlQueries: '{}'", this.getClass().getSimpleName());
+ // System Information Functions: https://www.postgresql.org/docs/9.2/static/functions-info.html
+ sqlGetDB = "SELECT CURRENT_DATABASE()";
+ sqlIfTableExists = "SELECT * FROM PG_TABLES WHERE TABLENAME='#searchTable#'";
+ sqlCreateItemsTableIfNot = "CREATE TABLE IF NOT EXISTS #itemsManageTable# (itemid SERIAL NOT NULL, #colname# #coltype# NOT NULL, CONSTRAINT #itemsManageTable#_pkey PRIMARY KEY (itemid))";
+ sqlCreateNewEntryInItemsTable = "INSERT INTO items (itemname) SELECT itemname FROM #itemsManageTable# UNION VALUES ('#itemname#') EXCEPT SELECT itemname FROM items";
+ sqlGetItemTables = "SELECT table_name FROM information_schema.tables WHERE table_type='BASE TABLE' AND table_schema=(SELECT table_schema "
+ + "FROM information_schema.tables WHERE table_type='BASE TABLE' AND table_name='#itemsManageTable#') AND NOT table_name='#itemsManageTable#'";
+ // http://stackoverflow.com/questions/17267417/how-do-i-do-an-upsert-merge-insert-on-duplicate-update-in-postgresql
+ // for later use, PostgreSql > 9.5 to prevent PRIMARY key violation use:
+ // SQL_INSERT_ITEM_VALUE = "INSERT INTO #tableName# (TIME, VALUE) VALUES( NOW(), CAST( ? as #dbType#) ) ON
+ // CONFLICT DO NOTHING";
+ sqlInsertItemValue = "INSERT INTO #tableName# (TIME, VALUE) VALUES( #tablePrimaryValue#, CAST( ? as #dbType#) )";
+ }
+
+ /**
+ * INFO: http://www.java2s.com/Code/Java/Database-SQL-JDBC/StandardSQLDataTypeswithTheirJavaEquivalents.htm
+ */
+ private void initSqlTypes() {
+ // Initialize the type array
+ sqlTypes.put("CALLITEM", "VARCHAR");
+ sqlTypes.put("COLORITEM", "VARCHAR");
+ sqlTypes.put("CONTACTITEM", "VARCHAR");
+ sqlTypes.put("DATETIMEITEM", "TIMESTAMP");
+ sqlTypes.put("DIMMERITEM", "SMALLINT");
+ sqlTypes.put("IMAGEITEM", "VARCHAR");
+ sqlTypes.put("LOCATIONITEM", "VARCHAR");
+ sqlTypes.put("NUMBERITEM", "DOUBLE PRECISION");
+ sqlTypes.put("PLAYERITEM", "VARCHAR");
+ sqlTypes.put("ROLLERSHUTTERITEM", "SMALLINT");
+ sqlTypes.put("STRINGITEM", "VARCHAR");
+ sqlTypes.put("SWITCHITEM", "VARCHAR");
+ logger.debug("JDBC::initSqlTypes: Initialized the type array sqlTypes={}", sqlTypes.values());
+ }
+
+ /**
+ * INFO: https://github.com/brettwooldridge/HikariCP
+ */
+ private void initDbProps() {
+ // Performance:
+ // databaseProps.setProperty("dataSource.cachePrepStmts", "true");
+ // databaseProps.setProperty("dataSource.prepStmtCacheSize", "250");
+ // databaseProps.setProperty("dataSource.prepStmtCacheSqlLimit", "2048");
+
+ // Properties for HikariCP
+ databaseProps.setProperty("driverClassName", DRIVER_CLASS_NAME);
+ // driverClassName OR BETTER USE dataSourceClassName
+ // databaseProps.setProperty("dataSourceClassName", DATA_SOURCE_CLASS_NAME);
+ // databaseProps.setProperty("maximumPoolSize", "3");
+ // databaseProps.setProperty("minimumIdle", "2");
+ }
+
+ /**************
+ * ITEMS DAOs *
+ **************/
+ @Override
+ public ItemsVO doCreateItemsTableIfNot(ItemsVO vo) throws JdbcSQLException {
+ String sql = StringUtilsExt.replaceArrayMerge(sqlCreateItemsTableIfNot,
+ new String[] { "#itemsManageTable#", "#colname#", "#coltype#", "#itemsManageTable#" },
+ new String[] { vo.getItemsManageTable(), vo.getColname(), vo.getColtype(), vo.getItemsManageTable() });
+ logger.debug("JDBC::doCreateItemsTableIfNot sql={}", sql);
+ try {
+ Yank.execute(sql, null);
+ } catch (YankSQLException e) {
+ throw new JdbcSQLException(e);
+ }
+ return vo;
+ }
+
+ @Override
+ public Long doCreateNewEntryInItemsTable(ItemsVO vo) throws JdbcSQLException {
+ String sql = StringUtilsExt.replaceArrayMerge(sqlCreateNewEntryInItemsTable,
+ new String[] { "#itemsManageTable#", "#itemname#" },
+ new String[] { vo.getItemsManageTable(), vo.getItemName() });
+ logger.debug("JDBC::doCreateNewEntryInItemsTable sql={}", sql);
+ try {
+ return Yank.insert(sql, null);
+ } catch (YankSQLException e) {
+ throw new JdbcSQLException(e);
+ }
+ }
+
+ @Override
+ public List<ItemsVO> doGetItemTables(ItemsVO vo) throws JdbcSQLException {
+ String sql = StringUtilsExt.replaceArrayMerge(this.sqlGetItemTables,
+ new String[] { "#itemsManageTable#", "#itemsManageTable#" },
+ new String[] { vo.getItemsManageTable(), vo.getItemsManageTable() });
+ this.logger.debug("JDBC::doGetItemTables sql={}", sql);
+ try {
+ return Yank.queryBeanList(sql, ItemsVO.class, null);
+ } catch (YankSQLException e) {
+ throw new JdbcSQLException(e);
+ }
+ }
+
+ /*************
+ * ITEM DAOs *
+ *************/
+ @Override
+ public void doStoreItemValue(Item item, State itemState, ItemVO vo) throws JdbcSQLException {
+ ItemVO storedVO = storeItemValueProvider(item, itemState, vo);
+ String sql = StringUtilsExt.replaceArrayMerge(sqlInsertItemValue,
+ new String[] { "#tableName#", "#dbType#", "#tablePrimaryValue#" },
+ new String[] { storedVO.getTableName(), storedVO.getDbType(), sqlTypes.get("tablePrimaryValue") });
+ Object[] params = { storedVO.getValue() };
+ logger.debug("JDBC::doStoreItemValue sql={} value='{}'", sql, storedVO.getValue());
+ try {
+ Yank.execute(sql, params);
+ } catch (YankSQLException e) {
+ throw new JdbcSQLException(e);
+ }
+ }
+
+ /****************************
+ * SQL generation Providers *
+ ****************************/
+
+ @Override
+ protected String histItemFilterQueryProvider(FilterCriteria filter, int numberDecimalcount, String table,
+ String simpleName, ZoneId timeZone) {
+ logger.debug(
+ "JDBC::getHistItemFilterQueryProvider filter = {}, numberDecimalcount = {}, table = {}, simpleName = {}",
+ filter.toString(), numberDecimalcount, table, simpleName);
+
+ String filterString = "";
+ if (filter.getBeginDate() != null) {
+ filterString += filterString.isEmpty() ? " WHERE" : " AND";
+ filterString += " TIME>'" + JDBC_DATE_FORMAT.format(filter.getBeginDate().withZoneSameInstant(timeZone))
+ + "'";
+ }
+ if (filter.getEndDate() != null) {
+ filterString += filterString.isEmpty() ? " WHERE" : " AND";
+ filterString += " TIME<'" + JDBC_DATE_FORMAT.format(filter.getEndDate().withZoneSameInstant(timeZone))
+ + "'";
+ }
+ filterString += (filter.getOrdering() == Ordering.ASCENDING) ? " ORDER BY time ASC" : " ORDER BY time DESC";
+ if (filter.getPageSize() != 0x7fffffff) {
+ // see:
+ // http://www.jooq.org/doc/3.5/manual/sql-building/sql-statements/select-statement/limit-clause/
+ filterString += " OFFSET " + filter.getPageNumber() * filter.getPageSize() + " LIMIT "
+ + filter.getPageSize();
+ }
+ String queryString = "NUMBERITEM".equalsIgnoreCase(simpleName) && numberDecimalcount > -1
+ ? "SELECT time, ROUND(CAST (value AS numeric)," + numberDecimalcount + ") FROM " + table
+ : "SELECT time, value FROM " + table;
+ if (!filterString.isEmpty()) {
+ queryString += filterString;
+ }
+ logger.debug("JDBC::query queryString = {}", queryString);
+ return queryString;
+ }
+
+ /*****************
+ * H E L P E R S *
+ *****************/
+
+ /******************************
+ * public Getters and Setters *
+ ******************************/
+}
--- /dev/null
+/**
+ * Copyright (c) 2010-2022 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.persistence.jdbc.internal.db;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.knowm.yank.Yank;
+import org.knowm.yank.exceptions.YankSQLException;
+import org.openhab.core.items.Item;
+import org.openhab.core.types.State;
+import org.openhab.persistence.jdbc.internal.dto.ItemVO;
+import org.openhab.persistence.jdbc.internal.dto.ItemsVO;
+import org.openhab.persistence.jdbc.internal.exceptions.JdbcSQLException;
+import org.openhab.persistence.jdbc.internal.utils.StringUtilsExt;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Extended Database Configuration class. Class represents
+ * the extended database-specific configuration. Overrides and supplements the
+ * default settings from JdbcBaseDAO. Enter only the differences to JdbcBaseDAO here.
+ *
+ * @author Helmut Lehmeyer - Initial contribution
+ */
+@NonNullByDefault
+public class JdbcSqliteDAO extends JdbcBaseDAO {
+ private static final String DRIVER_CLASS_NAME = org.sqlite.JDBC.class.getName();
+ @SuppressWarnings("unused")
+ private static final String DATA_SOURCE_CLASS_NAME = org.sqlite.SQLiteDataSource.class.getName();
+
+ private final Logger logger = LoggerFactory.getLogger(JdbcSqliteDAO.class);
+
+ /********
+ * INIT *
+ ********/
+ public JdbcSqliteDAO() {
+ initSqlQueries();
+ initSqlTypes();
+ initDbProps();
+ }
+
+ private void initSqlQueries() {
+ logger.debug("JDBC::initSqlQueries: '{}'", this.getClass().getSimpleName());
+ sqlGetDB = "PRAGMA DATABASE_LIST"; // "SELECT SQLITE_VERSION()"; // "PRAGMA DATABASE_LIST"->db Path/Name
+ // "PRAGMA SCHEMA_VERSION";
+ sqlIfTableExists = "SELECT name FROM sqlite_master WHERE type='table' AND name='#searchTable#'";
+ sqlCreateItemsTableIfNot = "CREATE TABLE IF NOT EXISTS #itemsManageTable# (ItemId INTEGER PRIMARY KEY AUTOINCREMENT, #colname# #coltype# NOT NULL)";
+ sqlInsertItemValue = "INSERT OR IGNORE INTO #tableName# (TIME, VALUE) VALUES( #tablePrimaryValue#, CAST( ? as #dbType#) )";
+ }
+
+ /**
+ * INFO: http://www.java2s.com/Code/Java/Database-SQL-JDBC/StandardSQLDataTypeswithTheirJavaEquivalents.htm
+ */
+ private void initSqlTypes() {
+ logger.debug("JDBC::initSqlTypes: Initialize the type array");
+ sqlTypes.put("tablePrimaryValue", "strftime('%Y-%m-%d %H:%M:%f' , 'now' , 'localtime')");
+ }
+
+ /**
+ * INFO: https://github.com/brettwooldridge/HikariCP
+ */
+ private void initDbProps() {
+ // Properties for HikariCP
+ databaseProps.setProperty("driverClassName", DRIVER_CLASS_NAME);
+ // driverClassName OR BETTER USE dataSourceClassName
+ // databaseProps.setProperty("dataSourceClassName", DATA_SOURCE_CLASS_NAME);
+ }
+
+ /**************
+ * ITEMS DAOs *
+ **************/
+
+ @Override
+ public @Nullable String doGetDB() throws JdbcSQLException {
+ try {
+ return Yank.queryColumn(sqlGetDB, "file", String.class, null).get(0);
+ } catch (YankSQLException e) {
+ throw new JdbcSQLException(e);
+ }
+ }
+
+ @Override
+ public ItemsVO doCreateItemsTableIfNot(ItemsVO vo) throws JdbcSQLException {
+ String sql = StringUtilsExt.replaceArrayMerge(sqlCreateItemsTableIfNot,
+ new String[] { "#itemsManageTable#", "#colname#", "#coltype#" },
+ new String[] { vo.getItemsManageTable(), vo.getColname(), vo.getColtype() });
+ logger.debug("JDBC::doCreateItemsTableIfNot sql={}", sql);
+ try {
+ Yank.execute(sql, null);
+ } catch (YankSQLException e) {
+ throw new JdbcSQLException(e);
+ }
+ return vo;
+ }
+
+ /*************
+ * ITEM DAOs *
+ *************/
+ @Override
+ public void doStoreItemValue(Item item, State itemState, ItemVO vo) throws JdbcSQLException {
+ ItemVO storedVO = storeItemValueProvider(item, itemState, vo);
+ String sql = StringUtilsExt.replaceArrayMerge(sqlInsertItemValue,
+ new String[] { "#tableName#", "#dbType#", "#tablePrimaryValue#" },
+ new String[] { storedVO.getTableName(), storedVO.getDbType(), sqlTypes.get("tablePrimaryValue") });
+ Object[] params = { storedVO.getValue() };
+ logger.debug("JDBC::doStoreItemValue sql={} value='{}'", sql, storedVO.getValue());
+ try {
+ Yank.execute(sql, params);
+ } catch (YankSQLException e) {
+ throw new JdbcSQLException(e);
+ }
+ }
+
+ /****************************
+ * SQL generation Providers *
+ ****************************/
+
+ /*****************
+ * H E L P E R S *
+ *****************/
+
+ /******************************
+ * public Getters and Setters *
+ ******************************/
+}
--- /dev/null
+/**
+ * Copyright (c) 2010-2022 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.persistence.jdbc.internal.db;
+
+import java.util.Properties;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.knowm.yank.Yank;
+import org.knowm.yank.exceptions.YankSQLException;
+import org.openhab.persistence.jdbc.internal.dto.ItemVO;
+import org.openhab.persistence.jdbc.internal.exceptions.JdbcSQLException;
+import org.openhab.persistence.jdbc.internal.utils.StringUtilsExt;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Extended Database Configuration class. Class represents the extended database-specific configuration. Overrides and
+ * supplements the default settings from JdbcBaseDAO and JdbcPostgresqlDAO.
+ *
+ * @author Riccardo Nimser-Joseph - Initial contribution
+ * @author Dan Cunningham - Fixes and refactoring
+ */
+@NonNullByDefault
+public class JdbcTimescaledbDAO extends JdbcPostgresqlDAO {
+ private final Logger logger = LoggerFactory.getLogger(JdbcTimescaledbDAO.class);
+
+ private final String sqlCreateHypertable = "SELECT created from create_hypertable('#tableName#', 'time')";
+
+ @Override
+ public Properties getConnectionProperties() {
+ Properties properties = (Properties) this.databaseProps.clone();
+ // Adjust the jdbc url since the service name 'timescaledb' is only used to differentiate the DAOs
+ if (properties.containsKey("jdbcUrl")) {
+ properties.put("jdbcUrl", properties.getProperty("jdbcUrl").replace("timescaledb", "postgresql"));
+ }
+ return properties;
+ }
+
+ @Override
+ public void doCreateItemTable(ItemVO vo) throws JdbcSQLException {
+ super.doCreateItemTable(vo);
+ String sql = StringUtilsExt.replaceArrayMerge(this.sqlCreateHypertable, new String[] { "#tableName#" },
+ new String[] { vo.getTableName() });
+ this.logger.debug("JDBC::doCreateItemTable sql={}", sql);
+ try {
+ Yank.queryScalar(sql, Boolean.class, null);
+ } catch (YankSQLException e) {
+ throw new JdbcSQLException(e);
+ }
+ }
+}
--- /dev/null
+/**
+ * Copyright (c) 2010-2022 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.persistence.jdbc.internal.dto;
+
+import java.io.Serializable;
+import java.util.Date;
+import java.util.Objects;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Represents the Item-data on the part of MyBatis/database.
+ *
+ * @author Helmut Lehmeyer - Initial contribution
+ */
+public class ItemVO implements Serializable {
+ private final Logger logger = LoggerFactory.getLogger(ItemVO.class);
+
+ private static final long serialVersionUID = 1871441039821454890L;
+
+ private String tableName;
+ private @Nullable String newTableName;
+ private String dbType;
+ private String jdbcType;
+ private String itemType;
+ private Class<?> javaType;
+ private Date time;
+ private Object value;
+
+ public ItemVO(String tableName, @Nullable String newTableName) {
+ logger.debug("JDBC:ItemVO tableName={}; newTableName={}; ", tableName, newTableName);
+ this.tableName = tableName;
+ this.newTableName = newTableName;
+ }
+
+ public ItemVO() {
+ }
+
+ public void setValueTypes(String dbType, Class<?> javaType) {
+ logger.debug("JDBC:ItemVO setValueTypes dbType={}; javaType={};", dbType, javaType);
+ this.dbType = dbType;
+ this.javaType = javaType;
+ }
+
+ public String getTableName() {
+ return tableName;
+ }
+
+ public void setTableName(String tableName) {
+ this.tableName = tableName;
+ }
+
+ public @Nullable String getNewTableName() {
+ return newTableName;
+ }
+
+ public void setNewTableName(String newTableName) {
+ this.newTableName = newTableName;
+ }
+
+ public String getDbType() {
+ return dbType;
+ }
+
+ public void setDbType(String dbType) {
+ this.dbType = dbType;
+ }
+
+ public String getJdbcType() {
+ return jdbcType;
+ }
+
+ public void setJdbcType(String jdbcType) {
+ this.jdbcType = jdbcType;
+ }
+
+ public String getItemType() {
+ return itemType;
+ }
+
+ public void setItemType(String itemType) {
+ this.itemType = itemType;
+ }
+
+ public String getJavaType() {
+ return javaType.getName();
+ }
+
+ public void setJavaType(Class<?> javaType) {
+ this.javaType = javaType;
+ }
+
+ public Date getTime() {
+ return time;
+ }
+
+ public void setTime(Date time) {
+ this.time = time;
+ }
+
+ public Object getValue() {
+ return value;
+ }
+
+ public void setValue(Object value) {
+ this.value = value;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ ItemVO other = (ItemVO) obj;
+ if (value == null) {
+ if (other.value != null) {
+ return false;
+ }
+ } else if (!value.equals(other.value)) {
+ return false;
+ }
+ return Objects.equals(time, other.time);
+ }
+
+ @Override
+ public String toString() {
+ return new StringBuilder("ItemVO [tableName=").append(tableName).append(", newTableName=").append(newTableName)
+ .append(", dbType=").append(dbType).append(", javaType=").append(javaType).append(", time=")
+ .append(time).append(", value=").append(value).append("]").toString();
+ }
+}
--- /dev/null
+/**
+ * Copyright (c) 2010-2022 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.persistence.jdbc.internal.dto;
+
+import java.io.Serializable;
+import java.util.Objects;
+
+/**
+ * Represents the table naming data.
+ *
+ * @author Helmut Lehmeyer - Initial contribution
+ */
+public class ItemsVO implements Serializable {
+
+ private static final long serialVersionUID = 2871961811177601520L;
+
+ private static final String STR_FILTER = "[^a-zA-Z0-9]";
+
+ private String coltype = "VARCHAR(500)";
+ private String colname = "itemname";
+ private String itemsManageTable = "items";
+ private int itemId;
+ private String itemName;
+ private String tableName;
+ private String jdbcUriDatabaseName;
+
+ public String getColtype() {
+ return coltype;
+ }
+
+ public void setColtype(String coltype) {
+ this.coltype = coltype.replaceAll(STR_FILTER, "");
+ }
+
+ public String getColname() {
+ return colname;
+ }
+
+ public void setColname(String colname) {
+ this.colname = colname.replaceAll(STR_FILTER, "");
+ }
+
+ public String getItemsManageTable() {
+ return itemsManageTable;
+ }
+
+ public void setItemsManageTable(String itemsManageTable) {
+ this.itemsManageTable = itemsManageTable.replaceAll(STR_FILTER, "");
+ }
+
+ public int getItemId() {
+ return itemId;
+ }
+
+ public void setItemId(int itemId) {
+ this.itemId = itemId;
+ }
+
+ public String getItemName() {
+ return itemName;
+ }
+
+ public void setItemName(String itemName) {
+ this.itemName = itemName;
+ }
+
+ public String getTableName() {
+ return tableName;
+ }
+
+ public void setTableName(String tableName) {
+ this.tableName = tableName;
+ }
+
+ public String getJdbcUriDatabaseName() {
+ return jdbcUriDatabaseName;
+ }
+
+ public void setJdbcUriDatabaseName(String jdbcUriDatabaseName) {
+ this.jdbcUriDatabaseName = jdbcUriDatabaseName;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return Objects.hash(itemName, itemId);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ ItemsVO other = (ItemsVO) obj;
+ if (itemName == null) {
+ if (other.itemName != null) {
+ return false;
+ }
+ } else if (!itemName.equals(other.itemName)) {
+ return false;
+ }
+ return itemId == other.itemId;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("ItemsVO [coltype=");
+ builder.append(coltype);
+ builder.append(", colname=");
+ builder.append(colname);
+ builder.append(", itemsManageTable=");
+ builder.append(itemsManageTable);
+ builder.append(", itemid=");
+ builder.append(itemId);
+ builder.append(", itemname=");
+ builder.append(itemName);
+ builder.append(", table_name=");
+ builder.append(tableName);
+ builder.append("]");
+ return builder.toString();
+ }
+}
--- /dev/null
+/**
+ * Copyright (c) 2010-2022 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.persistence.jdbc.internal.dto;
+
+import java.time.ZonedDateTime;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.core.persistence.HistoricItem;
+import org.openhab.core.types.State;
+
+/**
+ * Represents the data on the part of openHAB.
+ *
+ * @author Helmut Lehmeyer - Initial contribution
+ */
+@NonNullByDefault
+public class JdbcHistoricItem implements HistoricItem {
+
+ private final String name;
+ private final State state;
+ private final ZonedDateTime timestamp;
+
+ public JdbcHistoricItem(String name, State state, ZonedDateTime timestamp) {
+ this.name = name;
+ this.state = state;
+ this.timestamp = timestamp;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public State getState() {
+ return state;
+ }
+
+ @Override
+ public ZonedDateTime getTimestamp() {
+ return timestamp;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("JdbcItem [name=");
+ builder.append(name);
+ builder.append(", state=");
+ builder.append(state);
+ builder.append(", timestamp=");
+ builder.append(timestamp);
+ builder.append("]");
+ return builder.toString();
+ }
+}
--- /dev/null
+/**
+ * Copyright (c) 2010-2022 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.persistence.jdbc.internal.dto;
+
+import java.util.Date;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.core.persistence.PersistenceItemInfo;
+
+/**
+ * Represents the item info for openHAB.
+ *
+ * @author Christoph Weitkamp - Initial contribution
+ */
+@NonNullByDefault
+public class JdbcPersistenceItemInfo implements PersistenceItemInfo {
+
+ private final String name;
+ private final @Nullable Integer count;
+ private final @Nullable Date earliest;
+ private final @Nullable Date latest;
+
+ public JdbcPersistenceItemInfo(String name) {
+ this(name, null, null, null);
+ }
+
+ public JdbcPersistenceItemInfo(String name, @Nullable Integer count, @Nullable Date earliest,
+ @Nullable Date latest) {
+ this.name = name;
+ this.count = count;
+ this.earliest = earliest;
+ this.latest = latest;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public @Nullable Integer getCount() {
+ return count;
+ }
+
+ @Override
+ public @Nullable Date getEarliest() {
+ return earliest;
+ }
+
+ @Override
+ public @Nullable Date getLatest() {
+ return latest;
+ }
+}
--- /dev/null
+/**
+ * Copyright (c) 2010-2022 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.persistence.jdbc.internal.exceptions;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * Base class for JDBC exceptions.
+ *
+ * @author Jacob Laursen - Initial contribution
+ */
+@NonNullByDefault
+public class JdbcException extends Exception {
+
+ private static final long serialVersionUID = 1911437557128995424L;
+
+ public JdbcException(String message) {
+ super(message);
+ }
+
+ public JdbcException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
--- /dev/null
+/**
+ * Copyright (c) 2010-2022 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.persistence.jdbc.internal.exceptions;
+
+import java.util.Objects;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.knowm.yank.exceptions.YankSQLException;
+
+/**
+ * This exception wraps a {@link YankSQLException}.
+ *
+ * @author Jacob Laursen - Initial contribution
+ */
+@NonNullByDefault
+public class JdbcSQLException extends JdbcException {
+
+ private static final long serialVersionUID = 4562191548585905000L;
+
+ public JdbcSQLException(YankSQLException sqlException) {
+ super(Objects.requireNonNull(sqlException.getMessage()));
+ }
+}
--- /dev/null
+/**
+ * Copyright (c) 2010-2022 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.persistence.jdbc.internal.utils;
+
+import java.sql.DatabaseMetaData;
+import java.sql.SQLException;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.knowm.yank.Yank;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.zaxxer.hikari.HikariDataSource;
+
+/**
+ * Meta data class
+ *
+ * @author Helmut Lehmeyer - Initial contribution
+ */
+@NonNullByDefault
+public class DbMetaData {
+
+ private final Logger logger = LoggerFactory.getLogger(DbMetaData.class);
+
+ private int dbMajorVersion;
+ private int dbMinorVersion;
+ private int driverMajorVersion;
+ private int driverMinorVersion;
+ private @Nullable String dbProductName;
+ private @Nullable String dbProductVersion;
+
+ public DbMetaData() {
+ HikariDataSource h = Yank.getDefaultConnectionPool();
+
+ DatabaseMetaData meta;
+ try {
+ meta = h.getConnection().getMetaData();
+
+ // Oracle (and some other vendors) do not support
+ // some the following methods; therefore, we need
+ // to use try-catch block.
+ try {
+ dbMajorVersion = meta.getDatabaseMajorVersion();
+ logger.debug("dbMajorVersion = '{}'", dbMajorVersion);
+ } catch (Exception e) {
+ logger.error("Asking for 'dbMajorVersion' is unsupported: '{}'", e.getMessage());
+ }
+
+ try {
+ dbMinorVersion = meta.getDatabaseMinorVersion();
+ logger.debug("dbMinorVersion = '{}'", dbMinorVersion);
+ } catch (Exception e) {
+ logger.error("Asking for 'dbMinorVersion' is unsupported: '{}'", e.getMessage());
+ }
+
+ driverMajorVersion = meta.getDriverMajorVersion();
+ logger.debug("driverMajorVersion = '{}'", driverMajorVersion);
+
+ driverMinorVersion = meta.getDriverMinorVersion();
+ logger.debug("driverMinorVersion = '{}'", driverMinorVersion);
+
+ dbProductName = meta.getDatabaseProductName();
+ logger.debug("dbProductName = '{}'", dbProductName);
+
+ dbProductVersion = meta.getDatabaseProductVersion();
+ logger.debug("dbProductVersion = '{}'", dbProductVersion);
+ } catch (SQLException e1) {
+ logger.error("Asking for 'dbMajorVersion' seems to be unsupported: '{}'", e1.getMessage());
+ }
+ }
+
+ public int getDbMajorVersion() {
+ return dbMajorVersion;
+ }
+
+ public int getDbMinorVersion() {
+ return dbMinorVersion;
+ }
+
+ public boolean isDbVersionGreater(int major, int minor) {
+ if (dbMajorVersion > major) {
+ return true;
+ } else if (dbMajorVersion == major) {
+ if (dbMinorVersion > minor) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public int getDriverMajorVersion() {
+ return driverMajorVersion;
+ }
+
+ public int getDriverMinorVersion() {
+ return driverMinorVersion;
+ }
+
+ public boolean isDriverVersionGreater(int major, int minor) {
+ if (major > driverMajorVersion) {
+ return true;
+ } else if (major == driverMajorVersion) {
+ if (minor > driverMinorVersion) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public @Nullable String getDbProductName() {
+ return dbProductName;
+ }
+
+ public @Nullable String getDbProductVersion() {
+ return dbProductVersion;
+ }
+}
--- /dev/null
+/**
+ * Copyright (c) 2010-2022 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.persistence.jdbc.internal.utils;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.util.LinkedList;
+import java.util.Queue;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * Calculates the average/mean of a number series.
+ *
+ * @author Helmut Lehmeyer - Initial contribution
+ */
+@NonNullByDefault
+public class MovingAverage {
+
+ private final Queue<BigDecimal> win = new LinkedList<>();
+ private final int period;
+ private BigDecimal sum = BigDecimal.ZERO;
+
+ public MovingAverage(int period) {
+ assert period > 0 : "Period must be a positive integer";
+ this.period = period;
+ }
+
+ public void add(Double num) {
+ add(new BigDecimal(num));
+ }
+
+ public void add(Long num) {
+ add(new BigDecimal(num));
+ }
+
+ public void add(Integer num) {
+ add(new BigDecimal(num));
+ }
+
+ public void add(BigDecimal num) {
+ sum = sum.add(num);
+ win.add(num);
+ if (win.size() > period) {
+ sum = sum.subtract(win.remove());
+ }
+ }
+
+ public BigDecimal getAverage() {
+ if (win.isEmpty()) {
+ return BigDecimal.ZERO; // technically the average is undefined
+ }
+ BigDecimal divisor = BigDecimal.valueOf(win.size());
+ return sum.divide(divisor, 2, RoundingMode.HALF_UP);
+ }
+
+ public double getAverageDouble() {
+ return getAverage().doubleValue();
+ }
+
+ public int getAverageInteger() {
+ return getAverage().intValue();
+ }
+}
--- /dev/null
+/**
+ * Copyright (c) 2010-2022 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.persistence.jdbc.internal.utils;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.core.persistence.FilterCriteria;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Utility class
+ *
+ * @author Helmut Lehmeyer - Initial contribution
+ */
+@NonNullByDefault
+public class StringUtilsExt {
+ private static final Logger LOGGER = LoggerFactory.getLogger(StringUtilsExt.class);
+
+ /**
+ * Replaces multiple found words with the given Array contents
+ *
+ * @param str String for replacement
+ * @param separate A String or Array to be replaced
+ * @param separators Array will be merged to str
+ * @return
+ */
+ public static final String replaceArrayMerge(String str, String separate, Object[] separators) {
+ String s = str;
+ for (int i = 0; i < separators.length; i++) {
+ s = s.replaceFirst(separate, (String) separators[i]);
+ }
+ return s;
+ }
+
+ /**
+ * @see #replaceArrayMerge(String str, String separate, Object[] separators)
+ */
+ public static final String replaceArrayMerge(String str, String[] separate, String[] separators) {
+ String s = str;
+ for (int i = 0; i < separators.length; i++) {
+ s = s.replaceFirst(separate[i], separators[i]);
+ }
+ return s;
+ }
+
+ /**
+ * @see #parseJdbcURL(String url, Properties def)
+ */
+ public static Properties parseJdbcURL(String url) {
+ return parseJdbcURL(url, null);
+ }
+
+ /**
+ * <b>JDBC-URI Examples:</b><br/>
+ * jdbc:dbShortcut:c:/dev/databaseName<br/>
+ * jdbc:dbShortcut:scheme:c:/dev/databaseName<br/>
+ * jdbc:dbShortcut:scheme:c:\\dev\\databaseName<br/>
+ * jdbc:dbShortcut:./databaseName<br/>
+ * jdbc:dbShortcut:/databaseName<br/>
+ * jdbc:dbShortcut:~/databaseName<br/>
+ * jdbc:dbShortcut:/path/databaseName.db<br/>
+ * jdbc:dbShortcut:./../../path/databaseName<br/>
+ * jdbc:dbShortcut:scheme:./path/../path/databaseName;param1=true;<br/>
+ * jdbc:dbShortcut://192.168.0.145:3306/databaseName?param1=false¶m2=true
+ * <p/>
+ *
+ * @param url JDBC-URI
+ * @param def Predefined Properties Object
+ * @return A merged Properties Object may contain:<br/>
+ * parseValid (mandatory)<br/>
+ * scheme<br/>
+ * serverPath<br/>
+ * dbShortcut<br/>
+ * databaseName<br/>
+ * portNumber<br/>
+ * serverName<br/>
+ * pathQuery<br/>
+ */
+ public static Properties parseJdbcURL(String url, @Nullable Properties def) {
+ Properties props;
+ if (def == null) {
+ props = new Properties();
+ } else {
+ props = new Properties(def);
+ }
+
+ if (url.length() < 9) {
+ return props;
+ }
+
+ // replace all \
+ if (url.contains("\\")) {
+ url = url.replaceAll("\\\\", "/");
+ }
+
+ // replace first ; with ?
+ if (url.contains(";")) {
+ // replace first ; with ?
+ url = url.replaceFirst(";", "?");
+ // replace other ; with &
+ url = url.replaceAll(";", "&");
+ }
+
+ if (url.split(":").length < 3 || url.indexOf("/") == -1) {
+ LOGGER.error("parseJdbcURL: URI '{}' is not well formated, expected uri like 'jdbc:dbShortcut:/path'", url);
+ props.put("parseValid", "false");
+ return props;
+ }
+
+ String[] protAndDb = stringBeforeSubstr(url, ":", 1).split(":");
+ if (!"jdbc".equals(protAndDb[0])) {
+ LOGGER.error("parseJdbcURL: URI '{}' is not well formated, expected suffix 'jdbc' found '{}'", url,
+ protAndDb[0]);
+ props.put("parseValid", "false");
+ return props;
+ }
+ props.put("parseValid", "true");
+ props.put("dbShortcut", protAndDb[1]);
+
+ URI dbURI = null;
+ try {
+ dbURI = new URI(stringAfterSubstr(url, ":", 1).replaceFirst(" ", ""));
+ if (dbURI.getScheme() != null) {
+ props.put("scheme", dbURI.getScheme());
+ dbURI = new URI(stringAfterSubstr(url, ":", 2).replaceFirst(" ", ""));
+ }
+ } catch (URISyntaxException e) {
+ LOGGER.error("parseJdbcURL: URI '{}' is not well formated.", url, e);
+ return props;
+ }
+
+ // Query-Parameters
+ if (dbURI.getQuery() != null) {
+ String[] q = dbURI.getQuery().split("&");
+ for (int i = 0; i < q.length; i++) {
+ String[] t = q[i].split("=");
+ props.put(t[0], t[1]);
+ }
+ props.put("pathQuery", dbURI.getQuery());
+ }
+
+ String path = "";
+ if (dbURI.getPath() != null) {
+ String gp = dbURI.getPath();
+ String st = "/";
+ if (gp.indexOf("/") <= 1) {
+ if (substrPos(gp, st).size() > 1) {
+ path = stringBeforeLastSubstr(gp, st) + st;
+ } else {
+ path = stringBeforeSubstr(gp, st) + st;
+ }
+ }
+ if (dbURI.getScheme() != null && dbURI.getScheme().length() == 1) {
+ path = dbURI.getScheme() + ":" + path;
+ }
+ props.put("serverPath", path);
+ }
+ if (dbURI.getPath() != null) {
+ props.put("databaseName", stringAfterLastSubstr(dbURI.getPath(), "/"));
+ }
+ if (dbURI.getPort() != -1) {
+ props.put("portNumber", dbURI.getPort() + "");
+ }
+ if (dbURI.getHost() != null) {
+ props.put("serverName", dbURI.getHost());
+ }
+
+ return props;
+ }
+
+ /**
+ * Returns a String before the last occurrence of a substring
+ */
+ public static String stringBeforeLastSubstr(String s, String substr) {
+ List<Integer> a = substrPos(s, substr);
+ return s.substring(0, a.get(a.size() - 1));
+ }
+
+ /**
+ * Returns a String after the last occurrence of a substring
+ */
+ public static String stringAfterLastSubstr(String s, String substr) {
+ List<Integer> a = substrPos(s, substr);
+ return s.substring(a.get(a.size() - 1) + 1);
+ }
+
+ /**
+ * Returns a String after the first occurrence of a substring
+ */
+ public static String stringAfterSubstr(String s, String substr) {
+ return s.substring(s.indexOf(substr) + 1);
+ }
+
+ /**
+ * Returns a String after the n occurrence of a substring
+ */
+ public static String stringAfterSubstr(String s, String substr, int n) {
+ return s.substring(substrPos(s, substr).get(n) + 1);
+ }
+
+ /**
+ * Returns a String before the first occurrence of a substring
+ */
+ public static String stringBeforeSubstr(String s, String substr) {
+ return s.substring(0, s.indexOf(substr));
+ }
+
+ /**
+ * Returns a String before the n occurrence of a substring.
+ */
+ public static String stringBeforeSubstr(String s, String substr, int n) {
+ return s.substring(0, substrPos(s, substr).get(n));
+ }
+
+ /**
+ * Returns a list with indices of the occurrence of a substring.
+ */
+ public static List<Integer> substrPos(String s, String substr) {
+ return substrPos(s, substr, true);
+ }
+
+ /**
+ * Returns a list with indices of the occurrence of a substring.
+ */
+ public static List<Integer> substrPos(String s, String substr, boolean ignoreCase) {
+ int substrLength = substr.length();
+ int strLength = s.length();
+ List<Integer> arr = new ArrayList<>();
+
+ for (int i = 0; i < strLength - substrLength + 1; i++) {
+ if (s.regionMatches(ignoreCase, i, substr, 0, substrLength)) {
+ arr.add(i);
+ }
+ }
+ return arr;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#toString()
+ */
+ public static String filterToString(FilterCriteria filter) {
+ StringBuilder builder = new StringBuilder();
+ builder.append("FilterCriteria [itemName=");
+ builder.append(filter.getItemName());
+ builder.append(", beginDate=");
+ builder.append(filter.getBeginDate());
+ builder.append(", endDate=");
+ builder.append(filter.getEndDate());
+ builder.append(", pageNumber=");
+ builder.append(filter.getPageNumber());
+ builder.append(", pageSize=");
+ builder.append(filter.getPageSize());
+ builder.append(", operator=");
+ builder.append(filter.getOperator());
+ builder.append(", ordering=");
+ builder.append(filter.getOrdering());
+ builder.append(", state=");
+ builder.append(filter.getState());
+ builder.append("]");
+ return builder.toString();
+ }
+}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.persistence.jdbc.utils;
-
-import java.sql.DatabaseMetaData;
-import java.sql.SQLException;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.knowm.yank.Yank;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.zaxxer.hikari.HikariDataSource;
-
-/**
- * Meta data class
- *
- * @author Helmut Lehmeyer - Initial contribution
- */
-@NonNullByDefault
-public class DbMetaData {
-
- private final Logger logger = LoggerFactory.getLogger(DbMetaData.class);
-
- private int dbMajorVersion;
- private int dbMinorVersion;
- private int driverMajorVersion;
- private int driverMinorVersion;
- private @Nullable String dbProductName;
- private @Nullable String dbProductVersion;
-
- public DbMetaData() {
- HikariDataSource h = Yank.getDefaultConnectionPool();
- // HikariDataSource h = Yank.getDataSource();
-
- DatabaseMetaData meta;
- try {
- meta = h.getConnection().getMetaData();
-
- // Oracle (and some other vendors) do not support
- // some the following methods; therefore, we need
- // to use try-catch block.
- try {
- dbMajorVersion = meta.getDatabaseMajorVersion();
- logger.debug("dbMajorVersion = '{}'", dbMajorVersion);
- } catch (Exception e) {
- logger.error("Asking for 'dbMajorVersion' is unsupported: '{}'", e.getMessage());
- }
-
- try {
- dbMinorVersion = meta.getDatabaseMinorVersion();
- logger.debug("dbMinorVersion = '{}'", dbMinorVersion);
- } catch (Exception e) {
- logger.error("Asking for 'dbMinorVersion' is unsupported: '{}'", e.getMessage());
- }
-
- driverMajorVersion = meta.getDriverMajorVersion();
- logger.debug("driverMajorVersion = '{}'", driverMajorVersion);
-
- driverMinorVersion = meta.getDriverMinorVersion();
- logger.debug("driverMinorVersion = '{}'", driverMinorVersion);
-
- dbProductName = meta.getDatabaseProductName();
- logger.debug("dbProductName = '{}'", dbProductName);
-
- dbProductVersion = meta.getDatabaseProductVersion();
- logger.debug("dbProductVersion = '{}'", dbProductVersion);
- } catch (SQLException e1) {
- logger.error("Asking for 'dbMajorVersion' seems to be unsupported: '{}'", e1.getMessage());
- }
- }
-
- public int getDbMajorVersion() {
- return dbMajorVersion;
- }
-
- public int getDbMinorVersion() {
- return dbMinorVersion;
- }
-
- public boolean isDbVersionGreater(int major, int minor) {
- if (dbMajorVersion > major) {
- return true;
- } else if (dbMajorVersion == major) {
- if (dbMinorVersion > minor) {
- return true;
- }
- }
- return false;
- }
-
- public int getDriverMajorVersion() {
- return driverMajorVersion;
- }
-
- public int getDriverMinorVersion() {
- return driverMinorVersion;
- }
-
- public boolean isDriverVersionGreater(int major, int minor) {
- if (major > driverMajorVersion) {
- return true;
- } else if (major == driverMajorVersion) {
- if (minor > driverMinorVersion) {
- return true;
- }
- }
- return false;
- }
-
- public @Nullable String getDbProductName() {
- return dbProductName;
- }
-
- public @Nullable String getDbProductVersion() {
- return dbProductVersion;
- }
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.persistence.jdbc.utils;
-
-import java.math.BigDecimal;
-import java.math.RoundingMode;
-import java.util.LinkedList;
-import java.util.Queue;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-
-/**
- * Calculates the average/mean of a number series.
- *
- * @author Helmut Lehmeyer - Initial contribution
- */
-@NonNullByDefault
-public class MovingAverage {
-
- private final Queue<BigDecimal> win = new LinkedList<>();
- private final int period;
- private BigDecimal sum = BigDecimal.ZERO;
-
- public MovingAverage(int period) {
- assert period > 0 : "Period must be a positive integer";
- this.period = period;
- }
-
- public void add(Double num) {
- add(new BigDecimal(num));
- }
-
- public void add(Long num) {
- add(new BigDecimal(num));
- }
-
- public void add(Integer num) {
- add(new BigDecimal(num));
- }
-
- public void add(BigDecimal num) {
- sum = sum.add(num);
- win.add(num);
- if (win.size() > period) {
- sum = sum.subtract(win.remove());
- }
- }
-
- public BigDecimal getAverage() {
- if (win.isEmpty()) {
- return BigDecimal.ZERO; // technically the average is undefined
- }
- BigDecimal divisor = BigDecimal.valueOf(win.size());
- return sum.divide(divisor, 2, RoundingMode.HALF_UP);
- }
-
- public double getAverageDouble() {
- return getAverage().doubleValue();
- }
-
- public int getAverageInteger() {
- return getAverage().intValue();
- }
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.persistence.jdbc.utils;
-
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Properties;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.core.persistence.FilterCriteria;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Utility class
- *
- * @author Helmut Lehmeyer - Initial contribution
- */
-@NonNullByDefault
-public class StringUtilsExt {
- private static final Logger LOGGER = LoggerFactory.getLogger(StringUtilsExt.class);
-
- /**
- * Replaces multiple found words with the given Array contents
- *
- * @param str String for replacement
- * @param separate A String or Array to be replaced
- * @param separators Array will be merged to str
- * @return
- */
- public static final String replaceArrayMerge(String str, String separate, Object[] separators) {
- String s = str;
- for (int i = 0; i < separators.length; i++) {
- s = s.replaceFirst(separate, (String) separators[i]);
- }
- return s;
- }
-
- /**
- * @see #replaceArrayMerge(String str, String separate, Object[] separators)
- */
- public static final String replaceArrayMerge(String str, String[] separate, String[] separators) {
- String s = str;
- for (int i = 0; i < separators.length; i++) {
- s = s.replaceFirst(separate[i], separators[i]);
- }
- return s;
- }
-
- /**
- * @see #parseJdbcURL(String url, Properties def)
- */
- public static Properties parseJdbcURL(String url) {
- return parseJdbcURL(url, null);
- }
-
- /**
- * <b>JDBC-URI Examples:</b><br/>
- * jdbc:dbShortcut:c:/dev/databaseName<br/>
- * jdbc:dbShortcut:scheme:c:/dev/databaseName<br/>
- * jdbc:dbShortcut:scheme:c:\\dev\\databaseName<br/>
- * jdbc:dbShortcut:./databaseName<br/>
- * jdbc:dbShortcut:/databaseName<br/>
- * jdbc:dbShortcut:~/databaseName<br/>
- * jdbc:dbShortcut:/path/databaseName.db<br/>
- * jdbc:dbShortcut:./../../path/databaseName<br/>
- * jdbc:dbShortcut:scheme:./path/../path/databaseName;param1=true;<br/>
- * jdbc:dbShortcut://192.168.0.145:3306/databaseName?param1=false¶m2=true
- * <p/>
- *
- * @param url JDBC-URI
- * @param def Predefined Properties Object
- * @return A merged Properties Object may contain:<br/>
- * parseValid (mandatory)<br/>
- * scheme<br/>
- * serverPath<br/>
- * dbShortcut<br/>
- * databaseName<br/>
- * portNumber<br/>
- * serverName<br/>
- * pathQuery<br/>
- */
- public static Properties parseJdbcURL(String url, @Nullable Properties def) {
- Properties props;
- if (def == null) {
- props = new Properties();
- } else {
- props = new Properties(def);
- }
-
- if (url.length() < 9) {
- return props;
- }
-
- // replace all \
- if (url.contains("\\")) {
- url = url.replaceAll("\\\\", "/");
- }
-
- // replace first ; with ?
- if (url.contains(";")) {
- // replace first ; with ?
- url = url.replaceFirst(";", "?");
- // replace other ; with &
- url = url.replaceAll(";", "&");
- }
-
- if (url.split(":").length < 3 || url.indexOf("/") == -1) {
- LOGGER.error("parseJdbcURL: URI '{}' is not well formated, expected uri like 'jdbc:dbShortcut:/path'", url);
- props.put("parseValid", "false");
- return props;
- }
-
- String[] protAndDb = stringBeforeSubstr(url, ":", 1).split(":");
- if (!"jdbc".equals(protAndDb[0])) {
- LOGGER.error("parseJdbcURL: URI '{}' is not well formated, expected suffix 'jdbc' found '{}'", url,
- protAndDb[0]);
- props.put("parseValid", "false");
- return props;
- }
- props.put("parseValid", "true");
- props.put("dbShortcut", protAndDb[1]);
-
- URI dbURI = null;
- try {
- dbURI = new URI(stringAfterSubstr(url, ":", 1).replaceFirst(" ", ""));
- if (dbURI.getScheme() != null) {
- props.put("scheme", dbURI.getScheme());
- dbURI = new URI(stringAfterSubstr(url, ":", 2).replaceFirst(" ", ""));
- }
- } catch (URISyntaxException e) {
- LOGGER.error("parseJdbcURL: URI '{}' is not well formated.", url, e);
- return props;
- }
-
- // Query-Parameters
- if (dbURI.getQuery() != null) {
- String[] q = dbURI.getQuery().split("&");
- for (int i = 0; i < q.length; i++) {
- String[] t = q[i].split("=");
- props.put(t[0], t[1]);
- }
- props.put("pathQuery", dbURI.getQuery());
- }
-
- String path = "";
- if (dbURI.getPath() != null) {
- String gp = dbURI.getPath();
- String st = "/";
- if (gp.indexOf("/") <= 1) {
- if (substrPos(gp, st).size() > 1) {
- path = stringBeforeLastSubstr(gp, st) + st;
- } else {
- path = stringBeforeSubstr(gp, st) + st;
- }
- }
- if (dbURI.getScheme() != null && dbURI.getScheme().length() == 1) {
- path = dbURI.getScheme() + ":" + path;
- }
- props.put("serverPath", path);
- }
- if (dbURI.getPath() != null) {
- props.put("databaseName", stringAfterLastSubstr(dbURI.getPath(), "/"));
- }
- if (dbURI.getPort() != -1) {
- props.put("portNumber", dbURI.getPort() + "");
- }
- if (dbURI.getHost() != null) {
- props.put("serverName", dbURI.getHost());
- }
-
- return props;
- }
-
- /**
- * Returns a String before the last occurrence of a substring
- */
- public static String stringBeforeLastSubstr(String s, String substr) {
- List<Integer> a = substrPos(s, substr);
- return s.substring(0, a.get(a.size() - 1));
- }
-
- /**
- * Returns a String after the last occurrence of a substring
- */
- public static String stringAfterLastSubstr(String s, String substr) {
- List<Integer> a = substrPos(s, substr);
- return s.substring(a.get(a.size() - 1) + 1);
- }
-
- /**
- * Returns a String after the first occurrence of a substring
- */
- public static String stringAfterSubstr(String s, String substr) {
- return s.substring(s.indexOf(substr) + 1);
- }
-
- /**
- * Returns a String after the n occurrence of a substring
- */
- public static String stringAfterSubstr(String s, String substr, int n) {
- return s.substring(substrPos(s, substr).get(n) + 1);
- }
-
- /**
- * Returns a String before the first occurrence of a substring
- */
- public static String stringBeforeSubstr(String s, String substr) {
- return s.substring(0, s.indexOf(substr));
- }
-
- /**
- * Returns a String before the n occurrence of a substring.
- */
- public static String stringBeforeSubstr(String s, String substr, int n) {
- return s.substring(0, substrPos(s, substr).get(n));
- }
-
- /**
- * Returns a list with indices of the occurrence of a substring.
- */
- public static List<Integer> substrPos(String s, String substr) {
- return substrPos(s, substr, true);
- }
-
- /**
- * Returns a list with indices of the occurrence of a substring.
- */
- public static List<Integer> substrPos(String s, String substr, boolean ignoreCase) {
- int substrLength = substr.length();
- int strLength = s.length();
- List<Integer> arr = new ArrayList<>();
-
- for (int i = 0; i < strLength - substrLength + 1; i++) {
- if (s.regionMatches(ignoreCase, i, substr, 0, substrLength)) {
- arr.add(i);
- }
- }
- return arr;
- }
-
- /*
- * (non-Javadoc)
- *
- * @see java.lang.Object#toString()
- */
- public static String filterToString(FilterCriteria filter) {
- StringBuilder builder = new StringBuilder();
- builder.append("FilterCriteria [itemName=");
- builder.append(filter.getItemName());
- builder.append(", beginDate=");
- builder.append(filter.getBeginDate());
- builder.append(", endDate=");
- builder.append(filter.getEndDate());
- builder.append(", pageNumber=");
- builder.append(filter.getPageNumber());
- builder.append(", pageSize=");
- builder.append(filter.getPageSize());
- builder.append(", operator=");
- builder.append(filter.getOperator());
- builder.append(", ordering=");
- builder.append(filter.getOrdering());
- builder.append(", state=");
- builder.append(filter.getState());
- builder.append("]");
- return builder.toString();
- }
-}
+++ /dev/null
-/**
- * Copyright (c) 2010-2022 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.persistence.jdbc.db;
-
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertInstanceOf;
-
-import java.time.Instant;
-import java.time.LocalDateTime;
-import java.time.ZoneId;
-import java.time.ZonedDateTime;
-import java.time.format.DateTimeFormatter;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import org.openhab.core.library.items.CallItem;
-import org.openhab.core.library.items.ColorItem;
-import org.openhab.core.library.items.ContactItem;
-import org.openhab.core.library.items.DateTimeItem;
-import org.openhab.core.library.items.DimmerItem;
-import org.openhab.core.library.items.ImageItem;
-import org.openhab.core.library.items.LocationItem;
-import org.openhab.core.library.items.NumberItem;
-import org.openhab.core.library.items.PlayerItem;
-import org.openhab.core.library.items.RollershutterItem;
-import org.openhab.core.library.items.StringItem;
-import org.openhab.core.library.items.SwitchItem;
-import org.openhab.core.library.types.DateTimeType;
-import org.openhab.core.library.types.DecimalType;
-import org.openhab.core.library.types.HSBType;
-import org.openhab.core.library.types.OnOffType;
-import org.openhab.core.library.types.OpenClosedType;
-import org.openhab.core.library.types.PercentType;
-import org.openhab.core.library.types.PlayPauseType;
-import org.openhab.core.library.types.PointType;
-import org.openhab.core.library.types.QuantityType;
-import org.openhab.core.library.types.RawType;
-import org.openhab.core.library.types.RewindFastforwardType;
-import org.openhab.core.library.types.StringListType;
-import org.openhab.core.library.types.StringType;
-import org.openhab.core.library.unit.SIUnits;
-import org.openhab.core.persistence.FilterCriteria;
-import org.openhab.core.persistence.FilterCriteria.Ordering;
-import org.openhab.core.types.State;
-
-/**
- * Tests the {@link JdbcBaseDAO}.
- *
- * @author Christoph Weitkamp - Initial contribution
- */
-@NonNullByDefault
-public class JdbcBaseDAOTest {
-
- private static final String DATE_PATTERN = "yyyy-MM-dd'T'HH:mm:ss";
- private static final DateTimeFormatter DATE_PARSER = DateTimeFormatter.ofPattern(DATE_PATTERN);
- private static final ZoneId UTC_ZONE_ID = ZoneId.of("UTC");
- private static final String DB_TABLE_NAME = "testitem";
-
- private final JdbcBaseDAO jdbcBaseDAO = new JdbcBaseDAO();
- private @NonNullByDefault({}) FilterCriteria filter;
-
- @BeforeEach
- public void setup() {
- filter = new FilterCriteria();
- }
-
- @Test
- public void testObjectAsStateReturnsValidState() {
- State decimalType = jdbcBaseDAO.objectAsState(new NumberItem("testNumberItem"), null, 7.3);
- assertInstanceOf(DecimalType.class, decimalType);
- assertEquals(DecimalType.valueOf("7.3"), decimalType);
- State quantityType = jdbcBaseDAO.objectAsState(new NumberItem("testNumberItem"), SIUnits.CELSIUS, 7.3);
- assertInstanceOf(QuantityType.class, quantityType);
- assertEquals(QuantityType.valueOf("7.3 °C"), quantityType);
-
- State hsbType = jdbcBaseDAO.objectAsState(new ColorItem("testColorItem"), null, "184,100,52");
- assertInstanceOf(HSBType.class, hsbType);
- assertEquals(HSBType.valueOf("184,100,52"), hsbType);
-
- State percentType = jdbcBaseDAO.objectAsState(new DimmerItem("testDimmerItem"), null, 52);
- assertInstanceOf(PercentType.class, percentType);
- assertEquals(PercentType.valueOf("52"), percentType);
-
- percentType = jdbcBaseDAO.objectAsState(new RollershutterItem("testRollershutterItem"), null, 39);
- assertInstanceOf(PercentType.class, percentType);
- assertEquals(PercentType.valueOf("39"), percentType);
-
- State openClosedType = jdbcBaseDAO.objectAsState(new ContactItem("testContactItem"), null, "OPEN");
- assertInstanceOf(OpenClosedType.class, openClosedType);
- assertThat(openClosedType, is(OpenClosedType.OPEN));
-
- State playPauseType = jdbcBaseDAO.objectAsState(new PlayerItem("testPlayerItem"), null, "PLAY");
- assertInstanceOf(PlayPauseType.class, playPauseType);
- assertThat(playPauseType, is(PlayPauseType.PLAY));
- State rewindFastforwardType = jdbcBaseDAO.objectAsState(new PlayerItem("testPlayerItem"), null, "REWIND");
- assertInstanceOf(RewindFastforwardType.class, rewindFastforwardType);
- assertThat(rewindFastforwardType, is(RewindFastforwardType.REWIND));
-
- State onOffType = jdbcBaseDAO.objectAsState(new SwitchItem("testSwitchItem"), null, "ON");
- assertInstanceOf(OnOffType.class, onOffType);
- assertThat(onOffType, is(OnOffType.ON));
-
- State stringListType = jdbcBaseDAO.objectAsState(new CallItem("testCallItem"), null, "0699222222,0179999998");
- assertInstanceOf(StringListType.class, stringListType);
- assertEquals(StringListType.valueOf("0699222222,0179999998"), stringListType);
-
- State expectedRawType = new RawType(new byte[0], "application/octet-stream");
- State rawType = jdbcBaseDAO.objectAsState(new ImageItem("testImageItem"), null, expectedRawType.toFullString());
- assertInstanceOf(RawType.class, rawType);
- assertThat(rawType, is(expectedRawType));
-
- State pointType = jdbcBaseDAO.objectAsState(new LocationItem("testLocationItem"), null, "1,2,3");
- assertInstanceOf(PointType.class, pointType);
- assertEquals(PointType.valueOf("1,2,3"), pointType);
-
- State stringType = jdbcBaseDAO.objectAsState(new StringItem("testStringItem"), null, "String");
- assertInstanceOf(StringType.class, stringType);
- assertEquals(StringType.valueOf("String"), stringType);
- }
-
- @Test
- public void objectAsStateReturnsValiDateTimeTypeForTimestamp() {
- State dateTimeType = jdbcBaseDAO.objectAsState(new DateTimeItem("testDateTimeItem"), null,
- java.sql.Timestamp.valueOf("2021-02-01 23:30:02.049"));
- assertInstanceOf(DateTimeType.class, dateTimeType);
- assertEquals(DateTimeType.valueOf("2021-02-01T23:30:02.049"), dateTimeType);
- }
-
- @Test
- public void objectAsStateReturnsValidDateTimeTypeForLocalDateTime() {
- State dateTimeType = jdbcBaseDAO.objectAsState(new DateTimeItem("testDateTimeItem"), null,
- LocalDateTime.parse("2021-02-01T23:30:02.049"));
- assertInstanceOf(DateTimeType.class, dateTimeType);
- assertEquals(DateTimeType.valueOf("2021-02-01T23:30:02.049"), dateTimeType);
- }
-
- @Test
- public void objectAsStateReturnsValidDateTimeTypeForLong() {
- State dateTimeType = jdbcBaseDAO.objectAsState(new DateTimeItem("testDateTimeItem"), null,
- Long.valueOf("1612222202049"));
- assertInstanceOf(DateTimeType.class, dateTimeType);
- assertEquals(
- new DateTimeType(
- ZonedDateTime.ofInstant(Instant.parse("2021-02-01T23:30:02.049Z"), ZoneId.systemDefault())),
- dateTimeType);
- }
-
- @Test
- public void objectAsStateReturnsValidDateTimeTypeForSqlDate() {
- State dateTimeType = jdbcBaseDAO.objectAsState(new DateTimeItem("testDateTimeItem"), null,
- java.sql.Date.valueOf("2021-02-01"));
- assertInstanceOf(DateTimeType.class, dateTimeType);
- assertEquals(DateTimeType.valueOf("2021-02-01T00:00:00.000"), dateTimeType);
- }
-
- @Test
- public void objectAsStateReturnsValidDateTimeTypeForInstant() {
- State dateTimeType = jdbcBaseDAO.objectAsState(new DateTimeItem("testDateTimeItem"), null,
- Instant.parse("2021-02-01T23:30:02.049Z"));
- assertInstanceOf(DateTimeType.class, dateTimeType);
- assertEquals(
- new DateTimeType(
- ZonedDateTime.ofInstant(Instant.parse("2021-02-01T23:30:02.049Z"), ZoneId.systemDefault())),
- dateTimeType);
- }
-
- @Test
- public void objectAsStateReturnsValidDateTimeTypeForString() {
- State dateTimeType = jdbcBaseDAO.objectAsState(new DateTimeItem("testDateTimeItem"), null,
- "2021-02-01 23:30:02.049");
- assertInstanceOf(DateTimeType.class, dateTimeType);
- assertEquals(DateTimeType.valueOf("2021-02-01T23:30:02.049"), dateTimeType);
- }
-
- @Test
- public void testHistItemFilterQueryProviderReturnsSelectQueryWithoutWhereClauseDescendingOrder() {
- String sql = jdbcBaseDAO.histItemFilterQueryProvider(filter, 0, DB_TABLE_NAME, "TEST", UTC_ZONE_ID);
- assertThat(sql, is("SELECT time, value FROM " + DB_TABLE_NAME + " ORDER BY time DESC"));
- }
-
- @Test
- public void testHistItemFilterQueryProviderReturnsSelectQueryWithoutWhereClauseAscendingOrder() {
- filter.setOrdering(Ordering.ASCENDING);
-
- String sql = jdbcBaseDAO.histItemFilterQueryProvider(filter, 0, DB_TABLE_NAME, "TEST", UTC_ZONE_ID);
- assertThat(sql, is("SELECT time, value FROM " + DB_TABLE_NAME + " ORDER BY time ASC"));
- }
-
- @Test
- public void testHistItemFilterQueryProviderWithStartAndEndDateReturnsDeleteQueryWithWhereClauseDescendingOrder() {
- filter.setBeginDate(parseDateTimeString("2022-01-10T15:01:44"));
- filter.setEndDate(parseDateTimeString("2022-01-15T15:01:44"));
-
- String sql = jdbcBaseDAO.histItemFilterQueryProvider(filter, 0, DB_TABLE_NAME, "TEST", UTC_ZONE_ID);
- assertThat(sql, is("SELECT time, value FROM " + DB_TABLE_NAME + " WHERE TIME>'" //
- + JdbcBaseDAO.JDBC_DATE_FORMAT.format(filter.getBeginDate()) + "'" //
- + " AND TIME<'" + JdbcBaseDAO.JDBC_DATE_FORMAT.format(filter.getEndDate()) + "' ORDER BY time DESC"));
- }
-
- @Test
- public void testHistItemFilterQueryProviderReturnsSelectQueryWithoutWhereClauseDescendingOrderAndLimit() {
- filter.setPageSize(1);
-
- String sql = jdbcBaseDAO.histItemFilterQueryProvider(filter, 0, DB_TABLE_NAME, "TEST", UTC_ZONE_ID);
- assertThat(sql, is("SELECT time, value FROM " + DB_TABLE_NAME + " ORDER BY time DESC LIMIT 0,1"));
- }
-
- @Test
- public void testHistItemFilterDeleteProviderReturnsDeleteQueryWithoutWhereClause() {
- String sql = jdbcBaseDAO.histItemFilterDeleteProvider(filter, DB_TABLE_NAME, UTC_ZONE_ID);
- assertThat(sql, is("TRUNCATE TABLE " + DB_TABLE_NAME));
- }
-
- @Test
- public void testHistItemFilterDeleteProviderWithStartAndEndDateReturnsDeleteQueryWithWhereClause() {
- filter.setBeginDate(parseDateTimeString("2022-01-10T15:01:44"));
- filter.setEndDate(parseDateTimeString("2022-01-15T15:01:44"));
-
- String sql = jdbcBaseDAO.histItemFilterDeleteProvider(filter, DB_TABLE_NAME, UTC_ZONE_ID);
- assertThat(sql, is("DELETE FROM " + DB_TABLE_NAME + " WHERE TIME>'" //
- + JdbcBaseDAO.JDBC_DATE_FORMAT.format(filter.getBeginDate()) + "'" //
- + " AND TIME<'" + JdbcBaseDAO.JDBC_DATE_FORMAT.format(filter.getEndDate()) + "'"));
- }
-
- @Test
- public void testResolveTimeFilterWithNoDatesReturnsEmptyString() {
- String sql = jdbcBaseDAO.resolveTimeFilter(filter, UTC_ZONE_ID);
- assertThat(sql, is(""));
- }
-
- @Test
- public void testResolveTimeFilterWithStartDateOnlyReturnsWhereClause() {
- filter.setBeginDate(parseDateTimeString("2022-01-10T15:01:44"));
-
- String sql = jdbcBaseDAO.resolveTimeFilter(filter, UTC_ZONE_ID);
- assertThat(sql, is(" WHERE TIME>'" + JdbcBaseDAO.JDBC_DATE_FORMAT.format(filter.getBeginDate()) + "'"));
- }
-
- @Test
- public void testResolveTimeFilterWithEndDateOnlyReturnsWhereClause() {
- filter.setEndDate(parseDateTimeString("2022-01-15T15:01:44"));
-
- String sql = jdbcBaseDAO.resolveTimeFilter(filter, UTC_ZONE_ID);
- assertThat(sql, is(" WHERE TIME<'" + JdbcBaseDAO.JDBC_DATE_FORMAT.format(filter.getEndDate()) + "'"));
- }
-
- @Test
- public void testResolveTimeFilterWithStartAndEndDateReturnsWhereClauseWithTwoConditions() {
- filter.setBeginDate(parseDateTimeString("2022-01-10T15:01:44"));
- filter.setEndDate(parseDateTimeString("2022-01-15T15:01:44"));
-
- String sql = jdbcBaseDAO.resolveTimeFilter(filter, UTC_ZONE_ID);
- assertThat(sql, is(" WHERE TIME>'" + JdbcBaseDAO.JDBC_DATE_FORMAT.format(filter.getBeginDate()) + "'" //
- + " AND TIME<'" + JdbcBaseDAO.JDBC_DATE_FORMAT.format(filter.getEndDate()) + "'"));
- }
-
- private ZonedDateTime parseDateTimeString(String dts) {
- return ZonedDateTime.of(LocalDateTime.parse(dts, DATE_PARSER), UTC_ZONE_ID);
- }
-}
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.quality.Strictness;
-import org.openhab.persistence.jdbc.dto.ItemVO;
+import org.openhab.persistence.jdbc.internal.dto.ItemVO;
import org.slf4j.LoggerFactory;
import ch.qos.logback.classic.Level;
--- /dev/null
+/**
+ * Copyright (c) 2010-2022 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.persistence.jdbc.internal.db;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.openhab.core.library.items.CallItem;
+import org.openhab.core.library.items.ColorItem;
+import org.openhab.core.library.items.ContactItem;
+import org.openhab.core.library.items.DateTimeItem;
+import org.openhab.core.library.items.DimmerItem;
+import org.openhab.core.library.items.ImageItem;
+import org.openhab.core.library.items.LocationItem;
+import org.openhab.core.library.items.NumberItem;
+import org.openhab.core.library.items.PlayerItem;
+import org.openhab.core.library.items.RollershutterItem;
+import org.openhab.core.library.items.StringItem;
+import org.openhab.core.library.items.SwitchItem;
+import org.openhab.core.library.types.DateTimeType;
+import org.openhab.core.library.types.DecimalType;
+import org.openhab.core.library.types.HSBType;
+import org.openhab.core.library.types.OnOffType;
+import org.openhab.core.library.types.OpenClosedType;
+import org.openhab.core.library.types.PercentType;
+import org.openhab.core.library.types.PlayPauseType;
+import org.openhab.core.library.types.PointType;
+import org.openhab.core.library.types.QuantityType;
+import org.openhab.core.library.types.RawType;
+import org.openhab.core.library.types.RewindFastforwardType;
+import org.openhab.core.library.types.StringListType;
+import org.openhab.core.library.types.StringType;
+import org.openhab.core.library.unit.SIUnits;
+import org.openhab.core.persistence.FilterCriteria;
+import org.openhab.core.persistence.FilterCriteria.Ordering;
+import org.openhab.core.types.State;
+
+/**
+ * Tests the {@link JdbcBaseDAO}.
+ *
+ * @author Christoph Weitkamp - Initial contribution
+ */
+@NonNullByDefault
+public class JdbcBaseDAOTest {
+
+ private static final String DATE_PATTERN = "yyyy-MM-dd'T'HH:mm:ss";
+ private static final DateTimeFormatter DATE_PARSER = DateTimeFormatter.ofPattern(DATE_PATTERN);
+ private static final ZoneId UTC_ZONE_ID = ZoneId.of("UTC");
+ private static final String DB_TABLE_NAME = "testitem";
+
+ private final JdbcBaseDAO jdbcBaseDAO = new JdbcBaseDAO();
+ private @NonNullByDefault({}) FilterCriteria filter;
+
+ @BeforeEach
+ public void setup() {
+ filter = new FilterCriteria();
+ }
+
+ @Test
+ public void testObjectAsStateReturnsValidState() {
+ State decimalType = jdbcBaseDAO.objectAsState(new NumberItem("testNumberItem"), null, 7.3);
+ assertInstanceOf(DecimalType.class, decimalType);
+ assertEquals(DecimalType.valueOf("7.3"), decimalType);
+ State quantityType = jdbcBaseDAO.objectAsState(new NumberItem("testNumberItem"), SIUnits.CELSIUS, 7.3);
+ assertInstanceOf(QuantityType.class, quantityType);
+ assertEquals(QuantityType.valueOf("7.3 °C"), quantityType);
+
+ State hsbType = jdbcBaseDAO.objectAsState(new ColorItem("testColorItem"), null, "184,100,52");
+ assertInstanceOf(HSBType.class, hsbType);
+ assertEquals(HSBType.valueOf("184,100,52"), hsbType);
+
+ State percentType = jdbcBaseDAO.objectAsState(new DimmerItem("testDimmerItem"), null, 52);
+ assertInstanceOf(PercentType.class, percentType);
+ assertEquals(PercentType.valueOf("52"), percentType);
+
+ percentType = jdbcBaseDAO.objectAsState(new RollershutterItem("testRollershutterItem"), null, 39);
+ assertInstanceOf(PercentType.class, percentType);
+ assertEquals(PercentType.valueOf("39"), percentType);
+
+ State openClosedType = jdbcBaseDAO.objectAsState(new ContactItem("testContactItem"), null, "OPEN");
+ assertInstanceOf(OpenClosedType.class, openClosedType);
+ assertThat(openClosedType, is(OpenClosedType.OPEN));
+
+ State playPauseType = jdbcBaseDAO.objectAsState(new PlayerItem("testPlayerItem"), null, "PLAY");
+ assertInstanceOf(PlayPauseType.class, playPauseType);
+ assertThat(playPauseType, is(PlayPauseType.PLAY));
+ State rewindFastforwardType = jdbcBaseDAO.objectAsState(new PlayerItem("testPlayerItem"), null, "REWIND");
+ assertInstanceOf(RewindFastforwardType.class, rewindFastforwardType);
+ assertThat(rewindFastforwardType, is(RewindFastforwardType.REWIND));
+
+ State onOffType = jdbcBaseDAO.objectAsState(new SwitchItem("testSwitchItem"), null, "ON");
+ assertInstanceOf(OnOffType.class, onOffType);
+ assertThat(onOffType, is(OnOffType.ON));
+
+ State stringListType = jdbcBaseDAO.objectAsState(new CallItem("testCallItem"), null, "0699222222,0179999998");
+ assertInstanceOf(StringListType.class, stringListType);
+ assertEquals(StringListType.valueOf("0699222222,0179999998"), stringListType);
+
+ State expectedRawType = new RawType(new byte[0], "application/octet-stream");
+ State rawType = jdbcBaseDAO.objectAsState(new ImageItem("testImageItem"), null, expectedRawType.toFullString());
+ assertInstanceOf(RawType.class, rawType);
+ assertThat(rawType, is(expectedRawType));
+
+ State pointType = jdbcBaseDAO.objectAsState(new LocationItem("testLocationItem"), null, "1,2,3");
+ assertInstanceOf(PointType.class, pointType);
+ assertEquals(PointType.valueOf("1,2,3"), pointType);
+
+ State stringType = jdbcBaseDAO.objectAsState(new StringItem("testStringItem"), null, "String");
+ assertInstanceOf(StringType.class, stringType);
+ assertEquals(StringType.valueOf("String"), stringType);
+ }
+
+ @Test
+ public void objectAsStateReturnsValiDateTimeTypeForTimestamp() {
+ State dateTimeType = jdbcBaseDAO.objectAsState(new DateTimeItem("testDateTimeItem"), null,
+ java.sql.Timestamp.valueOf("2021-02-01 23:30:02.049"));
+ assertInstanceOf(DateTimeType.class, dateTimeType);
+ assertEquals(DateTimeType.valueOf("2021-02-01T23:30:02.049"), dateTimeType);
+ }
+
+ @Test
+ public void objectAsStateReturnsValidDateTimeTypeForLocalDateTime() {
+ State dateTimeType = jdbcBaseDAO.objectAsState(new DateTimeItem("testDateTimeItem"), null,
+ LocalDateTime.parse("2021-02-01T23:30:02.049"));
+ assertInstanceOf(DateTimeType.class, dateTimeType);
+ assertEquals(DateTimeType.valueOf("2021-02-01T23:30:02.049"), dateTimeType);
+ }
+
+ @Test
+ public void objectAsStateReturnsValidDateTimeTypeForLong() {
+ State dateTimeType = jdbcBaseDAO.objectAsState(new DateTimeItem("testDateTimeItem"), null,
+ Long.valueOf("1612222202049"));
+ assertInstanceOf(DateTimeType.class, dateTimeType);
+ assertEquals(
+ new DateTimeType(
+ ZonedDateTime.ofInstant(Instant.parse("2021-02-01T23:30:02.049Z"), ZoneId.systemDefault())),
+ dateTimeType);
+ }
+
+ @Test
+ public void objectAsStateReturnsValidDateTimeTypeForSqlDate() {
+ State dateTimeType = jdbcBaseDAO.objectAsState(new DateTimeItem("testDateTimeItem"), null,
+ java.sql.Date.valueOf("2021-02-01"));
+ assertInstanceOf(DateTimeType.class, dateTimeType);
+ assertEquals(DateTimeType.valueOf("2021-02-01T00:00:00.000"), dateTimeType);
+ }
+
+ @Test
+ public void objectAsStateReturnsValidDateTimeTypeForInstant() {
+ State dateTimeType = jdbcBaseDAO.objectAsState(new DateTimeItem("testDateTimeItem"), null,
+ Instant.parse("2021-02-01T23:30:02.049Z"));
+ assertInstanceOf(DateTimeType.class, dateTimeType);
+ assertEquals(
+ new DateTimeType(
+ ZonedDateTime.ofInstant(Instant.parse("2021-02-01T23:30:02.049Z"), ZoneId.systemDefault())),
+ dateTimeType);
+ }
+
+ @Test
+ public void objectAsStateReturnsValidDateTimeTypeForString() {
+ State dateTimeType = jdbcBaseDAO.objectAsState(new DateTimeItem("testDateTimeItem"), null,
+ "2021-02-01 23:30:02.049");
+ assertInstanceOf(DateTimeType.class, dateTimeType);
+ assertEquals(DateTimeType.valueOf("2021-02-01T23:30:02.049"), dateTimeType);
+ }
+
+ @Test
+ public void testHistItemFilterQueryProviderReturnsSelectQueryWithoutWhereClauseDescendingOrder() {
+ String sql = jdbcBaseDAO.histItemFilterQueryProvider(filter, 0, DB_TABLE_NAME, "TEST", UTC_ZONE_ID);
+ assertThat(sql, is("SELECT time, value FROM " + DB_TABLE_NAME + " ORDER BY time DESC"));
+ }
+
+ @Test
+ public void testHistItemFilterQueryProviderReturnsSelectQueryWithoutWhereClauseAscendingOrder() {
+ filter.setOrdering(Ordering.ASCENDING);
+
+ String sql = jdbcBaseDAO.histItemFilterQueryProvider(filter, 0, DB_TABLE_NAME, "TEST", UTC_ZONE_ID);
+ assertThat(sql, is("SELECT time, value FROM " + DB_TABLE_NAME + " ORDER BY time ASC"));
+ }
+
+ @Test
+ public void testHistItemFilterQueryProviderWithStartAndEndDateReturnsDeleteQueryWithWhereClauseDescendingOrder() {
+ filter.setBeginDate(parseDateTimeString("2022-01-10T15:01:44"));
+ filter.setEndDate(parseDateTimeString("2022-01-15T15:01:44"));
+
+ String sql = jdbcBaseDAO.histItemFilterQueryProvider(filter, 0, DB_TABLE_NAME, "TEST", UTC_ZONE_ID);
+ assertThat(sql, is("SELECT time, value FROM " + DB_TABLE_NAME + " WHERE TIME>'" //
+ + JdbcBaseDAO.JDBC_DATE_FORMAT.format(filter.getBeginDate()) + "'" //
+ + " AND TIME<'" + JdbcBaseDAO.JDBC_DATE_FORMAT.format(filter.getEndDate()) + "' ORDER BY time DESC"));
+ }
+
+ @Test
+ public void testHistItemFilterQueryProviderReturnsSelectQueryWithoutWhereClauseDescendingOrderAndLimit() {
+ filter.setPageSize(1);
+
+ String sql = jdbcBaseDAO.histItemFilterQueryProvider(filter, 0, DB_TABLE_NAME, "TEST", UTC_ZONE_ID);
+ assertThat(sql, is("SELECT time, value FROM " + DB_TABLE_NAME + " ORDER BY time DESC LIMIT 0,1"));
+ }
+
+ @Test
+ public void testHistItemFilterDeleteProviderReturnsDeleteQueryWithoutWhereClause() {
+ String sql = jdbcBaseDAO.histItemFilterDeleteProvider(filter, DB_TABLE_NAME, UTC_ZONE_ID);
+ assertThat(sql, is("TRUNCATE TABLE " + DB_TABLE_NAME));
+ }
+
+ @Test
+ public void testHistItemFilterDeleteProviderWithStartAndEndDateReturnsDeleteQueryWithWhereClause() {
+ filter.setBeginDate(parseDateTimeString("2022-01-10T15:01:44"));
+ filter.setEndDate(parseDateTimeString("2022-01-15T15:01:44"));
+
+ String sql = jdbcBaseDAO.histItemFilterDeleteProvider(filter, DB_TABLE_NAME, UTC_ZONE_ID);
+ assertThat(sql, is("DELETE FROM " + DB_TABLE_NAME + " WHERE TIME>'" //
+ + JdbcBaseDAO.JDBC_DATE_FORMAT.format(filter.getBeginDate()) + "'" //
+ + " AND TIME<'" + JdbcBaseDAO.JDBC_DATE_FORMAT.format(filter.getEndDate()) + "'"));
+ }
+
+ @Test
+ public void testResolveTimeFilterWithNoDatesReturnsEmptyString() {
+ String sql = jdbcBaseDAO.resolveTimeFilter(filter, UTC_ZONE_ID);
+ assertThat(sql, is(""));
+ }
+
+ @Test
+ public void testResolveTimeFilterWithStartDateOnlyReturnsWhereClause() {
+ filter.setBeginDate(parseDateTimeString("2022-01-10T15:01:44"));
+
+ String sql = jdbcBaseDAO.resolveTimeFilter(filter, UTC_ZONE_ID);
+ assertThat(sql, is(" WHERE TIME>'" + JdbcBaseDAO.JDBC_DATE_FORMAT.format(filter.getBeginDate()) + "'"));
+ }
+
+ @Test
+ public void testResolveTimeFilterWithEndDateOnlyReturnsWhereClause() {
+ filter.setEndDate(parseDateTimeString("2022-01-15T15:01:44"));
+
+ String sql = jdbcBaseDAO.resolveTimeFilter(filter, UTC_ZONE_ID);
+ assertThat(sql, is(" WHERE TIME<'" + JdbcBaseDAO.JDBC_DATE_FORMAT.format(filter.getEndDate()) + "'"));
+ }
+
+ @Test
+ public void testResolveTimeFilterWithStartAndEndDateReturnsWhereClauseWithTwoConditions() {
+ filter.setBeginDate(parseDateTimeString("2022-01-10T15:01:44"));
+ filter.setEndDate(parseDateTimeString("2022-01-15T15:01:44"));
+
+ String sql = jdbcBaseDAO.resolveTimeFilter(filter, UTC_ZONE_ID);
+ assertThat(sql, is(" WHERE TIME>'" + JdbcBaseDAO.JDBC_DATE_FORMAT.format(filter.getBeginDate()) + "'" //
+ + " AND TIME<'" + JdbcBaseDAO.JDBC_DATE_FORMAT.format(filter.getEndDate()) + "'"));
+ }
+
+ private ZonedDateTime parseDateTimeString(String dts) {
+ return ZonedDateTime.of(LocalDateTime.parse(dts, DATE_PARSER), UTC_ZONE_ID);
+ }
+}