OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [gnu-dev/] [or1k-gcc/] [libgo/] [go/] [database/] [sql/] [fakedb_test.go] - Blame information for rev 814

Go to most recent revision | Details | Compare with Previous | View Log

=,=,...
Line No. Rev Author Line
1 747 jeremybenn
// Copyright 2011 The Go Authors. All rights reserved.
2
// Use of this source code is governed by a BSD-style
3
// license that can be found in the LICENSE file.
4
 
5
package sql
6
 
7
import (
8
        "database/sql/driver"
9
        "errors"
10
        "fmt"
11
        "io"
12
        "log"
13
        "strconv"
14
        "strings"
15
        "sync"
16
        "time"
17
)
18
 
19
var _ = log.Printf
20
 
21
// fakeDriver is a fake database that implements Go's driver.Driver
22
// interface, just for testing.
23
//
24
// It speaks a query language that's semantically similar to but
25
// syntantically different and simpler than SQL.  The syntax is as
26
// follows:
27
//
28
//   WIPE
29
//   CREATE||
30
//     where types are: "string", [u]int{8,16,32,64}, "bool"
31
//   INSERT||col=val,col2=val2,col3=?
32
//   SELECT||projectcol1,projectcol2|filtercol=?,filtercol2=?
33
//
34
// When opening a a fakeDriver's database, it starts empty with no
35
// tables.  All tables and data are stored in memory only.
36
type fakeDriver struct {
37
        mu        sync.Mutex
38
        openCount int
39
        dbs       map[string]*fakeDB
40
}
41
 
42
type fakeDB struct {
43
        name string
44
 
45
        mu     sync.Mutex
46
        free   []*fakeConn
47
        tables map[string]*table
48
}
49
 
50
type table struct {
51
        mu      sync.Mutex
52
        colname []string
53
        coltype []string
54
        rows    []*row
55
}
56
 
57
func (t *table) columnIndex(name string) int {
58
        for n, nname := range t.colname {
59
                if name == nname {
60
                        return n
61
                }
62
        }
63
        return -1
64
}
65
 
66
type row struct {
67
        cols []interface{} // must be same size as its table colname + coltype
68
}
69
 
70
func (r *row) clone() *row {
71
        nrow := &row{cols: make([]interface{}, len(r.cols))}
72
        copy(nrow.cols, r.cols)
73
        return nrow
74
}
75
 
76
type fakeConn struct {
77
        db *fakeDB // where to return ourselves to
78
 
79
        currTx *fakeTx
80
 
81
        // Stats for tests:
82
        mu          sync.Mutex
83
        stmtsMade   int
84
        stmtsClosed int
85
}
86
 
87
func (c *fakeConn) incrStat(v *int) {
88
        c.mu.Lock()
89
        *v++
90
        c.mu.Unlock()
91
}
92
 
93
type fakeTx struct {
94
        c *fakeConn
95
}
96
 
97
type fakeStmt struct {
98
        c *fakeConn
99
        q string // just for debugging
100
 
101
        cmd   string
102
        table string
103
 
104
        closed bool
105
 
106
        colName      []string      // used by CREATE, INSERT, SELECT (selected columns)
107
        colType      []string      // used by CREATE
108
        colValue     []interface{} // used by INSERT (mix of strings and "?" for bound params)
109
        placeholders int           // used by INSERT/SELECT: number of ? params
110
 
111
        whereCol []string // used by SELECT (all placeholders)
112
 
113
        placeholderConverter []driver.ValueConverter // used by INSERT
114
}
115
 
116
var fdriver driver.Driver = &fakeDriver{}
117
 
118
func init() {
119
        Register("test", fdriver)
120
}
121
 
122
// Supports dsn forms:
123
//    
124
//    ;  (no currently supported options)
125
func (d *fakeDriver) Open(dsn string) (driver.Conn, error) {
126
        parts := strings.Split(dsn, ";")
127
        if len(parts) < 1 {
128
                return nil, errors.New("fakedb: no database name")
129
        }
130
        name := parts[0]
131
 
132
        db := d.getDB(name)
133
 
134
        d.mu.Lock()
135
        d.openCount++
136
        d.mu.Unlock()
137
        return &fakeConn{db: db}, nil
138
}
139
 
