From 63cdc9576c3f3b6f89c80b55059f688dac62a557 Mon Sep 17 00:00:00 2001 From: Jacob Laursen Date: Mon, 21 Oct 2024 23:48:59 +0200 Subject: [PATCH] [jdbc] Fix case-sensitive table names for PostgreSQL (#17597) * Fix case-sensitive table names for PostgreSQL Signed-off-by: Jacob Laursen --- .../org.openhab.persistence.jdbc/README.md | 8 +-- .../jdbc/internal/db/JdbcBaseDAO.java | 48 +++++++------ .../jdbc/internal/db/JdbcDerbyDAO.java | 67 +++--------------- .../jdbc/internal/db/JdbcH2DAO.java | 5 +- .../jdbc/internal/db/JdbcHsqldbDAO.java | 35 ++-------- .../jdbc/internal/db/JdbcOracleDAO.java | 3 +- .../jdbc/internal/db/JdbcPostgresqlDAO.java | 68 ++++++++----------- .../jdbc/internal/db/JdbcSqliteDAO.java | 7 +- .../jdbc/internal/db/JdbcTimescaledbDAO.java | 6 +- 9 files changed, 87 insertions(+), 160 deletions(-) diff --git a/bundles/org.openhab.persistence.jdbc/README.md b/bundles/org.openhab.persistence.jdbc/README.md index d0830ff256a..04b0f82a43b 100644 --- a/bundles/org.openhab.persistence.jdbc/README.md +++ b/bundles/org.openhab.persistence.jdbc/README.md @@ -79,7 +79,7 @@ All item- and event-related configuration is done in the file `persistence/jdbc. To configure this service as the default persistence service for openHAB, add or change the line -``` +```ini org.openhab.core.persistence:default=jdbc ``` @@ -89,7 +89,7 @@ in the file `services/runtime.cfg`. services/jdbc.cfg -``` +```ini url=jdbc:postgresql://192.168.0.1:5432/testPostgresql ``` @@ -102,7 +102,7 @@ To connect to an Oracle Autonomous Database, use the instructions at https://www Your services/jdbc.cfg should contain the following minimal configuration for connecting to an Oracle Autonomous Database: -``` +```ini url=jdbc:oracle:thin:@dbname?TNS_ADMIN=./dbname_tns_admin_folder user=openhab password=openhab_password @@ -147,7 +147,7 @@ Here is an example of a configuration for a MySQL database named `testMysql` wit services/jdbc.cfg -``` +```ini url=jdbc:mysql://192.168.0.1:3306/testMysql user=test password=test diff --git a/bundles/org.openhab.persistence.jdbc/src/main/java/org/openhab/persistence/jdbc/internal/db/JdbcBaseDAO.java b/bundles/org.openhab.persistence.jdbc/src/main/java/org/openhab/persistence/jdbc/internal/db/JdbcBaseDAO.java index f524c6fc50a..35179d1eccd 100644 --- a/bundles/org.openhab.persistence.jdbc/src/main/java/org/openhab/persistence/jdbc/internal/db/JdbcBaseDAO.java +++ b/bundles/org.openhab.persistence.jdbc/src/main/java/org/openhab/persistence/jdbc/internal/db/JdbcBaseDAO.java @@ -296,7 +296,7 @@ public class JdbcBaseDAO { public Long doCreateNewEntryInItemsTable(ItemsVO vo) throws JdbcSQLException { String sql = StringUtilsExt.replaceArrayMerge(sqlCreateNewEntryInItemsTable, new String[] { "#itemsManageTable#", "#itemname#" }, - new String[] { vo.getItemsManageTable(), vo.getItemName() }); + new String[] { formattedIdentifier(vo.getItemsManageTable()), vo.getItemName() }); logger.debug("JDBC::doCreateNewEntryInItemsTable sql={}", sql); try { return Yank.insert(sql, null); @@ -308,7 +308,7 @@ public class JdbcBaseDAO { 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() }); + new String[] { formattedIdentifier(vo.getItemsManageTable()), vo.getColname(), vo.getColtype() }); logger.debug("JDBC::doCreateItemsTableIfNot sql={}", sql); try { Yank.execute(sql, null); @@ -320,7 +320,7 @@ public class JdbcBaseDAO { public ItemsVO doDropItemsTableIfExists(ItemsVO vo) throws JdbcSQLException { String sql = StringUtilsExt.replaceArrayMerge(sqlDropItemsTableIfExists, new String[] { "#itemsManageTable#" }, - new String[] { vo.getItemsManageTable() }); + new String[] { formattedIdentifier(vo.getItemsManageTable()) }); logger.debug("JDBC::doDropItemsTableIfExists sql={}", sql); try { Yank.execute(sql, null); @@ -332,7 +332,7 @@ public class JdbcBaseDAO { public void doDropTable(String tableName) throws JdbcSQLException { String sql = StringUtilsExt.replaceArrayMerge(sqlDropTable, new String[] { "#tableName#" }, - new String[] { tableName }); + new String[] { formattedIdentifier(tableName) }); logger.debug("JDBC::doDropTable sql={}", sql); try { Yank.execute(sql, null); @@ -344,7 +344,7 @@ public class JdbcBaseDAO { public void doDeleteItemsEntry(ItemsVO vo) throws JdbcSQLException { String sql = StringUtilsExt.replaceArrayMerge(sqlDeleteItemsEntry, new String[] { "#itemsManageTable#", "#itemname#" }, - new String[] { vo.getItemsManageTable(), vo.getItemName() }); + new String[] { formattedIdentifier(vo.getItemsManageTable()), vo.getItemName() }); logger.debug("JDBC::doDeleteItemsEntry sql={}", sql); try { Yank.execute(sql, null); @@ -355,7 +355,7 @@ public class JdbcBaseDAO { public List doGetItemIDTableNames(ItemsVO vo) throws JdbcSQLException { String sql = StringUtilsExt.replaceArrayMerge(sqlGetItemIDTableNames, new String[] { "#itemsManageTable#" }, - new String[] { vo.getItemsManageTable() }); + new String[] { formattedIdentifier(vo.getItemsManageTable()) }); logger.debug("JDBC::doGetItemIDTableNames sql={}", sql); try { return Yank.queryBeanList(sql, ItemsVO.class, null); @@ -405,8 +405,8 @@ public class JdbcBaseDAO { 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") }); + new String[] { "#tableName#", "#dbType#", "#tablePrimaryKey#" }, new String[] { + formattedIdentifier(vo.getTableName()), vo.getDbType(), sqlTypes.get("tablePrimaryKey") }); logger.debug("JDBC::doCreateItemTable sql={}", sql); try { Yank.execute(sql, null); @@ -418,8 +418,8 @@ public class JdbcBaseDAO { public void doAlterTableColumn(String tableName, String columnName, String columnType, boolean nullable) throws JdbcSQLException { String sql = StringUtilsExt.replaceArrayMerge(sqlAlterTableColumn, - new String[] { "#tableName#", "#columnName#", "#columnType#" }, - new String[] { tableName, columnName, nullable ? columnType : columnType + " NOT NULL" }); + new String[] { "#tableName#", "#columnName#", "#columnType#" }, new String[] { + formattedIdentifier(tableName), columnName, nullable ? columnType : columnType + " NOT NULL" }); logger.debug("JDBC::doAlterTableColumn sql={}", sql); try { Yank.execute(sql, null); @@ -432,7 +432,7 @@ public class JdbcBaseDAO { ItemVO storedVO = storeItemValueProvider(item, itemState, vo); String sql = StringUtilsExt.replaceArrayMerge(sqlInsertItemValue, new String[] { "#tableName#", "#tablePrimaryValue#" }, - new String[] { storedVO.getTableName(), sqlTypes.get("tablePrimaryValue") }); + new String[] { formattedIdentifier(storedVO.getTableName()), sqlTypes.get("tablePrimaryValue") }); Object[] params = { storedVO.getValue(), storedVO.getValue() }; logger.debug("JDBC::doStoreItemValue sql={} value='{}'", sql, storedVO.getValue()); try { @@ -445,7 +445,8 @@ public class JdbcBaseDAO { 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(), "?" }); + new String[] { "#tableName#", "#tablePrimaryValue#" }, + new String[] { formattedIdentifier(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()); @@ -490,7 +491,7 @@ public class JdbcBaseDAO { public long doGetRowCount(String tableName) throws JdbcSQLException { final String sql = StringUtilsExt.replaceArrayMerge(sqlGetRowCount, new String[] { "#tableName#" }, - new String[] { tableName }); + new String[] { formattedIdentifier(tableName) }); logger.debug("JDBC::doGetRowCount sql={}", sql); try { final @Nullable Long result = Yank.queryScalar(sql, Long.class, null); @@ -519,8 +520,8 @@ public class JdbcBaseDAO { // 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; + ? "SELECT time, ROUND(value," + numberDecimalcount + ") FROM " + formattedIdentifier(table) + : "SELECT time, value FROM " + formattedIdentifier(table); if (!filterString.isEmpty()) { queryString += filterString; } @@ -532,8 +533,8 @@ public class JdbcBaseDAO { logger.debug("JDBC::histItemFilterDeleteProvider filter = {}, table = {}", filter, table); String filterString = resolveTimeFilter(filter, timeZone); - String deleteString = filterString.isEmpty() ? "TRUNCATE TABLE " + table - : "DELETE FROM " + table + filterString; + String deleteString = filterString.isEmpty() ? "TRUNCATE TABLE " + formattedIdentifier(table) + : "DELETE FROM " + formattedIdentifier(table) + filterString; logger.debug("JDBC::delete deleteString = {}", deleteString); return deleteString; } @@ -554,7 +555,12 @@ public class JdbcBaseDAO { } private String updateItemTableNamesProvider(ItemVO itemTable) { - String queryString = "ALTER TABLE " + itemTable.getTableName() + " RENAME TO " + itemTable.getNewTableName(); + String newTableName = itemTable.getNewTableName(); + if (newTableName == null) { + throw new IllegalArgumentException("New table name is not provided"); + } + String queryString = "ALTER TABLE " + formattedIdentifier(itemTable.getTableName()) + " RENAME TO " + + formattedIdentifier(newTableName); logger.debug("JDBC::query queryString = {}", queryString); return queryString; } @@ -757,7 +763,11 @@ public class JdbcBaseDAO { throw new UnsupportedOperationException("String of type '" + v.getClass().getName() + "' is not supported"); } - public String getItemType(Item i) { + protected String formattedIdentifier(String identifier) { + return identifier; + } + + private String getItemType(Item i) { Item item = i; String def = "STRINGITEM"; if (i instanceof GroupItem groupItem) { diff --git a/bundles/org.openhab.persistence.jdbc/src/main/java/org/openhab/persistence/jdbc/internal/db/JdbcDerbyDAO.java b/bundles/org.openhab.persistence.jdbc/src/main/java/org/openhab/persistence/jdbc/internal/db/JdbcDerbyDAO.java index d1be0bee42f..95b294430f4 100644 --- a/bundles/org.openhab.persistence.jdbc/src/main/java/org/openhab/persistence/jdbc/internal/db/JdbcDerbyDAO.java +++ b/bundles/org.openhab.persistence.jdbc/src/main/java/org/openhab/persistence/jdbc/internal/db/JdbcDerbyDAO.java @@ -14,26 +14,18 @@ package org.openhab.persistence.jdbc.internal.db; import java.time.ZoneId; import java.time.ZonedDateTime; -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; @@ -121,7 +113,7 @@ public class JdbcDerbyDAO extends JdbcBaseDAO { @Override public boolean doIfTableExists(ItemsVO vo) throws JdbcSQLException { String sql = StringUtilsExt.replaceArrayMerge(sqlIfTableExists, new String[] { "#searchTable#" }, - new String[] { vo.getItemsManageTable().toUpperCase() }); + new String[] { formattedIdentifier(vo.getItemsManageTable()) }); logger.debug("JDBC::doIfTableExists sql={}", sql); try { final @Nullable String result = Yank.queryScalar(sql, String.class, null); @@ -131,26 +123,13 @@ public class JdbcDerbyDAO extends JdbcBaseDAO { } } - @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() }); + new String[] { formattedIdentifier(vo.getItemsManageTable()), vo.getColname(), vo.getColtype() }); logger.debug("JDBC::doCreateItemsTableIfNot tableExists={} therefore sql={}", tableExists, sql); try { Yank.execute(sql, null); @@ -166,24 +145,12 @@ public class JdbcDerbyDAO extends JdbcBaseDAO { /************* * 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(), + new String[] { formattedIdentifier(storedVO.getTableName()), storedVO.getDbType(), sqlTypes.get("tablePrimaryValue") }); Object[] params = { storedVO.getValue() }; logger.debug("JDBC::doStoreItemValue sql={} value='{}'", sql, storedVO.getValue()); @@ -199,7 +166,7 @@ public class JdbcDerbyDAO extends JdbcBaseDAO { ItemVO storedVO = storeItemValueProvider(item, itemState, vo); String sql = StringUtilsExt.replaceArrayMerge(sqlInsertItemValue, new String[] { "#tableName#", "#dbType#", "#tablePrimaryValue#" }, - new String[] { storedVO.getTableName().toUpperCase(), storedVO.getDbType(), "?" }); + new String[] { formattedIdentifier(storedVO.getTableName()), storedVO.getDbType(), "?" }); java.sql.Timestamp timestamp = new java.sql.Timestamp(date.toInstant().toEpochMilli()); Object[] params = { timestamp, storedVO.getValue() }; logger.debug("JDBC::doStoreItemValue sql={} timestamp={} value='{}'", sql, timestamp, storedVO.getValue()); @@ -210,26 +177,6 @@ public class JdbcDerbyDAO extends JdbcBaseDAO { } } - @Override - public List doGetHistItemFilterQuery(Item item, FilterCriteria filter, int numberDecimalcount, - String table, String name, ZoneId timeZone) throws JdbcSQLException { - String sql = histItemFilterQueryProvider(filter, numberDecimalcount, table, name, timeZone); - List 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> unit = item instanceof NumberItem ni ? ni.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. toList()); - } - /**************************** * SQL generation Providers * ****************************/ @@ -280,7 +227,7 @@ public class JdbcDerbyDAO extends JdbcBaseDAO { 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(); + queryString += " value FROM " + formattedIdentifier(table); } if (!filterString.isEmpty()) { @@ -293,6 +240,10 @@ public class JdbcDerbyDAO extends JdbcBaseDAO { /***************** * H E L P E R S * *****************/ + @Override + protected String formattedIdentifier(String identifier) { + return identifier.toUpperCase(); + } /****************************** * public Getters and Setters * diff --git a/bundles/org.openhab.persistence.jdbc/src/main/java/org/openhab/persistence/jdbc/internal/db/JdbcH2DAO.java b/bundles/org.openhab.persistence.jdbc/src/main/java/org/openhab/persistence/jdbc/internal/db/JdbcH2DAO.java index 51c7d3567ed..409b2895b61 100644 --- a/bundles/org.openhab.persistence.jdbc/src/main/java/org/openhab/persistence/jdbc/internal/db/JdbcH2DAO.java +++ b/bundles/org.openhab.persistence.jdbc/src/main/java/org/openhab/persistence/jdbc/internal/db/JdbcH2DAO.java @@ -85,7 +85,8 @@ public class JdbcH2DAO extends JdbcBaseDAO { 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") }); + new String[] { formattedIdentifier(storedVO.getTableName()), storedVO.getDbType(), + sqlTypes.get("tablePrimaryValue") }); Object[] params = { storedVO.getValue() }; logger.debug("JDBC::doStoreItemValue sql={} value='{}'", sql, storedVO.getValue()); try { @@ -100,7 +101,7 @@ public class JdbcH2DAO extends JdbcBaseDAO { ItemVO storedVO = storeItemValueProvider(item, itemState, vo); String sql = StringUtilsExt.replaceArrayMerge(sqlInsertItemValue, new String[] { "#tableName#", "#dbType#", "#tablePrimaryValue#" }, - new String[] { storedVO.getTableName(), storedVO.getDbType(), "?" }); + new String[] { formattedIdentifier(storedVO.getTableName()), storedVO.getDbType(), "?" }); java.sql.Timestamp timestamp = new java.sql.Timestamp(date.toInstant().toEpochMilli()); Object[] params = { timestamp, storedVO.getValue() }; logger.debug("JDBC::doStoreItemValue sql={} timestamp={} value='{}'", sql, timestamp, storedVO.getValue()); diff --git a/bundles/org.openhab.persistence.jdbc/src/main/java/org/openhab/persistence/jdbc/internal/db/JdbcHsqldbDAO.java b/bundles/org.openhab.persistence.jdbc/src/main/java/org/openhab/persistence/jdbc/internal/db/JdbcHsqldbDAO.java index 9caa2fcc2ff..150a0cede4f 100644 --- a/bundles/org.openhab.persistence.jdbc/src/main/java/org/openhab/persistence/jdbc/internal/db/JdbcHsqldbDAO.java +++ b/bundles/org.openhab.persistence.jdbc/src/main/java/org/openhab/persistence/jdbc/internal/db/JdbcHsqldbDAO.java @@ -21,7 +21,6 @@ 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; @@ -97,33 +96,6 @@ public class JdbcHsqldbDAO extends JdbcBaseDAO { } } - @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 * *************/ @@ -132,8 +104,8 @@ public class JdbcHsqldbDAO extends JdbcBaseDAO { 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") }); + new String[] { formattedIdentifier(storedVO.getTableName()), storedVO.getDbType(), + storedVO.getTableName(), sqlTypes.get("tablePrimaryValue") }); Object[] params = { storedVO.getValue() }; logger.debug("JDBC::doStoreItemValue sql={} value='{}'", sql, storedVO.getValue()); try { @@ -148,7 +120,8 @@ public class JdbcHsqldbDAO extends JdbcBaseDAO { 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(), "?" }); + new String[] { formattedIdentifier(storedVO.getTableName()), storedVO.getDbType(), + storedVO.getTableName(), "?" }); java.sql.Timestamp timestamp = new java.sql.Timestamp(date.toInstant().toEpochMilli()); Object[] params = { timestamp, storedVO.getValue() }; logger.debug("JDBC::doStoreItemValue sql={} timestamp={} value='{}'", sql, timestamp, storedVO.getValue()); diff --git a/bundles/org.openhab.persistence.jdbc/src/main/java/org/openhab/persistence/jdbc/internal/db/JdbcOracleDAO.java b/bundles/org.openhab.persistence.jdbc/src/main/java/org/openhab/persistence/jdbc/internal/db/JdbcOracleDAO.java index 63db39eadc3..4925726ee13 100644 --- a/bundles/org.openhab.persistence.jdbc/src/main/java/org/openhab/persistence/jdbc/internal/db/JdbcOracleDAO.java +++ b/bundles/org.openhab.persistence.jdbc/src/main/java/org/openhab/persistence/jdbc/internal/db/JdbcOracleDAO.java @@ -175,8 +175,7 @@ public class JdbcOracleDAO extends JdbcBaseDAO { throw new JdbcSQLException(e); } // We need to return the itemId, but Yank.insert does not retrieve the value from Oracle. So do an explicit - // query - // for it. + // query for it. sql = StringUtilsExt.replaceArrayMerge(sqlGetItemTableID, new String[] { "#itemsManageTable#", "#colname#" }, new String[] { vo.getItemsManageTable(), vo.getColname() }); logger.debug("JDBC::doGetEntryIdInItemsTable sql={}", sql); diff --git a/bundles/org.openhab.persistence.jdbc/src/main/java/org/openhab/persistence/jdbc/internal/db/JdbcPostgresqlDAO.java b/bundles/org.openhab.persistence.jdbc/src/main/java/org/openhab/persistence/jdbc/internal/db/JdbcPostgresqlDAO.java index 577a93dfe17..5235fc90e19 100644 --- a/bundles/org.openhab.persistence.jdbc/src/main/java/org/openhab/persistence/jdbc/internal/db/JdbcPostgresqlDAO.java +++ b/bundles/org.openhab.persistence.jdbc/src/main/java/org/openhab/persistence/jdbc/internal/db/JdbcPostgresqlDAO.java @@ -60,28 +60,25 @@ public class JdbcPostgresqlDAO extends JdbcBaseDAO { 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#\"'"; - sqlDropTable = "DROP TABLE \"#tableName#\""; - 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"; + sqlIfTableExists = "SELECT * FROM PG_TABLES WHERE TABLENAME='#searchTable#'"; + sqlCreateItemsTableIfNot = "CREATE TABLE IF NOT EXISTS #itemsManageTable# (itemid SERIAL NOT NULL, #colname# #coltype# NOT NULL, CONSTRAINT #tablePrimaryKey# 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#\"'\ + FROM information_schema.tables WHERE table_type='BASE TABLE' AND table_name='#itemsManageTable#') AND NOT table_name='#itemsManageTable#'\ """; // The PostgreSQL equivalent to MySQL columns.column_type is data_type (e.g. "timestamp with time zone") and // udt_name which contains a shorter alias (e.g. "timestamptz"). We alias data_type as "column_type" and // udt_name as "column_type_alias" to be compatible with the 'Column' class used in Yank.queryBeanList sqlGetTableColumnTypes = """ SELECT column_name, data_type as column_type, udt_name as column_type_alias, is_nullable FROM information_schema.columns \ - WHERE table_name='\"#tableName#\"' AND table_catalog='#jdbcUriDatabaseName#' AND table_schema=(SELECT table_schema FROM information_schema.tables WHERE table_type='BASE TABLE' \ - AND table_name='\"#itemsManageTable#\"')\ + WHERE table_name='#tableName#' AND table_catalog='#jdbcUriDatabaseName#' AND table_schema=(SELECT table_schema FROM information_schema.tables WHERE table_type='BASE TABLE' \ + AND table_name='#itemsManageTable#')\ """; // NOTICE: on PostgreSql >= 9.5, sqlInsertItemValue query template is modified to do an "upsert" (overwrite // existing value). The version check and query change is performed at initAfterFirstDbConnection() - sqlInsertItemValue = "INSERT INTO \"#tableName#\" (TIME, VALUE) VALUES( #tablePrimaryValue#, CAST( ? as #dbType#) )"; - sqlCreateItemTable = "CREATE TABLE IF NOT EXISTS \"#tableName#\" (time #tablePrimaryKey# NOT NULL, value #dbType#, PRIMARY KEY(time))"; - sqlAlterTableColumn = "ALTER TABLE \"#tableName#\" ALTER COLUMN #columnName# TYPE #columnType#"; - sqlGetRowCount = "SELECT COUNT(*) FROM \"#tableName#\""; + sqlInsertItemValue = "INSERT INTO #tableName# (TIME, VALUE) VALUES( #tablePrimaryValue#, CAST( ? as #dbType#) )"; + sqlAlterTableColumn = "ALTER TABLE #tableName# ALTER COLUMN #columnName# TYPE #columnType#"; } @Override @@ -95,7 +92,7 @@ public class JdbcPostgresqlDAO extends JdbcBaseDAO { if (dbMeta.isDbVersionGreater(9, 4)) { logger.debug("JDBC::initAfterFirstDbConnection: Values with the same time will be upserted (Pg >= 9.5)"); sqlInsertItemValue = """ - INSERT INTO \"#tableName#\" (TIME, VALUE) VALUES( #tablePrimaryValue#, CAST( ? as #dbType#) )\ + INSERT INTO #tableName# (TIME, VALUE) VALUES( #tablePrimaryValue#, CAST( ? as #dbType#) )\ ON CONFLICT (TIME) DO UPDATE SET VALUE=EXCLUDED.VALUE\ """; } @@ -145,8 +142,9 @@ public class JdbcPostgresqlDAO extends JdbcBaseDAO { @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() }); + new String[] { "#itemsManageTable#", "#colname#", "#coltype#", "#tablePrimaryKey#" }, + new String[] { formattedIdentifier(vo.getItemsManageTable()), vo.getColname(), vo.getColtype(), + vo.getItemsManageTable() + "_pkey" }); logger.debug("JDBC::doCreateItemsTableIfNot sql={}", sql); try { Yank.execute(sql, null); @@ -156,25 +154,12 @@ public class JdbcPostgresqlDAO extends JdbcBaseDAO { 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 doGetItemTables(ItemsVO vo) throws JdbcSQLException { - String sql = StringUtilsExt.replaceArrayMerge(this.sqlGetItemTables, + String sql = StringUtilsExt.replaceArrayMerge(sqlGetItemTables, new String[] { "#itemsManageTable#", "#itemsManageTable#" }, new String[] { vo.getItemsManageTable(), vo.getItemsManageTable() }); - this.logger.debug("JDBC::doGetItemTables sql={}", sql); + logger.debug("JDBC::doGetItemTables sql={}", sql); try { return Yank.queryBeanList(sql, ItemsVO.class, null); } catch (YankSQLException e) { @@ -210,15 +195,16 @@ public class JdbcPostgresqlDAO extends JdbcBaseDAO { throws JdbcSQLException { String sql = StringUtilsExt.replaceArrayMerge(sqlAlterTableColumn, new String[] { "#tableName#", "#columnName#", "#columnType#" }, - new String[] { tableName, columnName, columnType }); - logger.info("JDBC::doAlterTableColumn sql={}", sql); + new String[] { formattedIdentifier(tableName), columnName, columnType }); + logger.debug("JDBC::doAlterTableColumn sql={}", sql); try { Yank.execute(sql, null); if (!nullable) { String sql2 = StringUtilsExt.replaceArrayMerge( - "ALTER TABLE \"#tableName#\" ALTER COLUMN #columnName# SET NOT NULL", - new String[] { "#tableName#", "#columnName#" }, new String[] { tableName, columnName }); - logger.info("JDBC::doAlterTableColumn sql={}", sql2); + "ALTER TABLE #tableName# ALTER COLUMN #columnName# SET NOT NULL", + new String[] { "#tableName#", "#columnName#" }, + new String[] { formattedIdentifier(tableName), columnName }); + logger.debug("JDBC::doAlterTableColumn sql={}", sql2); Yank.execute(sql2, null); } } catch (YankSQLException e) { @@ -231,7 +217,8 @@ public class JdbcPostgresqlDAO extends JdbcBaseDAO { 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") }); + new String[] { formattedIdentifier(storedVO.getTableName()), storedVO.getDbType(), + sqlTypes.get("tablePrimaryValue") }); Object[] params = { storedVO.getValue() }; logger.debug("JDBC::doStoreItemValue sql={} value='{}'", sql, storedVO.getValue()); try { @@ -246,7 +233,7 @@ public class JdbcPostgresqlDAO extends JdbcBaseDAO { ItemVO storedVO = storeItemValueProvider(item, itemState, vo); String sql = StringUtilsExt.replaceArrayMerge(sqlInsertItemValue, new String[] { "#tableName#", "#dbType#", "#tablePrimaryValue#" }, - new String[] { storedVO.getTableName(), storedVO.getDbType(), "?" }); + new String[] { formattedIdentifier(storedVO.getTableName()), storedVO.getDbType(), "?" }); java.sql.Timestamp timestamp = new java.sql.Timestamp(date.toInstant().toEpochMilli()); Object[] params = { timestamp, storedVO.getValue() }; logger.debug("JDBC::doStoreItemValue sql={} timestamp={} value='{}'", sql, timestamp, storedVO.getValue()); @@ -287,8 +274,9 @@ public class JdbcPostgresqlDAO extends JdbcBaseDAO { + filter.getPageSize(); } String queryString = "NUMBERITEM".equalsIgnoreCase(simpleName) && numberDecimalcount > -1 - ? "SELECT time, ROUND(CAST (value AS numeric)," + numberDecimalcount + ") FROM " + table - : "SELECT time, value FROM " + table; + ? "SELECT time, ROUND(CAST (value AS numeric)," + numberDecimalcount + ") FROM " + + formattedIdentifier(table) + : "SELECT time, value FROM " + formattedIdentifier(table); if (!filterString.isEmpty()) { queryString += filterString; } @@ -299,6 +287,10 @@ public class JdbcPostgresqlDAO extends JdbcBaseDAO { /***************** * H E L P E R S * *****************/ + @Override + protected String formattedIdentifier(String identifier) { + return "\"" + identifier + "\""; + } /****************************** * public Getters and Setters * diff --git a/bundles/org.openhab.persistence.jdbc/src/main/java/org/openhab/persistence/jdbc/internal/db/JdbcSqliteDAO.java b/bundles/org.openhab.persistence.jdbc/src/main/java/org/openhab/persistence/jdbc/internal/db/JdbcSqliteDAO.java index c5bc9d1f324..95e4495cb4f 100644 --- a/bundles/org.openhab.persistence.jdbc/src/main/java/org/openhab/persistence/jdbc/internal/db/JdbcSqliteDAO.java +++ b/bundles/org.openhab.persistence.jdbc/src/main/java/org/openhab/persistence/jdbc/internal/db/JdbcSqliteDAO.java @@ -96,7 +96,7 @@ public class JdbcSqliteDAO extends JdbcBaseDAO { 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() }); + new String[] { formattedIdentifier(vo.getItemsManageTable()), vo.getColname(), vo.getColtype() }); logger.debug("JDBC::doCreateItemsTableIfNot sql={}", sql); try { Yank.execute(sql, null); @@ -114,7 +114,8 @@ public class JdbcSqliteDAO extends JdbcBaseDAO { 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") }); + new String[] { formattedIdentifier(storedVO.getTableName()), storedVO.getDbType(), + sqlTypes.get("tablePrimaryValue") }); Object[] params = { storedVO.getValue() }; logger.debug("JDBC::doStoreItemValue sql={} value='{}'", sql, storedVO.getValue()); try { @@ -129,7 +130,7 @@ public class JdbcSqliteDAO extends JdbcBaseDAO { ItemVO storedVO = storeItemValueProvider(item, itemState, vo); String sql = StringUtilsExt.replaceArrayMerge(sqlInsertItemValue, new String[] { "#tableName#", "#dbType#", "#tablePrimaryValue#" }, - new String[] { storedVO.getTableName(), storedVO.getDbType(), "?" }); + new String[] { formattedIdentifier(storedVO.getTableName()), storedVO.getDbType(), "?" }); java.sql.Timestamp timestamp = new java.sql.Timestamp(date.toInstant().toEpochMilli()); Object[] params = { timestamp, storedVO.getValue() }; logger.debug("JDBC::doStoreItemValue sql={} timestamp={} value='{}'", sql, timestamp, storedVO.getValue()); diff --git a/bundles/org.openhab.persistence.jdbc/src/main/java/org/openhab/persistence/jdbc/internal/db/JdbcTimescaledbDAO.java b/bundles/org.openhab.persistence.jdbc/src/main/java/org/openhab/persistence/jdbc/internal/db/JdbcTimescaledbDAO.java index 6c6bbba7ee1..ba7d89a5f58 100644 --- a/bundles/org.openhab.persistence.jdbc/src/main/java/org/openhab/persistence/jdbc/internal/db/JdbcTimescaledbDAO.java +++ b/bundles/org.openhab.persistence.jdbc/src/main/java/org/openhab/persistence/jdbc/internal/db/JdbcTimescaledbDAO.java @@ -36,8 +36,8 @@ import org.slf4j.LoggerFactory; public class JdbcTimescaledbDAO extends JdbcPostgresqlDAO { private final Logger logger = LoggerFactory.getLogger(JdbcTimescaledbDAO.class); - private final String sqlCreateHypertable = "SELECT created FROM create_hypertable('\"#tableName#\"', 'time')"; - private final String sqlGetItemTables = "SELECT hypertable_name as table_name FROM timescaledb_information.hypertables WHERE hypertable_name != '\"#itemsManageTable#\"'"; + private final String sqlCreateHypertable = "SELECT created FROM create_hypertable('#tableName#', 'time')"; + private final String sqlGetItemTables = "SELECT hypertable_name AS table_name FROM timescaledb_information.hypertables WHERE hypertable_name != '#itemsManageTable#'"; @Override public Properties getConnectionProperties() { @@ -57,7 +57,7 @@ public class JdbcTimescaledbDAO extends JdbcPostgresqlDAO { public void doCreateItemTable(ItemVO vo) throws JdbcSQLException { super.doCreateItemTable(vo); String sql = StringUtilsExt.replaceArrayMerge(this.sqlCreateHypertable, new String[] { "#tableName#" }, - new String[] { vo.getTableName() }); + new String[] { formattedIdentifier(vo.getTableName()) }); this.logger.debug("JDBC::doCreateItemTable sql={}", sql); try { Yank.queryScalar(sql, Boolean.class, null);