Bug #3708 Advanced Query Cursor in Stored Procedure Crashes The server
Submitted: 11 May 2004 8:18 Modified: 19 Jun 2004 21:46
Reporter: Steven Szelei Email Updates:
Status: No Feedback Impact on me:
None 
Category:MySQL Server Severity:S1 (Critical)
Version:5.0 alpha OS:Windows (Windows 2000 Adv Svr)
Assigned to: CPU Architecture:Any

[11 May 2004 8:18] Steven Szelei
Description:
Using the sample in the documentation to open and repeat till done on a cursor will work for simple queries but not for advanced queries.

Attempting to open a query for the following 
DECLARE invcur CURSOR FOR SELECT i.invoice_id, i.Amount AS Total, SUM(ip.CPL_applied_amt) AS ApplAmt, i.InvoiceType_id FROM invoice i LEFT JOIN invoicepayment ip on i.invoice_id = ip.invoice_id WHERE  i.Customer_id = inCustID GROUP BY i.invoice_id HAVING sum(IF(ip.CPL_applied_amt IS NULL, 0,ip.CPL_applied_amt)) < Total;

Tables are developed using INNODB
Working from the command line

How to repeat:
Table strucure
--
-- Table structure for table `customerpaymentlog`
--

DROP TABLE IF EXISTS customerpaymentlog;
CREATE TABLE customerpaymentlog (
  CPL_id bigint(20) unsigned NOT NULL auto_increment,
  Customer_id bigint(20) unsigned NOT NULL default '0',
  Payment float(10,2) NOT NULL default '0.00',
  Commission float(10,2) default NULL,
  Transaction_dt date NOT NULL default '0000-00-00',
  Dealer_id bigint(20) unsigned NOT NULL default '0',
  CPL_id_void bigint(20) unsigned default NULL,
  Reconcile enum('Y','N') default 'N',
  PRIMARY KEY  (CPL_id),
  INDEX IDX_CUSTPAYLOG_CUSTOMER (Customer_id),
  INDEX IDX_CUSTPAYLOG_DEALER (Dealer_id),
  CONSTRAINT FK_CUSTPAYLOG_CUST_ID FOREIGN KEY (Customer_id) REFERENCES Customer(Customer_id),
  CONSTRAINT FK_CUSTPAYLOG_DEALER_ID FOREIGN KEY (Dealer_id) REFERENCES Dealer(Dealer_id)
) ENGINE=INNODB DEFAULT CHARSET=latin1;

--
-- Dumping data for table `customerpaymentlog`
--

/*!40000 ALTER TABLE customerpaymentlog DISABLE KEYS */;
LOCK TABLES customerpaymentlog WRITE;
UNLOCK TABLES;
/*!40000 ALTER TABLE customerpaymentlog ENABLE KEYS */;

--
-- Table structure for table `invoicetype`
--

DROP TABLE IF EXISTS invoicetype;
CREATE TABLE invoicetype (
  InvoiceType_id bigint(20) unsigned NOT NULL auto_increment,
  Descr varchar(30) default NULL,
  PRIMARY KEY  (Invoicetype_id)
) ENGINE=INNODB DEFAULT CHARSET=latin1;

--
-- Dumping data for table `invoicetype`
--

/*!40000 ALTER TABLE invoicetype DISABLE KEYS */;
LOCK TABLES invoicetype WRITE;
INSERT INTO invoicetype VALUES (1,'Automatic'),(2,'Change Feature'),(3,'First Payment'),(4,'Reconnection');
UNLOCK TABLES;
/*!40000 ALTER TABLE invoicetype ENABLE KEYS */;

-- Table structure for table `invoice`
--

