diff --git a/ma_helper.c b/ma_helper.c index 0c9df8ff..a33d1ece 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; } @@ -84,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= _strdup(StmtStr); + /* 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; } @@ -102,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'; } @@ -121,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 524270ef..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*/ -ODBC_TEST(t_odbc_74) +/* 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_odbc_74) # ;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,11 +227,44 @@ ODBC_TEST(t_odbc_74) 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; } +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 +273,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_odbc74and_odbc97"}, + {t_odbc95, "t_odbc95" }, {NULL, NULL} }; diff --git a/test/tap.h b/test/tap.h index 145f42c6..5cf57650 100644 --- a/test/tap.h +++ b/test/tap.h @@ -832,7 +832,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);