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  }