140
func (d *fakeDriver) getDB(name string) *fakeDB {
141
        d.mu.Lock()
142
        defer d.mu.Unlock()
143
        if d.dbs == nil {
144
                d.dbs = make(map[string]*fakeDB)
145
        }
146
        db, ok := d.dbs[name]
147
        if !ok {
148
                db = &fakeDB{name: name}
149
                d.dbs[name] = db
150
        }
151
        return db
152
}
153
 
154
func (db *fakeDB) wipe() {
155
        db.mu.Lock()
156
        defer db.mu.Unlock()
157
        db.tables = nil
158
}
159
 
160
func (db *fakeDB) createTable(name string, columnNames, columnTypes []string) error {
161
        db.mu.Lock()
162
        defer db.mu.Unlock()
163
        if db.tables == nil {
164
                db.tables = make(map[string]*table)
165
        }
166
        if _, exist := db.tables[name]; exist {
167
                return fmt.Errorf("table %q already exists", name)
168
        }
169
        if len(columnNames) != len(columnTypes) {
170
                return fmt.Errorf("create table of %q len(names) != len(types): %d vs %d",
171
                        name, len(columnNames), len(columnTypes))
172
        }
173
        db.tables[name] = &table{colname: columnNames, coltype: columnTypes}
174
        return nil
175
}
176
 
177
// must be called with db.mu lock held
178
func (db *fakeDB) table(table string) (*table, bool) {
179
        if db.tables == nil {
180
                return nil, false
181
        }
182
        t, ok := db.tables[table]
183
        return t, ok
184
}
185
 
186
func (db *fakeDB) columnType(table, column string) (typ string, ok bool) {
187
        db.mu.Lock()
188
        defer db.mu.Unlock()
189
        t, ok := db.table(table)
190
        if !ok {
191
                return
192
        }
193
        for n, cname := range t.colname {
194
                if cname == column {
195
                        return t.coltype[n], true
196
                }
197
        }
198
        return "", false
199
}
200
 
201
func (c *fakeConn) Begin() (driver.Tx, error) {
202
        if c.currTx != nil {
203
                return nil, errors.New("already in a transaction")
204
        }
205
        c.currTx = &fakeTx{c: c}
206
        return c.currTx, nil
207
}
208
 
209
func (c *fakeConn) Close() error {
210
        if c.currTx != nil {
211
                return errors.New("can't close; in a Transaction")
212
        }
213
        if c.db == nil {
214
                return errors.New("can't close; already closed")
215
        }
216
        c.db = nil
217
        return nil
218
}
219
 
220
func checkSubsetTypes(args []interface{}) error {
221
        for n, arg := range args {
222
                switch arg.(type) {
223
                case int64, float64, bool, nil, []byte, string, time.Time:
224
                default:
225
                        return fmt.Errorf("fakedb_test: invalid argument #%d: %v, type %T", n+1, arg, arg)
226
                }
227
        }
228
        return nil
229
}
230
 
231
func (c *fakeConn) Exec(query string, args []interface{}) (driver.Result, error) {
232
        // This is an optional interface, but it's implemented here
233
        // just to check that all the args of of the proper types.
234
        // ErrSkip is returned so the caller acts as if we didn't
235
        // implement this at all.
236
        err := checkSubsetTypes(args)
237
        if err != nil {
238
                return nil, err
239
        }
240
        return nil, driver.ErrSkip
241
}
242
 
243
func errf(msg string, args ...interface{}) error {
244
        return errors.New("fakedb: " + fmt.Sprintf(msg, args...))
245
}
246
 
247
// parts are table|selectCol1,selectCol2|whereCol=?,whereCol2=?
248
// (note that where where columns must always contain ? marks,
249
//  just a limitation for fakedb)
250
func (c *fakeConn) prepareSelect(stmt *fakeStmt, parts []string) (driver.Stmt, error) {
251
        if len(parts) != 3 {
252
                return nil, errf("invalid SELECT syntax with %d parts; want 3", len(parts))
253
        }
254
        stmt.table = parts[0]
255
        stmt.colName = strings.Split(parts[1], ",")
256
        for n, colspec := range strings.Split(parts[2], ",") {
257
                if colspec == "" {
258
                        continue
259
                }
260
                nameVal := strings.Split(colspec, "=")
261
                if len(nameVal) != 2 {
262
                        return nil, errf("SELECT on table %q has invalid column spec of %q (index %d)", stmt.table, colspec, n)
263
                }
264
                column, value := nameVal[0], nameVal[1]
265
                _, ok := c.db.columnType(stmt.table, column)
266
                if !ok {
267
                        return nil, errf("SELECT on table %q references non-existent column %q", stmt.table, column)
268
                }
269
                if value != "?" {
270
                        return nil, errf("SELECT on table %q has pre-bound value for where column %q; need a question mark",
271
                                stmt.table, column)
272
                }
273
                stmt.whereCol = append(stmt.whereCol, column)
274
                stmt.placeholders++
275
        }
276
        return stmt, nil
277
}
278
 
