<|start_filename|>shell/lex.go<|end_filename|>
// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package shell

import (
	"fmt"
	"strings"
	"unicode"
	"unicode/utf8"
)

// SQL lexer based on template/parse/lex.go.
// itemType identifies the type of lex items.
type itemType int

// item represents a token or text string returned from the scanner.
type item struct {
	typ itemType
	pos int
	val string
}

func (i item) String() string {
	switch {
	case i.typ == itemEOF:
		return "EOF"
	case i.typ == itemError:
		return i.val
	case i.typ == itemID:
		return fmt.Sprintf("[%s]", i.val)
	case i.typ > itemKeyword:
		return fmt.Sprintf("<%s>", i.val)
	case len(i.val) > 10:
		return fmt.Sprintf("%.10q...", i.val)
	}
	return fmt.Sprintf("%q %d", i.val, i.typ)
}

const (
	itemError itemType = iota // error occurred; value is text of error
	itemEOF
	itemSpace // run of spaces separating arguments
	itemMinus
	itemLP
	itemRP
	itemSemi
	itemPlus
	itemStar
	itemSlash
	itemRem
	itemEq
	itemLT
	itemLE
	itemNE
	itemLShift
	itemGT
	itemGE
	itemRShift
	itemConcat
	itemBitOr
	itemComma
	itemBitAnd
	itemBitNot
	itemString
	itemID
	itemVariable
	itemBlob
	itemDot
	itemInteger
	itemFloat
	// Keywords appear after all the rest.
	itemKeyword // used only to delimit the keywords
	itemAbort
	itemAction
	itemAdd
	itemAfter
	itemAll
	itemAlter
	itemAnalyze
	itemAnd
	itemAs
	itemAsc
	itemAttach
	itemAutoincr
	itemBefore
	itemBegin
	itemBetween
	itemBy
	itemCascade
	itemCase
	itemCast
	itemCheck
	itemCollate
	itemColumnKw
	itemCommit
	itemConflict
	itemConstraint
	itemCreate
	itemCtimeKw
	itemDatabase
	itemDefault
	itemDeferrable
	itemDeferred
	itemDelete
	itemDesc
	itemDetach
	itemDistinct
	itemDrop
	itemEach
	itemElse
	itemEnd
	itemEscape
	itemExcept
	itemExclusive
	itemExists
	itemExplain
	itemFail
	itemFor
	itemForeign
	itemFrom
	itemGroup
	itemHaving
	itemIf
	itemIgnore
	itemImmediate
	itemIn
	itemIndex
	itemIndexed
	itemInitially
	itemInsert
	itemInstead
	itemIntersect
	itemInto
	itemIs
	itemIsnull
	itemJoin
	itemJoinKw
	itemKey
	itemLikeKw
	itemLimit
	itemMatch
	itemNo
	itemNot
	itemNotNull
	itemNull
	itemOf
	itemOffset
	itemOn
	itemOr
	itemOrder
	itemPlan
	itemPragma
	itemPrimary
	itemQuery
	itemRaise
	itemRecursive
	itemReferences
	itemReindex
	itemRelease
	itemRename
	itemReplace
	itemRestrict
	itemRollback
	itemRow
	itemSavepoint
	itemSelect
	itemSet
	itemTable
	itemTemp
	itemThen
	itemTo
	itemTransaction
	itemTrigger
	itemUnion
	itemUnique
	itemUpdate
	itemUsing
	itemVacuum
	itemValues
	itemView
	itemVirtual
	itemWhen
	itemWhere
	itemWith
	itemWithout
)

const eof = -1

var key = map[string]itemType{
	"ABORT":             itemAbort,
	"ACTION":            itemAction,
	"ADD":               itemAdd,
	"AFTER":             itemAfter,
	"ALL":               itemAll,
	"ALTER":             itemAlter,
	"ANALYZE":           itemAnalyze,
	"AND":               itemAnd,
	"AS":                itemAs,
	"ASC":               itemAsc,
	"ATTACH":            itemAttach,
	"AUTOINCREMENT":     itemAutoincr,
	"BEFORE":            itemBefore,
	"BEGIN":             itemBegin,
	"BETWEEN":           itemBetween,
	"BY":                itemBy,
	"CASCADE":           itemCascade,
	"CASE":              itemCase,
	"CAST":              itemCast,
	"CHECK":             itemCheck,
	"COLLATE":           itemCollate,
	"COLUMN":            itemColumnKw,
	"COMMIT":            itemCommit,
	"CONFLICT":          itemConflict,
	"CONSTRAINT":        itemConstraint,
	"CREATE":            itemCreate,
	"CROSS":             itemJoinKw,
	"CURRENT_DATE":      itemCtimeKw,
	"CURRENT_TIME":      itemCtimeKw,
	"CURRENT_TIMESTAMP": itemCtimeKw,
	"DATABASE":          itemDatabase,
	"DEFAULT":           itemDefault,
	"DEFERRED":          itemDeferred,
	"DEFERRABLE":        itemDeferrable,
	"DELETE":            itemDelete,
	"DESC":              itemDesc,
	"DETACH":            itemDetach,
	"DISTINCT":          itemDistinct,
	"DROP":              itemDrop,
	"END":               itemEnd,
	"EACH":              itemEach,
	"ELSE":              itemElse,
	"ESCAPE":            itemEscape,
	"EXCEPT":            itemExcept,
	"EXCLUSIVE":         itemExclusive,
	"EXISTS":            itemExists,
	"EXPLAIN":           itemExplain,
	"FAIL":              itemFail,
	"FOR":               itemFor,
	"FOREIGN":           itemForeign,
	"FROM":              itemFrom,
	"FULL":              itemJoinKw,
	"GLOB":              itemLikeKw,
	"GROUP":             itemGroup,
	"HAVING":            itemHaving,
	"IF":                itemIf,
	"IGNORE":            itemIgnore,
	"IMMEDIATE":         itemImmediate,
	"IN":                itemIn,
	"INDEX":             itemIndex,
	"INDEXED":           itemIndexed,
	"INITIALLY":         itemInitially,
	"INNER":             itemJoinKw,
	"INSERT":            itemInsert,
	"INSTEAD":           itemInstead,
	"INTERSECT":         itemIntersect,
	"INTO":              itemInto,
	"IS":                itemIs,
	"ISNULL":            itemIsnull,
	"JOIN":              itemJoin,
	"KEY":               itemKey,
	"LEFT":              itemJoinKw,
	"LIKE":              itemLikeKw,
	"LIMIT":             itemLimit,
	"MATCH":             itemMatch,
	"NATURAL":           itemJoinKw,
	"NO":                itemNo,
	"NOT":               itemNot,
	"NOTNULL":           itemNotNull,
	"NULL":              itemNull,
	"OF":                itemOf,
	"OFFSET":            itemOffset,
	"ON":                itemOn,
	"OR":                itemOr,
	"ORDER":             itemOrder,
	"OUTER":             itemJoinKw,
	"PLAN":              itemPlan,
	"PRAGMA":            itemPragma,
	"PRIMARY":           itemPrimary,
	"QUERY":             itemQuery,
	"RAISE":             itemRaise,
	"RECURSIVE":         itemRecursive,
	"REFERENCES":        itemReferences,
	"REGEXP":            itemLikeKw,
	"REINDEX":           itemReindex,
	"RELEASE":           itemRelease,
	"RENAME":            itemRename,
	"REPLACE":           itemReplace,
	"RESTRICT":          itemRestrict,
	"RIGHT":             itemJoinKw,
	"ROLLBACK":          itemRollback,
	"ROW":               itemRow,
	"SAVEPOINT":         itemSavepoint,
	"SELECT":            itemSelect,
	"SET":               itemSet,
	"TABLE":             itemTable,
	"TEMP":              itemTemp,
	"TEMPORARY":         itemTemp,
	"THEN":              itemThen,
	"TO":                itemTo,
	"TRANSACTION":       itemTransaction,
	"TRIGGER":           itemTrigger,
	"UNION":             itemUnion,
	"UNIQUE":            itemUnique,
	"UPDATE":            itemUpdate,
	"USING":             itemUsing,
	"VACUUM":            itemVacuum,
	"VALUES":            itemValues,
	"VIEW":              itemView,
	"VIRTUAL":           itemVirtual,
	"WITH":              itemWith,
	"WITHOUT":           itemWithout,
	"WHEN":              itemWhen,
	"WHERE":             itemWhere,
}

