A RetroSearch Logo

Home - News ( United States | United Kingdom | Italy | Germany ) - Football scores

Search Query:

Showing content from https://github.com/orgrim/pg_back/commit/3e0e420f2e451a764422a172d57ff20fd1230a3a below:

allow postgresql URIs as connection strings · orgrim/pg_back@3e0e420 · GitHub

@@ -27,15 +27,162 @@ package main

27 27 28 28

import (

29 29

"fmt"

30 +

"net/url"

30 31

"os"

31 32

"sort"

32 33

"strings"

33 34

"unicode"

34 35

)

35 36 36 -

// parseConnInfo parses and converts a key=value connection string of

37 +

const (

38 +

CI_KEYVAL int = iota

39 +

CI_URI

40 +

)

41 + 42 +

type ConnInfo struct {

43 +

Kind int // See CI_* constants

44 +

Infos map[string]string

45 +

}

46 + 47 +

func parseConnInfo(connstring string) (*ConnInfo, error) {

48 +

c := ConnInfo{}

49 + 50 +

if strings.HasPrefix(connstring, "postgresql://") {

51 +

c.Kind = CI_URI

52 +

i, err := parseUrlConnInfo(connstring)

53 +

if err != nil {

54 +

return nil, err

55 +

}

56 +

c.Infos = i

57 +

return &c, nil

58 +

}

59 + 60 +

if strings.Contains(connstring, "=") {

61 +

c.Kind = CI_KEYVAL

62 +

i, err := parseKeywordConnInfo(connstring)

63 +

if err != nil {

64 +

return nil, err

65 +

}

66 +

c.Infos = i

67 +

return &c, nil

68 +

}

69 + 70 +

return nil, fmt.Errorf("parseConnInfo: invalid input connection string")

71 +

}

72 + 73 +

func (c *ConnInfo) String() string {

74 +

switch c.Kind {

75 +

case CI_KEYVAL:

76 +

return makeKeywordConnInfo(c.Infos)

77 +

case CI_URI:

78 +

return makeUrlConnInfo(c.Infos)

79 +

}

80 + 81 +

return ""

82 +

}

83 + 84 +

func (c *ConnInfo) Copy() *ConnInfo {

85 +

newC := ConnInfo{

86 +

Kind: c.Kind,

87 +

Infos: make(map[string]string, len(c.Infos)),

88 +

}

89 + 90 +

for k, v := range c.Infos {

91 +

newC.Infos[k] = v

92 +

}

93 + 94 +

return &newC

95 +

}

96 + 97 +

// Set returns a pointer to a full copy of the conninfo with the key added or

98 +

// the value updated

99 +

func (c *ConnInfo) Set(keyword, value string) *ConnInfo {

100 +

newC := c.Copy()

101 +

newC.Infos[keyword] = value

102 + 103 +

return newC

104 +

}

105 + 106 +

// Del returns a pointer to a full copy of the conninfo with the key removed

107 +

func (c *ConnInfo) Del(keyword string) *ConnInfo {

108 +

newC := c.Copy()

109 +

delete(newC.Infos, keyword)

110 + 111 +

return newC

112 +

}

113 + 114 +

