1 package pgx_test
2
3 import (
4 "context"
5 "io"
6 "os"
7 "testing"
8 "time"
9
10 "github.com/jackc/pgconn"
11 "github.com/jackc/pgx/v4"
12 )
13
14 func TestLargeObjects(t *testing.T) {
15 t.Parallel()
16
17 ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
18 defer cancel()
19
20 conn, err := pgx.Connect(ctx, os.Getenv("PGX_TEST_DATABASE"))
21 if err != nil {
22 t.Fatal(err)
23 }
24
25 skipCockroachDB(t, conn, "Server does support large objects")
26
27 tx, err := conn.Begin(ctx)
28 if err != nil {
29 t.Fatal(err)
30 }
31
32 testLargeObjects(t, ctx, tx)
33 }
34
35 func TestLargeObjectsPreferSimpleProtocol(t *testing.T) {
36 t.Parallel()
37
38 ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
39 defer cancel()
40
41 config, err := pgx.ParseConfig(os.Getenv("PGX_TEST_DATABASE"))
42 if err != nil {
43 t.Fatal(err)
44 }
45
46 config.PreferSimpleProtocol = true
47
48 conn, err := pgx.ConnectConfig(ctx, config)
49 if err != nil {
50 t.Fatal(err)
51 }
52
53 skipCockroachDB(t, conn, "Server does support large objects")
54
55 tx, err := conn.Begin(ctx)
56 if err != nil {
57 t.Fatal(err)
58 }
59
60 testLargeObjects(t, ctx, tx)
61 }
62
63 func testLargeObjects(t *testing.T, ctx context.Context, tx pgx.Tx) {
64 lo := tx.LargeObjects()
65
66 id, err := lo.Create(ctx, 0)
67 if err != nil {
68 t.Fatal(err)
69 }
70
71 obj, err := lo.Open(ctx, id, pgx.LargeObjectModeRead|pgx.LargeObjectModeWrite)
72 if err != nil {
73 t.Fatal(err)
74 }
75
76 n, err := obj.Write([]byte("testing"))
77 if err != nil {
78 t.Fatal(err)
79 }
80 if n != 7 {
81 t.Errorf("Expected n to be 7, got %d", n)
82 }
83
84 pos, err := obj.Seek(1, 0)
85 if err != nil {
86 t.Fatal(err)
87 }
88 if pos != 1 {
89 t.Errorf("Expected pos to be 1, got %d", pos)
90 }
91
92 res := make([]byte, 6)
93 n, err = obj.Read(res)
94 if err != nil {
95 t.Fatal(err)
96 }
97 if string(res) != "esting" {
98 t.Errorf(`Expected res to be "esting", got %q`, res)
99 }
100 if n != 6 {
101 t.Errorf("Expected n to be 6, got %d", n)
102 }
103
104 n, err = obj.Read(res)
105 if err != io.EOF {
106 t.Error("Expected io.EOF, go nil")
107 }
108 if n != 0 {
109 t.Errorf("Expected n to be 0, got %d", n)
110 }
111
112 pos, err = obj.Tell()
113 if err != nil {
114 t.Fatal(err)
115 }
116 if pos != 7 {
117 t.Errorf("Expected pos to be 7, got %d", pos)
118 }
119
120 err = obj.Truncate(1)
121 if err != nil {
122 t.Fatal(err)
123 }
124
125 pos, err = obj.Seek(-1, 2)
126 if err != nil {
127 t.Fatal(err)
128 }
129 if pos != 0 {
130 t.Errorf("Expected pos to be 0, got %d", pos)
131 }
132
133 res = make([]byte, 2)
134 n, err = obj.Read(res)
135 if err != io.EOF {
136 t.Errorf("Expected err to be io.EOF, got %v", err)
137 }
138 if n != 1 {
139 t.Errorf("Expected n to be 1, got %d", n)
140 }
141 if res[0] != 't' {
142 t.Errorf("Expected res[0] to be 't', got %v", res[0])
143 }
144
145 err = obj.Close()
146 if err != nil {
147 t.Fatal(err)
148 }
149
150 err = lo.Unlink(ctx, id)
151 if err != nil {
152 t.Fatal(err)
153 }
154
155 _, err = lo.Open(ctx, id, pgx.LargeObjectModeRead)
156 if e, ok := err.(*pgconn.PgError); !ok || e.Code != "42704" {
157 t.Errorf("Expected undefined_object error (42704), got %#v", err)
158 }
159 }
160
161 func TestLargeObjectsMultipleTransactions(t *testing.T) {
162 t.Parallel()
163
164 ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
165 defer cancel()
166
167 conn, err := pgx.Connect(ctx, os.Getenv("PGX_TEST_DATABASE"))
168 if err != nil {
169 t.Fatal(err)
170 }
171
172 skipCockroachDB(t, conn, "Server does support large objects")
173
174 tx, err := conn.Begin(ctx)
175 if err != nil {
176 t.Fatal(err)
177 }
178
179 lo := tx.LargeObjects()
180
181 id, err := lo.Create(ctx, 0)
182 if err != nil {
183 t.Fatal(err)
184 }
185
186 obj, err := lo.Open(ctx, id, pgx.LargeObjectModeWrite)
187 if err != nil {
188 t.Fatal(err)
189 }
190
191 n, err := obj.Write([]byte("testing"))
192 if err != nil {
193 t.Fatal(err)
194 }
195 if n != 7 {
196 t.Errorf("Expected n to be 7, got %d", n)
197 }
198
199
200 err = tx.Commit(ctx)
201 if err != nil {
202 t.Fatal(err)
203 }
204
205
206 query := `select n from generate_series(1,10) n`
207 rows, err := conn.Query(ctx, query)
208 if err != nil {
209 t.Fatal(err)
210 }
211 rows.Close()
212
213
214 tx2, err := conn.Begin(ctx)
215 if err != nil {
216 t.Fatal(err)
217 }
218
219 lo2 := tx2.LargeObjects()
220
221
222 obj2, err := lo2.Open(ctx, id, pgx.LargeObjectModeRead|pgx.LargeObjectModeWrite)
223 if err != nil {
224 t.Fatal(err)
225 }
226
227 pos, err := obj2.Seek(1, 0)
228 if err != nil {
229 t.Fatal(err)
230 }
231 if pos != 1 {
232 t.Errorf("Expected pos to be 1, got %d", pos)
233 }
234
235 res := make([]byte, 6)
236 n, err = obj2.Read(res)
237 if err != nil {
238 t.Fatal(err)
239 }
240 if string(res) != "esting" {
241 t.Errorf(`Expected res to be "esting", got %q`, res)
242 }
243 if n != 6 {
244 t.Errorf("Expected n to be 6, got %d", n)
245 }
246
247 n, err = obj2.Read(res)
248 if err != io.EOF {
249 t.Error("Expected io.EOF, go nil")
250 }
251 if n != 0 {
252 t.Errorf("Expected n to be 0, got %d", n)
253 }
254
255 pos, err = obj2.Tell()
256 if err != nil {
257 t.Fatal(err)
258 }
259 if pos != 7 {
260 t.Errorf("Expected pos to be 7, got %d", pos)
261 }
262
263 err = obj2.Truncate(1)
264 if err != nil {
265 t.Fatal(err)
266 }
267
268 pos, err = obj2.Seek(-1, 2)
269 if err != nil {
270 t.Fatal(err)
271 }
272 if pos != 0 {
273 t.Errorf("Expected pos to be 0, got %d", pos)
274 }
275
276 res = make([]byte, 2)
277 n, err = obj2.Read(res)
278 if err != io.EOF {
279 t.Errorf("Expected err to be io.EOF, got %v", err)
280 }
281 if n != 1 {
282 t.Errorf("Expected n to be 1, got %d", n)
283 }
284 if res[0] != 't' {
285 t.Errorf("Expected res[0] to be 't', got %v", res[0])
286 }
287
288 err = obj2.Close()
289 if err != nil {
290 t.Fatal(err)
291 }
292
293 err = lo2.Unlink(ctx, id)
294 if err != nil {
295 t.Fatal(err)
296 }
297
298 _, err = lo2.Open(ctx, id, pgx.LargeObjectModeRead)
299 if e, ok := err.(*pgconn.PgError); !ok || e.Code != "42704" {
300 t.Errorf("Expected undefined_object error (42704), got %#v", err)
301 }
302 }
303
View as plain text