// stateFn represents the state of the scanner as a function that returns the next state.
type stateFn func(*lexer) stateFn

type lexer struct {
	input string    // the input being scanned.
	items chan item // channel of scanned items.
	pos   int       // current position in the line
	start int       // start position of this item
	width int       // width of last rune read from input
}

// next returns the next rune in the input.
func (l *lexer) next() rune {
	if l.pos >= len(l.input) {
		l.width = 0
		return eof
	}
	r, w := utf8.DecodeRuneInString(l.input[l.pos:])
	l.width = w
	l.pos += l.width
	return r
}

// peek returns but does not consume the next rune in the input.
func (l *lexer) peek() rune {
	r := l.next()
	l.backup()
	return r
}

// backup steps back one rune. Can only be called once per call of next.
func (l *lexer) backup() {
	l.pos -= l.width
}

// emit passes an item back to the client.
func (l *lexer) emit(t itemType) {
	l.items <- item{t, l.start, l.input[l.start:l.pos]}
	l.start = l.pos
}

// ignore skips over the pending input before this point.
func (l *lexer) ignore() {
	l.start = l.pos
}

// errorf returns an error token and terminates the scan by passing
// back a nil pointer that will be the next state, terminating l.Nextitem.
func (l *lexer) errorf(format string, args ...interface{}) stateFn {
	l.items <- item{itemError, l.start, fmt.Sprintf(format, args...)}
	return nil
}

// nextItem returns the next item from the input.
func (l *lexer) nextItem() item {
	return <-l.items
}

// lex creates a new scanner for the input string.
func lex(input string) *lexer {
	l := &lexer{
		input: input,
		items: make(chan item),
	}
	go l.run()
	return l
}

// run runs the state machine for the lexer.
func (l *lexer) run() {
	for state := lexStart; state != nil; {
		state = state(l)
	}
}

func lexSpace(l *lexer) stateFn {
	for {
		r := l.next()
		if !isSpace(r) {
			break
		}
	}
	l.backup()
	l.emit(itemSpace)
	return lexStart
}

// isSpace reports whether r is a space character.
func isSpace(r rune) bool {
	return r == ' ' || r == '\t' || r == '\n' || r == '\r' || r == '\f' || r == '\v'
}

func lexLineComment(l *lexer) stateFn {
	for {
		r := l.next()
		if r == eof || r == '\n' {
			break
		}
	}
	l.emit(itemSpace)
	return lexStart
}

func lexBlockComment(l *lexer) stateFn {
	var pr rune
	for {
		r := l.next()
		if r == eof || (pr == '*' && r == '/') {
			break
		}
		pr = r
	}
	l.emit(itemSpace)
	return lexStart
}

func lexQuote(delim rune) stateFn {
	return func(l *lexer) stateFn {
		var pr rune
		for {
			r := l.next()
			if r == eof {
				return l.errorf("unterminated quoted string")
			}
			if r == delim && pr != delim {
				break
			}
			pr = r
		}
		if delim == '\'' {
			l.emit(itemString)
			return lexStart
		}
		l.emit(itemID)
		return lexStart
	}
}

func lexBracket(l *lexer) stateFn {
	for {
		r := l.next()
		if r == eof {
			return l.errorf("unterminated bracketed identifier")
		}
		if r == ']' {
			break
		}
	}
	l.emit(itemID)
	return lexStart
}

func lexBlobLiteral(l *lexer) stateFn {
	n := 0
	for {
		r := l.next()
		if ('0' <= r && r <= '9') || ('a' <= r && r <= 'f') || ('A' <= r && r <= 'F') { // isxdigit
			n++
			continue
		}
		if r != '\'' || n%2 != 0 {
			// TODO consume until '\''
			return l.errorf("malformed blob literal")
		}
		break
	}
	l.emit(itemBlob)
	return lexStart
}

func lexHexInteger(l *lexer) stateFn {
	n := 0
	for {
		r := l.next()
		if ('0' <= r && r <= '9') || ('a' <= r && r <= 'f') || ('A' <= r && r <= 'F') { // isxdigit
			continue
		}
		if n == 0 {
			return l.errorf("malformed hex integer")
		}
		break
	}
	l.backup()
	l.emit(itemInteger)
	return lexStart
}

func idChar(r rune) bool {
	return (r >= '0' && r <= '9') || (r >= 'A' && r <= 'Z') || r == '_' || (r >= 'a' && r <= 'z') || r > 127
}

