-
Notifications
You must be signed in to change notification settings - Fork 78
/
param.cr
164 lines (132 loc) · 3.74 KB
/
param.cr
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
require "../pg/geo"
module PQ
# :nodoc:
record Param, slice : Slice(UInt8), size : Int32, format : Int16 do
delegate to_unsafe, to: slice
# Internal wrapper to represent an encoded parameter
def self.encode(val : Nil)
binary Pointer(UInt8).null.to_slice(0), -1
end
def self.encode(val : Slice)
binary val, val.size
end
def self.encode(val : Array)
text encode_array(val)
end
def self.encode(val : Time)
text Time::Format::RFC_3339.format(val, fraction_digits: 9)
end
def self.encode(val : Enum)
encode val.value
end
def self.encode(val : PG::Geo::Point)
text "(#{val.x},#{val.y})"
end
def self.encode(val : PG::Geo::Line)
text "{#{val.a},#{val.b},#{val.c}}"
end
def self.encode(val : PG::Geo::Circle)
text "<(#{val.x},#{val.y}),#{val.radius}>"
end
def self.encode(val : PG::Geo::LineSegment)
text "((#{val.x1},#{val.y1}),(#{val.x2},#{val.y2}))"
end
def self.encode(val : PG::Geo::Box)
text "((#{val.x1},#{val.y1}),(#{val.x2},#{val.y2}))"
end
def self.encode(val : PG::Geo::Path)
if val.closed?
encode_points "(", val.points, ")"
else
encode_points "[", val.points, "]"
end
end
def self.encode(val : PG::Geo::Polygon)
encode_points "(", val.points, ")"
end
private def self.encode_points(left, points, right)
string = String.build do |io|
io << left
points.each_with_index do |point, i|
io << "," if i > 0
io << "(" << point.x << "," << point.y << ")"
end
io << right
end
text string
end
def self.encode(val : PG::Interval)
# https://www.postgresql.org/docs/current/datatype-datetime.html#DATATYPE-INTERVAL-INPUT
text "#{val.months} months #{val.days} days #{val.microseconds} microseconds"
end
def self.encode(val)
text val.to_s
end
def self.binary(slice, size)
new slice, size, 1_i16
end
def self.text(string : String)
text string.to_slice
end
def self.text(slice : Bytes)
new slice, slice.size, 0_i16
end
def self.encode_array(array)
String.build(array.size + 2) do |io|
encode_array(io, array)
end
end
def self.encode_array(io, value : Array)
io << "{"
value.join(io, ",") do |item|
encode_array(io, item)
end
io << "}"
end
def self.encode_array(io, value)
io << value
end
def self.encode_array(io, value : Nil)
io << "NULL"
end
def self.encode_array(io, value : Bool)
io << (value ? 't' : 'f')
end
def self.encode_array(io, value : Bytes)
io << %{"\\\\x}
value.each do |byte|
byte.to_s io, base: 16, precision: 2
end
io << '"'
end
def self.encode_array(io, value : String)
io << '"'
if value.ascii_only?
special_chars = {'"'.ord.to_u8, '\\'.ord.to_u8}
last_index = 0
value.to_slice.each_with_index do |byte, index|
if special_chars.includes?(byte)
io.write value.unsafe_byte_slice(last_index, index - last_index)
last_index = index
io << '\\'
end
end
io.write value.unsafe_byte_slice(last_index)
else
last_index = 0
reader = Char::Reader.new(value)
while reader.has_next?
char = reader.current_char
if {'"', '\\'}.includes?(char)
io.write value.unsafe_byte_slice(last_index, reader.pos - last_index)
last_index = reader.pos
io << '\\'
end
reader.next_char
end
io.write value.unsafe_byte_slice(last_index)
end
io << '"'
end
end
end