formular lesen (binär) und openspec für codex

This commit is contained in:
2026-09-05 08:36:40 +02:00
parent 05837cd846
commit 1cb6ae8fd9
34 changed files with 2211 additions and 149 deletions

View File

@@ -38,8 +38,11 @@ fn kodierung_erhaelt_die_ordnung_bei_zufallspaaren() {
let a = r.next() as i16;
let b = r.next() as i16;
assert_eq!(
kodiere(&Value::Int(a), &TypeInit::Int, false)
.cmp(&kodiere(&Value::Int(b), &TypeInit::Int, false)),
kodiere(&Value::Int(a), &TypeInit::Int, false).cmp(&kodiere(
&Value::Int(b),
&TypeInit::Int,
false
)),
a.cmp(&b),
"INTEGER {a} vs {b}"
);
@@ -48,8 +51,11 @@ fn kodierung_erhaelt_die_ordnung_bei_zufallspaaren() {
let a = r.next() as i32;
let b = r.next() as i32;
assert_eq!(
kodiere(&Value::Lng(a), &TypeInit::Lng, false)
.cmp(&kodiere(&Value::Lng(b), &TypeInit::Lng, false)),
kodiere(&Value::Lng(a), &TypeInit::Lng, false).cmp(&kodiere(
&Value::Lng(b),
&TypeInit::Lng,
false
)),
a.cmp(&b),
"LONG {a} vs {b}"
);
@@ -58,8 +64,11 @@ fn kodierung_erhaelt_die_ordnung_bei_zufallspaaren() {
let a = r.next() as i32 as i64;
let b = r.next() as i32 as i64;
assert_eq!(
kodiere(&Value::Cur(a), &TypeInit::Cur, false)
.cmp(&kodiere(&Value::Cur(b), &TypeInit::Cur, false)),
kodiere(&Value::Cur(a), &TypeInit::Cur, false).cmp(&kodiere(
&Value::Cur(b),
&TypeInit::Cur,
false
)),
a.cmp(&b),
"CURRENCY {a} vs {b}"
);
@@ -68,8 +77,11 @@ fn kodierung_erhaelt_die_ordnung_bei_zufallspaaren() {
let a = (r.next() as i32 as f64) / 1024.0;
let b = (r.next() as i32 as f64) / 1024.0;
assert_eq!(
kodiere(&Value::Dbl(a), &TypeInit::Dbl, false)
.cmp(&kodiere(&Value::Dbl(b), &TypeInit::Dbl, false)),
kodiere(&Value::Dbl(a), &TypeInit::Dbl, false).cmp(&kodiere(
&Value::Dbl(b),
&TypeInit::Dbl,
false
)),
a.partial_cmp(&b).unwrap(),
"DOUBLE {a} vs {b}"
);
@@ -78,8 +90,11 @@ fn kodierung_erhaelt_die_ordnung_bei_zufallspaaren() {
let a = (r.next() as i32 as f32) / 64.0;
let b = (r.next() as i32 as f32) / 64.0;
assert_eq!(
kodiere(&Value::Sng(a), &TypeInit::Sng, false)
.cmp(&kodiere(&Value::Sng(b), &TypeInit::Sng, false)),
kodiere(&Value::Sng(a), &TypeInit::Sng, false).cmp(&kodiere(
&Value::Sng(b),
&TypeInit::Sng,
false
)),
a.partial_cmp(&b).unwrap(),
"SINGLE {a} vs {b}"
);
@@ -88,14 +103,19 @@ fn kodierung_erhaelt_die_ordnung_bei_zufallspaaren() {
// Dialekts, der auf UTF-8-Bytes vergleicht (interp::CmpStr).
let mach = |x: u32| -> String {
let zeichen = ['a', 'b', 'z', 'A', 'Z', 'ä', 'ß', '€'];
(0..4).map(|i| zeichen[((x >> (i * 3)) & 7) as usize]).collect()
(0..4)
.map(|i| zeichen[((x >> (i * 3)) & 7) as usize])
.collect()
};
let sa = mach(r.next());
let sb = mach(r.next());
let t = TypeInit::FixedStr(4);
assert_eq!(
kodiere(&Value::Str(Rc::from(sa.as_str())), &t, false)
.cmp(&kodiere(&Value::Str(Rc::from(sb.as_str())), &t, false)),
kodiere(&Value::Str(Rc::from(sa.as_str())), &t, false).cmp(&kodiere(
&Value::Str(Rc::from(sb.as_str())),
&t,
false
)),
sa.as_bytes().cmp(sb.as_bytes()),
"TEXT {sa:?} vs {sb:?}"
);
@@ -181,7 +201,10 @@ impl Drop for TempDb {
}
fn isam_mit_tabelle(db: &str) -> Isam {
let mut i = Isam { udts: test_udts(), ..Default::default() };
let mut i = Isam {
udts: test_udts(),
..Default::default()
};
i.oeffnen(1, db, "Kunden", "Nummer,Name", 0).unwrap();
i
}
@@ -203,9 +226,15 @@ fn hoehere_formatversion_wird_abgewiesen() {
meta_schreiben(&txn, "version", &(FORMATVERSION + 1).to_le_bytes()).unwrap();
txn.commit().unwrap();
}
let mut i = Isam { udts: test_udts(), ..Default::default() };
let mut i = Isam {
udts: test_udts(),
..Default::default()
};
let e = i.oeffnen(1, &pfad, "Kunden", "Nummer,Name", 0).unwrap_err();
assert_eq!(e, INKONSISTENT, "höhere Formatversion muss benannt scheitern");
assert_eq!(
e, INKONSISTENT,
"höhere Formatversion muss benannt scheitern"
);
}
#[test]
@@ -213,7 +242,10 @@ fn beschaedigte_datei_meldet_fehler_88() {
let t = TempDb::neu("kaputt");
let pfad = t.pfad("kaputt.isam");
std::fs::write(&pfad, b"das ist keine Datenbankdatei").unwrap();
let mut i = Isam { udts: test_udts(), ..Default::default() };
let mut i = Isam {
udts: test_udts(),
..Default::default()
};
let e = i.oeffnen(1, &pfad, "Kunden", "Nummer,Name", 0).unwrap_err();
assert_eq!(e, RuntimeError(88));
}
@@ -229,7 +261,10 @@ fn geloeschte_satz_id_wird_nicht_neu_vergeben() {
i.satz_loeschen(1).unwrap();
i.einfuegen(1, &satz(2, "zwei")).unwrap();
let zweite = i.satznummer(1).unwrap();
assert_ne!(erste, zweite, "eine gelöschte ID darf nicht neu vergeben werden");
assert_ne!(
erste, zweite,
"eine gelöschte ID darf nicht neu vergeben werden"
);
assert!(zweite > erste);
}
@@ -249,8 +284,12 @@ fn satz_roundtrip_ueber_alle_feldtypen() {
TypeInit::FixedStr(5),
],
}];
let mut i = Isam { udts, ..Default::default() };
i.oeffnen(1, &t.pfad("db.isam"), "Alles", "A,B,C,D,E,F", 0).unwrap();
let mut i = Isam {
udts,
..Default::default()
};
i.oeffnen(1, &t.pfad("db.isam"), "Alles", "A,B,C,D,E,F", 0)
.unwrap();
let original = Value::Rec(Rc::new(RefCell::new(RecordObj {
fields: vec![
Value::Int(-7),
@@ -262,7 +301,9 @@ fn satz_roundtrip_ueber_alle_feldtypen() {
],
})));
i.einfuegen(1, &original).unwrap();
let Value::Rec(gelesen) = i.satz_lesen(1).unwrap() else { panic!("kein Record") };
let Value::Rec(gelesen) = i.satz_lesen(1).unwrap() else {
panic!("kein Record")
};
let f = &gelesen.borrow().fields;
assert!(matches!(f[0], Value::Int(-7)));
assert!(matches!(f[1], Value::Lng(123456)));
@@ -279,7 +320,8 @@ fn satz_roundtrip_ueber_alle_feldtypen() {
fn indexeintraege_stehen_in_schluesselreihenfolge() {
let t = TempDb::neu("indexordnung");
let mut i = isam_mit_tabelle(&t.pfad("db.isam"));
i.index_anlegen(1, "NachName", false, &["Name".into()]).unwrap();
i.index_anlegen(1, "NachName", false, &["Name".into()])
.unwrap();
for (n, name) in [(3, "Cäsar"), (1, "Anton"), (2, "Berta")] {
i.einfuegen(1, &satz(n, name)).unwrap();
}
@@ -287,8 +329,12 @@ fn indexeintraege_stehen_in_schluesselreihenfolge() {
let mut namen = Vec::new();
i.bewegen(1, Richtung::Erster).unwrap();
while !i.eof(1) {
let Value::Rec(r) = i.satz_lesen(1).unwrap() else { panic!() };
let Value::Str(s) = r.borrow().fields[1].clone() else { panic!() };
let Value::Rec(r) = i.satz_lesen(1).unwrap() else {
panic!()
};
let Value::Str(s) = r.borrow().fields[1].clone() else {
panic!()
};
namen.push(s.trim_end().to_string());
i.bewegen(1, Richtung::Naechster).unwrap();
}
@@ -303,7 +349,8 @@ fn indexeintraege_stehen_in_schluesselreihenfolge() {
fn cursor_ueberlebt_eine_satzaenderung() {
let t = TempDb::neu("cursor");
let mut i = isam_mit_tabelle(&t.pfad("db.isam"));
i.index_anlegen(1, "NachName", false, &["Name".into()]).unwrap();
i.index_anlegen(1, "NachName", false, &["Name".into()])
.unwrap();
for (n, name) in [(1, "Anton"), (2, "Berta"), (3, "Cäsar")] {
i.einfuegen(1, &satz(n, name)).unwrap();
}
@@ -313,18 +360,31 @@ fn cursor_ueberlebt_eine_satzaenderung() {
let vorher = i.satznummer(1).unwrap();
// Über eine zweite Bindung einen anderen Satz einfügen.
i.oeffnen(2, &t.pfad("db.isam"), "Kunden", "Nummer,Name", 0).unwrap();
i.oeffnen(2, &t.pfad("db.isam"), "Kunden", "Nummer,Name", 0)
.unwrap();
i.einfuegen(2, &satz(4, "Anna")).unwrap();
assert_eq!(i.satznummer(1).unwrap(), vorher, "Cursor darf nicht wandern");
let Value::Rec(r) = i.satz_lesen(1).unwrap() else { panic!() };
let Value::Str(s) = r.borrow().fields[1].clone() else { panic!() };
assert_eq!(
i.satznummer(1).unwrap(),
vorher,
"Cursor darf nicht wandern"
);
let Value::Rec(r) = i.satz_lesen(1).unwrap() else {
panic!()
};
let Value::Str(s) = r.borrow().fields[1].clone() else {
panic!()
};
assert_eq!(s.trim_end(), "Berta");
// Und er bewegt sich weiter in der neuen Ordnung.
i.bewegen(1, Richtung::Naechster).unwrap();
let Value::Rec(r) = i.satz_lesen(1).unwrap() else { panic!() };
let Value::Str(s) = r.borrow().fields[1].clone() else { panic!() };
let Value::Rec(r) = i.satz_lesen(1).unwrap() else {
panic!()
};
let Value::Str(s) = r.borrow().fields[1].clone() else {
panic!()
};
assert_eq!(s.trim_end(), "Cäsar");
}
@@ -383,12 +443,16 @@ fn ungueltige_namen_werden_abgewiesen() {
fn close_beendet_keine_transaktion() {
let t = TempDb::neu("close_trans");
let mut i = isam_mit_tabelle(&t.pfad("db.isam"));
i.oeffnen(2, &t.pfad("db.isam"), "Kunden", "Nummer,Name", 0).unwrap();
i.oeffnen(2, &t.pfad("db.isam"), "Kunden", "Nummer,Name", 0)
.unwrap();
i.trans_beginn().unwrap();
i.einfuegen(1, &satz(1, "eins")).unwrap();
i.schliessen(2).unwrap();
assert!(i.trans.is_some(), "CLOSE darf die Transaktion nicht beenden");
assert!(
i.trans.is_some(),
"CLOSE darf die Transaktion nicht beenden"
);
// Die Rücknahme wirkt danach noch auf die Änderung an #1.
i.ruecknahme(ROLLBACK_ALL).unwrap();
@@ -407,7 +471,10 @@ fn nicht_festgeschriebene_transaktion_verfaellt() {
i.einfuegen(1, &satz(1, "eins")).unwrap();
i.alles_schliessen();
}
let mut i = Isam { udts: test_udts(), ..Default::default() };
let mut i = Isam {
udts: test_udts(),
..Default::default()
};
i.oeffnen(1, &pfad, "Kunden", "Nummer,Name", 0).unwrap();
assert_eq!(
i.satzzahl(1).unwrap(),
@@ -427,7 +494,9 @@ fn gescheitertes_createindex_hinterlaesst_keinen_index() {
i.einfuegen(1, &satz(2, "gleich")).unwrap();
i.trans_beginn().unwrap();
let e = i.index_anlegen(1, "Eindeutig", true, &["Name".into()]).unwrap_err();
let e = i
.index_anlegen(1, "Eindeutig", true, &["Name".into()])
.unwrap_err();
assert_eq!(e, DOPPELTER_SCHLUESSEL);
i.trans_festschreiben().unwrap();