shsql.cpp
来自「Shorthand是一个强大的脚本语言」· C++ 代码 · 共 543 行
CPP
543 行
///////////////////////////////////////////////////////////////////////////////
// $Header: /shorthand/src/shsql.cpp 1 2/14/03 4:20a Arm $
//-----------------------------------------------------------------------------
// Project: ShortHand interpreter
// Author: Andrei Remenchuk <andrei@remenchuk.com>
//-----------------------------------------------------------------------------
// sql.cpp: ShortHand SQL objects and functions
///////////////////////////////////////////////////////////////////////////////
#include <ctype.h>
#include "shsql.h"
#include "nodes.h"
#include "shorthand.h"
#include "module.h"
class ShhDbDriverRegistry
{
private:
static ShhDbDriverRegistry* m_instance;
array<ShhDbDriver> m_drivers;
ShhDbDriverRegistry();
public:
static ShhDbDriverRegistry* getInstance();
ShhDbDriver* getDriver(const char* driverName);
};
ShhDbDriverRegistry* ShhDbDriverRegistry::m_instance = NULL;
ShhDbDriverRegistry* ShhDbDriverRegistry::getInstance()
{
if (m_instance == NULL) m_instance = new ShhDbDriverRegistry();
return m_instance;
}
ShhDbDriverRegistry::ShhDbDriverRegistry()
{
extern ShhDbDriver* create_mysql_driver();
extern ShhDbDriver* create_odbc_driver();
ShhDbDriver* driver;
driver = create_mysql_driver();
if (driver != NULL) m_drivers.add(driver);
driver = create_odbc_driver();
if (driver != NULL) m_drivers.add(driver);
}
ShhDbDriver* ShhDbDriverRegistry::getDriver(const char* driverName)
{
for(int i=0,n=m_drivers.size(); i<n; i++)
{
ShhDbDriver* driver = m_drivers.get(i);
if (driver != NULL && driver->spi_RecognizeDriverName(driverName))
{
return driver;
}
}
return NULL;
}
ShhConnection::ShhConnection(ShhModule& module, const YYLTYPE& location)
: ShhObject(module, "CONNECTION", location)
{
m_session = NULL;
}
void ShhConnection::constructor(ShhValueList* args)
{
if (args == NULL || args->size() == 0 || !args->get(0)->isString())
throw new ShhObjectException("Connection constructor requires driver name as first argument");
string driverName, connectionString;
args->get(0)->toString(driverName);
m_driver = ShhDbDriverRegistry::getInstance()->getDriver(driverName);
if (m_driver == NULL)
throw new ShhObjectException("Database driver '%s' is not supported", driverName.cstr());
if (args->size() < 2 || !args->get(1)->isString())
throw new ShhObjectException("Connection constructor requires connection string as second argument");
args->get(1)->toString(connectionString);
// assign read-only properties
setProperty("DRIVER", *(args->get(0)) );
setProperty("SPEC", *(args->get(1)) );
m_driverName = driverName;
m_connectionString = connectionString;
connect();
}
void ShhConnection::connect()
{
/*
connection string has the following format:
"driver={mysql}; database=yourdatabase;server=server;uid=username;pwd=password;option=16386;"
and is stored in the "STRING" property
*/
m_session = m_driver->spi_Connect(m_connectionString);
TRACE((1, "+ ShhConnection::connect(0x%08x)\n", m_session));
}
ShhConnection::~ShhConnection()
{
disconnect();
}
void ShhConnection::disconnect()
{
if (m_session != NULL)
{
TRACE((1, "- ShhConnection::disconnect(0x%08x)\n", m_session));
m_driver->spi_Disconnect(m_session);
}
}
ShhValue ShhConnection::executeMethod(const char* name, ShhValueList* args)
{
return ShhValue::EMPTY;
}
bool ShhConnection::hasMethod(const char* name)
{
return false;
}
ShhDbHandle ShhConnection::provide_session()
{
if (m_session == NULL)
{
connect();
}
return m_session;
}
ShhSQL::ShhSQL(ShhModule& module, const char* type, const YYLTYPE& location)
: ShhObject(module, type, location)
{
m_connection = NULL;
m_driver = NULL;
m_cursor = NULL;
m_result = NULL;
}
ShhSQL::~ShhSQL()
{
}
ShhDDL::ShhDDL(ShhModule& module, const YYLTYPE& location)
: ShhSQL(module, "DDL", location)
{
}
ShhRecordSet::ShhRecordSet(ShhModule& module, const YYLTYPE& location)
: ShhSQL(module, "RECORDSET", location)
{
m_row = NULL;
m_field_count = -1;
m_row_num = 0;
m_changed_since_execute = false;
m_no_more_rows = false;
}
ShhRecordSet::~ShhRecordSet()
{
if (m_driver != NULL)
{
if (m_result != NULL)
m_driver->spi_CloseResult(m_result);
if (m_cursor != NULL)
m_driver->spi_CloseCursor(m_cursor);
}
}
bool ShhSQL::hasMethod(const char* method)
{
if (stricmp(method, "EXECUTE") == 0)
return true;
else
return false;
}
bool ShhRecordSet::hasMethod(const char* method)
{
if (ShhSQL::hasMethod(method)) return true;
if (stricmp(method, "COUNT") == 0
|| stricmp(method, "NEXT") == 0
|| stricmp(method, "EOF") == 0
|| stricmp(method, "MORE") == 0
|| stricmp(method, "ROWNUM") == 0
|| stricmp(method, "VALUE") == 0
)
{
return true;
}
else
{
return false;
}
}
void ShhSQL::constructor(ShhValueList* args)
{
const char* type = getTypeName();
if (args == NULL || args->size() == 0)
throw new ShhObjectException(1701, "%s constructor requires Connection argument", type);
if (!args->get(0)->isObject())
throw new ShhObjectException(1701, "%s constructor requires Connection object as first argument", type);
ShhObject* arg0 = args->get(0)->toObject();
if (arg0 == NULL || stricmp(arg0->getTypeName(), "CONNECTION") != 0)
throw new ShhObjectException(1702, "%s constructor requires Connection object as first argument", type);
if (args->size() < 2 || !args->get(1)->isString())
throw new ShhObjectException("%s constructor requires SQL query string as second argument", type);
m_connection = (ShhConnection*) arg0;
m_driver = m_connection->m_driver;
setProperty("STATEMENT", *(args->get(1)) );
}
ShhValue ShhSQL::executeMethod(const char* method, ShhValueList* args)
{
if (stricmp(method, "EXECUTE") == 0)
{
return execute(args);
}
else
return ShhValue::null();
}
ShhValue ShhRecordSet::executeMethod(const char* method, ShhValueList* args)
{
if (stricmp(method, "EXECUTE") == 0)
{
// optimize
if (m_result == NULL || !m_changed_since_execute)
return execute(args);
else
return ShhValue::null();
}
else if (stricmp(method, "NEXT") == 0)
return method_next();
else if (stricmp(method, "MORE") == 0)
return method_more();
else if (stricmp(method, "ROWNUM") == 0)
return method_rownum();
else if (stricmp(method, "COUNT") == 0)
return method_count();
else if (stricmp(method, "EOF") == 0)
return method_finished();
else if (stricmp(method, "VALUE") == 0)
return method_value(args);
else
throw new ShhObjectException("Object %s doesn't have method \"%s\"", m_type.cstr(), method);
}
void ShhSQL::throwSQL(const char* format, ...)
{
va_list args; va_start(args, format);
throw new ShhObjectException(format, args);
}
ShhDbHandle ShhSQL::provide_session()
{
if (m_connection == NULL)
throw new ShhObjectException("Connection was not defined for RecordSet object");
return m_connection->provide_session();
}
const char* ShhSQL::fpi_StoreParameter(int index, const char* value)
{
string* o = new string(value);
m_parameters.add(o);
return o->cstr();
}
ShhDbHandle ShhSQL::fpi_GetConnection()
{
return m_connection->m_session;
}
void ShhSQL::paste_value(string& out, ShhValue value, int index)
{
string data;
switch (value.m_type)
{
case SHH_INT: data.printf("%d", value.toInt()); break;
case SHH_FLOAT: data.printf("%.15g", value.toFloat()); break;
case SHH_DATE:
{
string s; datetime dt; value.toDate(dt);
dt.export_common(s);
data.printf("%s", s.cstr());
break;
}
case SHH_STRING:
{
value.toString(data);
break;
}
case SHH_OBJECT:
{
const char* type = (value.m_value.u_object != NULL) ?
value.m_value.u_object->getTypeName() : "NULL";
throw new ShhObjectException(2803, "Object <%s> cannot be inserted into SQL. Use explicit conversion");
}
default:
throw new ShhObjectException(2801, "Value of unknown type cannot be pasted into SQL");
}
m_driver->spi_PasteValue(m_cursor, this, index, out, data);
}
void ShhSQL::substitute(ShhValueList* args, const char* sqlin, string &sqlout)
{
int argind = 0;
int argc = args ? args->size() : 0;
const char* in = sqlin;
char quote = '\0';
char comment = '\0';
sqlout.clear();
int paramIndex = 0;
while(*in)
{
if (quote != '\0')
{
if (*in == '\\') { sqlout.append(*in); in++; }
else if (*in == quote) quote = '\0';
}
else if (*in == '\'' || *in == '\"')
{
quote = *in;
}
else if (*in == '?')
{
if (args == NULL || argind >= argc)
throw new ShhObjectException(2800, "this SQL requires at least %d parameter(s) - %d passed", argind+1, argc);
paste_value(sqlout, *(args->get(argind)), paramIndex++);
in++;
argind++;
continue;
}
else if (*in == ':')
{
string name; in++;
while(*in && (isalnum(*in) || *in == '_')) name.append(*in++);
ShhVariable* var = m_module.findVariable(name);
if (var == NULL)
throw new ShhObjectException(2804, "variable '%s' for SQL substitution is not found", name.cstr());
paste_value(sqlout, var->value(), paramIndex++);
continue;
}
sqlout.append(*in);
in++;
}
}
ShhValue ShhSQL::execute(ShhValueList* args)
{
ShhDbHandle session = provide_session();
string sqlin, sqlout;
getProperty("STATEMENT").toString(sqlin);
if (sqlin.is_empty())
throw new ShhObjectException("SQL statement is not defined for object");
m_parameters.clear();
substitute(args, sqlin, sqlout);
m_cursor = m_driver->spi_PrepareSQL(session, sqlout);
for(int i=0,n=m_parameters.size(); i<n; i++)
{
m_driver->spi_BindValue(m_cursor, this, i, m_parameters.get(i)->cstr());
}
m_result = m_driver->spi_ExecuteCursor(session, m_cursor, sqlout);
m_driver->spi_ClearParameters(m_cursor);
m_parameters.clear();
return ShhValue::null();
}
ShhValue ShhRecordSet::execute(ShhValueList* args)
{
ShhSQL::execute(args);
m_no_more_rows = false;
int fieldCount = m_driver->spi_GetFieldCount(m_result);
if (fieldCount == 0)
{
m_field_count = 0;
m_total_row_count = 0;
m_row_num = 0;
}
else
{
m_field_count = fieldCount;
m_total_row_count = m_driver->spi_GetRowCount(m_result);
m_row_num = 1;
}
return ShhValue::null();
}
ShhValue ShhRecordSet::method_more()
{
if (m_row_num < 0)
{
return ShhValue::null();
}
if (m_no_more_rows) return ShhValue::null();
return ShhValue(m_row_num < m_total_row_count);
}
ShhValue ShhRecordSet::method_count()
{
if (m_result == 0)
throw new ShhObjectException(2807, "Recordset was not executed");
return ShhValue(m_total_row_count);
}
ShhValue ShhRecordSet::method_finished()
{
return ShhValue(m_no_more_rows || m_row_num <= 0);
}
ShhValue ShhRecordSet::method_next()
{
ShhDbHandle session = provide_session();
m_changed_since_execute = true;
ShhValue zero(0);
if (m_result == 0)
throw new ShhObjectException(2806, "Recordset was not executed");
m_row = m_driver->spi_Fetch(m_result);
if (m_row == 0)
{
m_no_more_rows = true;
return zero;
}
if (m_field_count == 0)
{
m_row_num = 0;
return zero;
}
for(int i=0,n=m_field_count; i<n; i++)
{
string rawValue, fieldName;
rawValue.clear();
unsigned int length = m_driver->spi_GetStringValue(m_result, m_row, i, rawValue);
m_driver->spi_GetFieldName(m_result, i, fieldName);
ShhValue value("");
if (length > 0)
{
value.m_value.u_string = m_module.scratch().strdup(rawValue);
value.m_type = SHH_STRING;
}
setProperty(fieldName, value);
}
m_row_num++;
return ShhValue(1);
}
ShhValue ShhRecordSet::method_rownum()
{
ShhDbHandle session = provide_session();
return ShhValue(m_row_num);
}
ShhValue ShhRecordSet::method_value(ShhValueList* args)
{
if (args == NULL || args->size() < 1)
{
throw new ShhObjectException("method VALUE() needs an argument (field name)");
}
string name; args->get(0)->toString(name);
ShhDbHandle session = provide_session();
return getProperty(name);
}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?