Changeset 1388

Show
Ignore:
Timestamp:
08/28/08 07:21:35 (4 months ago)
Author:
jtv
Message:

Ensure transaction is open before executing prepared statement, thanks qelo. Fixes #159.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/ChangeLog

    r1387 r1388  
     12008-08-28  Jeroen T. Vermeulen <jtv@xs4all.nl> 
     2 include/pqxx/connection_base.hxx, test/test085.cxx, 
     3 test/unit/test_prepared_statement.cxx: 
     4  - Re-branded test085 as a unit test 
     5 include/pqxx/transaction_base.hxx, sr/transaction_base.cxx: 
     6  - New function activate() for explicit bracketing 
     7  - Activate before executing prepared statement, thanks qelo 
    182008-08-27  Jeroen T. Vermeulen <jtv@xs4all.nl> 
    29 include/pqxx/compiler-public.hxx: 
  • trunk/include/pqxx/connection_base.hxx

    r1386 r1388  
    608608   */ 
    609609  prepare::declaration prepare(const PGSTD::string &name, 
    610         const PGSTD::string &definition);                              //[t85] 
     610        const PGSTD::string &definition); 
    611611 
    612612  /// Drop prepared statement 
    613   void unprepare(const PGSTD::string &name);                           //[t85] 
     613  void unprepare(const PGSTD::string &name); 
    614614 
    615615  /// Request that prepared statement be registered with the server 
     
    624624   * whether to register prepared statements that you've defined. 
    625625   */ 
    626   void prepare_now(const PGSTD::string &name);                         //[t85] 
     626  void prepare_now(const PGSTD::string &name); 
    627627 
    628628  /** 
  • trunk/include/pqxx/transaction_base.hxx

    r1349 r1388  
    223223   * ongoing transaction in the process. 
    224224   */ 
    225   prepare::invocation prepared(const PGSTD::string &statement);                //[t85] 
     225  prepare::invocation prepared(const PGSTD::string &statement); 
    226226 
    227227  //@} 
     
    338338  }; 
    339339 
     340  /// Make sure transaction is opened on backend, if appropriate 
     341  void PQXX_PRIVATE activate(); 
    340342 
    341343  void PQXX_PRIVATE CheckPendingError(); 
  • trunk/src/transaction_base.cxx

    r1349 r1388  
    198198 
    199199 
     200void pqxx::transaction_base::activate() 
     201{ 
     202  switch (m_Status) 
     203  { 
     204  case st_nascent: 
     205    // Make sure transaction has begun before executing anything 
     206    Begin(); 
     207    break; 
     208 
     209  case st_active: 
     210    break; 
     211 
     212  case st_committed: 
     213  case st_aborted: 
     214  case st_in_doubt: 
     215    throw usage_error( 
     216        "Attempt to activate " + description() + " " 
     217        "which is already closed"); 
     218 
     219  default: 
     220    throw internal_error("pqxx::transaction: invalid status code"); 
     221  } 
     222} 
     223 
     224 
    200225pqxx::result pqxx::transaction_base::exec(const PGSTD::string &Query, 
    201226                                          const PGSTD::string &Desc) 
     
    211236                      "still open"); 
    212237 
    213   switch (m_Status) 
    214   { 
    215   case st_nascent: 
    216     // Make sure transaction has begun before executing anything 
    217     Begin(); 
    218     break; 
    219  
    220   case st_active: 
    221     break; 
    222  
    223   case st_committed: 
    224   case st_aborted: 
    225   case st_in_doubt: 
    226     throw usage_error("Attempt to execute query " + N + " " 
    227         "in " + description() + ", which is already closed"); 
    228  
    229   default: 
    230     throw internal_error("pqxx::transaction: invalid status code"); 
     238  try 
     239  { 
     240    activate(); 
     241  } 
     242  catch (const usage_error &e) 
     243  { 
     244    throw usage_error("Error executing query " + N + ".  " + e.what()); 
    231245  } 
    232246 
     
    239253pqxx::transaction_base::prepared(const PGSTD::string &statement) 
    240254{ 
     255  try 
     256  { 
     257    activate(); 
     258  } 
     259  catch (const usage_error &e) 
     260  { 
     261    throw usage_error( 
     262        "Error executing prepared statement " + statement + ".  " + e.what()); 
     263  } 
    241264  return prepare::invocation(*this, statement); 
    242265} 
  • trunk/test/unit/test_prepared_statement.cxx

    r1378 r1388  
    7878 
    7979 
    80 void test_085(connection_base &C, transaction_base &T) 
    81 
     80void test_prepared_statement(connection_base &C, transaction_base &T) 
     81
     82  /* A bit of nastiness in prepared statements: on 7.3.x backends we can't 
     83   * compare pg_tables.tablename to a string.  We work around this by using 
     84   * the LIKE operator. 
     85   * 
     86   * Later backend versions do not suffer from this problem. 
     87   */ 
     88  const string 
     89        Q_readpgtables = "SELECT * FROM pg_tables", 
     90        Q_seetable = Q_readpgtables + " WHERE tablename LIKE $1", 
     91        Q_seetables = Q_seetable + " OR tablename LIKE $2"; 
     92 
    8293  try 
    8394  { 
    84     /* A bit of nastiness in prepared statements: on 7.3.x backends we can't 
    85      * compare pg_tables.tablename to a string.  We work around this by using 
    86      * the LIKE operator. 
    87      * 
    88      * Later backend versions do not suffer from this problem. 
    89      */ 
    90     const string 
    91           Q_readpgtables = "SELECT * FROM pg_tables", 
    92           Q_seetable = Q_readpgtables + " WHERE tablename LIKE $1", 
    93           Q_seetables = Q_seetable + " OR tablename LIKE $2"; 
    94  
    9595    cout << "Preparing a simple statement..." << endl; 
    9696    C.prepare("ReadPGTables", Q_readpgtables); 
     
    102102        T.exec(Q_readpgtables), 
    103103        "ReadPGTables"); 
    104  
    105     // Try prepare_now() on an already prepared statement 
    106     C.prepare_now("ReadPGTables"); 
    107  
    108     // Pro forma check: same thing but with name passed as C-style string 
    109     COMPARE_RESULTS("ReadPGTables_char", 
    110         T.prepared("ReadPGTables").exec(), 
    111         T.exec(Q_readpgtables)); 
    112  
    113     cout << "Dropping prepared statement..." << endl; 
    114     C.unprepare("ReadPGTables"); 
    115  
    116     PQXX_CHECK_THROWS( 
    117         C.prepare_now("ReadPGTables"), 
    118         exception, 
    119         "prepare_now() succeeded on dropped statement."); 
    120  
    121     // Just to try and confuse things, "unprepare" twice 
    122     cout << "Testing error detection and handling..." << endl; 
    123     try { C.unprepare("ReadPGTables"); } 
    124     catch (const exception &e) { cout << "(Expected) " << e.what() << endl; } 
    125  
    126     // Verify that attempt to execute unprepared statement fails 
    127     PQXX_CHECK_THROWS( 
    128         T.prepared("ReadPGTables").exec(), 
    129         exception, 
    130         "Execute unprepared statement didn't fail."); 
    131  
    132     // Re-prepare the same statement and test again 
    133     C.prepare("ReadPGTables", Q_readpgtables); 
    134     C.prepare_now("ReadPGTables"); 
    135     COMPARE_RESULTS("ReadPGTables_2", 
    136         T.prepared("ReadPGTables").exec(), 
    137         T.exec(Q_readpgtables)); 
    138  
    139     // Double preparation of identical statement should be ignored... 
    140     C.prepare("ReadPGTables", Q_readpgtables); 
    141     COMPARE_RESULTS("ReadPGTables_double", 
    142         T.prepared("ReadPGTables").exec(), 
    143         T.exec(Q_readpgtables)); 
    144  
    145     // ...But a modified definition shouldn't 
    146     PQXX_CHECK_THROWS( 
    147         C.prepare("ReadPGTables", Q_readpgtables + " ORDER BY tablename"), 
    148         exception, 
    149         "Bad redefinition of statement went unnoticed."); 
    150  
    151     cout << "Testing prepared statement with parameter..." << endl; 
    152  
    153     C.prepare("SeeTable", Q_seetable)("varchar", pqxx::prepare::treat_string); 
    154  
    155     vector<string> args; 
    156     args.push_back("pg_type"); 
    157     COMPARE_RESULTS("SeeTable_seq", 
    158         T.prepared("SeeTable")(args[0]).exec(), 
    159         T.exec(subst(T,Q_seetable,args))); 
    160  
    161     cout << "Testing prepared statement with 2 parameters..." << endl; 
    162  
    163     C.prepare("SeeTables", Q_seetables) 
    164       ("varchar",pqxx::prepare::treat_string) 
    165       ("varchar",pqxx::prepare::treat_string); 
    166     args.push_back("pg_index"); 
    167     COMPARE_RESULTS("SeeTables_seq", 
    168       T.prepared("SeeTables")(args[0])(args[1]).exec(), 
    169       T.exec(subst(T,Q_seetables,args))); 
    170  
    171     cout << "Testing prepared statement with a null parameter..." << endl; 
    172     vector<const char *> ptrs; 
    173     ptrs.push_back(0); 
    174     ptrs.push_back("pg_index"); 
    175     COMPARE_RESULTS("SeeTables_null1", 
    176         T.prepared("SeeTables")(ptrs[0])(ptrs[1]).exec(), 
    177         T.exec(subst(T,Q_seetables,ptrs))); 
    178     COMPARE_RESULTS("SeeTables_null2", 
    179         T.prepared("SeeTables")(ptrs[0])(ptrs[1]).exec(), 
    180         T.prepared("SeeTables")()(ptrs[1]).exec()); 
    181     COMPARE_RESULTS("SeeTables_null3", 
    182         T.prepared("SeeTables")(ptrs[0])(ptrs[1]).exec(), 
    183         T.prepared("SeeTables")("somestring",false)(ptrs[1]).exec()); 
    184     COMPARE_RESULTS("SeeTables_null4", 
    185         T.prepared("SeeTables")(ptrs[0])(ptrs[1]).exec(), 
    186         T.prepared("SeeTables")(42,false)(ptrs[1]).exec()); 
    187     COMPARE_RESULTS("SeeTables_null5", 
    188         T.prepared("SeeTables")(ptrs[0])(ptrs[1]).exec(), 
    189         T.prepared("SeeTables")(0,false)(ptrs[1]).exec()); 
    190  
    191     cout << "Testing wrong numbers of parameters..." << endl; 
    192     PQXX_CHECK_THROWS( 
    193         T.prepared("SeeTables")()()("hi mom!").exec(), 
    194         exception, 
    195         "No error for too many parameters."); 
    196  
    197     PQXX_CHECK_THROWS( 
    198         T.prepared("SeeTables")("who, me?").exec(), 
    199         exception, 
    200         "No error for too few parameters."); 
    201104  } 
    202105  catch (const feature_not_supported &e) 
     
    204107    cout << "Backend version does not support prepared statements.  Skipping." 
    205108         << endl; 
     109    return; 
    206110  } 
     111 
     112  // Try prepare_now() on an already prepared statement 
     113  C.prepare_now("ReadPGTables"); 
     114 
     115  // Pro forma check: same thing but with name passed as C-style string 
     116  COMPARE_RESULTS("ReadPGTables_char", 
     117        T.prepared("ReadPGTables").exec(), 
     118        T.exec(Q_readpgtables)); 
     119 
     120  cout << "Dropping prepared statement..." << endl; 
     121  C.unprepare("ReadPGTables"); 
     122 
     123  PQXX_CHECK_THROWS( 
     124        C.prepare_now("ReadPGTables"), 
     125        exception, 
     126        "prepare_now() succeeded on dropped statement."); 
     127 
     128  // Just to try and confuse things, "unprepare" twice 
     129  cout << "Testing error detection and handling..." << endl; 
     130  try { C.unprepare("ReadPGTables"); } 
     131  catch (const exception &e) { cout << "(Expected) " << e.what() << endl; } 
     132 
     133  // Verify that attempt to execute unprepared statement fails 
     134  PQXX_CHECK_THROWS( 
     135        T.prepared("ReadPGTables").exec(), 
     136        exception, 
     137        "Execute unprepared statement didn't fail."); 
     138 
     139  // Re-prepare the same statement and test again 
     140  C.prepare("ReadPGTables", Q_readpgtables); 
     141  C.prepare_now("ReadPGTables"); 
     142  COMPARE_RESULTS("ReadPGTables_2", 
     143        T.prepared("ReadPGTables").exec(), 
     144        T.exec(Q_readpgtables)); 
     145 
     146  // Double preparation of identical statement should be ignored... 
     147  C.prepare("ReadPGTables", Q_readpgtables); 
     148  COMPARE_RESULTS("ReadPGTables_double", 
     149        T.prepared("ReadPGTables").exec(), 
     150        T.exec(Q_readpgtables)); 
     151 
     152  // ...But a modified definition shouldn't 
     153  PQXX_CHECK_THROWS( 
     154        C.prepare("ReadPGTables", Q_readpgtables + " ORDER BY tablename"), 
     155        exception, 
     156        "Bad redefinition of statement went unnoticed."); 
     157 
     158  cout << "Testing prepared statement with parameter..." << endl; 
     159 
     160  C.prepare("SeeTable", Q_seetable)("varchar", pqxx::prepare::treat_string); 
     161 
     162  vector<string> args; 
     163  args.push_back("pg_type"); 
     164  COMPARE_RESULTS("SeeTable_seq", 
     165        T.prepared("SeeTable")(args[0]).exec(), 
     166        T.exec(subst(T,Q_seetable,args))); 
     167 
     168  cout << "Testing prepared statement with 2 parameters..." << endl; 
     169 
     170  C.prepare("SeeTables", Q_seetables) 
     171      ("varchar",pqxx::prepare::treat_string) 
     172      ("varchar",pqxx::prepare::treat_string); 
     173  args.push_back("pg_index"); 
     174  COMPARE_RESULTS("SeeTables_seq", 
     175      T.prepared("SeeTables")(args[0])(args[1]).exec(), 
     176      T.exec(subst(T,Q_seetables,args))); 
     177 
     178  cout << "Testing prepared statement with a null parameter..." << endl; 
     179  vector<const char *> ptrs; 
     180  ptrs.push_back(0); 
     181  ptrs.push_back("pg_index"); 
     182  COMPARE_RESULTS("SeeTables_null1", 
     183        T.prepared("SeeTables")(ptrs[0])(ptrs[1]).exec(), 
     184        T.exec(subst(T,Q_seetables,ptrs))); 
     185  COMPARE_RESULTS("SeeTables_null2", 
     186        T.prepared("SeeTables")(ptrs[0])(ptrs[1]).exec(), 
     187        T.prepared("SeeTables")()(ptrs[1]).exec()); 
     188  COMPARE_RESULTS("SeeTables_null3", 
     189        T.prepared("SeeTables")(ptrs[0])(ptrs[1]).exec(), 
     190        T.prepared("SeeTables")("somestring",false)(ptrs[1]).exec()); 
     191  COMPARE_RESULTS("SeeTables_null4", 
     192        T.prepared("SeeTables")(ptrs[0])(ptrs[1]).exec(), 
     193        T.prepared("SeeTables")(42,false)(ptrs[1]).exec()); 
     194  COMPARE_RESULTS("SeeTables_null5", 
     195        T.prepared("SeeTables")(ptrs[0])(ptrs[1]).exec(), 
     196        T.prepared("SeeTables")(0,false)(ptrs[1]).exec()); 
     197 
     198  cout << "Testing wrong numbers of parameters..." << endl; 
     199  PQXX_CHECK_THROWS( 
     200        T.prepared("SeeTables")()()("hi mom!").exec(), 
     201        exception, 
     202        "No error for too many parameters."); 
     203 
     204  PQXX_CHECK_THROWS( 
     205        T.prepared("SeeTables")("who, me?").exec(), 
     206        exception, 
     207        "No error for too few parameters."); 
    207208} 
    208209} // namespace 
    209210 
    210 PQXX_REGISTER_TEST(test_085
     211PQXX_REGISTER_TEST(test_prepared_statement