Skip to content

Commit

Permalink
wip sql: clean up Local engine
Browse files Browse the repository at this point in the history
  • Loading branch information
erikgrinaker committed Jun 19, 2024
1 parent 249fd65 commit 738afb2
Show file tree
Hide file tree
Showing 21 changed files with 243 additions and 176 deletions.
349 changes: 208 additions & 141 deletions src/sql/engine/local.rs

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/sql/engine/raft.rs
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@ impl<E: storage::Engine> raft::State for State<E> {
};
txn.state().encode()
}
Read::Status => self.local.kv.status()?.encode(),
Read::Status => self.local.mvcc.status()?.encode(),

Read::Get { txn, table, ids } => self.local.resume(txn)?.get(&table, &ids)?.encode(),
Read::LookupIndex { txn, table, column, values } => {
Expand Down
25 changes: 16 additions & 9 deletions src/sql/types/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use crate::{errdata, errinput};
use serde::{Deserialize, Serialize};
use std::borrow::Cow;

/// A table schema, which specifies the structure and constraints of its data.
/// A table schema, which specifies the data structure and constraints.
///
/// Tables can't change after they are created. There is no ALTER TABLE nor
/// CREATE/DROP INDEX -- only CREATE TABLE and DROP TABLE.
Expand Down Expand Up @@ -183,16 +183,23 @@ impl Table {
Ok(())
}

/// Validates a row.
/// Validates a row, include uniqueness constraints and references.
///
/// TODO: clean this up together with the Local engine. Who should be
/// responsible for non-local validation (i.e. primary/unique conflicts and
/// reference integrity)?
pub fn validate_row(&self, row: &[Value], txn: &impl Transaction) -> Result<()> {
/// If update is true, the row replaces an existing entry with the same
/// primary key. Otherwise, it is an insert. Primary key changes are
/// implemented as a delete+insert.
pub fn validate_row(&self, row: &[Value], update: bool, txn: &impl Transaction) -> Result<()> {
if row.len() != self.columns.len() {
return errinput!("invalid row size for table {}", self.name);
}
let pk = self.get_row_key(row)?;

// Validate primary key.
let id = &row[self.primary_key];
let idslice = &row[self.primary_key..=self.primary_key];
if !update && !txn.get(&self.name, idslice)?.is_empty() {
return errinput!("primary key {id} already exists");
}

for (i, (column, value)) in self.columns.iter().zip(row.iter()).enumerate() {
// Validate datatype.
match value.datatype() {
Expand Down Expand Up @@ -222,7 +229,7 @@ impl Table {
match value {
Value::Null => Ok(()),
Value::Float(f) if f.is_nan() => Ok(()),
v if target == &self.name && v == pk => Ok(()),
v if target == &self.name && v == id => Ok(()),
v if txn.get(target, &[v.clone()])?.is_empty() => {
errinput!("referenced primary key {v} in table {target} does not exist",)
}
Expand All @@ -237,7 +244,7 @@ impl Table {
let mut scan = txn.scan(&self.name, None)?;
while let Some(row) = scan.next().transpose()? {
if row.get(index).unwrap_or(&Value::Null) == value
&& self.get_row_key(&row)? != pk
&& self.get_row_key(&row)? != id
{
return errinput!(
"unique value {value} already exists for column {}",
Expand Down
2 changes: 1 addition & 1 deletion tests/sql/schema/delete_ref_conflict
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Query: DELETE FROM target WHERE id = 1
Error: InvalidInput("primary key 1 referenced by table source column target_id")
Error: InvalidInput("row referenced by source.target_id for source.id=1")

Storage:
CREATE TABLE source (
Expand Down
9 changes: 1 addition & 8 deletions tests/sql/schema/delete_ref_self_all
Original file line number Diff line number Diff line change
@@ -1,18 +1,11 @@
Query: DELETE FROM self
Error: InvalidInput("primary key 1 referenced by table self column self_id")
Result: Delete { count: 4 }

Storage:
CREATE TABLE self (
id INTEGER PRIMARY KEY,
self_id INTEGER DEFAULT NULL INDEX REFERENCES self,
value STRING DEFAULT NULL
)
[Integer(1), Integer(1), String("a")]
[Integer(2), Integer(1), String("b")]
[Integer(3), Integer(3), String("c")]
[Integer(4), Null, String("d")]

Index self.self_id
Null => [Integer(4)]
Integer(1) => [Integer(1), Integer(2)]
Integer(3) => [Integer(3)]
2 changes: 1 addition & 1 deletion tests/sql/schema/delete_ref_self_conflict
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Query: DELETE FROM self WHERE id = 1
Error: InvalidInput("primary key 1 referenced by table self column self_id")
Error: InvalidInput("row referenced by self.self_id for self.id=2")

Storage:
CREATE TABLE self (
Expand Down
2 changes: 1 addition & 1 deletion tests/sql/schema/drop_table_ref_target
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Query: DROP TABLE target
Error: InvalidInput("table target is referenced by table source column target_id")
Error: InvalidInput("table target is referenced from source.target_id")

Storage:
CREATE TABLE self (
Expand Down
2 changes: 1 addition & 1 deletion tests/sql/schema/insert_pk_boolean_conflict
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Query: INSERT INTO "boolean" VALUES (FALSE)
Error: InvalidInput("primary key FALSE already exists for table boolean")
Error: InvalidInput("primary key FALSE already exists")

Storage:
CREATE TABLE "boolean" (
Expand Down
2 changes: 1 addition & 1 deletion tests/sql/schema/insert_pk_float_conflict
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Query: INSERT INTO "float" VALUES (3.14)
Error: InvalidInput("primary key 3.14 already exists for table float")
Error: InvalidInput("primary key 3.14 already exists")

Storage:
CREATE TABLE "float" (
Expand Down
2 changes: 1 addition & 1 deletion tests/sql/schema/insert_pk_float_infinity
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Query: INSERT INTO "float" VALUES (INFINITY)
Error: InvalidInput("primary key inf already exists for table float")
Error: InvalidInput("primary key inf already exists")

Storage:
CREATE TABLE "float" (
Expand Down
2 changes: 1 addition & 1 deletion tests/sql/schema/insert_pk_float_nan
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Query: INSERT INTO "float" VALUES (NAN)
Error: InvalidInput("primary key NaN already exists for table float")
Error: InvalidInput("primary key NaN already exists")

Storage:
CREATE TABLE "float" (
Expand Down
2 changes: 1 addition & 1 deletion tests/sql/schema/insert_pk_integer_conflict
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Query: INSERT INTO "integer" VALUES (1)
Error: InvalidInput("primary key 1 already exists for table integer")
Error: InvalidInput("primary key 1 already exists")

Storage:
CREATE TABLE "integer" (
Expand Down
2 changes: 1 addition & 1 deletion tests/sql/schema/insert_pk_string_conflict
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Query: INSERT INTO "string" VALUES ('foo')
Error: InvalidInput("primary key foo already exists for table string")
Error: InvalidInput("primary key foo already exists")

Storage:
CREATE TABLE "string" (
Expand Down
2 changes: 1 addition & 1 deletion tests/sql/schema/update_pk_float_conflict
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Query: UPDATE "float" SET pk = 2.718 WHERE pk = 3.14
Error: InvalidInput("primary key 2.718 already exists for table float")
Error: InvalidInput("primary key 2.718 already exists")

Storage:
CREATE TABLE "float" (
Expand Down
2 changes: 1 addition & 1 deletion tests/sql/schema/update_pk_float_conflict_all
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Query: UPDATE "float" SET pk = 3.14
Error: InvalidInput("primary key 3.14 already exists for table float")
Error: InvalidInput("primary key 3.14 already exists")

Storage:
CREATE TABLE "float" (
Expand Down
2 changes: 1 addition & 1 deletion tests/sql/schema/update_pk_integer_conflict
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Query: UPDATE "integer" SET pk = 1 WHERE pk = 2
Error: InvalidInput("primary key 1 already exists for table integer")
Error: InvalidInput("primary key 1 already exists")

Storage:
CREATE TABLE "integer" (
Expand Down
2 changes: 1 addition & 1 deletion tests/sql/schema/update_pk_integer_conflict_all
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Query: UPDATE "integer" SET pk = 1
Error: InvalidInput("primary key 1 already exists for table integer")
Error: InvalidInput("primary key 1 already exists")

Storage:
CREATE TABLE "integer" (
Expand Down
2 changes: 1 addition & 1 deletion tests/sql/schema/update_pk_string_conflict
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Query: UPDATE "string" SET pk = 'bar' WHERE pk = 'foo'
Error: InvalidInput("primary key bar already exists for table string")
Error: InvalidInput("primary key bar already exists")

Storage:
CREATE TABLE "string" (
Expand Down
2 changes: 1 addition & 1 deletion tests/sql/schema/update_pk_string_conflict_all
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Query: UPDATE "string" SET pk = 'foo'
Error: InvalidInput("primary key foo already exists for table string")
Error: InvalidInput("primary key foo already exists")

Storage:
CREATE TABLE "string" (
Expand Down
2 changes: 1 addition & 1 deletion tests/sql/schema/update_ref_pk
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Query: UPDATE target SET id = 9 WHERE id = 1
Error: InvalidInput("primary key 1 referenced by table source column target_id")
Error: InvalidInput("row referenced by source.target_id for source.id=1")

Storage:
CREATE TABLE source (
Expand Down
2 changes: 1 addition & 1 deletion tests/sql/schema/update_ref_self_pk
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Query: UPDATE self SET id = 9 WHERE id = 1
Error: InvalidInput("primary key 1 referenced by table self column self_id")
Error: InvalidInput("row referenced by self.self_id for self.id=2")

Storage:
CREATE TABLE self (
Expand Down

0 comments on commit 738afb2

Please sign in to comment.