BUG #1550: LOCK TABLE in plpgsql function doesn't work. - Mailing list pgsql-bugs

From Spencer Riddering
Subject BUG #1550: LOCK TABLE in plpgsql function doesn't work.
Date
Msg-id 20050317084854.0DC76F1B49@svr2.postgresql.org
Whole thread Raw
Responses Re: BUG #1550: LOCK TABLE in plpgsql function doesn't work.
Re: BUG #1550: LOCK TABLE in plpgsql function doesn't work.
List pgsql-bugs
The following bug has been logged online:

Bug reference:      1550
Logged by:          Spencer Riddering
Email address:      spencer@riddering.net
PostgreSQL version: 7.4.6
Operating system:   Debian Woody ( Postgresql from backports.org)
Description:        LOCK TABLE in plpgsql function doesn't work.
Details:

When a LOCK TABLE statement is included in a plpgsql function it does not
actually lock the table.

But, if prior to calling the function I execute a seperate statement using
the same connection and same transaction then the LOCK TABLE does work.

I think the expectation is that LOCK TABLE should actually lock the table
even when included in a plpgsql function.

I used JDBC (pg74.215.jdbc3.jar) to discover this behavior.



/***************** FC_PROCESS_ORDER ****************/
DECLARE
  in_receipt      ALIAS FOR $1;
  in_familyName   ALIAS FOR $2;
  in_givenName    ALIAS FOR $3;
  in_address1     ALIAS FOR $4;
  in_address2     ALIAS FOR $5;
  in_zipCode      ALIAS FOR $6;
  in_area         ALIAS FOR $7;
  in_areaDetail   ALIAS FOR $8;
  in_emailAddress ALIAS FOR $9;
  in_product      ALIAS FOR $10;
  in_phone        ALIAS FOR $11;
  in_country      ALIAS FOR $12;


  p_curtime timestamp;
  p_payment_record RECORD;
  p_payment_consumed RECORD;
  p_updated_oid oid; -- set to NULL
  p_order_id int4; -- set to NULL
  p_customer_id int4; -- set to NULL
  p_tmp_order_record RECORD;

BEGIN
--  LOCK TABLE orders IN SHARE ROW EXCLUSIVE MODE;
--  LOCK TABLE payments IN SHARE ROW EXCLUSIVE MODE;

  p_curtime := 'now';

  -- Determine wether payment has occured.
  SELECT INTO p_payment_record * from payments where in_receipt =
payments.receipt;
  IF NOT FOUND THEN
    RETURN -101; -- PAYMENT_NOT_FOUND
  END IF;


  -- *** Payment was recieved ***


  -- Make sure that the payment is not used.
--  SELECT INTO p_tmp_order_record * FROM orders WHERE payment_id =
p_payment_record.id;
  SELECT INTO p_tmp_order_record * FROM orders WHERE payment_id =
p_payment_record.id;
  IF FOUND THEN
    RETURN -102; -- PAYMENT_CONSUMED
  END IF;

  -- *** Payment is available ***

  -- Add user data.
  INSERT INTO customers (family_name,     given_name,   address_1,
address_2,   zip_code, area,    area_detail,   email          , phone   ,
country)
      VALUES            (in_familyName, in_givenName, in_address1,
in_address2, in_zipCode, in_area, in_areaDetail, in_emailAddress, in_phone,
in_country);


  -- Find the newly created id.
  GET DIAGNOSTICS p_updated_oid = RESULT_OID;
  SELECT INTO p_customer_id id from customers where OID = p_updated_oid;

  -- *** customers record added *** ---

  -- *** Add orders Record *** ---

  INSERT INTO orders (customer_id, payment_id       ,    product_id)
      VALUES         (p_customer_id, p_payment_record.id, in_product);

  -- *** orders record added *** ---

  GET DIAGNOSTICS p_updated_oid = RESULT_OID;
  SELECT INTO p_order_id id from orders where OID = p_updated_oid;

  RETURN p_order_id;

END;
/***********************************************/

/***********************************************/
/***********************************************/
/***********************************************/
/***********************************************/
/***********************************************/
/***********************************************/
/***********************************************/

/****************** Java Code ******************/
        // Get Result code/transaction id.
        int int_transactId;

        Connection conn = null;

        try {
            conn = ds.getConnection();
            conn.setAutoCommit(false);
            // This is good. We see updates after they are commited.

conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);

            // Call out to database
            CallableStatement callstat = null;
            Statement stat = null;
            ResultSet rs = null;

            try {
                // I had to add these lines to actually
                //   Lock the tables.
                stat = conn.createStatement();
                stat.executeUpdate("LOCK TABLE orders IN SHARE ROW EXCLUSIVE
MODE");
                stat.close();

                stat = conn.createStatement();
                stat.executeUpdate("LOCK TABLE payments IN SHARE ROW
EXCLUSIVE MODE");
                stat.close();

                //                             1 2 3 4 5 6 7 8 9 10 11 12 13

                callstat = conn
                        .prepareCall("{ ? = call FC_PROCESS_ORDER(?, ?, ?,
?, ?, ?, ?, ?, ?, ?, ?, ?) }");
                callstat.registerOutParameter(1, java.sql.Types.INTEGER);
                callstat.setString(2, receipt);
                callstat.setString(3, familyName);
                callstat.setString(4, givenName);
                callstat.setString(5, address1);
                callstat.setString(6, address2);
                callstat.setInt(7, zipCode);
                callstat.setString(8, area);
                callstat.setString(9, areaDetail);
                callstat.setString(10, emailAddress);
                callstat.setInt(11, product_id);
                callstat.setString(12, phone);
                callstat.setString(13, country);
                if (!callstat.execute()) { // A failure occured, either an
                                           // update count or no result was
                                           // returned.
                    // Package and then delagate the exception.
                    throw new OrderException(
                            "The stored procedure FC_PROCESS_ORDER failed to
return expected results.");
                }

                // *** Executed with out error ***

                // Catch warnings durring debugging.
                if (log.isDebugEnabled()) {
                    printWarnings(callstat.getWarnings());
                }

                int_transactId = callstat.getInt(1);
                conn.commit();
            } finally {
                if (callstat != null) {
                    try {
                        callstat.close();
                    } catch (SQLException err) {
                        log.warn("Failed to properly close CallableStatement
object.",err);
                    }
                }
            }

        } catch (SQLException e) {
            while (e != null) {
                log.error("\nSQL Exception: \n  ANSI-92 SQL State: "
                        + e.getSQLState() + "\n  Vendor Error Code: "
                        + e.getErrorCode(), e);
                e = e.getNextException();
            }
            try {
                conn.rollback();
            } catch (SQLException e1) {
                log.warn("Failed to rollback transaction.",e1);
            }
            throw new OrderException("Unable to retrieve data from
database.");
        } finally {
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e1) {
                    log.warn("Failed to properly close connection object.",
e1);
                }
            }
        }
/*************************************************/

pgsql-bugs by date:

Previous
From: "Qingqing Zhou"
Date:
Subject: random FlushRelationBuffers() failed on Pg8.0.1 Win32
Next
From: "Oliver Siegmar"
Date:
Subject: BUG #1546: Temp table isn't deleted at the end of a transaction / ON COMMIT DROP has no effect