session_insert.go 17 KB

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