Skip to content

Commit 10c17e2

Browse files
authored
Merge pull request #1457 from lowcoder-org/feat/alasql
Feat/alasql
2 parents c3068a6 + 3a4ab7c commit 10c17e2

File tree

14 files changed

+231
-3
lines changed

14 files changed

+231
-3
lines changed

client/packages/lowcoder/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
"@types/react-signature-canvas": "^1.0.2",
3737
"@types/react-test-renderer": "^18.0.0",
3838
"@types/react-virtualized": "^9.21.21",
39+
"alasql": "^4.6.2",
3940
"animate.css": "^4.1.1",
4041
"antd": "^5.20.0",
4142
"axios": "^1.7.7",

client/packages/lowcoder/src/components/ResCreatePanel.tsx

+8
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,13 @@ const ResButton = (props: {
169169
compType: "streamApi",
170170
},
171171
},
172+
alasql: {
173+
label: trans("query.quickAlasql"),
174+
type: BottomResTypeEnum.Query,
175+
extra: {
176+
compType: "alasql",
177+
},
178+
},
172179
graphql: {
173180
label: trans("query.quickGraphql"),
174181
type: BottomResTypeEnum.Query,
@@ -319,6 +326,7 @@ export function ResCreatePanel(props: ResCreateModalProps) {
319326
<DataSourceListWrapper $placement={placement}>
320327
<ResButton size={buttonSize} identifier={"restApi"} onSelect={onSelect} />
321328
<ResButton size={buttonSize} identifier={"streamApi"} onSelect={onSelect} />
329+
<ResButton size={buttonSize} identifier={"alasql"} onSelect={onSelect} />
322330
<ResButton size={buttonSize} identifier={"graphql"} onSelect={onSelect} />
323331
{datasource.map((i) => (
324332
<ResButton size={buttonSize} key={i.id} identifier={i} onSelect={onSelect} />
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
import { QueryConfigItemWrapper, QueryConfigLabel, QueryConfigWrapper } from "components/query";
2+
import { simpleMultiComp } from "comps/generators/multi";
3+
import { JSONValue } from "../../../util/jsonTypes";
4+
import { ParamsStringControl } from "../../controls/paramsControl";
5+
import { dropdownControl } from "@lowcoder-ee/comps/controls/dropdownControl";
6+
import { QueryResult } from "../queryComp";
7+
import { QUERY_EXECUTION_ERROR, QUERY_EXECUTION_OK } from "@lowcoder-ee/constants/queryConstants";
8+
import { getDynamicStringSegments, isDynamicSegment } from "lowcoder-core";
9+
import alasql from "alasql";
10+
import { trans } from "i18n";
11+
12+
const childrenMap = {
13+
databaseType: dropdownControl(
14+
[
15+
{ label: "Data Query", value: "dataQuery" },
16+
{ label: "Local Database", value: "localDB" },
17+
] as const,
18+
"dataQuery"
19+
),
20+
database: dropdownControl(
21+
[
22+
{ label: "Local Storage", value: "LOCALSTORAGE" },
23+
{ label: "IndexedDB", value: "INDEXEDDB" },
24+
] as const,
25+
"LOCALSTORAGE"
26+
),
27+
sql: ParamsStringControl,
28+
};
29+
30+
const AlaSqlTmpQuery = simpleMultiComp(childrenMap);
31+
32+
// TODO: Support multiple queries
33+
export class AlaSqlQuery extends AlaSqlTmpQuery {
34+
override getView() {
35+
const children = this.children;
36+
const params = [ ...children.sql.getQueryParams() ];
37+
const databaseType = children.databaseType.getView();
38+
const selectedDB = children.database.getView();
39+
const paramsMap: Record<string, any> = {};
40+
params.forEach(({key, value}) => {
41+
paramsMap[key] = value();
42+
});
43+
44+
const sqlQuery = children.sql.children.text.unevaledValue.replace(/ +/g, ' ');
45+
const isCreateDBQuery = sqlQuery.toUpperCase().startsWith('CREATE DATABASE');
46+
47+
return async (p: { args?: Record<string, unknown> }): Promise<QueryResult> => {
48+
try {
49+
let result: JSONValue;
50+
const timer = performance.now();
51+
52+
if (databaseType === 'localDB' && isCreateDBQuery) {
53+
const updatedQuery = `${sqlQuery.slice(0, 6)} ${selectedDB} ${sqlQuery.slice(6)}`;
54+
const tableName = updatedQuery.split(' ').pop()?.replace(';', '');
55+
result = alasql(updatedQuery);
56+
result = alasql(`ATTACH ${selectedDB} DATABASE ${tableName};`);
57+
} else {
58+
let segments = getDynamicStringSegments(sqlQuery);
59+
let dataArr: any = [];
60+
segments = segments.map((segment) => {
61+
if (isDynamicSegment(segment)) {
62+
const key = segment.replace('{{','').replace('}}','');
63+
dataArr.push(paramsMap[key]);
64+
return '?';
65+
}
66+
return segment;
67+
})
68+
result = alasql(segments.join(' '), dataArr);
69+
}
70+
71+
return {
72+
data: result as JSONValue,
73+
code: QUERY_EXECUTION_OK,
74+
success: true,
75+
runTime: Number((performance.now() - timer).toFixed()),
76+
};
77+
} catch (e) {
78+
return {
79+
success: false,
80+
data: "",
81+
code: QUERY_EXECUTION_ERROR,
82+
message: (e as any).message || "",
83+
};
84+
}
85+
};
86+
}
87+
88+
propertyView(props: { datasourceId: string }) {
89+
return <PropertyView {...props} comp={this} />;
90+
}
91+
}
92+
93+
const PropertyView = (props: { comp: InstanceType<typeof AlaSqlQuery>; datasourceId: string }) => {
94+
const { comp } = props;
95+
const { children } = comp;
96+
97+
return (
98+
<>
99+
<QueryConfigWrapper>
100+
<QueryConfigLabel>{trans("query.databaseType")}</QueryConfigLabel>
101+
<QueryConfigItemWrapper>
102+
{children.databaseType.propertyView({
103+
styleName: "medium",
104+
width: "100%",
105+
})}
106+
</QueryConfigItemWrapper>
107+
</QueryConfigWrapper>
108+
109+
{children.databaseType.getView() === 'localDB' && (
110+
<QueryConfigWrapper>
111+
<QueryConfigLabel>{trans("query.chooseDatabase")}</QueryConfigLabel>
112+
<QueryConfigItemWrapper>
113+
{children.database.propertyView({
114+
styleName: "medium",
115+
width: "100%",
116+
})}
117+
</QueryConfigItemWrapper>
118+
</QueryConfigWrapper>
119+
)}
120+
121+
<QueryConfigWrapper>
122+
<QueryConfigItemWrapper>
123+
{children.sql.propertyView({
124+
placement: "bottom",
125+
placeholder: "SELECT * FROM users WHERE user_id = {{userId}}::uuid",
126+
styleName: "medium",
127+
language: "sql",
128+
enableMetaCompletion: true,
129+
})}
130+
</QueryConfigItemWrapper>
131+
</QueryConfigWrapper>
132+
</>
133+
);
134+
};

client/packages/lowcoder/src/comps/queries/queryComp/queryPropertyView.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -734,6 +734,7 @@ function useDatasourceStatus(datasourceId: string, datasourceType: ResourceType)
734734
datasourceType === "js" ||
735735
datasourceType === "streamApi" ||
736736
datasourceType === "libraryQuery" ||
737+
datasourceType === "alasql" ||
737738
datasourceId === QUICK_REST_API_ID ||
738739
datasourceId === QUICK_GRAPHQL_ID
739740
) {

client/packages/lowcoder/src/comps/queries/resourceDropdown.tsx

+16
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,11 @@ const QuickGraphqlValue: ResourceOptionValue = {
102102
type: "graphql",
103103
};
104104

105+
const QuickAlasqlValue: ResourceOptionValue = {
106+
id: "",
107+
type: "alasql",
108+
};
109+
105110
interface ResourceDropdownProps {
106111
changeResource: (datasourceId: string, value: string) => void;
107112
selectedResource: ResourceOptionValue;
@@ -265,6 +270,17 @@ export const ResourceDropdown = (props: ResourceDropdownProps) => {
265270
<SelectOptionLabel>{trans("query.quickStreamAPI")} </SelectOptionLabel>
266271
</SelectOptionContains>
267272
</SelectOption>
273+
274+
<SelectOption
275+
key={JSON.stringify(QuickAlasqlValue)}
276+
label={trans("query.quickAlasql")}
277+
value={JSON.stringify(QuickAlasqlValue)}
278+
>
279+
<SelectOptionContains>
280+
{getBottomResIcon("restApi")}
281+
<SelectOptionLabel>{trans("query.quickAlasql")} </SelectOptionLabel>
282+
</SelectOptionContains>
283+
</SelectOption>
268284

269285
<SelectOption
270286
key={JSON.stringify(QuickGraphqlValue)}

client/packages/lowcoder/src/comps/queries/sqlQuery/SQLQuery.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,7 @@ export const NOT_SUPPORT_GUI_SQL_QUERY: string[] = [
255255
"snowflake",
256256
"tdengine",
257257
"dameng",
258+
"alasql",
258259
];
259260
const SUPPORT_UPSERT_SQL_QUERY: string[] = [
260261
"mysql",

client/packages/lowcoder/src/constants/datasourceConstants.ts

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ export const databasePlugins: Partial<DatasourceType>[] = [
1515
"clickHouse",
1616
"snowflake",
1717
"mariadb",
18+
"alasql",
1819
];
1920

2021
export const apiPluginsForQueryLibrary: Partial<DatasourceType>[] = [
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
export const libNames = new Set(["uuid", "numbro", "Papa", "supabase"]);
1+
export const libNames = new Set(["uuid", "numbro", "Papa", "supabase", "alasql"]);

client/packages/lowcoder/src/constants/queryConstants.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { GraphqlQuery } from "../comps/queries/httpQuery/graphqlQuery";
1313
import { toPluginQuery } from "comps/queries/pluginQuery/pluginQuery";
1414
import { MultiCompConstructor } from "lowcoder-core";
1515
import { DataSourcePluginMeta } from "lowcoder-sdk/dataSource";
16+
import { AlaSqlQuery } from "@lowcoder-ee/comps/queries/httpQuery/alasqlQuery";
1617

1718
export type DatasourceType =
1819
| "mysql"
@@ -29,13 +30,15 @@ export type DatasourceType =
2930
| "googleSheets"
3031
| "graphql"
3132
| "snowflake"
32-
| "mariadb";
33+
| "mariadb"
34+
| "alasql";
3335

3436
export type ResourceType = DatasourceType | "js" | "libraryQuery" | "view";
3537

3638
export const QueryMap = {
3739
js: JSQuery,
3840
mysql: SQLQuery,
41+
alasql: AlaSqlQuery,
3942
restApi: HttpQuery,
4043
streamApi: StreamQuery,
4144
mongodb: MongoQuery,

client/packages/lowcoder/src/global.ts

+1
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,6 @@ declare global {
99
numbro: any;
1010
Papa: any;
1111
uuid: any;
12+
alasql: any;
1213
}
1314
}

client/packages/lowcoder/src/i18n/locales/en.ts

+3
Original file line numberDiff line numberDiff line change
@@ -731,6 +731,9 @@ export const en = {
731731
"quickRestAPI": "REST Query",
732732
"quickStreamAPI": "Stream Query",
733733
"quickGraphql": "GraphQL Query",
734+
"quickAlasql": "Local SQL Query",
735+
"databaseType": "Database Type",
736+
"chooseDatabase": "Choose Database",
734737
"lowcoderAPI": "Lowcoder API",
735738
"executeJSCode": "Run JavaScript Code",
736739
"importFromQueryLibrary": "Import from Query Library",

client/packages/lowcoder/src/index.sdk.ts

+2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import numbro from "numbro";
22
import Papa from "papaparse";
33
import * as uuid from "uuid";
44
import * as supabase from "@supabase/supabase-js";
5+
import * as alasql from "alasql";
56

67
import * as styledNameExports from "styled-components";
78
import styledDefault from "styled-components";
@@ -136,3 +137,4 @@ window.numbro = numbro;
136137
window.Papa = Papa;
137138
window.uuid = uuid;
138139
window.supabase = supabase;
140+
window.alasql = alasql;

client/packages/lowcoder/src/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import ResizeObserver from "resize-observer-polyfill";
33
import numbro from "numbro";
44
import Papa from "papaparse";
55
import * as supabase from "@supabase/supabase-js";
6+
import * as alasql from "alasql";
67

78
import * as uuid from "uuid";
89
import "regenerator-runtime/runtime";
@@ -18,6 +19,7 @@ window.numbro = numbro;
1819
window.Papa = Papa;
1920
window.uuid = uuid;
2021
window.supabase = supabase;
22+
window.alasql = alasql;
2123

2224
// for chrome 63
2325
if (!window.ResizeObserver) {

client/yarn.lock

+56-1
Original file line numberDiff line numberDiff line change
@@ -5932,6 +5932,18 @@ __metadata:
59325932
languageName: node
59335933
linkType: hard
59345934

5935+
"alasql@npm:^4.6.2":
5936+
version: 4.6.2
5937+
resolution: "alasql@npm:4.6.2"
5938+
dependencies:
5939+
cross-fetch: 4.1.0
5940+
yargs: 16
5941+
bin:
5942+
alasql: bin/alasql-cli.js
5943+
checksum: cc68e87eeaa72ddaec5f20c4ca631e2a8ddb45e38d4b7de41cb14661ead657b1afec8d9530160f66fe5253e9724db9ada5fc63ba2c5bcacf5b8f9583c7b0870f
5944+
languageName: node
5945+
linkType: hard
5946+
59355947
"animate.css@npm:^4.1.1":
59365948
version: 4.1.1
59375949
resolution: "animate.css@npm:4.1.1"
@@ -7309,6 +7321,17 @@ __metadata:
73097321
languageName: node
73107322
linkType: hard
73117323

7324+
"cliui@npm:^7.0.2":
7325+
version: 7.0.4
7326+
resolution: "cliui@npm:7.0.4"
7327+
dependencies:
7328+
string-width: ^4.2.0
7329+
strip-ansi: ^6.0.0
7330+
wrap-ansi: ^7.0.0
7331+
checksum: ce2e8f578a4813806788ac399b9e866297740eecd4ad1823c27fd344d78b22c5f8597d548adbcc46f0573e43e21e751f39446c5a5e804a12aace402b7a315d7f
7332+
languageName: node
7333+
linkType: hard
7334+
73127335
"cliui@npm:^8.0.1":
73137336
version: 8.0.1
73147337
resolution: "cliui@npm:8.0.1"
@@ -7870,6 +7893,15 @@ coolshapes-react@lowcoder-org/coolshapes-react:
78707893
languageName: node
78717894
linkType: hard
78727895

7896+
"cross-fetch@npm:4.1.0":
7897+
version: 4.1.0
7898+
resolution: "cross-fetch@npm:4.1.0"
7899+
dependencies:
7900+
node-fetch: ^2.7.0
7901+
checksum: c02fa85d59f83e50dbd769ee472c9cc984060c403ee5ec8654659f61a525c1a655eef1c7a35e365c1a107b4e72d76e786718b673d1cb3c97f61d4644cb0a9f9d
7902+
languageName: node
7903+
linkType: hard
7904+
78737905
"cross-fetch@npm:^3.1.5":
78747906
version: 3.1.8
78757907
resolution: "cross-fetch@npm:3.1.8"
@@ -14090,6 +14122,7 @@ coolshapes-react@lowcoder-org/coolshapes-react:
1409014122
"@types/regenerator-runtime": ^0.13.1
1409114123
"@types/uuid": ^8.3.4
1409214124
"@vitejs/plugin-react": ^2.2.0
14125+
alasql: ^4.6.2
1409314126
animate.css: ^4.1.1
1409414127
antd: ^5.20.0
1409514128
axios: ^1.7.7
@@ -15684,7 +15717,7 @@ coolshapes-react@lowcoder-org/coolshapes-react:
1568415717
languageName: node
1568515718
linkType: hard
1568615719

15687-
"node-fetch@npm:^2.6.12":
15720+
"node-fetch@npm:^2.6.12, node-fetch@npm:^2.7.0":
1568815721
version: 2.7.0
1568915722
resolution: "node-fetch@npm:2.7.0"
1569015723
dependencies:
@@ -22446,13 +22479,35 @@ coolshapes-react@lowcoder-org/coolshapes-react:
2244622479
languageName: node
2244722480
linkType: hard
2244822481

22482+
"yargs-parser@npm:^20.2.2":
22483+
version: 20.2.9
22484+
resolution: "yargs-parser@npm:20.2.9"
22485+
checksum: 8bb69015f2b0ff9e17b2c8e6bfe224ab463dd00ca211eece72a4cd8a906224d2703fb8a326d36fdd0e68701e201b2a60ed7cf81ce0fd9b3799f9fe7745977ae3
22486+
languageName: node
22487+
linkType: hard
22488+
2244922489
"yargs-parser@npm:^21.1.1":
2245022490
version: 21.1.1
2245122491
resolution: "yargs-parser@npm:21.1.1"
2245222492
checksum: ed2d96a616a9e3e1cc7d204c62ecc61f7aaab633dcbfab2c6df50f7f87b393993fe6640d017759fe112d0cb1e0119f2b4150a87305cc873fd90831c6a58ccf1c
2245322493
languageName: node
2245422494
linkType: hard
2245522495

22496+
"yargs@npm:16":
22497+
version: 16.2.0
22498+
resolution: "yargs@npm:16.2.0"
22499+
dependencies:
22500+
cliui: ^7.0.2
22501+
escalade: ^3.1.1
22502+
get-caller-file: ^2.0.5
22503+
require-directory: ^2.1.1
22504+
string-width: ^4.2.0
22505+
y18n: ^5.0.5
22506+
yargs-parser: ^20.2.2
22507+
checksum: b14afbb51e3251a204d81937c86a7e9d4bdbf9a2bcee38226c900d00f522969ab675703bee2a6f99f8e20103f608382936034e64d921b74df82b63c07c5e8f59
22508+
languageName: node
22509+
linkType: hard
22510+
2245622511
"yargs@npm:^17.3.1, yargs@npm:^17.5.1":
2245722512
version: 17.7.2
2245822513
resolution: "yargs@npm:17.7.2"

0 commit comments

Comments
 (0)