gdb_driver_pgsql.go 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. // Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
  2. //
  3. // This Source Code Form is subject to the terms of the MIT License.
  4. // If a copy of the MIT was not distributed with this file,
  5. // You can obtain one at https://github.com/gogf/gf.
  6. //
  7. // Note:
  8. // 1. It needs manually import: _ "github.com/lib/pq"
  9. // 2. It does not support Save/Replace features.
  10. // 3. It does not support LastInsertId.
  11. package gdb
  12. import (
  13. "context"
  14. "database/sql"
  15. "fmt"
  16. "github.com/gogf/gf/errors/gcode"
  17. "strings"
  18. "github.com/gogf/gf/errors/gerror"
  19. "github.com/gogf/gf/internal/intlog"
  20. "github.com/gogf/gf/text/gstr"
  21. "github.com/gogf/gf/text/gregex"
  22. )
  23. // DriverPgsql is the driver for postgresql database.
  24. type DriverPgsql struct {
  25. *Core
  26. }
  27. // New creates and returns a database object for postgresql.
  28. // It implements the interface of gdb.Driver for extra database driver installation.
  29. func (d *DriverPgsql) New(core *Core, node *ConfigNode) (DB, error) {
  30. return &DriverPgsql{
  31. Core: core,
  32. }, nil
  33. }
  34. // Open creates and returns a underlying sql.DB object for pgsql.
  35. func (d *DriverPgsql) Open(config *ConfigNode) (*sql.DB, error) {
  36. var source string
  37. if config.Link != "" {
  38. source = config.Link
  39. } else {
  40. source = fmt.Sprintf(
  41. "user=%s password=%s host=%s port=%s dbname=%s sslmode=disable",
  42. config.User, config.Pass, config.Host, config.Port, config.Name,
  43. )
  44. if config.Timezone != "" {
  45. source = fmt.Sprintf("%s timezone=%s", source, config.Timezone)
  46. }
  47. }
  48. intlog.Printf(d.GetCtx(), "Open: %s", source)
  49. if db, err := sql.Open("postgres", source); err == nil {
  50. return db, nil
  51. } else {
  52. return nil, err
  53. }
  54. }
  55. // FilteredLink retrieves and returns filtered `linkInfo` that can be using for
  56. // logging or tracing purpose.
  57. func (d *DriverPgsql) FilteredLink() string {
  58. linkInfo := d.GetConfig().Link
  59. if linkInfo == "" {
  60. return ""
  61. }
  62. s, _ := gregex.ReplaceString(
  63. `(.+?)\s*password=(.+)\s*host=(.+)`,
  64. `$1 password=xxx host=$3`,
  65. linkInfo,
  66. )
  67. return s
  68. }
  69. // GetChars returns the security char for this type of database.
  70. func (d *DriverPgsql) GetChars() (charLeft string, charRight string) {
  71. return "\"", "\""
  72. }
  73. // DoCommit deals with the sql string before commits it to underlying sql driver.
  74. func (d *DriverPgsql) DoCommit(ctx context.Context, link Link, sql string, args []interface{}) (newSql string, newArgs []interface{}, err error) {
  75. defer func() {
  76. newSql, newArgs, err = d.Core.DoCommit(ctx, link, newSql, newArgs)
  77. }()
  78. var index int
  79. // Convert place holder char '?' to string "$x".
  80. sql, _ = gregex.ReplaceStringFunc("\\?", sql, func(s string) string {
  81. index++
  82. return fmt.Sprintf("$%d", index)
  83. })
  84. newSql, _ = gregex.ReplaceString(` LIMIT (\d+),\s*(\d+)`, ` LIMIT $2 OFFSET $1`, sql)
  85. return newSql, args, nil
  86. }
  87. // Tables retrieves and returns the tables of current schema.
  88. // It's mainly used in cli tool chain for automatically generating the models.
  89. func (d *DriverPgsql) Tables(ctx context.Context, schema ...string) (tables []string, err error) {
  90. var result Result
  91. link, err := d.SlaveLink(schema...)
  92. if err != nil {
  93. return nil, err
  94. }
  95. query := "SELECT TABLENAME FROM PG_TABLES WHERE SCHEMANAME = 'public' ORDER BY TABLENAME"
  96. if len(schema) > 0 && schema[0] != "" {
  97. query = fmt.Sprintf("SELECT TABLENAME FROM PG_TABLES WHERE SCHEMANAME = '%s' ORDER BY TABLENAME", schema[0])
  98. }
  99. result, err = d.DoGetAll(ctx, link, query)
  100. if err != nil {
  101. return
  102. }
  103. for _, m := range result {
  104. for _, v := range m {
  105. tables = append(tables, v.String())
  106. }
  107. }
  108. return
  109. }
  110. // TableFields retrieves and returns the fields information of specified table of current schema.
  111. //
  112. // Also see DriverMysql.TableFields.
  113. func (d *DriverPgsql) TableFields(ctx context.Context, table string, schema ...string) (fields map[string]*TableField, err error) {
  114. charL, charR := d.GetChars()
  115. table = gstr.Trim(table, charL+charR)
  116. if gstr.Contains(table, " ") {
  117. return nil, gerror.NewCode(gcode.CodeInvalidParameter, "function TableFields supports only single table operations")
  118. }
  119. table, _ = gregex.ReplaceString("\"", "", table)
  120. useSchema := d.db.GetSchema()
  121. if len(schema) > 0 && schema[0] != "" {
  122. useSchema = schema[0]
  123. }
  124. tableFieldsCacheKey := fmt.Sprintf(
  125. `pgsql_table_fields_%s_%s@group:%s`,
  126. table, useSchema, d.GetGroup(),
  127. )
  128. v := tableFieldsMap.GetOrSetFuncLock(tableFieldsCacheKey, func() interface{} {
  129. var (
  130. result Result
  131. link, err = d.SlaveLink(useSchema)
  132. structureSql = fmt.Sprintf(`
  133. SELECT a.attname AS field, t.typname AS type,a.attnotnull as null,
  134. (case when d.contype is not null then 'pri' else '' end) as key
  135. ,ic.column_default as default_value,b.description as comment
  136. ,coalesce(character_maximum_length, numeric_precision, -1) as length
  137. ,numeric_scale as scale
  138. FROM pg_attribute a
  139. left join pg_class c on a.attrelid = c.oid
  140. left join pg_constraint d on d.conrelid = c.oid and a.attnum = d.conkey[1]
  141. left join pg_description b ON a.attrelid=b.objoid AND a.attnum = b.objsubid
  142. left join pg_type t ON a.atttypid = t.oid
  143. left join information_schema.columns ic on ic.column_name = a.attname and ic.table_name = c.relname
  144. WHERE c.relname = '%s' and a.attnum > 0
  145. ORDER BY a.attnum`,
  146. strings.ToLower(table),
  147. )
  148. )
  149. if err != nil {
  150. return nil
  151. }
  152. structureSql, _ = gregex.ReplaceString(`[\n\r\s]+`, " ", gstr.Trim(structureSql))
  153. result, err = d.DoGetAll(ctx, link, structureSql)
  154. if err != nil {
  155. return nil
  156. }
  157. fields = make(map[string]*TableField)
  158. for i, m := range result {
  159. fields[m["field"].String()] = &TableField{
  160. Index: i,
  161. Name: m["field"].String(),
  162. Type: m["type"].String(),
  163. Null: m["null"].Bool(),
  164. Key: m["key"].String(),
  165. Default: m["default_value"].Val(),
  166. Comment: m["comment"].String(),
  167. }
  168. }
  169. return fields
  170. })
  171. if v != nil {
  172. fields = v.(map[string]*TableField)
  173. }
  174. return
  175. }
  176. // DoInsert is not supported in pgsql.
  177. func (d *DriverPgsql) DoInsert(ctx context.Context, link Link, table string, list List, option DoInsertOption) (result sql.Result, err error) {
  178. switch option.InsertOption {
  179. case insertOptionSave:
  180. return nil, gerror.NewCode(gcode.CodeNotSupported, `Save operation is not supported by pgsql driver`)
  181. case insertOptionReplace:
  182. return nil, gerror.NewCode(gcode.CodeNotSupported, `Replace operation is not supported by pgsql driver`)
  183. default:
  184. return d.Core.DoInsert(ctx, link, table, list, option)
  185. }
  186. }