Skip to content

Commit 25b5faa

Browse files
committed
Just a quick patch. This makes the JDBC driver thread safe, which is an
important step towards making the driver compliant, and means that for some Java applications and servlets, only a single database connection is needed, so in a sence this is a nice little show stopper for 6.4 (and should still be backward compatible to 6.3.2). Peter
1 parent 9042e9d commit 25b5faa

File tree

4 files changed

+368
-4
lines changed

4 files changed

+368
-4
lines changed

src/interfaces/jdbc/Makefile

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
# Makefile for Java JDBC interface
55
#
66
# IDENTIFICATION
7-
# $Header: /cvsroot/pgsql/src/interfaces/jdbc/Attic/Makefile,v 1.9 1998/09/03 02:29:41 momjian Exp $
7+
# $Header: /cvsroot/pgsql/src/interfaces/jdbc/Attic/Makefile,v 1.10 1998/10/08 00:38:18 momjian Exp $
88
#
99
#-------------------------------------------------------------------------
1010

@@ -137,7 +137,8 @@ EX= example/basic.class \
137137
example/datestyle.class \
138138
example/psql.class \
139139
example/ImageViewer.class \
140-
example/metadata.class
140+
example/metadata.class \
141+
example/threadsafe.class
141142
# example/Objects.class
142143

143144
# This rule builds the examples
@@ -160,7 +161,9 @@ examples: postgresql.jar $(EX)
160161
@echo " example.psql Simple java implementation of psql"
161162
@echo " example.Objects Demonstrates Object Serialisation"
162163
@echo " "
164+
@echo These are not really examples, but tests various parts of the driver
163165
@echo " example.metadata Tests various metadata methods"
166+
@echo " example.threadsafe Tests the driver's thread safety"
164167
@echo ------------------------------------------------------------
165168
@echo
166169

