final Data changes

This commit is contained in:
Guenter Obiltschnig 2007-05-16 11:23:29 +00:00
parent 6027101fa6
commit d480055a85
29 changed files with 375 additions and 174 deletions

View File

@ -1,7 +1,7 @@
// //
// ODBCStatementImpl.h // ODBCStatementImpl.h
// //
// $Id: //poco/Main/Data/ODBC/include/Poco/Data/ODBC/ODBCStatementImpl.h#3 $ // $Id: //poco/Main/Data/ODBC/include/Poco/Data/ODBC/ODBCStatementImpl.h#4 $
// //
// Library: ODBC // Library: ODBC
// Package: ODBC // Package: ODBC

View File

@ -1,7 +1,7 @@
// //
// ODBCColumn.cpp // ODBCColumn.cpp
// //
// $Id: //poco/Main/Data/ODBC/src/ODBCColumn.cpp#3 $ // $Id: //poco/Main/Data/ODBC/src/ODBCColumn.cpp#4 $
// //
// Library: ODBC // Library: ODBC
// Package: ODBC // Package: ODBC

View File

@ -1,7 +1,7 @@
// //
// ODBCStatementImpl.cpp // ODBCStatementImpl.cpp
// //
// $Id: //poco/Main/Data/ODBC/src/ODBCStatementImpl.cpp#2 $ // $Id: //poco/Main/Data/ODBC/src/ODBCStatementImpl.cpp#3 $
// //
// Library: ODBC // Library: ODBC
// Package: ODBC // Package: ODBC

View File

@ -1,14 +1,14 @@
# #
# Makefile # Makefile
# #
# $Id: //poco/Main/Data/SQLite/Makefile#3 $ # $Id: //poco/Main/Data/SQLite/Makefile#4 $
# #
# Makefile for Poco SQLite # Makefile for Poco SQLite
# #
include $(POCO_BASE)/build/rules/global include $(POCO_BASE)/build/rules/global
SYSFLAGS += -DTHREADSAFE -DNO_TCL SYSFLAGS += -DTHREADSAFE -DNO_TCL -DSQLITE_DISABLE_LFS
objects = Binder Extractor SessionImpl Connector \ objects = Binder Extractor SessionImpl Connector \
SQLiteException SQLiteStatementImpl Utility \ SQLiteException SQLiteStatementImpl Utility \

View File

