Skip to content

Commit

Permalink
feat: support experimental access to ast
Browse files Browse the repository at this point in the history
Signed-off-by: Carson Farmer <[email protected]>
  • Loading branch information
carsonfarmer committed Aug 15, 2023
1 parent 05efce0 commit 82eface
Show file tree
Hide file tree
Showing 10 changed files with 101 additions and 30 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
.vscode
.vscode
.DS_Store
diagrams*
33 changes: 33 additions & 0 deletions cmd/wasm/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
package main

import (
"encoding/json"
"regexp"
"strings"
"syscall/js"
Expand Down Expand Up @@ -65,6 +66,37 @@ func UpdateTableNames(node sqlparser.Node, nameMapper func(string) (string, bool
return node, nil
}

func getAst(this js.Value, args []js.Value) interface{} {
Error := js.Global().Get("Error")
Promise := js.Global().Get("Promise")
if len(args) < 1 {
return Promise.Call("reject", Error.New("missing required argument: statement"))
}
statement := args[0].String()
handler := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
resolve := args[0]
reject := args[1]
go func() interface{} {
ast, err := sqlparser.Parse(statement)
if err != nil {
return reject.Invoke(Error.New("error parsing statement: " + err.Error()))
}
if len(ast.Statements) == 0 {
return reject.Invoke(Error.New("error parsing statement: empty string"))
}
if len(ast.String()) > maxQuerySize {
return reject.Invoke(Error.New("statement size error: larger than specified max"))
}
b, _ := json.Marshal(&ast)
var response map[string]interface{}
_ = json.Unmarshal(b, &response)
return resolve.Invoke(js.ValueOf(response))
}()
return nil
})
return Promise.New(handler)
}

func validateTableName(this js.Value, args []js.Value) interface{} {
Error := js.Global().Get("Error")
Promise := js.Global().Get("Promise")
Expand Down Expand Up @@ -239,6 +271,7 @@ func main() {
"normalize": js.FuncOf(normalize),
"validateTableName": js.FuncOf(validateTableName),
"getUniqueTableNames": js.FuncOf(getUniqueTableNames),
"getAst": js.FuncOf(getAst),
}))

<-make(chan bool)
Expand Down
7 changes: 5 additions & 2 deletions js/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,17 +72,20 @@ To get started clone this repo.

## Install tinygo

We require tinygo version `0.28.1` or greater

```
brew tap tinygo-org/tools
brew install tinygo
```

## Fetch wasm helpers

Use the corresponding tinygo version
Use the corresponding tinygo version.
**Warning** this will overwrite any existing `wasm_exec.js` file, which has Tableland specific modifications.

```
wget https://raw.githubusercontent.com/tinygo-org/tinygo/v0.23.0/targets/wasm_exec.js
wget https://raw.githubusercontent.com/tinygo-org/tinygo/v0.28.1/targets/wasm_exec.js
```

## Build with tinygo
Expand Down
Binary file modified js/main.wasm
Binary file not shown.
53 changes: 28 additions & 25 deletions js/test/example.html
Original file line number Diff line number Diff line change
@@ -1,26 +1,29 @@
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<p>
<code id="raw">SELECT * from blah WHERE id = 1;</code>
</p>
<script type="module">
// Load module
import init from "../module.js";
// Initialize module
await init();
// Parse content in header
const { statements } = await globalThis.sqlparser.normalize(
document.querySelector("#raw").innerText
);
// Create code element with parsed string
const output = document.createElement("code");
output.id = "parsed";
output.innerHTML = statements.join(";");
// Add to body
document.body.appendChild(output);
</script>
</body>
</html>

<head>
<meta charset="utf-8" />
</head>

<body>
<p>
<code id="raw">SELECT * from blah WHERE id = 1;</code>
</p>
<script type="module">
// Load module
import init from "../esm/out.js";
// Initialize module
await init();
// Parse content in header
const { Statements } = await globalThis.sqlparser.getAst(
document.querySelector("#raw").innerText
);
// Create code element with parsed string
const output = document.createElement("code");
output.id = "parsed";
output.innerHTML = JSON.stringify(Statements, null, 2);
// Add to body
document.body.appendChild(output);
</script>
</body>

</html>
9 changes: 9 additions & 0 deletions js/test/main.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,15 @@ describe("sqlparser", function () {
await init();
});

describe("getAst", function () {
test("when there is a single valid create statement", async function () {
const struct = await globalThis.sqlparser.getAst(
"CREATE table blah_5_ (id int, image blob, description text);"
);
// TODO: Write actual tests!
});
})

describe("normalize", function () {
test("when there is a basic syntax error", async function () {
await rejects(
Expand Down
9 changes: 9 additions & 0 deletions js/test/main.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,15 @@ describe("sqlparser", function () {
await init();
});

describe("getAst", function () {
test("when there is a single valid create statement", async function () {
const struct = await globalThis.sqlparser.getAst(
"CREATE table blah_5_ (id int, image blob, description text);"
);
// TODO: Write actual tests!
});
})

describe("normalize", function () {
test("when there is a basic syntax error", async function () {
await rejects(
Expand Down
5 changes: 4 additions & 1 deletion js/test/test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,12 @@ expectType<Promise<NormalizedStatement>>(
})
);

const { normalize, validateTableName, getUniqueTableNames } =
const { normalize, validateTableName, getUniqueTableNames, getAst } =
globalThis.sqlparser;

// TODO: Can we do better type checking here?
expectType<Promise<Record<string, any>>>(getAst("select * from table where id = 1;"));

expectType<Promise<NormalizedStatement>>(
normalize("select * from table where id = 1;")
);
Expand Down
1 change: 0 additions & 1 deletion js/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,5 @@
"strict": true,
"allowJs": true,
"skipLibCheck": true,
"types": ["types.d.ts", "@types/node"]
}
}
10 changes: 10 additions & 0 deletions js/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,16 @@ declare namespace sqlparser {
nameMap?: Record<string, string>
): Promise<NormalizedStatement>;


/**
* Parse a string containing (possibly multiple) SQL statement(s) and return the AST.
* @param sql A string containing SQL statement(s).
* @return A `Promise` that resolves to an object.
*/
export function getAst(
sql: string,
): Promise<Record<string, any>>; // TODO: Can we generate proper types here?

/**
* Validate a table name.
* @param tableName A string containing a table name of the form `prefix_chainId_tokenId`.
Expand Down

0 comments on commit 82eface

Please sign in to comment.