func lexStart(l *lexer) stateFn {
	r := l.next()
	switch r {
	case eof:
		l.emit(itemEOF)
		return nil
	case ' ', '\t', '\n', '\f', '\r':
		return lexSpace
	case '-':
		if l.peek() == '-' {
			return lexLineComment
		}
		l.emit(itemMinus)
		return lexStart
	case '(':
		l.emit(itemLP)
		return lexStart
	case ')':
		l.emit(itemRP)
		return lexStart
	case ';':
		l.emit(itemSemi)
		return lexStart
	case '+':
		l.emit(itemPlus)
		return lexStart
	case '*':
		l.emit(itemStar)
		return lexStart
	case '/':
		if l.peek() == '*' {
			l.next()
			return lexBlockComment
		}
		l.emit(itemSlash)
		return lexStart
	case '%':
		l.emit(itemRem)
		return lexStart
	case '=':
		if l.peek() == '=' {
			l.next()
		}
		l.emit(itemEq)
		return lexStart
	case '<':
		it := itemLT
		switch l.peek() {
		case '=':
			it = itemLE
			l.next()
		case '>':
			it = itemNE
			l.next()
		case '<':
			it = itemLShift
			l.next()
		}
		l.emit(it)
		return lexStart
	case '>':
		it := itemGT
		switch l.peek() {
		case '=':
			it = itemGE
			l.next()
		case '>':
			it = itemRShift
			l.next()
		}
		l.emit(it)
		return lexStart
	case '!':
		if l.next() != '=' {
			return l.errorf("illegal rune: %q", r)
		}
		l.emit(itemNE)
		return lexStart
	case '|':
		if l.peek() == '|' {
			l.next()
			l.emit(itemConcat)
			return lexStart
		}
		l.emit(itemBitOr)
		return lexStart
	case ',':
		l.emit(itemComma)
		return lexStart
	case '&':
		l.emit(itemBitAnd)
		return lexStart
	case '~':
		l.emit(itemBitNot)
		return lexStart
	case '`', '\'', '"':
		return lexQuote(r)
	case '.':
		if !unicode.IsDigit(l.peek()) {
			l.emit(itemDot)
			return lexStart
		}
		fallthrough
	case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
		if r == '0' {
			p := l.peek()
			if p == 'x' || p == 'X' {
				l.next()
				return lexHexInteger
			}
		}
		for unicode.IsDigit(r) {
			r = l.next()
		}
		it := itemInteger
		if r == '.' {
			r = l.next()
			for unicode.IsDigit(r) {
				r = l.next()
			}
			it = itemFloat
		}
		if r == 'e' || r == 'E' {
			r = l.next()
			if unicode.IsDigit(r) {
				for unicode.IsDigit(r) {
					r = l.next()
				}
			} else if r == '+' || r == '-' {
				r = l.next()
				if unicode.IsDigit(r) {
					for unicode.IsDigit(r) {
						r = l.next()
					}
				} else {
					return l.errorf("bad number")
				}
			} else {
				return l.errorf("bad number")
			}
		}
		if idChar(r) {
			return l.errorf("bad number")
		}
		l.backup()
		l.emit(it)
		return lexStart
	case '[':
		return lexBracket
	case '?':
		for nr := l.next(); '0' <= nr && nr <= '9'; nr = l.next() {
		}
		l.backup()
		l.emit(itemVariable)
		return lexStart
	case '$', '@', '#', ':':
		r = l.next()
		if !idChar(r) {
			return l.errorf("bad variable name")
		}
		for idChar(r) {
			r = l.next()
		}
		l.backup()
		l.emit(itemVariable)
		return lexStart
	case 'x', 'X':
		if l.peek() == '\'' {
			l.next()
			return lexBlobLiteral
		}
		fallthrough
	default:
		if !idChar(r) {
			break
		}
		for idChar(r) {
			r = l.next()
		}
		l.backup()
		it, ok := key[strings.ToUpper(l.input[l.start:l.pos])]
		if !ok {
			it = itemID
		}
		l.emit(it)
		return lexStart
	}
	return l.errorf("illegal rune: %q", r)
}

func Parse(line string) {
	l := lex(line)

	for {
		item := l.nextItem()
		if item.typ == itemEOF {
			break
		}
		//fmt.Printf("%s\n", item)
	}
}


<|start_filename|>shell/autocomplete.go<|end_filename|>
// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package shell

import (
	"strings"

	"github.com/gwenn/gosqlite"
)

type pendingAction struct {
	action  sqlite.Action
	dbName  string
	tblName string
	typ     string
}

type CompletionCache struct {
	memDb          *sqlite.Conn    // SQLite FTS extension is used to do auto-completion
	insert         *sqlite.Stmt    // statement used to update col_names table
	pendingActions []pendingAction // actions trapped by the authorizer for deferred cache update
}

func CreateCache() (*CompletionCache, error) {
	db, err := sqlite.Open(":memory:")
	if err != nil {
		return nil, err
	}
	cc := &CompletionCache{memDb: db, pendingActions: make([]pendingAction, 0, 5)}
	if err = cc.init(); err != nil {
		db.Close()
		return nil, err
	}
	s, err := cc.memDb.Prepare("INSERT INTO col_names (db_name, tbl_name, type, col_name) VALUES (?, ?, ?, ?)")
	if err != nil {
		return nil, err
	}
	cc.insert = s
	return cc, nil
}

