Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Require NOT NULL on all primary key columns #361

Merged
merged 3 commits into from
Sep 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,8 @@ Usage looks like:
.load crsqlite
.mode qbox
-- create tables as normal
create table foo (a primary key, b);
create table baz (a primary key, b, c, d);
create table foo (a primary key not null, b);
create table baz (a primary key not null, b, c, d);

-- update those tables to be crrs / crdts
select crsql_as_crr('foo');
Expand Down Expand Up @@ -180,7 +180,7 @@ Example table definition:

```sql
CREATE CLSet post (
id INTEGER PRIMARY KEY,
id INTEGER PRIMARY KEY NOT NULL,
views COUNTER,
content PERITEXT,
owner_id LWW INTEGER
Expand Down
22 changes: 17 additions & 5 deletions core/rs/core/src/tableinfo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -496,17 +496,29 @@ pub fn is_table_compatible(
}

// Must have a primary key
if db.count(&format!(
let valid_pks = db.count(&format!(
// pragma_index_list does not include primary keys that alias rowid...
// hence why we cannot use
// `select * from pragma_index_list where origin = pk`
"SELECT count(*) FROM pragma_table_info('{table}')
WHERE \"pk\" > 0"
))? == 0
WHERE \"pk\" > 0 AND \"notnull\" > 0"
))?;
if valid_pks == 0 {
err.set(&format!(
"Table {table} has no primary key or primary key is nullable. \
CRRs must have a non nullable primary key"
));
return Ok(false);
}

// All primary keys have to be non-nullable
if db.count(&format!(
"SELECT count(*) FROM pragma_table_info('{table}') WHERE \"pk\" > 0"
))? != valid_pks
{
err.set(&format!(
"Table {table} has no primary key. \
CRRs must have a primary key"
"Table {table} has composite primary key part of which is nullable. \
CRRs must have a non nullable primary key"
));
return Ok(false);
}
Expand Down
2 changes: 1 addition & 1 deletion core/rs/integration_check/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ pub extern "C" fn crsql_integration_check() {
t::pk_only_tables::run_suite().expect("pk only tables suite");
println!("Running sync_bit_honored");
t::sync_bit_honored::run_suite().expect("sync bit honored suite");
println!("Running run_suite");
println!("Running tableinfo");
t::tableinfo::run_suite();
println!("Running tear_down");
t::teardown::run_suite().expect("tear down suite");
Expand Down
80 changes: 40 additions & 40 deletions core/rs/integration_check/src/t/automigrate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use sqlite_nostd as sqlite;
fn idempotent() {
let db = crate::opendb().expect("db opened");
let schema = "
CREATE TABLE IF NOT EXISTS item (id integer primary key, data any) strict;
CREATE TABLE IF NOT EXISTS item (id integer primary key not null, data any) strict;
CREATE TABLE IF NOT EXISTS container (id integer primary key, contained integer);
CREATE INDEX IF NOT EXISTS container_contained ON container (contained);
SELECT crsql_as_crr('item');
Expand Down Expand Up @@ -61,7 +61,7 @@ SELECT crsql_automigrate(?, 'SELECT crsql_finalize();')"#,
1,
r#"
CREATE TABLE IF NOT EXISTS "deck" (
"id" INTEGER primary key,
"id" INTEGER primary key not null,
"title",
"created",
"modified",
Expand All @@ -70,7 +70,7 @@ CREATE TABLE IF NOT EXISTS "deck" (
);

CREATE TABLE IF NOT EXISTS "slide" (
"id" INTEGER primary key,
"id" INTEGER primary key not null,
"deck_id",
"order",
"created",
Expand All @@ -83,20 +83,20 @@ CREATE TABLE IF NOT EXISTS "slide" (
CREATE INDEX IF NOT EXISTS "slide_deck_id" ON "slide" ("deck_id", "order");

CREATE TABLE IF NOT EXISTS "text_component" (
"id" INTEGER primary key,
"id" INTEGER primary key not null,
"slide_id",
"text",
"styles",
"x",
"y"
);

CREATE TABLE IF NOT EXISTS "embed_component" ("id" primary key, "slide_id", "src", "x", "y");
CREATE TABLE IF NOT EXISTS "embed_component" ("id" primary key not null, "slide_id", "src", "x", "y");

CREATE INDEX IF NOT EXISTS "embed_component_slide_id" ON "embed_component" ("slide_id");

CREATE TABLE IF NOT EXISTS "shape_component" (
"id" INTEGER primary key,
"id" INTEGER primary key not null,
"slide_id",
"type",
"props",
Expand All @@ -106,18 +106,18 @@ CREATE TABLE IF NOT EXISTS "shape_component" (

CREATE INDEX IF NOT EXISTS "shape_component_slide_id" ON "shape_component" ("slide_id");

CREATE TABLE IF NOT EXISTS "line_component" ("id" primary key, "slide_id", "props");
CREATE TABLE IF NOT EXISTS "line_component" ("id" primary key not null, "slide_id", "props");

CREATE INDEX IF NOT EXISTS "line_component_slide_id" ON "line_component" ("slide_id");

CREATE TABLE IF NOT EXISTS "line_point" ("id" primary key, "line_id", "x", "y");
CREATE TABLE IF NOT EXISTS "line_point" ("id" primary key not null, "line_id", "x", "y");

CREATE INDEX IF NOT EXISTS "line_point_line_id" ON "line_point" ("line_id");

CREATE INDEX IF NOT EXISTS "text_component_slide_id" ON "text_component" ("slide_id");

CREATE TABLE IF NOT EXISTS "theme" (
"id" INTEGER primary key,
"id" INTEGER primary key not null,
"name",
"bg_colorset",
"fg_colorset",
Expand All @@ -127,14 +127,14 @@ CREATE TABLE IF NOT EXISTS "theme" (
);

CREATE TABLE IF NOT EXISTS "recent_color" (
"color" INTEGER primary key,
"color" INTEGER primary key not null,
"last_used",
"first_used",
"theme_id"
);

CREATE TABLE IF NOT EXISTS "presenter" (
"name" primary key,
"name" primary key not null,
"available_transitions",
"picked_transition"
);
Expand Down Expand Up @@ -162,29 +162,29 @@ SELECT crsql_as_crr('recent_color');
SELECT crsql_as_crr('presenter');

CREATE TABLE IF NOT EXISTS "selected_slide" (
"deck_id",
"slide_id",
"deck_id" not null,
"slide_id" not null,
primary key ("deck_id", "slide_id")
);

CREATE TABLE IF NOT EXISTS "selected_component" (
"slide_id",
"component_id",
"slide_id" not null,
"component_id" not null,
"component_type",
primary key ("slide_id", "component_id")
);

CREATE TABLE IF NOT EXISTS "undo_stack" (
"deck_id",
"deck_id" not null,
"operation",
"order",
"order" not null,
primary key ("deck_id", "order")
);

CREATE TABLE IF NOT EXISTS "redo_stack" (
"deck_id",
"deck_id" not null,
"operation",
"order",
"order" not null,
primary key ("deck_id", "order")
);"#,
sqlite::Destructor::STATIC,
Expand Down Expand Up @@ -214,7 +214,7 @@ SELECT crsql_automigrate(?, 'SELECT crsql_finalize();')"#,
1,
r#"
CREATE TABLE IF NOT EXISTS "deck" (
"id" INTEGER primary key,
"id" INTEGER primary key not null,
"title",
"created",
"modified",
Expand All @@ -223,7 +223,7 @@ CREATE TABLE IF NOT EXISTS "deck" (
);

CREATE TABLE IF NOT EXISTS "slide" (
"id" INTEGER primary key,
"id" INTEGER primary key not null,
"deck_id",
"order",
"created",
Expand All @@ -236,7 +236,7 @@ CREATE TABLE IF NOT EXISTS "slide" (
CREATE INDEX IF NOT EXISTS "slide_deck_id" ON "slide" ("deck_id", "order");

CREATE TABLE IF NOT EXISTS "text_component" (
"id" INTEGER primary key,
"id" INTEGER primary key not null,
"slide_id",
"text",
"styles",
Expand All @@ -246,12 +246,12 @@ CREATE TABLE IF NOT EXISTS "text_component" (
"height"
);

CREATE TABLE IF NOT EXISTS "embed_component" ("id" primary key, "slide_id", "src", "x", "y", "width", "height");
CREATE TABLE IF NOT EXISTS "embed_component" ("id" primary key not null, "slide_id", "src", "x", "y", "width", "height");

CREATE INDEX IF NOT EXISTS "embed_component_slide_id" ON "embed_component" ("slide_id");

CREATE TABLE IF NOT EXISTS "shape_component" (
"id" INTEGER primary key,
"id" INTEGER primary key not null,
"slide_id",
"type",
"props",
Expand All @@ -263,18 +263,18 @@ CREATE TABLE IF NOT EXISTS "shape_component" (

CREATE INDEX IF NOT EXISTS "shape_component_slide_id" ON "shape_component" ("slide_id");

CREATE TABLE IF NOT EXISTS "line_component" ("id" primary key, "slide_id", "props");
CREATE TABLE IF NOT EXISTS "line_component" ("id" primary key not null, "slide_id", "props");

CREATE INDEX IF NOT EXISTS "line_component_slide_id" ON "line_component" ("slide_id");

CREATE TABLE IF NOT EXISTS "line_point" ("id" primary key, "line_id", "x", "y");
CREATE TABLE IF NOT EXISTS "line_point" ("id" primary key not null, "line_id", "x", "y");

CREATE INDEX IF NOT EXISTS "line_point_line_id" ON "line_point" ("line_id");

CREATE INDEX IF NOT EXISTS "text_component_slide_id" ON "text_component" ("slide_id");

CREATE TABLE IF NOT EXISTS "theme" (
"id" INTEGER primary key,
"id" INTEGER primary key not null,
"name",
"bg_colorset",
"fg_colorset",
Expand All @@ -284,14 +284,14 @@ CREATE TABLE IF NOT EXISTS "theme" (
);

CREATE TABLE IF NOT EXISTS "recent_color" (
"color" INTEGER primary key,
"color" INTEGER primary key not null,
"last_used",
"first_used",
"theme_id"
);

CREATE TABLE IF NOT EXISTS "presenter" (
"name" primary key,
"name" primary key not null,
"available_transitions",
"picked_transition"
);
Expand Down Expand Up @@ -319,29 +319,29 @@ SELECT crsql_as_crr('recent_color');
SELECT crsql_as_crr('presenter');

CREATE TABLE IF NOT EXISTS "selected_slide" (
"deck_id",
"slide_id",
"deck_id" not null,
"slide_id" not null,
primary key ("deck_id", "slide_id")
);

CREATE TABLE IF NOT EXISTS "selected_component" (
"slide_id",
"component_id",
"slide_id" not null,
"component_id" not null,
"component_type",
primary key ("slide_id", "component_id")
);

CREATE TABLE IF NOT EXISTS "undo_stack" (
"deck_id",
"deck_id" not null,
"operation",
"order",
"order" not null,
primary key ("deck_id", "order")
);

CREATE TABLE IF NOT EXISTS "redo_stack" (
"deck_id",
"deck_id" not null,
"operation",
"order",
"order" not null,
primary key ("deck_id", "order")
);"#,
sqlite::Destructor::STATIC,
Expand All @@ -368,7 +368,7 @@ fn to_empty_from_something() -> Result<(), ResultCode> {
db.db.exec_safe("CREATE TABLE foo (a primary key, b);")?;
db.db.exec_safe("CREATE TABLE bar (a, b, c);")?;
db.db
.exec_safe("CREATE TABLE item (id1, id2, x, primary key (id1, id2));")?;
.exec_safe("CREATE TABLE item (id1 not null, id2 not null, x, primary key (id1, id2));")?;
db.db.exec_safe("SELECT crsql_as_crr('item')")?;
db.db
.exec_safe("SELECT crsql_automigrate('', 'SELECT crsql_finalize();')")?;
Expand All @@ -382,7 +382,7 @@ fn to_something_from_empty() -> Result<(), ResultCode> {
let db = crate::opendb()?;
let schema = "
CREATE TABLE IF NOT EXISTS foo (a primary key, b);
CREATE TABLE IF NOT EXISTS bar (a, b, c, primary key(a, b));
CREATE TABLE IF NOT EXISTS bar (a not null, b not null, c, primary key(a, b));
SELECT crsql_as_crr('bar');
CREATE INDEX IF NOT EXISTS foo_b ON foo (b);
";
Expand Down Expand Up @@ -453,7 +453,7 @@ fn remove_col() -> Result<(), ResultCode> {
fn remove_col_fract_table() {
let db = crate::opendb().expect("db opened");
db.db
.exec_safe("CREATE TABLE todo (id primary key, content text, position, thing)")
.exec_safe("CREATE TABLE todo (id primary key not null, content text, position, thing)")
.expect("table made");
db.db
.exec_safe("SELECT crsql_fract_as_ordered('todo', 'position');")
Expand Down
6 changes: 3 additions & 3 deletions core/rs/integration_check/src/t/backfill.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ fn new_empty_table() -> Result<(), ResultCode> {
let db = crate::opendb()?;
// Just testing that we can execute these statements without error
db.db
.exec_safe("CREATE TABLE foo (id PRIMARY KEY, name);")?;
.exec_safe("CREATE TABLE foo (id PRIMARY KEY NOT NULL, name);")?;
db.db.exec_safe("SELECT crsql_as_crr('foo');")?;
db.db.exec_safe("SELECT * FROM foo__crsql_clock;")?;
Ok(())
Expand All @@ -18,7 +18,7 @@ fn new_empty_table() -> Result<(), ResultCode> {
fn new_nonempty_table(apply_twice: bool) -> Result<(), ResultCode> {
let db = crate::opendb()?;
db.db
.exec_safe("CREATE TABLE foo (id PRIMARY KEY, name);")?;
.exec_safe("CREATE TABLE foo (id PRIMARY KEY NOT NULL, name);")?;
db.db
.exec_safe("INSERT INTO foo VALUES (1, 'one'), (2, 'two');")?;
db.db.exec_safe("SELECT crsql_as_crr('foo');")?;
Expand Down Expand Up @@ -64,7 +64,7 @@ fn reapplied_empty_table() -> Result<(), ResultCode> {
let db = crate::opendb()?;
// Just testing that we can execute these statements without error
db.db
.exec_safe("CREATE TABLE foo (id PRIMARY KEY, name);")?;
.exec_safe("CREATE TABLE foo (id PRIMARY KEY NOT NULL, name);")?;
db.db.exec_safe("SELECT crsql_as_crr('foo');")?;
db.db.exec_safe("SELECT * FROM foo__crsql_clock;")?;
db.db.exec_safe("SELECT crsql_as_crr('foo');")?;
Expand Down
Loading