diff --git a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java index 8544f60ca0..fedbd48503 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java @@ -20,7 +20,6 @@ import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.media.ArraySchema; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; @@ -31,7 +30,6 @@ import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; @@ -490,7 +488,7 @@ public class DeviceController extends BaseController { @RequestMapping(value = "/devices", params = {"deviceIds"}, method = RequestMethod.GET) @ResponseBody public List getDevicesByIds( - @Parameter(description = "A list of devices ids, separated by comma ','", array = @ArraySchema(schema = @Schema(type = "string"))) + @Parameter(description = "A list of devices ids, separated by comma ','") @RequestParam("deviceIds") String[] strDeviceIds) throws ThingsboardException, ExecutionException, InterruptedException { checkArrayParameter("deviceIds", strDeviceIds); SecurityUser user = getCurrentUser(); diff --git a/dao/src/main/java/org/thingsboard/server/dao/AbstractSequenceInsertRepository.java b/dao/src/main/java/org/thingsboard/server/dao/AbstractVersionedInsertRepository.java similarity index 93% rename from dao/src/main/java/org/thingsboard/server/dao/AbstractSequenceInsertRepository.java rename to dao/src/main/java/org/thingsboard/server/dao/AbstractVersionedInsertRepository.java index 0808f5341f..cb92ca5e1f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/AbstractSequenceInsertRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/AbstractVersionedInsertRepository.java @@ -29,9 +29,9 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; -public abstract class AbstractSequenceInsertRepository extends AbstractInsertRepository { +public abstract class AbstractVersionedInsertRepository extends AbstractInsertRepository { - public static final String SEQ_NUMBER = "seq_number"; + public static final String VERSION_COLUMN = "version"; public List saveOrUpdate(List entities) { return transactionTemplate.execute(status -> { @@ -54,7 +54,7 @@ public abstract class AbstractSequenceInsertRepository extends AbstractInsert seqNumbers.add(0L); toInsertIndexes.add(i); } else { - seqNumbers.add((Long) seqNumbersList.get(keyHolderIndex).get(SEQ_NUMBER)); + seqNumbers.add((Long) seqNumbersList.get(keyHolderIndex).get(VERSION_COLUMN)); keyHolderIndex++; } } @@ -68,7 +68,7 @@ public abstract class AbstractSequenceInsertRepository extends AbstractInsert seqNumbersList = keyHolder.getKeyList(); for (int i = 0; i < seqNumbersList.size(); i++) { - seqNumbers.set(toInsertIndexes.get(i), (Long) seqNumbersList.get(i).get(SEQ_NUMBER)); + seqNumbers.set(toInsertIndexes.get(i), (Long) seqNumbersList.get(i).get(VERSION_COLUMN)); } return seqNumbers; @@ -113,7 +113,7 @@ public abstract class AbstractSequenceInsertRepository extends AbstractInsert private record SequencePreparedStatementCreator(String sql) implements PreparedStatementCreator, SqlProvider { - private static final String[] COLUMNS = {SEQ_NUMBER}; + private static final String[] COLUMNS = {VERSION_COLUMN}; @Override public PreparedStatement createPreparedStatement(Connection con) throws SQLException { diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/AttributeKvInsertRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/AttributeKvInsertRepository.java index 9e188d39b1..586ee3c0ca 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/AttributeKvInsertRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/AttributeKvInsertRepository.java @@ -17,7 +17,7 @@ package org.thingsboard.server.dao.sql.attributes; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; -import org.thingsboard.server.dao.AbstractSequenceInsertRepository; +import org.thingsboard.server.dao.AbstractVersionedInsertRepository; import org.thingsboard.server.dao.model.sql.AttributeKvEntity; import org.thingsboard.server.dao.util.SqlDao; @@ -29,16 +29,16 @@ import java.util.List; @Repository @Transactional @SqlDao -public class AttributeKvInsertRepository extends AbstractSequenceInsertRepository { +public class AttributeKvInsertRepository extends AbstractVersionedInsertRepository { - private static final String BATCH_UPDATE = "UPDATE attribute_kv SET str_v = ?, long_v = ?, dbl_v = ?, bool_v = ?, json_v = cast(? AS json), last_update_ts = ?, seq_number = nextval('attribute_kv_latest_seq') " + - "WHERE entity_id = ? and attribute_type =? and attribute_key = ? RETURNING seq_number;"; + private static final String BATCH_UPDATE = "UPDATE attribute_kv SET str_v = ?, long_v = ?, dbl_v = ?, bool_v = ?, json_v = cast(? AS json), last_update_ts = ?, version = nextval('attribute_kv_version_seq') " + + "WHERE entity_id = ? and attribute_type =? and attribute_key = ? RETURNING version;"; private static final String INSERT_OR_UPDATE = - "INSERT INTO attribute_kv (entity_id, attribute_type, attribute_key, str_v, long_v, dbl_v, bool_v, json_v, last_update_ts, seq_number) " + - "VALUES(?, ?, ?, ?, ?, ?, ?, cast(? AS json), ?, nextval('attribute_kv_latest_seq')) " + + "INSERT INTO attribute_kv (entity_id, attribute_type, attribute_key, str_v, long_v, dbl_v, bool_v, json_v, last_update_ts, version) " + + "VALUES(?, ?, ?, ?, ?, ?, ?, cast(? AS json), ?, nextval('attribute_kv_version_seq')) " + "ON CONFLICT (entity_id, attribute_type, attribute_key) " + - "DO UPDATE SET str_v = ?, long_v = ?, dbl_v = ?, bool_v = ?, json_v = cast(? AS json), last_update_ts = ?, seq_number = nextval('attribute_kv_latest_seq') RETURNING seq_number;"; + "DO UPDATE SET str_v = ?, long_v = ?, dbl_v = ?, bool_v = ?, json_v = cast(? AS json), last_update_ts = ?, version = nextval('attribute_kv_version_seq') RETURNING version;"; @Override protected void setOnBatchUpdateValues(PreparedStatement ps, int i, List entities) throws SQLException { diff --git a/dao/src/main/java/org/thingsboard/server/dao/sqlts/insert/latest/sql/SqlLatestInsertTsRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sqlts/insert/latest/sql/SqlLatestInsertTsRepository.java index 5b953dfe59..323eec0851 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sqlts/insert/latest/sql/SqlLatestInsertTsRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sqlts/insert/latest/sql/SqlLatestInsertTsRepository.java @@ -19,7 +19,7 @@ import jakarta.annotation.PostConstruct; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; -import org.thingsboard.server.dao.AbstractSequenceInsertRepository; +import org.thingsboard.server.dao.AbstractVersionedInsertRepository; import org.thingsboard.server.dao.model.sqlts.latest.TsKvLatestEntity; import org.thingsboard.server.dao.sqlts.insert.latest.InsertLatestTsRepository; import org.thingsboard.server.dao.util.SqlDao; @@ -35,23 +35,23 @@ import java.util.List; @Repository @Transactional @SqlDao -public class SqlLatestInsertTsRepository extends AbstractSequenceInsertRepository implements InsertLatestTsRepository { +public class SqlLatestInsertTsRepository extends AbstractVersionedInsertRepository implements InsertLatestTsRepository { @Value("${sql.ts_latest.update_by_latest_ts:true}") private Boolean updateByLatestTs; private static final String BATCH_UPDATE = - "UPDATE ts_kv_latest SET ts = ?, bool_v = ?, str_v = ?, long_v = ?, dbl_v = ?, json_v = cast(? AS json), seq_number = nextval('ts_kv_latest_seq') WHERE entity_id = ? AND key = ?"; + "UPDATE ts_kv_latest SET ts = ?, bool_v = ?, str_v = ?, long_v = ?, dbl_v = ?, json_v = cast(? AS json), version = nextval('ts_kv_latest_version_seq') WHERE entity_id = ? AND key = ?"; private static final String INSERT_OR_UPDATE = - "INSERT INTO ts_kv_latest (entity_id, key, ts, bool_v, str_v, long_v, dbl_v, json_v, seq_number) VALUES(?, ?, ?, ?, ?, ?, ?, cast(? AS json), nextval('ts_kv_latest_seq')) " + - "ON CONFLICT (entity_id, key) DO UPDATE SET ts = ?, bool_v = ?, str_v = ?, long_v = ?, dbl_v = ?, json_v = cast(? AS json), seq_number = nextval('ts_kv_latest_seq')"; + "INSERT INTO ts_kv_latest (entity_id, key, ts, bool_v, str_v, long_v, dbl_v, json_v, version) VALUES(?, ?, ?, ?, ?, ?, ?, cast(? AS json), nextval('ts_kv_latest_version_seq')) " + + "ON CONFLICT (entity_id, key) DO UPDATE SET ts = ?, bool_v = ?, str_v = ?, long_v = ?, dbl_v = ?, json_v = cast(? AS json), version = nextval('ts_kv_latest_version_seq')"; private static final String BATCH_UPDATE_BY_LATEST_TS = BATCH_UPDATE + " AND ts_kv_latest.ts <= ?"; private static final String INSERT_OR_UPDATE_BY_LATEST_TS = INSERT_OR_UPDATE + " WHERE ts_kv_latest.ts <= ?"; - private static final String RETURNING = " RETURNING seq_number"; + private static final String RETURNING = " RETURNING version"; private String batchUpdateQuery; private String insertOrUpdateQuery; diff --git a/dao/src/main/resources/sql/schema-entities.sql b/dao/src/main/resources/sql/schema-entities.sql index a26262970c..45deaf931d 100644 --- a/dao/src/main/resources/sql/schema-entities.sql +++ b/dao/src/main/resources/sql/schema-entities.sql @@ -102,7 +102,7 @@ CREATE TABLE IF NOT EXISTS audit_log ( action_failure_details varchar(1000000) ) PARTITION BY RANGE (created_time); -CREATE SEQUENCE IF NOT EXISTS attribute_kv_latest_seq cache 1000; +CREATE SEQUENCE IF NOT EXISTS attribute_kv_version_seq cache 1000; CREATE TABLE IF NOT EXISTS attribute_kv ( entity_id uuid, @@ -114,7 +114,7 @@ CREATE TABLE IF NOT EXISTS attribute_kv ( dbl_v double precision, json_v json, last_update_ts bigint, - seq_number bigint, + version bigint, CONSTRAINT attribute_kv_pkey PRIMARY KEY (entity_id, attribute_type, attribute_key) ); @@ -541,7 +541,7 @@ CREATE TABLE IF NOT EXISTS entity_view ( CONSTRAINT entity_view_external_id_unq_key UNIQUE (tenant_id, external_id) ); -CREATE SEQUENCE IF NOT EXISTS ts_kv_latest_seq cache 1000; +CREATE SEQUENCE IF NOT EXISTS ts_kv_latest_version_seq cache 1000; CREATE TABLE IF NOT EXISTS ts_kv_latest ( @@ -553,7 +553,7 @@ CREATE TABLE IF NOT EXISTS ts_kv_latest long_v bigint, dbl_v double precision, json_v json, - seq_number bigint, + version bigint, CONSTRAINT ts_kv_latest_pkey PRIMARY KEY (entity_id, key) ); diff --git a/dao/src/main/resources/sql/schema-timescale.sql b/dao/src/main/resources/sql/schema-timescale.sql index ecd8aa1e9d..e5d44cb24a 100644 --- a/dao/src/main/resources/sql/schema-timescale.sql +++ b/dao/src/main/resources/sql/schema-timescale.sql @@ -34,6 +34,8 @@ CREATE TABLE IF NOT EXISTS key_dictionary ( CONSTRAINT key_dictionary_id_pkey PRIMARY KEY (key) ); +CREATE SEQUENCE IF NOT EXISTS ts_kv_latest_version_seq cache 1000; + CREATE TABLE IF NOT EXISTS ts_kv_latest ( entity_id uuid NOT NULL, key int NOT NULL, @@ -43,6 +45,7 @@ CREATE TABLE IF NOT EXISTS ts_kv_latest ( long_v bigint, dbl_v double precision, json_v json, + version bigint, CONSTRAINT ts_kv_latest_pkey PRIMARY KEY (entity_id, key) ); diff --git a/dao/src/main/resources/sql/schema-ts-latest-psql.sql b/dao/src/main/resources/sql/schema-ts-latest-psql.sql index c6701315b4..adae100823 100644 --- a/dao/src/main/resources/sql/schema-ts-latest-psql.sql +++ b/dao/src/main/resources/sql/schema-ts-latest-psql.sql @@ -14,6 +14,8 @@ -- limitations under the License. -- +CREATE SEQUENCE IF NOT EXISTS ts_kv_latest_version_seq cache 1000; + CREATE TABLE IF NOT EXISTS ts_kv_latest ( entity_id uuid NOT NULL, @@ -24,12 +26,6 @@ CREATE TABLE IF NOT EXISTS ts_kv_latest long_v bigint, dbl_v double precision, json_v json, + version bigint, CONSTRAINT ts_kv_latest_pkey PRIMARY KEY (entity_id, key) -); - -CREATE TABLE IF NOT EXISTS ts_kv_dictionary -( - key varchar(255) NOT NULL, - key_id serial UNIQUE, - CONSTRAINT ts_key_id_pkey PRIMARY KEY (key) ); \ No newline at end of file diff --git a/dao/src/test/resources/sql/psql/drop-all-tables.sql b/dao/src/test/resources/sql/psql/drop-all-tables.sql index 3b1b37242f..c731e239af 100644 --- a/dao/src/test/resources/sql/psql/drop-all-tables.sql +++ b/dao/src/test/resources/sql/psql/drop-all-tables.sql @@ -23,6 +23,7 @@ DROP TABLE IF EXISTS alarm_type; DROP TABLE IF EXISTS asset; DROP TABLE IF EXISTS audit_log; DROP TABLE IF EXISTS attribute_kv; +DROP SEQUENCE IF EXISTS attribute_kv_version_seq; DROP TABLE IF EXISTS component_descriptor; DROP TABLE IF EXISTS customer; DROP TABLE IF EXISTS device; @@ -37,7 +38,7 @@ DROP TABLE IF EXISTS tenant; DROP TABLE IF EXISTS ts_kv; DROP TABLE IF EXISTS ts_kv_latest; DROP TABLE IF EXISTS ts_kv_dictionary; -DROP SEQUENCE IF EXISTS ts_kv_latest_seq; +DROP SEQUENCE IF EXISTS ts_kv_latest_version_seq; DROP TABLE IF EXISTS user_credentials; DROP TABLE IF EXISTS widgets_bundle_widget; DROP TABLE IF EXISTS widget_type;