1
0
mirror of https://github.com/VCMP-SqMod/SqMod.git synced 2024-11-08 00:37:15 +01:00

Basic data record-set implementation.

This commit is contained in:
Sandu Liviu Catalin 2021-02-04 04:30:20 +02:00
parent 6ed0328dfe
commit 5d63520c16
2 changed files with 401 additions and 1 deletions

View File

@ -1,6 +1,9 @@
// ------------------------------------------------------------------------------------------------
#include "PocoLib/Data.hpp"
// ------------------------------------------------------------------------------------------------
#include <sqratConst.h>
// ------------------------------------------------------------------------------------------------
#ifdef SQMOD_POCO_HAS_SQLITE
#include <Poco/Data/SQLite/Connector.h>
@ -23,6 +26,7 @@ SQMOD_DECL_TYPENAME(SqBoolBinding, _SC("SqBoolBinding"))
// ------------------------------------------------------------------------------------------------
SQMOD_DECL_TYPENAME(SqPcDataSession, _SC("SqDataSession"))
SQMOD_DECL_TYPENAME(SqPcDataStatement, _SC("SqDataStatement"))
SQMOD_DECL_TYPENAME(SqPcDataRecordSet, _SC("SqDataRecordSet"))
// ------------------------------------------------------------------------------------------------
static const Poco::Data::NullData g_NullData{Poco::NULL_GENERIC};
@ -111,6 +115,12 @@ SqDataStatement SqDataSession::GetStatement(StackStrF & data)
return SqDataStatement(*this, data);
}
// ------------------------------------------------------------------------------------------------
SqDataRecordSet SqDataSession::GetRecordSet(StackStrF & data)
{
return SqDataRecordSet(*this, data);
}
// ------------------------------------------------------------------------------------------------
SqDataSession & SqDataSession::Execute(StackStrF & query)
{
@ -443,6 +453,7 @@ void Register_POCO_Data(HSQUIRRELVM vm, Table &)
.Func(_SC("Close"), &SqDataSession::Close)
.Func(_SC("Reconnect"), &SqDataSession::Reconnect)
.Func(_SC("Statement"), &SqDataSession::GetStatement)
.Func(_SC("RecordSet"), &SqDataSession::GetRecordSet)
.Func(_SC("Begin"), &SqDataSession::Begin)
.Func(_SC("Commit"), &SqDataSession::Commit)
.Func(_SC("Rollback"), &SqDataSession::Rollback)
@ -506,6 +517,8 @@ void Register_POCO_Data(HSQUIRRELVM vm, Table &)
// Overloaded Member Methods
.Overload(_SC("Execute"), &SqDataStatement::Execute)
.Overload(_SC("Execute"), &SqDataStatement::Execute_)
.Overload(_SC("Execute_"), &SqDataStatement::ExecuteChained)
.Overload(_SC("Execute_"), &SqDataStatement::ExecuteChained_)
.Overload(_SC("ExecuteAsync"), &SqDataStatement::ExecuteAsync)
.Overload(_SC("ExecuteAsync"), &SqDataStatement::ExecuteAsync_)
.Overload(_SC("Into"), &SqDataStatement::Into)
@ -516,6 +529,7 @@ void Register_POCO_Data(HSQUIRRELVM vm, Table &)
.Overload(_SC("Range"), &SqDataStatement::Range)
.Overload(_SC("Range"), &SqDataStatement::RangeEx)
// Static Values
.SetStaticValue(_SC("Unlimited"), static_cast< SQInteger >(Poco::Data::Limit::LIMIT_UNLIMITED))
.SetStaticValue(_SC("WaitForever"), static_cast< SQInteger >(SqDataStatement::WAIT_FOREVER))
.SetStaticValue(_SC("UseCurrentDataSet"), static_cast< SQInteger >(Poco::Data::StatementImpl::USE_CURRENT_DATA_SET))
.SetStaticValue(_SC("StorageDeque"), static_cast< SQInteger >(SqDataStatement::STORAGE_DEQUE))
@ -524,12 +538,69 @@ void Register_POCO_Data(HSQUIRRELVM vm, Table &)
.SetStaticValue(_SC("StorageUnknown"), static_cast< SQInteger >(SqDataStatement::STORAGE_UNKNOWN))
);
// --------------------------------------------------------------------------------------------
ns.Bind(_SC("RecordSet"),
Class< SqDataRecordSet >(vm, SqPcDataRecordSet::Str)
// Constructors
.Ctor< SqDataSession &, StackStrF & >()
// Meta-methods
.SquirrelFunc(_SC("_typename"), &SqPcDataRecordSet::Fn)
// Properties
.Prop(_SC("RowCount"), &SqDataRecordSet::RowCount)
.Prop(_SC("ExtractedRowCount"), &SqDataRecordSet::ExtractedRowCount)
.Prop(_SC("TotalRowCount"), &SqDataRecordSet::GetTotalRowCount, &SqDataRecordSet::SetTotalRowCount)
.Prop(_SC("ColumnCount"), &SqDataRecordSet::ColumnCount)
.Prop(_SC("IsFiltered"), &SqDataRecordSet::IsFiltered)
// Member Methods
.FmtFunc(_SC("SetTotalRowCount"), &SqDataRecordSet::SetTotalRowCountQ)
.Func(_SC("First"), &SqDataRecordSet::MoveFirst)
.Func(_SC("Next"), &SqDataRecordSet::MoveNext)
.Func(_SC("Previous"), &SqDataRecordSet::MovePrevious)
.Func(_SC("Last"), &SqDataRecordSet::MoveLast)
.Func(_SC("Reset"), &SqDataRecordSet::Reset)
.Func(_SC("ColumnTypeAt"), &SqDataRecordSet::ColumnTypeAt)
.Func(_SC("ColumnType"), &SqDataRecordSet::ColumnType)
.Func(_SC("ColumnName"), &SqDataRecordSet::ColumnName)
.Func(_SC("ColumnLengthAt"), &SqDataRecordSet::ColumnLengthAt)
.Func(_SC("ColumnLength"), &SqDataRecordSet::ColumnLength)
.Func(_SC("ColumnPrecisionAt"), &SqDataRecordSet::ColumnPrecisionAt)
.Func(_SC("ColumnPrecision"), &SqDataRecordSet::ColumnPrecision)
.Func(_SC("IsNull"), &SqDataRecordSet::IsNull)
// Overloaded Member Methods
.Overload(_SC("ValueAt"), &SqDataRecordSet::GetValueAt)
.Overload(_SC("ValueAt"), &SqDataRecordSet::GetValueAtOr)
.Overload(_SC("Value"), &SqDataRecordSet::GetValue)
.Overload(_SC("Value"), &SqDataRecordSet::GetValueOr)
);
// --------------------------------------------------------------------------------------------
Register_POCO_Data_Binding< SQInteger, SqIntegerBinding >(vm, ns, _SC("IntBind"));
Register_POCO_Data_Binding< String, SqStringBinding >(vm, ns, _SC("StrBind"));
Register_POCO_Data_Binding< SQFloat, SqFloatBinding >(vm, ns, _SC("FloatBind"));
Register_POCO_Data_Binding< bool, SqBoolBinding >(vm, ns, _SC("BoolBind"));
RootTable(vm).Bind(_SC("SqData"), ns);
// --------------------------------------------------------------------------------------------
ConstTable(vm).Enum(_SC("SqDataColumnType"), Enumeration(vm)
.Const(_SC("Bool"), static_cast< SQInteger >(Poco::Data::MetaColumn::FDT_BOOL))
.Const(_SC("Int8"), static_cast< SQInteger >(Poco::Data::MetaColumn::FDT_INT8))
.Const(_SC("Uint8"), static_cast< SQInteger >(Poco::Data::MetaColumn::FDT_UINT8))
.Const(_SC("Int16"), static_cast< SQInteger >(Poco::Data::MetaColumn::FDT_INT16))
.Const(_SC("Uint16"), static_cast< SQInteger >(Poco::Data::MetaColumn::FDT_UINT16))
.Const(_SC("Int32"), static_cast< SQInteger >(Poco::Data::MetaColumn::FDT_INT32))
.Const(_SC("Uint32"), static_cast< SQInteger >(Poco::Data::MetaColumn::FDT_UINT32))
.Const(_SC("Int64"), static_cast< SQInteger >(Poco::Data::MetaColumn::FDT_INT64))
.Const(_SC("Uint64"), static_cast< SQInteger >(Poco::Data::MetaColumn::FDT_UINT64))
.Const(_SC("Float"), static_cast< SQInteger >(Poco::Data::MetaColumn::FDT_FLOAT))
.Const(_SC("Double"), static_cast< SQInteger >(Poco::Data::MetaColumn::FDT_DOUBLE))
.Const(_SC("String"), static_cast< SQInteger >(Poco::Data::MetaColumn::FDT_STRING))
.Const(_SC("WString"), static_cast< SQInteger >(Poco::Data::MetaColumn::FDT_WSTRING))
.Const(_SC("Blob"), static_cast< SQInteger >(Poco::Data::MetaColumn::FDT_BLOB))
.Const(_SC("Clob"), static_cast< SQInteger >(Poco::Data::MetaColumn::FDT_CLOB))
.Const(_SC("Date"), static_cast< SQInteger >(Poco::Data::MetaColumn::FDT_DATE))
.Const(_SC("Time"), static_cast< SQInteger >(Poco::Data::MetaColumn::FDT_TIME))
.Const(_SC("TimeStamp"), static_cast< SQInteger >(Poco::Data::MetaColumn::FDT_TIMESTAMP))
.Const(_SC("Unknown"), static_cast< SQInteger >(Poco::Data::MetaColumn::FDT_UNKNOWN))
);
}
} // Namespace:: SqMod