279
// parts are table|col=type,col2=type2
280
func (c *fakeConn) prepareCreate(stmt *fakeStmt, parts []string) (driver.Stmt, error) {
281
        if len(parts) != 2 {
282
                return nil, errf("invalid CREATE syntax with %d parts; want 2", len(parts))
283
        }
284
        stmt.table = parts[0]
285
        for n, colspec := range strings.Split(parts[1], ",") {
286
                nameType := strings.Split(colspec, "=")
287
                if len(nameType) != 2 {
288
                        return nil, errf("CREATE table %q has invalid column spec of %q (index %d)", stmt.table, colspec, n)
289
                }
290
                stmt.colName = append(stmt.colName, nameType[0])
291
                stmt.colType = append(stmt.colType, nameType[1])
292
        }
293
        return stmt, nil
294
}
295
 
296
// parts are table|col=?,col2=val
297
func (c *fakeConn) prepareInsert(stmt *fakeStmt, parts []string) (driver.Stmt, error) {
298
        if len(parts) != 2 {
299
                return nil, errf("invalid INSERT syntax with %d parts; want 2", len(parts))
300
        }
301
        stmt.table = parts[0]
302
        for n, colspec := range strings.Split(parts[1], ",") {
303
                nameVal := strings.Split(colspec, "=")
304
                if len(nameVal) != 2 {
305
                        return nil, errf("INSERT table %q has invalid column spec of %q (index %d)", stmt.table, colspec, n)
306
                }
307
                column, value := nameVal[0], nameVal[1]
308
                ctype, ok := c.db.columnType(stmt.table, column)
309
                if !ok {
310
                        return nil, errf("INSERT table %q references non-existent column %q", stmt.table, column)
311
                }
312
                stmt.colName = append(stmt.colName, column)
313
 
314
                if value != "?" {
315
                        var subsetVal interface{}
316
                        // Convert to driver subset type
317
                        switch ctype {
318
                        case "string":
319
                                subsetVal = []byte(value)
320
                        case "blob":
321
                                subsetVal = []byte(value)
322
                        case "int32":
323
                                i, err := strconv.Atoi(value)
324
                                if err != nil {
325
                                        return nil, errf("invalid conversion to int32 from %q", value)
326
                                }
327
                                subsetVal = int64(i) // int64 is a subset type, but not int32
328
                        default:
329
                                return nil, errf("unsupported conversion for pre-bound parameter %q to type %q", value, ctype)
330
                        }
331
                        stmt.colValue = append(stmt.colValue, subsetVal)
332
                } else {
333
                        stmt.placeholders++
334
                        stmt.placeholderConverter = append(stmt.placeholderConverter, converterForType(ctype))
335
                        stmt.colValue = append(stmt.colValue, "?")
336
                }
337
        }
338
        return stmt, nil
339
}
340
 
341
func (c *fakeConn) Prepare(query string) (driver.Stmt, error) {
342
        if c.db == nil {
343
                panic("nil c.db; conn = " + fmt.Sprintf("%#v", c))
344
        }
345
        parts := strings.Split(query, "|")
346
        if len(parts) < 1 {
347
                return nil, errf("empty query")
348
        }
349
        cmd := parts[0]
350
        parts = parts[1:]
351
        stmt := &fakeStmt{q: query, c: c, cmd: cmd}
352
        c.incrStat(&c.stmtsMade)
353
        switch cmd {
354
        case "WIPE":
355
                // Nothing
356
        case "SELECT":
357
                return c.prepareSelect(stmt, parts)
358
        case "CREATE":
359
                return c.prepareCreate(stmt, parts)
360
        case "INSERT":
361
                return c.prepareInsert(stmt, parts)
362
        default:
363
                return nil, errf("unsupported command type %q", cmd)
364
        }
365
        return stmt, nil
366
}
367
 
