Skip to content

bwaklog/codd

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Codd

motivation

Named after EF Codd after the paper A Relational Model of Data for Large Shared Data Banks. The current project is just a single file POC trying to get relational algebra to work. There are no optimisations.

I do have plans to build upon this for a toy database implementation.

references

  • erikgrinaker/toydb was a good reference especially from the AST to Executor phase for schema creation

example

Here is an example of a simple users relation, with columns (id INT PK, name STR, phone STR). The test performs a projection selecting only the name and phone attributes and produces a derived relation.

#[test]
fn test_user_schema() {
    // tbl users
    // schema: id INT PK | name STR | phone INT
    let mut relation = Relation {
        name: "users".to_string(),
        pk: 0,
        schema: Schema {
            attributes: vec![
                Attribute { name: "id".to_string(), atype: Type::Int },
                Attribute { name: "name".to_string(), atype: Type::Str },
                Attribute { name: "phone".to_string(), atype: Type::Int },
            ],
        },
        // This is going to change in the future
        data: Data::WithPK(Box::new(BTreeMap::new())),
    };

    // id   | name      | phone
    // 100  | bob       | 9999999999
    // 101  | alice     | 6666666666
    let insert_result = relation.insert_rows(vec![
        vec![
            Value::Int(100),
            Value::Str("bob".to_string()),
            Value::Int(9999999999),
        ],
        vec![
            Value::Int(101),
            Value::Str("alice".to_string()),
            Value::Int(6666666666),
        ],
    ]);
    assert!(insert_result);
    println!("{:?}", relation.data);

    // π_{name, phone} (users) ≡ SELECT name, phone FROM users
    let query = Operator::Unary(UnaryOpr::Projection(
        ProjAttrs::Attr(
            Attribute { name: "name".to_string(), atype: Type::Str },
            Some(Box::new(ProjAttrs::Attr(
                Attribute { name: "phone".to_string(), atype: Type::Int },
                None,
            ))),
        ),
        &relation,
    ));

    let result = query.evaluate();

    // tbl derived
    // name  | phone
    // bob   | 9999999999
    // alice | 6666666666
    let mut left = result.as_ref().unwrap().data.tuples();
    let mut right = vec![
        vec![Value::Str("bob".to_string()), Value::Int(9999999999)],
        vec![Value::Str("alice".to_string()), Value::Int(6666666666)],
    ];

    left.sort();
    right.sort();
    assert_eq!(left, right);

    println!("{:?}", result.unwrap());
}

Example where projection removes duplicate tuples

#[test]
fn test_remove_duplicates() {
    let mut relation = Relation {
        name: "pk_less".to_string(),
        pk: Some(0),
        schema: Schema {
            attributes: vec![
                Attribute { name: "id".to_string(), atype: Type::Int },
                Attribute { name: "value".to_string(), atype: Type::Str },
            ],
        },
        data: Data::WithPK(Box::new(BTreeMap::new())),
    };

    let insert_result = relation.insert_rows(vec![
        vec![Value::Int(1), Value::Str("foo".to_string())],
        vec![Value::Int(2), Value::Str("bar".to_string())],
        vec![Value::Int(3), Value::Str("baz".to_string())],
        vec![Value::Int(4), Value::Str("foo".to_string())],
    ]);

    assert!(insert_result);
    assert_eq!(
        relation.data.tuples(),
        vec![
            vec![Value::Int(1), Value::Str("foo".to_string())],
            vec![Value::Int(2), Value::Str("bar".to_string())],
            vec![Value::Int(3), Value::Str("baz".to_string())],
            vec![Value::Int(4), Value::Str("foo".to_string())],
        ]
    );

    let query = Operator::Unary(UnaryOpr::Projection(
        ProjAttrs::Attr(
            Attribute {
                name: "value".to_string(),
                atype: Type::Str,
            },
            None,
        ),
        &relation,
    ));
    let result = query.evaluate();
    // result here is a new relation named derived with data
    // defiend by
    assert!(result.is_some());

    let mut left = result.as_ref().unwrap().data.tuples();
    let mut right = vec![
        vec![Value::Str("foo".to_string())],
        vec![Value::Str("bar".to_string())],
        vec![Value::Str("baz".to_string())],
    ];

    left.sort();
    right.sort();
    assert_eq!(left, right);
}

You can query on derived queries as well

// tbl users
// id INT PK  | name STR  | phone INT
// 100        | bob       | 9999999999
// 101        | alice     | 6666666666

// SELECT name, phone FROM users;
let query = Operator::Unary(UnaryOpr::Projection(
    ProjAttrs::Attr(
        Attribute { name: "name".to_string(), atype: Type::Str },
        Some(Box::new(ProjAttrs::Attr(
            Attribute { name: "phone".to_string(), atype: Type::Int },
            None,
        ))),
    ),
    &users,
)).evaluate();

let result = query.evaluate();
assert!(result.is_some());
let derived = result.unwrap();
// tbl derived
// name STR  | phone INT
// bob       | 9999999999
// alice     | 6666666666

// SELECT phone FROM (
//  SELECT name, phone FROM users
// );
let query = Operator::Unary(UnaryOpr::Projection(
    ProjAttrs::Attr(
        Attribute { name: "phone".to_string(), atype: Type::Int },
        None,
    ),
    &derived,
)).evaluate();

let result = query.evaluate();
assert!(result.is_some());
let derived = result.unwrap();
// tbl derived
// phone INT
// 9999999999
// 6666666666

Releases

No releases published

Packages

No packages published

Languages