1package gorm 2 3import ( 4 "database/sql" 5 "database/sql/driver" 6 "encoding/json" 7 "reflect" 8 9 "gorm.io/gorm/clause" 10 "gorm.io/gorm/schema" 11) 12 13type DeletedAt sql.NullTime 14 15// Scan implements the Scanner interface. 16func (n *DeletedAt) Scan(value interface{}) error { 17 return (*sql.NullTime)(n).Scan(value) 18} 19 20// Value implements the driver Valuer interface. 21func (n DeletedAt) Value() (driver.Value, error) { 22 if !n.Valid { 23 return nil, nil 24 } 25 return n.Time, nil 26} 27 28func (n DeletedAt) MarshalJSON() ([]byte, error) { 29 if n.Valid { 30 return json.Marshal(n.Time) 31 } 32 return json.Marshal(nil) 33} 34 35func (n *DeletedAt) UnmarshalJSON(b []byte) error { 36 if string(b) == "null" { 37 n.Valid = false 38 return nil 39 } 40 err := json.Unmarshal(b, &n.Time) 41 if err == nil { 42 n.Valid = true 43 } 44 return err 45} 46 47func (DeletedAt) QueryClauses(f *schema.Field) []clause.Interface { 48 return []clause.Interface{SoftDeleteQueryClause{Field: f}} 49} 50 51type SoftDeleteQueryClause struct { 52 Field *schema.Field 53} 54 55func (sd SoftDeleteQueryClause) Name() string { 56 return "" 57} 58 59func (sd SoftDeleteQueryClause) Build(clause.Builder) { 60} 61 62func (sd SoftDeleteQueryClause) MergeClause(*clause.Clause) { 63} 64 65func (sd SoftDeleteQueryClause) ModifyStatement(stmt *Statement) { 66 if _, ok := stmt.Clauses["soft_delete_enabled"]; !ok { 67 if c, ok := stmt.Clauses["WHERE"]; ok { 68 if where, ok := c.Expression.(clause.Where); ok && len(where.Exprs) > 1 { 69 for _, expr := range where.Exprs { 70 if orCond, ok := expr.(clause.OrConditions); ok && len(orCond.Exprs) == 1 { 71 where.Exprs = []clause.Expression{clause.And(where.Exprs...)} 72 c.Expression = where 73 stmt.Clauses["WHERE"] = c 74 break 75 } 76 } 77 } 78 } 79 80 stmt.AddClause(clause.Where{Exprs: []clause.Expression{ 81 clause.Eq{Column: clause.Column{Table: clause.CurrentTable, Name: sd.Field.DBName}, Value: nil}, 82 }}) 83 stmt.Clauses["soft_delete_enabled"] = clause.Clause{} 84 } 85} 86 87func (DeletedAt) UpdateClauses(f *schema.Field) []clause.Interface { 88 return []clause.Interface{SoftDeleteUpdateClause{Field: f}} 89} 90 91type SoftDeleteUpdateClause struct { 92 Field *schema.Field 93} 94 95func (sd SoftDeleteUpdateClause) Name() string { 96 return "" 97} 98 99func (sd SoftDeleteUpdateClause) Build(clause.Builder) { 100} 101 102func (sd SoftDeleteUpdateClause) MergeClause(*clause.Clause) { 103} 104 105func (sd SoftDeleteUpdateClause) ModifyStatement(stmt *Statement) { 106 if stmt.SQL.String() == "" { 107 if _, ok := stmt.Clauses["WHERE"]; stmt.DB.AllowGlobalUpdate || ok { 108 SoftDeleteQueryClause(sd).ModifyStatement(stmt) 109 } 110 } 111} 112 113func (DeletedAt) DeleteClauses(f *schema.Field) []clause.Interface { 114 return []clause.Interface{SoftDeleteDeleteClause{Field: f}} 115} 116 117type SoftDeleteDeleteClause struct { 118 Field *schema.Field 119} 120 121func (sd SoftDeleteDeleteClause) Name() string { 122 return "" 123} 124 125func (sd SoftDeleteDeleteClause) Build(clause.Builder) { 126} 127 128func (sd SoftDeleteDeleteClause) MergeClause(*clause.Clause) { 129} 130 131func (sd SoftDeleteDeleteClause) ModifyStatement(stmt *Statement) { 132 if stmt.SQL.String() == "" { 133 curTime := stmt.DB.NowFunc() 134 stmt.AddClause(clause.Set{{Column: clause.Column{Name: sd.Field.DBName}, Value: curTime}}) 135 stmt.SetColumn(sd.Field.DBName, curTime, true) 136 137 if stmt.Schema != nil { 138 _, queryValues := schema.GetIdentityFieldValuesMap(stmt.ReflectValue, stmt.Schema.PrimaryFields) 139 column, values := schema.ToQueryValues(stmt.Table, stmt.Schema.PrimaryFieldDBNames, queryValues) 140 141 if len(values) > 0 { 142 stmt.AddClause(clause.Where{Exprs: []clause.Expression{clause.IN{Column: column, Values: values}}}) 143 } 144 145 if stmt.ReflectValue.CanAddr() && stmt.Dest != stmt.Model && stmt.Model != nil { 146 _, queryValues = schema.GetIdentityFieldValuesMap(reflect.ValueOf(stmt.Model), stmt.Schema.PrimaryFields) 147 column, values = schema.ToQueryValues(stmt.Table, stmt.Schema.PrimaryFieldDBNames, queryValues) 148 149 if len(values) > 0 { 150 stmt.AddClause(clause.Where{Exprs: []clause.Expression{clause.IN{Column: column, Values: values}}}) 151 } 152 } 153 } 154 155 if _, ok := stmt.Clauses["WHERE"]; !stmt.DB.AllowGlobalUpdate && !ok { 156 stmt.DB.AddError(ErrMissingWhereClause) 157 } else { 158 SoftDeleteQueryClause(sd).ModifyStatement(stmt) 159 } 160 161 stmt.AddClauseIfNotExists(clause.Update{}) 162 stmt.Build(stmt.DB.Callback().Update().Clauses...) 163 } 164} 165