resource.go 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. package mysql
  2. import (
  3. "context"
  4. "database/sql"
  5. "fmt"
  6. "strings"
  7. "github.com/pkg/errors"
  8. "github.com/usememos/memos/store"
  9. )
  10. func (d *DB) CreateResource(ctx context.Context, create *store.Resource) (*store.Resource, error) {
  11. fields := []string{"`filename`", "`blob`", "`external_link`", "`type`", "`size`", "`creator_id`", "`internal_path`"}
  12. placeholder := []string{"?", "?", "?", "?", "?", "?", "?"}
  13. args := []any{create.Filename, create.Blob, create.ExternalLink, create.Type, create.Size, create.CreatorID, create.InternalPath}
  14. if create.ID != 0 {
  15. fields = append(fields, "`id`")
  16. placeholder = append(placeholder, "?")
  17. args = append(args, create.ID)
  18. }
  19. if create.CreatedTs != 0 {
  20. fields = append(fields, "`created_ts`")
  21. placeholder = append(placeholder, "FROM_UNIXTIME(?)")
  22. args = append(args, create.CreatedTs)
  23. }
  24. if create.UpdatedTs != 0 {
  25. fields = append(fields, "`updated_ts`")
  26. placeholder = append(placeholder, "FROM_UNIXTIME(?)")
  27. args = append(args, create.UpdatedTs)
  28. }
  29. if create.MemoID != nil {
  30. fields = append(fields, "`memo_id`")
  31. placeholder = append(placeholder, "?")
  32. args = append(args, *create.MemoID)
  33. }
  34. stmt := "INSERT INTO `resource` (" + strings.Join(fields, ", ") + ") VALUES (" + strings.Join(placeholder, ", ") + ")"
  35. result, err := d.db.ExecContext(ctx, stmt, args...)
  36. if err != nil {
  37. return nil, err
  38. }
  39. id, err := result.LastInsertId()
  40. if err != nil {
  41. return nil, err
  42. }
  43. id32 := int32(id)
  44. list, err := d.ListResources(ctx, &store.FindResource{ID: &id32})
  45. if err != nil {
  46. return nil, err
  47. }
  48. if len(list) != 1 {
  49. return nil, errors.Wrapf(nil, "unexpected resource count: %d", len(list))
  50. }
  51. return list[0], nil
  52. }
  53. func (d *DB) ListResources(ctx context.Context, find *store.FindResource) ([]*store.Resource, error) {
  54. where, args := []string{"1 = 1"}, []any{}
  55. if v := find.ID; v != nil {
  56. where, args = append(where, "`id` = ?"), append(args, *v)
  57. }
  58. if v := find.CreatorID; v != nil {
  59. where, args = append(where, "`creator_id` = ?"), append(args, *v)
  60. }
  61. if v := find.Filename; v != nil {
  62. where, args = append(where, "`filename` = ?"), append(args, *v)
  63. }
  64. if v := find.MemoID; v != nil {
  65. where, args = append(where, "`memo_id` = ?"), append(args, *v)
  66. }
  67. if find.HasRelatedMemo {
  68. where = append(where, "`memo_id` IS NOT NULL")
  69. }
  70. fields := []string{"`id`", "`filename`", "`external_link`", "`type`", "`size`", "`creator_id`", "UNIX_TIMESTAMP(`created_ts`)", "UNIX_TIMESTAMP(`updated_ts`)", "`internal_path`", "`memo_id`"}
  71. if find.GetBlob {
  72. fields = append(fields, "`blob`")
  73. }
  74. query := fmt.Sprintf("SELECT %s FROM `resource` WHERE %s GROUP BY `id` ORDER BY `created_ts` DESC", strings.Join(fields, ", "), strings.Join(where, " AND "))
  75. if find.Limit != nil {
  76. query = fmt.Sprintf("%s LIMIT %d", query, *find.Limit)
  77. if find.Offset != nil {
  78. query = fmt.Sprintf("%s OFFSET %d", query, *find.Offset)
  79. }
  80. }
  81. rows, err := d.db.QueryContext(ctx, query, args...)
  82. if err != nil {
  83. return nil, err
  84. }
  85. defer rows.Close()
  86. list := make([]*store.Resource, 0)
  87. for rows.Next() {
  88. resource := store.Resource{}
  89. var memoID sql.NullInt32
  90. dests := []any{
  91. &resource.ID,
  92. &resource.Filename,
  93. &resource.ExternalLink,
  94. &resource.Type,
  95. &resource.Size,
  96. &resource.CreatorID,
  97. &resource.CreatedTs,
  98. &resource.UpdatedTs,
  99. &resource.InternalPath,
  100. &memoID,
  101. }
  102. if find.GetBlob {
  103. dests = append(dests, &resource.Blob)
  104. }
  105. if err := rows.Scan(dests...); err != nil {
  106. return nil, err
  107. }
  108. if memoID.Valid {
  109. resource.MemoID = &memoID.Int32
  110. }
  111. list = append(list, &resource)
  112. }
  113. if err := rows.Err(); err != nil {
  114. return nil, err
  115. }
  116. return list, nil
  117. }
  118. func (d *DB) UpdateResource(ctx context.Context, update *store.UpdateResource) (*store.Resource, error) {
  119. set, args := []string{}, []any{}
  120. if v := update.UpdatedTs; v != nil {
  121. set, args = append(set, "`updated_ts` = ?"), append(args, *v)
  122. }
  123. if v := update.Filename; v != nil {
  124. set, args = append(set, "`filename` = ?"), append(args, *v)
  125. }
  126. if v := update.InternalPath; v != nil {
  127. set, args = append(set, "`internal_path` = ?"), append(args, *v)
  128. }
  129. if v := update.MemoID; v != nil {
  130. set, args = append(set, "`memo_id` = ?"), append(args, *v)
  131. }
  132. if v := update.Blob; v != nil {
  133. set, args = append(set, "`blob` = ?"), append(args, v)
  134. }
  135. args = append(args, update.ID)
  136. stmt := "UPDATE `resource` SET " + strings.Join(set, ", ") + " WHERE `id` = ?"
  137. if _, err := d.db.ExecContext(ctx, stmt, args...); err != nil {
  138. return nil, err
  139. }
  140. list, err := d.ListResources(ctx, &store.FindResource{ID: &update.ID})
  141. if err != nil {
  142. return nil, err
  143. }
  144. if len(list) != 1 {
  145. return nil, errors.Wrapf(nil, "unexpected resource count: %d", len(list))
  146. }
  147. return list[0], nil
  148. }
  149. func (d *DB) DeleteResource(ctx context.Context, delete *store.DeleteResource) error {
  150. stmt := "DELETE FROM `resource` WHERE `id` = ?"
  151. result, err := d.db.ExecContext(ctx, stmt, delete.ID)
  152. if err != nil {
  153. return err
  154. }
  155. if _, err := result.RowsAffected(); err != nil {
  156. return err
  157. }
  158. if err := d.Vacuum(ctx); err != nil {
  159. // Prevent linter warning.
  160. return err
  161. }
  162. return nil
  163. }
  164. func vacuumResource(ctx context.Context, tx *sql.Tx) error {
  165. stmt := "DELETE FROM `resource` WHERE `creator_id` NOT IN (SELECT `id` FROM `user`)"
  166. _, err := tx.ExecContext(ctx, stmt)
  167. if err != nil {
  168. return err
  169. }
  170. return nil
  171. }