func (cc *CompletionCache) init() error {
	cmd := `CREATE VIRTUAL TABLE pragma_names USING fts4(name, args, tokenize=porter, matchinfo=fts3, notindexed=args);
	CREATE VIRTUAL TABLE func_names USING fts4(name, args, tokenize=porter, matchinfo=fts3, notindexed=args);
	CREATE VIRTUAL TABLE module_names USING fts4(name, args, tokenize=porter, matchinfo=fts3, notindexed=args);
	CREATE VIRTUAL TABLE cmd_names USING fts4(name, args, tokenize=porter, matchinfo=fts3, notindexed=args);
	CREATE VIRTUAL TABLE col_names USING fts4(db_name, tbl_name, type, col_name, tokenize=porter, matchinfo=fts3, notindexed=type);
	`
	var err error
	if err = cc.memDb.FastExec(cmd); err != nil {
		return err
	}
	if err = cc.memDb.Begin(); err != nil {
		return err
	}
	defer func() {
		if err != nil {
			cc.memDb.Rollback()
		} else {
			err = cc.memDb.Commit()
		}
	}()
	s, err := cc.memDb.Prepare("INSERT INTO pragma_names (name, args) VALUES (?, ?)")
	if err != nil {
		return err
	}
	pragmas := []struct {
		Name string
		Args string
	}{
		{Name: "application_id", Args: "integer"},
		{Name: "auto_vacuum", Args: "0 | NONE | 1 | FULL | 2 | INCREMENTAL"},
		{Name: "automatic_index", Args: "boolean"},
		{Name: "busy_timeout", Args: "milliseconds"},
		{Name: "cache_size", Args: "pages or -kibibytes"},
		{Name: "cache_spill", Args: "boolean"},
		{Name: "case_sensitive_like=", Args: "boolean"}, // set-only
		{Name: "checkpoint_fullfsync", Args: "boolean"},
		{Name: "collation_list", Args: ""},  // no =
		{Name: "compile_options", Args: ""}, // no =
		//{Name: "count_changes", Args: "boolean"},
		//{Name: "data_store_directory", Args: "'directory-name'"},
		{Name: "database_list", Args: ""},
		//{Name: "default_cache_size", Args: "Number-of-pages"},
		{Name: "defer_foreign_keys", Args: "boolean"},
		//{Name: "empty_result_callbacks","boolean"},
		{Name: "encoding", Args: "UTF-8 | UTF-16 | UTF-16le | UTF-16be"},
		{Name: "foreign_key_check", Args: "(table-name)"}, // no =
		{Name: "foreign_key_list(", Args: "table-name"},   // no =
		{Name: "foreign_keys", Args: "boolean"},
		{Name: "freelist_count", Args: ""},
		//{Name: "full_column_names", Args: "boolean"},
		{Name: "fullfsync", Args: "boolean"},
		{Name: "ignore_check_constraints=", Args: "boolean"},
		{Name: "incremental_vacuum(", Args: "N"},
		{Name: "index_info(", Args: "index-name"}, // no =
		{Name: "index_list(", Args: "table-name"}, // no =
		{Name: "integrity_check", Args: "(N)"},
		{Name: "journal_mode", Args: "DELETE | TRUNCATE | PERSIST | MEMORY | WAL | OFF"},
		{Name: "journal_size_limit", Args: "N"},
		{Name: "legacy_file_format", Args: "boolean"},
		{Name: "locking_mode", Args: "NORMAL | EXCLUSIVE"},
		{Name: "max_page_count", Args: "N"},
		{Name: "mmap_size", Args: "N"},
		{Name: "page_count", Args: ""}, // no =
		{Name: "page_size", Args: "bytes"},
		//{Name: "parser_trace=", Args: "boolean"},
		{Name: "query_only", Args: "boolean"},
		{Name: "quick_check", Args: "(N)"}, // no =
		{Name: "read_uncommitted", Args: "boolean"},
		{Name: "recursive_triggers", Args: "boolean"},
		{Name: "reverse_unordered_selects", Args: "boolean"},
		{Name: "schema_version", Args: "integer"},
		{Name: "secure_delete", Args: "boolean"},
		//{Name: "short_column_names", Args: "boolean"},
		{Name: "shrink_memory", Args: ""}, // no =
		{Name: "soft_heap_limit", Args: "N"},
		//{Name: "stats", Args: ""},
		{Name: "synchronous", Args: "0 | OFF | 1 | NORMAL | 2 | FULL"},
		{Name: "table_info(", Args: "table-name"}, // no =
		{Name: "temp_store", Args: "0 | DEFAULT | 1 | FILE | 2 | MEMORY"},
		//{Name: "temp_store_directory", Args: "'directory-name'"},
		{Name: "user_version", Args: "integer"},
		//{Name: "vdbe_addoptrace=", Args: "boolean"},
		//{Name: "vdbe_debug=", Args: "boolean"},
		//{Name: "vdbe_listing=", Args: "boolean"},
		//{Name: "vdbe_trace=", Args: "boolean"},
		{Name: "wal_autocheckpoint", Args: "N"},
		{Name: "wal_checkpoint", Args: "(PASSIVE | FULL | RESTART)"}, // no =
		{Name: "writable_schema=", Args: "boolean"},                  // set-only
	}
	for _, pragma := range pragmas {
		if err = s.Exec(pragma.Name, pragma.Args); err != nil {
			return err
		}
	}
	if err = s.Finalize(); err != nil {
		return err
	}
	// Only built-in functions are supported.
	// TODO make possible to register extended/user-defined functions
	s, err = cc.memDb.Prepare("INSERT INTO func_names (name, args) VALUES (?, ?)")
	if err != nil {
		return err
	}
	funs := []struct {
		Name string
		Args string
	}{
		{Name: "abs(", Args: "X"},
		{Name: "changes()", Args: ""},
		{Name: "char(", Args: "X1,X2,...,XN"},
		{Name: "coalesce(", Args: "X,Y,..."},
		{Name: "glob(", Args: "X,Y"},
		{Name: "ifnull(", Args: "X,Y"},
		{Name: "instr(", Args: "X,Y"},
		{Name: "hex(", Args: "X"},
		{Name: "last_insert_rowid()", Args: ""},
		{Name: "length(", Args: "X"},
		{Name: "like(", Args: "X,Y[,Z]"},
		{Name: "likelihood(", Args: "X,Y"},
		{Name: "load_extension(", Args: "X[,Y]"},
		{Name: "lower(", Args: "X"},
		{Name: "ltrim(", Args: "X[,Y]"},
		{Name: "max(", Args: "X[,Y,...]"},
		{Name: "min(", Args: "X[,Y,...]"},
		{Name: "nullif(", Args: "X,Y"},
		{Name: "printf(", Args: "FORMAT,..."},
		{Name: "quote(", Args: "X"},
		{Name: "random()", Args: ""},
		{Name: "randomblob(", Args: "N"},
		{Name: "replace", Args: "X,Y,Z"},
		{Name: "round(", Args: "X[,Y]"},
		{Name: "rtrim(", Args: "X[,Y]"},
		{Name: "soundex(", Args: "X"},
		{Name: "sqlite_compileoption_get(", Args: "N"},
		{Name: "sqlite_compileoption_used(", Args: "X"},
		{Name: "sqlite_source_id()", Args: ""},
		{Name: "sqlite_version()", Args: ""},
		{Name: "substr(", Args: "X,Y[,Z]"},
		{Name: "total_changes()", Args: ""},
		{Name: "trim(", Args: "X[,Y]"},
		{Name: "typeof(", Args: "X"},
		{Name: "unlikely(", Args: "X"},
		{Name: "unicode(", Args: "X"},
		{Name: "upper(", Args: "X"},
		{Name: "zeroblob(", Args: "N"},
		// aggregate functions
		{Name: "avg(", Args: "X"},
		{Name: "count(", Args: "X|*"},
		{Name: "group_concat(", Args: "X[,Y]"},
		//{Name: "max(", Args: "X"},
		//{Name: "min(", Args: "X"},
		{Name: "sum(", Args: "X"},
		{Name: "total(", Args: "X"},
		// date functions
		{Name: "date(", Args: "timestring, modifier, modifier, ..."},
		{Name: "time(", Args: "timestring, modifier, modifier, ..."},
		{Name: "datetime(", Args: "timestring, modifier, modifier, ..."},
		{Name: "julianday(", Args: "timestring, modifier, modifier, ..."},
		{Name: "strftime(", Args: "format, timestring, modifier, modifier, ..."},
	}
	for _, fun := range funs {
		if err = s.Exec(fun.Name, fun.Args); err != nil {
			return err
		}
	}
	if err = s.Finalize(); err != nil {
		return err
	}
	// Only built-in modules are supported.
	// TODO make possible to register extended/user-defined modules
	s, err = cc.memDb.Prepare("INSERT INTO module_names (name, args) VALUES (?, ?)")
	if err != nil {
		return err
	}
	mods := []struct {
		Name string
		Args string
	}{
		{Name: "fts3(", Args: ""},
		{Name: "fts4(", Args: ""},
		{Name: "rtree(", Args: ""},
	}
	for _, mod := range mods {
		if err = s.Exec(mod.Name, mod.Args); err != nil {
			return err
		}
	}
	if err = s.Finalize(); err != nil {
		return err
	}
	s, err = cc.memDb.Prepare("INSERT INTO cmd_names (name, args) VALUES (?, ?)")
	if err != nil {
		return err
	}
	cmds := []struct {
		Name string
		Args string
	}{
		{Name: ".backup", Args: "?DB? FILE"},
		{Name: ".bail", Args: "ON|OFF"},
		{Name: ".clone", Args: "NEWDB"},
		{Name: ".databases", Args: ""},
		{Name: ".dump", Args: "?TABLE? ..."},
		{Name: ".echo", Args: "ON|OFF"},
		{Name: ".eqp", Args: "ON|OFF"},
		{Name: ".exit", Args: ""},
		{Name: ".explain", Args: "?ON|OFF?"},
		{Name: ".fullschema", Args: ""},
		//{Name: ".header", Args: "ON|OFF"},
		{Name: ".headers", Args: "ON|OFF"},
		{Name: ".help", Args: ""},
		{Name: ".import", Args: "FILE TABLE"},
		{Name: ".indices", Args: "?TABLE?"},
		{Name: ".load", Args: "FILE ?ENTRY?"},
		{Name: ".log", Args: "FILE|off"},
		{Name: ".mode", Args: "MODE ?TABLE?"},
		{Name: ".nullvalue", Args: "STRING"},
		{Name: ".open", Args: "?FILENAME?"},
		{Name: ".output", Args: "stdout | FILENAME"},
		{Name: ".print", Args: "STRING..."},
		{Name: ".prompt", Args: "MAIN CONTINUE"},
		{Name: ".quit", Args: ""},
		{Name: ".read", Args: "FILENAME"},
		{Name: ".restore", Args: "?DB? FILE"},
		{Name: ".save", Args: "FILE"},
		{Name: ".schema", Args: "?TABLE?"},
		{Name: ".separator", Args: "STRING ?NL?"},
		{Name: ".shell", Args: "CMD ARGS..."},
		{Name: ".show", Args: ""},
		{Name: ".stats", Args: "ON|OFF"},
		{Name: ".system", Args: "CMD ARGS..."},
		{Name: ".tables", Args: "?TABLE?"},
		{Name: ".timeout", Args: "MS"},
		{Name: ".timer", Args: "ON|OFF"},
		{Name: ".trace", Args: "FILE|off"},
		{Name: ".vfsname", Args: "?AUX?"},
		{Name: ".width", Args: "NUM1 NUM2 ..."},
	}
	for _, cmd := range cmds {
		if err = s.Exec(cmd.Name, cmd.Args); err != nil {
			return err
		}
	}
	if err = s.Finalize(); err != nil {
		return err
	}
	return err
}