func parseUrlConnInfo(connstring string) (map[string]string, error) {

115 +

u, err := url.Parse(connstring)

116 +

if err != nil {

117 +

return nil, fmt.Errorf("parsing of URI conninfo failed: %w", err)

118 +

}

119 + 120 +

connInfo := make(map[string]string, 0)

121 +

if u.Host != "" {

122 +

fullHosts := strings.Split(u.Host, ",")

123 +

if len(fullHosts) == 1 {

124 +

v := u.Hostname()

125 +

if v != "" {

126 +

connInfo["host"] = v

127 +

}

128 +

v = u.Port()

129 +

if v != "" {

130 +

connInfo["port"] = v

131 +

}

132 +

} else {

133 +

// We need to split and group hosts and ports

134 +

// ourselves, net/url does not handle multiple hosts

135 +

// correctly

136 +

hosts := make([]string, 0)

137 +

ports := make([]string, 0)

138 +

for _, fullHost := range fullHosts {

139 +

hostPort := make([]string, 0)

140 +

if strings.HasPrefix(fullHost, "[") {

141 +

// Handle literal IPv6 addresses

142 +

hostPort = strings.Split(strings.TrimPrefix(fullHost, "["), "]:")

143 +

} else {

144 +

hostPort = strings.Split(fullHost, ":")

145 +

}

146 +

if len(hostPort) == 1 {

147 +

hosts = append(hosts, strings.Trim(hostPort[0], "[]"))

148 +

} else {

149 +

hosts = append(hosts, strings.Trim(hostPort[0], "[]"))

150 +

ports = append(ports, hostPort[1])

151 +

}

152 +

}

153 +

connInfo["host"] = strings.Join(hosts, ",")

154 +

connInfo["port"] = strings.Join(ports, ",")

155 +

}

156 +

}

157 + 158 +

user := u.User.Username()

159 +

if user != "" {

160 +

connInfo["user"] = user

161 +

}

162 + 163 +

password, set := u.User.Password()

164 +

if password != "" && set {

165 +

connInfo["password"] = password

166 +

}

167 + 168 +

dbname := strings.TrimPrefix(u.Path, "/")

169 +

if dbname != "" {

170 +

connInfo["dbname"] = dbname

171 +

}

172 + 173 +

for k, vs := range u.Query() {

174 +

if k == "" {

175 +

continue

176 +

}

177 +

connInfo[k] = strings.Join(vs, ",")

178 +

}

179 + 180 +

return connInfo, nil

181 +

}

182 + 183 +

// parseKeywordConnInfo parses and converts a key=value connection string of

37 184

// PostgreSQL into a map

38 -

