#include "Sql.h" #define LTIMING(x) namespace Upp { template String MakeSqlValue(int code, T& value) { StringBuffer b(sizeof(T) + 1); b[0] = code; memcpy(~b + 1, &value, sizeof(T)); return b; } template T ReadSqlValue(T& x, const char *&s) { memcpy(&x, s, sizeof(T)); s += sizeof(T); return x; } static bool sSqlIdQuoted; static bool sToUpperCase; static bool sToLowerCase; bool SqlId::IsUseQuotes() { return sSqlIdQuoted; } void SqlId::UseQuotes(bool b) { sSqlIdQuoted = b; } void SqlId::ToLowerCase(bool b) { sToUpperCase = sToUpperCase && !b; sToLowerCase = b; } void SqlId::ToUpperCase(bool b) { sToLowerCase = sToLowerCase && !b; sToUpperCase = b; } String SqlId::Quoted() const { if(!id.IsNull()) return String().Cat() << '\t' << id << '\t'; return id.ToString(); } void SqlCompile(const char *&s, StringBuffer *r, byte dialect, Vector *split) { char quote = dialect == MY_SQL ? '`' : '\"'; const char *b = s; int lvl = 0; for(;;) { int c = *s++; switch(c) { case SQLC_OF: if(r) *r << '.'; break; case SQLC_AS: if(r) { if(dialect & (MSSQL | PGSQL)) *r << " as "; else *r << ' '; } break; case SQLC_COMMA: if(r) *r << ", "; break; case SQLC_ID: { for(;;) { const char *b = s; bool do_quote = sSqlIdQuoted && *s != '*'; while((byte)*s >= 32) s++; int c = *s; if(r) { if(do_quote) *r << quote; if(sToUpperCase) r->Cat(ToUpper(String(b, s))); else if(sToLowerCase) r->Cat(ToLower(String(b, s))); else r->Cat(b, s); if(do_quote) *r << quote; if(c == SQLC_AS) { if(dialect & (MSSQL | PGSQL)) *r << " as "; else *r << ' '; } else if(c == SQLC_OF) *r << '.'; else if(c == SQLC_COMMA) *r << ", "; } s++; if(c == SQLC_ID) break; if(c == '\0') return; } } break; case SQLC_IF: { LTIMING("SqlCompile IF"); StringBuffer *er = r; for(;;) { c = *s++; if(c & dialect) { SqlCompile(s, er, dialect, NULL); er = NULL; } else SqlCompile(s, NULL, dialect, NULL); if(*s == '\0') return; c = *s++; if(c == SQLC_ELSE) { SqlCompile(s, er, dialect, NULL); ASSERT(*s == SQLC_ENDIF); s++; break; } if(c == SQLC_ENDIF) break; ASSERT(c == SQLC_ELSEIF); } break; } case SQLC_DATE: { LTIMING("SqlCompile DATE"); Date x; ReadSqlValue(x, s); if(!r) break; if(IsNull(x)) { *r << "NULL"; break; } switch(dialect) { case MSSQL: if(x.year < 1753) x.year = 1753; // Date::Low() *r << Format("convert(datetime, '%d/%d/%d', 120)", x.year, x.month, x.day); break; case ORACLE: *r << Format("to_date('%d/%d/%d', 'SYYYY/MM/DD')", x.year, x.month, x.day); break; case PGSQL: if(x.year < 1) x.year = 1; // Date::Low() *r << "date "; default: *r << Format("\'%04d-%02d-%02d\'", x.year, x.month, x.day); } break; } case SQLC_TIME: { LTIMING("SqlCompile TIME"); Time x; ReadSqlValue(x, s); if(!r) break; if(IsNull(x)) { *r << "NULL"; break; } switch(dialect) { case MSSQL: if(x.year < 1753) x.year = 1753; // Date::Low() *r << Format(x.hour || x.minute || x.second ? "convert(datetime, '%d/%d/%d %d:%d:%d', 120)" : "convert(datetime, '%d/%d/%d', 120)", x.year, x.month, x.day, x.hour, x.minute, x.second); break; case ORACLE: *r << Format("to_date('%d/%d/%d/%d', 'SYYYY/MM/DD/SSSSS')", x.year, x.month, x.day, x.second + 60 * (x.minute + 60 * x.hour)); break; case PGSQL: if(x.year < 1) x.year = 1; // Date::Low() *r << "timestamp "; default: *r << Format("\'%04d-%02d-%02d %02d:%02d:%02d\'", x.year, x.month, x.day, x.hour, x.minute, x.second); } break; } case SQLC_BINARY: { int l; ReadSqlValue(l, s); if(r) { if(l == 0) *r << "NULL"; else switch(dialect) { case PGSQL: { *r << "E\'"; const char *e = s + l; while(s < e) { byte c = *s++; if(c < 32 || c > 126 || c == 39 || c == 92) { *r << '\\' << '\\'; r->Cat(((c >> 6) & 3) + '0'); r->Cat(((c >> 3) & 7) + '0'); r->Cat((c & 7) + '0'); } else r->Cat(c); } *r << "\'::bytea"; break; } case MSSQL: *r << "0x" << HexString(s, l); s += l; break; case SQLITE3: case MY_SQL: *r << "X"; default: *r << "\'" << HexString(s, l) << "\'"; s += l; break; } } else s += l; break; } case SQLC_STRING: { LTIMING("SqlCompile STRING"); int l; ReadSqlValue(l, s); String x = String(s, l); s += l; if(!r) break; if(IsNull(x)) { *r << "NULL"; break; } if(dialect == PGSQL) r->Cat('E'); r->Cat('\''); for(const char *q = x; *q; q++) { int c = (byte)*q; if(c == '\'') { if(dialect == MY_SQL) r->Cat("\\\'"); else if(dialect == PGSQL) r->Cat("\\'"); else r->Cat("\'\'"); } else { if((c == '\"' || c == '\\') && (dialect == MY_SQL || dialect == PGSQL)) r->Cat('\\'); if(dialect == PGSQL && c < 32) { if(c == '\n') r->Cat("\\n"); else if(c == '\r') r->Cat("\\r"); else if(c == '\t') r->Cat("\\t"); else { char h[4]; h[0] = '\\'; h[1] = (3 & (c >> 6)) + '0'; h[2] = (7 & (c >> 3)) + '0'; h[3] = (7 & c) + '0'; r->Cat(h, 4); } } else r->Cat(c); } } r->Cat('\''); break; } default: bool end = c >= 0 && c < 32; if(split) { if(c == '(') lvl++; if(c == ')') lvl--; if((c == ',' && lvl == 0 || end) && s - 1 > b) { while(*b == ' ') b++; split->Add(SqlVal(String(b, s - 1), SqlS::HIGH)); b = s; } } if(end) { s--; return; } else if(r) { const char *p = s - 1; while((byte)*s >= 32) s++; r->Cat(p, s); } } } } String SqlCompile(byte dialect, const String& s) { StringBuffer b; b.Reserve(s.GetLength() + 100); const char *q = s; SqlCompile(q, &b, dialect, NULL); return b; } #ifndef NOAPPSQL String SqlCompile(const String& s) { return SqlCompile(SQL.GetDialect(), s); } #endif Vector SplitSqlSet(const SqlSet& set) { String h = ~set; const char *q = h; Vector r; SqlCompile(q, NULL, ORACLE, &r); return r; } String SqlFormat(int x) { if(IsNull(x)) return "NULL"; return Format("%d", x); } String SqlFormat(double x) { if(IsNull(x)) return "NULL"; return FormatDouble(x, 20); } String SqlFormat(int64 x) { if(IsNull(x)) return "NULL"; return FormatInt64(x); } String SqlFormat0(const char *s, int l, int code) { StringBuffer b(1 + sizeof(int) + l); b[0] = code; memcpy(~b + 1, &l, sizeof(int)); memcpy(~b + 1 + sizeof(int), s, l); return b; } String SqlFormat(const char *s, int l) { return SqlFormat0(s, l, SQLC_STRING); } String SqlFormat(const char *s) { return SqlFormat(s, (int)strlen(s)); } String SqlFormat(const String& x) { return SqlFormat(x, x.GetLength()); } String SqlFormatBinary(const char *s, int l) { return SqlFormat0(s, l, SQLC_BINARY); } String SqlFormatBinary(const String& x) { return SqlFormatBinary(x, x.GetLength()); } String SqlFormat(Date x) { return MakeSqlValue(SQLC_DATE, x); } String SqlFormat(Time x) { return MakeSqlValue(SQLC_TIME, x); } String SqlFormat(const Value& x) { if(x.IsNull()) return "NULL"; switch(x.GetType()) { case BOOL_V: case INT_V: return SqlFormat((int) x); case INT64_V: return SqlFormat((int64) x); case DOUBLE_V: return SqlFormat((double) x); case STRING_V: case WSTRING_V: return SqlFormat(String(x)); case DATE_V: return SqlFormat(Date(x)); case TIME_V: return SqlFormat(Time(x)); case SQLRAW_V: return SqlFormatBinary(SqlRaw(x)); } NEVER(); return "NULL"; } String SqlCode::operator()(const String& text) { return s << (char)SQLC_ELSE << text << (char)SQLC_ENDIF; } String SqlCode::operator()() { return s << (char)SQLC_ENDIF; } SqlCode SqlCode::operator()(byte cond, const String& text) { s << (char)SQLC_ELSEIF << (char)cond << text; return *this; } SqlCode::SqlCode(byte cond, const String& text) { s << (char)SQLC_IF << (char)cond << text; } // Put to different file to force non-inline (size opt) void SqlId::PutOf0(String& s, const SqlId& b) const { s << ToString() << (char)SQLC_OF << ~b; } void SqlId::PutOf(String& s, const SqlId& b) const { s << (char)SQLC_COMMA; PutOf0(s, b); } }