func (cc *CompletionCache) Close() error {
	if err := cc.insert.Finalize(); err != nil {
		return err
	}
	return cc.memDb.Close()
}

func (cc *CompletionCache) Cache(db *sqlite.Conn) error {
	// It seems not necessary to disable the SQLite statement cache to make this authorizer work
	// because DDL are re-prepared when the schema has been touched.
	db.SetAuthorizer(func(udp interface{}, action sqlite.Action, arg1, arg2, dbName, triggerName string) sqlite.Auth {
		switch action {
		case sqlite.Detach:
			cc.pendingActions = append(cc.pendingActions, pendingAction{action: action, dbName: arg1})
		case sqlite.Attach:
			// database name is not available, only the path...
			if arg1 != "" && arg1 != ":memory:" { // temporary db: "" and memory db: ":memory:" are empty when attached.
				cc.pendingActions = append(cc.pendingActions, pendingAction{action: action, dbName: arg1})
			}
		case sqlite.DropTable, sqlite.DropTempTable, sqlite.DropView, sqlite.DropTempView, sqlite.DropVTable:
			cc.pendingActions = append(cc.pendingActions, pendingAction{action: action, dbName: dbName, tblName: arg1})
		case sqlite.CreateTable, sqlite.CreateTempTable, sqlite.CreateVTable:
			cc.pendingActions = append(cc.pendingActions, pendingAction{action: action, dbName: dbName, tblName: arg1, typ: "table"})
		case sqlite.CreateView, sqlite.CreateTempView:
			cc.pendingActions = append(cc.pendingActions, pendingAction{action: action, dbName: dbName, tblName: arg1, typ: "view"})
		case sqlite.AlterTable:
			cc.pendingActions = append(cc.pendingActions, pendingAction{action: action, dbName: arg1, tblName: arg2, typ: "table"})
			// TODO trigger, index
		}
		return sqlite.AuthOk
	}, nil)
	dbNames, err := db.Databases()
	if err != nil {
		return err
	}
	// cache databases schema
	for dbName := range dbNames {
		err = cc.cache(db, dbName)
		if err != nil {
			return err
		}
	}
	return nil
}

func (cc *CompletionCache) cache(db *sqlite.Conn, dbName string) error {
	ts, err := db.Tables(dbName)
	if err != nil {
		return err
	}
	if dbName == "temp" {
		ts = append(ts, "sqlite_temp_master")
	} else {
		ts = append(ts, "sqlite_master")
	}

	for _, table := range ts {
		if err = cc.cacheTable(db, dbName, table, "table"); err != nil {
			return err
		}
	}

	vs, err := db.Views(dbName)
	if err != nil {
		return err
	}
	for _, view := range vs {
		if err = cc.cacheTable(db, dbName, view, "view"); err != nil {
			return err
		}
	}

	return nil
}

func (cc *CompletionCache) cacheTable(db *sqlite.Conn, dbName, tblName, typ string) error {
	cs, err := db.Columns(dbName, tblName)
	if err != nil {
		return err
	}
	for _, c := range cs {
		if err = cc.insert.Exec(dbName, tblName, typ, c.Name); err != nil {
			return err
		}
	}
	return nil
}

func (cc *CompletionCache) Update(db *sqlite.Conn) error {
	for _, pa := range cc.pendingActions {
		switch pa.action {
		case sqlite.Attach:
			dbs, err := db.Databases()
			if err != nil {
				return err
			}
			for dbName, dbPath := range dbs {
				if dbPath == pa.dbName {
					if err := cc.cache(db, dbName); err != nil {
						return err
					}
				}
			}
		case sqlite.Detach:
			if err := cc.memDb.Exec("DELETE FROM col_names WHERE db_name = ?", pa.dbName); err != nil {
				return err
			}
		case sqlite.AlterTable:
			if err := cc.memDb.Exec("DELETE FROM col_names WHERE db_name = ? AND tbl_name = ?", pa.dbName, pa.tblName); err != nil {
				return err
			}
			fallthrough
		case sqlite.CreateTable, sqlite.CreateTempTable, sqlite.CreateView, sqlite.CreateTempView, sqlite.CreateVTable:
			if err := cc.cacheTable(db, pa.dbName, pa.tblName, pa.typ); err != nil {
				return err
			}
		case sqlite.DropTable, sqlite.DropTempTable, sqlite.DropView, sqlite.DropTempView, sqlite.DropVTable:
			if err := cc.memDb.Exec("DELETE FROM col_names WHERE db_name = ? AND tbl_name = ?", pa.dbName, pa.tblName); err != nil {
				return err
			}
		}
	}
	cc.pendingActions = cc.pendingActions[:0]
	return nil
}

func (cc *CompletionCache) Flush(db *sqlite.Conn) error {
	cc.pendingActions = cc.pendingActions[:0]
	return cc.memDb.FastExec("DELETE FROM col_names")
}

