github.com/ncruces/go-sqlite3@v0.15.1-0.20240520133447-53eef1510ff0/ext/pivot/pivot_test.go (about) 1 package pivot_test 2 3 import ( 4 "fmt" 5 "log" 6 "strings" 7 "testing" 8 9 "github.com/ncruces/go-sqlite3" 10 _ "github.com/ncruces/go-sqlite3/embed" 11 "github.com/ncruces/go-sqlite3/ext/pivot" 12 _ "github.com/ncruces/go-sqlite3/tests/testcfg" 13 ) 14 15 // https://antonz.org/sqlite-pivot-table/ 16 func Example() { 17 db, err := sqlite3.Open(":memory:") 18 if err != nil { 19 log.Fatal(err) 20 } 21 defer db.Close() 22 23 pivot.Register(db) 24 25 err = db.Exec(` 26 CREATE TABLE sales(product TEXT, year INT, income DECIMAL); 27 INSERT INTO sales(product, year, income) VALUES 28 ('alpha', 2020, 100), 29 ('alpha', 2021, 120), 30 ('alpha', 2022, 130), 31 ('alpha', 2023, 140), 32 ('beta', 2020, 10), 33 ('beta', 2021, 20), 34 ('beta', 2022, 40), 35 ('beta', 2023, 80), 36 ('gamma', 2020, 80), 37 ('gamma', 2021, 75), 38 ('gamma', 2022, 78), 39 ('gamma', 2023, 80); 40 `) 41 if err != nil { 42 log.Fatal(err) 43 } 44 45 err = db.Exec(` 46 CREATE VIRTUAL TABLE v_sales USING pivot( 47 -- rows 48 (SELECT DISTINCT product FROM sales), 49 -- columns 50 (SELECT DISTINCT year, year FROM sales), 51 -- cells 52 (SELECT sum(income) FROM sales WHERE product = ? AND year = ?) 53 )`) 54 if err != nil { 55 log.Fatal(err) 56 } 57 58 stmt, _, err := db.Prepare(`SELECT * FROM v_sales`) 59 if err != nil { 60 log.Fatal(err) 61 } 62 defer stmt.Close() 63 64 cols := make([]string, stmt.ColumnCount()) 65 for i := range cols { 66 cols[i] = stmt.ColumnName(i) 67 } 68 fmt.Println(pretty(cols)) 69 for stmt.Step() { 70 for i := range cols { 71 cols[i] = stmt.ColumnText(i) 72 } 73 fmt.Println(pretty(cols)) 74 } 75 if err := stmt.Reset(); err != nil { 76 log.Fatal(err) 77 } 78 79 // Output: 80 // product 2020 2021 2022 2023 81 // alpha 100 120 130 140 82 // beta 10 20 40 80 83 // gamma 80 75 78 80 84 } 85 86 func TestRegister(t *testing.T) { 87 t.Parallel() 88 89 db, err := sqlite3.Open(":memory:") 90 if err != nil { 91 t.Fatal(err) 92 } 93 defer db.Close() 94 95 pivot.Register(db) 96 97 err = db.Exec(` 98 CREATE TABLE r AS 99 SELECT 1 id UNION SELECT 2 UNION SELECT 3; 100 101 CREATE TABLE c( 102 id INTEGER PRIMARY KEY, 103 name TEXT 104 ); 105 INSERT INTO c (name) VALUES 106 ('a'),('b'),('c'),('d'); 107 108 CREATE TABLE x( 109 r_id INT, 110 c_id INT, 111 val TEXT 112 ); 113 INSERT INTO x (r_id, c_id, val) 114 SELECT r.id, c.id, c.name || r.id 115 FROM c, r; 116 `) 117 if err != nil { 118 t.Fatal(err) 119 } 120 121 err = db.Exec(` 122 CREATE VIRTUAL TABLE v_x USING pivot( 123 -- rows 124 (SELECT id r_id FROM r), 125 -- columns 126 (SELECT id c_id, name FROM c), 127 -- cells 128 (SELECT val FROM x WHERE r_id = ?1 AND c_id = ?2) 129 )`) 130 if err != nil { 131 t.Fatal(err) 132 } 133 134 stmt, _, err := db.Prepare(`SELECT * FROM v_x WHERE rowid <> 0 AND r_id <> 1 ORDER BY rowid, r_id DESC LIMIT 1`) 135 if err != nil { 136 t.Fatal(err) 137 } 138 defer stmt.Close() 139 140 if stmt.Step() { 141 if got := stmt.ColumnInt(0); got != 3 { 142 t.Errorf("got %d, want 3", got) 143 } 144 } 145 } 146 147 func TestRegister_errors(t *testing.T) { 148 t.Parallel() 149 150 db, err := sqlite3.Open(":memory:") 151 if err != nil { 152 t.Fatal(err) 153 } 154 defer db.Close() 155 156 pivot.Register(db) 157 158 err = db.Exec(`CREATE VIRTUAL TABLE pivot USING pivot()`) 159 if err == nil { 160 t.Fatal("want error") 161 } else { 162 t.Log(err) 163 } 164 165 err = db.Exec(`CREATE VIRTUAL TABLE split_date USING pivot(SELECT 1, SELECT 2, SELECT 3)`) 166 if err == nil { 167 t.Fatal("want error") 168 } else { 169 t.Log(err) 170 } 171 172 err = db.Exec(`CREATE VIRTUAL TABLE split_date USING pivot((SELECT 1), SELECT 2, SELECT 3)`) 173 if err == nil { 174 t.Fatal("want error") 175 } else { 176 t.Log(err) 177 } 178 179 err = db.Exec(`CREATE VIRTUAL TABLE split_date USING pivot((SELECT 1), (SELECT 2), SELECT 3)`) 180 if err == nil { 181 t.Fatal("want error") 182 } else { 183 t.Log(err) 184 } 185 186 err = db.Exec(`CREATE VIRTUAL TABLE split_date USING pivot((SELECT 1), (SELECT 1, 2), SELECT 3)`) 187 if err == nil { 188 t.Fatal("want error") 189 } else { 190 t.Log(err) 191 } 192 193 err = db.Exec(`CREATE VIRTUAL TABLE split_date USING pivot((SELECT 1), (SELECT 1, 2), (SELECT 3, 4))`) 194 if err == nil { 195 t.Fatal("want error") 196 } else { 197 t.Log(err) 198 } 199 200 err = db.Exec(`CREATE VIRTUAL TABLE split_date USING pivot((SELECT 1), (SELECT 1, 2), (SELECT 3))`) 201 if err == nil { 202 t.Fatal("want error") 203 } else { 204 t.Log(err) 205 } 206 } 207 208 func pretty(cols []string) string { 209 var buf strings.Builder 210 for i, s := range cols { 211 if i != 0 { 212 buf.WriteByte(' ') 213 } 214 for buf.Len()%8 != 0 { 215 buf.WriteByte(' ') 216 } 217 buf.WriteString(s) 218 } 219 return buf.String() 220 }