Skip to content

Commit

Permalink
[ODBC-141] The fix and the testcase.
Browse files Browse the repository at this point in the history
The fix is more or less complete parsing and prepare routine refactoring.
As a side effect, addeded optimization to run batches, that don't contain returning
resultset statements, and that do not have any parameters, as a batches
via text protocol, and not preparing and executing them one by one. Also fixed numerous, but hopefully rather minor, parsing bugs.
  • Loading branch information
lawrinn committed May 16, 2018
1 parent ad6dbb5 commit 860c985
Show file tree
Hide file tree
Showing 16 changed files with 551 additions and 368 deletions.
2 changes: 1 addition & 1 deletion libmariadb
17 changes: 13 additions & 4 deletions ma_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,23 @@
MARIADB_CHARSET_INFO* utf16= NULL;
Client_Charset utf8= {CP_UTF8, NULL};

/* {{{ ltrim */
char* ltrim(char *Str)
{
/* I am not sure using iswspace, and not isspace makes any sense here. But probably does not hurt either */
while (Str && iswspace(Str[0]))
++Str;
return Str;
}
/* }}} */

/* {{{ trim */
char *trim(char *Str)
char* trim(char *Str)
{
char *end;
/* I am not sure using iswspace, and not isspace makes any sense here. But probably does not hurt either */
while (Str && iswspace(Str[0]))
Str++;

Str= ltrim(Str);

end= Str + strlen(Str) - 1;
while (iswspace(*end))
*end--= 0;
Expand Down
4 changes: 2 additions & 2 deletions ma_connection.c
Original file line number Diff line number Diff line change
Expand Up @@ -536,9 +536,9 @@ BOOL MADB_SqlMode(MADB_Dbc *Connection, enum enum_madb_sql_mode SqlMode)
switch (SqlMode)
{
case MADB_NO_BACKSLASH_ESCAPES:
return Connection->mariadb->server_status & SERVER_STATUS_NO_BACKSLASH_ESCAPES;
return test(Connection->mariadb->server_status & SERVER_STATUS_NO_BACKSLASH_ESCAPES);
case MADB_ANSI_QUOTES:
return Connection->mariadb->server_status & SERVER_STATUS_ANSI_QUOTES;
return test(Connection->mariadb->server_status & SERVER_STATUS_ANSI_QUOTES);
}
return FALSE;
}
Expand Down
201 changes: 58 additions & 143 deletions ma_helper.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,185 +25,100 @@ void CloseMultiStatements(MADB_Stmt *Stmt)
{
unsigned int i;

for (i=0; i < Stmt->MultiStmtCount; ++i)
for (i=0; i < Stmt->Query.MultiStmtCount; ++i)
{
MDBUG_C_PRINT(Stmt->Connection, "-->closing %0x", Stmt->MultiStmts[i]);
mysql_stmt_close(Stmt->MultiStmts[i]);
if (Stmt->MultiStmts[i] != NULL)
{
mysql_stmt_close(Stmt->MultiStmts[i]);
}
}
MADB_FREE(Stmt->MultiStmts);
Stmt->MultiStmtCount= 0;
Stmt->Query.MultiStmtCount= 0;
Stmt->stmt= NULL;
}


/* Required, but not sufficient condition */
BOOL QueryIsPossiblyMultistmt(char *queryStr)
BOOL QueryIsPossiblyMultistmt(MADB_QUERY *Query)
{
char *semicolon_pos= strchr(queryStr, ';');
/* String supposed to come here trimmed. Checking that semicolon is not last char in the string */
if (semicolon_pos != NULL && semicolon_pos < queryStr + strlen(queryStr) - 1)
{
/* CREATE PROCEDURE uses semicolons but is not supported in prepared statement
protocol */
if (!MADB_IsStatementSupported(queryStr, "CREATE", "PROCEDURE"))
return FALSE;
if (!MADB_IsStatementSupported(queryStr, "CREATE", "FUNCTION"))
return FALSE;
if (!MADB_IsStatementSupported(queryStr, "CREATE", "DEFINER"))
return FALSE;
return TRUE;
}

return FALSE;
return Query->QueryType != MADB_QUERY_CREATE_PROC && Query->QueryType != MADB_QUERY_CREATE_FUNC &&
Query->QueryType != MADB_QUERY_CREATE_DEFINER;
}