func (cc *CompletionCache) CompletePragma(prefix string) ([]string, error) {
	return cc.complete("pragma_names", prefix)
}
func (cc *CompletionCache) CompleteFunc(prefix string) ([]string, error) {
	return cc.complete("func_names", prefix)
}
func (cc *CompletionCache) CompleteCmd(prefix string) ([]string, error) {
	return cc.complete("cmd_names", prefix)
}

func (cc *CompletionCache) complete(tbl, prefix string) ([]string, error) {
	s, err := cc.memDb.Prepare("SELECT name FROM "+tbl+" WHERE name MATCH ?||'*' ORDER BY 1", prefix)
	if err != nil {
		return nil, err
	}
	defer s.Finalize()
	var names []string
	if err = s.Select(func(s *sqlite.Stmt) error {
		name, _ := s.ScanText(0)
		names = append(names, name)
		return nil
	}); err != nil {
		return nil, err
	}
	return names, nil
}

func (cc *CompletionCache) CompleteDbName(prefix string) ([]string, error) {
	s, err := cc.memDb.Prepare("SELECT DISTINCT db_name FROM col_names WHERE db_name MATCH ?||'*' ORDER BY 1", prefix)
	if err != nil {
		return nil, err
	}
	defer s.Finalize()
	var names []string
	if err = s.Select(func(s *sqlite.Stmt) error {
		name, _ := s.ScanText(0)
		names = append(names, name)
		return nil
	}); err != nil {
		return nil, err
	}
	return names, nil
}

func (cc *CompletionCache) CompleteTableName(dbName, prefix, typ string) ([]string, error) {
	args := make([]interface{}, 0, 3)
	if dbName != "" {
		args = append(args, dbName)
	}
	args = append(args, prefix)
	if typ != "" {
		args = append(args, typ)
	}
	var sql string
	if dbName == "" {
		if typ == "" {
			sql = "SELECT DISTINCT tbl_name FROM col_names WHERE tbl_name MATCH ?||'*' ORDER BY 1"
		} else {
			sql = "SELECT DISTINCT tbl_name FROM col_names WHERE tbl_name MATCH ?||'*' AND type = ? ORDER BY 1"
		}
	} else {
		if typ == "" {
			sql = "SELECT DISTINCT tbl_name FROM col_names WHERE db_name = ? AND tbl_name MATCH ?||'*' ORDER BY 1"
		} else {
			sql = "SELECT DISTINCT tbl_name FROM col_names WHERE db_name = ? AND tbl_name MATCH ?||'*' AND type = ? ORDER BY 1"
		}
	}
	s, err := cc.memDb.Prepare(sql, args...)
	if err != nil {
		return nil, err
	}
	defer s.Finalize()
	var names []string
	if err = s.Select(func(s *sqlite.Stmt) error {
		name, _ := s.ScanText(0)
		names = append(names, name)
		return nil
	}); err != nil {
		return nil, err
	}
	return names, nil
}

// tbl_names is mandatory
func (cc *CompletionCache) CompleteColName(dbName string, tbl_names []string, prefix string) ([]string, error) {
	args := make([]interface{}, 0, 10)
	if dbName != "" {
		args = append(args, dbName)
	}
	phs := make([]string, 0, 10)
	for _, tbl_name := range tbl_names {
		args = append(args, tbl_name)
		phs = append(phs, "?")
	}
	args = append(args, prefix)
	var sql string
	if dbName == "" {
		sql = "SELECT DISTINCT col_name FROM col_names WHERE tbl_name IN (" + strings.Join(phs, ",") + ") AND col_name MATCH ?||'*' ORDER BY 1"
	} else {
		sql = "SELECT DISTINCT col_name FROM col_names WHERE db_name = ? AND tbl_name IN (" + strings.Join(phs, ",") + ") AND col_name MATCH ?||'*' ORDER BY 1"
	}
	s, err := cc.memDb.Prepare(sql, args...)
	if err != nil {
		return nil, err
	}
	defer s.Finalize()
	var names []string
	if err = s.Select(func(s *sqlite.Stmt) error {
		name, _ := s.ScanText(0)
		names = append(names, name)
		return nil
	}); err != nil {
		return nil, err
	}
	return names, nil
}


<|start_filename|>intarray.c<|end_filename|>
/*
** 2009 November 10
**
** The author disclaims copyright to this source code.  In place of
** a legal notice, here is a blessing:
**
**    May you do good and not evil.
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
**
** This file implements a read-only VIRTUAL TABLE that contains the
** content of a C-language array of integer values.  See the corresponding
** header file for full details.
*/
#include <sqlite3.h>
#include <string.h>
#include <assert.h>

/*
** An sqlite3_intarray is an abstract type to stores an instance of
** an integer array.
*/
typedef struct sqlite3_intarray sqlite3_intarray;

/*
** Definition of the sqlite3_intarray object.
**
** The internal representation of an intarray object is subject
** to change, is not externally visible, and should be used by
** the implementation of intarray only.  This object is opaque
** to users.
*/
struct sqlite3_intarray {
  int n;                    /* Number of elements in the array */
  sqlite3_int64 *a;         /* Contents of the array */
  void (*xFree)(void*);     /* Function used to free a[] */
};

/* Objects used internally by the virtual table implementation */
typedef struct intarray_vtab intarray_vtab;
typedef struct intarray_cursor intarray_cursor;

/* A intarray table object */
struct intarray_vtab {
  sqlite3_vtab base;            /* Base class */
  sqlite3_intarray *pContent;   /* Content of the integer array */
};

/* A intarray cursor object */
struct intarray_cursor {
  sqlite3_vtab_cursor base;    /* Base class */
  int i;                       /* Current cursor position */
};

/*
** Free an sqlite3_intarray object.
*/
static void intarrayFree(sqlite3_intarray *p){
  if( p->xFree ){
    p->xFree(p->a);
  }
  sqlite3_free(p);
}

/*
** Table destructor for the intarray module.
*/
static int intarrayDestroy(sqlite3_vtab *p){
  intarray_vtab *pVtab = (intarray_vtab*)p;
  sqlite3_free(pVtab);
  return 0;
}

/*
** Table constructor for the intarray module.
*/
static int intarrayCreate(
  sqlite3 *db,              /* Database where module is created */
  void *pAux,               /* clientdata for the module */
  int argc,                 /* Number of arguments */
  const char *const*argv,   /* Value for all arguments */
  sqlite3_vtab **ppVtab,    /* Write the new virtual table object here */
  char **pzErr              /* Put error message text here */
){
  int rc = SQLITE_NOMEM;
  intarray_vtab *pVtab = sqlite3_malloc(sizeof(intarray_vtab));

  if( pVtab ){
    memset(pVtab, 0, sizeof(intarray_vtab));
    pVtab->pContent = (sqlite3_intarray*)pAux;
    rc = sqlite3_declare_vtab(db, "CREATE TABLE x(value INTEGER PRIMARY KEY)");
  }
  *ppVtab = (sqlite3_vtab *)pVtab;
  return rc;
}

