Browse Source

Update sql views in system patch applier

pull/14604/head
Andrii Landiak 8 months ago
parent
commit
12fbfc30f3
  1. 13
      application/src/main/java/org/thingsboard/server/service/install/SqlEntityDatabaseSchemaService.java
  2. 18
      application/src/main/java/org/thingsboard/server/service/system/SystemPatchApplier.java
  3. 93
      dao/src/main/resources/sql/schema-functions.sql
  4. 106
      dao/src/main/resources/sql/schema-views.sql
  5. 3
      dao/src/test/java/org/thingsboard/server/dao/PostgreSqlInitializer.java
  6. 4
      dao/src/test/java/org/thingsboard/server/dao/TimescaleSqlInitializer.java
  7. 6
      dao/src/test/java/org/thingsboard/server/dao/service/install/sql/EntitiesSchemaSqlTest.java

13
application/src/main/java/org/thingsboard/server/service/install/SqlEntityDatabaseSchemaService.java

@ -22,12 +22,13 @@ import org.springframework.stereotype.Service;
@Service
@Profile("install")
@Slf4j
public class SqlEntityDatabaseSchemaService extends SqlAbstractDatabaseSchemaService
implements EntityDatabaseSchemaService {
public class SqlEntityDatabaseSchemaService extends SqlAbstractDatabaseSchemaService implements EntityDatabaseSchemaService {
public static final String SCHEMA_ENTITIES_SQL = "schema-entities.sql";
public static final String SCHEMA_ENTITIES_IDX_SQL = "schema-entities-idx.sql";
public static final String SCHEMA_ENTITIES_IDX_PSQL_ADDON_SQL = "schema-entities-idx-psql-addon.sql";
public static final String SCHEMA_VIEWS_AND_FUNCTIONS_SQL = "schema-views-and-functions.sql";
public static final String SCHEMA_VIEWS_SQL = "schema-views.sql";
public static final String SCHEMA_FUNCTIONS_SQL = "schema-functions.sql";
public SqlEntityDatabaseSchemaService() {
super(SCHEMA_ENTITIES_SQL, SCHEMA_ENTITIES_IDX_SQL);
@ -49,8 +50,10 @@ public class SqlEntityDatabaseSchemaService extends SqlAbstractDatabaseSchemaSer
@Override
public void createOrUpdateViewsAndFunctions() throws Exception {
log.info("Installing SQL DataBase schema views and functions: " + SCHEMA_VIEWS_AND_FUNCTIONS_SQL);
executeQueryFromFile(SCHEMA_VIEWS_AND_FUNCTIONS_SQL);
log.info("Installing SQL DataBase schema views: " + SCHEMA_VIEWS_SQL);
executeQueryFromFile(SCHEMA_VIEWS_SQL);
log.info("Installing SQL DataBase schema functions: " + SCHEMA_FUNCTIONS_SQL);
executeQueryFromFile(SCHEMA_FUNCTIONS_SQL);
}
}

18
application/src/main/java/org/thingsboard/server/service/system/SystemPatchApplier.java

@ -16,7 +16,9 @@
package org.thingsboard.server.service.system;
import com.fasterxml.jackson.databind.JsonNode;
import com.google.common.base.Charsets;
import com.google.common.hash.Hashing;
import com.google.common.io.Resources;
import jakarta.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@ -34,6 +36,7 @@ import org.thingsboard.server.service.install.update.DefaultDataUpdateService;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
@ -53,6 +56,8 @@ import java.util.stream.Stream;
@RequiredArgsConstructor
public class SystemPatchApplier {
private static final String SCHEMA_VIEWS_SQL = "sql/schema-views.sql";
private static final long ADVISORY_LOCK_ID = 7536891047216478431L;
private final JdbcTemplate jdbcTemplate;
@ -86,6 +91,9 @@ public class SystemPatchApplier {
}
try {
updateSqlViews();
log.info("Updated sql database views");
int updated = updateWidgetTypes();
log.info("Updated {} widget types", updated);
@ -124,6 +132,16 @@ public class SystemPatchApplier {
&& packageVersion.maintenance == dbVersion.maintenance && packageVersion.patch > dbVersion.patch;
}
private void updateSqlViews() {
try {
URL schemaViewsUrl = Resources.getResource(SCHEMA_VIEWS_SQL);
String sql = Resources.toString(schemaViewsUrl, Charsets.UTF_8);
jdbcTemplate.execute(sql);
} catch (IOException e) {
throw new RuntimeException("Unable to update database views from schema-views.sql", e);
}
}
private int updateWidgetTypes() {
AtomicInteger updated = new AtomicInteger();
Path widgetTypesDir = installScripts.getWidgetTypesDir();

93
dao/src/main/resources/sql/schema-views-and-functions.sql → dao/src/main/resources/sql/schema-functions.sql

@ -14,84 +14,6 @@
-- limitations under the License.
--
DROP VIEW IF EXISTS device_info_active_attribute_view CASCADE;
CREATE OR REPLACE VIEW device_info_active_attribute_view AS
SELECT d.*
, c.title as customer_title
, COALESCE((c.additional_info::json->>'isPublic')::bool, FALSE) as customer_is_public
, d.type as device_profile_name
, COALESCE(da.bool_v, FALSE) as active
FROM device d
LEFT JOIN customer c ON c.id = d.customer_id
LEFT JOIN attribute_kv da ON da.entity_id = d.id AND da.attribute_type = 2 AND da.attribute_key = (select key_id from key_dictionary where key = 'active');
DROP VIEW IF EXISTS device_info_active_ts_view CASCADE;
CREATE OR REPLACE VIEW device_info_active_ts_view AS
SELECT d.*
, c.title as customer_title
, COALESCE((c.additional_info::json->>'isPublic')::bool, FALSE) as customer_is_public
, d.type as device_profile_name
, COALESCE(dt.bool_v, FALSE) as active
FROM device d
LEFT JOIN customer c ON c.id = d.customer_id
LEFT JOIN ts_kv_latest dt ON dt.entity_id = d.id and dt.key = (select key_id from key_dictionary where key = 'active');
DROP VIEW IF EXISTS device_info_view CASCADE;
CREATE OR REPLACE VIEW device_info_view AS SELECT * FROM device_info_active_attribute_view;
DROP VIEW IF EXISTS alarm_info CASCADE;
CREATE VIEW alarm_info AS
SELECT a.*,
(CASE WHEN a.acknowledged AND a.cleared THEN 'CLEARED_ACK'
WHEN NOT a.acknowledged AND a.cleared THEN 'CLEARED_UNACK'
WHEN a.acknowledged AND NOT a.cleared THEN 'ACTIVE_ACK'
WHEN NOT a.acknowledged AND NOT a.cleared THEN 'ACTIVE_UNACK' END) as status,
COALESCE(CASE WHEN a.originator_type = 0 THEN (select title from tenant where id = a.originator_id)
WHEN a.originator_type = 1 THEN (select title from customer where id = a.originator_id)
WHEN a.originator_type = 2 THEN (select email from tb_user where id = a.originator_id)
WHEN a.originator_type = 3 THEN (select title from dashboard where id = a.originator_id)
WHEN a.originator_type = 4 THEN (select name from asset where id = a.originator_id)
WHEN a.originator_type = 5 THEN (select name from device where id = a.originator_id)
WHEN a.originator_type = 9 THEN (select name from entity_view where id = a.originator_id)
WHEN a.originator_type = 13 THEN (select name from device_profile where id = a.originator_id)
WHEN a.originator_type = 14 THEN (select name from asset_profile where id = a.originator_id)
WHEN a.originator_type = 18 THEN (select name from edge where id = a.originator_id) END
, 'Deleted') originator_name,
COALESCE(CASE WHEN a.originator_type = 0 THEN (select title from tenant where id = a.originator_id)
WHEN a.originator_type = 1 THEN (select COALESCE(NULLIF(title, ''), email) from customer where id = a.originator_id)
WHEN a.originator_type = 2 THEN (select email from tb_user where id = a.originator_id)
WHEN a.originator_type = 3 THEN (select title from dashboard where id = a.originator_id)
WHEN a.originator_type = 4 THEN (select COALESCE(NULLIF(label, ''), name) from asset where id = a.originator_id)
WHEN a.originator_type = 5 THEN (select COALESCE(NULLIF(label, ''), name) from device where id = a.originator_id)
WHEN a.originator_type = 9 THEN (select name from entity_view where id = a.originator_id)
WHEN a.originator_type = 13 THEN (select name from device_profile where id = a.originator_id)
WHEN a.originator_type = 14 THEN (select name from asset_profile where id = a.originator_id)
WHEN a.originator_type = 18 THEN (select COALESCE(NULLIF(label, ''), name) from edge where id = a.originator_id) END
, 'Deleted') as originator_label,
u.first_name as assignee_first_name, u.last_name as assignee_last_name, u.email as assignee_email
FROM alarm a
LEFT JOIN tb_user u ON u.id = a.assignee_id;
DROP VIEW IF EXISTS edge_active_attribute_view CASCADE;
CREATE OR REPLACE VIEW edge_active_attribute_view AS
SELECT ee.id
, ee.created_time
, ee.additional_info
, ee.customer_id
, ee.root_rule_chain_id
, ee.type
, ee.name
, ee.label
, ee.routing_key
, ee.secret
, ee.tenant_id
, ee.version
FROM edge ee
JOIN attribute_kv ON ee.id = attribute_kv.entity_id
JOIN key_dictionary ON attribute_kv.attribute_key = key_dictionary.key_id
WHERE attribute_kv.bool_v = true AND key_dictionary.key = 'active'
ORDER BY ee.id;
CREATE OR REPLACE FUNCTION create_or_update_active_alarm(
t_id uuid, c_id uuid, a_id uuid, a_created_ts bigint,
a_o_id uuid, a_o_type integer, a_type varchar,
@ -306,19 +228,6 @@ BEGIN
END
$$;
DROP VIEW IF EXISTS widget_type_info_view CASCADE;
CREATE OR REPLACE VIEW widget_type_info_view AS
SELECT t.*,
COALESCE((t.descriptor::json->>'type')::text, '') as widget_type,
array_to_json(ARRAY(
SELECT json_build_object('id', wb.widgets_bundle_id, 'name', b.title)
FROM widgets_bundle_widget wb
JOIN widgets_bundle b ON wb.widgets_bundle_id = b.id
WHERE wb.widget_type_id = t.id
ORDER BY b.title
)) AS bundles
FROM widget_type t;
CREATE OR REPLACE PROCEDURE cleanup_timeseries_by_ttl(IN null_uuid uuid,
IN system_ttl bigint, INOUT deleted bigint)
LANGUAGE plpgsql AS
@ -389,4 +298,4 @@ BEGIN
FETCH tenant_cursor INTO tenant_id_record;
END LOOP;
END
$$;
$$;

106
dao/src/main/resources/sql/schema-views.sql

@ -0,0 +1,106 @@
--
-- Copyright © 2016-2025 The Thingsboard Authors
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
-- http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
--
DROP VIEW IF EXISTS device_info_active_attribute_view CASCADE;
CREATE OR REPLACE VIEW device_info_active_attribute_view AS
SELECT d.*
, c.title as customer_title
, COALESCE((c.additional_info::json->>'isPublic')::bool, FALSE) as customer_is_public
, d.type as device_profile_name
, COALESCE(da.bool_v, FALSE) as active
FROM device d
LEFT JOIN customer c ON c.id = d.customer_id
LEFT JOIN attribute_kv da ON da.entity_id = d.id AND da.attribute_type = 2 AND da.attribute_key = (select key_id from key_dictionary where key = 'active');
DROP VIEW IF EXISTS device_info_active_ts_view CASCADE;
CREATE OR REPLACE VIEW device_info_active_ts_view AS
SELECT d.*
, c.title as customer_title
, COALESCE((c.additional_info::json->>'isPublic')::bool, FALSE) as customer_is_public
, d.type as device_profile_name
, COALESCE(dt.bool_v, FALSE) as active
FROM device d
LEFT JOIN customer c ON c.id = d.customer_id
LEFT JOIN ts_kv_latest dt ON dt.entity_id = d.id and dt.key = (select key_id from key_dictionary where key = 'active');
DROP VIEW IF EXISTS device_info_view CASCADE;
CREATE OR REPLACE VIEW device_info_view AS SELECT * FROM device_info_active_attribute_view;
DROP VIEW IF EXISTS alarm_info CASCADE;
CREATE VIEW alarm_info AS
SELECT a.*,
(CASE WHEN a.acknowledged AND a.cleared THEN 'CLEARED_ACK'
WHEN NOT a.acknowledged AND a.cleared THEN 'CLEARED_UNACK'
WHEN a.acknowledged AND NOT a.cleared THEN 'ACTIVE_ACK'
WHEN NOT a.acknowledged AND NOT a.cleared THEN 'ACTIVE_UNACK' END) as status,
COALESCE(CASE WHEN a.originator_type = 0 THEN (select title from tenant where id = a.originator_id)
WHEN a.originator_type = 1 THEN (select title from customer where id = a.originator_id)
WHEN a.originator_type = 2 THEN (select email from tb_user where id = a.originator_id)
WHEN a.originator_type = 3 THEN (select title from dashboard where id = a.originator_id)
WHEN a.originator_type = 4 THEN (select name from asset where id = a.originator_id)
WHEN a.originator_type = 5 THEN (select name from device where id = a.originator_id)
WHEN a.originator_type = 9 THEN (select name from entity_view where id = a.originator_id)
WHEN a.originator_type = 13 THEN (select name from device_profile where id = a.originator_id)
WHEN a.originator_type = 14 THEN (select name from asset_profile where id = a.originator_id)
WHEN a.originator_type = 18 THEN (select name from edge where id = a.originator_id) END
, 'Deleted') originator_name,
COALESCE(CASE WHEN a.originator_type = 0 THEN (select title from tenant where id = a.originator_id)
WHEN a.originator_type = 1 THEN (select COALESCE(NULLIF(title, ''), email) from customer where id = a.originator_id)
WHEN a.originator_type = 2 THEN (select email from tb_user where id = a.originator_id)
WHEN a.originator_type = 3 THEN (select title from dashboard where id = a.originator_id)
WHEN a.originator_type = 4 THEN (select COALESCE(NULLIF(label, ''), name) from asset where id = a.originator_id)
WHEN a.originator_type = 5 THEN (select COALESCE(NULLIF(label, ''), name) from device where id = a.originator_id)
WHEN a.originator_type = 9 THEN (select name from entity_view where id = a.originator_id)
WHEN a.originator_type = 13 THEN (select name from device_profile where id = a.originator_id)
WHEN a.originator_type = 14 THEN (select name from asset_profile where id = a.originator_id)
WHEN a.originator_type = 18 THEN (select COALESCE(NULLIF(label, ''), name) from edge where id = a.originator_id) END
, 'Deleted') as originator_label,
u.first_name as assignee_first_name, u.last_name as assignee_last_name, u.email as assignee_email
FROM alarm a
LEFT JOIN tb_user u ON u.id = a.assignee_id;
DROP VIEW IF EXISTS edge_active_attribute_view CASCADE;
CREATE OR REPLACE VIEW edge_active_attribute_view AS
SELECT ee.id
, ee.created_time
, ee.additional_info
, ee.customer_id
, ee.root_rule_chain_id
, ee.type
, ee.name
, ee.label
, ee.routing_key
, ee.secret
, ee.tenant_id
, ee.version
FROM edge ee
JOIN attribute_kv ON ee.id = attribute_kv.entity_id
JOIN key_dictionary ON attribute_kv.attribute_key = key_dictionary.key_id
WHERE attribute_kv.bool_v = true AND key_dictionary.key = 'active'
ORDER BY ee.id;
DROP VIEW IF EXISTS widget_type_info_view CASCADE;
CREATE OR REPLACE VIEW widget_type_info_view AS
SELECT t.*,
COALESCE((t.descriptor::json->>'type')::text, '') as widget_type,
array_to_json(ARRAY(
SELECT json_build_object('id', wb.widgets_bundle_id, 'name', b.title)
FROM widgets_bundle_widget wb
JOIN widgets_bundle b ON wb.widgets_bundle_id = b.id
WHERE wb.widget_type_id = t.id
ORDER BY b.title
)) AS bundles
FROM widget_type t;

3
dao/src/test/java/org/thingsboard/server/dao/PostgreSqlInitializer.java

@ -33,7 +33,8 @@ public class PostgreSqlInitializer {
"sql/schema-entities.sql",
"sql/schema-entities-idx.sql",
"sql/schema-entities-idx-psql-addon.sql",
"sql/schema-views-and-functions.sql",
"sql/schema-views.sql",
"sql/schema-functions.sql",
"sql/system-data.sql",
"sql/system-test-psql.sql");
private static final String dropAllTablesSqlFile = "sql/psql/drop-all-tables.sql";

4
dao/src/test/java/org/thingsboard/server/dao/TimescaleSqlInitializer.java

@ -33,7 +33,8 @@ public class TimescaleSqlInitializer {
"sql/schema-entities.sql",
"sql/schema-entities-idx.sql",
"sql/schema-entities-idx-psql-addon.sql",
"sql/schema-views-and-functions.sql",
"sql/schema-views.sql",
"sql/schema-functions.sql",
"sql/system-data.sql",
"sql/system-test-psql.sql");
private static final String dropAllTablesSqlFile = "sql/psql/drop-all-tables.sql";
@ -63,4 +64,5 @@ public class TimescaleSqlInitializer {
throw new RuntimeException("Unable to clean up the Timescale database. Reason: " + e.getMessage(), e);
}
}
}

6
dao/src/test/java/org/thingsboard/server/dao/service/install/sql/EntitiesSchemaSqlTest.java

@ -35,8 +35,10 @@ public class EntitiesSchemaSqlTest extends AbstractServiceTest {
@Value("${classpath:sql/schema-entities.sql}")
private Path installEntitiesPath;
@Value("${classpath:sql/schema-views-and-functions.sql}")
@Value("${classpath:sql/schema-views.sql}")
private Path installViewsPath;
@Value("${classpath:sql/schema-functions.sql}")
private Path installFunctionsPath;
@Autowired
private JdbcTemplate jdbcTemplate;
@ -45,10 +47,12 @@ public class EntitiesSchemaSqlTest extends AbstractServiceTest {
public void testRepeatedInstall() throws IOException {
String entitiesScript = Files.readString(installEntitiesPath);
String viewsScript = Files.readString(installViewsPath);
String functionsScript = Files.readString(installFunctionsPath);
try {
for (int i = 1; i <= 2; i++) {
jdbcTemplate.execute(entitiesScript);
jdbcTemplate.execute(viewsScript);
jdbcTemplate.execute(functionsScript);
}
} catch (Exception e) {
Assertions.fail("Failed to execute reinstall", e);

Loading…
Cancel
Save