From f210bc824f6da5573128710ee96cdb995a08fbfe Mon Sep 17 00:00:00 2001 From: Chuck Preslar Date: Wed, 14 Aug 2013 16:33:55 -0400 Subject: [PATCH] Update FOREIGN KEY to work without index name. --- README.md | 55 ++++++++++++++++++++++++++ codex.go | 1 + managers/alter_manager.go | 8 +--- nodes/constraint_node.go | 10 ++--- visitors/to_sql_visitor.go | 79 +++++++++++++++++++++++++++++++++----- 5 files changed, 131 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index a7a0c0c..b1d270f 100644 --- a/README.md +++ b/README.md @@ -98,6 +98,61 @@ sql, err := users.Delete(users("id").Eq(1)).ToSql() // DELETE FROM "users" WHERE "users"."id" = 1 ``` + +## Creations + +The codex package currently provides a few common SQL data and constraint types as constants to use with creating and altering tables. + +__Constraints__ + +* NOT_NULL +* UNIQUE +* PRIMARY_KEY +* FOREIGN_KEY +* CHECK +* DEFAULT + +__Types__ + +* STRING +* TEXT +* BOOLEAN +* INTEGER +* FLOAT +* DECIMAL +* DATE +* TIME +* DATETIME +* TIMESTAMP + +```go +// ... + +users := codex.CreateTable("users"). + AddColumn("first_name", codex.STRING). + AddColumn("last_name", codex.STRING). + AddColumn("email", codex.STRING). + AddColumn("id", codex.INTEGER). + AddConstraint("first_name", codex.NOT_NULL). + AddConstraint("id", codex.PRIMARY_KEY). + AddConstraint("email", codex.UNIQUE, "users_uniq_email") // Optional last argument supplies index name. + +sql, err := users.ToSql() + +// CREATE TABLE "users" (); +// ALTER TABLE "users" ADD "first_name" character varying(255); +// ALTER TABLE "users" ADD "last_name" character varying(255); +// ALTER TABLE "users" ADD "email" character varying(255); +// ALTER TABLE "users" ADD "id" integer; +// ALTER TABLE "users" ALTER "first_name" SET NOT NULL; +// ALTER TABLE "users" ADD PRIMARY KEY("id"); +// ALTER TABLE "users" ADD CONSTRAINT "users_email_uniq" UNIQUE("email"); +``` + +## Alterations + +Alterations work the same as Creations, only the the initializer function `AlterTable` is used in place of `CreateTable`. + ## Documentation View godoc or visit [godoc.org](http://godoc.org/github.com/chuckpreslar/codex). diff --git a/codex.go b/codex.go index 131c83e..adcb39e 100644 --- a/codex.go +++ b/codex.go @@ -32,6 +32,7 @@ const ( TIMESTAMP = sql.TIMESTAMP ) +// ToggleDebugMode toggles debugger variable for managers package. func ToggleDebugMode() { visitors.DEBUG = !visitors.DEBUG } diff --git a/managers/alter_manager.go b/managers/alter_manager.go index 43cc52e..b00a1ed 100644 --- a/managers/alter_manager.go +++ b/managers/alter_manager.go @@ -21,17 +21,11 @@ func (self *AlterManager) AddColumn(name interface{}, typ sql.Type) *AlterManage } func (self *AlterManager) AddConstraint(column interface{}, kind sql.Constraint, options ...interface{}) *AlterManager { - var expr interface{} - - if 0 < len(options) { - expr = options[0] - } - if _, ok := column.(string); ok { column = nodes.UnqualifiedColumn(column) } - self.Tree.Constraints = append(self.Tree.Constraints, nodes.Constraint(column, kind, expr)) + self.Tree.Constraints = append(self.Tree.Constraints, nodes.Constraint(column, kind, options...)) return self } diff --git a/nodes/constraint_node.go b/nodes/constraint_node.go index a6b060a..dac7960 100644 --- a/nodes/constraint_node.go +++ b/nodes/constraint_node.go @@ -7,16 +7,16 @@ import ( // ConstraintNode is a Nary node. type ConstraintNode struct { - Column interface{} - Kind sql.Constraint - Expr interface{} + Column interface{} + Kind sql.Constraint + Options []interface{} } // ConstraintNode factory method. -func Constraint(column interface{}, kind sql.Constraint, expr interface{}) (constraint *ConstraintNode) { +func Constraint(column interface{}, kind sql.Constraint, options ...interface{}) (constraint *ConstraintNode) { constraint = new(ConstraintNode) constraint.Column = column constraint.Kind = kind - constraint.Expr = expr + constraint.Options = options return } diff --git a/visitors/to_sql_visitor.go b/visitors/to_sql_visitor.go index 7907175..174bc19 100644 --- a/visitors/to_sql_visitor.go +++ b/visitors/to_sql_visitor.go @@ -368,22 +368,69 @@ func (self *ToSqlVisitor) VisitUnexistingColumn(o *nodes.UnexistingColumnNode, v func (self *ToSqlVisitor) VisitConstraint(o *nodes.ConstraintNode, visitor VisitorInterface) string { str := "" switch o.Kind { + //FIXME: This looks a little sloppy, clean it up. case sql.UNIQUE, sql.PRIMARY_KEY: str = fmt.Sprintf("%vADD%v", str, SPACE) - if nil != o.Expr { - if _, ok := o.Expr.(string); ok { - o.Expr = nodes.IndexName(o.Expr) + // Optional index name. + if 0 < len(o.Options) { + expr := o.Options[0] + if _, ok := expr.(string); ok { + expr = nodes.IndexName(expr) } - str = fmt.Sprintf("%vCONSTRAINT %v%v", str, visitor.Visit(o.Expr, visitor), SPACE) + str = fmt.Sprintf("%vCONSTRAINT %v%v", str, visitor.Visit(expr, visitor), SPACE) } str = fmt.Sprintf("%v%v(%v)", str, visitor.Visit(o.Kind, visitor), visitor.Visit(o.Column, visitor)) + + case sql.FOREIGN_KEY: + str = fmt.Sprintf("%vADD%v", str, SPACE) + // For FOREIGN KEY, index name is optional, REFERENCES is not. + // + // No index name ex. + // + // CreateTable("orders"). + // AddColumn("user_id"). + // AddConstraint("user_id", FOREIGN_KEY, "users") + // + // With option index name ex. + // + // CreateTable("orders"). + // AddColumn("user_id"). + // AddConstraint("user_id", FOREIGN_KEY, "users_fkey", "users") + if 1 < len(o.Options) { + expr := o.Options[0] + if _, ok := expr.(string); ok { + expr = nodes.IndexName(expr) + } + + // Remove this item from the array, avoiding any potential memory leak. + // https://code.google.com/p/go-wiki/wiki/SliceTricks + length := len(o.Options) - 1 + copy(o.Options[0:], o.Options[1:]) + o.Options[length] = nil + o.Options = o.Options[:length] + + str = fmt.Sprintf("%vCONSTRAINT %v%v", str, visitor.Visit(expr, visitor), SPACE) + } + + str = fmt.Sprintf("%v%v(%v)", str, visitor.Visit(o.Kind, visitor), visitor.Visit(o.Column, visitor)) + + // If option is not here, user didn't do it right, but don't dereference and panic. + if 0 < len(o.Options) { + relation := o.Options[0] + if _, ok := relation.(string); ok { + relation = nodes.Relation(relation.(string)) + } + + str = fmt.Sprintf("%v%vREFERENCES %v", str, SPACE, visitor.Visit(relation, visitor)) + } + default: str = fmt.Sprintf("ALTER %v SET %v", visitor.Visit(o.Column, visitor), visitor.Visit(o.Kind, visitor)) - if nil != o.Expr { - str = fmt.Sprintf("%v %v", str, visitor.Visit(o.Expr, visitor)) + if 0 < len(o.Options) { + str = fmt.Sprintf("%v %v", str, visitor.Visit(o.Options[0], visitor)) } str = fmt.Sprintf("%v", str) @@ -551,12 +598,24 @@ func (self *ToSqlVisitor) VisitAlterStatement(o *nodes.AlterStatementNode, visit str = fmt.Sprintf("CREATE TABLE %v ();\n", visitor.Visit(o.Relation, visitor)) } - for _, column := range o.Columns { - str = fmt.Sprintf("%vALTER TABLE %v %v;\n", str, visitor.Visit(o.Relation, visitor), visitor.Visit(column, visitor)) + length := len(o.Columns) - 1 + + for index, column := range o.Columns { + str = fmt.Sprintf("%vALTER TABLE %v %v;", str, visitor.Visit(o.Relation, visitor), visitor.Visit(column, visitor)) + + if index != length { + str = fmt.Sprintf("%v\n", str) + } } - for _, constraint := range o.Constraints { - str = fmt.Sprintf("%vALTER TABLE %v %v;\n", str, visitor.Visit(o.Relation, visitor), visitor.Visit(constraint, visitor)) + length = len(o.Constraints) - 1 + + for index, constraint := range o.Constraints { + str = fmt.Sprintf("%vALTER TABLE %v %v;", str, visitor.Visit(o.Relation, visitor), visitor.Visit(constraint, visitor)) + + if index != length { + str = fmt.Sprintf("%v\n", str) + } } return str