/*
** Open a new cursor on the intarray table.
*/
static int intarrayOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
  int rc = SQLITE_NOMEM;
  intarray_cursor *pCur;
  pCur = sqlite3_malloc(sizeof(intarray_cursor));
  if( pCur ){
    memset(pCur, 0, sizeof(intarray_cursor));
    *ppCursor = (sqlite3_vtab_cursor *)pCur;
    rc = SQLITE_OK;
  }
  return rc;
}

/*
** Close a intarray table cursor.
*/
static int intarrayClose(sqlite3_vtab_cursor *cur){
  intarray_cursor *pCur = (intarray_cursor *)cur;
  sqlite3_free(pCur);
  return SQLITE_OK;
}

/*
** Retrieve a column of data.
*/
static int intarrayColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
  intarray_cursor *pCur = (intarray_cursor*)cur;
  intarray_vtab *pVtab = (intarray_vtab*)cur->pVtab;
  if( pCur->i>=0 && pCur->i<pVtab->pContent->n ){
    sqlite3_result_int64(ctx, pVtab->pContent->a[pCur->i]);
  }
  return SQLITE_OK;
}

/*
** Retrieve the current rowid.
*/
static int intarrayRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
  intarray_cursor *pCur = (intarray_cursor *)cur;
  *pRowid = pCur->i;
  return SQLITE_OK;
}

static int intarrayEof(sqlite3_vtab_cursor *cur){
  intarray_cursor *pCur = (intarray_cursor *)cur;
  intarray_vtab *pVtab = (intarray_vtab *)cur->pVtab;
  return pCur->i>=pVtab->pContent->n;
}

/*
** Advance the cursor to the next row.
*/
static int intarrayNext(sqlite3_vtab_cursor *cur){
  intarray_cursor *pCur = (intarray_cursor *)cur;
  pCur->i++;
  return SQLITE_OK;
}

/*
** Reset a intarray table cursor.
*/
static int intarrayFilter(
  sqlite3_vtab_cursor *pVtabCursor, 
  int idxNum, const char *idxStr,
  int argc, sqlite3_value **argv
){
  intarray_cursor *pCur = (intarray_cursor *)pVtabCursor;
  pCur->i = 0;
  return SQLITE_OK;
}

/*
** Analyse the WHERE condition.
*/
static int intarrayBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
  return SQLITE_OK;
}

/*
** A virtual table module that merely echos method calls into TCL
** variables.
*/
static sqlite3_module intarrayModule = {
  0,                           /* iVersion */
  intarrayCreate,              /* xCreate - create a new virtual table */
  intarrayCreate,              /* xConnect - connect to an existing vtab */
  intarrayBestIndex,           /* xBestIndex - find the best query index */
  intarrayDestroy,             /* xDisconnect - disconnect a vtab */
  intarrayDestroy,             /* xDestroy - destroy a vtab */
  intarrayOpen,                /* xOpen - open a cursor */
  intarrayClose,               /* xClose - close a cursor */
  intarrayFilter,              /* xFilter - configure scan constraints */
  intarrayNext,                /* xNext - advance a cursor */
  intarrayEof,                 /* xEof */
  intarrayColumn,              /* xColumn - read data */
  intarrayRowid,               /* xRowid - read data */
  0,                           /* xUpdate */
  0,                           /* xBegin */
  0,                           /* xSync */
  0,                           /* xCommit */
  0,                           /* xRollback */
  0,                           /* xFindMethod */
  0,                           /* xRename */
};

/*
** Invoke this routine to create a specific instance of an intarray object.
** The new intarray object is returned by the 3rd parameter.
**
** Each intarray object corresponds to a virtual table in the TEMP table
** with a name of zName.
**
** Destroy the intarray object by dropping the virtual table.  If not done
** explicitly by the application, the virtual table will be dropped implicitly
** by the system when the database connection is closed.
*/
int sqlite3_intarray_create(
  sqlite3 *db,
  const char *zName,
  sqlite3_intarray **ppReturn
){
  int rc = SQLITE_OK;
  sqlite3_intarray *p;

  *ppReturn = p = sqlite3_malloc( sizeof(*p) );
  if( p==0 ){
    return SQLITE_NOMEM;
  }
  memset(p, 0, sizeof(*p));
  rc = sqlite3_create_module_v2(db, zName, &intarrayModule, p,
                                (void(*)(void*))intarrayFree);
  if( rc==SQLITE_OK ){
    char *zSql;
    zSql = sqlite3_mprintf("CREATE VIRTUAL TABLE temp.%Q USING %Q",
                           zName, zName);
    rc = sqlite3_exec(db, zSql, 0, 0, 0);
    sqlite3_free(zSql);
  }
  return rc;
}

/*
** Bind a new array array of integers to a specific intarray object.
**
** The array of integers bound must be unchanged for the duration of
** any query against the corresponding virtual table.  If the integer
** array does change or is deallocated undefined behavior will result.
*/
int sqlite3_intarray_bind(
  sqlite3_intarray *pIntArray,   /* The intarray object to bind to */
  int nElements,                 /* Number of elements in the intarray */
  sqlite3_int64 *aElements,      /* Content of the intarray */
  void (*xFree)(void*)           /* How to dispose of the intarray when done */
){
  if( pIntArray->xFree ){
    pIntArray->xFree(pIntArray->a);
  }
  pIntArray->n = nElements;
  pIntArray->a = aElements;
  pIntArray->xFree = xFree;
  return SQLITE_OK;
}


<|start_filename|>shell/shell.go<|end_filename|>
// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build ignore

package main

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"os"
	"os/signal"
	"os/user"
	"path"
	"strings"
	"syscall"
	"text/tabwriter"
	"unicode"

	"github.com/gwenn/gosqlite"
	"github.com/gwenn/gosqlite/shell"
	"github.com/gwenn/liner"
)

func check(err error) {
	if err != nil {
		panic(err)
	}
}
func trace(err error) bool {
	if err != nil {
		fmt.Fprintln(os.Stderr, err.Error())
	}
	return err != nil
}

const (
	mainPrompt      = "sqlite> "
	continuePrompt  = "   ...> "
	historyFileName = ".gosqlite_history"
)

func loadHistory(state *liner.State, historyFileName string) error {
	//liner.HistoryLimit = 100 // to limit memory usage
	user, err := user.Current()
	if err != nil {
		return err
	}
	path := path.Join(user.HomeDir, historyFileName)
	f, err := os.Open(path)
	if err != nil {
		if os.IsNotExist(err) {
			return nil
		}
		return err
	}
	defer f.Close()

	r := bufio.NewReader(f)
	_, err = state.ReadHistory(r)
	return err
}

func isBlank(line string) bool {
	for _, r := range line {
		if unicode.IsSpace(r) {
			continue
		}
		return false
	}
	return true
}
func isCommand(line string) bool {
	return len(line) > 0 && line[0] == '.'
}

func appendHistory(state *liner.State, item string) {
	state.AppendHistory(item) // ignore consecutive dups, blank line, space
}

