- {{data.alias}}
- {{data.name ? data.name : '-'}}
- {{data.host}}
- {{data.protocol}}
- {{data.type}}
- {{data.username ? data.username : '-'}}
- {{data.version ? data.version : '-'}}
+ {{data.alias}}
+ {{data.name ? data.name : '-'}}
+ {{data.host}}
+ {{data.protocol}}
+
+
+
+ {{data.username ? data.username : '-'}}
+ {{data.version ? data.version : '-'}}
@@ -112,8 +114,8 @@
-
-
+
+
diff --git a/src/renderer/app/pages/management/datasource/datasource.module.ts b/src/renderer/app/pages/management/datasource/datasource.module.ts
index 5a764aa7..7d630fdd 100644
--- a/src/renderer/app/pages/management/datasource/datasource.module.ts
+++ b/src/renderer/app/pages/management/datasource/datasource.module.ts
@@ -27,6 +27,10 @@ import {
DatasourcePostgresqlComponent
} from "@renderer/components/datasource/postgresql/datasource.postgresql.component";
import { DatasourceDruidComponent } from "@renderer/components/datasource/druid/datasource.druid.component";
+import {
+ DatasourceElasticSearchComponent
+} from "@renderer/components/datasource/elasticsearch/datasource.elasticsearch.component";
+import { DatasourceHologresComponent } from "@renderer/components/datasource/hologres/datasource.hologres.component";
const DATASOURCE_ROUTES: Routes = [
{path: '', component: DatasourceComponent}
@@ -53,7 +57,9 @@ const DATASOURCE_ROUTES: Routes = [
DatasourceMysqlComponent,
DatasourceDeleteComponent,
DatasourcePostgresqlComponent,
- DatasourceDruidComponent
+ DatasourceDruidComponent,
+ DatasourceElasticSearchComponent,
+ DatasourceHologresComponent
],
providers: [
DatasourceService,
diff --git a/src/renderer/app/pages/management/metadata/metadata.component.html b/src/renderer/app/pages/management/metadata/metadata.component.html
index ba4bebf2..b4d13038 100644
--- a/src/renderer/app/pages/management/metadata/metadata.component.html
+++ b/src/renderer/app/pages/management/metadata/metadata.component.html
@@ -43,7 +43,7 @@
style="margin-bottom: 10px;">
-
+
diff --git a/src/renderer/app/pages/management/metadata/metadata.component.ts b/src/renderer/app/pages/management/metadata/metadata.component.ts
index 5c54e96b..7ab85745 100644
--- a/src/renderer/app/pages/management/metadata/metadata.component.ts
+++ b/src/renderer/app/pages/management/metadata/metadata.component.ts
@@ -13,6 +13,7 @@ import { NzContextMenuService, NzDropdownMenuComponent } from 'ng-zorro-antd/dro
import { NzMessageService } from 'ng-zorro-antd/message';
import { NzFormatEmitEvent } from 'ng-zorro-antd/tree';
import { DatabaseEnum } from "@renderer/enum/database.enum";
+import { StringUtils } from "@renderer/utils/string.utils";
@Component({
selector: 'app-management-metadata',
@@ -24,6 +25,8 @@ export class MetadataComponent extends BaseComponent implements OnInit {
selectNode: any;
selectMenu: MenuModel;
rootNode: any;
+ selectDatabase: string;
+ selectTable: string;
switchType = TypeEnum.disk;
outerHeight: number;
contextMenus: MenuModel[];
@@ -56,10 +59,11 @@ export class MetadataComponent extends BaseComponent implements OnInit {
configModel.title = k.alias;
configModel.type = TypeEnum.disk;
configModel.disabled = k.status ? false : true;
- if (k.type === DatabaseEnum.presto || k.type === DatabaseEnum.trino || k.type === DatabaseEnum.druid) {
+ if (k.type === DatabaseEnum.presto || k.type === DatabaseEnum.trino || k.type === DatabaseEnum.druid
+ || k.type === DatabaseEnum.elasticsearch) {
configModel.isLeaf = true;
}
- if (k.type === DatabaseEnum.druid) {
+ if (k.type === DatabaseEnum.druid || k.type === DatabaseEnum.elasticsearch) {
configModel.disabled = true;
}
if (configModel.disabled) {
@@ -184,6 +188,13 @@ export class MetadataComponent extends BaseComponent implements OnInit {
const dataSource = await this.dataSourceService.getByAliasAsync(this.rootNode.value);
this.rootNode['sourceType'] = dataSource.type;
request.config = dataSource;
+
+ if (dataSource.type === DatabaseEnum.postgresql) {
+ if (StringUtils.isNotEmpty(this.selectDatabase)) {
+ request.config.database = this.selectDatabase;
+ }
+ }
+
if (dataSource.type === DatabaseEnum.trino || dataSource.type === DatabaseEnum.presto) {
this.items = [];
this.loading.button = false;
@@ -214,6 +225,14 @@ export class MetadataComponent extends BaseComponent implements OnInit {
if (node?.getChildren().length === 0 && node?.isExpanded) {
const request = new RequestModel();
request.config = await this.dataSourceService.getByAliasAsync(this.rootNode.value);
+
+ if (request.config.type === DatabaseEnum.postgresql) {
+ if (StringUtils.isNotEmpty(this.selectDatabase)) {
+ request.config.database = this.selectDatabase;
+ }
+ originNode.database = this.selectTable;
+ }
+
this.metadataService.getChild(request, originNode).then(response => {
if (response.status) {
node.addChildren(TreeUtils.builderTreeNode(response.data.columns, originNode.type));
@@ -234,12 +253,16 @@ export class MetadataComponent extends BaseComponent implements OnInit {
switch (node.level) {
case 0:
this.rootNode = node.origin;
+ this.selectDatabase = '';
+ this.selectTable = '';
break;
case 1:
node.origin.type = TypeEnum.database;
+ this.selectDatabase = node.origin.key;
break;
case 2:
node.origin.type = TypeEnum.table;
+ this.selectTable = node.origin.key;
break;
case 3:
node.origin.type = TypeEnum.column;
diff --git a/src/renderer/app/pages/management/metadata/metadata.module.ts b/src/renderer/app/pages/management/metadata/metadata.module.ts
index 37bcd8bc..13a42746 100644
--- a/src/renderer/app/pages/management/metadata/metadata.module.ts
+++ b/src/renderer/app/pages/management/metadata/metadata.module.ts
@@ -42,6 +42,7 @@ import { ColumnCreateComponent } from '@renderer/components/column/create/column
import { CommentColumnComponent } from '@renderer/components/column/comment/column.comment.component';
import { DatabaseFilterComponent } from '@renderer/components/database/filter/database.filter.component';
import { TableFilterComponent } from '@renderer/components/table/filter/table.filter.component';
+import { CollationService } from "@renderer/services/management/collation.service";
const MANAGEMENT_METADATA_ROUTES: Routes = [
{path: '', component: MetadataComponent}
@@ -96,7 +97,8 @@ const MANAGEMENT_METADATA_ROUTES: Routes = [
EditorService,
TableService,
ColumnService,
- DatabaseService
+ DatabaseService,
+ CollationService
]
})
export class MetadataModule {
diff --git a/src/renderer/app/pages/monitor/connection/monitor.connection.component.ts b/src/renderer/app/pages/monitor/connection/monitor.connection.component.ts
index 8d2e259c..51d0c762 100644
--- a/src/renderer/app/pages/monitor/connection/monitor.connection.component.ts
+++ b/src/renderer/app/pages/monitor/connection/monitor.connection.component.ts
@@ -30,7 +30,8 @@ export class MonitorConnectionComponent extends BaseComponent implements OnDestr
super();
this.datasourceService.getAll().then(response => {
this.dataSources = response.map(item => {
- if (item.type === DatabaseEnum.postgresql || item.type === DatabaseEnum.druid) {
+ if (item.type === DatabaseEnum.postgresql || item.type === DatabaseEnum.druid || item.type === DatabaseEnum.elasticsearch
+ || item.type === DatabaseEnum.hologres) {
item.status = false;
}
return item;
diff --git a/src/renderer/app/pages/monitor/mutations/monitor.mutations.component.ts b/src/renderer/app/pages/monitor/mutations/monitor.mutations.component.ts
index 79ce5051..50d462d2 100644
--- a/src/renderer/app/pages/monitor/mutations/monitor.mutations.component.ts
+++ b/src/renderer/app/pages/monitor/mutations/monitor.mutations.component.ts
@@ -35,7 +35,9 @@ export class MonitorMutationsComponent extends BaseComponent implements OnDestro
if (item.type === DatabaseEnum.trino || item.type === DatabaseEnum.presto
|| item.type === DatabaseEnum.mysql
|| item.type === DatabaseEnum.postgresql
- || item.type === DatabaseEnum.druid) {
+ || item.type === DatabaseEnum.druid
+ || item.type === DatabaseEnum.elasticsearch
+ || item.type === DatabaseEnum.hologres) {
item.status = false;
}
return item;
diff --git a/src/renderer/app/pages/monitor/processor/monitor.processor.component.ts b/src/renderer/app/pages/monitor/processor/monitor.processor.component.ts
index 9185edab..c0e57f7a 100644
--- a/src/renderer/app/pages/monitor/processor/monitor.processor.component.ts
+++ b/src/renderer/app/pages/monitor/processor/monitor.processor.component.ts
@@ -36,7 +36,9 @@ export class MonitorProcessorComponent extends BaseComponent implements OnDestro
super();
this.datasourceService.getAll().then(response => {
this.dataSources = response.map(item => {
- if (item.type === DatabaseEnum.postgresql || item.type === DatabaseEnum.druid) {
+ if (item.type === DatabaseEnum.postgresql || item.type === DatabaseEnum.druid
+ || item.type === DatabaseEnum.elasticsearch
+ || item.type === DatabaseEnum.hologres) {
item.status = false;
}
return item;
diff --git a/src/renderer/app/pages/monitor/query/monitor.query.component.ts b/src/renderer/app/pages/monitor/query/monitor.query.component.ts
index 4c8ab74b..3ed6a9ef 100644
--- a/src/renderer/app/pages/monitor/query/monitor.query.component.ts
+++ b/src/renderer/app/pages/monitor/query/monitor.query.component.ts
@@ -29,7 +29,9 @@ export class MonitorQueryComponent extends BaseComponent {
super();
this.datasourceService.getAll().then(response => {
this.dataSources = response.map(item => {
- if (item.type === DatabaseEnum.mysql || item.type === DatabaseEnum.postgresql || item.type === DatabaseEnum.druid) {
+ if (item.type === DatabaseEnum.mysql || item.type === DatabaseEnum.postgresql || item.type === DatabaseEnum.druid
+ || item.type === DatabaseEnum.elasticsearch
+ || item.type === DatabaseEnum.hologres) {
item.status = false;
}
return item;
diff --git a/src/renderer/app/pages/query/beta/query.beta.component.html b/src/renderer/app/pages/query/beta/query.beta.component.html
index e272d235..65429d43 100644
--- a/src/renderer/app/pages/query/beta/query.beta.component.html
+++ b/src/renderer/app/pages/query/beta/query.beta.component.html
@@ -44,6 +44,20 @@
{{'common.cancel'|translate}}
+ 0 && applyResult?.statistics" style="float: right;">
+
+ {{applyResult.columns.length}}
+
+
+ {{applyResult.statistics.elapsed}}
+
+
+ {{applyResult.statistics.rows_read}}
+
+
+ {{applyResult.statistics.bytes_read}}
+
+
diff --git a/src/renderer/app/pages/query/beta/query.beta.component.ts b/src/renderer/app/pages/query/beta/query.beta.component.ts
index 2ed01e45..7b9d10a2 100644
--- a/src/renderer/app/pages/query/beta/query.beta.component.ts
+++ b/src/renderer/app/pages/query/beta/query.beta.component.ts
@@ -14,12 +14,10 @@ import { ColumnService } from "@renderer/services/management/column.service";
import { DatabaseService } from "@renderer/services/management/database.service";
import { DatasourceService } from "@renderer/services/management/datasource.service";
import { TableService } from "@renderer/services/management/table.service";
-import { QueryService } from "@renderer/services/query/query.service";
import { ObjectUtils } from "@renderer/utils/object.utils";
import { SqlUtils } from "@renderer/utils/sql.utils";
import { StringUtils } from "@renderer/utils/string.utils";
import { TreeUtils } from "@renderer/utils/tree.utils";
-import { NzContextMenuService } from "ng-zorro-antd/dropdown";
import { NzModalService } from "ng-zorro-antd/modal";
import { NzFormatEmitEvent } from "ng-zorro-antd/tree";
import { DefaultConfig } from "ngx-easy-table";
@@ -64,6 +62,7 @@ export class QueryBetaComponent implements AfterViewInit, AfterViewChecked {
configuration: {...DefaultConfig},
headers: [],
columns: [],
+ statistics: null,
message: null,
status: false,
height: 0,
@@ -208,7 +207,7 @@ export class QueryBetaComponent implements AfterViewInit, AfterViewChecked {
configure.table = this.selectData.table;
configure.value = this.selectData.currentValue.key;
configure.request = this.requestConfig;
- this.menuCommonService.applySqlForOperation(command, configure);
+ this.applyEditor.value = this.menuCommonService.applySqlForOperation(command, configure);
}
handlerExecute() {
@@ -233,6 +232,7 @@ export class QueryBetaComponent implements AfterViewInit, AfterViewChecked {
this.applyResult.headers.push({key: column.name, title: column.name});
});
this.applyResult.columns = response.data.columns;
+ this.applyResult.statistics = response.data.statistics;
if (this.applyResult.headers.length > 0) {
this.applyResult.columns = response.data.columns;
if (this.applyResult.columns.length > 0) {
diff --git a/src/renderer/app/pages/query/beta/query.beta.module.ts b/src/renderer/app/pages/query/beta/query.beta.module.ts
index ce1017a1..f0c0acaf 100644
--- a/src/renderer/app/pages/query/beta/query.beta.module.ts
+++ b/src/renderer/app/pages/query/beta/query.beta.module.ts
@@ -21,6 +21,7 @@ import { AceModule } from "ngx-ace-wrapper";
import { TableModule } from "ngx-easy-table";
import { EllipsisModule } from "ngx-ellipsis";
import { QueryBetaComponent } from './query.beta.component';
+import { ElasticsearchPlugin } from "@renderer/plugin/elasticsearch.plugin";
const QUERY_ROUTES: Routes = [
{path: '', component: QueryBetaComponent}
@@ -54,7 +55,8 @@ const QUERY_ROUTES: Routes = [
EditorService,
QueryService,
PluginFactory,
- { provide: PluginToken, useClass: ClickHousePlugin, multi: true }
+ {provide: PluginToken, useClass: ClickHousePlugin, multi: true},
+ {provide: PluginToken, useClass: ElasticsearchPlugin, multi: true}
]
})
export class QueryBetaModule {
diff --git a/src/renderer/app/pages/query/query/query.module.ts b/src/renderer/app/pages/query/query/query.module.ts
index 5edbe4ef..9de5bd62 100644
--- a/src/renderer/app/pages/query/query/query.module.ts
+++ b/src/renderer/app/pages/query/query/query.module.ts
@@ -18,6 +18,10 @@ import { QuoteSnippetComponent } from '@renderer/components/snippet/quote/quote.
import { SnippetService } from '@renderer/services/snippet/snippet.service';
import { TableModule } from 'ngx-easy-table';
import { EllipsisModule } from 'ngx-ellipsis';
+import { PluginFactory } from "@renderer/factory/plugin.factory";
+import { PluginToken } from "@renderer/token/plugin.token";
+import { ClickHousePlugin } from "@renderer/plugin/clickhouse.plugin";
+import { ElasticsearchPlugin } from "@renderer/plugin/elasticsearch.plugin";
const QUERY_ROUTES: Routes = [
{path: '', component: QueryComponent}
@@ -48,7 +52,10 @@ const QUERY_ROUTES: Routes = [
QueryService,
QueryHistoryService,
QueryQuickService,
- SnippetService
+ SnippetService,
+ PluginFactory,
+ {provide: PluginToken, useClass: ClickHousePlugin, multi: true},
+ {provide: PluginToken, useClass: ElasticsearchPlugin, multi: true}
]
})
export class QueryModule {
diff --git a/src/renderer/assets/i18n/en.json b/src/renderer/assets/i18n/en.json
index da6fac2c..4742c2fd 100644
--- a/src/renderer/assets/i18n/en.json
+++ b/src/renderer/assets/i18n/en.json
@@ -24,9 +24,7 @@
"cancel": "Cancel",
"select": "Select",
"quick": "Quick",
- "result": "Result",
"editor": "Editor",
- "id": "ID",
"server": "Server",
"state": "State",
"time": "Time",
@@ -74,7 +72,6 @@
"atomic": "Atomic",
"expiration": "Expiration",
"lazy": "Lazy",
- "mysql": "MySQL",
"materialized_mysql": "MaterializeMySQL",
"github": "GitHub",
"drop": "Drop",
@@ -143,8 +140,6 @@
"comment": "Comment",
"snippet": "Snippet",
"id": "ID",
- "name": "Name",
- "description": "Description",
"code": "Code",
"created": "Created",
"updated": "Updated",
@@ -171,7 +166,13 @@
"location": "Location",
"failed": "Failed",
"druid": "Druid",
- "materialized_postgresql": "MaterializePostgreSQL"
+ "materialized_postgresql": "MaterializePostgreSQL",
+ "elasticsearch": "ElasticSearch",
+ "character": "Character",
+ "collation": "Collation",
+ "hologres": "Hologres",
+ "accesskey_id": "AccessKey ID",
+ "accesskey_secret": "AccessKey Secret"
},
"language": {
"english": "English",
@@ -228,7 +229,8 @@
"lazy": "Keeps tables in RAM only expiration_time_in_seconds seconds after last access. Can be used only with *Log tables.",
"mysql": "Allows to connect to databases on a remote MySQL server and perform INSERT and SELECT queries to exchange data between ClickHouse and MySQL.",
"materialized_mysql": "Creates ClickHouse database with all the tables existing in MySQL, and all the data in those tables. ClickHouse server works as MySQL replica. It reads binlog and performs DDL and DML queries.",
- "materialized_postgresql": "Creates a ClickHouse database with tables from PostgreSQL database. Firstly, database with engine MaterializedPostgreSQL creates a snapshot of PostgreSQL database and loads required tables. Required tables can include any subset of tables from any subset of schemas from specified database. Along with the snapshot database engine acquires LSN and once initial dump of tables is performed - it starts pulling updates from WAL. After database is created, newly added tables to PostgreSQL database are not automatically added to replication. They have to be added manually with ATTACH TABLE db.table query."
+ "materialized_postgresql": "Creates a ClickHouse database with tables from PostgreSQL database. Firstly, database with engine MaterializedPostgreSQL creates a snapshot of PostgreSQL database and loads required tables. Required tables can include any subset of tables from any subset of schemas from specified database. Along with the snapshot database engine acquires LSN and once initial dump of tables is performed - it starts pulling updates from WAL. After database is created, newly added tables to PostgreSQL database are not automatically added to replication. They have to be added manually with ATTACH TABLE db.table query.",
+ "postgresql": "Allows to connect to databases on a remote PostgreSQL server. Supports read and write operations (SELECT and INSERT queries) to exchange data between ClickHouse and PostgreSQL."
},
"table": {
"log": "Lightweight engines with minimum functionality. They’re the most effective when you need to quickly write many small tables (up to approximately 1 million rows) and read them later as a whole.",
@@ -239,7 +241,8 @@
"sqlite": "The engine allows to import and export data to SQLite and supports queries to SQLite tables directly from ClickHouse.",
"odbc": "Allows ClickHouse to connect to external databases via ODBC",
"mongodb": "MongoDB engine is read-only table engine which allows to read data (SELECT queries) from remote MongoDB collection. Engine supports only non-nested data types. INSERT queries are not supported.",
- "default": "Default table engine"
+ "default": "Default table engine",
+ "mysql": "The MySQL engine allows you to perform SELECT and INSERT queries on data that is stored on a remote MySQL server."
},
"property": {
"timeSeconds": "Retention time in RAM (unit per second)",
@@ -282,7 +285,9 @@
"presto": "Integrate Presto data sources",
"mysql": "Integrate MySQL data sources",
"postgresql": "Integrate PostgreSQL data sources",
- "druid": "Integrate Druid data sources"
+ "druid": "Integrate Druid data sources",
+ "elasticsearch": "Integrate ElasticSearch data sources",
+ "hologres": "Integrate Alibaba Hologres data sources"
}
},
"alert": {
@@ -308,5 +313,15 @@
},
"formatter": {
"migrate_data": "Need to migrate {0} data, please confirm whether to migrate?"
+ },
+ "engine": {
+ "table": {
+ "hive": {
+ "description": "The Hive engine allows you to perform SELECT quries on HDFS Hive table.",
+ "uri": "Hive Metastore address, egg: thrift://host:port",
+ "database": "Remote database name",
+ "table": "Remote table name"
+ }
+ }
}
}
diff --git a/src/renderer/assets/i18n/zh.json b/src/renderer/assets/i18n/zh.json
index 972a6dbc..bfb905b9 100644
--- a/src/renderer/assets/i18n/zh.json
+++ b/src/renderer/assets/i18n/zh.json
@@ -16,7 +16,6 @@
"action": "操作",
"delete": "删除",
"edit": "修改",
- "name": "名称",
"query": "查询",
"execute": "执行",
"history": "历史",
@@ -24,9 +23,7 @@
"cancel": "取消",
"select": "选择",
"quick": "Quick",
- "result": "结果",
"editor": "编辑器",
- "id": "ID",
"server": "服务",
"state": "状态",
"time": "时间",
@@ -74,7 +71,6 @@
"atomic": "Atomic",
"expiration": "过期",
"lazy": "Lazy",
- "mysql": "MySQL",
"materialized_mysql": "MaterializeMySQL",
"github": "GitHub",
"drop": "Drop",
@@ -138,7 +134,6 @@
"remove": "移除",
"status": "状态",
"success": "成功",
- "description": "描述",
"comment": "评论",
"snippet": "片段",
"id": "标记",
@@ -170,7 +165,13 @@
"location": "位置",
"failed": "失败",
"druid": "Druid",
- "materialized_postgresql": "MaterializePostgreSQL"
+ "materialized_postgresql": "MaterializePostgreSQL",
+ "elasticsearch": "ElasticSearch",
+ "character": "字符集",
+ "collation": "排序规则",
+ "hologres": "Hologres",
+ "accesskey_id": "AccessKey ID",
+ "accesskey_secret": "AccessKey Secret"
},
"language": {
"english": "英语",
@@ -229,7 +230,8 @@
"lazy": "Keeps tables in RAM only expiration_time_in_seconds seconds after last access. Can be used only with *Log tables.",
"mysql": "Allows to connect to databases on a remote MySQL server and perform INSERT and SELECT queries to exchange data between ClickHouse and MySQL.",
"materialized_mysql": "Creates ClickHouse database with all the tables existing in MySQL, and all the data in those tables. ClickHouse server works as MySQL replica. It reads binlog and performs DDL and DML queries.",
- "materialized_postgresql": "Creates a ClickHouse database with tables from PostgreSQL database. Firstly, database with engine MaterializedPostgreSQL creates a snapshot of PostgreSQL database and loads required tables. Required tables can include any subset of tables from any subset of schemas from specified database. Along with the snapshot database engine acquires LSN and once initial dump of tables is performed - it starts pulling updates from WAL. After database is created, newly added tables to PostgreSQL database are not automatically added to replication. They have to be added manually with ATTACH TABLE db.table query."
+ "materialized_postgresql": "Creates a ClickHouse database with tables from PostgreSQL database. Firstly, database with engine MaterializedPostgreSQL creates a snapshot of PostgreSQL database and loads required tables. Required tables can include any subset of tables from any subset of schemas from specified database. Along with the snapshot database engine acquires LSN and once initial dump of tables is performed - it starts pulling updates from WAL. After database is created, newly added tables to PostgreSQL database are not automatically added to replication. They have to be added manually with ATTACH TABLE db.table query.",
+ "postgresql": "Allows to connect to databases on a remote PostgreSQL server. Supports read and write operations (SELECT and INSERT queries) to exchange data between ClickHouse and PostgreSQL."
},
"table": {
"log": "轻量级引擎。当您需要快速地构建多个小表(最多100万行),然后将它们作为一个整体读取时,它们是最有效的。",
@@ -240,7 +242,8 @@
"sqlite": "The engine allows to import and export data to SQLite and supports queries to SQLite tables directly from ClickHouse.",
"odbc": "Allows ClickHouse to connect to external databases via ODBC",
"mongodb": "MongoDB engine is read-only table engine which allows to read data (SELECT queries) from remote MongoDB collection. Engine supports only non-nested data types. INSERT queries are not supported.",
- "default": "默认表引擎"
+ "default": "默认表引擎",
+ "mysql": "The MySQL engine allows you to perform SELECT and INSERT queries on data that is stored on a remote MySQL server."
},
"property": {
"timeSeconds": "Retention time in RAM (unit per second)",
@@ -283,7 +286,9 @@
"presto": "整合Presto数据源",
"mysql": "整合MySQL数据源",
"postgresql": "整合PostgreSQL数据源",
- "druid": "整合Druid数据源"
+ "druid": "整合Druid数据源",
+ "elasticsearch": "整合ElasticSearch数据源",
+ "hologres": "整合Alibaba Hologres数据源"
}
},
"alert": {
@@ -309,5 +314,15 @@
},
"formatter": {
"migrate_data": "需要迁移{0}条数据, 请确认是否迁移?"
+ },
+ "engine": {
+ "table": {
+ "hive": {
+ "description": "The Hive engine allows you to perform SELECT quries on HDFS Hive table.",
+ "uri": "Hive Metastore address, egg: thrift://host:port",
+ "database": "Remote database name",
+ "table": "Remote table name"
+ }
+ }
}
}
diff --git a/src/renderer/assets/icon/source/ElasticSearch.svg b/src/renderer/assets/icon/source/ElasticSearch.svg
new file mode 100644
index 00000000..b95507cd
--- /dev/null
+++ b/src/renderer/assets/icon/source/ElasticSearch.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/renderer/assets/icon/source/Hologres.svg b/src/renderer/assets/icon/source/Hologres.svg
new file mode 100644
index 00000000..0bf2084c
--- /dev/null
+++ b/src/renderer/assets/icon/source/Hologres.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/renderer/components/database/basic/database.basic.component.html b/src/renderer/components/database/basic/database.basic.component.html
index 5e9643e6..76e230fe 100644
--- a/src/renderer/components/database/basic/database.basic.component.html
+++ b/src/renderer/components/database/basic/database.basic.component.html
@@ -41,27 +41,65 @@
diff --git a/src/renderer/components/database/basic/database.basic.component.ts b/src/renderer/components/database/basic/database.basic.component.ts
index c8c28086..f1adf36f 100644
--- a/src/renderer/components/database/basic/database.basic.component.ts
+++ b/src/renderer/components/database/basic/database.basic.component.ts
@@ -13,6 +13,7 @@ import * as cloneDeep from 'lodash/cloneDeep';
import { PropertyModel } from '@renderer/model/property.model';
import { NzTreeNode } from 'ng-zorro-antd/core/tree/nz-tree-base-node';
import { MenuModel } from '@renderer/model/menu.model';
+import { CollationService } from "@renderer/services/management/collation.service";
@Component({
selector: 'app-component-database',
@@ -35,9 +36,15 @@ export class DatabaseBasicComponent extends BaseComponent implements AfterViewIn
configure: DatabaseModel;
databaseType = DatabaseEnum;
properties: PropertyModel[];
+ collationConfigure = {
+ elements: [],
+ characters: [],
+ collations: []
+ };
constructor(private dataSourceService: DatasourceService,
private metadataService: MetadataService,
+ private collationService: CollationService,
private messageService: NzMessageService) {
super();
this.configure = new DatabaseModel();
@@ -97,19 +104,53 @@ export class DatabaseBasicComponent extends BaseComponent implements AfterViewIn
this.handlerValidate();
}
- async handlerComplete() {
- const request = new RequestModel();
- request.config = await this.dataSourceService.getByAliasAsync(this.config.value);
- this.metadataService.createDatabase(request, this.configure).then(response => {
- if (response.status) {
- this.messageService.success(response.message);
- this.config.status = true;
- this.config.menu = this.menu;
- this.config.currentNode = this.node;
- this.emitter.emit(this.config);
- } else {
- this.messageService.error(response.message);
- }
- });
+ handlerComplete() {
+ this.dataSourceService.getByAliasAsync(this.config.value)
+ .then(dataSource => {
+ const request = new RequestModel();
+ request.config = dataSource;
+ this.metadataService.createDatabase(request, this.configure)
+ .then(response => {
+ if (response.status) {
+ this.messageService.success(response.message);
+ this.config.status = true;
+ this.config.menu = this.menu;
+ this.config.currentNode = this.node;
+ this.emitter.emit(this.config);
+ } else {
+ this.messageService.error(response.message);
+ }
+ });
+ });
+
+ }
+
+ handlerLoadCharacterAndCollation() {
+ if (this.collationConfigure.characters.length <= 0) {
+ this.dataSourceService.getByAliasAsync(this.config.value)
+ .then(dataSource => {
+ const request = new RequestModel();
+ request.config = dataSource;
+ this.collationService.getCharacterAndCollation(request)
+ .then(response => {
+ response.data.columns.forEach(v => {
+ const collation = {
+ name: v['name'],
+ values: v['values'].split(',')
+ };
+ this.collationConfigure.characters.push(v['name']);
+ this.collationConfigure.elements.push(collation);
+ });
+ })
+ });
+ }
+ }
+
+ handlerChangeCharacter(value: string) {
+ this.configure.characterAndCollationConfigure.collationConfigure.value = null;
+ if (this.configure.characterAndCollationConfigure.collationConfigure.enable) {
+ this.collationConfigure.collations = this.collationConfigure.elements
+ .filter(item => item.name === value)[0].values;
+ }
}
}
diff --git a/src/renderer/components/database/rename/database.rename.component.ts b/src/renderer/components/database/rename/database.rename.component.ts
index 24a5b4cb..b9c632f1 100644
--- a/src/renderer/components/database/rename/database.rename.component.ts
+++ b/src/renderer/components/database/rename/database.rename.component.ts
@@ -6,6 +6,7 @@ import { DatasourceService } from '@renderer/services/management/datasource.serv
import { StringUtils } from '@renderer/utils/string.utils';
import { NzMessageService } from 'ng-zorro-antd/message';
import { DatabaseService } from '@renderer/services/management/database.service';
+import { DatabaseEnum } from "@renderer/enum/database.enum";
@Component({
selector: 'app-component-rename-database',
@@ -37,14 +38,16 @@ export class DatabaseRenameComponent extends BaseComponent implements AfterViewI
this.checkStatus = true;
const request = new RequestModel();
request.config = await this.dataSourceService.getByAliasAsync(this.config.value);
- this.databaseService.getDatabase(request, this.value)
- .then(response => {
- if (response.status) {
- this.checkStatus = response.data?.columns[0]?.isSupport;
- } else {
- this.messageService.error(response.message);
- }
- });
+ if (request.config.type === DatabaseEnum.clickhosue) {
+ this.databaseService.getDatabase(request, this.value)
+ .then(response => {
+ if (response.status) {
+ this.checkStatus = response.data?.columns[0]?.isSupport;
+ } else {
+ this.messageService.error(response.message);
+ }
+ });
+ }
}
handlerValidate() {
@@ -60,15 +63,15 @@ export class DatabaseRenameComponent extends BaseComponent implements AfterViewI
const request = new RequestModel();
request.config = await this.dataSourceService.getByAliasAsync(this.config.value);
this.databaseService.rename(request, this.value, this.inputValue)
- .then(response => {
- if (response.status) {
- this.messageService.success(response.message);
- this.config.status = true;
- this.emitter.emit(this.config);
- } else {
- this.messageService.error(response.message);
- }
- this.loading.button = false;
- });
+ .then(response => {
+ if (response.status) {
+ this.messageService.success(response.message);
+ this.config.status = true;
+ this.emitter.emit(this.config);
+ } else {
+ this.messageService.error(response.message);
+ }
+ this.loading.button = false;
+ });
}
}
diff --git a/src/renderer/components/datasource/clickhouse/datasource.clickhouse.component.html b/src/renderer/components/datasource/clickhouse/datasource.clickhouse.component.html
index a503d06e..139b9c86 100644
--- a/src/renderer/components/datasource/clickhouse/datasource.clickhouse.component.html
+++ b/src/renderer/components/datasource/clickhouse/datasource.clickhouse.component.html
@@ -23,6 +23,7 @@
HTTP
+ HTTPS
SSH
diff --git a/src/renderer/components/datasource/common/datasource.common.component.html b/src/renderer/components/datasource/common/datasource.common.component.html
index e358d616..6452f5f4 100644
--- a/src/renderer/components/datasource/common/datasource.common.component.html
+++ b/src/renderer/components/datasource/common/datasource.common.component.html
@@ -13,3 +13,9 @@
+
+
+
+
diff --git a/src/renderer/components/datasource/elasticsearch/datasource.elasticsearch.component.html b/src/renderer/components/datasource/elasticsearch/datasource.elasticsearch.component.html
new file mode 100644
index 00000000..9ee8651c
--- /dev/null
+++ b/src/renderer/components/datasource/elasticsearch/datasource.elasticsearch.component.html
@@ -0,0 +1,41 @@
+
diff --git a/src/renderer/components/datasource/elasticsearch/datasource.elasticsearch.component.ts b/src/renderer/components/datasource/elasticsearch/datasource.elasticsearch.component.ts
new file mode 100644
index 00000000..816f8ba1
--- /dev/null
+++ b/src/renderer/components/datasource/elasticsearch/datasource.elasticsearch.component.ts
@@ -0,0 +1,45 @@
+import { Component, EventEmitter, Input, Output } from '@angular/core';
+import { BaseComponent } from '@renderer/app/base.component';
+import { DatasourceModel } from "@renderer/model/datasource.model";
+import { FormBuilder, FormGroup, Validators } from "@angular/forms";
+
+@Component({
+ selector: 'app-component-datasource-elasticsearch',
+ templateUrl: './datasource.elasticsearch.component.html'
+})
+export class DatasourceElasticSearchComponent extends BaseComponent {
+ @Input()
+ configure: DatasourceModel;
+ @Output()
+ emitterValue = new EventEmitter
();
+ validateForm!: FormGroup;
+
+ constructor(private formBuilder: FormBuilder) {
+ super();
+ this.validateForm = this.formBuilder.group({
+ alias: [null, [Validators.required]],
+ host: [null, [Validators.required]],
+ port: [null, [Validators.required]]
+ });
+ }
+
+ handlerValidate() {
+ if (this.validateForm.valid) {
+ this.configure.validate = true;
+ this.configure.url = '_xpack/sql';
+ this.emitterValue.emit(this.configure);
+ } else {
+ this.configure.validate = false;
+ Object.values(this.validateForm.controls).forEach(control => {
+ if (control.invalid) {
+ control.markAsDirty();
+ control.updateValueAndValidity({onlySelf: true});
+ }
+ });
+ }
+ }
+
+ handlerEmitterValue() {
+ this.handlerValidate();
+ }
+}
diff --git a/src/renderer/components/datasource/hologres/datasource.hologres.component.html b/src/renderer/components/datasource/hologres/datasource.hologres.component.html
new file mode 100644
index 00000000..24bb6482
--- /dev/null
+++ b/src/renderer/components/datasource/hologres/datasource.hologres.component.html
@@ -0,0 +1,85 @@
+
diff --git a/src/renderer/components/datasource/hologres/datasource.hologres.component.ts b/src/renderer/components/datasource/hologres/datasource.hologres.component.ts
new file mode 100644
index 00000000..e5fe437b
--- /dev/null
+++ b/src/renderer/components/datasource/hologres/datasource.hologres.component.ts
@@ -0,0 +1,63 @@
+import { AfterViewInit, Component, EventEmitter, Input, Output } from '@angular/core';
+import { BaseComponent } from '@renderer/app/base.component';
+import { DatasourceModel } from "@renderer/model/datasource.model";
+import { FormBuilder, FormControl, FormGroup, Validators } from "@angular/forms";
+
+@Component({
+ selector: 'app-component-datasource-hologres',
+ templateUrl: './datasource.hologres.component.html'
+})
+export class DatasourceHologresComponent extends BaseComponent implements AfterViewInit {
+ @Input()
+ configure: DatasourceModel;
+ @Output()
+ emitterValue = new EventEmitter();
+ validateForm!: FormGroup;
+
+ constructor(private formBuilder: FormBuilder) {
+ super();
+ this.validateForm = this.formBuilder.group({
+ alias: [null, [Validators.required]],
+ host: [null, [Validators.required]],
+ port: [null, [Validators.required]],
+ authorization: [null, [Validators.required]],
+ catalog: [null, []],
+ database: [null, []]
+ });
+ }
+
+ ngAfterViewInit(): void {
+ setTimeout(() => {
+ if (this.configure?.port === 8123) {
+ this.configure.port = 5443;
+ }
+ this.handlerAuthorization();
+ }, 0);
+ }
+
+ handlerValidate() {
+ if (this.validateForm.valid) {
+ this.configure.validate = true;
+ this.emitterValue.emit(this.configure);
+ } else {
+ this.configure.validate = false;
+ Object.values(this.validateForm.controls).forEach(control => {
+ if (control.invalid) {
+ control.markAsDirty();
+ control.updateValueAndValidity({onlySelf: true});
+ }
+ });
+ }
+ }
+
+ handlerAuthorization() {
+ if (this.configure.authorization) {
+ this.validateForm.addControl('username', new FormControl(null, [Validators.required]));
+ this.validateForm.addControl('password', new FormControl(null, [Validators.required]));
+ } else {
+ this.validateForm.removeControl('username');
+ this.validateForm.removeControl('password');
+ }
+ this.handlerValidate();
+ }
+}
diff --git a/src/renderer/components/query/quick/quick.query.component.ts b/src/renderer/components/query/quick/quick.query.component.ts
index 639488a0..6748546b 100644
--- a/src/renderer/components/query/quick/quick.query.component.ts
+++ b/src/renderer/components/query/quick/quick.query.component.ts
@@ -47,7 +47,12 @@ export class QuickQueryComponent extends BaseComponent {
super();
this.quickCommands = this.queryQuickService.getQuickAll();
this.dataSourceService.getAll().then(response => {
- this.dataSourceSet = response;
+ this.dataSourceSet = response.map(item => {
+ if (item.type === DatabaseEnum.elasticsearch) {
+ item.status = false;
+ }
+ return item;
+ });
});
}
diff --git a/src/renderer/components/table/basic/basic.table.component.html b/src/renderer/components/table/basic/basic.table.component.html
index 3a469585..9498077b 100644
--- a/src/renderer/components/table/basic/basic.table.component.html
+++ b/src/renderer/components/table/basic/basic.table.component.html
@@ -2,13 +2,14 @@
{{'common.export'|translate}}
-
-
-
-
-
-
+
+
+
+ {{row[header.key]}}
+
+
diff --git a/src/renderer/components/table/basic/basic.table.component.ts b/src/renderer/components/table/basic/basic.table.component.ts
index 0305c9bd..197ccd13 100644
--- a/src/renderer/components/table/basic/basic.table.component.ts
+++ b/src/renderer/components/table/basic/basic.table.component.ts
@@ -5,6 +5,8 @@ import { ExportToCsv } from 'export-to-csv';
import { Md5 } from 'ts-md5';
import { TableExportModel } from "@renderer/components/table/basic/table.export.model";
import { StringUtils } from "@renderer/utils/string.utils";
+import { TranslateService } from "@ngx-translate/core";
+import { NzModalService } from "ng-zorro-antd/modal";
@Component({
selector: 'app-component-basic-table',
@@ -18,7 +20,8 @@ export class BasicTableComponent extends BaseComponent implements AfterViewInit
public id: string;
exportInfo: TableExportModel;
- constructor() {
+ constructor(private translateService: TranslateService,
+ private modalService: NzModalService) {
super();
this.configuration = {...DefaultConfig};
this.configuration.horizontalScroll = true;
@@ -68,4 +71,14 @@ export class BasicTableComponent extends BaseComponent implements AfterViewInit
const csvExporter = new ExportToCsv(options);
csvExporter.generateCsv(this.value.columns);
}
+
+ handlerShowMoreEllipsis(value: any): void {
+ this.modalService.info({
+ nzWidth: '80%',
+ nzKeyboard: false,
+ nzMaskClosable: false,
+ nzOkText: this.translateService.instant('common.ok'),
+ nzContent: value.toString()
+ });
+ }
}
diff --git a/src/renderer/components/table/create/table.create.component.html b/src/renderer/components/table/create/table.create.component.html
index 27bde476..434fe439 100644
--- a/src/renderer/components/table/create/table.create.component.html
+++ b/src/renderer/components/table/create/table.create.component.html
@@ -37,78 +37,119 @@
diff --git a/src/renderer/components/table/create/table.create.component.ts b/src/renderer/components/table/create/table.create.component.ts
index 39360acb..17c74f85 100644
--- a/src/renderer/components/table/create/table.create.component.ts
+++ b/src/renderer/components/table/create/table.create.component.ts
@@ -28,6 +28,11 @@ export class CreateTableComponent extends BaseComponent implements AfterViewInit
selectValue: string;
columns: ColumnModel[] = new Array();
columnTypes: string[] = new Array();
+ validated = {
+ basic: false,
+ column: false,
+ property: false
+ }
constructor(private tableService: TableService,
private dataSourceService: DatasourceService,
@@ -71,6 +76,30 @@ export class CreateTableComponent extends BaseComponent implements AfterViewInit
this.configure = cloneDeep(value);
}
+ handlerValidateStep(step: string) {
+ switch (step) {
+ case 'Basic':
+ if (StringUtils.isNotEmpty(this.configure.targetName)) {
+ this.validated.basic = true;
+ } else {
+ this.validated.basic = false;
+ }
+ break;
+ case 'Column':
+ const emptyCount = this.columns.filter(column => StringUtils.isEmpty(column.name)).length;
+ if (emptyCount === 0) {
+ this.validated.column = true;
+ } else {
+ this.validated.column = false;
+ }
+ break;
+ case 'Property':
+ this.validated.property = this.configure.validate;
+ break;
+ }
+ this.handlerValidate();
+ }
+
handlerValidate() {
let flag;
if (this.configure.validate != undefined) {
@@ -107,7 +136,7 @@ export class CreateTableComponent extends BaseComponent implements AfterViewInit
} else {
this.configure.optionalProperties = $event.properties;
}
- this.handlerValidate();
+ this.handlerValidateStep('Property');
}
handlerPrevious(): void {
@@ -122,6 +151,7 @@ export class CreateTableComponent extends BaseComponent implements AfterViewInit
const request = new RequestModel();
request.config = await this.dataSourceService.getByAliasAsync(this.config.value);
this.configure.database = this.value;
+ request.config.database = this.value;
this.tableService.createTable(request, this.configure, this.columns).then(response => {
if (response.status) {
this.messageService.success(response.message);
diff --git a/src/renderer/config/base.config.ts b/src/renderer/config/base.config.ts
index d87d7077..206c0888 100644
--- a/src/renderer/config/base.config.ts
+++ b/src/renderer/config/base.config.ts
@@ -25,4 +25,7 @@ export interface BaseConfig {
stopProcessor: string;
showCreateDatabase: string;
showTableWithSize: string;
+ getCharacterAndCollation: string;
+ // database
+ databaseRename: string;
}
diff --git a/src/renderer/config/clickhouse.config.ts b/src/renderer/config/clickhouse.config.ts
new file mode 100644
index 00000000..0fbbacca
--- /dev/null
+++ b/src/renderer/config/clickhouse.config.ts
@@ -0,0 +1,22 @@
+import { ConfigInterface } from "@renderer/interfaces/config.interface";
+import { Injectable } from "@angular/core";
+import { Factory } from "@renderer/factory";
+import { DatabaseEnum } from "@renderer/enum/database.enum";
+import { ClickhouseConfig } from "@renderer/config/plugin/clickhouse.config";
+
+@Injectable()
+export class ClickHouseConfig implements ConfigInterface {
+ private readonly config: ClickhouseConfig;
+
+ constructor() {
+ this.config = Factory.create(ClickhouseConfig);
+ }
+
+ getName(): DatabaseEnum {
+ return DatabaseEnum.clickhosue;
+ }
+
+ getStatement(key: string): string {
+ return this.config[key];
+ }
+}
diff --git a/src/renderer/config/database.config.ts b/src/renderer/config/database.config.ts
index ed1f6cb5..b7af8925 100644
--- a/src/renderer/config/database.config.ts
+++ b/src/renderer/config/database.config.ts
@@ -4,6 +4,8 @@ import { DatabaseModel } from '@renderer/model/database.model';
import { StringUtils } from '@renderer/utils/string.utils';
import { TranslateUtils } from '@renderer/utils/translate.utils';
import { PropertyModel } from '@renderer/model/property.model';
+import { DefaultEngine } from "@renderer/config/engine/database/mysql/engine.database.mysql.default.config";
+import { PostgreSQLDatabaseEngine } from "@renderer/config/engine/database/engine.database.postgresql";
@Injectable()
export class DatabaseConfig {
@@ -21,8 +23,12 @@ export class DatabaseConfig {
TranslateUtils.getValue('tooltip.database.default'),
DatabaseEnum.none,
null);
- defaultEngine.supportedSource.push(DatabaseEnum.presto, DatabaseEnum.trino, DatabaseEnum.mysql, DatabaseEnum.postgresql);
+ defaultEngine.supportedSource.push(DatabaseEnum.presto, DatabaseEnum.trino, DatabaseEnum.postgresql);
defaultEngines.push(defaultEngine);
+
+ // MySQL
+ defaultEngines.push(DefaultEngine);
+
defaultEngines.push(DatabaseModel.builder(TranslateUtils.getValue('common.atomic'),
TranslateUtils.getValue('tooltip.database.atomic'),
DatabaseEnum.atomic,
@@ -31,6 +37,7 @@ export class DatabaseConfig {
TranslateUtils.getValue('tooltip.database.lazy'),
DatabaseEnum.lazy,
null));
+
// mysql
const properties = new Array();
properties.push(PropertyModel.builder('host',
@@ -57,6 +64,10 @@ export class DatabaseConfig {
TranslateUtils.getValue('tooltip.database.mysql'),
DatabaseEnum.mysql,
properties));
+
+ // PostgreSQL
+ defaultEngines.push(PostgreSQLDatabaseEngine);
+
basicDatabase.engines = defaultEngines;
databaseEngines.push(basicDatabase);
/**
diff --git a/src/renderer/config/engine/database/engine.database.postgresql.ts b/src/renderer/config/engine/database/engine.database.postgresql.ts
new file mode 100644
index 00000000..ac273b26
--- /dev/null
+++ b/src/renderer/config/engine/database/engine.database.postgresql.ts
@@ -0,0 +1,36 @@
+import { DatabaseModel } from "@renderer/model/database.model";
+import { TranslateUtils } from "@renderer/utils/translate.utils";
+import { DatabaseEnum } from "@renderer/enum/database.enum";
+import { PropertyModel } from "@renderer/model/property.model";
+
+const properties = new Array();
+properties.push(PropertyModel.builder('host',
+ TranslateUtils.getValue('common.host'),
+ TranslateUtils.getValue('placeholder.host'),
+ TranslateUtils.getValue('tooltip.property.host')));
+properties.push(PropertyModel.builder('port',
+ TranslateUtils.getValue('common.port'),
+ TranslateUtils.getValue('placeholder.port'),
+ TranslateUtils.getValue('tooltip.property.port')));
+properties.push(PropertyModel.builder('database',
+ TranslateUtils.getValue('common.database'),
+ TranslateUtils.getValue('placeholder.database'),
+ TranslateUtils.getValue('tooltip.property.database')));
+properties.push(PropertyModel.builder('username',
+ TranslateUtils.getValue('common.username'),
+ TranslateUtils.getValue('placeholder.username'),
+ TranslateUtils.getValue('tooltip.property.username')));
+properties.push(PropertyModel.builder('password',
+ TranslateUtils.getValue('common.password'),
+ TranslateUtils.getValue('placeholder.password'),
+ TranslateUtils.getValue('tooltip.property.password')));
+
+const PostgreSQLDatabaseEngine = DatabaseModel.builder(TranslateUtils.getValue('common.postgresql'),
+ TranslateUtils.getValue('tooltip.database.postgresql'),
+ DatabaseEnum.postgresql,
+ properties);
+PostgreSQLDatabaseEngine.supportedSource = [DatabaseEnum.clickhosue];
+
+export {
+ PostgreSQLDatabaseEngine
+}
diff --git a/src/renderer/config/engine/database/mysql/engine.database.mysql.default.config.ts b/src/renderer/config/engine/database/mysql/engine.database.mysql.default.config.ts
new file mode 100644
index 00000000..06673ea1
--- /dev/null
+++ b/src/renderer/config/engine/database/mysql/engine.database.mysql.default.config.ts
@@ -0,0 +1,17 @@
+import { DatabaseModel } from "@renderer/model/database.model";
+import { TranslateUtils } from "@renderer/utils/translate.utils";
+import { DatabaseEnum } from "@renderer/enum/database.enum";
+
+const DefaultEngine = DatabaseModel.builder(TranslateUtils.getValue('common.default'),
+ TranslateUtils.getValue('tooltip.database.default'),
+ DatabaseEnum.none,
+ null);
+DefaultEngine.supportedSource = [DatabaseEnum.mysql];
+
+DefaultEngine.characterAndCollationConfigure.enable = true;
+DefaultEngine.characterAndCollationConfigure.characterSetConfigure.enable = true;
+DefaultEngine.characterAndCollationConfigure.collationConfigure.enable = true;
+
+export {
+ DefaultEngine
+}
diff --git a/src/renderer/config/engine/table/engine.table.hive.config.ts b/src/renderer/config/engine/table/engine.table.hive.config.ts
new file mode 100644
index 00000000..91c933c4
--- /dev/null
+++ b/src/renderer/config/engine/table/engine.table.hive.config.ts
@@ -0,0 +1,37 @@
+import { PropertyModel } from "@renderer/model/property.model";
+import { TranslateUtils } from "@renderer/utils/translate.utils";
+import { DatabaseModel } from "@renderer/model/database.model";
+import { DatabaseEnum } from "@renderer/enum/database.enum";
+import { PropertyEnum } from "@renderer/enum/property.enum";
+
+const hiveProperties = new Array();
+hiveProperties.push(PropertyModel.builder('uri',
+ TranslateUtils.getValue('common.uri'),
+ TranslateUtils.getValue('engine.table.hive.uri'),
+ TranslateUtils.getValue('engine.table.hive.uri'),
+ null,
+ false,
+ true));
+hiveProperties.push(PropertyModel.builder('database',
+ TranslateUtils.getValue('common.database'),
+ TranslateUtils.getValue('engine.table.hive.database'),
+ TranslateUtils.getValue('engine.table.hive.database'),
+ null,
+ false));
+hiveProperties.push(PropertyModel.builder('table',
+ TranslateUtils.getValue('common.table'),
+ TranslateUtils.getValue('engine.table.hive.table'),
+ TranslateUtils.getValue('engine.table.hive.table'),
+ null,
+ false));
+const HiveTableEngine = DatabaseModel.builder(DatabaseEnum.hive.toString(),
+ TranslateUtils.getValue('engine.table.hive.description'),
+ DatabaseEnum.hive,
+ hiveProperties,
+ false,
+ PropertyEnum.name);
+
+HiveTableEngine.partitionConfigure.enable = true;
+HiveTableEngine.supportedSource = [DatabaseEnum.clickhosue];
+
+export { HiveTableEngine };
diff --git a/src/renderer/config/mysql.config.ts b/src/renderer/config/mysql.config.ts
new file mode 100644
index 00000000..f7b8b93a
--- /dev/null
+++ b/src/renderer/config/mysql.config.ts
@@ -0,0 +1,22 @@
+import { ConfigInterface } from "@renderer/interfaces/config.interface";
+import { DatabaseEnum } from "@renderer/enum/database.enum";
+import { Injectable } from "@angular/core";
+import { Factory } from "@renderer/factory";
+import { MySQLConfig } from "@renderer/config/plugin/mysql.config";
+
+@Injectable()
+export class MysqlConfig implements ConfigInterface {
+ private readonly config: MySQLConfig;
+
+ constructor() {
+ this.config = Factory.create(MySQLConfig);
+ }
+
+ getName(): DatabaseEnum {
+ return DatabaseEnum.mysql;
+ }
+
+ getStatement(key: string): string {
+ return this.config[key];
+ }
+}
diff --git a/src/renderer/config/operation.config.ts b/src/renderer/config/operation.config.ts
index 9e198e0c..f347f36b 100644
--- a/src/renderer/config/operation.config.ts
+++ b/src/renderer/config/operation.config.ts
@@ -51,7 +51,11 @@ export class OperationConfig {
actions: [OperationEnum.structure],
supportedSource: [DatabaseEnum.clickhosue, DatabaseEnum.mysql]
},
- {type: TypeEnum.database, actions: [OperationEnum.rename], supportedSource: [DatabaseEnum.clickhosue]}
+ {
+ type: TypeEnum.database,
+ actions: [OperationEnum.rename],
+ supportedSource: [DatabaseEnum.clickhosue, DatabaseEnum.postgresql]
+ }
];
opertions.push(database);
const table = new OperationModel();
diff --git a/src/renderer/config/plugin/clickhouse.config.ts b/src/renderer/config/plugin/clickhouse.config.ts
index 14caed3f..6746f7b8 100644
--- a/src/renderer/config/plugin/clickhouse.config.ts
+++ b/src/renderer/config/plugin/clickhouse.config.ts
@@ -171,4 +171,6 @@ ALTER TABLE {0} RENAME COLUMN {1} TO {2}
columnAddComment = `
ALTER TABLE {0} COMMENT COLUMN {1} '{2}'
`;
+ getCharacterAndCollation: string;
+ databaseRename = 'RENAME DATABASE `{0}` TO `{1}`';
}
diff --git a/src/renderer/config/plugin/druid.config.ts b/src/renderer/config/plugin/druid.config.ts
index 2478ef16..95d1b077 100644
--- a/src/renderer/config/plugin/druid.config.ts
+++ b/src/renderer/config/plugin/druid.config.ts
@@ -8,7 +8,10 @@ export class DruidConfig implements BaseConfig {
connectionFetchAll: string;
databaseCreate: string;
databaseDiskUsedRatio: string;
- databaseFetchAll: string;
+ databaseFetchAll = `
+ SELECT SCHEMA_NAME AS name
+ FROM INFORMATION_SCHEMA.SCHEMATA
+ `;
databaseItems: string;
databaseItemsFilterFuzzy: string;
databaseItemsFilterPrecise: string;
@@ -21,10 +24,16 @@ export class DruidConfig implements BaseConfig {
slowQueryFetchAll: string;
stopProcessor: string;
tableDiskUsedRatio: string;
- tableFetchAll: string;
+ tableFetchAll = `
+ SELECT TABLE_NAME AS name
+ FROM INFORMATION_SCHEMA.TABLES
+ WHERE TABLE_SCHEMA = '{0}'
+ `;
tableItems: string;
tableItemsFilterFuzzy: string;
tableItemsFilterPrecise: string;
tableSchemaFetchAll: string;
version = `SELECT '-' AS version`;
+ getCharacterAndCollation: string;
+ databaseRename: string;
}
diff --git a/src/renderer/config/plugin/elasticsearch.config.ts b/src/renderer/config/plugin/elasticsearch.config.ts
new file mode 100644
index 00000000..792b3822
--- /dev/null
+++ b/src/renderer/config/plugin/elasticsearch.config.ts
@@ -0,0 +1,32 @@
+import { BaseConfig } from "@renderer/config/base.config";
+
+export class ElasticsearchConfig implements BaseConfig {
+ columnAddComment: string;
+ columnDiskUsedRatio: string;
+ columnItems: string;
+ columnRename: string;
+ connectionFetchAll: string;
+ databaseCreate: string;
+ databaseDiskUsedRatio: string;
+ databaseFetchAll: string;
+ databaseItems: string;
+ databaseItemsFilterFuzzy: string;
+ databaseItemsFilterPrecise: string;
+ diskUsedRatio: string;
+ processesFetchAll: string;
+ schemaFetchAll: string;
+ serverInfo: string;
+ showCreateDatabase: string;
+ showTableWithSize: string;
+ slowQueryFetchAll: string;
+ stopProcessor: string;
+ tableDiskUsedRatio: string;
+ tableFetchAll: string;
+ tableItems: string;
+ tableItemsFilterFuzzy: string;
+ tableItemsFilterPrecise: string;
+ tableSchemaFetchAll: string;
+ version = `SELECT 1 AS version`;
+ getCharacterAndCollation: string;
+ databaseRename: string;
+}
diff --git a/src/renderer/config/plugin/mysql.config.ts b/src/renderer/config/plugin/mysql.config.ts
index ccfa4e97..81b3bf32 100644
--- a/src/renderer/config/plugin/mysql.config.ts
+++ b/src/renderer/config/plugin/mysql.config.ts
@@ -3,10 +3,9 @@ import { BaseConfig } from "@renderer/config/base.config";
export class MySQLConfig implements BaseConfig {
columnDiskUsedRatio = `SELECT '{0}' AS db, '{1}' AS name`;
columnItems = `
-SELECT
- TABLE_SCHEMA AS "database", TABLE_NAME AS tableName, COLUMN_NAME AS name,
+SELECT TABLE_SCHEMA AS "database", TABLE_NAME AS tableName, COLUMN_NAME AS name
-- DATA_TYPE AS type
- concat(DATA_TYPE, '(', CHARACTER_MAXIMUM_LENGTH, ')') AS type
+-- concat(DATA_TYPE, '(', CHARACTER_MAXIMUM_LENGTH, ')') AS type
FROM information_schema.columns
WHERE table_schema = '{0}' AND table_name = '{1}'
GROUP BY COLUMN_NAME
@@ -87,9 +86,7 @@ FROM information_schema.tables
WHERE table_schema = '{0}'
`;
tableItems = `
-SELECT
- table_schema AS "database", TABLE_NAME AS name,
- ENGINE AS value, MAX_DATA_LENGTH AS total_rows
+SELECT table_schema AS "database", TABLE_NAME AS name
FROM information_schema.tables
WHERE table_schema = '{0}'
GROUP BY TABLE_NAME
@@ -122,4 +119,13 @@ ALTER TABLE {0} CHANGE COLUMN {1} {2} {3}
columnAddComment = `
ALTER TABLE {0} CHANGE COLUMN {1} {2} {3} COMMENT '{4}'
`;
+ getCharacterAndCollation = `
+ SELECT
+ CHARACTER_SET_NAME AS 'name',
+ GROUP_CONCAT(COLLATION_NAME ORDER BY COLLATION_NAME ASC) AS 'values'
+ FROM information_schema.COLLATIONS
+ GROUP BY CHARACTER_SET_NAME
+ ORDER BY CHARACTER_SET_NAME ASC
+ `;
+ databaseRename: string;
}
diff --git a/src/renderer/config/plugin/postgresql.config.ts b/src/renderer/config/plugin/postgresql.config.ts
index 2642b77f..4bce7aec 100644
--- a/src/renderer/config/plugin/postgresql.config.ts
+++ b/src/renderer/config/plugin/postgresql.config.ts
@@ -7,7 +7,7 @@ export class PostgresqlConfig implements BaseConfig {
table_schema AS "database", table_name AS tableName, column_name AS name,
data_type AS type
FROM information_schema.columns
- WHERE table_schema = '{0}' AND table_name = '{1}'
+ WHERE table_name = '{0}'
`;
connectionFetchAll: string;
databaseCreate = `CREATE DATABASE {0}`;
@@ -68,9 +68,15 @@ export class PostgresqlConfig implements BaseConfig {
AND table_schema = 'public'
`;
tableItems = `
- SELECT table_schema AS "database", TABLE_NAME AS name
+-- SELECT table_schema AS "database", TABLE_NAME AS name
+-- FROM information_schema.tables
+-- WHERE table_catalog = '{0}'
+-- AND table_type = 'BASE TABLE'
+-- AND table_schema = 'public'
+ SELECT table_name AS name
FROM information_schema.tables
- WHERE table_schema = '{0}'
+ WHERE table_type = 'BASE TABLE'
+ AND table_schema = 'public'
`;
tableItemsFilterFuzzy: string;
tableItemsFilterPrecise: string;
@@ -89,4 +95,6 @@ export class PostgresqlConfig implements BaseConfig {
`;
columnRename: string;
columnAddComment: string;
+ getCharacterAndCollation: string;
+ databaseRename = `ALTER DATABASE "{0}" RENAME TO "{1}"`;
}
diff --git a/src/renderer/config/plugin/presto.config.ts b/src/renderer/config/plugin/presto.config.ts
index 03879422..c4eea542 100644
--- a/src/renderer/config/plugin/presto.config.ts
+++ b/src/renderer/config/plugin/presto.config.ts
@@ -1,6 +1,7 @@
import { BaseConfig } from "@renderer/config/base.config";
export class PrestoConfig implements BaseConfig {
+ getCharacterAndCollation: string;
version = `
SELECT node_version AS version
FROM system.runtime.nodes LIMIT 1
@@ -66,4 +67,5 @@ export class PrestoConfig implements BaseConfig {
showTableWithSize: string;
columnRename: string;
columnAddComment: string;
+ databaseRename: string;
}
diff --git a/src/renderer/config/postgresql.config.ts b/src/renderer/config/postgresql.config.ts
new file mode 100644
index 00000000..4ab9d369
--- /dev/null
+++ b/src/renderer/config/postgresql.config.ts
@@ -0,0 +1,21 @@
+import { ConfigInterface } from "@renderer/interfaces/config.interface";
+import { DatabaseEnum } from "@renderer/enum/database.enum";
+import { Injectable } from "@angular/core";
+import { PostgresqlConfig } from "@renderer/config/plugin/postgresql.config";
+
+@Injectable()
+export class PostgreSQLConfig implements ConfigInterface {
+ private readonly config: PostgresqlConfig;
+
+ constructor() {
+ this.config = new PostgresqlConfig();
+ }
+
+ getName(): DatabaseEnum {
+ return DatabaseEnum.postgresql;
+ }
+
+ getStatement(key: string): string {
+ return this.config[key];
+ }
+}
diff --git a/src/renderer/config/source.type.config.ts b/src/renderer/config/source.type.config.ts
index 422bc298..16b4ef1f 100644
--- a/src/renderer/config/source.type.config.ts
+++ b/src/renderer/config/source.type.config.ts
@@ -3,6 +3,7 @@ import {DatabaseEnum} from '@renderer/enum/database.enum';
import {DatabaseModel} from '@renderer/model/database.model';
import {StringUtils} from '@renderer/utils/string.utils';
import {TranslateUtils} from '@renderer/utils/translate.utils';
+import { HologresDataSource } from "@renderer/config/source/source.hologres";
@Injectable()
export class SourceTypeConfig {
@@ -69,6 +70,18 @@ export class SourceTypeConfig {
true,
null,
'./renderer/assets/icon/source/Druid.svg'));
+ // ElasticSearch
+ experimentalEngines.push(DatabaseModel.builder(TranslateUtils.getValue('common.elasticsearch'),
+ TranslateUtils.getValue('tooltip.source.elasticsearch'),
+ DatabaseEnum.elasticsearch,
+ null,
+ true,
+ null,
+ './renderer/assets/icon/source/ElasticSearch.svg'));
+
+ // Hologres
+ experimentalEngines.push(HologresDataSource);
+
experimentalType.engines = experimentalEngines;
typeEngines.push(basicType, experimentalType);
diff --git a/src/renderer/config/source/source.hologres.ts b/src/renderer/config/source/source.hologres.ts
new file mode 100644
index 00000000..9fd4d762
--- /dev/null
+++ b/src/renderer/config/source/source.hologres.ts
@@ -0,0 +1,13 @@
+import { DatabaseModel } from "@renderer/model/database.model";
+import { TranslateUtils } from "@renderer/utils/translate.utils";
+import { DatabaseEnum } from "@renderer/enum/database.enum";
+
+const HologresDataSource = DatabaseModel.builder(TranslateUtils.getValue('common.hologres'),
+ TranslateUtils.getValue('tooltip.source.hologres'),
+ DatabaseEnum.hologres,
+ null,
+ true,
+ null,
+ './renderer/assets/icon/source/Hologres.svg');
+
+export { HologresDataSource };
diff --git a/src/renderer/config/table.config.ts b/src/renderer/config/table.config.ts
index d610777c..edb23407 100644
--- a/src/renderer/config/table.config.ts
+++ b/src/renderer/config/table.config.ts
@@ -5,6 +5,7 @@ import { StringUtils } from '@renderer/utils/string.utils';
import { TranslateUtils } from '@renderer/utils/translate.utils';
import { PropertyModel } from '@renderer/model/property.model';
import { PropertyEnum } from '@renderer/enum/property.enum';
+import { HiveTableEngine } from "@renderer/config/engine/table/engine.table.hive.config";
@Injectable()
export class TableConfig {
@@ -258,6 +259,53 @@ export class TableConfig {
mongodb.supportedSource = [DatabaseEnum.clickhosue];
integrationEngines.push(mongodb);
+ // MySQL
+ const mysqlProperties = new Array();
+ mysqlProperties.push(PropertyModel.builder('uri',
+ TranslateUtils.getValue('common.uri'),
+ TranslateUtils.getValue('placeholder.uri'),
+ TranslateUtils.getValue('tooltip.property.mongodb.uri'),
+ null,
+ false,
+ true));
+ mysqlProperties.push(PropertyModel.builder('database',
+ TranslateUtils.getValue('common.database'),
+ TranslateUtils.getValue('placeholder.database'),
+ TranslateUtils.getValue('tooltip.property.database'),
+ null,
+ false));
+ mysqlProperties.push(PropertyModel.builder('table',
+ TranslateUtils.getValue('common.table'),
+ TranslateUtils.getValue('placeholder.table'),
+ TranslateUtils.getValue('tooltip.property.table'),
+ null,
+ false));
+ mysqlProperties.push(PropertyModel.builder('username',
+ TranslateUtils.getValue('common.username'),
+ TranslateUtils.getValue('tooltip.property.username'),
+ TranslateUtils.getValue('tooltip.property.username'),
+ null,
+ false,
+ true));
+ mysqlProperties.push(PropertyModel.builder('password',
+ TranslateUtils.getValue('common.password'),
+ TranslateUtils.getValue('tooltip.property.password'),
+ TranslateUtils.getValue('tooltip.property.password'),
+ null,
+ false,
+ true));
+ const mysql = DatabaseModel.builder(DatabaseEnum.mysql.toString(),
+ TranslateUtils.getValue('tooltip.table.mysql'),
+ DatabaseEnum.mysql,
+ mysqlProperties,
+ false,
+ PropertyEnum.name);
+ mysql.supportedSource = [DatabaseEnum.clickhosue];
+ integrationEngines.push(mysql);
+
+ // Hive
+ integrationEngines.push(HiveTableEngine);
+
integrationTable.engines = integrationEngines;
tableEngines.push(integrationTable);
return tableEngines;
diff --git a/src/renderer/enum/database.enum.ts b/src/renderer/enum/database.enum.ts
index 900ba4b6..f773e23a 100644
--- a/src/renderer/enum/database.enum.ts
+++ b/src/renderer/enum/database.enum.ts
@@ -16,11 +16,14 @@ export enum DatabaseEnum {
sqlite = 'SQLite',
odbc = 'ODBC',
mongodb = 'MongoDB',
+ hive = 'Hive',
// DataSource Type
clickhosue = 'ClickHouse',
trino = 'Trino',
presto = 'Presto',
postgresql = 'PostgreSQL',
- druid = 'Druid'
+ druid = 'Druid',
+ elasticsearch = 'ElasticSearch',
+ hologres = 'Hologres'
}
diff --git a/src/renderer/factory/config.factory.ts b/src/renderer/factory/config.factory.ts
new file mode 100644
index 00000000..c0fddb04
--- /dev/null
+++ b/src/renderer/factory/config.factory.ts
@@ -0,0 +1,14 @@
+import { DatabaseEnum } from "@renderer/enum/database.enum";
+import { Inject, Injectable } from "@angular/core";
+import { ConfigToken } from "@renderer/token/config.token";
+import { ConfigInterface } from "@renderer/interfaces/config.interface";
+
+@Injectable()
+export class ConfigFactory {
+ constructor(@Inject(ConfigToken) private configs: ConfigInterface[]) {
+ }
+
+ createConfig(name: DatabaseEnum) {
+ return this.configs.filter(item => item.getName() === name)[0];
+ }
+}
diff --git a/src/renderer/interfaces/config.interface.ts b/src/renderer/interfaces/config.interface.ts
new file mode 100644
index 00000000..c326b14c
--- /dev/null
+++ b/src/renderer/interfaces/config.interface.ts
@@ -0,0 +1,11 @@
+import { DatabaseEnum } from "@renderer/enum/database.enum";
+
+export interface ConfigInterface {
+ getName(): DatabaseEnum;
+
+ /**
+ * Get SQL statement
+ * @param key statement key
+ */
+ getStatement(key: string): string;
+}
diff --git a/src/renderer/model/database.model.ts b/src/renderer/model/database.model.ts
index 5caf8872..2ec67a17 100644
--- a/src/renderer/model/database.model.ts
+++ b/src/renderer/model/database.model.ts
@@ -30,6 +30,29 @@ export class DatabaseModel {
optionalProperties: PropertyModel[];
supportedSource: DatabaseEnum[] = [DatabaseEnum.clickhosue];
+ /**
+ * Optional parameter. Whether zone configuration is supported
+ */
+ partitionConfigure = {
+ enable: false,
+ columns: []
+ }
+
+ /**
+ * Database character set and collation rules
+ */
+ characterAndCollationConfigure = {
+ enable: false,
+ characterSetConfigure: {
+ enable: false,
+ value: null
+ },
+ collationConfigure: {
+ enable: false,
+ value: null
+ }
+ }
+
public static builder(name: string,
description: string,
type: DatabaseEnum,
diff --git a/src/renderer/plugin/clickhouse.plugin.ts b/src/renderer/plugin/clickhouse.plugin.ts
index 225caced..6c75afce 100644
--- a/src/renderer/plugin/clickhouse.plugin.ts
+++ b/src/renderer/plugin/clickhouse.plugin.ts
@@ -14,8 +14,9 @@ import { UrlUtils } from "@renderer/utils/url.utils";
export class ClickHousePlugin implements PluginInterface {
constructor(private httpService: HttpService,
- private sshService: SshService,
- private basicService: BasicService) { }
+ private sshService: SshService,
+ private basicService: BasicService) {
+ }
getName(): DatabaseEnum {
return DatabaseEnum.clickhosue;
@@ -29,6 +30,7 @@ export class ClickHousePlugin implements PluginInterface {
const configure = request.config;
switch (configure.protocol) {
case 'HTTP':
+ case 'HTTPS':
return this.httpService.post(UrlUtils.formatUrl(request), sql);
case 'SSH':
const basicConfig = this.getConfig();
@@ -49,4 +51,4 @@ export class ClickHousePlugin implements PluginInterface {
return Promise.reject(new Error('Unsupported protocol'));
}
}
-}
\ No newline at end of file
+}
diff --git a/src/renderer/plugin/elasticsearch.plugin.ts b/src/renderer/plugin/elasticsearch.plugin.ts
new file mode 100644
index 00000000..ed5913ee
--- /dev/null
+++ b/src/renderer/plugin/elasticsearch.plugin.ts
@@ -0,0 +1,128 @@
+import { PluginInterface } from "@renderer/interfaces/plugin.interface";
+import { Injectable } from "@angular/core";
+import { DatabaseEnum } from "@renderer/enum/database.enum";
+import { HttpService } from "@renderer/services/http.service";
+import { SshService } from "@renderer/services/ssh.service";
+import { BasicService } from "@renderer/services/system/basic.service";
+import { ResponseDataModel, ResponseModel } from "@renderer/model/response.model";
+import { RequestModel } from "@renderer/model/request.model";
+import { SystemBasicModel } from "@renderer/model/system.model";
+import { timeout, TimeoutError } from 'promise-timeout';
+
+const http = require('http');
+
+@Injectable()
+export class ElasticsearchPlugin implements PluginInterface {
+ constructor(private httpService: HttpService,
+ private sshService: SshService,
+ private basicService: BasicService) {
+ }
+
+ getName(): DatabaseEnum {
+ return DatabaseEnum.elasticsearch;
+ }
+
+ getResponse(configure: RequestModel, sql?: string): Promise
{
+ const network = this.getConfig().network * 1000;
+ const response = new ResponseModel();
+ const start = new Date().getTime();
+ const somePromise = new Promise((resolve) => {
+ const body = {query: sql};
+ const options = {
+ hostname: configure.config.host,
+ port: configure.config.port,
+ path: configure.config.url,
+ method: 'POST',
+ headers: {'Content-Type': 'application/json'}
+ }
+ const req = http.request(options, (response) => {
+ const bodyArray = [];
+ response.on('data', (chunk) => {
+ bodyArray.push(chunk);
+ })
+ .on('error', function (err) {
+ console.log('HTTP :: ERROR: ' + err);
+ response.status = false;
+ response.message = err;
+ resolve(response);
+ })
+ .on('end', function () {
+ if (response.statusCode === 200) {
+ const body = JSON.parse(Buffer.concat(bodyArray).toString());
+ const responseData = new ResponseDataModel();
+ responseData.headers = body['columns'];
+ const rows = new Array();
+ /** Parse Elasticsearch to return the original data, the original format is
+ {
+ "columns": [
+ {
+ "name": "name",
+ "type": "keyword"
+ },
+ {
+ "name": "type",
+ "type": "keyword"
+ }
+ ],
+ "rows": [
+ [
+ "AVG",
+ "AGGREGATE"
+ ]
+ ]
+ }
+ */
+ const keys = Object.keys(body['columns']);
+ body['rows'].forEach(row => {
+ const column = {};
+ keys.forEach(index => {
+ const title = responseData.headers[index].name;
+ column[title] = row[index];
+ });
+ rows.push(column);
+ });
+ responseData.columns = rows;
+ responseData.rows = responseData.columns.length;
+ const end = new Date().getTime();
+ const statistics = {
+ elapsed: end - start
+ };
+ responseData.statistics = statistics;
+ response.data = responseData;
+ response.status = true;
+ } else {
+ response.status = false;
+ const errorJson = JSON.parse(Buffer.concat(bodyArray).toString());
+ response.message = errorJson['error']['reason'];
+ }
+ resolve(response);
+ });
+ })
+
+ req.on('error', error => {
+ const response = new ResponseModel();
+ response.status = false;
+ response.message = error;
+ resolve(response);
+ })
+ req.end(JSON.stringify(body));
+ });
+ return timeout(somePromise, network)
+ .then((thing) => {
+ return thing;
+ })
+ .catch((err) => {
+ response.status = false;
+ if (err instanceof TimeoutError) {
+ response.message = `Promise timed out after ${this.getConfig().network} ms`;
+ } else {
+ response.message = err;
+ }
+ return response;
+ });
+ }
+
+ private getConfig(): SystemBasicModel {
+ return this.basicService.get() === null ? new SystemBasicModel() : this.basicService.get();
+ }
+}
diff --git a/src/renderer/plugin/mysql.plugin.ts b/src/renderer/plugin/mysql.plugin.ts
new file mode 100644
index 00000000..db369f04
--- /dev/null
+++ b/src/renderer/plugin/mysql.plugin.ts
@@ -0,0 +1,77 @@
+import { PluginInterface } from "@renderer/interfaces/plugin.interface";
+import { Injectable } from "@angular/core";
+import { DatabaseEnum } from "@renderer/enum/database.enum";
+import { ResponseDataModel, ResponseModel } from "@renderer/model/response.model";
+import { RequestModel } from "@renderer/model/request.model";
+import { SystemBasicModel } from "@renderer/model/system.model";
+import { BasicService } from "@renderer/services/system/basic.service";
+import { timeout, TimeoutError } from 'promise-timeout';
+
+const mysql = require('mysql');
+
+@Injectable()
+export class MysqlPlugin implements PluginInterface {
+
+ constructor(private basicService: BasicService) {
+ }
+
+ getName(): DatabaseEnum {
+ return DatabaseEnum.mysql;
+ }
+
+ private getConfig(): SystemBasicModel {
+ return this.basicService.get() === null ? new SystemBasicModel() : this.basicService.get();
+ }
+
+ getResponse(request: RequestModel, sql?: string): Promise {
+ const configure = request.config;
+ const start = new Date().getTime();
+ const network = this.getConfig().network * 1000;
+ const connection = mysql.createConnection({
+ host: configure.host,
+ port: configure.port,
+ user: configure.username,
+ password: configure.password,
+ database: configure.database
+ });
+ const response = new ResponseModel();
+ const responseData = new ResponseDataModel();
+ const somePromise = new Promise((resolve) => {
+ connection.connect();
+ connection.query(sql, function (error, results, fields) {
+ if (error) {
+ response.status = false;
+ response.message = error?.message;
+ resolve(response);
+ } else {
+ responseData.headers = fields;
+ responseData.columns = results;
+ const end = new Date().getTime();
+ const statistics = {
+ elapsed: end - start
+ };
+ responseData.statistics = statistics;
+ responseData.rows = results.length;
+ response.status = true;
+ response.data = responseData;
+ resolve(response);
+ }
+ connection.end();
+ console.log('Close connection');
+ });
+ });
+ return timeout(somePromise, network)
+ .then((thing) => {
+ return thing;
+ })
+ .catch((err) => {
+ response.status = false;
+ if (err instanceof TimeoutError) {
+ response.message = `Promise timed out after ${this.getConfig().network} ms`;
+ } else {
+ response.message = err;
+ }
+ return response;
+ });
+ }
+}
diff --git a/src/renderer/plugin/postgresql.plugin.ts b/src/renderer/plugin/postgresql.plugin.ts
new file mode 100644
index 00000000..09d3344f
--- /dev/null
+++ b/src/renderer/plugin/postgresql.plugin.ts
@@ -0,0 +1,91 @@
+import { PluginInterface } from "@renderer/interfaces/plugin.interface";
+import { DatabaseEnum } from "@renderer/enum/database.enum";
+import { Injectable } from "@angular/core";
+import { BasicService } from "@renderer/services/system/basic.service";
+import { SystemBasicModel } from "@renderer/model/system.model";
+import { RequestModel } from "@renderer/model/request.model";
+import { ResponseDataModel, ResponseModel } from "@renderer/model/response.model";
+import { StringUtils } from "@renderer/utils/string.utils";
+import { timeout, TimeoutError } from 'promise-timeout';
+
+const {Client} = require('pg');
+
+@Injectable()
+export class PostgreSQLPlugin implements PluginInterface {
+ constructor(private basicService: BasicService) {
+ }
+
+ private getConfig(): SystemBasicModel {
+ return this.basicService.get() === null ? new SystemBasicModel() : this.basicService.get();
+ }
+
+ getName(): DatabaseEnum {
+ return DatabaseEnum.postgresql;
+ }
+
+ getResponse(request: RequestModel, sql?: string): Promise {
+ const configure = request.config;
+ const start = new Date().getTime();
+ const network = this.getConfig().network * 1000;
+ const hasAuthentication = (StringUtils.isNotEmpty(configure.username) && StringUtils.isNotEmpty(configure.password));
+ let connection;
+ if (hasAuthentication) {
+ connection = new Client({
+ host: configure.host,
+ port: configure.port,
+ user: configure.username,
+ password: configure.password,
+ database: configure.database
+ });
+ } else {
+ connection = new Client({
+ host: configure.host,
+ port: configure.port,
+ database: configure.database
+ });
+ }
+ const response = new ResponseModel();
+ const responseData = new ResponseDataModel();
+ const somePromise = new Promise((resolve) => {
+ connection.connect().catch(error => {
+ response.status = false;
+ response.message = error?.message;
+ resolve(response);
+ })
+ connection.query(sql, function (error, results) {
+ if (error) {
+ response.status = false;
+ response.message = error?.message;
+ resolve(response);
+ } else {
+ responseData.headers = results?.fields;
+ responseData.columns = results?.rows;
+ const end = new Date().getTime();
+ const statistics = {
+ elapsed: end - start
+ };
+ responseData.statistics = statistics;
+ responseData.rows = results?.rowCount;
+ response.status = true;
+ response.data = responseData;
+ resolve(response);
+ }
+ connection.end();
+ console.log('Connection ', configure.host, ' is closed');
+ });
+ });
+ return timeout(somePromise, network)
+ .then((thing) => {
+ return thing;
+ })
+ .catch((err) => {
+ response.status = false;
+ if (err instanceof TimeoutError) {
+ response.message = `Promise timed out after ${this.getConfig().network} ms`;
+ } else {
+ response.message = err;
+ }
+ return response;
+ });
+ }
+}
diff --git a/src/renderer/services/builder/base.builder.ts b/src/renderer/services/builder/base.builder.ts
index b7d6f70f..1cc976a7 100644
--- a/src/renderer/services/builder/base.builder.ts
+++ b/src/renderer/services/builder/base.builder.ts
@@ -1,6 +1,46 @@
import { DatabaseModel } from "@renderer/model/database.model";
import { ColumnModel } from "@renderer/model/column.model";
+import { StringUtils } from "@renderer/utils/string.utils";
+import { SqlUtils } from "@renderer/utils/sql.utils";
-export interface BaseBuilder {
- builder(configure: DatabaseModel, columns: ColumnModel[]): string;
+export abstract class BaseBuilder {
+ abstract builder(configure: DatabaseModel, columns: ColumnModel[]): string;
+
+ builderPrefix(configure: DatabaseModel): string {
+ return StringUtils.format('CREATE TABLE {0} (\n',
+ [SqlUtils.getTableName(configure.database, configure.targetName)]);
+ }
+
+ builderColumns(columns: ColumnModel[]): string {
+ let columnStr = '';
+ columns.forEach((value, index) => {
+ if (index !== columns.length - 1) {
+ columnStr += this.builderColumnToString(value, true);
+ } else {
+ columnStr += this.builderColumnToString(value, false);
+ }
+ });
+ return columnStr;
+ }
+
+ builderColumnToString(value: ColumnModel, end: boolean): string {
+ let column: string;
+ let dStr: string;
+ if (value.empty) {
+ dStr = StringUtils.format(' {0} {1} NOT NULL', [value.name, value.type]);
+ } else {
+ dStr = StringUtils.format(' {0} {1}', [value.name, value.type]);
+ }
+ const endStr = end ? ',\n' : '';
+ if (StringUtils.isNotEmpty(value.description)) {
+ column = StringUtils.format(` {0} COMMENT '{1}' {2}`, [dStr, value.description, endStr]);
+ } else {
+ column = StringUtils.format(' {0} {1}', [dStr, endStr]);
+ }
+ return column;
+ }
+
+ builderSuffix(): string {
+ return ')';
+ }
}
diff --git a/src/renderer/services/builder/clickhouse.builder.ts b/src/renderer/services/builder/clickhouse.builder.ts
index 061fb673..ca6823f0 100644
--- a/src/renderer/services/builder/clickhouse.builder.ts
+++ b/src/renderer/services/builder/clickhouse.builder.ts
@@ -6,13 +6,16 @@ import { PropertyModel } from "@renderer/model/property.model";
import { DatabaseModel } from "@renderer/model/database.model";
import { PropertyEnum } from "@renderer/enum/property.enum";
-export class ClickhouseBuilder implements BaseBuilder {
+export class ClickhouseBuilder extends BaseBuilder {
public builder(configure: DatabaseModel, columns: ColumnModel[]): string {
let sql = StringUtils.format('CREATE TABLE {0} (\n', [SqlUtils.getTableName(configure.database, configure.targetName)]);
sql += StringUtils.format('{0}\n', [this.builderColumnsToString(columns)]);
sql += StringUtils.format(') {0}\n', [this.builderEngine(configure)]);
const mergeProperties = this.mergeProperties(configure);
sql += this.builderProperties(mergeProperties);
+ if (configure.partitionConfigure.enable) {
+ sql += this.builderPartition(configure);
+ }
return sql;
}
@@ -121,4 +124,10 @@ export class ClickhouseBuilder implements BaseBuilder {
}
return applyArray;
}
+
+ private builderPartition(configure: DatabaseModel): string {
+ let partition = configure.partitionConfigure.columns.join(' , ');
+ const partitionStr = StringUtils.format('\nPARTITION BY ({0})', [partition]);
+ return partitionStr;
+ }
}
diff --git a/src/renderer/services/builder/default.builder.ts b/src/renderer/services/builder/default.builder.ts
index d2975118..efb0f17e 100644
--- a/src/renderer/services/builder/default.builder.ts
+++ b/src/renderer/services/builder/default.builder.ts
@@ -1,52 +1,12 @@
import { BaseBuilder } from "@renderer/services/builder/base.builder";
import { DatabaseModel } from "@renderer/model/database.model";
import { ColumnModel } from "@renderer/model/column.model";
-import { StringUtils } from "@renderer/utils/string.utils";
-import { SqlUtils } from "@renderer/utils/sql.utils";
-export class DefaultBuilder implements BaseBuilder {
+export class DefaultBuilder extends BaseBuilder {
builder(configure: DatabaseModel, columns: ColumnModel[]): string {
let sql = this.builderPrefix(configure);
sql += this.builderColumns(columns);
sql += this.builderSuffix();
return sql;
}
-
- builderPrefix(configure: DatabaseModel): string {
- return StringUtils.format('CREATE TABLE {0} (\n',
- [SqlUtils.getTableName(configure.database, configure.targetName)]);
- }
-
- builderColumns(columns: ColumnModel[]): string {
- let columnStr = '';
- columns.forEach((value, index) => {
- if (index !== columns.length - 1) {
- columnStr += this.builderColumnToString(value, true);
- } else {
- columnStr += this.builderColumnToString(value, false);
- }
- });
- return columnStr;
- }
-
- builderColumnToString(value: ColumnModel, end: boolean): string {
- let column: string;
- let dStr: string;
- if (value.empty) {
- dStr = StringUtils.format(' {0} {1} NOT NULL', [value.name, value.type]);
- } else {
- dStr = StringUtils.format(' {0} {1}', [value.name, value.type]);
- }
- const endStr = end ? ',\n' : '';
- if (StringUtils.isNotEmpty(value.description)) {
- column = StringUtils.format(` {0} COMMENT '{1}' {2}`, [dStr, value.description, endStr]);
- } else {
- column = StringUtils.format(' {0} {1}', [dStr, endStr]);
- }
- return column;
- }
-
- builderSuffix(): string {
- return ')';
- }
}
diff --git a/src/renderer/services/builder/postgresql.builder.ts b/src/renderer/services/builder/postgresql.builder.ts
new file mode 100644
index 00000000..a1de5baf
--- /dev/null
+++ b/src/renderer/services/builder/postgresql.builder.ts
@@ -0,0 +1,17 @@
+import { BaseBuilder } from "@renderer/services/builder/base.builder";
+import { DatabaseModel } from "@renderer/model/database.model";
+import { ColumnModel } from "@renderer/model/column.model";
+import { StringUtils } from "@renderer/utils/string.utils";
+
+export class PostgresqlBuilder extends BaseBuilder {
+ builder(configure: DatabaseModel, columns: ColumnModel[]): string {
+ let sql = this.builderPrefix(configure);
+ sql += this.builderColumns(columns);
+ sql += this.builderSuffix();
+ return sql;
+ }
+
+ builderPrefix(configure: DatabaseModel): string {
+ return StringUtils.format('CREATE TABLE {0} (\n', [configure.targetName]);
+ }
+}
diff --git a/src/renderer/services/common/menu.common.service.ts b/src/renderer/services/common/menu.common.service.ts
index b299442d..01f23405 100644
--- a/src/renderer/services/common/menu.common.service.ts
+++ b/src/renderer/services/common/menu.common.service.ts
@@ -6,17 +6,20 @@ import { OperationEnum } from "@renderer/enum/operation.enum";
import { DatabaseEnum } from "@renderer/enum/database.enum";
import { ConfigModel } from "@renderer/model/config.model";
import { Injectable } from "@angular/core";
+import { TranslateService } from "@ngx-translate/core";
import { ClipboardComService } from "@renderer/services/other/clipboard.service";
+import { SqlUtils } from "@renderer/utils/sql.utils";
@Injectable()
export class MenuCommonService {
- constructor(private clipboardComService: ClipboardComService) {
+ constructor(private clipboardComService: ClipboardComService,
+ private translateService: TranslateService) {
}
public applyContextMenu(type: TypeEnum, configure: ConfigModel): MenuModel[] {
const menus = new Array();
const operationOfCopy = new OperationModel();
- operationOfCopy.name = StringUtils.format('Copy: {0}', [configure.value]);
+ operationOfCopy.name = StringUtils.format(this.translateService.instant('common.copy') + ': {0}', [configure.value]);
operationOfCopy.type = type;
operationOfCopy.icon = 'fa-files-o';
operationOfCopy.operations = [{
@@ -24,6 +27,22 @@ export class MenuCommonService {
actions: [OperationEnum.copy],
supportedSource: [DatabaseEnum.clickhosue]
}];
+
+ switch (type) {
+ case TypeEnum.table:
+ const operationOfDrop = new OperationModel();
+ operationOfDrop.name = StringUtils.format('{0} {1}', [this.translateService.instant('common.delete'), configure.value]);
+ operationOfDrop.type = type;
+ operationOfDrop.icon = 'fa-trash';
+ operationOfDrop.operations = [{
+ type: type,
+ actions: [OperationEnum.delete],
+ supportedSource: [DatabaseEnum.clickhosue]
+ }];
+ menus.push(operationOfDrop);
+ break;
+ }
+
menus.push(operationOfCopy);
return menus;
}
@@ -34,6 +53,9 @@ export class MenuCommonService {
case OperationEnum.copy:
this.clipboardComService.copy(configure.value);
break;
+ case OperationEnum.delete:
+ sql = StringUtils.format('DROP TABLE {0}', [SqlUtils.getTableName(configure.database, configure.value)]);
+ break;
}
return sql;
}
diff --git a/src/renderer/services/factory.service.ts b/src/renderer/services/factory.service.ts
index 209df0f8..47b77f64 100644
--- a/src/renderer/services/factory.service.ts
+++ b/src/renderer/services/factory.service.ts
@@ -7,6 +7,8 @@ import { ClickhouseBuilder } from "@renderer/services/builder/clickhouse.builder
import { PrestoConfig } from "@renderer/config/plugin/presto.config";
import { ClickhouseConfig } from "@renderer/config/plugin/clickhouse.config";
import { DruidConfig } from "@renderer/config/plugin/druid.config";
+import { ElasticsearchConfig } from "@renderer/config/plugin/elasticsearch.config";
+import { PostgresqlBuilder } from "@renderer/services/builder/postgresql.builder";
export class FactoryService {
public forward(type: string) {
@@ -19,9 +21,12 @@ export class FactoryService {
case DatabaseEnum.mysql:
return Factory.create(MySQLConfig);
case DatabaseEnum.postgresql:
+ case DatabaseEnum.hologres:
return Factory.create(PostgresqlConfig);
case DatabaseEnum.druid:
return Factory.create(DruidConfig);
+ case DatabaseEnum.elasticsearch:
+ return Factory.create(ElasticsearchConfig);
default:
new Error("Unsupported database type");
return null;
@@ -32,6 +37,8 @@ export class FactoryService {
switch (type) {
case DatabaseEnum.clickhosue:
return Factory.create(ClickhouseBuilder);
+ case DatabaseEnum.postgresql:
+ return Factory.create(PostgresqlBuilder);
default:
return Factory.create(DefaultBuilder);
}
diff --git a/src/renderer/services/forward.service.ts b/src/renderer/services/forward.service.ts
index ab008a93..b7be4093 100644
--- a/src/renderer/services/forward.service.ts
+++ b/src/renderer/services/forward.service.ts
@@ -11,6 +11,8 @@ import { DatabaseEnum } from "@renderer/enum/database.enum";
import { FactoryService } from "@renderer/services/factory.service";
import { MySQLService } from "@renderer/services/plugin/mysql.service";
import { PostgresqlService } from "@renderer/services/plugin/postgresql.service";
+import { PluginFactory } from "@renderer/factory/plugin.factory";
+import { ConfigFactory } from "@renderer/factory/config.factory";
export class ForwardService {
constructor(
@@ -20,7 +22,9 @@ export class ForwardService {
protected sshService: SshService,
protected prestoService?: PrestoService,
protected mysqlService?: MySQLService,
- protected postgresqlService?: PostgresqlService
+ protected postgresqlService?: PostgresqlService,
+ protected pluginFactory?: PluginFactory,
+ protected configFactory?: ConfigFactory
) {
}
@@ -32,6 +36,7 @@ export class ForwardService {
const configure = request.config;
switch (configure.protocol) {
case 'HTTP':
+ case 'HTTPS':
let response;
switch (configure.type) {
case DatabaseEnum.clickhosue:
@@ -45,11 +50,15 @@ export class ForwardService {
response = this.mysqlService.execute(configure, sql);
break
case DatabaseEnum.postgresql:
+ case DatabaseEnum.hologres:
response = this.postgresqlService.execute(configure, sql);
break
case DatabaseEnum.druid:
response = this.httpService.post(UrlUtils.formatUrl(request), sql, true);
break
+ case DatabaseEnum.elasticsearch:
+ response = this.pluginFactory.createService(configure.type).getResponse(request, sql);
+ break;
}
return response;
case 'SSH':
diff --git a/src/renderer/services/management/collation.service.ts b/src/renderer/services/management/collation.service.ts
new file mode 100644
index 00000000..b960f93f
--- /dev/null
+++ b/src/renderer/services/management/collation.service.ts
@@ -0,0 +1,23 @@
+import { BaseService } from "@renderer/services/base.service";
+import { ResponseModel } from "@renderer/model/response.model";
+import { PluginFactory } from "@renderer/factory/plugin.factory";
+import { Injectable } from "@angular/core";
+import { RequestModel } from "@renderer/model/request.model";
+import { ConfigFactory } from "@renderer/factory/config.factory";
+
+@Injectable()
+export class CollationService implements BaseService {
+
+ constructor(private pluginFactory: PluginFactory,
+ private configFactory: ConfigFactory) {
+ }
+
+ getResponse(request: RequestModel, sql?: string): Promise {
+ return this.pluginFactory.createService(request.config.type).getResponse(request, sql);
+ }
+
+ getCharacterAndCollation(request: RequestModel): Promise {
+ const source = this.configFactory.createConfig(request.config.type).getStatement('getCharacterAndCollation');
+ return this.getResponse(request, source);
+ }
+}
diff --git a/src/renderer/services/management/database.service.ts b/src/renderer/services/management/database.service.ts
index 43e063a9..e35f84b1 100644
--- a/src/renderer/services/management/database.service.ts
+++ b/src/renderer/services/management/database.service.ts
@@ -10,6 +10,8 @@ import { ForwardService } from '@renderer/services/forward.service';
import { FactoryService } from "@renderer/services/factory.service";
import { MySQLService } from "@renderer/services/plugin/mysql.service";
import { PostgresqlService } from "@renderer/services/plugin/postgresql.service";
+import { PluginFactory } from "@renderer/factory/plugin.factory";
+import { ConfigFactory } from "@renderer/factory/config.factory";
@Injectable()
export class DatabaseService extends ForwardService implements BaseService {
@@ -18,12 +20,14 @@ export class DatabaseService extends ForwardService implements BaseService {
sshService: SshService,
basicService: BasicService,
mysqlService: MySQLService,
- postgresqlService: PostgresqlService) {
- super(basicService, factoryService, httpService, sshService, null, mysqlService, postgresqlService);
+ postgresqlService: PostgresqlService,
+ pluginFactory: PluginFactory,
+ configFactory: ConfigFactory) {
+ super(basicService, factoryService, httpService, sshService, null, mysqlService, postgresqlService, pluginFactory, configFactory);
}
getResponse(request: RequestModel, sql?: string): Promise {
- return this.forward(request, sql);
+ return this.pluginFactory.createService(request.config.type).getResponse(request, sql);
}
getAll(request: RequestModel): Promise {
@@ -48,7 +52,8 @@ export class DatabaseService extends ForwardService implements BaseService {
}
rename(request: RequestModel, source: string, target: string): Promise {
- const sql = StringUtils.format('RENAME DATABASE `{0}` TO `{1}`', [source, target]);
+ const sourceSql = this.configFactory.createConfig(request.config.type).getStatement('databaseRename');
+ const sql = StringUtils.format(sourceSql, [source, target]);
return this.getResponse(request, sql);
}
}
diff --git a/src/renderer/services/management/datasource.service.ts b/src/renderer/services/management/datasource.service.ts
index 1c7e703c..6234fa98 100644
--- a/src/renderer/services/management/datasource.service.ts
+++ b/src/renderer/services/management/datasource.service.ts
@@ -13,6 +13,7 @@ import { PrestoService } from "@renderer/services/presto.service";
import { FactoryService } from "@renderer/services/factory.service";
import { MySQLService } from "@renderer/services/plugin/mysql.service";
import { PostgresqlService } from "@renderer/services/plugin/postgresql.service";
+import { PluginFactory } from "@renderer/factory/plugin.factory";
@Injectable()
export class DatasourceService extends PersistenceService implements BaseService {
@@ -25,8 +26,9 @@ export class DatasourceService extends PersistenceService implements BaseService
sshService: SshService,
prestoService: PrestoService,
mysqlService: MySQLService,
- postgresqlService: PostgresqlService) {
- super(basicService, factoryService, httpService, sshService, prestoService, mysqlService, postgresqlService);
+ postgresqlService: PostgresqlService,
+ pluginFactory: PluginFactory) {
+ super(basicService, factoryService, httpService, sshService, prestoService, mysqlService, postgresqlService, pluginFactory);
this.db = new DexieDb();
}
diff --git a/src/renderer/services/management/metadata.service.ts b/src/renderer/services/management/metadata.service.ts
index f25808ed..c49b30fa 100644
--- a/src/renderer/services/management/metadata.service.ts
+++ b/src/renderer/services/management/metadata.service.ts
@@ -112,9 +112,26 @@ export class MetadataService extends ForwardService implements BaseService {
case DatabaseEnum.mysql:
case DatabaseEnum.materialized_mysql:
case DatabaseEnum.materialized_postgresql:
+ case DatabaseEnum.postgresql:
suffix = this.builderDatabaseMySQL(database);
break;
}
+
+ if (request.config.type === DatabaseEnum.mysql) {
+ if (database.characterAndCollationConfigure.enable) {
+ if (database.characterAndCollationConfigure.characterSetConfigure.enable
+ && StringUtils.isNotEmpty(database.characterAndCollationConfigure.characterSetConfigure.value)) {
+ suffix += StringUtils.format(` CHARACTER SET '{0}'`,
+ [database.characterAndCollationConfigure.characterSetConfigure.value]);
+ }
+ if (database.characterAndCollationConfigure.collationConfigure.enable
+ && StringUtils.isNotEmpty(database.characterAndCollationConfigure.collationConfigure.value)) {
+ suffix += StringUtils.format(` COLLATE '{0}'`,
+ [database.characterAndCollationConfigure.collationConfigure.value]);
+ }
+ }
+ }
+
return this.getResponse(request, StringUtils.format('{0} {1}', [prefix, suffix]));
}
diff --git a/src/renderer/services/query/query.service.ts b/src/renderer/services/query/query.service.ts
index 3edcc37e..75117aa0 100644
--- a/src/renderer/services/query/query.service.ts
+++ b/src/renderer/services/query/query.service.ts
@@ -11,6 +11,7 @@ import { FactoryService } from "@renderer/services/factory.service";
import { PrestoService } from "@renderer/services/presto.service";
import { MySQLService } from "@renderer/services/plugin/mysql.service";
import { PostgresqlService } from "@renderer/services/plugin/postgresql.service";
+import { PluginFactory } from "@renderer/factory/plugin.factory";
const {Parser} = require('node-sql-parser');
@@ -22,8 +23,9 @@ export class QueryService extends ForwardService implements BaseService {
factoryService: FactoryService,
prestoService: PrestoService,
mysqlService: MySQLService,
- postgresqlService: PostgresqlService) {
- super(basicService, factoryService, httpService, sshService, prestoService, mysqlService, postgresqlService);
+ postgresqlService: PostgresqlService,
+ pluginFactory: PluginFactory) {
+ super(basicService, factoryService, httpService, sshService, prestoService, mysqlService, postgresqlService, pluginFactory);
}
getResponse(request: RequestModel, sql?: string): Promise {
diff --git a/src/renderer/token/config.token.ts b/src/renderer/token/config.token.ts
new file mode 100644
index 00000000..aaaddc15
--- /dev/null
+++ b/src/renderer/token/config.token.ts
@@ -0,0 +1,4 @@
+import { InjectionToken } from '@angular/core';
+import { ConfigInterface } from "@renderer/interfaces/config.interface";
+
+export const ConfigToken = new InjectionToken('');
diff --git a/src/renderer/utils/url.utils.ts b/src/renderer/utils/url.utils.ts
index 0ca47c0b..388716e0 100644
--- a/src/renderer/utils/url.utils.ts
+++ b/src/renderer/utils/url.utils.ts
@@ -38,4 +38,9 @@ export class UrlUtils {
}
return remoteUrl;
}
+
+ public static formatUrlWithHostAndPort(request: RequestModel): string {
+ const protocol = StringUtils.getValue(request.config.protocol, 'http');
+ return StringUtils.format('{0}://{1}:{2}', [protocol, request.config.host, request.config.port]);
+ }
}
diff --git a/src/shared/assets/integrate/elasticsearch.svg b/src/shared/assets/integrate/elasticsearch.svg
new file mode 100644
index 00000000..bb9a3a12
--- /dev/null
+++ b/src/shared/assets/integrate/elasticsearch.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/shared/assets/integrate/hologres.svg b/src/shared/assets/integrate/hologres.svg
new file mode 100644
index 00000000..0bf2084c
--- /dev/null
+++ b/src/shared/assets/integrate/hologres.svg
@@ -0,0 +1 @@
+
\ No newline at end of file