File: test007.cxx

package info (click to toggle)
libpqxx 4.0.1%2Bdfsg3-8
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 16,012 kB
  • ctags: 9,469
  • sloc: sh: 11,289; cpp: 10,801; xml: 1,256; makefile: 287; ansic: 195; python: 159
file content (193 lines) | stat: -rw-r--r-- 5,483 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
#include <iostream>
#include <map>

#include "test_helpers.hxx"

using namespace PGSTD;
using namespace pqxx;


// Example program for libpqxx.  Modify the database, retaining transactional
// integrity using the transactor framework.
//
// This assumes the existence of a database table "pqxxevents" containing a
// 2-digit "year" field, which is extended to a 4-digit format by assuming all
// year numbers of 70 or higher are in the 20th century, and all others in the
// 21st, and that no years before 1970 are possible.

namespace
{

// Global list of converted year numbers and what they've been converted to
map<int,int> theConversions;


// Convert year to 4-digit format.
int To4Digits(int Y)
{
  int Result = Y;

  PQXX_CHECK(Y >= 0, "Negative year: " + to_string(Y));
  if (Y  < 70)   Result += 2000;
  else if (Y  < 100)  Result += 1900;
  else if (Y  < 1970) PQXX_CHECK_NOTREACHED("Unexpected year: " + to_string(Y));

  return Result;
}


// Transaction definition for year-field update
class UpdateYears : public transactor<>
{
public:
  UpdateYears() : transactor<>("YearUpdate") {}

  // Transaction definition
  void operator()(argument_type &T)
  {
    // First select all different years occurring in the table.
    result R( T.exec("SELECT year FROM pqxxevents") );

    // See if we get reasonable type identifier for this column
    const oid rctype = R.column_type(0);
    PQXX_CHECK_EQUAL(
	R.column_type(pqxx::tuple::size_type(0)),
	rctype,
	"Inconsistent result::column_type().");

    const string rct = to_string(rctype);
    PQXX_CHECK(rctype > 0, "Got strange type ID for column: " + rct);

    const string rcol = R.column_name(0);
    PQXX_CHECK( !rcol.empty(), "Didn't get a name for column.");

    const oid rcctype = R.column_type(rcol);
    PQXX_CHECK_EQUAL(rcctype, rctype, "Column type is not what it is by name.");

    const oid rawrcctype = R.column_type(rcol.c_str());
    PQXX_CHECK_EQUAL(
	rawrcctype,
	rctype,
	"Column type by C-style name is different.");

    // Note all different years currently occurring in the table, writing them
    // and their correct mappings to m_Conversions
    for (result::const_iterator r = R.begin(); r != R.end(); ++r)
    {
      int Y;

      // Read year, and if it is non-null, note its converted value
      if (r[0] >> Y) m_Conversions[Y] = To4Digits(Y);

      // See if type identifiers are consistent
      const oid tctype = r->column_type(0);

      PQXX_CHECK_EQUAL(
	tctype,
	r->column_type(pqxx::tuple::size_type(0)),
	"Inconsistent pqxx::tuple::column_type()");

      PQXX_CHECK_EQUAL(
	tctype,
	rctype,
	"tuple::column_type() is inconsistent with result::column_type().");

      const oid ctctype = r->column_type(rcol);

      PQXX_CHECK_EQUAL(
	ctctype,
	rctype,
	"Column type lookup by column name is broken.");

      const oid rawctctype = r->column_type(rcol.c_str());

      PQXX_CHECK_EQUAL(
	rawctctype,
	rctype,
	"Column type lookup by C-style name is broken.");

      const oid fctype = r[0].type();
      PQXX_CHECK_EQUAL(
	fctype,
	rctype,
	"Field type lookup is broken.");
    }

    result::size_type AffectedRows = 0;

    // For each occurring year, write converted date back to whereever it may
    // occur in the table.  Since we're in a transaction, any changes made by
    // others at the same time will not affect us.
    for (map<int,int>::const_iterator c = m_Conversions.begin();
	 c != m_Conversions.end();
	 ++c)
    {
      R = T.exec(("UPDATE pqxxevents "
	          "SET year=" + to_string(c->second) + " "
	          "WHERE year=" + to_string(c->first)).c_str());
      AffectedRows += R.affected_rows();
    }
    cout << AffectedRows << " rows updated." << endl;
  }

  // Postprocessing code for successful execution
  void on_commit()
  {
    // Report the conversions performed once the transaction has completed
    // successfully.  Do not report conversions occurring in unsuccessful
    // attempts, as some of those may have been removed from the table by
    // somebody else between our attempts.
    // Even if this fails (eg. because we run out of memory), the actual
    // database transaction will still have been performed.
    theConversions = m_Conversions;
  }

  // Postprocessing code for aborted execution attempt
  void on_abort(const char Reason[]) throw ()
  {
    try
    {
      // Notify user that the transaction attempt went wrong; we may retry.
      cerr << "Transaction interrupted: " << Reason << endl;
    }
    catch (const exception &)
    {
      // Ignore any exceptions on cerr.
    }
  }

private:
  // Local store of date conversions to be performed
  map<int,int> m_Conversions;
};


void test_007(transaction_base &T)
{
  connection_base &C(T.conn());
  T.abort();
  C.set_client_encoding("SQL_ASCII");

  {
    work T2(C);
    test::create_pqxxevents(T2);
    T2.commit();
  }

  // Perform (an instantiation of) the UpdateYears transactor we've defined
  // in the code above.  This is where the work gets done.
  C.perform(UpdateYears());

  // Just for fun, report the exact conversions performed.  Note that this
  // list will be accurate even if other people were modifying the database
  // at the same time; this property was established through use of the
  // transactor framework.
  for (map<int,int>::const_iterator i = theConversions.begin();
	 i != theConversions.end();
	 ++i)
    cout << '\t' << i->first << "\t-> " << i->second << endl;
}

} // namespace

PQXX_REGISTER_TEST(test_007)