368
func (s *fakeStmt) ColumnConverter(idx int) driver.ValueConverter {
369
        return s.placeholderConverter[idx]
370
}
371
 
372
func (s *fakeStmt) Close() error {
373
        if !s.closed {
374
                s.c.incrStat(&s.c.stmtsClosed)
375
                s.closed = true
376
        }
377
        return nil
378
}
379
 
380
var errClosed = errors.New("fakedb: statement has been closed")
381
 
382
func (s *fakeStmt) Exec(args []interface{}) (driver.Result, error) {
383
        if s.closed {
384
                return nil, errClosed
385
        }
386
        err := checkSubsetTypes(args)
387
        if err != nil {
388
                return nil, err
389
        }
390
 
391
        db := s.c.db
392
        switch s.cmd {
393
        case "WIPE":
394
                db.wipe()
395
                return driver.DDLSuccess, nil
396
        case "CREATE":
397
                if err := db.createTable(s.table, s.colName, s.colType); err != nil {
398
                        return nil, err
399
                }
400
                return driver.DDLSuccess, nil
401
        case "INSERT":
402
                return s.execInsert(args)
403
        }
404
        fmt.Printf("EXEC statement, cmd=%q: %#v\n", s.cmd, s)
405
        return nil, fmt.Errorf("unimplemented statement Exec command type of %q", s.cmd)
406
}
407
 
408
func (s *fakeStmt) execInsert(args []interface{}) (driver.Result, error) {
409
        db := s.c.db
410
        if len(args) != s.placeholders {
411
                panic("error in pkg db; should only get here if size is correct")
412
        }
413
        db.mu.Lock()
414
        t, ok := db.table(s.table)
415
        db.mu.Unlock()
416
        if !ok {
417
                return nil, fmt.Errorf("fakedb: table %q doesn't exist", s.table)
418
        }
419
 
420
        t.mu.Lock()
421
        defer t.mu.Unlock()
422
 
423
        cols := make([]interface{}, len(t.colname))
424
        argPos := 0
425
        for n, colname := range s.colName {
426
                colidx := t.columnIndex(colname)
427
                if colidx == -1 {
428
                        return nil, fmt.Errorf("fakedb: column %q doesn't exist or dropped since prepared statement was created", colname)
429
                }
430
                var val interface{}
431
                if strvalue, ok := s.colValue[n].(string); ok && strvalue == "?" {
432
                        val = args[argPos]
433
                        argPos++
434
                } else {
435
                        val = s.colValue[n]
436
                }
437
                cols[colidx] = val
438
        }
439
 
440
        t.rows = append(t.rows, &row{cols: cols})
441
        return driver.RowsAffected(1), nil
442
}
443
 
444
func (s *fakeStmt) Query(args []interface{}) (driver.Rows, error) {
445
        if s.closed {
446
                return nil, errClosed
447
        }
448
        err := checkSubsetTypes(args)
449
        if err != nil {
450
                return nil, err
451
        }
452
 
453
        db := s.c.db
454
        if len(args) != s.placeholders {
455
                panic("error in pkg db; should only get here if size is correct")
456
        }
457
 
458
        db.mu.Lock()
459
        t, ok := db.table(s.table)
460
        db.mu.Unlock()
461
        if !ok {
462
                return nil, fmt.Errorf("fakedb: table %q doesn't exist", s.table)
463
        }
464
        t.mu.Lock()
465
        defer t.mu.Unlock()
466
 
467
        colIdx := make(map[string]int) // select column name -> column index in table
468
        for _, name := range s.colName {
469
                idx := t.columnIndex(name)
470
                if idx == -1 {
471
                        return nil, fmt.Errorf("fakedb: unknown column name %q", name)
472
                }
473
                colIdx[name] = idx
474
        }
475
 
476
        mrows := []*row{}
477
rows:
478
        for _, trow := range t.rows {
479
                // Process the where clause, skipping non-match rows. This is lazy
480
                // and just uses fmt.Sprintf("%v") to test equality.  Good enough
481
                // for test code.
482
                for widx, wcol := range s.whereCol {
483
                        idx := t.columnIndex(wcol)
484
                        if idx == -1 {
485
                                return nil, fmt.Errorf("db: invalid where clause column %q", wcol)
486
                        }
487
                        tcol := trow.cols[idx]
488
                        if bs, ok := tcol.([]byte); ok {
489
                                // lazy hack to avoid sprintf %v on a []byte
490
                                tcol = string(bs)
491
                        }
492
                        if fmt.Sprintf("%v", tcol) != fmt.Sprintf("%v", args[widx]) {
493
                                continue rows
494
                        }
495
                }
496
                mrow := &row{cols: make([]interface{}, len(s.colName))}
497
                for seli, name := range s.colName {
498
                        mrow.cols[seli] = trow.cols[colIdx[name]]
499
                }
500
                mrows = append(mrows, mrow)
501
        }
502
 
503
        cursor := &rowsCursor{
504
                pos:  -1,
505
                rows: mrows,
506
                cols: s.colName,
507
        }
508
        return cursor, nil
509
}
510
 
