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
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
|
package example;
import java.io.*;
import java.sql.*;
import java.text.*;
// rare in user code, but we use the LargeObject API in this test
import org.postgresql.largeobject.*;
/*
* This example tests the thread safety of the driver.
*
* It does this by performing several queries, in different threads. Each
* thread has it's own Statement object, which is (in my understanding of the
* jdbc specification) the minimum requirement.
*
*/
public class threadsafe
{
Connection db; // The connection to the database
Statement st; // Our statement to run queries with
public threadsafe(String args[]) throws ClassNotFoundException, FileNotFoundException, IOException, SQLException
{
String url = args[0];
String usr = args[1];
String pwd = args[2];
// Load the driver
Class.forName("org.postgresql.Driver");
// Connect to database
System.out.println("Connecting to Database URL = " + url);
db = DriverManager.getConnection(url, usr, pwd);
System.out.println("Connected...Now creating a statement");
st = db.createStatement();
// Clean up the database (in case we failed earlier) then initialise
cleanup();
// Because we use LargeObjects, we must use Transactions
db.setAutoCommit(false);
// Now run tests using JDBC methods, then LargeObjects
doexample();
// Clean up the database
cleanup();
// Finally close the database
System.out.println("Now closing the connection");
st.close();
db.close();
}
/*
* This drops the table (if it existed). No errors are reported.
*/
public void cleanup()
{
try
{
st.executeUpdate("drop table basic1");
}
catch (Exception ex)
{
// We ignore any errors here
}
try
{
st.executeUpdate("drop table basic2");
}
catch (Exception ex)
{
// We ignore any errors here
}
}
/*
* This performs the example
*/
public void doexample() throws SQLException
{
System.out.println("\nThis test runs three Threads. Two simply insert data into a table, then\nthey perform a query. While they are running, a third thread is running,\nand it load data into, then reads from a Large Object.\n\nIf alls well, this should run without any errors. If so, we are Thread Safe.\nWhy test JDBC & LargeObject's? Because both will run over the network\nconnection, and if locking on the stream isn't done correctly, the backend\nwill get pretty confused!\n");
thread3 thread3 = null;
try
{
// create the two threads
Thread thread0 = Thread.currentThread();
Thread thread1 = new thread1(db);
Thread thread2 = new thread2(db);
thread3 = new thread3(db);
// now run, and wait for them
thread1.start();
thread2.start();
thread3.start();
// ok, I know this is bad, but it does the trick here as our main thread
// will yield as long as either of the children are still running
System.out.println("Waiting for threads to run");
while (thread1.isAlive() || thread2.isAlive() || thread3.isAlive())
thread0.yield();
}
finally
{
// clean up after thread3 (the finally ensures this is run even
// if an exception is thrown inside the try { } construct)
if (thread3 != null)
thread3.cleanup();
}
System.out.println("No Exceptions have been thrown. This is a good omen, as it means that we are\npretty much thread safe as we can get.");
}
// This is the first thread. It's the same as the basic test
class thread1 extends Thread
{
Connection c;
Statement st;
public thread1(Connection c) throws SQLException
{
this.c = c;
st = c.createStatement();
}
public void run()
{
try
{
System.out.println("Thread 1 running...");
// First we need a table to store data in
st.executeUpdate("create table basic1 (a int2, b int2)");
// Now insert some data, using the Statement
st.executeUpdate("insert into basic1 values (1,1)");
st.executeUpdate("insert into basic1 values (2,1)");
st.executeUpdate("insert into basic1 values (3,1)");
// For large inserts, a PreparedStatement is more efficient, because it
// supports the idea of precompiling the SQL statement, and to store
// directly, a Java object into any column. PostgreSQL doesnt support
// precompiling, but does support setting a column to the value of a
// Java object (like Date, String, etc).
//
// Also, this is the only way of writing dates in a datestyle independent
// manner. (DateStyles are PostgreSQL's way of handling different methods
// of representing dates in the Date data type.)
PreparedStatement ps = db.prepareStatement("insert into basic1 values (?,?)");
for (int i = 2;i < 2000;i++)
{
ps.setInt(1, 4); // "column a" = 5
ps.setInt(2, i); // "column b" = i
ps.executeUpdate(); // executeUpdate because insert returns no data
// c.commit();
if ((i % 50) == 0)
DriverManager.println("Thread 1 done " + i + " inserts");
}
ps.close(); // Always close when we are done with it
// Finally perform a query on the table
DriverManager.println("Thread 1 performing a query");
ResultSet rs = st.executeQuery("select a, b from basic1");
int cnt = 0;
if (rs != null)
{
// Now we run through the result set, printing out the result.
// Note, we must call .next() before attempting to read any results
while (rs.next())
{
int a = rs.getInt("a"); // This shows how to get the value by name
int b = rs.getInt(2); // This shows how to get the value by column
//System.out.println(" a="+a+" b="+b);
cnt++;
}
rs.close(); // again, you must close the result when done
}
DriverManager.println("Thread 1 read " + cnt + " rows");
// The last thing to do is to drop the table. This is done in the
// cleanup() method.
System.out.println("Thread 1 finished");
}
catch (SQLException se)
{
System.err.println("Thread 1: " + se.toString());
se.printStackTrace();
System.exit(1);
}
}
}
// This is the second thread. It's the similar to the basic test, and thread1
// except it works on another table.
class thread2 extends Thread
{
Connection c;
Statement st;
public thread2(Connection c) throws SQLException
{
this.c = c;
st = c.createStatement();
}
public void run()
{
try
{
System.out.println("Thread 2 running...");
// First we need a table to store data in
st.executeUpdate("create table basic2 (a int2, b int2)");
// For large inserts, a PreparedStatement is more efficient, because it
// supports the idea of precompiling the SQL statement, and to store
// directly, a Java object into any column. PostgreSQL doesnt support
// precompiling, but does support setting a column to the value of a
// Java object (like Date, String, etc).
//
// Also, this is the only way of writing dates in a datestyle independent
// manner. (DateStyles are PostgreSQL's way of handling different methods
// of representing dates in the Date data type.)
PreparedStatement ps = db.prepareStatement("insert into basic2 values (?,?)");
for (int i = 2;i < 2000;i++)
{
ps.setInt(1, 4); // "column a" = 5
ps.setInt(2, i); // "column b" = i
ps.executeUpdate(); // executeUpdate because insert returns no data
// c.commit();
if ((i % 50) == 0)
DriverManager.println("Thread 2 done " + i + " inserts");
}
ps.close(); // Always close when we are done with it
// Finally perform a query on the table
DriverManager.println("Thread 2 performing a query");
ResultSet rs = st.executeQuery("select * from basic2 where b>1");
int cnt = 0;
if (rs != null)
{
// First find out the column numbers.
//
// It's best to do this here, as calling the methods with the column
// numbers actually performs this call each time they are called. This
// really speeds things up on large queries.
//
int col_a = rs.findColumn("a");
int col_b = rs.findColumn("b");
// Now we run through the result set, printing out the result.
// Again, we must call .next() before attempting to read any results
while (rs.next())
{
int a = rs.getInt(col_a); // This shows how to get the value by name
int b = rs.getInt(col_b); // This shows how to get the value by column
//System.out.println(" a="+a+" b="+b);
cnt++;
}
rs.close(); // again, you must close the result when done
}
DriverManager.println("Thread 2 read " + cnt + " rows");
// The last thing to do is to drop the table. This is done in the
// cleanup() method.
System.out.println("Thread 2 finished");
}
catch (SQLException se)
{
System.err.println("Thread 2: " + se.toString());
se.printStackTrace();
System.exit(1);
}
}
}
// This is the third thread. It loads, then reads from a LargeObject, using
// our LargeObject api.
//
// The purpose of this is to test that FastPath will work in between normal
// JDBC queries.
class thread3 extends Thread
{
Connection c;
Statement st;
LargeObjectManager lom;
LargeObject lo;
int oid;
public thread3(Connection c) throws SQLException
{
this.c = c;
//st = c.createStatement();
// create a blob
lom = ((org.postgresql.Connection)c).getLargeObjectAPI();
oid = lom.create();
System.out.println("Thread 3 has created a blob of oid " + oid);
}
public void run()
{
try
{
System.out.println("Thread 3 running...");
DriverManager.println("Thread 3: Loading data into blob " + oid);
lo = lom.open(oid);
FileInputStream fis = new FileInputStream("example/threadsafe.java");
// keep the buffer size small, to allow the other thread a chance
byte buf[] = new byte[128];
int rc, bc = 1, bs = 0;
while ((rc = fis.read(buf)) > 0)
{
DriverManager.println("Thread 3 read block " + bc + " " + bs + " bytes");
lo.write(buf, 0, rc);
bc++;
bs += rc;
}
lo.close();
fis.close();
DriverManager.println("Thread 3: Reading blob " + oid);
lo = lom.open(oid);
bc = 0;
while (buf.length > 0)
{
buf = lo.read(buf.length);
if (buf.length > 0)
{
String s = new String(buf);
bc++;
DriverManager.println("Thread 3 block " + bc);
DriverManager.println("Block " + bc + " got " + s);
}
}
lo.close();
System.out.println("Thread 3 finished");
}
catch (Exception se)
{
System.err.println("Thread 3: " + se.toString());
se.printStackTrace();
System.exit(1);
}
}
public void cleanup() throws SQLException
{
if (lom != null && oid != 0)
{
System.out.println("Thread 3: Removing blob oid=" + oid);
lom.delete(oid);
}
}
}
/*
* Display some instructions on how to run the example
*/
public static void instructions()
{
System.out.println("\nThis tests the thread safety of the driver.\n\nThis is done in two parts, the first with standard JDBC calls, and the\nsecond mixing FastPath and LargeObject calls with queries.\n");
System.out.println("Useage:\n java example.threadsafe jdbc:postgresql:database user password [debug]\n\nThe debug field can be anything. It's presence will enable DriverManager's\ndebug trace. Unless you want to see screens of items, don't put anything in\nhere.");
System.exit(1);
}
/*
* This little lot starts the test
*/
public static void main(String args[])
{
System.out.println("PostgreSQL Thread Safety test v6.4 rev 1\n");
if (args.length < 3)
instructions();
// This line outputs debug information to stderr. To enable this, simply
// add an extra parameter to the command line
if (args.length > 3)
DriverManager.setLogStream(System.err);
// Now run the tests
try
{
threadsafe test = new threadsafe(args);
}
catch (Exception ex)
{
System.err.println("Exception caught.\n" + ex);
ex.printStackTrace();
}
}
}
|