From 4b805ecaf86b1761982d64521dee593fd6928c26 Mon Sep 17 00:00:00 2001 From: Lawrin Novitsky Date: Mon, 15 May 2017 09:36:34 +0200 Subject: [PATCH 1/2] [ODBC-95] If any of statements in a bacth could not be prepared, application would crash on the attempt to drom that stmt handle. Fix and testcase --- ma_helper.c | 1 + test/multistatement.c | 14 ++++++++++++-- test/tap.h | 5 ++++- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/ma_helper.c b/ma_helper.c index 1bb5cafc..a14a62bb 100644 --- a/ma_helper.c +++ b/ma_helper.c @@ -32,6 +32,7 @@ void CloseMultiStatements(MADB_Stmt *Stmt) } MADB_FREE(Stmt->MultiStmts); Stmt->MultiStmtCount= 0; + Stmt->stmt= NULL; } diff --git a/test/multistatement.c b/test/multistatement.c index 524270ef..bf26803d 100644 --- a/test/multistatement.c +++ b/test/multistatement.c @@ -196,7 +196,7 @@ ODBC_TEST(test_semicolon) } /* Double quote inside single quotes caused error in parsing while*/ -ODBC_TEST(t_odbc_74) +ODBC_TEST(t_odbc74) { SQLCHAR ref[][4]={"\"", "'", "*/", "/*", "end", "one", "two"}, val[4]; unsigned int i; @@ -229,6 +229,15 @@ ODBC_TEST(t_odbc_74) return OK; } +ODBC_TEST(t_odbc95) +{ + EXPECT_STMT(Stmt, SQLPrepare(Stmt, "SELECT 1;INSERT INTO non_existing VALUES(2)", SQL_NTS), SQL_ERROR); + CHECK_STMT_RC(Stmt, SQLFreeStmt(Stmt, SQL_DROP)); + Stmt= NULL; + + return OK; +} + MA_ODBC_TESTS my_tests[]= { {test_multi_statements, "test_multi_statements"}, @@ -237,7 +246,8 @@ MA_ODBC_TESTS my_tests[]= {test_params, "test_params"}, {t_odbc_16, "test_odbc_16"}, {test_semicolon, "test_semicolon_in_string"}, - {t_odbc_74, "test_odbc_74"}, + {t_odbc74, "t_odbc74"}, + {t_odbc95, "t_odbc95" }, {NULL, NULL} }; diff --git a/test/tap.h b/test/tap.h index 043a8f9a..3b945e0a 100644 --- a/test/tap.h +++ b/test/tap.h @@ -829,7 +829,10 @@ int run_tests(MA_ODBC_TESTS *tests) buff_pos= buff_before_test; *buff_pos= 0; - SQLFreeStmt(Stmt, SQL_DROP); + if (Stmt != NULL) + { + SQLFreeStmt(Stmt, SQL_DROP); + } SQLAllocHandle(SQL_HANDLE_STMT, Connection, &Stmt); /* reset Statement */ fflush(stdout); From 6a1619befbe800e3e7e27ca7dd8c87f2da0d3393 Mon Sep 17 00:00:00 2001 From: Lawrin Novitsky Date: Tue, 16 May 2017 04:44:37 +0200 Subject: [PATCH 2/2] [ODBC-97] Fix and addition to old testcase to care about this issue. The problem was that parser would think of \ that it escapes closing quote, even if it was escaped in its turn(i.e. if it represented the character in the string. Also the case of NO_BACKSLASH_ESCAPES is now respected. --- ma_helper.c | 35 +++++++++++++++++++++++++---------- test/multistatement.c | 37 ++++++++++++++++++++++++++++++++----- 2 files changed, 57 insertions(+), 15 deletions(-) diff --git a/ma_helper.c b/ma_helper.c index a14a62bb..be58bed1 100644 --- a/ma_helper.c +++ b/ma_helper.c @@ -85,16 +85,24 @@ unsigned int GetMultiStatements(MADB_Stmt *Stmt, char *StmtStr, SQLINTEGER Lengt { end= StmtStr + Length - 1; while (end > StmtStr && (isspace(*end) || *end == ';')) + { --end; - Length= (SQLINTEGER)(end - StmtStr); + --Length; + } } - p= StmtCopy= my_strdup(StmtStr, MYF(0)); + /* We can have here not NULL-terminated string as a source, thus we need to allocate, copy meaningful characters and + add NULL */ + p= StmtCopy= MADB_ALLOC(Length + 1); + strncpy(StmtCopy, StmtStr, Length); + StmtCopy[Length]= '\0'; + + end= StmtCopy + Length; - while (p < StmtCopy + Length) + while (p < end) { if (wait_char) { - if (*p == wait_char && *prev != '\\') /* What if that is escaped backslash? */ + if (*p == wait_char && *prev != '\\') { wait_char= 0; } @@ -103,7 +111,7 @@ unsigned int GetMultiStatements(MADB_Stmt *Stmt, char *StmtStr, SQLINTEGER Lengt { switch (*p) { case '-': - if (!STRING_OR_COMMENT() && (p < StmtCopy + Length + 1) && (char)*(p+1) == '-') + if (!STRING_OR_COMMENT() && (p < end - 1) && (char)*(p+1) == '-') { wait_char= '\n'; } @@ -122,19 +130,26 @@ unsigned int GetMultiStatements(MADB_Stmt *Stmt, char *StmtStr, SQLINTEGER Lengt } break; case '/': - if (!STRING_OR_COMMENT() && (p < StmtCopy + Length + 1) && (char)*(p+1) == '*') + if (!STRING_OR_COMMENT() && (p < end - 1) && (char)*(p+1) == '*') comment= 1; else if (comment && (p > StmtCopy) && (char)*(prev) == '*') comment= 0; break; case '"': - if (prev && *prev != '\\') - quote[0] = !STRING_OR_COMMENT(); + quote[0] = !STRING_OR_COMMENT(); break; case '\'': - if (prev && *prev != '\\') - quote[1] = !STRING_OR_COMMENT(); + quote[1] = !STRING_OR_COMMENT(); break; + case '\\': + /* *end is \0, so we are safe here to increment by 2 bytes */ + if ((Stmt->Connection->mariadb->server_status & SERVER_STATUS_NO_BACKSLASH_ESCAPES) == 0 && p < end - 1) + { + /* Skipping escaped character and resetting prev */ + p+= 2; + prev= 0; + continue; + } default: break; } diff --git a/test/multistatement.c b/test/multistatement.c index bf26803d..6be5f450 100644 --- a/test/multistatement.c +++ b/test/multistatement.c @@ -195,11 +195,14 @@ ODBC_TEST(test_semicolon) return OK; } -/* Double quote inside single quotes caused error in parsing while*/ +/* Double quote inside single quotes caused error in parsing while + Also tests ODBC-97*/ ODBC_TEST(t_odbc74) { - SQLCHAR ref[][4]={"\"", "'", "*/", "/*", "end", "one", "two"}, val[4]; + SQLCHAR ref[][4]={"\"", "'", "*/", "/*", "end", "one\\", "two\\"}, val[8]; unsigned int i; + SQLHDBC hdbc1; + SQLHSTMT Stmt1; OK_SIMPLE_STMT(Stmt, "DROP TABLE IF EXISTS odbc74; CREATE TABLE odbc74(id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,\ val VARCHAR(64) NOT NULL)"); @@ -212,8 +215,8 @@ ODBC_TEST(t_odbc74) # ;Unhappy comment at the end "); OK_SIMPLE_STMT(Stmt, "-- comment ;1 \n\ # comment ;2 \n\ - INSERT INTO odbc74 (val) VALUES('one');\ - INSERT INTO odbc74 (val) VALUES(\"two\");"); + INSERT INTO odbc74 (val) VALUES('one\\\\');\ + INSERT INTO odbc74 (val) VALUES(\"two\\\\\");"); OK_SIMPLE_STMT(Stmt, "SELECT val FROM odbc74 ORDER BY id"); for (i= 0; i < sizeof(ref)/sizeof(ref[0]); ++i) @@ -224,6 +227,30 @@ ODBC_TEST(t_odbc74) EXPECT_STMT(Stmt, SQLFetch(Stmt), SQL_NO_DATA); CHECK_STMT_RC(Stmt, SQLFreeStmt(Stmt, SQL_CLOSE)); + OK_SIMPLE_STMT(Stmt, "TRUNCATE TABLE odbc74"); + + AllocEnvConn(&Env, &hdbc1); + Stmt1= DoConnect(&hdbc1, NULL, NULL, NULL, 0, NULL, 0, NULL, NULL); + FAIL_IF(Stmt1 == NULL, "Could not connect and/or allocate"); + + OK_SIMPLE_STMT(Stmt1, "SET @@SESSION.sql_mode='NO_BACKSLASH_ESCAPES'"); + + OK_SIMPLE_STMT(Stmt1, "INSERT INTO odbc74 (val) VALUES('one\\');\ + INSERT INTO odbc74 (val) VALUES(\"two\\\");"); + OK_SIMPLE_STMT(Stmt1, "SELECT val FROM odbc74 ORDER BY id"); + + /* We only have to last rows */ + for (i= sizeof(ref)/sizeof(ref[0]) - 2; i < sizeof(ref)/sizeof(ref[0]); ++i) + { + CHECK_STMT_RC(Stmt1, SQLFetch(Stmt1)); + IS_STR(my_fetch_str(Stmt1, val, 1), ref[i], sizeof(ref[i])); + } + EXPECT_STMT(Stmt1, SQLFetch(Stmt1), SQL_NO_DATA); + + CHECK_STMT_RC(Stmt1, SQLFreeStmt(Stmt1, SQL_DROP)); + CHECK_DBC_RC(hdbc1, SQLDisconnect(hdbc1)); + CHECK_DBC_RC(hdbc1, SQLFreeConnect(hdbc1)); + OK_SIMPLE_STMT(Stmt, "DROP TABLE IF EXISTS odbc74"); return OK; @@ -246,7 +273,7 @@ MA_ODBC_TESTS my_tests[]= {test_params, "test_params"}, {t_odbc_16, "test_odbc_16"}, {test_semicolon, "test_semicolon_in_string"}, - {t_odbc74, "t_odbc74"}, + {t_odbc74, "t_odbc74and_odbc97"}, {t_odbc95, "t_odbc95" }, {NULL, NULL} };