Skip to content

Commit

Permalink
Merge pull request #50 from k1LoW/parse-check-constraints
Browse files Browse the repository at this point in the history
Support SQLite CHECK constraints
  • Loading branch information
k1LoW authored Aug 6, 2018
2 parents b7f73dc + 2575ccb commit 06ca144
Show file tree
Hide file tree
Showing 9 changed files with 189 additions and 6 deletions.
56 changes: 56 additions & 0 deletions drivers/sqlite/sqlite.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

"github.com/k1LoW/tbls/schema"
"github.com/pkg/errors"
"regexp"
)

// Sqlite struct
Expand Down Expand Up @@ -258,6 +259,12 @@ WHERE name != 'sqlite_sequence' AND (type = 'table' OR type = 'view');`)
indexes = append(indexes, index)
}

// constraints(CHECK)
checkConstraints := parseCheckConstraints(tableDef)
for _, c := range checkConstraints {
constraints = append(constraints, c)
}

// triggers
triggerRows, err := db.Query(`
SELECT name, sql FROM sqlite_master WHERE type = 'trigger' AND tbl_name = ?;
Expand Down Expand Up @@ -303,3 +310,52 @@ func convertColumnNullable(str string) bool {
}
return true
}

func parseCheckConstraints(sql string) []*schema.Constraint {
// tokenize
re := regexp.MustCompile(`\s+`)
separator := "__SEP__"
space := "__SP__"
r1 := strings.NewReplacer("(", fmt.Sprintf("%s(%s", separator, separator), ")", fmt.Sprintf("%s)%s", separator, separator), ",", fmt.Sprintf("%s,%s", separator, separator))
r2 := strings.NewReplacer(" ", fmt.Sprintf("%s%s%s", separator, space, separator))
tokens := strings.Split(r1.Replace(r2.Replace(re.ReplaceAllString(sql, " "))), separator)

r3 := strings.NewReplacer(space, " ")
constraints := []*schema.Constraint{}
def := ""
counter := 0
for _, v := range tokens {
if counter == 0 && (v == "CHECK" || v == "check") {
def = v
continue
}
if def != "" && v == space {
def = def + v
continue
}
if def != "" && v == "(" {
def = def + v
counter = counter + 1
continue
}
if def != "" && v == ")" {
def = def + v
counter = counter - 1
if counter == 0 {
constraint := &schema.Constraint{
Name: "-",
Type: "CHECK",
Def: r3.Replace(def),
}
constraints = append(constraints, constraint)
def = ""
}
continue
}
if def != "" && counter > 0 {
def = def + v
}
}

return constraints
}
45 changes: 45 additions & 0 deletions drivers/sqlite/sqlite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
_ "github.com/mattn/go-sqlite3"
"github.com/xo/dburl"
"path/filepath"
"reflect"
)

var s *schema.Schema
Expand Down Expand Up @@ -42,3 +43,47 @@ func TestAnalyzeView(t *testing.T) {
t.Errorf("actual not empty string.")
}
}

func TestParseCheckConstraints(t *testing.T) {
sql := `CREATE TABLE check_constraints (
id INTEGER PRIMARY KEY AUTOINCREMENT,
col TEXT CHECK(length(col) > 4),
brackets TEXT UNIQUE NOT NULL CHECK(((length(brackets) > 4))),
checkcheck TEXT UNIQUE NOT NULL CHECK(length(checkcheck) > 4),
downcase TEXT UNIQUE NOT NULL check(length(downcase) > 4),
nl TEXT UNIQUE NOT
NULL check(length(nl) > 4 OR
nl != 'ln')
);`
expected := []*schema.Constraint{
&schema.Constraint{
Name: "-",
Type: "CHECK",
Def: "CHECK(length(col) > 4)",
},
&schema.Constraint{
Name: "-",
Type: "CHECK",
Def: "CHECK(((length(brackets) > 4)))",
},
&schema.Constraint{
Name: "-",
Type: "CHECK",
Def: "CHECK(length(checkcheck) > 4)",
},
&schema.Constraint{
Name: "-",
Type: "CHECK",
Def: "check(length(downcase) > 4)",
},
&schema.Constraint{
Name: "-",
Type: "CHECK",
Def: "check(length(nl) > 4 OR nl != 'ln')",
},
}
actual := parseCheckConstraints(sql)
if !reflect.DeepEqual(actual, expected) {
t.Errorf("got: %#v\nwant: %#v", actual, expected)
}
}
12 changes: 6 additions & 6 deletions output/md/templates.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,12 @@ var _Assetsac44302fb6150a621aa9d04a0350aac972bf7e18 = "# {{ .Table.Name }}\n\n##