DROP TABLE IF EXISTS invoice;
CREATE TABLE invoice (
  Invoice_id bigint(20) unsigned NOT NULL auto_increment,
  Customer_id bigint(20) unsigned NOT NULL default '0',
  Tax float(10,2) NOT NULL default '0.00',
  Amount float(10,2) default '0.00',
  Invoice_dt date NOT NULL default '0000-00-00',
  Creation_dt timestamp NOT NULL,
  InvoiceType_id bigint(20) unsigned NOT NULL default '0',
  PRIMARY KEY  (Invoice_id),
  INDEX IDX_INVOICE_CUST (Customer_id),
  INDEX IDX_INVOICE_INVTYPE (InvoiceType_id),
  CONSTRAINT FK_INVOICE_CUST_ID FOREIGN KEY (Customer_id) REFERENCES Customer(Customer_id),
  CONSTRAINT FK_INVOICE_INVTYPE_ID FOREIGN KEY (InvoiceType_id) REFERENCES InvoiceType(InvoiceType_id)
) ENGINE=INNODB DEFAULT CHARSET=latin1;

--
-- Dumping data for table `invoice`
--

/*!40000 ALTER TABLE invoice DISABLE KEYS */;
LOCK TABLES invoice WRITE;
UNLOCK TABLES;
/*!40000 ALTER TABLE invoice ENABLE KEYS */;

--
-- Table structure for table `invoicepayment`
--

DROP TABLE IF EXISTS invoicepayment;
CREATE TABLE invoicepayment (
  Invoice_id bigint(20) unsigned NOT NULL default '0',
  CPL_id bigint(20) unsigned NOT NULL default '0',
  CPL_applied_amt float(10,2) NOT NULL default '0.00',
  INDEX IDX_IPAY_INVOICE (Invoice_id),
  INDEX IDX_IPAY_CPL (CPL_id),
  CONSTRAINT FK_IPAY_INVOICE_ID FOREIGN KEY (Invoice_id) REFERENCES Invoice(Invoice_id),
  CONSTRAINT FK_IPAY_CPL_ID FOREIGN KEY (CPL_ID) REFERENCES customerpaymentlog(CPL_id)
) ENGINE=INNODB DEFAULT CHARSET=latin1;

--
-- Dumping data for table `invoicepayment`
--

/*!40000 ALTER TABLE invoicepayment DISABLE KEYS */;
LOCK TABLES invoicepayment WRITE;
UNLOCK TABLES;
/*!40000 ALTER TABLE invoicepayment ENABLE KEYS */;

input the following stored procedure 

Create procedure sp_CustomerPayment (in inCPLid BIGINT unsigned, in inCustID BIGINT unsigned, in inPayment Float(10,2), in inTransDt Date, in inDealID BIGINT Unsigned, in inReconciled varchar(1) , in inAction integer, OUT ErrID INTEGER)
BEGIN
   DECLARE PotOMoney FLOAT(10,2);
   DECLARE InvPayExt FLOAT(10,2);
   DECLARE RecExt VARCHAR(1);
   DECLARE CommisInvID BIGINT Unsigned;
   DECLARE invid, invtypid BIGINT Unsigned;
   DECLARE tot,aplamt,CurAplAmt,calcComm FLOAT(10,2);
   
   DECLARE done INT DEFAULT 0;
   DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done = 1;
 --  DECLARE invcur CURSOR FOR SELECT i.invoice_id, i.Amount AS Total, SUM(ip.CPL_applied_amt) AS ApplAmt, i.InvoiceType_id FROM invoice i LEFT JOIN invoicepayment ip on i.invoice_id = ip.invoice_id WHERE  i.Customer_id = inCustID GROUP BY i.invoice_id HAVING sum(IF(ip.CPL_applied_amt IS NULL, 0,ip.CPL_applied_amt)) < Total;
   DECLARE invcur CURSOR FOR SELECT i.invoice_id, i.Amount AS Total, SUM(ip.CPL_applied_amt) AS ApplAmt, i.InvoiceType_id FROM invoice i LEFT JOIN invoicepayment ip on i.invoice_id = ip.invoice_id GROUP BY i.invoice_id;         
  DECLARE ERROR_REC_PAY_DEL INTEGER DEFAULT  -5;  -- look up in ERROR table
  DECLARE ERROR_REC_PAY_UPD INTEGER DEFAULT  -4;  -- look up in ERROR table
   
  SET ErrID = 0;
  
  -- CPL_id_void --> to reference the transaction with the corrected dealer / customer
   CASE inAction
      WHEN 1 THEN -- INSERT
        INSERT INTO CustomerPaymentLog (Customer_id, Payment, Transaction_dt, Dealer_id) VALUES (inCustID,inPayment,inTransDt,inDealID);
      WHEN 2 THEN -- UPDATE
          SET PotOMoney = inPayment;
		  	
          SELECT Payment, Reconcile into InvPayExt, RecExt FROM CustomerPaymentLog WHERE CPL_id = inCPLid;

          -- calculate the commission
          -- determine if the payment entered pays at least 50% a base package; if not then no commission on this payment.
          -- get all invoices where the Amount of the invoice is greater than the summation of the applied amount from invoicepayment for that invoice
          -- apply this payment to the earliest unpaid invoice. Any excess will be applied to the next in cronological order till no money is left in this payment          
        IF (RecExt = 'Y') THEN
          SET ErrID = ERROR_REC_PAY;
        END IF;
