Skip to content

Feat/alasql #1457

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jan 22, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions client/packages/lowcoder/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"@types/react-signature-canvas": "^1.0.2",
"@types/react-test-renderer": "^18.0.0",
"@types/react-virtualized": "^9.21.21",
"alasql": "^4.6.2",
"animate.css": "^4.1.1",
"antd": "^5.20.0",
"axios": "^1.7.7",
Expand Down
8 changes: 8 additions & 0 deletions client/packages/lowcoder/src/components/ResCreatePanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,13 @@ const ResButton = (props: {
compType: "streamApi",
},
},
alasql: {
label: trans("query.quickAlasql"),
type: BottomResTypeEnum.Query,
extra: {
compType: "alasql",
},
},
graphql: {
label: trans("query.quickGraphql"),
type: BottomResTypeEnum.Query,
Expand Down Expand Up @@ -319,6 +326,7 @@ export function ResCreatePanel(props: ResCreateModalProps) {
<DataSourceListWrapper $placement={placement}>
<ResButton size={buttonSize} identifier={"restApi"} onSelect={onSelect} />
<ResButton size={buttonSize} identifier={"streamApi"} onSelect={onSelect} />
<ResButton size={buttonSize} identifier={"alasql"} onSelect={onSelect} />
<ResButton size={buttonSize} identifier={"graphql"} onSelect={onSelect} />
{datasource.map((i) => (
<ResButton size={buttonSize} key={i.id} identifier={i} onSelect={onSelect} />
Expand Down
134 changes: 134 additions & 0 deletions client/packages/lowcoder/src/comps/queries/httpQuery/alasqlQuery.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import { QueryConfigItemWrapper, QueryConfigLabel, QueryConfigWrapper } from "components/query";
import { simpleMultiComp } from "comps/generators/multi";
import { JSONValue } from "../../../util/jsonTypes";
import { ParamsStringControl } from "../../controls/paramsControl";
import { dropdownControl } from "@lowcoder-ee/comps/controls/dropdownControl";
import { QueryResult } from "../queryComp";
import { QUERY_EXECUTION_ERROR, QUERY_EXECUTION_OK } from "@lowcoder-ee/constants/queryConstants";
import { getDynamicStringSegments, isDynamicSegment } from "lowcoder-core";
import alasql from "alasql";
import { trans } from "i18n";

const childrenMap = {
databaseType: dropdownControl(
[
{ label: "Data Query", value: "dataQuery" },
{ label: "Local Database", value: "localDB" },
] as const,
"dataQuery"
),
database: dropdownControl(
[
{ label: "Local Storage", value: "LOCALSTORAGE" },
{ label: "IndexedDB", value: "INDEXEDDB" },
] as const,
"LOCALSTORAGE"
),
sql: ParamsStringControl,
};

const AlaSqlTmpQuery = simpleMultiComp(childrenMap);

// TODO: Support multiple queries
export class AlaSqlQuery extends AlaSqlTmpQuery {
override getView() {
const children = this.children;
const params = [ ...children.sql.getQueryParams() ];
const databaseType = children.databaseType.getView();
const selectedDB = children.database.getView();
const paramsMap: Record<string, any> = {};
params.forEach(({key, value}) => {
paramsMap[key] = value();
});

const sqlQuery = children.sql.children.text.unevaledValue.replace(/ +/g, ' ');
const isCreateDBQuery = sqlQuery.toUpperCase().startsWith('CREATE DATABASE');

return async (p: { args?: Record<string, unknown> }): Promise<QueryResult> => {
try {
let result: JSONValue;
const timer = performance.now();

if (databaseType === 'localDB' && isCreateDBQuery) {
const updatedQuery = `${sqlQuery.slice(0, 6)} ${selectedDB} ${sqlQuery.slice(6)}`;
const tableName = updatedQuery.split(' ').pop()?.replace(';', '');
result = alasql(updatedQuery);
result = alasql(`ATTACH ${selectedDB} DATABASE ${tableName};`);
} else {
let segments = getDynamicStringSegments(sqlQuery);
let dataArr: any = [];
segments = segments.map((segment) => {
if (isDynamicSegment(segment)) {
const key = segment.replace('{{','').replace('}}','');
dataArr.push(paramsMap[key]);
return '?';
}
return segment;
})
result = alasql(segments.join(' '), dataArr);
}

return {
data: result as JSONValue,
code: QUERY_EXECUTION_OK,
success: true,
runTime: Number((performance.now() - timer).toFixed()),
};
} catch (e) {
return {
success: false,
data: "",
code: QUERY_EXECUTION_ERROR,
message: (e as any).message || "",
};
}
};
}

propertyView(props: { datasourceId: string }) {
return <PropertyView {...props} comp={this} />;
}
}

const PropertyView = (props: { comp: InstanceType<typeof AlaSqlQuery>; datasourceId: string }) => {
const { comp } = props;
const { children } = comp;

return (
<>
<QueryConfigWrapper>
<QueryConfigLabel>{trans("query.databaseType")}</QueryConfigLabel>
<QueryConfigItemWrapper>
{children.databaseType.propertyView({
styleName: "medium",
width: "100%",
})}
</QueryConfigItemWrapper>
</QueryConfigWrapper>

{children.databaseType.getView() === 'localDB' && (
<QueryConfigWrapper>
<QueryConfigLabel>{trans("query.chooseDatabase")}</QueryConfigLabel>
<QueryConfigItemWrapper>
{children.database.propertyView({
styleName: "medium",
width: "100%",
})}
</QueryConfigItemWrapper>
</QueryConfigWrapper>
)}

<QueryConfigWrapper>
<QueryConfigItemWrapper>
{children.sql.propertyView({
placement: "bottom",
placeholder: "SELECT * FROM users WHERE user_id = {{userId}}::uuid",
styleName: "medium",
language: "sql",
enableMetaCompletion: true,
})}
</QueryConfigItemWrapper>
</QueryConfigWrapper>
</>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -734,6 +734,7 @@ function useDatasourceStatus(datasourceId: string, datasourceType: ResourceType)
datasourceType === "js" ||
datasourceType === "streamApi" ||
datasourceType === "libraryQuery" ||
datasourceType === "alasql" ||
datasourceId === QUICK_REST_API_ID ||
datasourceId === QUICK_GRAPHQL_ID
) {
Expand Down
16 changes: 16 additions & 0 deletions client/packages/lowcoder/src/comps/queries/resourceDropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,11 @@ const QuickGraphqlValue: ResourceOptionValue = {
type: "graphql",
};

const QuickAlasqlValue: ResourceOptionValue = {
id: "",
type: "alasql",
};

interface ResourceDropdownProps {
changeResource: (datasourceId: string, value: string) => void;
selectedResource: ResourceOptionValue;
Expand Down Expand Up @@ -265,6 +270,17 @@ export const ResourceDropdown = (props: ResourceDropdownProps) => {
<SelectOptionLabel>{trans("query.quickStreamAPI")} </SelectOptionLabel>
</SelectOptionContains>
</SelectOption>

<SelectOption
key={JSON.stringify(QuickAlasqlValue)}
label={trans("query.quickAlasql")}
value={JSON.stringify(QuickAlasqlValue)}
>
<SelectOptionContains>
{getBottomResIcon("restApi")}
<SelectOptionLabel>{trans("query.quickAlasql")} </SelectOptionLabel>
</SelectOptionContains>
</SelectOption>

<SelectOption
key={JSON.stringify(QuickGraphqlValue)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@ export const NOT_SUPPORT_GUI_SQL_QUERY: string[] = [
"snowflake",
"tdengine",
"dameng",
"alasql",
];
const SUPPORT_UPSERT_SQL_QUERY: string[] = [
"mysql",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export const databasePlugins: Partial<DatasourceType>[] = [
"clickHouse",
"snowflake",
"mariadb",
"alasql",
];

export const apiPluginsForQueryLibrary: Partial<DatasourceType>[] = [
Expand Down
2 changes: 1 addition & 1 deletion client/packages/lowcoder/src/constants/libConstants.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export const libNames = new Set(["uuid", "numbro", "Papa", "supabase"]);
export const libNames = new Set(["uuid", "numbro", "Papa", "supabase", "alasql"]);
5 changes: 4 additions & 1 deletion client/packages/lowcoder/src/constants/queryConstants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { GraphqlQuery } from "../comps/queries/httpQuery/graphqlQuery";
import { toPluginQuery } from "comps/queries/pluginQuery/pluginQuery";
import { MultiCompConstructor } from "lowcoder-core";
import { DataSourcePluginMeta } from "lowcoder-sdk/dataSource";
import { AlaSqlQuery } from "@lowcoder-ee/comps/queries/httpQuery/alasqlQuery";

export type DatasourceType =
| "mysql"
Expand All @@ -29,13 +30,15 @@ export type DatasourceType =
| "googleSheets"
| "graphql"
| "snowflake"
| "mariadb";
| "mariadb"
| "alasql";

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

export const QueryMap = {
js: JSQuery,
mysql: SQLQuery,
alasql: AlaSqlQuery,
restApi: HttpQuery,
streamApi: StreamQuery,
mongodb: MongoQuery,
Expand Down
1 change: 1 addition & 0 deletions client/packages/lowcoder/src/global.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ declare global {
numbro: any;
Papa: any;
uuid: any;
alasql: any;
}
}
3 changes: 3 additions & 0 deletions client/packages/lowcoder/src/i18n/locales/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -731,6 +731,9 @@ export const en = {
"quickRestAPI": "REST Query",
"quickStreamAPI": "Stream Query",
"quickGraphql": "GraphQL Query",
"quickAlasql": "Local SQL Query",
"databaseType": "Database Type",
"chooseDatabase": "Choose Database",
"lowcoderAPI": "Lowcoder API",
"executeJSCode": "Run JavaScript Code",
"importFromQueryLibrary": "Import from Query Library",
Expand Down
2 changes: 2 additions & 0 deletions client/packages/lowcoder/src/index.sdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import numbro from "numbro";
import Papa from "papaparse";
import * as uuid from "uuid";
import * as supabase from "@supabase/supabase-js";
import * as alasql from "alasql";

import * as styledNameExports from "styled-components";
import styledDefault from "styled-components";
Expand Down Expand Up @@ -136,3 +137,4 @@ window.numbro = numbro;
window.Papa = Papa;
window.uuid = uuid;
window.supabase = supabase;
window.alasql = alasql;
2 changes: 2 additions & 0 deletions client/packages/lowcoder/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import ResizeObserver from "resize-observer-polyfill";
import numbro from "numbro";
import Papa from "papaparse";
import * as supabase from "@supabase/supabase-js";
import * as alasql from "alasql";

import * as uuid from "uuid";
import "regenerator-runtime/runtime";
Expand All @@ -18,6 +19,7 @@ window.numbro = numbro;
window.Papa = Papa;
window.uuid = uuid;
window.supabase = supabase;
window.alasql = alasql;

// for chrome 63
if (!window.ResizeObserver) {
Expand Down
57 changes: 56 additions & 1 deletion client/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -5932,6 +5932,18 @@ __metadata:
languageName: node
linkType: hard

"alasql@npm:^4.6.2":
version: 4.6.2
resolution: "alasql@npm:4.6.2"
dependencies:
cross-fetch: 4.1.0
yargs: 16
bin:
alasql: bin/alasql-cli.js
checksum: cc68e87eeaa72ddaec5f20c4ca631e2a8ddb45e38d4b7de41cb14661ead657b1afec8d9530160f66fe5253e9724db9ada5fc63ba2c5bcacf5b8f9583c7b0870f
languageName: node
linkType: hard

"animate.css@npm:^4.1.1":
version: 4.1.1
resolution: "animate.css@npm:4.1.1"
Expand Down Expand Up @@ -7309,6 +7321,17 @@ __metadata:
languageName: node
linkType: hard

"cliui@npm:^7.0.2":
version: 7.0.4
resolution: "cliui@npm:7.0.4"
dependencies:
string-width: ^4.2.0
strip-ansi: ^6.0.0
wrap-ansi: ^7.0.0
checksum: ce2e8f578a4813806788ac399b9e866297740eecd4ad1823c27fd344d78b22c5f8597d548adbcc46f0573e43e21e751f39446c5a5e804a12aace402b7a315d7f
languageName: node
linkType: hard

"cliui@npm:^8.0.1":
version: 8.0.1
resolution: "cliui@npm:8.0.1"
Expand Down Expand Up @@ -7870,6 +7893,15 @@ coolshapes-react@lowcoder-org/coolshapes-react:
languageName: node
linkType: hard

"cross-fetch@npm:4.1.0":
version: 4.1.0
resolution: "cross-fetch@npm:4.1.0"
dependencies:
node-fetch: ^2.7.0
checksum: c02fa85d59f83e50dbd769ee472c9cc984060c403ee5ec8654659f61a525c1a655eef1c7a35e365c1a107b4e72d76e786718b673d1cb3c97f61d4644cb0a9f9d
languageName: node
linkType: hard

"cross-fetch@npm:^3.1.5":
version: 3.1.8
resolution: "cross-fetch@npm:3.1.8"
Expand Down Expand Up @@ -14090,6 +14122,7 @@ coolshapes-react@lowcoder-org/coolshapes-react:
"@types/regenerator-runtime": ^0.13.1
"@types/uuid": ^8.3.4
"@vitejs/plugin-react": ^2.2.0
alasql: ^4.6.2
animate.css: ^4.1.1
antd: ^5.20.0
axios: ^1.7.7
Expand Down Expand Up @@ -15684,7 +15717,7 @@ coolshapes-react@lowcoder-org/coolshapes-react:
languageName: node
linkType: hard

"node-fetch@npm:^2.6.12":
"node-fetch@npm:^2.6.12, node-fetch@npm:^2.7.0":
version: 2.7.0
resolution: "node-fetch@npm:2.7.0"
dependencies:
Expand Down Expand Up @@ -22446,13 +22479,35 @@ coolshapes-react@lowcoder-org/coolshapes-react:
languageName: node
linkType: hard

"yargs-parser@npm:^20.2.2":
version: 20.2.9
resolution: "yargs-parser@npm:20.2.9"
checksum: 8bb69015f2b0ff9e17b2c8e6bfe224ab463dd00ca211eece72a4cd8a906224d2703fb8a326d36fdd0e68701e201b2a60ed7cf81ce0fd9b3799f9fe7745977ae3
languageName: node
linkType: hard

"yargs-parser@npm:^21.1.1":
version: 21.1.1
resolution: "yargs-parser@npm:21.1.1"
checksum: ed2d96a616a9e3e1cc7d204c62ecc61f7aaab633dcbfab2c6df50f7f87b393993fe6640d017759fe112d0cb1e0119f2b4150a87305cc873fd90831c6a58ccf1c
languageName: node
linkType: hard

"yargs@npm:16":
version: 16.2.0
resolution: "yargs@npm:16.2.0"
dependencies:
cliui: ^7.0.2
escalade: ^3.1.1
get-caller-file: ^2.0.5
require-directory: ^2.1.1
string-width: ^4.2.0
y18n: ^5.0.5
yargs-parser: ^20.2.2
checksum: b14afbb51e3251a204d81937c86a7e9d4bdbf9a2bcee38226c900d00f522969ab675703bee2a6f99f8e20103f608382936034e64d921b74df82b63c07c5e8f59
languageName: node
linkType: hard

"yargs@npm:^17.3.1, yargs@npm:^17.5.1":
version: 17.7.2
resolution: "yargs@npm:17.7.2"
Expand Down
Loading