Skip to content

Commit

Permalink
row: Improve API and implementation
Browse files Browse the repository at this point in the history
- The `Row` now has new functions for `get_bool`, `get_null` and
`get_unknown` for respective value types.
- The get methods are sensitive to returning only on the correct types
(ie. `get_f64` can only be used on numeric values).
- An error is returned from any get method if the column does not exist.
- Should a column not exist but there is a column with a different
case, a more helpful message is returned like
"no such column foo, did you mean FOO?"

Fixes #13
  • Loading branch information
elliotchance committed Jul 28, 2021
1 parent a649dd4 commit 811480d
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 10 deletions.
11 changes: 8 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ Usage
import elliotchance.vsql.vsql
fn example() ? {
mut db := vsql.open('/tmp/test.vsql') ?
mut db := vsql.open('test.vsql') ?
// All SQL commands use query():
db.query('CREATE TABLE foo (a FLOAT)') ?
Expand All @@ -47,8 +47,10 @@ fn example() ? {
// Iterate through a result:
result := db.query('SELECT * FROM foo') ?
println(result.columns)
for row in result {
println(row.get_f64('a'))
println(row.get_f64('A') ?)
}
// See SQLSTATE (Errors) below for more examples.
Expand All @@ -58,11 +60,14 @@ fn example() ? {
Outputs:

```
['A']
1.23
4.56
I knew 'BAR' did not exist!
```

You can find the documentation for a
[`Row` here](https://github.com/elliotchance/vsql/blob/main/vsql/row.v).

### CLI

You can also work with database files through the CLI (ctrl+c to exit).
Expand Down
2 changes: 1 addition & 1 deletion vsql/eval.v
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ fn eval_binary(data Row, e BinaryExpr) ?Value {
col := identifier_name(e.col)

if data.data[col].typ.uses_f64() && e.value.typ.uses_f64() {
return eval_cmp<f64>(data.get_f64(col), e.value.f64_value, e.op)
return eval_cmp<f64>(data.get_f64(col) ?, e.value.f64_value, e.op)
}

// TODO(elliotchance): Use the correct SQLSTATE error.
Expand Down
70 changes: 65 additions & 5 deletions vsql/row.v
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,81 @@

module vsql

pub struct Row {
struct Row {
mut:
offset u32
data map[string]Value
}

pub fn (r Row) get_f64(name string) f64 {
return r.data[name].f64_value
// get_null will return true if the column name is NULL. An error will be
// returned if the column does not exist.
pub fn (r Row) get_null(name string) ?bool {
value := r.get(name) ?
return value.typ.typ == .is_null
}

pub fn (r Row) get_string(name string) string {
return match r.data[name].typ.typ {
// get_f64 will only work for columns that are numerical (FLOAT, REAL, etc). If
// the value is NULL, 0 will be returned. See get_null().
pub fn (r Row) get_f64(name string) ?f64 {
value := r.get(name) ?
if value.typ.uses_f64() {
return value.f64_value
}

return error("cannot use get_f64('$name') when type is $value.typ")
}

// get_string is the most flexible getter and will try to coerce the value
// (including non-strings like numbers, booleans, NULL, etc) into some kind of
// string.
//
// An error is only returned if the column does not exist.
pub fn (r Row) get_string(name string) ?string {
value := r.get(name) ?
return match value.typ.typ {
.is_null { 'NULL' }
.is_boolean { bool_str(r.data[name].f64_value) }
.is_float, .is_real, .is_bigint, .is_integer, .is_smallint { r.data[name].f64_value.str().trim('.') }
.is_varchar, .is_character { r.data[name].string_value }
}
}

// get_bool only works on a BOOLEAN value. If the value is NULL or UNKNOWN,
// false will be returned. See get_null() and get_unknown() repsectively.
//
// An error is returned if the type is not a BOOLEAN or the column name does not
// exist.
pub fn (r Row) get_bool(name string) ?bool {
value := r.get(name) ?
return match value.typ.typ {
.is_boolean { value.f64_value == 1 }
else { false }
}
}

// get_unknown returns true only is a value is a BOOLEAN and in the UNKNOWN
// state. The UNKNOWN state is a third state beyond TRUE and FALSE defined in
// the SQL standard. A NULL BOOLEAN will return false.
//
// An error is returned if the type is not a BOOLEAN or the column name does not
// exist.
pub fn (r Row) get_unknown(name string) ?bool {
value := r.get(name) ?
return match value.typ.typ {
.is_boolean { value.f64_value == 2 }
else { false }
}
}

fn (r Row) get(name string) ?Value {
return r.data[name] or {
// Be helpful and look for silly mistakes.
for n, _ in r.data {
if n.to_upper() == name.to_upper() {
return error('no such column $name, did you mean $n?')
}
}

return error('no such column $name')
}
}
2 changes: 1 addition & 1 deletion vsql/sql_test.v
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ fn test_all() ? {
for row in result {
mut line := ''
for col in result.columns {
line += '$col: ${row.get_string(col)} '
line += '$col: ${row.get_string(col) ?} '
}
actual += line.trim_space() + '\n'
}
Expand Down

0 comments on commit 811480d

Please sign in to comment.