Sql: Refactored SQL 'default app cursor', added per-thread SQL option, added secondary SQLR 'default app cursor'

git-svn-id: svn://ultimatepp.org/upp/trunk@4290 f0d560ea-af0d-0410-9eb7-867de7ffcac7
This commit is contained in:
cxl 2011-12-10 16:03:24 +00:00
parent fa7fe19881
commit 988e51f30a
14 changed files with 913 additions and 788 deletions

View file

@ -5,6 +5,7 @@
#if defined(flagMT) #if defined(flagMT)
#define _MULTITHREADED #define _MULTITHREADED
#define MULTITHREADED
#ifdef flagDLL #ifdef flagDLL
#define flagUSEMALLOC #define flagUSEMALLOC
#endif #endif

View file

@ -109,6 +109,7 @@ bool MySqlSession::Open(const char *connect) {
} }
void MySqlSession::Close() { void MySqlSession::Close() {
SessionClose();
if(mysql) { if(mysql) {
mysql_close(mysql); mysql_close(mysql);
mysql = NULL; mysql = NULL;

View file

@ -13,7 +13,7 @@
#endif #endif
#ifdef PLATFORM_POSIX #ifdef PLATFORM_POSIX
#include </usr/include/mysql/mysql.h> #include <mysql/mysql.h>
#endif #endif
NAMESPACE_UPP NAMESPACE_UPP

View file

@ -101,5 +101,7 @@
#undef UNIQUE #undef UNIQUE
#undef REFERENCES
#undef TIMESTAMP #undef TIMESTAMP
#undef COMMENT #undef COMMENT

View file

@ -74,6 +74,7 @@ bool ODBCSession::IsOpen() const
void ODBCSession::Close() void ODBCSession::Close()
{ {
SessionClose();
if(hdbc != SQL_NULL_HANDLE) { if(hdbc != SQL_NULL_HANDLE) {
current = NULL; current = NULL;
FlushConnections(); FlushConnections();

View file

@ -830,6 +830,7 @@ SqlConnection *Oracle7::CreateConnection() {
} }
void Oracle7::Close() { void Oracle7::Close() {
SessionClose();
while(!clink.IsEmpty()) { while(!clink.IsEmpty()) {
clink.GetNext()->Clear(); clink.GetNext()->Clear();
clink.GetNext()->Unlink(); clink.GetNext()->Unlink();

View file

@ -1304,6 +1304,7 @@ bool Oracle8::Login(const char *name, const char *pwd, const char *db, bool use_
} }
void Oracle8::Logoff() { void Oracle8::Logoff() {
SessionClose();
LOG("Oracle8::Logoff, #" << conn_count << " connections pending"); LOG("Oracle8::Logoff, #" << conn_count << " connections pending");
while(!clink.IsEmpty()) { while(!clink.IsEmpty()) {
clink.GetNext()->Clear(); clink.GetNext()->Clear();

View file

@ -397,11 +397,7 @@ void PostgreSQLSession::Close()
{ {
if(!conn) if(!conn)
return; return;
#ifndef flagNOAPPSQL SessionClose();
if(SQL.IsOpen() && &SQL.GetSession() == this)
SQL.Cancel();
#endif
PQfinish(conn); PQfinish(conn);
conn = NULL; conn = NULL;
level = 0; level = 0;

113
uppsrc/Sql/Script.cpp Normal file
View file

@ -0,0 +1,113 @@
#include "Sql.h"
NAMESPACE_UPP
bool StdStatementExecutor::Execute(const String& stmt)
{
cursor.Execute(stmt);
return true;
}
#ifndef NOAPPSQL
struct SQLStatementExecutorClass : StatementExecutor {
virtual bool Execute(const String& stmt) { SQL.Execute(stmt); return true; }
};
StatementExecutor& SQLStatementExecutor() {
return Single<SQLStatementExecutorClass>();
}
#endif
bool SqlPerformScript(SqlSession& session, Stream& script,
Gate2<int, int> progress_canceled, bool stoponerror)
{
String stmt;
int level = 0;
bool ok = true;
while(!script.IsEof()) {
int c = script.Term();
if(IsAlpha(c)) {
String id;
while(IsAlpha(script.Term())) {
c = script.Get();
stmt.Cat(c);
id.Cat(ToUpper(c));
}
if(id == "BEGIN")
level++;
if(id == "END")
level--;
}
else
if(c == '\'') {
stmt.Cat(c);
script.Get();
for(;;) {
c = script.Get();
if(c < 0) {
ok = false;
if(stoponerror)
return false;
break;
}
stmt.Cat(c);
if(c == '\'')
break;
}
}
else
if(c == ';' && level == 0) {
Sql sql(session);
session.ClearError();
int q = 0;
while(stmt[q] == '\r' || stmt[q] == '\n')
q++;
stmt = stmt.Mid(q);
if(!sql.Execute(stmt)) {
ok = false;
if(stoponerror)
break;
}
stmt.Clear();
script.Get();
}
else {
if(c == '(')
level++;
if(c == ')')
level--;
if(c != '\r') {
if(session.GetDialect() == ORACLE && c == '\n')
stmt.Cat('\r');
stmt.Cat(c);
}
script.Get();
}
}
return ok;
}
bool SqlPerformScript(SqlSession& session, const String& script,
Gate2<int, int> progress_canceled, bool stoponerror)
{
StringStream ss(script);
return SqlPerformScript(session, ss, progress_canceled, stoponerror);
}
#ifndef NOAPPSQL
bool SqlPerformScript(Stream& script,
Gate2<int, int> progress_canceled, bool stoponerror)
{
return SqlPerformScript(SQL.GetSession(), script, progress_canceled, stoponerror);
}
bool SqlPerformScript(const String& script,
Gate2<int, int> progress_canceled, bool stoponerror)
{
return SqlPerformScript(SQL.GetSession(), script, progress_canceled, stoponerror);
}
#endif
END_UPP_NAMESPACE

184
uppsrc/Sql/Session.cpp Normal file
View file

@ -0,0 +1,184 @@
#include "Sql.h"
NAMESPACE_UPP
SqlSession::SqlSession()
{
trace = NULL;
traceslow = INT_MAX / 4;
logerrors = false;
usrlog = false;
tracetime = false;
dialect = 255;
errorcode_number = Null;
errorclass = Sql::ERROR_UNSPECIFIED;
error_handler = NULL;
}
SqlSession::~SqlSession()
{
}
void SqlSession::Begin() { NEVER(); }
void SqlSession::Commit() { NEVER(); }
void SqlSession::Rollback() { NEVER(); }
String SqlSession::Savepoint() { NEVER(); return Null; }
void SqlSession::RollbackTo(const String&) { NEVER(); }
bool SqlSession::IsOpen() const { return false; }
int SqlSession::GetTransactionLevel() const { return 0; }
RunScript SqlSession::GetRunScript() const { return NULL; }
SqlConnection *SqlSession::CreateConnection() { return NULL; }
Vector<String> SqlSession::EnumUsers() { return Vector<String>(); }
Vector<String> SqlSession::EnumDatabases() { return Vector<String>(); }
Vector<String> SqlSession::EnumTables(String database) { return Vector<String>(); }
Vector<String> SqlSession::EnumViews(String database) { return Vector<String>(); }
Vector<String> SqlSession::EnumSequences(String database) { return Vector<String>(); }
Vector<String> SqlSession::EnumPrimaryKey(String database, String table) { return Vector<String>(); }
Vector<String> SqlSession::EnumReservedWords() { return Vector<String>(); }
String SqlSession::EnumRowID(String database, String table) { return Null; }
Vector<SqlColumnInfo> SqlSession::EnumColumns(String database, String table)
{
Sql cursor(*this);
Vector<SqlColumnInfo> info;
SqlBool none;
none.SetFalse();
String full_name = database;
if(!IsNull(database))
full_name << '.';
full_name << table;
if(cursor.Execute(Select(SqlAll()).From(SqlSet(SqlId(full_name))).Where(none))) {
info.SetCount(cursor.GetColumns());
for(int i = 0; i < info.GetCount(); i++)
info[i] = cursor.GetColumnInfo(i);
}
return info;
}
void SqlSession::SetError(String error, String stmt, int code, const char *scode, Sql::ERRORCLASS clss) {
if(error_handler && (*error_handler)(error, stmt, code, scode, clss))
return;
if(GetTransactionLevel() && errorstatement.GetCount())
return;
lasterror = error;
errorstatement = stmt;
errorcode_number = code;
errorcode_string = scode;
errorclass = clss;
String err;
err << "ERROR " << error << "(" << code << "): " << stmt << '\n';
if(logerrors)
BugLog() << err;
if(GetTrace())
*GetTrace() << err;
}
void SqlSession::InstallErrorHandler(bool (*handler)(String error, String stmt, int code, const char *scode, Sql::ERRORCLASS clss))
{
error_handler = handler;
}
void SqlSession::SessionClose()
{
if(sql) {
sql->Cancel();
sql.Clear();
}
if(sqlr) {
sqlr->Cancel();
sqlr.Clear();
}
}
Sql& SqlSession::GetSessionSql()
{
if(!sql)
sql = new Sql(*this);
return *sql;
}
Sql& SqlSession::GetSessionSqlR()
{
if(!sqlr)
sqlr = new Sql(*this);
return *sqlr;
}
void SqlSession::ClearError()
{
lasterror.Clear();
errorstatement.Clear();
errorcode_number = Null;
errorcode_string = Null;
errorclass = Sql::ERROR_UNSPECIFIED;
}
StaticMutex sDefs;
static SqlSession *sGlobalSession;
static SqlSession *sGlobalSessionR;
#ifdef _MULTITHREADED
thread__ SqlSession *sThreadSession;
thread__ SqlSession *sThreadSessionR;
#endif
void Sql::operator=(SqlSession& s)
{
Mutex::Lock __(sDefs);
if(this == &AppCursor()) {
#ifdef _MULTITHREADED
sThreadSession = &s;
if(!sGlobalSession)
#endif
sGlobalSession = &s;
return;
}
if(this == &AppCursorR()) {
#ifdef _MULTITHREADED
sThreadSessionR = &s;
if(!sGlobalSessionR)
#endif
sGlobalSessionR = &s;
return;
}
NEVER();
}
Sql& AppCursor()
{
#ifdef _MULTITHREADED
if(sThreadSession)
return sThreadSession->GetSessionSql();
#endif
if(sGlobalSession)
return sGlobalSession->GetSessionSql();
static Sql *empty;
ONCELOCK {
static Sql h(Sql::NULLSQL);
empty = &h;
}
return *empty;
}
Sql& AppCursorR()
{
#ifdef _MULTITHREADED
if(sThreadSessionR)
return sThreadSessionR->GetSessionSqlR();
#endif
if(sGlobalSessionR)
return sGlobalSessionR->GetSessionSqlR();
#ifdef _MULTITHREADED
if(sThreadSession)
return sThreadSession->GetSessionSqlR();
#endif
if(sGlobalSession)
return sGlobalSession->GetSessionSqlR();
static Sql *empty;
ONCELOCK {
static Sql h(Sql::NULLSQL);
empty = &h;
}
return *empty;
}
END_UPP_NAMESPACE

View file

@ -114,6 +114,9 @@ bool Sql::Execute() {
#ifndef NOAPPSQL #ifndef NOAPPSQL
if(this == &AppCursor()) if(this == &AppCursor())
*s << "SQL* "; *s << "SQL* ";
else
if(this == &AppCursorR())
*s << "SQLR* ";
#endif #endif
int i = 0; int i = 0;
for(const char *q = cn->statement; *q; q++) for(const char *q = cn->statement; *q; q++)
@ -577,10 +580,6 @@ void Sql::RollbackTo(const String& savepoint) { GetSession().RollbackTo(save
bool Sql::IsOpen() { return cn && GetSession().IsOpen(); } bool Sql::IsOpen() { return cn && GetSession().IsOpen(); }
#ifndef NOAPPSQL
GLOBAL_VAR(AppSql, AppCursor)
#endif
#ifndef NOAPPSQL #ifndef NOAPPSQL
Sql::Sql() { Sql::Sql() {
cn = NULL; cn = NULL;
@ -631,120 +630,17 @@ Sql::~Sql() {
Detach(); Detach();
} }
SqlSession::SqlSession()
{
trace = NULL;
traceslow = INT_MAX / 4;
logerrors = false;
usrlog = false;
tracetime = false;
dialect = 255;
errorcode_number = Null;
errorclass = Sql::ERROR_UNSPECIFIED;
error_handler = NULL;
}
SqlSession::~SqlSession()
{
/*
#ifndef NOAPPSQL #ifndef NOAPPSQL
if(SQL.IsOpen() && &SQL.GetSession() == this) {
LLOG("Detaching SQL");
// SQL.Detach();
}
#endif
*/
}
void SqlSession::Begin() { NEVER(); } SqlR::SqlR()
void SqlSession::Commit() { NEVER(); } : Sql(SQLR.GetSession()) {}
void SqlSession::Rollback() { NEVER(); }
String SqlSession::Savepoint() { NEVER(); return Null; }
void SqlSession::RollbackTo(const String&) { NEVER(); }
bool SqlSession::IsOpen() const { return false; }
int SqlSession::GetTransactionLevel() const { return 0; }
RunScript SqlSession::GetRunScript() const { return NULL; }
SqlConnection *SqlSession::CreateConnection() { return NULL; }
Vector<String> SqlSession::EnumUsers() { return Vector<String>(); }
Vector<String> SqlSession::EnumDatabases() { return Vector<String>(); }
Vector<String> SqlSession::EnumTables(String database) { return Vector<String>(); }
Vector<String> SqlSession::EnumViews(String database) { return Vector<String>(); }
Vector<String> SqlSession::EnumSequences(String database) { return Vector<String>(); }
Vector<String> SqlSession::EnumPrimaryKey(String database, String table) { return Vector<String>(); }
Vector<String> SqlSession::EnumReservedWords() { return Vector<String>(); }
String SqlSession::EnumRowID(String database, String table) { return Null; }
Vector<SqlColumnInfo> SqlSession::EnumColumns(String database, String table) SqlR::SqlR(const char *stmt)
{ : Sql(stmt, SQLR.GetSession()) {}
Sql cursor(*this);
Vector<SqlColumnInfo> info;
SqlBool none;
none.SetFalse();
String full_name = database;
if(!IsNull(database))
full_name << '.';
full_name << table;
if(cursor.Execute(Select(SqlAll()).From(SqlSet(SqlId(full_name))).Where(none))) {
info.SetCount(cursor.GetColumns());
for(int i = 0; i < info.GetCount(); i++)
info[i] = cursor.GetColumnInfo(i);
}
return info;
}
void SqlSession::SetError(String error, String stmt, int code, const char *scode, Sql::ERRORCLASS clss) { SqlR::SqlR(const SqlStatement& s)
if(error_handler && (*error_handler)(error, stmt, code, scode, clss)) : Sql(s, SQLR.GetSession()) {}
return;
if(GetTransactionLevel() && errorstatement.GetCount())
return;
lasterror = error;
errorstatement = stmt;
errorcode_number = code;
errorcode_string = scode;
errorclass = clss;
String err;
err << "ERROR " << error << "(" << code << "): " << stmt << '\n';
if(logerrors)
BugLog() << err;
if(GetTrace())
*GetTrace() << err;
}
void SqlSession::InstallErrorHandler(bool (*handler)(String error, String stmt, int code, const char *scode, Sql::ERRORCLASS clss))
{
error_handler = handler;
}
Sql& SqlSession::GetSessionSql()
{
if(!sql)
sql = new Sql(*this);
return *sql;
}
void SqlSession::ClearError()
{
lasterror.Clear();
errorstatement.Clear();
errorcode_number = Null;
errorcode_string = Null;
errorclass = Sql::ERROR_UNSPECIFIED;
}
bool StdStatementExecutor::Execute(const String& stmt)
{
cursor.Execute(stmt);
return true;
}
#ifndef NOAPPSQL
struct SQLStatementExecutorClass : StatementExecutor {
virtual bool Execute(const String& stmt) { SQL.Execute(stmt); return true; }
};
StatementExecutor& SQLStatementExecutor() {
return Single<SQLStatementExecutorClass>();
}
#endif #endif
END_UPP_NAMESPACE END_UPP_NAMESPACE

View file

@ -18,6 +18,8 @@ file
SqlStatement.cpp optimize_speed, SqlStatement.cpp optimize_speed,
Sqls.h, Sqls.h,
Sql.cpp, Sql.cpp,
Session.cpp,
Script.cpp,
MassInsert.cpp, MassInsert.cpp,
Schema readonly separator, Schema readonly separator,
SqlSchema.h, SqlSchema.h,

View file

@ -419,94 +419,6 @@ Value SqlStatement::Fetch() const {
return Fetch(SQL); return Fetch(SQL);
} }
bool SqlPerformScript(SqlSession& session, Stream& script,
Gate2<int, int> progress_canceled, bool stoponerror)
{
String stmt;
int level = 0;
bool ok = true;
while(!script.IsEof()) {
int c = script.Term();
if(IsAlpha(c)) {
String id;
while(IsAlpha(script.Term())) {
c = script.Get();
stmt.Cat(c);
id.Cat(ToUpper(c));
}
if(id == "BEGIN")
level++;
if(id == "END")
level--;
}
else
if(c == '\'') {
stmt.Cat(c);
script.Get();
for(;;) {
c = script.Get();
if(c < 0) {
ok = false;
if(stoponerror)
return false;
break;
}
stmt.Cat(c);
if(c == '\'')
break;
}
}
else
if(c == ';' && level == 0) {
Sql sql(session);
session.ClearError();
int q = 0;
while(stmt[q] == '\r' || stmt[q] == '\n')
q++;
stmt = stmt.Mid(q);
if(!sql.Execute(stmt)) {
ok = false;
if(stoponerror)
break;
}
stmt.Clear();
script.Get();
}
else {
if(c == '(')
level++;
if(c == ')')
level--;
if(c != '\r') {
if(session.GetDialect() == ORACLE && c == '\n')
stmt.Cat('\r');
stmt.Cat(c);
}
script.Get();
}
}
return ok;
}
bool SqlPerformScript(Stream& script,
Gate2<int, int> progress_canceled, bool stoponerror)
{
return SqlPerformScript(SQL.GetSession(), script, progress_canceled, stoponerror);
}
bool SqlPerformScript(SqlSession& session, const String& script,
Gate2<int, int> progress_canceled, bool stoponerror)
{
StringStream ss(script);
return SqlPerformScript(session, ss, progress_canceled, stoponerror);
}
bool SqlPerformScript(const String& script,
Gate2<int, int> progress_canceled, bool stoponerror)
{
return SqlPerformScript(SQL.GetSession(), script, progress_canceled, stoponerror);
}
#endif #endif
END_UPP_NAMESPACE END_UPP_NAMESPACE

View file

@ -83,7 +83,8 @@ class Sql {
Vector<Value> param; Vector<Value> param;
friend class SqlSession; friend class SqlSession;
friend struct AppSql; friend Sql& AppCursor();
friend Sql& AppCursorR();
Value Select0(const String& what); Value Select0(const String& what);
void Assign(SqlSource& src); void Assign(SqlSource& src);
@ -235,26 +236,18 @@ public:
Sql(const SqlStatement& s, SqlSource& session); Sql(const SqlStatement& s, SqlSource& session);
Sql(SqlConnection *connection); Sql(SqlConnection *connection);
~Sql(); ~Sql();
void operator=(SqlSession& s); // this only works with SQL and SQLR...
}; };
#ifndef NOAPPSQL #ifndef NOAPPSQL
struct SqlR : Sql {
struct AppSql : Sql { SqlR();
void operator=(SqlSource& s) { Assign(s); } SqlR(const char *stmt);
void Detach() { Sql::Detach(); } SqlR(const SqlStatement& s);
AppSql() : Sql(NULLSQL) {}
}; };
AppSql& AppCursor();
//$-
#define SQL AppCursor()
//$+
// Assist++ cheat:
//$ AppSql SQL;
#endif #endif
struct StatementExecutor { struct StatementExecutor {
virtual bool Execute(const String& stmt) = 0; virtual bool Execute(const String& stmt) = 0;
virtual ~StatementExecutor() {} virtual ~StatementExecutor() {}
@ -262,6 +255,9 @@ struct StatementExecutor {
typedef bool (*RunScript)(const String& text, StatementExecutor& executor, Gate2<int, int> progress_canceled); typedef bool (*RunScript)(const String& text, StatementExecutor& executor, Gate2<int, int> progress_canceled);
class AppSql;
class AppSqlR;
class SqlSession : public SqlSource { class SqlSession : public SqlSource {
public: public:
enum { enum {
@ -301,6 +297,9 @@ protected:
int status; int status;
One<Sql> sql; One<Sql> sql;
One<Sql> sqlr;
void SessionClose();
public: public:
virtual void Begin(); virtual void Begin();
@ -359,6 +358,7 @@ public:
String GetUser() { return Sql(*this).GetUser(); } String GetUser() { return Sql(*this).GetUser(); }
Sql& GetSessionSql(); Sql& GetSessionSql();
Sql& GetSessionSqlR();
operator bool() const { return IsOpen(); } operator bool() const { return IsOpen(); }
@ -374,6 +374,21 @@ public:
}; };
#ifndef NOAPPSQL
Sql& AppCursor();
Sql& AppCursorR();
//$-
#define SQL AppCursor()
#define SQLR AppCursorR()
//$+
// Assist++ cheat:
//$ Sql SQL;
#endif
class OciConnection; class OciConnection;
bool SqlPerformScript(SqlSession& session, Stream& script, bool SqlPerformScript(SqlSession& session, Stream& script,