diff --git a/src/Wt/Dbo/Query.C b/src/Wt/Dbo/Query.C index a70e4bb9d7..0fea52e0b1 100644 --- a/src/Wt/Dbo/Query.C +++ b/src/Wt/Dbo/Query.C @@ -144,7 +144,7 @@ std::string createWrappedQueryCountSql(const std::string& query, bool requireSubqueryAlias) { if (requireSubqueryAlias) - return "select count(1) from (" + query + ") as dbocount"; + return "select count(1) from (" + query + ") dbocount"; else return "select count(1) from (" + query + ")"; } diff --git a/src/Wt/Dbo/Query_impl.h b/src/Wt/Dbo/Query_impl.h index 70a2e9f7b1..63d01602d6 100644 --- a/src/Wt/Dbo/Query_impl.h +++ b/src/Wt/Dbo/Query_impl.h @@ -527,7 +527,7 @@ void Query::bindParameters(SqlStatement *statement) int to = (limit_ == -1) ? (1 << 30) : (from + limit_ - 1); field(binder, to, "to"); } - } else {//this->session_->limitQueryMethod_ == Rownum + } else if (this->session_->limitQueryMethod_ == Rownum){ if (limit_ != -1){ int v = limit_; field(binder, v, "rownum"); diff --git a/src/Wt/Dbo/SqlConnection b/src/Wt/Dbo/SqlConnection index 885933c377..5e245a8d18 100644 --- a/src/Wt/Dbo/SqlConnection +++ b/src/Wt/Dbo/SqlConnection @@ -31,7 +31,8 @@ enum SqlDateTimeType { enum LimitQuery{ Limit, RowsFromTo, - Rownum + Rownum, + NotSupported }; class SqlStatement; diff --git a/src/Wt/Dbo/backend/Firebird b/src/Wt/Dbo/backend/Firebird index 63f3cf07f1..17689fa5e7 100644 --- a/src/Wt/Dbo/backend/Firebird +++ b/src/Wt/Dbo/backend/Firebird @@ -110,10 +110,11 @@ namespace Wt { virtual std::string autoincrementInsertSuffix(const std::string& id) const; virtual const char *dateTimeType(SqlDateTimeType type) const; virtual const char *blobType() const; - virtual const char *textType() const; - virtual const char *booleanType() const; + virtual std::string textType(int size) const; + virtual const char *booleanType() const; virtual LimitQuery limitQueryMethod() const; virtual bool supportAlterTable() const; + virtual bool usesRowsFromTo() const {return false;} //@} virtual void prepareForDropTables(); diff --git a/src/Wt/Dbo/backend/Firebird.C b/src/Wt/Dbo/backend/Firebird.C index b17f5df527..6af77d949c 100644 --- a/src/Wt/Dbo/backend/Firebird.C +++ b/src/Wt/Dbo/backend/Firebird.C @@ -638,9 +638,9 @@ namespace Wt impl_->m_tra->Rollback(); } - const char *Firebird::textType() const + std::string Firebird::textType(int size) const { - return "blob sub_type text"; + return std::string("blob sub_type text"); } const char *Firebird::booleanType() const @@ -655,7 +655,7 @@ namespace Wt LimitQuery Firebird::limitQueryMethod() const { - return RowsFromTo; + return RowsFromTo; } bool Firebird::supportAlterTable() const diff --git a/src/Wt/Dbo/backend/Postgres b/src/Wt/Dbo/backend/Postgres index 4f0c1f653b..7c938064e8 100644 --- a/src/Wt/Dbo/backend/Postgres +++ b/src/Wt/Dbo/backend/Postgres @@ -101,6 +101,7 @@ public: virtual const char *blobType() const; virtual bool supportAlterTable() const; virtual bool supportDeferrableFKConstraint() const; + virtual bool requireSubqueryAlias() const; //@} private: diff --git a/src/Wt/Dbo/backend/Postgres.C b/src/Wt/Dbo/backend/Postgres.C index 2e0d9550dd..ab94ee33fb 100644 --- a/src/Wt/Dbo/backend/Postgres.C +++ b/src/Wt/Dbo/backend/Postgres.C @@ -49,8 +49,10 @@ class PostgresStatement : public SqlStatement public: PostgresStatement(Postgres& conn, const std::string& sql) : conn_(conn), - sql_(convertToNumberedPlaceholders(sql)) + sql_(sql) { + convertToNumberedPlaceholders(); + lastId_ = -1; row_ = affectedRows_ = 0; result_ = 0; @@ -140,6 +142,12 @@ public: else { v = boost::posix_time::to_iso_extended_string(value); v[v.find('T')] = ' '; + /* + * Add explicit timezone offset. Postgres will ignore this for a TIMESTAMP + * column, but will treat the timestamp as UTC in a TIMESTAMP WITH TIME + * ZONE column -- possibly in a legacy table. + */ + v.append("+00"); } setValue(column, v); @@ -386,8 +394,21 @@ public: if (type == SqlDate) *value = boost::posix_time::ptime(boost::gregorian::from_string(v), boost::posix_time::hours(0)); - else - *value = boost::posix_time::time_from_string(v); + else { + /* + * Handle timezone offset. Postgres will append a timezone offset [+-]dd + * if a column is defined as TIMESTAMP WITH TIME ZONE -- possibly + * in a legacy table. If offset is present, subtract it for UTC output. + */ + if (v.size() >= 3 && std::strchr("+-", v[v.size() - 3])) { + int hours = boost::lexical_cast(v.substr(v.size() - 3)); + boost::posix_time::time_duration offset + = boost::posix_time::hours(hours); + *value = boost::posix_time::time_from_string(v.substr(0, v.size() - 3)) + - offset; + } else + *value = boost::posix_time::time_from_string(v); + } DEBUG(std::cerr << this << " result time_duration " << column << " " << *value << std::endl); @@ -449,6 +470,7 @@ private: enum { NoFirstRow, FirstRow, NextRow, Done } state_; std::vector params_; + int paramCount_; char **paramValues_; int *paramTypes_, *paramLengths_, *paramFormats_; @@ -470,6 +492,9 @@ private: } void setValue(int column, const std::string& value) { + if (column >= paramCount_) + throw PostgresException("Binding too much parameters"); + for (int i = (int)params_.size(); i <= column; ++i) params_.push_back(Param()); @@ -477,45 +502,46 @@ private: params_[column].isnull = false; } - std::string convertToNumberedPlaceholders(const std::string& sql) + void convertToNumberedPlaceholders() { std::stringstream result; enum { Statement, SQuote, DQuote } state = Statement; int placeholder = 1; - for (unsigned i = 0; i < sql.length(); ++i) { + for (unsigned i = 0; i < sql_.length(); ++i) { switch (state) { case Statement: - if (sql[i] == '\'') + if (sql_[i] == '\'') state = SQuote; - else if (sql[i] == '"') + else if (sql_[i] == '"') state = DQuote; - else if (sql[i] == '?') { + else if (sql_[i] == '?') { result << '$' << placeholder++; continue; } break; case SQuote: - if (sql[i] == '\'') { - if (i + 1 == sql.length()) + if (sql_[i] == '\'') { + if (i + 1 == sql_.length()) state = Statement; - else if (sql[i + 1] == '\'') { - result << sql[i]; + else if (sql_[i + 1] == '\'') { + result << sql_[i]; ++i; // skip to next } else state = Statement; } break; case DQuote: - if (sql[i] == '"') + if (sql_[i] == '"') state = Statement; break; } - result << sql[i]; + result << sql_[i]; } - return result.str(); + paramCount_ = placeholder - 1; + sql_ = result.str(); } }; @@ -648,6 +674,11 @@ bool Postgres::supportDeferrableFKConstraint() const return true; } +bool Postgres::requireSubqueryAlias() const +{ + return true; +} + void Postgres::startTransaction() { PGresult *result = PQexec(conn_, "start transaction"); diff --git a/test/dbo/DboFixture.h b/test/dbo/DboFixture.h index 47d359cd0a..5ccca0f2d1 100644 --- a/test/dbo/DboFixture.h +++ b/test/dbo/DboFixture.h @@ -78,7 +78,7 @@ struct DboFixtureBase logged = true; } - connection = new dbo::backend::Firebird ("localhost", + connection = new dbo::backend::Firebird ("vendetta", file, "test_user", "test_pwd", "", "", ""); @@ -94,7 +94,11 @@ struct DboFixtureBase ~DboFixtureBase() { - session_->dropTables(); + try { + session_->dropTables(); + } catch (...) { + + } delete session_; delete connectionPool_; diff --git a/test/dbo/DboTest.C b/test/dbo/DboTest.C index 4f8a8041da..1f9c9e5166 100644 --- a/test/dbo/DboTest.C +++ b/test/dbo/DboTest.C @@ -89,8 +89,6 @@ struct DboFixture : DboFixtureBase } catch (...) { } - std::cout << " -------------end of droping ---------------*****--------- --- ----- ---**" << std::endl; - std::cerr << session_->tableCreationSql() << std::endl; //session_->dropTables(); @@ -1835,3 +1833,123 @@ BOOST_AUTO_TEST_CASE( dbo_test21 ) delete model; } } + +BOOST_AUTO_TEST_CASE( dbo_test22a ) +{ +#ifdef POSTGRES + DboFixture f; + dbo::Session *session_ = f.session_; + Wt::WDateTime datetime1 = Wt::WDateTime(Wt::WDate(2009, 10, 1), + Wt::WTime(12, 11, 31)); + + { + dbo::Transaction t(*session_); + session_->execute("SET TIME ZONE \"America/New_York\""); + + dbo::ptr a1(new A()); + a1.modify()->datetime = datetime1; + + session_->add(a1); + t.commit(); + } + + { + dbo::Transaction t(*session_); + + dbo::ptr a2 = session_->find(); + + BOOST_REQUIRE(a2->datetime == datetime1); + } +#endif //POSTGRES +} + +BOOST_AUTO_TEST_CASE( dbo_test22b ) +{ +#ifdef POSTGRES + DboFixture f; + dbo::Session *session_ = f.session_; + Wt::WDateTime datetime1 = Wt::WDateTime(Wt::WDate(2009, 10, 1), + Wt::WTime(12, 11, 31)); + + { + dbo::Transaction t(*session_); + session_->execute("SET TIME ZONE \"Europe/Brussels\""); + + dbo::ptr a1(new A()); + a1.modify()->datetime = datetime1; + + session_->add(a1); + t.commit(); + } + + { + dbo::Transaction t(*session_); + + dbo::ptr a2 = session_->find(); + + BOOST_REQUIRE(a2->datetime == datetime1); + } +#endif //POSTGRES +} + +BOOST_AUTO_TEST_CASE( dbo_test22c ) +{ +#ifdef POSTGRES + DboFixture f; + dbo::Session *session_ = f.session_; + Wt::WDateTime datetime1 = Wt::WDateTime(Wt::WDate(2009, 10, 1), + Wt::WTime(12, 11, 31)); + + { + dbo::Transaction t(*session_); + session_->execute("ALTER TABLE table_a ALTER COLUMN datetime " + "TYPE TIMESTAMP WITH TIME ZONE" ); + session_->execute("SET TIME ZONE \"America/New_York\""); + + dbo::ptr a1(new A()); + a1.modify()->datetime = datetime1; + + session_->add(a1); + t.commit(); + } + + { + dbo::Transaction t(*session_); + + dbo::ptr a2 = session_->find(); + + BOOST_REQUIRE(a2->datetime == datetime1); + } +#endif //POSTGRES +} + +BOOST_AUTO_TEST_CASE( dbo_test22d ) +{ +#ifdef POSTGRES + DboFixture f; + dbo::Session *session_ = f.session_; + Wt::WDateTime datetime1 = Wt::WDateTime(Wt::WDate(2009, 10, 1), + Wt::WTime(12, 11, 31)); + + { + dbo::Transaction t(*session_); + session_->execute("ALTER TABLE table_a ALTER COLUMN datetime " + "TYPE TIMESTAMP WITH TIME ZONE" ); + session_->execute("SET TIME ZONE \"Europe/Brussels\""); + + dbo::ptr a1(new A()); + a1.modify()->datetime = datetime1; + + session_->add(a1); + t.commit(); + } + + { + dbo::Transaction t(*session_); + + dbo::ptr a2 = session_->find(); + + BOOST_REQUIRE(a2->datetime == datetime1); + } +#endif //POSTGRES +} diff --git a/test/dbo/DboTest2.C b/test/dbo/DboTest2.C index 99b30ed5c0..3778ced555 100644 --- a/test/dbo/DboTest2.C +++ b/test/dbo/DboTest2.C @@ -217,6 +217,9 @@ BOOST_AUTO_TEST_CASE( dbo2_test1 ) userNum--; } +#ifndef FIREBIRD + + Users usersOffset = session.find ().orderBy("\"name\" desc").limit(7).offset(3); @@ -229,21 +232,20 @@ BOOST_AUTO_TEST_CASE( dbo2_test1 ) BOOST_REQUIRE(user->name.compare(os.str()) == 0); userNum--; } - } +#endif //FIREBIRD + } /* * Check that we fail gracefully when tying to bind too much */ { dbo::Session& session = *f.session_; - dbo::Transaction transactionBindToMach(session); + dbo::Transaction transactionBindTooMuch(session); bool caught = false; try { - std::cerr << "The test was - check that we fail gracefully when tying to " - "bind to mach (search try{)" << std::endl; - Users allUsers2 = session.find ().bind("Joe"); - transactionBindToMach.commit(); + Users allUsers2 = session.find().bind("Joe"); + transactionBindTooMuch.commit(); } catch (std::exception& e) { std::cerr << "Catching exception: " << std::endl; std::cerr << "Catching exception: " << e.what() << std::endl; @@ -254,11 +256,11 @@ BOOST_AUTO_TEST_CASE( dbo2_test1 ) } /* - *Test bind 2 strings with different size + * Test bind 2 strings with different size */ { dbo::Session& session = *f.session_; - dbo::Transaction transactionBindToMach(session); + dbo::Transaction transactionBindTooMuch(session); User *sn = new User(); sn->name = "sn"; @@ -270,11 +272,9 @@ BOOST_AUTO_TEST_CASE( dbo2_test1 ) s+="longname"; ln->name = s; session.add(ln); - } //test OnDeleteCascade - { dbo::Transaction transaction2(session);