Skip to main content

rustdb/
test.rs

1#[cfg(test)]
2/// Get amount of testing from environment variable TA.
3pub fn test_amount() -> usize {
4    str::parse(&std::env::var("TA").unwrap_or("1".to_string())).unwrap()
5}
6
7#[test]
8/// Idea of this test is to check database saves and loads ok.
9fn save_test() {
10    use crate::*;
11
12    let mf = MemFile::new();
13
14    for i in 0..2 {
15        let mut bmap = BuiltinMap::default();
16        standard_builtins(&mut bmap);
17        let bmap = Arc::new(bmap);
18
19        let af = AtomicFile::new(mf.clone(), MemFile::new());
20        let spd = SharedPagedData::new(af);
21        let wapd = spd.new_writer();
22
23        let db = Database::new(wapd, "CREATE SCHEMA test", bmap.clone());
24
25        let mut tr = GenTransaction::default();
26
27        if i == 0 {
28            let sql = "
29CREATE TABLE test.Cust(Name string) GO
30INSERT INTO test.Cust(Name) VALUES ('freddy')
31";
32            db.run(&sql, &mut tr);
33            assert!(db.changed());
34            assert!(db.save() > 0);
35            spd.wait_complete();
36        } else {
37            let sql = "SELECT Name FROM test.Cust";
38            db.run(&sql, &mut tr);
39            assert_eq!(tr.rp.output, b"freddy");
40        }
41    }
42}
43
44#[test]
45fn concurrency() {
46    use crate::*;
47
48    let stg = AtomicFile::new(MemFile::new(), MemFile::new());
49
50    let mut bmap = BuiltinMap::default();
51    standard_builtins(&mut bmap);
52    let bmap = Arc::new(bmap);
53
54    let spd = SharedPagedData::new(stg);
55    let wapd = spd.new_writer();
56    let db = Database::new(wapd, "CREATE SCHEMA test", bmap.clone());
57
58    let nt = 100;
59
60    // Create nt tables.
61    for i in 0..nt {
62        let mut tr = GenTransaction::default();
63        let sql = format!(
64            "CREATE TABLE test.[T{}](N int) GO INSERT INTO test.[T{}](N) VALUES (0)",
65            i, i
66        );
67        db.run(&sql, &mut tr);
68        assert!(db.save() > 0);
69    }
70
71    // Create readers at different update times.
72    let mut rapd = Vec::new();
73    for i in 0..1000 * test_amount() {
74        rapd.push((i, spd.new_reader()));
75        let mut tr = GenTransaction::default();
76        let table = i % nt;
77        let sql = format!("UPDATE test.[T{}] SET N = N + 1 WHERE 1=1", table);
78        db.run(&sql, &mut tr);
79        assert!(db.save() > 0);
80    }
81
82    // Run the readers in random order, checking content of random table.
83    use rand::Rng;
84    let mut rng = rand::thread_rng();
85    while !rapd.is_empty() {
86        let r = rng.r#gen::<usize>() % rapd.len();
87        let (i, rapd) = rapd.remove(r);
88        let db = Database::new(rapd, "", bmap.clone());
89        let mut tr = GenTransaction::default();
90        let table = rng.r#gen::<usize>() % nt;
91        let sql = format!("SELECT N FROM test.[T{}]", table);
92        db.run(&sql, &mut tr);
93        let expect = i / nt + if i % nt > table { 1 } else { 0 };
94        assert!(tr.rp.output == format!("{}", expect).as_bytes());
95    }
96}
97
98#[test]
99fn rtest() {
100    use crate::*;
101
102    const INITSQL : &str = "
103
104CREATE FN sys.QuoteName( s string ) RETURNS string AS
105BEGIN
106  RETURN '[' | REPLACE( s, ']', ']]' ) | ']'
107END
108
109CREATE FN sys.Dot( schema string, name string ) RETURNS string AS
110BEGIN
111  RETURN sys.QuoteName( schema ) | '.' | sys.QuoteName( name )
112END
113
114CREATE FN sys.TableName( table int ) RETURNS string AS
115BEGIN
116  DECLARE schema int, name string
117  SET schema = Schema, name = Name FROM sys.Table WHERE Id = table
118  IF name = '' RETURN ''
119  SET result = sys.Dot( Name, name ) FROM sys.Schema WHERE Id = schema
120END
121
122CREATE FN sys.DropTable
123( t int ) AS 
124/* Note: this should not be called directly, instead use DROP TABLE statement */
125BEGIN
126  /* Delete the rows */
127  EXECUTE( 'DELETE FROM ' | sys.TableName(t) | ' WHERE true' )
128
129  DECLARE id int
130  /* Delete the Index data */
131  FOR id = Id FROM sys.Index WHERE Table = t
132  BEGIN
133    DELETE FROM sys.IndexColumn WHERE Index = id
134  END
135  DELETE FROM sys.Index WHERE Table = t
136   /* Delete the column data */
137  FOR id = Id FROM sys.Column WHERE Table = t
138  BEGIN
139    -- DELETE FROM browse.Column WHERE Id = id
140  END
141  /* Delete other data */
142  -- DELETE FROM browse.Table WHERE Id = t
143  DELETE FROM sys.Column WHERE Table = t
144  DELETE FROM sys.Table WHERE Id = t
145END
146
147CREATE FN sys.ClearTable
148(t int) AS 
149BEGIN 
150  EXECUTE( 'DELETE FROM ' | sys.TableName(t) | ' WHERE true' )
151END
152
153CREATE SCHEMA rtest
154GO
155CREATE TABLE rtest.Gen(x int)
156GO
157INSERT INTO rtest.Gen(x) VALUES(1)
158GO
159CREATE SCHEMA rtestdata
160GO
161
162CREATE FN rtest.repeat( s string, n int ) RETURNS string AS
163BEGIN
164  WHILE n > 0
165  BEGIN
166    SET result |= s
167    SET n -= 1
168  END
169END
170
171CREATE FN rtest.OneTest() AS
172BEGIN 
173  DECLARE rtestdata int
174  SET rtestdata = Id FROM sys.Schema WHERE Name = 'rtestdata'
175
176  DECLARE r int
177  SET r = x FROM rtest.Gen
178  SET r = r * 48271 % 2147483647
179
180  DECLARE sql string, a int
181  SET a = r % 2
182
183  DECLARE tname string
184  SET tname = 't' | ( r / 100 ) % 7
185
186  DECLARE exists string
187  SET exists = ''
188  SET exists = Name FROM sys.Table WHERE Schema = rtestdata AND Name = tname
189
190  SET sql = CASE 
191    WHEN r % 20 = 0 THEN 'SELECT VERIFYDB()'
192    WHEN r % 20 = 19 THEN 'SELECT REPACKFILE(-4,'''','''')'
193    WHEN r % 20 = 18 THEN 'SELECT REPACKFILE(-3,'''','''')'
194    WHEN r % 20 = 17 THEN 'SELECT RENUMBER()'
195    WHEN exists = '' THEN 
196      CASE WHEN r % 2 =1 THEN 'CREATE TABLE rtestdata.[' | tname | '](x string, y int(5))'
197      ELSE 'CREATE TABLE rtestdata.[' | tname | '](x string, y int(3), z string )'
198      END
199    WHEN r % 5 = 0 THEN 'ALTER TABLE rtestdata.[' | tname | '] ADD [z' | r | '] binary' 
200    WHEN r % 21 = 1 THEN 'DROP TABLE rtestdata.[' | tname | ']'
201    WHEN r % 2 = 1 THEN 'INSERT INTO rtestdata.[' | tname | '](x,y) VALUES ( rtest.repeat(''George Gordon Fairbrother Barwood'','|(r % 1000)|'),' | (r % 10) | ')'
202    ELSE 'DELETE FROM rtestdata.[' | tname | '] WHERE y = ' | ( r%15)
203  END 
204  SELECT ' sql=' | sql
205  EXECUTE( sql )
206  UPDATE rtest.Gen SET x = r WHERE true 
207END
208GO
209";
210
211    let stg = AtomicFile::new(MemFile::new(), MemFile::new());
212
213    let mut bmap = BuiltinMap::default();
214    standard_builtins(&mut bmap);
215    let bmap = Arc::new(bmap);
216
217    let spd = SharedPagedData::new(stg);
218    let wapd = spd.new_writer();
219
220    let db = Database::new(wapd, INITSQL, bmap.clone());
221
222    // To check things work with low mem_limit.
223    {
224        // let mut s = spd.stash.lock().unwrap();
225        // s.mem_limit = 1;
226    }
227
228    for _i in 0..1000 * test_amount() {
229        let mut tr = GenTransaction::default();
230        let sql = "EXEC rtest.OneTest()";
231        db.run(&sql, &mut tr);
232        db.save();
233        let s = std::str::from_utf8(&tr.rp.output).unwrap();
234        if s.len() > 0 {
235            // println!("output={}", s);
236        }
237        assert_eq!(tr.get_error(), "");
238    }
239}
240
241#[test]
242fn rollback() {
243    use crate::*;
244
245    let stg = AtomicFile::new(MemFile::new(), MemFile::new());
246
247    let mut bmap = BuiltinMap::default();
248    standard_builtins(&mut bmap);
249    let bmap = Arc::new(bmap);
250
251    let spd = SharedPagedData::new(stg);
252
253    let wapd = spd.new_writer();
254    let db = Database::new(wapd, "", bmap.clone());
255
256    let mut tr = GenTransaction::default();
257    let sql = "
258      CREATE TABLE sys.test(x int) 
259      DECLARE sql string SET sql = 'SELECT PARSEINT(''x'')'
260      EXECUTE(sql)
261    ";
262    db.run(&sql, &mut tr);
263}
264
265#[test]
266fn insert_delete() {
267    use crate::*;
268
269    let stg = AtomicFile::new(MemFile::new(), MemFile::new());
270
271    let mut bmap = BuiltinMap::default();
272    standard_builtins(&mut bmap);
273    let bmap = Arc::new(bmap);
274
275    let spd = SharedPagedData::new(stg);
276    let wapd = spd.new_writer();
277    let db = Database::new(wapd, "", bmap.clone());
278
279    let mut tr = GenTransaction::default();
280
281    let sql = format!(
282        "
283      CREATE TABLE sys.test(x int,name string) 
284      GO
285      DECLARE @i int
286      WHILE @i < {}
287      BEGIN
288        INSERT INTO sys.test(x,name) VALUES(@i,'Hello World')    
289        SET @i += 1
290      END      
291      DELETE FROM sys.test WHERE Id % 3 = 1
292      DELETE FROM sys.test WHERE Id % 3 = 2
293      DELETE FROM sys.test WHERE true
294    ",
295        test_amount() * 100000
296    );
297    db.run(&sql, &mut tr);
298    db.save();
299    assert_eq!(tr.get_error(), "");
300}
301
302//$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
303
304#[test]
305fn test_date_calc() {
306    use crate::*;
307    const INITSQL: &str = "
308CREATE SCHEMA [date]
309CREATE FN [date].[YearDayToYearMonthDay]( yd int ) RETURNS int AS
310BEGIN
311  DECLARE y int, d int, leap bool, fdm int, m int, dim int
312  SET y = yd / 512
313  SET d = yd % 512 - 1
314  SET leap = date.IsLeapYear( y )
315  -- Jan = 0..30, Feb = 0..27 or 0..28  
316  IF NOT leap AND d >= 59 SET d = d + 1
317  SET fdm = CASE 
318    WHEN d < 31 THEN 0 -- Jan
319    WHEN d < 60 THEN 31 -- Feb
320    WHEN d < 91 THEN 60 -- Mar
321    WHEN d < 121 THEN 91 -- Apr
322    WHEN d < 152 THEN 121 -- May
323    WHEN d < 182 THEN 152 -- Jun
324    WHEN d < 213 THEN 182 -- Jul
325    WHEN d < 244 THEN 213 -- Aug
326    WHEN d < 274 THEN 244 -- Sep
327    WHEN d < 305 THEN 274 -- Oct
328    WHEN d < 335 THEN 305 -- Nov
329    ELSE 335 -- Dec
330    END
331  SET dim = d - fdm
332  SET m = ( d - dim + 28 ) / 31
333  RETURN date.YearMonthDay( y, m+1, dim+1 )
334END
335
336CREATE FN [date].[DaysToYearDay]( days int ) RETURNS int AS
337BEGIN
338  -- Given a date represented by the number of days since 1 Jan 0000
339  -- calculate a date in Year/Day representation stored as
340  -- year * 512 + day where day is 1..366, the day in the year.
341  
342  DECLARE year int, day int, cycle int
343  -- 146097 is the number of the days in a 400 year cycle ( 400 * 365 + 97 leap years )
344  SET cycle = days / 146097
345  SET days = days - 146097 * cycle -- Same as days % 146097
346  SET year = days / 365
347  SET day = days - year * 365 -- Same as days % 365
348  -- Need to adjust day to allow for leap years.
349  -- Leap years are 0, 4, 8, 12 ... 96, not 100, 104 ... not 200... not 300, 400, 404 ... not 500.
350  -- Adjustment as function of y is 0 => 0, 1 => 1, 2 =>1, 3 => 1, 4 => 1, 5 => 2 ..
351  SET day = day - ( year + 3 ) / 4 + ( year + 99 ) / 100 - ( year + 399 ) / 400
352  
353  IF day < 0
354  BEGIN
355    SET year -= 1
356    SET day += CASE WHEN date.IsLeapYear( year ) THEN 366 ELSE 365 END
357  END
358  RETURN 512 * ( cycle * 400 + year ) + day + 1
359END
360
361CREATE FN [date].[YearMonthDay]( year int, month int, day int ) RETURNS int AS
362BEGIN
363  RETURN year * 512 + month * 32 + day
364END
365
366CREATE FN [date].[IsLeapYear]( y int ) RETURNS bool AS
367BEGIN
368  RETURN y % 4 = 0 AND ( y % 100 != 0 OR y % 400 = 0 )
369END
370
371CREATE FN [date].[DaysToYearMonthDay]( days int ) RETURNS int AS
372BEGIN
373  RETURN date.YearDayToYearMonthDay( date.DaysToYearDay( days ) )
374END
375
376CREATE FN [date].[test]() AS
377BEGIN
378   DECLARE days int, ymd int
379   WHILE days < 1000
380   BEGIN
381      SET days += 1
382      SET ymd = date.DaysToYearMonthDay(days)
383   END
384END
385";
386
387    let stg = AtomicFile::new(MemFile::new(), MemFile::new());
388
389    let mut bmap = BuiltinMap::default();
390    standard_builtins(&mut bmap);
391    let bmap = Arc::new(bmap);
392
393    let spd = SharedPagedData::new(stg);
394    let wapd = spd.new_writer();
395    let db = Database::new(wapd, INITSQL, bmap.clone());
396
397    // To check things work with low mem_limit.
398    {
399        // let mut s = spd.stash.lock().unwrap();
400        // s.mem_limit = 1;
401    }
402
403    let mut results = Vec::new();
404    for _i in 0..100 {
405        let start = std::time::Instant::now();
406        let mut tr = GenTransaction::default();
407        let sql = "EXEC date.test()";
408        db.run(&sql, &mut tr);
409        results.push(start.elapsed().as_micros() as u64);
410        assert_eq!(tr.get_error(), "");
411    }
412    crate::bench::print_results("date calc test", results);
413}