#define STRING_OR_COMMENT() (quote[0] || quote[1] || comment)
unsigned int GetMultiStatements(MADB_Stmt *Stmt, char *StmtStr, SQLINTEGER Length)
{
char *p, *prev= NULL, wait_char= 0;
unsigned int statements= 1;
int quote[2]= {0,0}, comment= 0;
char *end;
MYSQL_STMT *stmt;
char *StmtCopy= NULL;

/* mysql_stmt_init requires locking as well */
LOCK_MARIADB(Stmt->Connection);
stmt= mysql_stmt_init(Stmt->Connection->mariadb);

/* if the entire stmt string passes, we don't have multistatement */
if (stmt && !mysql_stmt_prepare(stmt, StmtStr, Length))
{
mysql_stmt_close(stmt);
UNLOCK_MARIADB(Stmt->Connection);
return 1;
}
mysql_stmt_close(stmt);

/* make sure we don't have trailing whitespace or semicolon */
if (Length)
/* Trims spaces and/or ';' at the end of query */
int SqlRtrim(char *StmtStr, int Length)
{
if (Length > 0)
{
end= StmtStr + Length - 1;
char *end= StmtStr + Length - 1;
while (end > StmtStr && (isspace(0x000000ff & *end) || *end == ';'))
{
*end= '\0';
--end;
--Length;
}
}
/* 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 < end)
{
if (wait_char)
{
if (*p == wait_char && *prev != '\\')
{
wait_char= 0;
}
}
else
{
switch (*p) {
case '-':
if (!STRING_OR_COMMENT() && (p < end - 1) && (char)*(p+1) == '-')
{
wait_char= '\n';
}
break;
case '#':
if (!STRING_OR_COMMENT())
{
wait_char= '\n';
}
break;
case ';':
if (!STRING_OR_COMMENT())
{
statements++;
*p= '\0';
}
break;
case '/':
if (!STRING_OR_COMMENT() && (p < end - 1) && (char)*(p+1) == '*')
comment= 1;
else if (comment && (p > StmtCopy) && (char)*(prev) == '*')
comment= 0;
break;
case '"':
quote[0] = !STRING_OR_COMMENT();
break;
case '\'':
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;
}
}
return Length;
}

prev= p;
++p;
}

if (statements > 1)
{
int i=0;
unsigned int MaxParams= 0;
unsigned int GetMultiStatements(MADB_Stmt *Stmt)
{
int i= 0;
unsigned int MaxParams= 0;
char *p= Stmt->Query.RefinedText;

Stmt->MultiStmtNr= 0;
Stmt->MultiStmts= (MYSQL_STMT **)MADB_CALLOC(sizeof(MYSQL_STMT) * Stmt->Query.MultiStmtCount);

p= StmtCopy;
Stmt->MultiStmtCount= 0;
Stmt->MultiStmtNr= 0;
Stmt->MultiStmts= (MYSQL_STMT **)MADB_CALLOC(sizeof(MYSQL_STMT) * statements);
while (p < Stmt->Query.RefinedText + Stmt->Query.RefinedLength)
{
Stmt->MultiStmts[i]= i == 0 ? Stmt->stmt : mysql_stmt_init(Stmt->Connection->mariadb);
MDBUG_C_PRINT(Stmt->Connection, "-->inited&preparing %0x(%d,%s)", Stmt->MultiStmts[i], i, p);

while (p < StmtCopy + Length)
if (mysql_stmt_prepare(Stmt->MultiStmts[i], p, (unsigned long)strlen(p)))
{
/* Need to be incremented before CloseMultiStatements() */
++Stmt->MultiStmtCount;
Stmt->MultiStmts[i]= i == 0 ? Stmt->stmt : mysql_stmt_init(Stmt->Connection->mariadb);
MDBUG_C_PRINT(Stmt->Connection, "-->inited&preparing %0x(%d,%s)", Stmt->MultiStmts[i], i, p);
MADB_SetNativeError(&Stmt->Error, SQL_HANDLE_STMT, Stmt->MultiStmts[i]);
CloseMultiStatements(Stmt);

if (mysql_stmt_prepare(Stmt->MultiStmts[i], p, (unsigned long)strlen(p)))
/* Last paranoid attempt make sure that we did not have a parsing error.
More to preserve "backward-compatimility" - we did this before, but before trying to
prepare "multi-statement". */
if (i == 0 && Stmt->Error.NativeError !=1295 /*ER_UNSUPPORTED_PS*/)
{
MADB_SetNativeError(&Stmt->Error, SQL_HANDLE_STMT, Stmt->MultiStmts[i]);
CloseMultiStatements(Stmt);
if (StmtCopy)
Stmt->stmt= mysql_stmt_init(Stmt->Connection->mariadb);
if (mysql_stmt_prepare(Stmt->stmt, STMT_STRING(Stmt), (unsigned long)strlen(STMT_STRING(Stmt))))
{
free(StmtCopy);
mysql_stmt_close(Stmt->stmt);
Stmt->stmt= NULL;
}
else
{
Stmt->Query.MultiStmtCount= 1;
return 0;
}
UNLOCK_MARIADB(Stmt->Connection);

return 0;
}
if (mysql_stmt_param_count(Stmt->MultiStmts[i]) > MaxParams)
MaxParams= mysql_stmt_param_count(Stmt->MultiStmts[i]);
p+= strlen(p) + 1;
++i;
return 1;
}
UNLOCK_MARIADB(Stmt->Connection);
if (mysql_stmt_param_count(Stmt->MultiStmts[i]) > MaxParams)
{
MaxParams= mysql_stmt_param_count(Stmt->MultiStmts[i]);
}
p+= strlen(p) + 1;
++i;
}

