Commit 6c3c2c28 authored by cznic's avatar cznic

WIP, updates #85.

parent d25101fe
......@@ -1397,7 +1397,9 @@
//
// The second form allows an arbitrary boolean expression to be used to
// validate the column. If the value of the expression if true then the
// validation succeeded.
// validation succeeded. If the value of the expression if false or NULL then
// the validation fails. If the value of the expression is not of type bool an
// error occurs.
//
// BEGIN TRANSACTION;
// CREATE TABLE department (
......@@ -1406,6 +1408,46 @@
// );
// COMMIT;
//
// BEGIN TRANSACTION;
// CREATE TABLE t (
// TimeStamp time TimeStamp < now() && since(TimeStamp) < duration("10s"),
// Event string Event != "" && Event like "[0-9]+:[ \t]+.*",
// );
// COMMIT;
//
// The optional DEFAULT clause is an expression which, if present, is
// substituted instead of a NULL value when the colum is assigned a value.
//
// BEGIN TRANSACTION;
// CREATE TABLE department (
// DepartmentID int,
// DepartmentName string DepartmentName IN ("HQ", "R/D", "Lab", "HR")
// DEFAULT "HQ",
// );
// COMMIT;
//
//
// Constraints and defaults evaluation order
//
// When a table row is inserted by the INSERT INTO statement or when a table
// row is updated by the UPDATE statement, the order of operations is as
// follows:
//
// 1. The new values of the affected columns are set and the values of all the
// row columns become the named values which can be referred to in default
// expressions evaluated in step 2.
//
// 2. If any row column value is NULL and the DEFAULT clause is present in the
// column's definition, the default expression is evaluated and its value is
// set as the respective column value.
//
// 3. The values, potentially updated, of row columns become the named values
// which can be referred to in constraint expressions evaluated during step 4.
//
// 4. All row columns which definition has the constraint clause present will
// have that constraint checked. If any constraint violation is detected, the
// overall operation fails and no changes to the table are made.
//
// DELETE FROM
//
// Delete from statements remove rows from a table, which must exist.
......@@ -1498,6 +1540,11 @@
// FROM department;
// COMMIT;
//
// If any of the columns of the table were defined using the optional
// constraints clause or the optional defaults clause then those are processed
// on a per row basis. The details are discussed in the "Constraints and
// defaults chapter" below the CREATE TABLE statement documentation.
//
// ROLLBACK
//
// The rollback statement closes the innermost transaction nesting level
......@@ -1776,6 +1823,11 @@
//
// Note: The SET clause is optional.
//
// If any of the columns of the table were defined using the optional
// constraints clause or the optional defaults clause then those are processed
// on a per row basis. The details are discussed in the "Constraints and
// defaults chapter" below the CREATE TABLE statement documentation.
//
// System Tables
//
// To allow to query for DB meta data, there exist specially named virtual
......
......@@ -187,7 +187,7 @@ type pLike struct {
}
func (p *pLike) isStatic() bool { return p.expr.isStatic() && p.pattern.isStatic() }
func (p *pLike) String() string { return fmt.Sprintf("%q LIKE %q", p.expr, p.pattern) }
func (p *pLike) String() string { return fmt.Sprintf("%s LIKE %s", p.expr, p.pattern) }
func (p *pLike) eval(execCtx *execCtx, ctx map[interface{}]interface{}, arg []interface{}) (v interface{}, err error) {
var sexpr string
......@@ -3039,6 +3039,10 @@ func (l value) String() string {
return "NULL"
case string:
return fmt.Sprintf("%q", x)
case time.Duration:
return fmt.Sprintf("duration(%q)", l.val)
case time.Time:
return fmt.Sprintf("time(%q)", l.val)
default:
return fmt.Sprintf("%v", l.val)
}
......
......@@ -3,7 +3,7 @@
//TODO Put your favorite license here
// yacc source generated by ebnf2y[1]
// at 2015-04-17 17:42:45.093107679 +0200 CEST
// at 2015-04-18 13:29:26.294598747 +0200 CEST
//
// $ ebnf2y -o ql.y -oe ql.ebnf -start StatementList -pkg ql -p _
//
......
......@@ -30,6 +30,9 @@ var (
_ stmt = beginTransactionStmt{}
_ stmt = commitStmt{}
_ stmt = rollbackStmt{}
exprCache = map[string]expression{}
exprCacheMu sync.Mutex
)
type stmt interface {
......@@ -58,6 +61,27 @@ type updateStmt struct {
where expression
}
func str2expr(expr string) (expression, error) {
exprCacheMu.Lock()
e := exprCache[expr]
exprCacheMu.Unlock()
if e != nil {
return e, nil
}
src := "select " + expr + " from t"
l, err := Compile(src)
if err != nil {
return nil, err
}
e = l.l[0].(*selectStmt).flds[0].expr
exprCacheMu.Lock()
exprCache[expr] = e
exprCacheMu.Unlock()
return e, nil
}
func (s *updateStmt) String() string {
u := fmt.Sprintf("UPDATE TABLE %s", s.tableName)
a := make([]string, len(s.list))
......@@ -430,7 +454,7 @@ type alterTableAddStmt struct {
}
func (s *alterTableAddStmt) String() string {
r := fmt.Sprintf("ALTER TABLE %s ADD %s;", s.tableName, s.c.name, typeStr(s.c.typ))
r := fmt.Sprintf("ALTER TABLE %s ADD %s %s;", s.tableName, s.c.name, typeStr(s.c.typ))
c := s.c
if x := c.constraint; x != nil { //TODO add (*col).String()
switch e := x.expr; {
......@@ -751,19 +775,26 @@ var (
`)
)
func constraintsAndDefaults(ctx *execCtx, table string, cols []*col) (constraints []*constraint, defaults []expression, _ error) {
if table == "__Column2" {
func constraintsAndDefaults(ctx *execCtx, table string) (constraints []*constraint, defaults []expression, _ error) {
if isSystemName[table] {
return nil, nil, nil
}
_, ok := ctx.db.root.tables["__Column2"]
if !ok {
return nil, nil, nil
}
t, ok := ctx.db.root.tables[table]
if !ok {
return nil, nil, fmt.Errorf("table %q does not exist", table)
}
cols := t.cols
constraints = make([]*constraint, len(cols))
defaults = make([]expression, len(cols))
arg := []interface{}{table}
rs, err := selectColumn2.l[0].exec(&execCtx{db: ctx.db, arg: arg})
rs, err := selectColumn2.l[0].exec(&execCtx{db: ctx.db})
if err != nil {
return nil, nil, err
}
......@@ -793,25 +824,20 @@ func constraintsAndDefaults(ctx *execCtx, table string, cols []*col) (constraint
dexpr := row[3].(string)
for i, c := range cols {
if c.name == nm {
co := &constraint{}
if !nonNull {
src := "select " + cexpr + " from t"
l, err := Compile(src)
if err != nil {
return nil, nil, fmt.Errorf("constraint %q: %v", cexpr, err)
var co *constraint
if nonNull || cexpr != "" {
co = &constraint{}
if cexpr != "" {
if co.expr, err = str2expr(cexpr); err != nil {
return nil, nil, fmt.Errorf("constraint %q: %v", cexpr, err)
}
}
co.expr = l.l[0].(*selectStmt).flds[0].expr
}
constraints[i] = co
if dexpr != "" {
src := "select " + dexpr + " from t"
l, err := Compile(src)
if err != nil {
return nil, nil, fmt.Errorf("default %q: %v", cexpr, err)
if defaults[i], err = str2expr(dexpr); err != nil {
return nil, nil, fmt.Errorf("constraint %q: %v", dexpr, err)
}
defaults[i] = l.l[0].(*selectStmt).flds[0].expr
}
}
}
......@@ -819,6 +845,83 @@ func constraintsAndDefaults(ctx *execCtx, table string, cols []*col) (constraint
return constraints, defaults, nil
}
func checkConstraintsAndDefaults(
ctx *execCtx,
row []interface{},
cols []*col,
m map[interface{}]interface{},
constraints []*constraint,
defaults []expression) error {
// 1.
for _, c := range cols {
m[c.name] = row[c.index]
}
// 2.
for i, c := range cols {
val := row[c.index]
expr := defaults[i]
if val != nil || expr == nil {
continue
}
dval, err := expr.eval(ctx, m, ctx.arg)
if err != nil {
return err
}
row[c.index] = dval
if err = typeCheck(row, []*col{c}); err != nil {
return err
}
}
// 3.
for _, c := range cols {
m[c.name] = row[c.index]
}
// 4.
for i, c := range cols {
constraint := constraints[i]
if constraint == nil {
continue
}
val := row[c.index]
expr := constraint.expr
if expr == nil { // Constraint: NOT NULL
if val == nil {
return fmt.Errorf("column %s: constraint violation: NOT NULL", c.name)
}
continue
}
// Constraint is an expression
cval, err := expr.eval(ctx, m, ctx.arg)
if err != nil {
return err
}
if cval == nil {
return fmt.Errorf("column %s: constraint violation: %s", c.name, expr)
}
bval, ok := cval.(bool)
if !ok {
return fmt.Errorf("column %s: non bool constraint expression: %s", c.name, expr)
}
if !bval {
return fmt.Errorf("column %s: constraint violation: %s", c.name, expr)
}
}
return nil
}
func (s *insertIntoStmt) exec(ctx *execCtx) (_ Recordset, err error) {
t, ok := ctx.db.root.tables[s.tableName]
if !ok {
......@@ -840,7 +943,7 @@ func (s *insertIntoStmt) exec(ctx *execCtx) (_ Recordset, err error) {
}
}
constraints, defaults, err := constraintsAndDefaults(ctx, s.tableName, cols)
constraints, defaults, err := constraintsAndDefaults(ctx, s.tableName)
if err != nil {
return nil, err
}
......@@ -867,25 +970,18 @@ func (s *insertIntoStmt) exec(ctx *execCtx) (_ Recordset, err error) {
return nil, err
}
//TODO constraints and defaults
if len(constraints) != 0 { // => len(defaults) != 0 as well
// V C D ?
// #1 null - - use V
// #2 null - D use D
// #3 null C - check V
// #4 null C D check D, set if ok
// #5 V - - use V
// #6 V - D use V
// #7 V C - check V
// #8 V C D check V
}
r[cols[i].index] = val
}
if err = typeCheck(r, cols); err != nil {
return
}
if len(constraints) != 0 { // => len(defaults) != 0 as well
if err = checkConstraintsAndDefaults(ctx, r, t.cols, m, constraints, defaults); err != nil {
return nil, err
}
}
id, err := t.addRecord(r)
if err != nil {
return nil, err
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment