http urls monitor.

collection.go 7.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. package sqladapter
  2. import (
  3. "errors"
  4. "fmt"
  5. "reflect"
  6. "upper.io/db.v3"
  7. "upper.io/db.v3/internal/sqladapter/exql"
  8. "upper.io/db.v3/lib/reflectx"
  9. )
  10. var mapper = reflectx.NewMapper("db")
  11. var errMissingPrimaryKeys = errors.New("Table %q has no primary keys")
  12. // Collection represents a SQL table.
  13. type Collection interface {
  14. PartialCollection
  15. BaseCollection
  16. }
  17. // PartialCollection defines methods that must be implemented by the adapter.
  18. type PartialCollection interface {
  19. // Database returns the parent database.
  20. Database() Database
  21. // Name returns the name of the table.
  22. Name() string
  23. // Insert inserts a new item into the collection.
  24. Insert(interface{}) (interface{}, error)
  25. }
  26. // BaseCollection provides logic for methods that can be shared across all SQL
  27. // adapters.
  28. type BaseCollection interface {
  29. // Exists returns true if the collection exists.
  30. Exists() bool
  31. // Find creates and returns a new result set.
  32. Find(conds ...interface{}) db.Result
  33. // Truncate removes all items on the collection.
  34. Truncate() error
  35. // InsertReturning inserts a new item and updates it with the
  36. // actual values from the database.
  37. InsertReturning(interface{}) error
  38. // UpdateReturning updates an item and returns the actual values from the
  39. // database.
  40. UpdateReturning(interface{}) error
  41. // PrimaryKeys returns the table's primary keys.
  42. PrimaryKeys() []string
  43. }
  44. type condsFilter interface {
  45. FilterConds(...interface{}) []interface{}
  46. }
  47. // collection is the implementation of Collection.
  48. type collection struct {
  49. BaseCollection
  50. PartialCollection
  51. pk []string
  52. err error
  53. }
  54. var (
  55. _ = Collection(&collection{})
  56. )
  57. // NewBaseCollection returns a collection with basic methods.
  58. func NewBaseCollection(p PartialCollection) BaseCollection {
  59. c := &collection{PartialCollection: p}
  60. c.pk, c.err = c.Database().PrimaryKeys(c.Name())
  61. return c
  62. }
  63. // PrimaryKeys returns the collection's primary keys, if any.
  64. func (c *collection) PrimaryKeys() []string {
  65. return c.pk
  66. }
  67. func (c *collection) filterConds(conds ...interface{}) []interface{} {
  68. if tr, ok := c.PartialCollection.(condsFilter); ok {
  69. return tr.FilterConds(conds...)
  70. }
  71. if len(conds) == 1 && len(c.pk) == 1 {
  72. if id := conds[0]; IsKeyValue(id) {
  73. conds[0] = db.Cond{c.pk[0]: db.Eq(id)}
  74. }
  75. }
  76. return conds
  77. }
  78. // Find creates a result set with the given conditions.
  79. func (c *collection) Find(conds ...interface{}) db.Result {
  80. if c.err != nil {
  81. res := &Result{}
  82. res.setErr(c.err)
  83. return res
  84. }
  85. return NewResult(
  86. c.Database(),
  87. c.Name(),
  88. c.filterConds(conds...),
  89. )
  90. }
  91. // Exists returns true if the collection exists.
  92. func (c *collection) Exists() bool {
  93. if err := c.Database().TableExists(c.Name()); err != nil {
  94. return false
  95. }
  96. return true
  97. }
  98. // InsertReturning inserts an item and updates the given variable reference.
  99. func (c *collection) InsertReturning(item interface{}) error {
  100. if item == nil || reflect.TypeOf(item).Kind() != reflect.Ptr {
  101. return fmt.Errorf("Expecting a pointer but got %T", item)
  102. }
  103. // Grab primary keys
  104. pks := c.PrimaryKeys()
  105. if len(pks) == 0 {
  106. if !c.Exists() {
  107. return db.ErrCollectionDoesNotExist
  108. }
  109. return fmt.Errorf(errMissingPrimaryKeys.Error(), c.Name())
  110. }
  111. var tx DatabaseTx
  112. inTx := false
  113. if currTx := c.Database().Transaction(); currTx != nil {
  114. tx = NewDatabaseTx(c.Database())
  115. inTx = true
  116. } else {
  117. // Not within a transaction, let's create one.
  118. var err error
  119. tx, err = c.Database().NewDatabaseTx(c.Database().Context())
  120. if err != nil {
  121. return err
  122. }
  123. defer tx.(Database).Close()
  124. }
  125. // Allocate a clone of item.
  126. newItem := reflect.New(reflect.ValueOf(item).Elem().Type()).Interface()
  127. var newItemFieldMap map[string]reflect.Value
  128. itemValue := reflect.ValueOf(item)
  129. col := tx.(Database).Collection(c.Name())
  130. // Insert item as is and grab the returning ID.
  131. id, err := col.Insert(item)
  132. if err != nil {
  133. goto cancel
  134. }
  135. if id == nil {
  136. err = fmt.Errorf("InsertReturning: Could not get a valid ID after inserting. Does the %q table have a primary key?", c.Name())
  137. goto cancel
  138. }
  139. // Fetch the row that was just interted into newItem
  140. err = col.Find(id).One(newItem)
  141. if err != nil {
  142. goto cancel
  143. }
  144. switch reflect.ValueOf(newItem).Elem().Kind() {
  145. case reflect.Struct:
  146. // Get valid fields from newItem to overwrite those that are on item.
  147. newItemFieldMap = mapper.ValidFieldMap(reflect.ValueOf(newItem))
  148. for fieldName := range newItemFieldMap {
  149. mapper.FieldByName(itemValue, fieldName).Set(newItemFieldMap[fieldName])
  150. }
  151. case reflect.Map:
  152. newItemV := reflect.ValueOf(newItem).Elem()
  153. itemV := reflect.ValueOf(item)
  154. if itemV.Kind() == reflect.Ptr {
  155. itemV = itemV.Elem()
  156. }
  157. for _, keyV := range newItemV.MapKeys() {
  158. itemV.SetMapIndex(keyV, newItemV.MapIndex(keyV))
  159. }
  160. default:
  161. err = fmt.Errorf("InsertReturning: expecting a pointer to map or struct, got %T", newItem)
  162. goto cancel
  163. }
  164. if !inTx {
  165. // This is only executed if t.Database() was **not** a transaction and if
  166. // sess was created with sess.NewTransaction().
  167. return tx.Commit()
  168. }
  169. return err
  170. cancel:
  171. // This goto label should only be used when we got an error within a
  172. // transaction and we don't want to continue.
  173. if !inTx {
  174. // This is only executed if t.Database() was **not** a transaction and if
  175. // sess was created with sess.NewTransaction().
  176. tx.Rollback()
  177. }
  178. return err
  179. }
  180. func (c *collection) UpdateReturning(item interface{}) error {
  181. if item == nil || reflect.TypeOf(item).Kind() != reflect.Ptr {
  182. return fmt.Errorf("Expecting a pointer but got %T", item)
  183. }
  184. // Grab primary keys
  185. pks := c.PrimaryKeys()
  186. if len(pks) == 0 {
  187. if !c.Exists() {
  188. return db.ErrCollectionDoesNotExist
  189. }
  190. return fmt.Errorf(errMissingPrimaryKeys.Error(), c.Name())
  191. }
  192. var tx DatabaseTx
  193. inTx := false
  194. if currTx := c.Database().Transaction(); currTx != nil {
  195. tx = NewDatabaseTx(c.Database())
  196. inTx = true
  197. } else {
  198. // Not within a transaction, let's create one.
  199. var err error
  200. tx, err = c.Database().NewDatabaseTx(c.Database().Context())
  201. if err != nil {
  202. return err
  203. }
  204. defer tx.(Database).Close()
  205. }
  206. // Allocate a clone of item.
  207. defaultItem := reflect.New(reflect.ValueOf(item).Elem().Type()).Interface()
  208. var defaultItemFieldMap map[string]reflect.Value
  209. itemValue := reflect.ValueOf(item)
  210. conds := db.Cond{}
  211. for _, pk := range pks {
  212. conds[pk] = db.Eq(mapper.FieldByName(itemValue, pk).Interface())
  213. }
  214. col := tx.(Database).Collection(c.Name())
  215. err := col.Find(conds).Update(item)
  216. if err != nil {
  217. goto cancel
  218. }
  219. if err = col.Find(conds).One(defaultItem); err != nil {
  220. goto cancel
  221. }
  222. switch reflect.ValueOf(defaultItem).Elem().Kind() {
  223. case reflect.Struct:
  224. // Get valid fields from defaultItem to overwrite those that are on item.
  225. defaultItemFieldMap = mapper.ValidFieldMap(reflect.ValueOf(defaultItem))
  226. for fieldName := range defaultItemFieldMap {
  227. mapper.FieldByName(itemValue, fieldName).Set(defaultItemFieldMap[fieldName])
  228. }
  229. case reflect.Map:
  230. defaultItemV := reflect.ValueOf(defaultItem).Elem()
  231. itemV := reflect.ValueOf(item)
  232. if itemV.Kind() == reflect.Ptr {
  233. itemV = itemV.Elem()
  234. }
  235. for _, keyV := range defaultItemV.MapKeys() {
  236. itemV.SetMapIndex(keyV, defaultItemV.MapIndex(keyV))
  237. }
  238. default:
  239. panic("default")
  240. }
  241. if !inTx {
  242. // This is only executed if t.Database() was **not** a transaction and if
  243. // sess was created with sess.NewTransaction().
  244. return tx.Commit()
  245. }
  246. return err
  247. cancel:
  248. // This goto label should only be used when we got an error within a
  249. // transaction and we don't want to continue.
  250. if !inTx {
  251. // This is only executed if t.Database() was **not** a transaction and if
  252. // sess was created with sess.NewTransaction().
  253. tx.Rollback()
  254. }
  255. return err
  256. }
  257. // Truncate deletes all rows from the table.
  258. func (c *collection) Truncate() error {
  259. stmt := exql.Statement{
  260. Type: exql.Truncate,
  261. Table: exql.TableWithName(c.Name()),
  262. }
  263. if _, err := c.Database().Exec(&stmt); err != nil {
  264. return err
  265. }
  266. return nil
  267. }