View File

@ -465,6 +465,7 @@ using Poco::Data::ReferenceBinding;
// ------------------------------------------------------------------------------------------------
struct SqDataSession;
struct SqDataStatement;
struct SqDataRecordSet;
/* ------------------------------------------------------------------------------------------------
* Utility used to transform optimal argument type to stored type.
@ -785,10 +786,15 @@ struct SqDataSession : public Session
SQMOD_NODISCARD LightObj GetProperty(StackStrF & name) const;
/* --------------------------------------------------------------------------------------------
* Look up the value of a property.
* Retrieve a statement from this session.
*/
SQMOD_NODISCARD SqDataStatement GetStatement(StackStrF & data);
/* --------------------------------------------------------------------------------------------
* Retrieve a record-set from this session.
*/
SQMOD_NODISCARD SqDataRecordSet GetRecordSet(StackStrF & data);
/* --------------------------------------------------------------------------------------------
* Create a statement and execute the given query immediately.
*/
@ -854,6 +860,7 @@ struct SqDataStatement : public Statement
SqDataStatement & Add(StackStrF & data)
{
Statement::operator<<(data);
// Allow chaining
return *this;
}
@ -1027,6 +1034,26 @@ struct SqDataStatement : public Statement
return static_cast< SQInteger >(execute(reset));
}
/* --------------------------------------------------------------------------------------------
* Executes the statement synchronously or asynchronously. Returns itself instead of rows.
*/
SqDataStatement & ExecuteChained()
{
execute(true);
// Allow chaining
return *this;
}
/* --------------------------------------------------------------------------------------------
* Executes the statement synchronously or asynchronously. Returns itself instead of rows.
*/
SqDataStatement & ExecuteChained_(bool reset)
{
execute(reset);
// Allow chaining
return *this;
}
/* --------------------------------------------------------------------------------------------
* Executes the statement asynchronously.
*/
@ -1237,4 +1264,306 @@ template < class T > inline SqDataBinding< T > & SqDataBinding< T >::BindAs(SqDa
return *this;
}
/* ------------------------------------------------------------------------------------------------
* RecordSet provides access to data returned from a query.
*/
struct SqDataRecordSet : public RecordSet
{
/* --------------------------------------------------------------------------------------------
* Creates the RecordSet for the given query.
*/
SqDataRecordSet(SqDataSession & session, StackStrF & query)
: RecordSet(session, query.ToStr())
{
}
/* --------------------------------------------------------------------------------------------
* Copy constructor. If the statement has been executed asynchronously and has not been
* synchronized prior to copy operation (i.e. is copied while executing), this constructor shall synchronize it.
*/
SqDataRecordSet(const SqDataRecordSet &) = default;
/* --------------------------------------------------------------------------------------------
* Move constructor.
*/
SqDataRecordSet(SqDataRecordSet &&) noexcept = default;
/* --------------------------------------------------------------------------------------------
* Destroys the RecordSet.
*/
~SqDataRecordSet() = default;
/* --------------------------------------------------------------------------------------------
* Assignment operator.
*/
SqDataRecordSet & operator = (const SqDataRecordSet &) = default;
/* --------------------------------------------------------------------------------------------
* Move assignment.
*/
SqDataRecordSet & operator = (SqDataRecordSet &&) noexcept = default;
/* --------------------------------------------------------------------------------------------
* Returns the number of rows in the RecordSet. The number of rows reported is dependent on filtering.
*/
SQInteger RowCount() const
{
return static_cast< SQInteger >(rowCount());
}
/* --------------------------------------------------------------------------------------------
* Returns the number of rows extracted during the last statement execution.
* The number of rows reported is independent of filtering.
*/
SQInteger ExtractedRowCount() const
{
return static_cast< SQInteger >(extractedRowCount());
}
/* --------------------------------------------------------------------------------------------
* Returns the total number of rows in the RecordSet.
* The number of rows reported is independent of filtering.
*/
SQInteger GetTotalRowCount() const
{
return static_cast< SQInteger >(getTotalRowCount());
}
/* --------------------------------------------------------------------------------------------
* Explicitly sets the total row count.
*/
void SetTotalRowCount(SQInteger totalRowCount)
{
setTotalRowCount(ClampL< SQInteger, size_t >(totalRowCount));
}
/* --------------------------------------------------------------------------------------------
* Implicitly sets the total row count. The supplied sql must return exactly one column and one row.
* The returned value must be an unsigned integer. The value is set as the total number of rows.
*/
void SetTotalRowCountQ(StackStrF & sql)
{
setTotalRowCount(sql.ToStr());
}
/* --------------------------------------------------------------------------------------------
* Returns the number of columns in the recordset.
*/
SQInteger ColumnCount() const
{
return static_cast< SQInteger >(columnCount());
}
/* --------------------------------------------------------------------------------------------
* Moves the row cursor to the first row.
* Returns true if there is at least one row in the RecordSet, false otherwise.
*/
bool MoveFirst()
{
return moveFirst();
}
/* --------------------------------------------------------------------------------------------
* Moves the row cursor to the next row.
* Returns true if the row is available, or false if the end of the record set has been reached and no more rows are available.
*/
bool MoveNext()
{
return moveNext();
}
/* --------------------------------------------------------------------------------------------
* Moves the row cursor to the previous row.
* Returns true if the row is available, or false if there are no more rows available.
*/
bool MovePrevious()
{
return movePrevious();
}
/* --------------------------------------------------------------------------------------------
* Moves the row cursor to the last row.
* Returns true if there is at least one row in the RecordSet, false otherwise.
*/
bool MoveLast()
{
return moveLast();
}
/* --------------------------------------------------------------------------------------------
* Resets the RecordSet and assigns a new statement.
* Should be called after the given statement has been reset, assigned a new SQL statement, and executed.
* Does not remove the associated RowFilter or RowFormatter.
*/
void Reset(const SqDataStatement & stmt)
{
reset(stmt);
}
/* --------------------------------------------------------------------------------------------
* Retrieve the value in the given column (index) of the current row if the value is not NULL.
*/
LightObj GetValueAt(SQInteger idx)
{
size_t i = ClampL< SQInteger, size_t >(idx);
return GetValueImpl(value(i), columnType(i), LightObj{});
}
/* --------------------------------------------------------------------------------------------
* Retrieve the value in the given column (index) of the current row if the value is not NULL, or `fallback` otherwise.
*/
LightObj GetValueAtOr(SQInteger idx, const LightObj & fallback)
{
size_t i = ClampL< SQInteger, size_t >(idx);
return GetValueImpl(value(i), columnType(i), fallback);
}
/* --------------------------------------------------------------------------------------------
* Retrieve the value in the given column (name) of the current row if the value is not NULL.
*/
LightObj GetValue(StackStrF & name)
{
String s(name.ToStr());
return GetValueImpl(value(s), columnType(s), LightObj{});
}
/* --------------------------------------------------------------------------------------------
* Retrieve the value in the given column (name) of the current row if the value is not NULL, or `fallback` otherwise.
*/
LightObj GetValueOr(StackStrF & name, const LightObj & fallback)
{
String s(name.ToStr());
return GetValueImpl(value(s), columnType(s), fallback);
}
/* --------------------------------------------------------------------------------------------
* Retrieve the type for the column at specified position.
*/
SQInteger ColumnTypeAt(SQInteger pos) const
{
return static_cast< SQInteger >(columnType(ClampL< SQInteger, size_t >(pos)));
}
/* --------------------------------------------------------------------------------------------
* Retrieve the type for the column with specified name.
*/
SQInteger ColumnType(StackStrF & name) const
{
return static_cast< SQInteger >(columnType(name.ToStr()));
}
/* --------------------------------------------------------------------------------------------
* Retrieve column name for the column at specified position.
*/
const std::string & ColumnName(SQInteger pos) const
{
return columnName(ClampL< SQInteger, size_t >(pos));
}
/* --------------------------------------------------------------------------------------------
* Retrieve column maximum length for the column at specified position.
*/
SQInteger ColumnLengthAt(SQInteger pos) const
{
return static_cast< SQInteger >(columnLength(ClampL< SQInteger, size_t >(pos)));
}
/* --------------------------------------------------------------------------------------------
* Retrieve column maximum length for the column with specified name.
*/
SQInteger ColumnLength(StackStrF & name) const
{
return static_cast< SQInteger >(columnLength(name.ToStr()));
}
/* --------------------------------------------------------------------------------------------
* Retrieve column precision for the column at specified position.
* Valid for floating point fields only (zero for other data types).
*/
SQInteger ColumnPrecisionAt(SQInteger pos) const
{
return static_cast< SQInteger >(columnPrecision(ClampL< SQInteger, size_t >(pos)));
}
/* --------------------------------------------------------------------------------------------
* Retrieve column precision for the column with specified name.
* Valid for floating point fields only (zero for other data types).
*/
SQInteger ColumnPrecision(StackStrF & name) const
{
return static_cast< SQInteger >(columnPrecision(name.ToStr()));
}
/* --------------------------------------------------------------------------------------------
* Returns true if column value of the current row is null.
*/
bool IsNull(StackStrF & name) const
{
return isNull(name.ToStr());
}
/* --------------------------------------------------------------------------------------------
* Returns true if recordset is filtered.
*/
bool IsFiltered() const
{
return isFiltered();
}
protected:
/* --------------------------------------------------------------------------------------------
* Retrieve the value in the given column (index) of the current row if the value is not NULL, or `fallback` otherwise.
*/
static LightObj GetValueImpl(const Poco::Dynamic::Var & v, Poco::Data::MetaColumn::ColumnDataType t, const LightObj & fb)
{
// Is null?
if (v.isEmpty())
{
return fb; // Use fallback
}
// Identify type
switch (t)
{
case Poco::Data::MetaColumn::FDT_BOOL:
return LightObj(SqInPlace{}, SqVM(), v.convert< bool >());
case Poco::Data::MetaColumn::FDT_INT8:
case Poco::Data::MetaColumn::FDT_INT16:
case Poco::Data::MetaColumn::FDT_INT32:
#ifndef _SQ64
return LightObj(SqInPlace{}, SqVM(), v.convert< SQInteger >());
#endif
case Poco::Data::MetaColumn::FDT_INT64:
return LightObj(SqInPlace{}, SqVM(), v.convert< int64_t >());
case Poco::Data::MetaColumn::FDT_UINT8:
case Poco::Data::MetaColumn::FDT_UINT16:
case Poco::Data::MetaColumn::FDT_UINT32:
#ifndef _SQ64
return LightObj(SqInPlace{}, SqVM(), v.convert< SQUnsignedInteger >());
#endif
case Poco::Data::MetaColumn::FDT_UINT64:
return LightObj(SqInPlace{}, SqVM(), v.convert< uint64_t >());
case Poco::Data::MetaColumn::FDT_FLOAT:
return LightObj(SqInPlace{}, SqVM(), v.convert< float >());
case Poco::Data::MetaColumn::FDT_DOUBLE:
return LightObj(SqInPlace{}, SqVM(), v.convert< double >());
case Poco::Data::MetaColumn::FDT_STRING:
return LightObj(SqInPlace{}, SqVM(), v.convert< std::string >());
case Poco::Data::MetaColumn::FDT_WSTRING:
return LightObj(SqInPlace{}, SqVM(), v.convert< std::wstring >());
case Poco::Data::MetaColumn::FDT_BLOB:
case Poco::Data::MetaColumn::FDT_CLOB:
case Poco::Data::MetaColumn::FDT_DATE:
case Poco::Data::MetaColumn::FDT_TIME:
case Poco::Data::MetaColumn::FDT_TIMESTAMP:
STHROWF("This type of data is currently not implemented.");
default:
STHROWF("Unknown or unsupported type");
}
SQ_UNREACHABLE
// Unreachable
return LightObj();
}
};
} // Namespace:: SqMod