Skip to content
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

feature/planner #38

Merged
merged 4 commits into from
Sep 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 49 additions & 1 deletion lykiadb-lang/src/ast/expr.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use std::{fmt::Display, sync::Arc};

use crate::{Identifier, Span, Spanned};

Expand Down Expand Up @@ -396,3 +396,51 @@
}
}
}

impl Display for Expr {
// A basic display function for Expr
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Expr::Select { .. } => write!(f, "<SqlSelect>"),
Expr::Insert { .. } => write!(f, "<SqlInsert>"),
Expr::Update { .. } => write!(f, "<SqlUpdate>"),
Expr::Delete { .. } => write!(f, "<SqlDelete>"),

Check warning on line 407 in lykiadb-lang/src/ast/expr.rs

View check run for this annotation

Codecov / codecov/patch

lykiadb-lang/src/ast/expr.rs#L404-L407

Added lines #L404 - L407 were not covered by tests
Expr::Variable { name, .. } => write!(f, "{}", name),
Expr::Grouping { expr, .. } => write!(f, "({})", expr),

Check warning on line 409 in lykiadb-lang/src/ast/expr.rs

View check run for this annotation

Codecov / codecov/patch

lykiadb-lang/src/ast/expr.rs#L409

Added line #L409 was not covered by tests
Expr::Literal { value, .. } => write!(f, "{:?}", value),
Expr::Function { name, parameters, .. } => {
write!(f, "fn {}({})", name.as_ref().unwrap(), parameters.iter().map(|x| x.to_string()).collect::<Vec<_>>().join(", "))

Check warning on line 412 in lykiadb-lang/src/ast/expr.rs

View check run for this annotation

Codecov / codecov/patch

lykiadb-lang/src/ast/expr.rs#L411-L412

Added lines #L411 - L412 were not covered by tests
}
Expr::Between {
lower,
upper,
subject,
kind,
..
} => write!(
f,
"{} {} {} AND {}",
subject,
match kind {
RangeKind::Between => "BETWEEN",
RangeKind::NotBetween => "NOT BETWEEN",

Check warning on line 426 in lykiadb-lang/src/ast/expr.rs

View check run for this annotation

Codecov / codecov/patch

lykiadb-lang/src/ast/expr.rs#L415-L426

Added lines #L415 - L426 were not covered by tests
},
lower,
upper
),
Expr::Binary { left, operation, right, .. } => {
write!(f, "({} {:?} {})", left, operation, right)
}
Expr::Unary { operation, expr, .. } => write!(f, "{:?}{}", operation, expr),
Expr::Assignment { dst, expr, .. } => write!(f, "{} = {}", dst, expr),
Expr::Logical { left, operation, right, .. } => {
write!(f, "{} {:?} {}", left, operation, right)

Check warning on line 437 in lykiadb-lang/src/ast/expr.rs

View check run for this annotation

Codecov / codecov/patch

lykiadb-lang/src/ast/expr.rs#L434-L437

Added lines #L434 - L437 were not covered by tests
}
Expr::Call { callee, args, .. } => {
write!(f, "{}({})", callee, args.iter().map(|x| x.to_string()).collect::<Vec<_>>().join(", "))

Check warning on line 440 in lykiadb-lang/src/ast/expr.rs

View check run for this annotation

Codecov / codecov/patch

lykiadb-lang/src/ast/expr.rs#L439-L440

Added lines #L439 - L440 were not covered by tests
}
Expr::Get { object, name, .. } => write!(f, "{}.{}", object, name),
Expr::Set { object, name, value, .. } => write!(f, "{}.{} = {}", object, name, value),

Check warning on line 443 in lykiadb-lang/src/ast/expr.rs

View check run for this annotation

Codecov / codecov/patch

lykiadb-lang/src/ast/expr.rs#L443

Added line #L443 was not covered by tests
}
}
}
8 changes: 7 additions & 1 deletion lykiadb-lang/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::sync::Arc;
use std::{fmt::{Display, Formatter, Result}, sync::Arc};

use ast::expr::Expr;
use rustc_hash::FxHashMap;
Expand Down Expand Up @@ -71,3 +71,9 @@ pub struct Identifier {
#[serde(skip)]
pub span: Span,
}

impl Display for Identifier {
fn fmt(&self, f: &mut Formatter) -> Result {
write!(f, "{}", self.name)
}
}
24 changes: 22 additions & 2 deletions lykiadb-lang/src/parser/program.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
use std::str::FromStr;

use serde::{Deserialize, Serialize};
use serde_json::Value;

use crate::{
ast::{expr::Expr, stmt::Stmt},
Locals,
ast::{expr::Expr, stmt::Stmt}, tokenizer::scanner::Scanner, Locals, Scopes
};

use super::{resolver::Resolver, ParseError, ParseResult, Parser};
#[derive(Serialize, Deserialize)]
pub struct Program {
root: Box<Stmt>,
Expand Down Expand Up @@ -47,3 +50,20 @@
serde_json::to_value(self.root.clone()).unwrap()
}
}

