From 5d9b4fb9682bc4bd0431f270a7cbd808fda11c9d Mon Sep 17 00:00:00 2001 From: Jason Moiron Date: Tue, 30 Sep 2014 22:28:16 -0400 Subject: [PATCH] add parent table pointer to colmap, add SetSqlCreate (fixes #21) --- dbmap.go | 31 +++++++++++++++++++++---------- modl_test.go | 20 ++++++++++++++++++++ tablemap.go | 12 ++++++++++++ 3 files changed, 53 insertions(+), 10 deletions(-) diff --git a/dbmap.go b/dbmap.go index 96b21a3..2c6c331 100644 --- a/dbmap.go +++ b/dbmap.go @@ -19,6 +19,7 @@ import ( "strings" "github.com/jmoiron/sqlx" + "github.com/jmoiron/sqlx/reflectx" ) // TableNameMapper is the function used by AddTable to map struct names to database table names, in analogy @@ -45,11 +46,17 @@ type DbMap struct { tables []*TableMap logger *log.Logger logPrefix string + mapper *reflectx.Mapper } -// NewDbMap returns a new DbMap using the db connection and dialect +// NewDbMap returns a new DbMap using the db connection and dialect. func NewDbMap(db *sql.DB, dialect Dialect) *DbMap { - return &DbMap{Db: db, Dialect: dialect, Dbx: sqlx.NewDb(db, dialect.DriverName())} + return &DbMap{ + Db: db, + Dialect: dialect, + Dbx: sqlx.NewDb(db, dialect.DriverName()), + mapper: reflectx.NewMapperFunc("db", sqlx.NameMapper), + } } // TraceOn turns on SQL statement logging for this DbMap. After this is @@ -101,7 +108,7 @@ func (m *DbMap) AddTable(i interface{}, name ...string) *TableMap { } } - tmap := &TableMap{gotype: t, TableName: Name, dbmap: m} + tmap := &TableMap{gotype: t, TableName: Name, dbmap: m, mapper: m.mapper} tmap.setupHooks(i) n := t.NumField() @@ -118,6 +125,7 @@ func (m *DbMap) AddTable(i interface{}, name ...string) *TableMap { Transient: columnName == "-", fieldName: f.Name, gotype: f.Type, + table: tmap, } tmap.Columns = append(tmap.Columns, cm) if cm.fieldName == "Version" { @@ -159,16 +167,19 @@ func (m *DbMap) CreateTablesIfNotExists() error { 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 +func writeColumnSql(sql *bytes.Buffer, col *ColumnMap) { + if len(col.createSql) > 0 { + sql.WriteString(col.createSql) + return + } sqltype := col.sqltype if len(sqltype) == 0 { - sqltype = table.dbmap.Dialect.ToSqlType(col) + sqltype = col.table.dbmap.Dialect.ToSqlType(col) } - sql.WriteString(fmt.Sprintf("%s %s", table.dbmap.Dialect.QuoteField(col.ColumnName), sqltype)) + sql.WriteString(fmt.Sprintf("%s %s", col.table.dbmap.Dialect.QuoteField(col.ColumnName), sqltype)) if col.isPK { sql.WriteString(" not null") - if len(table.Keys) == 1 { + if len(col.table.Keys) == 1 { sql.WriteString(" primary key") } } @@ -176,7 +187,7 @@ func writeColumnSql(sql *bytes.Buffer, table *TableMap, col *ColumnMap) { sql.WriteString(" unique") } if col.isAutoIncr { - sql.WriteString(" " + table.dbmap.Dialect.AutoIncrStr()) + sql.WriteString(" " + col.table.dbmap.Dialect.AutoIncrStr()) } } @@ -211,7 +222,7 @@ func (m *DbMap) createTables(ifNotExists, exec bool) (map[string]string, error) s.WriteString(sep) } s.WriteString(prefix) - writeColumnSql(&s, table, col) + writeColumnSql(&s, col) x++ } } diff --git a/modl_test.go b/modl_test.go index 6f4007c..e533c42 100644 --- a/modl_test.go +++ b/modl_test.go @@ -265,6 +265,26 @@ func TestDoubleAddTable(t *testing.T) { } } +// test overriding the create sql +func TestColMapCreateSql(t *testing.T) { + dbmap := newDbMap() + t1 := dbmap.AddTable(TableWithNull{}) + b := t1.ColMap("Bytes") + custom := "bytes text NOT NULL" + b.SetSqlCreate(custom) + var buf bytes.Buffer + writeColumnSql(&buf, b) + s := buf.String() + if s != custom { + t.Errorf("Expected custom sql `%s`, got %s", custom, s) + } + err := dbmap.CreateTables() + defer dbmap.Cleanup() + if err != nil { + t.Error(err) + } +} + // what happens if a legacy table has a null value? func TestNullValues(t *testing.T) { dbmap := initDbMapNulls() diff --git a/tablemap.go b/tablemap.go index d018387..98b32f3 100644 --- a/tablemap.go +++ b/tablemap.go @@ -306,9 +306,13 @@ type ColumnMap struct { // correct column type to map to in CreateTables() MaxSize int + // the table this column belongs to + table *TableMap + fieldName string gotype reflect.Type sqltype string + createSql string isPK bool isAutoIncr bool } @@ -327,6 +331,14 @@ func (c *ColumnMap) SetUnique(b bool) *ColumnMap { return c } +// SetSqlCreate overrides the default create statement used when this column +// is created by CreateTable. This will override all other options (like +// SetMaxSize, SetSqlType, etc). To unset, call with the empty string. +func (c *ColumnMap) SetSqlCreate(s string) *ColumnMap { + c.createSql = s + return c +} + // SetSqlType sets an override for the column's sql type. This is a string, // such as 'varchar(32)' or 'text', which will be used by CreateTable and // nothing else. It is the caller's responsibility to ensure this will map