wasCSharpSQLite – Rev 1
?pathlinks?
using System.Diagnostics;
namespace Community.CsharpSqlite
{
using u8 = System.Byte;
public partial class Sqlite3
{
/*
** 2001 September 15
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
** An tokenizer for SQL
**
** This file contains C code that implements the sqlite3_complete() API.
** This code used to be part of the tokenizer.c source file. But by
** separating it out, the code will be automatically omitted from
** static links that do not use it.
*************************************************************************
** Included in SQLite3 port to C#-SQLite; 2008 Noah B Hart
** C#-SQLite is an independent reimplementation of the SQLite software library
**
** SQLITE_SOURCE_ID: 2010-08-23 18:52:01 42537b60566f288167f1b5864a5435986838e3a3
**
*************************************************************************
*/
//#include "sqliteInt.h"
#if !SQLITE_OMIT_COMPLETE
/*
** This is defined in tokenize.c. We just have to import the definition.
*/
#if !SQLITE_AMALGAMATION
#if SQLITE_ASCII
//#define IdChar(C) ((sqlite3CtypeMap[(unsigned char)C]&0x46)!=0)
static bool IdChar( u8 C )
{
return ( sqlite3CtypeMap[(char)C] & 0x46 ) != 0;
}
#endif
//#if SQLITE_EBCDIC
//extern const char sqlite3IsEbcdicIdChar[];
//#define IdChar(C) (((c=C)>=0x42 && sqlite3IsEbcdicIdChar[c-0x40]))
//#endif
#endif // * SQLITE_AMALGAMATION */
/*
** Token types used by the sqlite3_complete() routine. See the header
** comments on that procedure for additional information.
*/
const int tkSEMI = 0;
const int tkWS = 1;
const int tkOTHER = 2;
#if !SQLITE_OMIT_TRIGGER
const int tkEXPLAIN = 3;
const int tkCREATE = 4;
const int tkTEMP = 5;
const int tkTRIGGER = 6;
const int tkEND = 7;
#endif
/*
** Return TRUE if the given SQL string ends in a semicolon.
**
** Special handling is require for CREATE TRIGGER statements.
** Whenever the CREATE TRIGGER keywords are seen, the statement
** must end with ";END;".
**
** This implementation uses a state machine with 8 states:
**
** (0) INVALID We have not yet seen a non-whitespace character.
**
** (1) START At the beginning or end of an SQL statement. This routine
** returns 1 if it ends in the START state and 0 if it ends
** in any other state.
**
** (2) NORMAL We are in the middle of statement which ends with a single
** semicolon.
**
** (3) EXPLAIN The keyword EXPLAIN has been seen at the beginning of
** a statement.
**
** (4) CREATE The keyword CREATE has been seen at the beginning of a
** statement, possibly preceeded by EXPLAIN and/or followed by
** TEMP or TEMPORARY
**
** (5) TRIGGER We are in the middle of a trigger definition that must be
** ended by a semicolon, the keyword END, and another semicolon.
**
** (6) SEMI We've seen the first semicolon in the ";END;" that occurs at
** the end of a trigger definition.
**
** (7) END We've seen the ";END" of the ";END;" that occurs at the end
** of a trigger difinition.
**
** Transitions between states above are determined by tokens extracted
** from the input. The following tokens are significant:
**
** (0) tkSEMI A semicolon.
** (1) tkWS Whitespace.
** (2) tkOTHER Any other SQL token.
** (3) tkEXPLAIN The "explain" keyword.
** (4) tkCREATE The "create" keyword.
** (5) tkTEMP The "temp" or "temporary" keyword.
** (6) tkTRIGGER The "trigger" keyword.
** (7) tkEND The "end" keyword.
**
** Whitespace never causes a state transition and is always ignored.
** This means that a SQL string of all whitespace is invalid.
**
** If we compile with SQLITE_OMIT_TRIGGER, all of the computation needed
** to recognize the end of a trigger can be omitted. All we have to do
** is look for a semicolon that is not part of an string or comment.
*/
static public int sqlite3_complete( string zSql )
{
int state = 0; /* Current state, using numbers defined in header comment */
int token; /* Value of the next token */
#if !SQLITE_OMIT_TRIGGER
/* A complex statement machine used to detect the end of a CREATE TRIGGER
** statement. This is the normal case.
*/
u8[][] trans = new u8[][] {
/* Token: */
/* State: ** SEMI WS OTHER EXPLAIN CREATE TEMP TRIGGER END */
/* 0 INVALID: */ new u8[]{ 1, 0, 2, 3, 4, 2, 2, 2, },
/* 1 START: */ new u8[]{ 1, 1, 2, 3, 4, 2, 2, 2, },
/* 2 NORMAL: */ new u8[]{ 1, 2, 2, 2, 2, 2, 2, 2, },
/* 3 EXPLAIN: */ new u8[]{ 1, 3, 3, 2, 4, 2, 2, 2, },
/* 4 CREATE: */ new u8[]{ 1, 4, 2, 2, 2, 4, 5, 2, },
/* 5 TRIGGER: */ new u8[]{ 6, 5, 5, 5, 5, 5, 5, 5, },
/* 6 SEMI: */ new u8[]{ 6, 6, 5, 5, 5, 5, 5, 7, },
/* 7 END: */ new u8[]{ 1, 7, 5, 5, 5, 5, 5, 5, },
};
#else
/* If triggers are not supported by this compile then the statement machine
** used to detect the end of a statement is much simplier
*/
u8[][] trans = new u8[][] {
/* Token: */
/* State: ** SEMI WS OTHER */
/* 0 INVALID: */new u8[] { 1, 0, 2, },
/* 1 START: */new u8[] { 1, 1, 2, },
/* 2 NORMAL: */new u8[] { 1, 2, 2, },
};
#endif // * SQLITE_OMIT_TRIGGER */
int zIdx = 0;
while ( zIdx < zSql.Length )
{
switch ( zSql[zIdx] )
{
case ';':
{ /* A semicolon */
token = tkSEMI;
break;
}
case ' ':
case '\r':
case '\t':
case '\n':
case '\f':
{ /* White space is ignored */
token = tkWS;
break;
}
case '/':
{ /* C-style comments */
if ( zSql[zIdx + 1] != '*' )
{
token = tkOTHER;
break;
}
zIdx += 2;
while ( zIdx < zSql.Length && zSql[zIdx] != '*' || zIdx < zSql.Length - 1 && zSql[zIdx + 1] != '/' )
{
zIdx++;
}
if ( zIdx == zSql.Length )
return 0;
zIdx++;
token = tkWS;
break;
}
case '-':
{ /* SQL-style comments from "--" to end of line */
if ( zSql[zIdx + 1] != '-' )
{
token = tkOTHER;
break;
}
while ( zIdx < zSql.Length && zSql[zIdx] != '\n' )
{
zIdx++;
}
if ( zIdx == zSql.Length )
return state == 1 ? 1 : 0;//if( *zSql==0 ) return state==1;
token = tkWS;
break;
}
case '[':
{ /* Microsoft-style identifiers in [...] */
zIdx++;
while ( zIdx < zSql.Length && zSql[zIdx] != ']' )
{
zIdx++;
}
if ( zIdx == zSql.Length )
return 0;
token = tkOTHER;
break;
}
case '`': /* Grave-accent quoted symbols used by MySQL */
case '"': /* single- and double-quoted strings */
case '\'':
{
int c = zSql[zIdx];
zIdx++;
while ( zIdx < zSql.Length && zSql[zIdx] != c )
{
zIdx++;
}
if ( zIdx == zSql.Length )
return 0;
token = tkOTHER;
break;
}
default:
{
//#if SQLITE_EBCDIC
// unsigned char c;
//#endif
if ( IdChar( (u8)zSql[zIdx] ) )
{
/* Keywords and unquoted identifiers */
int nId;
for ( nId = 1; ( zIdx + nId ) < zSql.Length && IdChar( (u8)zSql[zIdx + nId] ); nId++ )
{
}
#if SQLITE_OMIT_TRIGGER
token = tkOTHER;
#else
switch ( zSql[zIdx] )
{
case 'c':
case 'C':
{
if ( nId == 6 && sqlite3StrNICmp( zSql, zIdx, "create", 6 ) == 0 )
{
token = tkCREATE;
}
else
{
token = tkOTHER;
}
break;
}
case 't':
case 'T':
{
if ( nId == 7 && sqlite3StrNICmp( zSql, zIdx, "trigger", 7 ) == 0 )
{
token = tkTRIGGER;
}
else if ( nId == 4 && sqlite3StrNICmp( zSql, zIdx, "temp", 4 ) == 0 )
{
token = tkTEMP;
}
else if ( nId == 9 && sqlite3StrNICmp( zSql, zIdx, "temporary", 9 ) == 0 )
{
token = tkTEMP;
}
else
{
token = tkOTHER;
}
break;
}
case 'e':
case 'E':
{
if ( nId == 3 && sqlite3StrNICmp( zSql, zIdx, "end", 3 ) == 0 )
{
token = tkEND;
}
else
#if !SQLITE_OMIT_EXPLAIN
if ( nId == 7 && sqlite3StrNICmp( zSql, zIdx, "explain", 7 ) == 0 )
{
token = tkEXPLAIN;
}
else
#endif
{
token = tkOTHER;
}
break;
}
default:
{
token = tkOTHER;
break;
}
}
#endif // * SQLITE_OMIT_TRIGGER */
zIdx += nId - 1;
}
else
{
/* Operators and special symbols */
token = tkOTHER;
}
break;
}
}
state = trans[state][token];
zIdx++;
}
return ( state == 1 ) ? 1 : 0;//return state==1;
}
#if !SQLITE_OMIT_UTF16
/*
** This routine is the same as the sqlite3_complete() routine described
** above, except that the parameter is required to be UTF-16 encoded, not
** UTF-8.
*/
int sqlite3_complete16(const void *zSql){
sqlite3_value pVal;
char const *zSql8;
int rc = SQLITE_NOMEM;
#if !SQLITE_OMIT_AUTOINIT
rc = sqlite3_initialize();
if( rc !=0) return rc;
#endif
pVal = sqlite3ValueNew(0);
sqlite3ValueSetStr(pVal, -1, zSql, SQLITE_UTF16NATIVE, SQLITE_STATIC);
zSql8 = sqlite3ValueText(pVal, SQLITE_UTF8);
if( zSql8 ){
rc = sqlite3_complete(zSql8);
}else{
rc = SQLITE_NOMEM;
}
sqlite3ValueFree(pVal);
return sqlite3ApiExit(0, rc);
}
#endif // * SQLITE_OMIT_UTF16 */
#endif // * SQLITE_OMIT_COMPLETE */
}
}