mirror of https://github.com/postgres/postgres
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). PeterREL6_4
parent
9042e9d757
commit
25b5faa7cd
@ -0,0 +1,353 @@ |
|||||||
|
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 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("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(); |
||||||
|
|
||||||
|
// 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 basic"); |
||||||
|
} 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 basic (a int2, b int2)"); |
||||||
|
|
||||||
|
// Now insert some data, using the Statement
|
||||||
|
st.executeUpdate("insert into basic values (1,1)"); |
||||||
|
st.executeUpdate("insert into basic values (2,1)"); |
||||||
|
st.executeUpdate("insert into basic 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 basic values (?,?)"); |
||||||
|
for(int i=2;i<200;i++) { |
||||||
|
ps.setInt(1,4); // "column a" = 5
|
||||||
|
ps.setInt(2,i); // "column b" = i
|
||||||
|
ps.executeUpdate(); // executeUpdate because insert returns no data
|
||||||
|
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 basic"); |
||||||
|
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..."); |
||||||
|
|
||||||
|
// 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 basic values (?,?)"); |
||||||
|
for(int i=2;i<200;i++) { |
||||||
|
ps.setInt(1,4); // "column a" = 5
|
||||||
|
ps.setInt(2,i); // "column b" = i
|
||||||
|
ps.executeUpdate(); // executeUpdate because insert returns no data
|
||||||
|
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 basic 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 = ((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(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
Loading…
Reference in new issue