mysql.go 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. package mysql
  2. import (
  3. "database/sql"
  4. "fmt"
  5. "github.com/gogf/gf/v2/frame/g"
  6. "github.com/gogf/gf/v2/os/gctx"
  7. "strings"
  8. "sync"
  9. log "github.com/sirupsen/logrus"
  10. "github.com/xyproto/algernon/lua/convert"
  11. lua "github.com/xyproto/gopher-lua"
  12. // Using the PostgreSQL database engine
  13. _ "github.com/go-sql-driver/mysql"
  14. _ "github.com/lib/pq"
  15. )
  16. const (
  17. defaultQuery = "SELECT version()"
  18. defaultConnectionString = "host=localhost port=5432 user=postgres dbname=test sslmode=disable"
  19. )
  20. type LValueWrapper struct {
  21. LValue lua.LValue
  22. }
  23. type LValueWrappers [][]uint8
  24. var (
  25. // global map from connection string to database connection, to reuse connections, protected by a mutex
  26. reuseDB = make(map[string]*sql.DB)
  27. reuseMut = &sync.RWMutex{}
  28. )
  29. // Load makes functions related to building a library of Lua code available
  30. func Load(L *lua.LState, driver string) {
  31. // Register the PQ function
  32. L.SetGlobal("address", L.NewFunction(func(l *lua.LState) int {
  33. mainCtx := gctx.New()
  34. //root:=PDT49#80Z!RVv52_z@tcp(192.168.3.217:4000)/thirdparty
  35. address := g.Cfg().MustGet(mainCtx, "mysql.address", "").String()
  36. passWord := g.Cfg().MustGet(mainCtx, "mysql.passWord", "").String()
  37. userName := g.Cfg().MustGet(mainCtx, "mysql.userName", "").String()
  38. dbName := g.Cfg().MustGet(mainCtx, "mysql.dbName", "").String()
  39. mysqlCon := fmt.Sprintf("%s:%s@tcp(%s)/%s", userName, passWord, address, dbName)
  40. l.Push(lua.LString(mysqlCon))
  41. return 1
  42. }))
  43. L.SetGlobal("mysql", L.NewFunction(func(L *lua.LState) int {
  44. // Check if the optional argument is given
  45. query := defaultQuery
  46. if L.GetTop() >= 1 {
  47. query = L.ToString(1)
  48. if query == "" {
  49. query = defaultQuery
  50. }
  51. }
  52. connectionString := defaultConnectionString
  53. if L.GetTop() >= 2 {
  54. connectionString = L.ToString(2)
  55. }
  56. // Check if there is a connection that can be reused
  57. var db *sql.DB
  58. reuseMut.RLock()
  59. conn, ok := reuseDB[connectionString]
  60. reuseMut.RUnlock()
  61. if ok {
  62. // It exists, but is it still alive?
  63. err := conn.Ping()
  64. if err != nil {
  65. // no
  66. // log.Info("did not reuse the connection")
  67. reuseMut.Lock()
  68. delete(reuseDB, connectionString)
  69. reuseMut.Unlock()
  70. } else {
  71. // yes
  72. // log.Info("reused the connection")
  73. db = conn
  74. }
  75. }
  76. // Create a new connection, if needed
  77. var err error
  78. if db == nil {
  79. db, err = sql.Open(driver, connectionString)
  80. if err != nil {
  81. log.Error("Could not connect to database using " + connectionString + ": " + err.Error())
  82. return 0 // No results
  83. }
  84. // Save the connection for later
  85. reuseMut.Lock()
  86. reuseDB[connectionString] = db
  87. reuseMut.Unlock()
  88. }
  89. // log.Info(fmt.Sprintf("%s database: %v (%T)\n",driver, db, db))
  90. reuseMut.Lock()
  91. rows, err := db.Query(query)
  92. reuseMut.Unlock()
  93. if err != nil {
  94. errMsg := err.Error()
  95. if strings.Contains(errMsg, ": connect: connection refused") {
  96. log.Info(driver + " connection string: " + connectionString)
  97. log.Info(driver + " query: " + query)
  98. log.Error("Could not connect to database: " + errMsg)
  99. } else if strings.Contains(errMsg, "missing") && strings.Contains(errMsg, "in connection info string") {
  100. log.Info(driver + " connection string: " + connectionString)
  101. log.Info(driver + " query: " + query)
  102. log.Error(errMsg)
  103. } else {
  104. log.Info(driver + " query: " + query)
  105. log.Error("Query failed: " + errMsg)
  106. }
  107. return 0 // No results
  108. }
  109. if rows == nil {
  110. // Return an empty table
  111. L.Push(L.NewTable())
  112. return 1 // number of results
  113. }
  114. // Return the rows as a table
  115. var (
  116. m map[string]string
  117. maps []map[string]string
  118. values LValueWrappers
  119. )
  120. cols, err := rows.Columns()
  121. for rows.Next() {
  122. values = make(LValueWrappers, len(cols))
  123. err = rows.Scan(values.Interfaces()...)
  124. if err != nil {
  125. log.Error("Failed to scan data: " + err.Error())
  126. break
  127. }
  128. m = make(map[string]string, len(cols))
  129. for i, v := range values {
  130. cname := cols[i]
  131. m[cname] = string(v)
  132. }
  133. maps = append(maps, m)
  134. }
  135. // Convert the strings to a Lua table
  136. table := convert.ArrMaps2table(L, maps)
  137. // Return the table
  138. L.Push(table)
  139. return 1 // number of results
  140. }))
  141. }
  142. func (w LValueWrappers) Interfaces() (s []any) {
  143. s = make([]any, len(w))
  144. for i := range w {
  145. s[i] = &w[i]
  146. }
  147. return
  148. }
  149. /*func (w LValueWrappers) Unwrap() (s []lua.LValue) {
  150. s = make([]lua.LValue, len(w))
  151. for i, v := range w {
  152. s[i] = v
  153. }
  154. return
  155. }*/