--        IF (inReconciled = 'Y' AND RecExt = 'N') THEN

          open invcur;
             
          REPEAT 
			FETCH invcur into invid; 
    
--			IF NOT done THEN   
--			  INSERT INTO mytest(Invoice_id, Total, AppliedAmt, InvoiceTypeID) VALUES (invid,tot, aplamt, invtypid);
   SET ErrID = -10;
--			END IF;
			UNTIL done END REPEAT;
	
          close invcur; 
                     
  --      END IF;
      WHEN 3 THEN -- DELETE
        SELECT Payment, Reconcile into InvPayExt, RecExt FROM CustomerPaymentLog WHERE CPL_id = inCPLid;
        if(inReconciled = 'N' AND RecExt = 'N') THEN
           DELETE FROM CustomerPaymentLog WHERE CPL_id = inCPLid;
        ELSE
          SET ErrID = ERROR_REC_PAY_DEL;
        END IF;
   END CASE;  
     
END;$

Add data to the invoice customerpaymentlog and invoicepayment table
Run the query at the command line
CURSOR FOR SELECT i.invoice_id, i.Amount AS Total, SUM(ip.CPL_applied_amt) AS ApplAmt, i.InvoiceType_id FROM invoice i LEFT JOIN invoicepayment ip on i.invoice_id = ip.invoice_id WHERE  i.Customer_id = <<inCustID>> GROUP BY i.invoice_id HAVING sum(IF(ip.CPL_applied_amt IS NULL, 0,ip.CPL_applied_amt)) < Total;
rows return fine.
Clear the tables just set.

call the procedure and pass in data making the action 1 to start
call the procedure again pass in data making the action 2
The server will crash.
Restart the server
Change the cursor sql to something simple like 
   select i.invoice_id from invoice i;
drop the procedure and reup
run the procedure again using the second call defined above with action 2
procedure will run just fine.

Suggested fix:
cursor can't seem to open on complex query. Have no idea why! I would imagine that the same engine to parse a sql statement on command line would be used for a cursor as well.
[11 May 2004 21:20] Zack Angelo
I'm having trouble opening cursors as simple as: 

declare cursor_prod cursor for 
select wpicsp.prodid,whseid,baseprice,listprice,replcost,pricetype,conv 
	from wpicsp,wpicsw 
       	where 	wpicsp.prodid = wpicsw.prodid and
             	wpicsw.whseid = "DENV";
[12 May 2004 0:31] Steven Szelei
Sorry forgot to say what the error was.
Instruction at 0x0045a223 referenced memory at 0x010a3000. The memory could not be written.
[19 May 2004 21:46] MySQL Verification Team
I tried to run your test case without success.
Can you please provide it again attaching the
files using the Files tab instead to paste it.

Thanks in advance
[14 Feb 2005 22:54] Bugs System
No feedback was provided for this bug for over a month, so it is
being suspended automatically. If you are able to provide the
information that was originally requested, please do so and change
the status of the bug back to "Open".