impl FromStr for Program {
type Err = ParseError;

fn from_str(s: &str) -> ParseResult<Program> {
let tokens = Scanner::scan(s).unwrap();
let parse_result = Parser::parse(&tokens);

if let Ok(mut program) = parse_result {
let mut resolver = Resolver::new(Scopes::default(), &program, None);
let (_, locals) = resolver.resolve().unwrap();
program.set_locals(locals);
return Ok(program);
}
panic!("Failed to parse program.");

Check warning on line 67 in lykiadb-lang/src/parser/program.rs

View check run for this annotation

Codecov / codecov/patch

lykiadb-lang/src/parser/program.rs#L66-L67

Added lines #L66 - L67 were not covered by tests
}
}
39 changes: 11 additions & 28 deletions lykiadb-playground/app/editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,29 +13,12 @@ await init();

const EditorView = () => {
const [code, setCode] = React.useState(
`var $calc = {
add: function ($a, $b) {
return $a + $b;
},
sub: function ($a, $b) {
return $a - $b;
},
mul: function ($a, $b) {
return $a * $b;
},
div: function ($a, $b) {
return $a / $b;
},
};
print($calc.add(4, 5));
print($calc.sub(4, 5));
print($calc.mul(4, 5));
print($calc.div(4, 5));
`SELECT { 'name': user.name } from users;
`);

const [ast, setAst] = React.useState({});

const [sizes, setSizes] = React.useState([100, '30%', 'auto']);
const [sizes, setSizes] = React.useState(['50%', '50%']);

function updateCode(code: string) {
setCode(code)
Expand All @@ -49,25 +32,25 @@ print($calc.div(4, 5));
}

return (
<SplitPane sizes={sizes} onChange={setSizes} className={defaultFont.className}>
<Pane minSize={300} className="h-full p-1">
<div className="p-2 text-xs text-white bg-zinc-900 rounded-t-md">Script</div>
<SplitPane sizes={sizes} onChange={setSizes} className="border-y border-l border-zinc-500">
<Pane minSize={600} className="h-full">
<div className="p-4 text-md text-white bg-zinc-800 border-y border-l border-zinc-500"></div>
<div>
<CodeMirror
className={defaultFont.className}
value={code}
height="400px"
extensions={[lyql(tokenize)]}
onChange={(value: string) => updateCode(value)}
/>
</div>
<div className="p-2 text-white bg-zinc-900 rounded-b-md"></div>
<div className="p-2 text-white bg-zinc-800"></div>
</Pane>
<Pane minSize={600} className="h-full p-1">
<div className="p-2 text-xs text-white bg-zinc-900 rounded-t-md">Syntax tree</div>
<div className="overflow-y-auto h-full">
<Pane minSize={600} className="h-full border-l border-zinc-500">
<div className="p-2 text-md text-white bg-zinc-800 border-y border-r border-zinc-500">AST</div>
<div className="overflow-y-auto">
<div className="p-3 bg-white"><JsonView value={ast} /></div>
</div>
<div className="p-2 text-white bg-zinc-900 rounded-b-md"></div>
<div className="p-2 text-white bg-zinc-800"></div>
</Pane>
</SplitPane>
);
Expand Down
4 changes: 2 additions & 2 deletions lykiadb-playground/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
@media (prefers-color-scheme: dark) {
:root {
--foreground-rgb: 255, 255, 255;
--background-start-rgb: 0, 0, 0;
--background-end-rgb: 0, 0, 0;
--background-start-rgb: 56, 66, 96;
--background-end-rgb: 56, 66, 96;
}
}

Expand Down
10 changes: 5 additions & 5 deletions lykiadb-playground/app/lyqlSyntax.scss
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
.cm-{
$font-size: 14px;
$font-size: 16px;
$font-family: 'JetBrains Mono', monospace;
&gutters, &editor {
font-size: $font-size;
font-family: $font-family;
background-color: #222226!important;
background-color: #1c2130!important;
border: none!important;
}

Expand All @@ -26,15 +26,15 @@
color:rgb(94, 145, 255)
}
&identifier {
color:#FB773C;
color:#bbdf06;
text-decoration: underline
}
&keyword{
color: rgb(74, 216, 255);
color: rgb(122, 255, 228);
font-weight: bold;
}
&sqlkeyword{
color: #EB3678;
color: #027e44;
font-weight: bold;
}
&symbol {
Expand Down
3 changes: 1 addition & 2 deletions lykiadb-playground/styles/fonts.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { Inter, JetBrains_Mono } from 'next/font/google'
import { JetBrains_Mono, Inter } from 'next/font/google'

const defaultFont = Inter({ weight: '400', subsets: ['latin'] })
const jetBrainsMono = JetBrains_Mono({ weight: '400', subsets: ['latin'] })


export { defaultFont, jetBrainsMono }
12 changes: 12 additions & 0 deletions lykiadb-server/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,15 @@ pub mod engine;
pub mod plan;
pub mod util;
pub mod value;

#[macro_export]
macro_rules! assert_plan {
($($name:ident: {$field:literal => $value:literal}),*) => {
$(
#[test]
fn $name() {
expect_plan($field, $value);
}
)*
};
}
45 changes: 45 additions & 0 deletions lykiadb-server/src/plan/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::fmt::Display;

use lykiadb_lang::{
ast::{
expr::Expr,
Expand Down Expand Up @@ -84,3 +86,46 @@

Nothing,
}

impl Display for Plan {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Plan::Select(node) => write!(f, "{}", node),
}
}
}

impl Node {
const TAB: &'static str = " ";
const NEWLINE: &'static str = "\n";

fn _fmt_recursive(&self, f: &mut std::fmt::Formatter<'_>, indent: usize) -> std::fmt::Result {
let indent_str = Self::TAB.repeat(indent);
match self {
Node::Filter { source, predicate } => {
write!(f, "{}- filter {}:{}", indent_str, predicate, Self::NEWLINE)?;
source._fmt_recursive(f, indent + 1)
}
Node::Scan { source, filter } => {
write!(f, "{}- scan [{} as {}]{}", indent_str, source.name, source.alias.as_ref().unwrap_or(&source.name), Self::NEWLINE)
}
Node::Join {
left,
join_type,
right,
constraint,
} => {
write!(f, "{}- join [{:?}, {}]:{}", indent_str, join_type, constraint.as_ref().unwrap(), Self::NEWLINE)?;
left._fmt_recursive(f, indent + 1)?;
right._fmt_recursive(f, indent + 1)
}
_ => "<NotImplementedYet>".fmt(f),

Check warning on line 122 in lykiadb-server/src/plan/mod.rs

View check run for this annotation

Codecov / codecov/patch

lykiadb-server/src/plan/mod.rs#L122

Added line #L122 was not covered by tests
}
}
}

impl Display for Node {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self._fmt_recursive(f, 0)
}
}
26 changes: 23 additions & 3 deletions lykiadb-server/src/plan/planner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
id: _,
} => {
let plan = Plan::Select(self.build_select(query)?);
println!("{}", serde_json::to_value(&plan).unwrap());
Ok(plan)
}
_ => panic!("Not implemented yet."),
Expand All @@ -40,11 +39,11 @@
node = self.build_from(from)?;
}
// WHERE
if let Some(where_clause) = &query.core.r#where {
if let Some(predicate) = &query.core.r#where {
// TODO: Traverse expression
node = Node::Filter {
source: Box::new(node),
predicate: *where_clause.clone(),
predicate: *predicate.clone(),
}
}
// GROUP BY
Expand Down Expand Up @@ -95,3 +94,24 @@
}
}
}


pub mod test_helpers {
use lykiadb_lang::{ast::stmt::Stmt, parser::program::Program};

use super::Planner;

pub fn expect_plan(query: &str, expected_plan: &str) {
let mut planner = Planner::new();
let program = query.parse::<Program>().unwrap();
match *program.get_root() {
Stmt::Program { body, .. } if matches!(body.get(0), Some(Stmt::Expression { .. })) => {
if let Some(Stmt::Expression { expr, .. }) = body.get(0) {
let generated_plan = planner.build(&expr).unwrap();
assert_eq!(expected_plan, generated_plan.to_string());
}

Check warning on line 112 in lykiadb-server/src/plan/planner.rs

View check run for this annotation

Codecov / codecov/patch

lykiadb-server/src/plan/planner.rs#L112

Added line #L112 was not covered by tests
}
_ => panic!("Expected expression statement."),

Check warning on line 114 in lykiadb-server/src/plan/planner.rs

View check run for this annotation

Codecov / codecov/patch

lykiadb-server/src/plan/planner.rs#L114

Added line #L114 was not covered by tests
}
}
}
19 changes: 19 additions & 0 deletions lykiadb-server/tests/planner/join.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
use lykiadb_server::{assert_plan, plan::planner::test_helpers::expect_plan};


assert_plan! {
three_way_simple: {
"SELECT * FROM books b
INNER JOIN categories c ON b.category_id = c.id
INNER JOIN publishers AS p ON b.publisher_id = p.id
WHERE p.name = 'Springer';" =>

"- filter (p.name IsEqual Str(\"Springer\")):
- join [Inner, (b.publisher_id IsEqual p.id)]:
- join [Inner, (b.category_id IsEqual c.id)]:
- scan [books as b]
- scan [categories as c]
- scan [publishers as p]
"
}
}
1 change: 1 addition & 0 deletions lykiadb-server/tests/planner/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
mod join;
1 change: 1 addition & 0 deletions lykiadb-server/tests/tests.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#![recursion_limit = "192"]

mod runtime;
mod planner;
Loading