utils.ex (6053B)
1 defmodule Postgrex.Utils do 2 @moduledoc false 3 4 @extensions [ 5 Postgrex.Extensions.Array, 6 Postgrex.Extensions.BitString, 7 Postgrex.Extensions.Bool, 8 Postgrex.Extensions.Box, 9 Postgrex.Extensions.Circle, 10 Postgrex.Extensions.Date, 11 Postgrex.Extensions.Float4, 12 Postgrex.Extensions.Float8, 13 Postgrex.Extensions.HStore, 14 Postgrex.Extensions.INET, 15 Postgrex.Extensions.Int2, 16 Postgrex.Extensions.Int4, 17 Postgrex.Extensions.Int8, 18 Postgrex.Extensions.Interval, 19 Postgrex.Extensions.JSON, 20 Postgrex.Extensions.JSONB, 21 Postgrex.Extensions.Line, 22 Postgrex.Extensions.LineSegment, 23 Postgrex.Extensions.MACADDR, 24 Postgrex.Extensions.Name, 25 Postgrex.Extensions.Numeric, 26 Postgrex.Extensions.OID, 27 Postgrex.Extensions.Path, 28 Postgrex.Extensions.Point, 29 Postgrex.Extensions.Polygon, 30 Postgrex.Extensions.Range, 31 Postgrex.Extensions.Raw, 32 Postgrex.Extensions.Record, 33 Postgrex.Extensions.TID, 34 Postgrex.Extensions.Time, 35 Postgrex.Extensions.Timestamp, 36 Postgrex.Extensions.TimestampTZ, 37 Postgrex.Extensions.TimeTZ, 38 Postgrex.Extensions.TSVector, 39 Postgrex.Extensions.UUID, 40 Postgrex.Extensions.VoidBinary, 41 Postgrex.Extensions.VoidText, 42 Postgrex.Extensions.Xid8 43 ] 44 45 @doc """ 46 Checks if a given extension is a default extension. 47 """ 48 for ext <- @extensions do 49 def default_extension?(unquote(ext)), do: true 50 end 51 52 def default_extension?(_), do: false 53 54 @doc """ 55 List all default extensions. 56 """ 57 @spec default_extensions(Keyword.t()) :: [{module(), Keyword.t()}] 58 def default_extensions(opts \\ []) do 59 Enum.map(@extensions, &{&1, opts}) 60 end 61 62 @doc """ 63 Converts pg major.minor.patch (http://www.postgresql.org/support/versioning) version to an integer 64 """ 65 def parse_version(version) do 66 segments = 67 version 68 |> String.split(" ", parts: 2) 69 |> hd() 70 |> String.split(".", parts: 4) 71 |> Enum.map(&parse_version_bit/1) 72 73 case segments do 74 [major, minor, patch, _] -> {major, minor, patch} 75 [major, minor, patch] -> {major, minor, patch} 76 [major, minor] -> {major, minor, 0} 77 [major] -> {major, 0, 0} 78 end 79 end 80 81 @doc """ 82 Fills in the given `opts` with default options. 83 """ 84 @spec default_opts(Keyword.t()) :: Keyword.t() 85 def default_opts(opts) do 86 {field, value} = extract_host(System.get_env("PGHOST")) 87 88 opts 89 |> Keyword.put_new(:username, System.get_env("PGUSER") || System.get_env("USER")) 90 |> Keyword.put_new(:password, System.get_env("PGPASSWORD")) 91 |> Keyword.put_new(:database, System.get_env("PGDATABASE")) 92 |> Keyword.put_new(field, value) 93 |> Keyword.put_new(:port, System.get_env("PGPORT")) 94 |> Keyword.update!(:port, &normalize_port/1) 95 |> Keyword.put_new(:types, Postgrex.DefaultTypes) 96 |> Enum.reject(fn {_k, v} -> is_nil(v) end) 97 end 98 99 defp extract_host("/" <> _ = dir), do: {:socket_dir, dir} 100 defp extract_host(<<d, ?:>> <> _ = dir) when d in ?a..?z or d in ?A..?Z, do: {:socket_dir, dir} 101 defp extract_host("@" <> abstract_socket), do: {:socket, <<0>> <> abstract_socket} 102 defp extract_host(host), do: {:hostname, host || "localhost"} 103 104 defp normalize_port(port) when is_binary(port), do: String.to_integer(port) 105 defp normalize_port(port), do: port 106 107 @doc """ 108 Return encode error message. 109 """ 110 def encode_msg(%Postgrex.TypeInfo{type: type}, observed, expected) do 111 "Postgrex expected #{to_desc(expected)} that can be encoded/cast to " <> 112 "type #{inspect(type)}, got #{inspect(observed)}. Please make sure the " <> 113 "value you are passing matches the definition in your table or in your " <> 114 "query or convert the value accordingly." 115 end 116 117 @doc """ 118 Return encode error message. 119 """ 120 def encode_msg(%Date{calendar: calendar} = observed, _expected) when calendar != Calendar.ISO do 121 "Postgrex expected a %Date{} in the `Calendar.ISO` calendar, got #{inspect(observed)}. " <> 122 "Postgrex (and PostgreSQL) support dates in the `Calendar.ISO` calendar only." 123 end 124 125 def encode_msg(%NaiveDateTime{calendar: calendar} = observed, _expected) 126 when calendar != Calendar.ISO do 127 "Postgrex expected a %NaiveDateTime{} in the `Calendar.ISO` calendar, got #{inspect(observed)}. " <> 128 "Postgrex (and PostgreSQL) support naive datetimes in the `Calendar.ISO` calendar only." 129 end 130 131 def encode_msg(%DateTime{calendar: calendar} = observed, _expected) 132 when calendar != Calendar.ISO do 133 "Postgrex expected a %DateTime{} in the `Calendar.ISO` calendar, got #{inspect(observed)}. " <> 134 "Postgrex (and PostgreSQL) support datetimes in the `Calendar.ISO` calendar only." 135 end 136 137 def encode_msg(%Time{calendar: calendar} = observed, _expected) when calendar != Calendar.ISO do 138 "Postgrex expected a %Time{} in the `Calendar.ISO` calendar, got #{inspect(observed)}. " <> 139 "Postgrex (and PostgreSQL) support times in the `Calendar.ISO` calendar only." 140 end 141 142 def encode_msg(observed, expected) do 143 "Postgrex expected #{to_desc(expected)}, got #{inspect(observed)}. " <> 144 "Please make sure the value you are passing matches the definition in " <> 145 "your table or in your query or convert the value accordingly." 146 end 147 148 @doc """ 149 Return type error message. 150 """ 151 def type_msg(%Postgrex.TypeInfo{type: json}, module) 152 when json in ["json", "jsonb"] do 153 "type `#{json}` can not be handled by the types module #{inspect(module)}, " <> 154 "it must define a `:json` library in its options to support JSON types" 155 end 156 157 def type_msg(%Postgrex.TypeInfo{type: type}, module) do 158 "type `#{type}` can not be handled by the types module #{inspect(module)}" 159 end 160 161 ## Helpers 162 163 defp parse_version_bit(bit) do 164 {int, _} = Integer.parse(bit) 165 int 166 end 167 168 defp to_desc(struct) when is_atom(struct), do: "%#{inspect(struct)}{}" 169 defp to_desc(%Range{} = range), do: "an integer in #{inspect(range)}" 170 defp to_desc({a, b}), do: to_desc(a) <> " or " <> to_desc(b) 171 defp to_desc(desc) when is_binary(desc), do: desc 172 end