// Assets returns go-assets FileSystem
var Assets = assets.NewFileSystem(map[string][]string{"/": []string{"index.md.tmpl", "table.md.tmpl"}}, map[string]*assets.File{
"/index.md.tmpl": &assets.File{
"/": &assets.File{
Path: "/",
FileMode: 0x800001ed,
Mtime: time.Unix(1532785399, 1532785399000000000),
Data: nil,
}, "/index.md.tmpl": &assets.File{
Path: "/index.md.tmpl",
FileMode: 0x1a4,
Mtime: time.Unix(1532239511, 1532239511000000000),
Expand All @@ -21,9 +26,4 @@ var Assets = assets.NewFileSystem(map[string][]string{"/": []string{"index.md.tm
FileMode: 0x1a4,
Mtime: time.Unix(1532785399, 1532785399000000000),
Data: []byte(_Assetsac44302fb6150a621aa9d04a0350aac972bf7e18),
}, "/": &assets.File{
Path: "/",
FileMode: 0x800001ed,
Mtime: time.Unix(1532785399, 1532785399000000000),
Data: nil,
}}, "")
1 change: 1 addition & 0 deletions sample/sqlite/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
| [logs](logs.md) | 7 | audit log table | table |
| [post_comments](post_comments.md) | 7 | post and comments View table | view |
| [CamelizeTable](CamelizeTable.md) | 2 | | table |
| [check_constraints](check_constraints.md) | 6 | | table |

## Relations

Expand Down
68 changes: 68 additions & 0 deletions sample/sqlite/check_constraints.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# check_constraints

## Description


<details>
<summary><strong>Table Definition</strong></summary>

```sql
CREATE TABLE check_constraints (
id INTEGER PRIMARY KEY AUTOINCREMENT,
col TEXT CHECK(length(col) > 4),
brackets TEXT UNIQUE NOT NULL CHECK(((length(brackets) > 4))),
checkcheck TEXT UNIQUE NOT NULL CHECK(length(checkcheck) > 4),
downcase TEXT UNIQUE NOT NULL check(length(downcase) > 4),
nl TEXT UNIQUE NOT
NULL check(length(nl) > 4 OR
nl != 'ln')
)
```

</details>


## Columns

| Name | Type | Default | Nullable | Children | Parents | Comment |
| ---- | ---- | ------- | -------- | -------- | ------- | ------- |
| id | INTEGER | | true | | | |
| col | TEXT | | true | | | |
| brackets | TEXT | | false | | | |
| checkcheck | TEXT | | false | | | |
| downcase | TEXT | | false | | | |
| nl | TEXT | | false | | | |

## Constraints

| Name | Type | Definition |
| ---- | ---- | ---------- |
| id | PRIMARY KEY | PRIMARY KEY (id) |
| sqlite_autoindex_check_constraints_4 | UNIQUE | UNIQUE (nl) |
| sqlite_autoindex_check_constraints_3 | UNIQUE | UNIQUE (downcase) |
| sqlite_autoindex_check_constraints_2 | UNIQUE | UNIQUE (checkcheck) |
| sqlite_autoindex_check_constraints_1 | UNIQUE | UNIQUE (brackets) |
| - | CHECK | CHECK(length(col) > 4) |
| - | CHECK | CHECK(((length(brackets) > 4))) |
| - | CHECK | CHECK(length(checkcheck) > 4) |
| - | CHECK | check(length(downcase) > 4) |
| - | CHECK | check(length(nl) > 4 OR nl != 'ln') |

## Indexes

| Name | Definition |
| ---- | ---------- |
| sqlite_autoindex_check_constraints_4 | UNIQUE (nl) |
| sqlite_autoindex_check_constraints_3 | UNIQUE (downcase) |
| sqlite_autoindex_check_constraints_2 | UNIQUE (checkcheck) |
| sqlite_autoindex_check_constraints_1 | UNIQUE (brackets) |



## Relations

![er](check_constraints.png)

---

> Generated by [tbls](https://github.com/k1LoW/tbls)
Binary file added sample/sqlite/check_constraints.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified sample/sqlite/schema.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions sample/sqlite/users.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ CREATE TABLE users (
| id | PRIMARY KEY | PRIMARY KEY (id) |
| sqlite_autoindex_users_2 | UNIQUE | UNIQUE (email) |
| sqlite_autoindex_users_1 | UNIQUE | UNIQUE (username) |
| - | CHECK | CHECK(length(username) > 4) |

## Indexes

Expand Down
12 changes: 12 additions & 0 deletions test/sqlite.sql
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
PRAGMA foreign_keys = ON;
DROP TRIGGER IF EXISTS update_posts_updated;
DROP VIEW IF EXISTS post_comments;
DROP TABLE IF EXISTS check_constraints;
DROP TABLE IF EXISTS CamelizeTable;
DROP TABLE IF EXISTS logs;
DROP TABLE IF EXISTS comment_stars;
Expand Down Expand Up @@ -81,3 +82,14 @@ CREATE TRIGGER update_posts_updated AFTER UPDATE ON posts FOR EACH ROW
BEGIN
UPDATE posts SET updated = current_timestamp WHERE id = OLD.id;
END;

CREATE TABLE check_constraints (
id INTEGER PRIMARY KEY AUTOINCREMENT,
col TEXT CHECK(length(col) > 4),
brackets TEXT UNIQUE NOT NULL CHECK(((length(brackets) > 4))),
checkcheck TEXT UNIQUE NOT NULL CHECK(length(checkcheck) > 4),
downcase TEXT UNIQUE NOT NULL check(length(downcase) > 4),
nl TEXT UNIQUE NOT
NULL check(length(nl) > 4 OR
nl != 'ln')
);

0 comments on commit 06ca144

Please sign in to comment.