diff --git a/orm/cmd.go b/orm/cmd.go new file mode 100644 index 00000000..174091f4 --- /dev/null +++ b/orm/cmd.go @@ -0,0 +1,144 @@ +package orm + +import ( + "flag" + "fmt" + "os" + "strings" +) + +type commander interface { + Parse([]string) + Run() +} + +var ( + commands = make(map[string]commander) +) + +func printHelp(errs ...string) { + content := `orm command usage: + + syncdb - auto create tables + sqlall - print sql of create tables + help - print this help +` + + if len(errs) > 0 { + fmt.Println(errs[0]) + } + fmt.Println(content) + os.Exit(2) +} + +func RunCommand() { + if len(os.Args) < 2 || os.Args[1] != "orm" { + return + } + + BootStrap() + + args := argString(os.Args[2:]) + name := args.Get(0) + + if name == "help" { + printHelp() + } + + if cmd, ok := commands[name]; ok { + cmd.Parse(os.Args[3:]) + cmd.Run() + os.Exit(0) + } else { + if name == "" { + printHelp() + } else { + printHelp(fmt.Sprintf("unknown command %s", name)) + } + } +} + +type commandSyncDb struct { + al *alias + force bool + verbose bool +} + +func (d *commandSyncDb) Parse(args []string) { + var name string + + flagSet := flag.NewFlagSet("orm command: syncdb", flag.ExitOnError) + flagSet.StringVar(&name, "db", "default", "DataBase alias name") + flagSet.BoolVar(&d.force, "force", false, "drop tables before create") + flagSet.BoolVar(&d.verbose, "v", false, "verbose info") + flagSet.Parse(args) + + d.al = getDbAlias(name) +} + +func (d *commandSyncDb) Run() { + var drops []string + if d.force { + drops = getDbDropSql(d.al) + } + + db := d.al.DB + + if d.force { + for i, mi := range modelCache.allOrdered() { + query := drops[i] + _, err := db.Exec(query) + result := "" + if err != nil { + result = err.Error() + } + fmt.Printf("drop table `%s` %s\n", mi.table, result) + if d.verbose { + fmt.Printf(" %s\n\n", query) + } + } + } + + tables := getDbCreateSql(d.al) + + for i, mi := range modelCache.allOrdered() { + query := tables[i] + _, err := db.Exec(query) + fmt.Printf("create table `%s` \n", mi.table) + if d.verbose { + query = " " + strings.Join(strings.Split(query, "\n"), "\n ") + fmt.Println(query) + } + if err != nil { + fmt.Printf(" %s\n", err.Error()) + } + if d.verbose { + fmt.Println("") + } + } +} + +type commandSqlAll struct { + al *alias +} + +func (d *commandSqlAll) Parse(args []string) { + var name string + + flagSet := flag.NewFlagSet("orm command: sqlall", flag.ExitOnError) + flagSet.StringVar(&name, "db", "default", "DataBase alias name") + flagSet.Parse(args) + + d.al = getDbAlias(name) +} + +func (d *commandSqlAll) Run() { + sqls := getDbCreateSql(d.al) + sql := strings.Join(sqls, "\n\n") + fmt.Println(sql) +} + +func init() { + commands["syncdb"] = new(commandSyncDb) + commands["sqlall"] = new(commandSqlAll) +} diff --git a/orm/cmd_utils.go b/orm/cmd_utils.go new file mode 100644 index 00000000..2811ad3a --- /dev/null +++ b/orm/cmd_utils.go @@ -0,0 +1,142 @@ +package orm + +import ( + "fmt" + "os" + "strings" +) + +func getDbAlias(name string) *alias { + if al, ok := dataBaseCache.get(name); ok { + return al + } else { + fmt.Println(fmt.Sprintf("unknown DataBase alias name %s", name)) + os.Exit(2) + } + + return nil +} + +func getDbDropSql(al *alias) (sqls []string) { + if len(modelCache.cache) == 0 { + fmt.Println("no Model found, need register your model") + os.Exit(2) + } + + Q := al.DbBaser.TableQuote() + + for _, mi := range modelCache.allOrdered() { + sqls = append(sqls, fmt.Sprintf(`DROP TABLE IF EXISTS %s%s%s`, Q, mi.table, Q)) + } + return sqls +} + +func getDbCreateSql(al *alias) (sqls []string) { + if len(modelCache.cache) == 0 { + fmt.Println("no Model found, need register your model") + os.Exit(2) + } + + Q := al.DbBaser.TableQuote() + T := al.DbBaser.DbTypes() + + for _, mi := range modelCache.allOrdered() { + sql := fmt.Sprintf("-- %s\n", strings.Repeat("-", 50)) + sql += fmt.Sprintf("-- Table Structure for `%s`\n", mi.fullName) + sql += fmt.Sprintf("-- %s\n", strings.Repeat("-", 50)) + + sql += fmt.Sprintf("CREATE TABLE IF NOT EXISTS %s%s%s (\n", Q, mi.table, Q) + + columns := make([]string, 0, len(mi.fields.fieldsDB)) + + for _, fi := range mi.fields.fieldsDB { + + fieldType := fi.fieldType + column := fmt.Sprintf(" %s%s%s ", Q, fi.column, Q) + col := "" + + checkColumn: + switch fieldType { + case TypeBooleanField: + col = T["bool"] + case TypeCharField: + col = fmt.Sprintf(T["string"], fi.size) + case TypeTextField: + col = T["string-text"] + case TypeDateField: + col = T["time.Time-date"] + case TypeDateTimeField: + col = T["time.Time"] + case TypeBitField: + col = T["int8"] + case TypeSmallIntegerField: + col = T["int16"] + case TypeIntegerField: + col = T["int32"] + case TypeBigIntegerField: + if al.Driver == DR_Sqlite { + fieldType = TypeIntegerField + goto checkColumn + } + col = T["int64"] + case TypePositiveBitField: + col = T["uint8"] + case TypePositiveSmallIntegerField: + col = T["uint16"] + case TypePositiveIntegerField: + col = T["uint32"] + case TypePositiveBigIntegerField: + col = T["uint64"] + case TypeFloatField: + col = T["float64"] + case TypeDecimalField: + s := T["float64-decimal"] + if strings.Index(s, "%d") == -1 { + col = s + } else { + col = fmt.Sprintf(s, fi.digits, fi.decimals) + } + case RelForeignKey, RelOneToOne: + fieldType = fi.relModelInfo.fields.pk.fieldType + goto checkColumn + } + + if fi.auto { + if al.Driver == DR_Postgres { + column += T["auto"] + } else { + column += col + " " + T["auto"] + } + } else if fi.pk { + column += col + " " + T["pk"] + } else { + column += col + + if fi.null == false { + column += " " + "NOT NULL" + } + + if fi.unique { + column += " " + "UNIQUE" + } + } + + if strings.Index(column, "%COL%") != -1 { + column = strings.Replace(column, "%COL%", fi.column, -1) + } + + columns = append(columns, column) + } + + sql += strings.Join(columns, ",\n") + sql += "\n)" + + if al.Driver == DR_MySQL { + sql += " ENGINE=INNODB" + } + + sqls = append(sqls, sql) + } + + return sqls +} diff --git a/orm/command.go b/orm/command.go deleted file mode 100644 index 78508bdc..00000000 --- a/orm/command.go +++ /dev/null @@ -1,44 +0,0 @@ -package orm - -import ( - "flag" - "fmt" - "os" -) - -func printHelp() { - -} - -func getSqlAll() (sql string) { - for _, mi := range modelCache.allOrdered() { - _ = mi - } - return -} - -func runCommand() { - if len(os.Args) < 2 || os.Args[1] != "orm" { - return - } - - _ = flag.NewFlagSet("orm command", flag.ExitOnError) - - args := argString(os.Args[2:]) - cmd := args.Get(0) - - switch cmd { - case "syncdb": - case "sqlall": - sql := getSqlAll() - fmt.Println(sql) - default: - if cmd != "" { - fmt.Printf("unknown command %s", cmd) - } else { - printHelp() - } - - os.Exit(2) - } -} diff --git a/orm/db.go b/orm/db.go index d09dbe82..7b30797f 100644 --- a/orm/db.go +++ b/orm/db.go @@ -805,7 +805,7 @@ setValue: _, err = str.Int32() case TypeBigIntegerField: _, err = str.Int64() - case TypePostiveBitField: + case TypePositiveBitField: _, err = str.Uint8() case TypePositiveSmallIntegerField: _, err = str.Uint16() @@ -1112,3 +1112,7 @@ func (d *dbBase) TimeFromDB(t *time.Time, tz *time.Location) { func (d *dbBase) TimeToDB(t *time.Time, tz *time.Location) { *t = t.In(tz) } + +func (d *dbBase) DbTypes() map[string]string { + return nil +} diff --git a/orm/db_mysql.go b/orm/db_mysql.go index b6c3a118..08a9b509 100644 --- a/orm/db_mysql.go +++ b/orm/db_mysql.go @@ -17,6 +17,26 @@ var mysqlOperators = map[string]string{ "iendswith": "LIKE ?", } +var mysqlTypes = map[string]string{ + "auto": "AUTO_INCREMENT NOT NULL PRIMARY KEY", + "pk": "NOT NULL PRIMARY KEY", + "bool": "bool", + "string": "varchar(%d)", + "string-text": "longtext", + "time.Time-date": "date", + "time.Time": "datetime", + "int8": "tinyint", + "int16": "smallint", + "int32": "integer", + "int64": "bigint", + "uint8": "tinyint unsigned", + "uint16": "smallint unsigned", + "uint32": "integer unsigned", + "uint64": "bigint unsigned", + "float64": "double precision", + "float64-decimal": "numeric(%d, %d)", +} + type dbBaseMysql struct { dbBase } @@ -27,6 +47,10 @@ func (d *dbBaseMysql) OperatorSql(operator string) string { return mysqlOperators[operator] } +func (d *dbBaseMysql) DbTypes() map[string]string { + return mysqlTypes +} + func newdbBaseMysql() dbBaser { b := new(dbBaseMysql) b.ins = b diff --git a/orm/db_postgres.go b/orm/db_postgres.go index 729e6aab..5e20a110 100644 --- a/orm/db_postgres.go +++ b/orm/db_postgres.go @@ -20,6 +20,26 @@ var postgresOperators = map[string]string{ "iendswith": "LIKE UPPER(?)", } +var postgresTypes = map[string]string{ + "auto": "serial NOT NULL PRIMARY KEY", + "pk": "NOT NULL PRIMARY KEY", + "bool": "bool", + "string": "varchar(%d)", + "string-text": "text", + "time.Time-date": "date", + "time.Time": "timestamp with time zone", + "int8": `smallint CHECK("%COL%" >= -127 AND "%COL%" <= 128)`, + "int16": "smallint", + "int32": "integer", + "int64": "bigint", + "uint8": `smallint CHECK("%COL%" >= 0 AND "%COL%" <= 255)`, + "uint16": `integer CHECK("%COL%" >= 0)`, + "uint32": `bigint CHECK("%COL%" >= 0)`, + "uint64": `bigint CHECK("%COL%" >= 0)`, + "float64": "double precision", + "float64-decimal": "numeric(%d, %d)", +} + type dbBasePostgres struct { dbBase } @@ -87,6 +107,10 @@ func (d *dbBasePostgres) HasReturningID(mi *modelInfo, query *string) (has bool) return } +func (d *dbBasePostgres) DbTypes() map[string]string { + return postgresTypes +} + func newdbBasePostgres() dbBaser { b := new(dbBasePostgres) b.ins = b diff --git a/orm/db_sqlite.go b/orm/db_sqlite.go index efea1967..20a90cde 100644 --- a/orm/db_sqlite.go +++ b/orm/db_sqlite.go @@ -19,6 +19,26 @@ var sqliteOperators = map[string]string{ "iendswith": "LIKE ? ESCAPE '\\'", } +var sqliteTypes = map[string]string{ + "auto": "NOT NULL PRIMARY KEY AUTOINCREMENT", + "pk": "NOT NULL PRIMARY KEY", + "bool": "bool", + "string": "varchar(%d)", + "string-text": "text", + "time.Time-date": "date", + "time.Time": "datetime", + "int8": "tinyint", + "int16": "smallint", + "int32": "integer", + "int64": "bigint", + "uint8": "tinyint unsigned", + "uint16": "smallint unsigned", + "uint32": "integer unsigned", + "uint64": "bigint unsigned", + "float64": "real", + "float64-decimal": "decimal", +} + type dbBaseSqlite struct { dbBase } @@ -43,6 +63,10 @@ func (d *dbBaseSqlite) MaxLimit() uint64 { return 9223372036854775807 } +func (d *dbBaseSqlite) DbTypes() map[string]string { + return sqliteTypes +} + func newdbBaseSqlite() dbBaser { b := new(dbBaseSqlite) b.ins = b diff --git a/orm/models.go b/orm/models.go index 1c2270ce..112183c0 100644 --- a/orm/models.go +++ b/orm/models.go @@ -84,3 +84,10 @@ func (mc *_modelCache) set(table string, mi *modelInfo) *modelInfo { } return mii } + +func (mc *_modelCache) clean() { + mc.orders = make([]string, 0) + mc.cache = make(map[string]*modelInfo) + mc.cacheByFN = make(map[string]*modelInfo) + mc.done = false +} diff --git a/orm/models_boot.go b/orm/models_boot.go index fa18d394..33afa701 100644 --- a/orm/models_boot.go +++ b/orm/models_boot.go @@ -8,7 +8,7 @@ import ( "strings" ) -func registerModel(model interface{}) { +func registerModel(model interface{}, prefix string) { val := reflect.ValueOf(model) ind := reflect.Indirect(val) typ := ind.Type() @@ -17,20 +17,25 @@ func registerModel(model interface{}) { panic(fmt.Sprintf(" cannot use non-ptr model struct `%s`", getFullName(typ))) } - info := newModelInfo(val) + table := getTableName(val) + + if prefix != "" { + table = prefix + table + } name := getFullName(typ) if _, ok := modelCache.getByFN(name); ok { - fmt.Printf(" model `%s` redeclared, must be unique\n", name) + fmt.Printf(" model `%s` repeat register, must be unique\n", name) os.Exit(2) } - table := getTableName(val) if _, ok := modelCache.get(table); ok { - fmt.Printf(" table name `%s` redeclared, must be unique\n", table) + fmt.Printf(" table name `%s` repeat register, must be unique\n", table) os.Exit(2) } + info := newModelInfo(val) + if info.fields.pk == nil { outFor: for _, fi := range info.fields.fieldsDB { @@ -58,6 +63,7 @@ func registerModel(model interface{}) { info.pkg = typ.PkgPath() info.model = model info.manual = true + modelCache.set(table, info) } @@ -72,7 +78,7 @@ func bootStrap() { ) if dataBaseCache.getDefault() == nil { - err = fmt.Errorf("must have one register alias named `default`") + err = fmt.Errorf("must have one register DataBase alias named `default`") goto end } @@ -97,7 +103,7 @@ func bootStrap() { switch fi.fieldType { case RelManyToMany: if fi.relThrough != "" { - msg := fmt.Sprintf("filed `%s` wrong rel_through value `%s`", fi.fullName, fi.relThrough) + msg := fmt.Sprintf("field `%s` wrong rel_through value `%s`", fi.fullName, fi.relThrough) if i := strings.LastIndex(fi.relThrough, "."); i != -1 && len(fi.relThrough) > (i+1) { pn := fi.relThrough[:i] mn := fi.relThrough[i+1:] @@ -238,11 +244,22 @@ end: func RegisterModel(models ...interface{}) { if modelCache.done { - panic(fmt.Errorf("RegisterModel must be run begore BootStrap")) + panic(fmt.Errorf("RegisterModel must be run before BootStrap")) } for _, model := range models { - registerModel(model) + registerModel(model, "") + } +} + +// register model with a prefix +func RegisterModelWithPrefix(prefix string, models ...interface{}) { + if modelCache.done { + panic(fmt.Errorf("RegisterModel must be run before BootStrap")) + } + + for _, model := range models { + registerModel(model, prefix) } } diff --git a/orm/models_fields.go b/orm/models_fields.go index 269e8580..a5b5937e 100644 --- a/orm/models_fields.go +++ b/orm/models_fields.go @@ -31,7 +31,7 @@ const ( // int64 TypeBigIntegerField // uint8 - TypePostiveBitField + TypePositiveBitField // uint16 TypePositiveSmallIntegerField // uint32 diff --git a/orm/models_info_f.go b/orm/models_info_f.go index 33506829..f3d91474 100644 --- a/orm/models_info_f.go +++ b/orm/models_info_f.go @@ -399,7 +399,7 @@ checkType: _, err = v.Int32() case TypeBigIntegerField: _, err = v.Int64() - case TypePostiveBitField: + case TypePositiveBitField: _, err = v.Uint8() case TypePositiveSmallIntegerField: _, err = v.Uint16() diff --git a/orm/models_info_m.go b/orm/models_info_m.go index dfbad42e..31e849b2 100644 --- a/orm/models_info_m.go +++ b/orm/models_info_m.go @@ -90,6 +90,9 @@ func newM2MModelInfo(m1, m2 *modelInfo) (info *modelInfo) { fa.auto = true fa.pk = true fa.dbcol = true + fa.name = "Id" + fa.column = "id" + fa.fullName = info.fullName + "." + fa.name f1.dbcol = true f2.dbcol = true diff --git a/orm/models_test.go b/orm/models_test.go index c9bbce2b..7794ffb0 100644 --- a/orm/models_test.go +++ b/orm/models_test.go @@ -3,10 +3,8 @@ package orm import ( "fmt" "os" - "strings" "time" - // _ "github.com/bylevel/pq" _ "github.com/go-sql-driver/mysql" _ "github.com/lib/pq" _ "github.com/mattn/go-sqlite3" @@ -153,325 +151,7 @@ var ( var dORM Ormer -var initSQLs = map[string]string{ - "mysql": "DROP TABLE IF EXISTS `user_profile`;\n" + - "DROP TABLE IF EXISTS `user`;\n" + - "DROP TABLE IF EXISTS `post`;\n" + - "DROP TABLE IF EXISTS `tag`;\n" + - "DROP TABLE IF EXISTS `post_tags`;\n" + - "DROP TABLE IF EXISTS `comment`;\n" + - "DROP TABLE IF EXISTS `data`;\n" + - "DROP TABLE IF EXISTS `data_null`;\n" + - "CREATE TABLE `user_profile` (\n" + - " `id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,\n" + - " `age` smallint NOT NULL,\n" + - " `money` double precision NOT NULL\n" + - ") ENGINE=INNODB;\n" + - "CREATE TABLE `user` (\n" + - " `id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,\n" + - " `user_name` varchar(30) NOT NULL UNIQUE,\n" + - " `email` varchar(100) NOT NULL,\n" + - " `password` varchar(100) NOT NULL,\n" + - " `status` smallint NOT NULL,\n" + - " `is_staff` bool NOT NULL,\n" + - " `is_active` bool NOT NULL,\n" + - " `created` date NOT NULL,\n" + - " `updated` datetime NOT NULL,\n" + - " `profile_id` integer\n" + - ") ENGINE=INNODB;\n" + - "CREATE TABLE `post` (\n" + - " `id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,\n" + - " `user_id` integer NOT NULL,\n" + - " `title` varchar(60) NOT NULL,\n" + - " `content` longtext NOT NULL,\n" + - " `created` datetime NOT NULL,\n" + - " `updated` datetime NOT NULL\n" + - ") ENGINE=INNODB;\n" + - "CREATE TABLE `tag` (\n" + - " `id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,\n" + - " `name` varchar(30) NOT NULL\n" + - ") ENGINE=INNODB;\n" + - "CREATE TABLE `post_tags` (\n" + - " `id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,\n" + - " `post_id` integer NOT NULL,\n" + - " `tag_id` integer NOT NULL,\n" + - " UNIQUE (`post_id`, `tag_id`)\n" + - ") ENGINE=INNODB;\n" + - "CREATE TABLE `comment` (\n" + - " `id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,\n" + - " `post_id` integer NOT NULL,\n" + - " `content` longtext NOT NULL,\n" + - " `parent_id` integer,\n" + - " `created` datetime NOT NULL\n" + - ") ENGINE=INNODB;\n" + - "CREATE TABLE `data` (\n" + - " `id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,\n" + - " `boolean` bool NOT NULL,\n" + - " `char` varchar(50) NOT NULL,\n" + - " `text` longtext NOT NULL,\n" + - " `date` date NOT NULL,\n" + - " `date_time` datetime NOT NULL,\n" + - " `byte` tinyint unsigned NOT NULL,\n" + - " `rune` integer NOT NULL,\n" + - " `int` integer NOT NULL,\n" + - " `int8` tinyint NOT NULL,\n" + - " `int16` smallint NOT NULL,\n" + - " `int32` integer NOT NULL,\n" + - " `int64` bigint NOT NULL,\n" + - " `uint` integer unsigned NOT NULL,\n" + - " `uint8` tinyint unsigned NULL,\n" + - " `uint16` smallint unsigned NOT NULL,\n" + - " `uint32` integer unsigned NOT NULL,\n" + - " `uint64` bigint unsigned NOT NULL,\n" + - " `float32` double precision NOT NULL,\n" + - " `float64` double precision NOT NULL,\n" + - " `decimal` numeric(8,4) NOT NULL\n" + - ") ENGINE=INNODB;\n" + - "CREATE TABLE `data_null` (\n" + - " `id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,\n" + - " `boolean` bool,\n" + - " `char` varchar(50),\n" + - " `text` longtext,\n" + - " `date` date,\n" + - " `date_time` datetime,\n" + - " `byte` tinyint unsigned,\n" + - " `rune` integer,\n" + - " `int` integer,\n" + - " `int8` tinyint,\n" + - " `int16` smallint,\n" + - " `int32` integer,\n" + - " `int64` bigint,\n" + - " `uint` integer unsigned,\n" + - " `uint8` tinyint unsigned,\n" + - " `uint16` smallint unsigned,\n" + - " `uint32` integer unsigned,\n" + - " `uint64` bigint unsigned,\n" + - " `float32` double precision,\n" + - " `float64` double precision,\n" + - " `decimal` numeric(8,4)\n" + - ") ENGINE=INNODB;\n" + - "CREATE INDEX `user_141c6eec` ON `user` (`profile_id`);\n" + - "CREATE INDEX `post_fbfc09f1` ON `post` (`user_id`);\n" + - "CREATE INDEX `comment_699ae8ca` ON `comment` (`post_id`);\n" + - "CREATE INDEX `comment_63f17a16` ON `comment` (`parent_id`);", - - "sqlite3": ` -DROP TABLE IF EXISTS "user_profile"; -DROP TABLE IF EXISTS "user"; -DROP TABLE IF EXISTS "post"; -DROP TABLE IF EXISTS "tag"; -DROP TABLE IF EXISTS "post_tags"; -DROP TABLE IF EXISTS "comment"; -DROP TABLE IF EXISTS "data"; -DROP TABLE IF EXISTS "data_null"; -CREATE TABLE "user_profile" ( - "id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, - "age" smallint NOT NULL, - "money" real NOT NULL -); -CREATE TABLE "user" ( - "id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, - "user_name" varchar(30) NOT NULL UNIQUE, - "email" varchar(100) NOT NULL, - "password" varchar(100) NOT NULL, - "status" smallint NOT NULL, - "is_staff" bool NOT NULL, - "is_active" bool NOT NULL, - "created" date NOT NULL, - "updated" datetime NOT NULL, - "profile_id" integer -); -CREATE TABLE "post" ( - "id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, - "user_id" integer NOT NULL, - "title" varchar(60) NOT NULL, - "content" text NOT NULL, - "created" datetime NOT NULL, - "updated" datetime NOT NULL -); -CREATE TABLE "tag" ( - "id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, - "name" varchar(30) NOT NULL -); -CREATE TABLE "post_tags" ( - "id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, - "post_id" integer NOT NULL, - "tag_id" integer NOT NULL, - UNIQUE ("post_id", "tag_id") -); -CREATE TABLE "comment" ( - "id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, - "post_id" integer NOT NULL, - "content" text NOT NULL, - "parent_id" integer, - "created" datetime NOT NULL -); -CREATE TABLE "data" ( - "id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, - "boolean" bool NOT NULL, - "char" varchar(50) NOT NULL, - "text" text NOT NULL, - "date" date NOT NULL, - "date_time" datetime NOT NULL, - "byte" tinyint unsigned NOT NULL, - "rune" integer NOT NULL, - "int" integer NOT NULL, - "int8" tinyint NOT NULL, - "int16" smallint NOT NULL, - "int32" integer NOT NULL, - "int64" bigint NOT NULL, - "uint" integer unsigned NOT NULL, - "uint8" tinyint unsigned NOT NULL, - "uint16" smallint unsigned NOT NULL, - "uint32" integer unsigned NOT NULL, - "uint64" bigint unsigned NOT NULL, - "float32" real NOT NULL, - "float64" real NOT NULL, - "decimal" decimal -); -CREATE TABLE "data_null" ( - "id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, - "boolean" bool, - "char" varchar(50), - "text" text, - "date" date, - "date_time" datetime, - "byte" tinyint unsigned, - "rune" integer, - "int" integer, - "int8" tinyint, - "int16" smallint, - "int32" integer, - "int64" bigint, - "uint" integer unsigned, - "uint8" tinyint unsigned, - "uint16" smallint unsigned, - "uint32" integer unsigned, - "uint64" bigint unsigned, - "float32" real, - "float64" real, - "decimal" decimal -); -CREATE INDEX "user_141c6eec" ON "user" ("profile_id"); -CREATE INDEX "post_fbfc09f1" ON "post" ("user_id"); -CREATE INDEX "comment_699ae8ca" ON "comment" ("post_id"); -CREATE INDEX "comment_63f17a16" ON "comment" ("parent_id"); -`, - - "postgres": ` -DROP TABLE IF EXISTS "user_profile"; -DROP TABLE IF EXISTS "user"; -DROP TABLE IF EXISTS "post"; -DROP TABLE IF EXISTS "tag"; -DROP TABLE IF EXISTS "post_tags"; -DROP TABLE IF EXISTS "comment"; -DROP TABLE IF EXISTS "data"; -DROP TABLE IF EXISTS "data_null"; -CREATE TABLE "user_profile" ( - "id" serial NOT NULL PRIMARY KEY, - "age" smallint NOT NULL, - "money" double precision NOT NULL -); -CREATE TABLE "user" ( - "id" serial NOT NULL PRIMARY KEY, - "user_name" varchar(30) NOT NULL UNIQUE, - "email" varchar(100) NOT NULL, - "password" varchar(100) NOT NULL, - "status" smallint NOT NULL, - "is_staff" boolean NOT NULL, - "is_active" boolean NOT NULL, - "created" date NOT NULL, - "updated" timestamp with time zone NOT NULL, - "profile_id" integer -); -CREATE TABLE "post" ( - "id" serial NOT NULL PRIMARY KEY, - "user_id" integer NOT NULL, - "title" varchar(60) NOT NULL, - "content" text NOT NULL, - "created" timestamp with time zone NOT NULL, - "updated" timestamp with time zone NOT NULL -); -CREATE TABLE "tag" ( - "id" serial NOT NULL PRIMARY KEY, - "name" varchar(30) NOT NULL -); -CREATE TABLE "post_tags" ( - "id" serial NOT NULL PRIMARY KEY, - "post_id" integer NOT NULL, - "tag_id" integer NOT NULL, - UNIQUE ("post_id", "tag_id") -); -CREATE TABLE "comment" ( - "id" serial NOT NULL PRIMARY KEY, - "post_id" integer NOT NULL, - "content" text NOT NULL, - "parent_id" integer, - "created" timestamp with time zone NOT NULL -); -CREATE TABLE "data" ( - "id" serial NOT NULL PRIMARY KEY, - "boolean" bool NOT NULL, - "char" varchar(50) NOT NULL, - "text" text NOT NULL, - "date" date NOT NULL, - "date_time" timestamp with time zone NOT NULL, - "byte" smallint CHECK("byte" >= 0 AND "byte" <= 255) NOT NULL, - "rune" integer NOT NULL, - "int" integer NOT NULL, - "int8" smallint CHECK("int8" >= -127 AND "int8" <= 128) NOT NULL, - "int16" smallint NOT NULL, - "int32" integer NOT NULL, - "int64" bigint NOT NULL, - "uint" bigint CHECK("uint" >= 0) NOT NULL, - "uint8" smallint CHECK("uint8" >= 0 AND "uint8" <= 255) NOT NULL, - "uint16" integer CHECK("uint16" >= 0) NOT NULL, - "uint32" bigint CHECK("uint32" >= 0) NOT NULL, - "uint64" bigint CHECK("uint64" >= 0) NOT NULL, - "float32" double precision NOT NULL, - "float64" double precision NOT NULL, - "decimal" numeric(8, 4) -); -CREATE TABLE "data_null" ( - "id" serial NOT NULL PRIMARY KEY, - "boolean" bool, - "char" varchar(50), - "text" text, - "date" date, - "date_time" timestamp with time zone, - "byte" smallint CHECK("byte" >= 0 AND "byte" <= 255), - "rune" integer, - "int" integer, - "int8" smallint CHECK("int8" >= -127 AND "int8" <= 128), - "int16" smallint, - "int32" integer, - "int64" bigint, - "uint" bigint CHECK("uint" >= 0), - "uint8" smallint CHECK("uint8" >= 0 AND "uint8" <= 255), - "uint16" integer CHECK("uint16" >= 0), - "uint32" bigint CHECK("uint32" >= 0), - "uint64" bigint CHECK("uint64" >= 0), - "float32" double precision, - "float64" double precision, - "decimal" numeric(8, 4) -); -CREATE INDEX "user_profile_id" ON "user" ("profile_id"); -CREATE INDEX "post_user_id" ON "post" ("user_id"); -CREATE INDEX "comment_post_id" ON "comment" ("post_id"); -CREATE INDEX "comment_parent_id" ON "comment" ("parent_id"); -`} - func init() { - // err := os.Setenv("TZ", "+00:00") - // fmt.Println(err) - - RegisterModel(new(Data), new(DataNull)) - RegisterModel(new(User)) - RegisterModel(new(Profile)) - RegisterModel(new(Post)) - RegisterModel(new(Tag)) - RegisterModel(new(Comment)) - Debug, _ = StrTo(DBARGS.Debug).Bool() if DBARGS.Driver == "" || DBARGS.Source == "" { @@ -484,29 +164,35 @@ Default DB Drivers. sqlite3: https://github.com/mattn/go-sqlite3 postgres: https://github.com/lib/pq -eg: mysql -ORM_DRIVER=mysql ORM_SOURCE="root:root@/my_db?charset=utf8" go test github.com/astaxie/beego/orm +usage: + +go get -u github.com/astaxie/beego/orm +go get -u github.com/go-sql-driver/mysql +go get -u github.com/mattn/go-sqlite3 +go get -u github.com/lib/pq + +#### MySQL +mysql -u root -e 'create database orm_test;' +export ORM_DRIVER=mysql +export ORM_SOURCE="root:@/orm_test?charset=utf8" +go test -v github.com/astaxie/beego/orm + + +#### Sqlite3 +touch /path/to/orm_test.db +export ORM_DRIVER=sqlite3 +export ORM_SOURCE=/path/to/orm_test.db +go test -v github.com/astaxie/beego/orm + + +#### PostgreSQL +psql -c 'create database orm_test;' -U postgres +export ORM_DRIVER=postgres +export ORM_SOURCE="user=postgres dbname=orm_test sslmode=disable" +go test -v github.com/astaxie/beego/orm `) os.Exit(2) } RegisterDataBase("default", DBARGS.Driver, DBARGS.Source, 20) - - BootStrap() - - dORM = NewOrm() - - queries := strings.Split(initSQLs[DBARGS.Driver], ";") - - for _, query := range queries { - query = strings.TrimSpace(query) - if len(query) == 0 { - continue - } - _, err := dORM.Raw(query).Exec() - if err != nil { - fmt.Println(err) - os.Exit(2) - } - } } diff --git a/orm/models_utils.go b/orm/models_utils.go index fe9cb965..d4677a76 100644 --- a/orm/models_utils.go +++ b/orm/models_utils.go @@ -52,7 +52,7 @@ func getFieldType(val reflect.Value) (ft int, err error) { case reflect.Int64: ft = TypeBigIntegerField case reflect.Uint8: - ft = TypePostiveBitField + ft = TypePositiveBitField case reflect.Uint16: ft = TypePositiveSmallIntegerField case reflect.Uint32, reflect.Uint: diff --git a/orm/orm_test.go b/orm/orm_test.go index cd3289e3..0346c1be 100644 --- a/orm/orm_test.go +++ b/orm/orm_test.go @@ -189,6 +189,47 @@ func throwFailNow(t *testing.T, err error, args ...interface{}) { } } +func TestSyncDb(t *testing.T) { + RegisterModel(new(Data), new(DataNull)) + RegisterModel(new(User)) + RegisterModel(new(Profile)) + RegisterModel(new(Post)) + RegisterModel(new(Tag)) + RegisterModel(new(Comment)) + + BootStrap() + + al := dataBaseCache.getDefault() + db := al.DB + + drops := getDbDropSql(al) + for _, query := range drops { + _, err := db.Exec(query) + throwFailNow(t, err, query) + } + + tables := getDbCreateSql(al) + for _, query := range tables { + _, err := db.Exec(query) + throwFailNow(t, err, query) + } + + modelCache.clean() +} + +func TestRegisterModels(t *testing.T) { + RegisterModel(new(Data), new(DataNull)) + RegisterModel(new(User)) + RegisterModel(new(Profile)) + RegisterModel(new(Post)) + RegisterModel(new(Tag)) + RegisterModel(new(Comment)) + + BootStrap() + + dORM = NewOrm() +} + func TestModelSyntax(t *testing.T) { user := &User{} ind := reflect.ValueOf(user).Elem() diff --git a/orm/types.go b/orm/types.go index d20a4df8..b4fd5c84 100644 --- a/orm/types.go +++ b/orm/types.go @@ -132,4 +132,5 @@ type dbBaser interface { HasReturningID(*modelInfo, *string) bool TimeFromDB(*time.Time, *time.Location) TimeToDB(*time.Time, *time.Location) + DbTypes() map[string]string }