Resultset objects created from a schema file

Schema file:
  <table name="accounts">
    <column name="account_id" datatype="int64_t" primarykey="1" />
    <column name="name" />
    <column name="balance" datatype="double" />
accounts all_accounts=accounts::create(conn);

for (accounts::base::iterator b=all_accounts->begin(), e=all_accounts->end();
    b != e; ++b)
    accounts::base::row row=*b;

    std::cout << row->account_id.value() << std::endl;

    if (!row->name.isnull())
        std::cout << row->name.value() << std::endl;

    std::cout << row->balance.value() << std::endl;

The C++ code created by buildschema.xsl defines a reference-counted object whose name is the same as each table's name. In this example accounts is a resultset for the accounts table. The resultsets are subclasses of x::sql::dbi::resultsets.

The reference-counted objects have a begin() and an end() method that return a class::base::iterator that define an input sequence over the table's rows. Other methods, described later, add constraints that translate to SQL WHERE and other clauses. But without them, iterating over the input sequence becomes an equivalent to a simple SELECT * FROM table.

The iterators iterate over class::base::rows, each one is a single row in the table. Each column is a member of the class::base::row, whose value() returns the value of the column in that row.

The above example schema file defines a table with three columns, account_id, name, and balance; consequently account_id.value(), name.value(), balance.value() return the values of the corresponding columns, in each row. By default, each column value is a std::string. An optional datatype attribute specifies a different datatype to use for the column's value. An optional primarykey attribute specifies the table's primary key column(s). All tables must have primary keys. Primary keys are used when updating and inserting new rows.

All fields also have an field.isnull() method, that returns an indication if the value is NULL or not. It's possible that a field could be NULL even if it's NOT NULL in the schema: prefetching a row from an outer-joined table produces a row with all NULL fields if the outer joined table did not have a row that matched the join!

The begin() and end() methods are suitable for range iteration:

accounts all_accounts=accounts::create(conn);

for (const auto &row: *all_accounts)
    // ...


Do not use a temporary with range iteration:

for (const auto &row: *accounts:create())

This is because range iteration uses rvalue references, and create() goes out of scope in the body, resulting in the iterators getting invoked via a dangling rvalue reference.