@@ -170,6 +173,6 @@ example/datestyle.class: example/datestyle.java
170173
example/psql.class: example/psql.java
171174
example/ImageViewer.class: example/ImageViewer.java
172175
#example/Objects.class: example/Objects.java
173-
176+
example/threadsafe.class: example/threadsafe.java
174177
example/metadata.class: example/metadata.java
175178
#######################################################################
Lines changed: 353 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,353 @@
1+
package example;
2+
3+
import java.io.*;
4+
import java.sql.*;
5+
import java.text.*;
6+
7+
// rare in user code, but we use the LargeObject API in this test
8+
import postgresql.largeobject.*;
9+
10+
/**
11+
* This example tests the thread safety of the driver.
12+
*
13+
* It does this by performing several queries, in different threads. Each
14+
* thread has it's own Statement object, which is (in my understanding of the
15+
* jdbc specification) the minimum requirement.
16+
*
17+
*/
18+
19+
public class threadsafe
20+
{
21+
Connection db; // The connection to the database
22+
Statement st; // Our statement to run queries with
23+
24+
public threadsafe(String args[]) throws ClassNotFoundException, FileNotFoundException, IOException, SQLException
25+
{
26+
String url = args[0];
27+
String usr = args[1];
28+
String pwd = args[2];
29+
30+
// Load the driver
31+
Class.forName("postgresql.Driver");
32+
33+
// Connect to database
34+
System.out.println("Connecting to Database URL = " + url);
35+
db = DriverManager.getConnection(url, usr, pwd);
36+
37+
System.out.println("Connected...Now creating a statement");
38+
st = db.createStatement();
39+
40+
// Clean up the database (in case we failed earlier) then initialise
41+
cleanup();
42+
43+
// Now run tests using JDBC methods, then LargeObjects
44+
doexample();
45+
46+
// Clean up the database
47+
cleanup();
48+
49+
// Finally close the database
50+
System.out.println("Now closing the connection");
51+
st.close();
52+
db.close();
53+
54+
}
55+
56+
/**
57+
* This drops the table (if it existed). No errors are reported.
58+
*/
59+
public void cleanup()
60+
{
61+
try {
62+
st.executeUpdate("drop table basic");
63+
} catch(Exception ex) {
64+
// We ignore any errors here
65+
}
66+
}
67+
68+
/**
69+
* This performs the example
70+
*/
71+
public void doexample() throws SQLException
72+
{
73+
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");
74+
75+
thread3 thread3=null;
76+
77+
try {
78+
79+
// create the two threads
80+
Thread thread0 = Thread.currentThread();
81+
Thread thread1 = new thread1(db);
82+
Thread thread2 = new thread2(db);
83+
thread3 = new thread3(db);
84+
85+
// now run, and wait for them
86+
thread1.start();
87+
thread2.start();
88+
thread3.start();
89+
90+
// ok, I know this is bad, but it does the trick here as our main thread
91+
// will yield as long as either of the children are still running
92+
System.out.println("Waiting for threads to run");
93+
while(thread1.isAlive() || thread2.isAlive() || thread3.isAlive())
94+
thread0.yield();
95+
96+
} finally {
97+
// clean up after thread3 (the finally ensures this is run even
98+
// if an exception is thrown inside the try { } construct)
99+
if(thread3 != null)
100+
thread3.cleanup();
101+
}
102+
103+
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.");
104+
}
105+
106+
// This is the first thread. It's the same as the basic test
107+
class thread1 extends Thread
108+
{
109+
Connection c;
110+
Statement st;
111+
112+
public thread1(Connection c) throws SQLException {
113+
this.c = c;
114+
st = c.createStatement();
115+
}
116+
117+
public void run() {
118+
try {
119+
System.out.println("Thread 1 running...");
120+
121+
// First we need a table to store data in
122+
st.executeUpdate("create table basic (a int2, b int2)");
123+
124+
// Now insert some data, using the Statement
125+
st.executeUpdate("insert into basic values (1,1)");
126+
st.executeUpdate("insert into basic values (2,1)");
127+
st.executeUpdate("insert into basic values (3,1)");
128+
129+
// For large inserts, a PreparedStatement is more efficient, because it
130+
// supports the idea of precompiling the SQL statement, and to store
131+
// directly, a Java object into any column. PostgreSQL doesnt support
132+
// precompiling, but does support setting a column to the value of a
133+
// Java object (like Date, String, etc).
134+
//
135+
// Also, this is the only way of writing dates in a datestyle independent
136+
// manner. (DateStyles are PostgreSQL's way of handling different methods
137+
// of representing dates in the Date data type.)
138+
PreparedStatement ps = db.prepareStatement("insert into basic values (?,?)");
139+
for(int i=2;i<200;i++) {
140+
ps.setInt(1,4); // "column a" = 5
141+
ps.setInt(2,i); // "column b" = i
142+
ps.executeUpdate(); // executeUpdate because insert returns no data
143+
if((i%50)==0)
144+
DriverManager.println("Thread 1 done "+i+" inserts");
145+
}
146+
ps.close(); // Always close when we are done with it
147+
148+
// Finally perform a query on the table
149+
DriverManager.println("Thread 1 performing a query");
150+
ResultSet rs = st.executeQuery("select a, b from basic");
151+
int cnt=0;
152+
if(rs!=null) {
153+
// Now we run through the result set, printing out the result.
154+
// Note, we must call .next() before attempting to read any results
155+
while(rs.next()) {
156+
int a = rs.getInt("a"); // This shows how to get the value by name
157+
int b = rs.getInt(2); // This shows how to get the value by column
158+
//System.out.println(" a="+a+" b="+b);
159+
cnt++;
160+
}
161+
rs.close(); // again, you must close the result when done
162+
}
163+
DriverManager.println("Thread 1 read "+cnt+" rows");
164+
165+
// The last thing to do is to drop the table. This is done in the
166+
// cleanup() method.
167+
System.out.println("Thread 1 finished");
168+
} catch(SQLException se) {
169+
System.err.println("Thread 1: "+se.toString());
170+
se.printStackTrace();
171+
System.exit(1);
172+
}
173+
}
174+
}
175+
176+
// This is the second thread. It's the similar to the basic test, and thread1
177+
// except it works on another table.
178+
class thread2 extends Thread
179+
{
180+
Connection c;
181+
Statement st;
182+
183+
public thread2(Connection c) throws SQLException {
184+
this.c = c;
185+
st = c.createStatement();
186+
}
187+
188+
public void run() {
189+
try {
190+
System.out.println("Thread 2 running...");
191+
192+
// For large inserts, a PreparedStatement is more efficient, because it
193+
// supports the idea of precompiling the SQL statement, and to store
194+
// directly, a Java object into any column. PostgreSQL doesnt support
195+
// precompiling, but does support setting a column to the value of a
196+
// Java object (like Date, String, etc).
197+
//
198+
// Also, this is the only way of writing dates in a datestyle independent
199+
// manner. (DateStyles are PostgreSQL's way of handling different methods
200+
// of representing dates in the Date data type.)
201+
PreparedStatement ps = db.prepareStatement("insert into basic values (?,?)");
202+
for(int i=2;i<200;i++) {
203+
ps.setInt(1,4); // "column a" = 5
204+
ps.setInt(2,i); // "column b" = i
205+
ps.executeUpdate(); // executeUpdate because insert returns no data
206+
if((i%50)==0)
207+
DriverManager.println("Thread 2 done "+i+" inserts");
208+
}
209+
ps.close(); // Always close when we are done with it
210+
211+
// Finally perform a query on the table
212+
DriverManager.println("Thread 2 performing a query");
213+
ResultSet rs = st.executeQuery("select * from basic where b>1");
214+
int cnt=0;
215+
if(rs!=null) {
216+
// First find out the column numbers.
217+
//
218+
// It's best to do this here, as calling the methods with the column
219+
// numbers actually performs this call each time they are called. This
220+
// really speeds things up on large queries.
221+
//
222+
int col_a = rs.findColumn("a");
223+
int col_b = rs.findColumn("b");
224+
225+
// Now we run through the result set, printing out the result.
226+
// Again, we must call .next() before attempting to read any results
227+
while(rs.next()) {
228+
int a = rs.getInt(col_a); // This shows how to get the value by name
229+
int b = rs.getInt(col_b); // This shows how to get the value by column
230+
//System.out.println(" a="+a+" b="+b);
231+
cnt++;
232+
}
233+
rs.close(); // again, you must close the result when done
234+
}
235+
DriverManager.println("Thread 2 read "+cnt+" rows");
236+
237+
// The last thing to do is to drop the table. This is done in the
238+
// cleanup() method.
239+
System.out.println("Thread 2 finished");
240+
} catch(SQLException se) {
241+
System.err.println("Thread 2: "+se.toString());
242+
se.printStackTrace();
243+
System.exit(1);
244+
}
245+
}
246+
}
247+
248+
// This is the third thread. It loads, then reads from a LargeObject, using
249+
// our LargeObject api.
250+
//
251+
// The purpose of this is to test that FastPath will work in between normal
252+
// JDBC queries.
253+
class thread3 extends Thread
254+
{
255+
Connection c;
256+
Statement st;
257+
LargeObjectManager lom;
258+
LargeObject lo;
259+
int oid;
260+
261+
public thread3(Connection c) throws SQLException {
262+
this.c = c;
263+
//st = c.createStatement();
264+
265+
// create a blob
266+
lom = ((postgresql.Connection)c).getLargeObjectAPI();
267+
oid = lom.create();
268+
System.out.println("Thread 3 has created a blob of oid "+oid);
269+
}
270+
271+
public void run() {
272+
try {
273+
System.out.println("Thread 3 running...");
274+
275+
DriverManager.println("Thread 3: Loading data into blob "+oid);
276+
lo = lom.open(oid);
277+
FileInputStream fis = new FileInputStream("example/threadsafe.java");
278+
// keep the buffer size small, to allow the other thread a chance
279+
byte buf[] = new byte[128];
280+
int rc,bc=1,bs=0;
281+
while((rc=fis.read(buf))>0) {
282+
DriverManager.println("Thread 3 read block "+bc+" "+bs+" bytes");
283+
lo.write(buf,0,rc);
284+
bc++;
285+
bs+=rc;
286+
}
287+
lo.close();
288+
fis.close();
289+
290+
DriverManager.println("Thread 3: Reading blob "+oid);
291+
lo=lom.open(oid);
292+
bc=0;
293+
while(buf.length>0) {
294+
buf=lo.read(buf.length);
295+
if(buf.length>0) {
296+
String s = new String(buf);
297+
bc++;
298+
DriverManager.println("Thread 3 block "+bc);
299+
DriverManager.println("Block "+bc+" got "+s);
300+
}
301+
}
302+
lo.close();
303+
304+
System.out.println("Thread 3 finished");
305+
} catch(Exception se) {
306+
System.err.println("Thread 3: "+se.toString());
307+
se.printStackTrace();
308+
System.exit(1);
309+
}
310+
}
311+
312+
public void cleanup() throws SQLException {
313+
if(lom!=null && oid!=0) {
314+
System.out.println("Thread 3: Removing blob oid="+oid);
315+
lom.delete(oid);
316+
}
317+
}
318+
}
319+
320+
/**
321+
* Display some instructions on how to run the example
322+
*/
323+
public static void instructions()
324+
{
325+
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");
326+
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.");
327+
System.exit(1);
328+
}
329+
330+
/**
331+
* This little lot starts the test
332+
*/
333+
public static void main(String args[])
334+
{
335+
System.out.println("PostgreSQL Thread Safety test v6.4 rev 1\n");
336+
337+
if(args.length<3)
338+
instructions();
339+
340+
// This line outputs debug information to stderr. To enable this, simply
341+
// add an extra parameter to the command line
342+
if(args.length>3)
343+
DriverManager.setLogStream(System.err);
344+
345+
// Now run the tests
346+
try {
347+
threadsafe test = new threadsafe(args);
348+
} catch(Exception ex) {
349+
System.err.println("Exception caught.\n"+ex);
350+
ex.printStackTrace();
351+
}
352+
}
353+
}

0 commit comments

Comments
 (0)