session_insert.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635
  1. // Copyright 2016 The Xorm Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package xorm
  5. import (
  6. "errors"
  7. "fmt"
  8. "reflect"
  9. "strconv"
  10. "strings"
  11. "github.com/xormplus/core"
  12. )
  13. // Insert insert one or more beans
  14. func (session *Session) Insert(beans ...interface{}) (int64, error) {
  15. var affected int64
  16. var err error
  17. if session.isAutoClose {
  18. defer session.Close()
  19. }
  20. for _, bean := range beans {
  21. sliceValue := reflect.Indirect(reflect.ValueOf(bean))
  22. if sliceValue.Kind() == reflect.Slice {
  23. size := sliceValue.Len()
  24. if size > 0 {
  25. if session.engine.SupportInsertMany() {
  26. cnt, err := session.innerInsertMulti(bean)
  27. if err != nil {
  28. return affected, err
  29. }
  30. affected += cnt
  31. } else {
  32. for i := 0; i < size; i++ {
  33. cnt, err := session.innerInsert(sliceValue.Index(i).Interface())
  34. if err != nil {
  35. return affected, err
  36. }
  37. affected += cnt
  38. }
  39. }
  40. }
  41. } else {
  42. cnt, err := session.innerInsert(bean)
  43. if err != nil {
  44. return affected, err
  45. }
  46. affected += cnt
  47. }
  48. }
  49. return affected, err
  50. }
  51. func (session *Session) innerInsertMulti(rowsSlicePtr interface{}) (int64, error) {
  52. sliceValue := reflect.Indirect(reflect.ValueOf(rowsSlicePtr))
  53. if sliceValue.Kind() != reflect.Slice {
  54. return 0, errors.New("needs a pointer to a slice")
  55. }
  56. if sliceValue.Len() <= 0 {
  57. return 0, errors.New("could not insert a empty slice")
  58. }
  59. if err := session.statement.setRefValue(reflect.ValueOf(sliceValue.Index(0).Interface())); err != nil {
  60. return 0, err
  61. }
  62. if len(session.statement.TableName()) <= 0 {
  63. return 0, ErrTableNotFound
  64. }
  65. table := session.statement.RefTable
  66. size := sliceValue.Len()
  67. var colNames []string
  68. var colMultiPlaces []string
  69. var args []interface{}
  70. var cols []*core.Column
  71. for i := 0; i < size; i++ {
  72. v := sliceValue.Index(i)
  73. vv := reflect.Indirect(v)
  74. elemValue := v.Interface()
  75. var colPlaces []string
  76. // handle BeforeInsertProcessor
  77. // !nashtsai! does user expect it's same slice to passed closure when using Before()/After() when insert multi??
  78. for _, closure := range session.beforeClosures {
  79. closure(elemValue)
  80. }
  81. if processor, ok := interface{}(elemValue).(BeforeInsertProcessor); ok {
  82. processor.BeforeInsert()
  83. }
  84. // --
  85. if i == 0 {
  86. for _, col := range table.Columns() {
  87. ptrFieldValue, err := col.ValueOfV(&vv)
  88. if err != nil {
  89. return 0, err
  90. }
  91. fieldValue := *ptrFieldValue
  92. if col.IsAutoIncrement && isZero(fieldValue.Interface()) {
  93. continue
  94. }
  95. if col.MapType == core.ONLYFROMDB {
  96. continue
  97. }
  98. if col.IsDeleted {
  99. continue
  100. }
  101. if session.statement.omitColumnMap.contain(col.Name) {
  102. continue
  103. }
  104. if len(session.statement.columnMap) > 0 && !session.statement.columnMap.contain(col.Name) {
  105. continue
  106. }
  107. if (col.IsCreated || col.IsUpdated) && session.statement.UseAutoTime {
  108. val, t := session.engine.nowTime(col)
  109. args = append(args, val)
  110. var colName = col.Name
  111. session.afterClosures = append(session.afterClosures, func(bean interface{}) {
  112. col := table.GetColumn(colName)
  113. setColumnTime(bean, col, t)
  114. })
  115. } else if col.IsVersion && session.statement.checkVersion {
  116. args = append(args, 1)
  117. var colName = col.Name
  118. session.afterClosures = append(session.afterClosures, func(bean interface{}) {
  119. col := table.GetColumn(colName)
  120. setColumnInt(bean, col, 1)
  121. })
  122. } else {
  123. arg, err := session.value2Interface(col, fieldValue)
  124. if err != nil {
  125. return 0, err
  126. }
  127. args = append(args, arg)
  128. }
  129. colNames = append(colNames, col.Name)
  130. cols = append(cols, col)
  131. colPlaces = append(colPlaces, "?")
  132. }
  133. } else {
  134. for _, col := range cols {
  135. ptrFieldValue, err := col.ValueOfV(&vv)
  136. if err != nil {
  137. return 0, err
  138. }
  139. fieldValue := *ptrFieldValue
  140. if col.IsAutoIncrement && isZero(fieldValue.Interface()) {
  141. continue
  142. }
  143. if col.MapType == core.ONLYFROMDB {
  144. continue
  145. }
  146. if col.IsDeleted {
  147. continue
  148. }
  149. if session.statement.omitColumnMap.contain(col.Name) {
  150. continue
  151. }
  152. if len(session.statement.columnMap) > 0 && !session.statement.columnMap.contain(col.Name) {
  153. continue
  154. }
  155. if (col.IsCreated || col.IsUpdated) && session.statement.UseAutoTime {
  156. val, t := session.engine.nowTime(col)
  157. args = append(args, val)
  158. var colName = col.Name
  159. session.afterClosures = append(session.afterClosures, func(bean interface{}) {
  160. col := table.GetColumn(colName)
  161. setColumnTime(bean, col, t)
  162. })
  163. } else if col.IsVersion && session.statement.checkVersion {
  164. args = append(args, 1)
  165. var colName = col.Name
  166. session.afterClosures = append(session.afterClosures, func(bean interface{}) {
  167. col := table.GetColumn(colName)
  168. setColumnInt(bean, col, 1)
  169. })
  170. } else {
  171. arg, err := session.value2Interface(col, fieldValue)
  172. if err != nil {
  173. return 0, err
  174. }
  175. args = append(args, arg)
  176. }
  177. colPlaces = append(colPlaces, "?")
  178. }
  179. }
  180. colMultiPlaces = append(colMultiPlaces, strings.Join(colPlaces, ", "))
  181. }
  182. cleanupProcessorsClosures(&session.beforeClosures)
  183. var sql = "INSERT INTO %s (%v%v%v) VALUES (%v)"
  184. var statement string
  185. var tableName = session.statement.TableName()
  186. if session.engine.dialect.DBType() == core.ORACLE {
  187. sql = "INSERT ALL INTO %s (%v%v%v) VALUES (%v) SELECT 1 FROM DUAL"
  188. temp := fmt.Sprintf(") INTO %s (%v%v%v) VALUES (",
  189. session.engine.Quote(tableName),
  190. session.engine.QuoteStr(),
  191. strings.Join(colNames, session.engine.QuoteStr()+", "+session.engine.QuoteStr()),
  192. session.engine.QuoteStr())
  193. statement = fmt.Sprintf(sql,
  194. session.engine.Quote(tableName),
  195. session.engine.QuoteStr(),
  196. strings.Join(colNames, session.engine.QuoteStr()+", "+session.engine.QuoteStr()),
  197. session.engine.QuoteStr(),
  198. strings.Join(colMultiPlaces, temp))
  199. } else {
  200. statement = fmt.Sprintf(sql,
  201. session.engine.Quote(tableName),
  202. session.engine.QuoteStr(),
  203. strings.Join(colNames, session.engine.QuoteStr()+", "+session.engine.QuoteStr()),
  204. session.engine.QuoteStr(),
  205. strings.Join(colMultiPlaces, "),("))
  206. }
  207. res, err := session.exec(statement, args...)
  208. if err != nil {
  209. return 0, err
  210. }
  211. if cacher := session.engine.getCacher2(table); cacher != nil && session.statement.UseCache {
  212. session.cacheInsert(table, tableName)
  213. }
  214. lenAfterClosures := len(session.afterClosures)
  215. for i := 0; i < size; i++ {
  216. elemValue := reflect.Indirect(sliceValue.Index(i)).Addr().Interface()
  217. // handle AfterInsertProcessor
  218. if session.isAutoCommit {
  219. // !nashtsai! does user expect it's same slice to passed closure when using Before()/After() when insert multi??
  220. for _, closure := range session.afterClosures {
  221. closure(elemValue)
  222. }
  223. if processor, ok := interface{}(elemValue).(AfterInsertProcessor); ok {
  224. processor.AfterInsert()
  225. }
  226. } else {
  227. if lenAfterClosures > 0 {
  228. if value, has := session.afterInsertBeans[elemValue]; has && value != nil {
  229. *value = append(*value, session.afterClosures...)
  230. } else {
  231. afterClosures := make([]func(interface{}), lenAfterClosures)
  232. copy(afterClosures, session.afterClosures)
  233. session.afterInsertBeans[elemValue] = &afterClosures
  234. }
  235. } else {
  236. if _, ok := interface{}(elemValue).(AfterInsertProcessor); ok {
  237. session.afterInsertBeans[elemValue] = nil
  238. }
  239. }
  240. }
  241. }
  242. cleanupProcessorsClosures(&session.afterClosures)
  243. return res.RowsAffected()
  244. }
  245. // InsertMulti insert multiple records
  246. func (session *Session) InsertMulti(rowsSlicePtr interface{}) (int64, error) {
  247. if session.isAutoClose {
  248. defer session.Close()
  249. }
  250. sliceValue := reflect.Indirect(reflect.ValueOf(rowsSlicePtr))
  251. if sliceValue.Kind() != reflect.Slice {
  252. return 0, ErrParamsType
  253. }
  254. if sliceValue.Len() <= 0 {
  255. return 0, nil
  256. }
  257. return session.innerInsertMulti(rowsSlicePtr)
  258. }
  259. func (session *Session) innerInsert(bean interface{}) (int64, error) {
  260. if err := session.statement.setRefBean(bean); err != nil {
  261. return 0, err
  262. }
  263. if len(session.statement.TableName()) <= 0 {
  264. return 0, ErrTableNotFound
  265. }
  266. table := session.statement.RefTable
  267. // handle BeforeInsertProcessor
  268. for _, closure := range session.beforeClosures {
  269. closure(bean)
  270. }
  271. cleanupProcessorsClosures(&session.beforeClosures) // cleanup after used
  272. if processor, ok := interface{}(bean).(BeforeInsertProcessor); ok {
  273. processor.BeforeInsert()
  274. }
  275. colNames, args, err := session.genInsertColumns(bean)
  276. if err != nil {
  277. return 0, err
  278. }
  279. // insert expr columns, override if exists
  280. exprColumns := session.statement.getExpr()
  281. exprColVals := make([]string, 0, len(exprColumns))
  282. for _, v := range exprColumns {
  283. // remove the expr columns
  284. for i, colName := range colNames {
  285. if colName == v.colName {
  286. colNames = append(colNames[:i], colNames[i+1:]...)
  287. args = append(args[:i], args[i+1:]...)
  288. }
  289. }
  290. // append expr column to the end
  291. colNames = append(colNames, v.colName)
  292. exprColVals = append(exprColVals, v.expr)
  293. }
  294. colPlaces := strings.Repeat("?, ", len(colNames)-len(exprColumns))
  295. if len(exprColVals) > 0 {
  296. colPlaces = colPlaces + strings.Join(exprColVals, ", ")
  297. } else {
  298. if len(colPlaces) > 0 {
  299. colPlaces = colPlaces[0 : len(colPlaces)-2]
  300. }
  301. }
  302. var sqlStr string
  303. var tableName = session.statement.TableName()
  304. if len(colPlaces) > 0 {
  305. sqlStr = fmt.Sprintf("INSERT INTO %s (%v%v%v) VALUES (%v)",
  306. session.engine.Quote(tableName),
  307. session.engine.QuoteStr(),
  308. strings.Join(colNames, session.engine.Quote(", ")),
  309. session.engine.QuoteStr(),
  310. colPlaces)
  311. } else {
  312. if session.engine.dialect.DBType() == core.MYSQL {
  313. sqlStr = fmt.Sprintf("INSERT INTO %s VALUES ()", session.engine.Quote(tableName))
  314. } else {
  315. sqlStr = fmt.Sprintf("INSERT INTO %s DEFAULT VALUES", session.engine.Quote(tableName))
  316. }
  317. }
  318. handleAfterInsertProcessorFunc := func(bean interface{}) {
  319. if session.isAutoCommit {
  320. for _, closure := range session.afterClosures {
  321. closure(bean)
  322. }
  323. if processor, ok := interface{}(bean).(AfterInsertProcessor); ok {
  324. processor.AfterInsert()
  325. }
  326. } else {
  327. lenAfterClosures := len(session.afterClosures)
  328. if lenAfterClosures > 0 {
  329. if value, has := session.afterInsertBeans[bean]; has && value != nil {
  330. *value = append(*value, session.afterClosures...)
  331. } else {
  332. afterClosures := make([]func(interface{}), lenAfterClosures)
  333. copy(afterClosures, session.afterClosures)
  334. session.afterInsertBeans[bean] = &afterClosures
  335. }
  336. } else {
  337. if _, ok := interface{}(bean).(AfterInsertProcessor); ok {
  338. session.afterInsertBeans[bean] = nil
  339. }
  340. }
  341. }
  342. cleanupProcessorsClosures(&session.afterClosures) // cleanup after used
  343. }
  344. // for postgres, many of them didn't implement lastInsertId, so we should
  345. // implemented it ourself.
  346. if session.engine.dialect.DBType() == core.ORACLE && len(table.AutoIncrement) > 0 {
  347. res, err := session.queryBytes("select seq_atable.currval from dual", args...)
  348. if err != nil {
  349. return 0, err
  350. }
  351. defer handleAfterInsertProcessorFunc(bean)
  352. if cacher := session.engine.getCacher2(table); cacher != nil && session.statement.UseCache {
  353. session.cacheInsert(table, tableName)
  354. }
  355. if table.Version != "" && session.statement.checkVersion {
  356. verValue, err := table.VersionColumn().ValueOf(bean)
  357. if err != nil {
  358. session.engine.logger.Error(err)
  359. } else if verValue.IsValid() && verValue.CanSet() {
  360. verValue.SetInt(1)
  361. }
  362. }
  363. if len(res) < 1 {
  364. return 0, errors.New("insert no error but not returned id")
  365. }
  366. idByte := res[0][table.AutoIncrement]
  367. id, err := strconv.ParseInt(string(idByte), 10, 64)
  368. if err != nil || id <= 0 {
  369. return 1, err
  370. }
  371. aiValue, err := table.AutoIncrColumn().ValueOf(bean)
  372. if err != nil {
  373. session.engine.logger.Error(err)
  374. }
  375. if aiValue == nil || !aiValue.IsValid() || !aiValue.CanSet() {
  376. return 1, nil
  377. }
  378. aiValue.Set(int64ToIntValue(id, aiValue.Type()))
  379. return 1, nil
  380. } else if session.engine.dialect.DBType() == core.POSTGRES && len(table.AutoIncrement) > 0 {
  381. //assert table.AutoIncrement != ""
  382. sqlStr = sqlStr + " RETURNING " + session.engine.Quote(table.AutoIncrement)
  383. res, err := session.queryBytes(sqlStr, args...)
  384. if err != nil {
  385. return 0, err
  386. }
  387. defer handleAfterInsertProcessorFunc(bean)
  388. if cacher := session.engine.getCacher2(table); cacher != nil && session.statement.UseCache {
  389. session.cacheInsert(table, tableName)
  390. }
  391. if table.Version != "" && session.statement.checkVersion {
  392. verValue, err := table.VersionColumn().ValueOf(bean)
  393. if err != nil {
  394. session.engine.logger.Error(err)
  395. } else if verValue.IsValid() && verValue.CanSet() {
  396. verValue.SetInt(1)
  397. }
  398. }
  399. if len(res) < 1 {
  400. return 0, errors.New("insert no error but not returned id")
  401. }
  402. idByte := res[0][table.AutoIncrement]
  403. id, err := strconv.ParseInt(string(idByte), 10, 64)
  404. if err != nil || id <= 0 {
  405. return 1, err
  406. }
  407. aiValue, err := table.AutoIncrColumn().ValueOf(bean)
  408. if err != nil {
  409. session.engine.logger.Error(err)
  410. }
  411. if aiValue == nil || !aiValue.IsValid() || !aiValue.CanSet() {
  412. return 1, nil
  413. }
  414. aiValue.Set(int64ToIntValue(id, aiValue.Type()))
  415. return 1, nil
  416. } else {
  417. res, err := session.exec(sqlStr, args...)
  418. if err != nil {
  419. return 0, err
  420. }
  421. defer handleAfterInsertProcessorFunc(bean)
  422. if cacher := session.engine.getCacher2(table); cacher != nil && session.statement.UseCache {
  423. session.cacheInsert(table, tableName)
  424. }
  425. if table.Version != "" && session.statement.checkVersion {
  426. verValue, err := table.VersionColumn().ValueOf(bean)
  427. if err != nil {
  428. session.engine.logger.Error(err)
  429. } else if verValue.IsValid() && verValue.CanSet() {
  430. verValue.SetInt(1)
  431. }
  432. }
  433. if table.AutoIncrement == "" {
  434. return res.RowsAffected()
  435. }
  436. var id int64
  437. id, err = res.LastInsertId()
  438. if err != nil || id <= 0 {
  439. return res.RowsAffected()
  440. }
  441. aiValue, err := table.AutoIncrColumn().ValueOf(bean)
  442. if err != nil {
  443. session.engine.logger.Error(err)
  444. }
  445. if aiValue == nil || !aiValue.IsValid() || !aiValue.CanSet() {
  446. return res.RowsAffected()
  447. }
  448. aiValue.Set(int64ToIntValue(id, aiValue.Type()))
  449. return res.RowsAffected()
  450. }
  451. }
  452. // InsertOne insert only one struct into database as a record.
  453. // The in parameter bean must a struct or a point to struct. The return
  454. // parameter is inserted and error
  455. func (session *Session) InsertOne(bean interface{}) (int64, error) {
  456. if session.isAutoClose {
  457. defer session.Close()
  458. }
  459. return session.innerInsert(bean)
  460. }
  461. func (session *Session) cacheInsert(table *core.Table, tables ...string) error {
  462. if table == nil {
  463. return ErrCacheFailed
  464. }
  465. cacher := session.engine.getCacher2(table)
  466. for _, t := range tables {
  467. session.engine.logger.Debug("[cache] clear sql:", t)
  468. cacher.ClearIds(t)
  469. }
  470. return nil
  471. }
  472. // genInsertColumns generates insert needed columns
  473. func (session *Session) genInsertColumns(bean interface{}) ([]string, []interface{}, error) {
  474. table := session.statement.RefTable
  475. colNames := make([]string, 0, len(table.ColumnsSeq()))
  476. args := make([]interface{}, 0, len(table.ColumnsSeq()))
  477. for _, col := range table.Columns() {
  478. if col.MapType == core.ONLYFROMDB {
  479. continue
  480. }
  481. if col.IsDeleted {
  482. continue
  483. }
  484. if session.statement.omitColumnMap.contain(col.Name) {
  485. continue
  486. }
  487. if len(session.statement.columnMap) > 0 && !session.statement.columnMap.contain(col.Name) {
  488. continue
  489. }
  490. if _, ok := session.statement.incrColumns[col.Name]; ok {
  491. continue
  492. } else if _, ok := session.statement.decrColumns[col.Name]; ok {
  493. continue
  494. }
  495. fieldValuePtr, err := col.ValueOf(bean)
  496. if err != nil {
  497. return nil, nil, err
  498. }
  499. fieldValue := *fieldValuePtr
  500. if col.IsAutoIncrement {
  501. switch fieldValue.Type().Kind() {
  502. case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int, reflect.Int64:
  503. if fieldValue.Int() == 0 {
  504. continue
  505. }
  506. case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint, reflect.Uint64:
  507. if fieldValue.Uint() == 0 {
  508. continue
  509. }
  510. case reflect.String:
  511. if len(fieldValue.String()) == 0 {
  512. continue
  513. }
  514. case reflect.Ptr:
  515. if fieldValue.Pointer() == 0 {
  516. continue
  517. }
  518. }
  519. }
  520. // !evalphobia! set fieldValue as nil when column is nullable and zero-value
  521. if _, ok := getFlagForColumn(session.statement.nullableMap, col); ok {
  522. if col.Nullable && isZero(fieldValue.Interface()) {
  523. var nilValue *int
  524. fieldValue = reflect.ValueOf(nilValue)
  525. }
  526. }
  527. if (col.IsCreated || col.IsUpdated) && session.statement.UseAutoTime /*&& isZero(fieldValue.Interface())*/ {
  528. // if time is non-empty, then set to auto time
  529. val, t := session.engine.nowTime(col)
  530. args = append(args, val)
  531. var colName = col.Name
  532. session.afterClosures = append(session.afterClosures, func(bean interface{}) {
  533. col := table.GetColumn(colName)
  534. setColumnTime(bean, col, t)
  535. })
  536. } else if col.IsVersion && session.statement.checkVersion {
  537. args = append(args, 1)
  538. } else {
  539. arg, err := session.value2Interface(col, fieldValue)
  540. if err != nil {
  541. return colNames, args, err
  542. }
  543. args = append(args, arg)
  544. }
  545. colNames = append(colNames, col.Name)
  546. }
  547. return colNames, args, nil
  548. }