func parseConnInfo(connstring string) (map[string]string, error) {

185 +

func parseKeywordConnInfo(connstring string) (map[string]string, error) {

39 186 40 187

// Structure to hold the state of the parsing

41 188

s := struct {

@@ -173,7 +320,7 @@ func parseConnInfo(connstring string) (map[string]string, error) {

173 320

return pairs, nil

174 321

}

175 322 176 -

func makeConnInfo(infos map[string]string) string {

323 +

func makeKeywordConnInfo(infos map[string]string) string {

177 324

conninfo := ""

178 325 179 326

// Map keys are randomized, sort them so that the output is always the

@@ -204,42 +351,148 @@ func makeConnInfo(infos map[string]string) string {

204 351

return conninfo

205 352

}

206 353 207 -

func prepareConnInfo(host string, port int, username string, dbname string) string {

208 -

var conninfo string

354 +

func makeUrlConnInfo(infos map[string]string) string {

355 +

u := &url.URL{

356 +

Scheme: "postgresql",

357 +

}

358 + 359 +

// create user info

360 +

username, hasUser := infos["user"]

361 +

pass, hasPass := infos["password"]

362 + 363 +

var user *url.Userinfo

364 +

if hasPass {

365 +

user = url.UserPassword(username, pass)

366 +

} else if hasUser {

367 +

user = url.User(username)

368 +

}

369 +

u.User = user

370 + 371 +

// Manage host:port list with commas. When the hosts is a unix socket

372 +

// directory, do not set the Host field of the url because it won't be

373 +

// percent encoded, use the query part instead

374 +

if !strings.Contains(infos["host"], "/") {

375 +

hosts := strings.Split(infos["host"], ",")

376 +

ports := strings.Split(infos["port"], ",")

377 + 378 +

// Ensure we have lists of the same size to build host:port in a loop

379 +

if len(hosts) > len(ports) {

380 +

if len(ports) == 1 {

381 +

// same non default port for all hosts, duplicate it

382 +

// for the next loop

383 +

if ports[0] != "" {

384 +

for i := 0; i < len(hosts); i++ {

385 +

ports = append(ports, ports[0])

386 +

}

387 +

}

388 +

} else {

389 +

// fill with empty port to fix the list

390 +

for i := 0; i < len(hosts); i++ {

391 +

ports = append(ports, "")

392 +

}

393 +

}

394 +

}

395 + 396 +

hostnames := make([]string, 0, len(hosts))

209 397 210 -

// dbname may be a connstring. The database name option, usually -d for

211 -

// PostgreSQL binaires accept a connection string. We do a simple check

212 -

// for a = sign. If someone has a database name containing a space, one

213 -

// can still dump it by giving us connstring.

214 -

if strings.Contains(dbname, "=") {

215 -

conninfo = fmt.Sprintf("%v ", dbname)

398 +

for i, host := range hosts {

399 +

// Take care of IPv6 addresses

400 +

if strings.Contains(host, ":") && !strings.HasPrefix(host, "[") {

401 +

host = "[" + host + "]"

402 +

}

403 + 404 +

if ports[i] != "" {

405 +

hostnames = append(hostnames, host+":"+ports[i])

406 +

} else {

407 +

hostnames = append(hostnames, host)

408 +

}

409 +

}

410 + 411 +

u.Host = strings.Join(hostnames, ",")

412 +

}

413 + 414 +

// dbname

415 +

u.Path = "/" + infos["dbname"]

416 +

u.RawPath = "/" + url.PathEscape(infos["dbname"])

417 + 418 +

// compute query

419 +

query := url.Values{}

420 +

needPort := false

421 + 422 +

// Sort keys so that host comes before port and we can add port to the

423 +

// query when we are forced to add host to the query (unix socket

424 +

// directory) in the next loop

425 +

keys := make([]string, 0, len(infos))

426 +

for k := range infos {

427 +

keys = append(keys, k)

428 +

}

429 +

sort.Strings(keys)

430 + 431 +

for _, k := range keys {

432 +

if k == "host" && strings.Contains(infos[k], "/") || k == "port" && needPort {

433 +

needPort = true

434 +

query.Set(k, infos[k])

435 +

continue

436 +

}

437 + 438 +

if k != "host" && k != "port" && k != "user" && k != "password" && k != "dbname" {

439 +

query.Set(k, infos[k])

440 +

}

441 +

}

442 +

u.RawQuery = query.Encode()

443 + 444 +

return u.String()

445 +

}

446 + 447 +

// prepareConnInfo returns a connexion string computed from the input

448 +

// values. When the dbname is already a connection string or a postgresql://

449 +

// URI, it only add the application_name keyword if not set.

450 +

func prepareConnInfo(host string, port int, username string, dbname string) (*ConnInfo, error) {

451 +

var (

452 +

conninfo *ConnInfo

453 +

err error

454 +

)

455 + 456 +

// dbname may be a connstring or a URI. The database name option,

457 +

// usually -d for PostgreSQL binaires accept a connection string and

458 +

// URIs. We do a simple check for a = sign or the postgresql scheme. If

459 +

// someone has a database name containing a space, one can still dump

460 +

// it by giving us connstring.

461 +

if strings.HasPrefix(dbname, "postgresql://") || strings.Contains(dbname, "=") {

462 +

conninfo, err = parseConnInfo(dbname)

463 +

if err != nil {

464 +

return nil, err

465 +

}

216 466

} else {

467 +

conninfo = &ConnInfo{

468 +

Infos: make(map[string]string),

469 +

}

217 470 218 471

if host != "" {

219 -

conninfo += fmt.Sprintf("host=%v ", host)

472 +

conninfo.Infos["host"] = host

220 473

} else {

221 474

// driver lib/pq defaults to localhost for the host, so

222 475

// we have to check PGHOST and fallback to the unix

223 476

// socket directory to avoid overriding PGHOST

224 477

if _, ok := os.LookupEnv("PGHOST"); !ok {

225 -

conninfo += "host=/var/run/postgresql "

478 +

conninfo.Infos["host"] = "/var/run/postgresql"

226 479

}

227 480

}

228 481

if port != 0 {

229 -

conninfo += fmt.Sprintf("port=%v ", port)

482 +

conninfo.Infos["port"] = fmt.Sprintf("%v", port)

230 483

}

231 484

if username != "" {

232 -

conninfo += fmt.Sprintf("user=%v ", username)

485 +

conninfo.Infos["user"] = username

233 486

}

234 487

if dbname != "" {

235 -

conninfo += fmt.Sprintf("dbname=%v ", dbname)

488 +

conninfo.Infos["dbname"] = dbname

236 489

}

237 490

}

238 491 239 -

if !strings.Contains(conninfo, "application_name") {

492 +

if _, ok := conninfo.Infos["application_name"]; !ok {

240 493

l.Verboseln("using pg_back as application_name")

241 -

conninfo += "application_name=pg_back"

494 +

conninfo.Infos["application_name"] = "pg_back"

242 495

}

243 496 244 -

return conninfo

497 +

return conninfo, nil

245 498

}


RetroSearch is an open source project built by @garambo | Open a GitHub Issue

Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo

HTML: 3.2 | Encoding: UTF-8 | Version: 0.7.4