Merge branch '2.1-oracle-compat' into develop-new-merge-oracle

This commit is contained in:
Johan Wikman
2017-06-28 22:30:16 +02:00
88 changed files with 15138 additions and 745 deletions

View File

@ -1,5 +1,7 @@
# Include the embedded library headers
if (BUILD_QC_MYSQLEMBEDDED)
find_package(MySQL)
subdirs(MYSQL_INCLUDE_DIR_ALL ${MYSQL_EMBEDDED_INCLUDE_DIR})
foreach(DIR ${MYSQL_INCLUDE_DIR_ALL})
include_directories(${DIR})
@ -44,6 +46,42 @@ if (BUILD_QC_MYSQLEMBEDDED)
add_test(TestQC_CompareWhiteSpace compare -v 2 -S -s "select user from mysql.user; ")
add_test(TestQC_version_sensitivity version_sensitivity)
if(NOT (MYSQL_EMBEDDED_VERSION VERSION_LESS 10.3))
add_test(TestQC_Oracle-binlog_stm_ps compare -v 2 ${CMAKE_CURRENT_SOURCE_DIR}/oracle/binlog_stm_ps.test)
add_test(TestQC_Oracle-binlog_stm_sp compare -v 2 ${CMAKE_CURRENT_SOURCE_DIR}/oracle/binlog_stm_sp.test)
add_test(TestQC_Oracle-exception compare -v 2 ${CMAKE_CURRENT_SOURCE_DIR}/oracle/exception.test)
add_test(TestQC_Oracle-func_case compare -v 2 ${CMAKE_CURRENT_SOURCE_DIR}/oracle/func_case.test)
add_test(TestQC_Oracle-func_concat compare -v 2 ${CMAKE_CURRENT_SOURCE_DIR}/oracle/func_concat.test)
add_test(TestQC_Oracle-func_decode compare -v 2 ${CMAKE_CURRENT_SOURCE_DIR}/oracle/func_decode.test)
add_test(TestQC_Oracle-func_length compare -v 2 ${CMAKE_CURRENT_SOURCE_DIR}/oracle/func_length.test)
add_test(TestQC_Oracle-func_misc compare -v 2 ${CMAKE_CURRENT_SOURCE_DIR}/oracle/func_misc.test)
add_test(TestQC_Oracle-misc compare -v 2 ${CMAKE_CURRENT_SOURCE_DIR}/oracle/misc.test)
add_test(TestQC_Oracle-ps compare -v 2 ${CMAKE_CURRENT_SOURCE_DIR}/oracle/ps.test)
add_test(TestQC_Oracle-sequence compare -v 2 ${CMAKE_CURRENT_SOURCE_DIR}/oracle/sequence.test)
add_test(TestQC_Oracle-sp-anonymous compare -v 2 ${CMAKE_CURRENT_SOURCE_DIR}/oracle/sp-anonymous.test)
add_test(TestQC_Oracle-sp-code compare -v 2 ${CMAKE_CURRENT_SOURCE_DIR}/oracle/sp-code.test)
add_test(TestQC_Oracle-sp-cursor-decl compare -v 2 ${CMAKE_CURRENT_SOURCE_DIR}/oracle/sp-cursor-decl.test)
add_test(TestQC_Oracle-sp-cursor-rowtype compare -v 2 ${CMAKE_CURRENT_SOURCE_DIR}/oracle/sp-cursor-rowtype.test)
add_test(TestQC_Oracle-sp-cursor compare -v 2 ${CMAKE_CURRENT_SOURCE_DIR}/oracle/sp-cursor.test)
add_test(TestQC_Oracle-sp-goto compare -v 2 ${CMAKE_CURRENT_SOURCE_DIR}/oracle/sp-goto.test)
add_test(TestQC_Oracle-sp-param_inc compare -v 2 ${CMAKE_CURRENT_SOURCE_DIR}/oracle/sp-param.inc)
add_test(TestQC_Oracle-sp-param compare -v 2 ${CMAKE_CURRENT_SOURCE_DIR}/oracle/sp-param.test)
add_test(TestQC_Oracle-sp-row compare -v 2 ${CMAKE_CURRENT_SOURCE_DIR}/oracle/sp-row.test)
add_test(TestQC_Oracle-sp-row-vs-var_inc compare -v 2 ${CMAKE_CURRENT_SOURCE_DIR}/oracle/sp-row-vs-var.inc)
add_test(TestQC_Oracle-sp-security compare -v 2 ${CMAKE_CURRENT_SOURCE_DIR}/oracle/sp-security.test)
add_test(TestQC_Oracle-sp compare -v 2 ${CMAKE_CURRENT_SOURCE_DIR}/oracle/sp.test)
add_test(TestQC_Oracle-trigger compare -v 2 ${CMAKE_CURRENT_SOURCE_DIR}/oracle/trigger.test)
add_test(TestQC_Oracle-truncate compare -v 2 ${CMAKE_CURRENT_SOURCE_DIR}/oracle/truncate.test)
add_test(TestQC_Oracle-type_blob compare -v 2 ${CMAKE_CURRENT_SOURCE_DIR}/oracle/type_blob.test)
add_test(TestQC_Oracle-type_clob compare -v 2 ${CMAKE_CURRENT_SOURCE_DIR}/oracle/type_clob.test)
add_test(TestQC_Oracle-type_date compare -v 2 ${CMAKE_CURRENT_SOURCE_DIR}/oracle/type_date.test)
add_test(TestQC_Oracle-type_number compare -v 2 ${CMAKE_CURRENT_SOURCE_DIR}/oracle/type_number.test)
add_test(TestQC_Oracle-type_raw compare -v 2 ${CMAKE_CURRENT_SOURCE_DIR}/oracle/type_raw.test)
add_test(TestQC_Oracle-type_varchar compare -v 2 ${CMAKE_CURRENT_SOURCE_DIR}/oracle/type_varchar.test)
add_test(TestQC_Oracle-type_varchar2 compare -v 2 ${CMAKE_CURRENT_SOURCE_DIR}/oracle/type_varchar2.test)
add_test(TestQC_Oracle-type_variables compare -v 2 ${CMAKE_CURRENT_SOURCE_DIR}/oracle/variables.test)
endif()
endif()
add_subdirectory(canonical_tests)

View File

@ -46,7 +46,7 @@ int main(int argc, char** argv)
set_langdir(strdup("."));
set_process_datadir(strdup("/tmp"));
qc_setup("qc_sqlite", NULL);
qc_setup("qc_sqlite", QC_SQL_MODE_DEFAULT, NULL);
qc_process_init(QC_INIT_BOTH);
infile = fopen(argv[1], "rb");

View File