511
func (s *fakeStmt) NumInput() int {
512
        return s.placeholders
513
}
514
 
515
func (tx *fakeTx) Commit() error {
516
        tx.c.currTx = nil
517
        return nil
518
}
519
 
520
func (tx *fakeTx) Rollback() error {
521
        tx.c.currTx = nil
522
        return nil
523
}
524
 
525
type rowsCursor struct {
526
        cols   []string
527
        pos    int
528
        rows   []*row
529
        closed bool
530
 
531
        // a clone of slices to give out to clients, indexed by the
532
        // the original slice's first byte address.  we clone them
533
        // just so we're able to corrupt them on close.
534
        bytesClone map[*byte][]byte
535
}
536
 
537
func (rc *rowsCursor) Close() error {
538
        if !rc.closed {
539
                for _, bs := range rc.bytesClone {
540
                        bs[0] = 255 // first byte corrupted
541
                }
542
        }
543
        rc.closed = true
544
        return nil
545
}
546
 
547
func (rc *rowsCursor) Columns() []string {
548
        return rc.cols
549
}
550
 
551
func (rc *rowsCursor) Next(dest []interface{}) error {
552
        if rc.closed {
553
                return errors.New("fakedb: cursor is closed")
554
        }
555
        rc.pos++
556
        if rc.pos >= len(rc.rows) {
557
                return io.EOF // per interface spec
558
        }
559
        for i, v := range rc.rows[rc.pos].cols {
560
                // TODO(bradfitz): convert to subset types? naah, I
561
                // think the subset types should only be input to
562
                // driver, but the sql package should be able to handle
563
                // a wider range of types coming out of drivers. all
564
                // for ease of drivers, and to prevent drivers from
565
                // messing up conversions or doing them differently.
566
                dest[i] = v
567
 
568
                if bs, ok := v.([]byte); ok {
569
                        if rc.bytesClone == nil {
570
                                rc.bytesClone = make(map[*byte][]byte)
571
                        }
572
                        clone, ok := rc.bytesClone[&bs[0]]
573
                        if !ok {
574
                                clone = make([]byte, len(bs))
575
                                copy(clone, bs)
576
                                rc.bytesClone[&bs[0]] = clone
577
                        }
578
                        dest[i] = clone
579
                }
580
        }
581
        return nil
582
}
583
 
584
func converterForType(typ string) driver.ValueConverter {
585
        switch typ {
586
        case "bool":
587
                return driver.Bool
588
        case "nullbool":
589
                return driver.Null{Converter: driver.Bool}
590
        case "int32":
591
                return driver.Int32
592
        case "string":
593
                return driver.NotNull{Converter: driver.String}
594
        case "nullstring":
595
                return driver.Null{Converter: driver.String}
596
        case "int64":
597
                // TODO(coopernurse): add type-specific converter
598
                return driver.NotNull{Converter: driver.DefaultParameterConverter}
599
        case "nullint64":
600
                // TODO(coopernurse): add type-specific converter
601
                return driver.Null{Converter: driver.DefaultParameterConverter}
602
        case "float64":
603
                // TODO(coopernurse): add type-specific converter
604
                return driver.NotNull{Converter: driver.DefaultParameterConverter}
605
        case "nullfloat64":
606
                // TODO(coopernurse): add type-specific converter
607
                return driver.Null{Converter: driver.DefaultParameterConverter}
608
        case "datetime":
609
                return driver.DefaultParameterConverter
610
        }
611
        panic("invalid fakedb column type of " + typ)
612
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.