@ -1,7 +1,7 @@
// //
// SQLiteStatementImpl.cpp // SQLiteStatementImpl.cpp
// //
// $Id: //poco/Main/Data/SQLite/src/SQLiteStatementImpl.cpp#4 $ // $Id: //poco/Main/Data/SQLite/src/SQLiteStatementImpl.cpp#6 $
// //
// Library: SQLite // Library: SQLite
// Package: SQLite // Package: SQLite
@ -38,8 +38,9 @@
#include "Poco/Data/SQLite/Utility.h" #include "Poco/Data/SQLite/Utility.h"
#include "Poco/Data/SQLite/SQLiteException.h" #include "Poco/Data/SQLite/SQLiteException.h"
#include "Poco/String.h" #include "Poco/String.h"
#include "sqlite3.h"
#include <cstdlib> #include <cstdlib>
#include <cstring>
#include "sqlite3.h"
namespace Poco { namespace Poco {
@ -96,7 +97,7 @@ void SQLiteStatementImpl::compileImpl()
else if(rc == SQLITE_OK && !pStmt) // comment/whitespace ignore else if(rc == SQLITE_OK && !pStmt) // comment/whitespace ignore
{ {
pSql = pLeftover; pSql = pLeftover;
if (strlen(pSql) == 0) if (std::strlen(pSql) == 0)
{ {
// empty statement or an conditional statement! like CREATE IF NOT EXISTS // empty statement or an conditional statement! like CREATE IF NOT EXISTS
// this is valid // this is valid

View File

@ -1,7 +1,7 @@
// //
// Utility.cpp // Utility.cpp
// //
// $Id: //poco/Main/Data/SQLite/src/Utility.cpp#4 $ // $Id: //poco/Main/Data/SQLite/src/Utility.cpp#5 $
// //
// Library: SQLite // Library: SQLite
// Package: SQLite // Package: SQLite

View File

@ -1,7 +1,7 @@
// //
// SQLiteTest.cpp // SQLiteTest.cpp
// //
// $Id: //poco/Main/Data/SQLite/testsuite/src/SQLiteTest.cpp#3 $ // $Id: //poco/Main/Data/SQLite/testsuite/src/SQLiteTest.cpp#5 $
// //
// Copyright (c) 2006, Applied Informatics Software Engineering GmbH. // Copyright (c) 2006, Applied Informatics Software Engineering GmbH.
// and Contributors. // and Contributors.
@ -163,34 +163,6 @@ SQLiteTest::~SQLiteTest()
} }
void SQLiteTest::testTAC()
{
Poco::File aFile("sqlite.db");
if (aFile.exists())
aFile.remove();
Session ses (SessionFactory::instance().create(SQLite::Connector::KEY, "sqlite.db"));
ses << "CREATE TABLE LogTest (Id INTEGER PRIMARY KEY, Time INTEGER, Value INTEGER)", now;
//ses << "PRAGMA synchronous = OFF", now;
double value = -200000000000.0;
Poco::Int64 time = (Poco::Int64)22329988776655.0;
Poco::Stopwatch sw;
sw.start();
ses.begin();
Statement stmt = (ses << "INSERT INTO LogTest ([Time], Value) VALUES (:time, :value)", use(time), use(value));
for(int i = 0; i < 1000; i++)
{
stmt.execute();
}
ses.commit();
sw.stop();
std::cout << std::endl << "Ms for 1000 inserts: " << sw.elapsed() << std::endl;
}
void SQLiteTest::testSimpleAccess() void SQLiteTest::testSimpleAccess()
{ {
Session tmp (SessionFactory::instance().create(SQLite::Connector::KEY, "dummy.db")); Session tmp (SessionFactory::instance().create(SQLite::Connector::KEY, "dummy.db"));
@ -1619,7 +1591,6 @@ CppUnit::Test* SQLiteTest::suite()
{ {
CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("SQLiteTest"); CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("SQLiteTest");
CppUnit_addTest(pSuite, SQLiteTest, testTAC);
CppUnit_addTest(pSuite, SQLiteTest, testSimpleAccess); CppUnit_addTest(pSuite, SQLiteTest, testSimpleAccess);
CppUnit_addTest(pSuite, SQLiteTest, testInsertCharPointer); CppUnit_addTest(pSuite, SQLiteTest, testInsertCharPointer);
CppUnit_addTest(pSuite, SQLiteTest, testInsertCharPointer2); CppUnit_addTest(pSuite, SQLiteTest, testInsertCharPointer2);

View File

@ -1,7 +1,7 @@
// //
// SQLiteTest.h // SQLiteTest.h
// //
// $Id: //poco/Main/Data/SQLite/testsuite/src/SQLiteTest.h#2 $ // $Id: //poco/Main/Data/SQLite/testsuite/src/SQLiteTest.h#3 $
// //
// Definition of the SQLiteTest class. // Definition of the SQLiteTest class.
// //
@ -110,8 +110,6 @@ public:
void testInternalExtraction(); void testInternalExtraction();
void testTAC();
void setUp(); void setUp();
void tearDown(); void tearDown();

View File

@ -1,17 +1,18 @@
POCO Data Developer Guide POCO Data Connectors Developer Guide
Data Data
!!!Overview !!!Overview
Developing one's own <[DataConnector]> implementation is rather straight-forward. Developing one's own <*Data Connector*> implementation is rather straight-forward.
Just implement the following interfaces: Just implement the following interfaces:
* Poco::Data::AbstractBinder * Poco::Data::AbstractBinder
* Poco::Data::AbstractExtractor * Poco::Data::AbstractExtractor
* Poco::Data::StatementImpl * Poco::Data::StatementImpl
* Poco::Data::SessionImpl * Poco::Data::SessionImpl
* Poco::Data::SessionInstantiator * Poco::Data::Connector
* optional: Poco::Data::AbstractPreparation * optional: Poco::Data::AbstractPreparation
It is recommended to implement the classes from top to down (ie. start with Binder and Extractor) and to use a namespace that has <[ Poco::Data ]> as parent, e.g.<[ Poco::Data::SQLite ]>. It is recommended to implement the classes from top to down (ie. start with Binder and Extractor) and to use a
namespace that has <[ Poco::Data ]> as parent, e.g.<[ Poco::Data::SQLite ]>.
!!!AbstractBinder !!!AbstractBinder
An <[AbstractBinder]> is a class that maps values to placeholders. It is also responsible to bind primitive C++ data types to database An <[AbstractBinder]> is a class that maps values to placeholders. It is also responsible to bind primitive C++ data types to database
@ -398,31 +399,31 @@ The connection is opened in the constructor, and closed in the destructor.
/// Aborts a transaction /// Aborts a transaction
---- ----
!!!SessionInstantiator !!!Connector
Finally, one needs to implement the <[SessionInstantiator]>. Finally, one needs to implement the <[Connector]>.
Each <[SessionInstantiator]> should have a public static const string member named <*KEY*> and must have a factory method to <*create*> <[ Poco::AutoPtr ]> objects of type <[SessionImpl]>. Each <[Connector]> should have a public static const string member named <*KEY*> and must have a factory method to <*create*> <[ Poco::AutoPtr ]> objects of type <[SessionImpl]>.
It should also have a static <*addToFactory()*> and a static <*removeFromFactory()*> method: It should also have a static <*addToFactory()*> and a static <*removeFromFactory()*> method:
class My_API SessionInstantiator: public Poco::Data::SessionInstantiator class My_API Connector: public Poco::Data::Connector
/// SessionInstantiator instantiates SessionImpl objects. /// Connector instantiates SessionImpl objects.
{ {
public: public:
static const std::string KEY; static const std::string KEY;
/// Keyword for creating sessions /// Keyword for creating sessions
SessionInstantiator(); Connector();
/// Creates the SessionInstantiator. /// Creates the Connector.
~SessionInstantiator(); ~Connector();
/// Destroys the SessionInstantiator. /// Destroys the Connector.
Poco::AutoPtr < Poco::Data::SessionImpl > create(const std::string& initString); Poco::AutoPtr < Poco::Data::SessionImpl > createSession(const std::string& connectionString);
/// Creates a SessionImpl object and initializes it with the given initString. /// Creates a SessionImpl object and initializes it with the given connectionString.
static void addToFactory(); static void registerConnector();
/// Registers the SessionInstantiator under the Keyword SessionInstantiator::KEY at the Poco::Data::SessionFactory /// Registers the Connector under the Keyword Connector::KEY at the Poco::Data::SessionFactory
static void removeFromFactory(); static void unregisterConnector();
/// Unregisters the SessionInstantiator under the Keyword SessionInstantiator::KEY at the Poco::Data::SessionFactory /// Unregisters the Connector under the Keyword Connector::KEY at the Poco::Data::SessionFactory
}; };
---- ----

View File

@ -0,0 +1,26 @@
POCO Data Release Notes
Data
!!!Release 1.3
Release 1.3 of the POCO C++ Libraries is the first official release containing the Data library.
The Data library has been available in a development state for the 1.2 release. For the 1.3
release, a few things have been changed in an incompatible way that requires changes
to existing code.
!!Summary of Changes
- Class Poco::Data::RecordSet has been added providing generic access
to arbitrary tables.
- SessionInstantiator has been renamed Poco::Data::Connector.
- Poco::Data::BLOBInputStream and Poco::Data::BLOBOutputStream allow convenient access to Poco::Data::BLOB data.
- Poco::Data::Session and Poco::Data::Statement can be used in simpler ways.
- The DataConnectors project directory has been merged into the Data project directory.
!!Incompatible Changes and Possible Transition Issues
SessionInstantiator has been renamed Poco::Data::Connector, and the
member functions addToFactory() and removeFromFactory() are now named
registerConnector() and unregisterConnector(), respectively.
This requires a change in all applications already using POCO Data.

View File

@ -2,12 +2,13 @@ POCO Data User Guide
Data Data
!!!First Steps !!!First Steps
PocoData is POCO's database abstraction layer which allows users to easily send/retrieve data to/from various different databases. POCO Data is POCO's database abstraction layer which allows users to easily
send/retrieve data to/from various different SQL databases.
The following complete example shows how to use it: The following complete example shows how to use it:
#include "Poco/Data/Common.h" #include "Poco/Data/Common.h"
#include "Poco/Data/SQLite/SessionInstantiator.h" #include "Poco/Data/SQLite/Connector.h"
#include <iostream> #include <iostream>
using namespace Poco::Data; using namespace Poco::Data;
@ -15,20 +16,20 @@ The following complete example shows how to use it:
void init() void init()
{ {
SQLite::SessionInstantiator::addToFactory(); SQLite::Connector::registerConnector();
} }
void shutdown() void shutdown()
{ {
SQLite::SessionInstantiator::removeFromFactory(); SQLite::Connector::unregisterConnector();
} }
int main(int argc, char* argv[]) int main(int argc, char* argv[])
{ {
init(); init();
Session ses (SessionFactory::instance().create(SQLite::SessionInstantiator::KEY, "dummy.db")); Session ses("SQLite", "sample.db");
int count = 0; int count = 0;
ses << "SELECT COUNT(*) FROM PERSON", into(count), now; ses << "SELECT COUNT(*) FROM PERSON", into(count), now;
std::cout << "People in DB " << count; std::cout << "People in DB " << count;
@ -37,11 +38,20 @@ The following complete example shows how to use it:
---- ----
The above example is pretty much self explanatory. The <[Poco/Data/Common.h]> file pulls in some common includes, The above example is pretty much self explanatory. The <[Poco/Data/Common.h]> file pulls in some common includes,
the SQLite::SessionInstantiator is used to initialize the SQLite package so that we can later create an SQLite session the SQLite::Connector is used to register the SQLite connector so that we can later create an SQLite session
via the SessionFactory (note that <[ SQLite::SessionInstantiator::KEY ]> is simply the string "sqlite"). via the SessionFactory. The two-argument constructor
Sesssion ses("SQLite", "sample.db");
----
is actually equivalent to:
Session ses(SessionFactory::instance()::create("SQLite", "sample.db"));
----
The << operator is used to send SQL statements to the Session, the <*into(count)*> simply informs the session where to store the result of the query. The << operator is used to send SQL statements to the Session, the <*into(count)*> simply informs the session where to store the result of the query.
Take note of the __now__ at the end of the SQL statement. It is required, otherwise the statement would not be executed. Take note of the <!now!> at the end of the SQL statement. It is required, otherwise the statement would not be executed.
The <* <[ using namespace Poco::Data ]> *> is for convenience only but highly recommended for good readable code (while <* <[ ses << "SELECT COUNT(*) FROM PERSON", Poco::Data::into(count), Poco::Data::now; ]> *> is valid, it simply looks... strange). The <* <[ using namespace Poco::Data ]> *> is for convenience only but highly recommended for good readable code
(while <* <[ ses << "SELECT COUNT(*) FROM PERSON", Poco::Data::into(count), Poco::Data::now; ]> *> is valid, it simply looks... strange).
The remainder of this tutorial is split up into the following parts: The remainder of this tutorial is split up into the following parts:
* Creating Sessions * Creating Sessions
@ -52,11 +62,14 @@ The remainder of this tutorial is split up into the following parts:
* Working with complex data types: how to map C++ objects to a database table * Working with complex data types: how to map C++ objects to a database table
!!!Creating Sessions !!!Creating Sessions
Sessions are always created via the SessionFactory create method: Sessions are always created via the SessionFactory create method, or implicitly
via the two-argument Session constructor.
Session create(const std::string& dbType, const std::string& initParams); Session create(const std::string& connectorKey, const std::string& connectionString);
---- ----
The first parameter contains the type of the Session one wants to create, for the moment "sqlite" is supported and via the ODBC driver we support Oracle, SQLite, DB2, SQLServer and PostgreSQL. The second parameter contains the initialization string. The first parameter contains the type of the Session one wants to create. For the moment "SQLite" is supported
directly, and via the ODBC driver support for Oracle, SQLite, DB2, SQLServer and PostgreSQL is available.
The second parameter contains the (connector-specific) connection string.
In the case of SQLite, the location of the database file is sufficient. In the case of SQLite, the location of the database file is sufficient.
!!!Inserting and Retrieving Data !!!Inserting and Retrieving Data
@ -70,15 +83,20 @@ If we want to insert one single forename we could simply write:
ses << "INSERT INTO FORENAME VALUES(" << aName << ")", now; ses << "INSERT INTO FORENAME VALUES(" << aName << ")", now;
---- ----
Well, we could do that, but we won't. A much better solution is to use <!placeholders!> and connect each placeholder via a <!use!> expression with a variable that will provide the value during execution. Placeholders are recognized by having a <!:!> in front of their name. Rewriting the above code now simply gives Well, we could do that, but we won't. A much better solution is to use <!placeholders!> and connect each placeholder via a <!use!>
expression with a variable that will provide the value during execution.
Placeholders are recognized by having a <!:!> in front of their name. Rewriting the above code now simply gives
std::string aName("Peter"); std::string aName("Peter");
ses << "INSERT INTO FORENAME VALUES(:name)", use(aName), now; ses << "INSERT INTO FORENAME VALUES(:name)", use(aName), now;
---- ----
In this example the <!use!> expression matches the <* :name *> with the <*Peter*> value. Note that apart from the nicer syntax, the real benefit of placeholders - which is performance - doesn't show here. Check the <*Working with Statements*> section to find out more. In this example the <!use!> expression matches the <* :name *> with the <*Peter*> value.
Note that apart from the nicer syntax, the real benefit of placeholders - which is performance - doesn't show here.
Check the <*Working with Statements*> section to find out more.
Retrieving data from the Database works similar. The <!into!> expression matches the returned database values to C++ objects, it also allows to provide a default value in case null data is returned from the database: Retrieving data from the Database works similar. The <!into!> expression matches the returned database values to
C++ objects, it also allows to provide a default value in case null data is returned from the database:
std::string aName; std::string aName;
ses << "SELECT NAME FROM FORENAME", into(aName), now; // the default is the empty string ses << "SELECT NAME FROM FORENAME", into(aName), now; // the default is the empty string
@ -103,12 +121,16 @@ Lets assume we have a Person table that contains an age, a first and a last name
ses << "SELECT (firstname, lastname, age) FROM Person", into(firstName), into(lastName), into(age), now; ses << "SELECT (firstname, lastname, age) FROM Person", into(firstName), into(lastName), into(age), now;
---- ----
Most important here is the <!order!> of the into and use expressions. The first placeholder is matched by the first <*use*>, the 2nd by the 2nd <*use*> etc. Most important here is the <!order!> of the into and use expressions. The first placeholder is matched by the first <*use*>,
The same is true for the <*into*> statement. We select <*firstname*> as the first column of the result set, thus <*into(firstName)*> must be the first into clause. the 2nd by the 2nd <*use*> etc.
The same is true for the <*into*> statement. We select <*firstname*> as the first column of the result set,
thus <*into(firstName)*> must be the first into clause.
!! Handling NULL entries !! Handling NULL entries
A common case with databases are optional data fields that can contain NULL. To accomodate for NULL, the <*into*> expression allows you to define default values. A common case with databases are optional data fields that can contain NULL. To accomodate for NULL, the <*into*> expression allows
For example, assume that age is such an optional field and we want to provide as default value <!-1!> which is done by writing <!into(age, -1)!>: you to define default values.
For example, assume that age is such an optional field and we want to provide as default value <!-1!>
which is done by writing <!into(age, -1)!>:
std::string firstName("Peter"; std::string firstName("Peter";
std::string lastName("Junior"); std::string lastName("Junior");
@ -117,25 +139,35 @@ For example, assume that age is such an optional field and we want to provide as
ses << "SELECT (firstname, lastname, age) FROM Person", into(firstName), into(lastName), into(age, -1), now; ses << "SELECT (firstname, lastname, age) FROM Person", into(firstName), into(lastName), into(age, -1), now;
---- ----
While you can achieve the same effect by initializing age previously to -1 (<*int age = -1*>), this won't work with collection types. Here you must provide the second parameter While you can achieve the same effect by initializing age previously to -1 (<*int age = -1*>),
this won't work with collection types. Here you must provide the second parameter
to init. Otherwise, values will be initialized to compiler specific values. to init. Otherwise, values will be initialized to compiler specific values.
!!!Working with Statements !!!Working with Statements
We often mentioned the term <*Statement*> in the previous section, yet we only worked with database session objects so far, or at least, that's what I made you believe ;-). We often mentioned the term <*Statement*> in the previous section, yet we only worked with database session objects so far,
or at least, that's what you have been made believe ;-).
In reality, you have already worked with Statements. Lets take a look at the method signature of the << operator at Session: In reality, you have already worked with Statements. Lets take a look at the method signature of the << operator at Session:
template <typename T> template <typename T>
Statement Session::operator << (const T& t) Statement Session::operator << (const T& t)
---- ----
Simply ignore the template stuff in front, you won't need it. The only thing that counts here is that the operator <[ << ]> creates a <*Statement*> internally and returns it. Simply ignore the template stuff in front, you won't need it. The only thing that counts here is that the operator <[ << ]> creates a
What happened in the previous examples is that the returned Statement was never assigned to a variable but simply passed on to the <*now*> part which executed the statement. Afterwards the statement was destroyed. <*Statement*> internally and returns it.
What happened in the previous examples is that the returned Statement was never assigned to a variable but simply passed on to the <*now*>
part which executed the statement. Afterwards the statement was destroyed.
Let's take one of the previous examples and change it so that we assign the statement: Let's take one of the previous examples and change it so that we assign the statement:
std::string aName("Peter"); std::string aName("Peter");
Statement stmt = ( ses << "INSERT INTO FORENAME VALUES(:name)", use(aName) ); Statement stmt = ( ses << "INSERT INTO FORENAME VALUES(:name)", use(aName) );
---- ----
Note that we must put brackets around the right part of the assignment, otherwise the compiler will complain. Note that we must put brackets around the right part of the assignment, otherwise the compiler will complain.
If you don't like the above syntax, the following alternative is equivalent:
Statement stmt(ses);
stmt << "INSERT INTO FORENAME VALUES(:name)", use(aName);
----
What did we achieve by assigning the statement to a variable? Well, currently nothing, apart that we can control when to <*execute*>: What did we achieve by assigning the statement to a variable? Well, currently nothing, apart that we can control when to <*execute*>:
std::string aName("Peter"); std::string aName("Peter");
@ -143,7 +175,8 @@ What did we achieve by assigning the statement to a variable? Well, currently no
stmt.execute(); stmt.execute();
poco_assert (stmt.done()); poco_assert (stmt.done());
---- ----
By calling <*execute*> we asserted that our query was executed and that the value was inserted. The check to stmt.done()
By calling <*execute*> we asserted that our query was executed and that the value was inserted. The check to <[stmt.done()]>
simply guarantees that the statement was fully completed. simply guarantees that the statement was fully completed.
!!Prepared Statements !!Prepared Statements
@ -151,6 +184,7 @@ A prepared statement is created by omitting the <*now*> clause.
Statement stmt = ( ses << "INSERT INTO FORENAME VALUES(:name)", use(aName) ); Statement stmt = ( ses << "INSERT INTO FORENAME VALUES(:name)", use(aName) );
---- ----
The advantage of a prepared statement is performance. Assume the following loop: The advantage of a prepared statement is performance. Assume the following loop:
std::string aName(); std::string aName();
@ -161,14 +195,17 @@ The advantage of a prepared statement is performance. Assume the following loop:
stmt.execute(); stmt.execute();
} }
---- ----
Instead of creating and parsing the Statement 100 times, we only do this once and then use the placeholder in combination with the <*use*> clause Instead of creating and parsing the Statement 100 times, we only do this once and then use the placeholder in combination with the <*use*> clause
to insert 100 different values into the database. to insert 100 different values into the database.
Still, this isn't the best way to insert a collection of values into a database. Still, this isn't the best way to insert a collection of values into a database.
!!Things NOT To Do !!Things NOT To Do
<*use*> expects as input a <!reference!> parameter, which is bound later during execution. Thus, one can only use variables, but never constants. <!use!> expects as input a <!reference!> parameter, which is bound later during execution.
The following code will very likely fail (but this is platform/compiler dependent and also depends if your building in release or debug mode, it will work from Monday to Thursday but will always fail on Friday, so shortly spoken: the kind of bugs Thus, one can only use variables, but never constants.
software developers REALLY love): The following code will very likely fail (but this is platform/compiler dependent and also depends if your
building in release or debug mode, it will work from Monday to Thursday but will always fail on Friday, so shortly spoken: the kind of bugs
software developers <*really*> love):
Statement stmt = (ses << INSERT INTO PERSON VALUES (:fn, :ln, :age)", use("Peter"), use("Junior"), use(4)); //ERR! Statement stmt = (ses << INSERT INTO PERSON VALUES (:fn, :ln, :age)", use("Peter"), use("Junior"), use(4)); //ERR!
stmt.execute(); stmt.execute();
@ -177,7 +214,7 @@ The constant values <*Junior*>, <*Peter*> and <*4*> must be assigned to variable
!!!Collection Support !!!Collection Support
If one needs to handle many values at once, one ought to use a collection class. If one needs to handle many values at once, one ought to use a collection class.
Per default we support the following collection types: Per default, the following collection types are supported:
* vector: no requirements * vector: no requirements
* set: the < operator must be supported by the datatype. Note that duplicate key/value pairs are ignored. * set: the < operator must be supported by the datatype. Note that duplicate key/value pairs are ignored.
* multiset: the < operator must be supported by the datatype * multiset: the < operator must be supported by the datatype
@ -197,14 +234,16 @@ A bulk insert example via vector would be:
---- ----
The same example would work with set or multiset but not with map and multimap (std::string has no () operator). The same example would work with set or multiset but not with map and multimap (std::string has no () operator).
Note that <*use*> requires <!non-empty!> collections! Note that <!use!> requires <*non-empty*> collections!
Now reconsider the following example: Now reconsider the following example:
std::string aName; std::string aName;
ses << "SELECT NAME FROM FORENAME", into(aName), now; ses << "SELECT NAME FROM FORENAME", into(aName), now;
---- ----
Previously, it worked because the table contained only one single entry but now the database table contains at least 100 strings, yet we only offer storage space for one single result.
Previously, it worked because the table contained only one single entry but now the database table contains at least 100 strings,
yet we only offer storage space for one single result.
Thus, the above code will fail and throw an exception. Thus, the above code will fail and throw an exception.
One possible way to handle this is: One possible way to handle this is:
@ -215,17 +254,23 @@ One possible way to handle this is:
And again, instead of vector, one could use set or multiset. And again, instead of vector, one could use set or multiset.
!!!The limit clause !!!The limit clause
Working with collections might be convenient to bulk process data but there is also the risk that large operations will block your application for a very long time. In addition, you might want to have better fine-grained control over your query, e.g. you only want to extract a subset of data until a condition is met. Working with collections might be convenient to bulk process data but there is also the risk that large operations will
block your application for a very long time. In addition, you might want to have better fine-grained control over your
query, e.g. you only want to extract a subset of data until a condition is met.
To elevate that problem, one can use the <!limit!> keyword. To elevate that problem, one can use the <!limit!> keyword.
Let's assume we are retrieving thousands of rows from a database to render the data to a GUI. To allow the user to stop fetching data any time (and to avoid having the user franatically click inside the GUI because it doesn't show anything for seconds), we have to partition this process: Let's assume we are retrieving thousands of rows from a database to render the data to a GUI.
To allow the user to stop fetching data any time (and to avoid having the user franatically click inside the GUI because
it doesn't show anything for seconds), we have to partition this process:
std::vector<std::string> names; std::vector<std::string> names;
ses << "SELECT NAME FROM FORENAME", into(names), limit(50), now; ses << "SELECT NAME FROM FORENAME", into(names), limit(50), now;
---- ----
The above example will retrieve up to 50 rows from the database (note that returning nothing is valid!) and <*append*> it to the names collection, i.e. the collection is not cleared! The above example will retrieve up to 50 rows from the database (note that returning nothing is valid!) and <*append*>
If one wants to make sure that <!exactly!> 50 rows are returned one must set the 2nd limit parameter (which per default is set to <*false*>) to <*true*>: it to the names collection, i.e. the collection is not cleared!
If one wants to make sure that <!exactly!> 50 rows are returned one must set the 2nd limit parameter
(which per default is set to <*false*>) to <*true*>:
std::vector<std::string> names; std::vector<std::string> names;
ses << "SELECT NAME FROM FORENAME", into(names), limit(50, true), now; ses << "SELECT NAME FROM FORENAME", into(names), limit(50, true), now;
@ -257,7 +302,8 @@ To guarantee that at least one valid result row is returned use the <!lowerLimit
ses << "SELECT NAME FROM FORENAME", into(aName), lowerLimit(1), now; ses << "SELECT NAME FROM FORENAME", into(aName), lowerLimit(1), now;
---- ----
If the table is now empty, an exception will be thrown. If the query succeeds, aName is guaranteed to be initialized. If the table is now empty, an exception will be thrown. If the query succeeds, aName is guaranteed to be initialized.
Note that <!limit!> is only the short name for <!upperLimit!>. To iterate over a result set step-by-step, e.g. one wants to avoid using a collection class, one would write Note that <!limit!> is only the short name for <!upperLimit!>. To iterate over a result set step-by-step, e.g. one wants to avoid
using a collection class, one would write:
std::string aName; std::string aName;
Statement stmt = (ses << "SELECT NAME FROM FORENAME", into(aName), lowerLimit(1), upperLimit(1)); Statement stmt = (ses << "SELECT NAME FROM FORENAME", into(aName), lowerLimit(1), upperLimit(1));
@ -272,7 +318,8 @@ And for the lazy ones, there is the <!range!> command:
while (!stmt.done()) while (!stmt.done())
stmt.execute(); stmt.execute();
---- ----
The third parameter to range is an optional boolean value which specifies if the upper limit is a hard limit, ie. if the amount of rows returned by the query must match exactly. Per default exact matching is off. The third parameter to range is an optional boolean value which specifies if the upper limit is a hard limit, ie.
if the amount of rows returned by the query must match exactly. Per default exact matching is off.
!!!Complex Data Type Mapping !!!Complex Data Type Mapping
All the previous examples were contented to work with only the most basic data types: integer, string, ... All the previous examples were contented to work with only the most basic data types: integer, string, ...
@ -305,8 +352,9 @@ Assume you have a class Person:
} }
---- ----
Ideally, one would like to use a Person as simple as one used a string. All that is needed is a template specialization of the <*TypeHandler*> template. Note that template specializations must be declared in the Ideally, one would like to use a Person as simple as one used a string. All that is needed is a template specialization of the <*TypeHandler*>
<!same namespace!> as the original template, i.e. <*Poco::Data*>. The template specialization must implement the following methods: template. Note that template specializations must be declared in the <!same namespace!> as the original template, i.e. <*Poco::Data*>.
The template specialization must implement the following methods:
namespace Poco { namespace Poco {
namespace Data { namespace Data {
@ -315,6 +363,11 @@ Ideally, one would like to use a Person as simple as one used a string. All that
class TypeHandler<class Person> class TypeHandler<class Person>
{ {
public: public:
static std::size_t size()
{
return 3; // we handle three columns of the Table!
}
static void bind(std::size_t pos, const Person& obj, AbstractBinder* pBinder) static void bind(std::size_t pos, const Person& obj, AbstractBinder* pBinder)
{ {
poco_assert_dbg (pBinder != 0); poco_assert_dbg (pBinder != 0);
@ -325,11 +378,6 @@ Ideally, one would like to use a Person as simple as one used a string. All that
TypeHandler<Poco::UInt64>::bind(pos++, obj.getSocialSecNr(), pBinder); TypeHandler<Poco::UInt64>::bind(pos++, obj.getSocialSecNr(), pBinder);
} }
static std::size_t size()
{
return 3; // we handle three columns of the Table!
}
static void prepare(std::size_t pos, const Person& obj, AbstractPreparation* pPrepare) static void prepare(std::size_t pos, const Person& obj, AbstractPreparation* pPrepare)
{ {
poco_assert_dbg (pBinder != 0); poco_assert_dbg (pBinder != 0);
@ -354,12 +402,6 @@ Ideally, one would like to use a Person as simple as one used a string. All that
obj.setLastName(lastName); obj.setLastName(lastName);
obj.setSocialSecNr(socialSecNr); obj.setSocialSecNr(socialSecNr);
} }
private:
TypeHandler();
~TypeHandler();
TypeHandler(const TypeHandler&);
TypeHandler& operator=(const TypeHandler&);
}; };
} } // namespace Poco::Data } } // namespace Poco::Data
@ -370,3 +412,118 @@ And that's all you have to do. Working with Person is now as simple as working w
std::map<Poco::UInt64, Person> people; std::map<Poco::UInt64, Person> people;
ses << "SELECT * FROM Person", into(people), now; ses << "SELECT * FROM Person", into(people), now;
---- ----
!!!RecordSet
The Poco::Data::RecordSet class provides a generic way to work with database tables.
Using a <[RecordSet]>, one can:
- iterate over all columns and rows in a table
- obtain meta information about columns (such as name, type, length, etc.)
To work with a RecordSet, first create a Statement, execute it, and
create the RecordSet from the Statement, as follows:
Statement select(session);
select << "SELECT * FROM Person";
select.execute();
RecordSet rs(select);
----
The number of rows in the RecordSet can be limited by specifying
a limit for the Statement.
Following example demonstrates how to iterate over all rows and columns
in a RecordSet:
bool more = rs.moveFirst();
while (more)
{
for (std::size_t col = 0; col < cols; ++col)
{
std::cout << rs[col].convert<std::string>() << " ";
}
std::cout << std::endl;
more = rs.moveNext();
}
----
As mentioned above, the number of rows retrieved into a RecordSet at a
time can be limited using the <[limit]> or <[range]> clause. Iterating
over all rows in a table a bunch of rows at a time can thus be done as
follows:
Statement select(session);
select << "SELECT * FROM Person", range(0, 10);
RecordSet rs(select);
while (!select.done())
{
select.execute();
bool more = rs.moveFirst();
while (more)
{
for (std::size_t col = 0; col < cols; ++col)
{
std::cout << rs[col].convert<std::string>() << " ";
}
std::cout << std::endl;
more = rs.moveNext();
}
}
----
!!!Tuples
Poco::Tuple and vectors of Poco::Tuple provide a convenient way to work with rows when
column types are known, because TypeHandlers for them are readily available.
Consider the following example:
typedef Poco::Tuple<std::string, std::string, int> Person;
typedef std::vector<Person> People;
People people;
people.push_back(Person("Bart Simpson", "Springfield", 12));
people.push_back(Person("Lisa Simpson", "Springfield", 10));
Statement insert(session);
insert << "INSERT INTO Person VALUES(:name, :address, :age)",
use(people), now;
----
Of course, tuples can also be used in queries:
Statement select(session);
select << "SELECT Name, Address, Age FROM Person",
into(people),
now;
for (People::const_iterator it = people.begin(); it != people.end(); ++it)
{
std::cout << "Name: " << it->get<0>() <<
", Address: " << it->get<1>() <<
", Age: " << it->get<2>() <<std::endl;
}
----
!!!Session Pooling
Creating a connection to a database is often a time consuming
operation. Therefore it makes sense to save a session object for
later reuse once it is no longer needed.
A Poco::Data::SessionPool manages a collection of sessions.
When a session is requested, the SessionPool first
looks in its set of already initialized sessions for an
available object. If one is found, it is returned to the
client and marked as "in-use". If no session is available,
the SessionPool attempts to create a new one for the client.
To avoid excessive creation of sessions, a limit
can be set on the maximum number of objects.
The following code fragment shows how to use the SessionPool:
SessionPool pool("ODBC", "...");
// ...
Session sess(pool.get());
----
Pooled sessions are automatically returned to the pool when the
Session variable holding them is destroyed.

View File

@ -1,7 +1,7 @@
// //
// BLOB.h // BLOB.h
// //
// $Id: //poco/Main/Data/include/Poco/Data/BLOB.h#10 $ // $Id: //poco/Main/Data/include/Poco/Data/BLOB.h#11 $
// //
// Library: Data // Library: Data
// Package: DataCore // Package: DataCore
@ -56,6 +56,9 @@ class Data_API BLOB
/// ///
/// A BLOB can hold arbitrary binary data. /// A BLOB can hold arbitrary binary data.
/// The maximum size depends on the underlying database. /// The maximum size depends on the underlying database.
///
/// The BLOBInputStream and BLOBOutputStream classes provide
/// a convenient way to access the data in a BLOB.
{ {
public: public:
typedef std::vector<char>::const_iterator Iterator; typedef std::vector<char>::const_iterator Iterator;

View File

@ -1,7 +1,7 @@
// //
// BLOBStream.h // BLOBStream.h
// //
// $Id: //poco/Main/Data/include/Poco/Data/BLOBStream.h#2 $ // $Id: //poco/Main/Data/include/Poco/Data/BLOBStream.h#3 $
// //
// Library: Data // Library: Data
// Package: DataCore // Package: DataCore
@ -107,10 +107,6 @@ public:
class Data_API BLOBInputStream: public BLOBIOS, public std::istream class Data_API BLOBInputStream: public BLOBIOS, public std::istream
/// An input stream for reading from a BLOB. /// An input stream for reading from a BLOB.
///
/// Using formatted input from a BLOBInputStream
/// is not recommended, due to the read-ahead behavior of
/// istream with formatted reads.
{ {
public: public:
BLOBInputStream(BLOB& blob); BLOBInputStream(BLOB& blob);

View File

@ -1,7 +1,7 @@
// //
// Column.h // Column.h
// //
// $Id: //poco/Main/Data/include/Poco/Data/Column.h#3 $ // $Id: //poco/Main/Data/include/Poco/Data/Column.h#4 $
// //
// Library: Data // Library: Data
// Package: DataCore // Package: DataCore
@ -174,8 +174,6 @@ public:
return _pData->end(); return _pData->end();
} }
private: private:
Column(); Column();

View File

@ -1,7 +1,7 @@
// //
// Range.h // Range.h
// //
// $Id: //poco/Main/Data/include/Poco/Data/Range.h#4 $ // $Id: //poco/Main/Data/include/Poco/Data/Range.h#5 $
// //
// Library: Data // Library: Data
// Package: DataCore // Package: DataCore
@ -49,7 +49,7 @@ namespace Data {
class Data_API Range class Data_API Range
/// Limit stores information how many rows a query should return. /// Range stores information how many rows a query should return.
{ {
public: public:
Range(Poco::UInt32 lowValue, Poco::UInt32 upValue, bool hardLimit); Range(Poco::UInt32 lowValue, Poco::UInt32 upValue, bool hardLimit);

View File

@ -1,7 +1,7 @@
// //
// RecordSet.h // RecordSet.h
// //
// $Id: //poco/Main/Data/include/Poco/Data/RecordSet.h#5 $ // $Id: //poco/Main/Data/include/Poco/Data/RecordSet.h#6 $
// //
// Library: Data // Library: Data
// Package: DataCore // Package: DataCore
@ -54,10 +54,23 @@ namespace Data {
class Data_API RecordSet: private Statement class Data_API RecordSet: private Statement
/// RecordSet class provides access to data returned from a query. /// RecordSet provides access to data returned from a query.
/// Data access indexes (row and column) are 0-based. /// Data access indexes (row and column) are 0-based, as usual in C++.
///
/// Recordset provides navigation methods to iterate through the /// Recordset provides navigation methods to iterate through the
/// recordset and retrieval methods to extract data. /// recordset, retrieval methods to extract data, and methods
/// to get metadata (type, etc.) about columns.
///
/// To work with a RecordSet, first create a Statement, execute it, and
/// create the RecordSet from the Statement, as follows:
///
/// Statement select(session);
/// select << "SELECT * FROM Person";
/// select.execute();
/// RecordSet rs(select);
///
/// The number of rows in the RecordSet can be limited by specifying
/// a limit for the Statement.
{ {
public: public:
explicit RecordSet(const Statement& rStatement); explicit RecordSet(const Statement& rStatement);
@ -91,8 +104,8 @@ public:
const AbstractExtractionVec& rExtractions = extractions(); const AbstractExtractionVec& rExtractions = extractions();
std::size_t s = rExtractions.size(); std::size_t s = rExtractions.size();
if (0 == s || pos > s - 1) if (0 == s || pos >= s)
throw RangeException(format("Invalid column number: %z", pos)); throw RangeException(format("Invalid column index: %z", pos));
ExtractionVecPtr pExtraction = dynamic_cast<ExtractionVecPtr>(rExtractions[pos].get()); ExtractionVecPtr pExtraction = dynamic_cast<ExtractionVecPtr>(rExtractions[pos].get());
@ -130,15 +143,28 @@ public:
bool moveFirst(); bool moveFirst();
/// Moves the row cursor to the first row. /// Moves the row cursor to the first row.
///
/// Returns true if there is at least one row in the RecordSet,
/// false otherwise.
bool moveNext(); bool moveNext();
/// Moves the row cursor to the next row. /// 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 movePrevious(); bool movePrevious();
/// Moves the row cursor to the previous row. /// 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 moveLast(); bool moveLast();
/// Moves the row cursor to the last row. /// Moves the row cursor to the last row.
///
/// Returns true if there is at least one row in the RecordSet,
/// false otherwise.
DynamicAny value(const std::string& name); DynamicAny value(const std::string& name);
/// Returns the value in the named column of the current row. /// Returns the value in the named column of the current row.
@ -195,7 +221,8 @@ private:
if (pExtraction) if (pExtraction)
{ {
const Column<T>& col = pExtraction->column(); const Column<T>& col = pExtraction->column();
if (0 == Poco::icompare(name, col.name())) return col.position(); if (0 == Poco::icompare(name, col.name()))
return col.position();
} }
} }
@ -209,8 +236,6 @@ private:
/// ///
/// inlines /// inlines
/// ///
inline std::size_t RecordSet::rowCount() const inline std::size_t RecordSet::rowCount() const
{ {
poco_assert (extractions().size()); poco_assert (extractions().size());

View File

@ -1,7 +1,7 @@
// //
// SessionFactory.h // SessionFactory.h
// //
// $Id: //poco/Main/Data/include/Poco/Data/SessionFactory.h#5 $ // $Id: //poco/Main/Data/include/Poco/Data/SessionFactory.h#7 $
// //
// Library: Data // Library: Data
// Package: DataCore // Package: DataCore
@ -63,7 +63,7 @@ class Data_API SessionFactory
/// ///
/// A concrete example to open an SQLite database stored in the file "dummy.db" would be /// A concrete example to open an SQLite database stored in the file "dummy.db" would be
/// ///
/// Session tmp(SessionFactory::instance().create(SQLite::Connector::KEY, "dummy.db")); /// Session ses(SessionFactory::instance().create(SQLite::Connector::KEY, "dummy.db"));
/// ///
/// An even simpler way to create a session is to use the two argument constructor of Session, which /// An even simpler way to create a session is to use the two argument constructor of Session, which
/// automatically invokes the SessionFactory: /// automatically invokes the SessionFactory:
@ -85,7 +85,7 @@ public:
Session create(const std::string& key, const std::string& connectionString); Session create(const std::string& key, const std::string& connectionString);
/// Creates a Session for the given key with the connectionString. Throws an Poco:Data::UnknownDataBaseException /// Creates a Session for the given key with the connectionString. Throws an Poco:Data::UnknownDataBaseException
/// if no instantiator is registered for that key. /// if no Connector is registered for that key.
private: private:
SessionFactory(); SessionFactory();

View File

@ -1,7 +1,7 @@
// //
// Statement.h // Statement.h
// //
// $Id: //poco/Main/Data/include/Poco/Data/Statement.h#16 $ // $Id: //poco/Main/Data/include/Poco/Data/Statement.h#17 $
// //
// Library: Data // Library: Data
// Package: DataCore // Package: DataCore
@ -67,7 +67,7 @@ public:
Statement(StatementImpl* pImpl); Statement(StatementImpl* pImpl);
/// Creates the Statement. /// Creates the Statement.
Statement(Session& session); explicit Statement(Session& session);
/// Creates the Statement for the given Session. /// Creates the Statement for the given Session.
/// ///
/// The following: /// The following:
@ -131,6 +131,9 @@ public:
/// Returns if the statement was completely executed or if a previously set limit stopped it /// Returns if the statement was completely executed or if a previously set limit stopped it
/// and there is more work to do. When no limit is set, it will always - after calling execute() - return true. /// and there is more work to do. When no limit is set, it will always - after calling execute() - return true.
Statement& reset(Session& session);
/// Resets the Statement so that it can be filled with a new SQL command.
protected: protected:
const AbstractExtractionVec& extractions() const; const AbstractExtractionVec& extractions() const;
/// Returns the extractions vector. /// Returns the extractions vector.
@ -138,7 +141,7 @@ protected:
const MetaColumn& metaColumn(std::size_t pos) const; const MetaColumn& metaColumn(std::size_t pos) const;
/// Returns the type for the column at specified position. /// Returns the type for the column at specified position.
inline const MetaColumn& metaColumn(const std::string& name) const; const MetaColumn& metaColumn(const std::string& name) const;
/// Returns the type for the column with specified name. /// Returns the type for the column with specified name.
private: private:
@ -211,6 +214,7 @@ inline const MetaColumn& Statement::metaColumn(std::size_t pos) const
return _ptr->metaColumn(static_cast<UInt32>(pos)); return _ptr->metaColumn(static_cast<UInt32>(pos));
} }
inline const MetaColumn& Statement::metaColumn(const std::string& name) const inline const MetaColumn& Statement::metaColumn(const std::string& name) const
{ {
return _ptr->metaColumn(name); return _ptr->metaColumn(name);

View File

@ -216,8 +216,6 @@ private:
// //
// inlines // inlines
// //
inline void StatementImpl::addBinding(AbstractBinding* info) inline void StatementImpl::addBinding(AbstractBinding* info)
{ {
_bindings.push_back(info); _bindings.push_back(info);

View File

@ -1,7 +1,7 @@
# #
# Makefile # Makefile
# #
# $Id: //poco/Main/Data/samples/Makefile#1 $ # $Id: //poco/Main/Data/samples/Makefile#2 $
# #
# Makefile for Poco Data Samples # Makefile for Poco Data Samples
# #
@ -12,3 +12,4 @@ projects:
$(MAKE) -C Binding $(MAKECMDGOALS) $(MAKE) -C Binding $(MAKECMDGOALS)
$(MAKE) -C TypeHandler $(MAKECMDGOALS) $(MAKE) -C TypeHandler $(MAKECMDGOALS)
$(MAKE) -C RecordSet $(MAKECMDGOALS) $(MAKE) -C RecordSet $(MAKECMDGOALS)
$(MAKE) -C Tuple $(MAKECMDGOALS)

View File

@ -1,7 +1,7 @@
// //
// RecordSet.cpp // RecordSet.cpp
// //
// $Id: //poco/Main/Data/samples/RecordSet/src/RecordSet.cpp#1 $ // $Id: //poco/Main/Data/samples/RecordSet/src/RecordSet.cpp#2 $
// //
// This sample demonstrates the Data library. // This sample demonstrates the Data library.
// //
@ -59,13 +59,19 @@ int main(int argc, char** argv)
select << "SELECT * FROM Person"; select << "SELECT * FROM Person";
select.execute(); select.execute();
// create a RecordSet and iterate over it // create a RecordSet
RecordSet rs(select); RecordSet rs(select);
std::size_t cols = rs.columnCount(); std::size_t cols = rs.columnCount();
// print all column names
for (std::size_t col = 0; col < cols; ++col)
{
std::cout << rs.columnName(col) << std::endl;
}
// iterate over all rows and columns
bool more = rs.moveFirst(); bool more = rs.moveFirst();
while (more) while (more)
{ {
for (std::size_t col = 1; col <= cols; ++col) for (std::size_t col = 0; col < cols; ++col)
{ {
std::cout << rs[col].convert<std::string>() << " "; std::cout << rs[col].convert<std::string>() << " ";
} }

View File

@ -1,14 +1,14 @@
# #
# Makefile # Makefile
# #
# $Id: //poco/Main/Data/samples/Tuple/Makefile#1 $ # $Id: //poco/Main/Data/samples/Tuple/Makefile#2 $
# #
# Makefile for Poco Data Tuple sample # Makefile for Poco Data Tuple sample
# #
include $(POCO_BASE)/build/rules/global include $(POCO_BASE)/build/rules/global
objects = Binding objects = Tuple
target = Tuple target = Tuple
target_version = 1 target_version = 1

View File

@ -2,8 +2,8 @@
<VisualStudioProject <VisualStudioProject
ProjectType="Visual C++" ProjectType="Visual C++"
Version="7.10" Version="7.10"
Name="Binding" Name="Tuple"
ProjectGUID="{F2972327-DCA7-49BB-B55D-66C554CF1205}" ProjectGUID="{08C81227-3322-4DBD-A83F-55CCC933A5F7}"
Keyword="Win32Proj"> Keyword="Win32Proj">
<Platforms> <Platforms>
<Platform <Platform
@ -37,11 +37,11 @@
Name="VCCustomBuildTool"/> Name="VCCustomBuildTool"/>
<Tool <Tool
Name="VCLinkerTool" Name="VCLinkerTool"
OutputFile="bin/Bindingd.exe" OutputFile="bin/Tupled.exe"
LinkIncremental="2" LinkIncremental="2"
AdditionalLibraryDirectories="..\..\..\lib" AdditionalLibraryDirectories="..\..\..\lib"
GenerateDebugInformation="TRUE" GenerateDebugInformation="TRUE"
ProgramDatabaseFile="bin/Bindingd.pdb" ProgramDatabaseFile="bin/Tupled.pdb"
SubSystem="1" SubSystem="1"
TargetMachine="1"/> TargetMachine="1"/>
<Tool <Tool
@ -96,7 +96,7 @@
Name="VCCustomBuildTool"/> Name="VCCustomBuildTool"/>
<Tool <Tool
Name="VCLinkerTool" Name="VCLinkerTool"
OutputFile="bin/Binding.exe" OutputFile="bin/Tuple.exe"
LinkIncremental="1" LinkIncremental="1"
AdditionalLibraryDirectories="..\..\..\lib" AdditionalLibraryDirectories="..\..\..\lib"
GenerateDebugInformation="FALSE" GenerateDebugInformation="FALSE"
@ -138,7 +138,7 @@
Name="Source Files" Name="Source Files"
Filter=""> Filter="">
<File <File
RelativePath=".\src\Binding.cpp"> RelativePath=".\src\Tuple.cpp">
</File> </File>
</Filter> </Filter>
</Files> </Files>

View File

@ -3,7 +3,7 @@
ProjectType="Visual C++" ProjectType="Visual C++"
Version="8.00" Version="8.00"
Name="Tuple" Name="Tuple"
ProjectGUID="{79D784CB-663C-444E-BCB2-1A1166D19DFB}" ProjectGUID="{08C81227-3322-4DBD-A83F-55CCC933A5F7}"
Keyword="Win32Proj" Keyword="Win32Proj"
> >
<Platforms> <Platforms>

View File

@ -11,6 +11,10 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RecordSet", "RecordSet\Reco
ProjectSection(ProjectDependencies) = postProject ProjectSection(ProjectDependencies) = postProject
EndProjectSection EndProjectSection
EndProject EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Tuple", "Tuple\Tuple_vs71.vcproj", "{08C81227-3322-4DBD-A83F-55CCC933A5F7}"
ProjectSection(ProjectDependencies) = postProject
EndProjectSection
EndProject
Global Global
GlobalSection(SolutionConfiguration) = preSolution GlobalSection(SolutionConfiguration) = preSolution
debug_shared = debug_shared debug_shared = debug_shared
@ -29,6 +33,10 @@ Global
{56F66D36-F11E-4AA1-AD37-4518A253059D}.debug_shared.Build.0 = debug_shared|Win32 {56F66D36-F11E-4AA1-AD37-4518A253059D}.debug_shared.Build.0 = debug_shared|Win32
{56F66D36-F11E-4AA1-AD37-4518A253059D}.release_shared.ActiveCfg = release_shared|Win32 {56F66D36-F11E-4AA1-AD37-4518A253059D}.release_shared.ActiveCfg = release_shared|Win32
{56F66D36-F11E-4AA1-AD37-4518A253059D}.release_shared.Build.0 = release_shared|Win32 {56F66D36-F11E-4AA1-AD37-4518A253059D}.release_shared.Build.0 = release_shared|Win32
{08C81227-3322-4DBD-A83F-55CCC933A5F7}.debug_shared.ActiveCfg = debug_shared|Win32
{08C81227-3322-4DBD-A83F-55CCC933A5F7}.debug_shared.Build.0 = debug_shared|Win32
{08C81227-3322-4DBD-A83F-55CCC933A5F7}.release_shared.ActiveCfg = release_shared|Win32
{08C81227-3322-4DBD-A83F-55CCC933A5F7}.release_shared.Build.0 = release_shared|Win32
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
EndGlobalSection EndGlobalSection

View File

@ -58,18 +58,18 @@ DynamicAny RecordSet::value(std::size_t col, std::size_t row) const
switch (columnType(col)) switch (columnType(col))
{ {
case MetaColumn::FDT_BOOL: case MetaColumn::FDT_BOOL:
case MetaColumn::FDT_INT8: return value<Int8>(col, row); break; case MetaColumn::FDT_INT8: return value<Int8>(col, row);
case MetaColumn::FDT_UINT8: return value<UInt8>(col, row); break; case MetaColumn::FDT_UINT8: return value<UInt8>(col, row);
case MetaColumn::FDT_INT16: return value<Int16>(col, row); break; case MetaColumn::FDT_INT16: return value<Int16>(col, row);
case MetaColumn::FDT_UINT16: return value<UInt16>(col, row); break; case MetaColumn::FDT_UINT16: return value<UInt16>(col, row);
case MetaColumn::FDT_INT32: return value<Int32>(col, row); break; case MetaColumn::FDT_INT32: return value<Int32>(col, row);
case MetaColumn::FDT_UINT32: return value<UInt32>(col, row); break; case MetaColumn::FDT_UINT32: return value<UInt32>(col, row);
case MetaColumn::FDT_INT64: return value<Int64>(col, row); break; case MetaColumn::FDT_INT64: return value<Int64>(col, row);
case MetaColumn::FDT_UINT64: return value<UInt64>(col, row); break; case MetaColumn::FDT_UINT64: return value<UInt64>(col, row);
case MetaColumn::FDT_FLOAT: return value<float>(col, row); break; case MetaColumn::FDT_FLOAT: return value<float>(col, row);
case MetaColumn::FDT_DOUBLE: return value<double>(col, row); break; case MetaColumn::FDT_DOUBLE: return value<double>(col, row);
case MetaColumn::FDT_STRING: return value<std::string>(col, row); break; case MetaColumn::FDT_STRING: return value<std::string>(col, row);
case MetaColumn::FDT_BLOB: return value<BLOB>(col, row); break; case MetaColumn::FDT_BLOB: return value<BLOB>(col, row);
default: default:
throw Poco::InvalidArgumentException("Data type not supported."); throw Poco::InvalidArgumentException("Data type not supported.");
} }
@ -120,7 +120,7 @@ bool RecordSet::moveNext()
bool RecordSet::movePrevious() bool RecordSet::movePrevious()
{ {
if (-1 == _currentRow) return false; if (0 == _currentRow) return false;
--_currentRow; --_currentRow;
return true; return true;
} }
@ -137,6 +137,4 @@ bool RecordSet::moveLast()
} }
} } // namespace Poco::Data } } // namespace Poco::Data

View File

@ -58,8 +58,7 @@ Statement::Statement(StatementImpl* pImpl):
Statement::Statement(Session& session): Statement::Statement(Session& session):
_executed(false) _executed(false)
{ {
Statement stmt(session.createStatementImpl()); reset(session);
swap(stmt);
} }
@ -107,6 +106,14 @@ bool Statement::done()
} }
Statement& Statement::reset(Session& session)
{
Statement stmt(session.createStatementImpl());
swap(stmt);
return *this;
}
void now(Statement& statement) void now(Statement& statement)
{ {
statement.execute(); statement.execute();

View File

@ -1,7 +1,7 @@
// //
// StatementImpl.cpp // StatementImpl.cpp
// //
// $Id: //poco/Main/Data/src/StatementImpl.cpp#18 $ // $Id: //poco/Main/Data/src/StatementImpl.cpp#19 $
// //
// Library: Data // Library: Data
// Package: DataCore // Package: DataCore

View File

@ -1,7 +1,7 @@
// //
// DataTest.cpp // DataTest.cpp
// //
// $Id: //poco/Main/Data/testsuite/src/DataTest.cpp#9 $ // $Id: //poco/Main/Data/testsuite/src/DataTest.cpp#11 $
// //
// Copyright (c) 2006, Applied Informatics Software Engineering GmbH. // Copyright (c) 2006, Applied Informatics Software Engineering GmbH.
// and Contributors. // and Contributors.
@ -43,9 +43,12 @@
#include "Poco/BinaryWriter.h" #include "Poco/BinaryWriter.h"
#include "Poco/Types.h" #include "Poco/Types.h"
#include "Poco/Exception.h" #include "Poco/Exception.h"
#include <cstring>
using namespace Poco::Data; using namespace Poco::Data;
using Poco::BinaryReader; using Poco::BinaryReader;
using Poco::BinaryWriter; using Poco::BinaryWriter;
using Poco::UInt32; using Poco::UInt32;
@ -166,7 +169,7 @@ void DataTest::testBLOB()
BLOB blobNumStr(strDigit.c_str(), strDigit.size()); BLOB blobNumStr(strDigit.c_str(), strDigit.size());
assert (blobNumStr.size() == strDigit.size()); assert (blobNumStr.size() == strDigit.size());
assert (0 == strncmp(strDigit.c_str(), blobNumStr.rawContent(), blobNumStr.size())); assert (0 == std::strncmp(strDigit.c_str(), blobNumStr.rawContent(), blobNumStr.size()));
assert (*blobNumStr.begin() == '1'); assert (*blobNumStr.begin() == '1');
assert (*(blobNumStr.end()-1) == '0'); assert (*(blobNumStr.end()-1) == '0');
BLOB blobNumVec(vecDigit); BLOB blobNumVec(vecDigit);
@ -179,7 +182,7 @@ void DataTest::testBLOB()
BLOB blobChrStr(strAlpha.c_str(), strAlpha.size()); BLOB blobChrStr(strAlpha.c_str(), strAlpha.size());
BLOB blobChrVec(vecAlpha); BLOB blobChrVec(vecAlpha);
assert (blobChrStr.size() == strAlpha.size()); assert (blobChrStr.size() == strAlpha.size());
assert (0 == strncmp(strAlpha.c_str(), blobChrStr.rawContent(), blobChrStr.size())); assert (0 == std::strncmp(strAlpha.c_str(), blobChrStr.rawContent(), blobChrStr.size()));
assert (*blobChrStr.begin() == 'a'); assert (*blobChrStr.begin() == 'a');
assert (*(blobChrStr.end()-1) == 'z'); assert (*(blobChrStr.end()-1) == 'z');
assert (blobChrStr == blobChrVec); assert (blobChrStr == blobChrVec);