Commit 9a01e842 authored by Jason Moiron's avatar Jason Moiron

add support for returning sql create table as a map of table names to create...

add support for returning sql create table as a map of table names to create strings, use ColumnMap.sqltype in table creation
parent 7d2142f2
......@@ -121,55 +121,83 @@ func (m *DbMap) AddTableWithName(i interface{}, name string) *TableMap {
return m.AddTable(i, name)
}
// CreateTablesSql returns create table SQL as a map of table names to
// their associated CREATE TABLE statements
func (m *DbMap) CreateTablesSql() (map[string]string, error) {
return m.createTables(false, false)
}
// CreateTables iterates through TableMaps registered to this DbMap and
// executes "create table" statements against the database for each.
//
// This is particularly useful in unit tests where you want to create
// and destroy the schema automatically.
func (m *DbMap) CreateTables() error {
return m.createTables(false)
_, err := m.createTables(false, true)
return err
}
// CreateTablesIfNotExists is similar to CreateTables, but starts
// each statement with "create table if not exists" so that existing
// tables do not raise errors
func (m *DbMap) CreateTablesIfNotExists() error {
return m.createTables(true)
_, err := m.createTables(true, true)
return err
}
func writeColumnSql(sql *bytes.Buffer, table *TableMap, col *ColumnMap) {
// FIXME: Why can't the column have a reference to its own table as well
sqltype := col.sqltype
if len(sqltype) == 0 {
sqltype = table.dbmap.Dialect.ToSqlType(col)
}
sql.WriteString(fmt.Sprintf("%s %s", table.dbmap.Dialect.QuoteField(col.ColumnName), sqltype))
if col.isPK {
sql.WriteString(" not null")
if len(table.keys) == 1 {
sql.WriteString(" primary key")
}
}
if col.Unique {
sql.WriteString(" unique")
}
if col.isAutoIncr {
sql.WriteString(" " + table.dbmap.Dialect.AutoIncrStr())
}
}
func (m *DbMap) createTables(ifNotExists bool) error {
func (m *DbMap) createTables(ifNotExists, exec bool) (map[string]string, error) {
var err error
ret := map[string]string{}
sep := ", "
prefix := ""
if !exec {
sep = ",\n"
prefix = " "
}
for i := range m.tables {
table := m.tables[i]
create := "create table"
s := bytes.Buffer{}
s.WriteString("create table ")
if ifNotExists {
create += " if not exists"
s.WriteString("if not exists ")
}
s.WriteString(m.Dialect.QuoteField(table.TableName))
s.WriteString(" (")
if !exec {
s.WriteString("\n")
}
s := bytes.Buffer{}
s.WriteString(fmt.Sprintf("%s %s (", create, m.Dialect.QuoteField(table.TableName)))
x := 0
for _, col := range table.columns {
if !col.Transient {
if x > 0 {
s.WriteString(", ")
s.WriteString(sep)
}
stype := m.Dialect.ToSqlType(col.gotype, col.MaxSize, col.isAutoIncr)
s.WriteString(fmt.Sprintf("%s %s", m.Dialect.QuoteField(col.ColumnName), stype))
if col.isPK {
s.WriteString(" not null")
if len(table.keys) == 1 {
s.WriteString(" primary key")
}
}
if col.Unique {
s.WriteString(" unique")
}
if col.isAutoIncr {
s.WriteString(fmt.Sprintf(" %s", m.Dialect.AutoIncrStr()))
}
s.WriteString(prefix)
writeColumnSql(&s, table, col)
x++
}
}
......@@ -183,15 +211,17 @@ func (m *DbMap) createTables(ifNotExists bool) error {
}
s.WriteString(")")
}
s.WriteString(") ")
s.WriteString(m.Dialect.CreateTableSuffix())
s.WriteString(";")
_, err = m.Exec(s.String())
if err != nil {
break
s.WriteString(fmt.Sprintf(")%s;", m.Dialect.CreateTableSuffix()))
if exec {
_, err = m.Exec(s.String())
if err != nil {
break
}
} else {
ret[table.TableName] = s.String()
}
}
return err
return ret, err
}
// DropTables iterates through TableMaps registered to this DbMap and
......
......@@ -16,7 +16,7 @@ type Dialect interface {
// table of the given Go Type. maxsize can be used to switch based on
// size. For example, in MySQL []byte could map to BLOB, MEDIUMBLOB,
// or LONGBLOB depending on the maxsize
ToSqlType(val reflect.Type, maxsize int, isAutoIncr bool) string
ToSqlType(col *ColumnMap) string
// string to append to primary key column definitions
AutoIncrStr() string
......@@ -59,8 +59,8 @@ type SqliteDialect struct {
suffix string
}
func (d SqliteDialect) ToSqlType(val reflect.Type, maxsize int, isAutoIncr bool) string {
switch val.Kind() {
func (d SqliteDialect) ToSqlType(col *ColumnMap) string {
switch col.gotype.Kind() {
case reflect.Bool:
return "integer"
case reflect.Int, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint16, reflect.Uint32, reflect.Uint64:
......@@ -68,12 +68,12 @@ func (d SqliteDialect) ToSqlType(val reflect.Type, maxsize int, isAutoIncr bool)
case reflect.Float64, reflect.Float32:
return "real"
case reflect.Slice:
if val.Elem().Kind() == reflect.Uint8 {
if col.gotype.Elem().Kind() == reflect.Uint8 {
return "blob"
}
}
switch val.Name() {
switch col.gotype.Name() {
case "NullableInt64":
return "integer"
case "NullableFloat64":
......@@ -84,7 +84,8 @@ func (d SqliteDialect) ToSqlType(val reflect.Type, maxsize int, isAutoIncr bool)
return "blob"
}
if maxsize < 1 {
maxsize := col.MaxSize
if col.MaxSize < 1 {
maxsize = 255
}
return fmt.Sprintf("varchar(%d)", maxsize)
......@@ -129,29 +130,30 @@ type PostgresDialect struct {
suffix string
}
func (d PostgresDialect) ToSqlType(val reflect.Type, maxsize int, isAutoIncr bool) string {
switch val.Kind() {
func (d PostgresDialect) ToSqlType(col *ColumnMap) string {
switch col.gotype.Kind() {
case reflect.Bool:
return "boolean"
case reflect.Int, reflect.Int16, reflect.Int32, reflect.Uint16, reflect.Uint32:
if isAutoIncr {
if col.isAutoIncr {
return "serial"
}
return "integer"
case reflect.Int64, reflect.Uint64:
if isAutoIncr {
if col.isAutoIncr {
return "bigserial"
}
return "bigint"
case reflect.Float64, reflect.Float32:
return "real"
case reflect.Slice:
if val.Elem().Kind() == reflect.Uint8 {
if col.gotype.Elem().Kind() == reflect.Uint8 {
return "bytea"
}
}
switch val.Name() {
switch col.gotype.Name() {
case "NullableInt64":
return "bigint"
case "NullableFloat64":
......@@ -162,7 +164,8 @@ func (d PostgresDialect) ToSqlType(val reflect.Type, maxsize int, isAutoIncr boo
return "bytea"
}
if maxsize < 1 {
maxsize := col.MaxSize
if col.MaxSize < 1 {
maxsize = 255
}
return fmt.Sprintf("varchar(%d)", maxsize)
......@@ -225,8 +228,8 @@ type MySQLDialect struct {
Encoding string
}
func (m MySQLDialect) ToSqlType(val reflect.Type, maxsize int, isAutoIncr bool) string {
switch val.Kind() {
func (m MySQLDialect) ToSqlType(col *ColumnMap) string {
switch col.gotype.Kind() {
case reflect.Bool:
return "boolean"
case reflect.Int, reflect.Int16, reflect.Int32, reflect.Uint16, reflect.Uint32:
......@@ -236,12 +239,12 @@ func (m MySQLDialect) ToSqlType(val reflect.Type, maxsize int, isAutoIncr bool)
case reflect.Float64, reflect.Float32:
return "double"
case reflect.Slice:
if val.Elem().Kind() == reflect.Uint8 {
if col.gotype.Elem().Kind() == reflect.Uint8 {
return "mediumblob"
}
}
switch val.Name() {
switch col.gotype.Name() {
case "NullableInt64":
return "bigint"
case "NullableFloat64":
......@@ -252,7 +255,8 @@ func (m MySQLDialect) ToSqlType(val reflect.Type, maxsize int, isAutoIncr bool)
return "mediumblob"
}
if maxsize < 1 {
maxsize := col.MaxSize
if col.MaxSize < 1 {
maxsize = 255
}
return fmt.Sprintf("varchar(%d)", maxsize)
......
Future:
- some way to designate a column as a foreign key
Todo:
- benchmarks that can compare mainline gorp to this fork
......@@ -8,6 +12,10 @@ Todo:
In Progress:
(Both of these need tests)
- alter schema creation to take advantage of ColMap.sqltype
- alter schema creation to be able to return output (so people can look at or inspect it)
Done:
- remove list & new struct support form in favor of filling pointers and slices
......
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