Module hdbconnect::code_examples [] [src]

Here are some code examples for the usage of this database driver.

1. Get an authenticated database connection

Establish a physical connecton to the database server ...

 let (host, port): (&str, &str) = ...;
 let mut connection = try!(Connection::new(host,port));

.. and authenticate to the database:

 let (user, pw): (&str, &str) = ...;
 try!(connection.authenticate_user_password(user, pw));

2. Query the database

The most generic way to fire SQL statements without preparation is using Connection::any_statement(). This generic method can handle all kinds of calls, and thus has the most complex return type, HdbResponse.

 let my_statement = "..."; // some statement that doesn't need preparation
 let response: HdbResponse = try!(connection.any_statement(my_statement));

HdbResponse is quite a complex nested enum which covers all possible return values we can get from the database. You thus have to analyze it to come to the concrete response relevant for your call. You can do this either explicitly using match etc or with the adequate short-cut method, e.g.:

 let resultset: ResultSet = try!(response.as_resultset());

You can do the same of course with HdbResponses obtained from the execution of prepared statements.

In many cases it will be more appropriate and convenient to use one of the specialized methods

  • query_statement() -> ResultSet
  • dml_statement() -> usize
  • exec_statement() -> ()

    which have the adequate simple result type you usually want:

    let my_statement = "..."; // some statement that doesn't need preparation
    let resultset: ResultSet = try!(connection.query_statement(my_statement));
  • In many cases, you will need or want to use prepared statements. Then the code will look like this:

    let stmt_str = "insert into TEST_PREPARE (F_STRING, F_INTEGER) values(?, ?)";
    let mut stmt = try!(connection.prepare(stmt_str));
    try!(stmt.add_batch(&("foo", 45_i32)));
    try!(stmt.add_batch(&("bar", 46_i32)));
    try!(stmt.execute_batch());

    Or like this:

    let stmt_str = "select NAME, CITY from TEST_TABLE where age > ?";
    let mut stmt = try!(connection.prepare(stmt_str));
    try!(stmt.add_batch(&(45_i32)));
    let resultset: ResultSet = try!(stmt.execute_batch());

3. Evaluate a resultset

Evaluating a resultset by traversing rows and columns should always be possible, of course, but there's again a more convenient alternative. Thanks to the usage of serde you can convert the resultset directly into a fitting rust structure.

Note that you need to specify the type of your target variable explicitly, so that ResultSet::into_typed(self) can derive the type it needs to serialize into.

Depending on the usecase, ResultSet::into_typed(self) supports a variety of target data structures, with the only strong limitation that no data loss is supported.

  • It depends on the dimension of the resultset what target data structures you can choose for deserialization:

    • You can always use a Vec<MyRow>, where MyRow is a struct or tuple that matches the fields of the resultset.

      #[derive(Deserialize)]
      struct MyRow {
          ...
      }
      
      let result: Vec<MyRow> = try!(resultset.into_typed());
    • If the resultset contains only a single line (e.g. because you specified TOP 1 in your select, or qualified the full primary key), then you can optionally choose to deserialize into a plain MyRow directly.

      #[derive(Deserialize)]
      struct MyRow {
          ...
      }
      
      let result: MyRow = try!(resultset.into_typed());
    • If the resultset contains only a single column, then you can optionally choose to deserialize into a Vec<field>, where field is a type that matches the field of the resultset.

      let result: Vec<u32> = try!(resultset.into_typed());
    • If the resultset contains only a single value (one row with one column), then you can optionally choose to deserialize into a plain MyRow, or a Vec<field>, or a field.

      let result: u32 = try!(resultset.into_typed());
  • Also the (de)serialization of the individual field values provides flexibility.

    • You can e.g. convert values from a nullable column into a plain field, provided that no NULL values are given in the resultset.

    • Vice versa, you always can use an Option<field>, even if the column is marked as NOT NULL.

    • Similarly, integer types can differ, as long as the returned values can be assigned without loss.