if (MaxParams)
Stmt->params= (MYSQL_BIND *)MADB_CALLOC(sizeof(MYSQL_BIND) * MaxParams);
if (MaxParams)
{
Stmt->params= (MYSQL_BIND *)MADB_CALLOC(sizeof(MYSQL_BIND) * MaxParams);
}
if (StmtCopy)
free(StmtCopy);

return statements;
return 0;
}
#undef STRING_OR_COMMENT


my_bool MADB_CheckPtrLength(SQLINTEGER MaxLength, char *Ptr, SQLINTEGER NameLen)
{
Expand Down
8 changes: 6 additions & 2 deletions ma_helper.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,9 @@
#define _ma_helper_h_

void CloseMultiStatements(MADB_Stmt *Stmt);
BOOL QueryIsPossiblyMultistmt(char *queryStr);
unsigned int GetMultiStatements(MADB_Stmt *Stmt, char *StmtStr, SQLINTEGER Length);
BOOL QueryIsPossiblyMultistmt(MADB_QUERY *Query);
int SqlRtrim(char *StmtStr, int Length);
unsigned int GetMultiStatements(MADB_Stmt *Stmt);
int MADB_KeyTypeCount(MADB_Dbc *Connection, char *TableName, int KeyFlag);
MYSQL_RES *MADB_ReadDefaultValues(MADB_Dbc *Dbc, const char *Catalog, const char *TableName);
int MADB_GetDefaultType(int SQLDataType);
Expand All @@ -46,7 +47,10 @@ size_t MADB_GetHexString(char *BinaryBuffer, size_t BinaryLength,
size_t MADB_GetDisplaySize(MYSQL_FIELD field, MARIADB_CHARSET_INFO *charset);
size_t MADB_GetOctetLength(MYSQL_FIELD Field, unsigned short MaxCharLen);
char * MADB_GetTypeName(MYSQL_FIELD Field);

char * ltrim(char *Str);
char * trim(char *Str);

my_bool MADB_CheckPtrLength(SQLINTEGER MaxLength, char *Ptr, SQLINTEGER NameLen);
void * GetBindOffset(MADB_Desc *Ard, MADB_DescRecord *ArdRecord, void *Ptr, SQLULEN RowNumber, size_t PtrSize);
BOOL MADB_ColumnIgnoredInAllRows(MADB_Desc *Desc, MADB_DescRecord *Rec);
Expand Down
2 changes: 0 additions & 2 deletions ma_legacy_helpers.c
Original file line number Diff line number Diff line change
Expand Up @@ -389,5 +389,3 @@ void MADB_FreezeSizeDynamic(MADB_DynArray *array)
array->max_element=elements;
}
}


14 changes: 4 additions & 10 deletions ma_odbc.h
Original file line number Diff line number Diff line change
Expand Up @@ -265,11 +265,6 @@ enum MADB_StmtState {MADB_SS_INITED= 0, MADB_SS_EMULATED= 1, MADB_SS_PREPARED= 2
(Stmt_Hndl->State == MADB_SS_EMULATED ? MADB_SS_EMULATED : MADB_SS_PREPARED) :\
MADB_SS_INITED


typedef struct {
MADB_DynArray tokens;
} MADB_QUERY;

/* Struct used to define column type when driver has to fix it (in catalog functions + SQLGetTypeInfo) */
typedef struct
{
Expand All @@ -287,9 +282,11 @@ typedef struct

} MADB_BulkOperationInfo;

/* Stmt struct needs enum definition from my_parse.h, and it in its turn needs MADB_QUERY*/
/* Stmt struct needs definitions from my_parse.h */
#include <ma_parse.h>

#define STMT_STRING(STMT) (STMT)->Query.Original

struct st_ma_odbc_stmt
{
MADB_Dbc *Connection;
Expand All @@ -300,7 +297,7 @@ struct st_ma_odbc_stmt
MYSQL_STMT *stmt;
MYSQL_RES *metadata;
MADB_List ListItem;
MADB_QUERY *Tokens;
MADB_QUERY Query;
SQLSMALLINT ParamCount;
enum MADB_DaeType DataExecutionType;
MYSQL_RES *DefaultsResult;
Expand All @@ -309,11 +306,9 @@ struct st_ma_odbc_stmt
int Status;
MADB_DescRecord *PutDataRec;
MADB_Stmt *DaeStmt;
char *StmtString;
MADB_Stmt *PositionedCursor;
my_bool PositionedCommand;
enum MADB_StmtState State;
unsigned int MultiStmtCount;
MYSQL_STMT **MultiStmts;
unsigned int MultiStmtNr;
unsigned int MultiStmtMaxParam;
Expand All @@ -330,7 +325,6 @@ struct st_ma_odbc_stmt
char *CatalogName;
MADB_ShortTypeInfo *ColsTypeFixArr;
MADB_BulkOperationInfo Bulk;
enum enum_madb_query_type QueryType;
/* Application Descriptors */
MADB_Desc *Apd;
MADB_Desc *Ard;
Expand Down
Loading

0 comments on commit 860c985

Please sign in to comment.