func saveHistory(state *liner.State, historyFileName string) error {
	user, err := user.Current()
	if err != nil {
		return err
	}
	path := path.Join(user.HomeDir, historyFileName)
	// append for multi-sessions handling
	f, err := os.OpenFile(path, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0600) // os.O_TRUNC
	if err != nil {
		return err
	}
	defer f.Close()

	w := bufio.NewWriter(f)
	_, err = state.WriteHistory(w)
	if err != nil {
		return err
	}
	err = w.Flush()
	return err
}

// Ctl-C
func catchInterrupt() {
	ch := make(chan os.Signal)
	go func() {
		for range ch {
			/*db.Interrupt()
			if !interactive {
				os.Exit(0)
			}*/
			fmt.Fprintln(os.Stderr, "^C")
		}
	}()
	signal.Notify(ch, syscall.SIGINT)
}

func completion(cc *shell.CompletionCache, line string, pos int) (string, []string, string) {
	if isBlank(line) {
		return line[:pos], nil, line[pos:]
	}
	prefix := line[:pos]
	var err error
	var matches []string
	if isCommand(line) {
		i := strings.LastIndex(prefix, " ")
		if i == -1 {
			matches, err = cc.CompleteCmd(prefix)
			check(err)
			if len(matches) > 0 {
				prefix = ""
			}
		}
	} else {
		fields := strings.Fields(prefix)
		if strings.EqualFold("PRAGMA", fields[0]) { // TODO check pos
			matches, err = cc.CompletePragma(fields[1])
			check(err)
		}
	}
	return prefix, matches, line[pos:]
}

func main() {
	var err error
	check(err)
	if !liner.IsTerminal() {
		return // TODO non-interactive mode
	}
	state, err := liner.NewLiner()
	check(err)
	defer func() {
		err := saveHistory(state, historyFileName)
		if err != nil {
			fmt.Fprintln(os.Stderr, err.Error())
		}
		state.Close()
	}()
	completionCache, err := shell.CreateCache()
	check(err)
	defer completionCache.Close()
	state.SetWordCompleter(func(line string, pos int) (string, []string, string) {
		return completion(completionCache, line, pos)
	})
	err = loadHistory(state, historyFileName)
	check(err)

	dbFilename := ":memory:"
	if len(os.Args) > 1 {
		dbFilename = os.Args[1]
	}
	db, err := sqlite.Open(dbFilename) // TODO command-line flag
	check(err)
	defer db.Close()

	catchInterrupt()

	err = completionCache.Cache(db)
	check(err)

	// TODO .mode MODE ?TABLE?     Set output mode where MODE is one of:
	// TODO .separator STRING      Change separator used by output mode and .import
	tw := tabwriter.NewWriter(os.Stdout, 0, 8, 0, '\t', 0)
	prompt := mainPrompt
	var b bytes.Buffer
	for {
		line, err := state.Prompt(prompt)
		if err != nil {
			if err != io.EOF {
				fmt.Fprintf(os.Stderr, "%#v\n", err)
			}
			println() /* End of input */
			break
		}

		if isBlank(line) {
			continue
		} else if isCommand(line) {
			fmt.Println("TBD")
			continue
		}

		b.WriteString(line)
		cmd := b.String()
		if !sqlite.Complete(cmd) {
			b.WriteByte(' ') // TODO Validate ' ' versus '\n'
			prompt = continuePrompt
			continue
		}
		prompt = mainPrompt
		// TODO .echo ON|OFF           Turn command echo on or off
		//fmt.Println(cmd)
		appendHistory(state, cmd)
		for len(cmd) > 0 {
			s, err := db.Prepare(cmd)
			if trace(err) {
				break // TODO bail_on_error
			} else if s.Empty() {
				cmd = s.Tail()
				continue
			}
			columnCount := s.ColumnCount()
			if columnCount > 0 {
				// FIXME headers are displayed only if DataCount() > 0
				headers := s.ColumnNames() // TODO .header(s) ON|OFF      Turn display of headers on or off
				for _, header := range headers {
					io.WriteString(tw, header)
					io.WriteString(tw, "\t")
				}
				io.WriteString(tw, "\n")
				err = s.Select(func(s *sqlite.Stmt) error {
					for i := 0; i < columnCount; i++ {
						blob, _ := s.ScanRawBytes(i)
						// TODO .nullvalue STRING      Use STRING in place of NULL values
						tw.Write(blob)
						io.WriteString(tw, "\t") // https://github.com/kr/text
					}
					io.WriteString(tw, "\n")
					return nil
				})
				tw.Flush()
			} else {
				err = s.Exec()
			}
			if trace(err) {
				s.Finalize()
				break // TODO bail_on_error
			}
			if trace(s.Finalize()) {
				break // TODO bail_on_error
			}
			cmd = s.Tail()
		} // exec
		b.Reset()
		completionCache.Update(db)
	}
}

/*
.backup ?DB? FILE      Backup DB (default "main") to FILE => NewBackup(dst, "main", db, ?DB?)
.bail ON|OFF           Stop after hitting an error.  Default OFF
.clone NEWDB           Clone data into NEWDB from the existing database => ???
.databases             List names and files of attached databases => db.Databases
.dump ?TABLE? ...      Dump the database in an SQL text format => ???
.echo ON|OFF           Turn command echo on or off
.exit                  Exit this program => *
.explain ?ON|OFF?      Turn output mode suitable for EXPLAIN on or off.
.header(s) ON|OFF      Turn display of headers on or off => *
.help                  Show this message => *
.import FILE TABLE     Import data from FILE into TABLE => ImportCSV(FILE, ImportConfig, ???, TABLE) (TABLE may be qualified)
.indices ?TABLE?       Show names of all indices => db.Indexes("main", both) + filter
.load FILE ?ENTRY?     Load an extension library => db.LoadExtension(FILE, ?ENTRY?)
.log FILE|off          Turn logging on or off.  FILE can be stderr/stdout
.mode MODE ?TABLE?     Set output mode
.nullvalue STRING      Use STRING in place of NULL values
.open ?FILENAME?       Close existing database and reopen FILENAME
.output FILENAME       Send output to FILENAME => *
.output stdout         Send output to the screen => *
.print STRING...       Print literal STRING
.prompt MAIN CONTINUE  Replace the standard prompts
.quit                  Exit this program => *
.read FILENAME         Execute SQL in FILENAME => *
.restore ?DB? FILE     Restore content of DB (default "main") from FILE => NewBackup(db, ?DB?, src, "main")
.save FILE             Write in-memory database into FILE => NewBackup(dst, "main", db, "main")
.schema ?TABLE?        Show the CREATE statements => *
.separator STRING      Change separator used by output mode and .import => *
.show                  Show the current values for various settings
.stats ON|OFF          Turn stats on or off
.tables ?TABLE?        List names of tables => *
.timeout MS            Try opening locked tables for MS milliseconds
.trace FILE|off        Output each SQL statement as it is run => db.Trace(???, nil)
.vfsname ?AUX?         Print the name of the VFS stack
.width NUM1 NUM2 ...   Set column widths for "column" mode
.timer ON|OFF          Turn the CPU timer measurement on or off
*/
