orm add sqlite3 support, may be support postgres in next commit
Showing
12 changed files
with
692 additions
and
51 deletions
This diff is collapsed.
Click to expand it.
| 1 | package orm | 1 | package orm |
| 2 | 2 | ||
| 3 | var mysqlOperators = map[string]string{ | ||
| 4 | "exact": "= ?", | ||
| 5 | "iexact": "LIKE ?", | ||
| 6 | "contains": "LIKE BINARY ?", | ||
| 7 | "icontains": "LIKE ?", | ||
| 8 | // "regex": "REGEXP BINARY ?", | ||
| 9 | // "iregex": "REGEXP ?", | ||
| 10 | "gt": "> ?", | ||
| 11 | "gte": ">= ?", | ||
| 12 | "lt": "< ?", | ||
| 13 | "lte": "<= ?", | ||
| 14 | "startswith": "LIKE BINARY ?", | ||
| 15 | "endswith": "LIKE BINARY ?", | ||
| 16 | "istartswith": "LIKE ?", | ||
| 17 | "iendswith": "LIKE ?", | ||
| 18 | } | ||
| 19 | |||
| 3 | type dbBaseMysql struct { | 20 | type dbBaseMysql struct { |
| 4 | dbBase | 21 | dbBase |
| 5 | } | 22 | } |
| 6 | 23 | ||
| 7 | func (d *dbBaseMysql) GetOperatorSql(mi *modelInfo, operator string, args []interface{}) (sql string, params []interface{}) { | 24 | var _ dbBaser = new(dbBaseMysql) |
| 8 | return d.dbBase.GetOperatorSql(mi, operator, args) | 25 | |
| 26 | func (d *dbBaseMysql) OperatorSql(operator string) string { | ||
| 27 | return mysqlOperators[operator] | ||
| 9 | } | 28 | } |
| 10 | 29 | ||
| 11 | func newdbBaseMysql() dbBaser { | 30 | func newdbBaseMysql() dbBaser { | ... | ... |
| ... | @@ -4,6 +4,12 @@ type dbBaseOracle struct { | ... | @@ -4,6 +4,12 @@ type dbBaseOracle struct { |
| 4 | dbBase | 4 | dbBase |
| 5 | } | 5 | } |
| 6 | 6 | ||
| 7 | var _ dbBaser = new(dbBaseOracle) | ||
| 8 | |||
| 9 | func (d *dbBase) OperatorSql(operator string) string { | ||
| 10 | return "" | ||
| 11 | } | ||
| 12 | |||
| 7 | func newdbBaseOracle() dbBaser { | 13 | func newdbBaseOracle() dbBaser { |
| 8 | b := new(dbBaseOracle) | 14 | b := new(dbBaseOracle) |
| 9 | b.ins = b | 15 | b.ins = b | ... | ... |
| 1 | package orm | 1 | package orm |
| 2 | 2 | ||
| 3 | import ( | ||
| 4 | "strconv" | ||
| 5 | ) | ||
| 6 | |||
| 7 | var postgresOperators = map[string]string{ | ||
| 8 | "exact": "= ?", | ||
| 9 | "iexact": "= UPPER(?)", | ||
| 10 | "contains": "LIKE ?", | ||
| 11 | "icontains": "LIKE UPPER(?)", | ||
| 12 | "gt": "> ?", | ||
| 13 | "gte": ">= ?", | ||
| 14 | "lt": "< ?", | ||
| 15 | "lte": "<= ?", | ||
| 16 | "startswith": "LIKE ?", | ||
| 17 | "endswith": "LIKE ?", | ||
| 18 | "istartswith": "LIKE UPPER(?)", | ||
| 19 | "iendswith": "LIKE UPPER(?)", | ||
| 20 | } | ||
| 21 | |||
| 3 | type dbBasePostgres struct { | 22 | type dbBasePostgres struct { |
| 4 | dbBase | 23 | dbBase |
| 5 | } | 24 | } |
| 6 | 25 | ||
| 26 | var _ dbBaser = new(dbBasePostgres) | ||
| 27 | |||
| 28 | func (d *dbBasePostgres) OperatorSql(operator string) string { | ||
| 29 | return postgresOperators[operator] | ||
| 30 | } | ||
| 31 | |||
| 32 | func (d *dbBasePostgres) TableQuote() string { | ||
| 33 | return `"` | ||
| 34 | } | ||
| 35 | |||
| 36 | func (d *dbBasePostgres) ReplaceMarks(query *string) { | ||
| 37 | q := *query | ||
| 38 | num := 0 | ||
| 39 | for _, c := range q { | ||
| 40 | if c == '?' { | ||
| 41 | num += 1 | ||
| 42 | } | ||
| 43 | } | ||
| 44 | if num == 0 { | ||
| 45 | return | ||
| 46 | } | ||
| 47 | data := make([]byte, 0, len(q)+num) | ||
| 48 | num = 1 | ||
| 49 | for i := 0; i < len(q); i++ { | ||
| 50 | c := q[i] | ||
| 51 | if c == '?' { | ||
| 52 | data = append(data, '$') | ||
| 53 | data = append(data, []byte(strconv.Itoa(num))...) | ||
| 54 | num += 1 | ||
| 55 | } else { | ||
| 56 | data = append(data, c) | ||
| 57 | } | ||
| 58 | } | ||
| 59 | *query = string(data) | ||
| 60 | } | ||
| 61 | |||
| 62 | // func (d *dbBasePostgres) | ||
| 63 | |||
| 7 | func newdbBasePostgres() dbBaser { | 64 | func newdbBasePostgres() dbBaser { |
| 8 | b := new(dbBasePostgres) | 65 | b := new(dbBasePostgres) |
| 9 | b.ins = b | 66 | b.ins = b | ... | ... |
| 1 | package orm | 1 | package orm |
| 2 | 2 | ||
| 3 | var sqliteOperators = map[string]string{ | ||
| 4 | "exact": "= ?", | ||
| 5 | "iexact": "LIKE ? ESCAPE '\\'", | ||
| 6 | "contains": "LIKE ? ESCAPE '\\'", | ||
| 7 | "icontains": "LIKE ? ESCAPE '\\'", | ||
| 8 | "gt": "> ?", | ||
| 9 | "gte": ">= ?", | ||
| 10 | "lt": "< ?", | ||
| 11 | "lte": "<= ?", | ||
| 12 | "startswith": "LIKE ? ESCAPE '\\'", | ||
| 13 | "endswith": "LIKE ? ESCAPE '\\'", | ||
| 14 | "istartswith": "LIKE ? ESCAPE '\\'", | ||
| 15 | "iendswith": "LIKE ? ESCAPE '\\'", | ||
| 16 | } | ||
| 17 | |||
| 3 | type dbBaseSqlite struct { | 18 | type dbBaseSqlite struct { |
| 4 | dbBase | 19 | dbBase |
| 5 | } | 20 | } |
| 6 | 21 | ||
| 22 | var _ dbBaser = new(dbBaseSqlite) | ||
| 23 | |||
| 24 | func (d *dbBaseSqlite) OperatorSql(operator string) string { | ||
| 25 | return sqliteOperators[operator] | ||
| 26 | } | ||
| 27 | |||
| 28 | func (d *dbBaseSqlite) SupportUpdateJoin() bool { | ||
| 29 | return false | ||
| 30 | } | ||
| 31 | |||
| 32 | func (d *dbBaseSqlite) MaxLimit() uint64 { | ||
| 33 | return 9223372036854775807 | ||
| 34 | } | ||
| 35 | |||
| 7 | func newdbBaseSqlite() dbBaser { | 36 | func newdbBaseSqlite() dbBaser { |
| 8 | b := new(dbBaseSqlite) | 37 | b := new(dbBaseSqlite) |
| 9 | b.ins = b | 38 | b.ins = b | ... | ... |
orm/db_tables.go
0 → 100644
| 1 | package orm | ||
| 2 | |||
| 3 | import ( | ||
| 4 | "fmt" | ||
| 5 | "strings" | ||
| 6 | ) | ||
| 7 | |||
| 8 | type dbTable struct { | ||
| 9 | id int | ||
| 10 | index string | ||
| 11 | name string | ||
| 12 | names []string | ||
| 13 | sel bool | ||
| 14 | inner bool | ||
| 15 | mi *modelInfo | ||
| 16 | fi *fieldInfo | ||
| 17 | jtl *dbTable | ||
| 18 | } | ||
| 19 | |||
| 20 | type dbTables struct { | ||
| 21 | tablesM map[string]*dbTable | ||
| 22 | tables []*dbTable | ||
| 23 | mi *modelInfo | ||
| 24 | base dbBaser | ||
| 25 | } | ||
| 26 | |||
| 27 | func (t *dbTables) set(names []string, mi *modelInfo, fi *fieldInfo, inner bool) *dbTable { | ||
| 28 | name := strings.Join(names, ExprSep) | ||
| 29 | if j, ok := t.tablesM[name]; ok { | ||
| 30 | j.name = name | ||
| 31 | j.mi = mi | ||
| 32 | j.fi = fi | ||
| 33 | j.inner = inner | ||
| 34 | } else { | ||
| 35 | i := len(t.tables) + 1 | ||
| 36 | jt := &dbTable{i, fmt.Sprintf("T%d", i), name, names, false, inner, mi, fi, nil} | ||
| 37 | t.tablesM[name] = jt | ||
| 38 | t.tables = append(t.tables, jt) | ||
| 39 | } | ||
| 40 | return t.tablesM[name] | ||
| 41 | } | ||
| 42 | |||
| 43 | func (t *dbTables) add(names []string, mi *modelInfo, fi *fieldInfo, inner bool) (*dbTable, bool) { | ||
| 44 | name := strings.Join(names, ExprSep) | ||
| 45 | if _, ok := t.tablesM[name]; ok == false { | ||
| 46 | i := len(t.tables) + 1 | ||
| 47 | jt := &dbTable{i, fmt.Sprintf("T%d", i), name, names, false, inner, mi, fi, nil} | ||
| 48 | t.tablesM[name] = jt | ||
| 49 | t.tables = append(t.tables, jt) | ||
| 50 | return jt, true | ||
| 51 | } | ||
| 52 | return t.tablesM[name], false | ||
| 53 | } | ||
| 54 | |||
| 55 | func (t *dbTables) get(name string) (*dbTable, bool) { | ||
| 56 | j, ok := t.tablesM[name] | ||
| 57 | return j, ok | ||
| 58 | } | ||
| 59 | |||
| 60 | func (t *dbTables) loopDepth(depth int, prefix string, fi *fieldInfo, related []string) []string { | ||
| 61 | if depth < 0 || fi.fieldType == RelManyToMany { | ||
| 62 | return related | ||
| 63 | } | ||
| 64 | |||
| 65 | if prefix == "" { | ||
| 66 | prefix = fi.name | ||
| 67 | } else { | ||
| 68 | prefix = prefix + ExprSep + fi.name | ||
| 69 | } | ||
| 70 | related = append(related, prefix) | ||
| 71 | |||
| 72 | depth-- | ||
| 73 | for _, fi := range fi.relModelInfo.fields.fieldsRel { | ||
| 74 | related = t.loopDepth(depth, prefix, fi, related) | ||
| 75 | } | ||
| 76 | |||
| 77 | return related | ||
| 78 | } | ||
| 79 | |||
| 80 | func (t *dbTables) parseRelated(rels []string, depth int) { | ||
| 81 | |||
| 82 | relsNum := len(rels) | ||
| 83 | related := make([]string, relsNum) | ||
| 84 | copy(related, rels) | ||
| 85 | |||
| 86 | relDepth := depth | ||
| 87 | |||
| 88 | if relsNum != 0 { | ||
| 89 | relDepth = 0 | ||
| 90 | } | ||
| 91 | |||
| 92 | relDepth-- | ||
| 93 | for _, fi := range t.mi.fields.fieldsRel { | ||
| 94 | related = t.loopDepth(relDepth, "", fi, related) | ||
| 95 | } | ||
| 96 | |||
| 97 | for i, s := range related { | ||
| 98 | var ( | ||
| 99 | exs = strings.Split(s, ExprSep) | ||
| 100 | names = make([]string, 0, len(exs)) | ||
| 101 | mmi = t.mi | ||
| 102 | cansel = true | ||
| 103 | jtl *dbTable | ||
| 104 | ) | ||
| 105 | for _, ex := range exs { | ||
| 106 | if fi, ok := mmi.fields.GetByAny(ex); ok && fi.rel && fi.fieldType != RelManyToMany { | ||
| 107 | names = append(names, fi.name) | ||
| 108 | mmi = fi.relModelInfo | ||
| 109 | |||
| 110 | jt := t.set(names, mmi, fi, fi.null == false) | ||
| 111 | jt.jtl = jtl | ||
| 112 | |||
| 113 | if fi.reverse { | ||
| 114 | cansel = false | ||
| 115 | } | ||
| 116 | |||
| 117 | if cansel { | ||
| 118 | jt.sel = depth > 0 | ||
| 119 | |||
| 120 | if i < relsNum { | ||
| 121 | jt.sel = true | ||
| 122 | } | ||
| 123 | } | ||
| 124 | |||
| 125 | jtl = jt | ||
| 126 | |||
| 127 | } else { | ||
| 128 | panic(fmt.Sprintf("unknown model/table name `%s`", ex)) | ||
| 129 | } | ||
| 130 | } | ||
| 131 | } | ||
| 132 | } | ||
| 133 | |||
| 134 | func (t *dbTables) getJoinSql() (join string) { | ||
| 135 | Q := t.base.TableQuote() | ||
| 136 | |||
| 137 | for _, jt := range t.tables { | ||
| 138 | if jt.inner { | ||
| 139 | join += "INNER JOIN " | ||
| 140 | } else { | ||
| 141 | join += "LEFT OUTER JOIN " | ||
| 142 | } | ||
| 143 | var ( | ||
| 144 | table string | ||
| 145 | t1, t2 string | ||
| 146 | c1, c2 string | ||
| 147 | ) | ||
| 148 | t1 = "T0" | ||
| 149 | if jt.jtl != nil { | ||
| 150 | t1 = jt.jtl.index | ||
| 151 | } | ||
| 152 | t2 = jt.index | ||
| 153 | table = jt.mi.table | ||
| 154 | |||
| 155 | switch { | ||
| 156 | case jt.fi.fieldType == RelManyToMany || jt.fi.reverse && jt.fi.reverseFieldInfo.fieldType == RelManyToMany: | ||
| 157 | c1 = jt.fi.mi.fields.pk.column | ||
| 158 | for _, ffi := range jt.mi.fields.fieldsRel { | ||
| 159 | if jt.fi.mi == ffi.relModelInfo { | ||
| 160 | c2 = ffi.column | ||
| 161 | break | ||
| 162 | } | ||
| 163 | } | ||
| 164 | default: | ||
| 165 | c1 = jt.fi.column | ||
| 166 | c2 = jt.fi.relModelInfo.fields.pk.column | ||
| 167 | |||
| 168 | if jt.fi.reverse { | ||
| 169 | c1 = jt.mi.fields.pk.column | ||
| 170 | c2 = jt.fi.reverseFieldInfo.column | ||
| 171 | } | ||
| 172 | } | ||
| 173 | |||
| 174 | join += fmt.Sprintf("%s%s%s %s ON %s.%s%s%s = %s.%s%s%s ", Q, table, Q, t2, | ||
| 175 | t2, Q, c2, Q, t1, Q, c1, Q) | ||
| 176 | } | ||
| 177 | return | ||
| 178 | } | ||
| 179 | |||
| 180 | func (d *dbTables) parseExprs(mi *modelInfo, exprs []string) (index, column, name string, info *fieldInfo, success bool) { | ||
| 181 | var ( | ||
| 182 | ffi *fieldInfo | ||
| 183 | jtl *dbTable | ||
| 184 | mmi = mi | ||
| 185 | ) | ||
| 186 | |||
| 187 | num := len(exprs) - 1 | ||
| 188 | names := make([]string, 0) | ||
| 189 | |||
| 190 | for i, ex := range exprs { | ||
| 191 | exist := false | ||
| 192 | |||
| 193 | check: | ||
| 194 | fi, ok := mmi.fields.GetByAny(ex) | ||
| 195 | |||
| 196 | if ok { | ||
| 197 | |||
| 198 | if num != i { | ||
| 199 | names = append(names, fi.name) | ||
| 200 | |||
| 201 | switch { | ||
| 202 | case fi.rel: | ||
| 203 | mmi = fi.relModelInfo | ||
| 204 | if fi.fieldType == RelManyToMany { | ||
| 205 | mmi = fi.relThroughModelInfo | ||
| 206 | } | ||
| 207 | case fi.reverse: | ||
| 208 | mmi = fi.reverseFieldInfo.mi | ||
| 209 | if fi.reverseFieldInfo.fieldType == RelManyToMany { | ||
| 210 | mmi = fi.reverseFieldInfo.relThroughModelInfo | ||
| 211 | } | ||
| 212 | default: | ||
| 213 | return | ||
| 214 | } | ||
| 215 | |||
| 216 | jt, _ := d.add(names, mmi, fi, fi.null == false) | ||
| 217 | jt.jtl = jtl | ||
| 218 | jtl = jt | ||
| 219 | |||
| 220 | if fi.rel && fi.fieldType == RelManyToMany { | ||
| 221 | ex = fi.relModelInfo.name | ||
| 222 | goto check | ||
| 223 | } | ||
| 224 | |||
| 225 | if fi.reverse && fi.reverseFieldInfo.fieldType == RelManyToMany { | ||
| 226 | ex = fi.reverseFieldInfo.mi.name | ||
| 227 | goto check | ||
| 228 | } | ||
| 229 | |||
| 230 | exist = true | ||
| 231 | |||
| 232 | } else { | ||
| 233 | |||
| 234 | if ffi == nil { | ||
| 235 | index = "T0" | ||
| 236 | } else { | ||
| 237 | index = jtl.index | ||
| 238 | } | ||
| 239 | column = fi.column | ||
| 240 | info = fi | ||
| 241 | if jtl != nil { | ||
| 242 | name = jtl.name + ExprSep + fi.name | ||
| 243 | } else { | ||
| 244 | name = fi.name | ||
| 245 | } | ||
| 246 | |||
| 247 | switch fi.fieldType { | ||
| 248 | case RelManyToMany, RelReverseMany: | ||
| 249 | default: | ||
| 250 | exist = true | ||
| 251 | } | ||
| 252 | } | ||
| 253 | |||
| 254 | ffi = fi | ||
| 255 | } | ||
| 256 | |||
| 257 | if exist == false { | ||
| 258 | index = "" | ||
| 259 | column = "" | ||
| 260 | name = "" | ||
| 261 | success = false | ||
| 262 | return | ||
| 263 | } | ||
| 264 | } | ||
| 265 | |||
| 266 | success = index != "" && column != "" | ||
| 267 | return | ||
| 268 | } | ||
| 269 | |||
| 270 | func (d *dbTables) getCondSql(cond *Condition, sub bool) (where string, params []interface{}) { | ||
| 271 | if cond == nil || cond.IsEmpty() { | ||
| 272 | return | ||
| 273 | } | ||
| 274 | |||
| 275 | Q := d.base.TableQuote() | ||
| 276 | |||
| 277 | mi := d.mi | ||
| 278 | |||
| 279 | // outFor: | ||
| 280 | for i, p := range cond.params { | ||
| 281 | if i > 0 { | ||
| 282 | if p.isOr { | ||
| 283 | where += "OR " | ||
| 284 | } else { | ||
| 285 | where += "AND " | ||
| 286 | } | ||
| 287 | } | ||
| 288 | if p.isNot { | ||
| 289 | where += "NOT " | ||
| 290 | } | ||
| 291 | if p.isCond { | ||
| 292 | w, ps := d.getCondSql(p.cond, true) | ||
| 293 | if w != "" { | ||
| 294 | w = fmt.Sprintf("( %s) ", w) | ||
| 295 | } | ||
| 296 | where += w | ||
| 297 | params = append(params, ps...) | ||
| 298 | } else { | ||
| 299 | exprs := p.exprs | ||
| 300 | |||
| 301 | num := len(exprs) - 1 | ||
| 302 | operator := "" | ||
| 303 | if operators[exprs[num]] { | ||
| 304 | operator = exprs[num] | ||
| 305 | exprs = exprs[:num] | ||
| 306 | } | ||
| 307 | |||
| 308 | index, column, _, _, suc := d.parseExprs(mi, exprs) | ||
| 309 | if suc == false { | ||
| 310 | panic(fmt.Errorf("unknown field/column name `%s`", strings.Join(p.exprs, ExprSep))) | ||
| 311 | } | ||
| 312 | |||
| 313 | if operator == "" { | ||
| 314 | operator = "exact" | ||
| 315 | } | ||
| 316 | |||
| 317 | operSql, args := d.base.GenerateOperatorSql(mi, operator, p.args) | ||
| 318 | |||
| 319 | where += fmt.Sprintf("%s.%s%s%s %s ", index, Q, column, Q, operSql) | ||
| 320 | params = append(params, args...) | ||
| 321 | |||
| 322 | } | ||
| 323 | } | ||
| 324 | |||
| 325 | if sub == false && where != "" { | ||
| 326 | where = "WHERE " + where | ||
| 327 | } | ||
| 328 | |||
| 329 | return | ||
| 330 | } | ||
| 331 | |||
| 332 | func (d *dbTables) getOrderSql(orders []string) (orderSql string) { | ||
| 333 | if len(orders) == 0 { | ||
| 334 | return | ||
| 335 | } | ||
| 336 | |||
| 337 | Q := d.base.TableQuote() | ||
| 338 | |||
| 339 | orderSqls := make([]string, 0, len(orders)) | ||
| 340 | for _, order := range orders { | ||
| 341 | asc := "ASC" | ||
| 342 | if order[0] == '-' { | ||
| 343 | asc = "DESC" | ||
| 344 | order = order[1:] | ||
| 345 | } | ||
| 346 | exprs := strings.Split(order, ExprSep) | ||
| 347 | |||
| 348 | index, column, _, _, suc := d.parseExprs(d.mi, exprs) | ||
| 349 | if suc == false { | ||
| 350 | panic(fmt.Errorf("unknown field/column name `%s`", strings.Join(exprs, ExprSep))) | ||
| 351 | } | ||
| 352 | |||
| 353 | orderSqls = append(orderSqls, fmt.Sprintf("%s.%s%s%s %s", index, Q, column, Q, asc)) | ||
| 354 | } | ||
| 355 | |||
| 356 | orderSql = fmt.Sprintf("ORDER BY %s ", strings.Join(orderSqls, ", ")) | ||
| 357 | return | ||
| 358 | } | ||
| 359 | |||
| 360 | func (d *dbTables) getLimitSql(mi *modelInfo, offset int64, limit int) (limits string) { | ||
| 361 | if limit == 0 { | ||
| 362 | limit = DefaultRowsLimit | ||
| 363 | } | ||
| 364 | if limit < 0 { | ||
| 365 | // no limit | ||
| 366 | if offset > 0 { | ||
| 367 | maxLimit := d.base.MaxLimit() | ||
| 368 | limits = fmt.Sprintf("LIMIT %d OFFSET %d", maxLimit, offset) | ||
| 369 | } | ||
| 370 | } else if offset <= 0 { | ||
| 371 | limits = fmt.Sprintf("LIMIT %d", limit) | ||
| 372 | } else { | ||
| 373 | limits = fmt.Sprintf("LIMIT %d OFFSET %d", limit, offset) | ||
| 374 | } | ||
| 375 | return | ||
| 376 | } | ||
| 377 | |||
| 378 | func newDbTables(mi *modelInfo, base dbBaser) *dbTables { | ||
| 379 | tables := &dbTables{} | ||
| 380 | tables.tablesM = make(map[string]*dbTable) | ||
| 381 | tables.mi = mi | ||
| 382 | tables.base = base | ||
| 383 | return tables | ||
| 384 | } |
| ... | @@ -79,7 +79,7 @@ func newModelInfo(val reflect.Value) (info *modelInfo) { | ... | @@ -79,7 +79,7 @@ func newModelInfo(val reflect.Value) (info *modelInfo) { |
| 79 | func newM2MModelInfo(m1, m2 *modelInfo) (info *modelInfo) { | 79 | func newM2MModelInfo(m1, m2 *modelInfo) (info *modelInfo) { |
| 80 | info = new(modelInfo) | 80 | info = new(modelInfo) |
| 81 | info.fields = newFields() | 81 | info.fields = newFields() |
| 82 | info.table = m1.table + "_" + m2.table + "_rel" | 82 | info.table = m1.table + "_" + m2.table + "s" |
| 83 | info.name = camelString(info.table) | 83 | info.name = camelString(info.table) |
| 84 | info.fullName = m1.pkg + "." + info.name | 84 | info.fullName = m1.pkg + "." + info.name |
| 85 | 85 | ... | ... |
| ... | @@ -3,10 +3,11 @@ package orm | ... | @@ -3,10 +3,11 @@ package orm |
| 3 | import ( | 3 | import ( |
| 4 | "fmt" | 4 | "fmt" |
| 5 | "os" | 5 | "os" |
| 6 | "strings" | ||
| 6 | "time" | 7 | "time" |
| 7 | 8 | ||
| 8 | _ "github.com/bmizerany/pq" | ||
| 9 | _ "github.com/go-sql-driver/mysql" | 9 | _ "github.com/go-sql-driver/mysql" |
| 10 | _ "github.com/lib/pq" | ||
| 10 | _ "github.com/mattn/go-sqlite3" | 11 | _ "github.com/mattn/go-sqlite3" |
| 11 | ) | 12 | ) |
| 12 | 13 | ||
| ... | @@ -95,8 +96,178 @@ var DBARGS = struct { | ... | @@ -95,8 +96,178 @@ var DBARGS = struct { |
| 95 | os.Getenv("ORM_DEBUG"), | 96 | os.Getenv("ORM_DEBUG"), |
| 96 | } | 97 | } |
| 97 | 98 | ||
| 99 | var ( | ||
| 100 | IsMysql = DBARGS.Driver == "mysql" | ||
| 101 | IsSqlite = DBARGS.Driver == "sqlite3" | ||
| 102 | IsPostgres = DBARGS.Driver == "postgres" | ||
| 103 | ) | ||
| 104 | |||
| 98 | var dORM Ormer | 105 | var dORM Ormer |
| 99 | 106 | ||
| 107 | var initSQLs = map[string]string{ | ||
| 108 | "mysql": "DROP TABLE IF EXISTS `user_profile`;\n" + | ||
| 109 | "DROP TABLE IF EXISTS `user`;\n" + | ||
| 110 | "DROP TABLE IF EXISTS `post`;\n" + | ||
| 111 | "DROP TABLE IF EXISTS `tag`;\n" + | ||
| 112 | "DROP TABLE IF EXISTS `post_tags`;\n" + | ||
| 113 | "DROP TABLE IF EXISTS `comment`;\n" + | ||
| 114 | "CREATE TABLE `user_profile` (\n" + | ||
| 115 | " `id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,\n" + | ||
| 116 | " `age` smallint NOT NULL,\n" + | ||
| 117 | " `money` double precision NOT NULL\n" + | ||
| 118 | ") ENGINE=INNODB;\n" + | ||
| 119 | "CREATE TABLE `user` (\n" + | ||
| 120 | " `id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,\n" + | ||
| 121 | " `user_name` varchar(30) NOT NULL UNIQUE,\n" + | ||
| 122 | " `email` varchar(100) NOT NULL,\n" + | ||
| 123 | " `password` varchar(100) NOT NULL,\n" + | ||
| 124 | " `status` smallint NOT NULL,\n" + | ||
| 125 | " `is_staff` bool NOT NULL,\n" + | ||
| 126 | " `is_active` bool NOT NULL,\n" + | ||
| 127 | " `created` date NOT NULL,\n" + | ||
| 128 | " `updated` datetime NOT NULL,\n" + | ||
| 129 | " `profile_id` integer\n" + | ||
| 130 | ") ENGINE=INNODB;\n" + | ||
| 131 | "CREATE TABLE `post` (\n" + | ||
| 132 | " `id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,\n" + | ||
| 133 | " `user_id` integer NOT NULL,\n" + | ||
| 134 | " `title` varchar(60) NOT NULL,\n" + | ||
| 135 | " `content` longtext NOT NULL,\n" + | ||
| 136 | " `created` datetime NOT NULL,\n" + | ||
| 137 | " `updated` datetime NOT NULL\n" + | ||
| 138 | ") ENGINE=INNODB;\n" + | ||
| 139 | "CREATE TABLE `tag` (\n" + | ||
| 140 | " `id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,\n" + | ||
| 141 | " `name` varchar(30) NOT NULL\n" + | ||
| 142 | ") ENGINE=INNODB;\n" + | ||
| 143 | "CREATE TABLE `post_tags` (\n" + | ||
| 144 | " `id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,\n" + | ||
| 145 | " `post_id` integer NOT NULL,\n" + | ||
| 146 | " `tag_id` integer NOT NULL,\n" + | ||
| 147 | " UNIQUE (`post_id`, `tag_id`)\n" + | ||
| 148 | ") ENGINE=INNODB;\n" + | ||
| 149 | "CREATE TABLE `comment` (\n" + | ||
| 150 | " `id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,\n" + | ||
| 151 | " `post_id` integer NOT NULL,\n" + | ||
| 152 | " `content` longtext NOT NULL,\n" + | ||
| 153 | " `parent_id` integer,\n" + | ||
| 154 | " `created` datetime NOT NULL\n" + | ||
| 155 | ") ENGINE=INNODB;\n" + | ||
| 156 | "CREATE INDEX `user_141c6eec` ON `user` (`profile_id`);\n" + | ||
| 157 | "CREATE INDEX `post_fbfc09f1` ON `post` (`user_id`);\n" + | ||
| 158 | "CREATE INDEX `comment_699ae8ca` ON `comment` (`post_id`);\n" + | ||
| 159 | "CREATE INDEX `comment_63f17a16` ON `comment` (`parent_id`);", | ||
| 160 | |||
| 161 | "sqlite3": ` | ||
| 162 | DROP TABLE IF EXISTS "user_profile"; | ||
| 163 | DROP TABLE IF EXISTS "user"; | ||
| 164 | DROP TABLE IF EXISTS "post"; | ||
| 165 | DROP TABLE IF EXISTS "tag"; | ||
| 166 | DROP TABLE IF EXISTS "post_tags"; | ||
| 167 | DROP TABLE IF EXISTS "comment"; | ||
| 168 | CREATE TABLE "user_profile" ( | ||
| 169 | "id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, | ||
| 170 | "age" smallint NOT NULL, | ||
| 171 | "money" real NOT NULL | ||
| 172 | ); | ||
| 173 | CREATE TABLE "user" ( | ||
| 174 | "id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, | ||
| 175 | "user_name" varchar(30) NOT NULL UNIQUE, | ||
| 176 | "email" varchar(100) NOT NULL, | ||
| 177 | "password" varchar(100) NOT NULL, | ||
| 178 | "status" smallint NOT NULL, | ||
| 179 | "is_staff" bool NOT NULL, | ||
| 180 | "is_active" bool NOT NULL, | ||
| 181 | "created" date NOT NULL, | ||
| 182 | "updated" datetime NOT NULL, | ||
| 183 | "profile_id" integer | ||
| 184 | ); | ||
| 185 | CREATE TABLE "post" ( | ||
| 186 | "id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, | ||
| 187 | "user_id" integer NOT NULL, | ||
| 188 | "title" varchar(60) NOT NULL, | ||
| 189 | "content" text NOT NULL, | ||
| 190 | "created" datetime NOT NULL, | ||
| 191 | "updated" datetime NOT NULL | ||
| 192 | ); | ||
| 193 | CREATE TABLE "tag" ( | ||
| 194 | "id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, | ||
| 195 | "name" varchar(30) NOT NULL | ||
| 196 | ); | ||
| 197 | CREATE TABLE "post_tags" ( | ||
| 198 | "id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, | ||
| 199 | "post_id" integer NOT NULL, | ||
| 200 | "tag_id" integer NOT NULL, | ||
| 201 | UNIQUE ("post_id", "tag_id") | ||
| 202 | ); | ||
| 203 | CREATE TABLE "comment" ( | ||
| 204 | "id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, | ||
| 205 | "post_id" integer NOT NULL, | ||
| 206 | "content" text NOT NULL, | ||
| 207 | "parent_id" integer, | ||
| 208 | "created" datetime NOT NULL | ||
| 209 | ); | ||
| 210 | CREATE INDEX "user_141c6eec" ON "user" ("profile_id"); | ||
| 211 | CREATE INDEX "post_fbfc09f1" ON "post" ("user_id"); | ||
| 212 | CREATE INDEX "comment_699ae8ca" ON "comment" ("post_id"); | ||
| 213 | CREATE INDEX "comment_63f17a16" ON "comment" ("parent_id"); | ||
| 214 | `, | ||
| 215 | |||
| 216 | "postgres": ` | ||
| 217 | DROP TABLE IF EXISTS "user_profile"; | ||
| 218 | DROP TABLE IF EXISTS "user"; | ||
| 219 | DROP TABLE IF EXISTS "post"; | ||
| 220 | DROP TABLE IF EXISTS "tag"; | ||
| 221 | DROP TABLE IF EXISTS "post_tags"; | ||
| 222 | DROP TABLE IF EXISTS "comment"; | ||
| 223 | CREATE TABLE "user_profile" ( | ||
| 224 | "id" serial NOT NULL PRIMARY KEY, | ||
| 225 | "age" smallint NOT NULL, | ||
| 226 | "money" double precision NOT NULL | ||
| 227 | ); | ||
| 228 | CREATE TABLE "user" ( | ||
| 229 | "id" serial NOT NULL PRIMARY KEY, | ||
| 230 | "user_name" varchar(30) NOT NULL UNIQUE, | ||
| 231 | "email" varchar(100) NOT NULL, | ||
| 232 | "password" varchar(100) NOT NULL, | ||
| 233 | "status" smallint NOT NULL, | ||
| 234 | "is_staff" boolean NOT NULL, | ||
| 235 | "is_active" boolean NOT NULL, | ||
| 236 | "created" date NOT NULL, | ||
| 237 | "updated" timestamp with time zone NOT NULL, | ||
| 238 | "profile_id" integer | ||
| 239 | ); | ||
| 240 | CREATE TABLE "post" ( | ||
| 241 | "id" serial NOT NULL PRIMARY KEY, | ||
| 242 | "user_id" integer NOT NULL, | ||
| 243 | "title" varchar(60) NOT NULL, | ||
| 244 | "content" text NOT NULL, | ||
| 245 | "created" timestamp with time zone NOT NULL, | ||
| 246 | "updated" timestamp with time zone NOT NULL | ||
| 247 | ); | ||
| 248 | CREATE TABLE "tag" ( | ||
| 249 | "id" serial NOT NULL PRIMARY KEY, | ||
| 250 | "name" varchar(30) NOT NULL | ||
| 251 | ); | ||
| 252 | CREATE TABLE "post_tags" ( | ||
| 253 | "id" serial NOT NULL PRIMARY KEY, | ||
| 254 | "post_id" integer NOT NULL, | ||
| 255 | "tag_id" integer NOT NULL, | ||
| 256 | UNIQUE ("post_id", "tag_id") | ||
| 257 | ); | ||
| 258 | CREATE TABLE "comment" ( | ||
| 259 | "id" serial NOT NULL PRIMARY KEY, | ||
| 260 | "post_id" integer NOT NULL, | ||
| 261 | "content" text NOT NULL, | ||
| 262 | "parent_id" integer, | ||
| 263 | "created" timestamp with time zone NOT NULL | ||
| 264 | ); | ||
| 265 | CREATE INDEX "user_profile_id" ON "user" ("profile_id"); | ||
| 266 | CREATE INDEX "post_user_id" ON "post" ("user_id"); | ||
| 267 | CREATE INDEX "comment_post_id" ON "comment" ("post_id"); | ||
| 268 | CREATE INDEX "comment_parent_id" ON "comment" ("parent_id"); | ||
| 269 | `} | ||
| 270 | |||
| 100 | func init() { | 271 | func init() { |
| 101 | RegisterModel(new(User)) | 272 | RegisterModel(new(User)) |
| 102 | RegisterModel(new(Profile)) | 273 | RegisterModel(new(Profile)) |
| ... | @@ -114,7 +285,7 @@ Default DB Drivers. | ... | @@ -114,7 +285,7 @@ Default DB Drivers. |
| 114 | driver: url | 285 | driver: url |
| 115 | mysql: https://github.com/go-sql-driver/mysql | 286 | mysql: https://github.com/go-sql-driver/mysql |
| 116 | sqlite3: https://github.com/mattn/go-sqlite3 | 287 | sqlite3: https://github.com/mattn/go-sqlite3 |
| 117 | postgres: https://github.com/bmizerany/pq | 288 | postgres: https://github.com/lib/pq |
| 118 | 289 | ||
| 119 | eg: mysql | 290 | eg: mysql |
| 120 | ORM_DRIVER=mysql ORM_SOURCE="root:root@/my_db?charset=utf8" go test github.com/astaxie/beego/orm | 291 | ORM_DRIVER=mysql ORM_SOURCE="root:root@/my_db?charset=utf8" go test github.com/astaxie/beego/orm |
| ... | @@ -126,20 +297,16 @@ ORM_DRIVER=mysql ORM_SOURCE="root:root@/my_db?charset=utf8" go test github.com/a | ... | @@ -126,20 +297,16 @@ ORM_DRIVER=mysql ORM_SOURCE="root:root@/my_db?charset=utf8" go test github.com/a |
| 126 | 297 | ||
| 127 | BootStrap() | 298 | BootStrap() |
| 128 | 299 | ||
| 129 | truncateTables() | ||
| 130 | |||
| 131 | dORM = NewOrm() | 300 | dORM = NewOrm() |
| 132 | } | ||
| 133 | 301 | ||
| 134 | func truncateTables() { | 302 | queries := strings.Split(initSQLs[DBARGS.Driver], ";") |
| 135 | logs := "truncate tables for test\n" | 303 | |
| 136 | o := NewOrm() | 304 | for _, query := range queries { |
| 137 | for _, m := range modelCache.allOrdered() { | 305 | if strings.TrimSpace(query) == "" { |
| 138 | query := fmt.Sprintf("truncate table `%s`", m.table) | 306 | continue |
| 139 | _, err := o.Raw(query).Exec() | 307 | } |
| 140 | logs += query + "\n" | 308 | _, err := dORM.Raw(query).Exec() |
| 141 | if err != nil { | 309 | if err != nil { |
| 142 | fmt.Println(logs) | ||
| 143 | fmt.Println(err) | 310 | fmt.Println(err) |
| 144 | os.Exit(2) | 311 | os.Exit(2) |
| 145 | } | 312 | } | ... | ... |
| ... | @@ -135,7 +135,7 @@ func (d *dbQueryLog) Commit() error { | ... | @@ -135,7 +135,7 @@ func (d *dbQueryLog) Commit() error { |
| 135 | 135 | ||
| 136 | func (d *dbQueryLog) Rollback() error { | 136 | func (d *dbQueryLog) Rollback() error { |
| 137 | a := time.Now() | 137 | a := time.Now() |
| 138 | err := d.db.(txEnder).Commit() | 138 | err := d.db.(txEnder).Rollback() |
| 139 | debugLogQueies(d.alias, "tx.Rollback", "ROLLBACK", a, err) | 139 | debugLogQueies(d.alias, "tx.Rollback", "ROLLBACK", a, err) |
| 140 | return err | 140 | return err |
| 141 | } | 141 | } | ... | ... |
| ... | @@ -6,39 +6,17 @@ import ( | ... | @@ -6,39 +6,17 @@ import ( |
| 6 | "reflect" | 6 | "reflect" |
| 7 | ) | 7 | ) |
| 8 | 8 | ||
| 9 | func getResult(res sql.Result) (int64, error) { | ||
| 10 | if num, err := res.LastInsertId(); err != nil { | ||
| 11 | return 0, err | ||
| 12 | } else { | ||
| 13 | if num > 0 { | ||
| 14 | return num, nil | ||
| 15 | } | ||
| 16 | } | ||
| 17 | if num, err := res.RowsAffected(); err != nil { | ||
| 18 | return num, err | ||
| 19 | } else { | ||
| 20 | if num > 0 { | ||
| 21 | return num, nil | ||
| 22 | } | ||
| 23 | } | ||
| 24 | return 0, nil | ||
| 25 | } | ||
| 26 | |||
| 27 | type rawPrepare struct { | 9 | type rawPrepare struct { |
| 28 | rs *rawSet | 10 | rs *rawSet |
| 29 | stmt stmtQuerier | 11 | stmt stmtQuerier |
| 30 | closed bool | 12 | closed bool |
| 31 | } | 13 | } |
| 32 | 14 | ||
| 33 | func (o *rawPrepare) Exec(args ...interface{}) (int64, error) { | 15 | func (o *rawPrepare) Exec(args ...interface{}) (sql.Result, error) { |
| 34 | if o.closed { | 16 | if o.closed { |
| 35 | return 0, ErrStmtClosed | 17 | return nil, ErrStmtClosed |
| 36 | } | ||
| 37 | res, err := o.stmt.Exec(args...) | ||
| 38 | if err != nil { | ||
| 39 | return 0, err | ||
| 40 | } | 18 | } |
| 41 | return getResult(res) | 19 | return o.stmt.Exec(args...) |
| 42 | } | 20 | } |
| 43 | 21 | ||
| 44 | func (o *rawPrepare) Close() error { | 22 | func (o *rawPrepare) Close() error { |
| ... | @@ -74,12 +52,8 @@ func (o rawSet) SetArgs(args ...interface{}) RawSeter { | ... | @@ -74,12 +52,8 @@ func (o rawSet) SetArgs(args ...interface{}) RawSeter { |
| 74 | return &o | 52 | return &o |
| 75 | } | 53 | } |
| 76 | 54 | ||
| 77 | func (o *rawSet) Exec() (int64, error) { | 55 | func (o *rawSet) Exec() (sql.Result, error) { |
| 78 | res, err := o.orm.db.Exec(o.query, o.args...) | 56 | return o.orm.db.Exec(o.query, o.args...) |
| 79 | if err != nil { | ||
| 80 | return 0, err | ||
| 81 | } | ||
| 82 | return getResult(res) | ||
| 83 | } | 57 | } |
| 84 | 58 | ||
| 85 | func (o *rawSet) QueryRow(...interface{}) error { | 59 | func (o *rawSet) QueryRow(...interface{}) error { | ... | ... |
This diff is collapsed.
Click to expand it.
| ... | @@ -60,12 +60,12 @@ type QuerySeter interface { | ... | @@ -60,12 +60,12 @@ type QuerySeter interface { |
| 60 | } | 60 | } |
| 61 | 61 | ||
| 62 | type RawPreparer interface { | 62 | type RawPreparer interface { |
| 63 | Exec(...interface{}) (int64, error) | 63 | Exec(...interface{}) (sql.Result, error) |
| 64 | Close() error | 64 | Close() error |
| 65 | } | 65 | } |
| 66 | 66 | ||
| 67 | type RawSeter interface { | 67 | type RawSeter interface { |
| 68 | Exec() (int64, error) | 68 | Exec() (sql.Result, error) |
| 69 | QueryRow(...interface{}) error | 69 | QueryRow(...interface{}) error |
| 70 | QueryRows(...interface{}) (int64, error) | 70 | QueryRows(...interface{}) (int64, error) |
| 71 | SetArgs(...interface{}) RawSeter | 71 | SetArgs(...interface{}) RawSeter |
| ... | @@ -116,10 +116,15 @@ type dbBaser interface { | ... | @@ -116,10 +116,15 @@ type dbBaser interface { |
| 116 | Update(dbQuerier, *modelInfo, reflect.Value) (int64, error) | 116 | Update(dbQuerier, *modelInfo, reflect.Value) (int64, error) |
| 117 | Delete(dbQuerier, *modelInfo, reflect.Value) (int64, error) | 117 | Delete(dbQuerier, *modelInfo, reflect.Value) (int64, error) |
| 118 | ReadBatch(dbQuerier, *querySet, *modelInfo, *Condition, interface{}) (int64, error) | 118 | ReadBatch(dbQuerier, *querySet, *modelInfo, *Condition, interface{}) (int64, error) |
| 119 | SupportUpdateJoin() bool | ||
| 119 | UpdateBatch(dbQuerier, *querySet, *modelInfo, *Condition, Params) (int64, error) | 120 | UpdateBatch(dbQuerier, *querySet, *modelInfo, *Condition, Params) (int64, error) |
| 120 | DeleteBatch(dbQuerier, *querySet, *modelInfo, *Condition) (int64, error) | 121 | DeleteBatch(dbQuerier, *querySet, *modelInfo, *Condition) (int64, error) |
| 121 | Count(dbQuerier, *querySet, *modelInfo, *Condition) (int64, error) | 122 | Count(dbQuerier, *querySet, *modelInfo, *Condition) (int64, error) |
| 122 | GetOperatorSql(*modelInfo, string, []interface{}) (string, []interface{}) | 123 | OperatorSql(string) string |
| 124 | GenerateOperatorSql(*modelInfo, string, []interface{}) (string, []interface{}) | ||
| 123 | PrepareInsert(dbQuerier, *modelInfo) (stmtQuerier, string, error) | 125 | PrepareInsert(dbQuerier, *modelInfo) (stmtQuerier, string, error) |
| 124 | ReadValues(dbQuerier, *querySet, *modelInfo, *Condition, []string, interface{}) (int64, error) | 126 | ReadValues(dbQuerier, *querySet, *modelInfo, *Condition, []string, interface{}) (int64, error) |
| 127 | MaxLimit() uint64 | ||
| 128 | TableQuote() string | ||
| 129 | ReplaceMarks(*string) | ||
| 125 | } | 130 | } | ... | ... |
-
Please register or sign in to post a comment