From 7ccfecd551aa1945708124e1342e6e7953f2dc95 Mon Sep 17 00:00:00 2001 From: Jonathan King Date: Tue, 2 Jul 2024 12:16:25 -0400 Subject: [PATCH 1/4] pgupgrade: Fix clairpgupgrade path (PROJQUAY-7392) - Fix kustomize path for clairpgupgrade when clair is managed/unmanaged --- bundle/manifests/quayregistries.crd.yaml | 849 +++++++----------- .../bases/quay.redhat.com_quayregistries.yaml | 849 +++++++----------- pkg/kustomize/kustomize.go | 11 +- pkg/kustomize/kustomize_test.go | 2 +- 4 files changed, 702 insertions(+), 1009 deletions(-) diff --git a/bundle/manifests/quayregistries.crd.yaml b/bundle/manifests/quayregistries.crd.yaml index 49572e00e..25d24f345 100644 --- a/bundle/manifests/quayregistries.crd.yaml +++ b/bundle/manifests/quayregistries.crd.yaml @@ -2,7 +2,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.13.0 + controller-gen.kubebuilder.io/version: v0.14.0 name: quayregistries.quay.redhat.com spec: group: quay.redhat.com @@ -19,14 +19,19 @@ spec: description: QuayRegistry is the Schema for the quayregistries API. properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources type: string kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds type: string metadata: type: object @@ -44,9 +49,9 @@ spec: description: Kind is the unique name of this type of component. type: string managed: - description: Managed indicates whether or not the Operator is - responsible for the lifecycle of this component. Default is - true. + description: |- + Managed indicates whether or not the Operator is responsible for the lifecycle of this component. + Default is true. type: boolean overrides: description: Overrides holds information regarding component @@ -61,24 +66,20 @@ spec: for the pod. properties: preferredDuringSchedulingIgnoredDuringExecution: - description: The scheduler will prefer to schedule - pods to nodes that satisfy the affinity expressions - specified by this field, but it may choose a node - that violates one or more of the expressions. - The node that is most preferred is the one with - the greatest sum of weights, i.e. for each node - that meets all of the scheduling requirements - (resource request, requiredDuringScheduling affinity - expressions, etc.), compute a sum by iterating - through the elements of this field and adding - "weight" to the sum if the node matches the corresponding - matchExpressions; the node(s) with the highest - sum are the most preferred. + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node matches the corresponding matchExpressions; the + node(s) with the highest sum are the most preferred. items: - description: An empty preferred scheduling term - matches all objects with implicit weight 0 (i.e. - it's a no-op). A null preferred scheduling term - matches no objects (i.e. is also a no-op). + description: |- + An empty preferred scheduling term matches all objects with implicit weight 0 + (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op). properties: preference: description: A node selector term, associated @@ -88,34 +89,26 @@ spec: description: A list of node selector requirements by node's labels. items: - description: A node selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. properties: key: description: The label key that the selector applies to. type: string operator: - description: Represents a key's - relationship to a set of values. - Valid operators are In, NotIn, - Exists, DoesNotExist. Gt, and - Lt. + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. type: string values: - description: An array of string - values. If the operator is In - or NotIn, the values array must - be non-empty. If the operator - is Exists or DoesNotExist, the - values array must be empty. If - the operator is Gt or Lt, the - values array must have a single - element, which will be interpreted - as an integer. This array is replaced - during a strategic merge patch. + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. items: type: string type: array @@ -128,34 +121,26 @@ spec: description: A list of node selector requirements by node's fields. items: - description: A node selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. properties: key: description: The label key that the selector applies to. type: string operator: - description: Represents a key's - relationship to a set of values. - Valid operators are In, NotIn, - Exists, DoesNotExist. Gt, and - Lt. + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. type: string values: - description: An array of string - values. If the operator is In - or NotIn, the values array must - be non-empty. If the operator - is Exists or DoesNotExist, the - values array must be empty. If - the operator is Gt or Lt, the - values array must have a single - element, which will be interpreted - as an integer. This array is replaced - during a strategic merge patch. + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. items: type: string type: array @@ -178,55 +163,46 @@ spec: type: object type: array requiredDuringSchedulingIgnoredDuringExecution: - description: If the affinity requirements specified - by this field are not met at scheduling time, - the pod will not be scheduled onto the node. If - the affinity requirements specified by this field - cease to be met at some point during pod execution - (e.g. due to an update), the system may or may - not try to eventually evict the pod from its node. + description: |- + If the affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to an update), the system + may or may not try to eventually evict the pod from its node. properties: nodeSelectorTerms: description: Required. A list of node selector terms. The terms are ORed. items: - description: A null or empty node selector - term matches no objects. The requirements - of them are ANDed. The TopologySelectorTerm - type implements a subset of the NodeSelectorTerm. + description: |- + A null or empty node selector term matches no objects. The requirements of + them are ANDed. + The TopologySelectorTerm type implements a subset of the NodeSelectorTerm. properties: matchExpressions: description: A list of node selector requirements by node's labels. items: - description: A node selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. properties: key: description: The label key that the selector applies to. type: string operator: - description: Represents a key's - relationship to a set of values. - Valid operators are In, NotIn, - Exists, DoesNotExist. Gt, and - Lt. + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. type: string values: - description: An array of string - values. If the operator is In - or NotIn, the values array must - be non-empty. If the operator - is Exists or DoesNotExist, the - values array must be empty. If - the operator is Gt or Lt, the - values array must have a single - element, which will be interpreted - as an integer. This array is replaced - during a strategic merge patch. + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. items: type: string type: array @@ -239,34 +215,26 @@ spec: description: A list of node selector requirements by node's fields. items: - description: A node selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. properties: key: description: The label key that the selector applies to. type: string operator: - description: Represents a key's - relationship to a set of values. - Valid operators are In, NotIn, - Exists, DoesNotExist. Gt, and - Lt. + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. type: string values: - description: An array of string - values. If the operator is In - or NotIn, the values array must - be non-empty. If the operator - is Exists or DoesNotExist, the - values array must be empty. If - the operator is Gt or Lt, the - values array must have a single - element, which will be interpreted - as an integer. This array is replaced - during a strategic merge patch. + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. items: type: string type: array @@ -289,18 +257,15 @@ spec: as some other pod(s)). properties: preferredDuringSchedulingIgnoredDuringExecution: - description: The scheduler will prefer to schedule - pods to nodes that satisfy the affinity expressions - specified by this field, but it may choose a node - that violates one or more of the expressions. - The node that is most preferred is the one with - the greatest sum of weights, i.e. for each node - that meets all of the scheduling requirements - (resource request, requiredDuringScheduling affinity - expressions, etc.), compute a sum by iterating - through the elements of this field and adding - "weight" to the sum if the node has pods which - matches the corresponding podAffinityTerm; the + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the node(s) with the highest sum are the most preferred. items: description: The weights of all of the matched @@ -320,10 +285,9 @@ spec: list of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. properties: key: description: key is the label @@ -331,21 +295,15 @@ spec: to. type: string operator: - description: operator represents - a key's relationship to a - set of values. Valid operators - are In, NotIn, Exists and - DoesNotExist. + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array - of string values. If the operator - is In or NotIn, the values - array must be non-empty. If - the operator is Exists or - DoesNotExist, the values array - must be empty. This array - is replaced during a strategic + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic merge patch. items: type: string @@ -358,25 +316,19 @@ spec: matchLabels: additionalProperties: type: string - description: matchLabels is a map - of {key,value} pairs. A single {key,value} - in the matchLabels map is equivalent - to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are - ANDed. + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic namespaceSelector: - description: A label query over the set - of namespaces that the term applies - to. The term is applied to the union - of the namespaces selected by this field - and the ones listed in the namespaces - field. null selector and null or empty - namespaces list means "this pod's namespace". + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". An empty selector ({}) matches all namespaces. properties: matchExpressions: @@ -384,10 +336,9 @@ spec: list of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. properties: key: description: key is the label @@ -395,21 +346,15 @@ spec: to. type: string operator: - description: operator represents - a key's relationship to a - set of values. Valid operators - are In, NotIn, Exists and - DoesNotExist. + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array - of string values. If the operator - is In or NotIn, the values - array must be non-empty. If - the operator is Exists or - DoesNotExist, the values array - must be empty. This array - is replaced during a strategic + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic merge patch. items: type: string @@ -422,47 +367,37 @@ spec: matchLabels: additionalProperties: type: string - description: matchLabels is a map - of {key,value} pairs. A single {key,value} - in the matchLabels map is equivalent - to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are - ANDed. + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic namespaces: - description: namespaces specifies a static - list of namespace names that the term - applies to. The term is applied to the - union of the namespaces listed in this - field and the ones selected by namespaceSelector. - null or empty namespaces list and null - namespaceSelector means "this pod's - namespace". + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". items: type: string type: array topologyKey: - description: This pod should be co-located - (affinity) or not co-located (anti-affinity) - with the pods matching the labelSelector - in the specified namespaces, where co-located - is defined as running on a node whose - value of the label with key topologyKey - matches that of any node on which any - of the selected pods is running. Empty - topologyKey is not allowed. + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. type: string required: - topologyKey type: object weight: - description: weight associated with matching - the corresponding podAffinityTerm, in the - range 1-100. + description: |- + weight associated with matching the corresponding podAffinityTerm, + in the range 1-100. format: int32 type: integer required: @@ -471,24 +406,21 @@ spec: type: object type: array requiredDuringSchedulingIgnoredDuringExecution: - description: If the affinity requirements specified - by this field are not met at scheduling time, - the pod will not be scheduled onto the node. If - the affinity requirements specified by this field - cease to be met at some point during pod execution - (e.g. due to a pod label update), the system may - or may not try to eventually evict the pod from - its node. When there are multiple elements, the - lists of nodes corresponding to each podAffinityTerm - are intersected, i.e. all terms must be satisfied. + description: |- + If the affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to a pod label update), the + system may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding to each + podAffinityTerm are intersected, i.e. all terms must be satisfied. items: - description: Defines a set of pods (namely those - matching the labelSelector relative to the given - namespace(s)) that this pod should be co-located - (affinity) or not co-located (anti-affinity) - with, where co-located is defined as running - on a node whose value of the label with key - matches that of any node on which + description: |- + Defines a set of pods (namely those matching the labelSelector + relative to the given namespace(s)) that this pod should be + co-located (affinity) or not co-located (anti-affinity) with, + where co-located is defined as running on a node whose value of + the label with key matches that of any node on which a pod of the set of pods is running properties: labelSelector: @@ -500,29 +432,24 @@ spec: of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. properties: key: description: key is the label key that the selector applies to. type: string operator: - description: operator represents - a key's relationship to a set - of values. Valid operators are - In, NotIn, Exists and DoesNotExist. + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array - of string values. If the operator - is In or NotIn, the values array - must be non-empty. If the operator - is Exists or DoesNotExist, the - values array must be empty. This - array is replaced during a strategic + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic merge patch. items: type: string @@ -535,53 +462,44 @@ spec: matchLabels: additionalProperties: type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are ANDed. + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic namespaceSelector: - description: A label query over the set of - namespaces that the term applies to. The - term is applied to the union of the namespaces - selected by this field and the ones listed - in the namespaces field. null selector and - null or empty namespaces list means "this - pod's namespace". An empty selector ({}) - matches all namespaces. + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. properties: matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. properties: key: description: key is the label key that the selector applies to. type: string operator: - description: operator represents - a key's relationship to a set - of values. Valid operators are - In, NotIn, Exists and DoesNotExist. + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array - of string values. If the operator - is In or NotIn, the values array - must be non-empty. If the operator - is Exists or DoesNotExist, the - values array must be empty. This - array is replaced during a strategic + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic merge patch. items: type: string @@ -594,36 +512,29 @@ spec: matchLabels: additionalProperties: type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are ANDed. + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic namespaces: - description: namespaces specifies a static - list of namespace names that the term applies - to. The term is applied to the union of - the namespaces listed in this field and - the ones selected by namespaceSelector. - null or empty namespaces list and null namespaceSelector - means "this pod's namespace". + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". items: type: string type: array topologyKey: - description: This pod should be co-located - (affinity) or not co-located (anti-affinity) - with the pods matching the labelSelector - in the specified namespaces, where co-located - is defined as running on a node whose value - of the label with key topologyKey matches - that of any node on which any of the selected - pods is running. Empty topologyKey is not - allowed. + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. type: string required: - topologyKey @@ -636,18 +547,15 @@ spec: zone, etc. as some other pod(s)). properties: preferredDuringSchedulingIgnoredDuringExecution: - description: The scheduler will prefer to schedule - pods to nodes that satisfy the anti-affinity expressions - specified by this field, but it may choose a node - that violates one or more of the expressions. - The node that is most preferred is the one with - the greatest sum of weights, i.e. for each node - that meets all of the scheduling requirements - (resource request, requiredDuringScheduling anti-affinity - expressions, etc.), compute a sum by iterating - through the elements of this field and adding - "weight" to the sum if the node has pods which - matches the corresponding podAffinityTerm; the + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the anti-affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling anti-affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the node(s) with the highest sum are the most preferred. items: description: The weights of all of the matched @@ -667,10 +575,9 @@ spec: list of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. properties: key: description: key is the label @@ -678,21 +585,15 @@ spec: to. type: string operator: - description: operator represents - a key's relationship to a - set of values. Valid operators - are In, NotIn, Exists and - DoesNotExist. + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array - of string values. If the operator - is In or NotIn, the values - array must be non-empty. If - the operator is Exists or - DoesNotExist, the values array - must be empty. This array - is replaced during a strategic + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic merge patch. items: type: string @@ -705,25 +606,19 @@ spec: matchLabels: additionalProperties: type: string - description: matchLabels is a map - of {key,value} pairs. A single {key,value} - in the matchLabels map is equivalent - to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are - ANDed. + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic namespaceSelector: - description: A label query over the set - of namespaces that the term applies - to. The term is applied to the union - of the namespaces selected by this field - and the ones listed in the namespaces - field. null selector and null or empty - namespaces list means "this pod's namespace". + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". An empty selector ({}) matches all namespaces. properties: matchExpressions: @@ -731,10 +626,9 @@ spec: list of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. properties: key: description: key is the label @@ -742,21 +636,15 @@ spec: to. type: string operator: - description: operator represents - a key's relationship to a - set of values. Valid operators - are In, NotIn, Exists and - DoesNotExist. + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array - of string values. If the operator - is In or NotIn, the values - array must be non-empty. If - the operator is Exists or - DoesNotExist, the values array - must be empty. This array - is replaced during a strategic + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic merge patch. items: type: string @@ -769,47 +657,37 @@ spec: matchLabels: additionalProperties: type: string - description: matchLabels is a map - of {key,value} pairs. A single {key,value} - in the matchLabels map is equivalent - to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are - ANDed. + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic namespaces: - description: namespaces specifies a static - list of namespace names that the term - applies to. The term is applied to the - union of the namespaces listed in this - field and the ones selected by namespaceSelector. - null or empty namespaces list and null - namespaceSelector means "this pod's - namespace". + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". items: type: string type: array topologyKey: - description: This pod should be co-located - (affinity) or not co-located (anti-affinity) - with the pods matching the labelSelector - in the specified namespaces, where co-located - is defined as running on a node whose - value of the label with key topologyKey - matches that of any node on which any - of the selected pods is running. Empty - topologyKey is not allowed. + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. type: string required: - topologyKey type: object weight: - description: weight associated with matching - the corresponding podAffinityTerm, in the - range 1-100. + description: |- + weight associated with matching the corresponding podAffinityTerm, + in the range 1-100. format: int32 type: integer required: @@ -818,25 +696,21 @@ spec: type: object type: array requiredDuringSchedulingIgnoredDuringExecution: - description: If the anti-affinity requirements specified - by this field are not met at scheduling time, - the pod will not be scheduled onto the node. If - the anti-affinity requirements specified by this - field cease to be met at some point during pod - execution (e.g. due to a pod label update), the - system may or may not try to eventually evict - the pod from its node. When there are multiple - elements, the lists of nodes corresponding to - each podAffinityTerm are intersected, i.e. all - terms must be satisfied. + description: |- + If the anti-affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the anti-affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to a pod label update), the + system may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding to each + podAffinityTerm are intersected, i.e. all terms must be satisfied. items: - description: Defines a set of pods (namely those - matching the labelSelector relative to the given - namespace(s)) that this pod should be co-located - (affinity) or not co-located (anti-affinity) - with, where co-located is defined as running - on a node whose value of the label with key - matches that of any node on which + description: |- + Defines a set of pods (namely those matching the labelSelector + relative to the given namespace(s)) that this pod should be + co-located (affinity) or not co-located (anti-affinity) with, + where co-located is defined as running on a node whose value of + the label with key matches that of any node on which a pod of the set of pods is running properties: labelSelector: @@ -848,29 +722,24 @@ spec: of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. properties: key: description: key is the label key that the selector applies to. type: string operator: - description: operator represents - a key's relationship to a set - of values. Valid operators are - In, NotIn, Exists and DoesNotExist. + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array - of string values. If the operator - is In or NotIn, the values array - must be non-empty. If the operator - is Exists or DoesNotExist, the - values array must be empty. This - array is replaced during a strategic + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic merge patch. items: type: string @@ -883,53 +752,44 @@ spec: matchLabels: additionalProperties: type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are ANDed. + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic namespaceSelector: - description: A label query over the set of - namespaces that the term applies to. The - term is applied to the union of the namespaces - selected by this field and the ones listed - in the namespaces field. null selector and - null or empty namespaces list means "this - pod's namespace". An empty selector ({}) - matches all namespaces. + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. properties: matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. properties: key: description: key is the label key that the selector applies to. type: string operator: - description: operator represents - a key's relationship to a set - of values. Valid operators are - In, NotIn, Exists and DoesNotExist. + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array - of string values. If the operator - is In or NotIn, the values array - must be non-empty. If the operator - is Exists or DoesNotExist, the - values array must be empty. This - array is replaced during a strategic + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic merge patch. items: type: string @@ -942,36 +802,29 @@ spec: matchLabels: additionalProperties: type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are ANDed. + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic namespaces: - description: namespaces specifies a static - list of namespace names that the term applies - to. The term is applied to the union of - the namespaces listed in this field and - the ones selected by namespaceSelector. - null or empty namespaces list and null namespaceSelector - means "this pod's namespace". + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". items: type: string type: array topologyKey: - description: This pod should be co-located - (affinity) or not co-located (anti-affinity) - with the pods matching the labelSelector - in the specified namespaces, where co-located - is defined as running on a node whose value - of the label with key topologyKey matches - that of any node on which any of the selected - pods is running. Empty topologyKey is not - allowed. + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. type: string required: - topologyKey @@ -993,17 +846,16 @@ spec: be a C_IDENTIFIER. type: string value: - description: 'Variable references $(VAR_NAME) are - expanded using the previously defined environment - variables in the container and any service environment - variables. If a variable cannot be resolved, the - reference in the input string will be unchanged. - Double $$ are reduced to a single $, which allows - for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" - will produce the string literal "$(VAR_NAME)". Escaped - references will never be expanded, regardless of - whether the variable exists or not. Defaults to - "".' + description: |- + Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in the container and + any service environment variables. If a variable cannot be resolved, + the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. + "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". + Escaped references will never be expanded, regardless of whether the variable + exists or not. + Defaults to "". type: string valueFrom: description: Source for the environment variable's @@ -1016,10 +868,10 @@ spec: description: The key to select. type: string name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? type: string optional: description: Specify whether the ConfigMap @@ -1030,11 +882,9 @@ spec: type: object x-kubernetes-map-type: atomic fieldRef: - description: 'Selects a field of the pod: supports - metadata.name, metadata.namespace, `metadata.labels['''']`, - `metadata.annotations['''']`, spec.nodeName, - spec.serviceAccountName, status.hostIP, status.podIP, - status.podIPs.' + description: |- + Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, + spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs. properties: apiVersion: description: Version of the schema the FieldPath @@ -1049,11 +899,9 @@ spec: type: object x-kubernetes-map-type: atomic resourceFieldRef: - description: 'Selects a resource of the container: - only resources limits and requests (limits.cpu, - limits.memory, limits.ephemeral-storage, requests.cpu, - requests.memory and requests.ephemeral-storage) - are currently supported.' + description: |- + Selects a resource of the container: only resources limits and requests + (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported. properties: containerName: description: 'Container name: required for @@ -1083,10 +931,10 @@ spec: from. Must be a valid secret key. type: string name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? type: string optional: description: Specify whether the Secret or @@ -1147,9 +995,9 @@ spec: type: object type: array configBundleSecret: - description: ConfigBundleSecret is the name of the Kubernetes `Secret` - in the same namespace which contains the base Quay config and extra - certs. + description: |- + ConfigBundleSecret is the name of the Kubernetes `Secret` in the same namespace + which contains the base Quay config and extra certs. type: string type: object status: @@ -1159,12 +1007,11 @@ spec: description: Conditions represent the conditions that a QuayRegistry can have. items: - description: 'Condition is a single condition of a QuayRegistry. - Conditions should follow the "abnormal-true" principle in order - to only bring the attention of users to "broken" states. Example: - a condition of `type: "Ready", status: "True"“ is less useful - and should be omitted whereas `type: "NotReady", status: "True"` - is more useful when trying to monitor when something is wrong.' + description: |- + Condition is a single condition of a QuayRegistry. + Conditions should follow the "abnormal-true" principle in order to only bring the attention of users to "broken" states. + Example: a condition of `type: "Ready", status: "True"“ is less useful and should be omitted whereas `type: "NotReady", status: "True"` + is more useful when trying to monitor when something is wrong. properties: lastTransitionTime: format: date-time diff --git a/config/crd/bases/quay.redhat.com_quayregistries.yaml b/config/crd/bases/quay.redhat.com_quayregistries.yaml index 49572e00e..25d24f345 100644 --- a/config/crd/bases/quay.redhat.com_quayregistries.yaml +++ b/config/crd/bases/quay.redhat.com_quayregistries.yaml @@ -2,7 +2,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.13.0 + controller-gen.kubebuilder.io/version: v0.14.0 name: quayregistries.quay.redhat.com spec: group: quay.redhat.com @@ -19,14 +19,19 @@ spec: description: QuayRegistry is the Schema for the quayregistries API. properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources type: string kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds type: string metadata: type: object @@ -44,9 +49,9 @@ spec: description: Kind is the unique name of this type of component. type: string managed: - description: Managed indicates whether or not the Operator is - responsible for the lifecycle of this component. Default is - true. + description: |- + Managed indicates whether or not the Operator is responsible for the lifecycle of this component. + Default is true. type: boolean overrides: description: Overrides holds information regarding component @@ -61,24 +66,20 @@ spec: for the pod. properties: preferredDuringSchedulingIgnoredDuringExecution: - description: The scheduler will prefer to schedule - pods to nodes that satisfy the affinity expressions - specified by this field, but it may choose a node - that violates one or more of the expressions. - The node that is most preferred is the one with - the greatest sum of weights, i.e. for each node - that meets all of the scheduling requirements - (resource request, requiredDuringScheduling affinity - expressions, etc.), compute a sum by iterating - through the elements of this field and adding - "weight" to the sum if the node matches the corresponding - matchExpressions; the node(s) with the highest - sum are the most preferred. + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node matches the corresponding matchExpressions; the + node(s) with the highest sum are the most preferred. items: - description: An empty preferred scheduling term - matches all objects with implicit weight 0 (i.e. - it's a no-op). A null preferred scheduling term - matches no objects (i.e. is also a no-op). + description: |- + An empty preferred scheduling term matches all objects with implicit weight 0 + (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op). properties: preference: description: A node selector term, associated @@ -88,34 +89,26 @@ spec: description: A list of node selector requirements by node's labels. items: - description: A node selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. properties: key: description: The label key that the selector applies to. type: string operator: - description: Represents a key's - relationship to a set of values. - Valid operators are In, NotIn, - Exists, DoesNotExist. Gt, and - Lt. + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. type: string values: - description: An array of string - values. If the operator is In - or NotIn, the values array must - be non-empty. If the operator - is Exists or DoesNotExist, the - values array must be empty. If - the operator is Gt or Lt, the - values array must have a single - element, which will be interpreted - as an integer. This array is replaced - during a strategic merge patch. + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. items: type: string type: array @@ -128,34 +121,26 @@ spec: description: A list of node selector requirements by node's fields. items: - description: A node selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. properties: key: description: The label key that the selector applies to. type: string operator: - description: Represents a key's - relationship to a set of values. - Valid operators are In, NotIn, - Exists, DoesNotExist. Gt, and - Lt. + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. type: string values: - description: An array of string - values. If the operator is In - or NotIn, the values array must - be non-empty. If the operator - is Exists or DoesNotExist, the - values array must be empty. If - the operator is Gt or Lt, the - values array must have a single - element, which will be interpreted - as an integer. This array is replaced - during a strategic merge patch. + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. items: type: string type: array @@ -178,55 +163,46 @@ spec: type: object type: array requiredDuringSchedulingIgnoredDuringExecution: - description: If the affinity requirements specified - by this field are not met at scheduling time, - the pod will not be scheduled onto the node. If - the affinity requirements specified by this field - cease to be met at some point during pod execution - (e.g. due to an update), the system may or may - not try to eventually evict the pod from its node. + description: |- + If the affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to an update), the system + may or may not try to eventually evict the pod from its node. properties: nodeSelectorTerms: description: Required. A list of node selector terms. The terms are ORed. items: - description: A null or empty node selector - term matches no objects. The requirements - of them are ANDed. The TopologySelectorTerm - type implements a subset of the NodeSelectorTerm. + description: |- + A null or empty node selector term matches no objects. The requirements of + them are ANDed. + The TopologySelectorTerm type implements a subset of the NodeSelectorTerm. properties: matchExpressions: description: A list of node selector requirements by node's labels. items: - description: A node selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. properties: key: description: The label key that the selector applies to. type: string operator: - description: Represents a key's - relationship to a set of values. - Valid operators are In, NotIn, - Exists, DoesNotExist. Gt, and - Lt. + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. type: string values: - description: An array of string - values. If the operator is In - or NotIn, the values array must - be non-empty. If the operator - is Exists or DoesNotExist, the - values array must be empty. If - the operator is Gt or Lt, the - values array must have a single - element, which will be interpreted - as an integer. This array is replaced - during a strategic merge patch. + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. items: type: string type: array @@ -239,34 +215,26 @@ spec: description: A list of node selector requirements by node's fields. items: - description: A node selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. properties: key: description: The label key that the selector applies to. type: string operator: - description: Represents a key's - relationship to a set of values. - Valid operators are In, NotIn, - Exists, DoesNotExist. Gt, and - Lt. + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. type: string values: - description: An array of string - values. If the operator is In - or NotIn, the values array must - be non-empty. If the operator - is Exists or DoesNotExist, the - values array must be empty. If - the operator is Gt or Lt, the - values array must have a single - element, which will be interpreted - as an integer. This array is replaced - during a strategic merge patch. + description: |- + An array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + This array is replaced during a strategic merge patch. items: type: string type: array @@ -289,18 +257,15 @@ spec: as some other pod(s)). properties: preferredDuringSchedulingIgnoredDuringExecution: - description: The scheduler will prefer to schedule - pods to nodes that satisfy the affinity expressions - specified by this field, but it may choose a node - that violates one or more of the expressions. - The node that is most preferred is the one with - the greatest sum of weights, i.e. for each node - that meets all of the scheduling requirements - (resource request, requiredDuringScheduling affinity - expressions, etc.), compute a sum by iterating - through the elements of this field and adding - "weight" to the sum if the node has pods which - matches the corresponding podAffinityTerm; the + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the node(s) with the highest sum are the most preferred. items: description: The weights of all of the matched @@ -320,10 +285,9 @@ spec: list of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. properties: key: description: key is the label @@ -331,21 +295,15 @@ spec: to. type: string operator: - description: operator represents - a key's relationship to a - set of values. Valid operators - are In, NotIn, Exists and - DoesNotExist. + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array - of string values. If the operator - is In or NotIn, the values - array must be non-empty. If - the operator is Exists or - DoesNotExist, the values array - must be empty. This array - is replaced during a strategic + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic merge patch. items: type: string @@ -358,25 +316,19 @@ spec: matchLabels: additionalProperties: type: string - description: matchLabels is a map - of {key,value} pairs. A single {key,value} - in the matchLabels map is equivalent - to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are - ANDed. + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic namespaceSelector: - description: A label query over the set - of namespaces that the term applies - to. The term is applied to the union - of the namespaces selected by this field - and the ones listed in the namespaces - field. null selector and null or empty - namespaces list means "this pod's namespace". + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". An empty selector ({}) matches all namespaces. properties: matchExpressions: @@ -384,10 +336,9 @@ spec: list of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. properties: key: description: key is the label @@ -395,21 +346,15 @@ spec: to. type: string operator: - description: operator represents - a key's relationship to a - set of values. Valid operators - are In, NotIn, Exists and - DoesNotExist. + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array - of string values. If the operator - is In or NotIn, the values - array must be non-empty. If - the operator is Exists or - DoesNotExist, the values array - must be empty. This array - is replaced during a strategic + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic merge patch. items: type: string @@ -422,47 +367,37 @@ spec: matchLabels: additionalProperties: type: string - description: matchLabels is a map - of {key,value} pairs. A single {key,value} - in the matchLabels map is equivalent - to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are - ANDed. + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic namespaces: - description: namespaces specifies a static - list of namespace names that the term - applies to. The term is applied to the - union of the namespaces listed in this - field and the ones selected by namespaceSelector. - null or empty namespaces list and null - namespaceSelector means "this pod's - namespace". + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". items: type: string type: array topologyKey: - description: This pod should be co-located - (affinity) or not co-located (anti-affinity) - with the pods matching the labelSelector - in the specified namespaces, where co-located - is defined as running on a node whose - value of the label with key topologyKey - matches that of any node on which any - of the selected pods is running. Empty - topologyKey is not allowed. + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. type: string required: - topologyKey type: object weight: - description: weight associated with matching - the corresponding podAffinityTerm, in the - range 1-100. + description: |- + weight associated with matching the corresponding podAffinityTerm, + in the range 1-100. format: int32 type: integer required: @@ -471,24 +406,21 @@ spec: type: object type: array requiredDuringSchedulingIgnoredDuringExecution: - description: If the affinity requirements specified - by this field are not met at scheduling time, - the pod will not be scheduled onto the node. If - the affinity requirements specified by this field - cease to be met at some point during pod execution - (e.g. due to a pod label update), the system may - or may not try to eventually evict the pod from - its node. When there are multiple elements, the - lists of nodes corresponding to each podAffinityTerm - are intersected, i.e. all terms must be satisfied. + description: |- + If the affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to a pod label update), the + system may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding to each + podAffinityTerm are intersected, i.e. all terms must be satisfied. items: - description: Defines a set of pods (namely those - matching the labelSelector relative to the given - namespace(s)) that this pod should be co-located - (affinity) or not co-located (anti-affinity) - with, where co-located is defined as running - on a node whose value of the label with key - matches that of any node on which + description: |- + Defines a set of pods (namely those matching the labelSelector + relative to the given namespace(s)) that this pod should be + co-located (affinity) or not co-located (anti-affinity) with, + where co-located is defined as running on a node whose value of + the label with key matches that of any node on which a pod of the set of pods is running properties: labelSelector: @@ -500,29 +432,24 @@ spec: of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. properties: key: description: key is the label key that the selector applies to. type: string operator: - description: operator represents - a key's relationship to a set - of values. Valid operators are - In, NotIn, Exists and DoesNotExist. + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array - of string values. If the operator - is In or NotIn, the values array - must be non-empty. If the operator - is Exists or DoesNotExist, the - values array must be empty. This - array is replaced during a strategic + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic merge patch. items: type: string @@ -535,53 +462,44 @@ spec: matchLabels: additionalProperties: type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are ANDed. + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic namespaceSelector: - description: A label query over the set of - namespaces that the term applies to. The - term is applied to the union of the namespaces - selected by this field and the ones listed - in the namespaces field. null selector and - null or empty namespaces list means "this - pod's namespace". An empty selector ({}) - matches all namespaces. + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. properties: matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. properties: key: description: key is the label key that the selector applies to. type: string operator: - description: operator represents - a key's relationship to a set - of values. Valid operators are - In, NotIn, Exists and DoesNotExist. + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array - of string values. If the operator - is In or NotIn, the values array - must be non-empty. If the operator - is Exists or DoesNotExist, the - values array must be empty. This - array is replaced during a strategic + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic merge patch. items: type: string @@ -594,36 +512,29 @@ spec: matchLabels: additionalProperties: type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are ANDed. + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic namespaces: - description: namespaces specifies a static - list of namespace names that the term applies - to. The term is applied to the union of - the namespaces listed in this field and - the ones selected by namespaceSelector. - null or empty namespaces list and null namespaceSelector - means "this pod's namespace". + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". items: type: string type: array topologyKey: - description: This pod should be co-located - (affinity) or not co-located (anti-affinity) - with the pods matching the labelSelector - in the specified namespaces, where co-located - is defined as running on a node whose value - of the label with key topologyKey matches - that of any node on which any of the selected - pods is running. Empty topologyKey is not - allowed. + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. type: string required: - topologyKey @@ -636,18 +547,15 @@ spec: zone, etc. as some other pod(s)). properties: preferredDuringSchedulingIgnoredDuringExecution: - description: The scheduler will prefer to schedule - pods to nodes that satisfy the anti-affinity expressions - specified by this field, but it may choose a node - that violates one or more of the expressions. - The node that is most preferred is the one with - the greatest sum of weights, i.e. for each node - that meets all of the scheduling requirements - (resource request, requiredDuringScheduling anti-affinity - expressions, etc.), compute a sum by iterating - through the elements of this field and adding - "weight" to the sum if the node has pods which - matches the corresponding podAffinityTerm; the + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the anti-affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling anti-affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the node(s) with the highest sum are the most preferred. items: description: The weights of all of the matched @@ -667,10 +575,9 @@ spec: list of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. properties: key: description: key is the label @@ -678,21 +585,15 @@ spec: to. type: string operator: - description: operator represents - a key's relationship to a - set of values. Valid operators - are In, NotIn, Exists and - DoesNotExist. + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array - of string values. If the operator - is In or NotIn, the values - array must be non-empty. If - the operator is Exists or - DoesNotExist, the values array - must be empty. This array - is replaced during a strategic + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic merge patch. items: type: string @@ -705,25 +606,19 @@ spec: matchLabels: additionalProperties: type: string - description: matchLabels is a map - of {key,value} pairs. A single {key,value} - in the matchLabels map is equivalent - to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are - ANDed. + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic namespaceSelector: - description: A label query over the set - of namespaces that the term applies - to. The term is applied to the union - of the namespaces selected by this field - and the ones listed in the namespaces - field. null selector and null or empty - namespaces list means "this pod's namespace". + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". An empty selector ({}) matches all namespaces. properties: matchExpressions: @@ -731,10 +626,9 @@ spec: list of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. properties: key: description: key is the label @@ -742,21 +636,15 @@ spec: to. type: string operator: - description: operator represents - a key's relationship to a - set of values. Valid operators - are In, NotIn, Exists and - DoesNotExist. + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array - of string values. If the operator - is In or NotIn, the values - array must be non-empty. If - the operator is Exists or - DoesNotExist, the values array - must be empty. This array - is replaced during a strategic + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic merge patch. items: type: string @@ -769,47 +657,37 @@ spec: matchLabels: additionalProperties: type: string - description: matchLabels is a map - of {key,value} pairs. A single {key,value} - in the matchLabels map is equivalent - to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are - ANDed. + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic namespaces: - description: namespaces specifies a static - list of namespace names that the term - applies to. The term is applied to the - union of the namespaces listed in this - field and the ones selected by namespaceSelector. - null or empty namespaces list and null - namespaceSelector means "this pod's - namespace". + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". items: type: string type: array topologyKey: - description: This pod should be co-located - (affinity) or not co-located (anti-affinity) - with the pods matching the labelSelector - in the specified namespaces, where co-located - is defined as running on a node whose - value of the label with key topologyKey - matches that of any node on which any - of the selected pods is running. Empty - topologyKey is not allowed. + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. type: string required: - topologyKey type: object weight: - description: weight associated with matching - the corresponding podAffinityTerm, in the - range 1-100. + description: |- + weight associated with matching the corresponding podAffinityTerm, + in the range 1-100. format: int32 type: integer required: @@ -818,25 +696,21 @@ spec: type: object type: array requiredDuringSchedulingIgnoredDuringExecution: - description: If the anti-affinity requirements specified - by this field are not met at scheduling time, - the pod will not be scheduled onto the node. If - the anti-affinity requirements specified by this - field cease to be met at some point during pod - execution (e.g. due to a pod label update), the - system may or may not try to eventually evict - the pod from its node. When there are multiple - elements, the lists of nodes corresponding to - each podAffinityTerm are intersected, i.e. all - terms must be satisfied. + description: |- + If the anti-affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the anti-affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to a pod label update), the + system may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding to each + podAffinityTerm are intersected, i.e. all terms must be satisfied. items: - description: Defines a set of pods (namely those - matching the labelSelector relative to the given - namespace(s)) that this pod should be co-located - (affinity) or not co-located (anti-affinity) - with, where co-located is defined as running - on a node whose value of the label with key - matches that of any node on which + description: |- + Defines a set of pods (namely those matching the labelSelector + relative to the given namespace(s)) that this pod should be + co-located (affinity) or not co-located (anti-affinity) with, + where co-located is defined as running on a node whose value of + the label with key matches that of any node on which a pod of the set of pods is running properties: labelSelector: @@ -848,29 +722,24 @@ spec: of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. properties: key: description: key is the label key that the selector applies to. type: string operator: - description: operator represents - a key's relationship to a set - of values. Valid operators are - In, NotIn, Exists and DoesNotExist. + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array - of string values. If the operator - is In or NotIn, the values array - must be non-empty. If the operator - is Exists or DoesNotExist, the - values array must be empty. This - array is replaced during a strategic + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic merge patch. items: type: string @@ -883,53 +752,44 @@ spec: matchLabels: additionalProperties: type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are ANDed. + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic namespaceSelector: - description: A label query over the set of - namespaces that the term applies to. The - term is applied to the union of the namespaces - selected by this field and the ones listed - in the namespaces field. null selector and - null or empty namespaces list means "this - pod's namespace". An empty selector ({}) - matches all namespaces. + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. properties: matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. properties: key: description: key is the label key that the selector applies to. type: string operator: - description: operator represents - a key's relationship to a set - of values. Valid operators are - In, NotIn, Exists and DoesNotExist. + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array - of string values. If the operator - is In or NotIn, the values array - must be non-empty. If the operator - is Exists or DoesNotExist, the - values array must be empty. This - array is replaced during a strategic + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic merge patch. items: type: string @@ -942,36 +802,29 @@ spec: matchLabels: additionalProperties: type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are ANDed. + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic namespaces: - description: namespaces specifies a static - list of namespace names that the term applies - to. The term is applied to the union of - the namespaces listed in this field and - the ones selected by namespaceSelector. - null or empty namespaces list and null namespaceSelector - means "this pod's namespace". + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". items: type: string type: array topologyKey: - description: This pod should be co-located - (affinity) or not co-located (anti-affinity) - with the pods matching the labelSelector - in the specified namespaces, where co-located - is defined as running on a node whose value - of the label with key topologyKey matches - that of any node on which any of the selected - pods is running. Empty topologyKey is not - allowed. + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. type: string required: - topologyKey @@ -993,17 +846,16 @@ spec: be a C_IDENTIFIER. type: string value: - description: 'Variable references $(VAR_NAME) are - expanded using the previously defined environment - variables in the container and any service environment - variables. If a variable cannot be resolved, the - reference in the input string will be unchanged. - Double $$ are reduced to a single $, which allows - for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" - will produce the string literal "$(VAR_NAME)". Escaped - references will never be expanded, regardless of - whether the variable exists or not. Defaults to - "".' + description: |- + Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in the container and + any service environment variables. If a variable cannot be resolved, + the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. + "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". + Escaped references will never be expanded, regardless of whether the variable + exists or not. + Defaults to "". type: string valueFrom: description: Source for the environment variable's @@ -1016,10 +868,10 @@ spec: description: The key to select. type: string name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? type: string optional: description: Specify whether the ConfigMap @@ -1030,11 +882,9 @@ spec: type: object x-kubernetes-map-type: atomic fieldRef: - description: 'Selects a field of the pod: supports - metadata.name, metadata.namespace, `metadata.labels['''']`, - `metadata.annotations['''']`, spec.nodeName, - spec.serviceAccountName, status.hostIP, status.podIP, - status.podIPs.' + description: |- + Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, + spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs. properties: apiVersion: description: Version of the schema the FieldPath @@ -1049,11 +899,9 @@ spec: type: object x-kubernetes-map-type: atomic resourceFieldRef: - description: 'Selects a resource of the container: - only resources limits and requests (limits.cpu, - limits.memory, limits.ephemeral-storage, requests.cpu, - requests.memory and requests.ephemeral-storage) - are currently supported.' + description: |- + Selects a resource of the container: only resources limits and requests + (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported. properties: containerName: description: 'Container name: required for @@ -1083,10 +931,10 @@ spec: from. Must be a valid secret key. type: string name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? type: string optional: description: Specify whether the Secret or @@ -1147,9 +995,9 @@ spec: type: object type: array configBundleSecret: - description: ConfigBundleSecret is the name of the Kubernetes `Secret` - in the same namespace which contains the base Quay config and extra - certs. + description: |- + ConfigBundleSecret is the name of the Kubernetes `Secret` in the same namespace + which contains the base Quay config and extra certs. type: string type: object status: @@ -1159,12 +1007,11 @@ spec: description: Conditions represent the conditions that a QuayRegistry can have. items: - description: 'Condition is a single condition of a QuayRegistry. - Conditions should follow the "abnormal-true" principle in order - to only bring the attention of users to "broken" states. Example: - a condition of `type: "Ready", status: "True"“ is less useful - and should be omitted whereas `type: "NotReady", status: "True"` - is more useful when trying to monitor when something is wrong.' + description: |- + Condition is a single condition of a QuayRegistry. + Conditions should follow the "abnormal-true" principle in order to only bring the attention of users to "broken" states. + Example: a condition of `type: "Ready", status: "True"“ is less useful and should be omitted whereas `type: "NotReady", status: "True"` + is more useful when trying to monitor when something is wrong. properties: lastTransitionTime: format: date-time diff --git a/pkg/kustomize/kustomize.go b/pkg/kustomize/kustomize.go index 389e28b53..ccb0f73be 100644 --- a/pkg/kustomize/kustomize.go +++ b/pkg/kustomize/kustomize.go @@ -434,14 +434,13 @@ func KustomizationFor( } if ctx.NeedsPgUpgrade { - if v1.ComponentIsManaged(quay.Spec.Components, v1.ComponentClair) { - componentPaths = append(componentPaths, "../components/pgupgrade/scale-down-clair") - } else { - componentPaths = append(componentPaths, "../components/pgupgrade/base") - } + componentPaths = append(componentPaths, "../components/pgupgrade/base") } if ctx.NeedsClairPgUpgrade { - componentPaths = append(componentPaths, "../components/clairpgupgrade") + if v1.ComponentIsManaged(quay.Spec.Components, v1.ComponentClair) { + componentPaths = append(componentPaths, "../components/clairpgupgrade/scale-down-clair") + } + componentPaths = append(componentPaths, "../components/clairpgupgrade/base") } images := []types.Image{} diff --git a/pkg/kustomize/kustomize_test.go b/pkg/kustomize/kustomize_test.go index db1fb266d..47a072895 100644 --- a/pkg/kustomize/kustomize_test.go +++ b/pkg/kustomize/kustomize_test.go @@ -189,7 +189,7 @@ var kustomizationForTests = []struct { "../components/clair", "../components/redis", "../components/postgres", - "../components/pgupgrade/scale-down-clair", + "../components/pgupgrade/base", }, Images: []types.Image{ {Name: "quay.io/projectquay/quay", NewName: "quay", NewTag: "latest"}, From bf69ccb6a30d09b523899db891b4e0a771bd1189 Mon Sep 17 00:00:00 2001 From: Jonathan King Date: Wed, 3 Jul 2024 10:19:17 -0400 Subject: [PATCH 2/4] pgupgrade: Update pgupgrade path (PROJQUAY-7329) - Remove base from pgupgrade path to ensure that the file location is parsed properly by kustomize package --- pkg/kustomize/kustomize.go | 2 +- pkg/kustomize/kustomize_test.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/kustomize/kustomize.go b/pkg/kustomize/kustomize.go index ccb0f73be..b2d64c5f9 100644 --- a/pkg/kustomize/kustomize.go +++ b/pkg/kustomize/kustomize.go @@ -434,7 +434,7 @@ func KustomizationFor( } if ctx.NeedsPgUpgrade { - componentPaths = append(componentPaths, "../components/pgupgrade/base") + componentPaths = append(componentPaths, "../components/pgupgrade") } if ctx.NeedsClairPgUpgrade { if v1.ComponentIsManaged(quay.Spec.Components, v1.ComponentClair) { diff --git a/pkg/kustomize/kustomize_test.go b/pkg/kustomize/kustomize_test.go index 47a072895..2ec37be44 100644 --- a/pkg/kustomize/kustomize_test.go +++ b/pkg/kustomize/kustomize_test.go @@ -189,7 +189,7 @@ var kustomizationForTests = []struct { "../components/clair", "../components/redis", "../components/postgres", - "../components/pgupgrade/base", + "../components/pgupgrade", }, Images: []types.Image{ {Name: "quay.io/projectquay/quay", NewName: "quay", NewTag: "latest"}, @@ -224,7 +224,7 @@ var kustomizationForTests = []struct { Components: []string{ "../components/redis", "../components/postgres", - "../components/pgupgrade/base", + "../components/pgupgrade", }, Images: []types.Image{ {Name: "quay.io/projectquay/quay", NewName: "quay", NewTag: "latest"}, From 7abb6925af73364275ed0c76f41b089332ac7218 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Apr 2024 11:35:48 +0000 Subject: [PATCH 3/4] build(deps): bump github.com/jackc/pgx/v4 from 4.11.0 to 4.18.2 Bumps [github.com/jackc/pgx/v4](https://github.com/jackc/pgx) from 4.11.0 to 4.18.2. - [Changelog](https://github.com/jackc/pgx/blob/v4.18.2/CHANGELOG.md) - [Commits](https://github.com/jackc/pgx/compare/v4.11.0...v4.18.2) --- updated-dependencies: - dependency-name: github.com/jackc/pgx/v4 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- go.mod | 10 +- go.sum | 40 +- vendor/github.com/jackc/pgconn/CHANGELOG.md | 68 +++ vendor/github.com/jackc/pgconn/README.md | 6 + vendor/github.com/jackc/pgconn/auth_scram.go | 32 +- vendor/github.com/jackc/pgconn/config.go | 302 ++++++++-- vendor/github.com/jackc/pgconn/defaults.go | 14 + .../jackc/pgconn/defaults_windows.go | 13 + vendor/github.com/jackc/pgconn/errors.go | 59 +- .../internal/ctxwatch/context_watcher.go | 13 +- vendor/github.com/jackc/pgconn/krb5.go | 103 ++++ vendor/github.com/jackc/pgconn/pgconn.go | 422 ++++++++++---- .../github.com/jackc/pgconn/stmtcache/lru.go | 20 +- vendor/github.com/jackc/pgproto3/v2/README.md | 6 + .../v2/authentication_cleartext_password.go | 20 +- .../jackc/pgproto3/v2/authentication_gss.go | 58 ++ .../v2/authentication_gss_continue.go | 67 +++ .../v2/authentication_md5_password.go | 41 +- .../jackc/pgproto3/v2/authentication_ok.go | 20 +- .../jackc/pgproto3/v2/authentication_sasl.go | 23 +- .../v2/authentication_sasl_continue.go | 41 +- .../pgproto3/v2/authentication_sasl_final.go | 41 +- .../github.com/jackc/pgproto3/v2/backend.go | 114 +++- .../jackc/pgproto3/v2/backend_key_data.go | 7 +- vendor/github.com/jackc/pgproto3/v2/bind.go | 54 +- .../jackc/pgproto3/v2/bind_complete.go | 4 +- .../jackc/pgproto3/v2/cancel_request.go | 4 +- vendor/github.com/jackc/pgproto3/v2/close.go | 39 +- .../jackc/pgproto3/v2/close_complete.go | 4 +- .../jackc/pgproto3/v2/command_complete.go | 32 +- .../jackc/pgproto3/v2/copy_both_response.go | 41 +- .../github.com/jackc/pgproto3/v2/copy_data.go | 27 +- .../github.com/jackc/pgproto3/v2/copy_done.go | 4 +- .../github.com/jackc/pgproto3/v2/copy_fail.go | 14 +- .../jackc/pgproto3/v2/copy_in_response.go | 39 +- .../jackc/pgproto3/v2/copy_out_response.go | 39 +- .../github.com/jackc/pgproto3/v2/data_row.go | 40 +- .../github.com/jackc/pgproto3/v2/describe.go | 38 +- .../jackc/pgproto3/v2/empty_query_response.go | 4 +- .../jackc/pgproto3/v2/error_response.go | 278 ++++++--- .../github.com/jackc/pgproto3/v2/execute.go | 13 +- vendor/github.com/jackc/pgproto3/v2/flush.go | 4 +- .../github.com/jackc/pgproto3/v2/frontend.go | 38 +- .../jackc/pgproto3/v2/function_call.go | 102 ++++ .../pgproto3/v2/function_call_response.go | 28 +- .../jackc/pgproto3/v2/gss_enc_request.go | 4 +- .../jackc/pgproto3/v2/gss_response.go | 46 ++ .../github.com/jackc/pgproto3/v2/no_data.go | 4 +- .../jackc/pgproto3/v2/notice_response.go | 6 +- .../pgproto3/v2/notification_response.go | 12 +- .../pgproto3/v2/parameter_description.go | 15 +- .../jackc/pgproto3/v2/parameter_status.go | 14 +- vendor/github.com/jackc/pgproto3/v2/parse.go | 15 +- .../jackc/pgproto3/v2/parse_complete.go | 4 +- .../jackc/pgproto3/v2/password_message.go | 14 +- .../github.com/jackc/pgproto3/v2/pgproto3.go | 53 +- .../jackc/pgproto3/v2/portal_suspended.go | 4 +- vendor/github.com/jackc/pgproto3/v2/query.go | 11 +- .../jackc/pgproto3/v2/ready_for_query.go | 25 +- .../jackc/pgproto3/v2/row_description.go | 46 +- .../pgproto3/v2/sasl_initial_response.go | 32 +- .../jackc/pgproto3/v2/sasl_response.go | 26 +- .../jackc/pgproto3/v2/ssl_request.go | 4 +- .../jackc/pgproto3/v2/startup_message.go | 6 +- vendor/github.com/jackc/pgproto3/v2/sync.go | 4 +- .../github.com/jackc/pgproto3/v2/terminate.go | 4 +- vendor/github.com/jackc/pgtype/.travis.yml | 34 -- vendor/github.com/jackc/pgtype/CHANGELOG.md | 72 +++ vendor/github.com/jackc/pgtype/README.md | 6 + vendor/github.com/jackc/pgtype/array.go | 2 +- vendor/github.com/jackc/pgtype/array_type.go | 2 +- vendor/github.com/jackc/pgtype/bpchar.go | 21 +- vendor/github.com/jackc/pgtype/cidr.go | 12 + .../github.com/jackc/pgtype/composite_type.go | 4 + vendor/github.com/jackc/pgtype/convert.go | 45 +- vendor/github.com/jackc/pgtype/date.go | 41 +- vendor/github.com/jackc/pgtype/enum_type.go | 2 +- vendor/github.com/jackc/pgtype/float8.go | 2 +- vendor/github.com/jackc/pgtype/hstore.go | 30 +- vendor/github.com/jackc/pgtype/inet.go | 76 ++- vendor/github.com/jackc/pgtype/int2.go | 17 + .../jackc/pgtype/int4_multirange.go | 239 ++++++++ .../jackc/pgtype/int8_multirange.go | 239 ++++++++ vendor/github.com/jackc/pgtype/interval.go | 2 +- vendor/github.com/jackc/pgtype/json.go | 4 + vendor/github.com/jackc/pgtype/json_array.go | 546 ++++++++++++++++++ vendor/github.com/jackc/pgtype/jsonb_array.go | 29 + vendor/github.com/jackc/pgtype/lseg.go | 2 +- vendor/github.com/jackc/pgtype/ltree.go | 72 +++ vendor/github.com/jackc/pgtype/multirange.go | 83 +++ .../github.com/jackc/pgtype/num_multirange.go | 239 ++++++++ vendor/github.com/jackc/pgtype/numeric.go | 151 ++++- vendor/github.com/jackc/pgtype/pgtype.go | 208 ++++--- vendor/github.com/jackc/pgtype/record.go | 2 +- .../github.com/jackc/pgtype/record_array.go | 318 ++++++++++ vendor/github.com/jackc/pgtype/text.go | 30 + vendor/github.com/jackc/pgtype/timestamp.go | 24 +- vendor/github.com/jackc/pgtype/timestamptz.go | 36 +- .../jackc/pgtype/typed_array.go.erb | 22 +- .../jackc/pgtype/typed_array_gen.sh | 51 +- .../jackc/pgtype/typed_multirange.go.erb | 239 ++++++++ .../jackc/pgtype/typed_multirange_gen.sh | 8 + vendor/github.com/jackc/pgtype/uuid.go | 9 +- vendor/github.com/jackc/pgx/v4/CHANGELOG.md | 111 ++++ vendor/github.com/jackc/pgx/v4/README.md | 38 +- vendor/github.com/jackc/pgx/v4/batch.go | 63 +- vendor/github.com/jackc/pgx/v4/conn.go | 111 ++-- vendor/github.com/jackc/pgx/v4/copy_from.go | 4 +- vendor/github.com/jackc/pgx/v4/doc.go | 2 +- .../jackc/pgx/v4/extended_query_builder.go | 31 +- .../pgx/v4/internal/sanitize/sanitize.go | 72 ++- .../github.com/jackc/pgx/v4/large_objects.go | 12 +- vendor/github.com/jackc/pgx/v4/logger.go | 9 + vendor/github.com/jackc/pgx/v4/rows.go | 12 +- vendor/github.com/jackc/pgx/v4/tx.go | 65 ++- vendor/github.com/jackc/pgx/v4/values.go | 2 + vendor/modules.txt | 14 +- 117 files changed, 5277 insertions(+), 923 deletions(-) create mode 100644 vendor/github.com/jackc/pgconn/krb5.go create mode 100644 vendor/github.com/jackc/pgproto3/v2/authentication_gss.go create mode 100644 vendor/github.com/jackc/pgproto3/v2/authentication_gss_continue.go create mode 100644 vendor/github.com/jackc/pgproto3/v2/function_call.go create mode 100644 vendor/github.com/jackc/pgproto3/v2/gss_response.go delete mode 100644 vendor/github.com/jackc/pgtype/.travis.yml create mode 100644 vendor/github.com/jackc/pgtype/int4_multirange.go create mode 100644 vendor/github.com/jackc/pgtype/int8_multirange.go create mode 100644 vendor/github.com/jackc/pgtype/json_array.go create mode 100644 vendor/github.com/jackc/pgtype/ltree.go create mode 100644 vendor/github.com/jackc/pgtype/multirange.go create mode 100644 vendor/github.com/jackc/pgtype/num_multirange.go create mode 100644 vendor/github.com/jackc/pgtype/record_array.go create mode 100644 vendor/github.com/jackc/pgtype/typed_multirange.go.erb create mode 100644 vendor/github.com/jackc/pgtype/typed_multirange_gen.sh diff --git a/go.mod b/go.mod index c81e7bc72..9ee660cbd 100644 --- a/go.mod +++ b/go.mod @@ -65,13 +65,13 @@ require ( github.com/google/uuid v1.3.1 // indirect github.com/imdario/mergo v0.3.16 // indirect github.com/jackc/chunkreader/v2 v2.0.1 // indirect - github.com/jackc/pgconn v1.8.1 // indirect + github.com/jackc/pgconn v1.14.3 // indirect github.com/jackc/pgio v1.0.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect - github.com/jackc/pgproto3/v2 v2.0.6 // indirect - github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect - github.com/jackc/pgtype v1.7.0 // indirect - github.com/jackc/pgx/v4 v4.11.0 // indirect + github.com/jackc/pgproto3/v2 v2.3.3 // indirect + github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect + github.com/jackc/pgtype v1.14.0 // indirect + github.com/jackc/pgx/v4 v4.18.2 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect diff --git a/go.sum b/go.sum index 07c64d1e2..5e2630d8d 100644 --- a/go.sum +++ b/go.sum @@ -176,6 +176,7 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2 github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-ldap/ldap/v3 v3.2.4 h1:PFavAq2xTgzo/loE8qNXcQaofAaqIpI4WgaLdv+1l3E= github.com/go-ldap/ldap/v3 v3.2.4/go.mod h1:iYS1MdmrmceOJ1QOTnRXrIs7i3kloqtmGQjRvjKpyMg= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= @@ -225,8 +226,9 @@ github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/me github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= -github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE= github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= +github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= @@ -354,12 +356,18 @@ github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsU github.com/jackc/pgconn v1.4.0/go.mod h1:Y2O3ZDF0q4mMacyWV3AstPJpeHXWGEetiFttmq5lahk= github.com/jackc/pgconn v1.5.0/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI= github.com/jackc/pgconn v1.5.1-0.20200601181101-fa742c524853/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI= -github.com/jackc/pgconn v1.8.1 h1:ySBX7Q87vOMqKU2bbmKbUvtYhauDFclYbNDYIE1/h6s= +github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o= github.com/jackc/pgconn v1.8.1/go.mod h1:JV6m6b6jhjdmzchES0drzCcYcAHS1OPD5xu3OZ/lE2g= +github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY= +github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= +github.com/jackc/pgconn v1.14.3 h1:bVoTr12EGANZz66nZPkMInAV/KHD2TxH9npjXXgiB3w= +github.com/jackc/pgconn v1.14.3/go.mod h1:RZbme4uasqzybK2RK5c65VsHxoyaml09lx3tXOcO/VM= github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE= github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= -github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2 h1:JVX6jT/XfzNqIjye4717ITLaNwV9mWbJx0dLCpcRzdA= github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= +github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c= +github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 h1:DadwsjnMwFjfWc9y5Wi/+Zz7xoE5ALHsRQlOctkOiHc= +github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78= @@ -368,27 +376,34 @@ github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= github.com/jackc/pgproto3/v2 v2.0.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= -github.com/jackc/pgproto3/v2 v2.0.6 h1:b1105ZGEMFe7aCvrT1Cca3VoVb4ZFMaFJLJcg/3zD+8= github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgproto3/v2 v2.3.3 h1:1HLSx5H+tXR9pW3in3zaztoEwQYRC9SQaYUHjTSUOag= +github.com/jackc/pgproto3/v2 v2.3.3/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= github.com/jackc/pgservicefile v0.0.0-20200307190119-3430c5407db8/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= -github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg= github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= github.com/jackc/pgtype v1.2.0/go.mod h1:5m2OfMh1wTK7x+Fk952IDmI4nw3nPrvtQdM0ZT4WpC0= github.com/jackc/pgtype v1.3.1-0.20200510190516-8cd94a14c75a/go.mod h1:vaogEUkALtxZMCH411K+tKzNpwzCKU+AnPzBKZ+I+Po= github.com/jackc/pgtype v1.3.1-0.20200606141011-f6355165a91c/go.mod h1:cvk9Bgu/VzJ9/lxTO5R5sf80p0DiucVtN7ZxvaC4GmQ= -github.com/jackc/pgtype v1.7.0 h1:6f4kVsW01QftE38ufBYxKciO6gyioXSC0ABIRLcZrGs= github.com/jackc/pgtype v1.7.0/go.mod h1:ZnHF+rMePVqDKaOfJVI4Q8IVvAQMryDlDkZnKOI75BE= +github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM= +github.com/jackc/pgtype v1.14.0 h1:y+xUdabmyMkJLyApYuPj38mW+aAIqCe5uuBB51rH3Vw= +github.com/jackc/pgtype v1.14.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= github.com/jackc/pgx/v4 v4.5.0/go.mod h1:EpAKPLdnTorwmPUUsqrPxy5fphV18j9q3wrfRXgo+kA= github.com/jackc/pgx/v4 v4.6.1-0.20200510190926-94ba730bb1e9/go.mod h1:t3/cdRQl6fOLDxqtlyhe9UWgfIi9R8+8v8GKV5TRA/o= github.com/jackc/pgx/v4 v4.6.1-0.20200606145419-4e5062306904/go.mod h1:ZDaNWkt9sW1JMiNn0kdYBaLelIhw7Pg4qd+Vk6tw7Hg= -github.com/jackc/pgx/v4 v4.11.0 h1:J86tSWd3Y7nKjwT/43xZBvpi04keQWx8gNC2YkdJhZI= github.com/jackc/pgx/v4 v4.11.0/go.mod h1:i62xJgdrtVDsnL3U8ekyrQXEwGNTRoG7/8r+CIdYfcc= +github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs= +github.com/jackc/pgx/v4 v4.18.2 h1:xVpYkNR5pk5bMCZGfClbO962UIqVABcAGt7ha1s/FeU= +github.com/jackc/pgx/v4 v4.18.2/go.mod h1:Ey4Oru5tH5sB6tV7hDmfWFahwF15Eb7DNXlRKx2CkVw= github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= @@ -443,8 +458,9 @@ github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.7.0 h1:h93mCPfUSkaul3Ka/VG8uZdmW1uMHDGxzu0NWHuJmHY= github.com/lib/pq v1.7.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8= +github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= @@ -632,8 +648,9 @@ github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= -github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc h1:jUIKcSPO9MoMJBbEoyE/RJoE8vz7Mb8AjvifMMwSyvY= github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= +github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= @@ -754,7 +771,10 @@ golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= @@ -876,6 +896,7 @@ golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191002063906-3421d5a6bb1c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191112214154-59a1497f0cea/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -905,6 +926,7 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= diff --git a/vendor/github.com/jackc/pgconn/CHANGELOG.md b/vendor/github.com/jackc/pgconn/CHANGELOG.md index c377b3ed4..519996c8f 100644 --- a/vendor/github.com/jackc/pgconn/CHANGELOG.md +++ b/vendor/github.com/jackc/pgconn/CHANGELOG.md @@ -1,3 +1,71 @@ +# 1.14.3 (March 4, 2024) + +* Update golang.org/x/crypto and golang.org/x/text + +# 1.14.2 (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. + +# 1.14.1 (July 19, 2023) + +* Fix: Enable failover efforts when pg_hba.conf disallows non-ssl connections (Brandon Kauffman) +* Fix: connect_timeout is not obeyed for sslmode=allow|prefer (smaher-edb) +* Optimize redundant pgpass parsing in case password is explicitly set (Aleksandr Alekseev) + +# 1.14.0 (February 11, 2023) + +* Fix: each connection attempt to new node gets own timeout (Nathan Giardina) +* Set SNI for SSL connections (Stas Kelvich) +* Fix: CopyFrom I/O race (Tommy Reilly) +* Minor dependency upgrades + +# 1.13.0 (August 6, 2022) + +* Add sslpassword support (Eric McCormack and yun.xu) +* Add prefer-standby target_session_attrs support (sergey.bashilov) +* Fix GSS ErrorResponse handling (Oliver Tan) + +# 1.12.1 (May 7, 2022) + +* Fix: setting krbspn and krbsrvname in connection string (sireax) +* Add support for Unix sockets on Windows (Eno Compton) +* Stop ignoring ErrorResponse during SCRAM auth (Rafi Shamim) + +# 1.12.0 (April 21, 2022) + +* Add pluggable GSSAPI support (Oliver Tan) +* Fix: Consider any "0A000" error a possible cached plan changed error due to locale +* Better match psql fallback behavior with multiple hosts + +# 1.11.0 (February 7, 2022) + +* Support port in ip from LookupFunc to override config (James Hartig) +* Fix TLS connection timeout (Blake Embrey) +* Add support for read-only, primary, standby, prefer-standby target_session_attributes (Oscar) +* Fix connect when receiving NoticeResponse + +# 1.10.1 (November 20, 2021) + +* Close without waiting for response (Kei Kamikawa) +* Save waiting for network round-trip in CopyFrom (Rueian) +* Fix concurrency issue with ContextWatcher +* LRU.Get always checks context for cancellation / expiration (Georges Varouchas) + +# 1.10.0 (July 24, 2021) + +* net.Timeout errors are no longer returned when a query is canceled via context. A wrapped context error is returned. + +# 1.9.0 (July 10, 2021) + +* pgconn.Timeout only is true for errors originating in pgconn (Michael Darr) +* Add defaults for sslcert, sslkey, and sslrootcert (Joshua Brindle) +* Solve issue with 'sslmode=verify-full' when there are multiple hosts (mgoddard) +* Fix default host when parsing URL without host but with port +* Allow dbname query parameter in URL conn string +* Update underlying dependencies + # 1.8.1 (March 25, 2021) * Better connection string sanitization (ip.novikov) diff --git a/vendor/github.com/jackc/pgconn/README.md b/vendor/github.com/jackc/pgconn/README.md index 1c698a118..9af04fe74 100644 --- a/vendor/github.com/jackc/pgconn/README.md +++ b/vendor/github.com/jackc/pgconn/README.md @@ -1,6 +1,12 @@ [![](https://godoc.org/github.com/jackc/pgconn?status.svg)](https://godoc.org/github.com/jackc/pgconn) ![CI](https://github.com/jackc/pgconn/workflows/CI/badge.svg) +--- + +This version is used with pgx `v4`. In pgx `v5` it is part of the https://github.com/jackc/pgx repository. + +--- + # pgconn Package pgconn is a low-level PostgreSQL database driver. It operates at nearly the same level as the C library libpq. diff --git a/vendor/github.com/jackc/pgconn/auth_scram.go b/vendor/github.com/jackc/pgconn/auth_scram.go index 6a143fcdc..1545b7c52 100644 --- a/vendor/github.com/jackc/pgconn/auth_scram.go +++ b/vendor/github.com/jackc/pgconn/auth_scram.go @@ -41,7 +41,11 @@ func (c *PgConn) scramAuth(serverAuthMechanisms []string) error { AuthMechanism: "SCRAM-SHA-256", Data: sc.clientFirstMessage(), } - _, err = c.conn.Write(saslInitialResponse.Encode(nil)) + buf, err := saslInitialResponse.Encode(nil) + if err != nil { + return err + } + _, err = c.conn.Write(buf) if err != nil { return err } @@ -60,7 +64,11 @@ func (c *PgConn) scramAuth(serverAuthMechanisms []string) error { saslResponse := &pgproto3.SASLResponse{ Data: []byte(sc.clientFinalMessage()), } - _, err = c.conn.Write(saslResponse.Encode(nil)) + buf, err = saslResponse.Encode(nil) + if err != nil { + return err + } + _, err = c.conn.Write(buf) if err != nil { return err } @@ -78,12 +86,14 @@ func (c *PgConn) rxSASLContinue() (*pgproto3.AuthenticationSASLContinue, error) if err != nil { return nil, err } - saslContinue, ok := msg.(*pgproto3.AuthenticationSASLContinue) - if ok { - return saslContinue, nil + switch m := msg.(type) { + case *pgproto3.AuthenticationSASLContinue: + return m, nil + case *pgproto3.ErrorResponse: + return nil, ErrorResponseToPgError(m) } - return nil, errors.New("expected AuthenticationSASLContinue message but received unexpected message") + return nil, fmt.Errorf("expected AuthenticationSASLContinue message but received unexpected message %T", msg) } func (c *PgConn) rxSASLFinal() (*pgproto3.AuthenticationSASLFinal, error) { @@ -91,12 +101,14 @@ func (c *PgConn) rxSASLFinal() (*pgproto3.AuthenticationSASLFinal, error) { if err != nil { return nil, err } - saslFinal, ok := msg.(*pgproto3.AuthenticationSASLFinal) - if ok { - return saslFinal, nil + switch m := msg.(type) { + case *pgproto3.AuthenticationSASLFinal: + return m, nil + case *pgproto3.ErrorResponse: + return nil, ErrorResponseToPgError(m) } - return nil, errors.New("expected AuthenticationSASLFinal message but received unexpected message") + return nil, fmt.Errorf("expected AuthenticationSASLFinal message but received unexpected message %T", msg) } type scramClient struct { diff --git a/vendor/github.com/jackc/pgconn/config.go b/vendor/github.com/jackc/pgconn/config.go index c162d3c35..36b74c4a3 100644 --- a/vendor/github.com/jackc/pgconn/config.go +++ b/vendor/github.com/jackc/pgconn/config.go @@ -4,6 +4,7 @@ import ( "context" "crypto/tls" "crypto/x509" + "encoding/pem" "errors" "fmt" "io" @@ -25,6 +26,7 @@ import ( type AfterConnectFunc func(ctx context.Context, pgconn *PgConn) error type ValidateConnectFunc func(ctx context.Context, pgconn *PgConn) error +type GetSSLPasswordFunc func(ctx context.Context) string // Config is the settings used to establish a connection to a PostgreSQL server. It must be created by ParseConfig. A // manually initialized Config will cause ConnectConfig to panic. @@ -41,7 +43,9 @@ type Config struct { BuildFrontend BuildFrontendFunc RuntimeParams map[string]string // Run-time parameters to set on connection as session default values (e.g. search_path or application_name) - Fallbacks []*FallbackConfig + KerberosSrvName string + KerberosSpn string + Fallbacks []*FallbackConfig // ValidateConnect is called during a connection attempt after a successful authentication with the PostgreSQL server. // It can be used to validate that the server is acceptable. If this returns an error the connection is closed and the next @@ -61,6 +65,13 @@ type Config struct { 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 the libpq function + // PQsetSSLKeyPassHook_OpenSSL. + GetSSLPassword GetSSLPasswordFunc +} + // Copy returns a deep copy of the config that is safe to use and modify. // The only exception is the TLSConfig field: // according to the tls.Config docs it must not be modified after creation. @@ -98,10 +109,29 @@ type FallbackConfig struct { 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). +func isAbsolutePath(path string) bool { + isWindowsPath := func(p string) bool { + if len(p) < 3 { + return false + } + drive := p[0] + colon := p[1] + backslash := p[2] + if drive >= 'A' && drive <= 'Z' && colon == ':' && backslash == '\\' { + return true + } + return false + } + return strings.HasPrefix(path, "/") || isWindowsPath(path) +} + // NetworkAddress converts a PostgreSQL host and port into network and address suitable for use with // net.Dial. func NetworkAddress(host string, port uint16) (network, address string) { - if strings.HasPrefix(host, "/") { + if isAbsolutePath(host) { network = "unix" address = filepath.Join(host, ".s.PGSQL.") + strconv.FormatInt(int64(port), 10) } else { @@ -111,10 +141,10 @@ func NetworkAddress(host string, port uint16) (network, address string) { return network, address } -// ParseConfig builds a *Config 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 (DSN style). See -// https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING for details. connString also may be +// 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 (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 DSN @@ -138,21 +168,22 @@ func NetworkAddress(host string, port uint16) (network, address string) { // ParseConfig currently recognizes the following environment variable and their parameter key word equivalents passed // via database URL or DSN: // -// PGHOST -// PGPORT -// PGDATABASE -// PGUSER -// PGPASSWORD -// PGPASSFILE -// PGSERVICE -// PGSERVICEFILE -// PGSSLMODE -// PGSSLCERT -// PGSSLKEY -// PGSSLROOTCERT -// PGAPPNAME -// PGCONNECT_TIMEOUT -// PGTARGETSESSIONATTRS +// PGHOST +// PGPORT +// PGDATABASE +// PGUSER +// PGPASSWORD +// PGPASSFILE +// PGSERVICE +// PGSERVICEFILE +// PGSSLMODE +// PGSSLCERT +// PGSSLKEY +// PGSSLROOTCERT +// PGSSLPASSWORD +// PGAPPNAME +// PGCONNECT_TIMEOUT +// PGTARGETSESSIONATTRS // // See http://www.postgresql.org/docs/11/static/libpq-envars.html for details on the meaning of environment variables. // @@ -172,23 +203,29 @@ func NetworkAddress(host string, port uint16) (network, address string) { // sslmode "prefer" this means it will first try the main Config settings which use TLS, then it will try the fallback // which does not use TLS. This can lead to an unexpected unencrypted connection if the main TLS config is manually // changed later but the unencrypted fallback is present. Ensure there are no stale fallbacks when manually setting -// TLCConfig. +// TLSConfig. // // Other known differences with libpq: // -// If a host name resolves into multiple addresses, libpq will try all addresses. pgconn will only try the first. -// // When multiple hosts are specified, libpq allows them to have different passwords set via the .pgpass file. pgconn // does not. // // In addition, ParseConfig accepts the following options: // -// min_read_buffer_size -// The minimum size of the internal read buffer. Default 8192. -// servicefile -// libpq only reads servicefile from the PGSERVICEFILE environment variable. ParseConfig accepts servicefile as a -// part of the connection string. +// min_read_buffer_size +// The minimum size of the internal read buffer. Default 8192. +// servicefile +// libpq only reads servicefile from the PGSERVICEFILE environment variable. ParseConfig accepts servicefile as a +// part of the connection string. func ParseConfig(connString string) (*Config, error) { + var parseConfigOptions ParseConfigOptions + return ParseConfigWithOptions(connString, parseConfigOptions) +} + +// ParseConfigWithOptions builds a *Config from connString and options with similar behavior to the PostgreSQL standard +// C library libpq. options contains settings that cannot be specified in a connString such as providing a function to +// get the SSL password. +func ParseConfigWithOptions(connString string, options ParseConfigOptions) (*Config, error) { defaultSettings := defaultSettings() envSettings := parseEnvSettings() @@ -248,21 +285,33 @@ func ParseConfig(connString string) (*Config, error) { config.LookupFunc = makeDefaultResolver().LookupHost notRuntimeParams := map[string]struct{}{ - "host": struct{}{}, - "port": struct{}{}, - "database": struct{}{}, - "user": struct{}{}, - "password": struct{}{}, - "passfile": struct{}{}, - "connect_timeout": struct{}{}, - "sslmode": struct{}{}, - "sslkey": struct{}{}, - "sslcert": struct{}{}, - "sslrootcert": struct{}{}, - "target_session_attrs": struct{}{}, - "min_read_buffer_size": struct{}{}, - "service": struct{}{}, - "servicefile": struct{}{}, + "host": {}, + "port": {}, + "database": {}, + "user": {}, + "password": {}, + "passfile": {}, + "connect_timeout": {}, + "sslmode": {}, + "sslkey": {}, + "sslcert": {}, + "sslrootcert": {}, + "sslpassword": {}, + "sslsni": {}, + "krbspn": {}, + "krbsrvname": {}, + "target_session_attrs": {}, + "min_read_buffer_size": {}, + "service": {}, + "servicefile": {}, + } + + // Adding kerberos configuration + if _, present := settings["krbsrvname"]; present { + config.KerberosSrvName = settings["krbsrvname"] + } + if _, present := settings["krbspn"]; present { + config.KerberosSpn = settings["krbspn"] } for k, v := range settings { @@ -297,7 +346,7 @@ func ParseConfig(connString string) (*Config, error) { tlsConfigs = append(tlsConfigs, nil) } else { var err error - tlsConfigs, err = configTLS(settings) + tlsConfigs, err = configTLS(settings, host, options) if err != nil { return nil, &parseConfigError{connString: connString, msg: "failed to configure TLS", err: err} } @@ -317,9 +366,9 @@ func ParseConfig(connString string) (*Config, error) { config.TLSConfig = fallbacks[0].TLSConfig config.Fallbacks = fallbacks[1:] - passfile, err := pgpassfile.ReadPassfile(settings["passfile"]) - if err == nil { - if config.Password == "" { + if config.Password == "" { + passfile, err := pgpassfile.ReadPassfile(settings["passfile"]) + if err == nil { host := config.Host if network, _ := NetworkAddress(config.Host, config.Port); network == "unix" { host = "localhost" @@ -329,10 +378,21 @@ func ParseConfig(connString string) (*Config, error) { } } - if settings["target_session_attrs"] == "read-write" { + switch tsa := settings["target_session_attrs"]; tsa { + case "read-write": config.ValidateConnect = ValidateConnectTargetSessionAttrsReadWrite - } else if settings["target_session_attrs"] != "any" { - return nil, &parseConfigError{connString: connString, msg: fmt.Sprintf("unknown target_session_attrs value: %v", settings["target_session_attrs"])} + case "read-only": + config.ValidateConnect = ValidateConnectTargetSessionAttrsReadOnly + case "primary": + config.ValidateConnect = ValidateConnectTargetSessionAttrsPrimary + case "standby": + config.ValidateConnect = ValidateConnectTargetSessionAttrsStandby + case "prefer-standby": + config.ValidateConnect = ValidateConnectTargetSessionAttrsPreferStandby + case "any": + // do nothing + default: + return nil, &parseConfigError{connString: connString, msg: fmt.Sprintf("unknown target_session_attrs value: %v", tsa)} } return config, nil @@ -365,7 +425,9 @@ func parseEnvSettings() map[string]string { "PGSSLMODE": "sslmode", "PGSSLKEY": "sslkey", "PGSSLCERT": "sslcert", + "PGSSLSNI": "sslsni", "PGSSLROOTCERT": "sslrootcert", + "PGSSLPASSWORD": "sslpassword", "PGTARGETSESSIONATTRS": "target_session_attrs", "PGSERVICE": "service", "PGSERVICEFILE": "servicefile", @@ -411,8 +473,12 @@ func parseURLSettings(connString string) (map[string]string, error) { if err != nil { return nil, fmt.Errorf("failed to split host:port in '%s', err: %w", host, err) } - hosts = append(hosts, h) - ports = append(ports, p) + if h != "" { + hosts = append(hosts, h) + } + if p != "" { + ports = append(ports, p) + } } if len(hosts) > 0 { settings["host"] = strings.Join(hosts, ",") @@ -426,7 +492,15 @@ func parseURLSettings(connString string) (map[string]string, error) { settings["database"] = database } + nameMap := map[string]string{ + "dbname": "database", + } + for k, v := range url.Query() { + if k2, present := nameMap[k]; present { + k = k2 + } + settings[k] = v[0] } @@ -540,17 +614,22 @@ func parseServiceSettings(servicefilePath, serviceName string) (map[string]strin // configTLS uses libpq's TLS parameters to construct []*tls.Config. It is // necessary to allow returning multiple TLS configs as sslmode "allow" and // "prefer" allow fallback. -func configTLS(settings map[string]string) ([]*tls.Config, error) { - host := settings["host"] +func configTLS(settings map[string]string, thisHost string, parseConfigOptions ParseConfigOptions) ([]*tls.Config, error) { + host := thisHost sslmode := settings["sslmode"] sslrootcert := settings["sslrootcert"] sslcert := settings["sslcert"] sslkey := settings["sslkey"] + sslpassword := settings["sslpassword"] + sslsni := settings["sslsni"] // Match libpq default behavior if sslmode == "" { sslmode = "prefer" } + if sslsni == "" { + sslsni = "1" + } tlsConfig := &tls.Config{} @@ -633,14 +712,63 @@ func configTLS(settings map[string]string) ([]*tls.Config, error) { } if sslcert != "" && sslkey != "" { - cert, err := tls.LoadX509KeyPair(sslcert, sslkey) + buf, err := ioutil.ReadFile(sslkey) if err != nil { - return nil, fmt.Errorf("unable to read cert: %w", err) + return nil, fmt.Errorf("unable to read sslkey: %w", err) } + block, _ := pem.Decode(buf) + var pemKey []byte + var decryptedKey []byte + var decryptedError error + // If PEM is encrypted, attempt to decrypt using pass phrase + if x509.IsEncryptedPEMBlock(block) { + // Attempt decryption with pass phrase + // NOTE: only supports RSA (PKCS#1) + if sslpassword != "" { + decryptedKey, decryptedError = x509.DecryptPEMBlock(block, []byte(sslpassword)) + } + //if sslpassword not provided or has decryption error when use it + //try to find sslpassword with callback function + if sslpassword == "" || decryptedError != nil { + if parseConfigOptions.GetSSLPassword != nil { + sslpassword = parseConfigOptions.GetSSLPassword(context.Background()) + } + if sslpassword == "" { + return nil, fmt.Errorf("unable to find sslpassword") + } + } + decryptedKey, decryptedError = x509.DecryptPEMBlock(block, []byte(sslpassword)) + // Should we also provide warning for PKCS#1 needed? + if decryptedError != nil { + return nil, fmt.Errorf("unable to decrypt key: %w", err) + } + pemBytes := pem.Block{ + Type: "RSA PRIVATE KEY", + Bytes: decryptedKey, + } + pemKey = pem.EncodeToMemory(&pemBytes) + } else { + pemKey = pem.EncodeToMemory(block) + } + certfile, err := ioutil.ReadFile(sslcert) + if err != nil { + return nil, fmt.Errorf("unable to read cert: %w", err) + } + cert, err := tls.X509KeyPair(certfile, pemKey) + if err != nil { + return nil, fmt.Errorf("unable to load cert: %w", err) + } tlsConfig.Certificates = []tls.Certificate{cert} } + // Set Server Name Indication (SNI), if enabled by connection parameters. + // Per RFC 6066, do not set it if the host is a literal IP address (IPv4 + // or IPv6). + if sslsni == "1" && net.ParseIP(host) == nil { + tlsConfig.ServerName = host + } + switch sslmode { case "allow": return []*tls.Config{nil, tlsConfig}, nil @@ -715,3 +843,63 @@ func ValidateConnectTargetSessionAttrsReadWrite(ctx context.Context, pgConn *PgC return nil } + +// 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() + if result.Err != nil { + return result.Err + } + + if string(result.Rows[0][0]) != "on" { + return errors.New("connection is not read only") + } + + return nil +} + +// 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() + if result.Err != nil { + return result.Err + } + + if string(result.Rows[0][0]) != "t" { + return errors.New("server is not in hot standby mode") + } + + return nil +} + +// 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() + if result.Err != nil { + return result.Err + } + + if string(result.Rows[0][0]) == "t" { + return errors.New("server is in standby mode") + } + + return nil +} + +// 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() + if result.Err != nil { + return result.Err + } + + if string(result.Rows[0][0]) != "t" { + return &NotPreferredError{err: errors.New("server is not in hot standby mode")} + } + + return nil +} diff --git a/vendor/github.com/jackc/pgconn/defaults.go b/vendor/github.com/jackc/pgconn/defaults.go index d3313481f..c7209fdd3 100644 --- a/vendor/github.com/jackc/pgconn/defaults.go +++ b/vendor/github.com/jackc/pgconn/defaults.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows package pgconn @@ -22,6 +23,19 @@ func defaultSettings() map[string]string { settings["user"] = user.Username settings["passfile"] = filepath.Join(user.HomeDir, ".pgpass") settings["servicefile"] = filepath.Join(user.HomeDir, ".pg_service.conf") + sslcert := filepath.Join(user.HomeDir, ".postgresql", "postgresql.crt") + sslkey := filepath.Join(user.HomeDir, ".postgresql", "postgresql.key") + if _, err := os.Stat(sslcert); err == nil { + if _, err := os.Stat(sslkey); err == nil { + // Both the cert and key must be present to use them, or do not use either + settings["sslcert"] = sslcert + settings["sslkey"] = sslkey + } + } + sslrootcert := filepath.Join(user.HomeDir, ".postgresql", "root.crt") + if _, err := os.Stat(sslrootcert); err == nil { + settings["sslrootcert"] = sslrootcert + } } settings["target_session_attrs"] = "any" diff --git a/vendor/github.com/jackc/pgconn/defaults_windows.go b/vendor/github.com/jackc/pgconn/defaults_windows.go index 55243700c..71eb77dba 100644 --- a/vendor/github.com/jackc/pgconn/defaults_windows.go +++ b/vendor/github.com/jackc/pgconn/defaults_windows.go @@ -29,6 +29,19 @@ func defaultSettings() map[string]string { settings["user"] = username settings["passfile"] = filepath.Join(appData, "postgresql", "pgpass.conf") settings["servicefile"] = filepath.Join(user.HomeDir, ".pg_service.conf") + sslcert := filepath.Join(appData, "postgresql", "postgresql.crt") + sslkey := filepath.Join(appData, "postgresql", "postgresql.key") + if _, err := os.Stat(sslcert); err == nil { + if _, err := os.Stat(sslkey); err == nil { + // Both the cert and key must be present to use them, or do not use either + settings["sslcert"] = sslcert + settings["sslkey"] = sslkey + } + } + sslrootcert := filepath.Join(appData, "postgresql", "root.crt") + if _, err := os.Stat(sslrootcert); err == nil { + settings["sslrootcert"] = sslrootcert + } } settings["target_session_attrs"] = "any" diff --git a/vendor/github.com/jackc/pgconn/errors.go b/vendor/github.com/jackc/pgconn/errors.go index 77adfcf0b..66d35584a 100644 --- a/vendor/github.com/jackc/pgconn/errors.go +++ b/vendor/github.com/jackc/pgconn/errors.go @@ -18,15 +18,11 @@ func SafeToRetry(err error) bool { return false } -// Timeout checks if err was was caused by a timeout. To be specific, it is true if err is or was caused 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.Canceled, context.DeadlineExceeded or an implementer of net.Error where Timeout() is true. func Timeout(err error) bool { - if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) { - return true - } - - var netErr net.Error - return errors.As(err, &netErr) && netErr.Timeout() + var timeoutErr *errTimeout + return errors.As(err, &timeoutErr) } // PgError represents an error reported by the PostgreSQL server. See @@ -110,6 +106,15 @@ func (e *parseConfigError) Unwrap() error { return e.err } +// preferContextOverNetTimeoutError returns ctx.Err() if ctx.Err() is present and err is a net.Error with Timeout() == +// true. Otherwise returns err. +func preferContextOverNetTimeoutError(ctx context.Context, err error) error { + if err, ok := err.(net.Error); ok && err.Timeout() && ctx.Err() != nil { + return &errTimeout{err: ctx.Err()} + } + return err +} + type pgconnError struct { msg string err error @@ -134,6 +139,24 @@ func (e *pgconnError) Unwrap() error { return e.err } +// errTimeout occurs when an error was caused by a timeout. Specifically, it wraps an error which is +// context.Canceled, context.DeadlineExceeded, or an implementer of net.Error where Timeout() is true. +type errTimeout struct { + err error +} + +func (e *errTimeout) Error() string { + return fmt.Sprintf("timeout: %s", e.err.Error()) +} + +func (e *errTimeout) SafeToRetry() bool { + return SafeToRetry(e.err) +} + +func (e *errTimeout) Unwrap() error { + return e.err +} + type contextAlreadyDoneError struct { err error } @@ -150,6 +173,11 @@ func (e *contextAlreadyDoneError) Unwrap() error { return e.err } +// newContextAlreadyDoneError double-wraps a context error in `contextAlreadyDoneError` and `errTimeout`. +func newContextAlreadyDoneError(ctx context.Context) (err error) { + return &errTimeout{&contextAlreadyDoneError{err: ctx.Err()}} +} + type writeError struct { err error safeToRetry bool @@ -191,3 +219,20 @@ func redactURL(u *url.URL) string { } return u.String() } + +type NotPreferredError struct { + err error + safeToRetry bool +} + +func (e *NotPreferredError) Error() string { + return fmt.Sprintf("standby server not found: %s", e.err.Error()) +} + +func (e *NotPreferredError) SafeToRetry() bool { + return e.safeToRetry +} + +func (e *NotPreferredError) Unwrap() error { + return e.err +} diff --git a/vendor/github.com/jackc/pgconn/internal/ctxwatch/context_watcher.go b/vendor/github.com/jackc/pgconn/internal/ctxwatch/context_watcher.go index 391f0b791..b39cb3ee5 100644 --- a/vendor/github.com/jackc/pgconn/internal/ctxwatch/context_watcher.go +++ b/vendor/github.com/jackc/pgconn/internal/ctxwatch/context_watcher.go @@ -2,6 +2,7 @@ package ctxwatch import ( "context" + "sync" ) // ContextWatcher watches a context and performs an action when the context is canceled. It can watch one context at a @@ -10,8 +11,10 @@ type ContextWatcher struct { onCancel func() onUnwatchAfterCancel func() unwatchChan chan struct{} - watchInProgress bool - onCancelWasCalled bool + + lock sync.Mutex + watchInProgress bool + onCancelWasCalled bool } // NewContextWatcher returns a ContextWatcher. onCancel will be called when a watched context is canceled. @@ -29,6 +32,9 @@ func NewContextWatcher(onCancel func(), onUnwatchAfterCancel func()) *ContextWat // Watch starts watching ctx. If ctx is canceled then the onCancel function passed to NewContextWatcher will be called. func (cw *ContextWatcher) Watch(ctx context.Context) { + cw.lock.Lock() + defer cw.lock.Unlock() + if cw.watchInProgress { panic("Watch already in progress") } @@ -54,6 +60,9 @@ func (cw *ContextWatcher) Watch(ctx context.Context) { // Unwatch stops watching the previously watched context. If the onCancel function passed to NewContextWatcher was // called then onUnwatchAfterCancel will also be called. func (cw *ContextWatcher) Unwatch() { + cw.lock.Lock() + defer cw.lock.Unlock() + if cw.watchInProgress { cw.unwatchChan <- struct{}{} if cw.onCancelWasCalled { diff --git a/vendor/github.com/jackc/pgconn/krb5.go b/vendor/github.com/jackc/pgconn/krb5.go new file mode 100644 index 000000000..1639b7282 --- /dev/null +++ b/vendor/github.com/jackc/pgconn/krb5.go @@ -0,0 +1,103 @@ +package pgconn + +import ( + "errors" + "fmt" + + "github.com/jackc/pgproto3/v2" +) + +// NewGSSFunc creates a GSS authentication provider, for use with +// RegisterGSSProvider. +type NewGSSFunc func() (GSS, error) + +var newGSS NewGSSFunc + +// RegisterGSSProvider registers a GSS authentication provider. For example, if +// you need to use Kerberos to authenticate with your server, add this to your +// main package: +// +// import "github.com/otan/gopgkrb5" +// +// func init() { +// pgconn.RegisterGSSProvider(func() (pgconn.GSS, error) { return gopgkrb5.NewGSS() }) +// } +func RegisterGSSProvider(newGSSArg NewGSSFunc) { + newGSS = newGSSArg +} + +// GSS provides GSSAPI authentication (e.g., Kerberos). +type GSS interface { + GetInitToken(host string, service string) ([]byte, error) + GetInitTokenFromSPN(spn string) ([]byte, error) + Continue(inToken []byte) (done bool, outToken []byte, err error) +} + +func (c *PgConn) gssAuth() error { + if newGSS == nil { + return errors.New("kerberos error: no GSSAPI provider registered, see https://github.com/otan/gopgkrb5") + } + cli, err := newGSS() + if err != nil { + return err + } + + var nextData []byte + if c.config.KerberosSpn != "" { + // Use the supplied SPN if provided. + nextData, err = cli.GetInitTokenFromSPN(c.config.KerberosSpn) + } else { + // Allow the kerberos service name to be overridden + service := "postgres" + if c.config.KerberosSrvName != "" { + service = c.config.KerberosSrvName + } + nextData, err = cli.GetInitToken(c.config.Host, service) + } + if err != nil { + return err + } + + for { + gssResponse := &pgproto3.GSSResponse{ + Data: nextData, + } + buf, err := gssResponse.Encode(nil) + if err != nil { + return err + } + _, err = c.conn.Write(buf) + if err != nil { + return err + } + resp, err := c.rxGSSContinue() + if err != nil { + return err + } + var done bool + done, nextData, err = cli.Continue(resp.Data) + if err != nil { + return err + } + if done { + break + } + } + return nil +} + +func (c *PgConn) rxGSSContinue() (*pgproto3.AuthenticationGSSContinue, error) { + msg, err := c.receiveMessage() + if err != nil { + return nil, err + } + + switch m := msg.(type) { + case *pgproto3.AuthenticationGSSContinue: + return m, nil + case *pgproto3.ErrorResponse: + return nil, ErrorResponseToPgError(m) + } + + return nil, fmt.Errorf("expected AuthenticationGSSContinue message but received unexpected message %T", msg) +} diff --git a/vendor/github.com/jackc/pgconn/pgconn.go b/vendor/github.com/jackc/pgconn/pgconn.go index 197aad4ad..894baa293 100644 --- a/vendor/github.com/jackc/pgconn/pgconn.go +++ b/vendor/github.com/jackc/pgconn/pgconn.go @@ -11,6 +11,7 @@ import ( "io" "math" "net" + "strconv" "strings" "sync" "time" @@ -44,7 +45,8 @@ type Notification struct { // DialFunc is a function that can be used to connect to a PostgreSQL server. type DialFunc func(ctx context.Context, network, addr string) (net.Conn, error) -// LookupFunc is a function that can be used to lookup IPs addrs from host. +// LookupFunc is a function that can be used to lookup IPs addrs from host. Optionally an ip:port combination can be +// returned in order to override the connection string's port. type LookupFunc func(ctx context.Context, host string) (addrs []string, err error) // BuildFrontendFunc is a function that can be used to create Frontend implementation for connection. @@ -97,7 +99,7 @@ type PgConn struct { } // Connect establishes a connection to a PostgreSQL server using the environment and connString (in URL or DSN format) -// to provide configuration. See documention for ParseConfig for details. ctx can be used to cancel a connect attempt. +// 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 { @@ -107,6 +109,18 @@ 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 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 { + return nil, err + } + + return ConnectConfig(ctx, config) +} + // Connect establishes a connection to a PostgreSQL server using config. config must have been constructed with // ParseConfig. ctx can be used to cancel a connect attempt. // @@ -114,19 +128,13 @@ func Connect(ctx context.Context, connString string) (*PgConn, error) { // 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. Otherwise, // if all attempts fail the last error is returned. -func ConnectConfig(ctx context.Context, config *Config) (pgConn *PgConn, err error) { +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") } - // ConnectTimeout restricts the whole connection process. - if config.ConnectTimeout != 0 { - var cancel context.CancelFunc - ctx, cancel = context.WithTimeout(ctx, config.ConnectTimeout) - defer cancel() - } // Simplify usage by treating primary config and fallbacks the same. fallbackConfigs := []*FallbackConfig{ { @@ -136,7 +144,7 @@ func ConnectConfig(ctx context.Context, config *Config) (pgConn *PgConn, err err }, } 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} @@ -146,17 +154,47 @@ func ConnectConfig(ctx context.Context, config *Config) (pgConn *PgConn, err err return nil, &connectError{config: config, msg: "hostname resolving error", err: errors.New("ip addr wasn't found")} } - for _, fc := range fallbackConfigs { - pgConn, err = connect(ctx, config, fc) + 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} - ERRCODE_INVALID_PASSWORD := "28P01" // worng password - ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION := "28000" // db does not exist - if pgerr.Code == ERRCODE_INVALID_PASSWORD || pgerr.Code == ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION { + 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 + } + } + } + + 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} } } @@ -180,7 +218,7 @@ func expandWithIPs(ctx context.Context, lookupFn LookupFunc, fallbacks []*Fallba for _, fb := range fallbacks { // skip resolve for unix sockets - if strings.HasPrefix(fb.Host, "/") { + if isAbsolutePath(fb.Host) { configs = append(configs, &FallbackConfig{ Host: fb.Host, Port: fb.Port, @@ -196,18 +234,32 @@ func expandWithIPs(ctx context.Context, lookupFn LookupFunc, fallbacks []*Fallba } for _, ip := range ips { - configs = append(configs, &FallbackConfig{ - Host: ip, - Port: fb.Port, - TLSConfig: fb.TLSConfig, - }) + splitIP, splitPort, err := net.SplitHostPort(ip) + if err == nil { + port, err := strconv.ParseUint(splitPort, 10, 16) + if err != nil { + return nil, fmt.Errorf("error parsing port (%s) from lookup: %w", splitPort, err) + } + configs = append(configs, &FallbackConfig{ + Host: splitIP, + Port: uint16(port), + TLSConfig: fb.TLSConfig, + }) + } else { + configs = append(configs, &FallbackConfig{ + Host: ip, + Port: fb.Port, + TLSConfig: fb.TLSConfig, + }) + } } } return configs, nil } -func connect(ctx context.Context, config *Config, fallbackConfig *FallbackConfig) (*PgConn, error) { +func connect(ctx context.Context, config *Config, fallbackConfig *FallbackConfig, + ignoreNotPreferredErr bool) (*PgConn, error) { pgConn := new(PgConn) pgConn.config = config pgConn.wbuf = make([]byte, 0, wbufLen) @@ -215,29 +267,36 @@ func connect(ctx context.Context, config *Config, fallbackConfig *FallbackConfig var err error network, address := NetworkAddress(fallbackConfig.Host, fallbackConfig.Port) - pgConn.conn, err = config.DialFunc(ctx, network, address) + netConn, err := config.DialFunc(ctx, network, address) if err != nil { + var netErr net.Error + if errors.As(err, &netErr) && netErr.Timeout() { + err = &errTimeout{err: err} + } return nil, &connectError{config: config, msg: "dial error", err: err} } - pgConn.parameterStatuses = make(map[string]string) + pgConn.conn = netConn + pgConn.contextWatcher = newContextWatcher(netConn) + pgConn.contextWatcher.Watch(ctx) if fallbackConfig.TLSConfig != nil { - if err := pgConn.startTLS(fallbackConfig.TLSConfig); err != nil { - pgConn.conn.Close() + tlsConn, err := startTLS(netConn, fallbackConfig.TLSConfig) + pgConn.contextWatcher.Unwatch() // Always unwatch `netConn` after TLS. + if err != nil { + netConn.Close() return nil, &connectError{config: config, msg: "tls error", err: err} } - } - pgConn.status = connStatusConnecting - pgConn.contextWatcher = ctxwatch.NewContextWatcher( - func() { pgConn.conn.SetDeadline(time.Date(1, 1, 1, 1, 1, 1, 1, time.UTC)) }, - func() { pgConn.conn.SetDeadline(time.Time{}) }, - ) + pgConn.conn = tlsConn + pgConn.contextWatcher = newContextWatcher(tlsConn) + pgConn.contextWatcher.Watch(ctx) + } - pgConn.contextWatcher.Watch(ctx) defer pgConn.contextWatcher.Unwatch() + pgConn.parameterStatuses = make(map[string]string) + pgConn.status = connStatusConnecting pgConn.frontend = config.BuildFrontend(pgConn.conn, pgConn.conn) startupMsg := pgproto3.StartupMessage{ @@ -255,7 +314,11 @@ func connect(ctx context.Context, config *Config, fallbackConfig *FallbackConfig startupMsg.Parameters["database"] = config.Database } - if _, err := pgConn.conn.Write(startupMsg.Encode(pgConn.wbuf)); err != nil { + buf, err := startupMsg.Encode(pgConn.wbuf) + if err != nil { + return nil, &connectError{config: config, msg: "failed to write startup message", err: err} + } + if _, err := pgConn.conn.Write(buf); err != nil { pgConn.conn.Close() return nil, &connectError{config: config, msg: "failed to write startup message", err: err} } @@ -267,7 +330,7 @@ func connect(ctx context.Context, config *Config, fallbackConfig *FallbackConfig if err, ok := err.(*PgError); ok { return nil, err } - return nil, &connectError{config: config, msg: "failed to receive message", err: err} + return nil, &connectError{config: config, msg: "failed to receive message", err: preferContextOverNetTimeoutError(ctx, err)} } switch msg := msg.(type) { @@ -295,7 +358,12 @@ func connect(ctx context.Context, config *Config, fallbackConfig *FallbackConfig pgConn.conn.Close() 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, &connectError{config: config, msg: "failed GSS auth", err: err} + } case *pgproto3.ReadyForQuery: pgConn.status = connStatusIdle if config.ValidateConnect != nil { @@ -308,12 +376,15 @@ func connect(ctx context.Context, config *Config, fallbackConfig *FallbackConfig err := config.ValidateConnect(ctx, pgConn) if err != nil { + if _, ok := err.(*NotPreferredError); ignoreNotPreferredErr && ok { + return pgConn, nil + } pgConn.conn.Close() return nil, &connectError{config: config, msg: "ValidateConnect failed", err: err} } } return pgConn, nil - case *pgproto3.ParameterStatus: + case *pgproto3.ParameterStatus, *pgproto3.NoticeResponse: // handled by ReceiveMessage case *pgproto3.ErrorResponse: pgConn.conn.Close() @@ -325,29 +396,38 @@ func connect(ctx context.Context, config *Config, fallbackConfig *FallbackConfig } } -func (pgConn *PgConn) startTLS(tlsConfig *tls.Config) (err error) { - err = binary.Write(pgConn.conn, binary.BigEndian, []int32{8, 80877103}) +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 { - return + return nil, err } response := make([]byte, 1) - if _, err = io.ReadFull(pgConn.conn, response); err != nil { - return + if _, err = io.ReadFull(conn, response); err != nil { + return nil, err } if response[0] != 'S' { - return errors.New("server refused TLS connection") + return nil, errors.New("server refused TLS connection") } - pgConn.conn = tls.Client(pgConn.conn, tlsConfig) - - return nil + return tls.Client(conn, tlsConfig), nil } func (pgConn *PgConn) txPasswordMessage(password string) (err error) { msg := &pgproto3.PasswordMessage{Password: password} - _, err = pgConn.conn.Write(msg.Encode(pgConn.wbuf)) + buf, err := msg.Encode(pgConn.wbuf) + if err != nil { + return err + } + _, err = pgConn.conn.Write(buf) return err } @@ -389,7 +469,7 @@ func (pgConn *PgConn) SendBytes(ctx context.Context, buf []byte) error { if ctx != context.Background() { select { case <-ctx.Done(): - return &contextAlreadyDoneError{err: ctx.Err()} + return newContextAlreadyDoneError(ctx) default: } pgConn.contextWatcher.Watch(ctx) @@ -421,7 +501,7 @@ func (pgConn *PgConn) ReceiveMessage(ctx context.Context) (pgproto3.BackendMessa if ctx != context.Background() { select { case <-ctx.Done(): - return nil, &contextAlreadyDoneError{err: ctx.Err()} + return nil, newContextAlreadyDoneError(ctx) default: } pgConn.contextWatcher.Watch(ctx) @@ -430,7 +510,10 @@ func (pgConn *PgConn) ReceiveMessage(ctx context.Context) (pgproto3.BackendMessa msg, err := pgConn.receiveMessage() if err != nil { - err = &pgconnError{msg: "receive message failed", err: err, safeToRetry: true} + err = &pgconnError{ + msg: "receive message failed", + err: preferContextOverNetTimeoutError(ctx, err), + safeToRetry: true} } return msg, err } @@ -451,7 +534,8 @@ func (pgConn *PgConn) peekMessage() (pgproto3.BackendMessage, error) { pgConn.bufferingReceive = false // If a timeout error happened in the background try the read again. - if netErr, ok := err.(net.Error); ok && netErr.Timeout() { + var netErr net.Error + if errors.As(err, &netErr) && netErr.Timeout() { msg, err = pgConn.frontend.Receive() } } else { @@ -460,7 +544,9 @@ func (pgConn *PgConn) peekMessage() (pgproto3.BackendMessage, error) { if err != nil { // Close on anything other than timeout error - everything else is fatal - if err, ok := err.(net.Error); !(ok && err.Timeout()) { + var netErr net.Error + isNetErr := errors.As(err, &netErr) + if !(isNetErr && netErr.Timeout()) { pgConn.asyncClose() } @@ -476,7 +562,9 @@ func (pgConn *PgConn) receiveMessage() (pgproto3.BackendMessage, error) { msg, err := pgConn.peekMessage() if err != nil { // Close on anything other than timeout error - everything else is fatal - if err, ok := err.(net.Error); !(ok && err.Timeout()) { + var netErr net.Error + isNetErr := errors.As(err, &netErr) + if !(isNetErr && netErr.Timeout()) { pgConn.asyncClose() } @@ -522,9 +610,10 @@ func (pgConn *PgConn) PID() uint32 { // TxStatus returns the current TxStatus as reported by the server in the ReadyForQuery message. // // Possible return values: -// 'I' - idle / not in transaction -// 'T' - in a transaction -// 'E' - in a failed transaction +// +// 'I' - idle / not in transaction +// 'T' - in a transaction +// 'E' - in a failed transaction // // See https://www.postgresql.org/docs/current/protocol-message-formats.html. func (pgConn *PgConn) TxStatus() byte { @@ -566,7 +655,6 @@ func (pgConn *PgConn) Close(ctx context.Context) error { // // See https://github.com/jackc/pgx/issues/637 pgConn.conn.Write([]byte{'X', 0, 0, 0, 4}) - pgConn.conn.Read(make([]byte, 1)) return pgConn.conn.Close() } @@ -593,7 +681,6 @@ func (pgConn *PgConn) asyncClose() { pgConn.conn.SetDeadline(deadline) pgConn.conn.Write([]byte{'X', 0, 0, 0, 4}) - pgConn.conn.Read(make([]byte, 1)) }() } @@ -745,7 +832,7 @@ func (pgConn *PgConn) Prepare(ctx context.Context, name, sql string, paramOIDs [ if ctx != context.Background() { select { case <-ctx.Done(): - return nil, &contextAlreadyDoneError{err: ctx.Err()} + return nil, newContextAlreadyDoneError(ctx) default: } pgConn.contextWatcher.Watch(ctx) @@ -753,9 +840,19 @@ func (pgConn *PgConn) Prepare(ctx context.Context, name, sql string, paramOIDs [ } buf := pgConn.wbuf - buf = (&pgproto3.Parse{Name: name, Query: sql, ParameterOIDs: paramOIDs}).Encode(buf) - buf = (&pgproto3.Describe{ObjectType: 'S', Name: name}).Encode(buf) - buf = (&pgproto3.Sync{}).Encode(buf) + var err error + buf, err = (&pgproto3.Parse{Name: name, Query: sql, ParameterOIDs: paramOIDs}).Encode(buf) + if err != nil { + return nil, err + } + buf, err = (&pgproto3.Describe{ObjectType: 'S', Name: name}).Encode(buf) + if err != nil { + return nil, err + } + buf, err = (&pgproto3.Sync{}).Encode(buf) + if err != nil { + return nil, err + } n, err := pgConn.conn.Write(buf) if err != nil { @@ -772,7 +869,7 @@ readloop: msg, err := pgConn.receiveMessage() if err != nil { pgConn.asyncClose() - return nil, err + return nil, preferContextOverNetTimeoutError(ctx, err) } switch msg := msg.(type) { @@ -875,7 +972,7 @@ func (pgConn *PgConn) WaitForNotification(ctx context.Context) error { if ctx != context.Background() { select { case <-ctx.Done(): - return ctx.Err() + return newContextAlreadyDoneError(ctx) default: } @@ -886,7 +983,7 @@ func (pgConn *PgConn) WaitForNotification(ctx context.Context) error { for { msg, err := pgConn.receiveMessage() if err != nil { - return err + return preferContextOverNetTimeoutError(ctx, err) } switch msg.(type) { @@ -918,7 +1015,7 @@ func (pgConn *PgConn) Exec(ctx context.Context, sql string) *MultiResultReader { select { case <-ctx.Done(): multiResult.closed = true - multiResult.err = &contextAlreadyDoneError{err: ctx.Err()} + multiResult.err = newContextAlreadyDoneError(ctx) pgConn.unlock() return multiResult default: @@ -927,7 +1024,14 @@ func (pgConn *PgConn) Exec(ctx context.Context, sql string) *MultiResultReader { } buf := pgConn.wbuf - buf = (&pgproto3.Query{String: sql}).Encode(buf) + var err error + buf, err = (&pgproto3.Query{String: sql}).Encode(buf) + if err != nil { + return &MultiResultReader{ + closed: true, + err: err, + } + } n, err := pgConn.conn.Write(buf) if err != nil { @@ -964,7 +1068,7 @@ func (pgConn *PgConn) ReceiveResults(ctx context.Context) *MultiResultReader { select { case <-ctx.Done(): multiResult.closed = true - multiResult.err = &contextAlreadyDoneError{err: ctx.Err()} + multiResult.err = newContextAlreadyDoneError(ctx) pgConn.unlock() return multiResult default: @@ -1001,8 +1105,24 @@ func (pgConn *PgConn) ExecParams(ctx context.Context, sql string, paramValues [] } buf := pgConn.wbuf - buf = (&pgproto3.Parse{Query: sql, ParameterOIDs: paramOIDs}).Encode(buf) - buf = (&pgproto3.Bind{ParameterFormatCodes: paramFormats, Parameters: paramValues, ResultFormatCodes: resultFormats}).Encode(buf) + var err error + buf, err = (&pgproto3.Parse{Query: sql, ParameterOIDs: paramOIDs}).Encode(buf) + if err != nil { + result.concludeCommand(nil, err) + pgConn.contextWatcher.Unwatch() + result.closed = true + pgConn.unlock() + return result + } + + buf, err = (&pgproto3.Bind{ParameterFormatCodes: paramFormats, Parameters: paramValues, ResultFormatCodes: resultFormats}).Encode(buf) + if err != nil { + result.concludeCommand(nil, err) + pgConn.contextWatcher.Unwatch() + result.closed = true + pgConn.unlock() + return result + } pgConn.execExtendedSuffix(buf, result) @@ -1028,7 +1148,15 @@ func (pgConn *PgConn) ExecPrepared(ctx context.Context, stmtName string, paramVa } buf := pgConn.wbuf - buf = (&pgproto3.Bind{PreparedStatement: stmtName, ParameterFormatCodes: paramFormats, Parameters: paramValues, ResultFormatCodes: resultFormats}).Encode(buf) + var err error + buf, err = (&pgproto3.Bind{PreparedStatement: stmtName, ParameterFormatCodes: paramFormats, Parameters: paramValues, ResultFormatCodes: resultFormats}).Encode(buf) + if err != nil { + result.concludeCommand(nil, err) + pgConn.contextWatcher.Unwatch() + result.closed = true + pgConn.unlock() + return result + } pgConn.execExtendedSuffix(buf, result) @@ -1058,7 +1186,7 @@ func (pgConn *PgConn) execExtendedPrefix(ctx context.Context, paramValues [][]by if ctx != context.Background() { select { case <-ctx.Done(): - result.concludeCommand(nil, &contextAlreadyDoneError{err: ctx.Err()}) + result.concludeCommand(nil, newContextAlreadyDoneError(ctx)) result.closed = true pgConn.unlock() return result @@ -1071,9 +1199,31 @@ func (pgConn *PgConn) execExtendedPrefix(ctx context.Context, paramValues [][]by } func (pgConn *PgConn) execExtendedSuffix(buf []byte, result *ResultReader) { - buf = (&pgproto3.Describe{ObjectType: 'P'}).Encode(buf) - buf = (&pgproto3.Execute{}).Encode(buf) - buf = (&pgproto3.Sync{}).Encode(buf) + var err error + buf, err = (&pgproto3.Describe{ObjectType: 'P'}).Encode(buf) + if err != nil { + result.concludeCommand(nil, err) + pgConn.contextWatcher.Unwatch() + result.closed = true + pgConn.unlock() + return + } + buf, err = (&pgproto3.Execute{}).Encode(buf) + if err != nil { + result.concludeCommand(nil, err) + pgConn.contextWatcher.Unwatch() + result.closed = true + pgConn.unlock() + return + } + buf, err = (&pgproto3.Sync{}).Encode(buf) + if err != nil { + result.concludeCommand(nil, err) + pgConn.contextWatcher.Unwatch() + result.closed = true + pgConn.unlock() + return + } n, err := pgConn.conn.Write(buf) if err != nil { @@ -1098,7 +1248,7 @@ func (pgConn *PgConn) CopyTo(ctx context.Context, w io.Writer, sql string) (Comm select { case <-ctx.Done(): pgConn.unlock() - return nil, &contextAlreadyDoneError{err: ctx.Err()} + return nil, newContextAlreadyDoneError(ctx) default: } pgConn.contextWatcher.Watch(ctx) @@ -1107,7 +1257,12 @@ func (pgConn *PgConn) CopyTo(ctx context.Context, w io.Writer, sql string) (Comm // Send copy to command buf := pgConn.wbuf - buf = (&pgproto3.Query{String: sql}).Encode(buf) + var err error + buf, err = (&pgproto3.Query{String: sql}).Encode(buf) + if err != nil { + pgConn.unlock() + return nil, err + } n, err := pgConn.conn.Write(buf) if err != nil { @@ -1123,7 +1278,7 @@ func (pgConn *PgConn) CopyTo(ctx context.Context, w io.Writer, sql string) (Comm msg, err := pgConn.receiveMessage() if err != nil { pgConn.asyncClose() - return nil, err + return nil, preferContextOverNetTimeoutError(ctx, err) } switch msg := msg.(type) { @@ -1158,7 +1313,7 @@ func (pgConn *PgConn) CopyFrom(ctx context.Context, r io.Reader, sql string) (Co if ctx != context.Background() { select { case <-ctx.Done(): - return nil, &contextAlreadyDoneError{err: ctx.Err()} + return nil, newContextAlreadyDoneError(ctx) default: } pgConn.contextWatcher.Watch(ctx) @@ -1167,7 +1322,12 @@ func (pgConn *PgConn) CopyFrom(ctx context.Context, r io.Reader, sql string) (Co // Send copy to command buf := pgConn.wbuf - buf = (&pgproto3.Query{String: sql}).Encode(buf) + var err error + buf, err = (&pgproto3.Query{String: sql}).Encode(buf) + if err != nil { + pgConn.unlock() + return nil, err + } n, err := pgConn.conn.Write(buf) if err != nil { @@ -1175,33 +1335,15 @@ func (pgConn *PgConn) CopyFrom(ctx context.Context, r io.Reader, sql string) (Co return nil, &writeError{err: err, safeToRetry: n == 0} } - // Read until copy in response or error. - var commandTag CommandTag - var pgErr error - pendingCopyInResponse := true - for pendingCopyInResponse { - msg, err := pgConn.receiveMessage() - if err != nil { - pgConn.asyncClose() - return nil, err - } - - switch msg := msg.(type) { - case *pgproto3.CopyInResponse: - pendingCopyInResponse = false - case *pgproto3.ErrorResponse: - pgErr = ErrorResponseToPgError(msg) - case *pgproto3.ReadyForQuery: - return commandTag, pgErr - } - } - // Send copy data abortCopyChan := make(chan struct{}) copyErrChan := make(chan error, 1) signalMessageChan := pgConn.signalMessage() + var wg sync.WaitGroup + wg.Add(1) go func() { + defer wg.Done() buf := make([]byte, 0, 65536) buf = append(buf, 'd') sp := len(buf) @@ -1234,6 +1376,7 @@ func (pgConn *PgConn) CopyFrom(ctx context.Context, r io.Reader, sql string) (Co } }() + var pgErr error var copyErr error for copyErr == nil && pgErr == nil { select { @@ -1242,7 +1385,7 @@ func (pgConn *PgConn) CopyFrom(ctx context.Context, r io.Reader, sql string) (Co msg, err := pgConn.receiveMessage() if err != nil { pgConn.asyncClose() - return nil, err + return nil, preferContextOverNetTimeoutError(ctx, err) } switch msg := msg.(type) { @@ -1254,14 +1397,26 @@ func (pgConn *PgConn) CopyFrom(ctx context.Context, r io.Reader, sql string) (Co } } close(abortCopyChan) + // Make sure io goroutine finishes before writing. + wg.Wait() buf = buf[:0] if copyErr == io.EOF || pgErr != nil { copyDone := &pgproto3.CopyDone{} - buf = copyDone.Encode(buf) + var err error + buf, err = copyDone.Encode(buf) + if err != nil { + pgConn.asyncClose() + return nil, err + } } else { copyFail := &pgproto3.CopyFail{Message: copyErr.Error()} - buf = copyFail.Encode(buf) + var err error + buf, err = copyFail.Encode(buf) + if err != nil { + pgConn.asyncClose() + return nil, err + } } _, err = pgConn.conn.Write(buf) if err != nil { @@ -1270,11 +1425,12 @@ func (pgConn *PgConn) CopyFrom(ctx context.Context, r io.Reader, sql string) (Co } // Read results + var commandTag CommandTag for { msg, err := pgConn.receiveMessage() if err != nil { pgConn.asyncClose() - return nil, err + return nil, preferContextOverNetTimeoutError(ctx, err) } switch msg := msg.(type) { @@ -1316,7 +1472,7 @@ func (mrr *MultiResultReader) receiveMessage() (pgproto3.BackendMessage, error) if err != nil { mrr.pgConn.contextWatcher.Unwatch() - mrr.err = err + mrr.err = preferContextOverNetTimeoutError(mrr.ctx, err) mrr.closed = true mrr.pgConn.asyncClose() return nil, mrr.err @@ -1523,6 +1679,7 @@ func (rr *ResultReader) receiveMessage() (msg pgproto3.BackendMessage, err error } if err != nil { + err = preferContextOverNetTimeoutError(rr.ctx, err) rr.concludeCommand(nil, err) rr.pgConn.contextWatcher.Unwatch() rr.closed = true @@ -1566,24 +1723,54 @@ 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) { - batch.buf = (&pgproto3.Parse{Query: sql, ParameterOIDs: paramOIDs}).Encode(batch.buf) + if batch.err != nil { + return + } + + batch.buf, batch.err = (&pgproto3.Parse{Query: sql, ParameterOIDs: paramOIDs}).Encode(batch.buf) + if batch.err != nil { + return + } 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) { - 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) + 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 + } } // 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. 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, @@ -1601,7 +1788,7 @@ func (pgConn *PgConn) ExecBatch(ctx context.Context, batch *Batch) *MultiResultR select { case <-ctx.Done(): multiResult.closed = true - multiResult.err = &contextAlreadyDoneError{err: ctx.Err()} + multiResult.err = newContextAlreadyDoneError(ctx) pgConn.unlock() return multiResult default: @@ -1609,7 +1796,13 @@ func (pgConn *PgConn) ExecBatch(ctx context.Context, batch *Batch) *MultiResultR pgConn.contextWatcher.Watch(ctx) } - batch.buf = (&pgproto3.Sync{}).Encode(batch.buf) + batch.buf, batch.err = (&pgproto3.Sync{}).Encode(batch.buf) + if batch.err != nil { + multiResult.closed = true + multiResult.err = batch.err + pgConn.unlock() + return multiResult + } // A large batch can deadlock without concurrent reading and writing. If the Write fails the underlying net.Conn is // closed. This is all that can be done without introducing a race condition or adding a concurrent safe communication @@ -1702,10 +1895,7 @@ func Construct(hc *HijackedConn) (*PgConn, error) { cleanupDone: make(chan struct{}), } - pgConn.contextWatcher = ctxwatch.NewContextWatcher( - func() { pgConn.conn.SetDeadline(time.Date(1, 1, 1, 1, 1, 1, 1, time.UTC)) }, - func() { pgConn.conn.SetDeadline(time.Time{}) }, - ) + pgConn.contextWatcher = newContextWatcher(pgConn.conn) return pgConn, nil } diff --git a/vendor/github.com/jackc/pgconn/stmtcache/lru.go b/vendor/github.com/jackc/pgconn/stmtcache/lru.go index f58f2ac34..f0fb53b9c 100644 --- a/vendor/github.com/jackc/pgconn/stmtcache/lru.go +++ b/vendor/github.com/jackc/pgconn/stmtcache/lru.go @@ -42,6 +42,14 @@ func NewLRU(conn *pgconn.PgConn, mode int, cap int) *LRU { // Get returns the prepared statement description for sql preparing or describing the sql on the server as needed. func (c *LRU) Get(ctx context.Context, sql string) (*pgconn.StatementDescription, error) { + if ctx != context.Background() { + select { + case <-ctx.Done(): + return nil, ctx.Err() + default: + } + } + // flush an outstanding bad statements txStatus := c.conn.TxStatus() if (txStatus == 'I' || txStatus == 'T') && len(c.stmtsToClear) > 0 { @@ -94,10 +102,14 @@ func (c *LRU) StatementErrored(sql string, err error) { return } - isInvalidCachedPlanError := pgErr.Severity == "ERROR" && - pgErr.Code == "0A000" && - pgErr.Message == "cached plan must not change result type" - if isInvalidCachedPlanError { + // 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" + if possibleInvalidCachedPlanError { c.stmtsToClear = append(c.stmtsToClear, sql) } } diff --git a/vendor/github.com/jackc/pgproto3/v2/README.md b/vendor/github.com/jackc/pgproto3/v2/README.md index 565b3efd5..77a31700a 100644 --- a/vendor/github.com/jackc/pgproto3/v2/README.md +++ b/vendor/github.com/jackc/pgproto3/v2/README.md @@ -1,6 +1,12 @@ [![](https://godoc.org/github.com/jackc/pgproto3?status.svg)](https://godoc.org/github.com/jackc/pgproto3) [![Build Status](https://travis-ci.org/jackc/pgproto3.svg)](https://travis-ci.org/jackc/pgproto3) +--- + +This version is used with pgx `v4`. In pgx `v5` it is part of the https://github.com/jackc/pgx repository. + +--- + # pgproto3 Package pgproto3 is a encoder and decoder of the PostgreSQL wire protocol version 3. diff --git a/vendor/github.com/jackc/pgproto3/v2/authentication_cleartext_password.go b/vendor/github.com/jackc/pgproto3/v2/authentication_cleartext_password.go index dd82c7a77..1ec219bc3 100644 --- a/vendor/github.com/jackc/pgproto3/v2/authentication_cleartext_password.go +++ b/vendor/github.com/jackc/pgproto3/v2/authentication_cleartext_password.go @@ -2,6 +2,7 @@ package pgproto3 import ( "encoding/binary" + "encoding/json" "errors" "github.com/jackc/pgio" @@ -14,6 +15,9 @@ type AuthenticationCleartextPassword struct { // Backend identifies this message as sendable by the PostgreSQL backend. func (*AuthenticationCleartextPassword) Backend() {} +// Backend identifies this message as an authentication response. +func (*AuthenticationCleartextPassword) AuthenticationResponse() {} + // Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message // type identifier and 4 byte message length. func (dst *AuthenticationCleartextPassword) Decode(src []byte) error { @@ -31,9 +35,17 @@ 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 { - dst = append(dst, 'R') - dst = pgio.AppendInt32(dst, 8) +func (src *AuthenticationCleartextPassword) Encode(dst []byte) ([]byte, error) { + dst, sp := beginMessage(dst, 'R') dst = pgio.AppendUint32(dst, AuthTypeCleartextPassword) - return dst + return finishMessage(dst, sp) +} + +// MarshalJSON implements encoding/json.Marshaler. +func (src AuthenticationCleartextPassword) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + Type string + }{ + Type: "AuthenticationCleartextPassword", + }) } diff --git a/vendor/github.com/jackc/pgproto3/v2/authentication_gss.go b/vendor/github.com/jackc/pgproto3/v2/authentication_gss.go new file mode 100644 index 000000000..425be6efa --- /dev/null +++ b/vendor/github.com/jackc/pgproto3/v2/authentication_gss.go @@ -0,0 +1,58 @@ +package pgproto3 + +import ( + "encoding/binary" + "encoding/json" + "errors" + + "github.com/jackc/pgio" +) + +type AuthenticationGSS struct{} + +func (a *AuthenticationGSS) Backend() {} + +func (a *AuthenticationGSS) AuthenticationResponse() {} + +func (a *AuthenticationGSS) Decode(src []byte) error { + if len(src) < 4 { + return errors.New("authentication message too short") + } + + authType := binary.BigEndian.Uint32(src) + + if authType != AuthTypeGSS { + return errors.New("bad auth type") + } + return nil +} + +func (a *AuthenticationGSS) Encode(dst []byte) ([]byte, error) { + dst, sp := beginMessage(dst, 'R') + dst = pgio.AppendUint32(dst, AuthTypeGSS) + return finishMessage(dst, sp) +} + +func (a *AuthenticationGSS) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + Type string + Data []byte + }{ + Type: "AuthenticationGSS", + }) +} + +func (a *AuthenticationGSS) UnmarshalJSON(data []byte) error { + // Ignore null, like in the main JSON package. + if string(data) == "null" { + return nil + } + + var msg struct { + Type string + } + if err := json.Unmarshal(data, &msg); err != nil { + return err + } + return nil +} diff --git a/vendor/github.com/jackc/pgproto3/v2/authentication_gss_continue.go b/vendor/github.com/jackc/pgproto3/v2/authentication_gss_continue.go new file mode 100644 index 000000000..42a70daf9 --- /dev/null +++ b/vendor/github.com/jackc/pgproto3/v2/authentication_gss_continue.go @@ -0,0 +1,67 @@ +package pgproto3 + +import ( + "encoding/binary" + "encoding/json" + "errors" + + "github.com/jackc/pgio" +) + +type AuthenticationGSSContinue struct { + Data []byte +} + +func (a *AuthenticationGSSContinue) Backend() {} + +func (a *AuthenticationGSSContinue) AuthenticationResponse() {} + +func (a *AuthenticationGSSContinue) Decode(src []byte) error { + if len(src) < 4 { + return errors.New("authentication message too short") + } + + authType := binary.BigEndian.Uint32(src) + + if authType != AuthTypeGSSCont { + return errors.New("bad auth type") + } + + a.Data = src[4:] + return nil +} + +func (a *AuthenticationGSSContinue) Encode(dst []byte) ([]byte, error) { + dst, sp := beginMessage(dst, 'R') + dst = pgio.AppendUint32(dst, AuthTypeGSSCont) + dst = append(dst, a.Data...) + return finishMessage(dst, sp) +} + +func (a *AuthenticationGSSContinue) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + Type string + Data []byte + }{ + Type: "AuthenticationGSSContinue", + Data: a.Data, + }) +} + +func (a *AuthenticationGSSContinue) UnmarshalJSON(data []byte) error { + // Ignore null, like in the main JSON package. + if string(data) == "null" { + return nil + } + + var msg struct { + Type string + Data []byte + } + if err := json.Unmarshal(data, &msg); err != nil { + return err + } + + a.Data = msg.Data + return nil +} diff --git a/vendor/github.com/jackc/pgproto3/v2/authentication_md5_password.go b/vendor/github.com/jackc/pgproto3/v2/authentication_md5_password.go index d505d2649..9c0f5ee08 100644 --- a/vendor/github.com/jackc/pgproto3/v2/authentication_md5_password.go +++ b/vendor/github.com/jackc/pgproto3/v2/authentication_md5_password.go @@ -2,6 +2,7 @@ package pgproto3 import ( "encoding/binary" + "encoding/json" "errors" "github.com/jackc/pgio" @@ -15,6 +16,9 @@ type AuthenticationMD5Password struct { // Backend identifies this message as sendable by the PostgreSQL backend. func (*AuthenticationMD5Password) Backend() {} +// Backend identifies this message as an authentication response. +func (*AuthenticationMD5Password) AuthenticationResponse() {} + // Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message // type identifier and 4 byte message length. func (dst *AuthenticationMD5Password) Decode(src []byte) error { @@ -34,10 +38,39 @@ 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 { - dst = append(dst, 'R') - dst = pgio.AppendInt32(dst, 12) +func (src *AuthenticationMD5Password) Encode(dst []byte) ([]byte, error) { + dst, sp := beginMessage(dst, 'R') dst = pgio.AppendUint32(dst, AuthTypeMD5Password) dst = append(dst, src.Salt[:]...) - return dst + return finishMessage(dst, sp) +} + +// MarshalJSON implements encoding/json.Marshaler. +func (src AuthenticationMD5Password) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + Type string + Salt [4]byte + }{ + Type: "AuthenticationMD5Password", + Salt: src.Salt, + }) +} + +// UnmarshalJSON implements encoding/json.Unmarshaler. +func (dst *AuthenticationMD5Password) UnmarshalJSON(data []byte) error { + // Ignore null, like in the main JSON package. + if string(data) == "null" { + return nil + } + + var msg struct { + Type string + Salt [4]byte + } + if err := json.Unmarshal(data, &msg); err != nil { + return err + } + + dst.Salt = msg.Salt + return nil } diff --git a/vendor/github.com/jackc/pgproto3/v2/authentication_ok.go b/vendor/github.com/jackc/pgproto3/v2/authentication_ok.go index 7b13c6e01..021f820fe 100644 --- a/vendor/github.com/jackc/pgproto3/v2/authentication_ok.go +++ b/vendor/github.com/jackc/pgproto3/v2/authentication_ok.go @@ -2,6 +2,7 @@ package pgproto3 import ( "encoding/binary" + "encoding/json" "errors" "github.com/jackc/pgio" @@ -14,6 +15,9 @@ type AuthenticationOk struct { // Backend identifies this message as sendable by the PostgreSQL backend. func (*AuthenticationOk) Backend() {} +// Backend identifies this message as an authentication response. +func (*AuthenticationOk) AuthenticationResponse() {} + // Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message // type identifier and 4 byte message length. func (dst *AuthenticationOk) Decode(src []byte) error { @@ -31,9 +35,17 @@ 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 { - dst = append(dst, 'R') - dst = pgio.AppendInt32(dst, 8) +func (src *AuthenticationOk) Encode(dst []byte) ([]byte, error) { + dst, sp := beginMessage(dst, 'R') dst = pgio.AppendUint32(dst, AuthTypeOk) - return dst + return finishMessage(dst, sp) +} + +// MarshalJSON implements encoding/json.Marshaler. +func (src AuthenticationOk) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + Type string + }{ + Type: "AuthenticationOK", + }) } diff --git a/vendor/github.com/jackc/pgproto3/v2/authentication_sasl.go b/vendor/github.com/jackc/pgproto3/v2/authentication_sasl.go index c57ae32de..b56461cd3 100644 --- a/vendor/github.com/jackc/pgproto3/v2/authentication_sasl.go +++ b/vendor/github.com/jackc/pgproto3/v2/authentication_sasl.go @@ -3,6 +3,7 @@ package pgproto3 import ( "bytes" "encoding/binary" + "encoding/json" "errors" "github.com/jackc/pgio" @@ -16,6 +17,9 @@ type AuthenticationSASL struct { // Backend identifies this message as sendable by the PostgreSQL backend. func (*AuthenticationSASL) Backend() {} +// Backend identifies this message as an authentication response. +func (*AuthenticationSASL) AuthenticationResponse() {} + // Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message // type identifier and 4 byte message length. func (dst *AuthenticationSASL) Decode(src []byte) error { @@ -42,10 +46,8 @@ 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 { - dst = append(dst, 'R') - sp := len(dst) - dst = pgio.AppendInt32(dst, -1) +func (src *AuthenticationSASL) Encode(dst []byte) ([]byte, error) { + dst, sp := beginMessage(dst, 'R') dst = pgio.AppendUint32(dst, AuthTypeSASL) for _, s := range src.AuthMechanisms { @@ -54,7 +56,16 @@ func (src *AuthenticationSASL) Encode(dst []byte) []byte { } dst = append(dst, 0) - pgio.SetInt32(dst[sp:], int32(len(dst[sp:]))) + return finishMessage(dst, sp) +} - return dst +// MarshalJSON implements encoding/json.Marshaler. +func (src AuthenticationSASL) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + Type string + AuthMechanisms []string + }{ + Type: "AuthenticationSASL", + AuthMechanisms: src.AuthMechanisms, + }) } diff --git a/vendor/github.com/jackc/pgproto3/v2/authentication_sasl_continue.go b/vendor/github.com/jackc/pgproto3/v2/authentication_sasl_continue.go index 1b918a6ef..d405b1293 100644 --- a/vendor/github.com/jackc/pgproto3/v2/authentication_sasl_continue.go +++ b/vendor/github.com/jackc/pgproto3/v2/authentication_sasl_continue.go @@ -2,6 +2,7 @@ package pgproto3 import ( "encoding/binary" + "encoding/json" "errors" "github.com/jackc/pgio" @@ -15,6 +16,9 @@ type AuthenticationSASLContinue struct { // Backend identifies this message as sendable by the PostgreSQL backend. func (*AuthenticationSASLContinue) Backend() {} +// Backend identifies this message as an authentication response. +func (*AuthenticationSASLContinue) AuthenticationResponse() {} + // Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message // type identifier and 4 byte message length. func (dst *AuthenticationSASLContinue) Decode(src []byte) error { @@ -34,15 +38,38 @@ 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 { - dst = append(dst, 'R') - sp := len(dst) - dst = pgio.AppendInt32(dst, -1) +func (src *AuthenticationSASLContinue) Encode(dst []byte) ([]byte, error) { + dst, sp := beginMessage(dst, 'R') dst = pgio.AppendUint32(dst, AuthTypeSASLContinue) - dst = append(dst, src.Data...) + return finishMessage(dst, sp) +} + +// MarshalJSON implements encoding/json.Marshaler. +func (src AuthenticationSASLContinue) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + Type string + Data string + }{ + Type: "AuthenticationSASLContinue", + Data: string(src.Data), + }) +} - pgio.SetInt32(dst[sp:], int32(len(dst[sp:]))) +// UnmarshalJSON implements encoding/json.Unmarshaler. +func (dst *AuthenticationSASLContinue) UnmarshalJSON(data []byte) error { + // Ignore null, like in the main JSON package. + if string(data) == "null" { + return nil + } - return dst + var msg struct { + Data string + } + if err := json.Unmarshal(data, &msg); err != nil { + return err + } + + dst.Data = []byte(msg.Data) + return nil } diff --git a/vendor/github.com/jackc/pgproto3/v2/authentication_sasl_final.go b/vendor/github.com/jackc/pgproto3/v2/authentication_sasl_final.go index 11d356600..c34ac4e6b 100644 --- a/vendor/github.com/jackc/pgproto3/v2/authentication_sasl_final.go +++ b/vendor/github.com/jackc/pgproto3/v2/authentication_sasl_final.go @@ -2,6 +2,7 @@ package pgproto3 import ( "encoding/binary" + "encoding/json" "errors" "github.com/jackc/pgio" @@ -15,6 +16,9 @@ type AuthenticationSASLFinal struct { // Backend identifies this message as sendable by the PostgreSQL backend. func (*AuthenticationSASLFinal) Backend() {} +// Backend identifies this message as an authentication response. +func (*AuthenticationSASLFinal) AuthenticationResponse() {} + // Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message // type identifier and 4 byte message length. func (dst *AuthenticationSASLFinal) Decode(src []byte) error { @@ -34,15 +38,38 @@ func (dst *AuthenticationSASLFinal) 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 *AuthenticationSASLFinal) Encode(dst []byte) []byte { - dst = append(dst, 'R') - sp := len(dst) - dst = pgio.AppendInt32(dst, -1) +func (src *AuthenticationSASLFinal) Encode(dst []byte) ([]byte, error) { + dst, sp := beginMessage(dst, 'R') dst = pgio.AppendUint32(dst, AuthTypeSASLFinal) - dst = append(dst, src.Data...) + return finishMessage(dst, sp) +} + +// MarshalJSON implements encoding/json.Unmarshaler. +func (src AuthenticationSASLFinal) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + Type string + Data string + }{ + Type: "AuthenticationSASLFinal", + Data: string(src.Data), + }) +} - pgio.SetInt32(dst[sp:], int32(len(dst[sp:]))) +// UnmarshalJSON implements encoding/json.Unmarshaler. +func (dst *AuthenticationSASLFinal) UnmarshalJSON(data []byte) error { + // Ignore null, like in the main JSON package. + if string(data) == "null" { + return nil + } - return dst + var msg struct { + Data string + } + if err := json.Unmarshal(data, &msg); err != nil { + return err + } + + dst.Data = []byte(msg.Data) + return nil } diff --git a/vendor/github.com/jackc/pgproto3/v2/backend.go b/vendor/github.com/jackc/pgproto3/v2/backend.go index 1f854c693..6eabcd85f 100644 --- a/vendor/github.com/jackc/pgproto3/v2/backend.go +++ b/vendor/github.com/jackc/pgproto3/v2/backend.go @@ -2,6 +2,7 @@ package pgproto3 import ( "encoding/binary" + "errors" "fmt" "io" ) @@ -12,27 +13,35 @@ type Backend struct { w io.Writer // Frontend message flyweights - bind Bind - cancelRequest CancelRequest - _close Close - copyFail CopyFail - describe Describe - execute Execute - flush Flush - gssEncRequest GSSEncRequest - parse Parse - passwordMessage PasswordMessage - query Query - sslRequest SSLRequest - startupMessage StartupMessage - sync Sync - terminate Terminate + bind Bind + cancelRequest CancelRequest + _close Close + copyFail CopyFail + copyData CopyData + copyDone CopyDone + describe Describe + execute Execute + flush Flush + functionCall FunctionCall + gssEncRequest GSSEncRequest + parse Parse + query Query + sslRequest SSLRequest + startupMessage StartupMessage + sync Sync + terminate Terminate bodyLen int msgType byte partialMsg bool + authType uint32 } +const ( + minStartupPacketLen = 4 // minStartupPacketLen is a single 32-bit int version or code. + maxStartupPacketLen = 10000 // maxStartupPacketLen is MAX_STARTUP_PACKET_LENGTH from PG source. +) + // NewBackend creates a new Backend. func NewBackend(cr ChunkReader, w io.Writer) *Backend { return &Backend{cr: cr, w: w} @@ -40,7 +49,12 @@ func NewBackend(cr ChunkReader, w io.Writer) *Backend { // Send sends a message to the frontend. func (b *Backend) Send(msg BackendMessage) error { - _, err := b.w.Write(msg.Encode(nil)) + buf, err := msg.Encode(nil) + if err != nil { + return err + } + + _, err = b.w.Write(buf) return err } @@ -54,9 +68,13 @@ func (b *Backend) ReceiveStartupMessage() (FrontendMessage, error) { } msgSize := int(binary.BigEndian.Uint32(buf) - 4) + if msgSize < minStartupPacketLen || msgSize > maxStartupPacketLen { + return nil, fmt.Errorf("invalid length of startup packet: %d", msgSize) + } + buf, err = b.cr.Next(msgSize) if err != nil { - return nil, err + return nil, translateEOFtoErrUnexpectedEOF(err) } code := binary.BigEndian.Uint32(buf) @@ -96,12 +114,15 @@ func (b *Backend) Receive() (FrontendMessage, error) { if !b.partialMsg { header, err := b.cr.Next(5) if err != nil { - return nil, err + return nil, translateEOFtoErrUnexpectedEOF(err) } b.msgType = header[0] b.bodyLen = int(binary.BigEndian.Uint32(header[1:])) - 4 b.partialMsg = true + if b.bodyLen < 0 { + return nil, errors.New("invalid message with negative body length received") + } } var msg FrontendMessage @@ -114,14 +135,34 @@ func (b *Backend) Receive() (FrontendMessage, error) { msg = &b.describe case 'E': msg = &b.execute + case 'F': + msg = &b.functionCall case 'f': msg = &b.copyFail + case 'd': + msg = &b.copyData + case 'c': + msg = &b.copyDone case 'H': msg = &b.flush case 'P': msg = &b.parse case 'p': - msg = &b.passwordMessage + switch b.authType { + case AuthTypeSASL: + msg = &SASLInitialResponse{} + case AuthTypeSASLContinue: + msg = &SASLResponse{} + case AuthTypeSASLFinal: + msg = &SASLResponse{} + case AuthTypeGSS, AuthTypeGSSCont: + msg = &GSSResponse{} + case AuthTypeCleartextPassword, AuthTypeMD5Password: + fallthrough + default: + // to maintain backwards compatability + msg = &PasswordMessage{} + } case 'Q': msg = &b.query case 'S': @@ -134,7 +175,7 @@ func (b *Backend) Receive() (FrontendMessage, error) { msgBody, err := b.cr.Next(b.bodyLen) if err != nil { - return nil, err + return nil, translateEOFtoErrUnexpectedEOF(err) } b.partialMsg = false @@ -142,3 +183,36 @@ func (b *Backend) Receive() (FrontendMessage, error) { err = msg.Decode(msgBody) return msg, err } + +// SetAuthType sets the authentication type in the backend. +// Since multiple message types can start with 'p', SetAuthType allows +// contextual identification of FrontendMessages. For example, in the +// PG message flow documentation for PasswordMessage: +// +// Byte1('p') +// +// Identifies the message as a password response. Note that this is also used for +// GSSAPI, SSPI and SASL response messages. The exact message type can be deduced from +// the context. +// +// Since the Frontend does not know about the state of a backend, it is important +// to call SetAuthType() after an authentication request is received by the Frontend. +func (b *Backend) SetAuthType(authType uint32) error { + switch authType { + case AuthTypeOk, + AuthTypeCleartextPassword, + AuthTypeMD5Password, + AuthTypeSCMCreds, + AuthTypeGSS, + AuthTypeGSSCont, + AuthTypeSSPI, + AuthTypeSASL, + AuthTypeSASLContinue, + AuthTypeSASLFinal: + b.authType = authType + default: + return fmt.Errorf("authType not recognized: %d", authType) + } + + return nil +} diff --git a/vendor/github.com/jackc/pgproto3/v2/backend_key_data.go b/vendor/github.com/jackc/pgproto3/v2/backend_key_data.go index ca20dd259..0a3d5e55f 100644 --- a/vendor/github.com/jackc/pgproto3/v2/backend_key_data.go +++ b/vendor/github.com/jackc/pgproto3/v2/backend_key_data.go @@ -29,12 +29,11 @@ func (dst *BackendKeyData) 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 *BackendKeyData) Encode(dst []byte) []byte { - dst = append(dst, 'K') - dst = pgio.AppendUint32(dst, 12) +func (src *BackendKeyData) Encode(dst []byte) ([]byte, error) { + dst, sp := beginMessage(dst, 'K') dst = pgio.AppendUint32(dst, src.ProcessID) dst = pgio.AppendUint32(dst, src.SecretKey) - return dst + return finishMessage(dst, sp) } // MarshalJSON implements encoding/json.Marshaler. diff --git a/vendor/github.com/jackc/pgproto3/v2/bind.go b/vendor/github.com/jackc/pgproto3/v2/bind.go index 52372095d..dd5503b11 100644 --- a/vendor/github.com/jackc/pgproto3/v2/bind.go +++ b/vendor/github.com/jackc/pgproto3/v2/bind.go @@ -5,6 +5,9 @@ import ( "encoding/binary" "encoding/hex" "encoding/json" + "errors" + "fmt" + "math" "github.com/jackc/pgio" ) @@ -107,21 +110,25 @@ func (dst *Bind) 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 *Bind) Encode(dst []byte) []byte { - dst = append(dst, 'B') - sp := len(dst) - dst = pgio.AppendInt32(dst, -1) +func (src *Bind) Encode(dst []byte) ([]byte, error) { + dst, sp := beginMessage(dst, 'B') dst = append(dst, src.DestinationPortal...) dst = append(dst, 0) dst = append(dst, src.PreparedStatement...) dst = append(dst, 0) + if len(src.ParameterFormatCodes) > math.MaxUint16 { + return nil, errors.New("too many parameter format codes") + } dst = pgio.AppendUint16(dst, uint16(len(src.ParameterFormatCodes))) for _, fc := range src.ParameterFormatCodes { dst = pgio.AppendInt16(dst, fc) } + if len(src.Parameters) > math.MaxUint16 { + return nil, errors.New("too many parameters") + } dst = pgio.AppendUint16(dst, uint16(len(src.Parameters))) for _, p := range src.Parameters { if p == nil { @@ -133,14 +140,15 @@ func (src *Bind) Encode(dst []byte) []byte { dst = append(dst, p...) } + if len(src.ResultFormatCodes) > math.MaxUint16 { + return nil, errors.New("too many result format codes") + } dst = pgio.AppendUint16(dst, uint16(len(src.ResultFormatCodes))) for _, fc := range src.ResultFormatCodes { dst = pgio.AppendInt16(dst, fc) } - pgio.SetInt32(dst[sp:], int32(len(dst[sp:]))) - - return dst + return finishMessage(dst, sp) } // MarshalJSON implements encoding/json.Marshaler. @@ -181,3 +189,35 @@ func (src Bind) MarshalJSON() ([]byte, error) { ResultFormatCodes: src.ResultFormatCodes, }) } + +// UnmarshalJSON implements encoding/json.Unmarshaler. +func (dst *Bind) UnmarshalJSON(data []byte) error { + // Ignore null, like in the main JSON package. + if string(data) == "null" { + return nil + } + + var msg struct { + DestinationPortal string + PreparedStatement string + ParameterFormatCodes []int16 + Parameters []map[string]string + ResultFormatCodes []int16 + } + err := json.Unmarshal(data, &msg) + if err != nil { + return err + } + dst.DestinationPortal = msg.DestinationPortal + dst.PreparedStatement = msg.PreparedStatement + dst.ParameterFormatCodes = msg.ParameterFormatCodes + dst.Parameters = make([][]byte, len(msg.Parameters)) + dst.ResultFormatCodes = msg.ResultFormatCodes + for n, parameter := range msg.Parameters { + dst.Parameters[n], err = getValueFromJSON(parameter) + if err != nil { + return fmt.Errorf("cannot get param %d: %w", n, err) + } + } + return nil +} diff --git a/vendor/github.com/jackc/pgproto3/v2/bind_complete.go b/vendor/github.com/jackc/pgproto3/v2/bind_complete.go index 3be256c89..bacf30d88 100644 --- a/vendor/github.com/jackc/pgproto3/v2/bind_complete.go +++ b/vendor/github.com/jackc/pgproto3/v2/bind_complete.go @@ -20,8 +20,8 @@ func (dst *BindComplete) 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 *BindComplete) Encode(dst []byte) []byte { - return append(dst, '2', 0, 0, 0, 4) +func (src *BindComplete) Encode(dst []byte) ([]byte, error) { + return append(dst, '2', 0, 0, 0, 4), nil } // MarshalJSON implements encoding/json.Marshaler. diff --git a/vendor/github.com/jackc/pgproto3/v2/cancel_request.go b/vendor/github.com/jackc/pgproto3/v2/cancel_request.go index 942e404be..76acb3fc9 100644 --- a/vendor/github.com/jackc/pgproto3/v2/cancel_request.go +++ b/vendor/github.com/jackc/pgproto3/v2/cancel_request.go @@ -36,12 +36,12 @@ func (dst *CancelRequest) Decode(src []byte) error { } // Encode encodes src into dst. dst will include the 4 byte message length. -func (src *CancelRequest) Encode(dst []byte) []byte { +func (src *CancelRequest) Encode(dst []byte) ([]byte, error) { dst = pgio.AppendInt32(dst, 16) dst = pgio.AppendInt32(dst, cancelRequestCode) dst = pgio.AppendUint32(dst, src.ProcessID) dst = pgio.AppendUint32(dst, src.SecretKey) - return dst + return dst, nil } // MarshalJSON implements encoding/json.Marshaler. diff --git a/vendor/github.com/jackc/pgproto3/v2/close.go b/vendor/github.com/jackc/pgproto3/v2/close.go index 382969093..0b50f27cb 100644 --- a/vendor/github.com/jackc/pgproto3/v2/close.go +++ b/vendor/github.com/jackc/pgproto3/v2/close.go @@ -3,8 +3,7 @@ package pgproto3 import ( "bytes" "encoding/json" - - "github.com/jackc/pgio" + "errors" ) type Close struct { @@ -36,18 +35,12 @@ func (dst *Close) 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 *Close) Encode(dst []byte) []byte { - dst = append(dst, 'C') - sp := len(dst) - dst = pgio.AppendInt32(dst, -1) - +func (src *Close) Encode(dst []byte) ([]byte, error) { + dst, sp := beginMessage(dst, 'C') dst = append(dst, src.ObjectType) dst = append(dst, src.Name...) dst = append(dst, 0) - - pgio.SetInt32(dst[sp:], int32(len(dst[sp:]))) - - return dst + return finishMessage(dst, sp) } // MarshalJSON implements encoding/json.Marshaler. @@ -62,3 +55,27 @@ func (src Close) MarshalJSON() ([]byte, error) { Name: src.Name, }) } + +// UnmarshalJSON implements encoding/json.Unmarshaler. +func (dst *Close) UnmarshalJSON(data []byte) error { + // Ignore null, like in the main JSON package. + if string(data) == "null" { + return nil + } + + var msg struct { + ObjectType string + Name string + } + if err := json.Unmarshal(data, &msg); err != nil { + return err + } + + if len(msg.ObjectType) != 1 { + return errors.New("invalid length for Close.ObjectType") + } + + dst.ObjectType = byte(msg.ObjectType[0]) + dst.Name = msg.Name + return nil +} diff --git a/vendor/github.com/jackc/pgproto3/v2/close_complete.go b/vendor/github.com/jackc/pgproto3/v2/close_complete.go index 1d7b8f085..833f7a12c 100644 --- a/vendor/github.com/jackc/pgproto3/v2/close_complete.go +++ b/vendor/github.com/jackc/pgproto3/v2/close_complete.go @@ -20,8 +20,8 @@ func (dst *CloseComplete) 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 *CloseComplete) Encode(dst []byte) []byte { - return append(dst, '3', 0, 0, 0, 4) +func (src *CloseComplete) Encode(dst []byte) ([]byte, error) { + return append(dst, '3', 0, 0, 0, 4), nil } // MarshalJSON implements encoding/json.Marshaler. diff --git a/vendor/github.com/jackc/pgproto3/v2/command_complete.go b/vendor/github.com/jackc/pgproto3/v2/command_complete.go index b5106fdaf..9d822064d 100644 --- a/vendor/github.com/jackc/pgproto3/v2/command_complete.go +++ b/vendor/github.com/jackc/pgproto3/v2/command_complete.go @@ -3,8 +3,6 @@ package pgproto3 import ( "bytes" "encoding/json" - - "github.com/jackc/pgio" ) type CommandComplete struct { @@ -28,17 +26,11 @@ func (dst *CommandComplete) 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 *CommandComplete) Encode(dst []byte) []byte { - dst = append(dst, 'C') - sp := len(dst) - dst = pgio.AppendInt32(dst, -1) - +func (src *CommandComplete) Encode(dst []byte) ([]byte, error) { + dst, sp := beginMessage(dst, 'C') dst = append(dst, src.CommandTag...) dst = append(dst, 0) - - pgio.SetInt32(dst[sp:], int32(len(dst[sp:]))) - - return dst + return finishMessage(dst, sp) } // MarshalJSON implements encoding/json.Marshaler. @@ -51,3 +43,21 @@ func (src CommandComplete) MarshalJSON() ([]byte, error) { CommandTag: string(src.CommandTag), }) } + +// UnmarshalJSON implements encoding/json.Unmarshaler. +func (dst *CommandComplete) UnmarshalJSON(data []byte) error { + // Ignore null, like in the main JSON package. + if string(data) == "null" { + return nil + } + + var msg struct { + CommandTag string + } + if err := json.Unmarshal(data, &msg); err != nil { + return err + } + + dst.CommandTag = []byte(msg.CommandTag) + return nil +} diff --git a/vendor/github.com/jackc/pgproto3/v2/copy_both_response.go b/vendor/github.com/jackc/pgproto3/v2/copy_both_response.go index 2d58f820e..4bf3ef325 100644 --- a/vendor/github.com/jackc/pgproto3/v2/copy_both_response.go +++ b/vendor/github.com/jackc/pgproto3/v2/copy_both_response.go @@ -4,6 +4,8 @@ import ( "bytes" "encoding/binary" "encoding/json" + "errors" + "math" "github.com/jackc/pgio" ) @@ -43,19 +45,18 @@ func (dst *CopyBothResponse) 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 *CopyBothResponse) Encode(dst []byte) []byte { - dst = append(dst, 'W') - sp := len(dst) - dst = pgio.AppendInt32(dst, -1) - +func (src *CopyBothResponse) Encode(dst []byte) ([]byte, error) { + dst, sp := beginMessage(dst, 'W') + dst = append(dst, src.OverallFormat) + if len(src.ColumnFormatCodes) > math.MaxUint16 { + return nil, errors.New("too many column format codes") + } dst = pgio.AppendUint16(dst, uint16(len(src.ColumnFormatCodes))) for _, fc := range src.ColumnFormatCodes { dst = pgio.AppendUint16(dst, fc) } - pgio.SetInt32(dst[sp:], int32(len(dst[sp:]))) - - return dst + return finishMessage(dst, sp) } // MarshalJSON implements encoding/json.Marshaler. @@ -68,3 +69,27 @@ func (src CopyBothResponse) MarshalJSON() ([]byte, error) { ColumnFormatCodes: src.ColumnFormatCodes, }) } + +// UnmarshalJSON implements encoding/json.Unmarshaler. +func (dst *CopyBothResponse) UnmarshalJSON(data []byte) error { + // Ignore null, like in the main JSON package. + if string(data) == "null" { + return nil + } + + var msg struct { + OverallFormat string + ColumnFormatCodes []uint16 + } + if err := json.Unmarshal(data, &msg); err != nil { + return err + } + + if len(msg.OverallFormat) != 1 { + return errors.New("invalid length for CopyBothResponse.OverallFormat") + } + + dst.OverallFormat = msg.OverallFormat[0] + dst.ColumnFormatCodes = msg.ColumnFormatCodes + return nil +} diff --git a/vendor/github.com/jackc/pgproto3/v2/copy_data.go b/vendor/github.com/jackc/pgproto3/v2/copy_data.go index 7d6002fe0..89ecdd4dd 100644 --- a/vendor/github.com/jackc/pgproto3/v2/copy_data.go +++ b/vendor/github.com/jackc/pgproto3/v2/copy_data.go @@ -3,8 +3,6 @@ package pgproto3 import ( "encoding/hex" "encoding/json" - - "github.com/jackc/pgio" ) type CopyData struct { @@ -25,11 +23,10 @@ func (dst *CopyData) 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 *CopyData) Encode(dst []byte) []byte { - dst = append(dst, 'd') - dst = pgio.AppendInt32(dst, int32(4+len(src.Data))) +func (src *CopyData) Encode(dst []byte) ([]byte, error) { + dst, sp := beginMessage(dst, 'd') dst = append(dst, src.Data...) - return dst + return finishMessage(dst, sp) } // MarshalJSON implements encoding/json.Marshaler. @@ -42,3 +39,21 @@ func (src CopyData) MarshalJSON() ([]byte, error) { Data: hex.EncodeToString(src.Data), }) } + +// UnmarshalJSON implements encoding/json.Unmarshaler. +func (dst *CopyData) UnmarshalJSON(data []byte) error { + // Ignore null, like in the main JSON package. + if string(data) == "null" { + return nil + } + + var msg struct { + Data string + } + if err := json.Unmarshal(data, &msg); err != nil { + return err + } + + dst.Data = []byte(msg.Data) + return nil +} diff --git a/vendor/github.com/jackc/pgproto3/v2/copy_done.go b/vendor/github.com/jackc/pgproto3/v2/copy_done.go index 0e13282bf..040814dbd 100644 --- a/vendor/github.com/jackc/pgproto3/v2/copy_done.go +++ b/vendor/github.com/jackc/pgproto3/v2/copy_done.go @@ -24,8 +24,8 @@ func (dst *CopyDone) 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 *CopyDone) Encode(dst []byte) []byte { - return append(dst, 'c', 0, 0, 0, 4) +func (src *CopyDone) Encode(dst []byte) ([]byte, error) { + return append(dst, 'c', 0, 0, 0, 4), nil } // MarshalJSON implements encoding/json.Marshaler. diff --git a/vendor/github.com/jackc/pgproto3/v2/copy_fail.go b/vendor/github.com/jackc/pgproto3/v2/copy_fail.go index 78ff0b30b..72a85fd09 100644 --- a/vendor/github.com/jackc/pgproto3/v2/copy_fail.go +++ b/vendor/github.com/jackc/pgproto3/v2/copy_fail.go @@ -3,8 +3,6 @@ package pgproto3 import ( "bytes" "encoding/json" - - "github.com/jackc/pgio" ) type CopyFail struct { @@ -28,17 +26,11 @@ func (dst *CopyFail) 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 *CopyFail) Encode(dst []byte) []byte { - dst = append(dst, 'f') - sp := len(dst) - dst = pgio.AppendInt32(dst, -1) - +func (src *CopyFail) Encode(dst []byte) ([]byte, error) { + dst, sp := beginMessage(dst, 'f') dst = append(dst, src.Message...) dst = append(dst, 0) - - pgio.SetInt32(dst[sp:], int32(len(dst[sp:]))) - - return dst + return finishMessage(dst, sp) } // MarshalJSON implements encoding/json.Marshaler. diff --git a/vendor/github.com/jackc/pgproto3/v2/copy_in_response.go b/vendor/github.com/jackc/pgproto3/v2/copy_in_response.go index 5f2595b87..bfc3ee073 100644 --- a/vendor/github.com/jackc/pgproto3/v2/copy_in_response.go +++ b/vendor/github.com/jackc/pgproto3/v2/copy_in_response.go @@ -4,6 +4,8 @@ import ( "bytes" "encoding/binary" "encoding/json" + "errors" + "math" "github.com/jackc/pgio" ) @@ -43,20 +45,19 @@ func (dst *CopyInResponse) 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 *CopyInResponse) Encode(dst []byte) []byte { - dst = append(dst, 'G') - sp := len(dst) - dst = pgio.AppendInt32(dst, -1) +func (src *CopyInResponse) Encode(dst []byte) ([]byte, error) { + dst, sp := beginMessage(dst, 'G') dst = append(dst, src.OverallFormat) + if len(src.ColumnFormatCodes) > math.MaxUint16 { + return nil, errors.New("too many column format codes") + } dst = pgio.AppendUint16(dst, uint16(len(src.ColumnFormatCodes))) for _, fc := range src.ColumnFormatCodes { dst = pgio.AppendUint16(dst, fc) } - pgio.SetInt32(dst[sp:], int32(len(dst[sp:]))) - - return dst + return finishMessage(dst, sp) } // MarshalJSON implements encoding/json.Marshaler. @@ -69,3 +70,27 @@ func (src CopyInResponse) MarshalJSON() ([]byte, error) { ColumnFormatCodes: src.ColumnFormatCodes, }) } + +// UnmarshalJSON implements encoding/json.Unmarshaler. +func (dst *CopyInResponse) UnmarshalJSON(data []byte) error { + // Ignore null, like in the main JSON package. + if string(data) == "null" { + return nil + } + + var msg struct { + OverallFormat string + ColumnFormatCodes []uint16 + } + if err := json.Unmarshal(data, &msg); err != nil { + return err + } + + if len(msg.OverallFormat) != 1 { + return errors.New("invalid length for CopyInResponse.OverallFormat") + } + + dst.OverallFormat = msg.OverallFormat[0] + dst.ColumnFormatCodes = msg.ColumnFormatCodes + return nil +} diff --git a/vendor/github.com/jackc/pgproto3/v2/copy_out_response.go b/vendor/github.com/jackc/pgproto3/v2/copy_out_response.go index 8538dfc7d..265e35f93 100644 --- a/vendor/github.com/jackc/pgproto3/v2/copy_out_response.go +++ b/vendor/github.com/jackc/pgproto3/v2/copy_out_response.go @@ -4,6 +4,8 @@ import ( "bytes" "encoding/binary" "encoding/json" + "errors" + "math" "github.com/jackc/pgio" ) @@ -42,21 +44,20 @@ func (dst *CopyOutResponse) 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 *CopyOutResponse) Encode(dst []byte) []byte { - dst = append(dst, 'H') - sp := len(dst) - dst = pgio.AppendInt32(dst, -1) +func (src *CopyOutResponse) Encode(dst []byte) ([]byte, error) { + dst, sp := beginMessage(dst, 'H') dst = append(dst, src.OverallFormat) + if len(src.ColumnFormatCodes) > math.MaxUint16 { + return nil, errors.New("too many column format codes") + } dst = pgio.AppendUint16(dst, uint16(len(src.ColumnFormatCodes))) for _, fc := range src.ColumnFormatCodes { dst = pgio.AppendUint16(dst, fc) } - pgio.SetInt32(dst[sp:], int32(len(dst[sp:]))) - - return dst + return finishMessage(dst, sp) } // MarshalJSON implements encoding/json.Marshaler. @@ -69,3 +70,27 @@ func (src CopyOutResponse) MarshalJSON() ([]byte, error) { ColumnFormatCodes: src.ColumnFormatCodes, }) } + +// UnmarshalJSON implements encoding/json.Unmarshaler. +func (dst *CopyOutResponse) UnmarshalJSON(data []byte) error { + // Ignore null, like in the main JSON package. + if string(data) == "null" { + return nil + } + + var msg struct { + OverallFormat string + ColumnFormatCodes []uint16 + } + if err := json.Unmarshal(data, &msg); err != nil { + return err + } + + if len(msg.OverallFormat) != 1 { + return errors.New("invalid length for CopyOutResponse.OverallFormat") + } + + dst.OverallFormat = msg.OverallFormat[0] + dst.ColumnFormatCodes = msg.ColumnFormatCodes + return nil +} diff --git a/vendor/github.com/jackc/pgproto3/v2/data_row.go b/vendor/github.com/jackc/pgproto3/v2/data_row.go index 5fa3c5d8c..d755515cc 100644 --- a/vendor/github.com/jackc/pgproto3/v2/data_row.go +++ b/vendor/github.com/jackc/pgproto3/v2/data_row.go @@ -4,6 +4,8 @@ import ( "encoding/binary" "encoding/hex" "encoding/json" + "errors" + "math" "github.com/jackc/pgio" ) @@ -63,11 +65,12 @@ func (dst *DataRow) 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 *DataRow) Encode(dst []byte) []byte { - dst = append(dst, 'D') - sp := len(dst) - dst = pgio.AppendInt32(dst, -1) +func (src *DataRow) Encode(dst []byte) ([]byte, error) { + dst, sp := beginMessage(dst, 'D') + if len(src.Values) > math.MaxUint16 { + return nil, errors.New("too many values") + } dst = pgio.AppendUint16(dst, uint16(len(src.Values))) for _, v := range src.Values { if v == nil { @@ -79,9 +82,7 @@ func (src *DataRow) Encode(dst []byte) []byte { dst = append(dst, v...) } - pgio.SetInt32(dst[sp:], int32(len(dst[sp:]))) - - return dst + return finishMessage(dst, sp) } // MarshalJSON implements encoding/json.Marshaler. @@ -115,3 +116,28 @@ func (src DataRow) MarshalJSON() ([]byte, error) { Values: formattedValues, }) } + +// UnmarshalJSON implements encoding/json.Unmarshaler. +func (dst *DataRow) UnmarshalJSON(data []byte) error { + // Ignore null, like in the main JSON package. + if string(data) == "null" { + return nil + } + + var msg struct { + Values []map[string]string + } + if err := json.Unmarshal(data, &msg); err != nil { + return err + } + + dst.Values = make([][]byte, len(msg.Values)) + for n, parameter := range msg.Values { + var err error + dst.Values[n], err = getValueFromJSON(parameter) + if err != nil { + return err + } + } + return nil +} diff --git a/vendor/github.com/jackc/pgproto3/v2/describe.go b/vendor/github.com/jackc/pgproto3/v2/describe.go index 308f582e7..89feff215 100644 --- a/vendor/github.com/jackc/pgproto3/v2/describe.go +++ b/vendor/github.com/jackc/pgproto3/v2/describe.go @@ -3,8 +3,7 @@ package pgproto3 import ( "bytes" "encoding/json" - - "github.com/jackc/pgio" + "errors" ) type Describe struct { @@ -36,18 +35,12 @@ func (dst *Describe) 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 *Describe) Encode(dst []byte) []byte { - dst = append(dst, 'D') - sp := len(dst) - dst = pgio.AppendInt32(dst, -1) - +func (src *Describe) Encode(dst []byte) ([]byte, error) { + dst, sp := beginMessage(dst, 'D') dst = append(dst, src.ObjectType) dst = append(dst, src.Name...) dst = append(dst, 0) - - pgio.SetInt32(dst[sp:], int32(len(dst[sp:]))) - - return dst + return finishMessage(dst, sp) } // MarshalJSON implements encoding/json.Marshaler. @@ -62,3 +55,26 @@ func (src Describe) MarshalJSON() ([]byte, error) { Name: src.Name, }) } + +// UnmarshalJSON implements encoding/json.Unmarshaler. +func (dst *Describe) UnmarshalJSON(data []byte) error { + // Ignore null, like in the main JSON package. + if string(data) == "null" { + return nil + } + + var msg struct { + ObjectType string + Name string + } + if err := json.Unmarshal(data, &msg); err != nil { + return err + } + if len(msg.ObjectType) != 1 { + return errors.New("invalid length for Describe.ObjectType") + } + + dst.ObjectType = byte(msg.ObjectType[0]) + dst.Name = msg.Name + return nil +} diff --git a/vendor/github.com/jackc/pgproto3/v2/empty_query_response.go b/vendor/github.com/jackc/pgproto3/v2/empty_query_response.go index 2b85e744b..cb6cca073 100644 --- a/vendor/github.com/jackc/pgproto3/v2/empty_query_response.go +++ b/vendor/github.com/jackc/pgproto3/v2/empty_query_response.go @@ -20,8 +20,8 @@ func (dst *EmptyQueryResponse) 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 *EmptyQueryResponse) Encode(dst []byte) []byte { - return append(dst, 'I', 0, 0, 0, 4) +func (src *EmptyQueryResponse) Encode(dst []byte) ([]byte, error) { + return append(dst, 'I', 0, 0, 0, 4), nil } // MarshalJSON implements encoding/json.Marshaler. diff --git a/vendor/github.com/jackc/pgproto3/v2/error_response.go b/vendor/github.com/jackc/pgproto3/v2/error_response.go index d444798bc..6ef9bd061 100644 --- a/vendor/github.com/jackc/pgproto3/v2/error_response.go +++ b/vendor/github.com/jackc/pgproto3/v2/error_response.go @@ -2,28 +2,29 @@ package pgproto3 import ( "bytes" - "encoding/binary" + "encoding/json" "strconv" ) type ErrorResponse struct { - 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 + Severity string + SeverityUnlocalized string // only in 9.6 and greater + 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 UnknownFields map[byte]string } @@ -56,6 +57,8 @@ func (dst *ErrorResponse) Decode(src []byte) error { switch k { case 'S': dst.Severity = v + case 'V': + dst.SeverityUnlocalized = v case 'C': dst.Code = v case 'M': @@ -107,112 +110,217 @@ func (dst *ErrorResponse) 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 *ErrorResponse) Encode(dst []byte) []byte { - return append(dst, src.marshalBinary('E')...) +func (src *ErrorResponse) Encode(dst []byte) ([]byte, error) { + dst, sp := beginMessage(dst, 'E') + dst = src.appendFields(dst) + return finishMessage(dst, sp) } -func (src *ErrorResponse) marshalBinary(typeByte byte) []byte { - var bigEndian BigEndianBuf - buf := &bytes.Buffer{} - - buf.WriteByte(typeByte) - buf.Write(bigEndian.Uint32(0)) - +func (src *ErrorResponse) appendFields(dst []byte) []byte { if src.Severity != "" { - buf.WriteByte('S') - buf.WriteString(src.Severity) - buf.WriteByte(0) + dst = append(dst, 'S') + dst = append(dst, src.Severity...) + dst = append(dst, 0) + } + if src.SeverityUnlocalized != "" { + dst = append(dst, 'V') + dst = append(dst, src.SeverityUnlocalized...) + dst = append(dst, 0) } if src.Code != "" { - buf.WriteByte('C') - buf.WriteString(src.Code) - buf.WriteByte(0) + dst = append(dst, 'C') + dst = append(dst, src.Code...) + dst = append(dst, 0) } if src.Message != "" { - buf.WriteByte('M') - buf.WriteString(src.Message) - buf.WriteByte(0) + dst = append(dst, 'M') + dst = append(dst, src.Message...) + dst = append(dst, 0) } if src.Detail != "" { - buf.WriteByte('D') - buf.WriteString(src.Detail) - buf.WriteByte(0) + dst = append(dst, 'D') + dst = append(dst, src.Detail...) + dst = append(dst, 0) } if src.Hint != "" { - buf.WriteByte('H') - buf.WriteString(src.Hint) - buf.WriteByte(0) + dst = append(dst, 'H') + dst = append(dst, src.Hint...) + dst = append(dst, 0) } if src.Position != 0 { - buf.WriteByte('P') - buf.WriteString(strconv.Itoa(int(src.Position))) - buf.WriteByte(0) + dst = append(dst, 'P') + dst = append(dst, strconv.Itoa(int(src.Position))...) + dst = append(dst, 0) } if src.InternalPosition != 0 { - buf.WriteByte('p') - buf.WriteString(strconv.Itoa(int(src.InternalPosition))) - buf.WriteByte(0) + dst = append(dst, 'p') + dst = append(dst, strconv.Itoa(int(src.InternalPosition))...) + dst = append(dst, 0) } if src.InternalQuery != "" { - buf.WriteByte('q') - buf.WriteString(src.InternalQuery) - buf.WriteByte(0) + dst = append(dst, 'q') + dst = append(dst, src.InternalQuery...) + dst = append(dst, 0) } if src.Where != "" { - buf.WriteByte('W') - buf.WriteString(src.Where) - buf.WriteByte(0) + dst = append(dst, 'W') + dst = append(dst, src.Where...) + dst = append(dst, 0) } if src.SchemaName != "" { - buf.WriteByte('s') - buf.WriteString(src.SchemaName) - buf.WriteByte(0) + dst = append(dst, 's') + dst = append(dst, src.SchemaName...) + dst = append(dst, 0) } if src.TableName != "" { - buf.WriteByte('t') - buf.WriteString(src.TableName) - buf.WriteByte(0) + dst = append(dst, 't') + dst = append(dst, src.TableName...) + dst = append(dst, 0) } if src.ColumnName != "" { - buf.WriteByte('c') - buf.WriteString(src.ColumnName) - buf.WriteByte(0) + dst = append(dst, 'c') + dst = append(dst, src.ColumnName...) + dst = append(dst, 0) } if src.DataTypeName != "" { - buf.WriteByte('d') - buf.WriteString(src.DataTypeName) - buf.WriteByte(0) + dst = append(dst, 'd') + dst = append(dst, src.DataTypeName...) + dst = append(dst, 0) } if src.ConstraintName != "" { - buf.WriteByte('n') - buf.WriteString(src.ConstraintName) - buf.WriteByte(0) + dst = append(dst, 'n') + dst = append(dst, src.ConstraintName...) + dst = append(dst, 0) } if src.File != "" { - buf.WriteByte('F') - buf.WriteString(src.File) - buf.WriteByte(0) + dst = append(dst, 'F') + dst = append(dst, src.File...) + dst = append(dst, 0) } if src.Line != 0 { - buf.WriteByte('L') - buf.WriteString(strconv.Itoa(int(src.Line))) - buf.WriteByte(0) + dst = append(dst, 'L') + dst = append(dst, strconv.Itoa(int(src.Line))...) + dst = append(dst, 0) } if src.Routine != "" { - buf.WriteByte('R') - buf.WriteString(src.Routine) - buf.WriteByte(0) + dst = append(dst, 'R') + dst = append(dst, src.Routine...) + dst = append(dst, 0) } for k, v := range src.UnknownFields { - buf.WriteByte(k) - buf.WriteByte(0) - buf.WriteString(v) - buf.WriteByte(0) + dst = append(dst, k) + dst = append(dst, v...) + dst = append(dst, 0) } - buf.WriteByte(0) - binary.BigEndian.PutUint32(buf.Bytes()[1:5], uint32(buf.Len()-1)) + dst = append(dst, 0) + + return dst +} - return buf.Bytes() +// MarshalJSON implements encoding/json.Marshaler. +func (src ErrorResponse) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + Type string + Severity string + SeverityUnlocalized string // only in 9.6 and greater + 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 + + UnknownFields map[byte]string + }{ + Type: "ErrorResponse", + Severity: src.Severity, + SeverityUnlocalized: src.SeverityUnlocalized, + Code: src.Code, + Message: src.Message, + Detail: src.Detail, + Hint: src.Hint, + Position: src.Position, + InternalPosition: src.InternalPosition, + InternalQuery: src.InternalQuery, + Where: src.Where, + SchemaName: src.SchemaName, + TableName: src.TableName, + ColumnName: src.ColumnName, + DataTypeName: src.DataTypeName, + ConstraintName: src.ConstraintName, + File: src.File, + Line: src.Line, + Routine: src.Routine, + UnknownFields: src.UnknownFields, + }) +} + +// UnmarshalJSON implements encoding/json.Unmarshaler. +func (dst *ErrorResponse) UnmarshalJSON(data []byte) error { + // Ignore null, like in the main JSON package. + if string(data) == "null" { + return nil + } + + var msg struct { + Type string + Severity string + SeverityUnlocalized string // only in 9.6 and greater + 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 + + UnknownFields map[byte]string + } + if err := json.Unmarshal(data, &msg); err != nil { + return err + } + + dst.Severity = msg.Severity + dst.SeverityUnlocalized = msg.SeverityUnlocalized + dst.Code = msg.Code + dst.Message = msg.Message + dst.Detail = msg.Detail + dst.Hint = msg.Hint + dst.Position = msg.Position + dst.InternalPosition = msg.InternalPosition + dst.InternalQuery = msg.InternalQuery + dst.Where = msg.Where + dst.SchemaName = msg.SchemaName + dst.TableName = msg.TableName + dst.ColumnName = msg.ColumnName + dst.DataTypeName = msg.DataTypeName + dst.ConstraintName = msg.ConstraintName + dst.File = msg.File + dst.Line = msg.Line + dst.Routine = msg.Routine + + dst.UnknownFields = msg.UnknownFields + + return nil } diff --git a/vendor/github.com/jackc/pgproto3/v2/execute.go b/vendor/github.com/jackc/pgproto3/v2/execute.go index 8bae61332..efb9e1e21 100644 --- a/vendor/github.com/jackc/pgproto3/v2/execute.go +++ b/vendor/github.com/jackc/pgproto3/v2/execute.go @@ -36,19 +36,12 @@ func (dst *Execute) 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 *Execute) Encode(dst []byte) []byte { - dst = append(dst, 'E') - sp := len(dst) - dst = pgio.AppendInt32(dst, -1) - +func (src *Execute) Encode(dst []byte) ([]byte, error) { + dst, sp := beginMessage(dst, 'E') dst = append(dst, src.Portal...) dst = append(dst, 0) - dst = pgio.AppendUint32(dst, src.MaxRows) - - pgio.SetInt32(dst[sp:], int32(len(dst[sp:]))) - - return dst + return finishMessage(dst, sp) } // MarshalJSON implements encoding/json.Marshaler. diff --git a/vendor/github.com/jackc/pgproto3/v2/flush.go b/vendor/github.com/jackc/pgproto3/v2/flush.go index 2725f6894..e5dc1fbbd 100644 --- a/vendor/github.com/jackc/pgproto3/v2/flush.go +++ b/vendor/github.com/jackc/pgproto3/v2/flush.go @@ -20,8 +20,8 @@ func (dst *Flush) 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 *Flush) Encode(dst []byte) []byte { - return append(dst, 'H', 0, 0, 0, 4) +func (src *Flush) Encode(dst []byte) ([]byte, error) { + return append(dst, 'H', 0, 0, 0, 4), nil } // MarshalJSON implements encoding/json.Marshaler. diff --git a/vendor/github.com/jackc/pgproto3/v2/frontend.go b/vendor/github.com/jackc/pgproto3/v2/frontend.go index b8f545ca8..623b0a98e 100644 --- a/vendor/github.com/jackc/pgproto3/v2/frontend.go +++ b/vendor/github.com/jackc/pgproto3/v2/frontend.go @@ -16,6 +16,8 @@ type Frontend struct { authenticationOk AuthenticationOk authenticationCleartextPassword AuthenticationCleartextPassword authenticationMD5Password AuthenticationMD5Password + authenticationGSS AuthenticationGSS + authenticationGSSContinue AuthenticationGSSContinue authenticationSASL AuthenticationSASL authenticationSASLContinue AuthenticationSASLContinue authenticationSASLFinal AuthenticationSASLFinal @@ -45,6 +47,7 @@ type Frontend struct { bodyLen int msgType byte partialMsg bool + authType uint32 } // NewFrontend creates a new Frontend. @@ -54,7 +57,11 @@ func NewFrontend(cr ChunkReader, w io.Writer) *Frontend { // Send sends a message to the backend. func (f *Frontend) Send(msg FrontendMessage) error { - _, err := f.w.Write(msg.Encode(nil)) + buf, err := msg.Encode(nil) + if err != nil { + return err + } + _, err = f.w.Write(buf) return err } @@ -76,6 +83,9 @@ func (f *Frontend) Receive() (BackendMessage, error) { f.msgType = header[0] f.bodyLen = int(binary.BigEndian.Uint32(header[1:])) - 4 f.partialMsg = true + if f.bodyLen < 0 { + return nil, errors.New("invalid message with negative body length received") + } } msgBody, err := f.cr.Next(f.bodyLen) @@ -146,10 +156,16 @@ func (f *Frontend) Receive() (BackendMessage, error) { } // Authentication message type constants. +// See src/include/libpq/pqcomm.h for all +// constants. const ( AuthTypeOk = 0 AuthTypeCleartextPassword = 3 AuthTypeMD5Password = 5 + AuthTypeSCMCreds = 6 + AuthTypeGSS = 7 + AuthTypeGSSCont = 8 + AuthTypeSSPI = 9 AuthTypeSASL = 10 AuthTypeSASLContinue = 11 AuthTypeSASLFinal = 12 @@ -159,15 +175,23 @@ func (f *Frontend) findAuthenticationMessageType(src []byte) (BackendMessage, er if len(src) < 4 { return nil, errors.New("authentication message too short") } - authType := binary.BigEndian.Uint32(src[:4]) + f.authType = binary.BigEndian.Uint32(src[:4]) - switch authType { + switch f.authType { case AuthTypeOk: return &f.authenticationOk, nil case AuthTypeCleartextPassword: return &f.authenticationCleartextPassword, nil case AuthTypeMD5Password: return &f.authenticationMD5Password, nil + case AuthTypeSCMCreds: + return nil, errors.New("AuthTypeSCMCreds is unimplemented") + case AuthTypeGSS: + return &f.authenticationGSS, nil + case AuthTypeGSSCont: + return &f.authenticationGSSContinue, nil + case AuthTypeSSPI: + return nil, errors.New("AuthTypeSSPI is unimplemented") case AuthTypeSASL: return &f.authenticationSASL, nil case AuthTypeSASLContinue: @@ -175,6 +199,12 @@ func (f *Frontend) findAuthenticationMessageType(src []byte) (BackendMessage, er case AuthTypeSASLFinal: return &f.authenticationSASLFinal, nil default: - return nil, fmt.Errorf("unknown authentication type: %d", authType) + return nil, fmt.Errorf("unknown authentication type: %d", f.authType) } } + +// GetAuthType returns the authType used in the current state of the frontend. +// See SetAuthType for more information. +func (f *Frontend) GetAuthType() uint32 { + return f.authType +} diff --git a/vendor/github.com/jackc/pgproto3/v2/function_call.go b/vendor/github.com/jackc/pgproto3/v2/function_call.go new file mode 100644 index 000000000..5d799c4d7 --- /dev/null +++ b/vendor/github.com/jackc/pgproto3/v2/function_call.go @@ -0,0 +1,102 @@ +package pgproto3 + +import ( + "encoding/binary" + "errors" + "math" + + "github.com/jackc/pgio" +) + +type FunctionCall struct { + Function uint32 + ArgFormatCodes []uint16 + Arguments [][]byte + ResultFormatCode uint16 +} + +// Frontend identifies this message as sendable by a PostgreSQL frontend. +func (*FunctionCall) Frontend() {} + +// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message +// type identifier and 4 byte message length. +func (dst *FunctionCall) Decode(src []byte) error { + *dst = FunctionCall{} + rp := 0 + // Specifies the object ID of the function to call. + dst.Function = binary.BigEndian.Uint32(src[rp:]) + rp += 4 + // The number of argument format codes that follow (denoted C below). + // This can be zero to indicate that there are no arguments or that the arguments all use the default format (text); + // or one, in which case the specified format code is applied to all arguments; + // or it can equal the actual number of arguments. + nArgumentCodes := int(binary.BigEndian.Uint16(src[rp:])) + rp += 2 + argumentCodes := make([]uint16, nArgumentCodes) + for i := 0; i < nArgumentCodes; i++ { + // The argument format codes. Each must presently be zero (text) or one (binary). + ac := binary.BigEndian.Uint16(src[rp:]) + if ac != 0 && ac != 1 { + return &invalidMessageFormatErr{messageType: "FunctionCall"} + } + argumentCodes[i] = ac + rp += 2 + } + dst.ArgFormatCodes = argumentCodes + + // Specifies the number of arguments being supplied to the function. + nArguments := int(binary.BigEndian.Uint16(src[rp:])) + rp += 2 + arguments := make([][]byte, nArguments) + for i := 0; i < nArguments; i++ { + // The length of the argument value, in bytes (this count does not include itself). Can be zero. + // As a special case, -1 indicates a NULL argument value. No value bytes follow in the NULL case. + argumentLength := int(binary.BigEndian.Uint32(src[rp:])) + rp += 4 + if argumentLength == -1 { + arguments[i] = nil + } else { + // The value of the argument, in the format indicated by the associated format code. n is the above length. + argumentValue := src[rp : rp+argumentLength] + rp += argumentLength + arguments[i] = argumentValue + } + } + dst.Arguments = arguments + // The format code for the function result. Must presently be zero (text) or one (binary). + resultFormatCode := binary.BigEndian.Uint16(src[rp:]) + if resultFormatCode != 0 && resultFormatCode != 1 { + return &invalidMessageFormatErr{messageType: "FunctionCall"} + } + dst.ResultFormatCode = resultFormatCode + return nil +} + +// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length. +func (src *FunctionCall) Encode(dst []byte) ([]byte, error) { + dst, sp := beginMessage(dst, 'F') + dst = pgio.AppendUint32(dst, src.Function) + + if len(src.ArgFormatCodes) > math.MaxUint16 { + return nil, errors.New("too many arg format codes") + } + dst = pgio.AppendUint16(dst, uint16(len(src.ArgFormatCodes))) + for _, argFormatCode := range src.ArgFormatCodes { + dst = pgio.AppendUint16(dst, argFormatCode) + } + + if len(src.Arguments) > math.MaxUint16 { + return nil, errors.New("too many arguments") + } + dst = pgio.AppendUint16(dst, uint16(len(src.Arguments))) + for _, argument := range src.Arguments { + if argument == nil { + dst = pgio.AppendInt32(dst, -1) + } else { + dst = pgio.AppendInt32(dst, int32(len(argument))) + dst = append(dst, argument...) + } + } + dst = pgio.AppendUint16(dst, src.ResultFormatCode) + return finishMessage(dst, sp) +} diff --git a/vendor/github.com/jackc/pgproto3/v2/function_call_response.go b/vendor/github.com/jackc/pgproto3/v2/function_call_response.go index 5cc2d4d29..abc14f0d1 100644 --- a/vendor/github.com/jackc/pgproto3/v2/function_call_response.go +++ b/vendor/github.com/jackc/pgproto3/v2/function_call_response.go @@ -39,10 +39,8 @@ func (dst *FunctionCallResponse) 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 *FunctionCallResponse) Encode(dst []byte) []byte { - dst = append(dst, 'V') - sp := len(dst) - dst = pgio.AppendInt32(dst, -1) +func (src *FunctionCallResponse) Encode(dst []byte) ([]byte, error) { + dst, sp := beginMessage(dst, 'V') if src.Result == nil { dst = pgio.AppendInt32(dst, -1) @@ -51,9 +49,7 @@ func (src *FunctionCallResponse) Encode(dst []byte) []byte { dst = append(dst, src.Result...) } - pgio.SetInt32(dst[sp:], int32(len(dst[sp:]))) - - return dst + return finishMessage(dst, sp) } // MarshalJSON implements encoding/json.Marshaler. @@ -81,3 +77,21 @@ func (src FunctionCallResponse) MarshalJSON() ([]byte, error) { Result: formattedValue, }) } + +// UnmarshalJSON implements encoding/json.Unmarshaler. +func (dst *FunctionCallResponse) UnmarshalJSON(data []byte) error { + // Ignore null, like in the main JSON package. + if string(data) == "null" { + return nil + } + + var msg struct { + Result map[string]string + } + err := json.Unmarshal(data, &msg) + if err != nil { + return err + } + dst.Result, err = getValueFromJSON(msg.Result) + return err +} diff --git a/vendor/github.com/jackc/pgproto3/v2/gss_enc_request.go b/vendor/github.com/jackc/pgproto3/v2/gss_enc_request.go index cf405a3e0..f6e4f6627 100644 --- a/vendor/github.com/jackc/pgproto3/v2/gss_enc_request.go +++ b/vendor/github.com/jackc/pgproto3/v2/gss_enc_request.go @@ -31,10 +31,10 @@ func (dst *GSSEncRequest) Decode(src []byte) error { } // Encode encodes src into dst. dst will include the 4 byte message length. -func (src *GSSEncRequest) Encode(dst []byte) []byte { +func (src *GSSEncRequest) Encode(dst []byte) ([]byte, error) { dst = pgio.AppendInt32(dst, 8) dst = pgio.AppendInt32(dst, gssEncReqNumber) - return dst + return dst, nil } // MarshalJSON implements encoding/json.Marshaler. diff --git a/vendor/github.com/jackc/pgproto3/v2/gss_response.go b/vendor/github.com/jackc/pgproto3/v2/gss_response.go new file mode 100644 index 000000000..10d937759 --- /dev/null +++ b/vendor/github.com/jackc/pgproto3/v2/gss_response.go @@ -0,0 +1,46 @@ +package pgproto3 + +import ( + "encoding/json" +) + +type GSSResponse struct { + Data []byte +} + +// Frontend identifies this message as sendable by a PostgreSQL frontend. +func (g *GSSResponse) Frontend() {} + +func (g *GSSResponse) Decode(data []byte) error { + g.Data = data + return nil +} + +func (g *GSSResponse) Encode(dst []byte) ([]byte, error) { + dst, sp := beginMessage(dst, 'p') + dst = append(dst, g.Data...) + return finishMessage(dst, sp) +} + +// MarshalJSON implements encoding/json.Marshaler. +func (g *GSSResponse) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + Type string + Data []byte + }{ + Type: "GSSResponse", + Data: g.Data, + }) +} + +// UnmarshalJSON implements encoding/json.Unmarshaler. +func (g *GSSResponse) UnmarshalJSON(data []byte) error { + var msg struct { + Data []byte + } + if err := json.Unmarshal(data, &msg); err != nil { + return err + } + g.Data = msg.Data + return nil +} diff --git a/vendor/github.com/jackc/pgproto3/v2/no_data.go b/vendor/github.com/jackc/pgproto3/v2/no_data.go index d8f85d38a..cbcaad40c 100644 --- a/vendor/github.com/jackc/pgproto3/v2/no_data.go +++ b/vendor/github.com/jackc/pgproto3/v2/no_data.go @@ -20,8 +20,8 @@ func (dst *NoData) 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 *NoData) Encode(dst []byte) []byte { - return append(dst, 'n', 0, 0, 0, 4) +func (src *NoData) Encode(dst []byte) ([]byte, error) { + return append(dst, 'n', 0, 0, 0, 4), nil } // MarshalJSON implements encoding/json.Marshaler. diff --git a/vendor/github.com/jackc/pgproto3/v2/notice_response.go b/vendor/github.com/jackc/pgproto3/v2/notice_response.go index 4ac28a791..497aba6dd 100644 --- a/vendor/github.com/jackc/pgproto3/v2/notice_response.go +++ b/vendor/github.com/jackc/pgproto3/v2/notice_response.go @@ -12,6 +12,8 @@ func (dst *NoticeResponse) 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 *NoticeResponse) Encode(dst []byte) []byte { - return append(dst, (*ErrorResponse)(src).marshalBinary('N')...) +func (src *NoticeResponse) Encode(dst []byte) ([]byte, error) { + dst, sp := beginMessage(dst, 'N') + dst = (*ErrorResponse)(src).appendFields(dst) + return finishMessage(dst, sp) } diff --git a/vendor/github.com/jackc/pgproto3/v2/notification_response.go b/vendor/github.com/jackc/pgproto3/v2/notification_response.go index e762eb967..5be3edd3c 100644 --- a/vendor/github.com/jackc/pgproto3/v2/notification_response.go +++ b/vendor/github.com/jackc/pgproto3/v2/notification_response.go @@ -41,20 +41,14 @@ func (dst *NotificationResponse) 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 *NotificationResponse) Encode(dst []byte) []byte { - dst = append(dst, 'A') - sp := len(dst) - dst = pgio.AppendInt32(dst, -1) - +func (src *NotificationResponse) Encode(dst []byte) ([]byte, error) { + dst, sp := beginMessage(dst, 'A') dst = pgio.AppendUint32(dst, src.PID) dst = append(dst, src.Channel...) dst = append(dst, 0) dst = append(dst, src.Payload...) dst = append(dst, 0) - - pgio.SetInt32(dst[sp:], int32(len(dst[sp:]))) - - return dst + return finishMessage(dst, sp) } // MarshalJSON implements encoding/json.Marshaler. diff --git a/vendor/github.com/jackc/pgproto3/v2/parameter_description.go b/vendor/github.com/jackc/pgproto3/v2/parameter_description.go index e28965c8a..fec0fce85 100644 --- a/vendor/github.com/jackc/pgproto3/v2/parameter_description.go +++ b/vendor/github.com/jackc/pgproto3/v2/parameter_description.go @@ -4,6 +4,8 @@ import ( "bytes" "encoding/binary" "encoding/json" + "errors" + "math" "github.com/jackc/pgio" ) @@ -39,19 +41,18 @@ func (dst *ParameterDescription) 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 *ParameterDescription) Encode(dst []byte) []byte { - dst = append(dst, 't') - sp := len(dst) - dst = pgio.AppendInt32(dst, -1) +func (src *ParameterDescription) Encode(dst []byte) ([]byte, error) { + dst, sp := beginMessage(dst, 't') + if len(src.ParameterOIDs) > math.MaxUint16 { + return nil, errors.New("too many parameter oids") + } dst = pgio.AppendUint16(dst, uint16(len(src.ParameterOIDs))) for _, oid := range src.ParameterOIDs { dst = pgio.AppendUint32(dst, oid) } - pgio.SetInt32(dst[sp:], int32(len(dst[sp:]))) - - return dst + return finishMessage(dst, sp) } // MarshalJSON implements encoding/json.Marshaler. diff --git a/vendor/github.com/jackc/pgproto3/v2/parameter_status.go b/vendor/github.com/jackc/pgproto3/v2/parameter_status.go index c4021d92f..9ee0720b5 100644 --- a/vendor/github.com/jackc/pgproto3/v2/parameter_status.go +++ b/vendor/github.com/jackc/pgproto3/v2/parameter_status.go @@ -3,8 +3,6 @@ package pgproto3 import ( "bytes" "encoding/json" - - "github.com/jackc/pgio" ) type ParameterStatus struct { @@ -37,19 +35,13 @@ func (dst *ParameterStatus) 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 *ParameterStatus) Encode(dst []byte) []byte { - dst = append(dst, 'S') - sp := len(dst) - dst = pgio.AppendInt32(dst, -1) - +func (src *ParameterStatus) Encode(dst []byte) ([]byte, error) { + dst, sp := beginMessage(dst, 'S') dst = append(dst, src.Name...) dst = append(dst, 0) dst = append(dst, src.Value...) dst = append(dst, 0) - - pgio.SetInt32(dst[sp:], int32(len(dst[sp:]))) - - return dst + return finishMessage(dst, sp) } // MarshalJSON implements encoding/json.Marshaler. diff --git a/vendor/github.com/jackc/pgproto3/v2/parse.go b/vendor/github.com/jackc/pgproto3/v2/parse.go index 723885d41..7dd06990e 100644 --- a/vendor/github.com/jackc/pgproto3/v2/parse.go +++ b/vendor/github.com/jackc/pgproto3/v2/parse.go @@ -4,6 +4,8 @@ import ( "bytes" "encoding/binary" "encoding/json" + "errors" + "math" "github.com/jackc/pgio" ) @@ -52,24 +54,23 @@ func (dst *Parse) 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 *Parse) Encode(dst []byte) []byte { - dst = append(dst, 'P') - sp := len(dst) - dst = pgio.AppendInt32(dst, -1) +func (src *Parse) Encode(dst []byte) ([]byte, error) { + dst, sp := beginMessage(dst, 'P') dst = append(dst, src.Name...) dst = append(dst, 0) dst = append(dst, src.Query...) dst = append(dst, 0) + if len(src.ParameterOIDs) > math.MaxUint16 { + return nil, errors.New("too many parameter oids") + } dst = pgio.AppendUint16(dst, uint16(len(src.ParameterOIDs))) for _, oid := range src.ParameterOIDs { dst = pgio.AppendUint32(dst, oid) } - pgio.SetInt32(dst[sp:], int32(len(dst[sp:]))) - - return dst + return finishMessage(dst, sp) } // MarshalJSON implements encoding/json.Marshaler. diff --git a/vendor/github.com/jackc/pgproto3/v2/parse_complete.go b/vendor/github.com/jackc/pgproto3/v2/parse_complete.go index 92c9498b6..cff9e27d0 100644 --- a/vendor/github.com/jackc/pgproto3/v2/parse_complete.go +++ b/vendor/github.com/jackc/pgproto3/v2/parse_complete.go @@ -20,8 +20,8 @@ func (dst *ParseComplete) 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 *ParseComplete) Encode(dst []byte) []byte { - return append(dst, '1', 0, 0, 0, 4) +func (src *ParseComplete) Encode(dst []byte) ([]byte, error) { + return append(dst, '1', 0, 0, 0, 4), nil } // MarshalJSON implements encoding/json.Marshaler. diff --git a/vendor/github.com/jackc/pgproto3/v2/password_message.go b/vendor/github.com/jackc/pgproto3/v2/password_message.go index 4b68b31a8..d820d3275 100644 --- a/vendor/github.com/jackc/pgproto3/v2/password_message.go +++ b/vendor/github.com/jackc/pgproto3/v2/password_message.go @@ -3,8 +3,6 @@ package pgproto3 import ( "bytes" "encoding/json" - - "github.com/jackc/pgio" ) type PasswordMessage struct { @@ -14,6 +12,9 @@ type PasswordMessage struct { // Frontend identifies this message as sendable by a PostgreSQL frontend. func (*PasswordMessage) Frontend() {} +// Frontend identifies this message as an authentication response. +func (*PasswordMessage) InitialResponse() {} + // Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message // type identifier and 4 byte message length. func (dst *PasswordMessage) Decode(src []byte) error { @@ -29,14 +30,11 @@ func (dst *PasswordMessage) 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 *PasswordMessage) Encode(dst []byte) []byte { - dst = append(dst, 'p') - dst = pgio.AppendInt32(dst, int32(4+len(src.Password)+1)) - +func (src *PasswordMessage) Encode(dst []byte) ([]byte, error) { + dst, sp := beginMessage(dst, 'p') dst = append(dst, src.Password...) dst = append(dst, 0) - - return dst + return finishMessage(dst, sp) } // MarshalJSON implements encoding/json.Marshaler. diff --git a/vendor/github.com/jackc/pgproto3/v2/pgproto3.go b/vendor/github.com/jackc/pgproto3/v2/pgproto3.go index fe7b085bc..aa4167c45 100644 --- a/vendor/github.com/jackc/pgproto3/v2/pgproto3.go +++ b/vendor/github.com/jackc/pgproto3/v2/pgproto3.go @@ -1,6 +1,16 @@ package pgproto3 -import "fmt" +import ( + "encoding/hex" + "errors" + "fmt" + + "github.com/jackc/pgio" +) + +// maxMessageBodyLen is the maximum length of a message body in bytes. See PG_LARGE_MESSAGE_LIMIT in the PostgreSQL +// source. It is defined as (MaxAllocSize - 1). MaxAllocSize is defined as 0x3fffffff. +const maxMessageBodyLen = (0x3fffffff - 1) // Message is the interface implemented by an object that can decode and encode // a particular PostgreSQL message. @@ -10,7 +20,7 @@ type Message interface { Decode(data []byte) error // Encode appends itself to dst and returns the new buffer. - Encode(dst []byte) []byte + Encode(dst []byte) ([]byte, error) } type FrontendMessage interface { @@ -23,6 +33,11 @@ type BackendMessage interface { Backend() // no-op method to distinguish frontend from backend methods } +type AuthenticationResponseMessage interface { + BackendMessage + AuthenticationResponse() // no-op method to distinguish authentication responses +} + type invalidMessageLenErr struct { messageType string expectedLen int @@ -40,3 +55,37 @@ type invalidMessageFormatErr struct { func (e *invalidMessageFormatErr) Error() string { return fmt.Sprintf("%s body is invalid", e.messageType) } + +// getValueFromJSON gets the value from a protocol message representation in JSON. +func getValueFromJSON(v map[string]string) ([]byte, error) { + if v == nil { + return nil, nil + } + if text, ok := v["text"]; ok { + return []byte(text), nil + } + if binary, ok := v["binary"]; ok { + return hex.DecodeString(binary) + } + return nil, errors.New("unknown protocol representation") +} + +// beginMessage begines a new message of type t. It appends the message type and a placeholder for the message length to +// dst. It returns the new buffer and the position of the message length placeholder. +func beginMessage(dst []byte, t byte) ([]byte, int) { + dst = append(dst, t) + sp := len(dst) + dst = pgio.AppendInt32(dst, -1) + return dst, sp +} + +// finishMessage finishes a message that was started with beginMessage. It computes the message length and writes it to +// dst[sp]. If the message length is too large it returns an error. Otherwise it returns the final message buffer. +func finishMessage(dst []byte, sp int) ([]byte, error) { + messageBodyLen := len(dst[sp:]) + if messageBodyLen > maxMessageBodyLen { + return nil, errors.New("message body too large") + } + pgio.SetInt32(dst[sp:], int32(messageBodyLen)) + return dst, nil +} diff --git a/vendor/github.com/jackc/pgproto3/v2/portal_suspended.go b/vendor/github.com/jackc/pgproto3/v2/portal_suspended.go index 1a9e7bfb1..9e2f8cbc4 100644 --- a/vendor/github.com/jackc/pgproto3/v2/portal_suspended.go +++ b/vendor/github.com/jackc/pgproto3/v2/portal_suspended.go @@ -20,8 +20,8 @@ func (dst *PortalSuspended) 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 *PortalSuspended) Encode(dst []byte) []byte { - return append(dst, 's', 0, 0, 0, 4) +func (src *PortalSuspended) Encode(dst []byte) ([]byte, error) { + return append(dst, 's', 0, 0, 0, 4), nil } // MarshalJSON implements encoding/json.Marshaler. diff --git a/vendor/github.com/jackc/pgproto3/v2/query.go b/vendor/github.com/jackc/pgproto3/v2/query.go index 41c93b4a8..aebdfde89 100644 --- a/vendor/github.com/jackc/pgproto3/v2/query.go +++ b/vendor/github.com/jackc/pgproto3/v2/query.go @@ -3,8 +3,6 @@ package pgproto3 import ( "bytes" "encoding/json" - - "github.com/jackc/pgio" ) type Query struct { @@ -28,14 +26,11 @@ func (dst *Query) 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 *Query) Encode(dst []byte) []byte { - dst = append(dst, 'Q') - dst = pgio.AppendInt32(dst, int32(4+len(src.String)+1)) - +func (src *Query) Encode(dst []byte) ([]byte, error) { + dst, sp := beginMessage(dst, 'Q') dst = append(dst, src.String...) dst = append(dst, 0) - - return dst + return finishMessage(dst, sp) } // MarshalJSON implements encoding/json.Marshaler. diff --git a/vendor/github.com/jackc/pgproto3/v2/ready_for_query.go b/vendor/github.com/jackc/pgproto3/v2/ready_for_query.go index 879afe390..a56af9fb2 100644 --- a/vendor/github.com/jackc/pgproto3/v2/ready_for_query.go +++ b/vendor/github.com/jackc/pgproto3/v2/ready_for_query.go @@ -2,6 +2,7 @@ package pgproto3 import ( "encoding/json" + "errors" ) type ReadyForQuery struct { @@ -24,8 +25,8 @@ func (dst *ReadyForQuery) 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 *ReadyForQuery) Encode(dst []byte) []byte { - return append(dst, 'Z', 0, 0, 0, 5, src.TxStatus) +func (src *ReadyForQuery) Encode(dst []byte) ([]byte, error) { + return append(dst, 'Z', 0, 0, 0, 5, src.TxStatus), nil } // MarshalJSON implements encoding/json.Marshaler. @@ -38,3 +39,23 @@ func (src ReadyForQuery) MarshalJSON() ([]byte, error) { TxStatus: string(src.TxStatus), }) } + +// UnmarshalJSON implements encoding/json.Unmarshaler. +func (dst *ReadyForQuery) UnmarshalJSON(data []byte) error { + // Ignore null, like in the main JSON package. + if string(data) == "null" { + return nil + } + + var msg struct { + TxStatus string + } + if err := json.Unmarshal(data, &msg); err != nil { + return err + } + if len(msg.TxStatus) != 1 { + return errors.New("invalid length for ReadyForQuery.TxStatus") + } + dst.TxStatus = msg.TxStatus[0] + return nil +} diff --git a/vendor/github.com/jackc/pgproto3/v2/row_description.go b/vendor/github.com/jackc/pgproto3/v2/row_description.go index d9b8c7c98..3f6b2c649 100644 --- a/vendor/github.com/jackc/pgproto3/v2/row_description.go +++ b/vendor/github.com/jackc/pgproto3/v2/row_description.go @@ -4,6 +4,8 @@ import ( "bytes" "encoding/binary" "encoding/json" + "errors" + "math" "github.com/jackc/pgio" ) @@ -99,11 +101,12 @@ func (dst *RowDescription) 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 *RowDescription) Encode(dst []byte) []byte { - dst = append(dst, 'T') - sp := len(dst) - dst = pgio.AppendInt32(dst, -1) +func (src *RowDescription) Encode(dst []byte) ([]byte, error) { + dst, sp := beginMessage(dst, 'T') + if len(src.Fields) > math.MaxUint16 { + return nil, errors.New("too many fields") + } dst = pgio.AppendUint16(dst, uint16(len(src.Fields))) for _, fd := range src.Fields { dst = append(dst, fd.Name...) @@ -117,9 +120,7 @@ func (src *RowDescription) Encode(dst []byte) []byte { dst = pgio.AppendInt16(dst, fd.Format) } - pgio.SetInt32(dst[sp:], int32(len(dst[sp:]))) - - return dst + return finishMessage(dst, sp) } // MarshalJSON implements encoding/json.Marshaler. @@ -132,3 +133,34 @@ func (src RowDescription) MarshalJSON() ([]byte, error) { Fields: src.Fields, }) } + +// UnmarshalJSON implements encoding/json.Unmarshaler. +func (dst *RowDescription) UnmarshalJSON(data []byte) error { + var msg struct { + Fields []struct { + Name string + TableOID uint32 + TableAttributeNumber uint16 + DataTypeOID uint32 + DataTypeSize int16 + TypeModifier int32 + Format int16 + } + } + if err := json.Unmarshal(data, &msg); err != nil { + return err + } + dst.Fields = make([]FieldDescription, len(msg.Fields)) + for n, field := range msg.Fields { + dst.Fields[n] = FieldDescription{ + Name: []byte(field.Name), + TableOID: field.TableOID, + TableAttributeNumber: field.TableAttributeNumber, + DataTypeOID: field.DataTypeOID, + DataTypeSize: field.DataTypeSize, + TypeModifier: field.TypeModifier, + Format: field.Format, + } + } + return nil +} diff --git a/vendor/github.com/jackc/pgproto3/v2/sasl_initial_response.go b/vendor/github.com/jackc/pgproto3/v2/sasl_initial_response.go index 0bf8a9e56..1938f6582 100644 --- a/vendor/github.com/jackc/pgproto3/v2/sasl_initial_response.go +++ b/vendor/github.com/jackc/pgproto3/v2/sasl_initial_response.go @@ -2,7 +2,6 @@ package pgproto3 import ( "bytes" - "encoding/hex" "encoding/json" "errors" @@ -39,10 +38,8 @@ func (dst *SASLInitialResponse) 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 *SASLInitialResponse) Encode(dst []byte) []byte { - dst = append(dst, 'p') - sp := len(dst) - dst = pgio.AppendInt32(dst, -1) +func (src *SASLInitialResponse) Encode(dst []byte) ([]byte, error) { + dst, sp := beginMessage(dst, 'p') dst = append(dst, []byte(src.AuthMechanism)...) dst = append(dst, 0) @@ -50,9 +47,7 @@ func (src *SASLInitialResponse) Encode(dst []byte) []byte { dst = pgio.AppendInt32(dst, int32(len(src.Data))) dst = append(dst, src.Data...) - pgio.SetInt32(dst[sp:], int32(len(dst[sp:]))) - - return dst + return finishMessage(dst, sp) } // MarshalJSON implements encoding/json.Marshaler. @@ -64,6 +59,25 @@ func (src SASLInitialResponse) MarshalJSON() ([]byte, error) { }{ Type: "SASLInitialResponse", AuthMechanism: src.AuthMechanism, - Data: hex.EncodeToString(src.Data), + Data: string(src.Data), }) } + +// UnmarshalJSON implements encoding/json.Unmarshaler. +func (dst *SASLInitialResponse) UnmarshalJSON(data []byte) error { + // Ignore null, like in the main JSON package. + if string(data) == "null" { + return nil + } + + var msg struct { + AuthMechanism string + Data string + } + if err := json.Unmarshal(data, &msg); err != nil { + return err + } + dst.AuthMechanism = msg.AuthMechanism + dst.Data = []byte(msg.Data) + return nil +} diff --git a/vendor/github.com/jackc/pgproto3/v2/sasl_response.go b/vendor/github.com/jackc/pgproto3/v2/sasl_response.go index 21be6d755..f4a131858 100644 --- a/vendor/github.com/jackc/pgproto3/v2/sasl_response.go +++ b/vendor/github.com/jackc/pgproto3/v2/sasl_response.go @@ -1,10 +1,7 @@ package pgproto3 import ( - "encoding/hex" "encoding/json" - - "github.com/jackc/pgio" ) type SASLResponse struct { @@ -22,13 +19,10 @@ func (dst *SASLResponse) 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 *SASLResponse) Encode(dst []byte) []byte { - dst = append(dst, 'p') - dst = pgio.AppendInt32(dst, int32(4+len(src.Data))) - +func (src *SASLResponse) Encode(dst []byte) ([]byte, error) { + dst, sp := beginMessage(dst, 'p') dst = append(dst, src.Data...) - - return dst + return finishMessage(dst, sp) } // MarshalJSON implements encoding/json.Marshaler. @@ -38,6 +32,18 @@ func (src SASLResponse) MarshalJSON() ([]byte, error) { Data string }{ Type: "SASLResponse", - Data: hex.EncodeToString(src.Data), + Data: string(src.Data), }) } + +// UnmarshalJSON implements encoding/json.Unmarshaler. +func (dst *SASLResponse) UnmarshalJSON(data []byte) error { + var msg struct { + Data string + } + if err := json.Unmarshal(data, &msg); err != nil { + return err + } + dst.Data = []byte(msg.Data) + return nil +} diff --git a/vendor/github.com/jackc/pgproto3/v2/ssl_request.go b/vendor/github.com/jackc/pgproto3/v2/ssl_request.go index 96ce489e5..8feff1a28 100644 --- a/vendor/github.com/jackc/pgproto3/v2/ssl_request.go +++ b/vendor/github.com/jackc/pgproto3/v2/ssl_request.go @@ -31,10 +31,10 @@ func (dst *SSLRequest) Decode(src []byte) error { } // Encode encodes src into dst. dst will include the 4 byte message length. -func (src *SSLRequest) Encode(dst []byte) []byte { +func (src *SSLRequest) Encode(dst []byte) ([]byte, error) { dst = pgio.AppendInt32(dst, 8) dst = pgio.AppendInt32(dst, sslRequestNumber) - return dst + return dst, nil } // MarshalJSON implements encoding/json.Marshaler. diff --git a/vendor/github.com/jackc/pgproto3/v2/startup_message.go b/vendor/github.com/jackc/pgproto3/v2/startup_message.go index 5f1cd24f7..255ea22d6 100644 --- a/vendor/github.com/jackc/pgproto3/v2/startup_message.go +++ b/vendor/github.com/jackc/pgproto3/v2/startup_message.go @@ -64,7 +64,7 @@ func (dst *StartupMessage) 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 *StartupMessage) Encode(dst []byte) []byte { +func (src *StartupMessage) Encode(dst []byte) ([]byte, error) { sp := len(dst) dst = pgio.AppendInt32(dst, -1) @@ -77,9 +77,7 @@ func (src *StartupMessage) Encode(dst []byte) []byte { } dst = append(dst, 0) - pgio.SetInt32(dst[sp:], int32(len(dst[sp:]))) - - return dst + return finishMessage(dst, sp) } // MarshalJSON implements encoding/json.Marshaler. diff --git a/vendor/github.com/jackc/pgproto3/v2/sync.go b/vendor/github.com/jackc/pgproto3/v2/sync.go index 5db8e07ac..ea4fc9594 100644 --- a/vendor/github.com/jackc/pgproto3/v2/sync.go +++ b/vendor/github.com/jackc/pgproto3/v2/sync.go @@ -20,8 +20,8 @@ func (dst *Sync) 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 *Sync) Encode(dst []byte) []byte { - return append(dst, 'S', 0, 0, 0, 4) +func (src *Sync) Encode(dst []byte) ([]byte, error) { + return append(dst, 'S', 0, 0, 0, 4), nil } // MarshalJSON implements encoding/json.Marshaler. diff --git a/vendor/github.com/jackc/pgproto3/v2/terminate.go b/vendor/github.com/jackc/pgproto3/v2/terminate.go index 135191eae..35a9dc837 100644 --- a/vendor/github.com/jackc/pgproto3/v2/terminate.go +++ b/vendor/github.com/jackc/pgproto3/v2/terminate.go @@ -20,8 +20,8 @@ func (dst *Terminate) 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 *Terminate) Encode(dst []byte) []byte { - return append(dst, 'X', 0, 0, 0, 4) +func (src *Terminate) Encode(dst []byte) ([]byte, error) { + return append(dst, 'X', 0, 0, 0, 4), nil } // MarshalJSON implements encoding/json.Marshaler. diff --git a/vendor/github.com/jackc/pgtype/.travis.yml b/vendor/github.com/jackc/pgtype/.travis.yml deleted file mode 100644 index d67627350..000000000 --- a/vendor/github.com/jackc/pgtype/.travis.yml +++ /dev/null @@ -1,34 +0,0 @@ -# source: https://github.com/jackc/pgx/blob/master/.travis.yml - -language: go - -go: - - 1.14.x - - 1.13.x - - tip - -# Derived from https://github.com/lib/pq/blob/master/.travis.yml -before_install: - - ./travis/before_install.bash - -env: - global: - - GO111MODULE=on - - PGX_TEST_DATABASE=postgres://pgx_md5:secret@127.0.0.1/pgx_test - - matrix: - - PGVERSION=12 - - PGVERSION=11 - - PGVERSION=10 - - PGVERSION=9.6 - - PGVERSION=9.5 - -before_script: - - ./travis/before_script.bash - -script: - - ./travis/script.bash - -matrix: - allow_failures: - - go: tip \ No newline at end of file diff --git a/vendor/github.com/jackc/pgtype/CHANGELOG.md b/vendor/github.com/jackc/pgtype/CHANGELOG.md index d89f6ddcb..a362a1df5 100644 --- a/vendor/github.com/jackc/pgtype/CHANGELOG.md +++ b/vendor/github.com/jackc/pgtype/CHANGELOG.md @@ -1,3 +1,75 @@ +# 1.14.0 (February 11, 2023) + +* Fix: BC timestamp text format support (jozeflami) +* Add Scanner and Valuer interfaces to CIDR (Yurii Popivniak) +* Fix crash when nilifying pointer to sql.Scanner + +# 1.13.0 (December 1, 2022) + +* Fix: Reset jsonb before unmarshal (Tomas Odinas) +* Fix: return correct zero value when UUID conversion fails (ndrpnt) +* Fix: EncodeText for Lseg includes [ and ] +* Support sql Value and Scan for custom date type (Hubert Krauze) +* Support Ltree binary encoding (AmineChikhaoui) +* Fix: dates with "BC" (jozeflami) + +# 1.12.0 (August 6, 2022) + +* Add JSONArray (Jakob Ackermann) +* Support Inet from fmt.Stringer and encoding.TextMarshaler (Ville Skyttä) +* Support UUID from fmt.Stringer interface (Lasse Hyldahl Jensen) +* Fix: shopspring-numeric extension does not panic on NaN +* Numeric can be assigned to string +* Fix: Do not send IPv4 networks as IPv4-mapped IPv6 (William Storey) +* Fix: PlanScan for interface{}(nil) (James Hartig) +* Fix: *sql.Scanner for NULL handling (James Hartig) +* Timestamp[tz].Set() supports string (Harmen) +* Fix: Hstore AssignTo with map of *string (Diego Becciolini) + +# 1.11.0 (April 21, 2022) + +* Add multirange for numeric, int4, and int8 (Vu) +* JSONBArray now supports json.RawMessage (Jens Emil Schulz Østergaard) +* Add RecordArray (WGH) +* Add UnmarshalJSON to pgtype.Int2 +* Hstore.Set accepts map[string]Text + +# 1.10.0 (February 7, 2022) + +* Normalize UTC timestamps to comply with stdlib (Torkel Rogstad) +* Assign Numeric to *big.Rat (Oleg Lomaka) +* Fix typo in float8 error message (Pinank Solanki) +* Scan type aliases for floating point types (Collin Forsyth) + +# 1.9.1 (November 28, 2021) + +* Fix: binary timestamp is assumed to be in UTC (restored behavior changed in v1.9.0) + +# 1.9.0 (November 20, 2021) + +* Fix binary hstore null decoding +* Add shopspring/decimal.NullDecimal support to integration (Eli Treuherz) +* Inet.Set supports bare IP address (Carl Dunham) +* Add zeronull.Float8 +* Fix NULL being lost when scanning unknown OID into sql.Scanner +* Fix BPChar.AssignTo **rune +* Add support for fmt.Stringer and driver.Valuer in String fields encoding (Jan Dubsky) +* Fix really big timestamp(tz)s binary format parsing (e.g. year 294276) (Jim Tsao) +* Support `map[string]*string` as hstore (Adrian Sieger) +* Fix parsing text array with negative bounds +* Add infinity support for numeric (Jim Tsao) + +# 1.8.1 (July 24, 2021) + +* Cleaned up Go module dependency chain + +# 1.8.0 (July 10, 2021) + +* Maintain host bits for inet types (Cameron Daniel) +* Support pointers of wrapping structs (Ivan Daunis) +* Register JSONBArray at NewConnInfo() (Rueian) +* CompositeTextScanner handles backslash escapes + # 1.7.0 (March 25, 2021) * Fix scanning int into **sql.Scanner implementor diff --git a/vendor/github.com/jackc/pgtype/README.md b/vendor/github.com/jackc/pgtype/README.md index 77d59b313..72dadcfc8 100644 --- a/vendor/github.com/jackc/pgtype/README.md +++ b/vendor/github.com/jackc/pgtype/README.md @@ -1,6 +1,12 @@ [![](https://godoc.org/github.com/jackc/pgtype?status.svg)](https://godoc.org/github.com/jackc/pgtype) ![CI](https://github.com/jackc/pgtype/workflows/CI/badge.svg) +--- + +This version is used with pgx `v4`. In pgx `v5` it is part of the https://github.com/jackc/pgx repository. + +--- + # pgtype pgtype implements Go types for over 70 PostgreSQL types. pgtype is the type system underlying the diff --git a/vendor/github.com/jackc/pgtype/array.go b/vendor/github.com/jackc/pgtype/array.go index 3d5930c1c..174007c17 100644 --- a/vendor/github.com/jackc/pgtype/array.go +++ b/vendor/github.com/jackc/pgtype/array.go @@ -305,7 +305,7 @@ func arrayParseInteger(buf *bytes.Buffer) (int32, error) { return 0, err } - if '0' <= r && r <= '9' { + if ('0' <= r && r <= '9') || r == '-' { s.WriteRune(r) } else { buf.UnreadRune() diff --git a/vendor/github.com/jackc/pgtype/array_type.go b/vendor/github.com/jackc/pgtype/array_type.go index 1bd0244b7..714665544 100644 --- a/vendor/github.com/jackc/pgtype/array_type.go +++ b/vendor/github.com/jackc/pgtype/array_type.go @@ -11,7 +11,7 @@ import ( // ArrayType represents an array type. While it implements Value, this is only in service of its type conversion duties // when registered as a data type in a ConnType. It should not be used directly as a Value. ArrayType is a convenience -// type for types that do not have an concrete array type. +// type for types that do not have a concrete array type. type ArrayType struct { elements []ValueTranscoder dimensions []ArrayDimension diff --git a/vendor/github.com/jackc/pgtype/bpchar.go b/vendor/github.com/jackc/pgtype/bpchar.go index e4d058e92..c5fa42eac 100644 --- a/vendor/github.com/jackc/pgtype/bpchar.go +++ b/vendor/github.com/jackc/pgtype/bpchar.go @@ -2,6 +2,7 @@ package pgtype import ( "database/sql/driver" + "fmt" ) // BPChar is fixed-length, blank padded char type @@ -20,7 +21,8 @@ func (dst BPChar) Get() interface{} { // AssignTo assigns from src to dst. func (src *BPChar) AssignTo(dst interface{}) error { - if src.Status == Present { + switch src.Status { + case Present: switch v := dst.(type) { case *rune: runes := []rune(src.String) @@ -28,9 +30,24 @@ func (src *BPChar) AssignTo(dst interface{}) error { *v = runes[0] return nil } + case *string: + *v = src.String + return nil + case *[]byte: + *v = make([]byte, len(src.String)) + copy(*v, src.String) + return nil + default: + if nextDst, retry := GetAssignToDstType(dst); retry { + return src.AssignTo(nextDst) + } + return fmt.Errorf("unable to assign to %T", dst) } + case Null: + return NullAssignTo(dst) } - return (*Text)(src).AssignTo(dst) + + return fmt.Errorf("cannot decode %#v into %T", src, dst) } func (BPChar) PreferredResultFormat() int16 { diff --git a/vendor/github.com/jackc/pgtype/cidr.go b/vendor/github.com/jackc/pgtype/cidr.go index 2241ca1c0..7c562cf2e 100644 --- a/vendor/github.com/jackc/pgtype/cidr.go +++ b/vendor/github.com/jackc/pgtype/cidr.go @@ -1,5 +1,7 @@ package pgtype +import "database/sql/driver" + type CIDR Inet func (dst *CIDR) Set(src interface{}) error { @@ -29,3 +31,13 @@ func (src CIDR) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { func (src CIDR) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { return (Inet)(src).EncodeBinary(ci, buf) } + +// Scan implements the database/sql Scanner interface. +func (dst *CIDR) Scan(src interface{}) error { + return (*Inet)(dst).Scan(src) +} + +// Value implements the database/sql/driver Valuer interface. +func (src CIDR) Value() (driver.Value, error) { + return (Inet)(src).Value() +} diff --git a/vendor/github.com/jackc/pgtype/composite_type.go b/vendor/github.com/jackc/pgtype/composite_type.go index 7c8dbcd5e..32e0aa26b 100644 --- a/vendor/github.com/jackc/pgtype/composite_type.go +++ b/vendor/github.com/jackc/pgtype/composite_type.go @@ -491,6 +491,10 @@ func (cfs *CompositeTextScanner) Next() bool { } else { break } + } else if ch == '\\' { + cfs.rp++ + cfs.fieldBytes = append(cfs.fieldBytes, cfs.src[cfs.rp]) + cfs.rp++ } else { cfs.fieldBytes = append(cfs.fieldBytes, ch) cfs.rp++ diff --git a/vendor/github.com/jackc/pgtype/convert.go b/vendor/github.com/jackc/pgtype/convert.go index 8ae599b9b..377fe3eac 100644 --- a/vendor/github.com/jackc/pgtype/convert.go +++ b/vendor/github.com/jackc/pgtype/convert.go @@ -8,9 +8,11 @@ import ( "time" ) -const maxUint = ^uint(0) -const maxInt = int(maxUint >> 1) -const minInt = -maxInt - 1 +const ( + maxUint = ^uint(0) + maxInt = int(maxUint >> 1) + minInt = -maxInt - 1 +) // underlyingNumberType gets the underlying type that can be converted to Int2, Int4, Int8, Float4, or Float8 func underlyingNumberType(val interface{}) (interface{}, bool) { @@ -170,7 +172,7 @@ func underlyingUUIDType(val interface{}) (interface{}, bool) { switch refVal.Kind() { case reflect.Ptr: if refVal.IsNil() { - return time.Time{}, false + return nil, false } convVal := refVal.Elem().Interface() return convVal, true @@ -335,6 +337,10 @@ func float64AssignTo(srcVal float64, srcStatus Status, dst interface{}) error { if v := reflect.ValueOf(dst); v.Kind() == reflect.Ptr { el := v.Elem() switch el.Kind() { + // if dst is a type alias of a float32 or 64, set dst val + case reflect.Float32, reflect.Float64: + el.SetFloat(srcVal) + return nil // if dst is a pointer to pointer, strip the pointer and try again case reflect.Ptr: if el.IsNil() { @@ -387,6 +393,11 @@ func NullAssignTo(dst interface{}) error { var kindTypes map[reflect.Kind]reflect.Type +func toInterface(dst reflect.Value, t reflect.Type) (interface{}, bool) { + nextDst := dst.Convert(t) + return nextDst.Interface(), dst.Type() != nextDst.Type() +} + // GetAssignToDstType attempts to convert dst to something AssignTo can assign // to. If dst is a pointer to pointer it allocates a value and returns the // dereferences pointer. If dst is a named type such as *Foo where Foo is type @@ -412,23 +423,33 @@ func GetAssignToDstType(dst interface{}) (interface{}, bool) { // if dst is pointer to a base type that has been renamed if baseValType, ok := kindTypes[dstVal.Kind()]; ok { - nextDst := dstPtr.Convert(reflect.PtrTo(baseValType)) - return nextDst.Interface(), dstPtr.Type() != nextDst.Type() + return toInterface(dstPtr, reflect.PtrTo(baseValType)) } if dstVal.Kind() == reflect.Slice { if baseElemType, ok := kindTypes[dstVal.Type().Elem().Kind()]; ok { - baseSliceType := reflect.PtrTo(reflect.SliceOf(baseElemType)) - nextDst := dstPtr.Convert(baseSliceType) - return nextDst.Interface(), dstPtr.Type() != nextDst.Type() + return toInterface(dstPtr, reflect.PtrTo(reflect.SliceOf(baseElemType))) } } if dstVal.Kind() == reflect.Array { if baseElemType, ok := kindTypes[dstVal.Type().Elem().Kind()]; ok { - baseArrayType := reflect.PtrTo(reflect.ArrayOf(dstVal.Len(), baseElemType)) - nextDst := dstPtr.Convert(baseArrayType) - return nextDst.Interface(), dstPtr.Type() != nextDst.Type() + return toInterface(dstPtr, reflect.PtrTo(reflect.ArrayOf(dstVal.Len(), baseElemType))) + } + } + + if dstVal.Kind() == reflect.Struct { + if dstVal.Type().NumField() == 1 && dstVal.Type().Field(0).Anonymous { + dstPtr = dstVal.Field(0).Addr() + nested := dstVal.Type().Field(0).Type + if nested.Kind() == reflect.Array { + if baseElemType, ok := kindTypes[nested.Elem().Kind()]; ok { + return toInterface(dstPtr, reflect.PtrTo(reflect.ArrayOf(nested.Len(), baseElemType))) + } + } + if _, ok := kindTypes[nested.Kind()]; ok && dstPtr.CanInterface() { + return dstPtr.Interface(), true + } } } diff --git a/vendor/github.com/jackc/pgtype/date.go b/vendor/github.com/jackc/pgtype/date.go index e8d21a78c..e68abf01d 100644 --- a/vendor/github.com/jackc/pgtype/date.go +++ b/vendor/github.com/jackc/pgtype/date.go @@ -1,10 +1,12 @@ package pgtype import ( + "database/sql" "database/sql/driver" "encoding/binary" "encoding/json" "fmt" + "strings" "time" "github.com/jackc/pgio" @@ -34,17 +36,25 @@ func (dst *Date) Set(src interface{}) error { } } + if value, ok := src.(interface{ Value() (driver.Value, error) }); ok { + v, err := value.Value() + if err != nil { + return fmt.Errorf("cannot get value %v for Date: %v", value, err) + } + return dst.Set(v) + } + switch value := src.(type) { case time.Time: *dst = Date{Time: value, Status: Present} - case string: - return dst.DecodeText(nil, []byte(value)) case *time.Time: if value == nil { *dst = Date{Status: Null} } else { return dst.Set(*value) } + case string: + return dst.DecodeText(nil, []byte(value)) case *string: if value == nil { *dst = Date{Status: Null} @@ -76,6 +86,24 @@ func (dst Date) Get() interface{} { } func (src *Date) AssignTo(dst interface{}) error { + if scanner, ok := dst.(sql.Scanner); ok { + var err error + switch src.Status { + case Present: + if src.InfinityModifier != None { + err = scanner.Scan(src.InfinityModifier.String()) + } else { + err = scanner.Scan(src.Time) + } + case Null: + err = scanner.Scan(nil) + } + if err != nil { + return fmt.Errorf("unable assign %v to %T: %s", src, dst, err) + } + return nil + } + switch src.Status { case Present: switch v := dst.(type) { @@ -111,6 +139,15 @@ func (dst *Date) DecodeText(ci *ConnInfo, src []byte) error { case "-infinity": *dst = Date{Status: Present, InfinityModifier: -Infinity} default: + if strings.HasSuffix(sbuf, " BC") { + t, err := time.ParseInLocation("2006-01-02", strings.TrimRight(sbuf, " BC"), time.UTC) + t2 := time.Date(1-t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), t.Location()) + if err != nil { + return err + } + *dst = Date{Time: t2, Status: Present} + return nil + } t, err := time.ParseInLocation("2006-01-02", sbuf, time.UTC) if err != nil { return err diff --git a/vendor/github.com/jackc/pgtype/enum_type.go b/vendor/github.com/jackc/pgtype/enum_type.go index d340320fa..526578226 100644 --- a/vendor/github.com/jackc/pgtype/enum_type.go +++ b/vendor/github.com/jackc/pgtype/enum_type.go @@ -2,7 +2,7 @@ package pgtype import "fmt" -// EnumType represents a enum type. While it implements Value, this is only in service of its type conversion duties +// EnumType represents an enum type. While it implements Value, this is only in service of its type conversion duties // when registered as a data type in a ConnType. It should not be used directly as a Value. type EnumType struct { value string diff --git a/vendor/github.com/jackc/pgtype/float8.go b/vendor/github.com/jackc/pgtype/float8.go index 4d9e7116a..6297ab5e2 100644 --- a/vendor/github.com/jackc/pgtype/float8.go +++ b/vendor/github.com/jackc/pgtype/float8.go @@ -204,7 +204,7 @@ func (dst *Float8) DecodeBinary(ci *ConnInfo, src []byte) error { } if len(src) != 8 { - return fmt.Errorf("invalid length for float4: %v", len(src)) + return fmt.Errorf("invalid length for float8: %v", len(src)) } n := int64(binary.BigEndian.Uint64(src)) diff --git a/vendor/github.com/jackc/pgtype/hstore.go b/vendor/github.com/jackc/pgtype/hstore.go index 18b413c6b..e42b75512 100644 --- a/vendor/github.com/jackc/pgtype/hstore.go +++ b/vendor/github.com/jackc/pgtype/hstore.go @@ -40,6 +40,18 @@ func (dst *Hstore) Set(src interface{}) error { m[k] = Text{String: v, Status: Present} } *dst = Hstore{Map: m, Status: Present} + case map[string]*string: + m := make(map[string]Text, len(value)) + for k, v := range value { + if v == nil { + m[k] = Text{Status: Null} + } else { + m[k] = Text{String: *v, Status: Present} + } + } + *dst = Hstore{Map: m, Status: Present} + case map[string]Text: + *dst = Hstore{Map: value, Status: Present} default: return fmt.Errorf("cannot convert %v to Hstore", src) } @@ -71,6 +83,20 @@ func (src *Hstore) AssignTo(dst interface{}) error { (*v)[k] = val.String } return nil + case *map[string]*string: + *v = make(map[string]*string, len(src.Map)) + for k, val := range src.Map { + switch val.Status { + case Null: + (*v)[k] = nil + case Present: + str := val.String + (*v)[k] = &str + default: + return fmt.Errorf("cannot decode %#v into %T", src, dst) + } + } + return nil default: if nextDst, retry := GetAssignToDstType(dst); retry { return src.AssignTo(nextDst) @@ -142,8 +168,8 @@ func (dst *Hstore) DecodeBinary(ci *ConnInfo, src []byte) error { var valueBuf []byte if valueLen >= 0 { valueBuf = src[rp : rp+valueLen] + rp += valueLen } - rp += valueLen var value Text err := value.DecodeBinary(ci, valueBuf) @@ -388,7 +414,7 @@ func parseHstore(s string) (k []string, v []Text, err error) { r, end = p.Consume() switch { case end: - err = errors.New("Found EOS after ',', expcting space") + err = errors.New("Found EOS after ',', expecting space") case (unicode.IsSpace(r)): r, end = p.Consume() state = hsKey diff --git a/vendor/github.com/jackc/pgtype/inet.go b/vendor/github.com/jackc/pgtype/inet.go index 101b9ab43..976f0d7b9 100644 --- a/vendor/github.com/jackc/pgtype/inet.go +++ b/vendor/github.com/jackc/pgtype/inet.go @@ -2,8 +2,10 @@ package pgtype import ( "database/sql/driver" + "encoding" "fmt" "net" + "strings" ) // Network address family is dependent on server socket.h value for AF_INET. @@ -45,10 +47,28 @@ func (dst *Inet) Set(src interface{}) error { *dst = Inet{IPNet: &net.IPNet{Mask: mask, IP: value}, Status: Present} } case string: - _, ipnet, err := net.ParseCIDR(value) + ip, ipnet, err := net.ParseCIDR(value) if err != nil { - return err + ip := net.ParseIP(value) + if ip == nil { + return fmt.Errorf("unable to parse inet address: %s", value) + } + + if ipv4 := maybeGetIPv4(value, ip); ipv4 != nil { + ipnet = &net.IPNet{IP: ipv4, Mask: net.CIDRMask(32, 32)} + } else { + ipnet = &net.IPNet{IP: ip, Mask: net.CIDRMask(128, 128)} + } + } else { + ipnet.IP = ip + if ipv4 := maybeGetIPv4(value, ipnet.IP); ipv4 != nil { + ipnet.IP = ipv4 + if len(ipnet.Mask) == 16 { + ipnet.Mask = ipnet.Mask[12:] // Not sure this is ever needed. + } + } } + *dst = Inet{IPNet: ipnet, Status: Present} case *net.IPNet: if value == nil { @@ -69,6 +89,16 @@ func (dst *Inet) Set(src interface{}) error { return dst.Set(*value) } default: + if tv, ok := src.(encoding.TextMarshaler); ok { + text, err := tv.MarshalText() + if err != nil { + return fmt.Errorf("cannot marshal %v: %w", value, err) + } + return dst.Set(string(text)) + } + if sv, ok := src.(fmt.Stringer); ok { + return dst.Set(sv.String()) + } if originalSrc, ok := underlyingPtrType(src); ok { return dst.Set(originalSrc) } @@ -78,6 +108,25 @@ func (dst *Inet) Set(src interface{}) error { return nil } +// Convert the net.IP to IPv4, if appropriate. +// +// When parsing a string to a net.IP using net.ParseIP() and the like, we get a +// 16 byte slice for IPv4 addresses as well as IPv6 addresses. This function +// calls To4() to convert them to a 4 byte slice. This is useful as it allows +// users of the net.IP check for IPv4 addresses based on the length and makes +// it clear we are handling IPv4 as opposed to IPv6 or IPv4-mapped IPv6 +// addresses. +func maybeGetIPv4(input string, ip net.IP) net.IP { + // Do not do this if the provided input looks like IPv6. This is because + // To4() on IPv4-mapped IPv6 addresses converts them to IPv4, which behave + // different in some cases. + if strings.Contains(input, ":") { + return nil + } + + return ip.To4() +} + func (dst Inet) Get() interface{} { switch dst.Status { case Present: @@ -109,6 +158,12 @@ func (src *Inet) AssignTo(dst interface{}) error { copy(*v, src.IPNet.IP) return nil default: + if tv, ok := dst.(encoding.TextUnmarshaler); ok { + if err := tv.UnmarshalText([]byte(src.IPNet.String())); err != nil { + return fmt.Errorf("cannot unmarshal %v to %T: %w", src, dst, err) + } + return nil + } if nextDst, retry := GetAssignToDstType(dst); retry { return src.AssignTo(nextDst) } @@ -131,18 +186,22 @@ func (dst *Inet) DecodeText(ci *ConnInfo, src []byte) error { var err error if ip := net.ParseIP(string(src)); ip != nil { - ipv4 := ip.To4() - if ipv4 != nil { + if ipv4 := ip.To4(); ipv4 != nil { ip = ipv4 } bitCount := len(ip) * 8 mask := net.CIDRMask(bitCount, bitCount) ipnet = &net.IPNet{Mask: mask, IP: ip} } else { - _, ipnet, err = net.ParseCIDR(string(src)) + ip, ipnet, err = net.ParseCIDR(string(src)) if err != nil { return err } + if ipv4 := ip.To4(); ipv4 != nil { + ip = ipv4 + } + ones, _ := ipnet.Mask.Size() + *ipnet = net.IPNet{IP: ip, Mask: net.CIDRMask(ones, len(ip)*8)} } *dst = Inet{IPNet: ipnet, Status: Present} @@ -156,7 +215,7 @@ func (dst *Inet) DecodeBinary(ci *ConnInfo, src []byte) error { } if len(src) != 8 && len(src) != 20 { - return fmt.Errorf("Received an invalid size for a inet: %d", len(src)) + return fmt.Errorf("Received an invalid size for an inet: %d", len(src)) } // ignore family @@ -167,7 +226,10 @@ func (dst *Inet) DecodeBinary(ci *ConnInfo, src []byte) error { var ipnet net.IPNet ipnet.IP = make(net.IP, int(addressLength)) copy(ipnet.IP, src[4:]) - ipnet.Mask = net.CIDRMask(int(bits), int(addressLength)*8) + if ipv4 := ipnet.IP.To4(); ipv4 != nil { + ipnet.IP = ipv4 + } + ipnet.Mask = net.CIDRMask(int(bits), len(ipnet.IP)*8) *dst = Inet{IPNet: &ipnet, Status: Present} diff --git a/vendor/github.com/jackc/pgtype/int2.go b/vendor/github.com/jackc/pgtype/int2.go index 3eb5aeb55..0775882ab 100644 --- a/vendor/github.com/jackc/pgtype/int2.go +++ b/vendor/github.com/jackc/pgtype/int2.go @@ -3,6 +3,7 @@ package pgtype import ( "database/sql/driver" "encoding/binary" + "encoding/json" "fmt" "math" "strconv" @@ -302,3 +303,19 @@ func (src Int2) MarshalJSON() ([]byte, error) { return nil, errBadStatus } + +func (dst *Int2) UnmarshalJSON(b []byte) error { + var n *int16 + err := json.Unmarshal(b, &n) + if err != nil { + return err + } + + if n == nil { + *dst = Int2{Status: Null} + } else { + *dst = Int2{Int: *n, Status: Present} + } + + return nil +} diff --git a/vendor/github.com/jackc/pgtype/int4_multirange.go b/vendor/github.com/jackc/pgtype/int4_multirange.go new file mode 100644 index 000000000..c3432ce63 --- /dev/null +++ b/vendor/github.com/jackc/pgtype/int4_multirange.go @@ -0,0 +1,239 @@ +package pgtype + +import ( + "database/sql/driver" + "encoding/binary" + "fmt" + + "github.com/jackc/pgio" +) + +type Int4multirange struct { + Ranges []Int4range + Status Status +} + +func (dst *Int4multirange) Set(src interface{}) error { + //untyped nil and typed nil interfaces are different + if src == nil { + *dst = Int4multirange{Status: Null} + return nil + } + + switch value := src.(type) { + case Int4multirange: + *dst = value + case *Int4multirange: + *dst = *value + case string: + return dst.DecodeText(nil, []byte(value)) + case []Int4range: + if value == nil { + *dst = Int4multirange{Status: Null} + } else if len(value) == 0 { + *dst = Int4multirange{Status: Present} + } else { + elements := make([]Int4range, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = Int4multirange{ + Ranges: elements, + Status: Present, + } + } + case []*Int4range: + if value == nil { + *dst = Int4multirange{Status: Null} + } else if len(value) == 0 { + *dst = Int4multirange{Status: Present} + } else { + elements := make([]Int4range, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = Int4multirange{ + Ranges: elements, + Status: Present, + } + } + default: + return fmt.Errorf("cannot convert %v to Int4multirange", src) + } + + return nil + +} + +func (dst Int4multirange) Get() interface{} { + switch dst.Status { + case Present: + return dst + case Null: + return nil + default: + return dst.Status + } +} + +func (src *Int4multirange) AssignTo(dst interface{}) error { + return fmt.Errorf("cannot assign %v to %T", src, dst) +} + +func (dst *Int4multirange) DecodeText(ci *ConnInfo, src []byte) error { + if src == nil { + *dst = Int4multirange{Status: Null} + return nil + } + + utmr, err := ParseUntypedTextMultirange(string(src)) + if err != nil { + return err + } + + var elements []Int4range + + if len(utmr.Elements) > 0 { + elements = make([]Int4range, len(utmr.Elements)) + + for i, s := range utmr.Elements { + var elem Int4range + + elemSrc := []byte(s) + + err = elem.DecodeText(ci, elemSrc) + if err != nil { + return err + } + + elements[i] = elem + } + } + + *dst = Int4multirange{Ranges: elements, Status: Present} + + return nil +} + +func (dst *Int4multirange) DecodeBinary(ci *ConnInfo, src []byte) error { + if src == nil { + *dst = Int4multirange{Status: Null} + return nil + } + + rp := 0 + + numElems := int(binary.BigEndian.Uint32(src[rp:])) + rp += 4 + + if numElems == 0 { + *dst = Int4multirange{Status: Present} + return nil + } + + elements := make([]Int4range, numElems) + + for i := range elements { + elemLen := int(int32(binary.BigEndian.Uint32(src[rp:]))) + rp += 4 + var elemSrc []byte + if elemLen >= 0 { + elemSrc = src[rp : rp+elemLen] + rp += elemLen + } + err := elements[i].DecodeBinary(ci, elemSrc) + if err != nil { + return err + } + } + + *dst = Int4multirange{Ranges: elements, Status: Present} + return nil +} + +func (src Int4multirange) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { + switch src.Status { + case Null: + return nil, nil + case Undefined: + return nil, errUndefined + } + + buf = append(buf, '{') + + inElemBuf := make([]byte, 0, 32) + for i, elem := range src.Ranges { + if i > 0 { + buf = append(buf, ',') + } + + elemBuf, err := elem.EncodeText(ci, inElemBuf) + if err != nil { + return nil, err + } + if elemBuf == nil { + return nil, fmt.Errorf("multi-range does not allow null range") + } else { + buf = append(buf, string(elemBuf)...) + } + + } + + buf = append(buf, '}') + + return buf, nil +} + +func (src Int4multirange) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { + switch src.Status { + case Null: + return nil, nil + case Undefined: + return nil, errUndefined + } + + buf = pgio.AppendInt32(buf, int32(len(src.Ranges))) + + for i := range src.Ranges { + sp := len(buf) + buf = pgio.AppendInt32(buf, -1) + + elemBuf, err := src.Ranges[i].EncodeBinary(ci, buf) + if err != nil { + return nil, err + } + if elemBuf != nil { + buf = elemBuf + pgio.SetInt32(buf[sp:], int32(len(buf[sp:])-4)) + } + } + + return buf, nil +} + +// Scan implements the database/sql Scanner interface. +func (dst *Int4multirange) Scan(src interface{}) error { + if src == nil { + return dst.DecodeText(nil, nil) + } + + switch src := src.(type) { + case string: + return dst.DecodeText(nil, []byte(src)) + case []byte: + srcCopy := make([]byte, len(src)) + copy(srcCopy, src) + return dst.DecodeText(nil, srcCopy) + } + + return fmt.Errorf("cannot scan %T", src) +} + +// Value implements the database/sql/driver Valuer interface. +func (src Int4multirange) Value() (driver.Value, error) { + return EncodeValueText(src) +} diff --git a/vendor/github.com/jackc/pgtype/int8_multirange.go b/vendor/github.com/jackc/pgtype/int8_multirange.go new file mode 100644 index 000000000..e0976427a --- /dev/null +++ b/vendor/github.com/jackc/pgtype/int8_multirange.go @@ -0,0 +1,239 @@ +package pgtype + +import ( + "database/sql/driver" + "encoding/binary" + "fmt" + + "github.com/jackc/pgio" +) + +type Int8multirange struct { + Ranges []Int8range + Status Status +} + +func (dst *Int8multirange) Set(src interface{}) error { + //untyped nil and typed nil interfaces are different + if src == nil { + *dst = Int8multirange{Status: Null} + return nil + } + + switch value := src.(type) { + case Int8multirange: + *dst = value + case *Int8multirange: + *dst = *value + case string: + return dst.DecodeText(nil, []byte(value)) + case []Int8range: + if value == nil { + *dst = Int8multirange{Status: Null} + } else if len(value) == 0 { + *dst = Int8multirange{Status: Present} + } else { + elements := make([]Int8range, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = Int8multirange{ + Ranges: elements, + Status: Present, + } + } + case []*Int8range: + if value == nil { + *dst = Int8multirange{Status: Null} + } else if len(value) == 0 { + *dst = Int8multirange{Status: Present} + } else { + elements := make([]Int8range, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = Int8multirange{ + Ranges: elements, + Status: Present, + } + } + default: + return fmt.Errorf("cannot convert %v to Int8multirange", src) + } + + return nil + +} + +func (dst Int8multirange) Get() interface{} { + switch dst.Status { + case Present: + return dst + case Null: + return nil + default: + return dst.Status + } +} + +func (src *Int8multirange) AssignTo(dst interface{}) error { + return fmt.Errorf("cannot assign %v to %T", src, dst) +} + +func (dst *Int8multirange) DecodeText(ci *ConnInfo, src []byte) error { + if src == nil { + *dst = Int8multirange{Status: Null} + return nil + } + + utmr, err := ParseUntypedTextMultirange(string(src)) + if err != nil { + return err + } + + var elements []Int8range + + if len(utmr.Elements) > 0 { + elements = make([]Int8range, len(utmr.Elements)) + + for i, s := range utmr.Elements { + var elem Int8range + + elemSrc := []byte(s) + + err = elem.DecodeText(ci, elemSrc) + if err != nil { + return err + } + + elements[i] = elem + } + } + + *dst = Int8multirange{Ranges: elements, Status: Present} + + return nil +} + +func (dst *Int8multirange) DecodeBinary(ci *ConnInfo, src []byte) error { + if src == nil { + *dst = Int8multirange{Status: Null} + return nil + } + + rp := 0 + + numElems := int(binary.BigEndian.Uint32(src[rp:])) + rp += 4 + + if numElems == 0 { + *dst = Int8multirange{Status: Present} + return nil + } + + elements := make([]Int8range, numElems) + + for i := range elements { + elemLen := int(int32(binary.BigEndian.Uint32(src[rp:]))) + rp += 4 + var elemSrc []byte + if elemLen >= 0 { + elemSrc = src[rp : rp+elemLen] + rp += elemLen + } + err := elements[i].DecodeBinary(ci, elemSrc) + if err != nil { + return err + } + } + + *dst = Int8multirange{Ranges: elements, Status: Present} + return nil +} + +func (src Int8multirange) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { + switch src.Status { + case Null: + return nil, nil + case Undefined: + return nil, errUndefined + } + + buf = append(buf, '{') + + inElemBuf := make([]byte, 0, 32) + for i, elem := range src.Ranges { + if i > 0 { + buf = append(buf, ',') + } + + elemBuf, err := elem.EncodeText(ci, inElemBuf) + if err != nil { + return nil, err + } + if elemBuf == nil { + return nil, fmt.Errorf("multi-range does not allow null range") + } else { + buf = append(buf, string(elemBuf)...) + } + + } + + buf = append(buf, '}') + + return buf, nil +} + +func (src Int8multirange) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { + switch src.Status { + case Null: + return nil, nil + case Undefined: + return nil, errUndefined + } + + buf = pgio.AppendInt32(buf, int32(len(src.Ranges))) + + for i := range src.Ranges { + sp := len(buf) + buf = pgio.AppendInt32(buf, -1) + + elemBuf, err := src.Ranges[i].EncodeBinary(ci, buf) + if err != nil { + return nil, err + } + if elemBuf != nil { + buf = elemBuf + pgio.SetInt32(buf[sp:], int32(len(buf[sp:])-4)) + } + } + + return buf, nil +} + +// Scan implements the database/sql Scanner interface. +func (dst *Int8multirange) Scan(src interface{}) error { + if src == nil { + return dst.DecodeText(nil, nil) + } + + switch src := src.(type) { + case string: + return dst.DecodeText(nil, []byte(src)) + case []byte: + srcCopy := make([]byte, len(src)) + copy(srcCopy, src) + return dst.DecodeText(nil, srcCopy) + } + + return fmt.Errorf("cannot scan %T", src) +} + +// Value implements the database/sql/driver Valuer interface. +func (src Int8multirange) Value() (driver.Value, error) { + return EncodeValueText(src) +} diff --git a/vendor/github.com/jackc/pgtype/interval.go b/vendor/github.com/jackc/pgtype/interval.go index b01fbb7cb..00ec47c53 100644 --- a/vendor/github.com/jackc/pgtype/interval.go +++ b/vendor/github.com/jackc/pgtype/interval.go @@ -174,7 +174,7 @@ func (dst *Interval) DecodeBinary(ci *ConnInfo, src []byte) error { } if len(src) != 16 { - return fmt.Errorf("Received an invalid size for a interval: %d", len(src)) + return fmt.Errorf("Received an invalid size for an interval: %d", len(src)) } microseconds := int64(binary.BigEndian.Uint64(src)) diff --git a/vendor/github.com/jackc/pgtype/json.go b/vendor/github.com/jackc/pgtype/json.go index 32bef5e76..a9508bdd8 100644 --- a/vendor/github.com/jackc/pgtype/json.go +++ b/vendor/github.com/jackc/pgtype/json.go @@ -5,6 +5,7 @@ import ( "encoding/json" "errors" "fmt" + "reflect" ) type JSON struct { @@ -107,6 +108,9 @@ func (src *JSON) AssignTo(dst interface{}) error { data = []byte("null") } + p := reflect.ValueOf(dst).Elem() + p.Set(reflect.Zero(p.Type())) + return json.Unmarshal(data, dst) } diff --git a/vendor/github.com/jackc/pgtype/json_array.go b/vendor/github.com/jackc/pgtype/json_array.go new file mode 100644 index 000000000..8d68882f0 --- /dev/null +++ b/vendor/github.com/jackc/pgtype/json_array.go @@ -0,0 +1,546 @@ +// Code generated by erb. DO NOT EDIT. + +package pgtype + +import ( + "database/sql/driver" + "encoding/binary" + "encoding/json" + "fmt" + "reflect" + + "github.com/jackc/pgio" +) + +type JSONArray struct { + Elements []JSON + Dimensions []ArrayDimension + Status Status +} + +func (dst *JSONArray) Set(src interface{}) error { + // untyped nil and typed nil interfaces are different + if src == nil { + *dst = JSONArray{Status: Null} + return nil + } + + if value, ok := src.(interface{ Get() interface{} }); ok { + value2 := value.Get() + if value2 != value { + return dst.Set(value2) + } + } + + // Attempt to match to select common types: + switch value := src.(type) { + + case []string: + if value == nil { + *dst = JSONArray{Status: Null} + } else if len(value) == 0 { + *dst = JSONArray{Status: Present} + } else { + elements := make([]JSON, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = JSONArray{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case [][]byte: + if value == nil { + *dst = JSONArray{Status: Null} + } else if len(value) == 0 { + *dst = JSONArray{Status: Present} + } else { + elements := make([]JSON, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = JSONArray{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []json.RawMessage: + if value == nil { + *dst = JSONArray{Status: Null} + } else if len(value) == 0 { + *dst = JSONArray{Status: Present} + } else { + elements := make([]JSON, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = JSONArray{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []JSON: + if value == nil { + *dst = JSONArray{Status: Null} + } else if len(value) == 0 { + *dst = JSONArray{Status: Present} + } else { + *dst = JSONArray{ + Elements: value, + Dimensions: []ArrayDimension{{Length: int32(len(value)), LowerBound: 1}}, + Status: Present, + } + } + default: + // Fallback to reflection if an optimised match was not found. + // The reflection is necessary for arrays and multidimensional slices, + // but it comes with a 20-50% performance penalty for large arrays/slices + reflectedValue := reflect.ValueOf(src) + if !reflectedValue.IsValid() || reflectedValue.IsZero() { + *dst = JSONArray{Status: Null} + return nil + } + + dimensions, elementsLength, ok := findDimensionsFromValue(reflectedValue, nil, 0) + if !ok { + return fmt.Errorf("cannot find dimensions of %v for JSONArray", src) + } + if elementsLength == 0 { + *dst = JSONArray{Status: Present} + return nil + } + if len(dimensions) == 0 { + if originalSrc, ok := underlyingSliceType(src); ok { + return dst.Set(originalSrc) + } + return fmt.Errorf("cannot convert %v to JSONArray", src) + } + + *dst = JSONArray{ + Elements: make([]JSON, elementsLength), + Dimensions: dimensions, + Status: Present, + } + elementCount, err := dst.setRecursive(reflectedValue, 0, 0) + if err != nil { + // Maybe the target was one dimension too far, try again: + if len(dst.Dimensions) > 1 { + dst.Dimensions = dst.Dimensions[:len(dst.Dimensions)-1] + elementsLength = 0 + for _, dim := range dst.Dimensions { + if elementsLength == 0 { + elementsLength = int(dim.Length) + } else { + elementsLength *= int(dim.Length) + } + } + dst.Elements = make([]JSON, elementsLength) + elementCount, err = dst.setRecursive(reflectedValue, 0, 0) + if err != nil { + return err + } + } else { + return err + } + } + if elementCount != len(dst.Elements) { + return fmt.Errorf("cannot convert %v to JSONArray, expected %d dst.Elements, but got %d instead", src, len(dst.Elements), elementCount) + } + } + + return nil +} + +func (dst *JSONArray) setRecursive(value reflect.Value, index, dimension int) (int, error) { + switch value.Kind() { + case reflect.Array: + fallthrough + case reflect.Slice: + if len(dst.Dimensions) == dimension { + break + } + + valueLen := value.Len() + if int32(valueLen) != dst.Dimensions[dimension].Length { + return 0, fmt.Errorf("multidimensional arrays must have array expressions with matching dimensions") + } + for i := 0; i < valueLen; i++ { + var err error + index, err = dst.setRecursive(value.Index(i), index, dimension+1) + if err != nil { + return 0, err + } + } + + return index, nil + } + if !value.CanInterface() { + return 0, fmt.Errorf("cannot convert all values to JSONArray") + } + if err := dst.Elements[index].Set(value.Interface()); err != nil { + return 0, fmt.Errorf("%v in JSONArray", err) + } + index++ + + return index, nil +} + +func (dst JSONArray) Get() interface{} { + switch dst.Status { + case Present: + return dst + case Null: + return nil + default: + return dst.Status + } +} + +func (src *JSONArray) AssignTo(dst interface{}) error { + switch src.Status { + case Present: + if len(src.Dimensions) <= 1 { + // Attempt to match to select common types: + switch v := dst.(type) { + + case *[]string: + *v = make([]string, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + case *[][]byte: + *v = make([][]byte, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + case *[]json.RawMessage: + *v = make([]json.RawMessage, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + } + } + + // Try to convert to something AssignTo can use directly. + if nextDst, retry := GetAssignToDstType(dst); retry { + return src.AssignTo(nextDst) + } + + // Fallback to reflection if an optimised match was not found. + // The reflection is necessary for arrays and multidimensional slices, + // but it comes with a 20-50% performance penalty for large arrays/slices + value := reflect.ValueOf(dst) + if value.Kind() == reflect.Ptr { + value = value.Elem() + } + + switch value.Kind() { + case reflect.Array, reflect.Slice: + default: + return fmt.Errorf("cannot assign %T to %T", src, dst) + } + + if len(src.Elements) == 0 { + if value.Kind() == reflect.Slice { + value.Set(reflect.MakeSlice(value.Type(), 0, 0)) + return nil + } + } + + elementCount, err := src.assignToRecursive(value, 0, 0) + if err != nil { + return err + } + if elementCount != len(src.Elements) { + return fmt.Errorf("cannot assign %v, needed to assign %d elements, but only assigned %d", dst, len(src.Elements), elementCount) + } + + return nil + case Null: + return NullAssignTo(dst) + } + + return fmt.Errorf("cannot decode %#v into %T", src, dst) +} + +func (src *JSONArray) assignToRecursive(value reflect.Value, index, dimension int) (int, error) { + switch kind := value.Kind(); kind { + case reflect.Array: + fallthrough + case reflect.Slice: + if len(src.Dimensions) == dimension { + break + } + + length := int(src.Dimensions[dimension].Length) + if reflect.Array == kind { + typ := value.Type() + if typ.Len() != length { + return 0, fmt.Errorf("expected size %d array, but %s has size %d array", length, typ, typ.Len()) + } + value.Set(reflect.New(typ).Elem()) + } else { + value.Set(reflect.MakeSlice(value.Type(), length, length)) + } + + var err error + for i := 0; i < length; i++ { + index, err = src.assignToRecursive(value.Index(i), index, dimension+1) + if err != nil { + return 0, err + } + } + + return index, nil + } + if len(src.Dimensions) != dimension { + return 0, fmt.Errorf("incorrect dimensions, expected %d, found %d", len(src.Dimensions), dimension) + } + if !value.CanAddr() { + return 0, fmt.Errorf("cannot assign all values from JSONArray") + } + addr := value.Addr() + if !addr.CanInterface() { + return 0, fmt.Errorf("cannot assign all values from JSONArray") + } + if err := src.Elements[index].AssignTo(addr.Interface()); err != nil { + return 0, err + } + index++ + return index, nil +} + +func (dst *JSONArray) DecodeText(ci *ConnInfo, src []byte) error { + if src == nil { + *dst = JSONArray{Status: Null} + return nil + } + + uta, err := ParseUntypedTextArray(string(src)) + if err != nil { + return err + } + + var elements []JSON + + if len(uta.Elements) > 0 { + elements = make([]JSON, len(uta.Elements)) + + for i, s := range uta.Elements { + var elem JSON + var elemSrc []byte + if s != "NULL" || uta.Quoted[i] { + elemSrc = []byte(s) + } + err = elem.DecodeText(ci, elemSrc) + if err != nil { + return err + } + + elements[i] = elem + } + } + + *dst = JSONArray{Elements: elements, Dimensions: uta.Dimensions, Status: Present} + + return nil +} + +func (dst *JSONArray) DecodeBinary(ci *ConnInfo, src []byte) error { + if src == nil { + *dst = JSONArray{Status: Null} + return nil + } + + var arrayHeader ArrayHeader + rp, err := arrayHeader.DecodeBinary(ci, src) + if err != nil { + return err + } + + if len(arrayHeader.Dimensions) == 0 { + *dst = JSONArray{Dimensions: arrayHeader.Dimensions, Status: Present} + return nil + } + + elementCount := arrayHeader.Dimensions[0].Length + for _, d := range arrayHeader.Dimensions[1:] { + elementCount *= d.Length + } + + elements := make([]JSON, elementCount) + + for i := range elements { + elemLen := int(int32(binary.BigEndian.Uint32(src[rp:]))) + rp += 4 + var elemSrc []byte + if elemLen >= 0 { + elemSrc = src[rp : rp+elemLen] + rp += elemLen + } + err = elements[i].DecodeBinary(ci, elemSrc) + if err != nil { + return err + } + } + + *dst = JSONArray{Elements: elements, Dimensions: arrayHeader.Dimensions, Status: Present} + return nil +} + +func (src JSONArray) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { + switch src.Status { + case Null: + return nil, nil + case Undefined: + return nil, errUndefined + } + + if len(src.Dimensions) == 0 { + return append(buf, '{', '}'), nil + } + + buf = EncodeTextArrayDimensions(buf, src.Dimensions) + + // dimElemCounts is the multiples of elements that each array lies on. For + // example, a single dimension array of length 4 would have a dimElemCounts of + // [4]. A multi-dimensional array of lengths [3,5,2] would have a + // dimElemCounts of [30,10,2]. This is used to simplify when to render a '{' + // or '}'. + dimElemCounts := make([]int, len(src.Dimensions)) + dimElemCounts[len(src.Dimensions)-1] = int(src.Dimensions[len(src.Dimensions)-1].Length) + for i := len(src.Dimensions) - 2; i > -1; i-- { + dimElemCounts[i] = int(src.Dimensions[i].Length) * dimElemCounts[i+1] + } + + inElemBuf := make([]byte, 0, 32) + for i, elem := range src.Elements { + if i > 0 { + buf = append(buf, ',') + } + + for _, dec := range dimElemCounts { + if i%dec == 0 { + buf = append(buf, '{') + } + } + + elemBuf, err := elem.EncodeText(ci, inElemBuf) + if err != nil { + return nil, err + } + if elemBuf == nil { + buf = append(buf, `NULL`...) + } else { + buf = append(buf, QuoteArrayElementIfNeeded(string(elemBuf))...) + } + + for _, dec := range dimElemCounts { + if (i+1)%dec == 0 { + buf = append(buf, '}') + } + } + } + + return buf, nil +} + +func (src JSONArray) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { + switch src.Status { + case Null: + return nil, nil + case Undefined: + return nil, errUndefined + } + + arrayHeader := ArrayHeader{ + Dimensions: src.Dimensions, + } + + if dt, ok := ci.DataTypeForName("json"); ok { + arrayHeader.ElementOID = int32(dt.OID) + } else { + return nil, fmt.Errorf("unable to find oid for type name %v", "json") + } + + for i := range src.Elements { + if src.Elements[i].Status == Null { + arrayHeader.ContainsNull = true + break + } + } + + buf = arrayHeader.EncodeBinary(ci, buf) + + for i := range src.Elements { + sp := len(buf) + buf = pgio.AppendInt32(buf, -1) + + elemBuf, err := src.Elements[i].EncodeBinary(ci, buf) + if err != nil { + return nil, err + } + if elemBuf != nil { + buf = elemBuf + pgio.SetInt32(buf[sp:], int32(len(buf[sp:])-4)) + } + } + + return buf, nil +} + +// Scan implements the database/sql Scanner interface. +func (dst *JSONArray) Scan(src interface{}) error { + if src == nil { + return dst.DecodeText(nil, nil) + } + + switch src := src.(type) { + case string: + return dst.DecodeText(nil, []byte(src)) + case []byte: + srcCopy := make([]byte, len(src)) + copy(srcCopy, src) + return dst.DecodeText(nil, srcCopy) + } + + return fmt.Errorf("cannot scan %T", src) +} + +// Value implements the database/sql/driver Valuer interface. +func (src JSONArray) Value() (driver.Value, error) { + buf, err := src.EncodeText(nil, nil) + if err != nil { + return nil, err + } + if buf == nil { + return nil, nil + } + + return string(buf), nil +} diff --git a/vendor/github.com/jackc/pgtype/jsonb_array.go b/vendor/github.com/jackc/pgtype/jsonb_array.go index c4b7cd3d8..e78ad3776 100644 --- a/vendor/github.com/jackc/pgtype/jsonb_array.go +++ b/vendor/github.com/jackc/pgtype/jsonb_array.go @@ -5,6 +5,7 @@ package pgtype import ( "database/sql/driver" "encoding/binary" + "encoding/json" "fmt" "reflect" @@ -72,6 +73,25 @@ func (dst *JSONBArray) Set(src interface{}) error { } } + case []json.RawMessage: + if value == nil { + *dst = JSONBArray{Status: Null} + } else if len(value) == 0 { + *dst = JSONBArray{Status: Present} + } else { + elements := make([]JSONB, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = JSONBArray{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + case []JSONB: if value == nil { *dst = JSONBArray{Status: Null} @@ -214,6 +234,15 @@ func (src *JSONBArray) AssignTo(dst interface{}) error { } return nil + case *[]json.RawMessage: + *v = make([]json.RawMessage, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + } } diff --git a/vendor/github.com/jackc/pgtype/lseg.go b/vendor/github.com/jackc/pgtype/lseg.go index 5c4babb69..894dae860 100644 --- a/vendor/github.com/jackc/pgtype/lseg.go +++ b/vendor/github.com/jackc/pgtype/lseg.go @@ -115,7 +115,7 @@ func (src Lseg) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { return nil, errUndefined } - buf = append(buf, fmt.Sprintf(`(%s,%s),(%s,%s)`, + buf = append(buf, fmt.Sprintf(`[(%s,%s),(%s,%s)]`, strconv.FormatFloat(src.P[0].X, 'f', -1, 64), strconv.FormatFloat(src.P[0].Y, 'f', -1, 64), strconv.FormatFloat(src.P[1].X, 'f', -1, 64), diff --git a/vendor/github.com/jackc/pgtype/ltree.go b/vendor/github.com/jackc/pgtype/ltree.go new file mode 100644 index 000000000..8c8d42133 --- /dev/null +++ b/vendor/github.com/jackc/pgtype/ltree.go @@ -0,0 +1,72 @@ +package pgtype + +import ( + "database/sql/driver" + "fmt" +) + +type Ltree Text + +func (dst *Ltree) Set(src interface{}) error { + return (*Text)(dst).Set(src) +} + +func (dst Ltree) Get() interface{} { + return (Text)(dst).Get() +} + +func (src *Ltree) AssignTo(dst interface{}) error { + return (*Text)(src).AssignTo(dst) +} + +func (src Ltree) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { + return (Text)(src).EncodeText(ci, buf) +} + +func (src Ltree) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { + switch src.Status { + case Null: + return nil, nil + case Undefined: + return nil, errUndefined + } + buf = append(buf, 1) + return append(buf, src.String...), nil +} + +func (Ltree) PreferredResultFormat() int16 { + return TextFormatCode +} + +func (dst *Ltree) DecodeText(ci *ConnInfo, src []byte) error { + return (*Text)(dst).DecodeText(ci, src) +} + +func (dst *Ltree) DecodeBinary(ci *ConnInfo, src []byte) error { + if src == nil { + *dst = Ltree{Status: Null} + return nil + } + + // Get Ltree version, only 1 is allowed + version := src[0] + if version != 1 { + return fmt.Errorf("unsupported ltree version %d", version) + } + + ltreeStr := string(src[1:]) + *dst = Ltree{String: ltreeStr, Status: Present} + return nil +} + +func (Ltree) PreferredParamFormat() int16 { + return TextFormatCode +} + +func (dst *Ltree) Scan(src interface{}) error { + return (*Text)(dst).Scan(src) +} + +func (src Ltree) Value() (driver.Value, error) { + return (Text)(src).Value() +} diff --git a/vendor/github.com/jackc/pgtype/multirange.go b/vendor/github.com/jackc/pgtype/multirange.go new file mode 100644 index 000000000..beb11f702 --- /dev/null +++ b/vendor/github.com/jackc/pgtype/multirange.go @@ -0,0 +1,83 @@ +package pgtype + +import ( + "bytes" + "fmt" +) + +type UntypedTextMultirange struct { + Elements []string +} + +func ParseUntypedTextMultirange(src string) (*UntypedTextMultirange, error) { + utmr := &UntypedTextMultirange{} + utmr.Elements = make([]string, 0) + + buf := bytes.NewBufferString(src) + + skipWhitespace(buf) + + r, _, err := buf.ReadRune() + if err != nil { + return nil, fmt.Errorf("invalid array: %v", err) + } + + if r != '{' { + return nil, fmt.Errorf("invalid multirange, expected '{': %v", err) + } + +parseValueLoop: + for { + r, _, err = buf.ReadRune() + if err != nil { + return nil, fmt.Errorf("invalid multirange: %v", err) + } + + switch r { + case ',': // skip range separator + case '}': + break parseValueLoop + default: + buf.UnreadRune() + value, err := parseRange(buf) + if err != nil { + return nil, fmt.Errorf("invalid multirange value: %v", err) + } + utmr.Elements = append(utmr.Elements, value) + } + } + + skipWhitespace(buf) + + if buf.Len() > 0 { + return nil, fmt.Errorf("unexpected trailing data: %v", buf.String()) + } + + return utmr, nil + +} + +func parseRange(buf *bytes.Buffer) (string, error) { + + s := &bytes.Buffer{} + + boundSepRead := false + for { + r, _, err := buf.ReadRune() + if err != nil { + return "", err + } + + switch r { + case ',', '}': + if r == ',' && !boundSepRead { + boundSepRead = true + break + } + buf.UnreadRune() + return s.String(), nil + } + + s.WriteRune(r) + } +} diff --git a/vendor/github.com/jackc/pgtype/num_multirange.go b/vendor/github.com/jackc/pgtype/num_multirange.go new file mode 100644 index 000000000..cbabc8acb --- /dev/null +++ b/vendor/github.com/jackc/pgtype/num_multirange.go @@ -0,0 +1,239 @@ +package pgtype + +import ( + "database/sql/driver" + "encoding/binary" + "fmt" + + "github.com/jackc/pgio" +) + +type Nummultirange struct { + Ranges []Numrange + Status Status +} + +func (dst *Nummultirange) Set(src interface{}) error { + //untyped nil and typed nil interfaces are different + if src == nil { + *dst = Nummultirange{Status: Null} + return nil + } + + switch value := src.(type) { + case Nummultirange: + *dst = value + case *Nummultirange: + *dst = *value + case string: + return dst.DecodeText(nil, []byte(value)) + case []Numrange: + if value == nil { + *dst = Nummultirange{Status: Null} + } else if len(value) == 0 { + *dst = Nummultirange{Status: Present} + } else { + elements := make([]Numrange, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = Nummultirange{ + Ranges: elements, + Status: Present, + } + } + case []*Numrange: + if value == nil { + *dst = Nummultirange{Status: Null} + } else if len(value) == 0 { + *dst = Nummultirange{Status: Present} + } else { + elements := make([]Numrange, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = Nummultirange{ + Ranges: elements, + Status: Present, + } + } + default: + return fmt.Errorf("cannot convert %v to Nummultirange", src) + } + + return nil + +} + +func (dst Nummultirange) Get() interface{} { + switch dst.Status { + case Present: + return dst + case Null: + return nil + default: + return dst.Status + } +} + +func (src *Nummultirange) AssignTo(dst interface{}) error { + return fmt.Errorf("cannot assign %v to %T", src, dst) +} + +func (dst *Nummultirange) DecodeText(ci *ConnInfo, src []byte) error { + if src == nil { + *dst = Nummultirange{Status: Null} + return nil + } + + utmr, err := ParseUntypedTextMultirange(string(src)) + if err != nil { + return err + } + + var elements []Numrange + + if len(utmr.Elements) > 0 { + elements = make([]Numrange, len(utmr.Elements)) + + for i, s := range utmr.Elements { + var elem Numrange + + elemSrc := []byte(s) + + err = elem.DecodeText(ci, elemSrc) + if err != nil { + return err + } + + elements[i] = elem + } + } + + *dst = Nummultirange{Ranges: elements, Status: Present} + + return nil +} + +func (dst *Nummultirange) DecodeBinary(ci *ConnInfo, src []byte) error { + if src == nil { + *dst = Nummultirange{Status: Null} + return nil + } + + rp := 0 + + numElems := int(binary.BigEndian.Uint32(src[rp:])) + rp += 4 + + if numElems == 0 { + *dst = Nummultirange{Status: Present} + return nil + } + + elements := make([]Numrange, numElems) + + for i := range elements { + elemLen := int(int32(binary.BigEndian.Uint32(src[rp:]))) + rp += 4 + var elemSrc []byte + if elemLen >= 0 { + elemSrc = src[rp : rp+elemLen] + rp += elemLen + } + err := elements[i].DecodeBinary(ci, elemSrc) + if err != nil { + return err + } + } + + *dst = Nummultirange{Ranges: elements, Status: Present} + return nil +} + +func (src Nummultirange) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { + switch src.Status { + case Null: + return nil, nil + case Undefined: + return nil, errUndefined + } + + buf = append(buf, '{') + + inElemBuf := make([]byte, 0, 32) + for i, elem := range src.Ranges { + if i > 0 { + buf = append(buf, ',') + } + + elemBuf, err := elem.EncodeText(ci, inElemBuf) + if err != nil { + return nil, err + } + if elemBuf == nil { + return nil, fmt.Errorf("multi-range does not allow null range") + } else { + buf = append(buf, string(elemBuf)...) + } + + } + + buf = append(buf, '}') + + return buf, nil +} + +func (src Nummultirange) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { + switch src.Status { + case Null: + return nil, nil + case Undefined: + return nil, errUndefined + } + + buf = pgio.AppendInt32(buf, int32(len(src.Ranges))) + + for i := range src.Ranges { + sp := len(buf) + buf = pgio.AppendInt32(buf, -1) + + elemBuf, err := src.Ranges[i].EncodeBinary(ci, buf) + if err != nil { + return nil, err + } + if elemBuf != nil { + buf = elemBuf + pgio.SetInt32(buf[sp:], int32(len(buf[sp:])-4)) + } + } + + return buf, nil +} + +// Scan implements the database/sql Scanner interface. +func (dst *Nummultirange) Scan(src interface{}) error { + if src == nil { + return dst.DecodeText(nil, nil) + } + + switch src := src.(type) { + case string: + return dst.DecodeText(nil, []byte(src)) + case []byte: + srcCopy := make([]byte, len(src)) + copy(srcCopy, src) + return dst.DecodeText(nil, srcCopy) + } + + return fmt.Errorf("cannot scan %T", src) +} + +// Value implements the database/sql/driver Valuer interface. +func (src Nummultirange) Value() (driver.Value, error) { + return EncodeValueText(src) +} diff --git a/vendor/github.com/jackc/pgtype/numeric.go b/vendor/github.com/jackc/pgtype/numeric.go index a7efa704c..1f32b36b4 100644 --- a/vendor/github.com/jackc/pgtype/numeric.go +++ b/vendor/github.com/jackc/pgtype/numeric.go @@ -1,6 +1,7 @@ package pgtype import ( + "bytes" "database/sql/driver" "encoding/binary" "fmt" @@ -18,6 +19,12 @@ const nbase = 10000 const ( pgNumericNaN = 0x00000000c0000000 pgNumericNaNSign = 0xc000 + + pgNumericPosInf = 0x00000000d0000000 + pgNumericPosInfSign = 0xd000 + + pgNumericNegInf = 0x00000000f0000000 + pgNumericNegInfSign = 0xf000 ) var big0 *big.Int = big.NewInt(0) @@ -49,10 +56,11 @@ var bigNBaseX3 *big.Int = big.NewInt(nbase * nbase * nbase) var bigNBaseX4 *big.Int = big.NewInt(nbase * nbase * nbase * nbase) type Numeric struct { - Int *big.Int - Exp int32 - Status Status - NaN bool + Int *big.Int + Exp int32 + Status Status + NaN bool + InfinityModifier InfinityModifier } func (dst *Numeric) Set(src interface{}) error { @@ -73,6 +81,12 @@ func (dst *Numeric) Set(src interface{}) error { if math.IsNaN(float64(value)) { *dst = Numeric{Status: Present, NaN: true} return nil + } else if math.IsInf(float64(value), 1) { + *dst = Numeric{Status: Present, InfinityModifier: Infinity} + return nil + } else if math.IsInf(float64(value), -1) { + *dst = Numeric{Status: Present, InfinityModifier: NegativeInfinity} + return nil } num, exp, err := parseNumericString(strconv.FormatFloat(float64(value), 'f', -1, 64)) if err != nil { @@ -83,6 +97,12 @@ func (dst *Numeric) Set(src interface{}) error { if math.IsNaN(value) { *dst = Numeric{Status: Present, NaN: true} return nil + } else if math.IsInf(value, 1) { + *dst = Numeric{Status: Present, InfinityModifier: Infinity} + return nil + } else if math.IsInf(value, -1) { + *dst = Numeric{Status: Present, InfinityModifier: NegativeInfinity} + return nil } num, exp, err := parseNumericString(strconv.FormatFloat(value, 'f', -1, 64)) if err != nil { @@ -193,6 +213,8 @@ func (dst *Numeric) Set(src interface{}) error { } else { return dst.Set(*value) } + case InfinityModifier: + *dst = Numeric{InfinityModifier: value, Status: Present} default: if originalSrc, ok := underlyingNumberType(src); ok { return dst.Set(originalSrc) @@ -206,6 +228,9 @@ func (dst *Numeric) Set(src interface{}) error { func (dst Numeric) Get() interface{} { switch dst.Status { case Present: + if dst.InfinityModifier != None { + return dst.InfinityModifier + } return dst case Null: return nil @@ -345,6 +370,18 @@ func (src *Numeric) AssignTo(dst interface{}) error { return fmt.Errorf("%d is greater than maximum value for %T", normalizedInt, *v) } *v = normalizedInt.Uint64() + case *big.Rat: + rat, err := src.toBigRat() + if err != nil { + return err + } + v.Set(rat) + case *string: + buf, err := encodeNumericText(*src, nil) + if err != nil { + return err + } + *v = string(buf) default: if nextDst, retry := GetAssignToDstType(dst); retry { return src.AssignTo(nextDst) @@ -382,9 +419,33 @@ func (dst *Numeric) toBigInt() (*big.Int, error) { return num, nil } +func (dst *Numeric) toBigRat() (*big.Rat, error) { + if dst.NaN { + return nil, fmt.Errorf("%v is not a number", dst) + } else if dst.InfinityModifier == Infinity { + return nil, fmt.Errorf("%v is infinity", dst) + } else if dst.InfinityModifier == NegativeInfinity { + return nil, fmt.Errorf("%v is -infinity", dst) + } + + num := new(big.Rat).SetInt(dst.Int) + if dst.Exp > 0 { + mul := new(big.Int).Exp(big10, big.NewInt(int64(dst.Exp)), nil) + num.Mul(num, new(big.Rat).SetInt(mul)) + } else if dst.Exp < 0 { + mul := new(big.Int).Exp(big10, big.NewInt(int64(-dst.Exp)), nil) + num.Quo(num, new(big.Rat).SetInt(mul)) + } + return num, nil +} + func (src *Numeric) toFloat64() (float64, error) { if src.NaN { return math.NaN(), nil + } else if src.InfinityModifier == Infinity { + return math.Inf(1), nil + } else if src.InfinityModifier == NegativeInfinity { + return math.Inf(-1), nil } buf := make([]byte, 0, 32) @@ -409,6 +470,12 @@ func (dst *Numeric) DecodeText(ci *ConnInfo, src []byte) error { if string(src) == "NaN" { *dst = Numeric{Status: Present, NaN: true} return nil + } else if string(src) == "Infinity" { + *dst = Numeric{Status: Present, InfinityModifier: Infinity} + return nil + } else if string(src) == "-Infinity" { + *dst = Numeric{Status: Present, InfinityModifier: NegativeInfinity} + return nil } num, exp, err := parseNumericString(string(src)) @@ -452,11 +519,11 @@ func (dst *Numeric) DecodeBinary(ci *ConnInfo, src []byte) error { } rp := 0 - ndigits := int16(binary.BigEndian.Uint16(src[rp:])) + ndigits := binary.BigEndian.Uint16(src[rp:]) rp += 2 weight := int16(binary.BigEndian.Uint16(src[rp:])) rp += 2 - sign := uint16(binary.BigEndian.Uint16(src[rp:])) + sign := binary.BigEndian.Uint16(src[rp:]) rp += 2 dscale := int16(binary.BigEndian.Uint16(src[rp:])) rp += 2 @@ -464,6 +531,12 @@ func (dst *Numeric) DecodeBinary(ci *ConnInfo, src []byte) error { if sign == pgNumericNaNSign { *dst = Numeric{Status: Present, NaN: true} return nil + } else if sign == pgNumericPosInfSign { + *dst = Numeric{Status: Present, InfinityModifier: Infinity} + return nil + } else if sign == pgNumericNegInfSign { + *dst = Numeric{Status: Present, InfinityModifier: NegativeInfinity} + return nil } if ndigits == 0 { @@ -504,7 +577,7 @@ func (dst *Numeric) DecodeBinary(ci *ConnInfo, src []byte) error { exp := (int32(weight) - int32(ndigits) + 1) * 4 if dscale > 0 { - fracNBaseDigits := ndigits - weight - 1 + fracNBaseDigits := int16(int32(ndigits) - int32(weight) - 1) fracDecimalDigits := fracNBaseDigits * 4 if dscale > fracDecimalDigits { @@ -575,6 +648,12 @@ func (src Numeric) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { if src.NaN { buf = append(buf, "NaN"...) return buf, nil + } else if src.InfinityModifier == Infinity { + buf = append(buf, "Infinity"...) + return buf, nil + } else if src.InfinityModifier == NegativeInfinity { + buf = append(buf, "-Infinity"...) + return buf, nil } buf = append(buf, src.Int.String()...) @@ -594,6 +673,12 @@ func (src Numeric) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { if src.NaN { buf = pgio.AppendUint64(buf, pgNumericNaN) return buf, nil + } else if src.InfinityModifier == Infinity { + buf = pgio.AppendUint64(buf, pgNumericPosInf) + return buf, nil + } else if src.InfinityModifier == NegativeInfinity { + buf = pgio.AppendUint64(buf, pgNumericNegInf) + return buf, nil } var sign int16 @@ -714,3 +799,55 @@ func (src Numeric) Value() (driver.Value, error) { return nil, errUndefined } } + +func encodeNumericText(n Numeric, buf []byte) (newBuf []byte, err error) { + // if !n.Valid { + // return nil, nil + // } + + if n.NaN { + buf = append(buf, "NaN"...) + return buf, nil + } else if n.InfinityModifier == Infinity { + buf = append(buf, "Infinity"...) + return buf, nil + } else if n.InfinityModifier == NegativeInfinity { + buf = append(buf, "-Infinity"...) + return buf, nil + } + + buf = append(buf, n.numberTextBytes()...) + + return buf, nil +} + +// numberString returns a string of the number. undefined if NaN, infinite, or NULL +func (n Numeric) numberTextBytes() []byte { + intStr := n.Int.String() + buf := &bytes.Buffer{} + exp := int(n.Exp) + if exp > 0 { + buf.WriteString(intStr) + for i := 0; i < exp; i++ { + buf.WriteByte('0') + } + } else if exp < 0 { + if len(intStr) <= -exp { + buf.WriteString("0.") + leadingZeros := -exp - len(intStr) + for i := 0; i < leadingZeros; i++ { + buf.WriteByte('0') + } + buf.WriteString(intStr) + } else if len(intStr) > -exp { + dpPos := len(intStr) + exp + buf.WriteString(intStr[:dpPos]) + buf.WriteByte('.') + buf.WriteString(intStr[dpPos:]) + } + } else { + buf.WriteString(intStr) + } + + return buf.Bytes() +} diff --git a/vendor/github.com/jackc/pgtype/pgtype.go b/vendor/github.com/jackc/pgtype/pgtype.go index f1d40146f..a52740e79 100644 --- a/vendor/github.com/jackc/pgtype/pgtype.go +++ b/vendor/github.com/jackc/pgtype/pgtype.go @@ -26,6 +26,7 @@ const ( XIDOID = 28 CIDOID = 29 JSONOID = 114 + JSONArrayOID = 199 PointOID = 600 LsegOID = 601 PathOID = 602 @@ -74,12 +75,15 @@ const ( JSONBArrayOID = 3807 DaterangeOID = 3912 Int4rangeOID = 3904 + Int4multirangeOID = 4451 NumrangeOID = 3906 + NummultirangeOID = 4532 TsrangeOID = 3908 TsrangeArrayOID = 3909 TstzrangeOID = 3910 TstzrangeArrayOID = 3911 Int8rangeOID = 3926 + Int8multirangeOID = 4536 ) type Status byte @@ -288,17 +292,22 @@ func NewConnInfo() *ConnInfo { ci.RegisterDataType(DataType{Value: &Int2{}, Name: "int2", OID: Int2OID}) ci.RegisterDataType(DataType{Value: &Int4{}, Name: "int4", OID: Int4OID}) ci.RegisterDataType(DataType{Value: &Int4range{}, Name: "int4range", OID: Int4rangeOID}) + ci.RegisterDataType(DataType{Value: &Int4multirange{}, Name: "int4multirange", OID: Int4multirangeOID}) ci.RegisterDataType(DataType{Value: &Int8{}, Name: "int8", OID: Int8OID}) ci.RegisterDataType(DataType{Value: &Int8range{}, Name: "int8range", OID: Int8rangeOID}) + ci.RegisterDataType(DataType{Value: &Int8multirange{}, Name: "int8multirange", OID: Int8multirangeOID}) ci.RegisterDataType(DataType{Value: &Interval{}, Name: "interval", OID: IntervalOID}) ci.RegisterDataType(DataType{Value: &JSON{}, Name: "json", OID: JSONOID}) + ci.RegisterDataType(DataType{Value: &JSONArray{}, Name: "_json", OID: JSONArrayOID}) ci.RegisterDataType(DataType{Value: &JSONB{}, Name: "jsonb", OID: JSONBOID}) + ci.RegisterDataType(DataType{Value: &JSONBArray{}, Name: "_jsonb", OID: JSONBArrayOID}) ci.RegisterDataType(DataType{Value: &Line{}, Name: "line", OID: LineOID}) ci.RegisterDataType(DataType{Value: &Lseg{}, Name: "lseg", OID: LsegOID}) ci.RegisterDataType(DataType{Value: &Macaddr{}, Name: "macaddr", OID: MacaddrOID}) ci.RegisterDataType(DataType{Value: &Name{}, Name: "name", OID: NameOID}) ci.RegisterDataType(DataType{Value: &Numeric{}, Name: "numeric", OID: NumericOID}) ci.RegisterDataType(DataType{Value: &Numrange{}, Name: "numrange", OID: NumrangeOID}) + ci.RegisterDataType(DataType{Value: &Nummultirange{}, Name: "nummultirange", OID: NummultirangeOID}) ci.RegisterDataType(DataType{Value: &OIDValue{}, Name: "oid", OID: OIDOID}) ci.RegisterDataType(DataType{Value: &Path{}, Name: "path", OID: PathOID}) ci.RegisterDataType(DataType{Value: &Point{}, Name: "point", OID: PointOID}) @@ -526,8 +535,22 @@ type scanPlanDataTypeSQLScanner DataType func (plan *scanPlanDataTypeSQLScanner) Scan(ci *ConnInfo, oid uint32, formatCode int16, src []byte, dst interface{}) error { scanner, ok := dst.(sql.Scanner) if !ok { - newPlan := ci.PlanScan(oid, formatCode, dst) - return newPlan.Scan(ci, oid, formatCode, src, dst) + dv := reflect.ValueOf(dst) + if dv.Kind() != reflect.Ptr || !dv.Type().Elem().Implements(scannerType) { + newPlan := ci.PlanScan(oid, formatCode, dst) + return newPlan.Scan(ci, oid, formatCode, src, dst) + } + if src == nil { + // Ensure the pointer points to a zero version of the value + dv.Elem().Set(reflect.Zero(dv.Type().Elem())) + return nil + } + dv = dv.Elem() + // If the pointer is to a nil pointer then set that before scanning + if dv.Kind() == reflect.Ptr && dv.IsNil() { + dv.Set(reflect.New(dv.Type().Elem())) + } + scanner = dv.Interface().(sql.Scanner) } dt := (*DataType)(plan) @@ -586,8 +609,30 @@ func (plan *scanPlanDataTypeAssignTo) Scan(ci *ConnInfo, oid uint32, formatCode type scanPlanSQLScanner struct{} func (scanPlanSQLScanner) Scan(ci *ConnInfo, oid uint32, formatCode int16, src []byte, dst interface{}) error { - scanner := dst.(sql.Scanner) - if formatCode == BinaryFormatCode { + scanner, ok := dst.(sql.Scanner) + if !ok { + dv := reflect.ValueOf(dst) + if dv.Kind() != reflect.Ptr || !dv.Type().Elem().Implements(scannerType) { + newPlan := ci.PlanScan(oid, formatCode, dst) + return newPlan.Scan(ci, oid, formatCode, src, dst) + } + if src == nil { + // Ensure the pointer points to a zero version of the value + dv.Elem().Set(reflect.Zero(dv.Elem().Type())) + return nil + } + dv = dv.Elem() + // If the pointer is to a nil pointer then set that before scanning + if dv.Kind() == reflect.Ptr && dv.IsNil() { + dv.Set(reflect.New(dv.Type().Elem())) + } + scanner = dv.Interface().(sql.Scanner) + } + if src == nil { + // This is necessary because interface value []byte:nil does not equal nil:nil for the binary format path and the + // text format path would be converted to empty string. + return scanner.Scan(nil) + } else if formatCode == BinaryFormatCode { return scanner.Scan(src) } else { return scanner.Scan(string(src)) @@ -750,6 +795,18 @@ func (scanPlanString) Scan(ci *ConnInfo, oid uint32, formatCode int16, src []byt return newPlan.Scan(ci, oid, formatCode, src, dst) } +var scannerType = reflect.TypeOf((*sql.Scanner)(nil)).Elem() + +func isScanner(dst interface{}) bool { + if _, ok := dst.(sql.Scanner); ok { + return true + } + if t := reflect.TypeOf(dst); t != nil && t.Kind() == reflect.Ptr && t.Elem().Implements(scannerType) { + return true + } + return false +} + // PlanScan prepares a plan to scan a value into dst. func (ci *ConnInfo) PlanScan(oid uint32, formatCode int16, dst interface{}) ScanPlan { switch formatCode { @@ -814,13 +871,13 @@ func (ci *ConnInfo) PlanScan(oid uint32, formatCode int16, dst interface{}) Scan } if dt != nil { - if _, ok := dst.(sql.Scanner); ok { + if isScanner(dst) { return (*scanPlanDataTypeSQLScanner)(dt) } return (*scanPlanDataTypeAssignTo)(dt) } - if _, ok := dst.(sql.Scanner); ok { + if isScanner(dst) { return scanPlanSQLScanner{} } @@ -868,72 +925,77 @@ var nameValues map[string]Value func init() { nameValues = map[string]Value{ - "_aclitem": &ACLItemArray{}, - "_bool": &BoolArray{}, - "_bpchar": &BPCharArray{}, - "_bytea": &ByteaArray{}, - "_cidr": &CIDRArray{}, - "_date": &DateArray{}, - "_float4": &Float4Array{}, - "_float8": &Float8Array{}, - "_inet": &InetArray{}, - "_int2": &Int2Array{}, - "_int4": &Int4Array{}, - "_int8": &Int8Array{}, - "_numeric": &NumericArray{}, - "_text": &TextArray{}, - "_timestamp": &TimestampArray{}, - "_timestamptz": &TimestamptzArray{}, - "_uuid": &UUIDArray{}, - "_varchar": &VarcharArray{}, - "_jsonb": &JSONBArray{}, - "aclitem": &ACLItem{}, - "bit": &Bit{}, - "bool": &Bool{}, - "box": &Box{}, - "bpchar": &BPChar{}, - "bytea": &Bytea{}, - "char": &QChar{}, - "cid": &CID{}, - "cidr": &CIDR{}, - "circle": &Circle{}, - "date": &Date{}, - "daterange": &Daterange{}, - "float4": &Float4{}, - "float8": &Float8{}, - "hstore": &Hstore{}, - "inet": &Inet{}, - "int2": &Int2{}, - "int4": &Int4{}, - "int4range": &Int4range{}, - "int8": &Int8{}, - "int8range": &Int8range{}, - "interval": &Interval{}, - "json": &JSON{}, - "jsonb": &JSONB{}, - "line": &Line{}, - "lseg": &Lseg{}, - "macaddr": &Macaddr{}, - "name": &Name{}, - "numeric": &Numeric{}, - "numrange": &Numrange{}, - "oid": &OIDValue{}, - "path": &Path{}, - "point": &Point{}, - "polygon": &Polygon{}, - "record": &Record{}, - "text": &Text{}, - "tid": &TID{}, - "timestamp": &Timestamp{}, - "timestamptz": &Timestamptz{}, - "tsrange": &Tsrange{}, - "_tsrange": &TsrangeArray{}, - "tstzrange": &Tstzrange{}, - "_tstzrange": &TstzrangeArray{}, - "unknown": &Unknown{}, - "uuid": &UUID{}, - "varbit": &Varbit{}, - "varchar": &Varchar{}, - "xid": &XID{}, + "_aclitem": &ACLItemArray{}, + "_bool": &BoolArray{}, + "_bpchar": &BPCharArray{}, + "_bytea": &ByteaArray{}, + "_cidr": &CIDRArray{}, + "_date": &DateArray{}, + "_float4": &Float4Array{}, + "_float8": &Float8Array{}, + "_inet": &InetArray{}, + "_int2": &Int2Array{}, + "_int4": &Int4Array{}, + "_int8": &Int8Array{}, + "_numeric": &NumericArray{}, + "_text": &TextArray{}, + "_timestamp": &TimestampArray{}, + "_timestamptz": &TimestamptzArray{}, + "_uuid": &UUIDArray{}, + "_varchar": &VarcharArray{}, + "_json": &JSONArray{}, + "_jsonb": &JSONBArray{}, + "aclitem": &ACLItem{}, + "bit": &Bit{}, + "bool": &Bool{}, + "box": &Box{}, + "bpchar": &BPChar{}, + "bytea": &Bytea{}, + "char": &QChar{}, + "cid": &CID{}, + "cidr": &CIDR{}, + "circle": &Circle{}, + "date": &Date{}, + "daterange": &Daterange{}, + "float4": &Float4{}, + "float8": &Float8{}, + "hstore": &Hstore{}, + "inet": &Inet{}, + "int2": &Int2{}, + "int4": &Int4{}, + "int4range": &Int4range{}, + "int4multirange": &Int4multirange{}, + "int8": &Int8{}, + "int8range": &Int8range{}, + "int8multirange": &Int8multirange{}, + "interval": &Interval{}, + "json": &JSON{}, + "jsonb": &JSONB{}, + "line": &Line{}, + "lseg": &Lseg{}, + "ltree": &Ltree{}, + "macaddr": &Macaddr{}, + "name": &Name{}, + "numeric": &Numeric{}, + "numrange": &Numrange{}, + "nummultirange": &Nummultirange{}, + "oid": &OIDValue{}, + "path": &Path{}, + "point": &Point{}, + "polygon": &Polygon{}, + "record": &Record{}, + "text": &Text{}, + "tid": &TID{}, + "timestamp": &Timestamp{}, + "timestamptz": &Timestamptz{}, + "tsrange": &Tsrange{}, + "_tsrange": &TsrangeArray{}, + "tstzrange": &Tstzrange{}, + "_tstzrange": &TstzrangeArray{}, + "unknown": &Unknown{}, + "uuid": &UUID{}, + "varbit": &Varbit{}, + "varchar": &Varchar{}, + "xid": &XID{}, } } diff --git a/vendor/github.com/jackc/pgtype/record.go b/vendor/github.com/jackc/pgtype/record.go index 718c35702..5cf2c93ab 100644 --- a/vendor/github.com/jackc/pgtype/record.go +++ b/vendor/github.com/jackc/pgtype/record.go @@ -6,7 +6,7 @@ import ( ) // Record is the generic PostgreSQL record type such as is created with the -// "row" function. Record only implements BinaryEncoder and Value. The text +// "row" function. Record only implements BinaryDecoder and Value. The text // format output format from PostgreSQL does not include type information and is // therefore impossible to decode. No encoders are implemented because // PostgreSQL does not support input of generic records. diff --git a/vendor/github.com/jackc/pgtype/record_array.go b/vendor/github.com/jackc/pgtype/record_array.go new file mode 100644 index 000000000..2271717a5 --- /dev/null +++ b/vendor/github.com/jackc/pgtype/record_array.go @@ -0,0 +1,318 @@ +// Code generated by erb. DO NOT EDIT. + +package pgtype + +import ( + "encoding/binary" + "fmt" + "reflect" +) + +type RecordArray struct { + Elements []Record + Dimensions []ArrayDimension + Status Status +} + +func (dst *RecordArray) Set(src interface{}) error { + // untyped nil and typed nil interfaces are different + if src == nil { + *dst = RecordArray{Status: Null} + return nil + } + + if value, ok := src.(interface{ Get() interface{} }); ok { + value2 := value.Get() + if value2 != value { + return dst.Set(value2) + } + } + + // Attempt to match to select common types: + switch value := src.(type) { + + case [][]Value: + if value == nil { + *dst = RecordArray{Status: Null} + } else if len(value) == 0 { + *dst = RecordArray{Status: Present} + } else { + elements := make([]Record, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = RecordArray{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []Record: + if value == nil { + *dst = RecordArray{Status: Null} + } else if len(value) == 0 { + *dst = RecordArray{Status: Present} + } else { + *dst = RecordArray{ + Elements: value, + Dimensions: []ArrayDimension{{Length: int32(len(value)), LowerBound: 1}}, + Status: Present, + } + } + default: + // Fallback to reflection if an optimised match was not found. + // The reflection is necessary for arrays and multidimensional slices, + // but it comes with a 20-50% performance penalty for large arrays/slices + reflectedValue := reflect.ValueOf(src) + if !reflectedValue.IsValid() || reflectedValue.IsZero() { + *dst = RecordArray{Status: Null} + return nil + } + + dimensions, elementsLength, ok := findDimensionsFromValue(reflectedValue, nil, 0) + if !ok { + return fmt.Errorf("cannot find dimensions of %v for RecordArray", src) + } + if elementsLength == 0 { + *dst = RecordArray{Status: Present} + return nil + } + if len(dimensions) == 0 { + if originalSrc, ok := underlyingSliceType(src); ok { + return dst.Set(originalSrc) + } + return fmt.Errorf("cannot convert %v to RecordArray", src) + } + + *dst = RecordArray{ + Elements: make([]Record, elementsLength), + Dimensions: dimensions, + Status: Present, + } + elementCount, err := dst.setRecursive(reflectedValue, 0, 0) + if err != nil { + // Maybe the target was one dimension too far, try again: + if len(dst.Dimensions) > 1 { + dst.Dimensions = dst.Dimensions[:len(dst.Dimensions)-1] + elementsLength = 0 + for _, dim := range dst.Dimensions { + if elementsLength == 0 { + elementsLength = int(dim.Length) + } else { + elementsLength *= int(dim.Length) + } + } + dst.Elements = make([]Record, elementsLength) + elementCount, err = dst.setRecursive(reflectedValue, 0, 0) + if err != nil { + return err + } + } else { + return err + } + } + if elementCount != len(dst.Elements) { + return fmt.Errorf("cannot convert %v to RecordArray, expected %d dst.Elements, but got %d instead", src, len(dst.Elements), elementCount) + } + } + + return nil +} + +func (dst *RecordArray) setRecursive(value reflect.Value, index, dimension int) (int, error) { + switch value.Kind() { + case reflect.Array: + fallthrough + case reflect.Slice: + if len(dst.Dimensions) == dimension { + break + } + + valueLen := value.Len() + if int32(valueLen) != dst.Dimensions[dimension].Length { + return 0, fmt.Errorf("multidimensional arrays must have array expressions with matching dimensions") + } + for i := 0; i < valueLen; i++ { + var err error + index, err = dst.setRecursive(value.Index(i), index, dimension+1) + if err != nil { + return 0, err + } + } + + return index, nil + } + if !value.CanInterface() { + return 0, fmt.Errorf("cannot convert all values to RecordArray") + } + if err := dst.Elements[index].Set(value.Interface()); err != nil { + return 0, fmt.Errorf("%v in RecordArray", err) + } + index++ + + return index, nil +} + +func (dst RecordArray) Get() interface{} { + switch dst.Status { + case Present: + return dst + case Null: + return nil + default: + return dst.Status + } +} + +func (src *RecordArray) AssignTo(dst interface{}) error { + switch src.Status { + case Present: + if len(src.Dimensions) <= 1 { + // Attempt to match to select common types: + switch v := dst.(type) { + + case *[][]Value: + *v = make([][]Value, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + } + } + + // Try to convert to something AssignTo can use directly. + if nextDst, retry := GetAssignToDstType(dst); retry { + return src.AssignTo(nextDst) + } + + // Fallback to reflection if an optimised match was not found. + // The reflection is necessary for arrays and multidimensional slices, + // but it comes with a 20-50% performance penalty for large arrays/slices + value := reflect.ValueOf(dst) + if value.Kind() == reflect.Ptr { + value = value.Elem() + } + + switch value.Kind() { + case reflect.Array, reflect.Slice: + default: + return fmt.Errorf("cannot assign %T to %T", src, dst) + } + + if len(src.Elements) == 0 { + if value.Kind() == reflect.Slice { + value.Set(reflect.MakeSlice(value.Type(), 0, 0)) + return nil + } + } + + elementCount, err := src.assignToRecursive(value, 0, 0) + if err != nil { + return err + } + if elementCount != len(src.Elements) { + return fmt.Errorf("cannot assign %v, needed to assign %d elements, but only assigned %d", dst, len(src.Elements), elementCount) + } + + return nil + case Null: + return NullAssignTo(dst) + } + + return fmt.Errorf("cannot decode %#v into %T", src, dst) +} + +func (src *RecordArray) assignToRecursive(value reflect.Value, index, dimension int) (int, error) { + switch kind := value.Kind(); kind { + case reflect.Array: + fallthrough + case reflect.Slice: + if len(src.Dimensions) == dimension { + break + } + + length := int(src.Dimensions[dimension].Length) + if reflect.Array == kind { + typ := value.Type() + if typ.Len() != length { + return 0, fmt.Errorf("expected size %d array, but %s has size %d array", length, typ, typ.Len()) + } + value.Set(reflect.New(typ).Elem()) + } else { + value.Set(reflect.MakeSlice(value.Type(), length, length)) + } + + var err error + for i := 0; i < length; i++ { + index, err = src.assignToRecursive(value.Index(i), index, dimension+1) + if err != nil { + return 0, err + } + } + + return index, nil + } + if len(src.Dimensions) != dimension { + return 0, fmt.Errorf("incorrect dimensions, expected %d, found %d", len(src.Dimensions), dimension) + } + if !value.CanAddr() { + return 0, fmt.Errorf("cannot assign all values from RecordArray") + } + addr := value.Addr() + if !addr.CanInterface() { + return 0, fmt.Errorf("cannot assign all values from RecordArray") + } + if err := src.Elements[index].AssignTo(addr.Interface()); err != nil { + return 0, err + } + index++ + return index, nil +} + +func (dst *RecordArray) DecodeBinary(ci *ConnInfo, src []byte) error { + if src == nil { + *dst = RecordArray{Status: Null} + return nil + } + + var arrayHeader ArrayHeader + rp, err := arrayHeader.DecodeBinary(ci, src) + if err != nil { + return err + } + + if len(arrayHeader.Dimensions) == 0 { + *dst = RecordArray{Dimensions: arrayHeader.Dimensions, Status: Present} + return nil + } + + elementCount := arrayHeader.Dimensions[0].Length + for _, d := range arrayHeader.Dimensions[1:] { + elementCount *= d.Length + } + + elements := make([]Record, elementCount) + + for i := range elements { + elemLen := int(int32(binary.BigEndian.Uint32(src[rp:]))) + rp += 4 + var elemSrc []byte + if elemLen >= 0 { + elemSrc = src[rp : rp+elemLen] + rp += elemLen + } + err = elements[i].DecodeBinary(ci, elemSrc) + if err != nil { + return err + } + } + + *dst = RecordArray{Elements: elements, Dimensions: arrayHeader.Dimensions, Status: Present} + return nil +} diff --git a/vendor/github.com/jackc/pgtype/text.go b/vendor/github.com/jackc/pgtype/text.go index 6b01d1b49..a01815d94 100644 --- a/vendor/github.com/jackc/pgtype/text.go +++ b/vendor/github.com/jackc/pgtype/text.go @@ -39,7 +39,37 @@ func (dst *Text) Set(src interface{}) error { } else { *dst = Text{String: string(value), Status: Present} } + case fmt.Stringer: + if value == fmt.Stringer(nil) { + *dst = Text{Status: Null} + } else { + *dst = Text{String: value.String(), Status: Present} + } default: + // Cannot be part of the switch: If Value() returns nil on + // non-string, we should still try to checks the underlying type + // using reflection. + // + // For example the struct might implement driver.Valuer with + // pointer receiver and fmt.Stringer with value receiver. + if value, ok := src.(driver.Valuer); ok { + if value == driver.Valuer(nil) { + *dst = Text{Status: Null} + return nil + } else { + v, err := value.Value() + if err != nil { + return fmt.Errorf("driver.Valuer Value() method failed: %w", err) + } + + // Handles also v == nil case. + if s, ok := v.(string); ok { + *dst = Text{String: s, Status: Present} + return nil + } + } + } + if originalSrc, ok := underlyingStringType(src); ok { return dst.Set(originalSrc) } diff --git a/vendor/github.com/jackc/pgtype/timestamp.go b/vendor/github.com/jackc/pgtype/timestamp.go index 466441158..fce490c83 100644 --- a/vendor/github.com/jackc/pgtype/timestamp.go +++ b/vendor/github.com/jackc/pgtype/timestamp.go @@ -4,6 +4,7 @@ import ( "database/sql/driver" "encoding/binary" "fmt" + "strings" "time" "github.com/jackc/pgio" @@ -46,6 +47,14 @@ func (dst *Timestamp) Set(src interface{}) error { } else { return dst.Set(*value) } + case string: + return dst.DecodeText(nil, []byte(value)) + case *string: + if value == nil { + *dst = Timestamp{Status: Null} + } else { + return dst.Set(*value) + } case InfinityModifier: *dst = Timestamp{InfinityModifier: value, Status: Present} default: @@ -110,6 +119,15 @@ func (dst *Timestamp) DecodeText(ci *ConnInfo, src []byte) error { case "-infinity": *dst = Timestamp{Status: Present, InfinityModifier: -Infinity} default: + if strings.HasSuffix(sbuf, " BC") { + t, err := time.Parse(pgTimestampFormat, strings.TrimRight(sbuf, " BC")) + t2 := time.Date(1-t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), t.Location()) + if err != nil { + return err + } + *dst = Timestamp{Time: t2, Status: Present} + return nil + } tim, err := time.Parse(pgTimestampFormat, sbuf) if err != nil { return err @@ -141,8 +159,10 @@ func (dst *Timestamp) DecodeBinary(ci *ConnInfo, src []byte) error { case negativeInfinityMicrosecondOffset: *dst = Timestamp{Status: Present, InfinityModifier: -Infinity} default: - microsecSinceUnixEpoch := microsecFromUnixEpochToY2K + microsecSinceY2K - tim := time.Unix(microsecSinceUnixEpoch/1000000, (microsecSinceUnixEpoch%1000000)*1000).UTC() + tim := time.Unix( + microsecFromUnixEpochToY2K/1000000+microsecSinceY2K/1000000, + (microsecFromUnixEpochToY2K%1000000*1000)+(microsecSinceY2K%1000000*1000), + ).UTC() *dst = Timestamp{Time: tim, Status: Present} } diff --git a/vendor/github.com/jackc/pgtype/timestamptz.go b/vendor/github.com/jackc/pgtype/timestamptz.go index e0743060b..72ae4991d 100644 --- a/vendor/github.com/jackc/pgtype/timestamptz.go +++ b/vendor/github.com/jackc/pgtype/timestamptz.go @@ -48,6 +48,14 @@ func (dst *Timestamptz) Set(src interface{}) error { } else { return dst.Set(*value) } + case string: + return dst.DecodeText(nil, []byte(value)) + case *string: + if value == nil { + *dst = Timestamptz{Status: Null} + } else { + return dst.Set(*value) + } case InfinityModifier: *dst = Timestamptz{InfinityModifier: value, Status: Present} default: @@ -124,7 +132,7 @@ func (dst *Timestamptz) DecodeText(ci *ConnInfo, src []byte) error { return err } - *dst = Timestamptz{Time: tim, Status: Present} + *dst = Timestamptz{Time: normalizePotentialUTC(tim), Status: Present} } return nil @@ -148,8 +156,10 @@ func (dst *Timestamptz) DecodeBinary(ci *ConnInfo, src []byte) error { case negativeInfinityMicrosecondOffset: *dst = Timestamptz{Status: Present, InfinityModifier: -Infinity} default: - microsecSinceUnixEpoch := microsecFromUnixEpochToY2K + microsecSinceY2K - tim := time.Unix(microsecSinceUnixEpoch/1000000, (microsecSinceUnixEpoch%1000000)*1000) + tim := time.Unix( + microsecFromUnixEpochToY2K/1000000+microsecSinceY2K/1000000, + (microsecFromUnixEpochToY2K%1000000*1000)+(microsecSinceY2K%1000000*1000), + ) *dst = Timestamptz{Time: tim, Status: Present} } @@ -229,6 +239,9 @@ func (src Timestamptz) Value() (driver.Value, error) { if src.InfinityModifier != None { return src.InfinityModifier.String(), nil } + if src.Time.Location().String() == time.UTC.String() { + return src.Time.UTC(), nil + } return src.Time, nil case Null: return nil, nil @@ -287,8 +300,23 @@ func (dst *Timestamptz) UnmarshalJSON(b []byte) error { return err } - *dst = Timestamptz{Time: tim, Status: Present} + *dst = Timestamptz{Time: normalizePotentialUTC(tim), Status: Present} } return nil } + +// Normalize timestamps in UTC location to behave similarly to how the Golang +// standard library does it: UTC timestamps lack a .loc value. +// +// Reason for this: when comparing two timestamps with reflect.DeepEqual (generally +// speaking not a good idea, but several testing libraries (for example testify) +// does this), their location data needs to be equal for them to be considered +// equal. +func normalizePotentialUTC(timestamp time.Time) time.Time { + if timestamp.Location().String() != time.UTC.String() { + return timestamp + } + + return timestamp.UTC() +} diff --git a/vendor/github.com/jackc/pgtype/typed_array.go.erb b/vendor/github.com/jackc/pgtype/typed_array.go.erb index 5788626b4..e8433c043 100644 --- a/vendor/github.com/jackc/pgtype/typed_array.go.erb +++ b/vendor/github.com/jackc/pgtype/typed_array.go.erb @@ -1,5 +1,17 @@ // Code generated by erb. DO NOT EDIT. +<% + # defaults when not explicitly set on command line + + binary_format ||= "true" + text_format ||= "true" + + text_null ||= "NULL" + + encode_binary ||= binary_format + decode_binary ||= binary_format +%> + package pgtype import ( @@ -279,6 +291,7 @@ func (src *<%= pgtype_array_type %>) assignToRecursive(value reflect.Value, inde return index, nil } +<% if text_format == "true" %> func (dst *<%= pgtype_array_type %>) DecodeText(ci *ConnInfo, src []byte) error { if src == nil { *dst = <%= pgtype_array_type %>{Status: Null} @@ -314,8 +327,9 @@ func (dst *<%= pgtype_array_type %>) DecodeText(ci *ConnInfo, src []byte) error return nil } +<% end %> -<% if binary_format == "true" %> +<% if decode_binary == "true" %> func (dst *<%= pgtype_array_type %>) DecodeBinary(ci *ConnInfo, src []byte) error { if src == nil { *dst = <%= pgtype_array_type %>{Status: Null} @@ -359,6 +373,7 @@ func (dst *<%= pgtype_array_type %>) DecodeBinary(ci *ConnInfo, src []byte) erro } <% end %> +<% if text_format == "true" %> func (src <%= pgtype_array_type %>) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { switch src.Status { case Null: @@ -415,8 +430,9 @@ func (src <%= pgtype_array_type %>) EncodeText(ci *ConnInfo, buf []byte) ([]byte return buf, nil } +<% end %> -<% if binary_format == "true" %> +<% if encode_binary == "true" %> func (src <%= pgtype_array_type %>) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { switch src.Status { case Null: @@ -462,6 +478,7 @@ func (src <%= pgtype_array_type %>) EncodeText(ci *ConnInfo, buf []byte) ([]byte } <% end %> +<% if text_format == "true" %> // Scan implements the database/sql Scanner interface. func (dst *<%= pgtype_array_type %>) Scan(src interface{}) error { if src == nil { @@ -492,3 +509,4 @@ func (src <%= pgtype_array_type %>) Value() (driver.Value, error) { return string(buf), nil } +<% end %> diff --git a/vendor/github.com/jackc/pgtype/typed_array_gen.sh b/vendor/github.com/jackc/pgtype/typed_array_gen.sh index ea28be077..9ec768bf1 100644 --- a/vendor/github.com/jackc/pgtype/typed_array_gen.sh +++ b/vendor/github.com/jackc/pgtype/typed_array_gen.sh @@ -1,28 +1,31 @@ -erb pgtype_array_type=Int2Array pgtype_element_type=Int2 go_array_types=[]int16,[]*int16,[]uint16,[]*uint16,[]int32,[]*int32,[]uint32,[]*uint32,[]int64,[]*int64,[]uint64,[]*uint64,[]int,[]*int,[]uint,[]*uint element_type_name=int2 text_null=NULL binary_format=true typed_array.go.erb > int2_array.go -erb pgtype_array_type=Int4Array pgtype_element_type=Int4 go_array_types=[]int16,[]*int16,[]uint16,[]*uint16,[]int32,[]*int32,[]uint32,[]*uint32,[]int64,[]*int64,[]uint64,[]*uint64,[]int,[]*int,[]uint,[]*uint element_type_name=int4 text_null=NULL binary_format=true typed_array.go.erb > int4_array.go -erb pgtype_array_type=Int8Array pgtype_element_type=Int8 go_array_types=[]int16,[]*int16,[]uint16,[]*uint16,[]int32,[]*int32,[]uint32,[]*uint32,[]int64,[]*int64,[]uint64,[]*uint64,[]int,[]*int,[]uint,[]*uint element_type_name=int8 text_null=NULL binary_format=true typed_array.go.erb > int8_array.go -erb pgtype_array_type=BoolArray pgtype_element_type=Bool go_array_types=[]bool,[]*bool element_type_name=bool text_null=NULL binary_format=true typed_array.go.erb > bool_array.go -erb pgtype_array_type=DateArray pgtype_element_type=Date go_array_types=[]time.Time,[]*time.Time element_type_name=date text_null=NULL binary_format=true typed_array.go.erb > date_array.go -erb pgtype_array_type=TimestamptzArray pgtype_element_type=Timestamptz go_array_types=[]time.Time,[]*time.Time element_type_name=timestamptz text_null=NULL binary_format=true typed_array.go.erb > timestamptz_array.go -erb pgtype_array_type=TstzrangeArray pgtype_element_type=Tstzrange go_array_types=[]Tstzrange element_type_name=tstzrange text_null=NULL binary_format=true typed_array.go.erb > tstzrange_array.go -erb pgtype_array_type=TsrangeArray pgtype_element_type=Tsrange go_array_types=[]Tsrange element_type_name=tsrange text_null=NULL binary_format=true typed_array.go.erb > tsrange_array.go -erb pgtype_array_type=TimestampArray pgtype_element_type=Timestamp go_array_types=[]time.Time,[]*time.Time element_type_name=timestamp text_null=NULL binary_format=true typed_array.go.erb > timestamp_array.go -erb pgtype_array_type=Float4Array pgtype_element_type=Float4 go_array_types=[]float32,[]*float32 element_type_name=float4 text_null=NULL binary_format=true typed_array.go.erb > float4_array.go -erb pgtype_array_type=Float8Array pgtype_element_type=Float8 go_array_types=[]float64,[]*float64 element_type_name=float8 text_null=NULL binary_format=true typed_array.go.erb > float8_array.go -erb pgtype_array_type=InetArray pgtype_element_type=Inet go_array_types=[]*net.IPNet,[]net.IP,[]*net.IP element_type_name=inet text_null=NULL binary_format=true typed_array.go.erb > inet_array.go -erb pgtype_array_type=MacaddrArray pgtype_element_type=Macaddr go_array_types=[]net.HardwareAddr,[]*net.HardwareAddr element_type_name=macaddr text_null=NULL binary_format=true typed_array.go.erb > macaddr_array.go -erb pgtype_array_type=CIDRArray pgtype_element_type=CIDR go_array_types=[]*net.IPNet,[]net.IP,[]*net.IP element_type_name=cidr text_null=NULL binary_format=true typed_array.go.erb > cidr_array.go -erb pgtype_array_type=TextArray pgtype_element_type=Text go_array_types=[]string,[]*string element_type_name=text text_null=NULL binary_format=true typed_array.go.erb > text_array.go -erb pgtype_array_type=VarcharArray pgtype_element_type=Varchar go_array_types=[]string,[]*string element_type_name=varchar text_null=NULL binary_format=true typed_array.go.erb > varchar_array.go -erb pgtype_array_type=BPCharArray pgtype_element_type=BPChar go_array_types=[]string,[]*string element_type_name=bpchar text_null=NULL binary_format=true typed_array.go.erb > bpchar_array.go -erb pgtype_array_type=ByteaArray pgtype_element_type=Bytea go_array_types=[][]byte element_type_name=bytea text_null=NULL binary_format=true typed_array.go.erb > bytea_array.go -erb pgtype_array_type=ACLItemArray pgtype_element_type=ACLItem go_array_types=[]string,[]*string element_type_name=aclitem text_null=NULL binary_format=false typed_array.go.erb > aclitem_array.go -erb pgtype_array_type=HstoreArray pgtype_element_type=Hstore go_array_types=[]map[string]string element_type_name=hstore text_null=NULL binary_format=true typed_array.go.erb > hstore_array.go -erb pgtype_array_type=NumericArray pgtype_element_type=Numeric go_array_types=[]float32,[]*float32,[]float64,[]*float64,[]int64,[]*int64,[]uint64,[]*uint64 element_type_name=numeric text_null=NULL binary_format=true typed_array.go.erb > numeric_array.go -erb pgtype_array_type=UUIDArray pgtype_element_type=UUID go_array_types=[][16]byte,[][]byte,[]string,[]*string element_type_name=uuid text_null=NULL binary_format=true typed_array.go.erb > uuid_array.go -erb pgtype_array_type=JSONBArray pgtype_element_type=JSONB go_array_types=[]string,[][]byte element_type_name=jsonb text_null=NULL binary_format=true typed_array.go.erb > jsonb_array.go +erb pgtype_array_type=Int2Array pgtype_element_type=Int2 go_array_types=[]int16,[]*int16,[]uint16,[]*uint16,[]int32,[]*int32,[]uint32,[]*uint32,[]int64,[]*int64,[]uint64,[]*uint64,[]int,[]*int,[]uint,[]*uint element_type_name=int2 typed_array.go.erb > int2_array.go +erb pgtype_array_type=Int4Array pgtype_element_type=Int4 go_array_types=[]int16,[]*int16,[]uint16,[]*uint16,[]int32,[]*int32,[]uint32,[]*uint32,[]int64,[]*int64,[]uint64,[]*uint64,[]int,[]*int,[]uint,[]*uint element_type_name=int4 typed_array.go.erb > int4_array.go +erb pgtype_array_type=Int8Array pgtype_element_type=Int8 go_array_types=[]int16,[]*int16,[]uint16,[]*uint16,[]int32,[]*int32,[]uint32,[]*uint32,[]int64,[]*int64,[]uint64,[]*uint64,[]int,[]*int,[]uint,[]*uint element_type_name=int8 typed_array.go.erb > int8_array.go +erb pgtype_array_type=BoolArray pgtype_element_type=Bool go_array_types=[]bool,[]*bool element_type_name=bool typed_array.go.erb > bool_array.go +erb pgtype_array_type=DateArray pgtype_element_type=Date go_array_types=[]time.Time,[]*time.Time element_type_name=date typed_array.go.erb > date_array.go +erb pgtype_array_type=TimestamptzArray pgtype_element_type=Timestamptz go_array_types=[]time.Time,[]*time.Time element_type_name=timestamptz typed_array.go.erb > timestamptz_array.go +erb pgtype_array_type=TstzrangeArray pgtype_element_type=Tstzrange go_array_types=[]Tstzrange element_type_name=tstzrange typed_array.go.erb > tstzrange_array.go +erb pgtype_array_type=TsrangeArray pgtype_element_type=Tsrange go_array_types=[]Tsrange element_type_name=tsrange typed_array.go.erb > tsrange_array.go +erb pgtype_array_type=TimestampArray pgtype_element_type=Timestamp go_array_types=[]time.Time,[]*time.Time element_type_name=timestamp typed_array.go.erb > timestamp_array.go +erb pgtype_array_type=Float4Array pgtype_element_type=Float4 go_array_types=[]float32,[]*float32 element_type_name=float4 typed_array.go.erb > float4_array.go +erb pgtype_array_type=Float8Array pgtype_element_type=Float8 go_array_types=[]float64,[]*float64 element_type_name=float8 typed_array.go.erb > float8_array.go +erb pgtype_array_type=InetArray pgtype_element_type=Inet go_array_types=[]*net.IPNet,[]net.IP,[]*net.IP element_type_name=inet typed_array.go.erb > inet_array.go +erb pgtype_array_type=MacaddrArray pgtype_element_type=Macaddr go_array_types=[]net.HardwareAddr,[]*net.HardwareAddr element_type_name=macaddr typed_array.go.erb > macaddr_array.go +erb pgtype_array_type=CIDRArray pgtype_element_type=CIDR go_array_types=[]*net.IPNet,[]net.IP,[]*net.IP element_type_name=cidr typed_array.go.erb > cidr_array.go +erb pgtype_array_type=TextArray pgtype_element_type=Text go_array_types=[]string,[]*string element_type_name=text typed_array.go.erb > text_array.go +erb pgtype_array_type=VarcharArray pgtype_element_type=Varchar go_array_types=[]string,[]*string element_type_name=varchar typed_array.go.erb > varchar_array.go +erb pgtype_array_type=BPCharArray pgtype_element_type=BPChar go_array_types=[]string,[]*string element_type_name=bpchar typed_array.go.erb > bpchar_array.go +erb pgtype_array_type=ByteaArray pgtype_element_type=Bytea go_array_types=[][]byte element_type_name=bytea typed_array.go.erb > bytea_array.go +erb pgtype_array_type=ACLItemArray pgtype_element_type=ACLItem go_array_types=[]string,[]*string element_type_name=aclitem binary_format=false typed_array.go.erb > aclitem_array.go +erb pgtype_array_type=HstoreArray pgtype_element_type=Hstore go_array_types=[]map[string]string element_type_name=hstore typed_array.go.erb > hstore_array.go +erb pgtype_array_type=NumericArray pgtype_element_type=Numeric go_array_types=[]float32,[]*float32,[]float64,[]*float64,[]int64,[]*int64,[]uint64,[]*uint64 element_type_name=numeric typed_array.go.erb > numeric_array.go +erb pgtype_array_type=UUIDArray pgtype_element_type=UUID go_array_types=[][16]byte,[][]byte,[]string,[]*string element_type_name=uuid typed_array.go.erb > uuid_array.go +erb pgtype_array_type=JSONArray pgtype_element_type=JSON go_array_types=[]string,[][]byte,[]json.RawMessage element_type_name=json typed_array.go.erb > json_array.go +erb pgtype_array_type=JSONBArray pgtype_element_type=JSONB go_array_types=[]string,[][]byte,[]json.RawMessage element_type_name=jsonb typed_array.go.erb > jsonb_array.go # While the binary format is theoretically possible it is only practical to use the text format. -erb pgtype_array_type=EnumArray pgtype_element_type=GenericText go_array_types=[]string,[]*string text_null=NULL binary_format=false typed_array.go.erb > enum_array.go +erb pgtype_array_type=EnumArray pgtype_element_type=GenericText go_array_types=[]string,[]*string binary_format=false typed_array.go.erb > enum_array.go + +erb pgtype_array_type=RecordArray pgtype_element_type=Record go_array_types=[][]Value element_type_name=record text_null=NULL encode_binary=false text_format=false typed_array.go.erb > record_array.go goimports -w *_array.go diff --git a/vendor/github.com/jackc/pgtype/typed_multirange.go.erb b/vendor/github.com/jackc/pgtype/typed_multirange.go.erb new file mode 100644 index 000000000..84c8299fa --- /dev/null +++ b/vendor/github.com/jackc/pgtype/typed_multirange.go.erb @@ -0,0 +1,239 @@ +package pgtype + +import ( + "database/sql/driver" + "encoding/binary" + "fmt" + + "github.com/jackc/pgio" +) + +type <%= multirange_type %> struct { + Ranges []<%= range_type %> + Status Status +} + +func (dst *<%= multirange_type %>) Set(src interface{}) error { + //untyped nil and typed nil interfaces are different + if src == nil { + *dst = <%= multirange_type %>{Status: Null} + return nil + } + + switch value := src.(type) { + case <%= multirange_type %>: + *dst = value + case *<%= multirange_type %>: + *dst = *value + case string: + return dst.DecodeText(nil, []byte(value)) + case []<%= range_type %>: + if value == nil { + *dst = <%= multirange_type %>{Status: Null} + } else if len(value) == 0 { + *dst = <%= multirange_type %>{Status: Present} + } else { + elements := make([]<%= range_type %>, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = <%= multirange_type %>{ + Ranges: elements, + Status: Present, + } + } + case []*<%= range_type %>: + if value == nil { + *dst = <%= multirange_type %>{Status: Null} + } else if len(value) == 0 { + *dst = <%= multirange_type %>{Status: Present} + } else { + elements := make([]<%= range_type %>, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = <%= multirange_type %>{ + Ranges: elements, + Status: Present, + } + } + default: + return fmt.Errorf("cannot convert %v to <%= multirange_type %>", src) + } + + return nil + +} + +func (dst <%= multirange_type %>) Get() interface{} { + switch dst.Status { + case Present: + return dst + case Null: + return nil + default: + return dst.Status + } +} + +func (src *<%= multirange_type %>) AssignTo(dst interface{}) error { + return fmt.Errorf("cannot assign %v to %T", src, dst) +} + +func (dst *<%= multirange_type %>) DecodeText(ci *ConnInfo, src []byte) error { + if src == nil { + *dst = <%= multirange_type %>{Status: Null} + return nil + } + + utmr, err := ParseUntypedTextMultirange(string(src)) + if err != nil { + return err + } + + var elements []<%= range_type %> + + if len(utmr.Elements) > 0 { + elements = make([]<%= range_type %>, len(utmr.Elements)) + + for i, s := range utmr.Elements { + var elem <%= range_type %> + + elemSrc := []byte(s) + + err = elem.DecodeText(ci, elemSrc) + if err != nil { + return err + } + + elements[i] = elem + } + } + + *dst = <%= multirange_type %>{Ranges: elements, Status: Present} + + return nil +} + +func (dst *<%= multirange_type %>) DecodeBinary(ci *ConnInfo, src []byte) error { + if src == nil { + *dst = <%= multirange_type %>{Status: Null} + return nil + } + + rp := 0 + + numElems := int(binary.BigEndian.Uint32(src[rp:])) + rp += 4 + + if numElems == 0 { + *dst = <%= multirange_type %>{Status: Present} + return nil + } + + elements := make([]<%= range_type %>, numElems) + + for i := range elements { + elemLen := int(int32(binary.BigEndian.Uint32(src[rp:]))) + rp += 4 + var elemSrc []byte + if elemLen >= 0 { + elemSrc = src[rp : rp+elemLen] + rp += elemLen + } + err := elements[i].DecodeBinary(ci, elemSrc) + if err != nil { + return err + } + } + + *dst = <%= multirange_type %>{Ranges: elements, Status: Present} + return nil +} + +func (src <%= multirange_type %>) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { + switch src.Status { + case Null: + return nil, nil + case Undefined: + return nil, errUndefined + } + + buf = append(buf, '{') + + inElemBuf := make([]byte, 0, 32) + for i, elem := range src.Ranges { + if i > 0 { + buf = append(buf, ',') + } + + elemBuf, err := elem.EncodeText(ci, inElemBuf) + if err != nil { + return nil, err + } + if elemBuf == nil { + return nil, fmt.Errorf("multi-range does not allow null range") + } else { + buf = append(buf, string(elemBuf)...) + } + + } + + buf = append(buf, '}') + + return buf, nil +} + +func (src <%= multirange_type %>) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { + switch src.Status { + case Null: + return nil, nil + case Undefined: + return nil, errUndefined + } + + buf = pgio.AppendInt32(buf, int32(len(src.Ranges))) + + for i := range src.Ranges { + sp := len(buf) + buf = pgio.AppendInt32(buf, -1) + + elemBuf, err := src.Ranges[i].EncodeBinary(ci, buf) + if err != nil { + return nil, err + } + if elemBuf != nil { + buf = elemBuf + pgio.SetInt32(buf[sp:], int32(len(buf[sp:])-4)) + } + } + + return buf, nil +} + +// Scan implements the database/sql Scanner interface. +func (dst *<%= multirange_type %>) Scan(src interface{}) error { + if src == nil { + return dst.DecodeText(nil, nil) + } + + switch src := src.(type) { + case string: + return dst.DecodeText(nil, []byte(src)) + case []byte: + srcCopy := make([]byte, len(src)) + copy(srcCopy, src) + return dst.DecodeText(nil, srcCopy) + } + + return fmt.Errorf("cannot scan %T", src) +} + +// Value implements the database/sql/driver Valuer interface. +func (src <%= multirange_type %>) Value() (driver.Value, error) { + return EncodeValueText(src) +} diff --git a/vendor/github.com/jackc/pgtype/typed_multirange_gen.sh b/vendor/github.com/jackc/pgtype/typed_multirange_gen.sh new file mode 100644 index 000000000..610f40a1e --- /dev/null +++ b/vendor/github.com/jackc/pgtype/typed_multirange_gen.sh @@ -0,0 +1,8 @@ +erb range_type=Numrange multirange_type=Nummultirange typed_multirange.go.erb > num_multirange.go +erb range_type=Int4range multirange_type=Int4multirange typed_multirange.go.erb > int4_multirange.go +erb range_type=Int8range multirange_type=Int8multirange typed_multirange.go.erb > int8_multirange.go +# TODO +# erb range_type=Tsrange multirange_type=Tsmultirange typed_multirange.go.erb > ts_multirange.go +# erb range_type=Tstzrange multirange_type=Tstzmultirange typed_multirange.go.erb > tstz_multirange.go +# erb range_type=Daterange multirange_type=Datemultirange typed_multirange.go.erb > date_multirange.go +goimports -w *multirange.go \ No newline at end of file diff --git a/vendor/github.com/jackc/pgtype/uuid.go b/vendor/github.com/jackc/pgtype/uuid.go index fa0be07fe..6839c052d 100644 --- a/vendor/github.com/jackc/pgtype/uuid.go +++ b/vendor/github.com/jackc/pgtype/uuid.go @@ -18,14 +18,15 @@ func (dst *UUID) Set(src interface{}) error { return nil } - if value, ok := src.(interface{ Get() interface{} }); ok { + switch value := src.(type) { + case interface{ Get() interface{} }: value2 := value.Get() if value2 != value { return dst.Set(value2) } - } - - switch value := src.(type) { + case fmt.Stringer: + value2 := value.String() + return dst.Set(value2) case [16]byte: *dst = UUID{Bytes: value, Status: Present} case []byte: diff --git a/vendor/github.com/jackc/pgx/v4/CHANGELOG.md b/vendor/github.com/jackc/pgx/v4/CHANGELOG.md index 1d8615139..17d29ccca 100644 --- a/vendor/github.com/jackc/pgx/v4/CHANGELOG.md +++ b/vendor/github.com/jackc/pgx/v4/CHANGELOG.md @@ -1,3 +1,114 @@ +# 4.18.2 (March 4, 2024) + +Fix CVE-2024-27289 + +SQL injection can occur when all of the following conditions are met: + +1. The non-default simple protocol is used. +2. A placeholder for a numeric value must be immediately preceded by a minus. +3. There must be a second placeholder for a string value after the first placeholder; both must be on the same line. +4. Both parameter values must be user-controlled. + +Thanks to Paul Gerste for reporting this issue. + +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 *dbTx.Exec not checking if it is already closed + +# 4.18.1 (February 27, 2023) + +* Fix: Support pgx v4 and v5 stdlib in same program (Tomáš Procházka) + +# 4.18.0 (February 11, 2023) + +* Upgrade pgconn to v1.14.0 +* Upgrade pgproto3 to v2.3.2 +* Upgrade pgtype to v1.14.0 +* Fix query sanitizer when query text contains Unicode replacement character +* Fix context with value in BeforeConnect (David Harju) +* Support pgx v4 and v5 stdlib in same program (Vitalii Solodilov) + +# 4.17.2 (September 3, 2022) + +* Fix panic when logging batch error (Tom Möller) + +# 4.17.1 (August 27, 2022) + +* Upgrade puddle to v1.3.0 - fixes context failing to cancel Acquire when acquire is creating resource which was introduced in v4.17.0 (James Hartig) +* Fix atomic alignment on 32-bit platforms + +# 4.17.0 (August 6, 2022) + +* Upgrade pgconn to v1.13.0 +* Upgrade pgproto3 to v2.3.1 +* Upgrade pgtype to v1.12.0 +* Allow background pool connections to continue even if cause is canceled (James Hartig) +* Add LoggerFunc (Gabor Szabad) +* pgxpool: health check should avoid going below minConns (James Hartig) +* Add pgxpool.Conn.Hijack() +* Logging improvements (Stepan Rabotkin) + +# 4.16.1 (May 7, 2022) + +* Upgrade pgconn to v1.12.1 +* Fix explicitly prepared statements with describe statement cache mode + +# 4.16.0 (April 21, 2022) + +* Upgrade pgconn to v1.12.0 +* Upgrade pgproto3 to v2.3.0 +* Upgrade pgtype to v1.11.0 +* Fix: Do not panic when context cancelled while getting statement from cache. +* Fix: Less memory pinning from old Rows. +* Fix: Support '\r' line ending when sanitizing SQL comment. +* Add pluggable GSSAPI support (Oliver Tan) + +# 4.15.0 (February 7, 2022) + +* Upgrade to pgconn v1.11.0 +* Upgrade to pgtype v1.10.0 +* Upgrade puddle to v1.2.1 +* Make BatchResults.Close safe to be called multiple times + +# 4.14.1 (November 28, 2021) + +* Upgrade pgtype to v1.9.1 (fixes unintentional change to timestamp binary decoding) +* Start pgxpool background health check after initial connections + +# 4.14.0 (November 20, 2021) + +* Upgrade pgconn to v1.10.1 +* Upgrade pgproto3 to v2.2.0 +* Upgrade pgtype to v1.9.0 +* Upgrade puddle to v1.2.0 +* Add QueryFunc to BatchResults +* Add context options to zerologadapter (Thomas Frössman) +* Add zerologadapter.NewContextLogger (urso) +* Eager initialize minpoolsize on connect (Daniel) +* Unpin memory used by large queries immediately after use + +# 4.13.0 (July 24, 2021) + +* Trimmed pseudo-dependencies in Go modules from other packages tests +* Upgrade pgconn -- context cancellation no longer will return a net.Error +* Support time durations for simple protocol (Michael Darr) + +# 4.12.0 (July 10, 2021) + +* ResetSession hook is called before a connection is reused from pool for another query (Dmytro Haranzha) +* stdlib: Add RandomizeHostOrderFunc (dkinder) +* stdlib: add OptionBeforeConnect (dkinder) +* stdlib: Do not reuse ConnConfig strings (Andrew Kimball) +* stdlib: implement Conn.ResetSession (Jonathan Amsterdam) +* Upgrade pgconn to v1.9.0 +* Upgrade pgtype to v1.8.0 + # 4.11.0 (March 25, 2021) * Add BeforeConnect callback to pgxpool.Config (Robert Froehlich) diff --git a/vendor/github.com/jackc/pgx/v4/README.md b/vendor/github.com/jackc/pgx/v4/README.md index 00b11f97e..46b9c559b 100644 --- a/vendor/github.com/jackc/pgx/v4/README.md +++ b/vendor/github.com/jackc/pgx/v4/README.md @@ -1,6 +1,11 @@ [![](https://godoc.org/github.com/jackc/pgx?status.svg)](https://pkg.go.dev/github.com/jackc/pgx/v4) [![Build Status](https://travis-ci.org/jackc/pgx.svg)](https://travis-ci.org/jackc/pgx) +--- + +This is the previous stable `v4` release. `v5` been released. + +--- # pgx - PostgreSQL Driver and Toolkit pgx is a pure Go driver and toolkit for PostgreSQL. @@ -29,6 +34,7 @@ import ( ) func main() { + // urlExample := "postgres://username:password@localhost:5432/database_name" conn, err := pgx.Connect(context.Background(), os.Getenv("DATABASE_URL")) if err != nil { fmt.Fprintf(os.Stderr, "Unable to connect to database: %v\n", err) @@ -72,7 +78,7 @@ pgx supports many features beyond what is available through `database/sql`: * Single-round trip query mode * Full TLS connection control * Binary format support for custom types (allows for much quicker encoding/decoding) -* Copy protocol support for faster bulk data loads +* COPY protocol support for faster bulk data loads * Extendable logging support including built-in support for `log15adapter`, [`logrus`](https://github.com/sirupsen/logrus), [`zap`](https://github.com/uber-go/zap), and [`zerolog`](https://github.com/rs/zerolog) * Connection pool with after-connect hook for arbitrary connection setup * Listen / notify @@ -97,26 +103,6 @@ There are three areas in particular where pgx can provide a significant performa perform nearly 3x the number of queries per second. 3. Batched queries - Multiple queries can be batched together to minimize network round trips. -## Comparison with Alternatives - -* [pq](http://godoc.org/github.com/lib/pq) -* [go-pg](https://github.com/go-pg/pg) - -For prepared queries with small sets of simple data types, all drivers will have have similar performance. However, if prepared statements aren't being explicitly used, pgx can have a significant performance advantage due to automatic statement preparation. -pgx also can perform better when using PostgreSQL-specific data types or query batching. See -[go_db_bench](https://github.com/jackc/go_db_bench) for some database driver benchmarks. - -### Compatibility with `database/sql` - -pq is exclusively used with `database/sql`. go-pg does not use `database/sql` at all. pgx supports `database/sql` as well as -its own interface. - -### Level of access, ORM - -go-pg is a PostgreSQL client and ORM. It includes many features that traditionally sit above the database driver, such as ORM, struct mapping, soft deletes, schema migrations, and sharding support. - -pgx is "closer to the metal" and such abstractions are beyond the scope of the pgx project, which first and foremost, aims to be a performant driver and toolkit. - ## Testing pgx tests naturally require a PostgreSQL database. It will connect to the database specified in the `PGX_TEST_DATABASE` environment @@ -148,7 +134,7 @@ In addition, there are tests specific for PgBouncer that will be executed if `PG ## 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.15 and higher and PostgreSQL 9.6 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.17 and higher and PostgreSQL 10 and higher. pgx also is tested against the latest version of [CockroachDB](https://www.cockroachlabs.com/product/). ## Version Policy @@ -200,3 +186,11 @@ pgerrcode contains constants for the PostgreSQL error codes. ### [github.com/georgysavva/scany](https://github.com/georgysavva/scany) Library for scanning data from a database into Go structs and more. + +### [https://github.com/otan/gopgkrb5](https://github.com/otan/gopgkrb5) + +Adds GSSAPI / Kerberos authentication support. + +### [https://github.com/vgarvardt/pgx-google-uuid](https://github.com/vgarvardt/pgx-google-uuid) + +Adds support for [`github.com/google/uuid`](https://github.com/google/uuid). diff --git a/vendor/github.com/jackc/pgx/v4/batch.go b/vendor/github.com/jackc/pgx/v4/batch.go index 4b96ca194..7f86ad5c3 100644 --- a/vendor/github.com/jackc/pgx/v4/batch.go +++ b/vendor/github.com/jackc/pgx/v4/batch.go @@ -3,6 +3,7 @@ package pgx import ( "context" "errors" + "fmt" "github.com/jackc/pgconn" ) @@ -41,19 +42,23 @@ type BatchResults interface { // QueryRow reads the results from the next query in the batch as if the query has been sent with Conn.QueryRow. QueryRow() Row + // QueryFunc reads the results from the next query in the batch as if the query has been sent with Conn.QueryFunc. + QueryFunc(scans []interface{}, f func(QueryFuncRow) error) (pgconn.CommandTag, error) + // Close closes the batch operation. This must be called before the underlying connection can be used again. Any error // that occurred during a batch operation may have made it impossible to resyncronize the connection with the server. - // In this case the underlying connection will have been closed. + // In this case the underlying connection will have been closed. Close is safe to call multiple times. Close() error } type batchResults struct { - ctx context.Context - conn *Conn - mrr *pgconn.MultiResultReader - err error - b *Batch - ix int + ctx context.Context + conn *Conn + mrr *pgconn.MultiResultReader + err error + b *Batch + ix int + closed bool } // Exec reads the results from the next query in the batch as if the query has been sent with Exec. @@ -61,6 +66,9 @@ func (br *batchResults) Exec() (pgconn.CommandTag, error) { if br.err != nil { return nil, br.err } + if br.closed { + return nil, fmt.Errorf("batch already closed") + } query, arguments, _ := br.nextQueryAndArgs() @@ -111,6 +119,11 @@ func (br *batchResults) Query() (Rows, error) { return &connRows{err: br.err, closed: true}, br.err } + if br.closed { + alreadyClosedErr := fmt.Errorf("batch already closed") + return &connRows{err: alreadyClosedErr, closed: true}, alreadyClosedErr + } + rows := br.conn.getRows(br.ctx, query, arguments) if !br.mrr.NextResult() { @@ -135,6 +148,37 @@ func (br *batchResults) Query() (Rows, error) { return rows, nil } +// QueryFunc reads the results from the next query in the batch as if the query has been sent with Conn.QueryFunc. +func (br *batchResults) QueryFunc(scans []interface{}, f func(QueryFuncRow) error) (pgconn.CommandTag, error) { + if br.closed { + return nil, fmt.Errorf("batch already closed") + } + + rows, err := br.Query() + if err != nil { + return nil, err + } + defer rows.Close() + + for rows.Next() { + err = rows.Scan(scans...) + if err != nil { + return nil, err + } + + err = f(rows) + if err != nil { + return nil, err + } + } + + if err := rows.Err(); err != nil { + return nil, err + } + + return rows.CommandTag(), nil +} + // QueryRow reads the results from the next query in the batch as if the query has been sent with QueryRow. func (br *batchResults) QueryRow() Row { rows, _ := br.Query() @@ -149,6 +193,11 @@ func (br *batchResults) Close() error { return br.err } + if br.closed { + return nil + } + br.closed = true + // log any queries that haven't yet been logged by Exec or Query for { query, args, ok := br.nextQueryAndArgs() diff --git a/vendor/github.com/jackc/pgx/v4/conn.go b/vendor/github.com/jackc/pgx/v4/conn.go index 9636f2fd6..6f83f4972 100644 --- a/vendor/github.com/jackc/pgx/v4/conn.go +++ b/vendor/github.com/jackc/pgx/v4/conn.go @@ -50,6 +50,7 @@ func (cc *ConnConfig) Copy() *ConnConfig { return newConfig } +// ConnString returns the connection string as parsed by pgx.ParseConfig into pgx.ConnConfig. func (cc *ConnConfig) ConnString() string { return cc.connString } // BuildStatementCacheFunc is a function that can be used to create a stmtcache.Cache implementation for connection. @@ -72,9 +73,8 @@ type Conn struct { connInfo *pgtype.ConnInfo - wbuf []byte - preallocatedRows []connRows - eqb extendedQueryBuilder + wbuf []byte + eqb extendedQueryBuilder } // Identifier a PostgreSQL identifier or name. Identifiers can be composed of @@ -107,8 +107,8 @@ func Connect(ctx context.Context, connString string) (*Conn, error) { return connect(ctx, connConfig) } -// Connect establishes a connection with a PostgreSQL server with a configuration struct. connConfig must have been -// created by ParseConfig. +// ConnectConfig establishes a connection with a PostgreSQL server with a configuration struct. +// connConfig must have been created by ParseConfig. func ConnectConfig(ctx context.Context, connConfig *ConnConfig) (*Conn, error) { return connect(ctx, connConfig) } @@ -116,14 +116,14 @@ func ConnectConfig(ctx context.Context, connConfig *ConnConfig) (*Conn, error) { // ParseConfig creates a ConnConfig from a connection string. ParseConfig handles all options that pgconn.ParseConfig // does. In addition, it accepts the following options: // -// statement_cache_capacity -// The maximum size of the automatic statement cache. Set to 0 to disable automatic statement caching. Default: 512. +// statement_cache_capacity +// The maximum size of the automatic statement cache. Set to 0 to disable automatic statement caching. Default: 512. // -// statement_cache_mode -// Possible values: "prepare" and "describe". "prepare" will create prepared statements on the PostgreSQL server. -// "describe" will use the anonymous prepared statement to describe a statement without creating a statement on the -// server. "describe" is primarily useful when the environment does not allow prepared statements such as when -// running a connection pooler like PgBouncer. Default: "prepare" +// statement_cache_mode +// Possible values: "prepare" and "describe". "prepare" will create prepared statements on the PostgreSQL server. +// "describe" will use the anonymous prepared statement to describe a statement without creating a statement on the +// server. "describe" is primarily useful when the environment does not allow prepared statements such as when +// running a connection pooler like PgBouncer. Default: "prepare" // // prefer_simple_protocol // Possible values: "true" and "false". Use the simple protocol instead of extended protocol. Default: false @@ -324,6 +324,7 @@ func (c *Conn) WaitForNotification(ctx context.Context) (*pgconn.Notification, e return n, err } +// IsClosed reports if the connection has been closed. func (c *Conn) IsClosed() bool { return c.pgConn.IsClosed() } @@ -357,35 +358,13 @@ func quoteIdentifier(s string) string { return `"` + strings.ReplaceAll(s, `"`, `""`) + `"` } +// Ping executes an empty sql statement against the *Conn +// If the sql returns without error, the database Ping is considered successful, otherwise, the error is returned. func (c *Conn) Ping(ctx context.Context) error { _, err := c.Exec(ctx, ";") return err } -func connInfoFromRows(rows Rows, err error) (map[string]uint32, error) { - if err != nil { - return nil, err - } - defer rows.Close() - - nameOIDs := make(map[string]uint32, 256) - for rows.Next() { - var oid uint32 - var name pgtype.Text - if err = rows.Scan(&oid, &name); err != nil { - return nil, err - } - - nameOIDs[name.String] = oid - } - - if err = rows.Err(); err != nil { - return nil, err - } - - return nameOIDs, err -} - // PgConn returns the underlying *pgconn.PgConn. This is an escape hatch method that allows lower level access to the // PostgreSQL connection than pgx exposes. // @@ -410,7 +389,8 @@ func (c *Conn) Exec(ctx context.Context, sql string, arguments ...interface{}) ( commandTag, err := c.exec(ctx, sql, arguments...) if err != nil { if c.shouldLog(LogLevelError) { - c.log(ctx, LogLevelError, "Exec", map[string]interface{}{"sql": sql, "args": logQueryArgs(arguments), "err": err}) + endTime := time.Now() + c.log(ctx, LogLevelError, "Exec", map[string]interface{}{"sql": sql, "args": logQueryArgs(arguments), "err": err, "time": endTime.Sub(startTime)}) } return commandTag, err } @@ -517,6 +497,7 @@ func (c *Conn) execParams(ctx context.Context, sd *pgconn.StatementDescription, } result := c.pgConn.ExecParams(ctx, sd.SQL, c.eqb.paramValues, sd.ParamOIDs, c.eqb.paramFormats, c.eqb.resultFormats).Read() + c.eqb.Reset() // Allow c.eqb internal memory to be GC'ed as soon as possible. return result.CommandTag, result.Err } @@ -527,16 +508,12 @@ func (c *Conn) execPrepared(ctx context.Context, sd *pgconn.StatementDescription } result := c.pgConn.ExecPrepared(ctx, sd.Name, c.eqb.paramValues, c.eqb.paramFormats, c.eqb.resultFormats).Read() + c.eqb.Reset() // Allow c.eqb internal memory to be GC'ed as soon as possible. return result.CommandTag, result.Err } func (c *Conn) getRows(ctx context.Context, sql string, args []interface{}) *connRows { - if len(c.preallocatedRows) == 0 { - c.preallocatedRows = make([]connRows, 64) - } - - r := &c.preallocatedRows[len(c.preallocatedRows)-1] - c.preallocatedRows = c.preallocatedRows[0 : len(c.preallocatedRows)-1] + r := &connRows{} r.ctx = ctx r.logger = c @@ -558,8 +535,16 @@ type QueryResultFormats []int16 // QueryResultFormatsByOID controls the result format (text=0, binary=1) of a query by the result column OID. type QueryResultFormatsByOID map[uint32]int16 -// Query executes sql with args. If there is an error the returned Rows will be returned in an error state. So it is -// allowed to ignore the error returned from Query and handle it in Rows. +// Query sends a query to the server and returns a Rows to read the results. Only errors encountered sending the query +// and initializing Rows will be returned. Err() on the returned Rows must be checked after the Rows is closed to +// determine if the query executed successfully. +// +// The returned Rows must be closed before the connection can be used again. It is safe to attempt to read from the +// returned Rows even if an error is returned. The error will be the available in rows.Err() after rows are closed. It +// is allowed to ignore the error returned from Query and handle it in Rows. +// +// Err() on the returned Rows must be checked after the Rows is closed to determine if the query executed successfully +// as some errors can only be detected by reading the entire response. e.g. A divide by zero error on the last row. // // For extra control over how the query is executed, the types QuerySimpleProtocol, QueryResultFormats, and // QueryResultFormatsByOID may be used as the first args to control exactly how the query is executed. This is rarely @@ -664,12 +649,14 @@ optionLoop: resultFormats = c.eqb.resultFormats } - if c.stmtcache != nil && c.stmtcache.Mode() == stmtcache.ModeDescribe { + if c.stmtcache != nil && c.stmtcache.Mode() == stmtcache.ModeDescribe && !ok { rows.resultReader = c.pgConn.ExecParams(ctx, sql, c.eqb.paramValues, sd.ParamOIDs, c.eqb.paramFormats, resultFormats) } else { rows.resultReader = c.pgConn.ExecPrepared(ctx, sd.Name, c.eqb.paramValues, c.eqb.paramFormats, resultFormats) } + c.eqb.Reset() // Allow c.eqb internal memory to be GC'ed as soon as possible. + return rows, rows.err } @@ -727,6 +714,8 @@ func (c *Conn) QueryFunc(ctx context.Context, sql string, args []interface{}, sc // explicit transaction control statements are executed. The returned BatchResults must be closed before the connection // is used again. func (c *Conn) SendBatch(ctx context.Context, b *Batch) BatchResults { + startTime := time.Now() + simpleProtocol := c.config.PreferSimpleProtocol var sb strings.Builder if simpleProtocol { @@ -785,24 +774,23 @@ func (c *Conn) SendBatch(ctx context.Context, b *Batch) BatchResults { var err error sd, err = stmtCache.Get(ctx, bi.query) if err != nil { - // the stmtCache was prefilled from distinctUnpreparedQueries above so we are guaranteed no errors - panic("BUG: unexpected error from stmtCache") + return c.logBatchResults(ctx, startTime, &batchResults{ctx: ctx, conn: c, err: err}) } } if len(sd.ParamOIDs) != len(bi.arguments) { - return &batchResults{ctx: ctx, conn: c, err: fmt.Errorf("mismatched param and argument count")} + return c.logBatchResults(ctx, startTime, &batchResults{ctx: ctx, conn: c, err: fmt.Errorf("mismatched param and argument count")}) } args, err := convertDriverValuers(bi.arguments) if err != nil { - return &batchResults{ctx: ctx, conn: c, err: err} + return c.logBatchResults(ctx, startTime, &batchResults{ctx: ctx, conn: c, err: err}) } for i := range args { err = c.eqb.AppendParam(c.connInfo, sd.ParamOIDs[i], args[i]) if err != nil { - return &batchResults{ctx: ctx, conn: c, err: err} + return c.logBatchResults(ctx, startTime, &batchResults{ctx: ctx, conn: c, err: err}) } } @@ -817,15 +805,34 @@ func (c *Conn) SendBatch(ctx context.Context, b *Batch) BatchResults { } } + c.eqb.Reset() // Allow c.eqb internal memory to be GC'ed as soon as possible. + mrr := c.pgConn.ExecBatch(ctx, batch) - return &batchResults{ + return c.logBatchResults(ctx, startTime, &batchResults{ ctx: ctx, conn: c, mrr: mrr, b: b, ix: 0, + }) +} + +func (c *Conn) logBatchResults(ctx context.Context, startTime time.Time, results *batchResults) BatchResults { + if results.err != nil { + if c.shouldLog(LogLevelError) { + endTime := time.Now() + c.log(ctx, LogLevelError, "SendBatch", map[string]interface{}{"err": results.err, "time": endTime.Sub(startTime)}) + } + return results + } + + if c.shouldLog(LogLevelInfo) { + endTime := time.Now() + c.log(ctx, LogLevelInfo, "SendBatch", map[string]interface{}{"batchLen": results.b.Len(), "time": endTime.Sub(startTime)}) } + + return results } func (c *Conn) sanitizeForSimpleQuery(sql string, args ...interface{}) (string, error) { diff --git a/vendor/github.com/jackc/pgx/v4/copy_from.go b/vendor/github.com/jackc/pgx/v4/copy_from.go index 3494e28f9..49139d050 100644 --- a/vendor/github.com/jackc/pgx/v4/copy_from.go +++ b/vendor/github.com/jackc/pgx/v4/copy_from.go @@ -153,13 +153,13 @@ func (ct *copyFrom) run(ctx context.Context) (int64, error) { <-doneChan rowsAffected := commandTag.RowsAffected() + endTime := time.Now() if err == nil { if ct.conn.shouldLog(LogLevelInfo) { - endTime := time.Now() ct.conn.log(ctx, LogLevelInfo, "CopyFrom", map[string]interface{}{"tableName": ct.tableName, "columnNames": ct.columnNames, "time": endTime.Sub(startTime), "rowCount": rowsAffected}) } } else if ct.conn.shouldLog(LogLevelError) { - ct.conn.log(ctx, LogLevelError, "CopyFrom", map[string]interface{}{"err": err, "tableName": ct.tableName, "columnNames": ct.columnNames}) + ct.conn.log(ctx, LogLevelError, "CopyFrom", map[string]interface{}{"err": err, "tableName": ct.tableName, "columnNames": ct.columnNames, "time": endTime.Sub(startTime)}) } return rowsAffected, err diff --git a/vendor/github.com/jackc/pgx/v4/doc.go b/vendor/github.com/jackc/pgx/v4/doc.go index 51b0d9f44..222f90479 100644 --- a/vendor/github.com/jackc/pgx/v4/doc.go +++ b/vendor/github.com/jackc/pgx/v4/doc.go @@ -309,7 +309,7 @@ CopyFrom can be faster than an insert with as few as 5 rows. Listen and Notify pgx can listen to the PostgreSQL notification system with the `Conn.WaitForNotification` method. It blocks until a -context is received or the context is canceled. +notification is received or the context is canceled. _, err := conn.Exec(context.Background(), "listen channelname") if err != nil { diff --git a/vendor/github.com/jackc/pgx/v4/extended_query_builder.go b/vendor/github.com/jackc/pgx/v4/extended_query_builder.go index 09419f0d0..d06f63fd1 100644 --- a/vendor/github.com/jackc/pgx/v4/extended_query_builder.go +++ b/vendor/github.com/jackc/pgx/v4/extended_query_builder.go @@ -13,8 +13,6 @@ type extendedQueryBuilder struct { paramValueBytes []byte paramFormats []int16 resultFormats []int16 - - resetCount int } func (eqb *extendedQueryBuilder) AppendParam(ci *pgtype.ConnInfo, oid uint32, arg interface{}) error { @@ -34,32 +32,27 @@ func (eqb *extendedQueryBuilder) AppendResultFormat(f int16) { eqb.resultFormats = append(eqb.resultFormats, f) } +// Reset readies eqb to build another query. func (eqb *extendedQueryBuilder) Reset() { eqb.paramValues = eqb.paramValues[0:0] eqb.paramValueBytes = eqb.paramValueBytes[0:0] eqb.paramFormats = eqb.paramFormats[0:0] eqb.resultFormats = eqb.resultFormats[0:0] - eqb.resetCount++ - - // Every so often shrink our reserved memory if it is abnormally high - if eqb.resetCount%128 == 0 { - if cap(eqb.paramValues) > 64 { - eqb.paramValues = make([][]byte, 0, cap(eqb.paramValues)/2) - } - - if cap(eqb.paramValueBytes) > 256 { - eqb.paramValueBytes = make([]byte, 0, cap(eqb.paramValueBytes)/2) - } + if cap(eqb.paramValues) > 64 { + eqb.paramValues = make([][]byte, 0, 64) + } - if cap(eqb.paramFormats) > 64 { - eqb.paramFormats = make([]int16, 0, cap(eqb.paramFormats)/2) - } - if cap(eqb.resultFormats) > 64 { - eqb.resultFormats = make([]int16, 0, cap(eqb.resultFormats)/2) - } + if cap(eqb.paramValueBytes) > 256 { + eqb.paramValueBytes = make([]byte, 0, 256) } + if cap(eqb.paramFormats) > 64 { + eqb.paramFormats = make([]int16, 0, 64) + } + if cap(eqb.resultFormats) > 64 { + eqb.resultFormats = make([]int16, 0, 64) + } } func (eqb *extendedQueryBuilder) encodeExtendedParamValue(ci *pgtype.ConnInfo, oid uint32, formatCode int16, arg interface{}) ([]byte, error) { diff --git a/vendor/github.com/jackc/pgx/v4/internal/sanitize/sanitize.go b/vendor/github.com/jackc/pgx/v4/internal/sanitize/sanitize.go index 2dba3b810..4c345d508 100644 --- a/vendor/github.com/jackc/pgx/v4/internal/sanitize/sanitize.go +++ b/vendor/github.com/jackc/pgx/v4/internal/sanitize/sanitize.go @@ -18,6 +18,12 @@ type Query struct { Parts []Part } +// utf.DecodeRune returns the utf8.RuneError for errors. But that is actually rune U+FFFD -- the unicode replacement +// character. utf8.RuneError is not an error if it is also width 3. +// +// https://github.com/jackc/pgx/issues/1380 +const replacementcharacterwidth = 3 + func (q *Query) Sanitize(args ...interface{}) (string, error) { argUse := make([]bool, len(args)) buf := &bytes.Buffer{} @@ -52,6 +58,10 @@ func (q *Query) Sanitize(args ...interface{}) (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) } @@ -138,11 +148,13 @@ func rawState(l *sqlLexer) stateFn { return multilineCommentState } case utf8.RuneError: - if l.pos-l.start > 0 { - l.parts = append(l.parts, l.src[l.start:l.pos]) - l.start = l.pos + if width != replacementcharacterwidth { + if l.pos-l.start > 0 { + l.parts = append(l.parts, l.src[l.start:l.pos]) + l.start = l.pos + } + return nil } - return nil } } } @@ -160,11 +172,13 @@ func singleQuoteState(l *sqlLexer) stateFn { } l.pos += width case utf8.RuneError: - if l.pos-l.start > 0 { - l.parts = append(l.parts, l.src[l.start:l.pos]) - l.start = l.pos + if width != replacementcharacterwidth { + if l.pos-l.start > 0 { + l.parts = append(l.parts, l.src[l.start:l.pos]) + l.start = l.pos + } + return nil } - return nil } } } @@ -182,11 +196,13 @@ func doubleQuoteState(l *sqlLexer) stateFn { } l.pos += width case utf8.RuneError: - if l.pos-l.start > 0 { - l.parts = append(l.parts, l.src[l.start:l.pos]) - l.start = l.pos + if width != replacementcharacterwidth { + if l.pos-l.start > 0 { + l.parts = append(l.parts, l.src[l.start:l.pos]) + l.start = l.pos + } + return nil } - return nil } } } @@ -228,11 +244,13 @@ func escapeStringState(l *sqlLexer) stateFn { } l.pos += width case utf8.RuneError: - if l.pos-l.start > 0 { - l.parts = append(l.parts, l.src[l.start:l.pos]) - l.start = l.pos + if width != replacementcharacterwidth { + if l.pos-l.start > 0 { + l.parts = append(l.parts, l.src[l.start:l.pos]) + l.start = l.pos + } + return nil } - return nil } } } @@ -246,14 +264,16 @@ func oneLineCommentState(l *sqlLexer) stateFn { case '\\': _, width = utf8.DecodeRuneInString(l.src[l.pos:]) l.pos += width - case '\n': + case '\n', '\r': return rawState case utf8.RuneError: - if l.pos-l.start > 0 { - l.parts = append(l.parts, l.src[l.start:l.pos]) - l.start = l.pos + if width != replacementcharacterwidth { + if l.pos-l.start > 0 { + l.parts = append(l.parts, l.src[l.start:l.pos]) + l.start = l.pos + } + return nil } - return nil } } } @@ -283,11 +303,13 @@ func multilineCommentState(l *sqlLexer) stateFn { l.nested-- case utf8.RuneError: - if l.pos-l.start > 0 { - l.parts = append(l.parts, l.src[l.start:l.pos]) - l.start = l.pos + if width != replacementcharacterwidth { + if l.pos-l.start > 0 { + l.parts = append(l.parts, l.src[l.start:l.pos]) + l.start = l.pos + } + return nil } - return nil } } } diff --git a/vendor/github.com/jackc/pgx/v4/large_objects.go b/vendor/github.com/jackc/pgx/v4/large_objects.go index 5255a3b48..c238ab9c2 100644 --- a/vendor/github.com/jackc/pgx/v4/large_objects.go +++ b/vendor/github.com/jackc/pgx/v4/large_objects.go @@ -56,10 +56,10 @@ func (o *LargeObjects) Unlink(ctx context.Context, oid uint32) error { // A LargeObject is a large object stored on the server. It is only valid within the transaction that it was initialized // in. It uses the context it was initialized with for all operations. It implements these interfaces: // -// io.Writer -// io.Reader -// io.Seeker -// io.Closer +// io.Writer +// io.Reader +// io.Seeker +// io.Closer type LargeObject struct { ctx context.Context tx Tx @@ -108,13 +108,13 @@ func (o *LargeObject) Tell() (n int64, err error) { return n, err } -// Trunctes the large object to size. +// Truncate the large object to size. func (o *LargeObject) Truncate(size int64) (err error) { _, err = o.tx.Exec(o.ctx, "select lo_truncate64($1, $2)", o.fd, size) return err } -// Close closees the large object descriptor. +// Close the large object descriptor. func (o *LargeObject) Close() error { _, err := o.tx.Exec(o.ctx, "select lo_close($1)", o.fd) return err diff --git a/vendor/github.com/jackc/pgx/v4/logger.go b/vendor/github.com/jackc/pgx/v4/logger.go index 89fd5af51..41f8b7e87 100644 --- a/vendor/github.com/jackc/pgx/v4/logger.go +++ b/vendor/github.com/jackc/pgx/v4/logger.go @@ -47,9 +47,18 @@ type Logger interface { Log(ctx context.Context, level LogLevel, msg string, data map[string]interface{}) } +// LoggerFunc is a wrapper around a function to satisfy the pgx.Logger interface +type LoggerFunc func(ctx context.Context, level LogLevel, msg string, data map[string]interface{}) + +// Log delegates the logging request to the wrapped function +func (f LoggerFunc) Log(ctx context.Context, level LogLevel, msg string, data map[string]interface{}) { + f(ctx, level, msg, data) +} + // LogLevelFromString converts log level string to constant // // Valid levels: +// // trace // debug // info diff --git a/vendor/github.com/jackc/pgx/v4/rows.go b/vendor/github.com/jackc/pgx/v4/rows.go index d57d5cbf6..4749ead99 100644 --- a/vendor/github.com/jackc/pgx/v4/rows.go +++ b/vendor/github.com/jackc/pgx/v4/rows.go @@ -41,10 +41,13 @@ type Rows interface { // Scan reads the values from the current row into dest values positionally. // dest can include pointers to core types, values implementing the Scanner - // interface, and nil. nil will skip the value entirely. + // interface, and nil. nil will skip the value entirely. It is an error to + // call Scan without first calling Next() and checking that it returned true. Scan(dest ...interface{}) error - // Values returns the decoded row values. + // Values returns the decoded row values. As with Scan(), it is an error to + // call Values without first calling Next() and checking that it returned + // true. Values() ([]interface{}, error) // RawValues returns the unparsed bytes of the row values. The returned [][]byte is only valid until the next Next @@ -140,14 +143,15 @@ func (rows *connRows) Close() { } if rows.logger != nil { + endTime := time.Now() + if rows.err == nil { if rows.logger.shouldLog(LogLevelInfo) { - endTime := time.Now() rows.logger.log(rows.ctx, LogLevelInfo, "Query", map[string]interface{}{"sql": rows.sql, "args": logQueryArgs(rows.args), "time": endTime.Sub(rows.startTime), "rowCount": rows.rowCount}) } } else { if rows.logger.shouldLog(LogLevelError) { - rows.logger.log(rows.ctx, LogLevelError, "Query", map[string]interface{}{"err": rows.err, "sql": rows.sql, "args": logQueryArgs(rows.args)}) + rows.logger.log(rows.ctx, LogLevelError, "Query", map[string]interface{}{"err": rows.err, "sql": rows.sql, "time": endTime.Sub(rows.startTime), "args": logQueryArgs(rows.args)}) } if rows.err != nil && rows.conn.stmtcache != nil { rows.conn.stmtcache.StatementErrored(rows.sql, rows.err) diff --git a/vendor/github.com/jackc/pgx/v4/tx.go b/vendor/github.com/jackc/pgx/v4/tx.go index 8f3178cb0..9ecaa17e9 100644 --- a/vendor/github.com/jackc/pgx/v4/tx.go +++ b/vendor/github.com/jackc/pgx/v4/tx.go @@ -10,39 +10,48 @@ import ( "github.com/jackc/pgconn" ) +// TxIsoLevel is the transaction isolation level (serializable, repeatable read, read committed or read uncommitted) type TxIsoLevel string // Transaction isolation levels const ( - Serializable = TxIsoLevel("serializable") - RepeatableRead = TxIsoLevel("repeatable read") - ReadCommitted = TxIsoLevel("read committed") - ReadUncommitted = TxIsoLevel("read uncommitted") + Serializable TxIsoLevel = "serializable" + RepeatableRead TxIsoLevel = "repeatable read" + ReadCommitted TxIsoLevel = "read committed" + ReadUncommitted TxIsoLevel = "read uncommitted" ) +// TxAccessMode is the transaction access mode (read write or read only) type TxAccessMode string // Transaction access modes const ( - ReadWrite = TxAccessMode("read write") - ReadOnly = TxAccessMode("read only") + ReadWrite TxAccessMode = "read write" + ReadOnly TxAccessMode = "read only" ) +// TxDeferrableMode is the transaction deferrable mode (deferrable or not deferrable) type TxDeferrableMode string // Transaction deferrable modes const ( - Deferrable = TxDeferrableMode("deferrable") - NotDeferrable = TxDeferrableMode("not deferrable") + Deferrable TxDeferrableMode = "deferrable" + NotDeferrable TxDeferrableMode = "not deferrable" ) +// TxOptions are transaction modes within a transaction block type TxOptions struct { IsoLevel TxIsoLevel AccessMode TxAccessMode DeferrableMode TxDeferrableMode } +var emptyTxOptions TxOptions + func (txOptions TxOptions) beginSQL() string { + if txOptions == emptyTxOptions { + return "begin" + } buf := &bytes.Buffer{} buf.WriteString("begin") if txOptions.IsoLevel != "" { @@ -104,7 +113,7 @@ func (c *Conn) BeginTxFunc(ctx context.Context, txOptions TxOptions, f func(Tx) } defer func() { rollbackErr := tx.Rollback(ctx) - if !(rollbackErr == nil || errors.Is(rollbackErr, ErrTxClosed)) { + if rollbackErr != nil && !errors.Is(rollbackErr, ErrTxClosed) { err = rollbackErr } }() @@ -183,7 +192,7 @@ func (tx *dbTx) Begin(ctx context.Context) (Tx, error) { return nil, err } - return &dbSavepoint{tx: tx, savepointNum: tx.savepointNum}, nil + return &dbSimulatedNestedTx{tx: tx, savepointNum: tx.savepointNum}, nil } func (tx *dbTx) BeginFunc(ctx context.Context, f func(Tx) error) (err error) { @@ -198,7 +207,7 @@ func (tx *dbTx) BeginFunc(ctx context.Context, f func(Tx) error) (err error) { } defer func() { rollbackErr := savepoint.Rollback(ctx) - if !(rollbackErr == nil || errors.Is(rollbackErr, ErrTxClosed)) { + if rollbackErr != nil && !errors.Is(rollbackErr, ErrTxClosed) { err = rollbackErr } }() @@ -255,6 +264,10 @@ func (tx *dbTx) Rollback(ctx context.Context) error { // Exec delegates to the underlying *Conn func (tx *dbTx) Exec(ctx context.Context, sql string, arguments ...interface{}) (commandTag pgconn.CommandTag, err error) { + if tx.closed { + return pgconn.CommandTag{}, ErrTxClosed + } + return tx.conn.Exec(ctx, sql, arguments...) } @@ -320,15 +333,15 @@ func (tx *dbTx) Conn() *Conn { return tx.conn } -// dbSavepoint represents a nested transaction implemented by a savepoint. -type dbSavepoint struct { +// dbSimulatedNestedTx represents a simulated nested transaction implemented by a savepoint. +type dbSimulatedNestedTx struct { tx Tx savepointNum int64 closed bool } // Begin starts a pseudo nested transaction implemented with a savepoint. -func (sp *dbSavepoint) Begin(ctx context.Context) (Tx, error) { +func (sp *dbSimulatedNestedTx) Begin(ctx context.Context) (Tx, error) { if sp.closed { return nil, ErrTxClosed } @@ -336,7 +349,7 @@ func (sp *dbSavepoint) Begin(ctx context.Context) (Tx, error) { return sp.tx.Begin(ctx) } -func (sp *dbSavepoint) BeginFunc(ctx context.Context, f func(Tx) error) (err error) { +func (sp *dbSimulatedNestedTx) BeginFunc(ctx context.Context, f func(Tx) error) (err error) { if sp.closed { return ErrTxClosed } @@ -345,7 +358,7 @@ func (sp *dbSavepoint) BeginFunc(ctx context.Context, f func(Tx) error) (err err } // Commit releases the savepoint essentially committing the pseudo nested transaction. -func (sp *dbSavepoint) Commit(ctx context.Context) error { +func (sp *dbSimulatedNestedTx) Commit(ctx context.Context) error { if sp.closed { return ErrTxClosed } @@ -358,7 +371,7 @@ func (sp *dbSavepoint) Commit(ctx context.Context) error { // Rollback rolls back to the savepoint essentially rolling back the pseudo nested transaction. Rollback will return // ErrTxClosed if the dbSavepoint is already closed, but is otherwise safe to call multiple times. Hence, a defer sp.Rollback() // is safe even if sp.Commit() will be called first in a non-error condition. -func (sp *dbSavepoint) Rollback(ctx context.Context) error { +func (sp *dbSimulatedNestedTx) Rollback(ctx context.Context) error { if sp.closed { return ErrTxClosed } @@ -369,7 +382,7 @@ func (sp *dbSavepoint) Rollback(ctx context.Context) error { } // Exec delegates to the underlying Tx -func (sp *dbSavepoint) Exec(ctx context.Context, sql string, arguments ...interface{}) (commandTag pgconn.CommandTag, err error) { +func (sp *dbSimulatedNestedTx) Exec(ctx context.Context, sql string, arguments ...interface{}) (commandTag pgconn.CommandTag, err error) { if sp.closed { return nil, ErrTxClosed } @@ -378,7 +391,7 @@ func (sp *dbSavepoint) Exec(ctx context.Context, sql string, arguments ...interf } // Prepare delegates to the underlying Tx -func (sp *dbSavepoint) Prepare(ctx context.Context, name, sql string) (*pgconn.StatementDescription, error) { +func (sp *dbSimulatedNestedTx) Prepare(ctx context.Context, name, sql string) (*pgconn.StatementDescription, error) { if sp.closed { return nil, ErrTxClosed } @@ -387,7 +400,7 @@ func (sp *dbSavepoint) Prepare(ctx context.Context, name, sql string) (*pgconn.S } // Query delegates to the underlying Tx -func (sp *dbSavepoint) Query(ctx context.Context, sql string, args ...interface{}) (Rows, error) { +func (sp *dbSimulatedNestedTx) Query(ctx context.Context, sql string, args ...interface{}) (Rows, error) { if sp.closed { // Because checking for errors can be deferred to the *Rows, build one with the error err := ErrTxClosed @@ -398,13 +411,13 @@ func (sp *dbSavepoint) Query(ctx context.Context, sql string, args ...interface{ } // QueryRow delegates to the underlying Tx -func (sp *dbSavepoint) QueryRow(ctx context.Context, sql string, args ...interface{}) Row { +func (sp *dbSimulatedNestedTx) QueryRow(ctx context.Context, sql string, args ...interface{}) Row { rows, _ := sp.Query(ctx, sql, args...) return (*connRow)(rows.(*connRows)) } // QueryFunc delegates to the underlying Tx. -func (sp *dbSavepoint) QueryFunc(ctx context.Context, sql string, args []interface{}, scans []interface{}, f func(QueryFuncRow) error) (pgconn.CommandTag, error) { +func (sp *dbSimulatedNestedTx) QueryFunc(ctx context.Context, sql string, args []interface{}, scans []interface{}, f func(QueryFuncRow) error) (pgconn.CommandTag, error) { if sp.closed { return nil, ErrTxClosed } @@ -413,7 +426,7 @@ func (sp *dbSavepoint) QueryFunc(ctx context.Context, sql string, args []interfa } // CopyFrom delegates to the underlying *Conn -func (sp *dbSavepoint) CopyFrom(ctx context.Context, tableName Identifier, columnNames []string, rowSrc CopyFromSource) (int64, error) { +func (sp *dbSimulatedNestedTx) CopyFrom(ctx context.Context, tableName Identifier, columnNames []string, rowSrc CopyFromSource) (int64, error) { if sp.closed { return 0, ErrTxClosed } @@ -422,7 +435,7 @@ func (sp *dbSavepoint) CopyFrom(ctx context.Context, tableName Identifier, colum } // SendBatch delegates to the underlying *Conn -func (sp *dbSavepoint) SendBatch(ctx context.Context, b *Batch) BatchResults { +func (sp *dbSimulatedNestedTx) SendBatch(ctx context.Context, b *Batch) BatchResults { if sp.closed { return &batchResults{err: ErrTxClosed} } @@ -430,10 +443,10 @@ func (sp *dbSavepoint) SendBatch(ctx context.Context, b *Batch) BatchResults { return sp.tx.SendBatch(ctx, b) } -func (sp *dbSavepoint) LargeObjects() LargeObjects { +func (sp *dbSimulatedNestedTx) LargeObjects() LargeObjects { return LargeObjects{tx: sp} } -func (sp *dbSavepoint) Conn() *Conn { +func (sp *dbSimulatedNestedTx) Conn() *Conn { return sp.tx.Conn() } diff --git a/vendor/github.com/jackc/pgx/v4/values.go b/vendor/github.com/jackc/pgx/v4/values.go index 45d8ff839..1a9454753 100644 --- a/vendor/github.com/jackc/pgx/v4/values.go +++ b/vendor/github.com/jackc/pgx/v4/values.go @@ -78,6 +78,8 @@ func convertSimpleArgument(ci *pgtype.ConnInfo, arg interface{}) (interface{}, e return arg, nil case bool: return arg, nil + case time.Duration: + return fmt.Sprintf("%d microsecond", int64(arg)/1000), nil case time.Time: return arg, nil case string: diff --git a/vendor/modules.txt b/vendor/modules.txt index 488146675..fd1196974 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -176,8 +176,8 @@ github.com/imdario/mergo # github.com/jackc/chunkreader/v2 v2.0.1 ## explicit; go 1.12 github.com/jackc/chunkreader/v2 -# github.com/jackc/pgconn v1.8.1 -## explicit; go 1.12 +# github.com/jackc/pgconn v1.14.3 +## explicit; go 1.17 github.com/jackc/pgconn github.com/jackc/pgconn/internal/ctxwatch github.com/jackc/pgconn/stmtcache @@ -187,17 +187,17 @@ github.com/jackc/pgio # github.com/jackc/pgpassfile v1.0.0 ## explicit; go 1.12 github.com/jackc/pgpassfile -# github.com/jackc/pgproto3/v2 v2.0.6 +# github.com/jackc/pgproto3/v2 v2.3.3 ## explicit; go 1.12 github.com/jackc/pgproto3/v2 -# github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b +# github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a ## explicit; go 1.14 github.com/jackc/pgservicefile -# github.com/jackc/pgtype v1.7.0 +# github.com/jackc/pgtype v1.14.0 ## explicit; go 1.13 github.com/jackc/pgtype -# github.com/jackc/pgx/v4 v4.11.0 -## explicit; go 1.13 +# github.com/jackc/pgx/v4 v4.18.2 +## explicit; go 1.17 github.com/jackc/pgx/v4 github.com/jackc/pgx/v4/internal/sanitize # github.com/jmespath/go-jmespath v0.4.0 From 5a9b9702a1fe51072589e9142fb27ac30a5a56b8 Mon Sep 17 00:00:00 2001 From: Syed Date: Fri, 17 Mar 2023 10:03:53 -0400 Subject: [PATCH 4/4] Add docs for env variable upgrades --- docs/environment-overrides.md | 55 +++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 docs/environment-overrides.md diff --git a/docs/environment-overrides.md b/docs/environment-overrides.md new file mode 100644 index 000000000..bd78cf3ee --- /dev/null +++ b/docs/environment-overrides.md @@ -0,0 +1,55 @@ +# Environment variables override + +If you want to override environment variables for any component in the operator +deployment, you can do so by adding a `overrides:` section + +example: + +``` + kind: QuayRegistry + metadata: + name: quay37 + spec: + configBundleSecret: config-bundle-secret + components: + - kind: objectstorage + managed: false + - kind: route + managed: true + - kind: mirror + managed: true + overrides: + env: + - name: DEBUGLOG + value: "true" + - name: HTTP_PROXY + value: quayproxy.qe.devcluster.openshift.com:3128 + - name: HTTPS_PROXY + value: quayproxy.qe.devcluster.openshift.com:3128 + - name: NO_PROXY + value: svc.cluster.local,localhost,quay370.apps.quayperf370.perfscale.devcluster.openshift.com + - kind: tls + managed: false + - kind: clair + managed: true + overrides: + env: + - name: HTTP_PROXY + value: quayproxy.qe.devcluster.openshift.com:3128 + - name: HTTPS_PROXY + value: quayproxy.qe.devcluster.openshift.com:3128 + - name: NO_PROXY + value: svc.cluster.local,localhost,quay370.apps.quayperf370.perfscale.devcluster.openshift.com + - kind: quay + managed: true + overrides: + env: + - name: DEBUGLOG + value: "true" + - name: NO_PROXY + value: svc.cluster.local,localhost,quay370.apps.quayperf370.perfscale.devcluster.openshift.com + - name: HTTP_PROXY + value: quayproxy.qe.devcluster.openshift.com:3128 + - name: HTTPS_PROXY + value: quayproxy.qe.devcluster.openshift.com:3128 +```