github.com/angenalZZZ/gofunc@v0.0.0-20210507121333-48ff1be3917b/cmd/natsql/handler.go (about) 1 package main 2 3 import ( 4 "context" 5 "fmt" 6 "os" 7 "strings" 8 "time" 9 10 "github.com/jmoiron/sqlx" 11 12 "github.com/angenalZZZ/gofunc/f" 13 "github.com/angenalZZZ/gofunc/js" 14 nat "github.com/angenalZZZ/gofunc/rpc/nats" 15 json "github.com/json-iterator/go" 16 17 _ "github.com/denisenkom/go-mssqldb" 18 _ "github.com/go-sql-driver/mysql" 19 _ "github.com/mattn/go-sqlite3" 20 ) 21 22 type handler struct { 23 context.Context 24 running bool 25 // js code 26 js string 27 jsr *js.GoRuntime 28 jsMod time.Time 29 jsFile string 30 jsName string 31 jsFunc func([]map[string]interface{}) interface{} 32 // js global variable 33 jso map[string]interface{} 34 // js runtime and register param 35 jsp *js.GoRuntimeParam 36 // natS subscriber 37 sub *nat.SubscriberFastCache 38 } 39 40 // Handle run default handler 41 func (h *handler) Handle(list [][]byte) error { 42 h.running = true 43 size := len(list) 44 if size == 0 { 45 h.running = false 46 return nil 47 } 48 49 // gets records 50 records := make([]map[string]interface{}, 0, size) 51 debug := configInfo.Log.Level == "debug" || nat.Log.GetLevel() < 1 52 for _, item := range list { 53 if len(item) == 0 { 54 break 55 } 56 if item[0] == '{' { 57 if debug { 58 nat.Log.Debug().Msgf("[nats] received on %q: %s", h.jsp.NatSubject, item) 59 } 60 61 var obj map[string]interface{} 62 if err := json.Unmarshal(item, &obj); err != nil { 63 continue 64 } 65 66 records = append(records, obj) 67 } 68 } 69 70 if len(records) == 0 { 71 h.running = false 72 return nil 73 } 74 75 script, sqlLen := h.js, 20 76 77 if h.jsFunc != nil { 78 // js runtime and register 79 //vm := js.NewRuntime(h.jsp) 80 //defer vm.Clear() 81 // 82 //if _, err := vm.Runtime.RunString(script); err != nil { 83 // h.running = false 84 // return err 85 //} 86 //var fn func([]map[string]interface{}) interface{} 87 //val := vm.Runtime.Get(h.jsName) 88 //if val == nil { 89 // h.running = false 90 // return fmt.Errorf("js function %q not found", fnName) 91 //} 92 //if err := vm.Runtime.ExportTo(val, &fn); err != nil { 93 // h.running = false 94 // return fmt.Errorf("js function %q not exported %s", fnName, err.Error()) 95 //} 96 97 db := h.jsp.DB 98 if db == nil && h.jsr != nil { 99 db = h.jsr.DB 100 } 101 if db == nil { 102 var err error 103 db, err = sqlx.Connect(h.jsp.DbType, h.jsp.DbConn) 104 if err != nil { 105 return err 106 } 107 if db == nil { 108 return fmt.Errorf("%s Connect Error", h.jsp.DbType) 109 } 110 defer func() { _ = db.Close() }() 111 } 112 113 // Split records with specified size not to exceed Database parameter limit 114 for _, rs := range f.SplitObjectMaps(records, configInfo.Nats.Bulk) { 115 // Output sql 116 val := h.jsFunc(rs) 117 if val == nil { 118 continue 119 } 120 121 switch sql := val.(type) { 122 case string: 123 if len(sql) < sqlLen { 124 continue 125 } 126 if _, err := db.Exec(sql); err != nil { 127 h.running = false 128 return err 129 } 130 case []string: 131 for _, s := range sql { 132 if len(s) < sqlLen { 133 continue 134 } 135 if _, err := db.Exec(s); err != nil { 136 h.running = false 137 return err 138 } 139 } 140 } 141 142 time.Sleep(time.Microsecond) 143 } 144 } else { 145 // js runtime and register 146 vm, fnName := h.jsr, h.jsName 147 if vm == nil { 148 vm = js.NewRuntime(h.jsp) 149 } 150 defer vm.Clear() 151 152 db := h.jsp.DB 153 if db == nil { 154 db = vm.DB 155 } 156 if db == nil { 157 var err error 158 db, err = sqlx.Connect(h.jsp.DbType, h.jsp.DbConn) 159 if err != nil { 160 return err 161 } 162 if db == nil { 163 return fmt.Errorf("%s Connect Error", h.jsp.DbType) 164 } 165 defer func() { _ = db.Close() }() 166 } 167 168 // Split records with specified size not to exceed Database parameter limit 169 for _, rs := range f.SplitObjectMaps(records, configInfo.Nats.Bulk) { 170 // Input records 171 vm.Runtime.Set(h.jsName, rs) 172 173 // Output sql 174 res, err := vm.Runtime.RunString(script) 175 if err != nil { 176 h.running = false 177 return fmt.Errorf("the table script error, must contain array %q, error: %s", fnName, err.Error()) 178 } 179 if res == nil { 180 continue 181 } 182 183 val := res.Export() 184 if val == nil { 185 continue 186 } 187 188 switch sql := val.(type) { 189 case string: 190 if len(sql) < sqlLen { 191 continue 192 } 193 if _, err := db.Exec(sql); err != nil { 194 h.running = false 195 return err 196 } 197 case []string: 198 for _, s := range sql { 199 if len(s) < sqlLen { 200 continue 201 } 202 if _, err := db.Exec(s); err != nil { 203 h.running = false 204 return err 205 } 206 } 207 } 208 209 time.Sleep(time.Microsecond) 210 } 211 } 212 213 h.running = false 214 return nil 215 } 216 217 // Stop run 218 func (h *handler) Stop(ms ...int) { 219 n := 10000 220 if len(ms) > 0 { 221 n = ms[0] 222 } 223 for ; h.running && n > 0; n-- { 224 time.Sleep(time.Millisecond) 225 } 226 } 227 228 func (h *handler) isScriptMod() bool { 229 if h.jsFile == "" { 230 return false 231 } 232 info, err := os.Stat(h.jsFile) 233 if os.IsNotExist(err) { 234 return false 235 } 236 if t := info.ModTime(); t.Unix() != h.jsMod.Unix() { 237 h.jsMod = t 238 return true 239 } 240 return false 241 } 242 243 func (h *handler) doScriptMod() error { 244 if h.jsFile == "" { 245 return nil 246 } 247 script, err := f.ReadFile(h.jsFile) 248 if err != nil { 249 return err 250 } 251 252 h.js = strings.TrimSpace(string(script)) 253 return nil 254 }