Skip to content

Commit

Permalink
Rename "field" to "column" throughout
Browse files Browse the repository at this point in the history
  • Loading branch information
erikgrinaker committed Jul 17, 2024
1 parent dfe775b commit b08ac1e
Show file tree
Hide file tree
Showing 30 changed files with 179 additions and 177 deletions.
2 changes: 1 addition & 1 deletion docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -479,7 +479,7 @@ ast::Statement::Select{

The parser will interpret the SQL _syntax_, determining the type of query and its parameters,
returning an error for any invalid syntax. However, it has no idea if the table `people`
actually exists, or if the field `birthyear` is an integer - that is the job of the planner.
actually exists, or if the column `birthyear` is an integer - that is the job of the planner.

Notably, the parser also parses expressions, such as `1 + 2 * 3`. This is non-trivial due to
precedence rules, i.e. `2 * 3` should be evaluated first, but not if there are parentheses
Expand Down
12 changes: 6 additions & 6 deletions docs/sql.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ The `-` prefix operator can be used to take negative numbers.

### Expressions

Expressions can be used wherever a value is expected, e.g. as `SELECT` fields and `INSERT` values. They are made up of constants, a column references, an operator invocations, and a function calls.
Expressions can be used wherever a value is expected, e.g. as `SELECT` columns nd `INSERT` values. They are made up of constants, a column references, an operator invocations, and a function calls.

Column references can either be unqualified, e.g. `name`, or prefixed with the relation identifier separated by `.`, e.g. `person.name`. Unqualified identifiers must be unambiguous.

Expand Down Expand Up @@ -290,7 +290,7 @@ If column names are given, an identical number of values must be given. If no co

* ***`column_name`***: a column to insert into in the given table. Errors if it does not exist.

* ***`expression`***: an expression to insert into the corresponding column. Must be a constant expression, i.e. it cannot refer to table fields.
* ***`expression`***: an expression to insert into the corresponding column. Must be a constant expression, i.e. it cannot refer to table columns.

#### Example

Expand Down Expand Up @@ -337,21 +337,21 @@ RIGHT [ OUTER ] JOIN

Fetches rows or expressions, either from table ***`table_name`*** (if given) or generated.

* ***`expression`***: [expression](#expressions) to fetch (can be a simple field name).
* ***`expression`***: [expression](#expressions) to fetch (can be a simple column name).

* ***`output_name`***: output column [identifier](#identifier), defaults to field name (if single field) otherwise nothing (displayed as `?`).
* ***`output_name`***: output column [identifier](#identifier), defaults to column name (if single column) otherwise nothing (displayed as `?`).

* ***`table_name`***: table to fetch rows from.

* ***`alias`***: table alias.

* ***`predicate`***: only return rows for which this [expression](#expressions) evaluates to `TRUE`.

* ***`group_expr`***: an expression to group aggregates by. Non-aggregate `SELECT` expressions must either reference a field given in `group_expr`, be idential with a `group_expr`, or have an `output_name` that is referenced by a `group_expr` field.
* ***`group_expr`***: an expression to group aggregates by. Non-aggregate `SELECT` expressions must either reference a column given in `group_expr`, be idential with a `group_expr`, or have an `output_name` that is referenced by a `group_expr` column.

* ***`having_expr`***: only return aggregate results for which this [expression](#expressions) evaluates to `TRUE`.

* ***`order_expr`***: order rows by this expression (can be a simple field name).
* ***`order_expr`***: order rows by this expression (can be a simple column name).

* ***`count`***: maximum number of rows to return. Must be a constant integer expression.

Expand Down
12 changes: 6 additions & 6 deletions src/sql/engine/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,17 +184,17 @@ impl TryFrom<StatementResult> for Row {
}
}

/// Extracts the value of the first field in the first row.
/// Extracts the value of the first column in the first row.
impl TryFrom<StatementResult> for Value {
type Error = Error;

fn try_from(result: StatementResult) -> Result<Self> {
let row: Row = result.try_into()?;
row.into_iter().next().ok_or(errdata!("no fields returned"))
row.into_iter().next().ok_or(errdata!("no columns returned"))
}
}

/// Extracts the first boolean value of the first field in the first row.
/// Extracts the first boolean value of the first column in the first row.
impl TryFrom<StatementResult> for bool {
type Error = Error;

Expand All @@ -204,7 +204,7 @@ impl TryFrom<StatementResult> for bool {
}
}

/// Extracts the first f64 value of the first field in the first row.
/// Extracts the first f64 value of the first column in the first row.
impl TryFrom<StatementResult> for f64 {
type Error = Error;

Expand All @@ -214,7 +214,7 @@ impl TryFrom<StatementResult> for f64 {
}
}

/// Extracts the first i64 value of the first field in the first row.
/// Extracts the first i64 value of the first column in the first row.
impl TryFrom<StatementResult> for i64 {
type Error = Error;

Expand All @@ -224,7 +224,7 @@ impl TryFrom<StatementResult> for i64 {
}
}

/// Extracts the first string value of the first field in the first row.
/// Extracts the first string value of the first column in the first row.
impl TryFrom<StatementResult> for String {
type Error = Error;

Expand Down
4 changes: 2 additions & 2 deletions src/sql/execution/execute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,11 @@ pub fn execute(node: Node, txn: &impl Transaction) -> Result<Rows> {
Ok(transform::filter(source, predicate))
}

Node::HashJoin { left, left_field, right, right_field, outer } => {
Node::HashJoin { left, left_column, right, right_column, outer } => {
let right_size = right.size();
let left = execute(*left, txn)?;
let right = execute(*right, txn)?;
join::hash(left, left_field, right, right_field, right_size, outer)
join::hash(left, left_column, right, right_column, right_size, outer)
}

Node::IndexLookup { table, column, values, alias: _ } => {
Expand Down
8 changes: 4 additions & 4 deletions src/sql/execution/join.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,17 +122,17 @@ impl Iterator for NestedLoopIterator {
/// TODO: add more tests for the multiple match case.
pub(super) fn hash(
left: Rows,
left_field: usize,
left_column: usize,
right: Rows,
right_field: usize,
right_column: usize,
right_size: usize,
outer: bool,
) -> Result<Rows> {
// Build the hash table from the right source.
let mut rows = right;
let mut right: HashMap<Value, Vec<Row>> = HashMap::new();
while let Some(row) = rows.next().transpose()? {
let id = row[right_field].clone();
let id = row[right_column].clone();
right.entry(id).or_default().push(row);
}

Expand All @@ -146,7 +146,7 @@ pub(super) fn hash(
return Box::new(std::iter::once(result));
};
// Join the left row with any matching right rows.
match right.get(&row[left_field]) {
match right.get(&row[left_column]) {
Some(matches) => Box::new(
std::iter::once(row)
.cartesian_product(matches.clone())
Expand Down
10 changes: 5 additions & 5 deletions src/sql/execution/write.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::{errdata, errinput};
use std::collections::{BTreeMap, HashMap};

/// Deletes rows, taking primary keys from the source (i.e. DELETE) using the
/// primary_key field index. Returns the number of rows deleted.
/// primary_key column index. Returns the number of rows deleted.
pub(super) fn delete(
txn: &impl Transaction,
table: String,
Expand Down Expand Up @@ -51,8 +51,8 @@ pub(super) fn insert(
}
}

// Fill in the row with default values for missing fields, and map
// source fields to table fields.
// Fill in the row with default values for missing columns, and map
// source columns to table columns.
let mut row = Vec::with_capacity(table.columns.len());
for (cidx, column) in table.columns.iter().enumerate() {
if column_map.is_none() && cidx < values.len() {
Expand Down Expand Up @@ -87,8 +87,8 @@ pub(super) fn update(
let mut updates = BTreeMap::new();
while let Some(row) = source.next().transpose()? {
let mut new = row.clone();
for (field, expr) in &expressions {
new[*field] = expr.evaluate(Some(&row))?;
for (column, expr) in &expressions {
new[*column] = expr.evaluate(Some(&row))?;
}
let id = row.into_iter().nth(primary_key).ok_or::<Error>(errdata!("short row"))?;
updates.insert(id, new);
Expand Down
8 changes: 4 additions & 4 deletions src/sql/parser/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,8 @@ pub enum Order {
/// Expressions. Can be nested.
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub enum Expression {
/// A field reference, with an optional table qualifier.
Field(Option<String>, String),
/// A column reference, optionally qualified with a table name.
Column(Option<String>, String),
/// A literal value.
Literal(Literal),
/// A function call (name and parameters).
Expand Down Expand Up @@ -217,7 +217,7 @@ impl Expression {

Self::Function(_, exprs) => exprs.iter().any(|expr| expr.walk(visitor)),

Self::Literal(_) | Self::Field(_, _) => true,
Self::Literal(_) | Self::Column(_, _) => true,
}
}

Expand Down Expand Up @@ -264,7 +264,7 @@ impl Expression {

Self::Function(_, exprs) => exprs.iter().for_each(|expr| expr.collect(visitor, c)),

Self::Literal(_) | Self::Field(_, _) => {}
Self::Literal(_) | Self::Column(_, _) => {}
}
}
}
Expand Down
8 changes: 4 additions & 4 deletions src/sql/parser/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -510,7 +510,7 @@ impl<'a> Parser<'a> {
/// Parses an expression atom. This is either:
///
/// * A literal value.
/// * A field name.
/// * A column name.
/// * A function call.
/// * A parenthesized expression.
fn parse_expression_atom(&mut self) -> Result<ast::Expression> {
Expand Down Expand Up @@ -544,11 +544,11 @@ impl<'a> Parser<'a> {
ast::Expression::Function(name, args)
}

// Field name, either qualified as table.field or unqualified.
// Column name, either qualified as table.column or unqualified.
Token::Ident(table) if self.next_is(Token::Period) => {
ast::Expression::Field(Some(table), self.next_ident()?)
ast::Expression::Column(Some(table), self.next_ident()?)
}
Token::Ident(field) => ast::Expression::Field(None, field),
Token::Ident(column) => ast::Expression::Column(None, column),

// Parenthesized expression.
Token::OpenParen => {
Expand Down
58 changes: 29 additions & 29 deletions src/sql/planner/optimizer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ pub(super) fn fold_constants(node: Node) -> Result<Node> {
// Transforms expressions.
let transform = |expr: Expression| {
// If the expression is constant, evaluate it.
if !expr.contains(&|e| matches!(e, Expression::Field(_))) {
if !expr.contains(&|e| matches!(e, Expression::Column(_))) {
return expr.evaluate(None).map(Expression::Constant);
}
// If the expression is a logical operator, and one of the sides is
Expand Down Expand Up @@ -55,7 +55,7 @@ pub(super) fn fold_constants(node: Node) -> Result<Node> {

// Transform expressions after descending, both to perform the logical
// short-circuiting on child expressions that have already been folded, and
// to reduce the quadratic cost when an expression contains a field.
// to reduce the quadratic cost when an expression contains a column.
node.transform(&|n| n.transform_expressions(&Ok, &transform), &Ok)
}

Expand Down Expand Up @@ -123,7 +123,7 @@ pub(super) fn push_filters(node: Node) -> Result<Node> {
for expr in cnf {
let (mut ref_left, mut ref_right) = (false, false);
expr.walk(&mut |e| {
if let Expression::Field(index) = e {
if let Expression::Column(index) = e {
ref_left = ref_left || *index < left.size();
ref_right = ref_right || *index >= left.size();
}
Expand All @@ -146,32 +146,32 @@ pub(super) fn push_filters(node: Node) -> Result<Node> {
// commonly happens when joining a foreign key (which is indexed) on a
// primary key, and we want to make use of the foreign key index, e.g.:
// SELECT m.name, g.name FROM movies m JOIN genres g ON m.genre_id = g.id AND g.id = 7;
let left_lookups: HashMap<usize, usize> = push_left // field → push_left index
let left_lookups: HashMap<usize, usize> = push_left // column → push_left index
.iter()
.enumerate()
.filter_map(|(i, expr)| expr.is_field_lookup().map(|field| (field, i)))
.filter_map(|(i, expr)| expr.is_column_lookup().map(|column| (column, i)))
.collect();
let right_lookups: HashMap<usize, usize> = push_right // field → push_right index
let right_lookups: HashMap<usize, usize> = push_right // column → push_right index
.iter()
.enumerate()
.filter_map(|(i, expr)| expr.is_field_lookup().map(|field| (field, i)))
.filter_map(|(i, expr)| expr.is_column_lookup().map(|column| (column, i)))
.collect();

for expr in &predicate {
// Find equijoins.
let Expression::Equal(lhs, rhs) = expr else { continue };
let Expression::Field(l) = lhs.as_ref() else { continue };
let Expression::Field(r) = rhs.as_ref() else { continue };
let Expression::Column(l) = lhs.as_ref() else { continue };
let Expression::Column(r) = rhs.as_ref() else { continue };

// The lhs may be a reference to the right source; swap them.
let (l, r) = if l > r { (r, l) } else { (l, r) };

// Check if either side is a field lookup, and copy it over.
// Check if either side is a column lookup, and copy it over.
if let Some(expr) = left_lookups.get(l).map(|i| push_left[*i].clone()) {
push_right.push(expr.replace_field(*l, *r));
push_right.push(expr.replace_column(*l, *r));
}
if let Some(expr) = right_lookups.get(r).map(|i| push_right[*i].clone()) {
push_left.push(expr.replace_field(*r, *l));
push_left.push(expr.replace_column(*r, *l));
}
}

Expand All @@ -184,11 +184,11 @@ pub(super) fn push_filters(node: Node) -> Result<Node> {
}

if let Some(mut expr) = Expression::and_vec(push_right) {
// Right fields have indexes in the joined row; shift them left.
expr = expr.shift_field(-(left.size() as isize));
// Right columns have indexes in the joined row; shift them left.
expr = expr.shift_column(-(left.size() as isize));
if let Some(mut expr) = push_into(expr, &mut right) {
// Pushdown failed, undo the field index shift.
expr = expr.shift_field(left.size() as isize);
// Pushdown failed, undo the column index shift.
expr = expr.shift_column(left.size() as isize);
predicate.push(expr)
}
}
Expand Down Expand Up @@ -222,15 +222,15 @@ pub(super) fn index_lookup(node: Node) -> Result<Node> {
// Find the first expression that's either a primary key or secondary
// index lookup. We could be more clever here, but this is fine.
let Some(i) = cnf.iter().enumerate().find_map(|(i, e)| {
e.is_field_lookup()
e.is_column_lookup()
.filter(|f| *f == table.primary_key || table.columns[*f].index)
.and(Some(i))
}) else {
return Node::Scan { table, alias, filter: Some(filter) };
};

// Extract the lookup values and expression from the cnf vector.
let (column, values) = cnf.remove(i).into_field_values().expect("field lookup failed");
let (column, values) = cnf.remove(i).into_column_values().expect("column lookup failed");

// Build the primary key or secondary index lookup node.
if column == table.primary_key {
Expand All @@ -249,7 +249,7 @@ pub(super) fn index_lookup(node: Node) -> Result<Node> {
node.transform(&Ok, &|n| Ok(transform(n)))
}

/// Uses a hash join instead of a nested loop join for single-field equijoins.
/// Uses a hash join instead of a nested loop join for single-column equijoins.
pub(super) fn join_type(node: Node) -> Result<Node> {
let transform = |node| match node {
// We could use a single match if we had deref patterns, but alas.
Expand All @@ -259,16 +259,16 @@ pub(super) fn join_type(node: Node) -> Result<Node> {
predicate: Some(Expression::Equal(lhs, rhs)),
outer,
} => match (*lhs, *rhs) {
(Expression::Field(mut left_field), Expression::Field(mut right_field)) => {
// The LHS field may be a field in the right table; swap them.
if right_field < left_field {
(left_field, right_field) = (right_field, left_field);
(Expression::Column(mut left_column), Expression::Column(mut right_column)) => {
// The LHS column may be a column in the right table; swap them.
if right_column < left_column {
(left_column, right_column) = (right_column, left_column);
}
// The NestedLoopJoin predicate uses field indexes in the joined
// row, while the HashJoin uses field indexes for each table
// individually. Adjust the RHS field reference.
right_field -= left.size();
Node::HashJoin { left, left_field, right, right_field, outer }
// The NestedLoopJoin predicate uses column indexes in the
// joined row, while the HashJoin uses column indexes for each
// table individually. Adjust the RHS column reference.
right_column -= left.size();
Node::HashJoin { left, left_column, right, right_column, outer }
}
(lhs, rhs) => {
let predicate = Some(Expression::Equal(lhs.into(), rhs.into()));
Expand Down Expand Up @@ -342,7 +342,7 @@ pub(super) fn short_circuit(node: Node) -> Result<Node> {
&& expressions
.iter()
.enumerate()
.all(|(i, e)| matches!(e, Expression::Field(f) if i == *f)) =>
.all(|(i, e)| matches!(e, Expression::Column(f) if i == *f)) =>
{
*source
}
Expand Down
Loading

0 comments on commit b08ac1e

Please sign in to comment.