@ -313,7 +313,7 @@ int main(int argc, char** argv)
if (mxs_log_init(NULL, ".", MXS_LOG_TARGET_DEFAULT))
{
if (qc_setup(lib, NULL) && qc_process_init(QC_INIT_BOTH))
if (qc_setup(lib, QC_SQL_MODE_DEFAULT, NULL) && qc_process_init(QC_INIT_BOTH))
{
rc = run(input_name, expected_name);
qc_process_end(QC_INIT_BOTH);

View File

@ -20,6 +20,8 @@
#include <set>
#include <string>
#include <sstream>
#include <my_config.h>
#define MYSQL_COM_QUERY COM_QUERY
#define MYSQL_COM_QUIT COM_QUIT
#define MYSQL_COM_INIT_DB COM_INIT_DB
#define MYSQL_COM_CHANGE_USER COM_CHANGE_USER
@ -27,6 +29,7 @@
#include <maxscale/log_manager.h>
#include <maxscale/protocol/mysql.h>
#include <maxscale/query_classifier.h>
#include "../../server/modules/protocol/MySQL/MySQLClient/setsqlmodeparser.hh"
#include "testreader.hh"
using std::cerr;
using std::cin;
@ -39,20 +42,29 @@ using std::ostream;
using std::string;
using std::stringstream;
#if MYSQL_VERSION_MAJOR == 10 && MYSQL_VERSION_MINOR == 3
#define USING_MARIADB_103
#else
#undef USING_MARIADB_103
#endif
namespace
{
char USAGE[] =
"usage: compare [-r count] [-d] [-1 classfier1] [-2 classifier2] "
"[-A args] [-B args] [-v [0..2]] [-s statement]|[file]]\n\n"
"[-A args] [-B args] [-C args] [-m [default|oracle]] [-v [0..2]] [-s statement]|[file]]\n\n"
"-r redo the test the specified number of times; 0 means forever, default is 1\n"
"-d don't stop after first failed query\n"
"-1 the first classifier, default qc_mysqlembedded\n"
"-2 the second classifier, default qc_sqlite\n"
"-1 the first classifier, default 'qc_mysqlembedded'\n"
"-2 the second classifier, default 'qc_sqlite'\n"
"-A arguments for the first classifier\n"
"-B arguments for the second classifier\n"
"-C arguments for both classifiers\n"
"-m initial sql mode, 'default' or 'oracle', default is 'default'\n"
"-s compare single statement\n"
"-S strict, also require that the parse result is identical\n"
"-R strict reporting, report if parse result is different\n"
"-v 0, only return code\n"
" 1, query and result for failed cases\n"
" 2, all queries, and result for failed cases\n"
@ -75,6 +87,7 @@ struct State
bool result_printed;
bool stop_at_error;
bool strict;
bool strict_reporting;
size_t line;
size_t n_statements;
size_t n_errors;
@ -87,6 +100,7 @@ struct State
false, // result_printed
true, // stop_at_error
false, // strict
false, // strict reporting
0, // line
0, // n_statements
0, // n_errors
@ -160,13 +174,13 @@ QUERY_CLASSIFIER* load_classifier(const char* name)
return pClassifier;
}
QUERY_CLASSIFIER* get_classifier(const char* zName, const char* zArgs)
QUERY_CLASSIFIER* get_classifier(const char* zName, qc_sql_mode_t sql_mode, const char* zArgs)
{
QUERY_CLASSIFIER* pClassifier = load_classifier(zName);
if (pClassifier)
{
if ((pClassifier->qc_setup(zArgs) != QC_RESULT_OK) ||
if ((pClassifier->qc_setup(sql_mode, zArgs) != QC_RESULT_OK) ||
((pClassifier->qc_process_init() != QC_RESULT_OK)))
{
cerr << "error: Could not setup or init classifier " << zName << "." << endl;
@ -187,16 +201,17 @@ void put_classifier(QUERY_CLASSIFIER* pClassifier)
}
}
bool get_classifiers(const char* zName1, const char* zArgs1, QUERY_CLASSIFIER** ppClassifier1,
bool get_classifiers(qc_sql_mode_t sql_mode,
const char* zName1, const char* zArgs1, QUERY_CLASSIFIER** ppClassifier1,
const char* zName2, const char* zArgs2, QUERY_CLASSIFIER** ppClassifier2)
{
bool rc = false;
QUERY_CLASSIFIER* pClassifier1 = get_classifier(zName1, zArgs1);
QUERY_CLASSIFIER* pClassifier1 = get_classifier(zName1, sql_mode, zArgs1);
if (pClassifier1)
{
QUERY_CLASSIFIER* pClassifier2 = get_classifier(zName2, zArgs2);
QUERY_CLASSIFIER* pClassifier2 = get_classifier(zName2, sql_mode, zArgs2);
if (pClassifier2)
{
@ -339,7 +354,10 @@ bool compare_parse(QUERY_CLASSIFIER* pClassifier1, GWBUF* pCopy1,
else
{
ss << "INF: ";
success = true;
if (!global.strict_reporting)
{
success = true;
}
}
ss << static_cast<qc_parse_result_t>(rv1) << " != " << static_cast<qc_parse_result_t>(rv2);
@ -1221,6 +1239,33 @@ bool compare(QUERY_CLASSIFIER* pClassifier1, QUERY_CLASSIFIER* pClassifier2, con
bool success = compare(pClassifier1, pCopy1, pClassifier2, pCopy2);
if (success)
{
SetSqlModeParser::sql_mode_t sql_mode;
SetSqlModeParser parser;
if (parser.get_sql_mode(&pCopy1, &sql_mode) == SetSqlModeParser::IS_SET_SQL_MODE)
{
switch (sql_mode)
{
case SetSqlModeParser::DEFAULT:
pClassifier1->qc_set_sql_mode(QC_SQL_MODE_DEFAULT);
pClassifier2->qc_set_sql_mode(QC_SQL_MODE_DEFAULT);
break;
case SetSqlModeParser::ORACLE:
pClassifier1->qc_set_sql_mode(QC_SQL_MODE_ORACLE);
pClassifier2->qc_set_sql_mode(QC_SQL_MODE_ORACLE);
break;
default:
ss_dassert(!true);
case SetSqlModeParser::SOMETHING:
break;
};
}
}
gwbuf_free(pCopy1);
gwbuf_free(pCopy2);
@ -1302,6 +1347,15 @@ int run(QUERY_CLASSIFIER* pClassifier1, QUERY_CLASSIFIER* pClassifier2, const st
return global.n_errors == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
}
void append_arg(string& args, const string& arg)
{
if (!args.empty())
{
args += ",";
}
args += arg;
}
}
int main(int argc, char* argv[])
@ -1310,14 +1364,19 @@ int main(int argc, char* argv[])
const char* zClassifier1 = "qc_mysqlembedded";
const char* zClassifier2 = "qc_sqlite";
const char* zClassifier1Args = NULL;
const char* zClassifier2Args = "log_unrecognized_statements=1";
string classifier1Args;
#if defined(USING_MARIADB_103)
string classifier2Args("parse_as=10.3,log_unrecognized_statements=1");
#else
string classifier2Args("log_unrecognized_statements=1");
#endif
const char* zStatement = NULL;
qc_sql_mode_t sql_mode = QC_SQL_MODE_DEFAULT;
size_t rounds = 1;
int v = VERBOSITY_NORMAL;
int c;
while ((c = getopt(argc, argv, "r:d1:2:v:A:B:s:S")) != -1)
while ((c = getopt(argc, argv, "r:d1:2:v:A:B:C:m:s:SR")) != -1)
{
switch (c)
{
@ -1338,11 +1397,16 @@ int main(int argc, char* argv[])
break;
case 'A':
zClassifier1Args = optarg;
append_arg(classifier1Args, optarg);
break;
case 'B':
zClassifier2Args = optarg;
append_arg(classifier2Args, optarg);
break;
case 'C':
append_arg(classifier1Args, optarg);
append_arg(classifier2Args, optarg);
break;
case 'd':
@ -1353,10 +1417,30 @@ int main(int argc, char* argv[])
zStatement = optarg;
break;
case 'm':
if (strcasecmp(optarg, "default") == 0)
{
sql_mode = QC_SQL_MODE_DEFAULT;
}
else if (strcasecmp(optarg, "oracle") == 0)
{
sql_mode = QC_SQL_MODE_ORACLE;
}
else
{
rc = EXIT_FAILURE;
break;
}
break;
case 'S':
global.strict = true;
break;
case 'R':
global.strict_reporting = true;
break;
default:
rc = EXIT_FAILURE;
break;
@ -1378,10 +1462,14 @@ int main(int argc, char* argv[])
if (mxs_log_init(NULL, ".", MXS_LOG_TARGET_DEFAULT))
{
const char* zClassifier1Args = classifier1Args.c_str();
const char* zClassifier2Args = classifier2Args.c_str();
QUERY_CLASSIFIER* pClassifier1;
QUERY_CLASSIFIER* pClassifier2;
if (get_classifiers(zClassifier1, zClassifier1Args, &pClassifier1,
if (get_classifiers(sql_mode,
zClassifier1, zClassifier1Args, &pClassifier1,
zClassifier2, zClassifier2Args, &pClassifier2))
{
size_t round = 0;

View File

@ -41,7 +41,7 @@ int main()
set_libdir(strdup("../qc_sqlite"));
if (qc_setup("qc_sqlite", NULL) && qc_process_init(QC_INIT_BOTH))
if (qc_setup("qc_sqlite", QC_SQL_MODE_DEFAULT, NULL) && qc_process_init(QC_INIT_BOTH))
{
const char s[] = "SELECT @@global.max_allowed_packet";

View File

@ -0,0 +1,11 @@
#
# This file contains PL/SQL statements that in some way are not handled
# completely or correctly.
#
PREPARE stmt FROM 'SELECT 1 AS a FROM ' || @table_name;
# qc_sqlite parses this correctly. However, currently there is no way
# that the "'SELECT 1 AS a FROM ' || @table_name" can be expressed as
# a statement that separately can be analyzed. Consequently, statements
# like this will not pass through the database firewall filter.

View File

@ -0,0 +1,37 @@
--source include/not_embedded.inc
--source include/have_binlog_format_statement.inc
--disable_query_log
#qc_sqlite: reset master; # get rid of previous tests binlog
--enable_query_log
SET sql_mode=ORACLE;
--echo #
--echo # MDEV-10801 sql_mode: dynamic SQL placeholders
--echo #
CREATE TABLE t1 (a INT, b INT);
SET @a=10, @b=20;
PREPARE stmt FROM 'INSERT INTO t1 VALUES (?,?)';
EXECUTE stmt USING @a, @b;
PREPARE stmt FROM 'INSERT INTO t1 VALUES (:a,:b)';
EXECUTE stmt USING @a, @b;
PREPARE stmt FROM 'INSERT INTO t1 VALUES (:aaa,:bbb)';
EXECUTE stmt USING @a, @b;
PREPARE stmt FROM 'INSERT INTO t1 VALUES (:"a",:"b")';
EXECUTE stmt USING @a, @b;
PREPARE stmt FROM 'INSERT INTO t1 VALUES (:"aaa",:"bbb")';
EXECUTE stmt USING @a, @b;
PREPARE stmt FROM 'INSERT INTO t1 VALUES (:1,:2)';
EXECUTE stmt USING @a, @b;
PREPARE stmt FROM 'INSERT INTO t1 VALUES (:222,:111)';
EXECUTE stmt USING @a, @b;
PREPARE stmt FROM 'INSERT INTO t1 VALUES (:0,:65535)';
EXECUTE stmt USING @a, @b;
PREPARE stmt FROM 'INSERT INTO t1 VALUES (:65535,:0)';
EXECUTE stmt USING @a, @b;
SELECT * FROM t1;
--let $binlog_file = LAST
source include/show_binlog_events.inc;
DROP TABLE t1;

View File

@ -0,0 +1,196 @@
--source include/not_embedded.inc
--source include/have_binlog_format_statement.inc
--disable_query_log
call mtr.add_suppression("Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT");
#qc_sqlite: reset master; # get rid of previous tests binlog
--enable_query_log
SET sql_mode=ORACLE;
--echo #
--echo # MDEV-10914 ROW data type for stored routine variables
--echo #
CREATE TABLE t1 (a INT, b INT);
DELIMITER $$;
CREATE PROCEDURE p1
AS
rec ROW(a INT,b INT);
BEGIN
rec.a:=100;
rec.b:=200;
INSERT INTO t1 VALUES (rec.a,rec.b);
INSERT INTO t1 VALUES (10, rec=ROW(100,200));
INSERT INTO t1 VALUES (10, ROW(100,200)=rec);
INSERT INTO t1 SELECT 10, 20 FROM DUAL WHERE rec=ROW(100,200);
INSERT INTO t1 SELECT 10, 21 FROM DUAL WHERE ROW(100,200)=rec;
rec.a:=NULL;
INSERT INTO t1 VALUES (11, rec=ROW(100,200));
INSERT INTO t1 VALUES (11, rec=ROW(100,201));
INSERT INTO t1 VALUES (11, ROW(100,200)=rec);
INSERT INTO t1 VALUES (11, ROW(100,201)=rec);
INSERT INTO t1 SELECT 11, 20 FROM DUAL WHERE rec=ROW(100,200);
INSERT INTO t1 SELECT 11, 21 FROM DUAL WHERE ROW(100,200)=rec;
rec.b:=NULL;
INSERT INTO t1 VALUES (12, rec=ROW(100,200));
INSERT INTO t1 VALUES (12, ROW(100,200)=rec);
INSERT INTO t1 SELECT 12, 20 FROM DUAL WHERE rec=ROW(100,200);
INSERT INTO t1 SELECT 12, 21 FROM DUAL WHERE ROW(100,200)=rec;
END;
$$
DELIMITER ;$$
CALL p1();
SELECT * FROM t1;
DROP TABLE t1;
DROP PROCEDURE p1;
--let $binlog_file = LAST
source include/show_binlog_events.inc;
--echo #
--echo # Testing ROW fields in LIMIT
--echo #
FLUSH LOGS;
CREATE TABLE t1 (a INT);
INSERT INTO t1 VALUES (10),(10);
CREATE TABLE t2 (a INT);
DELIMITER $$;
CREATE PROCEDURE p1()
AS
a INT:= 1;
rec ROW(a INT);
BEGIN
rec.a:= 1;
INSERT INTO t2 SELECT 1 FROM t1 LIMIT a;
INSERT INTO t2 SELECT 2 FROM t1 LIMIT rec.a;
END;
$$
DELIMITER ;$$
CALL p1();
DROP TABLE t1,t2;
DROP PROCEDURE p1;
--let $binlog_file = LAST
source include/show_binlog_events.inc;
--echo #
--echo # End of MDEV-10914 ROW data type for stored routine variables
--echo #
--echo #
--echo # MDEV-12133 sql_mode=ORACLE: table%ROWTYPE in variable declarations
--echo #
CREATE TABLE t1 (a INT, b INT);
DELIMITER $$;
CREATE PROCEDURE p1
AS
rec t1%ROWTYPE;
BEGIN
rec.a:=100;
rec.b:=200;
SELECT rec=ROW(100,200) AS true1, ROW(100,200)=rec AS true2;
INSERT INTO t1 VALUES (rec.a,rec.b);
INSERT INTO t1 VALUES (10, rec=ROW(100,200));
INSERT INTO t1 VALUES (10, ROW(100,200)=rec);
INSERT INTO t1 SELECT 10, 20 FROM DUAL WHERE rec=ROW(100,200);
INSERT INTO t1 SELECT 10, 21 FROM DUAL WHERE ROW(100,200)=rec;
rec.a:=NULL;
INSERT INTO t1 VALUES (11, rec=ROW(100,200));
INSERT INTO t1 VALUES (11, rec=ROW(100,201));
INSERT INTO t1 VALUES (11, ROW(100,200)=rec);
INSERT INTO t1 VALUES (11, ROW(100,201)=rec);
INSERT INTO t1 SELECT 11, 20 FROM DUAL WHERE rec=ROW(100,200);
INSERT INTO t1 SELECT 11, 21 FROM DUAL WHERE ROW(100,200)=rec;
rec.b:=NULL;
INSERT INTO t1 VALUES (12, rec=ROW(100,200));
INSERT INTO t1 VALUES (12, ROW(100,200)=rec);
INSERT INTO t1 SELECT 12, 20 FROM DUAL WHERE rec=ROW(100,200);
INSERT INTO t1 SELECT 12, 21 FROM DUAL WHERE ROW(100,200)=rec;
END;
$$
DELIMITER ;$$
CALL p1();
SELECT * FROM t1;
DROP TABLE t1;
DROP PROCEDURE p1;
--let $binlog_file = LAST
source include/show_binlog_events.inc;
--echo #
--echo # MDEV-12291 Allow ROW variables as SELECT INTO targets
--echo #
FLUSH LOGS;
CREATE TABLE t1 (a INT, b VARCHAR(32));
INSERT INTO t1 VALUES (10, 'b10');
CREATE TABLE t2 LIKE t1;
DELIMITER $$;
CREATE PROCEDURE p1
AS
rec1 ROW(a INT, b VARCHAR(32));
BEGIN
SELECT * INTO rec1 FROM t1;
INSERT INTO t2 VALUES (rec1.a, rec1.b);
END;
$$
DELIMITER ;$$
CALL p1();
SELECT * FROM t1;
DROP TABLE t1;
DROP TABLE t2;
DROP PROCEDURE p1;
--let $binlog_file = LAST
source include/show_binlog_events.inc;
FLUSH LOGS;
CREATE TABLE t1 (a INT, b VARCHAR(32));
INSERT INTO t1 VALUES (10, 'b10');
CREATE TABLE t2 LIKE t1;
DELIMITER $$;
CREATE PROCEDURE p1
AS
rec1 t1%ROWTYPE;
BEGIN
SELECT * INTO rec1 FROM t1;
INSERT INTO t2 VALUES (rec1.a, rec1.b);
END;
$$
DELIMITER ;$$
CALL p1();
SELECT * FROM t1;
DROP TABLE t1;
DROP TABLE t2;
DROP PROCEDURE p1;
--let $binlog_file = LAST
source include/show_binlog_events.inc;
FLUSH LOGS;
CREATE TABLE t1 (a INT, b VARCHAR(32));
INSERT INTO t1 VALUES (10, 'b10');
CREATE TABLE t2 LIKE t1;
DELIMITER $$;
CREATE PROCEDURE p1
AS
CURSOR cur1 IS SELECT * FROM t1;
rec1 cur1%ROWTYPE;
BEGIN
SELECT * INTO rec1 FROM t1;
INSERT INTO t2 VALUES (rec1.a, rec1.b);
END;
$$
DELIMITER ;$$
CALL p1();
SELECT * FROM t1;
DROP TABLE t1;
DROP TABLE t2;
DROP PROCEDURE p1;
--let $binlog_file = LAST
source include/show_binlog_events.inc;

View File

@ -0,0 +1,457 @@
SET sql_mode=ORACLE;
--echo #
--echo # sql_mode=ORACLE: Predefined exceptions: TOO_MANY_ROWS, NO_DATA_FOUND, DUP_VAL_ON_INDEX
--echo #
--echo #
--echo # Testing NO_DATA_FOUND and TOO_MANY_ROWS
--echo #
CREATE TABLE t1 (a INT);
INSERT INTO t1 VALUES (10),(20);
DELIMITER $$;
CREATE PROCEDURE p1(lim INT, res OUT VARCHAR)
AS
a INT;
BEGIN
SELECT a INTO a FROM t1 LIMIT lim;
EXCEPTION
WHEN TOO_MANY_ROWS THEN res:='--- too_many_rows cought ---';
WHEN NO_DATA_FOUND THEN res:='--- no_data_found cought ---';
END;
$$
DELIMITER ;$$
SET @res='';
CALL p1(0, @res);
SELECT @res;
CALL p1(2, @res);
SELECT @res;
DROP PROCEDURE p1;
DROP TABLE t1;
--echo #
--echo # Testing DUP_VAL_ON_INDEX
--echo #
CREATE TABLE t1 (a INT PRIMARY KEY);
DELIMITER $$;
CREATE PROCEDURE p1(res OUT VARCHAR)
AS
BEGIN
INSERT INTO t1 VALUES (10);
INSERT INTO t1 VALUES (10);
EXCEPTION
WHEN DUP_VAL_ON_INDEX THEN res:='--- dup_val_on_index cought ---';
END;
$$
DELIMITER ;$$
SET @res='';
CALL p1(@res);
SELECT @res;
SELECT * FROM t1;
DROP PROCEDURE p1;
DROP TABLE t1;
--echo #
--echo # MDEV-10840 sql_mode=ORACLE: RAISE statement for predefined exceptions
--echo #
--echo #
--echo # RAISE outside of an SP context
--echo #
--error ER_SP_COND_MISMATCH
RAISE NO_DATA_FOUND;
--error ER_SP_COND_MISMATCH
RAISE INVALID_CURSOR;
--error ER_SP_COND_MISMATCH
RAISE DUP_VAL_ON_INDEX;
--error ER_SP_COND_MISMATCH
RAISE TOO_MANY_ROWS;
--error ER_RESIGNAL_WITHOUT_ACTIVE_HANDLER
RAISE;
--echo #
--echo # RAISE for an undefinite exception
--echo #
DELIMITER $$;
--error ER_SP_COND_MISMATCH
CREATE PROCEDURE p1
AS
BEGIN
RAISE xxx;
END;
$$
DELIMITER ;$$
--echo #
--echo # RAISE for predefined exceptions
--echo #
DELIMITER $$;
CREATE PROCEDURE p1
AS
BEGIN
RAISE no_data_found;
END;
$$
DELIMITER ;$$
CALL p1();
DROP PROCEDURE p1;
DELIMITER $$;
CREATE PROCEDURE p1
AS
BEGIN
RAISE invalid_cursor;
END;
$$
DELIMITER ;$$
--error ER_SP_CURSOR_NOT_OPEN
CALL p1();
DROP PROCEDURE p1;
DELIMITER $$;
CREATE PROCEDURE p1
AS
BEGIN
RAISE dup_val_on_index;
END;
$$
DELIMITER ;$$
--error ER_DUP_ENTRY
CALL p1();
DROP PROCEDURE p1;
DELIMITER $$;
CREATE PROCEDURE p1
AS
BEGIN
raise too_many_rows;
END;
$$
DELIMITER ;$$
--error ER_TOO_MANY_ROWS
CALL p1();
DROP PROCEDURE p1;
--echo #
--echo # RAISE with no exception name (resignal)
--echo #
DELIMITER $$;
CREATE PROCEDURE p1()
AS
BEGIN
RAISE;
END;
$$
DELIMITER ;$$
--error ER_RESIGNAL_WITHOUT_ACTIVE_HANDLER
CALL p1();
DROP PROCEDURE p1;
CREATE TABLE t1 (a INT);
INSERT INTO t1 VALUES (10),(20);
DELIMITER $$;
CREATE PROCEDURE p1(lim INT)
AS
a INT;
BEGIN
SELECT a INTO a FROM t1 LIMIT lim;
EXCEPTION
WHEN TOO_MANY_ROWS THEN RAISE;
WHEN NO_DATA_FOUND THEN RAISE;
END;
$$
DELIMITER ;$$
CALL p1(0);
--error ER_TOO_MANY_ROWS
CALL p1(2);
DROP PROCEDURE p1;
DROP TABLE t1;
CREATE TABLE t1 (a INT);
INSERT INTO t1 VALUES (10),(20);
DELIMITER $$;
CREATE PROCEDURE p1(lim INT)
AS
a INT;
BEGIN
SELECT a INTO a FROM t1 LIMIT lim;
EXCEPTION
WHEN OTHERS THEN RAISE;
END;
$$
DELIMITER ;$$
CALL p1(0);
--error ER_TOO_MANY_ROWS
CALL p1(2);
DROP PROCEDURE p1;
DROP TABLE t1;
CREATE TABLE t1 (a INT);
INSERT INTO t1 VALUES (10),(20);
DELIMITER $$;
CREATE PROCEDURE p1()
AS
a INT;
CURSOR c IS SELECT a FROM t1;
BEGIN
FETCH c INTO a;
EXCEPTION
WHEN INVALID_CURSOR THEN RAISE;
END;
$$
DELIMITER ;$$
--error ER_SP_CURSOR_NOT_OPEN
CALL p1();
DROP PROCEDURE p1;
DROP TABLE t1;
CREATE TABLE t1 (a INT);
INSERT INTO t1 VALUES (10),(20);
DELIMITER $$;
CREATE PROCEDURE p1()
AS
a INT;
CURSOR c IS SELECT a FROM t1;
BEGIN
FETCH c INTO a;
EXCEPTION
WHEN OTHERS THEN RAISE;
END;
$$
DELIMITER ;$$
--error ER_SP_CURSOR_NOT_OPEN
CALL p1();
DROP PROCEDURE p1;
DROP TABLE t1;
--echo #
--echo # Testing that warning-alike errors are caught by OTHERS
--echo #
CREATE TABLE t1 (a INT);
DELIMITER $$;
CREATE FUNCTION f1 RETURN VARCHAR
AS
a INT:=10;
BEGIN
SELECT a INTO a FROM t1;
RETURN 'OK';
EXCEPTION
WHEN OTHERS THEN RETURN 'Exception';
END;
$$
DELIMITER ;$$
SELECT f1() FROM DUAL;
DROP FUNCTION f1;
DROP TABLE t1;
--echo #
--echo # End of MDEV-10840 sql_mode=ORACLE: RAISE statement for predefined exceptions
--echo #
--echo #
--echo # MDEV-10587 sql_mode=ORACLE: User defined exceptions
--echo #
--echo #
--echo # Checking that duplicate WHEN clause is not allowed
--echo #
DELIMITER $$;
--error ER_SP_DUP_HANDLER
CREATE FUNCTION f1() RETURN VARCHAR
AS
e EXCEPTION;
BEGIN
RETURN 'Got no exceptions';
EXCEPTION
WHEN e THEN RETURN 'Got exception e';
WHEN e THEN RETURN 'Got exception e';
END;
$$
DELIMITER ;$$
--echo #
--echo # Checking that raised user exceptions are further caught by name
--echo #
DELIMITER $$;
CREATE FUNCTION f1(c VARCHAR) RETURN VARCHAR
AS
e EXCEPTION;
f EXCEPTION;
BEGIN
IF c = 'e' THEN RAISE e; END IF;
IF c = 'f' THEN RAISE f; END IF;
RETURN 'Got no exceptions';
EXCEPTION
WHEN e THEN RETURN 'Got exception e';
END;
$$
DELIMITER ;$$
SELECT f1('');
SELECT f1('e');
--error ER_SIGNAL_EXCEPTION
SELECT f1('f');
DROP FUNCTION f1;
--echo #
--echo # Checking that raised user exceptions are further caught by OTHERS
--echo #
DELIMITER $$;
CREATE FUNCTION f1(c VARCHAR) RETURN VARCHAR
AS
e EXCEPTION;
f EXCEPTION;
BEGIN
IF c = 'e' THEN RAISE e; END IF;
IF c = 'f' THEN RAISE f; END IF;
RETURN 'Got no exceptions';
EXCEPTION
WHEN OTHERS THEN RETURN 'Got some exception';
END;
$$
DELIMITER ;$$
SELECT f1('');
SELECT f1('e');
SELECT f1('f');
DROP FUNCTION f1;
--echo #
--echo # Checking that 'WHEN e .. WHEN f' does not produce ER_SP_DUP_HANDLER
--echo #
DELIMITER $$;
CREATE FUNCTION f1(c VARCHAR) RETURN VARCHAR
AS
e EXCEPTION;
f EXCEPTION;
a VARCHAR(64):='';
BEGIN
BEGIN
IF c = 'e' THEN RAISE e; END IF;
IF c = 'f' THEN RAISE f; END IF;
EXCEPTION
WHEN e THEN BEGIN a:='Got EXCEPTION1/e; '; RAISE e; END;
WHEN f THEN BEGIN a:='Got EXCEPTION1/f; '; RAISE f; END;
END;
RETURN 'Got no exceptions';
EXCEPTION
WHEN OTHERS THEN RETURN a || 'Got EXCEPTION2/OTHERS;';
END;
$$
DELIMITER ;$$
SELECT f1('');
SELECT f1('e');
SELECT f1('f');
DROP FUNCTION f1;
--echo #
--echo # Checking that resignaled user exceptions are further caught by name
--echo #
DELIMITER $$;
CREATE FUNCTION f1(c VARCHAR) RETURN VARCHAR
AS
e EXCEPTION;
f EXCEPTION;
a VARCHAR(64):='';
BEGIN
BEGIN
IF c = 'e' THEN RAISE e; END IF;
IF c = 'f' THEN RAISE f; END IF;
EXCEPTION
WHEN e THEN BEGIN a:='Got EXCEPTION1/e; '; RAISE; END;
WHEN f THEN BEGIN a:='Got EXCEPTION1/f; '; RAISE; END;
END;
RETURN 'Got no exceptions';
EXCEPTION
WHEN e THEN RETURN a || 'Got EXCEPTION2/e;';
END;
$$
DELIMITER ;$$
SELECT f1('');
SELECT f1('e');
--error ER_SIGNAL_EXCEPTION
SELECT f1('f');
DROP FUNCTION f1;
--echo #
--echo # Checking that resignaled user exceptions are further caught by OTHERS
--echo #
DELIMITER $$;
CREATE FUNCTION f1(c VARCHAR) RETURN VARCHAR
AS
e EXCEPTION;
f EXCEPTION;
a VARCHAR(64):='';
BEGIN
BEGIN
IF c = 'e' THEN RAISE e; END IF;
IF c = 'f' THEN RAISE f; END IF;
EXCEPTION
WHEN e THEN BEGIN a:='Got EXCEPTION1/e; '; RAISE; END;
WHEN f THEN BEGIN a:='Got EXCEPTION1/f; '; RAISE; END;
END;
RETURN 'Got no exceptions';
EXCEPTION
WHEN OTHERS THEN RETURN a || 'Got EXCEPTION2/OTHERS;';
END;
$$
DELIMITER ;$$
SELECT f1('');
SELECT f1('e');
SELECT f1('f');
DROP FUNCTION f1;
--echo #
--echo # End of MDEV-10587 sql_mode=ORACLE: User defined exceptions
--echo #
--echo #
--echo # MDEV-12088 sql_mode=ORACLE: Do not require BEGIN..END in multi-statement exception handlers in THEN clause
--echo #
CREATE TABLE t1 (a INT PRIMARY KEY);
INSERT INTO t1 VALUES (10),(20),(30);
DELIMITER $$;
CREATE PROCEDURE p1(a INT) AS
BEGIN
INSERT INTO t1 (a) VALUES (a);
EXCEPTION
WHEN DUP_VAL_ON_INDEX THEN
a:= a+1;
INSERT INTO t1 VALUES (a);
WHEN OTHERS THEN
NULL;
NULL;
END;
$$
DELIMITER ;$$
CALL p1(30);
SELECT * FROM t1;
DROP PROCEDURE p1;
DROP TABLE t1;

View File

@ -0,0 +1,9 @@
#
# Testing CASE and its abbreviations
#
SET sql_mode=ORACLE;
SELECT NVL(NULL, 'a'), NVL('a', 'b');
SELECT NVL2(NULL, 'a', 'b'), NVL2('a', 'b', 'c');

View File

@ -0,0 +1,116 @@
#
# Testing CONCAT with null values
#
SET sql_mode=ORACLE;
EXPLAIN EXTENDED SELECT 'a'||'b'||'c';
EXPLAIN EXTENDED SELECT CONCAT('a'||'b'||'c');
SELECT '' || '';
SELECT '' || 'b';
SELECT '' || NULL;
SELECT 'a' || '';
SELECT 'a' || 'b';
SELECT 'a' || NULL;
SELECT NULL || '';
SELECT NULL || 'b';
SELECT NULL || NULL;
SELECT '' || '' || '';
SELECT '' || '' || 'c';
SELECT '' || '' || NULL;
SELECT '' || 'b' || '';
SELECT '' || 'b' || 'c';
SELECT '' || 'b' || NULL;
SELECT '' || NULL || '';
SELECT '' || NULL || 'c';
SELECT '' || NULL || NULL;
SELECT 'a' || '' || '';
SELECT 'a' || '' || 'c';
SELECT 'a' || '' || NULL;
SELECT 'a' || 'b' || '';
SELECT 'a' || 'b' || 'c';
SELECT 'a' || 'b' || NULL;
SELECT 'a' || NULL || '';
SELECT 'a' || NULL || 'c';
SELECT 'a' || NULL || NULL;
SELECT NULL || '' || '';
SELECT NULL || '' || 'c';
SELECT NULL || '' || NULL;
SELECT NULL || 'b' || '';
SELECT NULL || 'b' || 'c';
SELECT NULL || 'b' || NULL;
SELECT NULL || NULL || '';
SELECT NULL || NULL || 'c';
SELECT NULL || NULL || NULL;
CREATE TABLE t1 (a VARCHAR(10), b VARCHAR(10), c VARCHAR(10));
INSERT INTO t1 VALUES ('', '', '');
INSERT INTO t1 VALUES ('', '', 'c');
INSERT INTO t1 VALUES ('', '', NULL);
INSERT INTO t1 VALUES ('', 'b', '');
INSERT INTO t1 VALUES ('', 'b', 'c');
INSERT INTO t1 VALUES ('', 'b', NULL);
INSERT INTO t1 VALUES ('', NULL, '');
INSERT INTO t1 VALUES ('', NULL, 'c');
INSERT INTO t1 VALUES ('', NULL, NULL);
INSERT INTO t1 VALUES ('a', '', '');
INSERT INTO t1 VALUES ('a', '', 'c');
INSERT INTO t1 VALUES ('a', '', NULL);
INSERT INTO t1 VALUES ('a', 'b', '');
INSERT INTO t1 VALUES ('a', 'b', 'c');
INSERT INTO t1 VALUES ('a', 'b', NULL);
INSERT INTO t1 VALUES ('a', NULL, '');
INSERT INTO t1 VALUES ('a', NULL, 'c');
INSERT INTO t1 VALUES ('a', NULL, NULL);
INSERT INTO t1 VALUES (NULL, '', '');
INSERT INTO t1 VALUES (NULL, '', 'c');
INSERT INTO t1 VALUES (NULL, '', NULL);
INSERT INTO t1 VALUES (NULL, 'b', '');
INSERT INTO t1 VALUES (NULL, 'b', 'c');
INSERT INTO t1 VALUES (NULL, 'b', NULL);
INSERT INTO t1 VALUES (NULL, NULL, '');
INSERT INTO t1 VALUES (NULL, NULL, 'c');
INSERT INTO t1 VALUES (NULL, NULL, NULL);
SELECT LENGTH(a||b||c), a||b||c FROM t1 ORDER BY a,b,c;
SELECT LENGTH(CONCAT(a||b||c)), CONCAT(a||b||c) FROM t1 ORDER BY a,b,c;
DROP TABLE t1;
--echo #
--echo # MDEV-12478 CONCAT function inside view casts values incorrectly with Oracle sql_mode
--echo #
SET sql_mode=ORACLE;
CREATE VIEW v1 AS SELECT 'foo'||NULL||'bar' AS test;
SHOW CREATE VIEW v1;
SELECT * FROM v1;
SET sql_mode=DEFAULT;
SHOW CREATE VIEW v1;
SELECT * FROM v1;
DROP VIEW v1;
SET sql_mode=DEFAULT;
CREATE VIEW v1 AS SELECT CONCAT('foo',NULL,'bar') AS test;
SHOW CREATE VIEW v1;
SELECT * FROM v1;
SET sql_mode=ORACLE;
SHOW CREATE VIEW v1;
SELECT * FROM v1;
DROP VIEW v1;
SET sql_mode=DEFAULT;
CREATE VIEW v1 AS SELECT '0'||'1' AS test;
SHOW CREATE VIEW v1;
SELECT * FROM v1;
SET sql_mode=ORACLE;
SHOW CREATE VIEW v1;
SELECT * FROM v1;
DROP VIEW v1;

View File

@ -0,0 +1,21 @@
SET sql_mode=ORACLE;
--error ER_PARSE_ERROR
SELECT DECODE(10);
--error ER_PARSE_ERROR
SELECT DECODE(10,10);
SELECT DECODE(10,10,'x10');
SELECT DECODE(11,10,'x10');
SELECT DECODE(10,10,'x10','def');
SELECT DECODE(11,10,'x10','def');
SELECT DECODE(10,10,'x10',11,'x11','def');
SELECT DECODE(11,10,'x10',11,'x11','def');
SELECT DECODE(12,10,'x10',11,'x11','def');
EXPLAIN EXTENDED SELECT DECODE(12,10,'x10',11,'x11','def');
CREATE TABLE decode (decode int);
DROP TABLE decode;

View File

@ -0,0 +1,20 @@
SET sql_mode=ORACLE;
--echo #
--echo # MDEV-12783 sql_mode=ORACLE: Functions LENGTH() and LENGTHB()
--echo #
#
# Testing LENGTH / LENGTHB
#
# LENGTH : return the length of char
# LENGTHB : return the length of byte
SELECT LENGTH(null), LENGTH('a'), LENGTH(123);
SELECT LENGTHB(null), LENGTHB('a'), LENGTHB(123);
# qc_sqlite: SELECT LENGTH(_utf8 0xC39F), LENGTH(CHAR(14844588 USING utf8));
# Sqlite3 error: SQL logic error or missing database, near "0xC39F": syntax error
# qc_sqlite: SELECT LENGTHB(_utf8 0xC39F), LENGTHB(CHAR(14844588 USING utf8));
# Sqlite3 error: SQL logic error or missing database, near "0xC39F": syntax error
EXPLAIN EXTENDED SELECT LENGTH('a'), LENGTHB('a');

View File

@ -0,0 +1,346 @@
SET sql_mode=ORACLE;
--echo #
--echo # MDEV-10578 sql_mode=ORACLE: SP control functions SQLCODE, SQLERRM
--echo #
--echo #
--echo # Using SQLCODE and SQLERRM outside of an SP
--echo #
--error ER_BAD_FIELD_ERROR
SELECT SQLCODE;
--error ER_BAD_FIELD_ERROR
SELECT SQLERRM;
CREATE TABLE t1 (SQLCODE INT, SQLERRM VARCHAR(10));
INSERT INTO t1 VALUES (10, 'test');
SELECT SQLCODE, SQLERRM FROM t1;
DROP TABLE t1;
--echo #
--echo # Normal SQLCODE and SQLERRM usage
--echo #
DELIMITER $$;
CREATE PROCEDURE p1(stmt VARCHAR)
AS
BEGIN
EXECUTE IMMEDIATE stmt;
SELECT 'Error1: ' || SQLCODE || ' ' || SQLERRM;
EXCEPTION
WHEN OTHERS THEN
SELECT 'Error2: ' || SQLCODE || ' ' || SQLERRM;
END;
$$
DELIMITER ;$$
CALL p1('SELECT 1');
CALL p1('xxx');
CALL p1('SELECT 1');
DROP PROCEDURE p1;
--echo #
--echo # SQLCODE and SQLERRM hidden by local variables
--echo #
DELIMITER $$;
CREATE PROCEDURE p1()
AS
sqlcode INT:= 10;
sqlerrm VARCHAR(64) := 'test';
BEGIN
SELECT 'Error: ' || SQLCODE || ' ' || SQLERRM;
END;
$$
DELIMITER ;$$
CALL p1;
DROP PROCEDURE p1;
DELIMITER $$;
CREATE PROCEDURE p1()
AS
sqlcode INT;
sqlerrm VARCHAR(64);
BEGIN
SQLCODE:= 10;
sqlerrm:= 'test';
SELECT 'Error: ' || SQLCODE || ' ' || SQLERRM;
END;
$$
DELIMITER ;$$
CALL p1;
DROP PROCEDURE p1;
--echo #
--echo # SQLCODE and SQLERRM hidden by parameters
--echo #
DELIMITER $$;
CREATE PROCEDURE p1(sqlcode INT, sqlerrm VARCHAR)
AS
BEGIN
SELECT 'Error: ' || SQLCODE || ' ' || SQLERRM;
END;
$$
DELIMITER ;$$
CALL p1(10, 'test');
DROP PROCEDURE p1;
--echo #
--echo # SQLCODE and SQLERRM in CREATE..SELECT
--echo #
DELIMITER $$;
CREATE PROCEDURE p1
AS
BEGIN
CREATE TABLE t1 AS SELECT SQLCODE, SQLERRM;
END;
$$
DELIMITER ;$$
CALL p1;
SHOW CREATE TABLE t1;
DROP TABLE t1;
DROP PROCEDURE p1;
--echo #
--echo # SQLCODE and SQLERRM in EXPLAIN EXTENDED SELECT
--echo #
DELIMITER $$;
CREATE PROCEDURE p1
AS
BEGIN
EXPLAIN EXTENDED SELECT SQLCode, SQLErrm;
END;
$$
DELIMITER ;$$
CALL p1;
DROP PROCEDURE p1;
--echo #
--echo # Warning-alike errors in stored functions
--echo #
CREATE TABLE t1 (a INT);
DELIMITER $$;
CREATE FUNCTION f1 RETURN VARCHAR
AS
a INT;
BEGIN
SELECT a INTO a FROM t1;
RETURN 'No exception ' || SQLCODE || ' ' || SQLERRM;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RETURN 'Exception ' || SQLCODE || ' ' || SQLERRM;
END;
$$
DELIMITER ;$$
SELECT f1() FROM DUAL;
DROP FUNCTION f1;
DROP TABLE t1;
CREATE TABLE t1 (a INT);
DELIMITER $$;
CREATE FUNCTION f1 RETURN VARCHAR
AS
a INT;
BEGIN
SELECT a INTO a FROM t1;
RETURN 'No exception ' || SQLCODE || ' ' || SQLERRM;
EXCEPTION
WHEN OTHERS THEN
RETURN 'Exception ' || SQLCODE || ' ' || SQLERRM;
END;
$$
DELIMITER ;$$
SELECT f1() FROM DUAL;
DROP FUNCTION f1;
DROP TABLE t1;
--echo #
--echo # Warning-alike errors in stored procedures
--echo #
CREATE TABLE t1 (a INT);
DELIMITER $$;
CREATE PROCEDURE p1(res OUT VARCHAR)
AS
a INT;
BEGIN
SELECT a INTO a FROM t1;
res:= 'No exception ' || SQLCODE || ' ' || SQLERRM;
EXCEPTION
WHEN NO_DATA_FOUND THEN
res:= 'Exception ' || SQLCODE || ' ' || SQLERRM;
END;
$$
DELIMITER ;$$
CALL p1(@a);
SELECT @a;
DROP PROCEDURE p1;
DROP TABLE t1;
CREATE TABLE t1 (a INT);
DELIMITER $$;
CREATE PROCEDURE p1(res OUT VARCHAR)
AS
a INT;
BEGIN
SELECT a INTO a FROM t1;
res:= 'No exception ' || SQLCODE || ' ' || SQLERRM;
EXCEPTION
WHEN OTHERS THEN
res:= 'Exception ' || SQLCODE || ' ' || SQLERRM;
END;
$$
DELIMITER ;$$
CALL p1(@a);
SELECT @a;
DROP PROCEDURE p1;
DROP TABLE t1;
--echo #
--echo # SQLCODE and SQLERRM are cleared on RETURN
--echo #
CREATE TABLE t1 (a INT);
DELIMITER $$;
CREATE FUNCTION f1 RETURN VARCHAR
AS
a INT:=10;
BEGIN
SELECT a INTO a FROM t1;
RETURN 'Value=' || a;
EXCEPTION
WHEN NO_DATA_FOUND THEN RETURN 'Exception|' || SQLCODE || ' ' || SQLERRM;
END;
$$
CREATE FUNCTION f2 RETURN VARCHAR
AS
a VARCHAR(128);
BEGIN
RETURN f1() || '|' || SQLCODE || ' ' || SQLERRM;
END;
$$
DELIMITER ;$$
SELECT f1() FROM DUAL;
SELECT f2() FROM DUAL;
DROP TABLE t1;
DROP FUNCTION f2;
DROP FUNCTION f1;
CREATE TABLE t1 (a INT);
DELIMITER $$;
CREATE FUNCTION f1 RETURN VARCHAR
AS
a INT:=10;
BEGIN
SELECT a INTO a FROM t1;
RETURN 'Value=' || a;
EXCEPTION
WHEN OTHERS THEN RETURN 'Exception|' || SQLCODE || ' ' || SQLERRM;
END;
$$
CREATE FUNCTION f2 RETURN VARCHAR
AS
a VARCHAR(128);
BEGIN
RETURN f1() || '|' || SQLCODE || ' ' || SQLERRM;
END;
$$
DELIMITER ;$$
SELECT f1() FROM DUAL;
SELECT f2() FROM DUAL;
DROP TABLE t1;
DROP FUNCTION f2;
DROP FUNCTION f1;
--echo #
--echo # SQLCODE and SQLERRM are cleared on a return from a PROCEDURE
--echo #
CREATE TABLE t1 (a INT);
DELIMITER $$;
CREATE PROCEDURE p1(res OUT VARCHAR)
AS
a INT:=10;
BEGIN
SELECT a INTO a FROM t1;
res:='Value=' || a;
EXCEPTION
WHEN NO_DATA_FOUND THEN res:='Exception|' || SQLCODE || ' ' || SQLERRM;
END;
$$
CREATE FUNCTION f2 RETURN VARCHAR
AS
res VARCHAR(128);
BEGIN
CALL p1(res);
RETURN res || '|' || SQLCODE || ' ' || SQLERRM;
END;
$$
DELIMITER ;$$
SELECT f2() FROM DUAL;
DROP FUNCTION f2;
DROP PROCEDURE p1;
DROP TABLE t1;
CREATE TABLE t1 (a INT);
DELIMITER $$;
CREATE PROCEDURE p1(res OUT VARCHAR)
AS
a INT:=10;
BEGIN
SELECT a INTO a FROM t1;
res:='Value=' || a;
EXCEPTION
WHEN OTHERS THEN res:='Exception|' || SQLCODE || ' ' || SQLERRM;
END;
$$
CREATE FUNCTION f2 RETURN VARCHAR
AS
res VARCHAR(128);
BEGIN
CALL p1(res);
RETURN res || '|' || SQLCODE || ' ' || SQLERRM;
END;
$$
DELIMITER ;$$
SELECT f2() FROM DUAL;
DROP FUNCTION f2;
DROP PROCEDURE p1;
DROP TABLE t1;
--echo #
--echo # End of MDEV-10578 sql_mode=ORACLE: SP control functions SQLCODE, SQLERRM
--echo #
--echo #
--echo # MDEV-12854 Synchronize CREATE..SELECT data type and result set metadata data type for INT functions
--echo #
--enable_metadata
--disable_ps_protocol
DELIMITER $$;
BEGIN
SELECT SQLCODE;
END
$$
DELIMITER ;$$
--enable_ps_protocol
--disable_metadata

View File

@ -0,0 +1,10 @@
SET sql_mode=ORACLE;
--echo #
--echo # MDEV-12086 sql_mode=ORACLE: allow SELECT UNIQUE as a synonym for SELECT DISTINCT
--echo #
CREATE TABLE t1 (a INT);
INSERT INTO t1 VALUES (10),(20),(20),(30),(30),(30);
SELECT UNIQUE a FROM t1;
DROP TABLE t1;

View File

@ -0,0 +1,266 @@
SET sql_mode=ORACLE;
--echo #
--echo # MDEV-10801 sql_mode: dynamic SQL placeholders
--echo #
SET @a=10, @b=20;
PREPARE stmt FROM 'SELECT ?,?';
EXECUTE stmt USING @a, @b;
PREPARE stmt FROM 'SELECT :a,:b';
EXECUTE stmt USING @a, @b;
PREPARE stmt FROM 'SELECT :aaa,:bbb';
EXECUTE stmt USING @a, @b;
#qc_mysqlembedded: PREPARE stmt FROM 'SELECT :"a",:"b"';
EXECUTE stmt USING @a, @b;
#qc_mysqlembedded: PREPARE stmt FROM 'SELECT :"aaa",:"bbb"';
EXECUTE stmt USING @a, @b;
PREPARE stmt FROM 'SELECT :1,:2';
EXECUTE stmt USING @a, @b;
PREPARE stmt FROM 'SELECT :222,:111';
EXECUTE stmt USING @a, @b;
PREPARE stmt FROM 'SELECT :0,:65535';
EXECUTE stmt USING @a, @b;
PREPARE stmt FROM 'SELECT :65535,:0';
EXECUTE stmt USING @a, @b;
--echo #
--echo # MDEV-10709 Expressions as parameters to Dynamic SQL
--echo #
--echo #
--echo # Testing disallowed expressions in USING
--echo #
PREPARE stmt FROM 'SELECT :1 FROM DUAL';
--error ER_PARSE_ERROR
EXECUTE stmt USING (SELECT 1);
DEALLOCATE PREPARE stmt;
DELIMITER $$;
CREATE FUNCTION f1() RETURN VARCHAR
AS
BEGIN
RETURN 'test';
END;
$$
DELIMITER ;$$
PREPARE stmt FROM 'SELECT ? FROM DUAL';
--error ER_SUBQUERIES_NOT_SUPPORTED
EXECUTE stmt USING f1();
DEALLOCATE PREPARE stmt;
DROP FUNCTION f1;
--echo #
--echo # Using a user variable as a EXECUTE..USING out parameter
--echo #
DELIMITER /;
CREATE PROCEDURE p1(a OUT INT)
AS
BEGIN
a:= 10;
END;
/
DELIMITER ;/
SET @a=1;
CALL p1(@a);
SELECT @a;
SET @a=2;
PREPARE stmt FROM 'CALL p1(?)';
EXECUTE stmt USING @a;
SELECT @a;
DROP PROCEDURE p1;
--echo #
--echo # Using an SP variable as a EXECUTE..USING out parameter
--echo #
DELIMITER /;
CREATE PROCEDURE p1 (a OUT INT)
AS
BEGIN
a:=10;
END;
/
CREATE PROCEDURE p2 (a OUT INT)
AS
BEGIN
PREPARE stmt FROM 'CALL p1(?)';
EXECUTE stmt USING a;
END;
/
DELIMITER ;/
SET @a= 1;
CALL p2(@a);
SELECT @a;
DROP PROCEDURE p2;
DROP PROCEDURE p1;
--echo #
--echo # Using a trigger field as a EXECUTE..USING out parameter
--echo #
DELIMITER /;
CREATE PROCEDURE p1 (a OUT INT)
AS
BEGIN
a:= 10;
END;
/
DELIMITER ;/
CREATE TABLE t1 (a INT);
CREATE TRIGGER tr1 BEFORE INSERT ON t1 FOR EACH ROW CALL p1(:NEW.a);
INSERT INTO t1 VALUES (1);
SELECT * FROM t1;
DROP TABLE t1;
DROP PROCEDURE p1;
--echo #
--echo # Testing re-prepare on a table metadata update between PREPARE and EXECUTE
--echo #
CREATE TABLE t1 (a INT);
DELIMITER /;
CREATE PROCEDURE p1(a IN INT)
AS
BEGIN
INSERT INTO t1 VALUES (a);
END;
/
DELIMITER ;/
PREPARE stmt FROM 'CALL p1(?)';
EXECUTE stmt USING 10;
SELECT * FROM t1;
CREATE TRIGGER tr1 BEFORE INSERT ON t1 FOR EACH ROW NEW.a:=NEW.a+1;
EXECUTE stmt USING 20;
SELECT * FROM t1;
DEALLOCATE PREPARE stmt;
DROP PROCEDURE p1;
DROP TABLE t1;
--echo #
--echo # End of MDEV-10709 Expressions as parameters to Dynamic SQL
--echo #
--echo #
--echo # MDEV-10585 EXECUTE IMMEDIATE statement
--echo #
--echo #
--echo # Testing disallowed expressions in USING
--echo #
--error ER_PARSE_ERROR
EXECUTE IMMEDIATE 'SELECT :1 FROM DUAL' USING (SELECT 1);
DELIMITER $$;
CREATE FUNCTION f1() RETURN VARCHAR
AS
BEGIN
RETURN 'test';
END;
$$
DELIMITER ;$$
--error ER_SUBQUERIES_NOT_SUPPORTED
EXECUTE IMMEDIATE 'SELECT ? FROM DUAL' USING f1();
DROP FUNCTION f1;
--echo #
--echo # Testing simple expressions
--echo #
EXECUTE IMMEDIATE 'SELECT :1 FROM DUAL' USING 10;
--echo #
--echo # MDEV-10866 Extend PREPARE and EXECUTE IMMEDIATE to understand expressions
--echo #
--echo #
--echo # Testing erroneous and diallowed prepare source
--echo #
--error ER_CANT_AGGREGATE_2COLLATIONS
EXECUTE IMMEDIATE _latin1'SELECT 1 AS c FROM ' || _latin2 'DUAL';
--error ER_CANT_AGGREGATE_2COLLATIONS
PREPARE stmt FROM _latin1'SELECT 1 AS c FROM ' || _latin2 'DUAL';
--error ER_PARSE_ERROR
EXECUTE IMMEDIATE (SELECT 'SELECT 1');
--error ER_PARSE_ERROR
PREPARE stmt FROM (SELECT 'SELECT 1');
--error ER_BAD_FIELD_ERROR
EXECUTE IMMEDIATE a;
--error ER_BAD_FIELD_ERROR
PREPARE stmt FROM a;
--error ER_PARSE_ERROR
EXECUTE IMMEDIATE NULL;
--error ER_PARSE_ERROR
PREPARE stmt FROM NULL;
--error ER_PARSE_ERROR
EXECUTE IMMEDIATE COALESCE(NULL);
--error ER_PARSE_ERROR
PREPARE stmt FROM COALESCE(NULL);
DELIMITER $$;
CREATE FUNCTION f1() RETURN VARCHAR
AS
BEGIN
RETURN 't1';
END;
$$
DELIMITER ;$$
--error ER_SUBQUERIES_NOT_SUPPORTED
EXECUTE IMMEDIATE f1();
--error ER_SUBQUERIES_NOT_SUPPORTED
PREPARE stmt FROM f1();
DROP FUNCTION f1;
--echo #
--echo # Testing user variables in prepare source
--echo #
SET @table_name='DUAL';
EXECUTE IMMEDIATE 'SELECT 1 AS a FROM ' || @table_name;
#qc: PREPARE stmt FROM 'SELECT 1 AS a FROM ' || @table_name;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
--echo #
--echo # Testing SP parameters and variables in prepare source
--echo #
DELIMITER $$;
CREATE PROCEDURE p1(table_name VARCHAR)
AS
BEGIN
EXECUTE IMMEDIATE 'SELECT 1 AS c FROM '|| table_name;
END;
$$
DELIMITER ;$$
CALL p1('DUAL');
DROP PROCEDURE p1;
DELIMITER $$;
CREATE PROCEDURE p1()
AS
table_name VARCHAR(64):='DUAL';
BEGIN
EXECUTE IMMEDIATE 'SELECT 1 AS c FROM ' || table_name;
END;
$$
DELIMITER ;$$
CALL p1();
DROP PROCEDURE p1;
--echo #
--echo # End of MDEV-10866 Extend PREPARE and EXECUTE IMMEDIATE to understand expressions
--echo #

View File

@ -0,0 +1,43 @@
--source include/have_binlog_format_row.inc
SET sql_mode=ORACLE;
CREATE SEQUENCE s1;
SHOW CREATE SEQUENCE s1;
SELECT s1.currval;
SELECT s1.nextval;
SELECT s1.nextval;
SELECT s1.nextval;
EXPLAIN EXTENDED SELECT s1.nextval;
SELECT nextval(s1);
EXPLAIN EXTENDED SELECT s1.currval;
SELECT lastval(s1);
DROP SEQUENCE s1;
CREATE SEQUENCE s1;
CREATE VIEW v1 AS SELECT s1.nextval AS a;
SELECT VIEW_DEFINITION FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_NAME='v1';
SELECT * FROM v1;
SHOW CREATE VIEW v1;
DROP VIEW v1;
DROP SEQUENCE s1;
CREATE SEQUENCE s1;
CREATE VIEW v1 AS SELECT s1.currval AS a;
SELECT VIEW_DEFINITION FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_NAME='v1';
SELECT * FROM v1;
SHOW CREATE VIEW v1;
DROP VIEW v1;
DROP SEQUENCE s1;
--echo #
--echo # MDEV-12533 sql_mode=ORACLE: Add support for database qualified sequence names in NEXTVAL and CURRVAL
--echo #
CREATE SEQUENCE s1;
SELECT test.s1.nextval;
SELECT test.s1.currval;
SELECT .s1.nextval;
SELECT .s1.currval;
DROP SEQUENCE s1;

View File

@ -0,0 +1,244 @@
--source include/have_innodb.inc
SET sql_mode=ORACLE;
--echo #
--echo # MDEV-10655 Anonymous blocks
--echo #
--echo # Testing BEGIN NOT ATOMIC with no declarations
DELIMITER /;
BEGIN NOT ATOMIC
SELECT 1 AS a;
END
/
DELIMITER ;/
--echo # Testing BEGIN NOT ATOMIC with declarations
--echo # DECLARE starts a new block and thus must be followed by BEGIN .. END
DELIMITER /;
BEGIN NOT ATOMIC
DECLARE
i INT DEFAULT 5;
x INT DEFAULT 10;
BEGIN
<<label>>
WHILE i > 3 LOOP
i:= i - 1;
SELECT i;
END LOOP label;
END;
END
/
DELIMITER ;/
--echo # Anonymous blocks with no declarations and no exceptions
DELIMITER $$;
BEGIN
SELECT 1 AS a;
END
$$
DELIMITER ;$$
SET AUTOCOMMIT=OFF;
CREATE TABLE t1 (a INT) ENGINE=InnoDB;
INSERT INTO t1 VALUES (10);
DELIMITER $$;
BEGIN
INSERT INTO t1 VALUES(20);
INSERT INTO t1 VALUES(30);
ROLLBACK;
END;
$$
DELIMITER ;$$
SELECT * FROM t1;
DROP TABLE t1;
SET AUTOCOMMIT=DEFAULT;
SET AUTOCOMMIT=OFF;
CREATE TABLE t1 (a INT) ENGINE=InnoDB;
INSERT INTO t1 VALUES (10);
DELIMITER $$;
BEGIN
INSERT INTO t1 VALUES(20);
INSERT INTO t1 VALUES(30);
END;
$$
DELIMITER ;$$
ROLLBACK;
SELECT * FROM t1;
DROP TABLE t1;
SET AUTOCOMMIT=DEFAULT;
SET AUTOCOMMIT=OFF;
CREATE TABLE t1 (a INT) ENGINE=InnoDB;
INSERT INTO t1 VALUES (10);
DELIMITER $$;
BEGIN
INSERT INTO t1 VALUES(20);
INSERT INTO t1 VALUES(30);
COMMIT;
END;
$$
DELIMITER ;$$
SELECT * FROM t1;
DROP TABLE t1;
SET AUTOCOMMIT=DEFAULT;
SET AUTOCOMMIT=OFF;
CREATE TABLE t1 (a INT) ENGINE=InnoDB;
INSERT INTO t1 VALUES (10);
DELIMITER $$;
BEGIN
INSERT INTO t1 VALUES(20);
INSERT INTO t1 VALUES(30);
END;
$$
DELIMITER ;$$
COMMIT;
SELECT * FROM t1;
DROP TABLE t1;
SET AUTOCOMMIT=DEFAULT;
SET AUTOCOMMIT=OFF;
CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY) ENGINE=InnoDB;
INSERT INTO t1 VALUES (10);
DELIMITER $$;
--error ER_DUP_ENTRY
BEGIN
INSERT INTO t1 VALUES(20);
INSERT INTO t1 VALUES(20);
END;
$$
DELIMITER ;$$
COMMIT;
SELECT * FROM t1;
DROP TABLE t1;
SET AUTOCOMMIT=DEFAULT;
--echo # Anonymous blocks with no declarations, with exceptions
SET AUTOCOMMIT=OFF;
CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY) ENGINE=InnoDB;
INSERT INTO t1 VALUES (10);
DELIMITER $$;
BEGIN
INSERT INTO t1 VALUES(20);
INSERT INTO t1 VALUES(20);
EXCEPTION
WHEN DUP_VAL_ON_INDEX THEN NULL;
END;
$$
DELIMITER ;$$
COMMIT;
SELECT * FROM t1;
DROP TABLE t1;
SET AUTOCOMMIT=DEFAULT;
--echo # Anonymous blocks with declarations, with no exceptions
SET AUTOCOMMIT=OFF;
CREATE TABLE t1 (a INT) ENGINE=InnoDB;
INSERT INTO t1 VALUES (10);
DELIMITER $$;
DECLARE
a20 INT:=20;
a30 INT:=30;
BEGIN
INSERT INTO t1 VALUES(a20);
INSERT INTO t1 VALUES(a30);
ROLLBACK;
END;
$$
DELIMITER ;$$
SELECT * FROM t1;
DROP TABLE t1;
SET AUTOCOMMIT=DEFAULT;
SET AUTOCOMMIT=OFF;
CREATE TABLE t1 (a INT) ENGINE=InnoDB;
INSERT INTO t1 VALUES (10);
DELIMITER $$;
DECLARE
a20 INT:=20;
a30 INT:=30;
BEGIN
INSERT INTO t1 VALUES(a20);
INSERT INTO t1 VALUES(a30);
END;
$$
DELIMITER ;$$
ROLLBACK;
SELECT * FROM t1;
DROP TABLE t1;
SET AUTOCOMMIT=DEFAULT;
SET AUTOCOMMIT=OFF;
CREATE TABLE t1 (a INT) ENGINE=InnoDB;
INSERT INTO t1 VALUES (10);
DELIMITER $$;
DECLARE
a20 INT:=20;
a30 INT:=30;
BEGIN
INSERT INTO t1 VALUES(a20);
INSERT INTO t1 VALUES(a30);
COMMIT;
END;
$$
DELIMITER ;$$
SELECT * FROM t1;
DROP TABLE t1;
SET AUTOCOMMIT=DEFAULT;
SET AUTOCOMMIT=OFF;
CREATE TABLE t1 (a INT) ENGINE=InnoDB;
INSERT INTO t1 VALUES (10);
DELIMITER $$;
DECLARE
a20 INT:=20;
a30 INT:=30;
BEGIN
INSERT INTO t1 VALUES(a20);
INSERT INTO t1 VALUES(a30);
END;
$$
DELIMITER ;$$
COMMIT;
SELECT * FROM t1;
DROP TABLE t1;
SET AUTOCOMMIT=DEFAULT;
--echo # Anonymous blocks with declarations, with exceptions
SET AUTOCOMMIT=OFF;
CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY) ENGINE=InnoDB;
INSERT INTO t1 VALUES (10);
DELIMITER $$;
DECLARE
a20 INT:=20;
BEGIN
INSERT INTO t1 VALUES(a20);
INSERT INTO t1 VALUES(a20);
EXCEPTION
WHEN DUP_VAL_ON_INDEX THEN NULL;
END;
$$
DELIMITER ;$$
COMMIT;
SELECT * FROM t1;
DROP TABLE t1;
SET AUTOCOMMIT=DEFAULT;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,295 @@
SET sql_mode=ORACLE;
--echo #
--echo # MDEV-10598 sql_mode=ORACLE: Variable declarations can go after cursor declarations
--echo #
--echo #
--echo # Variable after cursor declaration
--echo #
CREATE TABLE t1 (a INT);
insert into t1 values (1);
insert into t1 values (2);
DELIMITER $$;
CREATE PROCEDURE p1
AS
CURSOR c IS SELECT a FROM t1;
var1 varchar(10);
BEGIN
OPEN c;
fetch c into var1;
SELECT c%ROWCOUNT,var1;
close c;
END;
$$
DELIMITER ;$$
CALL p1;
DROP PROCEDURE p1;
drop table t1;
--echo #
--echo # Variable after condition declaration
--echo #
CREATE TABLE t1 (col1 INT);
insert into t1 values (1);
create unique index t1_col1 on t1 (col1);
DELIMITER $$;
CREATE PROCEDURE p1
AS
dup_key CONDITION FOR SQLSTATE '23000';
var1 varchar(40);
CONTINUE HANDLER FOR dup_key
BEGIN
var1:='duplicate key in index';
END;
BEGIN
var1:='';
insert into t1 values (1);
select var1;
END;
$$
DELIMITER ;$$
CALL p1;
DROP PROCEDURE p1;
drop table t1;
--echo #
--echo # Condition after cursor declaration
--echo #
CREATE TABLE t1 (col1 INT);
insert into t1 values (1);
create unique index t1_col1 on t1 (col1);
DELIMITER $$;
CREATE PROCEDURE p1
AS
var1 varchar(40);
var2 integer;
CURSOR c IS SELECT col1 FROM t1;
dup_key CONDITION FOR SQLSTATE '23000';
CONTINUE HANDLER FOR dup_key
BEGIN
var1:='duplicate key in index';
END;
BEGIN
var1:='';
insert into t1 values (1);
SELECT var1;
END;
$$
DELIMITER ;$$
CALL p1;
DROP PROCEDURE p1;
drop table t1;
--echo #
--echo # Cursor after handler declaration
--echo #
CREATE TABLE t1 (col1 INT);
insert into t1 values (1);
create unique index t1_col1 on t1 (col1);
DELIMITER $$;
--error ER_PARSE_ERROR
CREATE PROCEDURE p1
AS
var1 varchar(40);
var2 integer;
dup_key CONDITION FOR SQLSTATE '23000';
CONTINUE HANDLER FOR dup_key
BEGIN
var1:='duplicate key in index';
END;
CURSOR c IS SELECT col1 FROM t1;
BEGIN
var1:='';
insert into t1 values (1);
SELECT var1;
END;
$$
DELIMITER ;$$
drop table t1;
--echo #
--echo # Condition after handler declaration
--echo #
CREATE TABLE t1 (col1 INT);
insert into t1 values (1);
create unique index t1_col1 on t1 (col1);
DELIMITER $$;
--error ER_PARSE_ERROR
CREATE PROCEDURE p1
AS
var1 varchar(40);
var2 integer;
dup_key CONDITION FOR SQLSTATE '23000';
CURSOR c IS SELECT col1 FROM t1;
CONTINUE HANDLER FOR dup_key
BEGIN
var1:='duplicate key in index';
END;
divide_by_zero CONDITION FOR SQLSTATE '22012';
BEGIN
var1:='';
insert into t1 values (1);
SELECT var1;
END;
$$
DELIMITER ;$$
drop table t1;
--echo #
--echo # Variable after handler declaration
--echo #
CREATE TABLE t1 (col1 INT);
insert into t1 values (1);
create unique index t1_col1 on t1 (col1);
DELIMITER $$;
--error ER_PARSE_ERROR
CREATE PROCEDURE p1
AS
var1 varchar(40);
var2 integer;
dup_key CONDITION FOR SQLSTATE '23000';
CURSOR c IS SELECT col1 FROM t1;
CONTINUE HANDLER FOR dup_key
BEGIN
var1:='duplicate key in index';
END;
divide_by_zero CONDITION FOR SQLSTATE '22012';
BEGIN
var1:='';
insert into t1 values (1);
SELECT var1;
END;
$$
DELIMITER ;$$
drop table t1;
--echo #
--echo # Variable after cursor (inner block)
--echo #
CREATE TABLE t1 (col1 INT);
insert into t1 values (1);
insert into t1 values (2);
create unique index t1_col1 on t1 (col1);
DELIMITER $$;
CREATE PROCEDURE p1
AS
CURSOR c IS SELECT col1 FROM t1;
var1 varchar(40);
BEGIN
OPEN c;
begin
declare
CURSOR c IS SELECT col1 FROM t1 where col1=2;
var2 integer;
dup_key CONDITION FOR SQLSTATE '23000';
CONTINUE HANDLER FOR dup_key
BEGIN
var1:='duplicate key in index';
END;
begin
OPEN c;
fetch c into var1;
SELECT 'inner cursor',var1;
insert into t1 values (2);
close c;
end;
end;
SELECT var1;
fetch c into var1;
SELECT c%ROWCOUNT,var1;
begin
insert into t1 values (2);
exception when 1062 then
begin
SELECT 'dup key caugth';
end;
end;
close c;
END;
$$
DELIMITER ;$$
CALL p1;
DROP PROCEDURE p1;
drop table t1;
--echo #
--echo # Cursor declaration and row type declaration in same block
--echo #
CREATE TABLE t1 (a INT, b VARCHAR(10));
insert into t1 values(1,'a');
delimiter $$;
CREATE PROCEDURE p1()
AS
CURSOR cur1 IS SELECT a FROM t1;
rec1 cur1%ROWTYPE;
BEGIN
rec1.a:= 10;
END;
$$
delimiter ;$$
call p1;
DROP PROCEDURE p1;
drop table t1;
--echo #
--echo # Recursive cursor and cursor%ROWTYPE declarations in the same block
--echo #
delimiter $$;
CREATE PROCEDURE p1
AS
a INT:=10;
b VARCHAR(10):='b0';
c DOUBLE:=0.1;
CURSOR cur1 IS SELECT a, b, c;
rec1 cur1%ROWTYPE;
CURSOR cur2 IS SELECT rec1.a + 1 "a", rec1.b||'0' AS b, rec1.c AS c;
rec2 cur2%ROWTYPE;
BEGIN
OPEN cur1;
FETCH cur1 INTO rec1;
CLOSE cur1;
SELECT rec1.a;
OPEN cur2;
FETCH cur2 INTO rec2;
CLOSE cur2;
SELECT rec2.a;
CREATE TABLE t2 AS SELECT rec2.a AS a, rec2.b AS b, rec2.c AS c;
SHOW CREATE TABLE t2;
DROP TABLE t2;
END;
$$
DELIMITER ;$$
CALL p1();
DROP PROCEDURE p1;
--echo #
--echo # MDEV-12916 Wrong column data type for an INT field of a cursor-anchored ROW variable
--echo #
DELIMITER $$;
CREATE PROCEDURE p1
AS
a INT DEFAULT 10;
CURSOR cur1 IS SELECT a;
rec1 cur1%ROWTYPE;
BEGIN
CREATE TABLE t1 AS SELECT rec1.a;
SHOW CREATE TABLE t1;
DROP TABLE t1;
END;
$$
DELIMITER ;$$
CALL p1();
DROP PROCEDURE p1;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,954 @@
SET sql_mode=ORACLE;
--echo #
--echo # MDEV-10582 sql_mode=ORACLE: explicit cursor attributes %ISOPEN, %ROWCOUNT, %FOUND, %NOTFOUND
--echo #
--echo #
--echo # Cursor attributes outside of an SP context
--echo #
--error ER_SP_CURSOR_MISMATCH
SELECT c%ISOPEN;
--error ER_SP_CURSOR_MISMATCH
SELECT c%FOUND;
--error ER_SP_CURSOR_MISMATCH
SELECT c%NOTFOUND;
--error ER_SP_CURSOR_MISMATCH
SELECT c%ROWCOUNT;
--echo #
--echo # Undefinite cursor attributes
--echo #
DELIMITER $$;
--error ER_SP_CURSOR_MISMATCH
CREATE PROCEDURE p1
AS
BEGIN
SELECT c%ISOPEN;
END;
$$
--error ER_SP_CURSOR_MISMATCH
CREATE PROCEDURE p1
AS
BEGIN
SELECT c%ROWCOUNT;
END;
$$
--error ER_SP_CURSOR_MISMATCH
CREATE PROCEDURE p1
AS
BEGIN
SELECT c%FOUND;
END;
$$
--error ER_SP_CURSOR_MISMATCH
CREATE PROCEDURE p1
AS
BEGIN
SELECT c%NOTFOUND;
END;
$$
DELIMITER ;$$
--echo #
--echo # Not opened cursor attributes %FOUND, %NOTFOUND, %ROWCOUNT
--echo #
DELIMITER $$;
CREATE PROCEDURE p1
AS
CURSOR c IS SELECT 1 AS c FROM DUAL;
BEGIN
SELECT c%ROWCOUNT;
END;
$$
DELIMITER ;$$
--error ER_SP_CURSOR_NOT_OPEN
CALL p1;
DROP PROCEDURE p1;
DELIMITER $$;
CREATE PROCEDURE p1
AS
CURSOR c IS SELECT 1 AS c FROM DUAL;
BEGIN
SELECT c%FOUND;
END;
$$
DELIMITER ;$$
--error ER_SP_CURSOR_NOT_OPEN
CALL p1;
DROP PROCEDURE p1;
DELIMITER $$;
CREATE PROCEDURE p1
AS
CURSOR c IS SELECT 1 AS c FROM DUAL;
BEGIN
SELECT c%NOTFOUND;
END;
$$
DELIMITER ;$$
--error ER_SP_CURSOR_NOT_OPEN
CALL p1;
DROP PROCEDURE p1;
--echo #
--echo # Not opened cursor attributes %FOUND, %NOTFOUND, %ROWCOUNT with INVALID_CURSOR exception
--echo #
DELIMITER $$;
CREATE PROCEDURE p1
AS
CURSOR c IS SELECT 1 AS c FROM DUAL;
BEGIN
SELECT c%ROWCOUNT;
EXCEPTION
WHEN INVALID_CURSOR THEN SELECT 'INVALID_CURSOR caught' AS msg;
END;
$$
DELIMITER ;$$
CALL p1;
DROP PROCEDURE p1;
DELIMITER $$;
CREATE PROCEDURE p1
AS
CURSOR c IS SELECT 1 AS c FROM DUAL;
BEGIN
SELECT c%FOUND;
EXCEPTION
WHEN INVALID_CURSOR THEN SELECT 'INVALID_CURSOR caught' AS msg;
END;
$$
DELIMITER ;$$
CALL p1;
DROP PROCEDURE p1;
DELIMITER $$;
CREATE PROCEDURE p1
AS
CURSOR c IS SELECT 1 AS c FROM DUAL;
BEGIN
SELECT c%NOTFOUND;
EXCEPTION
WHEN INVALID_CURSOR THEN SELECT 'INVALID_CURSOR caught' AS msg;
END;
$$
DELIMITER ;$$
CALL p1;
DROP PROCEDURE p1;
--echo #
--echo # print()
--echo #
CREATE TABLE t1 (a INT);
DELIMITER $$;
CREATE PROCEDURE p1
AS
CURSOR c IS SELECT * FROM t1 ORDER BY a;
BEGIN
EXPLAIN EXTENDED SELECT c%ISOPEN, c%ROWCOUNT, c%FOUND, c%NOTFOUND;
END;
$$
DELIMITER ;$$
CALL p1();
DROP PROCEDURE p1;
DROP TABLE t1;
--echo #
--echo # Declared data type of the attributes
--echo #
CREATE TABLE t1 (a INT);
INSERT INTO t1 VALUES (10);
DELIMITER $$;
CREATE PROCEDURE p1
AS
CURSOR c IS SELECT * FROM t1 ORDER BY a;
BEGIN
OPEN c;
CREATE TABLE t2 AS SELECT c%ISOPEN, c%ROWCOUNT, c%FOUND, c%NOTFOUND;
SHOW CREATE TABLE t2;
DROP TABLE t2;
CLOSE c;
END;
$$
DELIMITER ;$$
CALL p1();
DROP PROCEDURE p1;
DROP TABLE t1;
--echo #
--echo # Core functionality
--echo #
CREATE TABLE t1 (a INT);
INSERT INTO t1 VALUES (10);
INSERT INTO t1 VALUES (20);
INSERT INTO t1 VALUES (30);
DELIMITER $$;
CREATE PROCEDURE p1
AS
a INT:=0;
CURSOR c IS SELECT * FROM t1 ORDER BY a;
BEGIN
SELECT a, c%ISOPEN;
OPEN c;
/*
After OPEN and before FETCH:
- %ROWCOUNT returns 0
- %FOUND and %NOTFOUND return NULL
*/
SELECT a, c%ISOPEN, c%ROWCOUNT, c%FOUND, c%NOTFOUND;
FETCH c INTO a;
SELECT a, c%ISOPEN, c%ROWCOUNT, c%FOUND, c%NOTFOUND;
FETCH c INTO a;
SELECT a, c%ISOPEN, c%ROWCOUNT, c%FOUND, c%NOTFOUND;
FETCH c INTO a;
SELECT a, c%ISOPEN, c%ROWCOUNT, c%FOUND, c%NOTFOUND;
FETCH c INTO a;
SELECT a, c%ISOPEN, c%ROWCOUNT, c%FOUND, c%NOTFOUND;
CLOSE c;
SELECT a, c%ISOPEN;
/*
After reopen and before FETCH:
- %ROWCOUNT returns 0
- %FOUND and %NOTFOUND return NULL
*/
OPEN c;
SELECT a, c%ISOPEN, c%ROWCOUNT, c%FOUND, c%NOTFOUND;
FETCH c INTO a;
SELECT a, c%ISOPEN, c%ROWCOUNT, c%FOUND, c%NOTFOUND;
CLOSE c;
END;
$$
DELIMITER ;$$
CALL p1();
DROP PROCEDURE p1;
DROP TABLE t1;
--echo #
--echo # %NOTFOUND as a loop exit condition
--echo #
CREATE TABLE t1 (a INT);
INSERT INTO t1 VALUES (10);
INSERT INTO t1 VALUES (20);
INSERT INTO t1 VALUES (30);
DELIMITER $$;
CREATE PROCEDURE p1
AS
a INT:=0;
CURSOR c IS SELECT * FROM t1 ORDER BY a;
BEGIN
OPEN c;
LOOP
FETCH c INTO a;
EXIT WHEN c%NOTFOUND;
SELECT a;
END LOOP;
CLOSE c;
END;
$$
DELIMITER ;$$
CALL p1();
DROP PROCEDURE p1;
DROP TABLE t1;
--echo #
--echo # %FOUND as a loop exit condition
--echo #
CREATE TABLE t1 (a INT);
INSERT INTO t1 VALUES (10);
INSERT INTO t1 VALUES (20);
INSERT INTO t1 VALUES (30);
DELIMITER $$;
CREATE PROCEDURE p1
AS
a INT:=0;
CURSOR c IS SELECT * FROM t1 ORDER BY a;
BEGIN
OPEN c;
LOOP
FETCH c INTO a;
EXIT WHEN NOT c%FOUND;
SELECT a;
END LOOP;
CLOSE c;
END;
$$
DELIMITER ;$$
CALL p1();
DROP PROCEDURE p1;
DROP TABLE t1;
--echo #
--echo # End of MDEV-10582 sql_mode=ORACLE: explicit cursor attributes %ISOPEN, %ROWCOUNT, %FOUND, %NOTFOUND
--echo #
--echo #
--echo # MDEV-10597 Cursors with parameters
--echo #
--echo #
--echo # OPEN with a wrong number of parameters
--echo #
CREATE TABLE t1 (a INT, b VARCHAR(10));
DELIMITER $$;
--error ER_WRONG_PARAMCOUNT_TO_CURSOR
CREATE PROCEDURE p1(a_a INT,a_b VARCHAR)
AS
v_a INT;
v_b VARCHAR(10);
CURSOR c (p_a INT, p_b VARCHAR) IS SELECT * FROM t1 WHERE a=p_a;
BEGIN
OPEN c(a_a);
LOOP
FETCH c INTO v_a, v_b;
EXIT WHEN c%NOTFOUND;
DBMS_OUTPUT.PUT_LINE('Fetched a record a='||TO_CHAR(v_a)||' b='||v_b);
END LOOP;
CLOSE c;
END;
$$
DELIMITER ;$$
DROP TABLE t1;
--echo #
--echo # Cursor parameters are not visible outside of the cursor
--echo #
DELIMITER $$;
--error ER_UNKNOWN_SYSTEM_VARIABLE
CREATE PROCEDURE p1(a_a INT)
AS
v_a INT;
CURSOR c (p_a INT) IS SELECT a FROM t1 WHERE a=p_a;
BEGIN
OPEN c(a_a);
p_a:=10;
END;
$$
DELIMITER ;$$
DELIMITER $$;
--error ER_UNKNOWN_SYSTEM_VARIABLE
CREATE PROCEDURE p1(a_a INT)
AS
v_a INT;
CURSOR c (p_a INT) IS SELECT a FROM t1 WHERE a=p_a;
BEGIN
p_a:=10;
OPEN c(a_a);
END;
$$
DELIMITER ;$$
--echo #
--echo # Cursor parameter shadowing a local variable
--echo #
CREATE TABLE t1 (a INT);
INSERT INTO t1 VALUES (1);
DELIMITER $$;
CREATE PROCEDURE p1(a INT)
AS
v_a INT:=NULL;
p_a INT:=NULL;
CURSOR c (p_a VARCHAR2) IS SELECT a FROM t1 WHERE p_a IS NOT NULL;
BEGIN
OPEN c(a);
FETCH c INTO v_a;
IF c%NOTFOUND THEN
BEGIN
SELECT 'No records found' AS msg;
RETURN;
END;
END IF;
CLOSE c;
SELECT 'Fetched a record a='||v_a AS msg;
INSERT INTO t1 VALUES (v_a);
END;
$$
DELIMITER ;$$
CALL p1(1);
SELECT * FROM t1;
CALL p1(NULL);
SELECT * FROM t1;
DROP PROCEDURE p1;
DROP TABLE t1;
--echo #
--echo # Parameters in SELECT list
--echo #
DELIMITER $$;
CREATE PROCEDURE p1(a_a INT, a_b VARCHAR)
AS
v_a INT;
v_b VARCHAR(10);
CURSOR c (p_a INT, p_b VARCHAR) IS SELECT p_a,p_b FROM DUAL;
BEGIN
FOR i IN 0..1
LOOP
OPEN c(a_a + i,a_b);
LOOP
FETCH c INTO v_a, v_b;
EXIT WHEN c%NOTFOUND;
SELECT 'Fetched a record a=' || v_a || ' b=' || v_b AS msg;
END LOOP;
CLOSE c;
END LOOP;
END;
$$
DELIMITER ;$$
CALL p1(1,'b1');
DROP PROCEDURE p1;
--echo #
--echo # Parameters in SELECT list + UNION
--echo #
DELIMITER $$;
CREATE PROCEDURE p1(a_a INT, a_b VARCHAR)
AS
v_a INT;
v_b VARCHAR(10);
CURSOR c (p_a INT, p_b VARCHAR) IS
SELECT p_a,p_b FROM DUAL
UNION ALL
SELECT p_a+1,p_b||'b' FROM DUAL;
BEGIN
OPEN c(a_a,a_b);
LOOP
FETCH c INTO v_a, v_b;
EXIT WHEN c%NOTFOUND;
SELECT 'Fetched a record a=' || v_a || ' b=' || v_b AS msg;
END LOOP;
CLOSE c;
END;
$$
DELIMITER ;$$
CALL p1(1,'b1');
DROP PROCEDURE p1;
--echo #
--echo # Parameters in SELECT list + type conversion + warnings
--echo #
DELIMITER $$;
CREATE PROCEDURE p1(a_a VARCHAR)
AS
v_a INT;
CURSOR c (p_a INT) IS SELECT p_a FROM DUAL;
BEGIN
OPEN c(a_a);
LOOP
FETCH c INTO v_a;
EXIT WHEN c%NOTFOUND;
SELECT 'Fetched a record a=' || v_a AS msg;
END LOOP;
CLOSE c;
END;
$$
DELIMITER ;$$
CALL p1('1b');
CALL p1('b1');
DROP PROCEDURE p1;
--echo #
--echo # One parameter in SELECT list + subselect
--echo #
DELIMITER $$;
CREATE PROCEDURE p1(a_a VARCHAR)
AS
v_a VARCHAR(10);
CURSOR c (p_a VARCHAR) IS
SELECT p_a FROM DUAL UNION SELECT REVERSE(p_a) FROM DUAL;
BEGIN
OPEN c((SELECT a_a));
LOOP
FETCH c INTO v_a;
EXIT WHEN c%NOTFOUND;
SELECT v_a;
END LOOP;
CLOSE c;
END;
$$
DELIMITER ;$$
CALL p1('ab');
DROP PROCEDURE p1;
--echo #
--echo # Two parameters in SELECT list + subselect
--echo #
SET sql_mode=ORACLE;
DELIMITER $$;
CREATE PROCEDURE p1()
AS
v_a VARCHAR(10);
v_b VARCHAR(20);
CURSOR c (p_a VARCHAR, p_b VARCHAR) IS
SELECT p_a, p_b FROM DUAL
UNION
SELECT p_b, p_a FROM DUAL;
BEGIN
OPEN c((SELECT 'aaa'),(SELECT 'bbb'));
LOOP
FETCH c INTO v_a, v_b;
EXIT WHEN c%NOTFOUND;
SELECT v_a, v_b;
END LOOP;
CLOSE c;
END;
$$
DELIMITER ;$$
CALL p1();
DROP PROCEDURE p1;
--echo #
--echo # Two parameters in SELECT list + two parameters in WHERE + subselects
--echo #
SET sql_mode=ORACLE;
DELIMITER $$;
CREATE PROCEDURE p1(a_a VARCHAR, a_b VARCHAR)
AS
v_a VARCHAR(10);
v_b VARCHAR(20);
CURSOR c (value_a VARCHAR, value_b VARCHAR,
pattern_a VARCHAR, pattern_b VARCHAR) IS
SELECT value_a, value_b FROM DUAL WHERE value_a LIKE pattern_a
UNION
SELECT value_b, value_a FROM DUAL WHERE value_b LIKE pattern_b;
BEGIN
OPEN c((SELECT 'aaa'),(SELECT 'bbb'),(SELECT a_a),(SELECT a_b));
LOOP
FETCH c INTO v_a, v_b;
EXIT WHEN c%NOTFOUND;
SELECT v_a, v_b;
END LOOP;
CLOSE c;
END;
$$
DELIMITER ;$$
CALL p1('%','%');
CALL p1('aaa','xxx');
CALL p1('xxx','bbb');
CALL p1('xxx','xxx');
DROP PROCEDURE p1;
--echo #
--echo # Parameters in SELECT list + stored function
--echo #
DELIMITER $$;
CREATE FUNCTION f1 (a VARCHAR) RETURN VARCHAR
AS
BEGIN
RETURN a || 'y';
END;
$$
CREATE PROCEDURE p1(a_a VARCHAR)
AS
v_a VARCHAR(10);
v_b VARCHAR(10);
CURSOR c (p_sel_a VARCHAR, p_cmp_a VARCHAR) IS
SELECT p_sel_a, p_cmp_a FROM DUAL;
BEGIN
OPEN c(f1(a_a), f1(a_a));
LOOP
FETCH c INTO v_a, v_b;
EXIT WHEN c%NOTFOUND;
SELECT v_a;
END LOOP;
CLOSE c;
END;
$$
DELIMITER ;$$
CALL p1('x');
# A complex expression
CALL p1(f1(COALESCE(NULL, f1('x'))));
DROP PROCEDURE p1;
DROP FUNCTION f1;
--echo #
--echo # One parameter in WHERE clause
--echo #
CREATE TABLE t1 (a INT, b VARCHAR(10));
CREATE TABLE t2 (a INT, b VARCHAR(10));
INSERT INTO t1 VALUES (1,'11');
INSERT INTO t1 VALUES (1,'12');
INSERT INTO t1 VALUES (2,'21');
INSERT INTO t1 VALUES (2,'22');
INSERT INTO t1 VALUES (3,'31');
INSERT INTO t1 VALUES (3,'32');
DELIMITER $$;
CREATE PROCEDURE p1(a_a INT)
AS
v_a INT;
v_b VARCHAR(10);
CURSOR c (p_a INT) IS SELECT a,b FROM t1 WHERE a=p_a;
BEGIN
OPEN c(a_a);
LOOP
FETCH c INTO v_a, v_b;
EXIT WHEN c%NOTFOUND;
INSERT INTO t2 VALUES (v_a,v_b);
END LOOP;
CLOSE c;
END;
$$
DELIMITER ;$$
CALL p1(1);
SELECT * FROM t2;
DROP TABLE t1;
DROP TABLE t2;
DROP PROCEDURE p1;
--echo #
--echo # Two parameters in WHERE clause
--echo #
CREATE TABLE t1 (a INT, b VARCHAR(10));
CREATE TABLE t2 (a INT, b VARCHAR(10));
INSERT INTO t1 VALUES (1,'11');
INSERT INTO t1 VALUES (1,'12');
INSERT INTO t1 VALUES (2,'21');
INSERT INTO t1 VALUES (2,'22');
INSERT INTO t1 VALUES (3,'31');
INSERT INTO t1 VALUES (3,'32');
DELIMITER $$;
CREATE PROCEDURE p1(a_a INT, a_b VARCHAR)
AS
v_a INT;
v_b VARCHAR(10);
CURSOR c (p_a INT, p_b VARCHAR) IS SELECT a,b FROM t1 WHERE a=p_a AND b=p_b;
BEGIN
OPEN c(a_a, a_b);
LOOP
FETCH c INTO v_a, v_b;
EXIT WHEN c%NOTFOUND;
INSERT INTO t2 VALUES (v_a,v_b);
END LOOP;
CLOSE c;
END;
$$
DELIMITER ;$$
CALL p1(1,'11');
SELECT * FROM t2;
DROP TABLE t1;
DROP TABLE t2;
DROP PROCEDURE p1;
--echo #
--echo # Parameters in WHERE and HAVING clauses
--echo #
CREATE TABLE t1 (name VARCHAR(10), value INT);
INSERT INTO t1 VALUES ('but',1);
INSERT INTO t1 VALUES ('but',1);
INSERT INTO t1 VALUES ('but',1);
INSERT INTO t1 VALUES ('bin',1);
INSERT INTO t1 VALUES ('bin',1);
INSERT INTO t1 VALUES ('bot',1);
DELIMITER $$;
CREATE PROCEDURE p1 (arg_name_limit VARCHAR, arg_total_limit INT)
AS
v_name VARCHAR(10);
v_total INT;
-- +0 is needed to work around the bug MDEV-11081
CURSOR c(p_v INT) IS
SELECT name, SUM(value + p_v) + 0 AS total FROM t1
WHERE name LIKE arg_name_limit
GROUP BY name HAVING total>=arg_total_limit;
BEGIN
FOR i IN 0..1
LOOP
OPEN c(i);
LOOP
FETCH c INTO v_name, v_total;
EXIT WHEN c%NOTFOUND;
SELECT v_name, v_total;
END LOOP;
CLOSE c;
END LOOP;
END;
$$
DELIMITER ;$$
CALL p1('%', 2);
CALL p1('b_t', 0);
DROP PROCEDURE p1;
DROP TABLE t1;
--echo #
--echo # One parameter in LIMIT clause
--echo #
CREATE TABLE t1 (a INT, b VARCHAR(10));
INSERT INTO t1 VALUES (1,'b1');
INSERT INTO t1 VALUES (2,'b2');
INSERT INTO t1 VALUES (3,'b3');
INSERT INTO t1 VALUES (4,'b4');
INSERT INTO t1 VALUES (5,'b5');
INSERT INTO t1 VALUES (6,'b6');
DELIMITER $$;
CREATE PROCEDURE p1(a_a INT)
AS
v_a INT;
v_b VARCHAR(10);
CURSOR c (p_a INT) IS SELECT a,b FROM t1 ORDER BY a LIMIT p_a;
BEGIN
CREATE TABLE t2 (a INT, b VARCHAR(10));
OPEN c(a_a);
LOOP
FETCH c INTO v_a, v_b;
EXIT WHEN c%NOTFOUND;
INSERT INTO t2 VALUES (v_a,v_b);
END LOOP;
CLOSE c;
SELECT * FROM t2;
DROP TABLE t2;
END;
$$
DELIMITER ;$$
CALL p1(1);
CALL p1(3);
CALL p1(6);
DROP TABLE t1;
DROP PROCEDURE p1;
--echo #
--echo # End of MDEV-10597 Cursors with parameters
--echo #
--echo #
--echo # MDEV-12209 sql_mode=ORACLE: Syntax error in a OPEN cursor with parameters makes the server crash
--echo #
CREATE TABLE t1 (a INT, b VARCHAR(10));
INSERT INTO t1 VALUES (1,'A');
DELIMITER $$;
--error ER_PARSE_ERROR
CREATE PROCEDURE p1(a INT,b VARCHAR)
AS
CURSOR c (p_a INT, p_b VARCHAR) IS SELECT * FROM t1 WHERE a=p_a;
BEGIN
OPEN c(a+, b);
LOOP
FETCH c INTO a, b;
EXIT WHEN c%NOTFOUND;
SELECT a, b;
END LOOP;
CLOSE c;
END;
$$
DELIMITER ;$$
DROP TABLE t1;
--echo #
--echo # MDEV-10577 sql_mode=ORACLE: %TYPE in variable declarations
--echo #
CREATE TABLE t1 (a INT, b VARCHAR(10),c DATETIME(3));
INSERT INTO t1 VALUES (1,'b1','2001-01-01 10:20:30.123');
INSERT INTO t1 VALUES (2,'b2','2001-01-02 10:20:30.123');
CREATE TABLE t2 LIKE t1;
DELIMITER $$;
CREATE PROCEDURE p1()
AS
v_a t1.a%TYPE;
v_b t1.b%TYPE;
v_c t1.c%TYPE;
CURSOR c IS SELECT a,b,c FROM t1;
BEGIN
OPEN c;
LOOP
FETCH c INTO v_a, v_b, v_c;
EXIT WHEN c%NOTFOUND;
INSERT INTO t2 (a,b,c) VALUES (v_a, v_b, v_c);
END LOOP;
CLOSE c;
END;
$$
DELIMITER ;$$
CALL p1();
SELECT * FROM t2;
DROP TABLE t2;
DROP PROCEDURE p1;
DROP TABLE t1;
--echo #
--echo # MDEV-12007 Allow ROW variables as a cursor FETCH target
--echo #
CREATE TABLE t1 (a INT, b VARCHAR(32));
INSERT INTO t1 VALUES (10,'b10');
INSERT INTO t1 VALUES (20,'b20');
INSERT INTO t1 VALUES (30,'b30');
DELIMITER $$;
CREATE PROCEDURE p1 AS
rec ROW(a INT, b VARCHAR(32));
CURSOR c IS SELECT a,b FROM t1;
BEGIN
OPEN c;
LOOP
FETCH c INTO rec;
EXIT WHEN c%NOTFOUND;
SELECT ('rec=(' || rec.a ||','|| rec.b||')') AS c;
END LOOP;
CLOSE c;
END;
$$
DELIMITER ;$$
CALL p1();
DROP PROCEDURE p1;
DROP TABLE t1;
--echo #
--echo # MDEV-12441 Variables declared after cursors with parameters lose values
--echo #
DELIMITER $$;
CREATE PROCEDURE p1() AS
x0 INT:=100;
CURSOR cur(cp1 INT, cp2 INT) IS SELECT cp1+cp2;
x1 INT:=101;
BEGIN
OPEN cur(10,11);
CLOSE cur;
SELECT x0, x1;
END;
$$
DELIMITER ;$$
CALL p1();
DROP PROCEDURE p1;
CREATE TABLE t1 (a INT);
DELIMITER $$;
CREATE PROCEDURE p1() AS
x0 INT:=100;
CURSOR cur(cp1 INT, cp2 INT) IS SELECT cp1+cp2;
x1 t1.a%TYPE:=101;
BEGIN
OPEN cur(10,11);
CLOSE cur;
SELECT x0, x1;
END;
$$
DELIMITER ;$$
CALL p1();
DROP PROCEDURE p1;
DROP TABLE t1;
DELIMITER $$;
CREATE PROCEDURE p1() AS
x0 INT:=100;
CURSOR cur(cp1 INT, cp2 INT) IS SELECT cp1+cp2;
x1 ROW(a INT,b INT):=ROW(101,102);
BEGIN
OPEN cur(10,11);
CLOSE cur;
SELECT x0, x1.a, x1.b;
END;
$$
DELIMITER ;$$
CALL p1();
DROP PROCEDURE p1;
CREATE TABLE t1 (a INT, b VARCHAR(10));
INSERT INTO t1 VALUES (10,'Tbl-t1.b0');
DELIMITER $$;
CREATE PROCEDURE p1() AS
x0 INT:=100;
CURSOR cur(cp1 INT, cp2 INT) IS SELECT a,b FROM t1;
x1 t1%ROWTYPE:=ROW(101,'Var-x1.b0');
BEGIN
SELECT x0, x1.a, x1.b;
OPEN cur(10,11);
FETCH cur INTO x1;
CLOSE cur;
SELECT x0, x1.a, x1.b;
END;
$$
DELIMITER ;$$
CALL p1();
DROP PROCEDURE p1;
DROP TABLE t1;
CREATE TABLE t1 (a INT, b VARCHAR(10));
INSERT INTO t1 VALUES (10,'Tbl-t1.b0');
DELIMITER $$;
CREATE PROCEDURE p1() AS
x0 INT:=100;
CURSOR cur(cp1 INT, cp2 INT) IS SELECT a,b FROM t1;
x1 cur%ROWTYPE:=ROW(101,'Var-x1.b0');
BEGIN
SELECT x0, x1.a, x1.b;
OPEN cur(10,11);
FETCH cur INTO x1;
CLOSE cur;
SELECT x0, x1.a, x1.b;
END;
$$
DELIMITER ;$$
CALL p1();
DROP PROCEDURE p1;
DROP TABLE t1;
--echo #
--echo # MDEV-12854 Synchronize CREATE..SELECT data type and result set metadata data type for INT functions
--echo #
--enable_metadata
--disable_ps_protocol
DELIMITER $$;
DECLARE
CURSOR c IS SELECT 1 AS c FROM DUAL;
BEGIN
OPEN c;
SELECT
c%ISOPEN,
c%NOTFOUND,
c%FOUND,
c%ROWCOUNT;
CLOSE c;
END;
$$
DELIMITER ;$$
--enable_ps_protocol
--disable_metadata

View File

@ -0,0 +1,872 @@
set sql_mode=oracle;
--echo #
--echo # MDEV-10697 sql_mode=ORACLE: GOTO statement
--echo #
--echo # matrice of tests in procedure
--echo # |--------------------------------------------------------
--echo # | | Same | Outside | to sub | No |
--echo # | | block | one block | block | matching |
--echo # | | | | | label |
--echo # |--------------------------------------------------------
--echo # | Forward jump | F1 | F3 | F5 | F7 |
--echo # |--------------------------------------------------------
--echo # | Backward jump | F2 | F4 | F6 | F8 |
--echo # |--------------------------------------------------------
--echo # Jump from handler to outside handling code block : F9
--echo # Jump from handler to handling code block : F10 (forbidden)
--echo # Jump inside handler : F21
--echo # Jump between handler : F22 (forbidden)
--echo # Jump from cascaded block with handler : F11
--echo # Duplicate label in same block : F12 (forbidden)
--echo # Duplicate label in different block : F13
--echo # Jump outside unlabeled block : F14
--echo # Jump inside/outside labeled block : F15
--echo # Jump from if / else : F16
--echo # Jump with cursors : F17
--echo # Jump outside case : F18
--echo # Jump inside/outside case block : F19
--echo # Jump outside labeled loop : F20
--echo # Jump (continue) labeled loop : F23
--echo # Two consecutive label : P24
--echo # Two consecutive label (backward and forward jump) : P25
--echo # Two consecutive label, continue to wrong label : P26
--echo # Consecutive goto label and block label : P27
--echo # Test in function
--echo # backward jump : func1
--echo # forward jump : func2
--echo # Test in trigger
--echo # forward jump : trg1
--echo #
--echo # Forward jump in same block
--echo #
DELIMITER $$;
CREATE or replace procedure f1(p2 IN OUT VARCHAR)
AS
BEGIN
p2:='a';
goto lab1;
<<lab1>>
goto lab2;
p2:='b';
<<lab2>>
return ;
END;
$$
DELIMITER ;$$
call f1(@wp1);
select 'f1',@wp1;
DROP PROCEDURE f1;
--echo #
--echo # Backward jump in same block
--echo #
DELIMITER $$;
CREATE or replace procedure f2(p2 IN OUT VARCHAR)
AS
BEGIN
p2:='a';
<<lab1>>
if (p2='b') then
return ;
end if;
p2:='b';
goto lab1;
END;
$$
DELIMITER ;$$
call f2(@wp1);
select 'f2',@wp1;
DROP PROCEDURE f2;
--echo #
--echo # Forward jump outside one block
--echo #
DELIMITER $$;
CREATE or replace procedure f3(p2 IN OUT VARCHAR)
AS
BEGIN
p2:='a';
if (p2='a') then
goto lab1;
end if;
p2:='c';
<<lab1>>
return ;
END;
$$
DELIMITER ;$$
call f3(@wp1);
select 'f3',@wp1;
DROP PROCEDURE f3;
--echo #
--echo # Backward jump outside one block
--echo #
DELIMITER $$;
CREATE or replace procedure f4(p2 IN OUT VARCHAR)
AS
BEGIN
p2:='a';
<<lab1>>
if (p2='a') then
p2:=p2||'b';
goto lab1;
end if;
if (p2='ab') then
p2:=p2||'c';
end if;
return ;
END;
$$
DELIMITER ;$$
call f4(@wp1);
select 'f4',@wp1;
DROP PROCEDURE f4;
DELIMITER $$;
--echo #
--echo # Forward jump inside sub block
--error ER_SP_LILABEL_MISMATCH
CREATE or replace procedure f5(p2 IN OUT VARCHAR)
AS
BEGIN
p2:='a';
goto lab5 ;
if (p2='a') then
<<lab5>>
p2:=p2||'b';
end if;
return ;
END;
$$
DELIMITER ;$$
DELIMITER $$;
--echo #
--echo # Backward jump inside sub block
--error ER_SP_LILABEL_MISMATCH
CREATE or replace procedure f6(p2 IN OUT VARCHAR)
AS
BEGIN
p2:='a';
if (p2='a') then
<<lab6>>
p2:=p2||'b';
return ;
end if;
goto lab6 ;
END;
$$
DELIMITER ;$$
DELIMITER $$;
--echo #
--echo # Backward jump - missing label
--error ER_SP_LILABEL_MISMATCH
CREATE or replace procedure f7(p2 IN OUT VARCHAR)
AS
BEGIN
<<lab>>
goto lab7 ;
return ;
END;
$$
DELIMITER ;$$
DELIMITER $$;
--echo #
--echo # Forward jump - missing label
--error ER_SP_LILABEL_MISMATCH
CREATE or replace procedure f8(p2 IN OUT VARCHAR)
AS
BEGIN
goto lab8 ;
<<lab>>
return ;
END;
$$
DELIMITER ;$$
--echo #
--echo # Jump from handler to procedure code
--echo #
DELIMITER $$;
CREATE or replace procedure f9(lim INT, res OUT VARCHAR)
AS
a INT;
BEGIN
<<lab9>>
if lim=-1 then
res:=res||' -- goto end limit -1 --';
goto lab9_end;
end if;
begin
SELECT a INTO a FROM information_schema.tables LIMIT lim;
EXCEPTION
WHEN TOO_MANY_ROWS THEN
begin
res:=res||'--- too_many_rows cought ---';
lim:=0;
goto lab9;
end;
WHEN NO_DATA_FOUND THEN
begin
res:=res||'--- no_data_found cought ---';
lim:=-1;
goto lab9;
end;
end;
res:=res||'error';
<<lab9_end>>
return ;
END;
$$
DELIMITER ;$$
SET @res='';
CALL f9(2, @res);
SELECT 'f9',@res;
CALL f9(0, @res);
SELECT 'f9',@res;
DROP PROCEDURE f9;
DELIMITER $$;
--echo #
--echo # Jump from handler to handling bloc
--error ER_SP_LILABEL_MISMATCH
CREATE or replace procedure f10(lim INT, res OUT VARCHAR)
AS
a INT;
BEGIN
begin
<<lab10>>
SELECT a INTO a FROM information_schema.tables LIMIT lim;
EXCEPTION
WHEN TOO_MANY_ROWS THEN
begin
res:='--- too_many_rows cought ---';
goto lab10;
end;
WHEN NO_DATA_FOUND THEN res:='--- no_data_found cought ---';
end;
return ;
END;
$$
--echo #
--echo # Jump from cascaded block with handler
--echo #
CREATE or replace procedure f11(lim INT, res OUT VARCHAR)
AS
a INT;
BEGIN
<<lab11a>>
begin
SELECT a INTO a FROM information_schema.tables LIMIT lim;
EXCEPTION
WHEN TOO_MANY_ROWS THEN
begin
res:=res||'--- too_many_rows cought 1 ---';
goto lab11b;
end;
WHEN NO_DATA_FOUND THEN
begin
res:=res||'--- no_data_found cought 1 ---';
lim:=2;
SELECT a INTO a FROM information_schema.tables LIMIT lim;
EXCEPTION
WHEN TOO_MANY_ROWS THEN
begin
res:=res||'--- too_many_rows cought 2 ---';
goto lab11a;
end;
WHEN NO_DATA_FOUND THEN res:='--- no_data_found cought 2 ---';
end;
end;
set res:=res||' error ';
<<lab11b>>
return ;
END;
$$
DELIMITER ;$$
SET @res='';
CALL f11(0, @res);
SELECT 'f11',@res;
DROP PROCEDURE f11;
DELIMITER $$;
--echo #
--echo # Jump inside handler
--echo #
CREATE or replace procedure f21(lim INT, res OUT VARCHAR)
AS
a INT;
BEGIN
begin
SELECT a INTO a FROM information_schema.tables LIMIT lim;
EXCEPTION
WHEN TOO_MANY_ROWS THEN
begin
<<retry>>
lim:=lim-1;
loop
begin
SELECT a INTO a FROM information_schema.tables LIMIT lim;
EXCEPTION
WHEN TOO_MANY_ROWS THEN
begin
lim:=lim-1;
goto retry;
end;
end;
exit ;
end loop;
end;
end;
res:=lim;
return ;
END;
$$
DELIMITER ;$$
SET @res='';
CALL f21(10, @res);
SELECT 'f21',@res;
drop procedure f21;
DELIMITER $$;
--echo #
--echo # Jump beetween handler
--error ER_SP_LILABEL_MISMATCH
CREATE or replace procedure f22(lim INT, res OUT VARCHAR)
AS
a INT;
BEGIN
res:='ok';
begin
SELECT a INTO a FROM information_schema.tables LIMIT lim;
EXCEPTION
WHEN TOO_MANY_ROWS THEN
goto nodata ;
WHEN NO_DATA_FOUND THEN
begin
<<nodata>>
res:='error';
end;
end;
return ;
END;
$$
DELIMITER ;$$
DELIMITER $$;
--echo #
--echo # Duplicate label in same bloc
--error 1309
CREATE or replace procedure f12(lim INT, res OUT VARCHAR)
AS
a INT;
BEGIN
<<lab12>>
res:='error';
<<lab12>>
return ;
END;
$$
DELIMITER ;$$
--echo #
--echo # Duplicate label in different block
--echo #
DELIMITER $$;
CREATE or replace procedure f13(lim INT, res OUT VARCHAR)
AS
a INT;
BEGIN
a:=0;
<<lab13>>
a:=a+1;
begin
<<lab13>>
a:=a+1;
if (a<10) then
goto lab13;
end if;
end;
res:=a;
if (a=10) then
goto lab13;
end if;
return ;
END;
$$
DELIMITER ;$$
SET @res='';
CALL f13(0, @res);
SELECT 'f13',@res;
DROP PROCEDURE f13;
--echo #
--echo # Jump outside unlabeled block
--echo #
DELIMITER $$;
CREATE or replace procedure f14(lim INT, res OUT VARCHAR)
AS
a INT;
BEGIN
a:=0;
loop
a:=a+1;
if (a<10) then
continue;
end if;
if (a>=lim) then
goto lab14;
end if;
if (a>=20) then
exit;
end if;
end loop;
<<lab14>>
res:=a;
return ;
END;
$$
DELIMITER ;$$
SET @res='';
CALL f14(15, @res);
SELECT 'f14',@res;
CALL f14(8, @res);
SELECT 'f14',@res;
CALL f14(25, @res);
SELECT 'f14',@res;
DROP PROCEDURE f14;
--echo #
--echo # Jump inside/outside labeled block
--echo #
DELIMITER $$;
CREATE or replace procedure f15(lim INT, res OUT VARCHAR)
AS
a INT;
BEGIN
a:=0;
<<looplabel>> loop
<<beginlooplabel>>
a:=a+1;
if (a<10) then
continue looplabel;
end if;
if (a>=lim) then
goto lab15;
end if;
if (a>=20) then
exit looplabel;
end if;
goto beginlooplabel;
end loop;
<<lab15>>
res:=a;
return ;
END;
$$
DELIMITER ;$$
SET @res='';
CALL f15(15, @res);
SELECT 'f15',@res;
CALL f15(8, @res);
SELECT 'f15',@res;
CALL f15(25, @res);
SELECT 'f15',@res;
DROP PROCEDURE f15;
--echo #
--echo # Jump from if / else
--echo #
DELIMITER $$;
CREATE or replace procedure f16(lim INT, res OUT VARCHAR)
AS
a INT;
BEGIN
if (lim<10) then
goto lab16_1;
else
goto lab16_2;
end if;
<<lab16_1>>
res:='if lab16_1';
goto lab16_3;
<<lab16_2>>
res:='else lab16_2';
goto lab16_3;
res:='error lab16_3';
<<lab16_3>>
return ;
END;
$$
DELIMITER ;$$
SET @res='';
CALL f16(15, @res);
SELECT 'f16',@res;
CALL f16(8, @res);
SELECT 'f16',@res;
DROP PROCEDURE f16;
--echo #
--echo # Jump with cursors
--echo #
DELIMITER $$;
CREATE or replace procedure f17(lim INT, res OUT VARCHAR)
AS
v_a INT;
v_b VARCHAR(10);
CURSOR cur1 IS SELECT 1 FROM dual where 1=2;
BEGIN
OPEN cur1;
LOOP
FETCH cur1 INTO v_a;
EXIT WHEN cur1%NOTFOUND;
END LOOP;
CLOSE cur1;
<<lab17>>
lim:=lim-1;
begin
declare
CURSOR cur1 IS SELECT 1 FROM dual;
CURSOR cur2 IS SELECT 1 FROM dual where 1=2;
begin
LOOP
OPEN cur1;
FETCH cur1 INTO v_a;
EXIT WHEN cur1%NOTFOUND;
res:=res||'-'||lim ;
close cur1;
if (lim>0) then
goto lab17;
else
goto lab17_end;
end if;
END LOOP;
end;
<<lab17_end>>
null;
end;
END;
$$
DELIMITER ;$$
SET @res='';
CALL f17(5, @res);
SELECT 'f17',@res;
DROP PROCEDURE f17;
--echo #
--echo # Jump outside case
--echo #
DELIMITER $$;
CREATE or replace procedure f18(lim INT, res OUT VARCHAR)
AS
a INT;
BEGIN
case lim
when 1 then
res:='case branch 18_1';
goto lab18_1;
res:='error';
when 2 then
res:='case branch 18_2';
goto lab18_2;
res:='error';
else
res:='default branch 18';
end case;
<<lab18_1>>
null;
<<lab18_2>>
return ;
END;
$$
DELIMITER ;$$
SET @res='';
CALL f18(0, @res);
SELECT 'f18',@res;
CALL f18(1, @res);
SELECT 'f18',@res;
CALL f18(2, @res);
SELECT 'f18',@res;
DROP PROCEDURE f18;
--echo #
--echo # Jump inside/outside case block
--echo #
DELIMITER $$;
CREATE or replace procedure f19(lim INT, res OUT VARCHAR)
AS
a INT;
BEGIN
a:=1;
case lim
when 1 then
<<lab19_0>>
a:=a+1;
if (a<10) then
goto lab19_0;
else
goto lab19_1;
end if;
res:='case branch 19_1';
else
res:='default branch 18';
end case;
goto lab19_end;
<<lab19_1>>
res:=a;
<<lab19_end>>
return ;
END;
$$
DELIMITER ;$$
SET @res='';
CALL f19(1, @res);
SELECT 'f19',@res;
DROP PROCEDURE f19;
DELIMITER $$;
--echo #
--echo # Jump outside labeled loop
--echo #
CREATE OR REPLACE PROCEDURE f20(res OUT VARCHAR)
AS
a INT := 1;
BEGIN
<<lab>>
FOR i IN a..10 LOOP
IF i = 5 THEN
a:= a+1;
goto lab;
END IF;
END LOOP;
res:=a;
END;
$$
DELIMITER ;$$
CALL f20(@res);
SELECT 'f20',@res;
DROP PROCEDURE f20;
DELIMITER $$;
--echo #
--echo # Jump (continue) labeled loop
--echo #
CREATE OR REPLACE PROCEDURE f23(res OUT VARCHAR)
AS
a INT := 1;
BEGIN
<<lab>>
FOR i IN a..10 LOOP
IF i = 5 THEN
a:= a+1;
continue lab;
END IF;
END LOOP;
res:=a;
END;
$$
DELIMITER ;$$
CALL f23(@res);
SELECT 'f23',@res;
DROP PROCEDURE f23;
DELIMITER $$;
--echo #
--echo # Two consecutive label (backward jump)
--echo #
CREATE OR REPLACE PROCEDURE p24(action IN INT, res OUT varchar) AS
a integer;
BEGIN
<<lab1>>
<<lab2>>
if (action = 1) then
res:=res||' '||action;
action:=2;
goto lab1;
end if;
if (action = 2) then
res:=res||' '||action;
action:=3;
goto lab2;
end if;
END;
$$
DELIMITER ;$$
call p24(1,@res);
select 'p24',@res;
DROP PROCEDURE p24;
DELIMITER $$;
--echo #
--echo # Two consecutive label (backward and forward jump)
--echo #
CREATE OR REPLACE PROCEDURE p25(action IN INT, res OUT varchar) AS
a integer;
BEGIN
if (action = 1) then
res:=res||' '||action;
action:=2;
goto lab2;
end if;
goto lab_end;
<<lab1>>
<<lab2>>
res:=res||' '||action;
if (action = 2) then
res:=res||' '||action;
action:=3;
goto lab1;
end if;
<<lab_end>>
null;
END;
$$
DELIMITER ;$$
call p25(1,@res);
select 'p25',@res;
DROP PROCEDURE p25;
DELIMITER $$;
--echo #
--echo # Two consecutive label, continue to wrong label
--error ER_SP_LILABEL_MISMATCH
CREATE OR REPLACE PROCEDURE p26(action IN INT, res OUT varchar) AS
BEGIN
<<lab1>>
<<lab2>>
FOR i IN 1..10 LOOP
continue lab1;
END LOOP;
END;
$$
DELIMITER ;$$
DELIMITER $$;
--echo #
--echo # Consecutive goto label and block label
--echo #
CREATE OR REPLACE PROCEDURE p27(action IN INT, res OUT varchar) AS
BEGIN
res:='';
<<lab1>>
<<lab2>>
FOR i IN 1..10 LOOP
if (action = 1) then
res:=res||' '||action||'-'||i;
action:=2;
continue lab2;
end if;
if (action = 2) then
res:=res||' '||action||'-'||i;
action:='3';
goto lab2;
end if;
if (action = 3) then
res:=res||' '||action||'-'||i;
action:='4';
goto lab1;
end if;
if (action = 4) then
res:=res||' '||action||'-'||i;
exit lab2;
end if;
END LOOP;
END;
$$
DELIMITER ;$$
call p27(1,@res);
select 'p27',@res;
DROP PROCEDURE p27;
--echo # ----------------------
--echo # -- TEST IN FUNCTION --
--echo # ----------------------
--echo #
--echo # FUNCTION : Backward jump
--echo #
DELIMITER $$;
CREATE or replace function func1()
return varchar
AS
p2 varchar(10);
BEGIN
p2:='a';
<<lab1>>
if (p2='a') then
p2:=p2||'b';
goto lab1;
end if;
if (p2='ab') then
p2:=p2||'c';
end if;
return p2;
END;
$$
DELIMITER ;$$
select 'func1',func1();
DROP function func1;
--echo #
--echo # FUNCTION : forward jump
--echo #
DELIMITER $$;
CREATE or replace function func2()
return varchar
AS
p2 varchar(10);
BEGIN
p2:='a';
if (p2='a') then
goto lab1;
end if;
p2:='b';
<<lab1>>
return p2;
END;
$$
DELIMITER ;$$
select 'func2',func2();
DROP function func2;
--echo # ---------------------
--echo # -- TEST IN TRIGGER --
--echo # ---------------------
--echo #
--echo # TRIGGER : forward jump
--echo #
CREATE TABLE t1 (a INT);
DELIMITER $$;
CREATE TRIGGER trg1 BEFORE INSERT ON t1 FOR EACH ROW
BEGIN
IF :NEW.a IS NULL
THEN
:NEW.a:= 15;
goto end_trg;
END IF;
:NEW.a:= 10;
<<end_trg>>
null;
END;
$$
DELIMITER ;$$
insert into t1 values (1);
insert into t1 values (null);
SELECT * FROM t1;
DROP TRIGGER trg1;
DROP TABLE t1;

View File

@ -0,0 +1,9 @@
--eval CREATE FUNCTION f1(param $type) RETURN $type AS BEGIN RETURN param; END;
SHOW CREATE FUNCTION f1;
--eval SELECT LENGTH(f1(REPEAT('a',$length)));
--eval CREATE TABLE t1 AS SELECT f1(REPEAT('a',$length)) AS a;
SHOW CREATE TABLE t1;
DROP TABLE t1;
DROP FUNCTION f1;

View File

@ -0,0 +1,37 @@
SET sql_mode=ORACLE;
--echo #
--echo # MDEV-10596 Allow VARCHAR and VARCHAR2 without length as a data type of routine parameters and in RETURN clause
--echo #
--let type = CHAR
--let length = 2000
--source sp-param.inc
--let type = NCHAR
--let length = 2000
--source sp-param.inc
--let type = BINARY
--let length = 2000
--source sp-param.inc
--let type = VARCHAR
--let length = 4000
--source sp-param.inc
--let type = VARCHAR2
--let length = 4000
--source sp-param.inc
--let type = NVARCHAR
--let length = 4000
--source sp-param.inc
--let type = VARBINARY
--let length = 4000
--source sp-param.inc
--let type = RAW
--let length = 4000
--source sp-param.inc

View File

@ -0,0 +1,6 @@
--let $query= CREATE PROCEDURE p1() AS var $type; rec ROW(var $type); BEGIN CREATE TABLE t1 AS SELECT var,rec.var FROM DUAL;END
--eval $query
CALL p1();
SHOW CREATE TABLE t1;
DROP TABLE t1;
DROP PROCEDURE p1;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,346 @@
--source include/not_embedded.inc
SET sql_mode=ORACLE;
--echo #
--echo # MDEV-10577 sql_mode=ORACLE: %TYPE in variable declarations
--echo #
--echo #
--echo # Initiation:
--echo # - creating database db1
--echo # - creating user user1 with access rights to db1
--echo #
CREATE DATABASE db1;
CREATE TABLE db1.t1 (a INT, b VARCHAR(10));
CREATE USER user1;
GRANT ALL PRIVILEGES ON test.* TO user1;
connect (conn1,localhost,user1,,test);
SET sql_mode=ORACLE;
SELECT database();
SELECT user();
--echo #
--echo # Making sure that user1 does not have privileges to db1.t1
--echo #
--error ER_TABLEACCESS_DENIED_ERROR
SHOW CREATE TABLE db1.t1;
--error ER_TABLEACCESS_DENIED_ERROR
SHOW FIELDS IN db1.t1;
--echo #
--echo # Trigger: using %TYPE with a table we don't have access to
--echo #
CREATE TABLE test.t1 (a INT, b INT);
INSERT INTO test.t1 (a,b) VALUES (10,20);
SELECT * FROM t1;
DELIMITER $$;
CREATE TRIGGER test.tr1 BEFORE INSERT ON test.t1 FOR EACH ROW
BEGIN
DECLARE b db1.t1.b%TYPE := 20;
BEGIN
:NEW.b := 10;
END;
END
$$
DELIMITER ;$$
--error ER_TABLEACCESS_DENIED_ERROR
INSERT INTO t1 (a) VALUES (10);
SELECT * FROM t1;
DROP TRIGGER tr1;
DROP TABLE t1;
--echo #
--echo # Stored procedure: Using %TYPE for with a table that we don't have access to
--echo # DEFINER user1, SQL SECURITY DEFAULT
--echo #
DELIMITER $$;
CREATE PROCEDURE p1()
AS
a db1.t1.a%TYPE := 10;
BEGIN
SELECT a;
END;
$$
DELIMITER ;$$
--error ER_TABLEACCESS_DENIED_ERROR
CALL p1;
DROP PROCEDURE p1;
DELIMITER $$;
CREATE PROCEDURE p1()
AS
a db1.t1%ROWTYPE;
BEGIN
SELECT a.a;
END;
$$
DELIMITER ;$$
--error ER_TABLEACCESS_DENIED_ERROR
CALL p1;
DROP PROCEDURE p1;
--echo #
--echo # Stored procedure: Using %TYPE for with a table that we don't have access to
--echo # DEFINER root, SQL SECURITY INVOKER
--echo #
connection default;
DELIMITER $$;
CREATE PROCEDURE p1()
SQL SECURITY INVOKER
AS
a db1.t1.a%TYPE := 10;
BEGIN
SELECT a;
END;
$$
DELIMITER ;$$
connection conn1;
--error ER_TABLEACCESS_DENIED_ERROR
CALL p1;
DROP PROCEDURE p1;
connection default;
DELIMITER $$;
CREATE PROCEDURE p1()
SQL SECURITY INVOKER
AS
a db1.t1%ROWTYPE;
BEGIN
SELECT a.a;
END;
$$
DELIMITER ;$$
connection conn1;
--error ER_TABLEACCESS_DENIED_ERROR
CALL p1;
DROP PROCEDURE p1;
--echo #
--echo # Stored procedure: Using %TYPE for with a table that we don't have access to
--echo # DEFINER root, SQL SECURITY DEFINER
--echo #
connection default;
DELIMITER $$;
CREATE PROCEDURE p1()
SQL SECURITY DEFINER
AS
a db1.t1.a%TYPE := 10;
BEGIN
SELECT a;
END;
$$
DELIMITER ;$$
connection conn1;
CALL p1;
DROP PROCEDURE p1;
connection default;
DELIMITER $$;
CREATE PROCEDURE p1()
SQL SECURITY DEFINER
AS
a db1.t1%ROWTYPE;
BEGIN
a.a:= 10;
SELECT a.a;
END;
$$
DELIMITER ;$$
connection conn1;
CALL p1;
DROP PROCEDURE p1;
--echo #
--echo # Stored function: Using %TYPE for with a table that we don't have access to
--echo # DEFINER user1, SQL SECURITY DEFAULT
--echo #
CREATE TABLE t1 (a INT);
DELIMITER $$;
CREATE FUNCTION f1() RETURN INT
AS
a db1.t1.a%TYPE:=0;
BEGIN
RETURN OCTET_LENGTH(a);
END;
$$
DELIMITER ;$$
--error ER_TABLEACCESS_DENIED_ERROR
SELECT f1();
DROP FUNCTION f1;
DROP TABLE t1;
--echo #
--echo # Stored function: Using %TYPE for with a table that we don't have access to
--echo # DEFINER root, SQL SECURITY INVOKER
--echo #
connection default;
CREATE TABLE t1 (a INT);
DELIMITER $$;
CREATE FUNCTION f1() RETURN INT
SQL SECURITY INVOKER
AS
a db1.t1.a%TYPE:=0;
BEGIN
RETURN OCTET_LENGTH(a);
END;
$$
DELIMITER ;$$
connection conn1;
--error ER_TABLEACCESS_DENIED_ERROR
SELECT f1();
DROP FUNCTION f1;
DROP TABLE t1;
--echo #
--echo # Stored function: Using %TYPE for with a table that we don't have access to
--echo # DEFINER root, SQL SECURITY DEFINER
--echo #
connection default;
CREATE TABLE t1 (a INT);
DELIMITER $$;
CREATE FUNCTION f1() RETURN INT
SQL SECURITY DEFINER
AS
a db1.t1.a%TYPE:=0;
BEGIN
RETURN OCTET_LENGTH(a);
END;
$$
DELIMITER ;$$
connection conn1;
SELECT f1();
DROP FUNCTION f1;
DROP TABLE t1;
connection default;
# qc_sqlite: GRANT SELECT (a) ON db1.t1 TO user1;
# qc_sqlite: Does not collect database/able names.
connection conn1;
--echo #
--echo # Making sure that user1 has access to db1.t1.a, but not to db1.t1.b
--echo #
--error ER_TABLEACCESS_DENIED_ERROR
SHOW CREATE TABLE db1.t1;
SHOW FIELDS IN db1.t1;
--echo #
--echo # Trigger: Per-column privileges
--echo #
CREATE TABLE test.t1 (a INT, b INT);
INSERT INTO test.t1 (a,b) VALUES (10,20);
SELECT * FROM t1;
# %TYPE reference using a column we have access to
DELIMITER $$;
CREATE TRIGGER test.tr1 BEFORE INSERT ON test.t1 FOR EACH ROW
BEGIN
DECLARE a db1.t1.a%TYPE := 20;
BEGIN
:NEW.b := 10;
END;
END
$$
DELIMITER ;$$
INSERT INTO t1 (a) VALUES (10);
SELECT * FROM t1;
DROP TRIGGER tr1;
# %TYPE reference using a column that we don't have access to
DELIMITER $$;
CREATE TRIGGER test.tr1 BEFORE INSERT ON test.t1 FOR EACH ROW
BEGIN
DECLARE b db1.t1.b%TYPE := 20;
BEGIN
:NEW.b := 10;
END;
END
$$
DELIMITER ;$$
--error ER_COLUMNACCESS_DENIED_ERROR
INSERT INTO t1 (a) VALUES (10);
SELECT * FROM t1;
DROP TRIGGER tr1;
DROP TABLE t1;
--echo #
--echo # Stored procedure: Per-column privileges
--echo # DEFINER user1, SQL SECURITY DEFAULT
--echo #
DELIMITER $$;
CREATE PROCEDURE p1()
AS
a db1.t1.a%TYPE := 10;
BEGIN
SELECT a;
END;
$$
DELIMITER ;$$
CALL p1;
DROP PROCEDURE p1;
DELIMITER $$;
CREATE PROCEDURE p1()
AS
b db1.t1.b%TYPE := 10;
BEGIN
SELECT b;
END;
$$
DELIMITER ;$$
--error ER_COLUMNACCESS_DENIED_ERROR
CALL p1;
DROP PROCEDURE p1;
DELIMITER $$;
CREATE PROCEDURE p1()
AS
b db1.t1%ROWTYPE;
BEGIN
b.b:=10;
SELECT b.b;
END;
$$
DELIMITER ;$$
--error ER_COLUMNACCESS_DENIED_ERROR
CALL p1;
DROP PROCEDURE p1;
--echo #
--echo # Clean up
--echo #
disconnect conn1;
connection default;
DROP USER user1;
DROP DATABASE db1;
--echo #
--echo # End of MDEV-10577 sql_mode=ORACLE: %TYPE in variable declarations
--echo #

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,106 @@
set sql_mode=ORACLE;
--error ER_PARSE_ERROR
:NEW.a := 1;
--error ER_PARSE_ERROR
:OLD.a := 1;
--error ER_PARSE_ERROR
:OLa.a := 1;
--error ER_PARSE_ERROR
SELECT :NEW.a;
--error ER_PARSE_ERROR
SELECT :OLD.a;
--error ER_PARSE_ERROR
SELECT :OLa.a;
CREATE TABLE t1 (a INT);
CREATE TRIGGER tr1 BEFORE INSERT ON t1 FOR EACH ROW NEW.a:= 10;
INSERT INTO t1 VALUES ();
SELECT * FROM t1;
DROP TRIGGER tr1;
DROP TABLE t1;
CREATE TABLE t1 (a INT);
CREATE TRIGGER tr1 BEFORE INSERT ON t1 FOR EACH ROW :NEW.a:= 10;
INSERT INTO t1 VALUES ();
SELECT * FROM t1;
DROP TRIGGER tr1;
DROP TABLE t1;
CREATE TABLE t1 (a INT);
DELIMITER /;
CREATE TRIGGER tr1 BEFORE INSERT ON t1 FOR EACH ROW
BEGIN
IF :NEW.a IS NULL
THEN
:NEW.a:= 10;
END IF;
END;
/
DELIMITER ;/
INSERT INTO t1 VALUES (NULL);
SELECT * FROM t1;
DROP TRIGGER tr1;
DROP TABLE t1;
CREATE TABLE t1 (a INT);
DELIMITER /;
CREATE TRIGGER tr1 BEFORE UPDATE ON t1 FOR EACH ROW
BEGIN
IF :OLD.a IS NULL
THEN
:NEW.a:= 10;
END IF;
END;
/
DELIMITER ;/
INSERT INTO t1 VALUES (NULL);
UPDATE t1 SET a=NULL;
SELECT * FROM t1;
DROP TRIGGER tr1;
DROP TABLE t1;
CREATE TABLE t1 (a INT, b INT, c INT);
DELIMITER /;
CREATE TRIGGER tr1 BEFORE INSERT ON t1
FOR EACH ROW
DECLARE
cnt INT := 0;
BEGIN
IF :NEW.a IS NULL THEN cnt:=cnt+1; END IF;
IF :NEW.b IS NULL THEN cnt:=cnt+1; END IF;
IF :NEW.c IS NULL THEN :NEW.c:=cnt; END IF;
END;
/
DELIMITER ;/
INSERT INTO t1 VALUES ();
INSERT INTO t1 VALUES (1, NULL, NULL);
INSERT INTO t1 VALUES (NULL, 1, NULL);
INSERT INTO t1 VALUES (1, 1, NULL);
SELECT * FROM t1;
DROP TABLE t1;
--echo #
--echo # MDEV-10577 sql_mode=ORACLE: %TYPE in variable declarations
--echo #
CREATE TABLE t1 (a INT, b INT, total INT);
DELIMITER $$;
CREATE TRIGGER tr1 BEFORE INSERT ON t1
FOR EACH ROW
DECLARE
va t1.a%TYPE:= :NEW.a;
vb t1.b%TYPE:= :NEW.b;
BEGIN
:NEW.total:= va + vb;
END;
$$
DELIMITER ;$$
INSERT INTO t1 (a,b) VALUES (10, 20);
SELECT * FROM t1;
DROP TABLE t1;

View File

@ -0,0 +1,16 @@
SET sql_mode=ORACLE;
--echo #
--echo # MDEV-10588 sql_mode=ORACLE: TRUNCATE TABLE t1 [ {DROP|REUSE} STORAGE ]
--echo #
CREATE TABLE t1 (a INT);
TRUNCATE TABLE t1 REUSE STORAGE;
TRUNCATE TABLE t1 DROP STORAGE;
DROP TABLE t1;
# REUSE is actually a reserved word in Oracle.
# But we don't reserve it for MDEV-10588
CREATE TABLE reuse (reuse INT);
DROP TABLE reuse;

View File

@ -0,0 +1,4 @@
SET sql_mode=ORACLE;
CREATE TABLE t1 (a BLOB);
SHOW CREATE TABLE t1;
DROP TABLE t1;

View File

@ -0,0 +1,10 @@
SET sql_mode=ORACLE;
# CLOB is not a reserved word even in sql_mode=ORACLE
CREATE TABLE clob (clob INT);
SHOW CREATE TABLE clob;
DROP TABLE clob;
CREATE TABLE t1 (a CLOB);
SHOW CREATE TABLE t1;
DROP TABLE t1;

View File

@ -0,0 +1,4 @@
SET sql_mode=ORACLE;
CREATE TABLE t1 (a DATE);
SHOW CREATE TABLE t1;
DROP TABLE t1;

View File

@ -0,0 +1,9 @@
SET sql_mode=ORACLE;
CREATE TABLE t1 (a NUMBER);
SHOW CREATE TABLE t1;
DROP TABLE t1;
CREATE TABLE t1 (a NUMBER(10,2));
SHOW CREATE TABLE t1;
DROP TABLE t1;

View File

@ -0,0 +1,10 @@
SET sql_mode=ORACLE;
# RAW is not a reserved word even in sql_mode=ORACLE
CREATE TABLE raw (raw INT);
SHOW CREATE TABLE raw;
DROP TABLE raw;
CREATE TABLE t1 (a RAW(10));
SHOW CREATE TABLE t1;
DROP TABLE t1;

View File

@ -0,0 +1,9 @@
SET sql_mode=ORACLE;
--echo #
--echo # MDEV-11275 sql_mode=ORACLE: CAST(..AS VARCHAR(N))
--echo #
SELECT CAST(123 AS VARCHAR(10)) FROM DUAL;
--error ER_PARSE_ERROR
SELECT CAST(123 AS VARCHAR) FROM DUAL;

View File

@ -0,0 +1,19 @@
SET sql_mode=ORACLE;
# VARCHAR2 is not a reserved word even in sql_mode=ORACLE
CREATE TABLE varchar2 (varchar2 INT);
SHOW CREATE TABLE varchar2;
DROP TABLE varchar2;
CREATE TABLE t1 (a VARCHAR2(10));
SHOW CREATE TABLE t1;
DROP TABLE t1;
--echo #
--echo # MDEV-11275 sql_mode=ORACLE: CAST(..AS VARCHAR(N))
--echo #
SELECT CAST(123 AS VARCHAR2(10)) FROM DUAL;
--error ER_PARSE_ERROR
SELECT CAST(123 AS VARCHAR2) FROM DUAL;

View File

@ -0,0 +1,38 @@
SET sql_mode=oracle;
--echo #
--echo # MDEV-10411 Providing compatibility for basic PL/SQL constructs
--echo # Part 6: Assignment operator
--echo #
max_sort_length:=1030;
SELECT @@max_sort_length;
max_sort_length:=DEFAULT;
--echo #
--echo # Testing that SP variables shadow global variables in assignments
--echo #
DELIMITER $$;
CREATE PROCEDURE p1
AS
BEGIN
max_sort_length:=1030;
DECLARE
max_sort_length INT DEFAULT 1031;
BEGIN
SELECT @@max_sort_length, max_sort_length;
max_sort_length:=1032;
SELECT @@max_sort_length, max_sort_length;
END;
SELECT @@max_sort_length;
max_sort_length:= DEFAULT;
END;
$$
DELIMITER ;$$
CALL p1();
DROP PROCEDURE p1;
--echo #
--echo # End of MDEV-10411 Providing compatibility for basic PL/SQL constructs (part 6)
--echo #

View File

@ -53,3 +53,7 @@ PREPARE stmt FROM 'UPDATE t2 AS A NATURAL JOIN v1 B SET B.f1 = 1';
# (Sqlite3 error: SQL logic error or missing database, near "SET": syntax error):
# "UPDATE t2 AS A NATURAL JOIN v1 B SET B.f1 = 1"
SELECT LENGTH(_utf8 0xC39F), LENGTH(CHAR(14844588 USING utf8));
# warning: [qc_sqlite] Statement was classified only based on keywords
# (Sqlite3 error: SQL logic error or missing database, near "0xC39F": syntax error):
# "SELECT LENGTH(_utf8 0xC39F), LENGTH(CHAR(14844588 USING utf8));"

View File

@ -14,6 +14,7 @@
#include "testreader.hh"
#include <algorithm>
#include <map>
#include <iostream>
using std::istream;
using std::string;
@ -149,19 +150,34 @@ void init_keywords()
}
}
skip_action_t get_action(const string& keyword)
skip_action_t get_action(const string& keyword, const string& delimiter)
{
skip_action_t action = SKIP_NOTHING;
string key(keyword);
std::transform(key.begin(), key.end(), key.begin(), ::tolower);
KeywordActionMapping::iterator i = mtl_keywords.find(key);
if (i != mtl_keywords.end())
if (key == "delimiter")
{
action = i->second;
// DELIMITER is directly understood by the parser so it needs to
// be handled explicitly.
action = SKIP_DELIMITER;
}
else if (delimiter == ";")
{
// Some mysqltest keywords, such as "while", "exit" and "if" are also
// PL/SQL keywords. We assume they can only be used in the former role,
// if the delimiter is ";".
string key(keyword);
std::transform(key.begin(), key.end(), key.begin(), ::tolower);
KeywordActionMapping::iterator i = mtl_keywords.find(key);
if (i != mtl_keywords.end())
{
action = i->second;
}
}
return action;
@ -193,7 +209,7 @@ TestReader::TestReader(istream& in,
size_t line)
: m_in(in)
, m_line(line)
, m_delimiter(';')
, m_delimiter(";")
{
init();
}
@ -216,6 +232,12 @@ TestReader::result_t TestReader::get_statement(std::string& stmt)
if (!line.empty() && (line.at(0) != '#'))
{
// Ignore comment lines.
if ((line.substr(0, 3) == "-- ") || (line.substr(0, 1) == "#"))
{
continue;
}
if (!skip)
{
if (line.substr(0, 2) == "--")
@ -228,7 +250,7 @@ TestReader::result_t TestReader::get_statement(std::string& stmt)
std::ptr_fun<int,int>(std::isspace));
string keyword = line.substr(0, i - line.begin());
skip_action_t action = get_action(keyword);
skip_action_t action = get_action(keyword, m_delimiter);
switch (action)
{
@ -244,7 +266,21 @@ TestReader::result_t TestReader::get_statement(std::string& stmt)
trim(line);
if (line.length() > 0)
{
m_delimiter = line.at(0);
if (line.length() >= m_delimiter.length())
{
if (line.substr(line.length() - m_delimiter.length()) == m_delimiter)
{
m_delimiter = line.substr(0, line.length() - m_delimiter.length());
}
else
{
m_delimiter = line;
}
}
else
{
m_delimiter = line;
}
}
continue;
@ -268,15 +304,42 @@ TestReader::result_t TestReader::get_statement(std::string& stmt)
stmt += line;
char c = line.at(line.length() - 1);
// Look for a ';'. If we are dealing with a one line test statment
// the delimiter will in practice be ';' and if it is a multi-line
// test statement then the test-script delimiter will be something
// else than ';' and ';' will be the delimiter used in the multi-line
// statement.
string::size_type i = line.find(";");
if (i != string::npos)
{
// Is there a "-- " or "#" after the delimiter?
if ((line.find("-- ", i) != string::npos) ||
(line.find("#", i) != string::npos))
{
// If so, add a newline. Otherwise the rest of the
// statement would be included in the comment.
stmt += "\n";
}
// This is somewhat fragile as a ";", "#" or "-- " inside a
// string will trigger this behaviour...
}
string c;
if (line.length() >= m_delimiter.length())
{
c = line.substr(line.length() - m_delimiter.length());
}
if (c == m_delimiter)
{
if (c != ';')
if (c != ";")
{
// If the delimiter was something else but ';' we need to
// remove that before giving the line to the classifiers.
stmt.erase(stmt.length() - 1);
stmt.erase(stmt.length() - m_delimiter.length());
}
if (!skip)

View File

@ -76,7 +76,7 @@ private:
private:
std::istream& m_in; /*< The stream we are using. */
size_t m_line; /*< The current line. */
char m_delimiter; /*< The current delimiter. */
std::string m_delimiter; /*< The current delimiter. */
};
};

View File

@ -134,7 +134,7 @@ int main(int argc, char* argv[])
set_libdir(strdup(LIBDIR));
if (qc_setup(QC_LIB, NULL))
if (qc_setup(QC_LIB, QC_SQL_MODE_DEFAULT, NULL))
{
if (qc_process_init(QC_INIT_BOTH))
{