增加websocket支持
This commit is contained in:
7
vendor/github.com/bytedance/sonic/.gitmodules
generated
vendored
7
vendor/github.com/bytedance/sonic/.gitmodules
generated
vendored
@@ -1,6 +1,3 @@
|
||||
[submodule "cloudwego"]
|
||||
[submodule "tools/asm2asm"]
|
||||
path = tools/asm2asm
|
||||
url = https://github.com/cloudwego/asm2asm.git
|
||||
[submodule "tools/simde"]
|
||||
path = tools/simde
|
||||
url = https://github.com/simd-everywhere/simde.git
|
||||
url = https://github.com/chenzhuoyu/asm2asm
|
||||
|
||||
123
vendor/github.com/bytedance/sonic/README.md
generated
vendored
123
vendor/github.com/bytedance/sonic/README.md
generated
vendored
@@ -5,27 +5,18 @@ English | [中文](README_ZH_CN.md)
|
||||
A blazingly fast JSON serializing & deserializing library, accelerated by JIT (just-in-time compiling) and SIMD (single-instruction-multiple-data).
|
||||
|
||||
## Requirement
|
||||
|
||||
- Go 1.16~1.22
|
||||
- Linux / MacOS / Windows(need go1.17 above)
|
||||
- Go 1.15~1.20
|
||||
- Linux/MacOS/Windows
|
||||
- Amd64 ARCH
|
||||
|
||||
## Features
|
||||
|
||||
- Runtime object binding without code generation
|
||||
- Complete APIs for JSON value manipulation
|
||||
- Fast, fast, fast!
|
||||
|
||||
## APIs
|
||||
|
||||
see [go.dev](https://pkg.go.dev/github.com/bytedance/sonic)
|
||||
|
||||
## Benchmarks
|
||||
|
||||
For **all sizes** of json and **all scenarios** of usage, **Sonic performs best**.
|
||||
|
||||
- [Medium](https://github.com/bytedance/sonic/blob/main/decoder/testdata_test.go#L19) (13KB, 300+ key, 6 layers)
|
||||
|
||||
```powershell
|
||||
goversion: 1.17.1
|
||||
goos: darwin
|
||||
@@ -85,21 +76,15 @@ BenchmarkSetOne_Jsoniter-16 79475 ns/op 163.8
|
||||
BenchmarkSetOne_Parallel_Sonic-16 850.9 ns/op 15305.31 MB/s 1584 B/op 17 allocs/op
|
||||
BenchmarkSetOne_Parallel_Sjson-16 18194 ns/op 715.77 MB/s 52247 B/op 9 allocs/op
|
||||
BenchmarkSetOne_Parallel_Jsoniter-16 33560 ns/op 388.05 MB/s 45892 B/op 964 allocs/op
|
||||
BenchmarkLoadNode/LoadAll()-16 11384 ns/op 1143.93 MB/s 6307 B/op 25 allocs/op
|
||||
BenchmarkLoadNode_Parallel/LoadAll()-16 5493 ns/op 2370.68 MB/s 7145 B/op 25 allocs/op
|
||||
BenchmarkLoadNode/Interface()-16 17722 ns/op 734.85 MB/s 13323 B/op 88 allocs/op
|
||||
BenchmarkLoadNode_Parallel/Interface()-16 10330 ns/op 1260.70 MB/s 15178 B/op 88 allocs/op
|
||||
```
|
||||
|
||||
- [Small](https://github.com/bytedance/sonic/blob/main/testdata/small.go) (400B, 11 keys, 3 layers)
|
||||

|
||||
- [Large](https://github.com/bytedance/sonic/blob/main/testdata/twitter.json) (635KB, 10000+ key, 6 layers)
|
||||

|
||||
|
||||
See [bench.sh](https://github.com/bytedance/sonic/blob/main/scripts/bench.sh) for benchmark codes.
|
||||
See [bench.sh](https://github.com/bytedance/sonic/blob/main/bench.sh) for benchmark codes.
|
||||
|
||||
## How it works
|
||||
|
||||
See [INTRODUCTION.md](./docs/INTRODUCTION.md).
|
||||
|
||||
## Usage
|
||||
@@ -107,7 +92,6 @@ See [INTRODUCTION.md](./docs/INTRODUCTION.md).
|
||||
### Marshal/Unmarshal
|
||||
|
||||
Default behaviors are mostly consistent with `encoding/json`, except HTML escaping form (see [Escape HTML](https://github.com/bytedance/sonic/blob/main/README.md#escape-html)) and `SortKeys` feature (optional support see [Sort Keys](https://github.com/bytedance/sonic/blob/main/README.md#sort-keys)) that is **NOT** in conformity to [RFC8259](https://datatracker.ietf.org/doc/html/rfc8259).
|
||||
|
||||
```go
|
||||
import "github.com/bytedance/sonic"
|
||||
|
||||
@@ -119,11 +103,8 @@ err := sonic.Unmarshal(output, &data)
|
||||
```
|
||||
|
||||
### Streaming IO
|
||||
|
||||
Sonic supports decoding json from `io.Reader` or encoding objects into `io.Writer`, aims at handling multiple values as well as reducing memory consumption.
|
||||
|
||||
Sonic supports decoding json from `io.Reader` or encoding objects into `io.`Writer`, aims at handling multiple values as well as reducing memory consumption.
|
||||
- encoder
|
||||
|
||||
```go
|
||||
var o1 = map[string]interface{}{
|
||||
"a": "b",
|
||||
@@ -138,9 +119,7 @@ fmt.Println(w.String())
|
||||
// {"a":"b"}
|
||||
// 1
|
||||
```
|
||||
|
||||
- decoder
|
||||
|
||||
```go
|
||||
var o = map[string]interface{}{}
|
||||
var r = strings.NewReader(`{"a":"b"}{"1":"2"}`)
|
||||
@@ -153,7 +132,6 @@ fmt.Printf("%+v", o)
|
||||
```
|
||||
|
||||
### Use Number/Use Int64
|
||||
|
||||
```go
|
||||
import "github.com/bytedance/sonic/decoder"
|
||||
|
||||
@@ -182,9 +160,7 @@ fm := root.Interface().(float64) // jn == jm
|
||||
```
|
||||
|
||||
### Sort Keys
|
||||
|
||||
On account of the performance loss from sorting (roughly 10%), sonic doesn't enable this feature by default. If your component depends on it to work (like [zstd](https://github.com/facebook/zstd)), Use it like this:
|
||||
|
||||
```go
|
||||
import "github.com/bytedance/sonic"
|
||||
import "github.com/bytedance/sonic/encoder"
|
||||
@@ -197,26 +173,19 @@ v, err := encoder.Encode(m, encoder.SortMapKeys)
|
||||
var root := sonic.Get(JSON)
|
||||
err := root.SortKeys()
|
||||
```
|
||||
|
||||
### Escape HTML
|
||||
|
||||
On account of the performance loss (roughly 15%), sonic doesn't enable this feature by default. You can use `encoder.EscapeHTML` option to open this feature (align with `encoding/json.HTMLEscape`).
|
||||
|
||||
```go
|
||||
import "github.com/bytedance/sonic"
|
||||
|
||||
v := map[string]string{"&&":"<>"}
|
||||
ret, err := Encode(v, EscapeHTML) // ret == `{"\u0026\u0026":{"X":"\u003c\u003e"}}`
|
||||
```
|
||||
|
||||
### Compact Format
|
||||
|
||||
Sonic encodes primitive objects (struct/map...) as compact-format JSON by default, except marshaling `json.RawMessage` or `json.Marshaler`: sonic ensures validating their output JSON but **DONOT** compacting them for performance concerns. We provide the option `encoder.CompactMarshaler` to add compacting process.
|
||||
|
||||
### Print Error
|
||||
|
||||
If there invalid syntax in input JSON, sonic will return `decoder.SyntaxError`, which supports pretty-printing of error position
|
||||
|
||||
```go
|
||||
import "github.com/bytedance/sonic"
|
||||
import "github.com/bytedance/sonic/decoder"
|
||||
@@ -242,9 +211,7 @@ if err != nil {
|
||||
```
|
||||
|
||||
#### Mismatched Types [Sonic v1.6.0]
|
||||
|
||||
If there a **mismatch-typed** value for a given key, sonic will report `decoder.MismatchTypeError` (if there are many, report the last one), but still skip wrong the value and keep decoding next JSON.
|
||||
|
||||
```go
|
||||
import "github.com/bytedance/sonic"
|
||||
import "github.com/bytedance/sonic/decoder"
|
||||
@@ -257,15 +224,10 @@ err := UnmarshalString(`{"A":"1","B":1}`, &data)
|
||||
println(err.Error()) // Mismatch type int with value string "at index 5: mismatched type with value\n\n\t{\"A\":\"1\",\"B\":1}\n\t.....^.........\n"
|
||||
fmt.Printf("%+v", data) // {A:0 B:1}
|
||||
```
|
||||
|
||||
### Ast.Node
|
||||
|
||||
Sonic/ast.Node is a completely self-contained AST for JSON. It implements serialization and deserialization both and provides robust APIs for obtaining and modification of generic data.
|
||||
|
||||
#### Get/Index
|
||||
|
||||
Search partial JSON by given paths, which must be non-negative integer or string, or nil
|
||||
|
||||
```go
|
||||
import "github.com/bytedance/sonic"
|
||||
|
||||
@@ -279,13 +241,10 @@ raw := root.Raw() // == string(input)
|
||||
root, err := sonic.Get(input, "key1", 1, "key2")
|
||||
sub := root.Get("key3").Index(2).Int64() // == 3
|
||||
```
|
||||
|
||||
**Tip**: since `Index()` uses offset to locate data, which is much faster than scanning like `Get()`, we suggest you use it as much as possible. And sonic also provides another API `IndexOrGet()` to underlying use offset as well as ensure the key is matched.
|
||||
|
||||
#### Set/Unset
|
||||
|
||||
Modify the json content by Set()/Unset()
|
||||
|
||||
```go
|
||||
import "github.com/bytedance/sonic"
|
||||
|
||||
@@ -302,9 +261,7 @@ println(root.Get("key4").Check()) // "value not exist"
|
||||
```
|
||||
|
||||
#### Serialize
|
||||
|
||||
To encode `ast.Node` as json, use `MarshalJson()` or `json.Marshal()` (MUST pass the node's pointer)
|
||||
|
||||
```go
|
||||
import (
|
||||
"encoding/json"
|
||||
@@ -318,7 +275,6 @@ println(string(buf) == string(exp)) // true
|
||||
```
|
||||
|
||||
#### APIs
|
||||
|
||||
- validation: `Check()`, `Error()`, `Valid()`, `Exist()`
|
||||
- searching: `Index()`, `Get()`, `IndexPair()`, `IndexOrGet()`, `GetByPath()`
|
||||
- go-type casting: `Int64()`, `Float64()`, `String()`, `Number()`, `Bool()`, `Map[UseNumber|UseNode]()`, `Array[UseNumber|UseNode]()`, `Interface[UseNumber|UseNode]()`
|
||||
@@ -326,55 +282,13 @@ println(string(buf) == string(exp)) // true
|
||||
- iteration: `Values()`, `Properties()`, `ForEach()`, `SortKeys()`
|
||||
- modification: `Set()`, `SetByIndex()`, `Add()`
|
||||
|
||||
### Ast.Visitor
|
||||
|
||||
Sonic provides an advanced API for fully parsing JSON into non-standard types (neither `struct` not `map[string]interface{}`) without using any intermediate representation (`ast.Node` or `interface{}`). For example, you might have the following types which are like `interface{}` but actually not `interface{}`:
|
||||
|
||||
```go
|
||||
type UserNode interface {}
|
||||
|
||||
// the following types implement the UserNode interface.
|
||||
type (
|
||||
UserNull struct{}
|
||||
UserBool struct{ Value bool }
|
||||
UserInt64 struct{ Value int64 }
|
||||
UserFloat64 struct{ Value float64 }
|
||||
UserString struct{ Value string }
|
||||
UserObject struct{ Value map[string]UserNode }
|
||||
UserArray struct{ Value []UserNode }
|
||||
)
|
||||
```
|
||||
|
||||
Sonic provides the following API to return **the preorder traversal of a JSON AST**. The `ast.Visitor` is a SAX style interface which is used in some C++ JSON library. You should implement `ast.Visitor` by yourself and pass it to `ast.Preorder()` method. In your visitor you can make your custom types to represent JSON values. There may be an O(n) space container (such as stack) in your visitor to record the object / array hierarchy.
|
||||
|
||||
```go
|
||||
func Preorder(str string, visitor Visitor, opts *VisitorOptions) error
|
||||
|
||||
type Visitor interface {
|
||||
OnNull() error
|
||||
OnBool(v bool) error
|
||||
OnString(v string) error
|
||||
OnInt64(v int64, n json.Number) error
|
||||
OnFloat64(v float64, n json.Number) error
|
||||
OnObjectBegin(capacity int) error
|
||||
OnObjectKey(key string) error
|
||||
OnObjectEnd() error
|
||||
OnArrayBegin(capacity int) error
|
||||
OnArrayEnd() error
|
||||
}
|
||||
```
|
||||
|
||||
See [ast/visitor.go](https://github.com/bytedance/sonic/blob/main/ast/visitor.go) for detailed usage. We also implement a demo visitor for `UserNode` in [ast/visitor_test.go](https://github.com/bytedance/sonic/blob/main/ast/visitor_test.go).
|
||||
|
||||
## Compatibility
|
||||
|
||||
Sonic **DOES NOT** ensure to support all environments, due to the difficulty of developing high-performance codes. For developers who use sonic to build their applications in different environments, we have the following suggestions:
|
||||
|
||||
- Developing on **Mac M1**: Make sure you have Rosetta 2 installed on your machine, and set `GOARCH=amd64` when building your application. Rosetta 2 can automatically translate x86 binaries to arm64 binaries and run x86 applications on Mac M1.
|
||||
- Developing on **Linux arm64**: You can install qemu and use the `qemu-x86_64 -cpu max` command to convert x86 binaries to amr64 binaries for applications built with sonic. The qemu can achieve a similar transfer effect to Rosetta 2 on Mac M1.
|
||||
|
||||
For developers who want to use sonic on Linux arm64 without qemu, or those who want to handle JSON strictly consistent with `encoding/json`, we provide some compatible APIs as `sonic.API`
|
||||
|
||||
- `ConfigDefault`: the sonic's default config (`EscapeHTML=false`,`SortKeys=false`...) to run on sonic-supporting environment. It will fall back to `encoding/json` with the corresponding config, and some options like `SortKeys=false` will be invalid.
|
||||
- `ConfigStd`: the std-compatible config (`EscapeHTML=true`,`SortKeys=true`...) to run on sonic-supporting environment. It will fall back to `encoding/json`.
|
||||
- `ConfigFastest`: the fastest config (`NoQuoteTextMarshaler=true`) to run on sonic-supporting environment. It will fall back to `encoding/json` with the corresponding config, and some options will be invalid.
|
||||
@@ -382,9 +296,7 @@ For developers who want to use sonic on Linux arm64 without qemu, or those who w
|
||||
## Tips
|
||||
|
||||
### Pretouch
|
||||
|
||||
Since Sonic uses [golang-asm](https://github.com/twitchyliquid64/golang-asm) as a JIT assembler, which is NOT very suitable for runtime compiling, first-hit running of a huge schema may cause request-timeout or even process-OOM. For better stability, we advise **using `Pretouch()` for huge-schema or compact-memory applications** before `Marshal()/Unmarshal()`.
|
||||
|
||||
```go
|
||||
import (
|
||||
"reflect"
|
||||
@@ -399,7 +311,7 @@ func init() {
|
||||
err := sonic.Pretouch(reflect.TypeOf(v))
|
||||
|
||||
// with more CompileOption...
|
||||
err := sonic.Pretouch(reflect.TypeOf(v),
|
||||
err := sonic.Pretouch(reflect.TypeOf(v),
|
||||
// If the type is too deep nesting (nesting depth > option.DefaultMaxInlineDepth),
|
||||
// you can set compile recursive loops in Pretouch for better stability in JIT.
|
||||
option.WithCompileRecursiveDepth(loop),
|
||||
@@ -410,23 +322,17 @@ func init() {
|
||||
```
|
||||
|
||||
### Copy string
|
||||
|
||||
When decoding **string values without any escaped characters**, sonic references them from the origin JSON buffer instead of mallocing a new buffer to copy. This helps a lot for CPU performance but may leave the whole JSON buffer in memory as long as the decoded objects are being used. In practice, we found the extra memory introduced by referring JSON buffer is usually 20% ~ 80% of decoded objects. Once an application holds these objects for a long time (for example, cache the decoded objects for reusing), its in-use memory on the server may go up. - `Config.CopyString`/`decoder.CopyString()`: We provide the option for `Decode()` / `Unmarshal()` users to choose not to reference the JSON buffer, which may cause a decline in CPU performance to some degree.
|
||||
|
||||
- `GetFromStringNoCopy()`: For memory safety, `sonic.Get()` / `sonic.GetFromString()` now copies return JSON. If users want to get json more quickly and not care about memory usage, you can use `GetFromStringNoCopy()` to return a JSON directly referenced from source.
|
||||
When decoding **string values without any escaped characters**, sonic references them from the origin JSON buffer instead of mallocing a new buffer to copy. This helps a lot for CPU performance but may leave the whole JSON buffer in memory as long as the decoded objects are being used. In practice, we found the extra memory introduced by referring JSON buffer is usually 20% ~ 80% of decoded objects. Once an application holds these objects for a long time (for example, cache the decoded objects for reusing), its in-use memory on the server may go up. We provide the option `decoder.CopyString()` for users to choose not to reference the JSON buffer, which may cause a decline in CPU performance to some degree.
|
||||
|
||||
### Pass string or []byte?
|
||||
|
||||
For alignment to `encoding/json`, we provide API to pass `[]byte` as an argument, but the string-to-bytes copy is conducted at the same time considering safety, which may lose performance when the origin JSON is huge. Therefore, you can use `UnmarshalString()` and `GetFromString()` to pass a string, as long as your origin data is a string or **nocopy-cast** is safe for your []byte. We also provide API `MarshalString()` for convenient **nocopy-cast** of encoded JSON []byte, which is safe since sonic's output bytes is always duplicated and unique.
|
||||
|
||||
### Accelerate `encoding.TextMarshaler`
|
||||
|
||||
To ensure data security, sonic.Encoder quotes and escapes string values from `encoding.TextMarshaler` interfaces by default, which may degrade performance much if most of your data is in form of them. We provide `encoder.NoQuoteTextMarshaler` to skip these operations, which means you **MUST** ensure their output string escaped and quoted following [RFC8259](https://datatracker.ietf.org/doc/html/rfc8259).
|
||||
|
||||
|
||||
### Better performance for generic data
|
||||
|
||||
In **fully-parsed** scenario, `Unmarshal()` performs better than `Get()`+`Node.Interface()`. But if you only have a part of the schema for specific json, you can combine `Get()` and `Unmarshal()` together:
|
||||
|
||||
```go
|
||||
import "github.com/bytedance/sonic"
|
||||
|
||||
@@ -434,9 +340,7 @@ node, err := sonic.GetFromString(_TwitterJson, "statuses", 3, "user")
|
||||
var user User // your partial schema...
|
||||
err = sonic.UnmarshalString(node.Raw(), &user)
|
||||
```
|
||||
|
||||
Even if you don't have any schema, use `ast.Node` as the container of generic values instead of `map` or `interface`:
|
||||
|
||||
```go
|
||||
import "github.com/bytedance/sonic"
|
||||
|
||||
@@ -447,25 +351,12 @@ err = user.Check()
|
||||
// err = user.LoadAll() // only call this when you want to use 'user' concurrently...
|
||||
go someFunc(user)
|
||||
```
|
||||
|
||||
Why? Because `ast.Node` stores its children using `array`:
|
||||
|
||||
- `Array`'s performance is **much better** than `Map` when Inserting (Deserialize) and Scanning (Serialize) data;
|
||||
- **Hashing** (`map[x]`) is not as efficient as **Indexing** (`array[x]`), which `ast.Node` can conduct on **both array and object**;
|
||||
- Using `Interface()`/`Map()` means Sonic must parse all the underlying values, while `ast.Node` can parse them **on demand**.
|
||||
|
||||
**CAUTION:** `ast.Node` **DOESN'T** ensure concurrent security directly, due to its **lazy-load** design. However, you can call `Node.Load()`/`Node.LoadAll()` to achieve that, which may bring performance reduction while it still works faster than converting to `map` or `interface{}`
|
||||
|
||||
### Ast.Node or Ast.Visitor?
|
||||
|
||||
For generic data, `ast.Node` should be enough for your needs in most cases.
|
||||
|
||||
However, `ast.Node` is designed for partially processing JSON string. It has some special designs such as lazy-load which might not be suitable for directly parsing the whole JSON string like `Unmarshal()`. Although `ast.Node` is better then `map` or `interface{}`, it's also a kind of intermediate representation after all if your final types are customized and you have to convert the above types to your custom types after parsing.
|
||||
|
||||
For better performance, in previous case the `ast.Visitor` will be the better choice. It performs JSON decoding like `Unmarshal()` and you can directly use your final types to represents a JSON AST without any intermediate representations.
|
||||
|
||||
But `ast.Visitor` is not a very handy API. You might need to write a lot of code to implement your visitor and carefully maintain the tree hierarchy during decoding. Please read the comments in [ast/visitor.go](https://github.com/bytedance/sonic/blob/main/ast/visitor.go) carefully if you decide to use this API.
|
||||
|
||||
## Community
|
||||
|
||||
Sonic is a subproject of [CloudWeGo](https://www.cloudwego.io/). We are committed to building a cloud native ecosystem.
|
||||
|
||||
99
vendor/github.com/bytedance/sonic/README_ZH_CN.md
generated
vendored
99
vendor/github.com/bytedance/sonic/README_ZH_CN.md
generated
vendored
@@ -6,14 +6,10 @@
|
||||
|
||||
## 依赖
|
||||
|
||||
- Go 1.16~1.22
|
||||
- Linux / MacOS / Windows(需要 Go1.17 以上)
|
||||
- Go 1.15~1.20
|
||||
- Linux/MacOS/Windows
|
||||
- Amd64 架构
|
||||
|
||||
## 接口
|
||||
|
||||
详见 [go.dev](https://pkg.go.dev/github.com/bytedance/sonic)
|
||||
|
||||
## 特色
|
||||
|
||||
- 运行时对象绑定,无需代码生成
|
||||
@@ -23,9 +19,7 @@
|
||||
## 基准测试
|
||||
|
||||
对于**所有大小**的 json 和**所有使用场景**, **Sonic 表现均为最佳**。
|
||||
|
||||
- [中型](https://github.com/bytedance/sonic/blob/main/decoder/testdata_test.go#L19) (13kB, 300+ 键, 6 层)
|
||||
|
||||
```powershell
|
||||
goversion: 1.17.1
|
||||
goos: darwin
|
||||
@@ -85,18 +79,13 @@ BenchmarkSetOne_Jsoniter-16 79475 ns/op 163.8
|
||||
BenchmarkSetOne_Parallel_Sonic-16 850.9 ns/op 15305.31 MB/s 1584 B/op 17 allocs/op
|
||||
BenchmarkSetOne_Parallel_Sjson-16 18194 ns/op 715.77 MB/s 52247 B/op 9 allocs/op
|
||||
BenchmarkSetOne_Parallel_Jsoniter-16 33560 ns/op 388.05 MB/s 45892 B/op 964 allocs/op
|
||||
BenchmarkLoadNode/LoadAll()-16 11384 ns/op 1143.93 MB/s 6307 B/op 25 allocs/op
|
||||
BenchmarkLoadNode_Parallel/LoadAll()-16 5493 ns/op 2370.68 MB/s 7145 B/op 25 allocs/op
|
||||
BenchmarkLoadNode/Interface()-16 17722 ns/op 734.85 MB/s 13323 B/op 88 allocs/op
|
||||
BenchmarkLoadNode_Parallel/Interface()-16 10330 ns/op 1260.70 MB/s 15178 B/op 88 allocs/op
|
||||
```
|
||||
|
||||
- [小型](https://github.com/bytedance/sonic/blob/main/testdata/small.go) (400B, 11 个键, 3 层)
|
||||

|
||||
- [大型](https://github.com/bytedance/sonic/blob/main/testdata/twitter.json) (635kB, 10000+ 个键, 6 层)
|
||||

|
||||
|
||||
要查看基准测试代码,请参阅 [bench.sh](https://github.com/bytedance/sonic/blob/main/scripts/bench.sh) 。
|
||||
要查看基准测试代码,请参阅 [bench.sh](https://github.com/bytedance/sonic/blob/main/bench.sh) 。
|
||||
|
||||
## 工作原理
|
||||
|
||||
@@ -107,7 +96,6 @@ BenchmarkLoadNode_Parallel/Interface()-16 10330 ns/op 1260.7
|
||||
### 序列化/反序列化
|
||||
|
||||
默认的行为基本上与 `encoding/json` 相一致,除了 HTML 转义形式(参见 [Escape HTML](https://github.com/bytedance/sonic/blob/main/README.md#escape-html)) 和 `SortKeys` 功能(参见 [Sort Keys](https://github.com/bytedance/sonic/blob/main/README.md#sort-keys))**没有**遵循 [RFC8259](https://datatracker.ietf.org/doc/html/rfc8259) 。
|
||||
|
||||
```go
|
||||
import "github.com/bytedance/sonic"
|
||||
|
||||
@@ -121,9 +109,7 @@ err := sonic.Unmarshal(output, &data)
|
||||
### 流式输入输出
|
||||
|
||||
Sonic 支持解码 `io.Reader` 中输入的 json,或将对象编码为 json 后输出至 `io.Writer`,以处理多个值并减少内存消耗。
|
||||
|
||||
- 编码器
|
||||
|
||||
```go
|
||||
var o1 = map[string]interface{}{
|
||||
"a": "b",
|
||||
@@ -138,9 +124,7 @@ fmt.Println(w.String())
|
||||
// {"a":"b"}
|
||||
// 1
|
||||
```
|
||||
|
||||
- 解码器
|
||||
|
||||
```go
|
||||
var o = map[string]interface{}{}
|
||||
var r = strings.NewReader(`{"a":"b"}{"1":"2"}`)
|
||||
@@ -184,7 +168,6 @@ fm := root.Interface().(float64) // jn == jm
|
||||
### 对键排序
|
||||
|
||||
考虑到排序带来的性能损失(约 10% ), sonic 默认不会启用这个功能。如果你的组件依赖这个行为(如 [zstd](https://github.com/facebook/zstd)) ,可以仿照下面的例子:
|
||||
|
||||
```go
|
||||
import "github.com/bytedance/sonic"
|
||||
import "github.com/bytedance/sonic/encoder"
|
||||
@@ -201,7 +184,6 @@ err := root.SortKeys()
|
||||
### HTML 转义
|
||||
|
||||
考虑到性能损失(约15%), sonic 默认不会启用这个功能。你可以使用 `encoder.EscapeHTML` 选项来开启(与 `encoding/json.HTMLEscape` 行为一致)。
|
||||
|
||||
```go
|
||||
import "github.com/bytedance/sonic"
|
||||
|
||||
@@ -210,13 +192,11 @@ ret, err := Encode(v, EscapeHTML) // ret == `{"\u0026\u0026":{"X":"\u003c\u003e"
|
||||
```
|
||||
|
||||
### 紧凑格式
|
||||
|
||||
Sonic 默认将基本类型( `struct` , `map` 等)编码为紧凑格式的 JSON ,除非使用 `json.RawMessage` or `json.Marshaler` 进行编码: sonic 确保输出的 JSON 合法,但出于性能考虑,**不会**加工成紧凑格式。我们提供选项 `encoder.CompactMarshaler` 来添加此过程,
|
||||
|
||||
### 打印错误
|
||||
|
||||
如果输入的 JSON 存在无效的语法,sonic 将返回 `decoder.SyntaxError`,该错误支持错误位置的美化输出。
|
||||
|
||||
```go
|
||||
import "github.com/bytedance/sonic"
|
||||
import "github.com/bytedance/sonic/decoder"
|
||||
@@ -244,7 +224,6 @@ if err != nil {
|
||||
#### 类型不匹配 [Sonic v1.6.0]
|
||||
|
||||
如果给定键中存在**类型不匹配**的值, sonic 会抛出 `decoder.MismatchTypeError` (如果有多个,只会报告最后一个),但仍会跳过错误的值并解码下一个 JSON 。
|
||||
|
||||
```go
|
||||
import "github.com/bytedance/sonic"
|
||||
import "github.com/bytedance/sonic/decoder"
|
||||
@@ -257,7 +236,6 @@ err := UnmarshalString(`{"A":"1","B":1}`, &data)
|
||||
println(err.Error()) // Mismatch type int with value string "at index 5: mismatched type with value\n\n\t{\"A\":\"1\",\"B\":1}\n\t.....^.........\n"
|
||||
fmt.Printf("%+v", data) // {A:0 B:1}
|
||||
```
|
||||
|
||||
### `Ast.Node`
|
||||
|
||||
Sonic/ast.Node 是完全独立的 JSON 抽象语法树库。它实现了序列化和反序列化,并提供了获取和修改通用数据的鲁棒的 API。
|
||||
@@ -265,7 +243,6 @@ Sonic/ast.Node 是完全独立的 JSON 抽象语法树库。它实现了序列
|
||||
#### 查找/索引
|
||||
|
||||
通过给定的路径搜索 JSON 片段,路径必须为非负整数,字符串或 `nil` 。
|
||||
|
||||
```go
|
||||
import "github.com/bytedance/sonic"
|
||||
|
||||
@@ -279,13 +256,11 @@ raw := root.Raw() // == string(input)
|
||||
root, err := sonic.Get(input, "key1", 1, "key2")
|
||||
sub := root.Get("key3").Index(2).Int64() // == 3
|
||||
```
|
||||
|
||||
**注意**:由于 `Index()` 使用偏移量来定位数据,比使用扫描的 `Get()` 要快的多,建议尽可能的使用 `Index` 。 Sonic 也提供了另一个 API, `IndexOrGet()` ,以偏移量为基础并且也确保键的匹配。
|
||||
|
||||
#### 修改
|
||||
|
||||
使用 `Set()` / `Unset()` 修改 json 的内容
|
||||
|
||||
使用 ` Set()` / `Unset()` 修改 json 的内容
|
||||
```go
|
||||
import "github.com/bytedance/sonic"
|
||||
|
||||
@@ -302,9 +277,7 @@ println(root.Get("key4").Check()) // "value not exist"
|
||||
```
|
||||
|
||||
#### 序列化
|
||||
|
||||
要将 `ast.Node` 编码为 json ,使用 `MarshalJson()` 或者 `json.Marshal()` (必须传递指向节点的指针)
|
||||
|
||||
```go
|
||||
import (
|
||||
"encoding/json"
|
||||
@@ -318,7 +291,6 @@ println(string(buf) == string(exp)) // true
|
||||
```
|
||||
|
||||
#### APIs
|
||||
|
||||
- 合法性检查: `Check()`, `Error()`, `Valid()`, `Exist()`
|
||||
- 索引: `Index()`, `Get()`, `IndexPair()`, `IndexOrGet()`, `GetByPath()`
|
||||
- 转换至 go 内置类型: `Int64()`, `Float64()`, `String()`, `Number()`, `Bool()`, `Map[UseNumber|UseNode]()`, `Array[UseNumber|UseNode]()`, `Interface[UseNumber|UseNode]()`
|
||||
@@ -326,55 +298,13 @@ println(string(buf) == string(exp)) // true
|
||||
- 迭代: `Values()`, `Properties()`, `ForEach()`, `SortKeys()`
|
||||
- 修改: `Set()`, `SetByIndex()`, `Add()`
|
||||
|
||||
### `Ast.Visitor`
|
||||
|
||||
Sonic 提供了一个高级的 API 用于直接全量解析 JSON 到非标准容器里 (既不是 `struct` 也不是 `map[string]interface{}`) 且不需要借助任何中间表示 (`ast.Node` 或 `interface{}`)。举个例子,你可能定义了下述的类型,它们看起来像 `interface{}`,但实际上并不是:
|
||||
|
||||
```go
|
||||
type UserNode interface {}
|
||||
|
||||
// the following types implement the UserNode interface.
|
||||
type (
|
||||
UserNull struct{}
|
||||
UserBool struct{ Value bool }
|
||||
UserInt64 struct{ Value int64 }
|
||||
UserFloat64 struct{ Value float64 }
|
||||
UserString struct{ Value string }
|
||||
UserObject struct{ Value map[string]UserNode }
|
||||
UserArray struct{ Value []UserNode }
|
||||
)
|
||||
```
|
||||
|
||||
Sonic 提供了下述的 API 来返回 **“对 JSON AST 的前序遍历”**。`ast.Visitor` 是一个 SAX 风格的接口,这在某些 C++ 的 JSON 解析库中被使用到。你需要自己实现一个 `ast.Visitor`,将它传递给 `ast.Preorder()` 方法。在你的实现中你可以使用自定义的类型来表示 JSON 的值。在你的 `ast.Visitor` 中,可能需要有一个 O(n) 空间复杂度的容器(比如说栈)来记录 object / array 的层级。
|
||||
|
||||
```go
|
||||
func Preorder(str string, visitor Visitor, opts *VisitorOptions) error
|
||||
|
||||
type Visitor interface {
|
||||
OnNull() error
|
||||
OnBool(v bool) error
|
||||
OnString(v string) error
|
||||
OnInt64(v int64, n json.Number) error
|
||||
OnFloat64(v float64, n json.Number) error
|
||||
OnObjectBegin(capacity int) error
|
||||
OnObjectKey(key string) error
|
||||
OnObjectEnd() error
|
||||
OnArrayBegin(capacity int) error
|
||||
OnArrayEnd() error
|
||||
}
|
||||
```
|
||||
|
||||
详细用法参看 [ast/visitor.go](https://github.com/bytedance/sonic/blob/main/ast/visitor.go),我们还为 `UserNode` 实现了一个示例 `ast.Visitor`,你可以在 [ast/visitor_test.go](https://github.com/bytedance/sonic/blob/main/ast/visitor_test.go) 中找到它。
|
||||
|
||||
## 兼容性
|
||||
|
||||
由于开发高性能代码的困难性, Sonic **不**保证对所有环境的支持。对于在不同环境中使用 Sonic 构建应用程序的开发者,我们有以下建议:
|
||||
|
||||
- 在 **Mac M1** 上开发:确保在您的计算机上安装了 Rosetta 2,并在构建时设置 `GOARCH=amd64` 。 Rosetta 2 可以自动将 x86 二进制文件转换为 arm64 二进制文件,并在 Mac M1 上运行 x86 应用程序。
|
||||
- 在 **Linux arm64** 上开发:您可以安装 qemu 并使用 `qemu-x86_64 -cpu max` 命令来将 x86 二进制文件转换为 arm64 二进制文件。qemu可以实现与Mac M1上的Rosetta 2类似的转换效果。
|
||||
|
||||
对于希望在不使用 qemu 下使用 sonic 的开发者,或者希望处理 JSON 时与 `encoding/JSON` 严格保持一致的开发者,我们在 `sonic.API` 中提供了一些兼容性 API
|
||||
|
||||
- `ConfigDefault`: 在支持 sonic 的环境下 sonic 的默认配置(`EscapeHTML=false`,`SortKeys=false`等)。行为与具有相应配置的 `encoding/json` 一致,一些选项,如 `SortKeys=false` 将无效。
|
||||
- `ConfigStd`: 在支持 sonic 的环境下与标准库兼容的配置(`EscapeHTML=true`,`SortKeys=true`等)。行为与 `encoding/json` 一致。
|
||||
- `ConfigFastest`: 在支持 sonic 的环境下运行最快的配置(`NoQuoteTextMarshaler=true`)。行为与具有相应配置的 `encoding/json` 一致,某些选项将无效。
|
||||
@@ -382,9 +312,7 @@ type Visitor interface {
|
||||
## 注意事项
|
||||
|
||||
### 预热
|
||||
|
||||
由于 Sonic 使用 [golang-asm](https://github.com/twitchyliquid64/golang-asm) 作为 JIT 汇编器,这个库并不适用于运行时编译,第一次运行一个大型模式可能会导致请求超时甚至进程内存溢出。为了更好地稳定性,我们建议在运行大型模式或在内存有限的应用中,在使用 `Marshal()/Unmarshal()` 前运行 `Pretouch()`。
|
||||
|
||||
```go
|
||||
import (
|
||||
"reflect"
|
||||
@@ -399,7 +327,7 @@ func init() {
|
||||
err := sonic.Pretouch(reflect.TypeOf(v))
|
||||
|
||||
// with more CompileOption...
|
||||
err := sonic.Pretouch(reflect.TypeOf(v),
|
||||
err := sonic.Pretouch(reflect.TypeOf(v),
|
||||
// If the type is too deep nesting (nesting depth > option.DefaultMaxInlineDepth),
|
||||
// you can set compile recursive loops in Pretouch for better stability in JIT.
|
||||
option.WithCompileRecursiveDepth(loop),
|
||||
@@ -414,17 +342,16 @@ func init() {
|
||||
当解码 **没有转义字符的字符串**时, sonic 会从原始的 JSON 缓冲区内引用而不是复制到新的一个缓冲区中。这对 CPU 的性能方面很有帮助,但是可能因此在解码后对象仍在使用的时候将整个 JSON 缓冲区保留在内存中。实践中我们发现,通过引用 JSON 缓冲区引入的额外内存通常是解码后对象的 20% 至 80% ,一旦应用长期保留这些对象(如缓存以备重用),服务器所使用的内存可能会增加。我们提供了选项 `decoder.CopyString()` 供用户选择,不引用 JSON 缓冲区。这可能在一定程度上降低 CPU 性能。
|
||||
|
||||
### 传递字符串还是字节数组?
|
||||
|
||||
为了和 `encoding/json` 保持一致,我们提供了传递 `[]byte` 作为参数的 API ,但考虑到安全性,字符串到字节的复制是同时进行的,这在原始 JSON 非常大时可能会导致性能损失。因此,你可以使用 `UnmarshalString()` 和 `GetFromString()` 来传递字符串,只要你的原始数据是字符串,或**零拷贝类型转换**对于你的字节数组是安全的。我们也提供了 `MarshalString()` 的 API ,以便对编码的 JSON 字节数组进行**零拷贝类型转换**,因为 sonic 输出的字节始终是重复并且唯一的,所以这样是安全的。
|
||||
|
||||
### 加速 `encoding.TextMarshaler`
|
||||
|
||||
为了保证数据安全性, `sonic.Encoder` 默认会对来自 `encoding.TextMarshaler` 接口的字符串进行引用和转义,如果大部分数据都是这种形式那可能会导致很大的性能损失。我们提供了 `encoder.NoQuoteTextMarshaler` 选项来跳过这些操作,但你**必须**保证他们的输出字符串依照 [RFC8259](https://datatracker.ietf.org/doc/html/rfc8259) 进行了转义和引用。
|
||||
|
||||
|
||||
### 泛型的性能优化
|
||||
|
||||
在 **完全解析**的场景下, `Unmarshal()` 表现得比 `Get()`+`Node.Interface()` 更好。但是如果你只有特定 JSON 的部分模式,你可以将 `Get()` 和 `Unmarshal()` 结合使用:
|
||||
|
||||
```go
|
||||
import "github.com/bytedance/sonic"
|
||||
|
||||
@@ -432,9 +359,7 @@ node, err := sonic.GetFromString(_TwitterJson, "statuses", 3, "user")
|
||||
var user User // your partial schema...
|
||||
err = sonic.UnmarshalString(node.Raw(), &user)
|
||||
```
|
||||
|
||||
甚至如果你没有任何模式,可以用 `ast.Node` 代替 `map` 或 `interface` 作为泛型的容器:
|
||||
|
||||
```go
|
||||
import "github.com/bytedance/sonic"
|
||||
|
||||
@@ -445,25 +370,13 @@ err = user.Check()
|
||||
// err = user.LoadAll() // only call this when you want to use 'user' concurrently...
|
||||
go someFunc(user)
|
||||
```
|
||||
|
||||
为什么?因为 `ast.Node` 使用 `array` 来存储其子节点:
|
||||
|
||||
- 在插入(反序列化)和扫描(序列化)数据时,`Array` 的性能比 `Map` **好得多**;
|
||||
- **哈希**(`map[x]`)的效率不如**索引**(`array[x]`)高效,而 `ast.Node` 可以在数组和对象上使用索引;
|
||||
- 使用 `Interface()` / `Map()` 意味着 sonic 必须解析所有的底层值,而 `ast.Node` 可以**按需解析**它们。
|
||||
|
||||
**注意**:由于 `ast.Node` 的惰性加载设计,其**不能**直接保证并发安全性,但你可以调用 `Node.Load()` / `Node.LoadAll()` 来实现并发安全。尽管可能会带来性能损失,但仍比转换成 `map` 或 `interface{}` 更为高效。
|
||||
|
||||
### 使用 `ast.Node` 还是 `ast.Visitor`?
|
||||
|
||||
对于泛型数据的解析,`ast.Node` 在大多数场景上应该能够满足你的需求。
|
||||
|
||||
然而,`ast.Node` 是一种针对部分解析 JSON 而设计的泛型容器,它包含一些特殊设计,比如惰性加载,如果你希望像 `Unmarshal()` 那样直接解析整个 JSON,这些设计可能并不合适。尽管 `ast.Node` 相较于 `map` 或 `interface{}` 来说是更好的一种泛型容器,但它毕竟也是一种中间表示,如果你的最终类型是自定义的,你还得在解析完成后将上述类型转化成你自定义的类型。
|
||||
|
||||
在上述场景中,如果想要有更极致的性能,`ast.Visitor` 会是更好的选择。它采用和 `Unmarshal()` 类似的形式解析 JSON,并且你可以直接使用你的最终类型去表示 JSON AST,而不需要经过额外的任何中间表示。
|
||||
|
||||
但是,`ast.Visitor` 并不是一个很易用的 API。你可能需要写大量的代码去实现自己的 `ast.Visitor`,并且需要在解析过程中仔细维护树的层级。如果你决定要使用这个 API,请先仔细阅读 [ast/visitor.go](https://github.com/bytedance/sonic/blob/main/ast/visitor.go) 中的注释。
|
||||
|
||||
## 社区
|
||||
|
||||
Sonic 是 [CloudWeGo](https://www.cloudwego.io/) 下的一个子项目。我们致力于构建云原生生态系统。
|
||||
|
||||
50
vendor/github.com/bytedance/sonic/api.go
generated
vendored
50
vendor/github.com/bytedance/sonic/api.go
generated
vendored
@@ -20,7 +20,6 @@ import (
|
||||
`io`
|
||||
|
||||
`github.com/bytedance/sonic/ast`
|
||||
`github.com/bytedance/sonic/internal/rt`
|
||||
)
|
||||
|
||||
// Config is a combination of sonic/encoder.Options and sonic/decoder.Options
|
||||
@@ -69,18 +68,11 @@ type Config struct {
|
||||
|
||||
// ValidateString indicates decoder and encoder to valid string values: decoder will return errors
|
||||
// when unescaped control chars(\u0000-\u001f) in the string value of JSON.
|
||||
ValidateString bool
|
||||
|
||||
// NoValidateJSONMarshaler indicates that the encoder should not validate the output string
|
||||
// after encoding the JSONMarshaler to JSON.
|
||||
NoValidateJSONMarshaler bool
|
||||
|
||||
// NoEncoderNewline indicates that the encoder should not add a newline after every message
|
||||
NoEncoderNewline bool
|
||||
ValidateString bool
|
||||
}
|
||||
|
||||
var (
|
||||
// ConfigDefault is the default config of APIs, aiming at efficiency and safety.
|
||||
// ConfigDefault is the default config of APIs, aiming at efficiency and safty.
|
||||
ConfigDefault = Config{}.Froze()
|
||||
|
||||
// ConfigStd is the standard config of APIs, aiming at being compatible with encoding/json.
|
||||
@@ -95,7 +87,6 @@ var (
|
||||
// ConfigFastest is the fastest config of APIs, aiming at speed.
|
||||
ConfigFastest = Config{
|
||||
NoQuoteTextMarshaler: true,
|
||||
NoValidateJSONMarshaler: true,
|
||||
}.Froze()
|
||||
)
|
||||
|
||||
@@ -118,7 +109,7 @@ type API interface {
|
||||
NewEncoder(writer io.Writer) Encoder
|
||||
// NewDecoder create a Decoder holding reader
|
||||
NewDecoder(reader io.Reader) Decoder
|
||||
// Valid validates the JSON-encoded bytes and reports if it is valid
|
||||
// Valid validates the JSON-encoded bytes and reportes if it is valid
|
||||
Valid(data []byte) bool
|
||||
}
|
||||
|
||||
@@ -174,41 +165,22 @@ func UnmarshalString(buf string, val interface{}) error {
|
||||
return ConfigDefault.UnmarshalFromString(buf, val)
|
||||
}
|
||||
|
||||
// Get searches and locates the given path from src json,
|
||||
// and returns a ast.Node representing the partially json.
|
||||
// Get searches the given path from json,
|
||||
// and returns its representing ast.Node.
|
||||
//
|
||||
// Each path arg must be integer or string:
|
||||
// - Integer is target index(>=0), means searching current node as array.
|
||||
// - String is target key, means searching current node as object.
|
||||
//
|
||||
//
|
||||
// Notice: It expects the src json is **Well-formed** and **Immutable** when calling,
|
||||
// otherwise it may return unexpected result.
|
||||
// Considering memory safety, the returned JSON is **Copied** from the input
|
||||
// Note, the api expects the json is well-formed at least,
|
||||
// otherwise it may return unexpected result.
|
||||
func Get(src []byte, path ...interface{}) (ast.Node, error) {
|
||||
return GetCopyFromString(rt.Mem2Str(src), path...)
|
||||
return GetFromString(string(src), path...)
|
||||
}
|
||||
|
||||
// GetFromString is same with Get except src is string.
|
||||
//
|
||||
// WARNING: The returned JSON is **Referenced** from the input.
|
||||
// Caching or long-time holding the returned node may cause OOM.
|
||||
// If your src is big, consider use GetFromStringCopy().
|
||||
// GetFromString is same with Get except src is string,
|
||||
// which can reduce unnecessary memory copy.
|
||||
func GetFromString(src string, path ...interface{}) (ast.Node, error) {
|
||||
return ast.NewSearcher(src).GetByPath(path...)
|
||||
}
|
||||
|
||||
// GetCopyFromString is same with Get except src is string
|
||||
func GetCopyFromString(src string, path ...interface{}) (ast.Node, error) {
|
||||
return ast.NewSearcher(src).GetByPathCopy(path...)
|
||||
}
|
||||
|
||||
// Valid reports whether data is a valid JSON encoding.
|
||||
func Valid(data []byte) bool {
|
||||
return ConfigDefault.Valid(data)
|
||||
}
|
||||
|
||||
// Valid reports whether data is a valid JSON encoding.
|
||||
func ValidString(data string) bool {
|
||||
return ConfigDefault.Valid(rt.Str2Mem(data))
|
||||
}
|
||||
}
|
||||
86
vendor/github.com/bytedance/sonic/ast/api_compat.go
generated
vendored
86
vendor/github.com/bytedance/sonic/ast/api_compat.go
generated
vendored
@@ -1,40 +1,36 @@
|
||||
// +build !amd64,!arm64 go1.23 !go1.16 arm64,!go1.20
|
||||
// +build !amd64 go1.21
|
||||
|
||||
/*
|
||||
* Copyright 2022 ByteDance Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
* Copyright 2022 ByteDance Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package ast
|
||||
|
||||
import (
|
||||
`encoding/base64`
|
||||
`encoding/json`
|
||||
`unicode/utf8`
|
||||
`fmt`
|
||||
|
||||
`github.com/bytedance/sonic/internal/native/types`
|
||||
`github.com/bytedance/sonic/internal/rt`
|
||||
)
|
||||
|
||||
func init() {
|
||||
println("WARNING:(ast) sonic only supports Go1.16~1.22, but your environment is not suitable")
|
||||
}
|
||||
|
||||
func quote(buf *[]byte, val string) {
|
||||
quoteString(buf, val)
|
||||
}
|
||||
|
||||
// unquote unescapes a internal JSON string (it doesn't count quotas at the begining and end)
|
||||
func unquote(src string) (string, types.ParsingError) {
|
||||
sp := rt.IndexChar(src, -1)
|
||||
out, ok := unquoteBytes(rt.BytesFrom(sp, len(src)+2, len(src)+2))
|
||||
@@ -44,9 +40,16 @@ func unquote(src string) (string, types.ParsingError) {
|
||||
return rt.Mem2Str(out), 0
|
||||
}
|
||||
|
||||
func decodeBase64(src string) ([]byte, error) {
|
||||
return base64.StdEncoding.DecodeString(src)
|
||||
}
|
||||
|
||||
func encodeBase64(src []byte) string {
|
||||
return base64.StdEncoding.EncodeToString(src)
|
||||
}
|
||||
|
||||
func (self *Parser) decodeValue() (val types.JsonState) {
|
||||
e, v := decodeValue(self.s, self.p, self.dbuf == nil)
|
||||
e, v := decodeValue(self.s, self.p)
|
||||
if e < 0 {
|
||||
return v
|
||||
}
|
||||
@@ -81,34 +84,37 @@ func (self *Node) encodeInterface(buf *[]byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *Parser) getByPath(validate bool, path ...interface{}) (int, types.ParsingError) {
|
||||
func (self *Searcher) GetByPath(path ...interface{}) (Node, error) {
|
||||
self.parser.p = 0
|
||||
|
||||
var err types.ParsingError
|
||||
for _, p := range path {
|
||||
if idx, ok := p.(int); ok && idx >= 0 {
|
||||
if err := self.searchIndex(idx); err != 0 {
|
||||
return self.p, err
|
||||
if err = self.parser.searchIndex(idx); err != 0 {
|
||||
return Node{}, self.parser.ExportError(err)
|
||||
}
|
||||
} else if key, ok := p.(string); ok {
|
||||
if err := self.searchKey(key); err != 0 {
|
||||
return self.p, err
|
||||
if err = self.parser.searchKey(key); err != 0 {
|
||||
return Node{}, self.parser.ExportError(err)
|
||||
}
|
||||
} else {
|
||||
panic("path must be either int(>=0) or string")
|
||||
}
|
||||
}
|
||||
|
||||
var start int
|
||||
var e types.ParsingError
|
||||
if validate {
|
||||
start, e = self.skip()
|
||||
} else {
|
||||
start, e = self.skipFast()
|
||||
var start = self.parser.p
|
||||
if start, err = self.parser.skip(); err != 0 {
|
||||
return Node{}, self.parser.ExportError(err)
|
||||
}
|
||||
if e != 0 {
|
||||
return self.p, e
|
||||
ns := len(self.parser.s)
|
||||
if self.parser.p > ns || start >= ns || start>=self.parser.p {
|
||||
return Node{}, fmt.Errorf("skip %d char out of json boundary", start)
|
||||
}
|
||||
return start, 0
|
||||
}
|
||||
|
||||
func validate_utf8(str string) bool {
|
||||
return utf8.ValidString(str)
|
||||
}
|
||||
t := switchRawType(self.parser.s[start])
|
||||
if t == _V_NONE {
|
||||
return Node{}, self.parser.ExportError(err)
|
||||
}
|
||||
|
||||
return newRawNode(self.parser.s[start:self.parser.p], t), nil
|
||||
}
|
||||
71
vendor/github.com/bytedance/sonic/ast/decode.go
generated
vendored
71
vendor/github.com/bytedance/sonic/ast/decode.go
generated
vendored
@@ -220,7 +220,7 @@ func decodeFloat64(src string, pos int) (ret int, v float64, err error) {
|
||||
return ret, v, nil
|
||||
}
|
||||
|
||||
func decodeValue(src string, pos int, skipnum bool) (ret int, v types.JsonState) {
|
||||
func decodeValue(src string, pos int) (ret int, v types.JsonState) {
|
||||
pos = skipBlank(src, pos)
|
||||
if pos < 0 {
|
||||
return pos, types.JsonState{Vt: types.ValueType(pos)}
|
||||
@@ -256,30 +256,20 @@ func decodeValue(src string, pos int, skipnum bool) (ret int, v types.JsonState)
|
||||
}
|
||||
return ret, types.JsonState{Vt: types.V_FALSE}
|
||||
case '-', '+', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
if skipnum {
|
||||
ret = skipNumber(src, pos)
|
||||
if ret >= 0 {
|
||||
return ret, types.JsonState{Vt: types.V_DOUBLE, Iv: 0, Ep: pos}
|
||||
} else {
|
||||
return ret, types.JsonState{Vt: types.ValueType(ret)}
|
||||
}
|
||||
} else {
|
||||
var iv int64
|
||||
ret, iv, _ = decodeInt64(src, pos)
|
||||
if ret >= 0 {
|
||||
return ret, types.JsonState{Vt: types.V_INTEGER, Iv: iv, Ep: pos}
|
||||
} else if ret != -int(types.ERR_INVALID_NUMBER_FMT) {
|
||||
return ret, types.JsonState{Vt: types.ValueType(ret)}
|
||||
}
|
||||
var fv float64
|
||||
ret, fv, _ = decodeFloat64(src, pos)
|
||||
if ret >= 0 {
|
||||
return ret, types.JsonState{Vt: types.V_DOUBLE, Dv: fv, Ep: pos}
|
||||
} else {
|
||||
return ret, types.JsonState{Vt: types.ValueType(ret)}
|
||||
}
|
||||
var iv int64
|
||||
ret, iv, _ = decodeInt64(src, pos)
|
||||
if ret >= 0 {
|
||||
return ret, types.JsonState{Vt: types.V_INTEGER, Iv: iv, Ep: pos}
|
||||
} else if ret != -int(types.ERR_INVALID_NUMBER_FMT) {
|
||||
return ret, types.JsonState{Vt: types.ValueType(ret)}
|
||||
}
|
||||
var fv float64
|
||||
ret, fv, _ = decodeFloat64(src, pos)
|
||||
if ret >= 0 {
|
||||
return ret, types.JsonState{Vt: types.V_DOUBLE, Dv: fv, Ep: pos}
|
||||
} else {
|
||||
return ret, types.JsonState{Vt: types.ValueType(ret)}
|
||||
}
|
||||
|
||||
default:
|
||||
return -int(types.ERR_INVALID_CHAR), types.JsonState{Vt:-types.ValueType(types.ERR_INVALID_CHAR)}
|
||||
}
|
||||
@@ -583,36 +573,3 @@ func skipArray(src string, pos int) (ret int, start int) {
|
||||
pos++
|
||||
}
|
||||
}
|
||||
|
||||
// DecodeString decodes a JSON string from pos and return golang string.
|
||||
// - needEsc indicates if to unescaped escaping chars
|
||||
// - hasEsc tells if the returned string has escaping chars
|
||||
// - validStr enables validating UTF8 charset
|
||||
//
|
||||
func _DecodeString(src string, pos int, needEsc bool, validStr bool) (v string, ret int, hasEsc bool) {
|
||||
p := NewParserObj(src)
|
||||
p.p = pos
|
||||
switch val := p.decodeValue(); val.Vt {
|
||||
case types.V_STRING:
|
||||
str := p.s[val.Iv : p.p-1]
|
||||
if validStr && !validate_utf8(str) {
|
||||
return "", -int(types.ERR_INVALID_UTF8), false
|
||||
}
|
||||
/* fast path: no escape sequence */
|
||||
if val.Ep == -1 {
|
||||
return str, p.p, false
|
||||
} else if !needEsc {
|
||||
return str, p.p, true
|
||||
}
|
||||
/* unquote the string */
|
||||
out, err := unquote(str)
|
||||
/* check for errors */
|
||||
if err != 0 {
|
||||
return "", -int(err), true
|
||||
} else {
|
||||
return out, p.p, true
|
||||
}
|
||||
default:
|
||||
return "", -int(_ERR_UNSUPPORT_TYPE), false
|
||||
}
|
||||
}
|
||||
|
||||
52
vendor/github.com/bytedance/sonic/ast/encode.go
generated
vendored
52
vendor/github.com/bytedance/sonic/ast/encode.go
generated
vendored
@@ -19,6 +19,8 @@ package ast
|
||||
import (
|
||||
`sync`
|
||||
`unicode/utf8`
|
||||
|
||||
`github.com/bytedance/sonic/internal/rt`
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -163,18 +165,18 @@ func (self *Node) encodeFalse(buf *[]byte) error {
|
||||
}
|
||||
|
||||
func (self *Node) encodeNumber(buf *[]byte) error {
|
||||
str := self.toString()
|
||||
str := rt.StrFrom(self.p, self.v)
|
||||
*buf = append(*buf, str...)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *Node) encodeString(buf *[]byte) error {
|
||||
if self.l == 0 {
|
||||
if self.v == 0 {
|
||||
*buf = append(*buf, '"', '"')
|
||||
return nil
|
||||
}
|
||||
|
||||
quote(buf, self.toString())
|
||||
quote(buf, rt.StrFrom(self.p, self.v))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -193,17 +195,16 @@ func (self *Node) encodeArray(buf *[]byte) error {
|
||||
|
||||
*buf = append(*buf, '[')
|
||||
|
||||
var started bool
|
||||
for i := 0; i < nb; i++ {
|
||||
n := self.nodeAt(i)
|
||||
if !n.Exists() {
|
||||
continue
|
||||
}
|
||||
if started {
|
||||
*buf = append(*buf, ',')
|
||||
}
|
||||
started = true
|
||||
if err := n.encode(buf); err != nil {
|
||||
var p = (*Node)(self.p)
|
||||
err := p.encode(buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for i := 1; i < nb; i++ {
|
||||
*buf = append(*buf, ',')
|
||||
p = p.unsafe_next()
|
||||
err := p.encode(buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -239,21 +240,20 @@ func (self *Node) encodeObject(buf *[]byte) error {
|
||||
|
||||
*buf = append(*buf, '{')
|
||||
|
||||
var started bool
|
||||
for i := 0; i < nb; i++ {
|
||||
n := self.pairAt(i)
|
||||
if n == nil || !n.Value.Exists() {
|
||||
continue
|
||||
}
|
||||
if started {
|
||||
*buf = append(*buf, ',')
|
||||
}
|
||||
started = true
|
||||
if err := n.encode(buf); err != nil {
|
||||
var p = (*Pair)(self.p)
|
||||
err := p.encode(buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for i := 1; i < nb; i++ {
|
||||
*buf = append(*buf, ',')
|
||||
p = p.unsafe_next()
|
||||
err := p.encode(buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
*buf = append(*buf, '}')
|
||||
return nil
|
||||
}
|
||||
}
|
||||
44
vendor/github.com/bytedance/sonic/ast/error.go
generated
vendored
44
vendor/github.com/bytedance/sonic/ast/error.go
generated
vendored
@@ -8,33 +8,6 @@ import (
|
||||
`github.com/bytedance/sonic/internal/native/types`
|
||||
)
|
||||
|
||||
|
||||
func newError(err types.ParsingError, msg string) *Node {
|
||||
return &Node{
|
||||
t: V_ERROR,
|
||||
l: uint(err),
|
||||
p: unsafe.Pointer(&msg),
|
||||
}
|
||||
}
|
||||
|
||||
// Error returns error message if the node is invalid
|
||||
func (self Node) Error() string {
|
||||
if self.t != V_ERROR {
|
||||
return ""
|
||||
} else {
|
||||
return *(*string)(self.p)
|
||||
}
|
||||
}
|
||||
|
||||
func newSyntaxError(err SyntaxError) *Node {
|
||||
msg := err.Description()
|
||||
return &Node{
|
||||
t: V_ERROR,
|
||||
l: uint(err.Code),
|
||||
p: unsafe.Pointer(&msg),
|
||||
}
|
||||
}
|
||||
|
||||
func (self *Parser) syntaxError(err types.ParsingError) SyntaxError {
|
||||
return SyntaxError{
|
||||
Pos : self.p,
|
||||
@@ -43,17 +16,12 @@ func (self *Parser) syntaxError(err types.ParsingError) SyntaxError {
|
||||
}
|
||||
}
|
||||
|
||||
func unwrapError(err error) *Node {
|
||||
if se, ok := err.(*Node); ok {
|
||||
return se
|
||||
}else if sse, ok := err.(Node); ok {
|
||||
return &sse
|
||||
} else {
|
||||
msg := err.Error()
|
||||
return &Node{
|
||||
t: V_ERROR,
|
||||
p: unsafe.Pointer(&msg),
|
||||
}
|
||||
func newSyntaxError(err SyntaxError) *Node {
|
||||
msg := err.Description()
|
||||
return &Node{
|
||||
t: V_ERROR,
|
||||
v: int64(err.Code),
|
||||
p: unsafe.Pointer(&msg),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
87
vendor/github.com/bytedance/sonic/ast/iterator.go
generated
vendored
87
vendor/github.com/bytedance/sonic/ast/iterator.go
generated
vendored
@@ -32,11 +32,7 @@ func (self *Node) Values() (ListIterator, error) {
|
||||
if err := self.should(types.V_ARRAY, "an array"); err != nil {
|
||||
return ListIterator{}, err
|
||||
}
|
||||
return self.values(), nil
|
||||
}
|
||||
|
||||
func (self *Node) values() ListIterator {
|
||||
return ListIterator{Iterator{p: self}}
|
||||
return ListIterator{Iterator{p: self}}, nil
|
||||
}
|
||||
|
||||
// Properties returns iterator for object's children traversal
|
||||
@@ -44,11 +40,7 @@ func (self *Node) Properties() (ObjectIterator, error) {
|
||||
if err := self.should(types.V_OBJECT, "an object"); err != nil {
|
||||
return ObjectIterator{}, err
|
||||
}
|
||||
return self.properties(), nil
|
||||
}
|
||||
|
||||
func (self *Node) properties() ObjectIterator {
|
||||
return ObjectIterator{Iterator{p: self}}
|
||||
return ObjectIterator{Iterator{p: self}}, nil
|
||||
}
|
||||
|
||||
type Iterator struct {
|
||||
@@ -90,54 +82,26 @@ type ObjectIterator struct {
|
||||
Iterator
|
||||
}
|
||||
|
||||
func (self *ListIterator) next() *Node {
|
||||
next_start:
|
||||
if !self.HasNext() {
|
||||
return nil
|
||||
} else {
|
||||
n := self.p.nodeAt(self.i)
|
||||
self.i++
|
||||
if !n.Exists() {
|
||||
goto next_start
|
||||
}
|
||||
return n
|
||||
}
|
||||
}
|
||||
|
||||
// Next scans through children of underlying V_ARRAY,
|
||||
// copies each child to v, and returns .HasNext().
|
||||
func (self *ListIterator) Next(v *Node) bool {
|
||||
n := self.next()
|
||||
if n == nil {
|
||||
return false
|
||||
}
|
||||
*v = *n
|
||||
return true
|
||||
}
|
||||
|
||||
func (self *ObjectIterator) next() *Pair {
|
||||
next_start:
|
||||
if !self.HasNext() {
|
||||
return nil
|
||||
return false
|
||||
} else {
|
||||
n := self.p.pairAt(self.i)
|
||||
self.i++
|
||||
if n == nil || !n.Value.Exists() {
|
||||
goto next_start
|
||||
}
|
||||
return n
|
||||
*v, self.i = *self.p.nodeAt(self.i), self.i + 1
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// Next scans through children of underlying V_OBJECT,
|
||||
// copies each child to v, and returns .HasNext().
|
||||
func (self *ObjectIterator) Next(p *Pair) bool {
|
||||
n := self.next()
|
||||
if n == nil {
|
||||
if !self.HasNext() {
|
||||
return false
|
||||
} else {
|
||||
*p, self.i = *self.p.pairAt(self.i), self.i + 1
|
||||
return true
|
||||
}
|
||||
*p = *n
|
||||
return true
|
||||
}
|
||||
|
||||
// Sequence represents scanning path of single-layer nodes.
|
||||
@@ -165,39 +129,36 @@ type Scanner func(path Sequence, node *Node) bool
|
||||
//
|
||||
// Especailly, if the node is not V_ARRAY or V_OBJECT,
|
||||
// the node itself will be returned and Sequence.Index == -1.
|
||||
//
|
||||
// NOTICE: A unsetted node WON'T trigger sc, but its index still counts into Path.Index
|
||||
func (self *Node) ForEach(sc Scanner) error {
|
||||
switch self.itype() {
|
||||
case types.V_ARRAY:
|
||||
iter, err := self.Values()
|
||||
ns, err := self.UnsafeArray()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v := iter.next()
|
||||
for v != nil {
|
||||
if !sc(Sequence{iter.i-1, nil}, v) {
|
||||
return nil
|
||||
for i := range ns {
|
||||
if !sc(Sequence{i, nil}, &ns[i]) {
|
||||
return err
|
||||
}
|
||||
v = iter.next()
|
||||
}
|
||||
case types.V_OBJECT:
|
||||
iter, err := self.Properties()
|
||||
ns, err := self.UnsafeMap()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v := iter.next()
|
||||
for v != nil {
|
||||
if !sc(Sequence{iter.i-1, &v.Key}, &v.Value) {
|
||||
return nil
|
||||
for i := range ns {
|
||||
if !sc(Sequence{i, &ns[i].Key}, &ns[i].Value) {
|
||||
return err
|
||||
}
|
||||
v = iter.next()
|
||||
}
|
||||
default:
|
||||
if self.Check() != nil {
|
||||
return self
|
||||
}
|
||||
sc(Sequence{-1, nil}, self)
|
||||
}
|
||||
return nil
|
||||
return self.Check()
|
||||
}
|
||||
|
||||
type PairSlice []Pair
|
||||
|
||||
func (self PairSlice) Sort() {
|
||||
radixQsort(self, 0, maxDepth(len(self)))
|
||||
}
|
||||
844
vendor/github.com/bytedance/sonic/ast/node.go
generated
vendored
844
vendor/github.com/bytedance/sonic/ast/node.go
generated
vendored
File diff suppressed because it is too large
Load Diff
106
vendor/github.com/bytedance/sonic/ast/parser.go
generated
vendored
106
vendor/github.com/bytedance/sonic/ast/parser.go
generated
vendored
@@ -18,15 +18,11 @@ package ast
|
||||
|
||||
import (
|
||||
`fmt`
|
||||
|
||||
`github.com/bytedance/sonic/internal/native/types`
|
||||
`github.com/bytedance/sonic/internal/rt`
|
||||
)
|
||||
|
||||
const (
|
||||
_DEFAULT_NODE_CAP int = 8
|
||||
_APPEND_GROW_SHIFT = 1
|
||||
)
|
||||
const _DEFAULT_NODE_CAP int = 16
|
||||
|
||||
const (
|
||||
_ERR_NOT_FOUND types.ParsingError = 33
|
||||
@@ -34,10 +30,7 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrNotExist means both key and value doesn't exist
|
||||
ErrNotExist error = newError(_ERR_NOT_FOUND, "value not exists")
|
||||
|
||||
// ErrUnsupportType means API on the node is unsupported
|
||||
ErrUnsupportType error = newError(_ERR_UNSUPPORT_TYPE, "unsupported type")
|
||||
)
|
||||
|
||||
@@ -46,7 +39,6 @@ type Parser struct {
|
||||
s string
|
||||
noLazy bool
|
||||
skipValue bool
|
||||
dbuf *byte
|
||||
}
|
||||
|
||||
/** Parser Private Methods **/
|
||||
@@ -115,7 +107,7 @@ func (self *Parser) lspace(sp int) int {
|
||||
return sp
|
||||
}
|
||||
|
||||
func (self *Parser) decodeArray(ret *linkedNodes) (Node, types.ParsingError) {
|
||||
func (self *Parser) decodeArray(ret []Node) (Node, types.ParsingError) {
|
||||
sp := self.p
|
||||
ns := len(self.s)
|
||||
|
||||
@@ -127,7 +119,7 @@ func (self *Parser) decodeArray(ret *linkedNodes) (Node, types.ParsingError) {
|
||||
/* check for empty array */
|
||||
if self.s[self.p] == ']' {
|
||||
self.p++
|
||||
return Node{t: types.V_ARRAY}, 0
|
||||
return emptyArrayNode, 0
|
||||
}
|
||||
|
||||
/* allocate array space and parse every element */
|
||||
@@ -157,7 +149,7 @@ func (self *Parser) decodeArray(ret *linkedNodes) (Node, types.ParsingError) {
|
||||
}
|
||||
|
||||
/* add the value to result */
|
||||
ret.Push(val)
|
||||
ret = append(ret, val)
|
||||
self.p = self.lspace(self.p)
|
||||
|
||||
/* check for EOF */
|
||||
@@ -168,17 +160,17 @@ func (self *Parser) decodeArray(ret *linkedNodes) (Node, types.ParsingError) {
|
||||
/* check for the next character */
|
||||
switch self.s[self.p] {
|
||||
case ',' : self.p++
|
||||
case ']' : self.p++; return newArray(ret), 0
|
||||
default:
|
||||
// if val.isLazy() {
|
||||
// return newLazyArray(self, ret), 0
|
||||
// }
|
||||
return Node{}, types.ERR_INVALID_CHAR
|
||||
case ']' : self.p++; return NewArray(ret), 0
|
||||
default:
|
||||
if val.isLazy() {
|
||||
return newLazyArray(self, ret), 0
|
||||
}
|
||||
return Node{}, types.ERR_INVALID_CHAR
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (self *Parser) decodeObject(ret *linkedPairs) (Node, types.ParsingError) {
|
||||
func (self *Parser) decodeObject(ret []Pair) (Node, types.ParsingError) {
|
||||
sp := self.p
|
||||
ns := len(self.s)
|
||||
|
||||
@@ -190,7 +182,7 @@ func (self *Parser) decodeObject(ret *linkedPairs) (Node, types.ParsingError) {
|
||||
/* check for empty object */
|
||||
if self.s[self.p] == '}' {
|
||||
self.p++
|
||||
return Node{t: types.V_OBJECT}, 0
|
||||
return emptyObjectNode, 0
|
||||
}
|
||||
|
||||
/* decode each pair */
|
||||
@@ -243,8 +235,7 @@ func (self *Parser) decodeObject(ret *linkedPairs) (Node, types.ParsingError) {
|
||||
}
|
||||
|
||||
/* add the value to result */
|
||||
// FIXME: ret's address may change here, thus previous referred node in ret may be invalid !!
|
||||
ret.Push(Pair{Key: key, Value: val})
|
||||
ret = append(ret, Pair{Key: key, Value: val})
|
||||
self.p = self.lspace(self.p)
|
||||
|
||||
/* check for EOF */
|
||||
@@ -255,11 +246,11 @@ func (self *Parser) decodeObject(ret *linkedPairs) (Node, types.ParsingError) {
|
||||
/* check for the next character */
|
||||
switch self.s[self.p] {
|
||||
case ',' : self.p++
|
||||
case '}' : self.p++; return newObject(ret), 0
|
||||
case '}' : self.p++; return NewObject(ret), 0
|
||||
default:
|
||||
// if val.isLazy() {
|
||||
// return newLazyObject(self, ret), 0
|
||||
// }
|
||||
if val.isLazy() {
|
||||
return newLazyObject(self, ret), 0
|
||||
}
|
||||
return Node{}, types.ERR_INVALID_CHAR
|
||||
}
|
||||
}
|
||||
@@ -299,23 +290,15 @@ func (self *Parser) Parse() (Node, types.ParsingError) {
|
||||
case types.V_FALSE : return falseNode, 0
|
||||
case types.V_STRING : return self.decodeString(val.Iv, val.Ep)
|
||||
case types.V_ARRAY:
|
||||
if p := skipBlank(self.s, self.p); p >= self.p && self.s[p] == ']' {
|
||||
self.p = p + 1
|
||||
return Node{t: types.V_ARRAY}, 0
|
||||
}
|
||||
if self.noLazy {
|
||||
return self.decodeArray(new(linkedNodes))
|
||||
return self.decodeArray(make([]Node, 0, _DEFAULT_NODE_CAP))
|
||||
}
|
||||
return newLazyArray(self), 0
|
||||
return newLazyArray(self, make([]Node, 0, _DEFAULT_NODE_CAP)), 0
|
||||
case types.V_OBJECT:
|
||||
if p := skipBlank(self.s, self.p); p >= self.p && self.s[p] == '}' {
|
||||
self.p = p + 1
|
||||
return Node{t: types.V_OBJECT}, 0
|
||||
}
|
||||
if self.noLazy {
|
||||
return self.decodeObject(new(linkedPairs))
|
||||
return self.decodeObject(make([]Pair, 0, _DEFAULT_NODE_CAP))
|
||||
}
|
||||
return newLazyObject(self), 0
|
||||
return newLazyObject(self, make([]Pair, 0, _DEFAULT_NODE_CAP)), 0
|
||||
case types.V_DOUBLE : return NewNumber(self.s[val.Ep:self.p]), 0
|
||||
case types.V_INTEGER : return NewNumber(self.s[val.Ep:self.p]), 0
|
||||
default : return Node{}, types.ParsingError(-val.Vt)
|
||||
@@ -446,7 +429,7 @@ func (self *Node) skipNextNode() *Node {
|
||||
}
|
||||
|
||||
parser, stack := self.getParserAndArrayStack()
|
||||
ret := &stack.v
|
||||
ret := stack.v
|
||||
sp := parser.p
|
||||
ns := len(parser.s)
|
||||
|
||||
@@ -475,8 +458,7 @@ func (self *Node) skipNextNode() *Node {
|
||||
}
|
||||
|
||||
/* add the value to result */
|
||||
ret.Push(val)
|
||||
self.l++
|
||||
ret = append(ret, val)
|
||||
parser.p = parser.lspace(parser.p)
|
||||
|
||||
/* check for EOF */
|
||||
@@ -488,11 +470,12 @@ func (self *Node) skipNextNode() *Node {
|
||||
switch parser.s[parser.p] {
|
||||
case ',':
|
||||
parser.p++
|
||||
return ret.At(ret.Len()-1)
|
||||
self.setLazyArray(parser, ret)
|
||||
return &ret[len(ret)-1]
|
||||
case ']':
|
||||
parser.p++
|
||||
self.setArray(ret)
|
||||
return ret.At(ret.Len()-1)
|
||||
return &ret[len(ret)-1]
|
||||
default:
|
||||
return newSyntaxError(parser.syntaxError(types.ERR_INVALID_CHAR))
|
||||
}
|
||||
@@ -504,7 +487,7 @@ func (self *Node) skipNextPair() (*Pair) {
|
||||
}
|
||||
|
||||
parser, stack := self.getParserAndObjectStack()
|
||||
ret := &stack.v
|
||||
ret := stack.v
|
||||
sp := parser.p
|
||||
ns := len(parser.s)
|
||||
|
||||
@@ -558,8 +541,7 @@ func (self *Node) skipNextPair() (*Pair) {
|
||||
}
|
||||
|
||||
/* add the value to result */
|
||||
ret.Push(Pair{Key: key, Value: val})
|
||||
self.l++
|
||||
ret = append(ret, Pair{Key: key, Value: val})
|
||||
parser.p = parser.lspace(parser.p)
|
||||
|
||||
/* check for EOF */
|
||||
@@ -571,11 +553,12 @@ func (self *Node) skipNextPair() (*Pair) {
|
||||
switch parser.s[parser.p] {
|
||||
case ',':
|
||||
parser.p++
|
||||
return ret.At(ret.Len()-1)
|
||||
self.setLazyObject(parser, ret)
|
||||
return &ret[len(ret)-1]
|
||||
case '}':
|
||||
parser.p++
|
||||
self.setObject(ret)
|
||||
return ret.At(ret.Len()-1)
|
||||
return &ret[len(ret)-1]
|
||||
default:
|
||||
return &Pair{key, *newSyntaxError(parser.syntaxError(types.ERR_INVALID_CHAR))}
|
||||
}
|
||||
@@ -618,30 +601,10 @@ func LoadsUseNumber(src string) (int, interface{}, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// NewParser returns pointer of new allocated parser
|
||||
func NewParser(src string) *Parser {
|
||||
return &Parser{s: src}
|
||||
}
|
||||
|
||||
// NewParser returns new allocated parser
|
||||
func NewParserObj(src string) Parser {
|
||||
return Parser{s: src}
|
||||
}
|
||||
|
||||
// decodeNumber controls if parser decodes the number values instead of skip them
|
||||
// WARN: once you set decodeNumber(true), please set decodeNumber(false) before you drop the parser
|
||||
// otherwise the memory CANNOT be reused
|
||||
func (self *Parser) decodeNumber(decode bool) {
|
||||
if !decode && self.dbuf != nil {
|
||||
types.FreeDbuf(self.dbuf)
|
||||
self.dbuf = nil
|
||||
return
|
||||
}
|
||||
if decode && self.dbuf == nil {
|
||||
self.dbuf = types.NewDbuf()
|
||||
}
|
||||
}
|
||||
|
||||
// ExportError converts types.ParsingError to std Error
|
||||
func (self *Parser) ExportError(err types.ParsingError) error {
|
||||
if err == _ERR_NOT_FOUND {
|
||||
@@ -652,9 +615,4 @@ func (self *Parser) ExportError(err types.ParsingError) error {
|
||||
Src : self.s,
|
||||
Code: err,
|
||||
}.Description())
|
||||
}
|
||||
|
||||
func backward(src string, i int) int {
|
||||
for ; i>=0 && isSpace(src[i]); i-- {}
|
||||
return i
|
||||
}
|
||||
}
|
||||
108
vendor/github.com/bytedance/sonic/ast/search.go
generated
vendored
108
vendor/github.com/bytedance/sonic/ast/search.go
generated
vendored
@@ -16,11 +16,6 @@
|
||||
|
||||
package ast
|
||||
|
||||
import (
|
||||
`github.com/bytedance/sonic/internal/rt`
|
||||
`github.com/bytedance/sonic/internal/native/types`
|
||||
)
|
||||
|
||||
type Searcher struct {
|
||||
parser Parser
|
||||
}
|
||||
@@ -33,106 +28,3 @@ func NewSearcher(str string) *Searcher {
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// GetByPathCopy search in depth from top json and returns a **Copied** json node at the path location
|
||||
func (self *Searcher) GetByPathCopy(path ...interface{}) (Node, error) {
|
||||
return self.getByPath(true, true, path...)
|
||||
}
|
||||
|
||||
// GetByPathNoCopy search in depth from top json and returns a **Referenced** json node at the path location
|
||||
//
|
||||
// WARN: this search directly refer partial json from top json, which has faster speed,
|
||||
// may consumes more memory.
|
||||
func (self *Searcher) GetByPath(path ...interface{}) (Node, error) {
|
||||
return self.getByPath(false, true, path...)
|
||||
}
|
||||
|
||||
func (self *Searcher) getByPath(copystring bool, validate bool, path ...interface{}) (Node, error) {
|
||||
var err types.ParsingError
|
||||
var start int
|
||||
|
||||
self.parser.p = 0
|
||||
start, err = self.parser.getByPath(validate, path...)
|
||||
if err != 0 {
|
||||
// for compatibility with old version
|
||||
if err == types.ERR_NOT_FOUND {
|
||||
return Node{}, ErrNotExist
|
||||
}
|
||||
if err == types.ERR_UNSUPPORT_TYPE {
|
||||
panic("path must be either int(>=0) or string")
|
||||
}
|
||||
return Node{}, self.parser.syntaxError(err)
|
||||
}
|
||||
|
||||
t := switchRawType(self.parser.s[start])
|
||||
if t == _V_NONE {
|
||||
return Node{}, self.parser.ExportError(err)
|
||||
}
|
||||
|
||||
// copy string to reducing memory usage
|
||||
var raw string
|
||||
if copystring {
|
||||
raw = rt.Mem2Str([]byte(self.parser.s[start:self.parser.p]))
|
||||
} else {
|
||||
raw = self.parser.s[start:self.parser.p]
|
||||
}
|
||||
return newRawNode(raw, t), nil
|
||||
}
|
||||
|
||||
// GetByPath searches a path and returns relaction and types of target
|
||||
func _GetByPath(src string, path ...interface{}) (start int, end int, typ int, err error) {
|
||||
p := NewParserObj(src)
|
||||
s, e := p.getByPath(false, path...)
|
||||
if e != 0 {
|
||||
// for compatibility with old version
|
||||
if e == types.ERR_NOT_FOUND {
|
||||
return -1, -1, 0, ErrNotExist
|
||||
}
|
||||
if e == types.ERR_UNSUPPORT_TYPE {
|
||||
panic("path must be either int(>=0) or string")
|
||||
}
|
||||
return -1, -1, 0, p.syntaxError(e)
|
||||
}
|
||||
|
||||
t := switchRawType(p.s[s])
|
||||
if t == _V_NONE {
|
||||
return -1, -1, 0, ErrNotExist
|
||||
}
|
||||
if t == _V_NUMBER {
|
||||
p.p = 1 + backward(p.s, p.p-1)
|
||||
}
|
||||
return s, p.p, int(t), nil
|
||||
}
|
||||
|
||||
// ValidSyntax check if a json has a valid JSON syntax,
|
||||
// while not validate UTF-8 charset
|
||||
func _ValidSyntax(json string) bool {
|
||||
p := NewParserObj(json)
|
||||
_, e := p.skip()
|
||||
if e != 0 {
|
||||
return false
|
||||
}
|
||||
if skipBlank(p.s, p.p) != -int(types.ERR_EOF) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// SkipFast skip a json value in fast-skip algs,
|
||||
// while not strictly validate JSON syntax and UTF-8 charset.
|
||||
func _SkipFast(src string, i int) (int, int, error) {
|
||||
p := NewParserObj(src)
|
||||
p.p = i
|
||||
s, e := p.skipFast()
|
||||
if e != 0 {
|
||||
return -1, -1, p.ExportError(e)
|
||||
}
|
||||
t := switchRawType(p.s[s])
|
||||
if t == _V_NONE {
|
||||
return -1, -1, ErrNotExist
|
||||
}
|
||||
if t == _V_NUMBER {
|
||||
p.p = 1 + backward(p.s, p.p-1)
|
||||
}
|
||||
return s, p.p, nil
|
||||
}
|
||||
|
||||
2
vendor/github.com/bytedance/sonic/compat.go
generated
vendored
2
vendor/github.com/bytedance/sonic/compat.go
generated
vendored
@@ -1,4 +1,4 @@
|
||||
// +build !amd64 !go1.16 go1.23
|
||||
// +build !amd64 go1.21
|
||||
|
||||
/*
|
||||
* Copyright 2021 ByteDance Inc.
|
||||
|
||||
78
vendor/github.com/bytedance/sonic/decoder/decoder_compat.go
generated
vendored
78
vendor/github.com/bytedance/sonic/decoder/decoder_compat.go
generated
vendored
@@ -1,4 +1,4 @@
|
||||
// +build !amd64 !go1.16 go1.23
|
||||
// +build !amd64 go1.21
|
||||
|
||||
/*
|
||||
* Copyright 2023 ByteDance Inc.
|
||||
@@ -14,34 +14,28 @@
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
*/
|
||||
|
||||
package decoder
|
||||
|
||||
import (
|
||||
`bytes`
|
||||
`encoding/json`
|
||||
`io`
|
||||
`reflect`
|
||||
`unsafe`
|
||||
|
||||
`github.com/bytedance/sonic/internal/native/types`
|
||||
`github.com/bytedance/sonic/option`
|
||||
`encoding/json`
|
||||
`bytes`
|
||||
`reflect`
|
||||
`github.com/bytedance/sonic/internal/native/types`
|
||||
`github.com/bytedance/sonic/option`
|
||||
`io`
|
||||
)
|
||||
|
||||
func init() {
|
||||
println("WARNING: sonic only supports Go1.16~1.22 && CPU amd64, but your environment is not suitable")
|
||||
}
|
||||
|
||||
const (
|
||||
_F_use_int64 = 0
|
||||
_F_disable_urc = 2
|
||||
_F_disable_unknown = 3
|
||||
_F_copy_string = 4
|
||||
|
||||
_F_use_number = types.B_USE_NUMBER
|
||||
_F_validate_string = types.B_VALIDATE_STRING
|
||||
_F_allow_control = types.B_ALLOW_CONTROL
|
||||
_F_use_int64 = iota
|
||||
_F_use_number
|
||||
_F_disable_urc
|
||||
_F_disable_unknown
|
||||
_F_copy_string
|
||||
_F_validate_string
|
||||
|
||||
_F_allow_control = 31
|
||||
)
|
||||
|
||||
type Options uint64
|
||||
@@ -112,10 +106,10 @@ func (self *Decoder) CheckTrailings() error {
|
||||
func (self *Decoder) Decode(val interface{}) error {
|
||||
r := bytes.NewBufferString(self.s)
|
||||
dec := json.NewDecoder(r)
|
||||
if (self.f & uint64(OptionUseNumber)) != 0 {
|
||||
if (self.f | uint64(OptionUseNumber)) != 0 {
|
||||
dec.UseNumber()
|
||||
}
|
||||
if (self.f & uint64(OptionDisableUnknown)) != 0 {
|
||||
if (self.f | uint64(OptionDisableUnknown)) != 0 {
|
||||
dec.DisallowUnknownFields()
|
||||
}
|
||||
return dec.Decode(val)
|
||||
@@ -169,26 +163,34 @@ func Pretouch(vt reflect.Type, opts ...option.CompileOption) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type StreamDecoder = json.Decoder
|
||||
type StreamDecoder struct {
|
||||
r io.Reader
|
||||
buf []byte
|
||||
scanp int
|
||||
scanned int64
|
||||
err error
|
||||
Decoder
|
||||
}
|
||||
|
||||
// NewStreamDecoder adapts to encoding/json.NewDecoder API.
|
||||
//
|
||||
// NewStreamDecoder returns a new decoder that reads from r.
|
||||
func NewStreamDecoder(r io.Reader) *StreamDecoder {
|
||||
return json.NewDecoder(r)
|
||||
return &StreamDecoder{r : r}
|
||||
}
|
||||
|
||||
// SyntaxError represents json syntax error
|
||||
type SyntaxError json.SyntaxError
|
||||
|
||||
// Description
|
||||
func (s SyntaxError) Description() string {
|
||||
return (*json.SyntaxError)(unsafe.Pointer(&s)).Error()
|
||||
}
|
||||
// Error
|
||||
func (s SyntaxError) Error() string {
|
||||
return (*json.SyntaxError)(unsafe.Pointer(&s)).Error()
|
||||
// Decode decodes input stream into val with corresponding data.
|
||||
// Redundantly bytes may be read and left in its buffer, and can be used at next call.
|
||||
// Either io error from underlying io.Reader (except io.EOF)
|
||||
// or syntax error from data will be recorded and stop subsequently decoding.
|
||||
func (self *StreamDecoder) Decode(val interface{}) (err error) {
|
||||
dec := json.NewDecoder(self.r)
|
||||
if (self.f | uint64(OptionUseNumber)) != 0 {
|
||||
dec.UseNumber()
|
||||
}
|
||||
if (self.f | uint64(OptionDisableUnknown)) != 0 {
|
||||
dec.DisallowUnknownFields()
|
||||
}
|
||||
return dec.Decode(val)
|
||||
}
|
||||
|
||||
// MismatchTypeError represents dismatching between json and object
|
||||
type MismatchTypeError json.UnmarshalTypeError
|
||||
73
vendor/github.com/bytedance/sonic/encoder/encoder_compat.go
generated
vendored
73
vendor/github.com/bytedance/sonic/encoder/encoder_compat.go
generated
vendored
@@ -1,4 +1,4 @@
|
||||
// +build !amd64 !go1.16 go1.23
|
||||
// +build !amd64 go1.21
|
||||
|
||||
/*
|
||||
* Copyright 2023 ByteDance Inc.
|
||||
@@ -27,13 +27,6 @@ import (
|
||||
`github.com/bytedance/sonic/option`
|
||||
)
|
||||
|
||||
func init() {
|
||||
println("WARNING:(encoder) sonic only supports Go1.16~1.22 && CPU amd64, but your environment is not suitable")
|
||||
}
|
||||
|
||||
// EnableFallback indicates if encoder use fallback
|
||||
const EnableFallback = true
|
||||
|
||||
// Options is a set of encoding options.
|
||||
type Options uint64
|
||||
|
||||
@@ -44,8 +37,6 @@ const (
|
||||
bitNoQuoteTextMarshaler
|
||||
bitNoNullSliceOrMap
|
||||
bitValidateString
|
||||
bitNoValidateJSONMarshaler
|
||||
bitNoEncoderNewline
|
||||
|
||||
// used for recursive compile
|
||||
bitPointerValue = 63
|
||||
@@ -77,13 +68,6 @@ const (
|
||||
// ValidateString indicates that encoder should validate the input string
|
||||
// before encoding it into JSON.
|
||||
ValidateString Options = 1 << bitValidateString
|
||||
|
||||
// NoValidateJSONMarshaler indicates that the encoder should not validate the output string
|
||||
// after encoding the JSONMarshaler to JSON.
|
||||
NoValidateJSONMarshaler Options = 1 << bitNoValidateJSONMarshaler
|
||||
|
||||
// NoEncoderNewline indicates that the encoder should not add a newline after every message
|
||||
NoEncoderNewline Options = 1 << bitNoEncoderNewline
|
||||
|
||||
// CompatibleWithStd is used to be compatible with std encoder.
|
||||
CompatibleWithStd Options = SortMapKeys | EscapeHTML | CompactMarshaler
|
||||
@@ -128,24 +112,6 @@ func (self *Encoder) SetValidateString(f bool) {
|
||||
}
|
||||
}
|
||||
|
||||
// SetNoValidateJSONMarshaler specifies if option NoValidateJSONMarshaler opens
|
||||
func (self *Encoder) SetNoValidateJSONMarshaler(f bool) {
|
||||
if f {
|
||||
self.Opts |= NoValidateJSONMarshaler
|
||||
} else {
|
||||
self.Opts &= ^NoValidateJSONMarshaler
|
||||
}
|
||||
}
|
||||
|
||||
// SetNoEncoderNewline specifies if option NoEncoderNewline opens
|
||||
func (self *Encoder) SetNoEncoderNewline(f bool) {
|
||||
if f {
|
||||
self.Opts |= NoEncoderNewline
|
||||
} else {
|
||||
self.Opts &= ^NoEncoderNewline
|
||||
}
|
||||
}
|
||||
|
||||
// SetCompactMarshaler specifies if option CompactMarshaler opens
|
||||
func (self *Encoder) SetCompactMarshaler(f bool) {
|
||||
if f {
|
||||
@@ -191,19 +157,15 @@ func Encode(val interface{}, opts Options) ([]byte, error) {
|
||||
// EncodeInto is like Encode but uses a user-supplied buffer instead of allocating
|
||||
// a new one.
|
||||
func EncodeInto(buf *[]byte, val interface{}, opts Options) error {
|
||||
if buf == nil {
|
||||
panic("user-supplied buffer buf is nil")
|
||||
}
|
||||
w := bytes.NewBuffer(*buf)
|
||||
enc := json.NewEncoder(w)
|
||||
enc.SetEscapeHTML((opts & EscapeHTML) != 0)
|
||||
err := enc.Encode(val)
|
||||
*buf = w.Bytes()
|
||||
l := len(*buf)
|
||||
if l > 0 && (opts & NoEncoderNewline != 0) && (*buf)[l-1] == '\n' {
|
||||
*buf = (*buf)[:l-1]
|
||||
}
|
||||
return err
|
||||
if buf == nil {
|
||||
panic("user-supplied buffer buf is nil")
|
||||
}
|
||||
w := bytes.NewBuffer(*buf)
|
||||
enc := json.NewEncoder(w)
|
||||
enc.SetEscapeHTML((opts & EscapeHTML) != 0)
|
||||
err := enc.Encode(val)
|
||||
*buf = w.Bytes()
|
||||
return err
|
||||
}
|
||||
|
||||
// HTMLEscape appends to dst the JSON-encoded src with <, >, &, U+2028 and U+2029
|
||||
@@ -250,12 +212,23 @@ func Valid(data []byte) (ok bool, start int) {
|
||||
}
|
||||
|
||||
// StreamEncoder uses io.Writer as
|
||||
type StreamEncoder = json.Encoder
|
||||
type StreamEncoder struct {
|
||||
w io.Writer
|
||||
Encoder
|
||||
}
|
||||
|
||||
// NewStreamEncoder adapts to encoding/json.NewDecoder API.
|
||||
//
|
||||
// NewStreamEncoder returns a new encoder that write to w.
|
||||
func NewStreamEncoder(w io.Writer) *StreamEncoder {
|
||||
return json.NewEncoder(w)
|
||||
return &StreamEncoder{w: w}
|
||||
}
|
||||
|
||||
// Encode encodes interface{} as JSON to io.Writer
|
||||
func (enc *StreamEncoder) Encode(val interface{}) (err error) {
|
||||
jenc := json.NewEncoder(enc.w)
|
||||
jenc.SetEscapeHTML((enc.Opts & EscapeHTML) != 0)
|
||||
jenc.SetIndent(enc.prefix, enc.indent)
|
||||
err = jenc.Encode(val)
|
||||
return err
|
||||
}
|
||||
|
||||
2
vendor/github.com/bytedance/sonic/internal/encoder/compiler.go
generated
vendored
2
vendor/github.com/bytedance/sonic/internal/encoder/compiler.go
generated
vendored
@@ -831,7 +831,7 @@ func (self *_Compiler) compileStructFieldZero(p *_Program, vt reflect.Type) {
|
||||
case reflect.Float32 : p.add(_OP_is_zero_4)
|
||||
case reflect.Float64 : p.add(_OP_is_zero_8)
|
||||
case reflect.String : p.add(_OP_is_nil_p1)
|
||||
case reflect.Interface : p.add(_OP_is_nil)
|
||||
case reflect.Interface : p.add(_OP_is_nil_p1)
|
||||
case reflect.Map : p.add(_OP_is_zero_map)
|
||||
case reflect.Ptr : p.add(_OP_is_nil)
|
||||
case reflect.Slice : p.add(_OP_is_nil_p1)
|
||||
|
||||
33
vendor/github.com/bytedance/sonic/internal/encoder/encoder.go
generated
vendored
33
vendor/github.com/bytedance/sonic/internal/encoder/encoder.go
generated
vendored
@@ -40,8 +40,6 @@ const (
|
||||
bitNoQuoteTextMarshaler
|
||||
bitNoNullSliceOrMap
|
||||
bitValidateString
|
||||
bitNoValidateJSONMarshaler
|
||||
bitNoEncoderNewline
|
||||
|
||||
// used for recursive compile
|
||||
bitPointerValue = 63
|
||||
@@ -73,13 +71,6 @@ const (
|
||||
// ValidateString indicates that encoder should validate the input string
|
||||
// before encoding it into JSON.
|
||||
ValidateString Options = 1 << bitValidateString
|
||||
|
||||
// NoValidateJSONMarshaler indicates that the encoder should not validate the output string
|
||||
// after encoding the JSONMarshaler to JSON.
|
||||
NoValidateJSONMarshaler Options = 1 << bitNoValidateJSONMarshaler
|
||||
|
||||
// NoEncoderNewline indicates that the encoder should not add a newline after every message
|
||||
NoEncoderNewline Options = 1 << bitNoEncoderNewline
|
||||
|
||||
// CompatibleWithStd is used to be compatible with std encoder.
|
||||
CompatibleWithStd Options = SortMapKeys | EscapeHTML | CompactMarshaler
|
||||
@@ -124,25 +115,6 @@ func (self *Encoder) SetValidateString(f bool) {
|
||||
}
|
||||
}
|
||||
|
||||
// SetNoValidateJSONMarshaler specifies if option NoValidateJSONMarshaler opens
|
||||
func (self *Encoder) SetNoValidateJSONMarshaler(f bool) {
|
||||
if f {
|
||||
self.Opts |= NoValidateJSONMarshaler
|
||||
} else {
|
||||
self.Opts &= ^NoValidateJSONMarshaler
|
||||
}
|
||||
}
|
||||
|
||||
// SetNoEncoderNewline specifies if option NoEncoderNewline opens
|
||||
func (self *Encoder) SetNoEncoderNewline(f bool) {
|
||||
if f {
|
||||
self.Opts |= NoEncoderNewline
|
||||
} else {
|
||||
self.Opts &= ^NoEncoderNewline
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// SetCompactMarshaler specifies if option CompactMarshaler opens
|
||||
func (self *Encoder) SetCompactMarshaler(f bool) {
|
||||
if f {
|
||||
@@ -320,6 +292,7 @@ func Pretouch(vt reflect.Type, opts ...option.CompileOption) error {
|
||||
cfg := option.DefaultCompileOptions()
|
||||
for _, opt := range opts {
|
||||
opt(&cfg)
|
||||
break
|
||||
}
|
||||
return pretouchRec(map[reflect.Type]uint8{vt: 0}, cfg)
|
||||
}
|
||||
@@ -337,7 +310,7 @@ func Valid(data []byte) (ok bool, start int) {
|
||||
s := rt.Mem2Str(data)
|
||||
p := 0
|
||||
m := types.NewStateMachine()
|
||||
ret := native.ValidateOne(&s, &p, m, types.F_VALIDATE_STRING)
|
||||
ret := native.ValidateOne(&s, &p, m)
|
||||
types.FreeStateMachine(m)
|
||||
|
||||
if ret < 0 {
|
||||
@@ -352,4 +325,4 @@ func Valid(data []byte) (ok bool, start int) {
|
||||
}
|
||||
|
||||
return true, ret
|
||||
}
|
||||
}
|
||||
15
vendor/github.com/bytedance/sonic/internal/encoder/stream.go
generated
vendored
15
vendor/github.com/bytedance/sonic/internal/encoder/stream.go
generated
vendored
@@ -36,8 +36,7 @@ func NewStreamEncoder(w io.Writer) *StreamEncoder {
|
||||
|
||||
// Encode encodes interface{} as JSON to io.Writer
|
||||
func (enc *StreamEncoder) Encode(val interface{}) (err error) {
|
||||
buf := newBytes()
|
||||
out := buf
|
||||
out := newBytes()
|
||||
|
||||
/* encode into the buffer */
|
||||
err = EncodeInto(&out, val, enc.Opts)
|
||||
@@ -55,9 +54,7 @@ func (enc *StreamEncoder) Encode(val interface{}) (err error) {
|
||||
}
|
||||
|
||||
// according to standard library, terminate each value with a newline...
|
||||
if enc.Opts & NoEncoderNewline == 0 {
|
||||
buf.WriteByte('\n')
|
||||
}
|
||||
buf.WriteByte('\n')
|
||||
|
||||
/* copy into io.Writer */
|
||||
_, err = io.Copy(enc.w, buf)
|
||||
@@ -78,12 +75,10 @@ func (enc *StreamEncoder) Encode(val interface{}) (err error) {
|
||||
}
|
||||
|
||||
// according to standard library, terminate each value with a newline...
|
||||
if enc.Opts & NoEncoderNewline == 0 {
|
||||
enc.w.Write([]byte{'\n'})
|
||||
}
|
||||
enc.w.Write([]byte{'\n'})
|
||||
}
|
||||
|
||||
free_bytes:
|
||||
freeBytes(buf)
|
||||
freeBytes(out)
|
||||
return err
|
||||
}
|
||||
}
|
||||
12
vendor/github.com/bytedance/sonic/internal/jit/assembler_amd64.go
generated
vendored
12
vendor/github.com/bytedance/sonic/internal/jit/assembler_amd64.go
generated
vendored
@@ -72,6 +72,18 @@ func (self *BaseAssembler) NOPn(n int) {
|
||||
}
|
||||
}
|
||||
|
||||
func (self *BaseAssembler) StorePtr(ptr int64, to obj.Addr, tmp obj.Addr) {
|
||||
if (to.Type != obj.TYPE_MEM) || (tmp.Type != obj.TYPE_REG) {
|
||||
panic("must store imm to memory, tmp must be register")
|
||||
}
|
||||
if (ptr >> 32) != 0 {
|
||||
self.Emit("MOVQ", Imm(ptr), tmp)
|
||||
self.Emit("MOVQ", tmp, to)
|
||||
} else {
|
||||
self.Emit("MOVQ", Imm(ptr), to);
|
||||
}
|
||||
}
|
||||
|
||||
func (self *BaseAssembler) Byte(v ...byte) {
|
||||
for ; len(v) >= 8; v = v[8:] { self.From("QUAD", Imm(rt.Get64(v))) }
|
||||
for ; len(v) >= 4; v = v[4:] { self.From("LONG", Imm(int64(rt.Get32(v)))) }
|
||||
|
||||
7
vendor/github.com/bytedance/sonic/internal/jit/runtime.go
generated
vendored
7
vendor/github.com/bytedance/sonic/internal/jit/runtime.go
generated
vendored
@@ -24,6 +24,11 @@ import (
|
||||
`github.com/twitchyliquid64/golang-asm/obj`
|
||||
)
|
||||
|
||||
//go:noescape
|
||||
//go:linkname getitab runtime.getitab
|
||||
//goland:noinspection ALL
|
||||
func getitab(inter *rt.GoType, typ *rt.GoType, canfail bool) *rt.GoItab
|
||||
|
||||
func Func(f interface{}) obj.Addr {
|
||||
if p := rt.UnpackEface(f); p.Type.Kind() != reflect.Func {
|
||||
panic("f is not a function")
|
||||
@@ -37,7 +42,7 @@ func Type(t reflect.Type) obj.Addr {
|
||||
}
|
||||
|
||||
func Itab(i *rt.GoType, t reflect.Type) obj.Addr {
|
||||
return Imm(int64(uintptr(unsafe.Pointer(rt.Getitab(rt.IfaceType(i), rt.UnpackType(t), false)))))
|
||||
return Imm(int64(uintptr(unsafe.Pointer(getitab(i, rt.UnpackType(t), false)))))
|
||||
}
|
||||
|
||||
func Gitab(i *rt.GoItab) obj.Addr {
|
||||
|
||||
227
vendor/github.com/bytedance/sonic/internal/native/dispatch_amd64.go
generated
vendored
227
vendor/github.com/bytedance/sonic/internal/native/dispatch_amd64.go
generated
vendored
@@ -24,10 +24,12 @@ import (
|
||||
`github.com/bytedance/sonic/internal/native/avx2`
|
||||
`github.com/bytedance/sonic/internal/native/sse`
|
||||
`github.com/bytedance/sonic/internal/native/types`
|
||||
`github.com/bytedance/sonic/internal/rt`
|
||||
)
|
||||
|
||||
const MaxFrameSize uintptr = 400
|
||||
const (
|
||||
MaxFrameSize uintptr = 400
|
||||
BufPaddingSize int = 64
|
||||
)
|
||||
|
||||
var (
|
||||
S_f64toa uintptr
|
||||
@@ -59,221 +61,142 @@ var (
|
||||
S_skip_number uintptr
|
||||
)
|
||||
|
||||
var (
|
||||
__Quote func(s unsafe.Pointer, nb int, dp unsafe.Pointer, dn unsafe.Pointer, flags uint64) int
|
||||
|
||||
__Unquote func(s unsafe.Pointer, nb int, dp unsafe.Pointer, ep unsafe.Pointer, flags uint64) int
|
||||
|
||||
__HTMLEscape func(s unsafe.Pointer, nb int, dp unsafe.Pointer, dn unsafe.Pointer) int
|
||||
|
||||
__Value func(s unsafe.Pointer, n int, p int, v unsafe.Pointer, flags uint64) int
|
||||
|
||||
__SkipOne func(s unsafe.Pointer, p unsafe.Pointer, m unsafe.Pointer, flags uint64) int
|
||||
|
||||
__SkipOneFast func(s unsafe.Pointer, p unsafe.Pointer) int
|
||||
|
||||
__GetByPath func(s unsafe.Pointer, p unsafe.Pointer, path unsafe.Pointer, m unsafe.Pointer) int
|
||||
|
||||
__ValidateOne func(s unsafe.Pointer, p unsafe.Pointer, m unsafe.Pointer, flags uint64) int
|
||||
|
||||
__I64toa func(out unsafe.Pointer, val int64) (ret int)
|
||||
|
||||
__U64toa func(out unsafe.Pointer, val uint64) (ret int)
|
||||
|
||||
__F64toa func(out unsafe.Pointer, val float64) (ret int)
|
||||
|
||||
__F32toa func(out unsafe.Pointer, val float32) (ret int)
|
||||
|
||||
__ValidateUTF8 func(s unsafe.Pointer, p unsafe.Pointer, m unsafe.Pointer) (ret int)
|
||||
|
||||
__ValidateUTF8Fast func(s unsafe.Pointer) (ret int)
|
||||
)
|
||||
//go:nosplit
|
||||
//go:noescape
|
||||
//goland:noinspection GoUnusedParameter
|
||||
func Quote(s unsafe.Pointer, nb int, dp unsafe.Pointer, dn *int, flags uint64) int
|
||||
|
||||
//go:nosplit
|
||||
func Quote(s unsafe.Pointer, nb int, dp unsafe.Pointer, dn *int, flags uint64) int {
|
||||
return __Quote(rt.NoEscape(unsafe.Pointer(s)), nb, rt.NoEscape(unsafe.Pointer(dp)), rt.NoEscape(unsafe.Pointer(dn)), flags)
|
||||
}
|
||||
//go:noescape
|
||||
//goland:noinspection GoUnusedParameter
|
||||
func Unquote(s unsafe.Pointer, nb int, dp unsafe.Pointer, ep *int, flags uint64) int
|
||||
|
||||
//go:nosplit
|
||||
func Unquote(s unsafe.Pointer, nb int, dp unsafe.Pointer, ep *int, flags uint64) int {
|
||||
return __Unquote(rt.NoEscape(unsafe.Pointer(s)), nb, rt.NoEscape(unsafe.Pointer(dp)), rt.NoEscape(unsafe.Pointer(ep)), flags)
|
||||
}
|
||||
//go:noescape
|
||||
//goland:noinspection GoUnusedParameter
|
||||
func HTMLEscape(s unsafe.Pointer, nb int, dp unsafe.Pointer, dn *int) int
|
||||
|
||||
//go:nosplit
|
||||
func HTMLEscape(s unsafe.Pointer, nb int, dp unsafe.Pointer, dn *int) int {
|
||||
return __HTMLEscape(rt.NoEscape(unsafe.Pointer(s)), nb, rt.NoEscape(unsafe.Pointer(dp)), rt.NoEscape(unsafe.Pointer(dn)))
|
||||
}
|
||||
//go:noescape
|
||||
//goland:noinspection GoUnusedParameter
|
||||
func Value(s unsafe.Pointer, n int, p int, v *types.JsonState, flags uint64) int
|
||||
|
||||
//go:nosplit
|
||||
func Value(s unsafe.Pointer, n int, p int, v *types.JsonState, flags uint64) int {
|
||||
return __Value(rt.NoEscape(unsafe.Pointer(s)), n, p, rt.NoEscape(unsafe.Pointer(v)), flags)
|
||||
}
|
||||
//go:noescape
|
||||
//goland:noinspection GoUnusedParameter
|
||||
func SkipOne(s *string, p *int, m *types.StateMachine, flags uint64) int
|
||||
|
||||
//go:nosplit
|
||||
func SkipOne(s *string, p *int, m *types.StateMachine, flags uint64) int {
|
||||
return __SkipOne(rt.NoEscape(unsafe.Pointer(s)), rt.NoEscape(unsafe.Pointer(p)), rt.NoEscape(unsafe.Pointer(m)), flags)
|
||||
}
|
||||
//go:noescape
|
||||
//goland:noinspection GoUnusedParameter
|
||||
func SkipOneFast(s *string, p *int) int
|
||||
|
||||
//go:nosplit
|
||||
func SkipOneFast(s *string, p *int) int {
|
||||
return __SkipOneFast(rt.NoEscape(unsafe.Pointer(s)), rt.NoEscape(unsafe.Pointer(p)))
|
||||
}
|
||||
//go:noescape
|
||||
//goland:noinspection GoUnusedParameter
|
||||
func GetByPath(s *string, p *int, path *[]interface{}, m *types.StateMachine) int
|
||||
|
||||
//go:nosplit
|
||||
func GetByPath(s *string, p *int, path *[]interface{}, m *types.StateMachine) int {
|
||||
return __GetByPath(rt.NoEscape(unsafe.Pointer(s)), rt.NoEscape(unsafe.Pointer(p)), rt.NoEscape(unsafe.Pointer(path)), rt.NoEscape(unsafe.Pointer(m)))
|
||||
}
|
||||
//go:noescape
|
||||
//goland:noinspection GoUnusedParameter
|
||||
func ValidateOne(s *string, p *int, m *types.StateMachine) int
|
||||
|
||||
//go:nosplit
|
||||
func ValidateOne(s *string, p *int, m *types.StateMachine, flags uint64) int {
|
||||
return __ValidateOne(rt.NoEscape(unsafe.Pointer(s)), rt.NoEscape(unsafe.Pointer(p)), rt.NoEscape(unsafe.Pointer(m)), flags)
|
||||
}
|
||||
//go:noescape
|
||||
//goland:noinspection GoUnusedParameter
|
||||
func I64toa(out *byte, val int64) (ret int)
|
||||
|
||||
//go:nosplit
|
||||
func I64toa(out *byte, val int64) (ret int) {
|
||||
return __I64toa(rt.NoEscape(unsafe.Pointer(out)), val)
|
||||
}
|
||||
//go:noescape
|
||||
//goland:noinspection GoUnusedParameter
|
||||
func U64toa(out *byte, val uint64) (ret int)
|
||||
|
||||
//go:nosplit
|
||||
func U64toa(out *byte, val uint64) (ret int) {
|
||||
return __U64toa(rt.NoEscape(unsafe.Pointer(out)), val)
|
||||
}
|
||||
//go:noescape
|
||||
//goland:noinspection GoUnusedParameter
|
||||
func F64toa(out *byte, val float64) (ret int)
|
||||
|
||||
//go:nosplit
|
||||
func F64toa(out *byte, val float64) (ret int) {
|
||||
return __F64toa(rt.NoEscape(unsafe.Pointer(out)), val)
|
||||
}
|
||||
//go:noescape
|
||||
//goland:noinspection GoUnusedParameter
|
||||
func ValidateUTF8(s *string, p *int, m *types.StateMachine) (ret int)
|
||||
|
||||
//go:nosplit
|
||||
func F32toa(out *byte, val float32) (ret int) {
|
||||
return __F32toa(rt.NoEscape(unsafe.Pointer(out)), val)
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
func ValidateUTF8(s *string, p *int, m *types.StateMachine) (ret int) {
|
||||
return __ValidateUTF8(rt.NoEscape(unsafe.Pointer(s)), rt.NoEscape(unsafe.Pointer(p)), rt.NoEscape(unsafe.Pointer(m)))
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
func ValidateUTF8Fast(s *string) (ret int) {
|
||||
return __ValidateUTF8Fast(rt.NoEscape(unsafe.Pointer(s)))
|
||||
}
|
||||
|
||||
func useSSE() {
|
||||
sse.Use()
|
||||
S_f64toa = sse.S_f64toa
|
||||
__F64toa = sse.F_f64toa
|
||||
S_f32toa = sse.S_f32toa
|
||||
__F64toa = sse.F_f64toa
|
||||
S_i64toa = sse.S_i64toa
|
||||
__I64toa = sse.F_i64toa
|
||||
S_u64toa = sse.S_u64toa
|
||||
__U64toa = sse.F_u64toa
|
||||
S_lspace = sse.S_lspace
|
||||
S_quote = sse.S_quote
|
||||
__Quote = sse.F_quote
|
||||
S_unquote = sse.S_unquote
|
||||
__Unquote = sse.F_unquote
|
||||
S_value = sse.S_value
|
||||
__Value = sse.F_value
|
||||
S_vstring = sse.S_vstring
|
||||
S_vnumber = sse.S_vnumber
|
||||
S_vsigned = sse.S_vsigned
|
||||
S_vunsigned = sse.S_vunsigned
|
||||
S_skip_one = sse.S_skip_one
|
||||
__SkipOne = sse.F_skip_one
|
||||
__SkipOneFast = sse.F_skip_one_fast
|
||||
S_skip_array = sse.S_skip_array
|
||||
S_skip_object = sse.S_skip_object
|
||||
S_skip_number = sse.S_skip_number
|
||||
S_get_by_path = sse.S_get_by_path
|
||||
__GetByPath = sse.F_get_by_path
|
||||
__HTMLEscape = sse.F_html_escape
|
||||
__ValidateOne = sse.F_validate_one
|
||||
__ValidateUTF8= sse.F_validate_utf8
|
||||
__ValidateUTF8Fast = sse.F_validate_utf8_fast
|
||||
}
|
||||
|
||||
//go:noescape
|
||||
//goland:noinspection GoUnusedParameter
|
||||
func ValidateUTF8Fast(s *string) (ret int)
|
||||
|
||||
func useAVX() {
|
||||
avx.Use()
|
||||
S_f64toa = avx.S_f64toa
|
||||
__F64toa = avx.F_f64toa
|
||||
S_f32toa = avx.S_f32toa
|
||||
__F64toa = avx.F_f64toa
|
||||
S_i64toa = avx.S_i64toa
|
||||
__I64toa = avx.F_i64toa
|
||||
S_u64toa = avx.S_u64toa
|
||||
__U64toa = avx.F_u64toa
|
||||
S_lspace = avx.S_lspace
|
||||
S_quote = avx.S_quote
|
||||
__Quote = avx.F_quote
|
||||
S_unquote = avx.S_unquote
|
||||
__Unquote = avx.F_unquote
|
||||
S_value = avx.S_value
|
||||
__Value = avx.F_value
|
||||
S_vstring = avx.S_vstring
|
||||
S_vnumber = avx.S_vnumber
|
||||
S_vsigned = avx.S_vsigned
|
||||
S_vunsigned = avx.S_vunsigned
|
||||
S_skip_one = avx.S_skip_one
|
||||
__SkipOne = avx.F_skip_one
|
||||
__SkipOneFast = avx.F_skip_one_fast
|
||||
S_skip_one_fast = avx.S_skip_one_fast
|
||||
S_skip_array = avx.S_skip_array
|
||||
S_skip_object = avx.S_skip_object
|
||||
S_skip_number = avx.S_skip_number
|
||||
S_get_by_path = avx.S_get_by_path
|
||||
__GetByPath = avx.F_get_by_path
|
||||
__HTMLEscape = avx.F_html_escape
|
||||
__ValidateOne = avx.F_validate_one
|
||||
__ValidateUTF8= avx.F_validate_utf8
|
||||
__ValidateUTF8Fast = avx.F_validate_utf8_fast
|
||||
}
|
||||
|
||||
func useAVX2() {
|
||||
avx2.Use()
|
||||
S_f64toa = avx2.S_f64toa
|
||||
__F64toa = avx2.F_f64toa
|
||||
S_f32toa = avx2.S_f32toa
|
||||
__F64toa = avx2.F_f64toa
|
||||
S_i64toa = avx2.S_i64toa
|
||||
__I64toa = avx2.F_i64toa
|
||||
S_u64toa = avx2.S_u64toa
|
||||
__U64toa = avx2.F_u64toa
|
||||
S_lspace = avx2.S_lspace
|
||||
S_quote = avx2.S_quote
|
||||
__Quote = avx2.F_quote
|
||||
S_unquote = avx2.S_unquote
|
||||
__Unquote = avx2.F_unquote
|
||||
S_value = avx2.S_value
|
||||
__Value = avx2.F_value
|
||||
S_vstring = avx2.S_vstring
|
||||
S_vnumber = avx2.S_vnumber
|
||||
S_vsigned = avx2.S_vsigned
|
||||
S_vunsigned = avx2.S_vunsigned
|
||||
S_skip_one = avx2.S_skip_one
|
||||
__SkipOne = avx2.F_skip_one
|
||||
__SkipOneFast = avx2.F_skip_one_fast
|
||||
S_skip_one_fast = avx2.S_skip_one_fast
|
||||
S_skip_array = avx2.S_skip_array
|
||||
S_skip_object = avx2.S_skip_object
|
||||
S_skip_number = avx2.S_skip_number
|
||||
S_get_by_path = avx2.S_get_by_path
|
||||
__GetByPath = avx2.F_get_by_path
|
||||
__HTMLEscape = avx2.F_html_escape
|
||||
__ValidateOne = avx2.F_validate_one
|
||||
__ValidateUTF8= avx2.F_validate_utf8
|
||||
__ValidateUTF8Fast = avx2.F_validate_utf8_fast
|
||||
}
|
||||
|
||||
func useSSE() {
|
||||
S_f64toa = sse.S_f64toa
|
||||
S_f32toa = sse.S_f32toa
|
||||
S_i64toa = sse.S_i64toa
|
||||
S_u64toa = sse.S_u64toa
|
||||
S_lspace = sse.S_lspace
|
||||
S_quote = sse.S_quote
|
||||
S_unquote = sse.S_unquote
|
||||
S_value = sse.S_value
|
||||
S_vstring = sse.S_vstring
|
||||
S_vnumber = sse.S_vnumber
|
||||
S_vsigned = sse.S_vsigned
|
||||
S_vunsigned = sse.S_vunsigned
|
||||
S_skip_one = sse.S_skip_one
|
||||
S_skip_one_fast = sse.S_skip_one_fast
|
||||
S_skip_array = sse.S_skip_array
|
||||
S_skip_object = sse.S_skip_object
|
||||
S_skip_number = sse.S_skip_number
|
||||
S_get_by_path = sse.S_get_by_path
|
||||
}
|
||||
|
||||
func init() {
|
||||
if cpu.HasAVX2 {
|
||||
useAVX2()
|
||||
} else if cpu.HasAVX {
|
||||
useAVX()
|
||||
} else if cpu.HasSSE {
|
||||
useSSE()
|
||||
} else {
|
||||
panic("Unsupported CPU, maybe it's too old to run Sonic.")
|
||||
}
|
||||
if cpu.HasAVX2 {
|
||||
useAVX2()
|
||||
} else if cpu.HasAVX {
|
||||
useAVX()
|
||||
} else if cpu.HasSSE {
|
||||
useSSE()
|
||||
} else {
|
||||
panic("Unsupported CPU, maybe it's too old to run Sonic.")
|
||||
}
|
||||
}
|
||||
|
||||
26
vendor/github.com/bytedance/sonic/internal/native/types/types.go
generated
vendored
26
vendor/github.com/bytedance/sonic/internal/native/types/types.go
generated
vendored
@@ -19,7 +19,6 @@ package types
|
||||
import (
|
||||
`fmt`
|
||||
`sync`
|
||||
`unsafe`
|
||||
)
|
||||
|
||||
type ValueType int
|
||||
@@ -29,8 +28,6 @@ type SearchingError uint
|
||||
// NOTE: !NOT MODIFIED ONLY.
|
||||
// This definitions are followed in native/types.h.
|
||||
|
||||
const BufPaddingSize int = 64
|
||||
|
||||
const (
|
||||
V_EOF ValueType = 1
|
||||
V_NULL ValueType = 2
|
||||
@@ -49,23 +46,15 @@ const (
|
||||
)
|
||||
|
||||
const (
|
||||
// for native.Unquote() flags
|
||||
B_DOUBLE_UNQUOTE = 0
|
||||
B_UNICODE_REPLACE = 1
|
||||
|
||||
// for native.Value() flags
|
||||
B_USE_NUMBER = 1
|
||||
B_VALIDATE_STRING = 5
|
||||
B_ALLOW_CONTROL = 31
|
||||
)
|
||||
|
||||
const (
|
||||
F_DOUBLE_UNQUOTE = 1 << B_DOUBLE_UNQUOTE
|
||||
F_UNICODE_REPLACE = 1 << B_UNICODE_REPLACE
|
||||
|
||||
F_USE_NUMBER = 1 << B_USE_NUMBER
|
||||
F_VALIDATE_STRING = 1 << B_VALIDATE_STRING
|
||||
F_ALLOW_CONTROL = 1 << B_ALLOW_CONTROL
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -147,18 +136,3 @@ func FreeStateMachine(fsm *StateMachine) {
|
||||
stackPool.Put(fsm)
|
||||
}
|
||||
|
||||
const MaxDigitNums = 800
|
||||
|
||||
var digitPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return (*byte)(unsafe.Pointer(&[MaxDigitNums]byte{}))
|
||||
},
|
||||
}
|
||||
|
||||
func NewDbuf() *byte {
|
||||
return digitPool.Get().(*byte)
|
||||
}
|
||||
|
||||
func FreeDbuf(p *byte) {
|
||||
digitPool.Put(p)
|
||||
}
|
||||
|
||||
3
vendor/github.com/bytedance/sonic/internal/rt/asm_amd64.s
generated
vendored
3
vendor/github.com/bytedance/sonic/internal/rt/asm_amd64.s
generated
vendored
@@ -1,4 +1,5 @@
|
||||
// +build !noasm,amd64 !appengine,amd64
|
||||
// +build !noasm !appengine
|
||||
// Code generated by asm2asm, DO NOT EDIT·
|
||||
|
||||
#include "go_asm.h"
|
||||
#include "funcdata.h"
|
||||
|
||||
15
vendor/github.com/bytedance/sonic/internal/rt/fastmem.go
generated
vendored
15
vendor/github.com/bytedance/sonic/internal/rt/fastmem.go
generated
vendored
@@ -66,16 +66,15 @@ func FuncAddr(f interface{}) unsafe.Pointer {
|
||||
}
|
||||
}
|
||||
|
||||
//go:nocheckptr
|
||||
func IndexChar(src string, index int) unsafe.Pointer {
|
||||
return unsafe.Pointer(uintptr((*GoString)(unsafe.Pointer(&src)).Ptr) + uintptr(index))
|
||||
}
|
||||
|
||||
//go:nocheckptr
|
||||
func IndexByte(ptr []byte, index int) unsafe.Pointer {
|
||||
return unsafe.Pointer(uintptr((*GoSlice)(unsafe.Pointer(&ptr)).Ptr) + uintptr(index))
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
func GuardSlice(buf *[]byte, n int) {
|
||||
c := cap(*buf)
|
||||
l := len(*buf)
|
||||
@@ -110,16 +109,4 @@ func StrFrom(p unsafe.Pointer, n int64) (s string) {
|
||||
(*GoString)(unsafe.Pointer(&s)).Ptr = p
|
||||
(*GoString)(unsafe.Pointer(&s)).Len = int(n)
|
||||
return
|
||||
}
|
||||
|
||||
// NoEscape hides a pointer from escape analysis. NoEscape is
|
||||
// the identity function but escape analysis doesn't think the
|
||||
// output depends on the input. NoEscape is inlined and currently
|
||||
// compiles down to zero instructions.
|
||||
// USE CAREFULLY!
|
||||
//go:nosplit
|
||||
//goland:noinspection GoVetUnsafePointer
|
||||
func NoEscape(p unsafe.Pointer) unsafe.Pointer {
|
||||
x := uintptr(p)
|
||||
return unsafe.Pointer(x ^ 0)
|
||||
}
|
||||
30
vendor/github.com/bytedance/sonic/internal/rt/fastvalue.go
generated
vendored
30
vendor/github.com/bytedance/sonic/internal/rt/fastvalue.go
generated
vendored
@@ -211,33 +211,3 @@ func findReflectRtypeItab() *GoItab {
|
||||
v := reflect.TypeOf(struct{}{})
|
||||
return (*GoIface)(unsafe.Pointer(&v)).Itab
|
||||
}
|
||||
|
||||
func AssertI2I2(t *GoType, i GoIface) (r GoIface) {
|
||||
inter := IfaceType(t)
|
||||
tab := i.Itab
|
||||
if tab == nil {
|
||||
return
|
||||
}
|
||||
if (*GoInterfaceType)(tab.it) != inter {
|
||||
tab = Getitab(inter, tab.Vt, true)
|
||||
if tab == nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
r.Itab = tab
|
||||
r.Value = i.Value
|
||||
return
|
||||
}
|
||||
|
||||
//go:noescape
|
||||
//go:linkname Getitab runtime.getitab
|
||||
func Getitab(inter *GoInterfaceType, typ *GoType, canfail bool) *GoItab
|
||||
|
||||
|
||||
func GetFuncPC(fn interface{}) uintptr {
|
||||
ft := UnpackEface(fn)
|
||||
if ft.Type.Kind() != reflect.Func {
|
||||
panic("not a function")
|
||||
}
|
||||
return *(*uintptr)(ft.Value)
|
||||
}
|
||||
6
vendor/github.com/bytedance/sonic/internal/rt/int48.go
generated
vendored
6
vendor/github.com/bytedance/sonic/internal/rt/int48.go
generated
vendored
@@ -17,12 +17,12 @@
|
||||
package rt
|
||||
|
||||
const (
|
||||
MinInt48 int64 = -(1 << 47)
|
||||
MaxInt48 int64 = +(1 << 47) - 1
|
||||
MinInt48 = -(1 << 47)
|
||||
MaxInt48 = +(1 << 47) - 1
|
||||
)
|
||||
|
||||
func PackInt(v int) uint64 {
|
||||
if u := uint64(v); int64(v) < MinInt48 || int64(v) > MaxInt48 {
|
||||
if u := uint64(v); v < MinInt48 || v > MaxInt48 {
|
||||
panic("int48 out of range")
|
||||
} else {
|
||||
return ((u >> 63) << 47) | (u & 0x00007fffffffffff)
|
||||
|
||||
103
vendor/github.com/bytedance/sonic/loader/funcdata.go
generated
vendored
103
vendor/github.com/bytedance/sonic/loader/funcdata.go
generated
vendored
@@ -42,13 +42,6 @@ const (
|
||||
_SUB_BUCKETSIZE = _BUCKETSIZE / _SUBBUCKETS
|
||||
)
|
||||
|
||||
// Note: This list must match the list in runtime/symtab.go.
|
||||
const (
|
||||
FuncFlag_TOPFRAME = 1 << iota
|
||||
FuncFlag_SPWRITE
|
||||
FuncFlag_ASM
|
||||
)
|
||||
|
||||
// PCDATA and FUNCDATA table indexes.
|
||||
//
|
||||
// See funcdata.h and $GROOT/src/cmd/internal/objabi/funcdata.go.
|
||||
@@ -148,98 +141,4 @@ func funcNameParts(name string) (string, string, string) {
|
||||
return name, "", ""
|
||||
}
|
||||
return name[:i], "[...]", name[j+1:]
|
||||
}
|
||||
|
||||
|
||||
// func name table format:
|
||||
// nameOff[0] -> namePartA namePartB namePartC \x00
|
||||
// nameOff[1] -> namePartA namePartB namePartC \x00
|
||||
// ...
|
||||
func makeFuncnameTab(funcs []Func) (tab []byte, offs []int32) {
|
||||
offs = make([]int32, len(funcs))
|
||||
offset := 1
|
||||
tab = []byte{0}
|
||||
|
||||
for i, f := range funcs {
|
||||
offs[i] = int32(offset)
|
||||
|
||||
a, b, c := funcNameParts(f.Name)
|
||||
tab = append(tab, a...)
|
||||
tab = append(tab, b...)
|
||||
tab = append(tab, c...)
|
||||
tab = append(tab, 0)
|
||||
offset += len(a) + len(b) + len(c) + 1
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// CU table format:
|
||||
// cuOffsets[0] -> filetabOffset[0] filetabOffset[1] ... filetabOffset[len(CUs[0].fileNames)-1]
|
||||
// cuOffsets[1] -> filetabOffset[len(CUs[0].fileNames)] ... filetabOffset[len(CUs[0].fileNames) + len(CUs[1].fileNames)-1]
|
||||
// ...
|
||||
//
|
||||
// file name table format:
|
||||
// filetabOffset[0] -> CUs[0].fileNames[0] \x00
|
||||
// ...
|
||||
// filetabOffset[len(CUs[0]-1)] -> CUs[0].fileNames[len(CUs[0].fileNames)-1] \x00
|
||||
// ...
|
||||
// filetabOffset[SUM(CUs,fileNames)-1] -> CUs[len(CU)-1].fileNames[len(CUs[len(CU)-1].fileNames)-1] \x00
|
||||
func makeFilenametab(cus []compilationUnit) (cutab []uint32, filetab []byte, cuOffsets []uint32) {
|
||||
cuOffsets = make([]uint32, len(cus))
|
||||
cuOffset := 0
|
||||
fileOffset := 0
|
||||
|
||||
for i, cu := range cus {
|
||||
cuOffsets[i] = uint32(cuOffset)
|
||||
|
||||
for _, name := range cu.fileNames {
|
||||
cutab = append(cutab, uint32(fileOffset))
|
||||
|
||||
fileOffset += len(name) + 1
|
||||
filetab = append(filetab, name...)
|
||||
filetab = append(filetab, 0)
|
||||
}
|
||||
|
||||
cuOffset += len(cu.fileNames)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func writeFuncdata(out *[]byte, funcs []Func) (fstart int, funcdataOffs [][]uint32) {
|
||||
fstart = len(*out)
|
||||
*out = append(*out, byte(0))
|
||||
offs := uint32(1)
|
||||
|
||||
funcdataOffs = make([][]uint32, len(funcs))
|
||||
for i, f := range funcs {
|
||||
|
||||
var writer = func(fd encoding.BinaryMarshaler) {
|
||||
var ab []byte
|
||||
var err error
|
||||
if fd != nil {
|
||||
ab, err = fd.MarshalBinary()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
funcdataOffs[i] = append(funcdataOffs[i], offs)
|
||||
} else {
|
||||
ab = []byte{0}
|
||||
funcdataOffs[i] = append(funcdataOffs[i], _INVALID_FUNCDATA_OFFSET)
|
||||
}
|
||||
*out = append(*out, ab...)
|
||||
offs += uint32(len(ab))
|
||||
}
|
||||
|
||||
writer(f.ArgsPointerMaps)
|
||||
writer(f.LocalsPointerMaps)
|
||||
writer(f.StackObjects)
|
||||
writer(f.InlTree)
|
||||
writer(f.OpenCodedDeferInfo)
|
||||
writer(f.ArgInfo)
|
||||
writer(f.ArgLiveInfo)
|
||||
writer(f.WrapInfo)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
432
vendor/github.com/bytedance/sonic/loader/funcdata_go118.go
generated
vendored
432
vendor/github.com/bytedance/sonic/loader/funcdata_go118.go
generated
vendored
@@ -1,5 +1,4 @@
|
||||
// go:build go1.18 && !go1.20
|
||||
//go:build go1.18 && !go1.20
|
||||
// +build go1.18,!go1.20
|
||||
|
||||
/*
|
||||
@@ -21,13 +20,32 @@
|
||||
package loader
|
||||
|
||||
import (
|
||||
`github.com/bytedance/sonic/loader/internal/rt`
|
||||
`encoding`
|
||||
`os`
|
||||
`unsafe`
|
||||
|
||||
`github.com/bytedance/sonic/internal/rt`
|
||||
)
|
||||
|
||||
const (
|
||||
_Magic uint32 = 0xfffffff0
|
||||
)
|
||||
|
||||
type pcHeader struct {
|
||||
magic uint32 // 0xFFFFFFF0
|
||||
pad1, pad2 uint8 // 0,0
|
||||
minLC uint8 // min instruction size
|
||||
ptrSize uint8 // size of a ptr in bytes
|
||||
nfunc int // number of functions in the module
|
||||
nfiles uint // number of entries in the file tab
|
||||
textStart uintptr // base for function entry PC offsets in this module, equal to moduledata.text
|
||||
funcnameOffset uintptr // offset to the funcnametab variable from pcHeader
|
||||
cuOffset uintptr // offset to the cutab variable from pcHeader
|
||||
filetabOffset uintptr // offset to the filetab variable from pcHeader
|
||||
pctabOffset uintptr // offset to the pctab variable from pcHeader
|
||||
pclnOffset uintptr // offset to the pclntab variable from pcHeader
|
||||
}
|
||||
|
||||
type moduledata struct {
|
||||
pcHeader *pcHeader
|
||||
funcnametab []byte
|
||||
@@ -111,3 +129,413 @@ type _func struct {
|
||||
//
|
||||
// funcdata [nfuncdata]uint32
|
||||
}
|
||||
|
||||
type funcTab struct {
|
||||
entry uint32
|
||||
funcoff uint32
|
||||
}
|
||||
|
||||
type bitVector struct {
|
||||
n int32 // # of bits
|
||||
bytedata *uint8
|
||||
}
|
||||
|
||||
type ptabEntry struct {
|
||||
name int32
|
||||
typ int32
|
||||
}
|
||||
|
||||
type textSection struct {
|
||||
vaddr uintptr // prelinked section vaddr
|
||||
end uintptr // vaddr + section length
|
||||
baseaddr uintptr // relocated section address
|
||||
}
|
||||
|
||||
type modulehash struct {
|
||||
modulename string
|
||||
linktimehash string
|
||||
runtimehash *string
|
||||
}
|
||||
|
||||
// findfuncbucket is an array of these structures.
|
||||
// Each bucket represents 4096 bytes of the text segment.
|
||||
// Each subbucket represents 256 bytes of the text segment.
|
||||
// To find a function given a pc, locate the bucket and subbucket for
|
||||
// that pc. Add together the idx and subbucket value to obtain a
|
||||
// function index. Then scan the functab array starting at that
|
||||
// index to find the target function.
|
||||
// This table uses 20 bytes for every 4096 bytes of code, or ~0.5% overhead.
|
||||
type findfuncbucket struct {
|
||||
idx uint32
|
||||
_SUBBUCKETS [16]byte
|
||||
}
|
||||
|
||||
// func name table format:
|
||||
// nameOff[0] -> namePartA namePartB namePartC \x00
|
||||
// nameOff[1] -> namePartA namePartB namePartC \x00
|
||||
// ...
|
||||
func makeFuncnameTab(funcs []Func) (tab []byte, offs []int32) {
|
||||
offs = make([]int32, len(funcs))
|
||||
offset := 0
|
||||
|
||||
for i, f := range funcs {
|
||||
offs[i] = int32(offset)
|
||||
|
||||
a, b, c := funcNameParts(f.Name)
|
||||
tab = append(tab, a...)
|
||||
tab = append(tab, b...)
|
||||
tab = append(tab, c...)
|
||||
tab = append(tab, 0)
|
||||
offset += len(a) + len(b) + len(c) + 1
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
type compilationUnit struct {
|
||||
fileNames []string
|
||||
}
|
||||
|
||||
// CU table format:
|
||||
// cuOffsets[0] -> filetabOffset[0] filetabOffset[1] ... filetabOffset[len(CUs[0].fileNames)-1]
|
||||
// cuOffsets[1] -> filetabOffset[len(CUs[0].fileNames)] ... filetabOffset[len(CUs[0].fileNames) + len(CUs[1].fileNames)-1]
|
||||
// ...
|
||||
//
|
||||
// file name table format:
|
||||
// filetabOffset[0] -> CUs[0].fileNames[0] \x00
|
||||
// ...
|
||||
// filetabOffset[len(CUs[0]-1)] -> CUs[0].fileNames[len(CUs[0].fileNames)-1] \x00
|
||||
// ...
|
||||
// filetabOffset[SUM(CUs,fileNames)-1] -> CUs[len(CU)-1].fileNames[len(CUs[len(CU)-1].fileNames)-1] \x00
|
||||
func makeFilenametab(cus []compilationUnit) (cutab []uint32, filetab []byte, cuOffsets []uint32) {
|
||||
cuOffsets = make([]uint32, len(cus))
|
||||
cuOffset := 0
|
||||
fileOffset := 0
|
||||
|
||||
for i, cu := range cus {
|
||||
cuOffsets[i] = uint32(cuOffset)
|
||||
|
||||
for _, name := range cu.fileNames {
|
||||
cutab = append(cutab, uint32(fileOffset))
|
||||
|
||||
fileOffset += len(name) + 1
|
||||
filetab = append(filetab, name...)
|
||||
filetab = append(filetab, 0)
|
||||
}
|
||||
|
||||
cuOffset += len(cu.fileNames)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func writeFuncdata(out *[]byte, funcs []Func) (fstart int, funcdataOffs [][]uint32) {
|
||||
fstart = len(*out)
|
||||
*out = append(*out, byte(0))
|
||||
offs := uint32(1)
|
||||
|
||||
funcdataOffs = make([][]uint32, len(funcs))
|
||||
for i, f := range funcs {
|
||||
|
||||
var writer = func(fd encoding.BinaryMarshaler) {
|
||||
var ab []byte
|
||||
var err error
|
||||
if fd != nil {
|
||||
ab, err = fd.MarshalBinary()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
funcdataOffs[i] = append(funcdataOffs[i], offs)
|
||||
} else {
|
||||
ab = []byte{0}
|
||||
funcdataOffs[i] = append(funcdataOffs[i], _INVALID_FUNCDATA_OFFSET)
|
||||
}
|
||||
*out = append(*out, ab...)
|
||||
offs += uint32(len(ab))
|
||||
}
|
||||
|
||||
writer(f.ArgsPointerMaps)
|
||||
writer(f.LocalsPointerMaps)
|
||||
writer(f.StackObjects)
|
||||
writer(f.InlTree)
|
||||
writer(f.OpenCodedDeferInfo)
|
||||
writer(f.ArgInfo)
|
||||
writer(f.ArgLiveInfo)
|
||||
writer(f.WrapInfo)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func makeFtab(funcs []_func, lastFuncSize uint32) (ftab []funcTab) {
|
||||
// Allocate space for the pc->func table. This structure consists of a pc offset
|
||||
// and an offset to the func structure. After that, we have a single pc
|
||||
// value that marks the end of the last function in the binary.
|
||||
var size int64 = int64(len(funcs)*2*4 + 4)
|
||||
var startLocations = make([]uint32, len(funcs))
|
||||
for i, f := range funcs {
|
||||
size = rnd(size, int64(_PtrSize))
|
||||
//writePCToFunc
|
||||
startLocations[i] = uint32(size)
|
||||
size += int64(uint8(_FUNC_SIZE)+f.nfuncdata*4+uint8(f.npcdata)*4)
|
||||
}
|
||||
|
||||
ftab = make([]funcTab, 0, len(funcs)+1)
|
||||
|
||||
// write a map of pc->func info offsets
|
||||
for i, f := range funcs {
|
||||
ftab = append(ftab, funcTab{uint32(f.entryOff), uint32(startLocations[i])})
|
||||
}
|
||||
|
||||
// Final entry of table is just end pc offset.
|
||||
lastFunc := funcs[len(funcs)-1]
|
||||
ftab = append(ftab, funcTab{uint32(lastFunc.entryOff + lastFuncSize), 0})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Pcln table format: [...]funcTab + [...]_Func
|
||||
func makePclntable(funcs []_func, lastFuncSize uint32, pcdataOffs [][]uint32, funcdataOffs [][]uint32) (pclntab []byte) {
|
||||
// Allocate space for the pc->func table. This structure consists of a pc offset
|
||||
// and an offset to the func structure. After that, we have a single pc
|
||||
// value that marks the end of the last function in the binary.
|
||||
var size int64 = int64(len(funcs)*2*4 + 4)
|
||||
var startLocations = make([]uint32, len(funcs))
|
||||
for i := range funcs {
|
||||
size = rnd(size, int64(_PtrSize))
|
||||
//writePCToFunc
|
||||
startLocations[i] = uint32(size)
|
||||
size += int64(int(_FUNC_SIZE)+len(funcdataOffs[i])*4+len(pcdataOffs[i])*4)
|
||||
}
|
||||
|
||||
pclntab = make([]byte, size, size)
|
||||
|
||||
// write a map of pc->func info offsets
|
||||
offs := 0
|
||||
for i, f := range funcs {
|
||||
byteOrder.PutUint32(pclntab[offs:offs+4], uint32(f.entryOff))
|
||||
byteOrder.PutUint32(pclntab[offs+4:offs+8], uint32(startLocations[i]))
|
||||
offs += 8
|
||||
}
|
||||
// Final entry of table is just end pc offset.
|
||||
lastFunc := funcs[len(funcs)-1]
|
||||
byteOrder.PutUint32(pclntab[offs:offs+4], uint32(lastFunc.entryOff+lastFuncSize))
|
||||
|
||||
// write func info table
|
||||
for i, f := range funcs {
|
||||
off := startLocations[i]
|
||||
|
||||
// write _func structure to pclntab
|
||||
fb := rt.BytesFrom(unsafe.Pointer(&f), int(_FUNC_SIZE), int(_FUNC_SIZE))
|
||||
copy(pclntab[off:off+uint32(_FUNC_SIZE)], fb)
|
||||
off += uint32(_FUNC_SIZE)
|
||||
|
||||
// NOTICE: _func.pcdata always starts from PcUnsafePoint, which is index 3
|
||||
for j := 3; j < len(pcdataOffs[i]); j++ {
|
||||
byteOrder.PutUint32(pclntab[off:off+4], uint32(pcdataOffs[i][j]))
|
||||
off += 4
|
||||
}
|
||||
|
||||
// funcdata refs as offsets from gofunc
|
||||
for _, funcdata := range funcdataOffs[i] {
|
||||
byteOrder.PutUint32(pclntab[off:off+4], uint32(funcdata))
|
||||
off += 4
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// findfunc table used to map pc to belonging func,
|
||||
// returns the index in the func table.
|
||||
//
|
||||
// All text section are divided into buckets sized _BUCKETSIZE(4K):
|
||||
// every bucket is divided into _SUBBUCKETS sized _SUB_BUCKETSIZE(64),
|
||||
// and it has a base idx to plus the offset stored in jth subbucket.
|
||||
// see findfunc() in runtime/symtab.go
|
||||
func writeFindfunctab(out *[]byte, ftab []funcTab) (start int) {
|
||||
start = len(*out)
|
||||
|
||||
max := ftab[len(ftab)-1].entry
|
||||
min := ftab[0].entry
|
||||
nbuckets := (max - min + _BUCKETSIZE - 1) / _BUCKETSIZE
|
||||
n := (max - min + _SUB_BUCKETSIZE - 1) / _SUB_BUCKETSIZE
|
||||
|
||||
tab := make([]findfuncbucket, 0, nbuckets)
|
||||
var s, e = 0, 0
|
||||
for i := 0; i<int(nbuckets); i++ {
|
||||
var pc = min + uint32((i+1)*_BUCKETSIZE)
|
||||
// find the end func of the bucket
|
||||
for ; e < len(ftab)-1 && ftab[e+1].entry <= pc; e++ {}
|
||||
// store the start func of the bucket
|
||||
var fb = findfuncbucket{idx: uint32(s)}
|
||||
|
||||
for j := 0; j<_SUBBUCKETS && (i*_SUBBUCKETS+j)<int(n); j++ {
|
||||
pc = min + uint32(i*_BUCKETSIZE) + uint32((j+1)*_SUB_BUCKETSIZE)
|
||||
var ss = s
|
||||
// find the end func of the subbucket
|
||||
for ; ss < len(ftab)-1 && ftab[ss+1].entry <= pc; ss++ {}
|
||||
// store the start func of the subbucket
|
||||
fb._SUBBUCKETS[j] = byte(uint32(s) - fb.idx)
|
||||
s = ss
|
||||
}
|
||||
s = e
|
||||
tab = append(tab, fb)
|
||||
}
|
||||
|
||||
// write findfuncbucket
|
||||
if len(tab) > 0 {
|
||||
size := int(unsafe.Sizeof(findfuncbucket{}))*len(tab)
|
||||
*out = append(*out, rt.BytesFrom(unsafe.Pointer(&tab[0]), size, size)...)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func makeModuledata(name string, filenames []string, funcs []Func, text []byte) (mod *moduledata) {
|
||||
mod = new(moduledata)
|
||||
mod.modulename = name
|
||||
|
||||
// make filename table
|
||||
cu := make([]string, 0, len(filenames))
|
||||
for _, f := range filenames {
|
||||
cu = append(cu, f)
|
||||
}
|
||||
cutab, filetab, cuOffs := makeFilenametab([]compilationUnit{{cu}})
|
||||
mod.cutab = cutab
|
||||
mod.filetab = filetab
|
||||
|
||||
// make funcname table
|
||||
funcnametab, nameOffs := makeFuncnameTab(funcs)
|
||||
mod.funcnametab = funcnametab
|
||||
|
||||
// make pcdata table
|
||||
// NOTICE: _func only use offset to index pcdata, thus no need mmap() pcdata
|
||||
pctab, pcdataOffs, _funcs := makePctab(funcs, cuOffs, nameOffs)
|
||||
mod.pctab = pctab
|
||||
|
||||
// write func data
|
||||
// NOTICE: _func use mod.gofunc+offset to directly point funcdata, thus need cache funcdata
|
||||
// TODO: estimate accurate capacity
|
||||
cache := make([]byte, 0, len(funcs)*int(_PtrSize))
|
||||
fstart, funcdataOffs := writeFuncdata(&cache, funcs)
|
||||
|
||||
// make pc->func (binary search) func table
|
||||
lastFuncsize := funcs[len(funcs)-1].TextSize
|
||||
ftab := makeFtab(_funcs, lastFuncsize)
|
||||
mod.ftab = ftab
|
||||
|
||||
// write pc->func (modmap) findfunc table
|
||||
ffstart := writeFindfunctab(&cache, ftab)
|
||||
|
||||
// make pclnt table
|
||||
pclntab := makePclntable(_funcs, lastFuncsize, pcdataOffs, funcdataOffs)
|
||||
mod.pclntable = pclntab
|
||||
|
||||
// mmap() text and funcdata segements
|
||||
p := os.Getpagesize()
|
||||
size := int(rnd(int64(len(text)), int64(p)))
|
||||
addr := mmap(size)
|
||||
// copy the machine code
|
||||
s := rt.BytesFrom(unsafe.Pointer(addr), len(text), size)
|
||||
copy(s, text)
|
||||
// make it executable
|
||||
mprotect(addr, size)
|
||||
|
||||
// assign addresses
|
||||
mod.text = addr
|
||||
mod.etext = addr + uintptr(size)
|
||||
mod.minpc = addr
|
||||
mod.maxpc = addr + uintptr(len(text))
|
||||
|
||||
// cache funcdata and findfuncbucket
|
||||
moduleCache.Lock()
|
||||
moduleCache.m[mod] = cache
|
||||
moduleCache.Unlock()
|
||||
mod.gofunc = uintptr(unsafe.Pointer(&cache[fstart]))
|
||||
mod.findfunctab = uintptr(unsafe.Pointer(&cache[ffstart]))
|
||||
|
||||
// make pc header
|
||||
mod.pcHeader = &pcHeader {
|
||||
magic : _Magic,
|
||||
minLC : _MinLC,
|
||||
ptrSize : _PtrSize,
|
||||
nfunc : len(funcs),
|
||||
nfiles: uint(len(cu)),
|
||||
textStart: mod.text,
|
||||
funcnameOffset: getOffsetOf(moduledata{}, "funcnametab"),
|
||||
cuOffset: getOffsetOf(moduledata{}, "cutab"),
|
||||
filetabOffset: getOffsetOf(moduledata{}, "filetab"),
|
||||
pctabOffset: getOffsetOf(moduledata{}, "pctab"),
|
||||
pclnOffset: getOffsetOf(moduledata{}, "pclntable"),
|
||||
}
|
||||
|
||||
// sepecial case: gcdata and gcbss must by non-empty
|
||||
mod.gcdata = uintptr(unsafe.Pointer(&emptyByte))
|
||||
mod.gcbss = uintptr(unsafe.Pointer(&emptyByte))
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// makePctab generates pcdelta->valuedelta tables for functions,
|
||||
// and returns the table and the entry offset of every kind pcdata in the table.
|
||||
func makePctab(funcs []Func, cuOffset []uint32, nameOffset []int32) (pctab []byte, pcdataOffs [][]uint32, _funcs []_func) {
|
||||
_funcs = make([]_func, len(funcs))
|
||||
|
||||
// Pctab offsets of 0 are considered invalid in the runtime. We respect
|
||||
// that by just padding a single byte at the beginning of runtime.pctab,
|
||||
// that way no real offsets can be zero.
|
||||
pctab = make([]byte, 1, 12*len(funcs)+1)
|
||||
pcdataOffs = make([][]uint32, len(funcs))
|
||||
|
||||
for i, f := range funcs {
|
||||
_f := &_funcs[i]
|
||||
|
||||
var writer = func(pc *Pcdata) {
|
||||
var ab []byte
|
||||
var err error
|
||||
if pc != nil {
|
||||
ab, err = pc.MarshalBinary()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
pcdataOffs[i] = append(pcdataOffs[i], uint32(len(pctab)))
|
||||
} else {
|
||||
ab = []byte{0}
|
||||
pcdataOffs[i] = append(pcdataOffs[i], _PCDATA_INVALID_OFFSET)
|
||||
}
|
||||
pctab = append(pctab, ab...)
|
||||
}
|
||||
|
||||
if f.Pcsp != nil {
|
||||
_f.pcsp = uint32(len(pctab))
|
||||
}
|
||||
writer(f.Pcsp)
|
||||
if f.Pcfile != nil {
|
||||
_f.pcfile = uint32(len(pctab))
|
||||
}
|
||||
writer(f.Pcfile)
|
||||
if f.Pcline != nil {
|
||||
_f.pcln = uint32(len(pctab))
|
||||
}
|
||||
writer(f.Pcline)
|
||||
writer(f.PcUnsafePoint)
|
||||
writer(f.PcStackMapIndex)
|
||||
writer(f.PcInlTreeIndex)
|
||||
writer(f.PcArgLiveIndex)
|
||||
|
||||
_f.entryOff = f.EntryOff
|
||||
_f.nameOff = nameOffset[i]
|
||||
_f.args = f.ArgsSize
|
||||
_f.deferreturn = f.DeferReturn
|
||||
// NOTICE: _func.pcdata is always as [PCDATA_UnsafePoint(0) : PCDATA_ArgLiveIndex(3)]
|
||||
_f.npcdata = uint32(_N_PCDATA)
|
||||
_f.cuOffset = cuOffset[i]
|
||||
_f.funcID = f.ID
|
||||
_f.flag = f.Flag
|
||||
_f.nfuncdata = uint8(_N_FUNCDATA)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func registerFunction(name string, pc uintptr, textSize uintptr, fp int, args int, size uintptr, argptrs uintptr, localptrs uintptr) {}
|
||||
433
vendor/github.com/bytedance/sonic/loader/funcdata_go120.go
generated
vendored
433
vendor/github.com/bytedance/sonic/loader/funcdata_go120.go
generated
vendored
@@ -20,7 +20,11 @@
|
||||
package loader
|
||||
|
||||
import (
|
||||
`github.com/bytedance/sonic/loader/internal/rt`
|
||||
`encoding`
|
||||
`os`
|
||||
`unsafe`
|
||||
|
||||
`github.com/bytedance/sonic/internal/rt`
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -47,6 +51,8 @@ type moduledata struct {
|
||||
end, gcdata, gcbss uintptr
|
||||
types, etypes uintptr
|
||||
rodata uintptr
|
||||
|
||||
// TODO: generate funcinfo object to memory
|
||||
gofunc uintptr // go.func.* is actual funcinfo object in image
|
||||
|
||||
textsectmap []textSection // see runtime/symtab.go: textAddr()
|
||||
@@ -112,3 +118,428 @@ type _func struct {
|
||||
//
|
||||
// funcdata [nfuncdata]uint32
|
||||
}
|
||||
|
||||
type funcTab struct {
|
||||
entry uint32
|
||||
funcoff uint32
|
||||
}
|
||||
|
||||
type pcHeader struct {
|
||||
magic uint32 // 0xFFFFFFF0
|
||||
pad1, pad2 uint8 // 0,0
|
||||
minLC uint8 // min instruction size
|
||||
ptrSize uint8 // size of a ptr in bytes
|
||||
nfunc int // number of functions in the module
|
||||
nfiles uint // number of entries in the file tab
|
||||
textStart uintptr // base for function entry PC offsets in this module, equal to moduledata.text
|
||||
funcnameOffset uintptr // offset to the funcnametab variable from pcHeader
|
||||
cuOffset uintptr // offset to the cutab variable from pcHeader
|
||||
filetabOffset uintptr // offset to the filetab variable from pcHeader
|
||||
pctabOffset uintptr // offset to the pctab variable from pcHeader
|
||||
pclnOffset uintptr // offset to the pclntab variable from pcHeader
|
||||
}
|
||||
|
||||
type bitVector struct {
|
||||
n int32 // # of bits
|
||||
bytedata *uint8
|
||||
}
|
||||
|
||||
type ptabEntry struct {
|
||||
name int32
|
||||
typ int32
|
||||
}
|
||||
|
||||
type textSection struct {
|
||||
vaddr uintptr // prelinked section vaddr
|
||||
end uintptr // vaddr + section length
|
||||
baseaddr uintptr // relocated section address
|
||||
}
|
||||
|
||||
type modulehash struct {
|
||||
modulename string
|
||||
linktimehash string
|
||||
runtimehash *string
|
||||
}
|
||||
|
||||
// findfuncbucket is an array of these structures.
|
||||
// Each bucket represents 4096 bytes of the text segment.
|
||||
// Each subbucket represents 256 bytes of the text segment.
|
||||
// To find a function given a pc, locate the bucket and subbucket for
|
||||
// that pc. Add together the idx and subbucket value to obtain a
|
||||
// function index. Then scan the functab array starting at that
|
||||
// index to find the target function.
|
||||
// This table uses 20 bytes for every 4096 bytes of code, or ~0.5% overhead.
|
||||
type findfuncbucket struct {
|
||||
idx uint32
|
||||
_SUBBUCKETS [16]byte
|
||||
}
|
||||
|
||||
// func name table format:
|
||||
// nameOff[0] -> namePartA namePartB namePartC \x00
|
||||
// nameOff[1] -> namePartA namePartB namePartC \x00
|
||||
// ...
|
||||
func makeFuncnameTab(funcs []Func) (tab []byte, offs []int32) {
|
||||
offs = make([]int32, len(funcs))
|
||||
offset := 0
|
||||
|
||||
for i, f := range funcs {
|
||||
offs[i] = int32(offset)
|
||||
|
||||
a, b, c := funcNameParts(f.Name)
|
||||
tab = append(tab, a...)
|
||||
tab = append(tab, b...)
|
||||
tab = append(tab, c...)
|
||||
tab = append(tab, 0)
|
||||
offset += len(a) + len(b) + len(c) + 1
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
type compilationUnit struct {
|
||||
fileNames []string
|
||||
}
|
||||
|
||||
// CU table format:
|
||||
// cuOffsets[0] -> filetabOffset[0] filetabOffset[1] ... filetabOffset[len(CUs[0].fileNames)-1]
|
||||
// cuOffsets[1] -> filetabOffset[len(CUs[0].fileNames)] ... filetabOffset[len(CUs[0].fileNames) + len(CUs[1].fileNames)-1]
|
||||
// ...
|
||||
//
|
||||
// file name table format:
|
||||
// filetabOffset[0] -> CUs[0].fileNames[0] \x00
|
||||
// ...
|
||||
// filetabOffset[len(CUs[0]-1)] -> CUs[0].fileNames[len(CUs[0].fileNames)-1] \x00
|
||||
// ...
|
||||
// filetabOffset[SUM(CUs,fileNames)-1] -> CUs[len(CU)-1].fileNames[len(CUs[len(CU)-1].fileNames)-1] \x00
|
||||
func makeFilenametab(cus []compilationUnit) (cutab []uint32, filetab []byte, cuOffsets []uint32) {
|
||||
cuOffsets = make([]uint32, len(cus))
|
||||
cuOffset := 0
|
||||
fileOffset := 0
|
||||
|
||||
for i, cu := range cus {
|
||||
cuOffsets[i] = uint32(cuOffset)
|
||||
|
||||
for _, name := range cu.fileNames {
|
||||
cutab = append(cutab, uint32(fileOffset))
|
||||
|
||||
fileOffset += len(name) + 1
|
||||
filetab = append(filetab, name...)
|
||||
filetab = append(filetab, 0)
|
||||
}
|
||||
|
||||
cuOffset += len(cu.fileNames)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func writeFuncdata(out *[]byte, funcs []Func) (fstart int, funcdataOffs [][]uint32) {
|
||||
fstart = len(*out)
|
||||
*out = append(*out, byte(0))
|
||||
offs := uint32(1)
|
||||
|
||||
funcdataOffs = make([][]uint32, len(funcs))
|
||||
for i, f := range funcs {
|
||||
|
||||
var writer = func(fd encoding.BinaryMarshaler) {
|
||||
var ab []byte
|
||||
var err error
|
||||
if fd != nil {
|
||||
ab, err = fd.MarshalBinary()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
funcdataOffs[i] = append(funcdataOffs[i], offs)
|
||||
} else {
|
||||
ab = []byte{0}
|
||||
funcdataOffs[i] = append(funcdataOffs[i], _INVALID_FUNCDATA_OFFSET)
|
||||
}
|
||||
*out = append(*out, ab...)
|
||||
offs += uint32(len(ab))
|
||||
}
|
||||
|
||||
writer(f.ArgsPointerMaps)
|
||||
writer(f.LocalsPointerMaps)
|
||||
writer(f.StackObjects)
|
||||
writer(f.InlTree)
|
||||
writer(f.OpenCodedDeferInfo)
|
||||
writer(f.ArgInfo)
|
||||
writer(f.ArgLiveInfo)
|
||||
writer(f.WrapInfo)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func makeFtab(funcs []_func, lastFuncSize uint32) (ftab []funcTab) {
|
||||
// Allocate space for the pc->func table. This structure consists of a pc offset
|
||||
// and an offset to the func structure. After that, we have a single pc
|
||||
// value that marks the end of the last function in the binary.
|
||||
var size int64 = int64(len(funcs)*2*4 + 4)
|
||||
var startLocations = make([]uint32, len(funcs))
|
||||
for i, f := range funcs {
|
||||
size = rnd(size, int64(_PtrSize))
|
||||
//writePCToFunc
|
||||
startLocations[i] = uint32(size)
|
||||
size += int64(uint8(_FUNC_SIZE)+f.nfuncdata*4+uint8(f.npcdata)*4)
|
||||
}
|
||||
|
||||
ftab = make([]funcTab, 0, len(funcs)+1)
|
||||
|
||||
// write a map of pc->func info offsets
|
||||
for i, f := range funcs {
|
||||
ftab = append(ftab, funcTab{uint32(f.entryOff), uint32(startLocations[i])})
|
||||
}
|
||||
|
||||
// Final entry of table is just end pc offset.
|
||||
lastFunc := funcs[len(funcs)-1]
|
||||
ftab = append(ftab, funcTab{uint32(lastFunc.entryOff + lastFuncSize), 0})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Pcln table format: [...]funcTab + [...]_Func
|
||||
func makePclntable(funcs []_func, lastFuncSize uint32, pcdataOffs [][]uint32, funcdataOffs [][]uint32) (pclntab []byte) {
|
||||
// Allocate space for the pc->func table. This structure consists of a pc offset
|
||||
// and an offset to the func structure. After that, we have a single pc
|
||||
// value that marks the end of the last function in the binary.
|
||||
var size int64 = int64(len(funcs)*2*4 + 4)
|
||||
var startLocations = make([]uint32, len(funcs))
|
||||
for i := range funcs {
|
||||
size = rnd(size, int64(_PtrSize))
|
||||
//writePCToFunc
|
||||
startLocations[i] = uint32(size)
|
||||
size += int64(int(_FUNC_SIZE)+len(funcdataOffs[i])*4+len(pcdataOffs[i])*4)
|
||||
}
|
||||
|
||||
pclntab = make([]byte, size, size)
|
||||
|
||||
// write a map of pc->func info offsets
|
||||
offs := 0
|
||||
for i, f := range funcs {
|
||||
byteOrder.PutUint32(pclntab[offs:offs+4], uint32(f.entryOff))
|
||||
byteOrder.PutUint32(pclntab[offs+4:offs+8], uint32(startLocations[i]))
|
||||
offs += 8
|
||||
}
|
||||
// Final entry of table is just end pc offset.
|
||||
lastFunc := funcs[len(funcs)-1]
|
||||
byteOrder.PutUint32(pclntab[offs:offs+4], uint32(lastFunc.entryOff+lastFuncSize))
|
||||
|
||||
// write func info table
|
||||
for i, f := range funcs {
|
||||
off := startLocations[i]
|
||||
|
||||
// write _func structure to pclntab
|
||||
fb := rt.BytesFrom(unsafe.Pointer(&f), int(_FUNC_SIZE), int(_FUNC_SIZE))
|
||||
copy(pclntab[off:off+uint32(_FUNC_SIZE)], fb)
|
||||
off += uint32(_FUNC_SIZE)
|
||||
|
||||
// NOTICE: _func.pcdata always starts from PcUnsafePoint, which is index 3
|
||||
for j := 3; j < len(pcdataOffs[i]); j++ {
|
||||
byteOrder.PutUint32(pclntab[off:off+4], uint32(pcdataOffs[i][j]))
|
||||
off += 4
|
||||
}
|
||||
|
||||
// funcdata refs as offsets from gofunc
|
||||
for _, funcdata := range funcdataOffs[i] {
|
||||
byteOrder.PutUint32(pclntab[off:off+4], uint32(funcdata))
|
||||
off += 4
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// findfunc table used to map pc to belonging func,
|
||||
// returns the index in the func table.
|
||||
//
|
||||
// All text section are divided into buckets sized _BUCKETSIZE(4K):
|
||||
// every bucket is divided into _SUBBUCKETS sized _SUB_BUCKETSIZE(64),
|
||||
// and it has a base idx to plus the offset stored in jth subbucket.
|
||||
// see findfunc() in runtime/symtab.go
|
||||
func writeFindfunctab(out *[]byte, ftab []funcTab) (start int) {
|
||||
start = len(*out)
|
||||
|
||||
max := ftab[len(ftab)-1].entry
|
||||
min := ftab[0].entry
|
||||
nbuckets := (max - min + _BUCKETSIZE - 1) / _BUCKETSIZE
|
||||
n := (max - min + _SUB_BUCKETSIZE - 1) / _SUB_BUCKETSIZE
|
||||
|
||||
tab := make([]findfuncbucket, 0, nbuckets)
|
||||
var s, e = 0, 0
|
||||
for i := 0; i<int(nbuckets); i++ {
|
||||
var pc = min + uint32((i+1)*_BUCKETSIZE)
|
||||
// find the end func of the bucket
|
||||
for ; e < len(ftab)-1 && ftab[e+1].entry <= pc; e++ {}
|
||||
// store the start func of the bucket
|
||||
var fb = findfuncbucket{idx: uint32(s)}
|
||||
|
||||
for j := 0; j<_SUBBUCKETS && (i*_SUBBUCKETS+j)<int(n); j++ {
|
||||
pc = min + uint32(i*_BUCKETSIZE) + uint32((j+1)*_SUB_BUCKETSIZE)
|
||||
var ss = s
|
||||
// find the end func of the subbucket
|
||||
for ; ss < len(ftab)-1 && ftab[ss+1].entry <= pc; ss++ {}
|
||||
// store the start func of the subbucket
|
||||
fb._SUBBUCKETS[j] = byte(uint32(s) - fb.idx)
|
||||
s = ss
|
||||
}
|
||||
s = e
|
||||
tab = append(tab, fb)
|
||||
}
|
||||
|
||||
// write findfuncbucket
|
||||
if len(tab) > 0 {
|
||||
size := int(unsafe.Sizeof(findfuncbucket{}))*len(tab)
|
||||
*out = append(*out, rt.BytesFrom(unsafe.Pointer(&tab[0]), size, size)...)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func makeModuledata(name string, filenames []string, funcs []Func, text []byte) (mod *moduledata) {
|
||||
mod = new(moduledata)
|
||||
mod.modulename = name
|
||||
|
||||
// make filename table
|
||||
cu := make([]string, 0, len(filenames))
|
||||
for _, f := range filenames {
|
||||
cu = append(cu, f)
|
||||
}
|
||||
cutab, filetab, cuOffs := makeFilenametab([]compilationUnit{{cu}})
|
||||
mod.cutab = cutab
|
||||
mod.filetab = filetab
|
||||
|
||||
// make funcname table
|
||||
funcnametab, nameOffs := makeFuncnameTab(funcs)
|
||||
mod.funcnametab = funcnametab
|
||||
|
||||
// make pcdata table
|
||||
// NOTICE: _func only use offset to index pcdata, thus no need mmap() pcdata
|
||||
pctab, pcdataOffs, _funcs := makePctab(funcs, cuOffs, nameOffs)
|
||||
mod.pctab = pctab
|
||||
|
||||
// write func data
|
||||
// NOTICE: _func use mod.gofunc+offset to directly point funcdata, thus need cache funcdata
|
||||
// TODO: estimate accurate capacity
|
||||
cache := make([]byte, 0, len(funcs)*int(_PtrSize))
|
||||
fstart, funcdataOffs := writeFuncdata(&cache, funcs)
|
||||
|
||||
// make pc->func (binary search) func table
|
||||
lastFuncsize := funcs[len(funcs)-1].TextSize
|
||||
ftab := makeFtab(_funcs, lastFuncsize)
|
||||
mod.ftab = ftab
|
||||
|
||||
// write pc->func (modmap) findfunc table
|
||||
ffstart := writeFindfunctab(&cache, ftab)
|
||||
|
||||
// make pclnt table
|
||||
pclntab := makePclntable(_funcs, lastFuncsize, pcdataOffs, funcdataOffs)
|
||||
mod.pclntable = pclntab
|
||||
|
||||
// mmap() text and funcdata segements
|
||||
p := os.Getpagesize()
|
||||
size := int(rnd(int64(len(text)), int64(p)))
|
||||
addr := mmap(size)
|
||||
// copy the machine code
|
||||
s := rt.BytesFrom(unsafe.Pointer(addr), len(text), size)
|
||||
copy(s, text)
|
||||
// make it executable
|
||||
mprotect(addr, size)
|
||||
|
||||
// assign addresses
|
||||
mod.text = addr
|
||||
mod.etext = addr + uintptr(size)
|
||||
mod.minpc = addr
|
||||
mod.maxpc = addr + uintptr(len(text))
|
||||
|
||||
// cache funcdata and findfuncbucket
|
||||
moduleCache.Lock()
|
||||
moduleCache.m[mod] = cache
|
||||
moduleCache.Unlock()
|
||||
mod.gofunc = uintptr(unsafe.Pointer(&cache[fstart]))
|
||||
mod.findfunctab = uintptr(unsafe.Pointer(&cache[ffstart]))
|
||||
|
||||
// make pc header
|
||||
mod.pcHeader = &pcHeader {
|
||||
magic : _Magic,
|
||||
minLC : _MinLC,
|
||||
ptrSize : _PtrSize,
|
||||
nfunc : len(funcs),
|
||||
nfiles: uint(len(cu)),
|
||||
textStart: mod.text,
|
||||
funcnameOffset: getOffsetOf(moduledata{}, "funcnametab"),
|
||||
cuOffset: getOffsetOf(moduledata{}, "cutab"),
|
||||
filetabOffset: getOffsetOf(moduledata{}, "filetab"),
|
||||
pctabOffset: getOffsetOf(moduledata{}, "pctab"),
|
||||
pclnOffset: getOffsetOf(moduledata{}, "pclntable"),
|
||||
}
|
||||
|
||||
// sepecial case: gcdata and gcbss must by non-empty
|
||||
mod.gcdata = uintptr(unsafe.Pointer(&emptyByte))
|
||||
mod.gcbss = uintptr(unsafe.Pointer(&emptyByte))
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// makePctab generates pcdelta->valuedelta tables for functions,
|
||||
// and returns the table and the entry offset of every kind pcdata in the table.
|
||||
func makePctab(funcs []Func, cuOffset []uint32, nameOffset []int32) (pctab []byte, pcdataOffs [][]uint32, _funcs []_func) {
|
||||
_funcs = make([]_func, len(funcs))
|
||||
|
||||
// Pctab offsets of 0 are considered invalid in the runtime. We respect
|
||||
// that by just padding a single byte at the beginning of runtime.pctab,
|
||||
// that way no real offsets can be zero.
|
||||
pctab = make([]byte, 1, 12*len(funcs)+1)
|
||||
pcdataOffs = make([][]uint32, len(funcs))
|
||||
|
||||
for i, f := range funcs {
|
||||
_f := &_funcs[i]
|
||||
|
||||
var writer = func(pc *Pcdata) {
|
||||
var ab []byte
|
||||
var err error
|
||||
if pc != nil {
|
||||
ab, err = pc.MarshalBinary()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
pcdataOffs[i] = append(pcdataOffs[i], uint32(len(pctab)))
|
||||
} else {
|
||||
ab = []byte{0}
|
||||
pcdataOffs[i] = append(pcdataOffs[i], _PCDATA_INVALID_OFFSET)
|
||||
}
|
||||
pctab = append(pctab, ab...)
|
||||
}
|
||||
|
||||
if f.Pcsp != nil {
|
||||
_f.pcsp = uint32(len(pctab))
|
||||
}
|
||||
writer(f.Pcsp)
|
||||
if f.Pcfile != nil {
|
||||
_f.pcfile = uint32(len(pctab))
|
||||
}
|
||||
writer(f.Pcfile)
|
||||
if f.Pcline != nil {
|
||||
_f.pcln = uint32(len(pctab))
|
||||
}
|
||||
writer(f.Pcline)
|
||||
writer(f.PcUnsafePoint)
|
||||
writer(f.PcStackMapIndex)
|
||||
writer(f.PcInlTreeIndex)
|
||||
writer(f.PcArgLiveIndex)
|
||||
|
||||
_f.entryOff = f.EntryOff
|
||||
_f.nameOff = nameOffset[i]
|
||||
_f.args = f.ArgsSize
|
||||
_f.deferreturn = f.DeferReturn
|
||||
// NOTICE: _func.pcdata is always as [PCDATA_UnsafePoint(0) : PCDATA_ArgLiveIndex(3)]
|
||||
_f.npcdata = uint32(_N_PCDATA)
|
||||
_f.cuOffset = cuOffset[i]
|
||||
_f.funcID = f.ID
|
||||
_f.flag = f.Flag
|
||||
_f.nfuncdata = uint8(_N_FUNCDATA)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func registerFunction(name string, pc uintptr, textSize uintptr, fp int, args int, size uintptr, argptrs uintptr, localptrs uintptr) {}
|
||||
2
vendor/github.com/bytedance/sonic/loader/loader.go
generated
vendored
2
vendor/github.com/bytedance/sonic/loader/loader.go
generated
vendored
@@ -34,4 +34,4 @@ type Loader struct {
|
||||
Name string // module name
|
||||
File string // file name
|
||||
Options
|
||||
}
|
||||
}
|
||||
6
vendor/github.com/bytedance/sonic/loader/mmap_unix.go
generated
vendored
6
vendor/github.com/bytedance/sonic/loader/mmap_unix.go
generated
vendored
@@ -1,5 +1,5 @@
|
||||
//go:build !windows
|
||||
// +build !windows
|
||||
//go:build darwin || linux
|
||||
// +build darwin linux
|
||||
|
||||
/**
|
||||
* Copyright 2023 ByteDance Inc.
|
||||
@@ -42,4 +42,4 @@ func mprotect(p uintptr, nb int) {
|
||||
if _, _, err := syscall.RawSyscall(syscall.SYS_MPROTECT, p, uintptr(nb), _RX); err != 0 {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
63
vendor/github.com/bytedance/sonic/loader/pcdata.go
generated
vendored
63
vendor/github.com/bytedance/sonic/loader/pcdata.go
generated
vendored
@@ -16,10 +16,6 @@
|
||||
|
||||
package loader
|
||||
|
||||
import (
|
||||
`encoding/binary`
|
||||
)
|
||||
|
||||
const (
|
||||
_N_PCDATA = 4
|
||||
|
||||
@@ -53,16 +49,40 @@ const (
|
||||
|
||||
var emptyByte byte
|
||||
|
||||
// Pcvalue is the program count corresponding to the value Val
|
||||
// WARN: we use relative value here (to function entry)
|
||||
type Pcvalue struct {
|
||||
PC uint32 // program count relative to function entry
|
||||
Val int32 // value relative to the value in function entry
|
||||
func encodeValue(v int) []byte {
|
||||
return encodeVariant(toZigzag(v))
|
||||
}
|
||||
|
||||
func toZigzag(v int) int {
|
||||
return (v << 1) ^ (v >> 31)
|
||||
}
|
||||
|
||||
func encodeVariant(v int) []byte {
|
||||
var u int
|
||||
var r []byte
|
||||
|
||||
/* split every 7 bits */
|
||||
for v > 127 {
|
||||
u = v & 0x7f
|
||||
v = v >> 7
|
||||
r = append(r, byte(u) | 0x80)
|
||||
}
|
||||
|
||||
/* check for last one */
|
||||
if v == 0 {
|
||||
return r
|
||||
}
|
||||
|
||||
/* add the last one */
|
||||
r = append(r, byte(v))
|
||||
return r
|
||||
}
|
||||
|
||||
type Pcvalue struct {
|
||||
PC uint32 // PC offset from func entry
|
||||
Val int32
|
||||
}
|
||||
|
||||
// Pcdata represents pc->value mapping table.
|
||||
// WARN: we use ** [Pcdata[i].PC, Pcdata[i+1].PC) **
|
||||
// as the range where the Pcdata[i].Val is effective.
|
||||
type Pcdata []Pcvalue
|
||||
|
||||
// see https://docs.google.com/document/d/1lyPIbmsYbXnpNj57a261hgOYVpNRcgydurVQIyZOz_o/pub
|
||||
@@ -70,24 +90,11 @@ func (self Pcdata) MarshalBinary() (data []byte, err error) {
|
||||
// delta value always starts from -1
|
||||
sv := int32(_PCDATA_START_VAL)
|
||||
sp := uint32(0)
|
||||
buf := make([]byte, binary.MaxVarintLen32)
|
||||
for _, v := range self {
|
||||
if v.PC < sp {
|
||||
panic("PC must be in ascending order!")
|
||||
}
|
||||
dp := uint64(v.PC - sp)
|
||||
dv := int64(v.Val - sv)
|
||||
if dv == 0 || dp == 0 {
|
||||
continue
|
||||
}
|
||||
n := binary.PutVarint(buf, dv)
|
||||
data = append(data, buf[:n]...)
|
||||
n2 := binary.PutUvarint(buf, dp)
|
||||
data = append(data, buf[:n2]...)
|
||||
data = append(data, encodeVariant(toZigzag(int(v.Val - sv)))...)
|
||||
data = append(data, encodeVariant(int(v.PC - sp))...)
|
||||
sp = v.PC
|
||||
sv = v.Val
|
||||
}
|
||||
// put 0 to indicate ends
|
||||
data = append(data, 0)
|
||||
return
|
||||
}
|
||||
}
|
||||
34
vendor/github.com/bytedance/sonic/loader/stubs.go
generated
vendored
34
vendor/github.com/bytedance/sonic/loader/stubs.go
generated
vendored
@@ -17,8 +17,7 @@
|
||||
package loader
|
||||
|
||||
import (
|
||||
"sync/atomic"
|
||||
"unsafe"
|
||||
`sync`
|
||||
_ `unsafe`
|
||||
)
|
||||
|
||||
@@ -26,35 +25,16 @@ import (
|
||||
//goland:noinspection GoUnusedGlobalVariable
|
||||
var lastmoduledatap *moduledata
|
||||
|
||||
var moduledataMux sync.Mutex
|
||||
|
||||
func registerModule(mod *moduledata) {
|
||||
registerModuleLockFree(&lastmoduledatap, mod)
|
||||
moduledataMux.Lock()
|
||||
lastmoduledatap.next = mod
|
||||
lastmoduledatap = mod
|
||||
moduledataMux.Unlock()
|
||||
}
|
||||
|
||||
//go:linkname moduledataverify1 runtime.moduledataverify1
|
||||
func moduledataverify1(_ *moduledata)
|
||||
|
||||
func registerModuleLockFree(tail **moduledata, mod *moduledata) {
|
||||
for {
|
||||
oldTail := loadModule(tail)
|
||||
if casModule(tail, oldTail, mod) {
|
||||
storeModule(&oldTail.next, mod)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func loadModule(p **moduledata) *moduledata {
|
||||
return (*moduledata)(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(p))))
|
||||
}
|
||||
|
||||
func storeModule(p **moduledata, value *moduledata) {
|
||||
atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(p)), unsafe.Pointer(value))
|
||||
}
|
||||
|
||||
func casModule(p **moduledata, oldValue *moduledata, newValue *moduledata) bool {
|
||||
return atomic.CompareAndSwapPointer(
|
||||
(*unsafe.Pointer)(unsafe.Pointer(p)),
|
||||
unsafe.Pointer(oldValue),
|
||||
unsafe.Pointer(newValue),
|
||||
)
|
||||
}
|
||||
|
||||
46
vendor/github.com/bytedance/sonic/sonic.go
generated
vendored
46
vendor/github.com/bytedance/sonic/sonic.go
generated
vendored
@@ -1,4 +1,4 @@
|
||||
// +build amd64,go1.16,!go1.23
|
||||
// +build amd64,go1.15,!go1.21
|
||||
|
||||
/*
|
||||
* Copyright 2021 ByteDance Inc.
|
||||
@@ -58,12 +58,6 @@ func (cfg Config) Froze() API {
|
||||
if cfg.ValidateString {
|
||||
api.encoderOpts |= encoder.ValidateString
|
||||
}
|
||||
if cfg.NoValidateJSONMarshaler {
|
||||
api.encoderOpts |= encoder.NoValidateJSONMarshaler
|
||||
}
|
||||
if cfg.NoEncoderNewline {
|
||||
api.encoderOpts |= encoder.NoEncoderNewline
|
||||
}
|
||||
|
||||
// configure decoder options:
|
||||
if cfg.UseInt64 {
|
||||
@@ -145,23 +139,23 @@ func (cfg frozenConfig) Valid(data []byte) bool {
|
||||
// Opts are the compile options, for example, "option.WithCompileRecursiveDepth" is
|
||||
// a compile option to set the depth of recursive compile for the nested struct type.
|
||||
func Pretouch(vt reflect.Type, opts ...option.CompileOption) error {
|
||||
if err := encoder.Pretouch(vt, opts...); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := decoder.Pretouch(vt, opts...); err != nil {
|
||||
return err
|
||||
}
|
||||
// to pretouch the corresponding pointer type as well
|
||||
if vt.Kind() == reflect.Ptr {
|
||||
vt = vt.Elem()
|
||||
} else {
|
||||
vt = reflect.PtrTo(vt)
|
||||
}
|
||||
if err := encoder.Pretouch(vt, opts...); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := decoder.Pretouch(vt, opts...); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
if err := encoder.Pretouch(vt, opts...); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := decoder.Pretouch(vt, opts...); err != nil {
|
||||
return err
|
||||
}
|
||||
// to pretouch the corresponding pointer type as well
|
||||
if vt.Kind() == reflect.Ptr {
|
||||
vt = vt.Elem()
|
||||
} else {
|
||||
vt = reflect.PtrTo(vt)
|
||||
}
|
||||
if err := encoder.Pretouch(vt, opts...); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := decoder.Pretouch(vt, opts...); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
31
vendor/github.com/bytedance/sonic/unquote/unquote.go
generated
vendored
31
vendor/github.com/bytedance/sonic/unquote/unquote.go
generated
vendored
@@ -25,45 +25,27 @@ import (
|
||||
`github.com/bytedance/sonic/internal/rt`
|
||||
)
|
||||
|
||||
// String unescapes a escaped string (not including `"` at begining and end)
|
||||
// It validates invalid UTF8 and replace with `\ufffd`
|
||||
func String(s string) (ret string, err types.ParsingError) {
|
||||
mm := make([]byte, 0, len(s))
|
||||
err = intoBytesUnsafe(s, &mm, true)
|
||||
err = intoBytesUnsafe(s, &mm)
|
||||
ret = rt.Mem2Str(mm)
|
||||
return
|
||||
}
|
||||
|
||||
// IntoBytes is same with String besides it output result into a buffer m
|
||||
func IntoBytes(s string, m *[]byte) types.ParsingError {
|
||||
if cap(*m) < len(s) {
|
||||
return types.ERR_EOF
|
||||
} else {
|
||||
return intoBytesUnsafe(s, m, true)
|
||||
return intoBytesUnsafe(s, m)
|
||||
}
|
||||
}
|
||||
|
||||
// String unescapes a escaped string (not including `"` at begining and end)
|
||||
// - replace enables replacing invalid utf8 escaped char with `\uffd`
|
||||
func _String(s string, replace bool) (ret string, err error) {
|
||||
mm := make([]byte, 0, len(s))
|
||||
err = intoBytesUnsafe(s, &mm, replace)
|
||||
ret = rt.Mem2Str(mm)
|
||||
return
|
||||
}
|
||||
|
||||
func intoBytesUnsafe(s string, m *[]byte, replace bool) types.ParsingError {
|
||||
func intoBytesUnsafe(s string, m *[]byte) types.ParsingError {
|
||||
pos := -1
|
||||
slv := (*rt.GoSlice)(unsafe.Pointer(m))
|
||||
str := (*rt.GoString)(unsafe.Pointer(&s))
|
||||
|
||||
flags := uint64(0)
|
||||
if replace {
|
||||
/* unquote as the default configuration, replace invalid unicode with \ufffd */
|
||||
flags |= types.F_UNICODE_REPLACE
|
||||
}
|
||||
|
||||
ret := native.Unquote(str.Ptr, str.Len, slv.Ptr, &pos, flags)
|
||||
/* unquote as the default configuration, replace invalid unicode with \ufffd */
|
||||
ret := native.Unquote(str.Ptr, str.Len, slv.Ptr, &pos, types.F_UNICODE_REPLACE)
|
||||
|
||||
/* check for errors */
|
||||
if ret < 0 {
|
||||
@@ -75,6 +57,3 @@ func intoBytesUnsafe(s string, m *[]byte, replace bool) types.ParsingError {
|
||||
runtime.KeepAlive(s)
|
||||
return 0
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
2
vendor/github.com/bytedance/sonic/utf8/utf8.go
generated
vendored
2
vendor/github.com/bytedance/sonic/utf8/utf8.go
generated
vendored
@@ -68,4 +68,4 @@ func Validate(src []byte) bool {
|
||||
// ValidateString as Validate, but for string.
|
||||
func ValidateString(src string) bool {
|
||||
return native.ValidateUTF8Fast(&src) == 0
|
||||
}
|
||||
}
|
||||
3
vendor/github.com/gabriel-vasile/mimetype/README.md
generated
vendored
3
vendor/github.com/gabriel-vasile/mimetype/README.md
generated
vendored
@@ -10,6 +10,9 @@
|
||||
</h6>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://travis-ci.org/gabriel-vasile/mimetype">
|
||||
<img alt="Build Status" src="https://travis-ci.org/gabriel-vasile/mimetype.svg?branch=master">
|
||||
</a>
|
||||
<a href="https://pkg.go.dev/github.com/gabriel-vasile/mimetype">
|
||||
<img alt="Go Reference" src="https://pkg.go.dev/badge/github.com/gabriel-vasile/mimetype.svg">
|
||||
</a>
|
||||
|
||||
38
vendor/github.com/gabriel-vasile/mimetype/internal/magic/binary.go
generated
vendored
38
vendor/github.com/gabriel-vasile/mimetype/internal/magic/binary.go
generated
vendored
@@ -150,34 +150,32 @@ func Marc(raw []byte, limit uint32) bool {
|
||||
}
|
||||
|
||||
// Glb matches a glTF model format file.
|
||||
// GLB is the binary file format representation of 3D models saved in
|
||||
// GLB is the binary file format representation of 3D models save in
|
||||
// the GL transmission Format (glTF).
|
||||
// GLB uses little endian and its header structure is as follows:
|
||||
// see more: https://docs.fileformat.com/3d/glb/
|
||||
// https://www.iana.org/assignments/media-types/model/gltf-binary
|
||||
// GLB file format is based on little endian and its header structure
|
||||
// show below:
|
||||
//
|
||||
// <-- 12-byte header -->
|
||||
// | magic | version | length |
|
||||
// | (uint32) | (uint32) | (uint32) |
|
||||
// | \x67\x6C\x54\x46 | \x01\x00\x00\x00 | ... |
|
||||
// | g l T F | 1 | ... |
|
||||
//
|
||||
// Visit [glTF specification] and [IANA glTF entry] for more details.
|
||||
//
|
||||
// [glTF specification]: https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html
|
||||
// [IANA glTF entry]: https://www.iana.org/assignments/media-types/model/gltf-binary
|
||||
// <-- 12-byte header -->
|
||||
// | magic | version | length |
|
||||
// | (uint32) | (uint32) | (uint32) |
|
||||
// | \x67\x6C\x54\x46 | \x01\x00\x00\x00 | ... |
|
||||
// | g l T F | 1 | ... |
|
||||
var Glb = prefix([]byte("\x67\x6C\x54\x46\x02\x00\x00\x00"),
|
||||
[]byte("\x67\x6C\x54\x46\x01\x00\x00\x00"))
|
||||
|
||||
// TzIf matches a Time Zone Information Format (TZif) file.
|
||||
// See more: https://tools.ietf.org/id/draft-murchison-tzdist-tzif-00.html#rfc.section.3
|
||||
// Its header structure is shown below:
|
||||
// +---------------+---+
|
||||
// | magic (4) | <-+-- version (1)
|
||||
// +---------------+---+---------------------------------------+
|
||||
// | [unused - reserved for future use] (15) |
|
||||
// +---------------+---------------+---------------+-----------+
|
||||
// | isutccnt (4) | isstdcnt (4) | leapcnt (4) |
|
||||
// +---------------+---------------+---------------+
|
||||
// | timecnt (4) | typecnt (4) | charcnt (4) |
|
||||
// +---------------+---+
|
||||
// | magic (4) | <-+-- version (1)
|
||||
// +---------------+---+---------------------------------------+
|
||||
// | [unused - reserved for future use] (15) |
|
||||
// +---------------+---------------+---------------+-----------+
|
||||
// | isutccnt (4) | isstdcnt (4) | leapcnt (4) |
|
||||
// +---------------+---------------+---------------+
|
||||
// | timecnt (4) | typecnt (4) | charcnt (4) |
|
||||
func TzIf(raw []byte, limit uint32) bool {
|
||||
// File is at least 44 bytes (header size).
|
||||
if len(raw) < 44 {
|
||||
|
||||
4
vendor/github.com/gabriel-vasile/mimetype/internal/magic/magic.go
generated
vendored
4
vendor/github.com/gabriel-vasile/mimetype/internal/magic/magic.go
generated
vendored
@@ -177,9 +177,7 @@ func newXMLSig(localName, xmlns string) xmlSig {
|
||||
// and, optionally, followed by the arguments for the interpreter.
|
||||
//
|
||||
// Ex:
|
||||
//
|
||||
// #! /usr/bin/env php
|
||||
//
|
||||
// #! /usr/bin/env php
|
||||
// /usr/bin/env is the interpreter, php is the first and only argument.
|
||||
func shebang(sigs ...[]byte) Detector {
|
||||
return func(raw []byte, limit uint32) bool {
|
||||
|
||||
18
vendor/github.com/gabriel-vasile/mimetype/internal/magic/text_csv.go
generated
vendored
18
vendor/github.com/gabriel-vasile/mimetype/internal/magic/text_csv.go
generated
vendored
@@ -3,7 +3,6 @@ package magic
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/csv"
|
||||
"errors"
|
||||
"io"
|
||||
)
|
||||
|
||||
@@ -20,23 +19,12 @@ func Tsv(raw []byte, limit uint32) bool {
|
||||
func sv(in []byte, comma rune, limit uint32) bool {
|
||||
r := csv.NewReader(dropLastLine(in, limit))
|
||||
r.Comma = comma
|
||||
r.ReuseRecord = true
|
||||
r.TrimLeadingSpace = true
|
||||
r.LazyQuotes = true
|
||||
r.Comment = '#'
|
||||
|
||||
lines := 0
|
||||
for {
|
||||
_, err := r.Read()
|
||||
if errors.Is(err, io.EOF) {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
lines++
|
||||
}
|
||||
|
||||
return r.FieldsPerRecord > 1 && lines > 1
|
||||
lines, err := r.ReadAll()
|
||||
return err == nil && r.FieldsPerRecord > 1 && len(lines) > 1
|
||||
}
|
||||
|
||||
// dropLastLine drops the last incomplete line from b.
|
||||
|
||||
3
vendor/github.com/gabriel-vasile/mimetype/mimetype.go
generated
vendored
3
vendor/github.com/gabriel-vasile/mimetype/mimetype.go
generated
vendored
@@ -39,8 +39,7 @@ func Detect(in []byte) *MIME {
|
||||
//
|
||||
// DetectReader assumes the reader offset is at the start. If the input is an
|
||||
// io.ReadSeeker you previously read from, it should be rewinded before detection:
|
||||
//
|
||||
// reader.Seek(0, io.SeekStart)
|
||||
// reader.Seek(0, io.SeekStart)
|
||||
func DetectReader(r io.Reader) (*MIME, error) {
|
||||
var in []byte
|
||||
var err error
|
||||
|
||||
2
vendor/github.com/gabriel-vasile/mimetype/supported_mimes.md
generated
vendored
2
vendor/github.com/gabriel-vasile/mimetype/supported_mimes.md
generated
vendored
@@ -1,4 +1,4 @@
|
||||
## 173 Supported MIME types
|
||||
## 172 Supported MIME types
|
||||
This file is automatically generated when running tests. Do not edit manually.
|
||||
|
||||
Extension | MIME type | Aliases
|
||||
|
||||
4
vendor/github.com/gin-gonic/gin/.gitignore
generated
vendored
4
vendor/github.com/gin-gonic/gin/.gitignore
generated
vendored
@@ -5,7 +5,3 @@ count.out
|
||||
test
|
||||
profile.out
|
||||
tmp.out
|
||||
|
||||
# Develop tools
|
||||
.idea/
|
||||
.vscode/
|
||||
|
||||
1
vendor/github.com/gin-gonic/gin/.golangci.yml
generated
vendored
1
vendor/github.com/gin-gonic/gin/.golangci.yml
generated
vendored
@@ -3,6 +3,7 @@ run:
|
||||
linters:
|
||||
enable:
|
||||
- asciicheck
|
||||
- depguard
|
||||
- dogsled
|
||||
- durationcheck
|
||||
- errcheck
|
||||
|
||||
29
vendor/github.com/gin-gonic/gin/.goreleaser.yaml
generated
vendored
29
vendor/github.com/gin-gonic/gin/.goreleaser.yaml
generated
vendored
@@ -1,7 +1,8 @@
|
||||
project_name: gin
|
||||
|
||||
builds:
|
||||
- # If true, skip the build.
|
||||
-
|
||||
# If true, skip the build.
|
||||
# Useful for library projects.
|
||||
# Default is false
|
||||
skip: true
|
||||
@@ -9,7 +10,7 @@ builds:
|
||||
changelog:
|
||||
# Set it to true if you wish to skip the changelog generation.
|
||||
# This may result in an empty release notes on GitHub/GitLab/Gitea.
|
||||
disable: false
|
||||
skip: false
|
||||
|
||||
# Changelog generation implementation to use.
|
||||
#
|
||||
@@ -20,7 +21,7 @@ changelog:
|
||||
# - `github-native`: uses the GitHub release notes generation API, disables the groups feature.
|
||||
#
|
||||
# Defaults to `git`.
|
||||
use: github
|
||||
use: git
|
||||
|
||||
# Sorts the changelog by the commit's messages.
|
||||
# Could either be asc, desc or empty
|
||||
@@ -37,20 +38,20 @@ changelog:
|
||||
- title: Features
|
||||
regexp: "^.*feat[(\\w)]*:+.*$"
|
||||
order: 0
|
||||
- title: "Bug fixes"
|
||||
- title: 'Bug fixes'
|
||||
regexp: "^.*fix[(\\w)]*:+.*$"
|
||||
order: 1
|
||||
- title: "Enhancements"
|
||||
- title: 'Enhancements'
|
||||
regexp: "^.*chore[(\\w)]*:+.*$"
|
||||
order: 2
|
||||
- title: "Refactor"
|
||||
regexp: "^.*refactor[(\\w)]*:+.*$"
|
||||
order: 3
|
||||
- title: "Build process updates"
|
||||
regexp: ^.*?(build|ci)(\(.+\))??!?:.+$
|
||||
order: 4
|
||||
- title: "Documentation updates"
|
||||
regexp: ^.*?docs?(\(.+\))??!?:.+$
|
||||
order: 4
|
||||
- title: Others
|
||||
order: 999
|
||||
|
||||
filters:
|
||||
# Commit messages matching the regexp listed here will be removed from
|
||||
# the changelog
|
||||
# Default is empty
|
||||
exclude:
|
||||
- '^docs'
|
||||
- 'CICD'
|
||||
- typo
|
||||
|
||||
1
vendor/github.com/gin-gonic/gin/Makefile
generated
vendored
1
vendor/github.com/gin-gonic/gin/Makefile
generated
vendored
@@ -42,7 +42,6 @@ fmt-check:
|
||||
exit 1; \
|
||||
fi;
|
||||
|
||||
.PHONY: vet
|
||||
vet:
|
||||
$(GO) vet $(VETPACKAGES)
|
||||
|
||||
|
||||
25
vendor/github.com/gin-gonic/gin/auth.go
generated
vendored
25
vendor/github.com/gin-gonic/gin/auth.go
generated
vendored
@@ -16,9 +16,6 @@ import (
|
||||
// AuthUserKey is the cookie name for user credential in basic auth.
|
||||
const AuthUserKey = "user"
|
||||
|
||||
// AuthProxyUserKey is the cookie name for proxy_user credential in basic auth for proxy.
|
||||
const AuthProxyUserKey = "proxy_user"
|
||||
|
||||
// Accounts defines a key/value for user/pass list of authorized logins.
|
||||
type Accounts map[string]string
|
||||
|
||||
@@ -92,25 +89,3 @@ func authorizationHeader(user, password string) string {
|
||||
base := user + ":" + password
|
||||
return "Basic " + base64.StdEncoding.EncodeToString(bytesconv.StringToBytes(base))
|
||||
}
|
||||
|
||||
// BasicAuthForProxy returns a Basic HTTP Proxy-Authorization middleware.
|
||||
// If the realm is empty, "Proxy Authorization Required" will be used by default.
|
||||
func BasicAuthForProxy(accounts Accounts, realm string) HandlerFunc {
|
||||
if realm == "" {
|
||||
realm = "Proxy Authorization Required"
|
||||
}
|
||||
realm = "Basic realm=" + strconv.Quote(realm)
|
||||
pairs := processAccounts(accounts)
|
||||
return func(c *Context) {
|
||||
proxyUser, found := pairs.searchCredential(c.requestHeader("Proxy-Authorization"))
|
||||
if !found {
|
||||
// Credentials doesn't match, we return 407 and abort handlers chain.
|
||||
c.Header("Proxy-Authenticate", realm)
|
||||
c.AbortWithStatus(http.StatusProxyAuthRequired)
|
||||
return
|
||||
}
|
||||
// The proxy_user credentials was found, set proxy_user's id to key AuthProxyUserKey in this context, the proxy_user's id can be read later using
|
||||
// c.MustGet(gin.AuthProxyUserKey).
|
||||
c.Set(AuthProxyUserKey, proxyUser)
|
||||
}
|
||||
}
|
||||
|
||||
27
vendor/github.com/gin-gonic/gin/binding/binding.go
generated
vendored
27
vendor/github.com/gin-gonic/gin/binding/binding.go
generated
vendored
@@ -21,7 +21,6 @@ const (
|
||||
MIMEMSGPACK = "application/x-msgpack"
|
||||
MIMEMSGPACK2 = "application/msgpack"
|
||||
MIMEYAML = "application/x-yaml"
|
||||
MIMEYAML2 = "application/yaml"
|
||||
MIMETOML = "application/toml"
|
||||
)
|
||||
|
||||
@@ -73,18 +72,18 @@ var Validator StructValidator = &defaultValidator{}
|
||||
// These implement the Binding interface and can be used to bind the data
|
||||
// present in the request to struct instances.
|
||||
var (
|
||||
JSON BindingBody = jsonBinding{}
|
||||
XML BindingBody = xmlBinding{}
|
||||
Form Binding = formBinding{}
|
||||
Query Binding = queryBinding{}
|
||||
FormPost Binding = formPostBinding{}
|
||||
FormMultipart Binding = formMultipartBinding{}
|
||||
ProtoBuf BindingBody = protobufBinding{}
|
||||
MsgPack BindingBody = msgpackBinding{}
|
||||
YAML BindingBody = yamlBinding{}
|
||||
Uri BindingUri = uriBinding{}
|
||||
Header Binding = headerBinding{}
|
||||
TOML BindingBody = tomlBinding{}
|
||||
JSON = jsonBinding{}
|
||||
XML = xmlBinding{}
|
||||
Form = formBinding{}
|
||||
Query = queryBinding{}
|
||||
FormPost = formPostBinding{}
|
||||
FormMultipart = formMultipartBinding{}
|
||||
ProtoBuf = protobufBinding{}
|
||||
MsgPack = msgpackBinding{}
|
||||
YAML = yamlBinding{}
|
||||
Uri = uriBinding{}
|
||||
Header = headerBinding{}
|
||||
TOML = tomlBinding{}
|
||||
)
|
||||
|
||||
// Default returns the appropriate Binding instance based on the HTTP method
|
||||
@@ -103,7 +102,7 @@ func Default(method, contentType string) Binding {
|
||||
return ProtoBuf
|
||||
case MIMEMSGPACK, MIMEMSGPACK2:
|
||||
return MsgPack
|
||||
case MIMEYAML, MIMEYAML2:
|
||||
case MIMEYAML:
|
||||
return YAML
|
||||
case MIMETOML:
|
||||
return TOML
|
||||
|
||||
3
vendor/github.com/gin-gonic/gin/binding/binding_nomsgpack.go
generated
vendored
3
vendor/github.com/gin-gonic/gin/binding/binding_nomsgpack.go
generated
vendored
@@ -19,7 +19,6 @@ const (
|
||||
MIMEMultipartPOSTForm = "multipart/form-data"
|
||||
MIMEPROTOBUF = "application/x-protobuf"
|
||||
MIMEYAML = "application/x-yaml"
|
||||
MIMEYAML2 = "application/yaml"
|
||||
MIMETOML = "application/toml"
|
||||
)
|
||||
|
||||
@@ -97,7 +96,7 @@ func Default(method, contentType string) Binding {
|
||||
return XML
|
||||
case MIMEPROTOBUF:
|
||||
return ProtoBuf
|
||||
case MIMEYAML, MIMEYAML2:
|
||||
case MIMEYAML:
|
||||
return YAML
|
||||
case MIMEMultipartPOSTForm:
|
||||
return FormMultipart
|
||||
|
||||
5
vendor/github.com/gin-gonic/gin/binding/default_validator.go
generated
vendored
5
vendor/github.com/gin-gonic/gin/binding/default_validator.go
generated
vendored
@@ -54,10 +54,7 @@ func (v *defaultValidator) ValidateStruct(obj any) error {
|
||||
value := reflect.ValueOf(obj)
|
||||
switch value.Kind() {
|
||||
case reflect.Ptr:
|
||||
if value.Elem().Kind() != reflect.Struct {
|
||||
return v.ValidateStruct(value.Elem().Interface())
|
||||
}
|
||||
return v.validateStruct(obj)
|
||||
return v.ValidateStruct(value.Elem().Interface())
|
||||
case reflect.Struct:
|
||||
return v.validateStruct(obj)
|
||||
case reflect.Slice, reflect.Array:
|
||||
|
||||
28
vendor/github.com/gin-gonic/gin/binding/form_mapping.go
generated
vendored
28
vendor/github.com/gin-gonic/gin/binding/form_mapping.go
generated
vendored
@@ -7,7 +7,6 @@ package binding
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"mime/multipart"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -165,23 +164,6 @@ func tryToSetValue(value reflect.Value, field reflect.StructField, setter setter
|
||||
return setter.TrySet(value, field, tagValue, setOpt)
|
||||
}
|
||||
|
||||
// BindUnmarshaler is the interface used to wrap the UnmarshalParam method.
|
||||
type BindUnmarshaler interface {
|
||||
// UnmarshalParam decodes and assigns a value from an form or query param.
|
||||
UnmarshalParam(param string) error
|
||||
}
|
||||
|
||||
// trySetCustom tries to set a custom type value
|
||||
// If the value implements the BindUnmarshaler interface, it will be used to set the value, we will return `true`
|
||||
// to skip the default value setting.
|
||||
func trySetCustom(val string, value reflect.Value) (isSet bool, err error) {
|
||||
switch v := value.Addr().Interface().(type) {
|
||||
case BindUnmarshaler:
|
||||
return true, v.UnmarshalParam(val)
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func setByForm(value reflect.Value, field reflect.StructField, form map[string][]string, tagValue string, opt setOptions) (isSet bool, err error) {
|
||||
vs, ok := form[tagValue]
|
||||
if !ok && !opt.isDefaultExists {
|
||||
@@ -211,9 +193,6 @@ func setByForm(value reflect.Value, field reflect.StructField, form map[string][
|
||||
if len(vs) > 0 {
|
||||
val = vs[0]
|
||||
}
|
||||
if ok, err := trySetCustom(val, value); ok {
|
||||
return ok, err
|
||||
}
|
||||
return true, setWithProperType(val, value, field)
|
||||
}
|
||||
}
|
||||
@@ -256,17 +235,10 @@ func setWithProperType(val string, value reflect.Value, field reflect.StructFiel
|
||||
switch value.Interface().(type) {
|
||||
case time.Time:
|
||||
return setTimeField(val, field, value)
|
||||
case multipart.FileHeader:
|
||||
return nil
|
||||
}
|
||||
return json.Unmarshal(bytesconv.StringToBytes(val), value.Addr().Interface())
|
||||
case reflect.Map:
|
||||
return json.Unmarshal(bytesconv.StringToBytes(val), value.Addr().Interface())
|
||||
case reflect.Ptr:
|
||||
if !value.Elem().IsValid() {
|
||||
value.Set(reflect.New(value.Type().Elem()))
|
||||
}
|
||||
return setWithProperType(val, value.Elem(), field)
|
||||
default:
|
||||
return errUnknownType
|
||||
}
|
||||
|
||||
52
vendor/github.com/gin-gonic/gin/context.go
generated
vendored
52
vendor/github.com/gin-gonic/gin/context.go
generated
vendored
@@ -43,10 +43,6 @@ const BodyBytesKey = "_gin-gonic/gin/bodybyteskey"
|
||||
// ContextKey is the key that a Context returns itself for.
|
||||
const ContextKey = "_gin-gonic/gin/contextkey"
|
||||
|
||||
type ContextKeyType int
|
||||
|
||||
const ContextRequestKey ContextKeyType = 0
|
||||
|
||||
// abortIndex represents a typical value used in abort functions.
|
||||
const abortIndex int8 = math.MaxInt8 >> 1
|
||||
|
||||
@@ -117,27 +113,20 @@ func (c *Context) Copy() *Context {
|
||||
cp := Context{
|
||||
writermem: c.writermem,
|
||||
Request: c.Request,
|
||||
Params: c.Params,
|
||||
engine: c.engine,
|
||||
}
|
||||
|
||||
cp.writermem.ResponseWriter = nil
|
||||
cp.Writer = &cp.writermem
|
||||
cp.index = abortIndex
|
||||
cp.handlers = nil
|
||||
cp.fullPath = c.fullPath
|
||||
|
||||
cKeys := c.Keys
|
||||
cp.Keys = make(map[string]any, len(cKeys))
|
||||
c.mu.RLock()
|
||||
for k, v := range cKeys {
|
||||
cp.Keys = map[string]any{}
|
||||
for k, v := range c.Keys {
|
||||
cp.Keys[k] = v
|
||||
}
|
||||
c.mu.RUnlock()
|
||||
|
||||
cParams := c.Params
|
||||
cp.Params = make([]Param, len(cParams))
|
||||
copy(cp.Params, cParams)
|
||||
|
||||
paramCopy := make([]Param, len(cp.Params))
|
||||
copy(paramCopy, cp.Params)
|
||||
cp.Params = paramCopy
|
||||
return &cp
|
||||
}
|
||||
|
||||
@@ -397,7 +386,7 @@ func (c *Context) GetStringMapStringSlice(key string) (smss map[string][]string)
|
||||
//
|
||||
// router.GET("/user/:id", func(c *gin.Context) {
|
||||
// // a GET request to /user/john
|
||||
// id := c.Param("id") // id == "john"
|
||||
// id := c.Param("id") // id == "/john"
|
||||
// // a GET request to /user/john/
|
||||
// id := c.Param("id") // id == "/john/"
|
||||
// })
|
||||
@@ -739,7 +728,7 @@ func (c *Context) ShouldBindHeader(obj any) error {
|
||||
|
||||
// ShouldBindUri binds the passed struct pointer using the specified binding engine.
|
||||
func (c *Context) ShouldBindUri(obj any) error {
|
||||
m := make(map[string][]string, len(c.Params))
|
||||
m := make(map[string][]string)
|
||||
for _, v := range c.Params {
|
||||
m[v.Key] = []string{v.Value}
|
||||
}
|
||||
@@ -774,26 +763,6 @@ func (c *Context) ShouldBindBodyWith(obj any, bb binding.BindingBody) (err error
|
||||
return bb.BindBody(body, obj)
|
||||
}
|
||||
|
||||
// ShouldBindBodyWithJSON is a shortcut for c.ShouldBindBodyWith(obj, binding.JSON).
|
||||
func (c *Context) ShouldBindBodyWithJSON(obj any) error {
|
||||
return c.ShouldBindBodyWith(obj, binding.JSON)
|
||||
}
|
||||
|
||||
// ShouldBindBodyWithXML is a shortcut for c.ShouldBindBodyWith(obj, binding.XML).
|
||||
func (c *Context) ShouldBindBodyWithXML(obj any) error {
|
||||
return c.ShouldBindBodyWith(obj, binding.XML)
|
||||
}
|
||||
|
||||
// ShouldBindBodyWithYAML is a shortcut for c.ShouldBindBodyWith(obj, binding.YAML).
|
||||
func (c *Context) ShouldBindBodyWithYAML(obj any) error {
|
||||
return c.ShouldBindBodyWith(obj, binding.YAML)
|
||||
}
|
||||
|
||||
// ShouldBindBodyWithTOML is a shortcut for c.ShouldBindBodyWith(obj, binding.TOML).
|
||||
func (c *Context) ShouldBindBodyWithTOML(obj any) error {
|
||||
return c.ShouldBindBodyWith(obj, binding.TOML)
|
||||
}
|
||||
|
||||
// ClientIP implements one best effort algorithm to return the real client IP.
|
||||
// It calls c.RemoteIP() under the hood, to check if the remote IP is a trusted proxy or not.
|
||||
// If it is it will then try to parse the headers defined in Engine.RemoteIPHeaders (defaulting to [X-Forwarded-For, X-Real-Ip]).
|
||||
@@ -904,9 +873,6 @@ func (c *Context) GetHeader(key string) string {
|
||||
|
||||
// GetRawData returns stream data.
|
||||
func (c *Context) GetRawData() ([]byte, error) {
|
||||
if c.Request.Body == nil {
|
||||
return nil, errors.New("cannot read nil body")
|
||||
}
|
||||
return io.ReadAll(c.Request.Body)
|
||||
}
|
||||
|
||||
@@ -1249,7 +1215,7 @@ func (c *Context) Err() error {
|
||||
// if no value is associated with key. Successive calls to Value with
|
||||
// the same key returns the same result.
|
||||
func (c *Context) Value(key any) any {
|
||||
if key == ContextRequestKey {
|
||||
if key == 0 {
|
||||
return c.Request
|
||||
}
|
||||
if key == ContextKey {
|
||||
|
||||
20
vendor/github.com/gin-gonic/gin/debug.go
generated
vendored
20
vendor/github.com/gin-gonic/gin/debug.go
generated
vendored
@@ -23,9 +23,6 @@ func IsDebugging() bool {
|
||||
// DebugPrintRouteFunc indicates debug log output format.
|
||||
var DebugPrintRouteFunc func(httpMethod, absolutePath, handlerName string, nuHandlers int)
|
||||
|
||||
// DebugPrintFunc indicates debug log output format.
|
||||
var DebugPrintFunc func(format string, values ...interface{})
|
||||
|
||||
func debugPrintRoute(httpMethod, absolutePath string, handlers HandlersChain) {
|
||||
if IsDebugging() {
|
||||
nuHandlers := len(handlers)
|
||||
@@ -51,19 +48,12 @@ func debugPrintLoadTemplate(tmpl *template.Template) {
|
||||
}
|
||||
|
||||
func debugPrint(format string, values ...any) {
|
||||
if !IsDebugging() {
|
||||
return
|
||||
if IsDebugging() {
|
||||
if !strings.HasSuffix(format, "\n") {
|
||||
format += "\n"
|
||||
}
|
||||
fmt.Fprintf(DefaultWriter, "[GIN-debug] "+format, values...)
|
||||
}
|
||||
|
||||
if DebugPrintFunc != nil {
|
||||
DebugPrintFunc(format, values...)
|
||||
return
|
||||
}
|
||||
|
||||
if !strings.HasSuffix(format, "\n") {
|
||||
format += "\n"
|
||||
}
|
||||
fmt.Fprintf(DefaultWriter, "[GIN-debug] "+format, values...)
|
||||
}
|
||||
|
||||
func getMinVer(v string) (uint64, error) {
|
||||
|
||||
2
vendor/github.com/gin-gonic/gin/deprecated.go
generated
vendored
2
vendor/github.com/gin-gonic/gin/deprecated.go
generated
vendored
@@ -12,8 +12,6 @@ import (
|
||||
|
||||
// BindWith binds the passed struct pointer using the specified binding engine.
|
||||
// See the binding package.
|
||||
//
|
||||
// Deprecated: Use MustBindWith or ShouldBindWith.
|
||||
func (c *Context) BindWith(obj any, b binding.Binding) error {
|
||||
log.Println(`BindWith(\"any, binding.Binding\") error is going to
|
||||
be deprecated, please check issue #662 and either use MustBindWith() if you
|
||||
|
||||
37
vendor/github.com/gin-gonic/gin/gin.go
generated
vendored
37
vendor/github.com/gin-gonic/gin/gin.go
generated
vendored
@@ -47,9 +47,6 @@ var regRemoveRepeatedChar = regexp.MustCompile("/{2,}")
|
||||
// HandlerFunc defines the handler used by gin middleware as return value.
|
||||
type HandlerFunc func(*Context)
|
||||
|
||||
// OptionFunc defines the function to change the default configuration
|
||||
type OptionFunc func(*Engine)
|
||||
|
||||
// HandlersChain defines a HandlerFunc slice.
|
||||
type HandlersChain []HandlerFunc
|
||||
|
||||
@@ -80,8 +77,6 @@ const (
|
||||
// PlatformCloudflare when using Cloudflare's CDN. Trust CF-Connecting-IP for determining
|
||||
// the client's IP
|
||||
PlatformCloudflare = "CF-Connecting-IP"
|
||||
// PlatformFlyIO when running on Fly.io. Trust Fly-Client-IP for determining the client's IP
|
||||
PlatformFlyIO = "Fly-Client-IP"
|
||||
)
|
||||
|
||||
// Engine is the framework's instance, it contains the muxer, middleware and configuration settings.
|
||||
@@ -185,7 +180,7 @@ var _ IRouter = (*Engine)(nil)
|
||||
// - ForwardedByClientIP: true
|
||||
// - UseRawPath: false
|
||||
// - UnescapePathValues: true
|
||||
func New(opts ...OptionFunc) *Engine {
|
||||
func New() *Engine {
|
||||
debugPrintWARNINGNew()
|
||||
engine := &Engine{
|
||||
RouterGroup: RouterGroup{
|
||||
@@ -214,15 +209,15 @@ func New(opts ...OptionFunc) *Engine {
|
||||
engine.pool.New = func() any {
|
||||
return engine.allocateContext(engine.maxParams)
|
||||
}
|
||||
return engine.With(opts...)
|
||||
return engine
|
||||
}
|
||||
|
||||
// Default returns an Engine instance with the Logger and Recovery middleware already attached.
|
||||
func Default(opts ...OptionFunc) *Engine {
|
||||
func Default() *Engine {
|
||||
debugPrintWARNINGDefault()
|
||||
engine := New()
|
||||
engine.Use(Logger(), Recovery())
|
||||
return engine.With(opts...)
|
||||
return engine
|
||||
}
|
||||
|
||||
func (engine *Engine) Handler() http.Handler {
|
||||
@@ -316,15 +311,6 @@ func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes {
|
||||
return engine
|
||||
}
|
||||
|
||||
// With returns a new Engine instance with the provided options.
|
||||
func (engine *Engine) With(opts ...OptionFunc) *Engine {
|
||||
for _, opt := range opts {
|
||||
opt(engine)
|
||||
}
|
||||
|
||||
return engine
|
||||
}
|
||||
|
||||
func (engine *Engine) rebuild404Handlers() {
|
||||
engine.allNoRoute = engine.combineHandlers(engine.noRoute)
|
||||
}
|
||||
@@ -348,6 +334,7 @@ func (engine *Engine) addRoute(method, path string, handlers HandlersChain) {
|
||||
}
|
||||
root.addRoute(path, handlers)
|
||||
|
||||
// Update maxParams
|
||||
if paramsCount := countParams(path); paramsCount > engine.maxParams {
|
||||
engine.maxParams = paramsCount
|
||||
}
|
||||
@@ -647,25 +634,17 @@ func (engine *Engine) handleHTTPRequest(c *Context) {
|
||||
}
|
||||
|
||||
if engine.HandleMethodNotAllowed {
|
||||
// According to RFC 7231 section 6.5.5, MUST generate an Allow header field in response
|
||||
// containing a list of the target resource's currently supported methods.
|
||||
allowed := make([]string, 0, len(t)-1)
|
||||
for _, tree := range engine.trees {
|
||||
if tree.method == httpMethod {
|
||||
continue
|
||||
}
|
||||
if value := tree.root.getValue(rPath, nil, c.skippedNodes, unescape); value.handlers != nil {
|
||||
allowed = append(allowed, tree.method)
|
||||
c.handlers = engine.allNoMethod
|
||||
serveError(c, http.StatusMethodNotAllowed, default405Body)
|
||||
return
|
||||
}
|
||||
}
|
||||
if len(allowed) > 0 {
|
||||
c.handlers = engine.allNoMethod
|
||||
c.writermem.Header().Set("Allow", strings.Join(allowed, ", "))
|
||||
serveError(c, http.StatusMethodNotAllowed, default405Body)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
c.handlers = engine.allNoRoute
|
||||
serveError(c, http.StatusNotFound, default404Body)
|
||||
}
|
||||
|
||||
63
vendor/github.com/gin-gonic/gin/logger.go
generated
vendored
63
vendor/github.com/gin-gonic/gin/logger.go
generated
vendored
@@ -47,15 +47,8 @@ type LoggerConfig struct {
|
||||
// SkipPaths is an url path array which logs are not written.
|
||||
// Optional.
|
||||
SkipPaths []string
|
||||
|
||||
// Skip is a Skipper that indicates which logs should not be written.
|
||||
// Optional.
|
||||
Skip Skipper
|
||||
}
|
||||
|
||||
// Skipper is a function to skip logs based on provided Context
|
||||
type Skipper func(c *Context) bool
|
||||
|
||||
// LogFormatter gives the signature of the formatter function passed to LoggerWithFormatter
|
||||
type LogFormatter func(params LogFormatterParams) string
|
||||
|
||||
@@ -90,8 +83,6 @@ func (p *LogFormatterParams) StatusCodeColor() string {
|
||||
code := p.StatusCode
|
||||
|
||||
switch {
|
||||
case code >= http.StatusContinue && code < http.StatusOK:
|
||||
return white
|
||||
case code >= http.StatusOK && code < http.StatusMultipleChoices:
|
||||
return green
|
||||
case code >= http.StatusMultipleChoices && code < http.StatusBadRequest:
|
||||
@@ -248,34 +239,32 @@ func LoggerWithConfig(conf LoggerConfig) HandlerFunc {
|
||||
// Process request
|
||||
c.Next()
|
||||
|
||||
// Log only when it is not being skipped
|
||||
if _, ok := skip[path]; ok || (conf.Skip != nil && conf.Skip(c)) {
|
||||
return
|
||||
// Log only when path is not being skipped
|
||||
if _, ok := skip[path]; !ok {
|
||||
param := LogFormatterParams{
|
||||
Request: c.Request,
|
||||
isTerm: isTerm,
|
||||
Keys: c.Keys,
|
||||
}
|
||||
|
||||
// Stop timer
|
||||
param.TimeStamp = time.Now()
|
||||
param.Latency = param.TimeStamp.Sub(start)
|
||||
|
||||
param.ClientIP = c.ClientIP()
|
||||
param.Method = c.Request.Method
|
||||
param.StatusCode = c.Writer.Status()
|
||||
param.ErrorMessage = c.Errors.ByType(ErrorTypePrivate).String()
|
||||
|
||||
param.BodySize = c.Writer.Size()
|
||||
|
||||
if raw != "" {
|
||||
path = path + "?" + raw
|
||||
}
|
||||
|
||||
param.Path = path
|
||||
|
||||
fmt.Fprint(out, formatter(param))
|
||||
}
|
||||
|
||||
param := LogFormatterParams{
|
||||
Request: c.Request,
|
||||
isTerm: isTerm,
|
||||
Keys: c.Keys,
|
||||
}
|
||||
|
||||
// Stop timer
|
||||
param.TimeStamp = time.Now()
|
||||
param.Latency = param.TimeStamp.Sub(start)
|
||||
|
||||
param.ClientIP = c.ClientIP()
|
||||
param.Method = c.Request.Method
|
||||
param.StatusCode = c.Writer.Status()
|
||||
param.ErrorMessage = c.Errors.ByType(ErrorTypePrivate).String()
|
||||
|
||||
param.BodySize = c.Writer.Size()
|
||||
|
||||
if raw != "" {
|
||||
path = path + "?" + raw
|
||||
}
|
||||
|
||||
param.Path = path
|
||||
|
||||
fmt.Fprint(out, formatter(param))
|
||||
}
|
||||
}
|
||||
|
||||
32
vendor/github.com/gin-gonic/gin/render/render.go
generated
vendored
32
vendor/github.com/gin-gonic/gin/render/render.go
generated
vendored
@@ -15,22 +15,22 @@ type Render interface {
|
||||
}
|
||||
|
||||
var (
|
||||
_ Render = (*JSON)(nil)
|
||||
_ Render = (*IndentedJSON)(nil)
|
||||
_ Render = (*SecureJSON)(nil)
|
||||
_ Render = (*JsonpJSON)(nil)
|
||||
_ Render = (*XML)(nil)
|
||||
_ Render = (*String)(nil)
|
||||
_ Render = (*Redirect)(nil)
|
||||
_ Render = (*Data)(nil)
|
||||
_ Render = (*HTML)(nil)
|
||||
_ HTMLRender = (*HTMLDebug)(nil)
|
||||
_ HTMLRender = (*HTMLProduction)(nil)
|
||||
_ Render = (*YAML)(nil)
|
||||
_ Render = (*Reader)(nil)
|
||||
_ Render = (*AsciiJSON)(nil)
|
||||
_ Render = (*ProtoBuf)(nil)
|
||||
_ Render = (*TOML)(nil)
|
||||
_ Render = JSON{}
|
||||
_ Render = IndentedJSON{}
|
||||
_ Render = SecureJSON{}
|
||||
_ Render = JsonpJSON{}
|
||||
_ Render = XML{}
|
||||
_ Render = String{}
|
||||
_ Render = Redirect{}
|
||||
_ Render = Data{}
|
||||
_ Render = HTML{}
|
||||
_ HTMLRender = HTMLDebug{}
|
||||
_ HTMLRender = HTMLProduction{}
|
||||
_ Render = YAML{}
|
||||
_ Render = Reader{}
|
||||
_ Render = AsciiJSON{}
|
||||
_ Render = ProtoBuf{}
|
||||
_ Render = TOML{}
|
||||
)
|
||||
|
||||
func writeContentType(w http.ResponseWriter, value []string) {
|
||||
|
||||
2
vendor/github.com/gin-gonic/gin/render/yaml.go
generated
vendored
2
vendor/github.com/gin-gonic/gin/render/yaml.go
generated
vendored
@@ -15,7 +15,7 @@ type YAML struct {
|
||||
Data any
|
||||
}
|
||||
|
||||
var yamlContentType = []string{"application/yaml; charset=utf-8"}
|
||||
var yamlContentType = []string{"application/x-yaml; charset=utf-8"}
|
||||
|
||||
// Render (YAML) marshals the given interface object and writes data with custom ContentType.
|
||||
func (r YAML) Render(w http.ResponseWriter) error {
|
||||
|
||||
43
vendor/github.com/gin-gonic/gin/tree.go
generated
vendored
43
vendor/github.com/gin-gonic/gin/tree.go
generated
vendored
@@ -351,10 +351,7 @@ func (n *node) insertChild(path string, fullPath string, handlers HandlersChain)
|
||||
}
|
||||
|
||||
if len(n.path) > 0 && n.path[len(n.path)-1] == '/' {
|
||||
pathSeg := ""
|
||||
if len(n.children) != 0 {
|
||||
pathSeg = strings.SplitN(n.children[0].path, "/", 2)[0]
|
||||
}
|
||||
pathSeg := strings.SplitN(n.children[0].path, "/", 2)[0]
|
||||
panic("catch-all wildcard '" + path +
|
||||
"' in new path '" + fullPath +
|
||||
"' conflicts with existing path segment '" + pathSeg +
|
||||
@@ -481,7 +478,7 @@ walk: // Outer loop for walking the tree
|
||||
// We can recommend to redirect to the same URL without a
|
||||
// trailing slash if a leaf exists for that path.
|
||||
value.tsr = path == "/" && n.handlers != nil
|
||||
return value
|
||||
return
|
||||
}
|
||||
|
||||
// Handle wildcard child, which is always at the end of the array
|
||||
@@ -500,14 +497,7 @@ walk: // Outer loop for walking the tree
|
||||
}
|
||||
|
||||
// Save param value
|
||||
if params != nil {
|
||||
// Preallocate capacity if necessary
|
||||
if cap(*params) < int(globalParamsCount) {
|
||||
newParams := make(Params, len(*params), globalParamsCount)
|
||||
copy(newParams, *params)
|
||||
*params = newParams
|
||||
}
|
||||
|
||||
if params != nil && cap(*params) > 0 {
|
||||
if value.params == nil {
|
||||
value.params = params
|
||||
}
|
||||
@@ -536,12 +526,12 @@ walk: // Outer loop for walking the tree
|
||||
|
||||
// ... but we can't
|
||||
value.tsr = len(path) == end+1
|
||||
return value
|
||||
return
|
||||
}
|
||||
|
||||
if value.handlers = n.handlers; value.handlers != nil {
|
||||
value.fullPath = n.fullPath
|
||||
return value
|
||||
return
|
||||
}
|
||||
if len(n.children) == 1 {
|
||||
// No handle found. Check if a handle for this path + a
|
||||
@@ -549,18 +539,11 @@ walk: // Outer loop for walking the tree
|
||||
n = n.children[0]
|
||||
value.tsr = (n.path == "/" && n.handlers != nil) || (n.path == "" && n.indices == "/")
|
||||
}
|
||||
return value
|
||||
return
|
||||
|
||||
case catchAll:
|
||||
// Save param value
|
||||
if params != nil {
|
||||
// Preallocate capacity if necessary
|
||||
if cap(*params) < int(globalParamsCount) {
|
||||
newParams := make(Params, len(*params), globalParamsCount)
|
||||
copy(newParams, *params)
|
||||
*params = newParams
|
||||
}
|
||||
|
||||
if value.params == nil {
|
||||
value.params = params
|
||||
}
|
||||
@@ -581,7 +564,7 @@ walk: // Outer loop for walking the tree
|
||||
|
||||
value.handlers = n.handlers
|
||||
value.fullPath = n.fullPath
|
||||
return value
|
||||
return
|
||||
|
||||
default:
|
||||
panic("invalid node type")
|
||||
@@ -612,7 +595,7 @@ walk: // Outer loop for walking the tree
|
||||
// Check if this node has a handle registered.
|
||||
if value.handlers = n.handlers; value.handlers != nil {
|
||||
value.fullPath = n.fullPath
|
||||
return value
|
||||
return
|
||||
}
|
||||
|
||||
// If there is no handle for this route, but this route has a
|
||||
@@ -620,12 +603,12 @@ walk: // Outer loop for walking the tree
|
||||
// additional trailing slash
|
||||
if path == "/" && n.wildChild && n.nType != root {
|
||||
value.tsr = true
|
||||
return value
|
||||
return
|
||||
}
|
||||
|
||||
if path == "/" && n.nType == static {
|
||||
value.tsr = true
|
||||
return value
|
||||
return
|
||||
}
|
||||
|
||||
// No handle found. Check if a handle for this path + a
|
||||
@@ -635,11 +618,11 @@ walk: // Outer loop for walking the tree
|
||||
n = n.children[i]
|
||||
value.tsr = (len(n.path) == 1 && n.handlers != nil) ||
|
||||
(n.nType == catchAll && n.children[0].handlers != nil)
|
||||
return value
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return value
|
||||
return
|
||||
}
|
||||
|
||||
// Nothing found. We can recommend to redirect to the same URL with an
|
||||
@@ -665,7 +648,7 @@ walk: // Outer loop for walking the tree
|
||||
}
|
||||
}
|
||||
|
||||
return value
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
2
vendor/github.com/gin-gonic/gin/version.go
generated
vendored
2
vendor/github.com/gin-gonic/gin/version.go
generated
vendored
@@ -5,4 +5,4 @@
|
||||
package gin
|
||||
|
||||
// Version is the current gin framework's version.
|
||||
const Version = "v1.10.0"
|
||||
const Version = "v1.9.1"
|
||||
|
||||
6
vendor/github.com/go-playground/validator/v10/Makefile
generated
vendored
6
vendor/github.com/go-playground/validator/v10/Makefile
generated
vendored
@@ -1,4 +1,4 @@
|
||||
GOCMD=go
|
||||
GOCMD=GO111MODULE=on go
|
||||
|
||||
linters-install:
|
||||
@golangci-lint --version >/dev/null 2>&1 || { \
|
||||
@@ -13,6 +13,6 @@ test:
|
||||
$(GOCMD) test -cover -race ./...
|
||||
|
||||
bench:
|
||||
$(GOCMD) test -run=NONE -bench=. -benchmem ./...
|
||||
$(GOCMD) test -bench=. -benchmem ./...
|
||||
|
||||
.PHONY: test lint linters-install
|
||||
.PHONY: test lint linters-install
|
||||
141
vendor/github.com/go-playground/validator/v10/README.md
generated
vendored
141
vendor/github.com/go-playground/validator/v10/README.md
generated
vendored
@@ -1,7 +1,7 @@
|
||||
Package validator
|
||||
=================
|
||||
<img align="right" src="logo.png">[](https://gitter.im/go-playground/validator?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||

|
||||
<img align="right" src="https://raw.githubusercontent.com/go-playground/validator/v10/logo.png">[](https://gitter.im/go-playground/validator?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||

|
||||
[](https://travis-ci.org/go-playground/validator)
|
||||
[](https://coveralls.io/github/go-playground/validator?branch=master)
|
||||
[](https://goreportcard.com/report/github.com/go-playground/validator)
|
||||
@@ -67,12 +67,6 @@ Please see https://pkg.go.dev/github.com/go-playground/validator/v10 for detaile
|
||||
Baked-in Validations
|
||||
------
|
||||
|
||||
### Special Notes:
|
||||
- If new to using validator it is highly recommended to initialize it using the `WithRequiredStructEnabled` option which is opt-in to new behaviour that will become the default behaviour in v11+. See documentation for more details.
|
||||
```go
|
||||
validate := validator.New(validator.WithRequiredStructEnabled())
|
||||
```
|
||||
|
||||
### Fields:
|
||||
|
||||
| Tag | Description |
|
||||
@@ -164,7 +158,6 @@ validate := validator.New(validator.WithRequiredStructEnabled())
|
||||
| credit_card | Credit Card Number |
|
||||
| mongodb | MongoDB ObjectID |
|
||||
| cron | Cron |
|
||||
| spicedb | SpiceDb ObjectID/Permission/Type |
|
||||
| datetime | Datetime |
|
||||
| e164 | e164 formatted phone number |
|
||||
| email | E-mail String
|
||||
@@ -178,7 +171,6 @@ validate := validator.New(validator.WithRequiredStructEnabled())
|
||||
| isbn | International Standard Book Number |
|
||||
| isbn10 | International Standard Book Number 10 |
|
||||
| isbn13 | International Standard Book Number 13 |
|
||||
| issn | International Standard Serial Number |
|
||||
| iso3166_1_alpha2 | Two-letter country code (ISO 3166-1 alpha-2) |
|
||||
| iso3166_1_alpha3 | Three-letter country code (ISO 3166-1 alpha-3) |
|
||||
| iso3166_1_alpha_numeric | Numeric country code (ISO 3166-1 numeric) |
|
||||
@@ -267,72 +259,71 @@ Benchmarks
|
||||
------
|
||||
###### Run on MacBook Pro (15-inch, 2017) go version go1.10.2 darwin/amd64
|
||||
```go
|
||||
go version go1.21.0 darwin/arm64
|
||||
goos: darwin
|
||||
goarch: arm64
|
||||
pkg: github.com/go-playground/validator/v10
|
||||
BenchmarkFieldSuccess-8 33142266 35.94 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkFieldSuccessParallel-8 200816191 6.568 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkFieldFailure-8 6779707 175.1 ns/op 200 B/op 4 allocs/op
|
||||
BenchmarkFieldFailureParallel-8 11044147 108.4 ns/op 200 B/op 4 allocs/op
|
||||
BenchmarkFieldArrayDiveSuccess-8 6054232 194.4 ns/op 97 B/op 5 allocs/op
|
||||
BenchmarkFieldArrayDiveSuccessParallel-8 12523388 94.07 ns/op 97 B/op 5 allocs/op
|
||||
BenchmarkFieldArrayDiveFailure-8 3587043 334.3 ns/op 300 B/op 10 allocs/op
|
||||
BenchmarkFieldArrayDiveFailureParallel-8 5816665 200.8 ns/op 300 B/op 10 allocs/op
|
||||
BenchmarkFieldMapDiveSuccess-8 2217910 540.1 ns/op 288 B/op 14 allocs/op
|
||||
BenchmarkFieldMapDiveSuccessParallel-8 4446698 258.7 ns/op 288 B/op 14 allocs/op
|
||||
BenchmarkFieldMapDiveFailure-8 2392759 504.6 ns/op 376 B/op 13 allocs/op
|
||||
BenchmarkFieldMapDiveFailureParallel-8 4244199 286.9 ns/op 376 B/op 13 allocs/op
|
||||
BenchmarkFieldMapDiveWithKeysSuccess-8 2005857 592.1 ns/op 288 B/op 14 allocs/op
|
||||
BenchmarkFieldMapDiveWithKeysSuccessParallel-8 4400850 296.9 ns/op 288 B/op 14 allocs/op
|
||||
BenchmarkFieldMapDiveWithKeysFailure-8 1850227 643.8 ns/op 553 B/op 16 allocs/op
|
||||
BenchmarkFieldMapDiveWithKeysFailureParallel-8 3293233 375.1 ns/op 553 B/op 16 allocs/op
|
||||
BenchmarkFieldCustomTypeSuccess-8 12174412 98.25 ns/op 32 B/op 2 allocs/op
|
||||
BenchmarkFieldCustomTypeSuccessParallel-8 34389907 35.49 ns/op 32 B/op 2 allocs/op
|
||||
BenchmarkFieldCustomTypeFailure-8 7582524 156.6 ns/op 184 B/op 3 allocs/op
|
||||
BenchmarkFieldCustomTypeFailureParallel-8 13019902 92.79 ns/op 184 B/op 3 allocs/op
|
||||
BenchmarkFieldOrTagSuccess-8 3427260 349.4 ns/op 16 B/op 1 allocs/op
|
||||
BenchmarkFieldOrTagSuccessParallel-8 15144128 81.25 ns/op 16 B/op 1 allocs/op
|
||||
BenchmarkFieldOrTagFailure-8 5913546 201.9 ns/op 216 B/op 5 allocs/op
|
||||
BenchmarkFieldOrTagFailureParallel-8 9810212 113.7 ns/op 216 B/op 5 allocs/op
|
||||
BenchmarkStructLevelValidationSuccess-8 13456327 87.66 ns/op 16 B/op 1 allocs/op
|
||||
BenchmarkStructLevelValidationSuccessParallel-8 41818888 27.77 ns/op 16 B/op 1 allocs/op
|
||||
BenchmarkStructLevelValidationFailure-8 4166284 272.6 ns/op 264 B/op 7 allocs/op
|
||||
BenchmarkStructLevelValidationFailureParallel-8 7594581 152.1 ns/op 264 B/op 7 allocs/op
|
||||
BenchmarkStructSimpleCustomTypeSuccess-8 6508082 182.6 ns/op 32 B/op 2 allocs/op
|
||||
BenchmarkStructSimpleCustomTypeSuccessParallel-8 23078605 54.78 ns/op 32 B/op 2 allocs/op
|
||||
BenchmarkStructSimpleCustomTypeFailure-8 3118352 381.0 ns/op 416 B/op 9 allocs/op
|
||||
BenchmarkStructSimpleCustomTypeFailureParallel-8 5300738 224.1 ns/op 432 B/op 10 allocs/op
|
||||
BenchmarkStructFilteredSuccess-8 4761807 251.1 ns/op 216 B/op 5 allocs/op
|
||||
BenchmarkStructFilteredSuccessParallel-8 8792598 128.6 ns/op 216 B/op 5 allocs/op
|
||||
BenchmarkStructFilteredFailure-8 5202573 232.1 ns/op 216 B/op 5 allocs/op
|
||||
BenchmarkStructFilteredFailureParallel-8 9591267 121.4 ns/op 216 B/op 5 allocs/op
|
||||
BenchmarkStructPartialSuccess-8 5188512 231.6 ns/op 224 B/op 4 allocs/op
|
||||
BenchmarkStructPartialSuccessParallel-8 9179776 123.1 ns/op 224 B/op 4 allocs/op
|
||||
BenchmarkStructPartialFailure-8 3071212 392.5 ns/op 440 B/op 9 allocs/op
|
||||
BenchmarkStructPartialFailureParallel-8 5344261 223.7 ns/op 440 B/op 9 allocs/op
|
||||
BenchmarkStructExceptSuccess-8 3184230 375.0 ns/op 424 B/op 8 allocs/op
|
||||
BenchmarkStructExceptSuccessParallel-8 10090130 108.9 ns/op 208 B/op 3 allocs/op
|
||||
BenchmarkStructExceptFailure-8 3347226 357.7 ns/op 424 B/op 8 allocs/op
|
||||
BenchmarkStructExceptFailureParallel-8 5654923 209.5 ns/op 424 B/op 8 allocs/op
|
||||
BenchmarkStructSimpleCrossFieldSuccess-8 5232265 229.1 ns/op 56 B/op 3 allocs/op
|
||||
BenchmarkStructSimpleCrossFieldSuccessParallel-8 17436674 64.75 ns/op 56 B/op 3 allocs/op
|
||||
BenchmarkStructSimpleCrossFieldFailure-8 3128613 383.6 ns/op 272 B/op 8 allocs/op
|
||||
BenchmarkStructSimpleCrossFieldFailureParallel-8 6994113 168.8 ns/op 272 B/op 8 allocs/op
|
||||
BenchmarkStructSimpleCrossStructCrossFieldSuccess-8 3506487 340.9 ns/op 64 B/op 4 allocs/op
|
||||
BenchmarkStructSimpleCrossStructCrossFieldSuccessParallel-8 13431300 91.77 ns/op 64 B/op 4 allocs/op
|
||||
BenchmarkStructSimpleCrossStructCrossFieldFailure-8 2410566 500.9 ns/op 288 B/op 9 allocs/op
|
||||
BenchmarkStructSimpleCrossStructCrossFieldFailureParallel-8 6344510 188.2 ns/op 288 B/op 9 allocs/op
|
||||
BenchmarkStructSimpleSuccess-8 8922726 133.8 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkStructSimpleSuccessParallel-8 55291153 23.63 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkStructSimpleFailure-8 3171553 378.4 ns/op 416 B/op 9 allocs/op
|
||||
BenchmarkStructSimpleFailureParallel-8 5571692 212.0 ns/op 416 B/op 9 allocs/op
|
||||
BenchmarkStructComplexSuccess-8 1683750 714.5 ns/op 224 B/op 5 allocs/op
|
||||
BenchmarkStructComplexSuccessParallel-8 4578046 257.0 ns/op 224 B/op 5 allocs/op
|
||||
BenchmarkStructComplexFailure-8 481585 2547 ns/op 3041 B/op 48 allocs/op
|
||||
BenchmarkStructComplexFailureParallel-8 965764 1577 ns/op 3040 B/op 48 allocs/op
|
||||
BenchmarkOneof-8 17380881 68.50 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkOneofParallel-8 8084733 153.5 ns/op 0 B/op 0 allocs/op
|
||||
goarch: amd64
|
||||
pkg: github.com/go-playground/validator
|
||||
BenchmarkFieldSuccess-8 20000000 83.6 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkFieldSuccessParallel-8 50000000 26.8 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkFieldFailure-8 5000000 291 ns/op 208 B/op 4 allocs/op
|
||||
BenchmarkFieldFailureParallel-8 20000000 107 ns/op 208 B/op 4 allocs/op
|
||||
BenchmarkFieldArrayDiveSuccess-8 2000000 623 ns/op 201 B/op 11 allocs/op
|
||||
BenchmarkFieldArrayDiveSuccessParallel-8 10000000 237 ns/op 201 B/op 11 allocs/op
|
||||
BenchmarkFieldArrayDiveFailure-8 2000000 859 ns/op 412 B/op 16 allocs/op
|
||||
BenchmarkFieldArrayDiveFailureParallel-8 5000000 335 ns/op 413 B/op 16 allocs/op
|
||||
BenchmarkFieldMapDiveSuccess-8 1000000 1292 ns/op 432 B/op 18 allocs/op
|
||||
BenchmarkFieldMapDiveSuccessParallel-8 3000000 467 ns/op 432 B/op 18 allocs/op
|
||||
BenchmarkFieldMapDiveFailure-8 1000000 1082 ns/op 512 B/op 16 allocs/op
|
||||
BenchmarkFieldMapDiveFailureParallel-8 5000000 425 ns/op 512 B/op 16 allocs/op
|
||||
BenchmarkFieldMapDiveWithKeysSuccess-8 1000000 1539 ns/op 480 B/op 21 allocs/op
|
||||
BenchmarkFieldMapDiveWithKeysSuccessParallel-8 3000000 613 ns/op 480 B/op 21 allocs/op
|
||||
BenchmarkFieldMapDiveWithKeysFailure-8 1000000 1413 ns/op 721 B/op 21 allocs/op
|
||||
BenchmarkFieldMapDiveWithKeysFailureParallel-8 3000000 575 ns/op 721 B/op 21 allocs/op
|
||||
BenchmarkFieldCustomTypeSuccess-8 10000000 216 ns/op 32 B/op 2 allocs/op
|
||||
BenchmarkFieldCustomTypeSuccessParallel-8 20000000 82.2 ns/op 32 B/op 2 allocs/op
|
||||
BenchmarkFieldCustomTypeFailure-8 5000000 274 ns/op 208 B/op 4 allocs/op
|
||||
BenchmarkFieldCustomTypeFailureParallel-8 20000000 116 ns/op 208 B/op 4 allocs/op
|
||||
BenchmarkFieldOrTagSuccess-8 2000000 740 ns/op 16 B/op 1 allocs/op
|
||||
BenchmarkFieldOrTagSuccessParallel-8 3000000 474 ns/op 16 B/op 1 allocs/op
|
||||
BenchmarkFieldOrTagFailure-8 3000000 471 ns/op 224 B/op 5 allocs/op
|
||||
BenchmarkFieldOrTagFailureParallel-8 3000000 414 ns/op 224 B/op 5 allocs/op
|
||||
BenchmarkStructLevelValidationSuccess-8 10000000 213 ns/op 32 B/op 2 allocs/op
|
||||
BenchmarkStructLevelValidationSuccessParallel-8 20000000 91.8 ns/op 32 B/op 2 allocs/op
|
||||
BenchmarkStructLevelValidationFailure-8 3000000 473 ns/op 304 B/op 8 allocs/op
|
||||
BenchmarkStructLevelValidationFailureParallel-8 10000000 234 ns/op 304 B/op 8 allocs/op
|
||||
BenchmarkStructSimpleCustomTypeSuccess-8 5000000 385 ns/op 32 B/op 2 allocs/op
|
||||
BenchmarkStructSimpleCustomTypeSuccessParallel-8 10000000 161 ns/op 32 B/op 2 allocs/op
|
||||
BenchmarkStructSimpleCustomTypeFailure-8 2000000 640 ns/op 424 B/op 9 allocs/op
|
||||
BenchmarkStructSimpleCustomTypeFailureParallel-8 5000000 318 ns/op 440 B/op 10 allocs/op
|
||||
BenchmarkStructFilteredSuccess-8 2000000 597 ns/op 288 B/op 9 allocs/op
|
||||
BenchmarkStructFilteredSuccessParallel-8 10000000 266 ns/op 288 B/op 9 allocs/op
|
||||
BenchmarkStructFilteredFailure-8 3000000 454 ns/op 256 B/op 7 allocs/op
|
||||
BenchmarkStructFilteredFailureParallel-8 10000000 214 ns/op 256 B/op 7 allocs/op
|
||||
BenchmarkStructPartialSuccess-8 3000000 502 ns/op 256 B/op 6 allocs/op
|
||||
BenchmarkStructPartialSuccessParallel-8 10000000 225 ns/op 256 B/op 6 allocs/op
|
||||
BenchmarkStructPartialFailure-8 2000000 702 ns/op 480 B/op 11 allocs/op
|
||||
BenchmarkStructPartialFailureParallel-8 5000000 329 ns/op 480 B/op 11 allocs/op
|
||||
BenchmarkStructExceptSuccess-8 2000000 793 ns/op 496 B/op 12 allocs/op
|
||||
BenchmarkStructExceptSuccessParallel-8 10000000 193 ns/op 240 B/op 5 allocs/op
|
||||
BenchmarkStructExceptFailure-8 2000000 639 ns/op 464 B/op 10 allocs/op
|
||||
BenchmarkStructExceptFailureParallel-8 5000000 300 ns/op 464 B/op 10 allocs/op
|
||||
BenchmarkStructSimpleCrossFieldSuccess-8 3000000 417 ns/op 72 B/op 3 allocs/op
|
||||
BenchmarkStructSimpleCrossFieldSuccessParallel-8 10000000 163 ns/op 72 B/op 3 allocs/op
|
||||
BenchmarkStructSimpleCrossFieldFailure-8 2000000 645 ns/op 304 B/op 8 allocs/op
|
||||
BenchmarkStructSimpleCrossFieldFailureParallel-8 5000000 285 ns/op 304 B/op 8 allocs/op
|
||||
BenchmarkStructSimpleCrossStructCrossFieldSuccess-8 3000000 588 ns/op 80 B/op 4 allocs/op
|
||||
BenchmarkStructSimpleCrossStructCrossFieldSuccessParallel-8 10000000 221 ns/op 80 B/op 4 allocs/op
|
||||
BenchmarkStructSimpleCrossStructCrossFieldFailure-8 2000000 868 ns/op 320 B/op 9 allocs/op
|
||||
BenchmarkStructSimpleCrossStructCrossFieldFailureParallel-8 5000000 337 ns/op 320 B/op 9 allocs/op
|
||||
BenchmarkStructSimpleSuccess-8 5000000 260 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkStructSimpleSuccessParallel-8 20000000 90.6 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkStructSimpleFailure-8 2000000 619 ns/op 424 B/op 9 allocs/op
|
||||
BenchmarkStructSimpleFailureParallel-8 5000000 296 ns/op 424 B/op 9 allocs/op
|
||||
BenchmarkStructComplexSuccess-8 1000000 1454 ns/op 128 B/op 8 allocs/op
|
||||
BenchmarkStructComplexSuccessParallel-8 3000000 579 ns/op 128 B/op 8 allocs/op
|
||||
BenchmarkStructComplexFailure-8 300000 4140 ns/op 3041 B/op 53 allocs/op
|
||||
BenchmarkStructComplexFailureParallel-8 1000000 2127 ns/op 3041 B/op 53 allocs/op
|
||||
BenchmarkOneof-8 10000000 140 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkOneofParallel-8 20000000 70.1 ns/op 0 B/op 0 allocs/op
|
||||
```
|
||||
|
||||
Complementary Software
|
||||
|
||||
233
vendor/github.com/go-playground/validator/v10/baked_in.go
generated
vendored
233
vendor/github.com/go-playground/validator/v10/baked_in.go
generated
vendored
@@ -23,7 +23,7 @@ import (
|
||||
"golang.org/x/text/language"
|
||||
|
||||
"github.com/gabriel-vasile/mimetype"
|
||||
urn "github.com/leodido/go-urn"
|
||||
"github.com/leodido/go-urn"
|
||||
)
|
||||
|
||||
// Func accepts a FieldLevel interface for all validation needs. The return
|
||||
@@ -51,7 +51,6 @@ var (
|
||||
endKeysTag: {},
|
||||
structOnlyTag: {},
|
||||
omitempty: {},
|
||||
omitnil: {},
|
||||
skipValidationTag: {},
|
||||
utf8HexComma: {},
|
||||
utf8Pipe: {},
|
||||
@@ -64,9 +63,8 @@ var (
|
||||
// defines a common or complex set of validation(s) to simplify
|
||||
// adding validation to structs.
|
||||
bakedInAliases = map[string]string{
|
||||
"iscolor": "hexcolor|rgb|rgba|hsl|hsla",
|
||||
"country_code": "iso3166_1_alpha2|iso3166_1_alpha3|iso3166_1_alpha_numeric",
|
||||
"eu_country_code": "iso3166_1_alpha2_eu|iso3166_1_alpha3_eu|iso3166_1_alpha_numeric_eu",
|
||||
"iscolor": "hexcolor|rgb|rgba|hsl|hsla",
|
||||
"country_code": "iso3166_1_alpha2|iso3166_1_alpha3|iso3166_1_alpha_numeric",
|
||||
}
|
||||
|
||||
// bakedInValidators is the default map of ValidationFunc
|
||||
@@ -134,7 +132,6 @@ var (
|
||||
"urn_rfc2141": isUrnRFC2141, // RFC 2141
|
||||
"file": isFile,
|
||||
"filepath": isFilePath,
|
||||
"base32": isBase32,
|
||||
"base64": isBase64,
|
||||
"base64url": isBase64URL,
|
||||
"base64rawurl": isBase64RawURL,
|
||||
@@ -152,7 +149,6 @@ var (
|
||||
"isbn": isISBN,
|
||||
"isbn10": isISBN10,
|
||||
"isbn13": isISBN13,
|
||||
"issn": isISSN,
|
||||
"eth_addr": isEthereumAddress,
|
||||
"eth_addr_checksum": isEthereumAddressChecksum,
|
||||
"btc_addr": isBitcoinAddress,
|
||||
@@ -218,11 +214,8 @@ var (
|
||||
"datetime": isDatetime,
|
||||
"timezone": isTimeZone,
|
||||
"iso3166_1_alpha2": isIso3166Alpha2,
|
||||
"iso3166_1_alpha2_eu": isIso3166Alpha2EU,
|
||||
"iso3166_1_alpha3": isIso3166Alpha3,
|
||||
"iso3166_1_alpha3_eu": isIso3166Alpha3EU,
|
||||
"iso3166_1_alpha_numeric": isIso3166AlphaNumeric,
|
||||
"iso3166_1_alpha_numeric_eu": isIso3166AlphaNumericEU,
|
||||
"iso3166_2": isIso31662,
|
||||
"iso4217": isIso4217,
|
||||
"iso4217_numeric": isIso4217Numeric,
|
||||
@@ -237,7 +230,6 @@ var (
|
||||
"luhn_checksum": hasLuhnChecksum,
|
||||
"mongodb": isMongoDB,
|
||||
"cron": isCron,
|
||||
"spicedb": isSpiceDB,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -380,9 +372,9 @@ func isMAC(fl FieldLevel) bool {
|
||||
|
||||
// isCIDRv4 is the validation function for validating if the field's value is a valid v4 CIDR address.
|
||||
func isCIDRv4(fl FieldLevel) bool {
|
||||
ip, net, err := net.ParseCIDR(fl.Field().String())
|
||||
ip, _, err := net.ParseCIDR(fl.Field().String())
|
||||
|
||||
return err == nil && ip.To4() != nil && net.IP.Equal(ip)
|
||||
return err == nil && ip.To4() != nil
|
||||
}
|
||||
|
||||
// isCIDRv6 is the validation function for validating if the field's value is a valid v6 CIDR address.
|
||||
@@ -515,47 +507,47 @@ func isASCII(fl FieldLevel) bool {
|
||||
|
||||
// isUUID5 is the validation function for validating if the field's value is a valid v5 UUID.
|
||||
func isUUID5(fl FieldLevel) bool {
|
||||
return fieldMatchesRegexByStringerValOrString(uUID5Regex, fl)
|
||||
return uUID5Regex.MatchString(fl.Field().String())
|
||||
}
|
||||
|
||||
// isUUID4 is the validation function for validating if the field's value is a valid v4 UUID.
|
||||
func isUUID4(fl FieldLevel) bool {
|
||||
return fieldMatchesRegexByStringerValOrString(uUID4Regex, fl)
|
||||
return uUID4Regex.MatchString(fl.Field().String())
|
||||
}
|
||||
|
||||
// isUUID3 is the validation function for validating if the field's value is a valid v3 UUID.
|
||||
func isUUID3(fl FieldLevel) bool {
|
||||
return fieldMatchesRegexByStringerValOrString(uUID3Regex, fl)
|
||||
return uUID3Regex.MatchString(fl.Field().String())
|
||||
}
|
||||
|
||||
// isUUID is the validation function for validating if the field's value is a valid UUID of any version.
|
||||
func isUUID(fl FieldLevel) bool {
|
||||
return fieldMatchesRegexByStringerValOrString(uUIDRegex, fl)
|
||||
return uUIDRegex.MatchString(fl.Field().String())
|
||||
}
|
||||
|
||||
// isUUID5RFC4122 is the validation function for validating if the field's value is a valid RFC4122 v5 UUID.
|
||||
func isUUID5RFC4122(fl FieldLevel) bool {
|
||||
return fieldMatchesRegexByStringerValOrString(uUID5RFC4122Regex, fl)
|
||||
return uUID5RFC4122Regex.MatchString(fl.Field().String())
|
||||
}
|
||||
|
||||
// isUUID4RFC4122 is the validation function for validating if the field's value is a valid RFC4122 v4 UUID.
|
||||
func isUUID4RFC4122(fl FieldLevel) bool {
|
||||
return fieldMatchesRegexByStringerValOrString(uUID4RFC4122Regex, fl)
|
||||
return uUID4RFC4122Regex.MatchString(fl.Field().String())
|
||||
}
|
||||
|
||||
// isUUID3RFC4122 is the validation function for validating if the field's value is a valid RFC4122 v3 UUID.
|
||||
func isUUID3RFC4122(fl FieldLevel) bool {
|
||||
return fieldMatchesRegexByStringerValOrString(uUID3RFC4122Regex, fl)
|
||||
return uUID3RFC4122Regex.MatchString(fl.Field().String())
|
||||
}
|
||||
|
||||
// isUUIDRFC4122 is the validation function for validating if the field's value is a valid RFC4122 UUID of any version.
|
||||
func isUUIDRFC4122(fl FieldLevel) bool {
|
||||
return fieldMatchesRegexByStringerValOrString(uUIDRFC4122Regex, fl)
|
||||
return uUIDRFC4122Regex.MatchString(fl.Field().String())
|
||||
}
|
||||
|
||||
// isULID is the validation function for validating if the field's value is a valid ULID.
|
||||
func isULID(fl FieldLevel) bool {
|
||||
return fieldMatchesRegexByStringerValOrString(uLIDRegex, fl)
|
||||
return uLIDRegex.MatchString(fl.Field().String())
|
||||
}
|
||||
|
||||
// isMD4 is the validation function for validating if the field's value is a valid MD4.
|
||||
@@ -657,32 +649,6 @@ func isISBN10(fl FieldLevel) bool {
|
||||
return checksum%11 == 0
|
||||
}
|
||||
|
||||
// isISSN is the validation function for validating if the field's value is a valid ISSN.
|
||||
func isISSN(fl FieldLevel) bool {
|
||||
s := fl.Field().String()
|
||||
|
||||
if !iSSNRegex.MatchString(s) {
|
||||
return false
|
||||
}
|
||||
s = strings.ReplaceAll(s, "-", "")
|
||||
|
||||
pos := 8
|
||||
checksum := 0
|
||||
|
||||
for i := 0; i < 7; i++ {
|
||||
checksum += pos * int(s[i]-'0')
|
||||
pos--
|
||||
}
|
||||
|
||||
if s[7] == 'X' {
|
||||
checksum += 10
|
||||
} else {
|
||||
checksum += int(s[7] - '0')
|
||||
}
|
||||
|
||||
return checksum%11 == 0
|
||||
}
|
||||
|
||||
// isEthereumAddress is the validation function for validating if the field's value is a valid Ethereum address.
|
||||
func isEthereumAddress(fl FieldLevel) bool {
|
||||
address := fl.Field().String()
|
||||
@@ -1328,13 +1294,8 @@ func isEq(fl FieldLevel) bool {
|
||||
|
||||
return field.Uint() == p
|
||||
|
||||
case reflect.Float32:
|
||||
p := asFloat32(param)
|
||||
|
||||
return field.Float() == p
|
||||
|
||||
case reflect.Float64:
|
||||
p := asFloat64(param)
|
||||
case reflect.Float32, reflect.Float64:
|
||||
p := asFloat(param)
|
||||
|
||||
return field.Float() == p
|
||||
|
||||
@@ -1404,11 +1365,6 @@ func isPostcodeByIso3166Alpha2Field(fl FieldLevel) bool {
|
||||
return reg.MatchString(field.String())
|
||||
}
|
||||
|
||||
// isBase32 is the validation function for validating if the current field's value is a valid base 32.
|
||||
func isBase32(fl FieldLevel) bool {
|
||||
return base32Regex.MatchString(fl.Field().String())
|
||||
}
|
||||
|
||||
// isBase64 is the validation function for validating if the current field's value is a valid base 64.
|
||||
func isBase64(fl FieldLevel) bool {
|
||||
return base64Regex.MatchString(fl.Field().String())
|
||||
@@ -1451,15 +1407,6 @@ func isURI(fl FieldLevel) bool {
|
||||
panic(fmt.Sprintf("Bad field type %T", field.Interface()))
|
||||
}
|
||||
|
||||
// isFileURL is the helper function for validating if the `path` valid file URL as per RFC8089
|
||||
func isFileURL(path string) bool {
|
||||
if !strings.HasPrefix(path, "file:/") {
|
||||
return false
|
||||
}
|
||||
_, err := url.ParseRequestURI(path)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// isURL is the validation function for validating if the current field's value is a valid URL.
|
||||
func isURL(fl FieldLevel) bool {
|
||||
field := fl.Field()
|
||||
@@ -1467,25 +1414,25 @@ func isURL(fl FieldLevel) bool {
|
||||
switch field.Kind() {
|
||||
case reflect.String:
|
||||
|
||||
s := strings.ToLower(field.String())
|
||||
var i int
|
||||
s := field.String()
|
||||
|
||||
// checks needed as of Go 1.6 because of change https://github.com/golang/go/commit/617c93ce740c3c3cc28cdd1a0d712be183d0b328#diff-6c2d018290e298803c0c9419d8739885L195
|
||||
// emulate browser and strip the '#' suffix prior to validation. see issue-#237
|
||||
if i = strings.Index(s, "#"); i > -1 {
|
||||
s = s[:i]
|
||||
}
|
||||
|
||||
if len(s) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
if isFileURL(s) {
|
||||
return true
|
||||
}
|
||||
url, err := url.ParseRequestURI(s)
|
||||
|
||||
url, err := url.Parse(s)
|
||||
if err != nil || url.Scheme == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
if url.Host == "" && url.Fragment == "" && url.Opaque == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -1503,13 +1450,7 @@ func isHttpURL(fl FieldLevel) bool {
|
||||
case reflect.String:
|
||||
|
||||
s := strings.ToLower(field.String())
|
||||
|
||||
url, err := url.Parse(s)
|
||||
if err != nil || url.Host == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
return url.Scheme == "http" || url.Scheme == "https"
|
||||
return strings.HasPrefix(s, "http://") || strings.HasPrefix(s, "https://")
|
||||
}
|
||||
|
||||
panic(fmt.Sprintf("Bad field type %T", field.Interface()))
|
||||
@@ -1618,10 +1559,6 @@ func isFilePath(fl FieldLevel) bool {
|
||||
|
||||
field := fl.Field()
|
||||
|
||||
// Not valid if it is a directory.
|
||||
if isDir(fl) {
|
||||
return false
|
||||
}
|
||||
// If it exists, it obviously is valid.
|
||||
// This is done first to avoid code duplication and unnecessary additional logic.
|
||||
if exists = isFile(fl); exists {
|
||||
@@ -1771,7 +1708,7 @@ func hasValue(fl FieldLevel) bool {
|
||||
if fl.(*validate).fldIsPointer && field.Interface() != nil {
|
||||
return true
|
||||
}
|
||||
return field.IsValid() && !field.IsZero()
|
||||
return field.IsValid() && field.Interface() != reflect.Zero(field.Type()).Interface()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1795,7 +1732,7 @@ func requireCheckFieldKind(fl FieldLevel, param string, defaultNotFoundValue boo
|
||||
if nullable && field.Interface() != nil {
|
||||
return false
|
||||
}
|
||||
return field.IsValid() && field.IsZero()
|
||||
return field.IsValid() && field.Interface() == reflect.Zero(field.Type()).Interface()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1816,11 +1753,8 @@ func requireCheckFieldValue(
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
return field.Uint() == asUint(value)
|
||||
|
||||
case reflect.Float32:
|
||||
return field.Float() == asFloat32(value)
|
||||
|
||||
case reflect.Float64:
|
||||
return field.Float() == asFloat64(value)
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return field.Float() == asFloat(value)
|
||||
|
||||
case reflect.Slice, reflect.Map, reflect.Array:
|
||||
return int64(field.Len()) == asInt(value)
|
||||
@@ -2119,13 +2053,8 @@ func isGte(fl FieldLevel) bool {
|
||||
|
||||
return field.Uint() >= p
|
||||
|
||||
case reflect.Float32:
|
||||
p := asFloat32(param)
|
||||
|
||||
return field.Float() >= p
|
||||
|
||||
case reflect.Float64:
|
||||
p := asFloat64(param)
|
||||
case reflect.Float32, reflect.Float64:
|
||||
p := asFloat(param)
|
||||
|
||||
return field.Float() >= p
|
||||
|
||||
@@ -2170,16 +2099,10 @@ func isGt(fl FieldLevel) bool {
|
||||
|
||||
return field.Uint() > p
|
||||
|
||||
case reflect.Float32:
|
||||
p := asFloat32(param)
|
||||
case reflect.Float32, reflect.Float64:
|
||||
p := asFloat(param)
|
||||
|
||||
return field.Float() > p
|
||||
|
||||
case reflect.Float64:
|
||||
p := asFloat64(param)
|
||||
|
||||
return field.Float() > p
|
||||
|
||||
case reflect.Struct:
|
||||
|
||||
if field.Type().ConvertibleTo(timeType) {
|
||||
@@ -2218,13 +2141,8 @@ func hasLengthOf(fl FieldLevel) bool {
|
||||
|
||||
return field.Uint() == p
|
||||
|
||||
case reflect.Float32:
|
||||
p := asFloat32(param)
|
||||
|
||||
return field.Float() == p
|
||||
|
||||
case reflect.Float64:
|
||||
p := asFloat64(param)
|
||||
case reflect.Float32, reflect.Float64:
|
||||
p := asFloat(param)
|
||||
|
||||
return field.Float() == p
|
||||
}
|
||||
@@ -2356,13 +2274,8 @@ func isLte(fl FieldLevel) bool {
|
||||
|
||||
return field.Uint() <= p
|
||||
|
||||
case reflect.Float32:
|
||||
p := asFloat32(param)
|
||||
|
||||
return field.Float() <= p
|
||||
|
||||
case reflect.Float64:
|
||||
p := asFloat64(param)
|
||||
case reflect.Float32, reflect.Float64:
|
||||
p := asFloat(param)
|
||||
|
||||
return field.Float() <= p
|
||||
|
||||
@@ -2407,13 +2320,8 @@ func isLt(fl FieldLevel) bool {
|
||||
|
||||
return field.Uint() < p
|
||||
|
||||
case reflect.Float32:
|
||||
p := asFloat32(param)
|
||||
|
||||
return field.Float() < p
|
||||
|
||||
case reflect.Float64:
|
||||
p := asFloat64(param)
|
||||
case reflect.Float32, reflect.Float64:
|
||||
p := asFloat(param)
|
||||
|
||||
return field.Float() < p
|
||||
|
||||
@@ -2660,17 +2568,9 @@ func isDirPath(fl FieldLevel) bool {
|
||||
func isJSON(fl FieldLevel) bool {
|
||||
field := fl.Field()
|
||||
|
||||
switch field.Kind() {
|
||||
case reflect.String:
|
||||
if field.Kind() == reflect.String {
|
||||
val := field.String()
|
||||
return json.Valid([]byte(val))
|
||||
case reflect.Slice:
|
||||
fieldType := field.Type()
|
||||
|
||||
if fieldType.ConvertibleTo(byteSliceType) {
|
||||
b := field.Convert(byteSliceType).Interface().([]byte)
|
||||
return json.Valid(b)
|
||||
}
|
||||
}
|
||||
|
||||
panic(fmt.Sprintf("Bad field type %T", field.Interface()))
|
||||
@@ -2772,24 +2672,12 @@ func isIso3166Alpha2(fl FieldLevel) bool {
|
||||
return iso3166_1_alpha2[val]
|
||||
}
|
||||
|
||||
// isIso3166Alpha2EU is the validation function for validating if the current field's value is a valid iso3166-1 alpha-2 European Union country code.
|
||||
func isIso3166Alpha2EU(fl FieldLevel) bool {
|
||||
val := fl.Field().String()
|
||||
return iso3166_1_alpha2_eu[val]
|
||||
}
|
||||
|
||||
// isIso3166Alpha3 is the validation function for validating if the current field's value is a valid iso3166-1 alpha-3 country code.
|
||||
func isIso3166Alpha3(fl FieldLevel) bool {
|
||||
val := fl.Field().String()
|
||||
return iso3166_1_alpha3[val]
|
||||
}
|
||||
|
||||
// isIso3166Alpha3EU is the validation function for validating if the current field's value is a valid iso3166-1 alpha-3 European Union country code.
|
||||
func isIso3166Alpha3EU(fl FieldLevel) bool {
|
||||
val := fl.Field().String()
|
||||
return iso3166_1_alpha3_eu[val]
|
||||
}
|
||||
|
||||
// isIso3166AlphaNumeric is the validation function for validating if the current field's value is a valid iso3166-1 alpha-numeric country code.
|
||||
func isIso3166AlphaNumeric(fl FieldLevel) bool {
|
||||
field := fl.Field()
|
||||
@@ -2812,28 +2700,6 @@ func isIso3166AlphaNumeric(fl FieldLevel) bool {
|
||||
return iso3166_1_alpha_numeric[code]
|
||||
}
|
||||
|
||||
// isIso3166AlphaNumericEU is the validation function for validating if the current field's value is a valid iso3166-1 alpha-numeric European Union country code.
|
||||
func isIso3166AlphaNumericEU(fl FieldLevel) bool {
|
||||
field := fl.Field()
|
||||
|
||||
var code int
|
||||
switch field.Kind() {
|
||||
case reflect.String:
|
||||
i, err := strconv.Atoi(field.String())
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
code = i % 1000
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
code = int(field.Int() % 1000)
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
code = int(field.Uint() % 1000)
|
||||
default:
|
||||
panic(fmt.Sprintf("Bad field type %T", field.Interface()))
|
||||
}
|
||||
return iso3166_1_alpha_numeric_eu[code]
|
||||
}
|
||||
|
||||
// isIso31662 is the validation function for validating if the current field's value is a valid iso3166-2 code.
|
||||
func isIso31662(fl FieldLevel) bool {
|
||||
val := fl.Field().String()
|
||||
@@ -2932,23 +2798,6 @@ func isMongoDB(fl FieldLevel) bool {
|
||||
return mongodbRegex.MatchString(val)
|
||||
}
|
||||
|
||||
// isSpiceDB is the validation function for validating if the current field's value is valid for use with Authzed SpiceDB in the indicated way
|
||||
func isSpiceDB(fl FieldLevel) bool {
|
||||
val := fl.Field().String()
|
||||
param := fl.Param()
|
||||
|
||||
switch param {
|
||||
case "permission":
|
||||
return spicedbPermissionRegex.MatchString(val)
|
||||
case "type":
|
||||
return spicedbTypeRegex.MatchString(val)
|
||||
case "id", "":
|
||||
return spicedbIDRegex.MatchString(val)
|
||||
}
|
||||
|
||||
panic("Unrecognized parameter: " + param)
|
||||
}
|
||||
|
||||
// isCreditCard is the validation function for validating if the current field's value is a valid credit card number
|
||||
func isCreditCard(fl FieldLevel) bool {
|
||||
val := fl.Field().String()
|
||||
|
||||
7
vendor/github.com/go-playground/validator/v10/cache.go
generated
vendored
7
vendor/github.com/go-playground/validator/v10/cache.go
generated
vendored
@@ -20,7 +20,6 @@ const (
|
||||
typeOr
|
||||
typeKeys
|
||||
typeEndKeys
|
||||
typeOmitNil
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -126,7 +125,7 @@ func (v *Validate) extractStructCache(current reflect.Value, sName string) *cStr
|
||||
|
||||
fld = typ.Field(i)
|
||||
|
||||
if !v.privateFieldValidation && !fld.Anonymous && len(fld.PkgPath) > 0 {
|
||||
if !fld.Anonymous && len(fld.PkgPath) > 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -253,10 +252,6 @@ func (v *Validate) parseFieldTagsRecursive(tag string, fieldName string, alias s
|
||||
current.typeof = typeOmitEmpty
|
||||
continue
|
||||
|
||||
case omitnil:
|
||||
current.typeof = typeOmitNil
|
||||
continue
|
||||
|
||||
case structOnlyTag:
|
||||
current.typeof = typeStructOnly
|
||||
continue
|
||||
|
||||
27
vendor/github.com/go-playground/validator/v10/country_codes.go
generated
vendored
27
vendor/github.com/go-playground/validator/v10/country_codes.go
generated
vendored
@@ -54,15 +54,6 @@ var iso3166_1_alpha2 = map[string]bool{
|
||||
"EH": true, "YE": true, "ZM": true, "ZW": true, "XK": true,
|
||||
}
|
||||
|
||||
var iso3166_1_alpha2_eu = map[string]bool{
|
||||
"AT": true, "BE": true, "BG": true, "HR": true, "CY": true,
|
||||
"CZ": true, "DK": true, "EE": true, "FI": true, "FR": true,
|
||||
"DE": true, "GR": true, "HU": true, "IE": true, "IT": true,
|
||||
"LV": true, "LT": true, "LU": true, "MT": true, "NL": true,
|
||||
"PL": true, "PT": true, "RO": true, "SK": true, "SI": true,
|
||||
"ES": true, "SE": true,
|
||||
}
|
||||
|
||||
var iso3166_1_alpha3 = map[string]bool{
|
||||
// see: https://www.iso.org/iso-3166-country-codes.html
|
||||
"AFG": true, "ALB": true, "DZA": true, "ASM": true, "AND": true,
|
||||
@@ -116,15 +107,6 @@ var iso3166_1_alpha3 = map[string]bool{
|
||||
"VNM": true, "VGB": true, "VIR": true, "WLF": true, "ESH": true,
|
||||
"YEM": true, "ZMB": true, "ZWE": true, "ALA": true, "UNK": true,
|
||||
}
|
||||
|
||||
var iso3166_1_alpha3_eu = map[string]bool{
|
||||
"AUT": true, "BEL": true, "BGR": true, "HRV": true, "CYP": true,
|
||||
"CZE": true, "DNK": true, "EST": true, "FIN": true, "FRA": true,
|
||||
"DEU": true, "GRC": true, "HUN": true, "IRL": true, "ITA": true,
|
||||
"LVA": true, "LTU": true, "LUX": true, "MLT": true, "NLD": true,
|
||||
"POL": true, "PRT": true, "ROU": true, "SVK": true, "SVN": true,
|
||||
"ESP": true, "SWE": true,
|
||||
}
|
||||
var iso3166_1_alpha_numeric = map[int]bool{
|
||||
// see: https://www.iso.org/iso-3166-country-codes.html
|
||||
4: true, 8: true, 12: true, 16: true, 20: true,
|
||||
@@ -179,15 +161,6 @@ var iso3166_1_alpha_numeric = map[int]bool{
|
||||
887: true, 894: true, 716: true, 248: true, 153: true,
|
||||
}
|
||||
|
||||
var iso3166_1_alpha_numeric_eu = map[int]bool{
|
||||
40: true, 56: true, 100: true, 191: true, 196: true,
|
||||
200: true, 208: true, 233: true, 246: true, 250: true,
|
||||
276: true, 300: true, 348: true, 372: true, 380: true,
|
||||
428: true, 440: true, 442: true, 470: true, 528: true,
|
||||
616: true, 620: true, 642: true, 703: true, 705: true,
|
||||
724: true, 752: true,
|
||||
}
|
||||
|
||||
var iso3166_2 = map[string]bool{
|
||||
"AD-02": true, "AD-03": true, "AD-04": true, "AD-05": true, "AD-06": true,
|
||||
"AD-07": true, "AD-08": true, "AE-AJ": true, "AE-AZ": true, "AE-DU": true,
|
||||
|
||||
42
vendor/github.com/go-playground/validator/v10/doc.go
generated
vendored
42
vendor/github.com/go-playground/validator/v10/doc.go
generated
vendored
@@ -194,13 +194,6 @@ such as min or max won't run, but if a value is set validation will run.
|
||||
|
||||
Usage: omitempty
|
||||
|
||||
# Omit Nil
|
||||
|
||||
Allows to skip the validation if the value is nil (same as omitempty, but
|
||||
only for the nil-values).
|
||||
|
||||
Usage: omitnil
|
||||
|
||||
# Dive
|
||||
|
||||
This tells the validator to dive into a slice, array or map and validate that
|
||||
@@ -254,7 +247,7 @@ Example #2
|
||||
This validates that the value is not the data types default zero value.
|
||||
For numbers ensures value is not zero. For strings ensures value is
|
||||
not "". For slices, maps, pointers, interfaces, channels and functions
|
||||
ensures the value is not nil. For structs ensures value is not the zero value when using WithRequiredStructEnabled.
|
||||
ensures the value is not nil.
|
||||
|
||||
Usage: required
|
||||
|
||||
@@ -263,7 +256,7 @@ ensures the value is not nil. For structs ensures value is not the zero value wh
|
||||
The field under validation must be present and not empty only if all
|
||||
the other specified fields are equal to the value following the specified
|
||||
field. For strings ensures value is not "". For slices, maps, pointers,
|
||||
interfaces, channels and functions ensures the value is not nil. For structs ensures value is not the zero value.
|
||||
interfaces, channels and functions ensures the value is not nil.
|
||||
|
||||
Usage: required_if
|
||||
|
||||
@@ -280,7 +273,7 @@ Examples:
|
||||
The field under validation must be present and not empty unless all
|
||||
the other specified fields are equal to the value following the specified
|
||||
field. For strings ensures value is not "". For slices, maps, pointers,
|
||||
interfaces, channels and functions ensures the value is not nil. For structs ensures value is not the zero value.
|
||||
interfaces, channels and functions ensures the value is not nil.
|
||||
|
||||
Usage: required_unless
|
||||
|
||||
@@ -297,7 +290,7 @@ Examples:
|
||||
The field under validation must be present and not empty only if any
|
||||
of the other specified fields are present. For strings ensures value is
|
||||
not "". For slices, maps, pointers, interfaces, channels and functions
|
||||
ensures the value is not nil. For structs ensures value is not the zero value.
|
||||
ensures the value is not nil.
|
||||
|
||||
Usage: required_with
|
||||
|
||||
@@ -314,7 +307,7 @@ Examples:
|
||||
The field under validation must be present and not empty only if all
|
||||
of the other specified fields are present. For strings ensures value is
|
||||
not "". For slices, maps, pointers, interfaces, channels and functions
|
||||
ensures the value is not nil. For structs ensures value is not the zero value.
|
||||
ensures the value is not nil.
|
||||
|
||||
Usage: required_with_all
|
||||
|
||||
@@ -328,7 +321,7 @@ Example:
|
||||
The field under validation must be present and not empty only when any
|
||||
of the other specified fields are not present. For strings ensures value is
|
||||
not "". For slices, maps, pointers, interfaces, channels and functions
|
||||
ensures the value is not nil. For structs ensures value is not the zero value.
|
||||
ensures the value is not nil.
|
||||
|
||||
Usage: required_without
|
||||
|
||||
@@ -345,7 +338,7 @@ Examples:
|
||||
The field under validation must be present and not empty only when all
|
||||
of the other specified fields are not present. For strings ensures value is
|
||||
not "". For slices, maps, pointers, interfaces, channels and functions
|
||||
ensures the value is not nil. For structs ensures value is not the zero value.
|
||||
ensures the value is not nil.
|
||||
|
||||
Usage: required_without_all
|
||||
|
||||
@@ -359,7 +352,7 @@ Example:
|
||||
The field under validation must not be present or not empty only if all
|
||||
the other specified fields are equal to the value following the specified
|
||||
field. For strings ensures value is not "". For slices, maps, pointers,
|
||||
interfaces, channels and functions ensures the value is not nil. For structs ensures value is not the zero value.
|
||||
interfaces, channels and functions ensures the value is not nil.
|
||||
|
||||
Usage: excluded_if
|
||||
|
||||
@@ -376,7 +369,7 @@ Examples:
|
||||
The field under validation must not be present or empty unless all
|
||||
the other specified fields are equal to the value following the specified
|
||||
field. For strings ensures value is not "". For slices, maps, pointers,
|
||||
interfaces, channels and functions ensures the value is not nil. For structs ensures value is not the zero value.
|
||||
interfaces, channels and functions ensures the value is not nil.
|
||||
|
||||
Usage: excluded_unless
|
||||
|
||||
@@ -886,6 +879,8 @@ This is done using os.Stat and github.com/gabriel-vasile/mimetype
|
||||
|
||||
Usage: image
|
||||
|
||||
# URL String
|
||||
|
||||
# File Path
|
||||
|
||||
This validates that a string value contains a valid file path but does not
|
||||
@@ -916,15 +911,6 @@ according to the RFC 2141 spec.
|
||||
|
||||
Usage: urn_rfc2141
|
||||
|
||||
# Base32 String
|
||||
|
||||
This validates that a string value contains a valid bas324 value.
|
||||
Although an empty string is valid base32 this will report an empty string
|
||||
as an error, if you wish to accept an empty string as valid you can use
|
||||
this with the omitempty tag.
|
||||
|
||||
Usage: base32
|
||||
|
||||
# Base64 String
|
||||
|
||||
This validates that a string value contains a valid base64 value.
|
||||
@@ -1398,12 +1384,6 @@ This validates that a string value contains a valid cron expression.
|
||||
|
||||
Usage: cron
|
||||
|
||||
# SpiceDb ObjectID/Permission/Object Type
|
||||
|
||||
This validates that a string is valid for use with SpiceDb for the indicated purpose. If no purpose is given, a purpose of 'id' is assumed.
|
||||
|
||||
Usage: spicedb=id|permission|type
|
||||
|
||||
# Alias Validators and Tags
|
||||
|
||||
Alias Validators and Tags
|
||||
|
||||
8
vendor/github.com/go-playground/validator/v10/errors.go
generated
vendored
8
vendor/github.com/go-playground/validator/v10/errors.go
generated
vendored
@@ -257,19 +257,15 @@ func (fe *fieldError) Error() string {
|
||||
// NOTE: if no registered translation can be found, it returns the original
|
||||
// untranslated error message.
|
||||
func (fe *fieldError) Translate(ut ut.Translator) string {
|
||||
var fn TranslationFunc
|
||||
|
||||
m, ok := fe.v.transTagFunc[ut]
|
||||
if !ok {
|
||||
return fe.Error()
|
||||
}
|
||||
|
||||
fn, ok = m[fe.tag]
|
||||
fn, ok := m[fe.tag]
|
||||
if !ok {
|
||||
fn, ok = m[fe.actualTag]
|
||||
if !ok {
|
||||
return fe.Error()
|
||||
}
|
||||
return fe.Error()
|
||||
}
|
||||
|
||||
return fn(ut, fe)
|
||||
|
||||
12
vendor/github.com/go-playground/validator/v10/regexes.go
generated
vendored
12
vendor/github.com/go-playground/validator/v10/regexes.go
generated
vendored
@@ -17,13 +17,11 @@ const (
|
||||
hslaRegexString = "^hsla\\(\\s*(?:0|[1-9]\\d?|[12]\\d\\d|3[0-5]\\d|360)\\s*,\\s*(?:(?:0|[1-9]\\d?|100)%)\\s*,\\s*(?:(?:0|[1-9]\\d?|100)%)\\s*,\\s*(?:(?:0.[1-9]*)|[01])\\s*\\)$"
|
||||
emailRegexString = "^(?:(?:(?:(?:[a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+(?:\\.([a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+)*)|(?:(?:\\x22)(?:(?:(?:(?:\\x20|\\x09)*(?:\\x0d\\x0a))?(?:\\x20|\\x09)+)?(?:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]|\\x21|[\\x23-\\x5b]|[\\x5d-\\x7e]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(?:(?:[\\x01-\\x09\\x0b\\x0c\\x0d-\\x7f]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}]))))*(?:(?:(?:\\x20|\\x09)*(?:\\x0d\\x0a))?(\\x20|\\x09)+)?(?:\\x22))))@(?:(?:(?:[a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(?:(?:[a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])(?:[a-zA-Z]|\\d|-|\\.|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*(?:[a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.)+(?:(?:[a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(?:(?:[a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])(?:[a-zA-Z]|\\d|-|\\.|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*(?:[a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.?$"
|
||||
e164RegexString = "^\\+[1-9]?[0-9]{7,14}$"
|
||||
base32RegexString = "^(?:[A-Z2-7]{8})*(?:[A-Z2-7]{2}={6}|[A-Z2-7]{4}={4}|[A-Z2-7]{5}={3}|[A-Z2-7]{7}=|[A-Z2-7]{8})$"
|
||||
base64RegexString = "^(?:[A-Za-z0-9+\\/]{4})*(?:[A-Za-z0-9+\\/]{2}==|[A-Za-z0-9+\\/]{3}=|[A-Za-z0-9+\\/]{4})$"
|
||||
base64URLRegexString = "^(?:[A-Za-z0-9-_]{4})*(?:[A-Za-z0-9-_]{2}==|[A-Za-z0-9-_]{3}=|[A-Za-z0-9-_]{4})$"
|
||||
base64RawURLRegexString = "^(?:[A-Za-z0-9-_]{4})*(?:[A-Za-z0-9-_]{2,4})$"
|
||||
iSBN10RegexString = "^(?:[0-9]{9}X|[0-9]{10})$"
|
||||
iSBN13RegexString = "^(?:(?:97(?:8|9))[0-9]{10})$"
|
||||
iSSNRegexString = "^(?:[0-9]{4}-[0-9]{3}[0-9X])$"
|
||||
uUID3RegexString = "^[0-9a-f]{8}-[0-9a-f]{4}-3[0-9a-f]{3}-[0-9a-f]{4}-[0-9a-f]{12}$"
|
||||
uUID4RegexString = "^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$"
|
||||
uUID5RegexString = "^[0-9a-f]{8}-[0-9a-f]{4}-5[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$"
|
||||
@@ -32,7 +30,7 @@ const (
|
||||
uUID4RFC4122RegexString = "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$"
|
||||
uUID5RFC4122RegexString = "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-5[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$"
|
||||
uUIDRFC4122RegexString = "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"
|
||||
uLIDRegexString = "^(?i)[A-HJKMNP-TV-Z0-9]{26}$"
|
||||
uLIDRegexString = "^[A-HJKMNP-TV-Z0-9]{26}$"
|
||||
md4RegexString = "^[0-9a-f]{32}$"
|
||||
md5RegexString = "^[0-9a-f]{32}$"
|
||||
sha256RegexString = "^[0-9a-f]{64}$"
|
||||
@@ -70,9 +68,6 @@ const (
|
||||
cveRegexString = `^CVE-(1999|2\d{3})-(0[^0]\d{2}|0\d[^0]\d{1}|0\d{2}[^0]|[1-9]{1}\d{3,})$` // CVE Format Id https://cve.mitre.org/cve/identifiers/syntaxchange.html
|
||||
mongodbRegexString = "^[a-f\\d]{24}$"
|
||||
cronRegexString = `(@(annually|yearly|monthly|weekly|daily|hourly|reboot))|(@every (\d+(ns|us|µs|ms|s|m|h))+)|((((\d+,)+\d+|(\d+(\/|-)\d+)|\d+|\*) ?){5,7})`
|
||||
spicedbIDRegexString = `^(([a-zA-Z0-9/_|\-=+]{1,})|\*)$`
|
||||
spicedbPermissionRegexString = "^([a-z][a-z0-9_]{1,62}[a-z0-9])?$"
|
||||
spicedbTypeRegexString = "^([a-z][a-z0-9_]{1,61}[a-z0-9]/)?[a-z][a-z0-9_]{1,62}[a-z0-9]$"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -90,13 +85,11 @@ var (
|
||||
hslaRegex = regexp.MustCompile(hslaRegexString)
|
||||
e164Regex = regexp.MustCompile(e164RegexString)
|
||||
emailRegex = regexp.MustCompile(emailRegexString)
|
||||
base32Regex = regexp.MustCompile(base32RegexString)
|
||||
base64Regex = regexp.MustCompile(base64RegexString)
|
||||
base64URLRegex = regexp.MustCompile(base64URLRegexString)
|
||||
base64RawURLRegex = regexp.MustCompile(base64RawURLRegexString)
|
||||
iSBN10Regex = regexp.MustCompile(iSBN10RegexString)
|
||||
iSBN13Regex = regexp.MustCompile(iSBN13RegexString)
|
||||
iSSNRegex = regexp.MustCompile(iSSNRegexString)
|
||||
uUID3Regex = regexp.MustCompile(uUID3RegexString)
|
||||
uUID4Regex = regexp.MustCompile(uUID4RegexString)
|
||||
uUID5Regex = regexp.MustCompile(uUID5RegexString)
|
||||
@@ -141,7 +134,4 @@ var (
|
||||
cveRegex = regexp.MustCompile(cveRegexString)
|
||||
mongodbRegex = regexp.MustCompile(mongodbRegexString)
|
||||
cronRegex = regexp.MustCompile(cronRegexString)
|
||||
spicedbIDRegex = regexp.MustCompile(spicedbIDRegexString)
|
||||
spicedbPermissionRegex = regexp.MustCompile(spicedbPermissionRegexString)
|
||||
spicedbTypeRegex = regexp.MustCompile(spicedbTypeRegexString)
|
||||
)
|
||||
|
||||
29
vendor/github.com/go-playground/validator/v10/util.go
generated
vendored
29
vendor/github.com/go-playground/validator/v10/util.go
generated
vendored
@@ -1,9 +1,7 @@
|
||||
package validator
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -263,19 +261,13 @@ func asUint(param string) uint64 {
|
||||
return i
|
||||
}
|
||||
|
||||
// asFloat64 returns the parameter as a float64
|
||||
// asFloat returns the parameter as a float64
|
||||
// or panics if it can't convert
|
||||
func asFloat64(param string) float64 {
|
||||
func asFloat(param string) float64 {
|
||||
|
||||
i, err := strconv.ParseFloat(param, 64)
|
||||
panicIf(err)
|
||||
return i
|
||||
}
|
||||
|
||||
// asFloat64 returns the parameter as a float64
|
||||
// or panics if it can't convert
|
||||
func asFloat32(param string) float64 {
|
||||
i, err := strconv.ParseFloat(param, 32)
|
||||
panicIf(err)
|
||||
return i
|
||||
}
|
||||
|
||||
@@ -294,18 +286,3 @@ func panicIf(err error) {
|
||||
panic(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// Checks if field value matches regex. If fl.Field can be cast to Stringer, it uses the Stringer interfaces
|
||||
// String() return value. Otherwise, it uses fl.Field's String() value.
|
||||
func fieldMatchesRegexByStringerValOrString(regex *regexp.Regexp, fl FieldLevel) bool {
|
||||
switch fl.Field().Kind() {
|
||||
case reflect.String:
|
||||
return regex.MatchString(fl.Field().String())
|
||||
default:
|
||||
if stringer, ok := fl.Field().Interface().(fmt.Stringer); ok {
|
||||
return regex.MatchString(stringer.String())
|
||||
} else {
|
||||
return regex.MatchString(fl.Field().String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
169
vendor/github.com/go-playground/validator/v10/validator.go
generated
vendored
169
vendor/github.com/go-playground/validator/v10/validator.go
generated
vendored
@@ -5,7 +5,6 @@ import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// per validate construct
|
||||
@@ -100,8 +99,6 @@ func (v *validate) traverseField(ctx context.Context, parent reflect.Value, curr
|
||||
|
||||
current, kind, v.fldIsPointer = v.extractTypeInternal(current, false)
|
||||
|
||||
var isNestedStruct bool
|
||||
|
||||
switch kind {
|
||||
case reflect.Ptr, reflect.Interface, reflect.Invalid:
|
||||
|
||||
@@ -113,10 +110,6 @@ func (v *validate) traverseField(ctx context.Context, parent reflect.Value, curr
|
||||
return
|
||||
}
|
||||
|
||||
if ct.typeof == typeOmitNil && (kind != reflect.Invalid && current.IsNil()) {
|
||||
return
|
||||
}
|
||||
|
||||
if ct.hasTag {
|
||||
if kind == reflect.Invalid {
|
||||
v.str1 = string(append(ns, cf.altName...))
|
||||
@@ -157,7 +150,7 @@ func (v *validate) traverseField(ctx context.Context, parent reflect.Value, curr
|
||||
structNs: v.str2,
|
||||
fieldLen: uint8(len(cf.altName)),
|
||||
structfieldLen: uint8(len(cf.name)),
|
||||
value: getValue(current),
|
||||
value: current.Interface(),
|
||||
param: ct.param,
|
||||
kind: kind,
|
||||
typ: current.Type(),
|
||||
@@ -167,61 +160,86 @@ func (v *validate) traverseField(ctx context.Context, parent reflect.Value, curr
|
||||
}
|
||||
}
|
||||
|
||||
if kind == reflect.Invalid {
|
||||
case reflect.Struct:
|
||||
|
||||
typ = current.Type()
|
||||
|
||||
if !typ.ConvertibleTo(timeType) {
|
||||
|
||||
if ct != nil {
|
||||
|
||||
if ct.typeof == typeStructOnly {
|
||||
goto CONTINUE
|
||||
} else if ct.typeof == typeIsDefault {
|
||||
// set Field Level fields
|
||||
v.slflParent = parent
|
||||
v.flField = current
|
||||
v.cf = cf
|
||||
v.ct = ct
|
||||
|
||||
if !ct.fn(ctx, v) {
|
||||
v.str1 = string(append(ns, cf.altName...))
|
||||
|
||||
if v.v.hasTagNameFunc {
|
||||
v.str2 = string(append(structNs, cf.name...))
|
||||
} else {
|
||||
v.str2 = v.str1
|
||||
}
|
||||
|
||||
v.errs = append(v.errs,
|
||||
&fieldError{
|
||||
v: v.v,
|
||||
tag: ct.aliasTag,
|
||||
actualTag: ct.tag,
|
||||
ns: v.str1,
|
||||
structNs: v.str2,
|
||||
fieldLen: uint8(len(cf.altName)),
|
||||
structfieldLen: uint8(len(cf.name)),
|
||||
value: current.Interface(),
|
||||
param: ct.param,
|
||||
kind: kind,
|
||||
typ: typ,
|
||||
},
|
||||
)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
ct = ct.next
|
||||
}
|
||||
|
||||
if ct != nil && ct.typeof == typeNoStructLevel {
|
||||
return
|
||||
}
|
||||
|
||||
CONTINUE:
|
||||
// if len == 0 then validating using 'Var' or 'VarWithValue'
|
||||
// Var - doesn't make much sense to do it that way, should call 'Struct', but no harm...
|
||||
// VarWithField - this allows for validating against each field within the struct against a specific value
|
||||
// pretty handy in certain situations
|
||||
if len(cf.name) > 0 {
|
||||
ns = append(append(ns, cf.altName...), '.')
|
||||
structNs = append(append(structNs, cf.name...), '.')
|
||||
}
|
||||
|
||||
v.validateStruct(ctx, parent, current, typ, ns, structNs, ct)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
case reflect.Struct:
|
||||
isNestedStruct = !current.Type().ConvertibleTo(timeType)
|
||||
// For backward compatibility before struct level validation tags were supported
|
||||
// as there were a number of projects relying on `required` not failing on non-pointer
|
||||
// structs. Since it's basically nonsensical to use `required` with a non-pointer struct
|
||||
// are explicitly skipping the required validation for it. This WILL be removed in the
|
||||
// next major version.
|
||||
if isNestedStruct && !v.v.requiredStructEnabled && ct != nil && ct.tag == requiredTag {
|
||||
ct = ct.next
|
||||
}
|
||||
if ct == nil || !ct.hasTag {
|
||||
return
|
||||
}
|
||||
|
||||
typ = current.Type()
|
||||
|
||||
OUTER:
|
||||
for {
|
||||
if ct == nil || !ct.hasTag || (isNestedStruct && len(cf.name) == 0) {
|
||||
// isNestedStruct check here
|
||||
if isNestedStruct {
|
||||
// if len == 0 then validating using 'Var' or 'VarWithValue'
|
||||
// Var - doesn't make much sense to do it that way, should call 'Struct', but no harm...
|
||||
// VarWithField - this allows for validating against each field within the struct against a specific value
|
||||
// pretty handy in certain situations
|
||||
if len(cf.name) > 0 {
|
||||
ns = append(append(ns, cf.altName...), '.')
|
||||
structNs = append(append(structNs, cf.name...), '.')
|
||||
}
|
||||
|
||||
v.validateStruct(ctx, parent, current, typ, ns, structNs, ct)
|
||||
}
|
||||
if ct == nil {
|
||||
return
|
||||
}
|
||||
|
||||
switch ct.typeof {
|
||||
case typeNoStructLevel:
|
||||
return
|
||||
|
||||
case typeStructOnly:
|
||||
if isNestedStruct {
|
||||
// if len == 0 then validating using 'Var' or 'VarWithValue'
|
||||
// Var - doesn't make much sense to do it that way, should call 'Struct', but no harm...
|
||||
// VarWithField - this allows for validating against each field within the struct against a specific value
|
||||
// pretty handy in certain situations
|
||||
if len(cf.name) > 0 {
|
||||
ns = append(append(ns, cf.altName...), '.')
|
||||
structNs = append(append(structNs, cf.name...), '.')
|
||||
}
|
||||
|
||||
v.validateStruct(ctx, parent, current, typ, ns, structNs, ct)
|
||||
}
|
||||
return
|
||||
|
||||
case typeOmitEmpty:
|
||||
|
||||
@@ -238,26 +256,6 @@ OUTER:
|
||||
ct = ct.next
|
||||
continue
|
||||
|
||||
case typeOmitNil:
|
||||
v.slflParent = parent
|
||||
v.flField = current
|
||||
v.cf = cf
|
||||
v.ct = ct
|
||||
|
||||
switch field := v.Field(); field.Kind() {
|
||||
case reflect.Slice, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Chan, reflect.Func:
|
||||
if field.IsNil() {
|
||||
return
|
||||
}
|
||||
default:
|
||||
if v.fldIsPointer && field.Interface() == nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
ct = ct.next
|
||||
continue
|
||||
|
||||
case typeEndKeys:
|
||||
return
|
||||
|
||||
@@ -368,7 +366,7 @@ OUTER:
|
||||
ct = ct.next
|
||||
|
||||
if ct == nil {
|
||||
continue OUTER
|
||||
return
|
||||
}
|
||||
|
||||
if ct.typeof != typeOr {
|
||||
@@ -411,7 +409,7 @@ OUTER:
|
||||
structNs: v.str2,
|
||||
fieldLen: uint8(len(cf.altName)),
|
||||
structfieldLen: uint8(len(cf.name)),
|
||||
value: getValue(current),
|
||||
value: current.Interface(),
|
||||
param: ct.param,
|
||||
kind: kind,
|
||||
typ: typ,
|
||||
@@ -431,7 +429,7 @@ OUTER:
|
||||
structNs: v.str2,
|
||||
fieldLen: uint8(len(cf.altName)),
|
||||
structfieldLen: uint8(len(cf.name)),
|
||||
value: getValue(current),
|
||||
value: current.Interface(),
|
||||
param: ct.param,
|
||||
kind: kind,
|
||||
typ: typ,
|
||||
@@ -471,7 +469,7 @@ OUTER:
|
||||
structNs: v.str2,
|
||||
fieldLen: uint8(len(cf.altName)),
|
||||
structfieldLen: uint8(len(cf.name)),
|
||||
value: getValue(current),
|
||||
value: current.Interface(),
|
||||
param: ct.param,
|
||||
kind: kind,
|
||||
typ: typ,
|
||||
@@ -485,26 +483,3 @@ OUTER:
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func getValue(val reflect.Value) interface{} {
|
||||
if val.CanInterface() {
|
||||
return val.Interface()
|
||||
}
|
||||
|
||||
if val.CanAddr() {
|
||||
return reflect.NewAt(val.Type(), unsafe.Pointer(val.UnsafeAddr())).Elem().Interface()
|
||||
}
|
||||
|
||||
switch val.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return val.Int()
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
return val.Uint()
|
||||
case reflect.Complex64, reflect.Complex128:
|
||||
return val.Complex()
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return val.Float()
|
||||
default:
|
||||
return val.String()
|
||||
}
|
||||
}
|
||||
|
||||
36
vendor/github.com/go-playground/validator/v10/validator_instance.go
generated
vendored
36
vendor/github.com/go-playground/validator/v10/validator_instance.go
generated
vendored
@@ -22,7 +22,6 @@ const (
|
||||
structOnlyTag = "structonly"
|
||||
noStructLevelTag = "nostructlevel"
|
||||
omitempty = "omitempty"
|
||||
omitnil = "omitnil"
|
||||
isdefault = "isdefault"
|
||||
requiredWithoutAllTag = "required_without_all"
|
||||
requiredWithoutTag = "required_without"
|
||||
@@ -54,8 +53,6 @@ var (
|
||||
timeDurationType = reflect.TypeOf(time.Duration(0))
|
||||
timeType = reflect.TypeOf(time.Time{})
|
||||
|
||||
byteSliceType = reflect.TypeOf([]byte{})
|
||||
|
||||
defaultCField = &cField{namesEqual: true}
|
||||
)
|
||||
|
||||
@@ -80,21 +77,19 @@ type internalValidationFuncWrapper struct {
|
||||
|
||||
// Validate contains the validator settings and cache
|
||||
type Validate struct {
|
||||
tagName string
|
||||
pool *sync.Pool
|
||||
tagNameFunc TagNameFunc
|
||||
structLevelFuncs map[reflect.Type]StructLevelFuncCtx
|
||||
customFuncs map[reflect.Type]CustomTypeFunc
|
||||
aliases map[string]string
|
||||
validations map[string]internalValidationFuncWrapper
|
||||
transTagFunc map[ut.Translator]map[string]TranslationFunc // map[<locale>]map[<tag>]TranslationFunc
|
||||
rules map[reflect.Type]map[string]string
|
||||
tagCache *tagCache
|
||||
structCache *structCache
|
||||
hasCustomFuncs bool
|
||||
hasTagNameFunc bool
|
||||
requiredStructEnabled bool
|
||||
privateFieldValidation bool
|
||||
tagName string
|
||||
pool *sync.Pool
|
||||
hasCustomFuncs bool
|
||||
hasTagNameFunc bool
|
||||
tagNameFunc TagNameFunc
|
||||
structLevelFuncs map[reflect.Type]StructLevelFuncCtx
|
||||
customFuncs map[reflect.Type]CustomTypeFunc
|
||||
aliases map[string]string
|
||||
validations map[string]internalValidationFuncWrapper
|
||||
transTagFunc map[ut.Translator]map[string]TranslationFunc // map[<locale>]map[<tag>]TranslationFunc
|
||||
rules map[reflect.Type]map[string]string
|
||||
tagCache *tagCache
|
||||
structCache *structCache
|
||||
}
|
||||
|
||||
// New returns a new instance of 'validate' with sane defaults.
|
||||
@@ -102,7 +97,7 @@ type Validate struct {
|
||||
// It caches information about your struct and validations,
|
||||
// in essence only parsing your validation tags once per struct type.
|
||||
// Using multiple instances neglects the benefit of caching.
|
||||
func New(options ...Option) *Validate {
|
||||
func New() *Validate {
|
||||
|
||||
tc := new(tagCache)
|
||||
tc.m.Store(make(map[string]*cTag))
|
||||
@@ -149,9 +144,6 @@ func New(options ...Option) *Validate {
|
||||
},
|
||||
}
|
||||
|
||||
for _, o := range options {
|
||||
o(v)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
|
||||
5
vendor/github.com/jackc/pgservicefile/README.md
generated
vendored
5
vendor/github.com/jackc/pgservicefile/README.md
generated
vendored
@@ -1,6 +1,5 @@
|
||||
[](https://pkg.go.dev/github.com/jackc/pgservicefile)
|
||||
[](https://github.com/jackc/pgservicefile/actions/workflows/ci.yml)
|
||||
|
||||
[](https://godoc.org/github.com/jackc/pgservicefile)
|
||||
[](https://travis-ci.org/jackc/pgservicefile)
|
||||
|
||||
# pgservicefile
|
||||
|
||||
|
||||
4
vendor/github.com/jackc/pgservicefile/pgservicefile.go
generated
vendored
4
vendor/github.com/jackc/pgservicefile/pgservicefile.go
generated
vendored
@@ -57,7 +57,7 @@ func ParseServicefile(r io.Reader) (*Servicefile, error) {
|
||||
} else if strings.HasPrefix(line, "[") && strings.HasSuffix(line, "]") {
|
||||
service = &Service{Name: line[1 : len(line)-1], Settings: make(map[string]string)}
|
||||
servicefile.Services = append(servicefile.Services, service)
|
||||
} else if service != nil {
|
||||
} else {
|
||||
parts := strings.SplitN(line, "=", 2)
|
||||
if len(parts) != 2 {
|
||||
return nil, fmt.Errorf("unable to parse line %d", lineNum)
|
||||
@@ -67,8 +67,6 @@ func ParseServicefile(r io.Reader) (*Servicefile, error) {
|
||||
value := strings.TrimSpace(parts[1])
|
||||
|
||||
service.Settings[key] = value
|
||||
} else {
|
||||
return nil, fmt.Errorf("line %d is not in a section", lineNum)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
111
vendor/github.com/jackc/pgx/v5/CHANGELOG.md
generated
vendored
111
vendor/github.com/jackc/pgx/v5/CHANGELOG.md
generated
vendored
@@ -1,114 +1,3 @@
|
||||
# 5.7.1 (September 10, 2024)
|
||||
|
||||
* Fix data race in tracelog.TraceLog
|
||||
* Update puddle to v2.2.2. This removes the import of nanotime via linkname.
|
||||
* Update golang.org/x/crypto and golang.org/x/text
|
||||
|
||||
# 5.7.0 (September 7, 2024)
|
||||
|
||||
* Add support for sslrootcert=system (Yann Soubeyrand)
|
||||
* Add LoadTypes to load multiple types in a single SQL query (Nick Farrell)
|
||||
* Add XMLCodec supports encoding + scanning XML column type like json (nickcruess-soda)
|
||||
* Add MultiTrace (Stepan Rabotkin)
|
||||
* Add TraceLogConfig with customizable TimeKey (stringintech)
|
||||
* pgx.ErrNoRows wraps sql.ErrNoRows to aid in database/sql compatibility with native pgx functions (merlin)
|
||||
* Support scanning binary formatted uint32 into string / TextScanner (jennifersp)
|
||||
* Fix interval encoding to allow 0s and avoid extra spaces (Carlos Pérez-Aradros Herce)
|
||||
* Update pgservicefile - fixes panic when parsing invalid file
|
||||
* Better error message when reading past end of batch
|
||||
* Don't print url when url.Parse returns an error (Kevin Biju)
|
||||
* Fix snake case name normalization collision in RowToStructByName with db tag (nolandseigler)
|
||||
* Fix: Scan and encode types with underlying types of arrays
|
||||
|
||||
# 5.6.0 (May 25, 2024)
|
||||
|
||||
* Add StrictNamedArgs (Tomas Zahradnicek)
|
||||
* Add support for macaddr8 type (Carlos Pérez-Aradros Herce)
|
||||
* Add SeverityUnlocalized field to PgError / Notice
|
||||
* Performance optimization of RowToStructByPos/Name (Zach Olstein)
|
||||
* Allow customizing context canceled behavior for pgconn
|
||||
* Add ScanLocation to pgtype.Timestamp[tz]Codec
|
||||
* Add custom data to pgconn.PgConn
|
||||
* Fix ResultReader.Read() to handle nil values
|
||||
* Do not encode interval microseconds when they are 0 (Carlos Pérez-Aradros Herce)
|
||||
* pgconn.SafeToRetry checks for wrapped errors (tjasko)
|
||||
* Failed connection attempts include all errors
|
||||
* Optimize LargeObject.Read (Mitar)
|
||||
* Add tracing for connection acquire and release from pool (ngavinsir)
|
||||
* Fix encode driver.Valuer not called when nil
|
||||
* Add support for custom JSON marshal and unmarshal (Mitar)
|
||||
* Use Go default keepalive for TCP connections (Hans-Joachim Kliemeck)
|
||||
|
||||
# 5.5.5 (March 9, 2024)
|
||||
|
||||
Use spaces instead of parentheses for SQL sanitization.
|
||||
|
||||
This still solves the problem of negative numbers creating a line comment, but this avoids breaking edge cases such as
|
||||
`set foo to $1` where the substitution is taking place in a location where an arbitrary expression is not allowed.
|
||||
|
||||
# 5.5.4 (March 4, 2024)
|
||||
|
||||
Fix CVE-2024-27304
|
||||
|
||||
SQL injection can occur if an attacker can cause a single query or bind message to exceed 4 GB in size. An integer
|
||||
overflow in the calculated message size can cause the one large message to be sent as multiple messages under the
|
||||
attacker's control.
|
||||
|
||||
Thanks to Paul Gerste for reporting this issue.
|
||||
|
||||
* Fix behavior of CollectRows to return empty slice if Rows are empty (Felix)
|
||||
* Fix simple protocol encoding of json.RawMessage
|
||||
* Fix *Pipeline.getResults should close pipeline on error
|
||||
* Fix panic in TryFindUnderlyingTypeScanPlan (David Kurman)
|
||||
* Fix deallocation of invalidated cached statements in a transaction
|
||||
* Handle invalid sslkey file
|
||||
* Fix scan float4 into sql.Scanner
|
||||
* Fix pgtype.Bits not making copy of data from read buffer. This would cause the data to be corrupted by future reads.
|
||||
|
||||
# 5.5.3 (February 3, 2024)
|
||||
|
||||
* Fix: prepared statement already exists
|
||||
* Improve CopyFrom auto-conversion of text-ish values
|
||||
* Add ltree type support (Florent Viel)
|
||||
* Make some properties of Batch and QueuedQuery public (Pavlo Golub)
|
||||
* Add AppendRows function (Edoardo Spadolini)
|
||||
* Optimize convert UUID [16]byte to string (Kirill Malikov)
|
||||
* Fix: LargeObject Read and Write of more than ~1GB at a time (Mitar)
|
||||
|
||||
# 5.5.2 (January 13, 2024)
|
||||
|
||||
* Allow NamedArgs to start with underscore
|
||||
* pgproto3: Maximum message body length support (jeremy.spriet)
|
||||
* Upgrade golang.org/x/crypto to v0.17.0
|
||||
* Add snake_case support to RowToStructByName (Tikhon Fedulov)
|
||||
* Fix: update description cache after exec prepare (James Hartig)
|
||||
* Fix: pipeline checks if it is closed (James Hartig and Ryan Fowler)
|
||||
* Fix: normalize timeout / context errors during TLS startup (Samuel Stauffer)
|
||||
* Add OnPgError for easier centralized error handling (James Hartig)
|
||||
|
||||
# 5.5.1 (December 9, 2023)
|
||||
|
||||
* Add CopyFromFunc helper function. (robford)
|
||||
* Add PgConn.Deallocate method that uses PostgreSQL protocol Close message.
|
||||
* pgx uses new PgConn.Deallocate method. This allows deallocating statements to work in a failed transaction. This fixes a case where the prepared statement map could become invalid.
|
||||
* Fix: Prefer driver.Valuer over json.Marshaler for json fields. (Jacopo)
|
||||
* Fix: simple protocol SQL sanitizer previously panicked if an invalid $0 placeholder was used. This now returns an error instead. (maksymnevajdev)
|
||||
* Add pgtype.Numeric.ScanScientific (Eshton Robateau)
|
||||
|
||||
# 5.5.0 (November 4, 2023)
|
||||
|
||||
* Add CollectExactlyOneRow. (Julien GOTTELAND)
|
||||
* Add OpenDBFromPool to create *database/sql.DB from *pgxpool.Pool. (Lev Zakharov)
|
||||
* Prepare can automatically choose statement name based on sql. This makes it easier to explicitly manage prepared statements.
|
||||
* Statement cache now uses deterministic, stable statement names.
|
||||
* database/sql prepared statement names are deterministically generated.
|
||||
* Fix: SendBatch wasn't respecting context cancellation.
|
||||
* Fix: Timeout error from pipeline is now normalized.
|
||||
* Fix: database/sql encoding json.RawMessage to []byte.
|
||||
* CancelRequest: Wait for the cancel request to be acknowledged by the server. This should improve PgBouncer compatibility. (Anton Levakin)
|
||||
* stdlib: Use Ping instead of CheckConn in ResetSession
|
||||
* Add json.Marshaler and json.Unmarshaler for Float4, Float8 (Kirill Mironov)
|
||||
|
||||
# 5.4.3 (August 5, 2023)
|
||||
|
||||
* Fix: QCharArrayOID was defined with the wrong OID (Christoph Engelbert)
|
||||
|
||||
19
vendor/github.com/jackc/pgx/v5/CONTRIBUTING.md
generated
vendored
19
vendor/github.com/jackc/pgx/v5/CONTRIBUTING.md
generated
vendored
@@ -29,7 +29,6 @@ Create and setup a test database:
|
||||
export PGDATABASE=pgx_test
|
||||
createdb
|
||||
psql -c 'create extension hstore;'
|
||||
psql -c 'create extension ltree;'
|
||||
psql -c 'create domain uint64 as numeric(20,0);'
|
||||
```
|
||||
|
||||
@@ -80,11 +79,20 @@ echo "listen_addresses = '127.0.0.1'" >> .testdb/$POSTGRESQL_DATA_DIR/postgresql
|
||||
echo "port = $PGPORT" >> .testdb/$POSTGRESQL_DATA_DIR/postgresql.conf
|
||||
cat testsetup/postgresql_ssl.conf >> .testdb/$POSTGRESQL_DATA_DIR/postgresql.conf
|
||||
cp testsetup/pg_hba.conf .testdb/$POSTGRESQL_DATA_DIR/pg_hba.conf
|
||||
cp testsetup/ca.cnf .testdb
|
||||
cp testsetup/localhost.cnf .testdb
|
||||
cp testsetup/pgx_sslcert.cnf .testdb
|
||||
|
||||
cd .testdb
|
||||
|
||||
# Generate CA, server, and encrypted client certificates.
|
||||
go run ../testsetup/generate_certs.go
|
||||
# Generate a CA public / private key pair.
|
||||
openssl genrsa -out ca.key 4096
|
||||
openssl req -x509 -config ca.cnf -new -nodes -key ca.key -sha256 -days 365 -subj '/O=pgx-test-root' -out ca.pem
|
||||
|
||||
# Generate the certificate for localhost (the server).
|
||||
openssl genrsa -out localhost.key 2048
|
||||
openssl req -new -config localhost.cnf -key localhost.key -out localhost.csr
|
||||
openssl x509 -req -in localhost.csr -CA ca.pem -CAkey ca.key -CAcreateserial -out localhost.crt -days 364 -sha256 -extfile localhost.cnf -extensions v3_req
|
||||
|
||||
# Copy certificates to server directory and set permissions.
|
||||
cp ca.pem $POSTGRESQL_DATA_DIR/root.crt
|
||||
@@ -92,6 +100,11 @@ cp localhost.key $POSTGRESQL_DATA_DIR/server.key
|
||||
chmod 600 $POSTGRESQL_DATA_DIR/server.key
|
||||
cp localhost.crt $POSTGRESQL_DATA_DIR/server.crt
|
||||
|
||||
# Generate the certificate for client authentication.
|
||||
openssl genrsa -des3 -out pgx_sslcert.key -passout pass:certpw 2048
|
||||
openssl req -new -config pgx_sslcert.cnf -key pgx_sslcert.key -passin pass:certpw -out pgx_sslcert.csr
|
||||
openssl x509 -req -in pgx_sslcert.csr -CA ca.pem -CAkey ca.key -CAcreateserial -out pgx_sslcert.crt -days 363 -sha256 -extfile pgx_sslcert.cnf -extensions v3_req
|
||||
|
||||
cd ..
|
||||
```
|
||||
|
||||
|
||||
7
vendor/github.com/jackc/pgx/v5/README.md
generated
vendored
7
vendor/github.com/jackc/pgx/v5/README.md
generated
vendored
@@ -86,13 +86,9 @@ It is also possible to use the `database/sql` interface and convert a connection
|
||||
|
||||
See CONTRIBUTING.md for setup instructions.
|
||||
|
||||
## Architecture
|
||||
|
||||
See the presentation at Golang Estonia, [PGX Top to Bottom](https://www.youtube.com/watch?v=sXMSWhcHCf8) for a description of pgx architecture.
|
||||
|
||||
## Supported Go and PostgreSQL Versions
|
||||
|
||||
pgx supports the same versions of Go and PostgreSQL that are supported by their respective teams. For [Go](https://golang.org/doc/devel/release.html#policy) that is the two most recent major releases and for [PostgreSQL](https://www.postgresql.org/support/versioning/) the major releases in the last 5 years. This means pgx supports Go 1.21 and higher and PostgreSQL 12 and higher. pgx also is tested against the latest version of [CockroachDB](https://www.cockroachlabs.com/product/).
|
||||
pgx supports the same versions of Go and PostgreSQL that are supported by their respective teams. For [Go](https://golang.org/doc/devel/release.html#policy) that is the two most recent major releases and for [PostgreSQL](https://www.postgresql.org/support/versioning/) the major releases in the last 5 years. This means pgx supports Go 1.19 and higher and PostgreSQL 11 and higher. pgx also is tested against the latest version of [CockroachDB](https://www.cockroachlabs.com/product/).
|
||||
|
||||
## Version Policy
|
||||
|
||||
@@ -120,7 +116,6 @@ pgerrcode contains constants for the PostgreSQL error codes.
|
||||
|
||||
* [github.com/jackc/pgx-gofrs-uuid](https://github.com/jackc/pgx-gofrs-uuid)
|
||||
* [github.com/jackc/pgx-shopspring-decimal](https://github.com/jackc/pgx-shopspring-decimal)
|
||||
* [github.com/twpayne/pgx-geos](https://github.com/twpayne/pgx-geos) ([PostGIS](https://postgis.net/) and [GEOS](https://libgeos.org/) via [go-geos](https://github.com/twpayne/go-geos))
|
||||
* [github.com/vgarvardt/pgx-google-uuid](https://github.com/vgarvardt/pgx-google-uuid)
|
||||
|
||||
|
||||
|
||||
84
vendor/github.com/jackc/pgx/v5/batch.go
generated
vendored
84
vendor/github.com/jackc/pgx/v5/batch.go
generated
vendored
@@ -10,9 +10,9 @@ import (
|
||||
|
||||
// QueuedQuery is a query that has been queued for execution via a Batch.
|
||||
type QueuedQuery struct {
|
||||
SQL string
|
||||
Arguments []any
|
||||
Fn batchItemFunc
|
||||
query string
|
||||
arguments []any
|
||||
fn batchItemFunc
|
||||
sd *pgconn.StatementDescription
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ type batchItemFunc func(br BatchResults) error
|
||||
|
||||
// Query sets fn to be called when the response to qq is received.
|
||||
func (qq *QueuedQuery) Query(fn func(rows Rows) error) {
|
||||
qq.Fn = func(br BatchResults) error {
|
||||
qq.fn = func(br BatchResults) error {
|
||||
rows, _ := br.Query()
|
||||
defer rows.Close()
|
||||
|
||||
@@ -36,7 +36,7 @@ func (qq *QueuedQuery) Query(fn func(rows Rows) error) {
|
||||
|
||||
// Query sets fn to be called when the response to qq is received.
|
||||
func (qq *QueuedQuery) QueryRow(fn func(row Row) error) {
|
||||
qq.Fn = func(br BatchResults) error {
|
||||
qq.fn = func(br BatchResults) error {
|
||||
row := br.QueryRow()
|
||||
return fn(row)
|
||||
}
|
||||
@@ -44,7 +44,7 @@ func (qq *QueuedQuery) QueryRow(fn func(row Row) error) {
|
||||
|
||||
// Exec sets fn to be called when the response to qq is received.
|
||||
func (qq *QueuedQuery) Exec(fn func(ct pgconn.CommandTag) error) {
|
||||
qq.Fn = func(br BatchResults) error {
|
||||
qq.fn = func(br BatchResults) error {
|
||||
ct, err := br.Exec()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -57,28 +57,22 @@ func (qq *QueuedQuery) Exec(fn func(ct pgconn.CommandTag) error) {
|
||||
// Batch queries are a way of bundling multiple queries together to avoid
|
||||
// unnecessary network round trips. A Batch must only be sent once.
|
||||
type Batch struct {
|
||||
QueuedQueries []*QueuedQuery
|
||||
queuedQueries []*QueuedQuery
|
||||
}
|
||||
|
||||
// Queue queues a query to batch b. query can be an SQL query or the name of a prepared statement. The only pgx option
|
||||
// argument that is supported is QueryRewriter. Queries are executed using the connection's DefaultQueryExecMode.
|
||||
//
|
||||
// While query can contain multiple statements if the connection's DefaultQueryExecMode is QueryModeSimple, this should
|
||||
// be avoided. QueuedQuery.Fn must not be set as it will only be called for the first query. That is, QueuedQuery.Query,
|
||||
// QueuedQuery.QueryRow, and QueuedQuery.Exec must not be called. In addition, any error messages or tracing that
|
||||
// include the current query may reference the wrong query.
|
||||
// Queue queues a query to batch b. query can be an SQL query or the name of a prepared statement.
|
||||
func (b *Batch) Queue(query string, arguments ...any) *QueuedQuery {
|
||||
qq := &QueuedQuery{
|
||||
SQL: query,
|
||||
Arguments: arguments,
|
||||
query: query,
|
||||
arguments: arguments,
|
||||
}
|
||||
b.QueuedQueries = append(b.QueuedQueries, qq)
|
||||
b.queuedQueries = append(b.queuedQueries, qq)
|
||||
return qq
|
||||
}
|
||||
|
||||
// Len returns number of queries that have been queued so far.
|
||||
func (b *Batch) Len() int {
|
||||
return len(b.QueuedQueries)
|
||||
return len(b.queuedQueries)
|
||||
}
|
||||
|
||||
type BatchResults interface {
|
||||
@@ -132,7 +126,7 @@ func (br *batchResults) Exec() (pgconn.CommandTag, error) {
|
||||
if !br.mrr.NextResult() {
|
||||
err := br.mrr.Close()
|
||||
if err == nil {
|
||||
err = errors.New("no more results in batch")
|
||||
err = errors.New("no result")
|
||||
}
|
||||
if br.conn.batchTracer != nil {
|
||||
br.conn.batchTracer.TraceBatchQuery(br.ctx, br.conn, TraceBatchQueryData{
|
||||
@@ -184,7 +178,7 @@ func (br *batchResults) Query() (Rows, error) {
|
||||
if !br.mrr.NextResult() {
|
||||
rows.err = br.mrr.Close()
|
||||
if rows.err == nil {
|
||||
rows.err = errors.New("no more results in batch")
|
||||
rows.err = errors.New("no result")
|
||||
}
|
||||
rows.closed = true
|
||||
|
||||
@@ -231,9 +225,9 @@ func (br *batchResults) Close() error {
|
||||
}
|
||||
|
||||
// Read and run fn for all remaining items
|
||||
for br.err == nil && !br.closed && br.b != nil && br.qqIdx < len(br.b.QueuedQueries) {
|
||||
if br.b.QueuedQueries[br.qqIdx].Fn != nil {
|
||||
err := br.b.QueuedQueries[br.qqIdx].Fn(br)
|
||||
for br.err == nil && !br.closed && br.b != nil && br.qqIdx < len(br.b.queuedQueries) {
|
||||
if br.b.queuedQueries[br.qqIdx].fn != nil {
|
||||
err := br.b.queuedQueries[br.qqIdx].fn(br)
|
||||
if err != nil {
|
||||
br.err = err
|
||||
}
|
||||
@@ -257,10 +251,10 @@ func (br *batchResults) earlyError() error {
|
||||
}
|
||||
|
||||
func (br *batchResults) nextQueryAndArgs() (query string, args []any, ok bool) {
|
||||
if br.b != nil && br.qqIdx < len(br.b.QueuedQueries) {
|
||||
bi := br.b.QueuedQueries[br.qqIdx]
|
||||
query = bi.SQL
|
||||
args = bi.Arguments
|
||||
if br.b != nil && br.qqIdx < len(br.b.queuedQueries) {
|
||||
bi := br.b.queuedQueries[br.qqIdx]
|
||||
query = bi.query
|
||||
args = bi.arguments
|
||||
ok = true
|
||||
br.qqIdx++
|
||||
}
|
||||
@@ -291,10 +285,7 @@ func (br *pipelineBatchResults) Exec() (pgconn.CommandTag, error) {
|
||||
return pgconn.CommandTag{}, br.err
|
||||
}
|
||||
|
||||
query, arguments, err := br.nextQueryAndArgs()
|
||||
if err != nil {
|
||||
return pgconn.CommandTag{}, err
|
||||
}
|
||||
query, arguments, _ := br.nextQueryAndArgs()
|
||||
|
||||
results, err := br.pipeline.GetResults()
|
||||
if err != nil {
|
||||
@@ -337,9 +328,9 @@ func (br *pipelineBatchResults) Query() (Rows, error) {
|
||||
return &baseRows{err: br.err, closed: true}, br.err
|
||||
}
|
||||
|
||||
query, arguments, err := br.nextQueryAndArgs()
|
||||
if err != nil {
|
||||
return &baseRows{err: err, closed: true}, err
|
||||
query, arguments, ok := br.nextQueryAndArgs()
|
||||
if !ok {
|
||||
query = "batch query"
|
||||
}
|
||||
|
||||
rows := br.conn.getRows(br.ctx, query, arguments)
|
||||
@@ -403,9 +394,9 @@ func (br *pipelineBatchResults) Close() error {
|
||||
}
|
||||
|
||||
// Read and run fn for all remaining items
|
||||
for br.err == nil && !br.closed && br.b != nil && br.qqIdx < len(br.b.QueuedQueries) {
|
||||
if br.b.QueuedQueries[br.qqIdx].Fn != nil {
|
||||
err := br.b.QueuedQueries[br.qqIdx].Fn(br)
|
||||
for br.err == nil && !br.closed && br.b != nil && br.qqIdx < len(br.b.queuedQueries) {
|
||||
if br.b.queuedQueries[br.qqIdx].fn != nil {
|
||||
err := br.b.queuedQueries[br.qqIdx].fn(br)
|
||||
if err != nil {
|
||||
br.err = err
|
||||
}
|
||||
@@ -428,16 +419,13 @@ func (br *pipelineBatchResults) earlyError() error {
|
||||
return br.err
|
||||
}
|
||||
|
||||
func (br *pipelineBatchResults) nextQueryAndArgs() (query string, args []any, err error) {
|
||||
if br.b == nil {
|
||||
return "", nil, errors.New("no reference to batch")
|
||||
func (br *pipelineBatchResults) nextQueryAndArgs() (query string, args []any, ok bool) {
|
||||
if br.b != nil && br.qqIdx < len(br.b.queuedQueries) {
|
||||
bi := br.b.queuedQueries[br.qqIdx]
|
||||
query = bi.query
|
||||
args = bi.arguments
|
||||
ok = true
|
||||
br.qqIdx++
|
||||
}
|
||||
|
||||
if br.qqIdx >= len(br.b.QueuedQueries) {
|
||||
return "", nil, errors.New("no more results in batch")
|
||||
}
|
||||
|
||||
bi := br.b.QueuedQueries[br.qqIdx]
|
||||
br.qqIdx++
|
||||
return bi.SQL, bi.Arguments, nil
|
||||
return
|
||||
}
|
||||
|
||||
207
vendor/github.com/jackc/pgx/v5/conn.go
generated
vendored
207
vendor/github.com/jackc/pgx/v5/conn.go
generated
vendored
@@ -2,15 +2,13 @@ package pgx
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"database/sql"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgx/v5/internal/anynil"
|
||||
"github.com/jackc/pgx/v5/internal/sanitize"
|
||||
"github.com/jackc/pgx/v5/internal/stmtcache"
|
||||
"github.com/jackc/pgx/v5/pgconn"
|
||||
@@ -37,7 +35,7 @@ type ConnConfig struct {
|
||||
|
||||
// DefaultQueryExecMode controls the default mode for executing queries. By default pgx uses the extended protocol
|
||||
// and automatically prepares and caches prepared statements. However, this may be incompatible with proxies such as
|
||||
// PGBouncer. In this case it may be preferable to use QueryExecModeExec or QueryExecModeSimpleProtocol. The same
|
||||
// PGBouncer. In this case it may be preferrable to use QueryExecModeExec or QueryExecModeSimpleProtocol. The same
|
||||
// functionality can be controlled on a per query basis by passing a QueryExecMode as the first query argument.
|
||||
DefaultQueryExecMode QueryExecMode
|
||||
|
||||
@@ -101,33 +99,11 @@ func (ident Identifier) Sanitize() string {
|
||||
return strings.Join(parts, ".")
|
||||
}
|
||||
|
||||
var (
|
||||
// ErrNoRows occurs when rows are expected but none are returned.
|
||||
ErrNoRows = newProxyErr(sql.ErrNoRows, "no rows in result set")
|
||||
// ErrTooManyRows occurs when more rows than expected are returned.
|
||||
ErrTooManyRows = errors.New("too many rows in result set")
|
||||
)
|
||||
// ErrNoRows occurs when rows are expected but none are returned.
|
||||
var ErrNoRows = errors.New("no rows in result set")
|
||||
|
||||
func newProxyErr(background error, msg string) error {
|
||||
return &proxyError{
|
||||
msg: msg,
|
||||
background: background,
|
||||
}
|
||||
}
|
||||
|
||||
type proxyError struct {
|
||||
msg string
|
||||
background error
|
||||
}
|
||||
|
||||
func (err *proxyError) Error() string { return err.msg }
|
||||
|
||||
func (err *proxyError) Unwrap() error { return err.background }
|
||||
|
||||
var (
|
||||
errDisabledStatementCache = fmt.Errorf("cannot use QueryExecModeCacheStatement with disabled statement cache")
|
||||
errDisabledDescriptionCache = fmt.Errorf("cannot use QueryExecModeCacheDescribe with disabled description cache")
|
||||
)
|
||||
var errDisabledStatementCache = fmt.Errorf("cannot use QueryExecModeCacheStatement with disabled statement cache")
|
||||
var errDisabledDescriptionCache = fmt.Errorf("cannot use QueryExecModeCacheDescribe with disabled description cache")
|
||||
|
||||
// Connect establishes a connection with a PostgreSQL server with a connection string. See
|
||||
// pgconn.Connect for details.
|
||||
@@ -293,7 +269,7 @@ func connect(ctx context.Context, config *ConnConfig) (c *Conn, err error) {
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// Close closes a connection. It is safe to call Close on an already closed
|
||||
// Close closes a connection. It is safe to call Close on a already closed
|
||||
// connection.
|
||||
func (c *Conn) Close(ctx context.Context) error {
|
||||
if c.IsClosed() {
|
||||
@@ -304,15 +280,12 @@ func (c *Conn) Close(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Prepare creates a prepared statement with name and sql. sql can contain placeholders for bound parameters. These
|
||||
// placeholders are referenced positionally as $1, $2, etc. name can be used instead of sql with Query, QueryRow, and
|
||||
// Exec to execute the statement. It can also be used with Batch.Queue.
|
||||
// Prepare creates a prepared statement with name and sql. sql can contain placeholders
|
||||
// for bound parameters. These placeholders are referenced positional as $1, $2, etc.
|
||||
//
|
||||
// The underlying PostgreSQL identifier for the prepared statement will be name if name != sql or a digest of sql if
|
||||
// name == sql.
|
||||
//
|
||||
// Prepare is idempotent; i.e. it is safe to call Prepare multiple times with the same name and sql arguments. This
|
||||
// allows a code path to Prepare and Query/Exec without concern for if the statement has already been prepared.
|
||||
// Prepare is idempotent; i.e. it is safe to call Prepare multiple times with the same
|
||||
// name and sql arguments. This allows a code path to Prepare and Query/Exec without
|
||||
// concern for if the statement has already been prepared.
|
||||
func (c *Conn) Prepare(ctx context.Context, name, sql string) (sd *pgconn.StatementDescription, err error) {
|
||||
if c.prepareTracer != nil {
|
||||
ctx = c.prepareTracer.TracePrepareStart(ctx, c, TracePrepareStartData{Name: name, SQL: sql})
|
||||
@@ -334,48 +307,23 @@ func (c *Conn) Prepare(ctx context.Context, name, sql string) (sd *pgconn.Statem
|
||||
}()
|
||||
}
|
||||
|
||||
var psName, psKey string
|
||||
if name == sql {
|
||||
digest := sha256.Sum256([]byte(sql))
|
||||
psName = "stmt_" + hex.EncodeToString(digest[0:24])
|
||||
psKey = sql
|
||||
} else {
|
||||
psName = name
|
||||
psKey = name
|
||||
}
|
||||
|
||||
sd, err = c.pgConn.Prepare(ctx, psName, sql, nil)
|
||||
sd, err = c.pgConn.Prepare(ctx, name, sql, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if psKey != "" {
|
||||
c.preparedStatements[psKey] = sd
|
||||
if name != "" {
|
||||
c.preparedStatements[name] = sd
|
||||
}
|
||||
|
||||
return sd, nil
|
||||
}
|
||||
|
||||
// Deallocate releases a prepared statement. Calling Deallocate on a non-existent prepared statement will succeed.
|
||||
// Deallocate released a prepared statement
|
||||
func (c *Conn) Deallocate(ctx context.Context, name string) error {
|
||||
var psName string
|
||||
sd := c.preparedStatements[name]
|
||||
if sd != nil {
|
||||
psName = sd.Name
|
||||
} else {
|
||||
psName = name
|
||||
}
|
||||
|
||||
err := c.pgConn.Deallocate(ctx, psName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if sd != nil {
|
||||
delete(c.preparedStatements, name)
|
||||
}
|
||||
|
||||
return nil
|
||||
delete(c.preparedStatements, name)
|
||||
_, err := c.pgConn.Exec(ctx, "deallocate "+quoteIdentifier(name)).ReadAll()
|
||||
return err
|
||||
}
|
||||
|
||||
// DeallocateAll releases all previously prepared statements from the server and client, where it also resets the statement and description cache.
|
||||
@@ -493,7 +441,7 @@ optionLoop:
|
||||
if queryRewriter != nil {
|
||||
sql, arguments, err = queryRewriter.RewriteQuery(ctx, c, sql, arguments)
|
||||
if err != nil {
|
||||
return pgconn.CommandTag{}, fmt.Errorf("rewrite query failed: %w", err)
|
||||
return pgconn.CommandTag{}, fmt.Errorf("rewrite query failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -513,7 +461,7 @@ optionLoop:
|
||||
}
|
||||
sd := c.statementCache.Get(sql)
|
||||
if sd == nil {
|
||||
sd, err = c.Prepare(ctx, stmtcache.StatementName(sql), sql)
|
||||
sd, err = c.Prepare(ctx, stmtcache.NextStatementName(), sql)
|
||||
if err != nil {
|
||||
return pgconn.CommandTag{}, err
|
||||
}
|
||||
@@ -531,7 +479,6 @@ optionLoop:
|
||||
if err != nil {
|
||||
return pgconn.CommandTag{}, err
|
||||
}
|
||||
c.descriptionCache.Put(sd)
|
||||
}
|
||||
|
||||
return c.execParams(ctx, sd, arguments)
|
||||
@@ -626,35 +573,32 @@ type QueryExecMode int32
|
||||
const (
|
||||
_ QueryExecMode = iota
|
||||
|
||||
// Automatically prepare and cache statements. This uses the extended protocol. Queries are executed in a single round
|
||||
// trip after the statement is cached. This is the default. If the database schema is modified or the search_path is
|
||||
// changed after a statement is cached then the first execution of a previously cached query may fail. e.g. If the
|
||||
// number of columns returned by a "SELECT *" changes or the type of a column is changed.
|
||||
// Automatically prepare and cache statements. This uses the extended protocol. Queries are executed in a single
|
||||
// round trip after the statement is cached. This is the default.
|
||||
QueryExecModeCacheStatement
|
||||
|
||||
// Cache statement descriptions (i.e. argument and result types) and assume they do not change. This uses the extended
|
||||
// protocol. Queries are executed in a single round trip after the description is cached. If the database schema is
|
||||
// modified or the search_path is changed after a statement is cached then the first execution of a previously cached
|
||||
// query may fail. e.g. If the number of columns returned by a "SELECT *" changes or the type of a column is changed.
|
||||
// Cache statement descriptions (i.e. argument and result types) and assume they do not change. This uses the
|
||||
// extended protocol. Queries are executed in a single round trip after the description is cached. If the database
|
||||
// schema is modified or the search_path is changed this may result in undetected result decoding errors.
|
||||
QueryExecModeCacheDescribe
|
||||
|
||||
// Get the statement description on every execution. This uses the extended protocol. Queries require two round trips
|
||||
// to execute. It does not use named prepared statements. But it does use the unnamed prepared statement to get the
|
||||
// statement description on the first round trip and then uses it to execute the query on the second round trip. This
|
||||
// may cause problems with connection poolers that switch the underlying connection between round trips. It is safe
|
||||
// even when the database schema is modified concurrently.
|
||||
// even when the the database schema is modified concurrently.
|
||||
QueryExecModeDescribeExec
|
||||
|
||||
// Assume the PostgreSQL query parameter types based on the Go type of the arguments. This uses the extended protocol
|
||||
// with text formatted parameters and results. Queries are executed in a single round trip. Type mappings can be
|
||||
// registered with pgtype.Map.RegisterDefaultPgType. Queries will be rejected that have arguments that are
|
||||
// unregistered or ambiguous. e.g. A map[string]string may have the PostgreSQL type json or hstore. Modes that know
|
||||
// unregistered or ambigious. e.g. A map[string]string may have the PostgreSQL type json or hstore. Modes that know
|
||||
// the PostgreSQL type can use a map[string]string directly as an argument. This mode cannot.
|
||||
QueryExecModeExec
|
||||
|
||||
// Use the simple protocol. Assume the PostgreSQL query parameter types based on the Go type of the arguments.
|
||||
// Queries are executed in a single round trip. Type mappings can be registered with
|
||||
// pgtype.Map.RegisterDefaultPgType. Queries will be rejected that have arguments that are unregistered or ambiguous.
|
||||
// pgtype.Map.RegisterDefaultPgType. Queries will be rejected that have arguments that are unregistered or ambigious.
|
||||
// e.g. A map[string]string may have the PostgreSQL type json or hstore. Modes that know the PostgreSQL type can use
|
||||
// a map[string]string directly as an argument. This mode cannot.
|
||||
//
|
||||
@@ -761,7 +705,7 @@ optionLoop:
|
||||
sql, args, err = queryRewriter.RewriteQuery(ctx, c, sql, args)
|
||||
if err != nil {
|
||||
rows := c.getRows(ctx, originalSQL, originalArgs)
|
||||
err = fmt.Errorf("rewrite query failed: %w", err)
|
||||
err = fmt.Errorf("rewrite query failed: %v", err)
|
||||
rows.fatal(err)
|
||||
return rows, err
|
||||
}
|
||||
@@ -773,6 +717,7 @@ optionLoop:
|
||||
}
|
||||
|
||||
c.eqb.reset()
|
||||
anynil.NormalizeSlice(args)
|
||||
rows := c.getRows(ctx, sql, args)
|
||||
|
||||
var err error
|
||||
@@ -862,6 +807,7 @@ func (c *Conn) getStatementDescription(
|
||||
mode QueryExecMode,
|
||||
sql string,
|
||||
) (sd *pgconn.StatementDescription, err error) {
|
||||
|
||||
switch mode {
|
||||
case QueryExecModeCacheStatement:
|
||||
if c.statementCache == nil {
|
||||
@@ -869,7 +815,7 @@ func (c *Conn) getStatementDescription(
|
||||
}
|
||||
sd = c.statementCache.Get(sql)
|
||||
if sd == nil {
|
||||
sd, err = c.Prepare(ctx, stmtcache.StatementName(sql), sql)
|
||||
sd, err = c.Prepare(ctx, stmtcache.NextStatementName(), sql)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -919,14 +865,15 @@ func (c *Conn) SendBatch(ctx context.Context, b *Batch) (br BatchResults) {
|
||||
return &batchResults{ctx: ctx, conn: c, err: err}
|
||||
}
|
||||
|
||||
for _, bi := range b.QueuedQueries {
|
||||
mode := c.config.DefaultQueryExecMode
|
||||
|
||||
for _, bi := range b.queuedQueries {
|
||||
var queryRewriter QueryRewriter
|
||||
sql := bi.SQL
|
||||
arguments := bi.Arguments
|
||||
sql := bi.query
|
||||
arguments := bi.arguments
|
||||
|
||||
optionLoop:
|
||||
for len(arguments) > 0 {
|
||||
// Update Batch.Queue function comment when additional options are implemented
|
||||
switch arg := arguments[0].(type) {
|
||||
case QueryRewriter:
|
||||
queryRewriter = arg
|
||||
@@ -940,23 +887,21 @@ func (c *Conn) SendBatch(ctx context.Context, b *Batch) (br BatchResults) {
|
||||
var err error
|
||||
sql, arguments, err = queryRewriter.RewriteQuery(ctx, c, sql, arguments)
|
||||
if err != nil {
|
||||
return &batchResults{ctx: ctx, conn: c, err: fmt.Errorf("rewrite query failed: %w", err)}
|
||||
return &batchResults{ctx: ctx, conn: c, err: fmt.Errorf("rewrite query failed: %v", err)}
|
||||
}
|
||||
}
|
||||
|
||||
bi.SQL = sql
|
||||
bi.Arguments = arguments
|
||||
bi.query = sql
|
||||
bi.arguments = arguments
|
||||
}
|
||||
|
||||
// TODO: changing mode per batch? Update Batch.Queue function comment when implemented
|
||||
mode := c.config.DefaultQueryExecMode
|
||||
if mode == QueryExecModeSimpleProtocol {
|
||||
return c.sendBatchQueryExecModeSimpleProtocol(ctx, b)
|
||||
}
|
||||
|
||||
// All other modes use extended protocol and thus can use prepared statements.
|
||||
for _, bi := range b.QueuedQueries {
|
||||
if sd, ok := c.preparedStatements[bi.SQL]; ok {
|
||||
for _, bi := range b.queuedQueries {
|
||||
if sd, ok := c.preparedStatements[bi.query]; ok {
|
||||
bi.sd = sd
|
||||
}
|
||||
}
|
||||
@@ -977,11 +922,11 @@ func (c *Conn) SendBatch(ctx context.Context, b *Batch) (br BatchResults) {
|
||||
|
||||
func (c *Conn) sendBatchQueryExecModeSimpleProtocol(ctx context.Context, b *Batch) *batchResults {
|
||||
var sb strings.Builder
|
||||
for i, bi := range b.QueuedQueries {
|
||||
for i, bi := range b.queuedQueries {
|
||||
if i > 0 {
|
||||
sb.WriteByte(';')
|
||||
}
|
||||
sql, err := c.sanitizeForSimpleQuery(bi.SQL, bi.Arguments...)
|
||||
sql, err := c.sanitizeForSimpleQuery(bi.query, bi.arguments...)
|
||||
if err != nil {
|
||||
return &batchResults{ctx: ctx, conn: c, err: err}
|
||||
}
|
||||
@@ -1000,21 +945,21 @@ func (c *Conn) sendBatchQueryExecModeSimpleProtocol(ctx context.Context, b *Batc
|
||||
func (c *Conn) sendBatchQueryExecModeExec(ctx context.Context, b *Batch) *batchResults {
|
||||
batch := &pgconn.Batch{}
|
||||
|
||||
for _, bi := range b.QueuedQueries {
|
||||
for _, bi := range b.queuedQueries {
|
||||
sd := bi.sd
|
||||
if sd != nil {
|
||||
err := c.eqb.Build(c.typeMap, sd, bi.Arguments)
|
||||
err := c.eqb.Build(c.typeMap, sd, bi.arguments)
|
||||
if err != nil {
|
||||
return &batchResults{ctx: ctx, conn: c, err: err}
|
||||
}
|
||||
|
||||
batch.ExecPrepared(sd.Name, c.eqb.ParamValues, c.eqb.ParamFormats, c.eqb.ResultFormats)
|
||||
} else {
|
||||
err := c.eqb.Build(c.typeMap, nil, bi.Arguments)
|
||||
err := c.eqb.Build(c.typeMap, nil, bi.arguments)
|
||||
if err != nil {
|
||||
return &batchResults{ctx: ctx, conn: c, err: err}
|
||||
}
|
||||
batch.ExecParams(bi.SQL, c.eqb.ParamValues, nil, c.eqb.ParamFormats, c.eqb.ResultFormats)
|
||||
batch.ExecParams(bi.query, c.eqb.ParamValues, nil, c.eqb.ParamFormats, c.eqb.ResultFormats)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1039,18 +984,18 @@ func (c *Conn) sendBatchQueryExecModeCacheStatement(ctx context.Context, b *Batc
|
||||
distinctNewQueries := []*pgconn.StatementDescription{}
|
||||
distinctNewQueriesIdxMap := make(map[string]int)
|
||||
|
||||
for _, bi := range b.QueuedQueries {
|
||||
for _, bi := range b.queuedQueries {
|
||||
if bi.sd == nil {
|
||||
sd := c.statementCache.Get(bi.SQL)
|
||||
sd := c.statementCache.Get(bi.query)
|
||||
if sd != nil {
|
||||
bi.sd = sd
|
||||
} else {
|
||||
if idx, present := distinctNewQueriesIdxMap[bi.SQL]; present {
|
||||
if idx, present := distinctNewQueriesIdxMap[bi.query]; present {
|
||||
bi.sd = distinctNewQueries[idx]
|
||||
} else {
|
||||
sd = &pgconn.StatementDescription{
|
||||
Name: stmtcache.StatementName(bi.SQL),
|
||||
SQL: bi.SQL,
|
||||
Name: stmtcache.NextStatementName(),
|
||||
SQL: bi.query,
|
||||
}
|
||||
distinctNewQueriesIdxMap[sd.SQL] = len(distinctNewQueries)
|
||||
distinctNewQueries = append(distinctNewQueries, sd)
|
||||
@@ -1071,17 +1016,17 @@ func (c *Conn) sendBatchQueryExecModeCacheDescribe(ctx context.Context, b *Batch
|
||||
distinctNewQueries := []*pgconn.StatementDescription{}
|
||||
distinctNewQueriesIdxMap := make(map[string]int)
|
||||
|
||||
for _, bi := range b.QueuedQueries {
|
||||
for _, bi := range b.queuedQueries {
|
||||
if bi.sd == nil {
|
||||
sd := c.descriptionCache.Get(bi.SQL)
|
||||
sd := c.descriptionCache.Get(bi.query)
|
||||
if sd != nil {
|
||||
bi.sd = sd
|
||||
} else {
|
||||
if idx, present := distinctNewQueriesIdxMap[bi.SQL]; present {
|
||||
if idx, present := distinctNewQueriesIdxMap[bi.query]; present {
|
||||
bi.sd = distinctNewQueries[idx]
|
||||
} else {
|
||||
sd = &pgconn.StatementDescription{
|
||||
SQL: bi.SQL,
|
||||
SQL: bi.query,
|
||||
}
|
||||
distinctNewQueriesIdxMap[sd.SQL] = len(distinctNewQueries)
|
||||
distinctNewQueries = append(distinctNewQueries, sd)
|
||||
@@ -1098,13 +1043,13 @@ func (c *Conn) sendBatchQueryExecModeDescribeExec(ctx context.Context, b *Batch)
|
||||
distinctNewQueries := []*pgconn.StatementDescription{}
|
||||
distinctNewQueriesIdxMap := make(map[string]int)
|
||||
|
||||
for _, bi := range b.QueuedQueries {
|
||||
for _, bi := range b.queuedQueries {
|
||||
if bi.sd == nil {
|
||||
if idx, present := distinctNewQueriesIdxMap[bi.SQL]; present {
|
||||
if idx, present := distinctNewQueriesIdxMap[bi.query]; present {
|
||||
bi.sd = distinctNewQueries[idx]
|
||||
} else {
|
||||
sd := &pgconn.StatementDescription{
|
||||
SQL: bi.SQL,
|
||||
SQL: bi.query,
|
||||
}
|
||||
distinctNewQueriesIdxMap[sd.SQL] = len(distinctNewQueries)
|
||||
distinctNewQueries = append(distinctNewQueries, sd)
|
||||
@@ -1117,7 +1062,7 @@ func (c *Conn) sendBatchQueryExecModeDescribeExec(ctx context.Context, b *Batch)
|
||||
}
|
||||
|
||||
func (c *Conn) sendBatchExtendedWithDescription(ctx context.Context, b *Batch, distinctNewQueries []*pgconn.StatementDescription, sdCache stmtcache.Cache) (pbr *pipelineBatchResults) {
|
||||
pipeline := c.pgConn.StartPipeline(ctx)
|
||||
pipeline := c.pgConn.StartPipeline(context.Background())
|
||||
defer func() {
|
||||
if pbr != nil && pbr.err != nil {
|
||||
pipeline.Close()
|
||||
@@ -1170,11 +1115,11 @@ func (c *Conn) sendBatchExtendedWithDescription(ctx context.Context, b *Batch, d
|
||||
}
|
||||
|
||||
// Queue the queries.
|
||||
for _, bi := range b.QueuedQueries {
|
||||
err := c.eqb.Build(c.typeMap, bi.sd, bi.Arguments)
|
||||
for _, bi := range b.queuedQueries {
|
||||
err := c.eqb.Build(c.typeMap, bi.sd, bi.arguments)
|
||||
if err != nil {
|
||||
// we wrap the error so we the user can understand which query failed inside the batch
|
||||
err = fmt.Errorf("error building query %s: %w", bi.SQL, err)
|
||||
err = fmt.Errorf("error building query %s: %w", bi.query, err)
|
||||
return &pipelineBatchResults{ctx: ctx, conn: c, err: err, closed: true}
|
||||
}
|
||||
|
||||
@@ -1219,15 +1164,7 @@ func (c *Conn) sanitizeForSimpleQuery(sql string, args ...any) (string, error) {
|
||||
return sanitize.SanitizeSQL(sql, valueArgs...)
|
||||
}
|
||||
|
||||
// LoadType inspects the database for typeName and produces a pgtype.Type suitable for registration. typeName must be
|
||||
// the name of a type where the underlying type(s) is already understood by pgx. It is for derived types. In particular,
|
||||
// typeName must be one of the following:
|
||||
// - An array type name of a type that is already registered. e.g. "_foo" when "foo" is registered.
|
||||
// - A composite type name where all field types are already registered.
|
||||
// - A domain type name where the base type is already registered.
|
||||
// - An enum type name.
|
||||
// - A range type name where the element type is already registered.
|
||||
// - A multirange type name where the element type is already registered.
|
||||
// LoadType inspects the database for typeName and produces a pgtype.Type suitable for registration.
|
||||
func (c *Conn) LoadType(ctx context.Context, typeName string) (*pgtype.Type, error) {
|
||||
var oid uint32
|
||||
|
||||
@@ -1370,17 +1307,17 @@ order by attnum`,
|
||||
}
|
||||
|
||||
func (c *Conn) deallocateInvalidatedCachedStatements(ctx context.Context) error {
|
||||
if txStatus := c.pgConn.TxStatus(); txStatus != 'I' && txStatus != 'T' {
|
||||
if c.pgConn.TxStatus() != 'I' {
|
||||
return nil
|
||||
}
|
||||
|
||||
if c.descriptionCache != nil {
|
||||
c.descriptionCache.RemoveInvalidated()
|
||||
c.descriptionCache.HandleInvalidated()
|
||||
}
|
||||
|
||||
var invalidatedStatements []*pgconn.StatementDescription
|
||||
if c.statementCache != nil {
|
||||
invalidatedStatements = c.statementCache.GetInvalidated()
|
||||
invalidatedStatements = c.statementCache.HandleInvalidated()
|
||||
}
|
||||
|
||||
if len(invalidatedStatements) == 0 {
|
||||
@@ -1392,6 +1329,7 @@ func (c *Conn) deallocateInvalidatedCachedStatements(ctx context.Context) error
|
||||
|
||||
for _, sd := range invalidatedStatements {
|
||||
pipeline.SendDeallocate(sd.Name)
|
||||
delete(c.preparedStatements, sd.Name)
|
||||
}
|
||||
|
||||
err := pipeline.Sync()
|
||||
@@ -1404,10 +1342,5 @@ func (c *Conn) deallocateInvalidatedCachedStatements(ctx context.Context) error
|
||||
return fmt.Errorf("failed to deallocate cached statement(s): %w", err)
|
||||
}
|
||||
|
||||
c.statementCache.RemoveInvalidated()
|
||||
for _, sd := range invalidatedStatements {
|
||||
delete(c.preparedStatements, sd.Name)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
27
vendor/github.com/jackc/pgx/v5/copy_from.go
generated
vendored
27
vendor/github.com/jackc/pgx/v5/copy_from.go
generated
vendored
@@ -64,33 +64,6 @@ func (cts *copyFromSlice) Err() error {
|
||||
return cts.err
|
||||
}
|
||||
|
||||
// CopyFromFunc returns a CopyFromSource interface that relies on nxtf for values.
|
||||
// nxtf returns rows until it either signals an 'end of data' by returning row=nil and err=nil,
|
||||
// or it returns an error. If nxtf returns an error, the copy is aborted.
|
||||
func CopyFromFunc(nxtf func() (row []any, err error)) CopyFromSource {
|
||||
return ©FromFunc{next: nxtf}
|
||||
}
|
||||
|
||||
type copyFromFunc struct {
|
||||
next func() ([]any, error)
|
||||
valueRow []any
|
||||
err error
|
||||
}
|
||||
|
||||
func (g *copyFromFunc) Next() bool {
|
||||
g.valueRow, g.err = g.next()
|
||||
// only return true if valueRow exists and no error
|
||||
return g.valueRow != nil && g.err == nil
|
||||
}
|
||||
|
||||
func (g *copyFromFunc) Values() ([]any, error) {
|
||||
return g.valueRow, g.err
|
||||
}
|
||||
|
||||
func (g *copyFromFunc) Err() error {
|
||||
return g.err
|
||||
}
|
||||
|
||||
// CopyFromSource is the interface used by *Conn.CopyFrom as the source for copy data.
|
||||
type CopyFromSource interface {
|
||||
// Next returns true if there is another row and makes the next row data
|
||||
|
||||
15
vendor/github.com/jackc/pgx/v5/doc.go
generated
vendored
15
vendor/github.com/jackc/pgx/v5/doc.go
generated
vendored
@@ -11,10 +11,9 @@ The primary way of establishing a connection is with [pgx.Connect]:
|
||||
|
||||
conn, err := pgx.Connect(context.Background(), os.Getenv("DATABASE_URL"))
|
||||
|
||||
The database connection string can be in URL or key/value format. Both PostgreSQL settings and pgx settings can be
|
||||
specified here. In addition, a config struct can be created by [ParseConfig] and modified before establishing the
|
||||
connection with [ConnectConfig] to configure settings such as tracing that cannot be configured with a connection
|
||||
string.
|
||||
The database connection string can be in URL or DSN format. Both PostgreSQL settings and pgx settings can be specified
|
||||
here. In addition, a config struct can be created by [ParseConfig] and modified before establishing the connection with
|
||||
[ConnectConfig] to configure settings such as tracing that cannot be configured with a connection string.
|
||||
|
||||
Connection Pool
|
||||
|
||||
@@ -24,8 +23,8 @@ github.com/jackc/pgx/v5/pgxpool for a concurrency safe connection pool.
|
||||
Query Interface
|
||||
|
||||
pgx implements Query in the familiar database/sql style. However, pgx provides generic functions such as CollectRows and
|
||||
ForEachRow that are a simpler and safer way of processing rows than manually calling defer rows.Close(), rows.Next(),
|
||||
rows.Scan, and rows.Err().
|
||||
ForEachRow that are a simpler and safer way of processing rows than manually calling rows.Next(), rows.Scan, and
|
||||
rows.Err().
|
||||
|
||||
CollectRows can be used collect all returned rows into a slice.
|
||||
|
||||
@@ -175,7 +174,7 @@ notification is received or the context is canceled.
|
||||
|
||||
Tracing and Logging
|
||||
|
||||
pgx supports tracing by setting ConnConfig.Tracer. To combine several tracers you can use the multitracer.Tracer.
|
||||
pgx supports tracing by setting ConnConfig.Tracer.
|
||||
|
||||
In addition, the tracelog package provides the TraceLog type which lets a traditional logger act as a Tracer.
|
||||
|
||||
@@ -188,7 +187,7 @@ implemented on top of pgconn. The Conn.PgConn() method can be used to access thi
|
||||
|
||||
PgBouncer
|
||||
|
||||
By default pgx automatically uses prepared statements. Prepared statements are incompatible with PgBouncer. This can be
|
||||
By default pgx automatically uses prepared statements. Prepared statements are incompaptible with PgBouncer. This can be
|
||||
disabled by setting a different QueryExecMode in ConnConfig.DefaultQueryExecMode.
|
||||
*/
|
||||
package pgx
|
||||
|
||||
90
vendor/github.com/jackc/pgx/v5/extended_query_builder.go
generated
vendored
90
vendor/github.com/jackc/pgx/v5/extended_query_builder.go
generated
vendored
@@ -1,8 +1,10 @@
|
||||
package pgx
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"fmt"
|
||||
|
||||
"github.com/jackc/pgx/v5/internal/anynil"
|
||||
"github.com/jackc/pgx/v5/pgconn"
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
)
|
||||
@@ -21,15 +23,10 @@ type ExtendedQueryBuilder struct {
|
||||
func (eqb *ExtendedQueryBuilder) Build(m *pgtype.Map, sd *pgconn.StatementDescription, args []any) error {
|
||||
eqb.reset()
|
||||
|
||||
anynil.NormalizeSlice(args)
|
||||
|
||||
if sd == nil {
|
||||
for i := range args {
|
||||
err := eqb.appendParam(m, 0, pgtype.TextFormatCode, args[i])
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to encode args[%d]: %w", i, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return eqb.appendParamsForQueryExecModeExec(m, args)
|
||||
}
|
||||
|
||||
if len(sd.ParamOIDs) != len(args) {
|
||||
@@ -39,7 +36,7 @@ func (eqb *ExtendedQueryBuilder) Build(m *pgtype.Map, sd *pgconn.StatementDescri
|
||||
for i := range args {
|
||||
err := eqb.appendParam(m, sd.ParamOIDs[i], -1, args[i])
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to encode args[%d]: %w", i, err)
|
||||
err = fmt.Errorf("failed to encode args[%d]: %v", i, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -116,6 +113,10 @@ func (eqb *ExtendedQueryBuilder) reset() {
|
||||
}
|
||||
|
||||
func (eqb *ExtendedQueryBuilder) encodeExtendedParamValue(m *pgtype.Map, oid uint32, formatCode int16, arg any) ([]byte, error) {
|
||||
if anynil.Is(arg) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if eqb.paramValueBytes == nil {
|
||||
eqb.paramValueBytes = make([]byte, 0, 128)
|
||||
}
|
||||
@@ -144,3 +145,74 @@ func (eqb *ExtendedQueryBuilder) chooseParameterFormatCode(m *pgtype.Map, oid ui
|
||||
|
||||
return m.FormatCodeForOID(oid)
|
||||
}
|
||||
|
||||
// appendParamsForQueryExecModeExec appends the args to eqb.
|
||||
//
|
||||
// Parameters must be encoded in the text format because of differences in type conversion between timestamps and
|
||||
// dates. In QueryExecModeExec we don't know what the actual PostgreSQL type is. To determine the type we use the
|
||||
// Go type to OID type mapping registered by RegisterDefaultPgType. However, the Go time.Time represents both
|
||||
// PostgreSQL timestamp[tz] and date. To use the binary format we would need to also specify what the PostgreSQL
|
||||
// type OID is. But that would mean telling PostgreSQL that we have sent a timestamp[tz] when what is needed is a date.
|
||||
// This means that the value is converted from text to timestamp[tz] to date. This means it does a time zone conversion
|
||||
// before converting it to date. This means that dates can be shifted by one day. In text format without that double
|
||||
// type conversion it takes the date directly and ignores time zone (i.e. it works).
|
||||
//
|
||||
// Given that the whole point of QueryExecModeExec is to operate without having to know the PostgreSQL types there is
|
||||
// no way to safely use binary or to specify the parameter OIDs.
|
||||
func (eqb *ExtendedQueryBuilder) appendParamsForQueryExecModeExec(m *pgtype.Map, args []any) error {
|
||||
for _, arg := range args {
|
||||
if arg == nil {
|
||||
err := eqb.appendParam(m, 0, TextFormatCode, arg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
dt, ok := m.TypeForValue(arg)
|
||||
if !ok {
|
||||
var tv pgtype.TextValuer
|
||||
if tv, ok = arg.(pgtype.TextValuer); ok {
|
||||
t, err := tv.TextValue()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dt, ok = m.TypeForOID(pgtype.TextOID)
|
||||
if ok {
|
||||
arg = t
|
||||
}
|
||||
}
|
||||
}
|
||||
if !ok {
|
||||
var dv driver.Valuer
|
||||
if dv, ok = arg.(driver.Valuer); ok {
|
||||
v, err := dv.Value()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dt, ok = m.TypeForValue(v)
|
||||
if ok {
|
||||
arg = v
|
||||
}
|
||||
}
|
||||
}
|
||||
if !ok {
|
||||
var str fmt.Stringer
|
||||
if str, ok = arg.(fmt.Stringer); ok {
|
||||
dt, ok = m.TypeForOID(pgtype.TextOID)
|
||||
if ok {
|
||||
arg = str.String()
|
||||
}
|
||||
}
|
||||
}
|
||||
if !ok {
|
||||
return &unknownArgumentTypeQueryExecModeExecError{arg: arg}
|
||||
}
|
||||
err := eqb.appendParam(m, dt.OID, TextFormatCode, arg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
9
vendor/github.com/jackc/pgx/v5/internal/sanitize/sanitize.go
generated
vendored
9
vendor/github.com/jackc/pgx/v5/internal/sanitize/sanitize.go
generated
vendored
@@ -35,11 +35,6 @@ func (q *Query) Sanitize(args ...any) (string, error) {
|
||||
str = part
|
||||
case int:
|
||||
argIdx := part - 1
|
||||
|
||||
if argIdx < 0 {
|
||||
return "", fmt.Errorf("first sql argument must be > 0")
|
||||
}
|
||||
|
||||
if argIdx >= len(args) {
|
||||
return "", fmt.Errorf("insufficient arguments")
|
||||
}
|
||||
@@ -63,10 +58,6 @@ func (q *Query) Sanitize(args ...any) (string, error) {
|
||||
return "", fmt.Errorf("invalid arg type: %T", arg)
|
||||
}
|
||||
argUse[argIdx] = true
|
||||
|
||||
// Prevent SQL injection via Line Comment Creation
|
||||
// https://github.com/jackc/pgx/security/advisories/GHSA-m7wr-2xf7-cm9p
|
||||
str = " " + str + " "
|
||||
default:
|
||||
return "", fmt.Errorf("invalid Part type: %T", part)
|
||||
}
|
||||
|
||||
22
vendor/github.com/jackc/pgx/v5/internal/stmtcache/lru_cache.go
generated
vendored
22
vendor/github.com/jackc/pgx/v5/internal/stmtcache/lru_cache.go
generated
vendored
@@ -34,8 +34,7 @@ func (c *LRUCache) Get(key string) *pgconn.StatementDescription {
|
||||
|
||||
}
|
||||
|
||||
// Put stores sd in the cache. Put panics if sd.SQL is "". Put does nothing if sd.SQL already exists in the cache or
|
||||
// sd.SQL has been invalidated and HandleInvalidated has not been called yet.
|
||||
// Put stores sd in the cache. Put panics if sd.SQL is "". Put does nothing if sd.SQL already exists in the cache.
|
||||
func (c *LRUCache) Put(sd *pgconn.StatementDescription) {
|
||||
if sd.SQL == "" {
|
||||
panic("cannot store statement description with empty SQL")
|
||||
@@ -45,13 +44,6 @@ func (c *LRUCache) Put(sd *pgconn.StatementDescription) {
|
||||
return
|
||||
}
|
||||
|
||||
// The statement may have been invalidated but not yet handled. Do not readd it to the cache.
|
||||
for _, invalidSD := range c.invalidStmts {
|
||||
if invalidSD.SQL == sd.SQL {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if c.l.Len() == c.cap {
|
||||
c.invalidateOldest()
|
||||
}
|
||||
@@ -81,16 +73,10 @@ func (c *LRUCache) InvalidateAll() {
|
||||
c.l = list.New()
|
||||
}
|
||||
|
||||
// GetInvalidated returns a slice of all statement descriptions invalidated since the last call to RemoveInvalidated.
|
||||
func (c *LRUCache) GetInvalidated() []*pgconn.StatementDescription {
|
||||
return c.invalidStmts
|
||||
}
|
||||
|
||||
// RemoveInvalidated removes all invalidated statement descriptions. No other calls to Cache must be made between a
|
||||
// call to GetInvalidated and RemoveInvalidated or RemoveInvalidated may remove statement descriptions that were
|
||||
// never seen by the call to GetInvalidated.
|
||||
func (c *LRUCache) RemoveInvalidated() {
|
||||
func (c *LRUCache) HandleInvalidated() []*pgconn.StatementDescription {
|
||||
invalidStmts := c.invalidStmts
|
||||
c.invalidStmts = nil
|
||||
return invalidStmts
|
||||
}
|
||||
|
||||
// Len returns the number of cached prepared statement descriptions.
|
||||
|
||||
40
vendor/github.com/jackc/pgx/v5/internal/stmtcache/stmtcache.go
generated
vendored
40
vendor/github.com/jackc/pgx/v5/internal/stmtcache/stmtcache.go
generated
vendored
@@ -2,17 +2,18 @@
|
||||
package stmtcache
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"strconv"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgconn"
|
||||
)
|
||||
|
||||
// StatementName returns a statement name that will be stable for sql across multiple connections and program
|
||||
// executions.
|
||||
func StatementName(sql string) string {
|
||||
digest := sha256.Sum256([]byte(sql))
|
||||
return "stmtcache_" + hex.EncodeToString(digest[0:24])
|
||||
var stmtCounter int64
|
||||
|
||||
// NextStatementName returns a statement name that will be unique for the lifetime of the program.
|
||||
func NextStatementName() string {
|
||||
n := atomic.AddInt64(&stmtCounter, 1)
|
||||
return "stmtcache_" + strconv.FormatInt(n, 10)
|
||||
}
|
||||
|
||||
// Cache caches statement descriptions.
|
||||
@@ -29,13 +30,8 @@ type Cache interface {
|
||||
// InvalidateAll invalidates all statement descriptions.
|
||||
InvalidateAll()
|
||||
|
||||
// GetInvalidated returns a slice of all statement descriptions invalidated since the last call to RemoveInvalidated.
|
||||
GetInvalidated() []*pgconn.StatementDescription
|
||||
|
||||
// RemoveInvalidated removes all invalidated statement descriptions. No other calls to Cache must be made between a
|
||||
// call to GetInvalidated and RemoveInvalidated or RemoveInvalidated may remove statement descriptions that were
|
||||
// never seen by the call to GetInvalidated.
|
||||
RemoveInvalidated()
|
||||
// HandleInvalidated returns a slice of all statement descriptions invalidated since the last call to HandleInvalidated.
|
||||
HandleInvalidated() []*pgconn.StatementDescription
|
||||
|
||||
// Len returns the number of cached prepared statement descriptions.
|
||||
Len() int
|
||||
@@ -43,3 +39,19 @@ type Cache interface {
|
||||
// Cap returns the maximum number of cached prepared statement descriptions.
|
||||
Cap() int
|
||||
}
|
||||
|
||||
func IsStatementInvalid(err error) bool {
|
||||
pgErr, ok := err.(*pgconn.PgError)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
// https://github.com/jackc/pgx/issues/1162
|
||||
//
|
||||
// We used to look for the message "cached plan must not change result type". However, that message can be localized.
|
||||
// Unfortunately, error code "0A000" - "FEATURE NOT SUPPORTED" is used for many different errors and the only way to
|
||||
// tell the difference is by the message. But all that happens is we clear a statement that we otherwise wouldn't
|
||||
// have so it should be safe.
|
||||
possibleInvalidCachedPlanError := pgErr.Code == "0A000"
|
||||
return possibleInvalidCachedPlanError
|
||||
}
|
||||
|
||||
12
vendor/github.com/jackc/pgx/v5/internal/stmtcache/unlimited_cache.go
generated
vendored
12
vendor/github.com/jackc/pgx/v5/internal/stmtcache/unlimited_cache.go
generated
vendored
@@ -54,16 +54,10 @@ func (c *UnlimitedCache) InvalidateAll() {
|
||||
c.m = make(map[string]*pgconn.StatementDescription)
|
||||
}
|
||||
|
||||
// GetInvalidated returns a slice of all statement descriptions invalidated since the last call to RemoveInvalidated.
|
||||
func (c *UnlimitedCache) GetInvalidated() []*pgconn.StatementDescription {
|
||||
return c.invalidStmts
|
||||
}
|
||||
|
||||
// RemoveInvalidated removes all invalidated statement descriptions. No other calls to Cache must be made between a
|
||||
// call to GetInvalidated and RemoveInvalidated or RemoveInvalidated may remove statement descriptions that were
|
||||
// never seen by the call to GetInvalidated.
|
||||
func (c *UnlimitedCache) RemoveInvalidated() {
|
||||
func (c *UnlimitedCache) HandleInvalidated() []*pgconn.StatementDescription {
|
||||
invalidStmts := c.invalidStmts
|
||||
c.invalidStmts = nil
|
||||
return invalidStmts
|
||||
}
|
||||
|
||||
// Len returns the number of cached prepared statement descriptions.
|
||||
|
||||
76
vendor/github.com/jackc/pgx/v5/large_objects.go
generated
vendored
76
vendor/github.com/jackc/pgx/v5/large_objects.go
generated
vendored
@@ -4,15 +4,8 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
)
|
||||
|
||||
// The PostgreSQL wire protocol has a limit of 1 GB - 1 per message. See definition of
|
||||
// PQ_LARGE_MESSAGE_LIMIT in the PostgreSQL source code. To allow for the other data
|
||||
// in the message,maxLargeObjectMessageLength should be no larger than 1 GB - 1 KB.
|
||||
var maxLargeObjectMessageLength = 1024*1024*1024 - 1024
|
||||
|
||||
// LargeObjects is a structure used to access the large objects API. It is only valid within the transaction where it
|
||||
// was created.
|
||||
//
|
||||
@@ -75,65 +68,32 @@ type LargeObject struct {
|
||||
|
||||
// Write writes p to the large object and returns the number of bytes written and an error if not all of p was written.
|
||||
func (o *LargeObject) Write(p []byte) (int, error) {
|
||||
nTotal := 0
|
||||
for {
|
||||
expected := len(p) - nTotal
|
||||
if expected == 0 {
|
||||
break
|
||||
} else if expected > maxLargeObjectMessageLength {
|
||||
expected = maxLargeObjectMessageLength
|
||||
}
|
||||
|
||||
var n int
|
||||
err := o.tx.QueryRow(o.ctx, "select lowrite($1, $2)", o.fd, p[nTotal:nTotal+expected]).Scan(&n)
|
||||
if err != nil {
|
||||
return nTotal, err
|
||||
}
|
||||
|
||||
if n < 0 {
|
||||
return nTotal, errors.New("failed to write to large object")
|
||||
}
|
||||
|
||||
nTotal += n
|
||||
|
||||
if n < expected {
|
||||
return nTotal, errors.New("short write to large object")
|
||||
} else if n > expected {
|
||||
return nTotal, errors.New("invalid write to large object")
|
||||
}
|
||||
var n int
|
||||
err := o.tx.QueryRow(o.ctx, "select lowrite($1, $2)", o.fd, p).Scan(&n)
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
|
||||
return nTotal, nil
|
||||
if n < 0 {
|
||||
return 0, errors.New("failed to write to large object")
|
||||
}
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// Read reads up to len(p) bytes into p returning the number of bytes read.
|
||||
func (o *LargeObject) Read(p []byte) (int, error) {
|
||||
nTotal := 0
|
||||
for {
|
||||
expected := len(p) - nTotal
|
||||
if expected == 0 {
|
||||
break
|
||||
} else if expected > maxLargeObjectMessageLength {
|
||||
expected = maxLargeObjectMessageLength
|
||||
}
|
||||
|
||||
res := pgtype.PreallocBytes(p[nTotal:])
|
||||
err := o.tx.QueryRow(o.ctx, "select loread($1, $2)", o.fd, expected).Scan(&res)
|
||||
// We compute expected so that it always fits into p, so it should never happen
|
||||
// that PreallocBytes's ScanBytes had to allocate a new slice.
|
||||
nTotal += len(res)
|
||||
if err != nil {
|
||||
return nTotal, err
|
||||
}
|
||||
|
||||
if len(res) < expected {
|
||||
return nTotal, io.EOF
|
||||
} else if len(res) > expected {
|
||||
return nTotal, errors.New("invalid read of large object")
|
||||
}
|
||||
var res []byte
|
||||
err := o.tx.QueryRow(o.ctx, "select loread($1, $2)", o.fd, len(p)).Scan(&res)
|
||||
copy(p, res)
|
||||
if err != nil {
|
||||
return len(res), err
|
||||
}
|
||||
|
||||
return nTotal, nil
|
||||
if len(res) < len(p) {
|
||||
err = io.EOF
|
||||
}
|
||||
return len(res), err
|
||||
}
|
||||
|
||||
// Seek moves the current location pointer to the new location specified by offset.
|
||||
|
||||
63
vendor/github.com/jackc/pgx/v5/named_args.go
generated
vendored
63
vendor/github.com/jackc/pgx/v5/named_args.go
generated
vendored
@@ -2,7 +2,6 @@ package pgx
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
@@ -15,41 +14,10 @@ import (
|
||||
//
|
||||
// conn.Query(ctx, "select * from widgets where foo = @foo and bar = @bar", pgx.NamedArgs{"foo": 1, "bar": 2})
|
||||
// conn.Query(ctx, "select * from widgets where foo = $1 and bar = $2", 1, 2)
|
||||
//
|
||||
// Named placeholders are case sensitive and must start with a letter or underscore. Subsequent characters can be
|
||||
// letters, numbers, or underscores.
|
||||
type NamedArgs map[string]any
|
||||
|
||||
// RewriteQuery implements the QueryRewriter interface.
|
||||
func (na NamedArgs) RewriteQuery(ctx context.Context, conn *Conn, sql string, args []any) (newSQL string, newArgs []any, err error) {
|
||||
return rewriteQuery(na, sql, false)
|
||||
}
|
||||
|
||||
// StrictNamedArgs can be used in the same way as NamedArgs, but provided arguments are also checked to include all
|
||||
// named arguments that the sql query uses, and no extra arguments.
|
||||
type StrictNamedArgs map[string]any
|
||||
|
||||
// RewriteQuery implements the QueryRewriter interface.
|
||||
func (sna StrictNamedArgs) RewriteQuery(ctx context.Context, conn *Conn, sql string, args []any) (newSQL string, newArgs []any, err error) {
|
||||
return rewriteQuery(sna, sql, true)
|
||||
}
|
||||
|
||||
type namedArg string
|
||||
|
||||
type sqlLexer struct {
|
||||
src string
|
||||
start int
|
||||
pos int
|
||||
nested int // multiline comment nesting level.
|
||||
stateFn stateFn
|
||||
parts []any
|
||||
|
||||
nameToOrdinal map[namedArg]int
|
||||
}
|
||||
|
||||
type stateFn func(*sqlLexer) stateFn
|
||||
|
||||
func rewriteQuery(na map[string]any, sql string, isStrict bool) (newSQL string, newArgs []any, err error) {
|
||||
l := &sqlLexer{
|
||||
src: sql,
|
||||
stateFn: rawState,
|
||||
@@ -73,24 +41,27 @@ func rewriteQuery(na map[string]any, sql string, isStrict bool) (newSQL string,
|
||||
|
||||
newArgs = make([]any, len(l.nameToOrdinal))
|
||||
for name, ordinal := range l.nameToOrdinal {
|
||||
var found bool
|
||||
newArgs[ordinal-1], found = na[string(name)]
|
||||
if isStrict && !found {
|
||||
return "", nil, fmt.Errorf("argument %s found in sql query but not present in StrictNamedArgs", name)
|
||||
}
|
||||
}
|
||||
|
||||
if isStrict {
|
||||
for name := range na {
|
||||
if _, found := l.nameToOrdinal[namedArg(name)]; !found {
|
||||
return "", nil, fmt.Errorf("argument %s of StrictNamedArgs not found in sql query", name)
|
||||
}
|
||||
}
|
||||
newArgs[ordinal-1] = na[string(name)]
|
||||
}
|
||||
|
||||
return sb.String(), newArgs, nil
|
||||
}
|
||||
|
||||
type namedArg string
|
||||
|
||||
type sqlLexer struct {
|
||||
src string
|
||||
start int
|
||||
pos int
|
||||
nested int // multiline comment nesting level.
|
||||
stateFn stateFn
|
||||
parts []any
|
||||
|
||||
nameToOrdinal map[namedArg]int
|
||||
}
|
||||
|
||||
type stateFn func(*sqlLexer) stateFn
|
||||
|
||||
func rawState(l *sqlLexer) stateFn {
|
||||
for {
|
||||
r, width := utf8.DecodeRuneInString(l.src[l.pos:])
|
||||
@@ -109,7 +80,7 @@ func rawState(l *sqlLexer) stateFn {
|
||||
return doubleQuoteState
|
||||
case '@':
|
||||
nextRune, _ := utf8.DecodeRuneInString(l.src[l.pos:])
|
||||
if isLetter(nextRune) || nextRune == '_' {
|
||||
if isLetter(nextRune) {
|
||||
if l.pos-l.start > 0 {
|
||||
l.parts = append(l.parts, l.src[l.start:l.pos-width])
|
||||
}
|
||||
|
||||
4
vendor/github.com/jackc/pgx/v5/pgconn/auth_scram.go
generated
vendored
4
vendor/github.com/jackc/pgx/v5/pgconn/auth_scram.go
generated
vendored
@@ -47,7 +47,7 @@ func (c *PgConn) scramAuth(serverAuthMechanisms []string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Receive server-first-message payload in an AuthenticationSASLContinue.
|
||||
// Receive server-first-message payload in a AuthenticationSASLContinue.
|
||||
saslContinue, err := c.rxSASLContinue()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -67,7 +67,7 @@ func (c *PgConn) scramAuth(serverAuthMechanisms []string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Receive server-final-message payload in an AuthenticationSASLFinal.
|
||||
// Receive server-final-message payload in a AuthenticationSASLFinal.
|
||||
saslFinal, err := c.rxSASLFinal()
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
147
vendor/github.com/jackc/pgx/v5/pgconn/config.go
generated
vendored
147
vendor/github.com/jackc/pgx/v5/pgconn/config.go
generated
vendored
@@ -19,7 +19,6 @@ import (
|
||||
|
||||
"github.com/jackc/pgpassfile"
|
||||
"github.com/jackc/pgservicefile"
|
||||
"github.com/jackc/pgx/v5/pgconn/ctxwatch"
|
||||
"github.com/jackc/pgx/v5/pgproto3"
|
||||
)
|
||||
|
||||
@@ -40,12 +39,7 @@ type Config struct {
|
||||
DialFunc DialFunc // e.g. net.Dialer.DialContext
|
||||
LookupFunc LookupFunc // e.g. net.Resolver.LookupHost
|
||||
BuildFrontend BuildFrontendFunc
|
||||
|
||||
// BuildContextWatcherHandler is called to create a ContextWatcherHandler for a connection. The handler is called
|
||||
// when a context passed to a PgConn method is canceled.
|
||||
BuildContextWatcherHandler func(*PgConn) ctxwatch.Handler
|
||||
|
||||
RuntimeParams map[string]string // Run-time parameters to set on connection as session default values (e.g. search_path or application_name)
|
||||
RuntimeParams map[string]string // Run-time parameters to set on connection as session default values (e.g. search_path or application_name)
|
||||
|
||||
KerberosSrvName string
|
||||
KerberosSpn string
|
||||
@@ -66,17 +60,12 @@ type Config struct {
|
||||
// OnNotification is a callback function called when a notification from the LISTEN/NOTIFY system is received.
|
||||
OnNotification NotificationHandler
|
||||
|
||||
// OnPgError is a callback function called when a Postgres error is received by the server. The default handler will close
|
||||
// the connection on any FATAL errors. If you override this handler you should call the previously set handler or ensure
|
||||
// that you close on FATAL errors by returning false.
|
||||
OnPgError PgErrorHandler
|
||||
|
||||
createdByParseConfig bool // Used to enforce created by ParseConfig rule.
|
||||
}
|
||||
|
||||
// ParseConfigOptions contains options that control how a config is built such as GetSSLPassword.
|
||||
type ParseConfigOptions struct {
|
||||
// GetSSLPassword gets the password to decrypt a SSL client certificate. This is analogous to the libpq function
|
||||
// GetSSLPassword gets the password to decrypt a SSL client certificate. This is analogous to the the libpq function
|
||||
// PQsetSSLKeyPassHook_OpenSSL.
|
||||
GetSSLPassword GetSSLPasswordFunc
|
||||
}
|
||||
@@ -118,14 +107,6 @@ type FallbackConfig struct {
|
||||
TLSConfig *tls.Config // nil disables TLS
|
||||
}
|
||||
|
||||
// connectOneConfig is the configuration for a single attempt to connect to a single host.
|
||||
type connectOneConfig struct {
|
||||
network string
|
||||
address string
|
||||
originalHostname string // original hostname before resolving
|
||||
tlsConfig *tls.Config // nil disables TLS
|
||||
}
|
||||
|
||||
// isAbsolutePath checks if the provided value is an absolute path either
|
||||
// beginning with a forward slash (as on Linux-based systems) or with a capital
|
||||
// letter A-Z followed by a colon and a backslash, e.g., "C:\", (as on Windows).
|
||||
@@ -160,11 +141,11 @@ func NetworkAddress(host string, port uint16) (network, address string) {
|
||||
|
||||
// ParseConfig builds a *Config from connString with similar behavior to the PostgreSQL standard C library libpq. It
|
||||
// uses the same defaults as libpq (e.g. port=5432) and understands most PG* environment variables. ParseConfig closely
|
||||
// matches the parsing behavior of libpq. connString may either be in URL format or keyword = value format. See
|
||||
// https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING for details. connString also may be empty
|
||||
// to only read from the environment. If a password is not supplied it will attempt to read the .pgpass file.
|
||||
// matches the parsing behavior of libpq. connString may either be in URL format or keyword = value format (DSN style).
|
||||
// See https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING for details. connString also may be
|
||||
// empty to only read from the environment. If a password is not supplied it will attempt to read the .pgpass file.
|
||||
//
|
||||
// # Example Keyword/Value
|
||||
// # Example DSN
|
||||
// user=jack password=secret host=pg.example.com port=5432 dbname=mydb sslmode=verify-ca
|
||||
//
|
||||
// # Example URL
|
||||
@@ -183,7 +164,7 @@ func NetworkAddress(host string, port uint16) (network, address string) {
|
||||
// postgres://jack:secret@foo.example.com:5432,bar.example.com:5432/mydb
|
||||
//
|
||||
// ParseConfig currently recognizes the following environment variable and their parameter key word equivalents passed
|
||||
// via database URL or keyword/value:
|
||||
// via database URL or DSN:
|
||||
//
|
||||
// PGHOST
|
||||
// PGPORT
|
||||
@@ -247,16 +228,16 @@ func ParseConfigWithOptions(connString string, options ParseConfigOptions) (*Con
|
||||
connStringSettings := make(map[string]string)
|
||||
if connString != "" {
|
||||
var err error
|
||||
// connString may be a database URL or in PostgreSQL keyword/value format
|
||||
// connString may be a database URL or a DSN
|
||||
if strings.HasPrefix(connString, "postgres://") || strings.HasPrefix(connString, "postgresql://") {
|
||||
connStringSettings, err = parseURLSettings(connString)
|
||||
if err != nil {
|
||||
return nil, &ParseConfigError{ConnString: connString, msg: "failed to parse as URL", err: err}
|
||||
return nil, &parseConfigError{connString: connString, msg: "failed to parse as URL", err: err}
|
||||
}
|
||||
} else {
|
||||
connStringSettings, err = parseKeywordValueSettings(connString)
|
||||
connStringSettings, err = parseDSNSettings(connString)
|
||||
if err != nil {
|
||||
return nil, &ParseConfigError{ConnString: connString, msg: "failed to parse as keyword/value", err: err}
|
||||
return nil, &parseConfigError{connString: connString, msg: "failed to parse as DSN", err: err}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -265,7 +246,7 @@ func ParseConfigWithOptions(connString string, options ParseConfigOptions) (*Con
|
||||
if service, present := settings["service"]; present {
|
||||
serviceSettings, err := parseServiceSettings(settings["servicefile"], service)
|
||||
if err != nil {
|
||||
return nil, &ParseConfigError{ConnString: connString, msg: "failed to read service", err: err}
|
||||
return nil, &parseConfigError{connString: connString, msg: "failed to read service", err: err}
|
||||
}
|
||||
|
||||
settings = mergeSettings(defaultSettings, envSettings, serviceSettings, connStringSettings)
|
||||
@@ -280,22 +261,12 @@ func ParseConfigWithOptions(connString string, options ParseConfigOptions) (*Con
|
||||
BuildFrontend: func(r io.Reader, w io.Writer) *pgproto3.Frontend {
|
||||
return pgproto3.NewFrontend(r, w)
|
||||
},
|
||||
BuildContextWatcherHandler: func(pgConn *PgConn) ctxwatch.Handler {
|
||||
return &DeadlineContextWatcherHandler{Conn: pgConn.conn}
|
||||
},
|
||||
OnPgError: func(_ *PgConn, pgErr *PgError) bool {
|
||||
// we want to automatically close any fatal errors
|
||||
if strings.EqualFold(pgErr.Severity, "FATAL") {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
},
|
||||
}
|
||||
|
||||
if connectTimeoutSetting, present := settings["connect_timeout"]; present {
|
||||
connectTimeout, err := parseConnectTimeoutSetting(connectTimeoutSetting)
|
||||
if err != nil {
|
||||
return nil, &ParseConfigError{ConnString: connString, msg: "invalid connect_timeout", err: err}
|
||||
return nil, &parseConfigError{connString: connString, msg: "invalid connect_timeout", err: err}
|
||||
}
|
||||
config.ConnectTimeout = connectTimeout
|
||||
config.DialFunc = makeConnectTimeoutDialFunc(connectTimeout)
|
||||
@@ -357,7 +328,7 @@ func ParseConfigWithOptions(connString string, options ParseConfigOptions) (*Con
|
||||
|
||||
port, err := parsePort(portStr)
|
||||
if err != nil {
|
||||
return nil, &ParseConfigError{ConnString: connString, msg: "invalid port", err: err}
|
||||
return nil, &parseConfigError{connString: connString, msg: "invalid port", err: err}
|
||||
}
|
||||
|
||||
var tlsConfigs []*tls.Config
|
||||
@@ -369,7 +340,7 @@ func ParseConfigWithOptions(connString string, options ParseConfigOptions) (*Con
|
||||
var err error
|
||||
tlsConfigs, err = configTLS(settings, host, options)
|
||||
if err != nil {
|
||||
return nil, &ParseConfigError{ConnString: connString, msg: "failed to configure TLS", err: err}
|
||||
return nil, &parseConfigError{connString: connString, msg: "failed to configure TLS", err: err}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -413,7 +384,7 @@ func ParseConfigWithOptions(connString string, options ParseConfigOptions) (*Con
|
||||
case "any":
|
||||
// do nothing
|
||||
default:
|
||||
return nil, &ParseConfigError{ConnString: connString, msg: fmt.Sprintf("unknown target_session_attrs value: %v", tsa)}
|
||||
return nil, &parseConfigError{connString: connString, msg: fmt.Sprintf("unknown target_session_attrs value: %v", tsa)}
|
||||
}
|
||||
|
||||
return config, nil
|
||||
@@ -467,17 +438,14 @@ func parseEnvSettings() map[string]string {
|
||||
func parseURLSettings(connString string) (map[string]string, error) {
|
||||
settings := make(map[string]string)
|
||||
|
||||
parsedURL, err := url.Parse(connString)
|
||||
url, err := url.Parse(connString)
|
||||
if err != nil {
|
||||
if urlErr := new(url.Error); errors.As(err, &urlErr) {
|
||||
return nil, urlErr.Err
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if parsedURL.User != nil {
|
||||
settings["user"] = parsedURL.User.Username()
|
||||
if password, present := parsedURL.User.Password(); present {
|
||||
if url.User != nil {
|
||||
settings["user"] = url.User.Username()
|
||||
if password, present := url.User.Password(); present {
|
||||
settings["password"] = password
|
||||
}
|
||||
}
|
||||
@@ -485,7 +453,7 @@ func parseURLSettings(connString string) (map[string]string, error) {
|
||||
// Handle multiple host:port's in url.Host by splitting them into host,host,host and port,port,port.
|
||||
var hosts []string
|
||||
var ports []string
|
||||
for _, host := range strings.Split(parsedURL.Host, ",") {
|
||||
for _, host := range strings.Split(url.Host, ",") {
|
||||
if host == "" {
|
||||
continue
|
||||
}
|
||||
@@ -511,7 +479,7 @@ func parseURLSettings(connString string) (map[string]string, error) {
|
||||
settings["port"] = strings.Join(ports, ",")
|
||||
}
|
||||
|
||||
database := strings.TrimLeft(parsedURL.Path, "/")
|
||||
database := strings.TrimLeft(url.Path, "/")
|
||||
if database != "" {
|
||||
settings["database"] = database
|
||||
}
|
||||
@@ -520,7 +488,7 @@ func parseURLSettings(connString string) (map[string]string, error) {
|
||||
"dbname": "database",
|
||||
}
|
||||
|
||||
for k, v := range parsedURL.Query() {
|
||||
for k, v := range url.Query() {
|
||||
if k2, present := nameMap[k]; present {
|
||||
k = k2
|
||||
}
|
||||
@@ -537,7 +505,7 @@ func isIPOnly(host string) bool {
|
||||
|
||||
var asciiSpace = [256]uint8{'\t': 1, '\n': 1, '\v': 1, '\f': 1, '\r': 1, ' ': 1}
|
||||
|
||||
func parseKeywordValueSettings(s string) (map[string]string, error) {
|
||||
func parseDSNSettings(s string) (map[string]string, error) {
|
||||
settings := make(map[string]string)
|
||||
|
||||
nameMap := map[string]string{
|
||||
@@ -548,7 +516,7 @@ func parseKeywordValueSettings(s string) (map[string]string, error) {
|
||||
var key, val string
|
||||
eqIdx := strings.IndexRune(s, '=')
|
||||
if eqIdx < 0 {
|
||||
return nil, errors.New("invalid keyword/value")
|
||||
return nil, errors.New("invalid dsn")
|
||||
}
|
||||
|
||||
key = strings.Trim(s[:eqIdx], " \t\n\r\v\f")
|
||||
@@ -600,7 +568,7 @@ func parseKeywordValueSettings(s string) (map[string]string, error) {
|
||||
}
|
||||
|
||||
if key == "" {
|
||||
return nil, errors.New("invalid keyword/value")
|
||||
return nil, errors.New("invalid dsn")
|
||||
}
|
||||
|
||||
settings[key] = val
|
||||
@@ -657,36 +625,6 @@ func configTLS(settings map[string]string, thisHost string, parseConfigOptions P
|
||||
|
||||
tlsConfig := &tls.Config{}
|
||||
|
||||
if sslrootcert != "" {
|
||||
var caCertPool *x509.CertPool
|
||||
|
||||
if sslrootcert == "system" {
|
||||
var err error
|
||||
|
||||
caCertPool, err = x509.SystemCertPool()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to load system certificate pool: %w", err)
|
||||
}
|
||||
|
||||
sslmode = "verify-full"
|
||||
} else {
|
||||
caCertPool = x509.NewCertPool()
|
||||
|
||||
caPath := sslrootcert
|
||||
caCert, err := os.ReadFile(caPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to read CA file: %w", err)
|
||||
}
|
||||
|
||||
if !caCertPool.AppendCertsFromPEM(caCert) {
|
||||
return nil, errors.New("unable to add CA to cert pool")
|
||||
}
|
||||
}
|
||||
|
||||
tlsConfig.RootCAs = caCertPool
|
||||
tlsConfig.ClientCAs = caCertPool
|
||||
}
|
||||
|
||||
switch sslmode {
|
||||
case "disable":
|
||||
return []*tls.Config{nil}, nil
|
||||
@@ -744,6 +682,23 @@ func configTLS(settings map[string]string, thisHost string, parseConfigOptions P
|
||||
return nil, errors.New("sslmode is invalid")
|
||||
}
|
||||
|
||||
if sslrootcert != "" {
|
||||
caCertPool := x509.NewCertPool()
|
||||
|
||||
caPath := sslrootcert
|
||||
caCert, err := os.ReadFile(caPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to read CA file: %w", err)
|
||||
}
|
||||
|
||||
if !caCertPool.AppendCertsFromPEM(caCert) {
|
||||
return nil, errors.New("unable to add CA to cert pool")
|
||||
}
|
||||
|
||||
tlsConfig.RootCAs = caCertPool
|
||||
tlsConfig.ClientCAs = caCertPool
|
||||
}
|
||||
|
||||
if (sslcert != "" && sslkey == "") || (sslcert == "" && sslkey != "") {
|
||||
return nil, errors.New(`both "sslcert" and "sslkey" are required`)
|
||||
}
|
||||
@@ -754,9 +709,6 @@ func configTLS(settings map[string]string, thisHost string, parseConfigOptions P
|
||||
return nil, fmt.Errorf("unable to read sslkey: %w", err)
|
||||
}
|
||||
block, _ := pem.Decode(buf)
|
||||
if block == nil {
|
||||
return nil, errors.New("failed to decode sslkey")
|
||||
}
|
||||
var pemKey []byte
|
||||
var decryptedKey []byte
|
||||
var decryptedError error
|
||||
@@ -833,8 +785,7 @@ func parsePort(s string) (uint16, error) {
|
||||
}
|
||||
|
||||
func makeDefaultDialer() *net.Dialer {
|
||||
// rely on GOLANG KeepAlive settings
|
||||
return &net.Dialer{}
|
||||
return &net.Dialer{KeepAlive: 5 * time.Minute}
|
||||
}
|
||||
|
||||
func makeDefaultResolver() *net.Resolver {
|
||||
@@ -858,7 +809,7 @@ func makeConnectTimeoutDialFunc(timeout time.Duration) DialFunc {
|
||||
return d.DialContext
|
||||
}
|
||||
|
||||
// ValidateConnectTargetSessionAttrsReadWrite is a ValidateConnectFunc that implements libpq compatible
|
||||
// ValidateConnectTargetSessionAttrsReadWrite is an ValidateConnectFunc that implements libpq compatible
|
||||
// target_session_attrs=read-write.
|
||||
func ValidateConnectTargetSessionAttrsReadWrite(ctx context.Context, pgConn *PgConn) error {
|
||||
result := pgConn.ExecParams(ctx, "show transaction_read_only", nil, nil, nil, nil).Read()
|
||||
@@ -873,7 +824,7 @@ func ValidateConnectTargetSessionAttrsReadWrite(ctx context.Context, pgConn *PgC
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateConnectTargetSessionAttrsReadOnly is a ValidateConnectFunc that implements libpq compatible
|
||||
// ValidateConnectTargetSessionAttrsReadOnly is an ValidateConnectFunc that implements libpq compatible
|
||||
// target_session_attrs=read-only.
|
||||
func ValidateConnectTargetSessionAttrsReadOnly(ctx context.Context, pgConn *PgConn) error {
|
||||
result := pgConn.ExecParams(ctx, "show transaction_read_only", nil, nil, nil, nil).Read()
|
||||
@@ -888,7 +839,7 @@ func ValidateConnectTargetSessionAttrsReadOnly(ctx context.Context, pgConn *PgCo
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateConnectTargetSessionAttrsStandby is a ValidateConnectFunc that implements libpq compatible
|
||||
// ValidateConnectTargetSessionAttrsStandby is an ValidateConnectFunc that implements libpq compatible
|
||||
// target_session_attrs=standby.
|
||||
func ValidateConnectTargetSessionAttrsStandby(ctx context.Context, pgConn *PgConn) error {
|
||||
result := pgConn.ExecParams(ctx, "select pg_is_in_recovery()", nil, nil, nil, nil).Read()
|
||||
@@ -903,7 +854,7 @@ func ValidateConnectTargetSessionAttrsStandby(ctx context.Context, pgConn *PgCon
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateConnectTargetSessionAttrsPrimary is a ValidateConnectFunc that implements libpq compatible
|
||||
// ValidateConnectTargetSessionAttrsPrimary is an ValidateConnectFunc that implements libpq compatible
|
||||
// target_session_attrs=primary.
|
||||
func ValidateConnectTargetSessionAttrsPrimary(ctx context.Context, pgConn *PgConn) error {
|
||||
result := pgConn.ExecParams(ctx, "select pg_is_in_recovery()", nil, nil, nil, nil).Read()
|
||||
@@ -918,7 +869,7 @@ func ValidateConnectTargetSessionAttrsPrimary(ctx context.Context, pgConn *PgCon
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateConnectTargetSessionAttrsPreferStandby is a ValidateConnectFunc that implements libpq compatible
|
||||
// ValidateConnectTargetSessionAttrsPreferStandby is an ValidateConnectFunc that implements libpq compatible
|
||||
// target_session_attrs=prefer-standby.
|
||||
func ValidateConnectTargetSessionAttrsPreferStandby(ctx context.Context, pgConn *PgConn) error {
|
||||
result := pgConn.ExecParams(ctx, "select pg_is_in_recovery()", nil, nil, nil, nil).Read()
|
||||
|
||||
16
vendor/github.com/jackc/pgx/v5/pgconn/doc.go
generated
vendored
16
vendor/github.com/jackc/pgx/v5/pgconn/doc.go
generated
vendored
@@ -5,8 +5,8 @@ nearly the same level is the C library libpq.
|
||||
|
||||
Establishing a Connection
|
||||
|
||||
Use Connect to establish a connection. It accepts a connection string in URL or keyword/value format and will read the
|
||||
environment for libpq style environment variables.
|
||||
Use Connect to establish a connection. It accepts a connection string in URL or DSN and will read the environment for
|
||||
libpq style environment variables.
|
||||
|
||||
Executing a Query
|
||||
|
||||
@@ -20,17 +20,13 @@ result. The ReadAll method reads all query results into memory.
|
||||
|
||||
Pipeline Mode
|
||||
|
||||
Pipeline mode allows sending queries without having read the results of previously sent queries. It allows control of
|
||||
exactly how many and when network round trips occur.
|
||||
Pipeline mode allows sending queries without having read the results of previously sent queries. It allows
|
||||
control of exactly how many and when network round trips occur.
|
||||
|
||||
Context Support
|
||||
|
||||
All potentially blocking operations take a context.Context. The default behavior when a context is canceled is for the
|
||||
method to immediately return. In most circumstances, this will also close the underlying connection. This behavior can
|
||||
be customized by using BuildContextWatcherHandler on the Config to create a ctxwatch.Handler with different behavior.
|
||||
This can be especially useful when queries that are frequently canceled and the overhead of creating new connections is
|
||||
a problem. DeadlineContextWatcherHandler and CancelRequestContextWatcherHandler can be used to introduce a delay before
|
||||
interrupting the query in such a way as to close the connection.
|
||||
All potentially blocking operations take a context.Context. If a context is canceled while the method is in progress the
|
||||
method immediately returns. In most circumstances, this will close the underlying connection.
|
||||
|
||||
The CancelRequest method may be used to request the PostgreSQL server cancel an in-progress query without forcing the
|
||||
client to abort.
|
||||
|
||||
104
vendor/github.com/jackc/pgx/v5/pgconn/errors.go
generated
vendored
104
vendor/github.com/jackc/pgx/v5/pgconn/errors.go
generated
vendored
@@ -12,14 +12,13 @@ import (
|
||||
|
||||
// SafeToRetry checks if the err is guaranteed to have occurred before sending any data to the server.
|
||||
func SafeToRetry(err error) bool {
|
||||
var retryableErr interface{ SafeToRetry() bool }
|
||||
if errors.As(err, &retryableErr) {
|
||||
return retryableErr.SafeToRetry()
|
||||
if e, ok := err.(interface{ SafeToRetry() bool }); ok {
|
||||
return e.SafeToRetry()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Timeout checks if err was caused by a timeout. To be specific, it is true if err was caused within pgconn by a
|
||||
// Timeout checks if err was was caused by a timeout. To be specific, it is true if err was caused within pgconn by a
|
||||
// context.DeadlineExceeded or an implementer of net.Error where Timeout() is true.
|
||||
func Timeout(err error) bool {
|
||||
var timeoutErr *errTimeout
|
||||
@@ -30,24 +29,23 @@ func Timeout(err error) bool {
|
||||
// http://www.postgresql.org/docs/11/static/protocol-error-fields.html for
|
||||
// detailed field description.
|
||||
type PgError struct {
|
||||
Severity string
|
||||
SeverityUnlocalized string
|
||||
Code string
|
||||
Message string
|
||||
Detail string
|
||||
Hint string
|
||||
Position int32
|
||||
InternalPosition int32
|
||||
InternalQuery string
|
||||
Where string
|
||||
SchemaName string
|
||||
TableName string
|
||||
ColumnName string
|
||||
DataTypeName string
|
||||
ConstraintName string
|
||||
File string
|
||||
Line int32
|
||||
Routine string
|
||||
Severity string
|
||||
Code string
|
||||
Message string
|
||||
Detail string
|
||||
Hint string
|
||||
Position int32
|
||||
InternalPosition int32
|
||||
InternalQuery string
|
||||
Where string
|
||||
SchemaName string
|
||||
TableName string
|
||||
ColumnName string
|
||||
DataTypeName string
|
||||
ConstraintName string
|
||||
File string
|
||||
Line int32
|
||||
Routine string
|
||||
}
|
||||
|
||||
func (pe *PgError) Error() string {
|
||||
@@ -59,37 +57,22 @@ func (pe *PgError) SQLState() string {
|
||||
return pe.Code
|
||||
}
|
||||
|
||||
// ConnectError is the error returned when a connection attempt fails.
|
||||
type ConnectError struct {
|
||||
Config *Config // The configuration that was used in the connection attempt.
|
||||
type connectError struct {
|
||||
config *Config
|
||||
msg string
|
||||
err error
|
||||
}
|
||||
|
||||
func (e *ConnectError) Error() string {
|
||||
prefix := fmt.Sprintf("failed to connect to `user=%s database=%s`:", e.Config.User, e.Config.Database)
|
||||
details := e.err.Error()
|
||||
if strings.Contains(details, "\n") {
|
||||
return prefix + "\n\t" + strings.ReplaceAll(details, "\n", "\n\t")
|
||||
} else {
|
||||
return prefix + " " + details
|
||||
func (e *connectError) Error() string {
|
||||
sb := &strings.Builder{}
|
||||
fmt.Fprintf(sb, "failed to connect to `host=%s user=%s database=%s`: %s", e.config.Host, e.config.User, e.config.Database, e.msg)
|
||||
if e.err != nil {
|
||||
fmt.Fprintf(sb, " (%s)", e.err.Error())
|
||||
}
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
func (e *ConnectError) Unwrap() error {
|
||||
return e.err
|
||||
}
|
||||
|
||||
type perDialConnectError struct {
|
||||
address string
|
||||
originalHostname string
|
||||
err error
|
||||
}
|
||||
|
||||
func (e *perDialConnectError) Error() string {
|
||||
return fmt.Sprintf("%s (%s): %s", e.address, e.originalHostname, e.err.Error())
|
||||
}
|
||||
|
||||
func (e *perDialConnectError) Unwrap() error {
|
||||
func (e *connectError) Unwrap() error {
|
||||
return e.err
|
||||
}
|
||||
|
||||
@@ -105,38 +88,33 @@ func (e *connLockError) Error() string {
|
||||
return e.status
|
||||
}
|
||||
|
||||
// ParseConfigError is the error returned when a connection string cannot be parsed.
|
||||
type ParseConfigError struct {
|
||||
ConnString string // The connection string that could not be parsed.
|
||||
type parseConfigError struct {
|
||||
connString string
|
||||
msg string
|
||||
err error
|
||||
}
|
||||
|
||||
func (e *ParseConfigError) Error() string {
|
||||
// Now that ParseConfigError is public and ConnString is available to the developer, perhaps it would be better only
|
||||
// return a static string. That would ensure that the error message cannot leak a password. The ConnString field would
|
||||
// allow access to the original string if desired and Unwrap would allow access to the underlying error.
|
||||
connString := redactPW(e.ConnString)
|
||||
func (e *parseConfigError) Error() string {
|
||||
connString := redactPW(e.connString)
|
||||
if e.err == nil {
|
||||
return fmt.Sprintf("cannot parse `%s`: %s", connString, e.msg)
|
||||
}
|
||||
return fmt.Sprintf("cannot parse `%s`: %s (%s)", connString, e.msg, e.err.Error())
|
||||
}
|
||||
|
||||
func (e *ParseConfigError) Unwrap() error {
|
||||
func (e *parseConfigError) Unwrap() error {
|
||||
return e.err
|
||||
}
|
||||
|
||||
func normalizeTimeoutError(ctx context.Context, err error) error {
|
||||
var netErr net.Error
|
||||
if errors.As(err, &netErr) && netErr.Timeout() {
|
||||
if err, ok := err.(net.Error); ok && err.Timeout() {
|
||||
if ctx.Err() == context.Canceled {
|
||||
// Since the timeout was caused by a context cancellation, the actual error is context.Canceled not the timeout error.
|
||||
return context.Canceled
|
||||
} else if ctx.Err() == context.DeadlineExceeded {
|
||||
return &errTimeout{err: ctx.Err()}
|
||||
} else {
|
||||
return &errTimeout{err: netErr}
|
||||
return &errTimeout{err: err}
|
||||
}
|
||||
}
|
||||
return err
|
||||
@@ -211,10 +189,10 @@ func redactPW(connString string) string {
|
||||
return redactURL(u)
|
||||
}
|
||||
}
|
||||
quotedKV := regexp.MustCompile(`password='[^']*'`)
|
||||
connString = quotedKV.ReplaceAllLiteralString(connString, "password=xxxxx")
|
||||
plainKV := regexp.MustCompile(`password=[^ ]*`)
|
||||
connString = plainKV.ReplaceAllLiteralString(connString, "password=xxxxx")
|
||||
quotedDSN := regexp.MustCompile(`password='[^']*'`)
|
||||
connString = quotedDSN.ReplaceAllLiteralString(connString, "password=xxxxx")
|
||||
plainDSN := regexp.MustCompile(`password=[^ ]*`)
|
||||
connString = plainDSN.ReplaceAllLiteralString(connString, "password=xxxxx")
|
||||
brokenURL := regexp.MustCompile(`:[^:@]+?@`)
|
||||
connString = brokenURL.ReplaceAllLiteralString(connString, ":xxxxxx@")
|
||||
return connString
|
||||
|
||||
604
vendor/github.com/jackc/pgx/v5/pgconn/pgconn.go
generated
vendored
604
vendor/github.com/jackc/pgx/v5/pgconn/pgconn.go
generated
vendored
@@ -18,8 +18,8 @@ import (
|
||||
|
||||
"github.com/jackc/pgx/v5/internal/iobufpool"
|
||||
"github.com/jackc/pgx/v5/internal/pgio"
|
||||
"github.com/jackc/pgx/v5/pgconn/ctxwatch"
|
||||
"github.com/jackc/pgx/v5/pgconn/internal/bgreader"
|
||||
"github.com/jackc/pgx/v5/pgconn/internal/ctxwatch"
|
||||
"github.com/jackc/pgx/v5/pgproto3"
|
||||
)
|
||||
|
||||
@@ -52,12 +52,6 @@ type LookupFunc func(ctx context.Context, host string) (addrs []string, err erro
|
||||
// BuildFrontendFunc is a function that can be used to create Frontend implementation for connection.
|
||||
type BuildFrontendFunc func(r io.Reader, w io.Writer) *pgproto3.Frontend
|
||||
|
||||
// PgErrorHandler is a function that handles errors returned from Postgres. This function must return true to keep
|
||||
// the connection open. Returning false will cause the connection to be closed immediately. You should return
|
||||
// false on any FATAL-severity errors. This will not receive network errors. The *PgConn is provided so the handler is
|
||||
// aware of the origin of the error, but it must not invoke any query method.
|
||||
type PgErrorHandler func(*PgConn, *PgError) bool
|
||||
|
||||
// NoticeHandler is a function that can handle notices received from the PostgreSQL server. Notices can be received at
|
||||
// any time, usually during handling of a query response. The *PgConn is provided so the handler is aware of the origin
|
||||
// of the notice, but it must not invoke any query method. Be aware that this is distinct from LISTEN/NOTIFY
|
||||
@@ -80,9 +74,6 @@ type PgConn struct {
|
||||
frontend *pgproto3.Frontend
|
||||
bgReader *bgreader.BGReader
|
||||
slowWriteTimer *time.Timer
|
||||
bgReaderStarted chan struct{}
|
||||
|
||||
customData map[string]any
|
||||
|
||||
config *Config
|
||||
|
||||
@@ -105,9 +96,8 @@ type PgConn struct {
|
||||
cleanupDone chan struct{}
|
||||
}
|
||||
|
||||
// Connect establishes a connection to a PostgreSQL server using the environment and connString (in URL or keyword/value
|
||||
// format) to provide configuration. See documentation for [ParseConfig] for details. ctx can be used to cancel a
|
||||
// connect attempt.
|
||||
// Connect establishes a connection to a PostgreSQL server using the environment and connString (in URL or DSN format)
|
||||
// to provide configuration. See documentation for [ParseConfig] for details. ctx can be used to cancel a connect attempt.
|
||||
func Connect(ctx context.Context, connString string) (*PgConn, error) {
|
||||
config, err := ParseConfig(connString)
|
||||
if err != nil {
|
||||
@@ -117,9 +107,9 @@ func Connect(ctx context.Context, connString string) (*PgConn, error) {
|
||||
return ConnectConfig(ctx, config)
|
||||
}
|
||||
|
||||
// Connect establishes a connection to a PostgreSQL server using the environment and connString (in URL or keyword/value
|
||||
// format) and ParseConfigOptions to provide additional configuration. See documentation for [ParseConfig] for details.
|
||||
// ctx can be used to cancel a connect attempt.
|
||||
// Connect establishes a connection to a PostgreSQL server using the environment and connString (in URL or DSN format)
|
||||
// and ParseConfigOptions to provide additional configuration. See documentation for [ParseConfig] for details. ctx can be
|
||||
// used to cancel a connect attempt.
|
||||
func ConnectWithOptions(ctx context.Context, connString string, parseConfigOptions ParseConfigOptions) (*PgConn, error) {
|
||||
config, err := ParseConfigWithOptions(connString, parseConfigOptions)
|
||||
if err != nil {
|
||||
@@ -134,46 +124,15 @@ func ConnectWithOptions(ctx context.Context, connString string, parseConfigOptio
|
||||
//
|
||||
// If config.Fallbacks are present they will sequentially be tried in case of error establishing network connection. An
|
||||
// authentication error will terminate the chain of attempts (like libpq:
|
||||
// https://www.postgresql.org/docs/11/libpq-connect.html#LIBPQ-MULTIPLE-HOSTS) and be returned as the error.
|
||||
func ConnectConfig(ctx context.Context, config *Config) (*PgConn, error) {
|
||||
// https://www.postgresql.org/docs/11/libpq-connect.html#LIBPQ-MULTIPLE-HOSTS) and be returned as the error. Otherwise,
|
||||
// if all attempts fail the last error is returned.
|
||||
func ConnectConfig(octx context.Context, config *Config) (pgConn *PgConn, err error) {
|
||||
// Default values are set in ParseConfig. Enforce initial creation by ParseConfig rather than setting defaults from
|
||||
// zero values.
|
||||
if !config.createdByParseConfig {
|
||||
panic("config must be created by ParseConfig")
|
||||
}
|
||||
|
||||
var allErrors []error
|
||||
|
||||
connectConfigs, errs := buildConnectOneConfigs(ctx, config)
|
||||
if len(errs) > 0 {
|
||||
allErrors = append(allErrors, errs...)
|
||||
}
|
||||
|
||||
if len(connectConfigs) == 0 {
|
||||
return nil, &ConnectError{Config: config, err: fmt.Errorf("hostname resolving error: %w", errors.Join(allErrors...))}
|
||||
}
|
||||
|
||||
pgConn, errs := connectPreferred(ctx, config, connectConfigs)
|
||||
if len(errs) > 0 {
|
||||
allErrors = append(allErrors, errs...)
|
||||
return nil, &ConnectError{Config: config, err: errors.Join(allErrors...)}
|
||||
}
|
||||
|
||||
if config.AfterConnect != nil {
|
||||
err := config.AfterConnect(ctx, pgConn)
|
||||
if err != nil {
|
||||
pgConn.conn.Close()
|
||||
return nil, &ConnectError{Config: config, err: fmt.Errorf("AfterConnect error: %w", err)}
|
||||
}
|
||||
}
|
||||
|
||||
return pgConn, nil
|
||||
}
|
||||
|
||||
// buildConnectOneConfigs resolves hostnames and builds a list of connectOneConfigs to try connecting to. It returns a
|
||||
// slice of successfully resolved connectOneConfigs and a slice of errors. It is possible for both slices to contain
|
||||
// values if some hosts were successfully resolved and others were not.
|
||||
func buildConnectOneConfigs(ctx context.Context, config *Config) ([]*connectOneConfig, []error) {
|
||||
// Simplify usage by treating primary config and fallbacks the same.
|
||||
fallbackConfigs := []*FallbackConfig{
|
||||
{
|
||||
@@ -183,28 +142,95 @@ func buildConnectOneConfigs(ctx context.Context, config *Config) ([]*connectOneC
|
||||
},
|
||||
}
|
||||
fallbackConfigs = append(fallbackConfigs, config.Fallbacks...)
|
||||
ctx := octx
|
||||
fallbackConfigs, err = expandWithIPs(ctx, config.LookupFunc, fallbackConfigs)
|
||||
if err != nil {
|
||||
return nil, &connectError{config: config, msg: "hostname resolving error", err: err}
|
||||
}
|
||||
|
||||
var configs []*connectOneConfig
|
||||
if len(fallbackConfigs) == 0 {
|
||||
return nil, &connectError{config: config, msg: "hostname resolving error", err: errors.New("ip addr wasn't found")}
|
||||
}
|
||||
|
||||
var allErrors []error
|
||||
foundBestServer := false
|
||||
var fallbackConfig *FallbackConfig
|
||||
for i, fc := range fallbackConfigs {
|
||||
// ConnectTimeout restricts the whole connection process.
|
||||
if config.ConnectTimeout != 0 {
|
||||
// create new context first time or when previous host was different
|
||||
if i == 0 || (fallbackConfigs[i].Host != fallbackConfigs[i-1].Host) {
|
||||
var cancel context.CancelFunc
|
||||
ctx, cancel = context.WithTimeout(octx, config.ConnectTimeout)
|
||||
defer cancel()
|
||||
}
|
||||
} else {
|
||||
ctx = octx
|
||||
}
|
||||
pgConn, err = connect(ctx, config, fc, false)
|
||||
if err == nil {
|
||||
foundBestServer = true
|
||||
break
|
||||
} else if pgerr, ok := err.(*PgError); ok {
|
||||
err = &connectError{config: config, msg: "server error", err: pgerr}
|
||||
const ERRCODE_INVALID_PASSWORD = "28P01" // wrong password
|
||||
const ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION = "28000" // wrong password or bad pg_hba.conf settings
|
||||
const ERRCODE_INVALID_CATALOG_NAME = "3D000" // db does not exist
|
||||
const ERRCODE_INSUFFICIENT_PRIVILEGE = "42501" // missing connect privilege
|
||||
if pgerr.Code == ERRCODE_INVALID_PASSWORD ||
|
||||
pgerr.Code == ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION && fc.TLSConfig != nil ||
|
||||
pgerr.Code == ERRCODE_INVALID_CATALOG_NAME ||
|
||||
pgerr.Code == ERRCODE_INSUFFICIENT_PRIVILEGE {
|
||||
break
|
||||
}
|
||||
} else if cerr, ok := err.(*connectError); ok {
|
||||
if _, ok := cerr.err.(*NotPreferredError); ok {
|
||||
fallbackConfig = fc
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, fb := range fallbackConfigs {
|
||||
if !foundBestServer && fallbackConfig != nil {
|
||||
pgConn, err = connect(ctx, config, fallbackConfig, true)
|
||||
if pgerr, ok := err.(*PgError); ok {
|
||||
err = &connectError{config: config, msg: "server error", err: pgerr}
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err // no need to wrap in connectError because it will already be wrapped in all cases except PgError
|
||||
}
|
||||
|
||||
if config.AfterConnect != nil {
|
||||
err := config.AfterConnect(ctx, pgConn)
|
||||
if err != nil {
|
||||
pgConn.conn.Close()
|
||||
return nil, &connectError{config: config, msg: "AfterConnect error", err: err}
|
||||
}
|
||||
}
|
||||
|
||||
return pgConn, nil
|
||||
}
|
||||
|
||||
func expandWithIPs(ctx context.Context, lookupFn LookupFunc, fallbacks []*FallbackConfig) ([]*FallbackConfig, error) {
|
||||
var configs []*FallbackConfig
|
||||
|
||||
var lookupErrors []error
|
||||
|
||||
for _, fb := range fallbacks {
|
||||
// skip resolve for unix sockets
|
||||
if isAbsolutePath(fb.Host) {
|
||||
network, address := NetworkAddress(fb.Host, fb.Port)
|
||||
configs = append(configs, &connectOneConfig{
|
||||
network: network,
|
||||
address: address,
|
||||
originalHostname: fb.Host,
|
||||
tlsConfig: fb.TLSConfig,
|
||||
configs = append(configs, &FallbackConfig{
|
||||
Host: fb.Host,
|
||||
Port: fb.Port,
|
||||
TLSConfig: fb.TLSConfig,
|
||||
})
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
ips, err := config.LookupFunc(ctx, fb.Host)
|
||||
ips, err := lookupFn(ctx, fb.Host)
|
||||
if err != nil {
|
||||
allErrors = append(allErrors, err)
|
||||
lookupErrors = append(lookupErrors, err)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -213,139 +239,70 @@ func buildConnectOneConfigs(ctx context.Context, config *Config) ([]*connectOneC
|
||||
if err == nil {
|
||||
port, err := strconv.ParseUint(splitPort, 10, 16)
|
||||
if err != nil {
|
||||
return nil, []error{fmt.Errorf("error parsing port (%s) from lookup: %w", splitPort, err)}
|
||||
return nil, fmt.Errorf("error parsing port (%s) from lookup: %w", splitPort, err)
|
||||
}
|
||||
network, address := NetworkAddress(splitIP, uint16(port))
|
||||
configs = append(configs, &connectOneConfig{
|
||||
network: network,
|
||||
address: address,
|
||||
originalHostname: fb.Host,
|
||||
tlsConfig: fb.TLSConfig,
|
||||
configs = append(configs, &FallbackConfig{
|
||||
Host: splitIP,
|
||||
Port: uint16(port),
|
||||
TLSConfig: fb.TLSConfig,
|
||||
})
|
||||
} else {
|
||||
network, address := NetworkAddress(ip, fb.Port)
|
||||
configs = append(configs, &connectOneConfig{
|
||||
network: network,
|
||||
address: address,
|
||||
originalHostname: fb.Host,
|
||||
tlsConfig: fb.TLSConfig,
|
||||
configs = append(configs, &FallbackConfig{
|
||||
Host: ip,
|
||||
Port: fb.Port,
|
||||
TLSConfig: fb.TLSConfig,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return configs, allErrors
|
||||
}
|
||||
|
||||
// connectPreferred attempts to connect to the preferred host from connectOneConfigs. The connections are attempted in
|
||||
// order. If a connection is successful it is returned. If no connection is successful then all errors are returned. If
|
||||
// a connection attempt returns a [NotPreferredError], then that host will be used if no other hosts are successful.
|
||||
func connectPreferred(ctx context.Context, config *Config, connectOneConfigs []*connectOneConfig) (*PgConn, []error) {
|
||||
octx := ctx
|
||||
var allErrors []error
|
||||
|
||||
var fallbackConnectOneConfig *connectOneConfig
|
||||
for i, c := range connectOneConfigs {
|
||||
// ConnectTimeout restricts the whole connection process.
|
||||
if config.ConnectTimeout != 0 {
|
||||
// create new context first time or when previous host was different
|
||||
if i == 0 || (connectOneConfigs[i].address != connectOneConfigs[i-1].address) {
|
||||
var cancel context.CancelFunc
|
||||
ctx, cancel = context.WithTimeout(octx, config.ConnectTimeout)
|
||||
defer cancel()
|
||||
}
|
||||
} else {
|
||||
ctx = octx
|
||||
}
|
||||
|
||||
pgConn, err := connectOne(ctx, config, c, false)
|
||||
if pgConn != nil {
|
||||
return pgConn, nil
|
||||
}
|
||||
|
||||
allErrors = append(allErrors, err)
|
||||
|
||||
var pgErr *PgError
|
||||
if errors.As(err, &pgErr) {
|
||||
const ERRCODE_INVALID_PASSWORD = "28P01" // wrong password
|
||||
const ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION = "28000" // wrong password or bad pg_hba.conf settings
|
||||
const ERRCODE_INVALID_CATALOG_NAME = "3D000" // db does not exist
|
||||
const ERRCODE_INSUFFICIENT_PRIVILEGE = "42501" // missing connect privilege
|
||||
if pgErr.Code == ERRCODE_INVALID_PASSWORD ||
|
||||
pgErr.Code == ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION && c.tlsConfig != nil ||
|
||||
pgErr.Code == ERRCODE_INVALID_CATALOG_NAME ||
|
||||
pgErr.Code == ERRCODE_INSUFFICIENT_PRIVILEGE {
|
||||
return nil, allErrors
|
||||
}
|
||||
}
|
||||
|
||||
var npErr *NotPreferredError
|
||||
if errors.As(err, &npErr) {
|
||||
fallbackConnectOneConfig = c
|
||||
}
|
||||
// See https://github.com/jackc/pgx/issues/1464. When Go 1.20 can be used in pgx consider using errors.Join so all
|
||||
// errors are reported.
|
||||
if len(configs) == 0 && len(lookupErrors) > 0 {
|
||||
return nil, lookupErrors[0]
|
||||
}
|
||||
|
||||
if fallbackConnectOneConfig != nil {
|
||||
pgConn, err := connectOne(ctx, config, fallbackConnectOneConfig, true)
|
||||
if err == nil {
|
||||
return pgConn, nil
|
||||
}
|
||||
allErrors = append(allErrors, err)
|
||||
}
|
||||
|
||||
return nil, allErrors
|
||||
return configs, nil
|
||||
}
|
||||
|
||||
// connectOne makes one connection attempt to a single host.
|
||||
func connectOne(ctx context.Context, config *Config, connectConfig *connectOneConfig,
|
||||
func connect(ctx context.Context, config *Config, fallbackConfig *FallbackConfig,
|
||||
ignoreNotPreferredErr bool,
|
||||
) (*PgConn, error) {
|
||||
pgConn := new(PgConn)
|
||||
pgConn.config = config
|
||||
pgConn.cleanupDone = make(chan struct{})
|
||||
pgConn.customData = make(map[string]any)
|
||||
|
||||
var err error
|
||||
|
||||
newPerDialConnectError := func(msg string, err error) *perDialConnectError {
|
||||
err = normalizeTimeoutError(ctx, err)
|
||||
e := &perDialConnectError{address: connectConfig.address, originalHostname: connectConfig.originalHostname, err: fmt.Errorf("%s: %w", msg, err)}
|
||||
return e
|
||||
}
|
||||
|
||||
pgConn.conn, err = config.DialFunc(ctx, connectConfig.network, connectConfig.address)
|
||||
network, address := NetworkAddress(fallbackConfig.Host, fallbackConfig.Port)
|
||||
netConn, err := config.DialFunc(ctx, network, address)
|
||||
if err != nil {
|
||||
return nil, newPerDialConnectError("dial error", err)
|
||||
return nil, &connectError{config: config, msg: "dial error", err: normalizeTimeoutError(ctx, err)}
|
||||
}
|
||||
|
||||
if connectConfig.tlsConfig != nil {
|
||||
pgConn.contextWatcher = ctxwatch.NewContextWatcher(&DeadlineContextWatcherHandler{Conn: pgConn.conn})
|
||||
pgConn.contextWatcher.Watch(ctx)
|
||||
tlsConn, err := startTLS(pgConn.conn, connectConfig.tlsConfig)
|
||||
pgConn.conn = netConn
|
||||
pgConn.contextWatcher = newContextWatcher(netConn)
|
||||
pgConn.contextWatcher.Watch(ctx)
|
||||
|
||||
if fallbackConfig.TLSConfig != nil {
|
||||
nbTLSConn, err := startTLS(netConn, fallbackConfig.TLSConfig)
|
||||
pgConn.contextWatcher.Unwatch() // Always unwatch `netConn` after TLS.
|
||||
if err != nil {
|
||||
pgConn.conn.Close()
|
||||
return nil, newPerDialConnectError("tls error", err)
|
||||
netConn.Close()
|
||||
return nil, &connectError{config: config, msg: "tls error", err: err}
|
||||
}
|
||||
|
||||
pgConn.conn = tlsConn
|
||||
pgConn.conn = nbTLSConn
|
||||
pgConn.contextWatcher = newContextWatcher(nbTLSConn)
|
||||
pgConn.contextWatcher.Watch(ctx)
|
||||
}
|
||||
|
||||
pgConn.contextWatcher = ctxwatch.NewContextWatcher(config.BuildContextWatcherHandler(pgConn))
|
||||
pgConn.contextWatcher.Watch(ctx)
|
||||
defer pgConn.contextWatcher.Unwatch()
|
||||
|
||||
pgConn.parameterStatuses = make(map[string]string)
|
||||
pgConn.status = connStatusConnecting
|
||||
pgConn.bgReader = bgreader.New(pgConn.conn)
|
||||
pgConn.slowWriteTimer = time.AfterFunc(time.Duration(math.MaxInt64),
|
||||
func() {
|
||||
pgConn.bgReader.Start()
|
||||
pgConn.bgReaderStarted <- struct{}{}
|
||||
},
|
||||
)
|
||||
pgConn.slowWriteTimer = time.AfterFunc(time.Duration(math.MaxInt64), pgConn.bgReader.Start)
|
||||
pgConn.slowWriteTimer.Stop()
|
||||
pgConn.bgReaderStarted = make(chan struct{})
|
||||
pgConn.frontend = config.BuildFrontend(pgConn.bgReader, pgConn.conn)
|
||||
|
||||
startupMsg := pgproto3.StartupMessage{
|
||||
@@ -366,7 +323,7 @@ func connectOne(ctx context.Context, config *Config, connectConfig *connectOneCo
|
||||
pgConn.frontend.Send(&startupMsg)
|
||||
if err := pgConn.flushWithPotentialWriteReadDeadlock(); err != nil {
|
||||
pgConn.conn.Close()
|
||||
return nil, newPerDialConnectError("failed to write startup message", err)
|
||||
return nil, &connectError{config: config, msg: "failed to write startup message", err: normalizeTimeoutError(ctx, err)}
|
||||
}
|
||||
|
||||
for {
|
||||
@@ -374,9 +331,9 @@ func connectOne(ctx context.Context, config *Config, connectConfig *connectOneCo
|
||||
if err != nil {
|
||||
pgConn.conn.Close()
|
||||
if err, ok := err.(*PgError); ok {
|
||||
return nil, newPerDialConnectError("server error", err)
|
||||
return nil, err
|
||||
}
|
||||
return nil, newPerDialConnectError("failed to receive message", err)
|
||||
return nil, &connectError{config: config, msg: "failed to receive message", err: normalizeTimeoutError(ctx, err)}
|
||||
}
|
||||
|
||||
switch msg := msg.(type) {
|
||||
@@ -389,26 +346,26 @@ func connectOne(ctx context.Context, config *Config, connectConfig *connectOneCo
|
||||
err = pgConn.txPasswordMessage(pgConn.config.Password)
|
||||
if err != nil {
|
||||
pgConn.conn.Close()
|
||||
return nil, newPerDialConnectError("failed to write password message", err)
|
||||
return nil, &connectError{config: config, msg: "failed to write password message", err: err}
|
||||
}
|
||||
case *pgproto3.AuthenticationMD5Password:
|
||||
digestedPassword := "md5" + hexMD5(hexMD5(pgConn.config.Password+pgConn.config.User)+string(msg.Salt[:]))
|
||||
err = pgConn.txPasswordMessage(digestedPassword)
|
||||
if err != nil {
|
||||
pgConn.conn.Close()
|
||||
return nil, newPerDialConnectError("failed to write password message", err)
|
||||
return nil, &connectError{config: config, msg: "failed to write password message", err: err}
|
||||
}
|
||||
case *pgproto3.AuthenticationSASL:
|
||||
err = pgConn.scramAuth(msg.AuthMechanisms)
|
||||
if err != nil {
|
||||
pgConn.conn.Close()
|
||||
return nil, newPerDialConnectError("failed SASL auth", err)
|
||||
return nil, &connectError{config: config, msg: "failed SASL auth", err: err}
|
||||
}
|
||||
case *pgproto3.AuthenticationGSS:
|
||||
err = pgConn.gssAuth()
|
||||
if err != nil {
|
||||
pgConn.conn.Close()
|
||||
return nil, newPerDialConnectError("failed GSS auth", err)
|
||||
return nil, &connectError{config: config, msg: "failed GSS auth", err: err}
|
||||
}
|
||||
case *pgproto3.ReadyForQuery:
|
||||
pgConn.status = connStatusIdle
|
||||
@@ -426,7 +383,7 @@ func connectOne(ctx context.Context, config *Config, connectConfig *connectOneCo
|
||||
return pgConn, nil
|
||||
}
|
||||
pgConn.conn.Close()
|
||||
return nil, newPerDialConnectError("ValidateConnect failed", err)
|
||||
return nil, &connectError{config: config, msg: "ValidateConnect failed", err: err}
|
||||
}
|
||||
}
|
||||
return pgConn, nil
|
||||
@@ -434,14 +391,21 @@ func connectOne(ctx context.Context, config *Config, connectConfig *connectOneCo
|
||||
// handled by ReceiveMessage
|
||||
case *pgproto3.ErrorResponse:
|
||||
pgConn.conn.Close()
|
||||
return nil, newPerDialConnectError("server error", ErrorResponseToPgError(msg))
|
||||
return nil, ErrorResponseToPgError(msg)
|
||||
default:
|
||||
pgConn.conn.Close()
|
||||
return nil, newPerDialConnectError("received unexpected message", err)
|
||||
return nil, &connectError{config: config, msg: "received unexpected message", err: err}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func newContextWatcher(conn net.Conn) *ctxwatch.ContextWatcher {
|
||||
return ctxwatch.NewContextWatcher(
|
||||
func() { conn.SetDeadline(time.Date(1, 1, 1, 1, 1, 1, 1, time.UTC)) },
|
||||
func() { conn.SetDeadline(time.Time{}) },
|
||||
)
|
||||
}
|
||||
|
||||
func startTLS(conn net.Conn, tlsConfig *tls.Config) (net.Conn, error) {
|
||||
err := binary.Write(conn, binary.BigEndian, []int32{8, 80877103})
|
||||
if err != nil {
|
||||
@@ -576,12 +540,11 @@ func (pgConn *PgConn) receiveMessage() (pgproto3.BackendMessage, error) {
|
||||
case *pgproto3.ParameterStatus:
|
||||
pgConn.parameterStatuses[msg.Name] = msg.Value
|
||||
case *pgproto3.ErrorResponse:
|
||||
err := ErrorResponseToPgError(msg)
|
||||
if pgConn.config.OnPgError != nil && !pgConn.config.OnPgError(pgConn, err) {
|
||||
if msg.Severity == "FATAL" {
|
||||
pgConn.status = connStatusClosed
|
||||
pgConn.conn.Close() // Ignore error as the connection is already broken and there is already an error to return.
|
||||
close(pgConn.cleanupDone)
|
||||
return nil, err
|
||||
return nil, ErrorResponseToPgError(msg)
|
||||
}
|
||||
case *pgproto3.NoticeResponse:
|
||||
if pgConn.config.OnNotice != nil {
|
||||
@@ -630,7 +593,7 @@ func (pgConn *PgConn) Frontend() *pgproto3.Frontend {
|
||||
return pgConn.frontend
|
||||
}
|
||||
|
||||
// Close closes a connection. It is safe to call Close on an already closed connection. Close attempts a clean close by
|
||||
// Close closes a connection. It is safe to call Close on a already closed connection. Close attempts a clean close by
|
||||
// sending the exit message to PostgreSQL. However, this could block so ctx is available to limit the time to wait. The
|
||||
// underlying net.Conn.Close() will always be called regardless of any other errors.
|
||||
func (pgConn *PgConn) Close(ctx context.Context) error {
|
||||
@@ -843,9 +806,6 @@ type StatementDescription struct {
|
||||
|
||||
// Prepare creates a prepared statement. If the name is empty, the anonymous prepared statement will be used. This
|
||||
// allows Prepare to also to describe statements without creating a server-side prepared statement.
|
||||
//
|
||||
// Prepare does not send a PREPARE statement to the server. It uses the PostgreSQL Parse and Describe protocol messages
|
||||
// directly.
|
||||
func (pgConn *PgConn) Prepare(ctx context.Context, name, sql string, paramOIDs []uint32) (*StatementDescription, error) {
|
||||
if err := pgConn.lock(); err != nil {
|
||||
return nil, err
|
||||
@@ -902,73 +862,26 @@ readloop:
|
||||
return psd, nil
|
||||
}
|
||||
|
||||
// Deallocate deallocates a prepared statement.
|
||||
//
|
||||
// Deallocate does not send a DEALLOCATE statement to the server. It uses the PostgreSQL Close protocol message
|
||||
// directly. This has slightly different behavior than executing DEALLOCATE statement.
|
||||
// - Deallocate can succeed in an aborted transaction.
|
||||
// - Deallocating a non-existent prepared statement is not an error.
|
||||
func (pgConn *PgConn) Deallocate(ctx context.Context, name string) error {
|
||||
if err := pgConn.lock(); err != nil {
|
||||
return err
|
||||
}
|
||||
defer pgConn.unlock()
|
||||
|
||||
if ctx != context.Background() {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return newContextAlreadyDoneError(ctx)
|
||||
default:
|
||||
}
|
||||
pgConn.contextWatcher.Watch(ctx)
|
||||
defer pgConn.contextWatcher.Unwatch()
|
||||
}
|
||||
|
||||
pgConn.frontend.SendClose(&pgproto3.Close{ObjectType: 'S', Name: name})
|
||||
pgConn.frontend.SendSync(&pgproto3.Sync{})
|
||||
err := pgConn.flushWithPotentialWriteReadDeadlock()
|
||||
if err != nil {
|
||||
pgConn.asyncClose()
|
||||
return err
|
||||
}
|
||||
|
||||
for {
|
||||
msg, err := pgConn.receiveMessage()
|
||||
if err != nil {
|
||||
pgConn.asyncClose()
|
||||
return normalizeTimeoutError(ctx, err)
|
||||
}
|
||||
|
||||
switch msg := msg.(type) {
|
||||
case *pgproto3.ErrorResponse:
|
||||
return ErrorResponseToPgError(msg)
|
||||
case *pgproto3.ReadyForQuery:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ErrorResponseToPgError converts a wire protocol error message to a *PgError.
|
||||
func ErrorResponseToPgError(msg *pgproto3.ErrorResponse) *PgError {
|
||||
return &PgError{
|
||||
Severity: msg.Severity,
|
||||
SeverityUnlocalized: msg.SeverityUnlocalized,
|
||||
Code: string(msg.Code),
|
||||
Message: string(msg.Message),
|
||||
Detail: string(msg.Detail),
|
||||
Hint: msg.Hint,
|
||||
Position: msg.Position,
|
||||
InternalPosition: msg.InternalPosition,
|
||||
InternalQuery: string(msg.InternalQuery),
|
||||
Where: string(msg.Where),
|
||||
SchemaName: string(msg.SchemaName),
|
||||
TableName: string(msg.TableName),
|
||||
ColumnName: string(msg.ColumnName),
|
||||
DataTypeName: string(msg.DataTypeName),
|
||||
ConstraintName: msg.ConstraintName,
|
||||
File: string(msg.File),
|
||||
Line: msg.Line,
|
||||
Routine: string(msg.Routine),
|
||||
Severity: msg.Severity,
|
||||
Code: string(msg.Code),
|
||||
Message: string(msg.Message),
|
||||
Detail: string(msg.Detail),
|
||||
Hint: msg.Hint,
|
||||
Position: msg.Position,
|
||||
InternalPosition: msg.InternalPosition,
|
||||
InternalQuery: string(msg.InternalQuery),
|
||||
Where: string(msg.Where),
|
||||
SchemaName: string(msg.SchemaName),
|
||||
TableName: string(msg.TableName),
|
||||
ColumnName: string(msg.ColumnName),
|
||||
DataTypeName: string(msg.DataTypeName),
|
||||
ConstraintName: msg.ConstraintName,
|
||||
File: string(msg.File),
|
||||
Line: msg.Line,
|
||||
Routine: string(msg.Routine),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1011,7 +924,10 @@ func (pgConn *PgConn) CancelRequest(ctx context.Context) error {
|
||||
defer cancelConn.Close()
|
||||
|
||||
if ctx != context.Background() {
|
||||
contextWatcher := ctxwatch.NewContextWatcher(&DeadlineContextWatcherHandler{Conn: cancelConn})
|
||||
contextWatcher := ctxwatch.NewContextWatcher(
|
||||
func() { cancelConn.SetDeadline(time.Date(1, 1, 1, 1, 1, 1, 1, time.UTC)) },
|
||||
func() { cancelConn.SetDeadline(time.Time{}) },
|
||||
)
|
||||
contextWatcher.Watch(ctx)
|
||||
defer contextWatcher.Unwatch()
|
||||
}
|
||||
@@ -1019,21 +935,16 @@ func (pgConn *PgConn) CancelRequest(ctx context.Context) error {
|
||||
buf := make([]byte, 16)
|
||||
binary.BigEndian.PutUint32(buf[0:4], 16)
|
||||
binary.BigEndian.PutUint32(buf[4:8], 80877102)
|
||||
binary.BigEndian.PutUint32(buf[8:12], pgConn.pid)
|
||||
binary.BigEndian.PutUint32(buf[12:16], pgConn.secretKey)
|
||||
|
||||
if _, err := cancelConn.Write(buf); err != nil {
|
||||
return fmt.Errorf("write to connection for cancellation: %w", err)
|
||||
}
|
||||
|
||||
// Wait for the cancel request to be acknowledged by the server.
|
||||
// It copies the behavior of the libpq: https://github.com/postgres/postgres/blob/REL_16_0/src/interfaces/libpq/fe-connect.c#L4946-L4960
|
||||
_, _ = cancelConn.Read(buf)
|
||||
|
||||
return nil
|
||||
binary.BigEndian.PutUint32(buf[8:12], uint32(pgConn.pid))
|
||||
binary.BigEndian.PutUint32(buf[12:16], uint32(pgConn.secretKey))
|
||||
// Postgres will process the request and close the connection
|
||||
// so when don't need to read the reply
|
||||
// https://www.postgresql.org/docs/current/protocol-flow.html#id-1.10.6.7.10
|
||||
_, err = cancelConn.Write(buf)
|
||||
return err
|
||||
}
|
||||
|
||||
// WaitForNotification waits for a LISTEN/NOTIFY message to be received. It returns an error if a notification was not
|
||||
// WaitForNotification waits for a LISTON/NOTIFY message to be received. It returns an error if a notification was not
|
||||
// received.
|
||||
func (pgConn *PgConn) WaitForNotification(ctx context.Context) error {
|
||||
if err := pgConn.lock(); err != nil {
|
||||
@@ -1544,10 +1455,8 @@ func (rr *ResultReader) Read() *Result {
|
||||
values := rr.Values()
|
||||
row := make([][]byte, len(values))
|
||||
for i := range row {
|
||||
if values[i] != nil {
|
||||
row[i] = make([]byte, len(values[i]))
|
||||
copy(row[i], values[i])
|
||||
}
|
||||
row[i] = make([]byte, len(values[i]))
|
||||
copy(row[i], values[i])
|
||||
}
|
||||
br.Rows = append(br.Rows, row)
|
||||
}
|
||||
@@ -1697,55 +1606,25 @@ func (rr *ResultReader) concludeCommand(commandTag CommandTag, err error) {
|
||||
// Batch is a collection of queries that can be sent to the PostgreSQL server in a single round-trip.
|
||||
type Batch struct {
|
||||
buf []byte
|
||||
err error
|
||||
}
|
||||
|
||||
// ExecParams appends an ExecParams command to the batch. See PgConn.ExecParams for parameter descriptions.
|
||||
func (batch *Batch) ExecParams(sql string, paramValues [][]byte, paramOIDs []uint32, paramFormats []int16, resultFormats []int16) {
|
||||
if batch.err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
batch.buf, batch.err = (&pgproto3.Parse{Query: sql, ParameterOIDs: paramOIDs}).Encode(batch.buf)
|
||||
if batch.err != nil {
|
||||
return
|
||||
}
|
||||
batch.buf = (&pgproto3.Parse{Query: sql, ParameterOIDs: paramOIDs}).Encode(batch.buf)
|
||||
batch.ExecPrepared("", paramValues, paramFormats, resultFormats)
|
||||
}
|
||||
|
||||
// ExecPrepared appends an ExecPrepared e command to the batch. See PgConn.ExecPrepared for parameter descriptions.
|
||||
func (batch *Batch) ExecPrepared(stmtName string, paramValues [][]byte, paramFormats []int16, resultFormats []int16) {
|
||||
if batch.err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
batch.buf, batch.err = (&pgproto3.Bind{PreparedStatement: stmtName, ParameterFormatCodes: paramFormats, Parameters: paramValues, ResultFormatCodes: resultFormats}).Encode(batch.buf)
|
||||
if batch.err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
batch.buf, batch.err = (&pgproto3.Describe{ObjectType: 'P'}).Encode(batch.buf)
|
||||
if batch.err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
batch.buf, batch.err = (&pgproto3.Execute{}).Encode(batch.buf)
|
||||
if batch.err != nil {
|
||||
return
|
||||
}
|
||||
batch.buf = (&pgproto3.Bind{PreparedStatement: stmtName, ParameterFormatCodes: paramFormats, Parameters: paramValues, ResultFormatCodes: resultFormats}).Encode(batch.buf)
|
||||
batch.buf = (&pgproto3.Describe{ObjectType: 'P'}).Encode(batch.buf)
|
||||
batch.buf = (&pgproto3.Execute{}).Encode(batch.buf)
|
||||
}
|
||||
|
||||
// ExecBatch executes all the queries in batch in a single round-trip. Execution is implicitly transactional unless a
|
||||
// transaction is already in progress or SQL contains transaction control statements. This is a simpler way of executing
|
||||
// multiple queries in a single round trip than using pipeline mode.
|
||||
func (pgConn *PgConn) ExecBatch(ctx context.Context, batch *Batch) *MultiResultReader {
|
||||
if batch.err != nil {
|
||||
return &MultiResultReader{
|
||||
closed: true,
|
||||
err: batch.err,
|
||||
}
|
||||
}
|
||||
|
||||
if err := pgConn.lock(); err != nil {
|
||||
return &MultiResultReader{
|
||||
closed: true,
|
||||
@@ -1771,13 +1650,7 @@ func (pgConn *PgConn) ExecBatch(ctx context.Context, batch *Batch) *MultiResultR
|
||||
pgConn.contextWatcher.Watch(ctx)
|
||||
}
|
||||
|
||||
batch.buf, batch.err = (&pgproto3.Sync{}).Encode(batch.buf)
|
||||
if batch.err != nil {
|
||||
multiResult.closed = true
|
||||
multiResult.err = batch.err
|
||||
pgConn.unlock()
|
||||
return multiResult
|
||||
}
|
||||
batch.buf = (&pgproto3.Sync{}).Encode(batch.buf)
|
||||
|
||||
pgConn.enterPotentialWriteReadDeadlock()
|
||||
defer pgConn.exitPotentialWriteReadDeadlock()
|
||||
@@ -1859,16 +1732,10 @@ func (pgConn *PgConn) enterPotentialWriteReadDeadlock() {
|
||||
|
||||
// exitPotentialWriteReadDeadlock must be called after a call to enterPotentialWriteReadDeadlock.
|
||||
func (pgConn *PgConn) exitPotentialWriteReadDeadlock() {
|
||||
if !pgConn.slowWriteTimer.Stop() {
|
||||
// The timer starts its function in a separate goroutine. It is necessary to ensure the background reader has
|
||||
// started before calling Stop. Otherwise, the background reader may not be stopped. That on its own is not a
|
||||
// serious problem. But what is a serious problem is that the background reader may start at an inopportune time in
|
||||
// a subsequent query. For example, if a subsequent query was canceled then a deadline may be set on the net.Conn to
|
||||
// interrupt an in-progress read. After the read is interrupted, but before the deadline is cleared, the background
|
||||
// reader could start and read a deadline error. Then the next query would receive the an unexpected deadline error.
|
||||
<-pgConn.bgReaderStarted
|
||||
pgConn.bgReader.Stop()
|
||||
}
|
||||
// The state of the timer is not relevant upon exiting the potential slow write. It may both
|
||||
// fire (due to a slow write), or not fire (due to a fast write).
|
||||
_ = pgConn.slowWriteTimer.Stop()
|
||||
pgConn.bgReader.Stop()
|
||||
}
|
||||
|
||||
func (pgConn *PgConn) flushWithPotentialWriteReadDeadlock() error {
|
||||
@@ -1897,16 +1764,11 @@ func (pgConn *PgConn) SyncConn(ctx context.Context) error {
|
||||
}
|
||||
}
|
||||
|
||||
// This should never happen. Only way I can imagine this occurring is if the server is constantly sending data such as
|
||||
// This should never happen. Only way I can imagine this occuring is if the server is constantly sending data such as
|
||||
// LISTEN/NOTIFY or log notifications such that we never can get an empty buffer.
|
||||
return errors.New("SyncConn: conn never synchronized")
|
||||
}
|
||||
|
||||
// CustomData returns a map that can be used to associate custom data with the connection.
|
||||
func (pgConn *PgConn) CustomData() map[string]any {
|
||||
return pgConn.customData
|
||||
}
|
||||
|
||||
// HijackedConn is the result of hijacking a connection.
|
||||
//
|
||||
// Due to the necessary exposure of internal implementation details, it is not covered by the semantic versioning
|
||||
@@ -1919,7 +1781,6 @@ type HijackedConn struct {
|
||||
TxStatus byte
|
||||
Frontend *pgproto3.Frontend
|
||||
Config *Config
|
||||
CustomData map[string]any
|
||||
}
|
||||
|
||||
// Hijack extracts the internal connection data. pgConn must be in an idle state. SyncConn should be called immediately
|
||||
@@ -1942,7 +1803,6 @@ func (pgConn *PgConn) Hijack() (*HijackedConn, error) {
|
||||
TxStatus: pgConn.txStatus,
|
||||
Frontend: pgConn.frontend,
|
||||
Config: pgConn.config,
|
||||
CustomData: pgConn.customData,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -1962,23 +1822,16 @@ func Construct(hc *HijackedConn) (*PgConn, error) {
|
||||
txStatus: hc.TxStatus,
|
||||
frontend: hc.Frontend,
|
||||
config: hc.Config,
|
||||
customData: hc.CustomData,
|
||||
|
||||
status: connStatusIdle,
|
||||
|
||||
cleanupDone: make(chan struct{}),
|
||||
}
|
||||
|
||||
pgConn.contextWatcher = ctxwatch.NewContextWatcher(hc.Config.BuildContextWatcherHandler(pgConn))
|
||||
pgConn.contextWatcher = newContextWatcher(pgConn.conn)
|
||||
pgConn.bgReader = bgreader.New(pgConn.conn)
|
||||
pgConn.slowWriteTimer = time.AfterFunc(time.Duration(math.MaxInt64),
|
||||
func() {
|
||||
pgConn.bgReader.Start()
|
||||
pgConn.bgReaderStarted <- struct{}{}
|
||||
},
|
||||
)
|
||||
pgConn.slowWriteTimer = time.AfterFunc(time.Duration(math.MaxInt64), pgConn.bgReader.Start)
|
||||
pgConn.slowWriteTimer.Stop()
|
||||
pgConn.bgReaderStarted = make(chan struct{})
|
||||
pgConn.frontend = hc.Config.BuildFrontend(pgConn.bgReader, pgConn.conn)
|
||||
|
||||
return pgConn, nil
|
||||
@@ -2120,13 +1973,6 @@ func (p *Pipeline) Flush() error {
|
||||
|
||||
// Sync establishes a synchronization point and flushes the queued requests.
|
||||
func (p *Pipeline) Sync() error {
|
||||
if p.closed {
|
||||
if p.err != nil {
|
||||
return p.err
|
||||
}
|
||||
return errors.New("pipeline closed")
|
||||
}
|
||||
|
||||
p.conn.frontend.SendSync(&pgproto3.Sync{})
|
||||
err := p.Flush()
|
||||
if err != nil {
|
||||
@@ -2143,28 +1989,14 @@ func (p *Pipeline) Sync() error {
|
||||
// *PipelineSync. If an ErrorResponse is received from the server, results will be nil and err will be a *PgError. If no
|
||||
// results are available, results and err will both be nil.
|
||||
func (p *Pipeline) GetResults() (results any, err error) {
|
||||
if p.closed {
|
||||
if p.err != nil {
|
||||
return nil, p.err
|
||||
}
|
||||
return nil, errors.New("pipeline closed")
|
||||
}
|
||||
|
||||
if p.expectedReadyForQueryCount == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return p.getResults()
|
||||
}
|
||||
|
||||
func (p *Pipeline) getResults() (results any, err error) {
|
||||
for {
|
||||
msg, err := p.conn.receiveMessage()
|
||||
if err != nil {
|
||||
p.closed = true
|
||||
p.err = err
|
||||
p.conn.asyncClose()
|
||||
return nil, normalizeTimeoutError(p.ctx, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch msg := msg.(type) {
|
||||
@@ -2186,8 +2018,7 @@ func (p *Pipeline) getResults() (results any, err error) {
|
||||
case *pgproto3.ParseComplete:
|
||||
peekedMsg, err := p.conn.peekMessage()
|
||||
if err != nil {
|
||||
p.conn.asyncClose()
|
||||
return nil, normalizeTimeoutError(p.ctx, err)
|
||||
return nil, err
|
||||
}
|
||||
if _, ok := peekedMsg.(*pgproto3.ParameterDescription); ok {
|
||||
return p.getResultsPrepare()
|
||||
@@ -2247,7 +2078,6 @@ func (p *Pipeline) Close() error {
|
||||
if p.closed {
|
||||
return p.err
|
||||
}
|
||||
|
||||
p.closed = true
|
||||
|
||||
if p.pendingSync {
|
||||
@@ -2260,7 +2090,7 @@ func (p *Pipeline) Close() error {
|
||||
}
|
||||
|
||||
for p.expectedReadyForQueryCount > 0 {
|
||||
_, err := p.getResults()
|
||||
_, err := p.GetResults()
|
||||
if err != nil {
|
||||
p.err = err
|
||||
var pgErr *PgError
|
||||
@@ -2276,71 +2106,3 @@ func (p *Pipeline) Close() error {
|
||||
|
||||
return p.err
|
||||
}
|
||||
|
||||
// DeadlineContextWatcherHandler handles canceled contexts by setting a deadline on a net.Conn.
|
||||
type DeadlineContextWatcherHandler struct {
|
||||
Conn net.Conn
|
||||
|
||||
// DeadlineDelay is the delay to set on the deadline set on net.Conn when the context is canceled.
|
||||
DeadlineDelay time.Duration
|
||||
}
|
||||
|
||||
func (h *DeadlineContextWatcherHandler) HandleCancel(ctx context.Context) {
|
||||
h.Conn.SetDeadline(time.Now().Add(h.DeadlineDelay))
|
||||
}
|
||||
|
||||
func (h *DeadlineContextWatcherHandler) HandleUnwatchAfterCancel() {
|
||||
h.Conn.SetDeadline(time.Time{})
|
||||
}
|
||||
|
||||
// CancelRequestContextWatcherHandler handles canceled contexts by sending a cancel request to the server. It also sets
|
||||
// a deadline on a net.Conn as a fallback.
|
||||
type CancelRequestContextWatcherHandler struct {
|
||||
Conn *PgConn
|
||||
|
||||
// CancelRequestDelay is the delay before sending the cancel request to the server.
|
||||
CancelRequestDelay time.Duration
|
||||
|
||||
// DeadlineDelay is the delay to set on the deadline set on net.Conn when the context is canceled.
|
||||
DeadlineDelay time.Duration
|
||||
|
||||
cancelFinishedChan chan struct{}
|
||||
handleUnwatchAfterCancelCalled func()
|
||||
}
|
||||
|
||||
func (h *CancelRequestContextWatcherHandler) HandleCancel(context.Context) {
|
||||
h.cancelFinishedChan = make(chan struct{})
|
||||
var handleUnwatchedAfterCancelCalledCtx context.Context
|
||||
handleUnwatchedAfterCancelCalledCtx, h.handleUnwatchAfterCancelCalled = context.WithCancel(context.Background())
|
||||
|
||||
deadline := time.Now().Add(h.DeadlineDelay)
|
||||
h.Conn.conn.SetDeadline(deadline)
|
||||
|
||||
go func() {
|
||||
defer close(h.cancelFinishedChan)
|
||||
|
||||
select {
|
||||
case <-handleUnwatchedAfterCancelCalledCtx.Done():
|
||||
return
|
||||
case <-time.After(h.CancelRequestDelay):
|
||||
}
|
||||
|
||||
cancelRequestCtx, cancel := context.WithDeadline(handleUnwatchedAfterCancelCalledCtx, deadline)
|
||||
defer cancel()
|
||||
h.Conn.CancelRequest(cancelRequestCtx)
|
||||
|
||||
// CancelRequest is inherently racy. Even though the cancel request has been received by the server at this point,
|
||||
// it hasn't necessarily been delivered to the other connection. If we immediately return and the connection is
|
||||
// immediately used then it is possible the CancelRequest will actually cancel our next query. The
|
||||
// TestCancelRequestContextWatcherHandler Stress test can produce this error without the sleep below. The sleep time
|
||||
// is arbitrary, but should be sufficient to prevent this error case.
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}()
|
||||
}
|
||||
|
||||
func (h *CancelRequestContextWatcherHandler) HandleUnwatchAfterCancel() {
|
||||
h.handleUnwatchAfterCancelCalled()
|
||||
<-h.cancelFinishedChan
|
||||
|
||||
h.Conn.conn.SetDeadline(time.Time{})
|
||||
}
|
||||
|
||||
2
vendor/github.com/jackc/pgx/v5/pgproto3/README.md
generated
vendored
2
vendor/github.com/jackc/pgx/v5/pgproto3/README.md
generated
vendored
@@ -1,6 +1,6 @@
|
||||
# pgproto3
|
||||
|
||||
Package pgproto3 is an encoder and decoder of the PostgreSQL wire protocol version 3.
|
||||
Package pgproto3 is a encoder and decoder of the PostgreSQL wire protocol version 3.
|
||||
|
||||
pgproto3 can be used as a foundation for PostgreSQL drivers, proxies, mock servers, load balancers and more.
|
||||
|
||||
|
||||
7
vendor/github.com/jackc/pgx/v5/pgproto3/authentication_cleartext_password.go
generated
vendored
7
vendor/github.com/jackc/pgx/v5/pgproto3/authentication_cleartext_password.go
generated
vendored
@@ -35,10 +35,11 @@ func (dst *AuthenticationCleartextPassword) Decode(src []byte) error {
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *AuthenticationCleartextPassword) Encode(dst []byte) ([]byte, error) {
|
||||
dst, sp := beginMessage(dst, 'R')
|
||||
func (src *AuthenticationCleartextPassword) Encode(dst []byte) []byte {
|
||||
dst = append(dst, 'R')
|
||||
dst = pgio.AppendInt32(dst, 8)
|
||||
dst = pgio.AppendUint32(dst, AuthTypeCleartextPassword)
|
||||
return finishMessage(dst, sp)
|
||||
return dst
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
|
||||
7
vendor/github.com/jackc/pgx/v5/pgproto3/authentication_gss.go
generated
vendored
7
vendor/github.com/jackc/pgx/v5/pgproto3/authentication_gss.go
generated
vendored
@@ -27,10 +27,11 @@ func (a *AuthenticationGSS) Decode(src []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *AuthenticationGSS) Encode(dst []byte) ([]byte, error) {
|
||||
dst, sp := beginMessage(dst, 'R')
|
||||
func (a *AuthenticationGSS) Encode(dst []byte) []byte {
|
||||
dst = append(dst, 'R')
|
||||
dst = pgio.AppendInt32(dst, 4)
|
||||
dst = pgio.AppendUint32(dst, AuthTypeGSS)
|
||||
return finishMessage(dst, sp)
|
||||
return dst
|
||||
}
|
||||
|
||||
func (a *AuthenticationGSS) MarshalJSON() ([]byte, error) {
|
||||
|
||||
7
vendor/github.com/jackc/pgx/v5/pgproto3/authentication_gss_continue.go
generated
vendored
7
vendor/github.com/jackc/pgx/v5/pgproto3/authentication_gss_continue.go
generated
vendored
@@ -31,11 +31,12 @@ func (a *AuthenticationGSSContinue) Decode(src []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *AuthenticationGSSContinue) Encode(dst []byte) ([]byte, error) {
|
||||
dst, sp := beginMessage(dst, 'R')
|
||||
func (a *AuthenticationGSSContinue) Encode(dst []byte) []byte {
|
||||
dst = append(dst, 'R')
|
||||
dst = pgio.AppendInt32(dst, int32(len(a.Data))+8)
|
||||
dst = pgio.AppendUint32(dst, AuthTypeGSSCont)
|
||||
dst = append(dst, a.Data...)
|
||||
return finishMessage(dst, sp)
|
||||
return dst
|
||||
}
|
||||
|
||||
func (a *AuthenticationGSSContinue) MarshalJSON() ([]byte, error) {
|
||||
|
||||
7
vendor/github.com/jackc/pgx/v5/pgproto3/authentication_md5_password.go
generated
vendored
7
vendor/github.com/jackc/pgx/v5/pgproto3/authentication_md5_password.go
generated
vendored
@@ -38,11 +38,12 @@ func (dst *AuthenticationMD5Password) Decode(src []byte) error {
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *AuthenticationMD5Password) Encode(dst []byte) ([]byte, error) {
|
||||
dst, sp := beginMessage(dst, 'R')
|
||||
func (src *AuthenticationMD5Password) Encode(dst []byte) []byte {
|
||||
dst = append(dst, 'R')
|
||||
dst = pgio.AppendInt32(dst, 12)
|
||||
dst = pgio.AppendUint32(dst, AuthTypeMD5Password)
|
||||
dst = append(dst, src.Salt[:]...)
|
||||
return finishMessage(dst, sp)
|
||||
return dst
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
|
||||
7
vendor/github.com/jackc/pgx/v5/pgproto3/authentication_ok.go
generated
vendored
7
vendor/github.com/jackc/pgx/v5/pgproto3/authentication_ok.go
generated
vendored
@@ -35,10 +35,11 @@ func (dst *AuthenticationOk) Decode(src []byte) error {
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *AuthenticationOk) Encode(dst []byte) ([]byte, error) {
|
||||
dst, sp := beginMessage(dst, 'R')
|
||||
func (src *AuthenticationOk) Encode(dst []byte) []byte {
|
||||
dst = append(dst, 'R')
|
||||
dst = pgio.AppendInt32(dst, 8)
|
||||
dst = pgio.AppendUint32(dst, AuthTypeOk)
|
||||
return finishMessage(dst, sp)
|
||||
return dst
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
|
||||
10
vendor/github.com/jackc/pgx/v5/pgproto3/authentication_sasl.go
generated
vendored
10
vendor/github.com/jackc/pgx/v5/pgproto3/authentication_sasl.go
generated
vendored
@@ -47,8 +47,10 @@ func (dst *AuthenticationSASL) Decode(src []byte) error {
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *AuthenticationSASL) Encode(dst []byte) ([]byte, error) {
|
||||
dst, sp := beginMessage(dst, 'R')
|
||||
func (src *AuthenticationSASL) Encode(dst []byte) []byte {
|
||||
dst = append(dst, 'R')
|
||||
sp := len(dst)
|
||||
dst = pgio.AppendInt32(dst, -1)
|
||||
dst = pgio.AppendUint32(dst, AuthTypeSASL)
|
||||
|
||||
for _, s := range src.AuthMechanisms {
|
||||
@@ -57,7 +59,9 @@ func (src *AuthenticationSASL) Encode(dst []byte) ([]byte, error) {
|
||||
}
|
||||
dst = append(dst, 0)
|
||||
|
||||
return finishMessage(dst, sp)
|
||||
pgio.SetInt32(dst[sp:], int32(len(dst[sp:])))
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
|
||||
12
vendor/github.com/jackc/pgx/v5/pgproto3/authentication_sasl_continue.go
generated
vendored
12
vendor/github.com/jackc/pgx/v5/pgproto3/authentication_sasl_continue.go
generated
vendored
@@ -38,11 +38,17 @@ func (dst *AuthenticationSASLContinue) Decode(src []byte) error {
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *AuthenticationSASLContinue) Encode(dst []byte) ([]byte, error) {
|
||||
dst, sp := beginMessage(dst, 'R')
|
||||
func (src *AuthenticationSASLContinue) Encode(dst []byte) []byte {
|
||||
dst = append(dst, 'R')
|
||||
sp := len(dst)
|
||||
dst = pgio.AppendInt32(dst, -1)
|
||||
dst = pgio.AppendUint32(dst, AuthTypeSASLContinue)
|
||||
|
||||
dst = append(dst, src.Data...)
|
||||
return finishMessage(dst, sp)
|
||||
|
||||
pgio.SetInt32(dst[sp:], int32(len(dst[sp:])))
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user