Adding upstream version 1.37.0.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
42613ad5c6
commit
271b368104
1329 changed files with 4727104 additions and 0 deletions
30
AUTHORS
Normal file
30
AUTHORS
Normal file
|
@ -0,0 +1,30 @@
|
|||
# This file lists authors for copyright purposes. This file is distinct from
|
||||
# the CONTRIBUTORS files. See the latter for an explanation.
|
||||
#
|
||||
# Names should be added to this file as:
|
||||
# Name or Organization <email address>
|
||||
#
|
||||
# The email address is not required for organizations.
|
||||
#
|
||||
# Please keep the list sorted.
|
||||
|
||||
Angus Dippenaar <angusdippenaar@gmail.com>
|
||||
Artyom Pervukhin <github@artyom.dev>
|
||||
Dan Kortschak <dan@kortschak.io>
|
||||
Dan Peterson <danp@danp.net>
|
||||
David Walton <david@davidwalton.com>
|
||||
Davsk Ltd Co <skinner.david@gmail.com>
|
||||
FerretDB Inc.
|
||||
Jaap Aarts <jaap.aarts1@gmail.com>
|
||||
Jan Mercl <0xjnml@gmail.com>
|
||||
Josh Bleecher Snyder <josharian@gmail.com>
|
||||
Josh Klein <josh.klein@outlook.com>
|
||||
Logan Snow <logansnow@protonmail.com>
|
||||
Michael Hoffmann <mhoffm@posteo.de>
|
||||
Michael Rykov <mrykov@gmail.com>
|
||||
Morgan Bazalgette <morgan@howl.moe>
|
||||
Ross Light <ross@zombiezen.com>
|
||||
Saed SayedAhmed <saadmtsa@gmail.com>
|
||||
Steffen Butzer <steffen(dot)butzer@outlook.com>
|
||||
Toni Spets <toni.spets@beeper.com>
|
||||
W. Michael Petullo <mike@flyn.org>
|
40
CONTRIBUTORS
Normal file
40
CONTRIBUTORS
Normal file
|
@ -0,0 +1,40 @@
|
|||
# This file lists people who contributed code to this repository. The AUTHORS
|
||||
# file lists the copyright holders; this file lists people.
|
||||
#
|
||||
# Names should be added to this file like so:
|
||||
# Name <email address>
|
||||
#
|
||||
# Please keep the list sorted.
|
||||
|
||||
Alexander Menzhinsky <amenzhinsky@gmail.com>
|
||||
Alexey Palazhchenko <alexey.palazhchenko@gmail.com>
|
||||
Angus Dippenaar <angusdippenaar@gmail.com>
|
||||
Artyom Pervukhin <github@artyom.dev>
|
||||
Dan Kortschak <dan@kortschak.io>
|
||||
Dan Peterson <danp@danp.net>
|
||||
David Skinner <skinner.david@gmail.com>
|
||||
David Walton <david@davidwalton.com>
|
||||
Elle Mouton <elle.mouton@gmail.com>
|
||||
FlyingOnion <731677080@qq.com>
|
||||
Gleb Sakhnov <gleb.sakhnov@gmail.com>
|
||||
Jaap Aarts <jaap.aarts1@gmail.com>
|
||||
Jan Mercl <0xjnml@gmail.com>
|
||||
Josh Bleecher Snyder <josharian@gmail.com>
|
||||
Josh Klein <josh.klein@outlook.com>
|
||||
Kim <grufwub@gmail.com>
|
||||
Logan Snow <logansnow@protonmail.com>
|
||||
Mario Salgado <mariozalgo@gmail.com>
|
||||
Mark Summerfield <mark@qtrac.eu>
|
||||
Matthew Gabeler-Lee <fastcat@gmail.com>
|
||||
Michael Hoffmann <mhoffm@posteo.de>
|
||||
Michael Rykov <mrykov@gmail.com>
|
||||
Morgan Bazalgette <morgan@howl.moe>
|
||||
Romain Le Disez <r.gitlab@ledisez.net>
|
||||
Ross Light <ross@zombiezen.com>
|
||||
Saed SayedAhmed <saadmtsa@gmail.com>
|
||||
Sean McGivern <sean@mcgivern.me.uk>
|
||||
Steffen Butzer <steffen(dot)butzer@outlook.com>
|
||||
Toni Spets <toni.spets@beeper.com>
|
||||
W. Michael Petullo <mike@flyn.org>
|
||||
Yaacov Akiba Slama <ya@slamail.org>
|
||||
Prathyush PV <prathyush.pv@temporal.io>
|
6
HACKING.md
Normal file
6
HACKING.md
Normal file
|
@ -0,0 +1,6 @@
|
|||
Please do not tag commits unless all builders at
|
||||
https://modern-c.appspot.com/-/builder/?importpath=modernc.org%2fsqlite are
|
||||
happy.
|
||||
|
||||
Since 2024-03-12 it should be no more necessary to manually tag releases at
|
||||
all as the repo is now set to be tagged automatically.
|
26
LICENSE
Normal file
26
LICENSE
Normal file
|
@ -0,0 +1,26 @@
|
|||
Copyright (c) 2017 The Sqlite Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
87
Makefile
Normal file
87
Makefile
Normal file
|
@ -0,0 +1,87 @@
|
|||
# Copyright 2024 The Sqlite Authors. All rights reserved.
|
||||
# Use of this source code is governed by a BSD-style
|
||||
# license that can be found in the LICENSE file.
|
||||
|
||||
.PHONY: all build_all_targets clean edit editor test vendor work
|
||||
|
||||
all: editor
|
||||
golint 2>&1
|
||||
staticcheck 2>&1
|
||||
|
||||
build_all_targets:
|
||||
GOOS=darwin GOARCH=amd64 go test -c -o /dev/null
|
||||
GOOS=darwin GOARCH=amd64 go build -v ./...
|
||||
GOOS=darwin GOARCH=arm64 go test -c -o /dev/null
|
||||
GOOS=darwin GOARCH=arm64 go build -v ./...
|
||||
GOOS=freebsd GOARCH=amd64 go test -c -o /dev/null
|
||||
GOOS=freebsd GOARCH=amd64 go build -v ./...
|
||||
GOOS=freebsd GOARCH=386 go test -c -o /dev/null
|
||||
GOOS=freebsd GOARCH=386 go build -v ./...
|
||||
GOOS=freebsd GOARCH=arm go test -c -o /dev/null
|
||||
GOOS=freebsd GOARCH=arm go build -v ./...
|
||||
GOOS=freebsd GOARCH=arm64 go test -c -o /dev/null
|
||||
GOOS=freebsd GOARCH=arm64 go build -v ./...
|
||||
GOOS=linux GOARCH=386 go test -c -o /dev/null
|
||||
GOOS=linux GOARCH=386 go build -v ./...
|
||||
GOOS=linux GOARCH=amd64 go test -c -o /dev/null
|
||||
GOOS=linux GOARCH=amd64 go build -v ./...
|
||||
GOOS=linux GOARCH=arm go test -c -o /dev/null
|
||||
GOOS=linux GOARCH=arm go build -v ./...
|
||||
GOOS=linux GOARCH=arm64 go test -c -o /dev/null
|
||||
GOOS=linux GOARCH=arm64 go build -v ./...
|
||||
GOOS=linux GOARCH=loong64 go test -c -o /dev/null
|
||||
GOOS=linux GOARCH=loong64 go build -v ./...
|
||||
GOOS=linux GOARCH=ppc64le go test -c -o /dev/null
|
||||
GOOS=linux GOARCH=ppc64le go build -v ./...
|
||||
GOOS=linux GOARCH=riscv64 go test -c -o /dev/null
|
||||
GOOS=linux GOARCH=riscv64 go build -v ./...
|
||||
GOOS=linux GOARCH=s390x go test -c -o /dev/null
|
||||
GOOS=linux GOARCH=s390x go build -v ./...
|
||||
# GOOS=netbsd GOARCH=amd64 go test -c -o /dev/null
|
||||
# GOOS=netbsd GOARCH=amd64 go build -v ./...
|
||||
GOOS=openbsd GOARCH=amd64 go test -c -o /dev/null
|
||||
GOOS=openbsd GOARCH=amd64 go build -v ./...
|
||||
GOOS=openbsd GOARCH=arm64 go test -c -o /dev/null
|
||||
GOOS=openbsd GOARCH=arm64 go build -v ./...
|
||||
GOOS=windows GOARCH=386 go test -c -o /dev/null
|
||||
GOOS=windows GOARCH=386 go build -v ./...
|
||||
GOOS=windows GOARCH=amd64 go test -c -o /dev/null
|
||||
GOOS=windows GOARCH=amd64 go build -v ./...
|
||||
GOOS=windows GOARCH=arm64 go test -c -o /dev/null
|
||||
GOOS=windows GOARCH=arm64 go build -v ./...
|
||||
echo done
|
||||
|
||||
clean:
|
||||
rm -f log-* cpu.test mem.test *.out go.work*
|
||||
go clean
|
||||
|
||||
edit:
|
||||
@if [ -f "Session.vim" ]; then gvim -S & else gvim -p Makefile go.mod builder.json all_test.go & fi
|
||||
|
||||
editor:
|
||||
gofmt -l -s -w .
|
||||
go test -c -o /dev/null
|
||||
go build -v -o /dev/null ./...
|
||||
cd vendor_libsqlite3 && go build -o /dev/null main.go
|
||||
|
||||
test:
|
||||
go test -v -timeout 24h
|
||||
|
||||
vendor:
|
||||
cd vendor_libsqlite3 && go build -o ../vendor main.go
|
||||
./vendor
|
||||
rm -f vendor
|
||||
make build_all_targets
|
||||
make build_all_targets
|
||||
|
||||
work:
|
||||
rm -f go.work*
|
||||
go work init
|
||||
go work use .
|
||||
go work use ../cc/v4
|
||||
go work use ../ccgo/v3
|
||||
go work use ../ccgo/v4
|
||||
go work use ../libc
|
||||
go work use ../libtcl8.6
|
||||
go work use ../libsqlite3
|
||||
go work use ../libz
|
7
README.md
Normal file
7
README.md
Normal file
|
@ -0,0 +1,7 @@
|
|||

|
||||
|
||||
[](https://pkg.go.dev/modernc.org/sqlite)
|
||||
|
||||
[](https://liberapay.com/jnml/donate)
|
||||
[](https://liberapay.com/jnml/donate)
|
||||
[](https://liberapay.com/jnml/donate)
|
25
SQLITE-LICENSE
Normal file
25
SQLITE-LICENSE
Normal file
|
@ -0,0 +1,25 @@
|
|||
SQLite Is Public Domain
|
||||
|
||||
All of the code and documentation in SQLite has been dedicated to the public
|
||||
domain by the authors. All code authors, and representatives of the companies
|
||||
they work for, have signed affidavits dedicating their contributions to the
|
||||
public domain and originals of those signed affidavits are stored in a firesafe
|
||||
at the main offices of Hwaci. Anyone is free to copy, modify, publish, use,
|
||||
compile, sell, or distribute the original SQLite code, either in source code
|
||||
form or as a compiled binary, for any purpose, commercial or non-commercial,
|
||||
and by any means.
|
||||
|
||||
The previous paragraph applies to the deliverable code and documentation in
|
||||
SQLite - those parts of the SQLite library that you actually bundle and ship
|
||||
with a larger application. Some scripts used as part of the build process (for
|
||||
example the "configure" scripts generated by autoconf) might fall under other
|
||||
open-source licenses. Nothing from these build scripts ever reaches the final
|
||||
deliverable SQLite library, however, and so the licenses associated with those
|
||||
scripts should not be a factor in assessing your rights to copy and use the
|
||||
SQLite library.
|
||||
|
||||
All of the deliverable code in SQLite has been written from scratch. No code
|
||||
has been taken from other projects or from the open internet. Every line of
|
||||
code can be traced back to its original author, and all of those authors have
|
||||
public domain dedications on file. So the SQLite code base is clean and is
|
||||
uncontaminated with licensed code from other projects.
|
64
addport.go
Normal file
64
addport.go
Normal file
|
@ -0,0 +1,64 @@
|
|||
// Copyright 2022 The Sqlite Authors. All rights reserved
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build ignore
|
||||
// +build ignore
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func fail(rc int, msg string, args ...interface{}) {
|
||||
fmt.Fprintf(os.Stderr, msg, args...)
|
||||
os.Exit(rc)
|
||||
}
|
||||
|
||||
func main() {
|
||||
if len(os.Args) != 3 {
|
||||
fail(1, "expected 2 args: pattern and replacement\n")
|
||||
}
|
||||
|
||||
pattern := os.Args[1]
|
||||
replacement := os.Args[2]
|
||||
if err := filepath.Walk(".", func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
dir, file := filepath.Split(path)
|
||||
if x := strings.Index(file, pattern); x >= 0 {
|
||||
// pattern freebsd
|
||||
// replacement netbsd
|
||||
// file libc_freebsd_amd64.go
|
||||
// replaced libc_netbsd_amd64.go
|
||||
// 01234567890123456789
|
||||
// 1
|
||||
// x 5
|
||||
file = file[:x] + replacement + file[x+len(pattern):]
|
||||
dst := filepath.Join(dir, file)
|
||||
b, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("reading %s: %v", path, err)
|
||||
}
|
||||
|
||||
if err := os.WriteFile(dst, b, 0640); err != nil {
|
||||
return fmt.Errorf("writing %s: %v", dst, err)
|
||||
}
|
||||
fmt.Printf("%s -> %s\n", path, dst)
|
||||
}
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
fail(1, "%s", err)
|
||||
}
|
||||
}
|
3309
all_test.go
Normal file
3309
all_test.go
Normal file
File diff suppressed because it is too large
Load diff
40
bind_blob.go
Normal file
40
bind_blob.go
Normal file
|
@ -0,0 +1,40 @@
|
|||
// Copyright 2024 The Sqlite Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !linux
|
||||
|
||||
package sqlite // import "modernc.org/sqlite"
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"modernc.org/libc"
|
||||
sqlite3 "modernc.org/sqlite/lib"
|
||||
)
|
||||
|
||||
// C documentation
|
||||
//
|
||||
// int sqlite3_bind_blob(sqlite3_stmt*, int, const void*, int n, void(*)(void*));
|
||||
func (c *conn) bindBlob(pstmt uintptr, idx1 int, value []byte) (uintptr, error) {
|
||||
if value != nil && len(value) == 0 {
|
||||
if rc := sqlite3.Xsqlite3_bind_zeroblob(c.tls, pstmt, int32(idx1), 0); rc != sqlite3.SQLITE_OK {
|
||||
return 0, c.errstr(rc)
|
||||
}
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
p, err := c.malloc(len(value))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if len(value) != 0 {
|
||||
copy((*libc.RawMem)(unsafe.Pointer(p))[:len(value):len(value)], value)
|
||||
}
|
||||
if rc := sqlite3.Xsqlite3_bind_blob(c.tls, pstmt, int32(idx1), p, int32(len(value)), 0); rc != sqlite3.SQLITE_OK {
|
||||
c.free(p)
|
||||
return 0, c.errstr(rc)
|
||||
}
|
||||
|
||||
return p, nil
|
||||
}
|
40
bind_blob_musl.go
Normal file
40
bind_blob_musl.go
Normal file
|
@ -0,0 +1,40 @@
|
|||
// Copyright 2024 The Sqlite Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build linux
|
||||
|
||||
package sqlite // import "modernc.org/sqlite"
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"modernc.org/libc"
|
||||
sqlite3 "modernc.org/sqlite/lib"
|
||||
)
|
||||
|
||||
// C documentation
|
||||
//
|
||||
// int sqlite3_bind_blob(sqlite3_stmt*, int, const void*, int n, void(*)(void*));
|
||||
func (c *conn) bindBlob(pstmt uintptr, idx1 int, value []byte) (uintptr, error) {
|
||||
if value == nil {
|
||||
if rc := sqlite3.Xsqlite3_bind_null(c.tls, pstmt, int32(idx1)); rc != sqlite3.SQLITE_OK {
|
||||
return 0, c.errstr(rc)
|
||||
}
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
p, err := c.malloc(len(value))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if len(value) != 0 {
|
||||
copy((*libc.RawMem)(unsafe.Pointer(p))[:len(value):len(value)], value)
|
||||
}
|
||||
if rc := sqlite3.Xsqlite3_bind_blob(c.tls, pstmt, int32(idx1), p, int32(len(value)), 0); rc != sqlite3.SQLITE_OK {
|
||||
c.free(p)
|
||||
return 0, c.errstr(rc)
|
||||
}
|
||||
|
||||
return p, nil
|
||||
}
|
6
builder.json
Normal file
6
builder.json
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"autogen": "<none>",
|
||||
"autotag": "<none>",
|
||||
"autoupdate": "<none>",
|
||||
"test": "darwin/(amd64|arm64)|freebsd/(amd64|arm64)|linux/(386|amd64|arm|arm64|loong64|ppc64le|riscv64|s390x)|windows/(amd64|arm64|386)"
|
||||
}
|
56
dmesg.go
Normal file
56
dmesg.go
Normal file
|
@ -0,0 +1,56 @@
|
|||
// Copyright 2023 The Sqlite Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build sqlite.dmesg
|
||||
// +build sqlite.dmesg
|
||||
|
||||
package sqlite // import "modernc.org/sqlite"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const dmesgs = true
|
||||
|
||||
var (
|
||||
pid = fmt.Sprintf("[%v %v] ", os.Getpid(), filepath.Base(os.Args[0]))
|
||||
logf *os.File
|
||||
)
|
||||
|
||||
func init() {
|
||||
t := time.Now()
|
||||
// 01/02 03:04:05PM '06 -0700
|
||||
dn := t.Format("sqlite-dmesg-2006-01-02-03-150405")
|
||||
dn = filepath.Join(os.TempDir(), fmt.Sprintf("%s.%d", dn, os.Getpid()))
|
||||
if err := os.Mkdir(dn, 0770); err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
|
||||
fn := filepath.Join(dn, "dmesg.log")
|
||||
var err error
|
||||
if logf, err = os.OpenFile(fn, os.O_APPEND|os.O_CREATE|os.O_WRONLY|os.O_SYNC, 0644); err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
|
||||
dmesg("%v", time.Now())
|
||||
fmt.Fprintf(os.Stderr, "debug messages in %s\n", fn)
|
||||
}
|
||||
|
||||
func dmesg(s string, args ...interface{}) {
|
||||
if s == "" {
|
||||
s = strings.Repeat("%v ", len(args))
|
||||
}
|
||||
s = fmt.Sprintf(pid+s, args...)
|
||||
s += fmt.Sprintf(" (%v: %v:)", origin(3), origin(2))
|
||||
switch {
|
||||
case len(s) != 0 && s[len(s)-1] == '\n':
|
||||
fmt.Fprint(logf, s)
|
||||
default:
|
||||
fmt.Fprintln(logf, s)
|
||||
}
|
||||
}
|
292
doc.go
Normal file
292
doc.go
Normal file
|
@ -0,0 +1,292 @@
|
|||
// Copyright 2017 The Sqlite 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 sqlite is a sql/database driver using a CGo-free port of the C
|
||||
// SQLite3 library.
|
||||
//
|
||||
// SQLite is an in-process implementation of a self-contained, serverless,
|
||||
// zero-configuration, transactional SQL database engine.
|
||||
//
|
||||
// # Fragile modernc.org/libc dependency
|
||||
//
|
||||
// When you import this package you should use in your go.mod file the exact
|
||||
// same version of modernc.org/libc as seen in the go.mod file of this
|
||||
// repository.
|
||||
//
|
||||
// See the discussion at https://gitlab.com/cznic/sqlite/-/issues/177 for more details.
|
||||
//
|
||||
// # Thanks
|
||||
//
|
||||
// This project is sponsored by Schleibinger Geräte Teubert u. Greim GmbH by
|
||||
// allowing one of the maintainers to work on it also in office hours.
|
||||
//
|
||||
// # Supported platforms and architectures
|
||||
//
|
||||
// These combinations of GOOS and GOARCH are currently supported
|
||||
//
|
||||
// OS Arch SQLite version
|
||||
// ------------------------------
|
||||
// darwin amd64 3.49.0
|
||||
// darwin arm64 3.49.0
|
||||
// freebsd amd64 3.49.0
|
||||
// freebsd arm64 3.49.0
|
||||
// linux 386 3.49.0
|
||||
// linux amd64 3.49.0
|
||||
// linux arm 3.49.0
|
||||
// linux arm64 3.49.0
|
||||
// linux loong64 3.49.0
|
||||
// linux ppc64le 3.49.0
|
||||
// linux riscv64 3.49.0
|
||||
// linux s390x 3.49.0
|
||||
// windows 386 3.49.0
|
||||
// windows amd64 3.49.0
|
||||
// windows arm64 3.49.0
|
||||
//
|
||||
// # Builders
|
||||
//
|
||||
// Builder results available at:
|
||||
//
|
||||
// https://modern-c.appspot.com/-/builder/?importpath=modernc.org%2fsqlite
|
||||
//
|
||||
// # Changelog
|
||||
//
|
||||
// - 2025-02-26 v1.36.0: Upgrade to SQLite 3.49.0.
|
||||
//
|
||||
// - 2024-11-16 v1.34.0: Implement ResetSession and IsValid methods in connection
|
||||
//
|
||||
// - 2024-07-22 v1.31.0: Support windows/386.
|
||||
//
|
||||
// - 2024-06-04 v1.30.0: Upgrade to SQLite 3.46.0, release notes at
|
||||
// https://sqlite.org/releaselog/3_46_0.html.
|
||||
//
|
||||
// - 2024-02-13 v1.29.0: Upgrade to SQLite 3.45.1, release notes at
|
||||
// https://sqlite.org/releaselog/3_45_1.html.
|
||||
//
|
||||
// - 2023-12-14: v1.28.0: Add (*Driver).RegisterConnectionHook,
|
||||
// ConnectionHookFn, ExecQuerierContext, RegisterConnectionHook.
|
||||
//
|
||||
// - 2023-08-03 v1.25.0: enable SQLITE_ENABLE_DBSTAT_VTAB.
|
||||
//
|
||||
// - 2023-07-11 v1.24.0: Add
|
||||
// (*conn).{Serialize,Deserialize,NewBackup,NewRestore} methods, add Backup
|
||||
// type.
|
||||
//
|
||||
// - 2023-06-01 v1.23.0: Allow registering aggregate functions.
|
||||
//
|
||||
// - 2023-04-22 v1.22.0: Support linux/s390x.
|
||||
//
|
||||
// - 2023-02-23 v1.21.0: Upgrade to SQLite 3.41.0, release notes at
|
||||
// https://sqlite.org/releaselog/3_41_0.html.
|
||||
//
|
||||
// - 2022-11-28 v1.20.0: Support linux/ppc64le.
|
||||
//
|
||||
// - 2022-09-16 v1.19.0: Support frebsd/arm64.
|
||||
//
|
||||
// - 2022-07-26 v1.18.0: Add support for Go fs.FS based SQLite virtual
|
||||
// filesystems, see function New in modernc.org/sqlite/vfs and/or TestVFS in
|
||||
// all_test.go
|
||||
//
|
||||
// - 2022-04-24 v1.17.0: Support windows/arm64.
|
||||
//
|
||||
// - 2022-04-04 v1.16.0: Support scalar application defined functions written
|
||||
// in Go. See https://www.sqlite.org/appfunc.html
|
||||
//
|
||||
// - 2022-03-13 v1.15.0: Support linux/riscv64.
|
||||
//
|
||||
// - 2021-11-13 v1.14.0: Support windows/amd64. This target had previously
|
||||
// only experimental status because of a now resolved memory leak.
|
||||
//
|
||||
// - 2021-09-07 v1.13.0: Support freebsd/amd64.
|
||||
//
|
||||
// - 2021-06-23 v1.11.0: Upgrade to use sqlite 3.36.0, release notes at
|
||||
// https://www.sqlite.org/releaselog/3_36_0.html.
|
||||
//
|
||||
// - 2021-05-06 v1.10.6: Fixes a memory corruption issue
|
||||
// (https://gitlab.com/cznic/sqlite/-/issues/53). Versions since v1.8.6 were
|
||||
// affected and should be updated to v1.10.6.
|
||||
//
|
||||
// - 2021-03-14 v1.10.0: Update to use sqlite 3.35.0, release notes at
|
||||
// https://www.sqlite.org/releaselog/3_35_0.html.
|
||||
//
|
||||
// - 2021-03-11 v1.9.0: Support darwin/arm64.
|
||||
//
|
||||
// - 2021-01-08 v1.8.0: Support darwin/amd64.
|
||||
//
|
||||
// - 2020-09-13 v1.7.0: Support linux/arm and linux/arm64.
|
||||
//
|
||||
// - 2020-09-08 v1.6.0: Support linux/386.
|
||||
//
|
||||
// - 2020-09-03 v1.5.0: This project is now completely CGo-free, including
|
||||
// the Tcl tests.
|
||||
//
|
||||
// - 2020-08-26 v1.4.0: First stable release for linux/amd64. The
|
||||
// database/sql driver and its tests are CGo free. Tests of the translated
|
||||
// sqlite3.c library still require CGo.
|
||||
//
|
||||
// - 2020-07-26 v1.4.0-beta1: The project has reached beta status while
|
||||
// supporting linux/amd64 only at the moment. The 'extraquick' Tcl testsuite
|
||||
// reports
|
||||
//
|
||||
// - 2019-12-28 v1.2.0-alpha.3: Third alpha fixes issue #19.
|
||||
//
|
||||
// - 2019-12-26 v1.1.0-alpha.2: Second alpha release adds support for
|
||||
// accessing a database concurrently by multiple goroutines and/or processes.
|
||||
// v1.1.0 is now considered feature-complete. Next planed release should be a
|
||||
// beta with a proper test suite.
|
||||
//
|
||||
// - 2019-12-18 v1.1.0-alpha.1: First alpha release using the new cc/v3,
|
||||
// gocc, qbe toolchain. Some primitive tests pass on linux_{amd64,386}. Not
|
||||
// yet safe for concurrent access by multiple goroutines. Next alpha release
|
||||
// is planed to arrive before the end of this year.
|
||||
//
|
||||
// - 2017-06-10: Windows/Intel no more uses the VM (thanks Steffen Butzer).
|
||||
//
|
||||
// - 2017-06-05 Linux/Intel no more uses the VM (cznic/virtual).
|
||||
//
|
||||
// # Connecting to a database
|
||||
//
|
||||
// To access a Sqlite database do something like
|
||||
//
|
||||
// import (
|
||||
// "database/sql"
|
||||
//
|
||||
// _ "modernc.org/sqlite"
|
||||
// )
|
||||
//
|
||||
// ...
|
||||
//
|
||||
//
|
||||
// db, err := sql.Open("sqlite", dsnURI)
|
||||
//
|
||||
// ...
|
||||
//
|
||||
// # Debug and development versions
|
||||
//
|
||||
// A comma separated list of options can be passed to `go generate` via the
|
||||
// environment variable GO_GENERATE. Some useful options include for example:
|
||||
//
|
||||
// -DSQLITE_DEBUG
|
||||
// -DSQLITE_MEM_DEBUG
|
||||
// -ccgo-verify-structs
|
||||
//
|
||||
// To create a debug/development version, issue for example:
|
||||
//
|
||||
// $ GO_GENERATE=-DSQLITE_DEBUG,-DSQLITE_MEM_DEBUG go generate
|
||||
//
|
||||
// Note: To run `go generate` you need to have modernc.org/ccgo/v3 installed.
|
||||
//
|
||||
// # Hacking
|
||||
//
|
||||
// This is an example of how to use the debug logs in modernc.org/libc when hunting a bug.
|
||||
//
|
||||
// 0:jnml@e5-1650:~/src/modernc.org/sqlite$ git status
|
||||
// On branch master
|
||||
// Your branch is up to date with 'origin/master'.
|
||||
//
|
||||
// nothing to commit, working tree clean
|
||||
// 0:jnml@e5-1650:~/src/modernc.org/sqlite$ git log -1
|
||||
// commit df33b8d15107f3cc777799c0fe105f74ef499e62 (HEAD -> master, tag: v1.21.1, origin/master, origin/HEAD, wips, ok)
|
||||
// Author: Jan Mercl <0xjnml@gmail.com>
|
||||
// Date: Mon Mar 27 16:18:28 2023 +0200
|
||||
//
|
||||
// upgrade to SQLite 3.41.2
|
||||
// 0:jnml@e5-1650:~/src/modernc.org/sqlite$ rm -f /tmp/libc.log ; go test -v -tags=libc.dmesg -run TestScalar ; ls -l /tmp/libc.log
|
||||
// test binary compiled for linux/amd64
|
||||
// === RUN TestScalar
|
||||
// --- PASS: TestScalar (0.09s)
|
||||
// PASS
|
||||
// ok modernc.org/sqlite 0.128s
|
||||
// -rw-r--r-- 1 jnml jnml 76 Apr 6 11:22 /tmp/libc.log
|
||||
// 0:jnml@e5-1650:~/src/modernc.org/sqlite$ cat /tmp/libc.log
|
||||
// [10723 sqlite.test] 2023-04-06 11:22:48.288066057 +0200 CEST m=+0.000707150
|
||||
// 0:jnml@e5-1650:~/src/modernc.org/sqlite$
|
||||
//
|
||||
// The /tmp/libc.log file is created as requested. No useful messages there because none are enabled in libc. Let's try to enable Xwrite as an example.
|
||||
//
|
||||
// 0:jnml@e5-1650:~/src/modernc.org/libc$ git status
|
||||
// On branch master
|
||||
// Your branch is up to date with 'origin/master'.
|
||||
//
|
||||
// Changes not staged for commit:
|
||||
// (use "git add <file>..." to update what will be committed)
|
||||
// (use "git restore <file>..." to discard changes in working directory)
|
||||
// modified: libc_linux.go
|
||||
//
|
||||
// no changes added to commit (use "git add" and/or "git commit -a")
|
||||
// 0:jnml@e5-1650:~/src/modernc.org/libc$ git log -1
|
||||
// commit 1e22c18cf2de8aa86d5b19b165f354f99c70479c (HEAD -> master, tag: v1.22.3, origin/master, origin/HEAD)
|
||||
// Author: Jan Mercl <0xjnml@gmail.com>
|
||||
// Date: Wed Feb 22 20:27:45 2023 +0100
|
||||
//
|
||||
// support sqlite 3.41 on linux targets
|
||||
// 0:jnml@e5-1650:~/src/modernc.org/libc$ git diff
|
||||
// diff --git a/libc_linux.go b/libc_linux.go
|
||||
// index 1c2f482..ac1f08d 100644
|
||||
// --- a/libc_linux.go
|
||||
// +++ b/libc_linux.go
|
||||
// @@ -332,19 +332,19 @@ func Xwrite(t *TLS, fd int32, buf uintptr, count types.Size_t) types.Ssize_t {
|
||||
// var n uintptr
|
||||
// switch n, _, err = unix.Syscall(unix.SYS_WRITE, uintptr(fd), buf, uintptr(count)); err {
|
||||
// case 0:
|
||||
// - // if dmesgs {
|
||||
// - // // dmesg("%v: %d %#x: %#x\n%s", origin(1), fd, count, n, hex.Dump(GoBytes(buf, int(n))))
|
||||
// - // dmesg("%v: %d %#x: %#x", origin(1), fd, count, n)
|
||||
// - // }
|
||||
// + if dmesgs {
|
||||
// + // dmesg("%v: %d %#x: %#x\n%s", origin(1), fd, count, n, hex.Dump(GoBytes(buf, int(n))))
|
||||
// + dmesg("%v: %d %#x: %#x", origin(1), fd, count, n)
|
||||
// + }
|
||||
// return types.Ssize_t(n)
|
||||
// case errno.EAGAIN:
|
||||
// // nop
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// - // if dmesgs {
|
||||
// - // dmesg("%v: fd %v, count %#x: %v", origin(1), fd, count, err)
|
||||
// - // }
|
||||
// + if dmesgs {
|
||||
// + dmesg("%v: fd %v, count %#x: %v", origin(1), fd, count, err)
|
||||
// + }
|
||||
// t.setErrno(err)
|
||||
// return -1
|
||||
// }
|
||||
// 0:jnml@e5-1650:~/src/modernc.org/libc$
|
||||
//
|
||||
// We need to tell the Go build system to use our local, patched/debug libc:
|
||||
//
|
||||
// 0:jnml@e5-1650:~/src/modernc.org/sqlite$ go work use $(go env GOPATH)/src/modernc.org/libc
|
||||
// 0:jnml@e5-1650:~/src/modernc.org/sqlite$ go work use .
|
||||
//
|
||||
// And run the test again:
|
||||
//
|
||||
// 0:jnml@e5-1650:~/src/modernc.org/sqlite$ rm -f /tmp/libc.log ; go test -v -tags=libc.dmesg -run TestScalar ; ls -l /tmp/libc.log
|
||||
// test binary compiled for linux/amd64
|
||||
// === RUN TestScalar
|
||||
// --- PASS: TestScalar (0.26s)
|
||||
// PASS
|
||||
// ok modernc.org/sqlite 0.285s
|
||||
// -rw-r--r-- 1 jnml jnml 918 Apr 6 11:29 /tmp/libc.log
|
||||
// 0:jnml@e5-1650:~/src/modernc.org/sqlite$ cat /tmp/libc.log
|
||||
// [11910 sqlite.test] 2023-04-06 11:29:13.143589542 +0200 CEST m=+0.000689270
|
||||
// [11910 sqlite.test] libc_linux.go:337:Xwrite: 8 0x200: 0x200
|
||||
// [11910 sqlite.test] libc_linux.go:337:Xwrite: 8 0xc: 0xc
|
||||
// [11910 sqlite.test] libc_linux.go:337:Xwrite: 7 0x1000: 0x1000
|
||||
// [11910 sqlite.test] libc_linux.go:337:Xwrite: 7 0x1000: 0x1000
|
||||
// [11910 sqlite.test] libc_linux.go:337:Xwrite: 8 0x200: 0x200
|
||||
// [11910 sqlite.test] libc_linux.go:337:Xwrite: 8 0x4: 0x4
|
||||
// [11910 sqlite.test] libc_linux.go:337:Xwrite: 8 0x1000: 0x1000
|
||||
// [11910 sqlite.test] libc_linux.go:337:Xwrite: 8 0x4: 0x4
|
||||
// [11910 sqlite.test] libc_linux.go:337:Xwrite: 8 0x4: 0x4
|
||||
// [11910 sqlite.test] libc_linux.go:337:Xwrite: 8 0x1000: 0x1000
|
||||
// [11910 sqlite.test] libc_linux.go:337:Xwrite: 8 0x4: 0x4
|
||||
// [11910 sqlite.test] libc_linux.go:337:Xwrite: 8 0xc: 0xc
|
||||
// [11910 sqlite.test] libc_linux.go:337:Xwrite: 7 0x1000: 0x1000
|
||||
// [11910 sqlite.test] libc_linux.go:337:Xwrite: 7 0x1000: 0x1000
|
||||
// 0:jnml@e5-1650:~/src/modernc.org/sqlite$
|
||||
//
|
||||
// # Sqlite documentation
|
||||
//
|
||||
// See https://sqlite.org/docs.html
|
||||
package sqlite // import "modernc.org/sqlite"
|
BIN
embed.db
Normal file
BIN
embed.db
Normal file
Binary file not shown.
BIN
embed2.db
Normal file
BIN
embed2.db
Normal file
Binary file not shown.
71
examples/example1/main.go
Normal file
71
examples/example1/main.go
Normal file
|
@ -0,0 +1,71 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
_ "modernc.org/sqlite"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if err := main1(); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func main1() error {
|
||||
dir, err := os.MkdirTemp("", "test-")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
fn := filepath.Join(dir, "db")
|
||||
|
||||
db, err := sql.Open("sqlite", fn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err = db.Exec(`
|
||||
drop table if exists t;
|
||||
create table t(i);
|
||||
insert into t values(42), (314);
|
||||
`); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rows, err := db.Query("select 3*i from t order by i;")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for rows.Next() {
|
||||
var i int
|
||||
if err = rows.Scan(&i); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println(i)
|
||||
}
|
||||
|
||||
if err = rows.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = db.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fi, err := os.Stat(fn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("%s size: %v\n", fn, fi.Size())
|
||||
return nil
|
||||
}
|
47
fcntl.go
Normal file
47
fcntl.go
Normal file
|
@ -0,0 +1,47 @@
|
|||
// Copyright 2024 The Sqlite 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 sqlite // import "modernc.org/sqlite"
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"unsafe"
|
||||
|
||||
"modernc.org/libc"
|
||||
sqlite3 "modernc.org/sqlite/lib"
|
||||
)
|
||||
|
||||
// Access to sqlite3_file_control
|
||||
type FileControl interface {
|
||||
// Set or query SQLITE_FCNTL_PERSIST_WAL, returns set mode or query result
|
||||
FileControlPersistWAL(dbName string, mode int) (int, error)
|
||||
}
|
||||
|
||||
var _ FileControl = (*conn)(nil)
|
||||
|
||||
func (c *conn) FileControlPersistWAL(dbName string, mode int) (int, error) {
|
||||
i32 := int32(mode)
|
||||
pi32 := &i32
|
||||
|
||||
var p runtime.Pinner
|
||||
p.Pin(pi32)
|
||||
defer p.Unpin()
|
||||
|
||||
err := c.fileControl(dbName, sqlite3.SQLITE_FCNTL_PERSIST_WAL, (uintptr)(unsafe.Pointer(pi32)))
|
||||
return int(i32), err
|
||||
}
|
||||
|
||||
func (c *conn) fileControl(dbName string, op int, pArg uintptr) error {
|
||||
zDbName, err := libc.CString(dbName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer c.free(zDbName)
|
||||
|
||||
if rc := sqlite3.Xsqlite3_file_control(c.tls, c.db, zDbName, int32(op), pArg); rc != sqlite3.SQLITE_OK {
|
||||
return c.errstr(rc)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
152
fcntl_test.go
Normal file
152
fcntl_test.go
Normal file
|
@ -0,0 +1,152 @@
|
|||
// Copyright 2024 The Sqlite 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 sqlite // import "modernc.org/sqlite"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestFcntlPersistWAL(t *testing.T) {
|
||||
t.Run("WAL is cleaned up without persist WAL", func(t *testing.T) {
|
||||
name := filepath.Join(t.TempDir(), "tmp.db")
|
||||
walName := name + "-wal"
|
||||
db, err := sql.Open(driverName, fmt.Sprintf("file:%s", name))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
// enable WAL journal
|
||||
if _, err := db.Exec("pragma journal_mode = WAL"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if _, err := db.Exec("create table t(b int)"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// database file must exist after creating a table
|
||||
if _, err := os.Stat(name); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// wal file must exist after creating a table
|
||||
if _, err := os.Stat(walName); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := db.Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// database file must exist after closing it
|
||||
if _, err := os.Stat(name); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// wal file must NOT exist after closing the db
|
||||
if _, err := os.Stat(walName); err == nil {
|
||||
t.Errorf("expected WAL file %s to not exist after closing db", walName)
|
||||
} else if !errors.Is(err, os.ErrNotExist) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("WAL is not cleaned up with persist WAL", func(t *testing.T) {
|
||||
name := filepath.Join(t.TempDir(), "tmp.db")
|
||||
walName := name + "-wal"
|
||||
db, err := sql.Open(driverName, fmt.Sprintf("file:%s", name))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
conn, err := db.Conn(context.TODO())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// enable persist WAL for a connection, normally this is done with a hook
|
||||
err = conn.Raw(func(driverConn any) error {
|
||||
fc, ok := driverConn.(FileControl)
|
||||
if !ok {
|
||||
return fmt.Errorf("driver connection didn't implement FileControl")
|
||||
}
|
||||
|
||||
// query
|
||||
mode, err := fc.FileControlPersistWAL("main", -1)
|
||||
if err != nil {
|
||||
return fmt.Errorf("file control call failed: %w", err)
|
||||
} else if mode != 0 {
|
||||
return fmt.Errorf("file control call returned unexpected mode: %d", mode)
|
||||
}
|
||||
|
||||
// enable
|
||||
mode, err = fc.FileControlPersistWAL("main", 1)
|
||||
if err != nil {
|
||||
return fmt.Errorf("file control call failed: %w", err)
|
||||
} else if mode != 1 {
|
||||
return fmt.Errorf("file control call returned unexpected mode: %d", mode)
|
||||
}
|
||||
|
||||
// verify
|
||||
mode, err = fc.FileControlPersistWAL("main", -1)
|
||||
if err != nil {
|
||||
return fmt.Errorf("file control call failed: %w", err)
|
||||
} else if mode != 1 {
|
||||
return fmt.Errorf("file control call returned unexpected mode: %d", mode)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if _, err := conn.ExecContext(context.TODO(), "pragma journal_mode = WAL"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if _, err := conn.ExecContext(context.TODO(), "create table t(b int)"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// database file must exist after creating a table
|
||||
if _, err := os.Stat(name); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// wal file must exist after creating a table
|
||||
if _, err := os.Stat(walName); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// close connection, should persist WAL
|
||||
if err := conn.Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// close database, should persist WAL
|
||||
if err := db.Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// database file must exist after closing it
|
||||
if _, err := os.Stat(name); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// wal file must exist after closing the db
|
||||
if _, err := os.Stat(walName); err != nil {
|
||||
t.Errorf("expected WAL file %s to exist after closing db: %s", walName, err.Error())
|
||||
}
|
||||
})
|
||||
}
|
963
func_test.go
Normal file
963
func_test.go
Normal file
|
@ -0,0 +1,963 @@
|
|||
// Copyright 2022 The Sqlite 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 sqlite // import "modernc.org/sqlite"
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/md5"
|
||||
"database/sql"
|
||||
"database/sql/driver"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
var finalCalled bool
|
||||
|
||||
type sumFunction struct {
|
||||
sum int64
|
||||
finalCalled *bool
|
||||
}
|
||||
|
||||
func (f *sumFunction) Step(ctx *FunctionContext, args []driver.Value) error {
|
||||
switch resTyped := args[0].(type) {
|
||||
case int64:
|
||||
f.sum += resTyped
|
||||
default:
|
||||
return fmt.Errorf("function did not return a valid driver.Value: %T", resTyped)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *sumFunction) WindowInverse(ctx *FunctionContext, args []driver.Value) error {
|
||||
switch resTyped := args[0].(type) {
|
||||
case int64:
|
||||
f.sum -= resTyped
|
||||
default:
|
||||
return fmt.Errorf("function did not return a valid driver.Value: %T", resTyped)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *sumFunction) WindowValue(ctx *FunctionContext) (driver.Value, error) {
|
||||
return f.sum, nil
|
||||
}
|
||||
|
||||
func (f *sumFunction) Final(ctx *FunctionContext) {
|
||||
*f.finalCalled = true
|
||||
}
|
||||
|
||||
func init() {
|
||||
MustRegisterDeterministicScalarFunction(
|
||||
"test_int64",
|
||||
0,
|
||||
func(ctx *FunctionContext, args []driver.Value) (driver.Value, error) {
|
||||
return int64(42), nil
|
||||
},
|
||||
)
|
||||
|
||||
MustRegisterDeterministicScalarFunction(
|
||||
"test_float64",
|
||||
0,
|
||||
func(ctx *FunctionContext, args []driver.Value) (driver.Value, error) {
|
||||
return float64(1e-2), nil
|
||||
},
|
||||
)
|
||||
|
||||
MustRegisterDeterministicScalarFunction(
|
||||
"test_null",
|
||||
0,
|
||||
func(ctx *FunctionContext, args []driver.Value) (driver.Value, error) {
|
||||
return nil, nil
|
||||
},
|
||||
)
|
||||
|
||||
MustRegisterDeterministicScalarFunction(
|
||||
"test_error",
|
||||
0,
|
||||
func(ctx *FunctionContext, args []driver.Value) (driver.Value, error) {
|
||||
return nil, errors.New("boom")
|
||||
},
|
||||
)
|
||||
|
||||
MustRegisterDeterministicScalarFunction(
|
||||
"test_empty_byte_slice",
|
||||
0,
|
||||
func(ctx *FunctionContext, args []driver.Value) (driver.Value, error) {
|
||||
return []byte{}, nil
|
||||
},
|
||||
)
|
||||
|
||||
MustRegisterDeterministicScalarFunction(
|
||||
"test_nonempty_byte_slice",
|
||||
0,
|
||||
func(ctx *FunctionContext, args []driver.Value) (driver.Value, error) {
|
||||
return []byte("abcdefg"), nil
|
||||
},
|
||||
)
|
||||
|
||||
MustRegisterDeterministicScalarFunction(
|
||||
"test_empty_string",
|
||||
0,
|
||||
func(ctx *FunctionContext, args []driver.Value) (driver.Value, error) {
|
||||
return "", nil
|
||||
},
|
||||
)
|
||||
|
||||
MustRegisterDeterministicScalarFunction(
|
||||
"test_nonempty_string",
|
||||
0,
|
||||
func(ctx *FunctionContext, args []driver.Value) (driver.Value, error) {
|
||||
return "abcdefg", nil
|
||||
},
|
||||
)
|
||||
|
||||
MustRegisterDeterministicScalarFunction(
|
||||
"yesterday",
|
||||
1,
|
||||
func(ctx *FunctionContext, args []driver.Value) (driver.Value, error) {
|
||||
var arg time.Time
|
||||
switch argTyped := args[0].(type) {
|
||||
case int64:
|
||||
arg = time.Unix(argTyped, 0)
|
||||
default:
|
||||
fmt.Println(argTyped)
|
||||
return nil, fmt.Errorf("expected argument to be int64, got: %T", argTyped)
|
||||
}
|
||||
return arg.Add(-24 * time.Hour), nil
|
||||
},
|
||||
)
|
||||
|
||||
MustRegisterDeterministicScalarFunction(
|
||||
"md5",
|
||||
1,
|
||||
func(ctx *FunctionContext, args []driver.Value) (driver.Value, error) {
|
||||
var arg *bytes.Buffer
|
||||
switch argTyped := args[0].(type) {
|
||||
case string:
|
||||
arg = bytes.NewBuffer([]byte(argTyped))
|
||||
case []byte:
|
||||
arg = bytes.NewBuffer(argTyped)
|
||||
default:
|
||||
return nil, fmt.Errorf("expected argument to be a string, got: %T", argTyped)
|
||||
}
|
||||
w := md5.New()
|
||||
if _, err := arg.WriteTo(w); err != nil {
|
||||
return nil, fmt.Errorf("unable to compute md5 checksum: %s", err)
|
||||
}
|
||||
return hex.EncodeToString(w.Sum(nil)), nil
|
||||
},
|
||||
)
|
||||
|
||||
MustRegisterDeterministicScalarFunction(
|
||||
"regexp",
|
||||
2,
|
||||
func(ctx *FunctionContext, args []driver.Value) (driver.Value, error) {
|
||||
var s1 string
|
||||
var s2 string
|
||||
|
||||
switch arg0 := args[0].(type) {
|
||||
case string:
|
||||
s1 = arg0
|
||||
default:
|
||||
return nil, errors.New("expected argv[0] to be text")
|
||||
}
|
||||
|
||||
fmt.Println(args)
|
||||
|
||||
switch arg1 := args[1].(type) {
|
||||
case string:
|
||||
s2 = arg1
|
||||
default:
|
||||
return nil, errors.New("expected argv[1] to be text")
|
||||
}
|
||||
|
||||
matched, err := regexp.MatchString(s1, s2)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("bad regular expression: %q", err)
|
||||
}
|
||||
|
||||
return matched, nil
|
||||
},
|
||||
)
|
||||
|
||||
MustRegisterFunction("test_sum", &FunctionImpl{
|
||||
NArgs: 1,
|
||||
Deterministic: true,
|
||||
MakeAggregate: func(ctx FunctionContext) (AggregateFunction, error) {
|
||||
return &sumFunction{finalCalled: &finalCalled}, nil
|
||||
},
|
||||
})
|
||||
|
||||
MustRegisterFunction("test_aggregate_error", &FunctionImpl{
|
||||
NArgs: 1,
|
||||
Deterministic: true,
|
||||
MakeAggregate: func(ctx FunctionContext) (AggregateFunction, error) {
|
||||
return nil, errors.New("boom")
|
||||
},
|
||||
})
|
||||
|
||||
MustRegisterFunction("test_aggregate_null_pointer", &FunctionImpl{
|
||||
NArgs: 1,
|
||||
Deterministic: true,
|
||||
MakeAggregate: func(ctx FunctionContext) (AggregateFunction, error) {
|
||||
return nil, nil
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestRegisteredFunctions(t *testing.T) {
|
||||
withDB := func(test func(db *sql.DB)) {
|
||||
db, err := sql.Open("sqlite", "file::memory:")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to open database: %v", err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
finalCalled = false
|
||||
test(db)
|
||||
}
|
||||
|
||||
t.Run("int64", func(tt *testing.T) {
|
||||
withDB(func(db *sql.DB) {
|
||||
row := db.QueryRow("select test_int64()")
|
||||
|
||||
var a int
|
||||
if err := row.Scan(&a); err != nil {
|
||||
tt.Fatal(err)
|
||||
}
|
||||
if g, e := a, 42; g != e {
|
||||
tt.Fatal(g, e)
|
||||
}
|
||||
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("float64", func(tt *testing.T) {
|
||||
withDB(func(db *sql.DB) {
|
||||
row := db.QueryRow("select test_float64()")
|
||||
|
||||
var a float64
|
||||
if err := row.Scan(&a); err != nil {
|
||||
tt.Fatal(err)
|
||||
}
|
||||
if g, e := a, 1e-2; g != e {
|
||||
tt.Fatal(g, e)
|
||||
}
|
||||
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("error", func(tt *testing.T) {
|
||||
withDB(func(db *sql.DB) {
|
||||
_, err := db.Query("select test_error()")
|
||||
if err == nil {
|
||||
tt.Fatal("expected error, got none")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "boom") {
|
||||
tt.Fatal(err)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("empty_byte_slice", func(tt *testing.T) {
|
||||
withDB(func(db *sql.DB) {
|
||||
row := db.QueryRow("select test_empty_byte_slice()")
|
||||
|
||||
var a []byte
|
||||
if err := row.Scan(&a); err != nil {
|
||||
tt.Fatal(err)
|
||||
}
|
||||
if len(a) > 0 {
|
||||
tt.Fatal("expected empty byte slice")
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("nonempty_byte_slice", func(tt *testing.T) {
|
||||
withDB(func(db *sql.DB) {
|
||||
row := db.QueryRow("select test_nonempty_byte_slice()")
|
||||
|
||||
var a []byte
|
||||
if err := row.Scan(&a); err != nil {
|
||||
tt.Fatal(err)
|
||||
}
|
||||
if g, e := a, []byte("abcdefg"); !bytes.Equal(g, e) {
|
||||
tt.Fatal(string(g), string(e))
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("empty_string", func(tt *testing.T) {
|
||||
withDB(func(db *sql.DB) {
|
||||
row := db.QueryRow("select test_empty_string()")
|
||||
|
||||
var a string
|
||||
if err := row.Scan(&a); err != nil {
|
||||
tt.Fatal(err)
|
||||
}
|
||||
if len(a) > 0 {
|
||||
tt.Fatal("expected empty string")
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("nonempty_string", func(tt *testing.T) {
|
||||
withDB(func(db *sql.DB) {
|
||||
row := db.QueryRow("select test_nonempty_string()")
|
||||
|
||||
var a string
|
||||
if err := row.Scan(&a); err != nil {
|
||||
tt.Fatal(err)
|
||||
}
|
||||
if g, e := a, "abcdefg"; g != e {
|
||||
tt.Fatal(g, e)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("null", func(tt *testing.T) {
|
||||
withDB(func(db *sql.DB) {
|
||||
row := db.QueryRow("select test_null()")
|
||||
|
||||
var a interface{}
|
||||
if err := row.Scan(&a); err != nil {
|
||||
tt.Fatal(err)
|
||||
}
|
||||
if a != nil {
|
||||
tt.Fatal("expected nil")
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("dates", func(tt *testing.T) {
|
||||
withDB(func(db *sql.DB) {
|
||||
row := db.QueryRow("select yesterday(unixepoch('2018-11-01'))")
|
||||
|
||||
var a int64
|
||||
if err := row.Scan(&a); err != nil {
|
||||
tt.Fatal(err)
|
||||
}
|
||||
if g, e := time.Unix(a, 0), time.Date(2018, time.October, 31, 0, 0, 0, 0, time.UTC); !g.Equal(e) {
|
||||
tt.Fatal(g, e)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("md5", func(tt *testing.T) {
|
||||
withDB(func(db *sql.DB) {
|
||||
row := db.QueryRow("select md5('abcdefg')")
|
||||
|
||||
var a string
|
||||
if err := row.Scan(&a); err != nil {
|
||||
tt.Fatal(err)
|
||||
}
|
||||
if g, e := a, "7ac66c0f148de9519b8bd264312c4d64"; g != e {
|
||||
tt.Fatal(g, e)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("md5 with blob input", func(tt *testing.T) {
|
||||
withDB(func(db *sql.DB) {
|
||||
if _, err := db.Exec("create table t(b blob); insert into t values (?)", []byte("abcdefg")); err != nil {
|
||||
tt.Fatal(err)
|
||||
}
|
||||
row := db.QueryRow("select md5(b) from t")
|
||||
|
||||
var a []byte
|
||||
if err := row.Scan(&a); err != nil {
|
||||
tt.Fatal(err)
|
||||
}
|
||||
if g, e := a, []byte("7ac66c0f148de9519b8bd264312c4d64"); !bytes.Equal(g, e) {
|
||||
tt.Fatal(string(g), string(e))
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("regexp filter", func(tt *testing.T) {
|
||||
withDB(func(db *sql.DB) {
|
||||
t1 := "seafood"
|
||||
t2 := "fruit"
|
||||
|
||||
if _, err := db.Exec("create table t(b text); insert into t values (?), (?)", t1, t2); err != nil {
|
||||
tt.Fatal(err)
|
||||
}
|
||||
rows, err := db.Query("select * from t where b regexp 'foo.*'")
|
||||
if err != nil {
|
||||
tt.Fatal(err)
|
||||
}
|
||||
|
||||
type rec struct {
|
||||
b string
|
||||
}
|
||||
var a []rec
|
||||
for rows.Next() {
|
||||
var r rec
|
||||
if err := rows.Scan(&r.b); err != nil {
|
||||
tt.Fatal(err)
|
||||
}
|
||||
|
||||
a = append(a, r)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
tt.Fatal(err)
|
||||
}
|
||||
|
||||
if g, e := len(a), 1; g != e {
|
||||
tt.Fatal(g, e)
|
||||
}
|
||||
|
||||
if g, e := a[0].b, t1; g != e {
|
||||
tt.Fatal(g, e)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("regexp matches", func(tt *testing.T) {
|
||||
withDB(func(db *sql.DB) {
|
||||
row := db.QueryRow("select 'seafood' regexp 'foo.*'")
|
||||
|
||||
var r int
|
||||
if err := row.Scan(&r); err != nil {
|
||||
tt.Fatal(err)
|
||||
}
|
||||
|
||||
if g, e := r, 1; g != e {
|
||||
tt.Fatal(g, e)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("regexp does not match", func(tt *testing.T) {
|
||||
withDB(func(db *sql.DB) {
|
||||
row := db.QueryRow("select 'fruit' regexp 'foo.*'")
|
||||
|
||||
var r int
|
||||
if err := row.Scan(&r); err != nil {
|
||||
tt.Fatal(err)
|
||||
}
|
||||
|
||||
if g, e := r, 0; g != e {
|
||||
tt.Fatal(g, e)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("regexp errors on bad regexp", func(tt *testing.T) {
|
||||
withDB(func(db *sql.DB) {
|
||||
_, err := db.Query("select 'seafood' regexp 'a(b'")
|
||||
if err == nil {
|
||||
tt.Fatal(errors.New("expected error, got none"))
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("regexp errors on bad first argument", func(tt *testing.T) {
|
||||
withDB(func(db *sql.DB) {
|
||||
_, err := db.Query("SELECT 1 REGEXP 'a(b'")
|
||||
if err == nil {
|
||||
tt.Fatal(errors.New("expected error, got none"))
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("regexp errors on bad second argument", func(tt *testing.T) {
|
||||
withDB(func(db *sql.DB) {
|
||||
_, err := db.Query("SELECT 'seafood' REGEXP 1")
|
||||
if err == nil {
|
||||
tt.Fatal(errors.New("expected error, got none"))
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("sumFunction type error", func(tt *testing.T) {
|
||||
withDB(func(db *sql.DB) {
|
||||
row := db.QueryRow("select test_sum('foo');")
|
||||
|
||||
err := row.Scan()
|
||||
if err == nil {
|
||||
tt.Fatal("expected error, got none")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "string") {
|
||||
tt.Fatal(err)
|
||||
}
|
||||
if !finalCalled {
|
||||
t.Error("xFinal not called")
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("sumFunction multiple columns", func(tt *testing.T) {
|
||||
withDB(func(db *sql.DB) {
|
||||
if _, err := db.Exec("create table t(a int64, b int64); insert into t values (1, 5), (2, 6), (3, 7), (4, 8)"); err != nil {
|
||||
tt.Fatal(err)
|
||||
}
|
||||
row := db.QueryRow("select test_sum(a), test_sum(b) from t;")
|
||||
|
||||
var a, b int64
|
||||
var e int64 = 10
|
||||
var f int64 = 26
|
||||
if err := row.Scan(&a, &b); err != nil {
|
||||
tt.Fatal(err)
|
||||
}
|
||||
if a != e {
|
||||
tt.Fatal(a, e)
|
||||
}
|
||||
if b != f {
|
||||
tt.Fatal(b, f)
|
||||
}
|
||||
if !finalCalled {
|
||||
t.Error("xFinal not called")
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
// https://www.sqlite.org/windowfunctions.html#user_defined_aggregate_window_functions
|
||||
t.Run("sumFunction as window function", func(tt *testing.T) {
|
||||
withDB(func(db *sql.DB) {
|
||||
if _, err := db.Exec("create table t3(x, y); insert into t3 values('a', 4), ('b', 5), ('c', 3), ('d', 8), ('e', 1);"); err != nil {
|
||||
tt.Fatal(err)
|
||||
}
|
||||
rows, err := db.Query("select x, test_sum(y) over (order by x rows between 1 preceding and 1 following) as sum_y from t3 order by x;")
|
||||
if err != nil {
|
||||
tt.Fatal(err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
type row struct {
|
||||
x string
|
||||
sumY int64
|
||||
}
|
||||
|
||||
got := make([]row, 0)
|
||||
for rows.Next() {
|
||||
var r row
|
||||
if err := rows.Scan(&r.x, &r.sumY); err != nil {
|
||||
tt.Fatal(err)
|
||||
}
|
||||
got = append(got, r)
|
||||
}
|
||||
|
||||
want := []row{
|
||||
{"a", 9},
|
||||
{"b", 12},
|
||||
{"c", 16},
|
||||
{"d", 12},
|
||||
{"e", 9},
|
||||
}
|
||||
|
||||
if len(got) != len(want) {
|
||||
tt.Fatal(len(got), len(want))
|
||||
}
|
||||
|
||||
for i, g := range got {
|
||||
if g != want[i] {
|
||||
tt.Fatal(i, g, want[i])
|
||||
}
|
||||
}
|
||||
if !finalCalled {
|
||||
t.Error("xFinal not called")
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("aggregate function cannot be created", func(tt *testing.T) {
|
||||
withDB(func(db *sql.DB) {
|
||||
row := db.QueryRow("select test_aggregate_error(1);")
|
||||
|
||||
err := row.Scan()
|
||||
if err == nil {
|
||||
tt.Fatal("expected error, got none")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "boom") {
|
||||
tt.Fatal(err)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("null aggregate function pointer", func(tt *testing.T) {
|
||||
withDB(func(db *sql.DB) {
|
||||
row := db.QueryRow("select test_aggregate_null_pointer(1);")
|
||||
|
||||
err := row.Scan()
|
||||
if err == nil {
|
||||
tt.Fatal("expected error, got none")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "MakeAggregate function returned nil") {
|
||||
tt.Fatal(err)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("serialize and deserialize", func(tt *testing.T) {
|
||||
type serializer interface {
|
||||
Serialize() ([]byte, error)
|
||||
Deserialize([]byte) error
|
||||
}
|
||||
|
||||
var buf []byte
|
||||
|
||||
withDB(func(db *sql.DB) {
|
||||
if _, err := db.Exec("create table t(b text); insert into t values (?), (?)", "text1", "text2"); err != nil {
|
||||
tt.Fatal(err)
|
||||
}
|
||||
|
||||
// Serialize the DB into buf
|
||||
func() {
|
||||
conn, err := db.Conn(context.Background())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer conn.Close()
|
||||
err = conn.Raw(func(driverConn any) error {
|
||||
var err error
|
||||
buf, err = driverConn.(serializer).Serialize()
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}()
|
||||
})
|
||||
|
||||
// Deserialize will be executed on a new in-memory DB
|
||||
withDB(func(db *sql.DB) {
|
||||
// Deserialize buf into the DB
|
||||
func() {
|
||||
conn, err := db.Conn(context.Background())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer conn.Close()
|
||||
err = conn.Raw(func(driverConn any) error {
|
||||
return driverConn.(serializer).Deserialize(buf)
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}()
|
||||
|
||||
// Note sqlite3_deserialize will disconnect all connections from
|
||||
// the database, so force to recreate a connection here. It means
|
||||
// all connections attached to the db object are now invalid. Do
|
||||
// not close it because it will be closed by withDB. It's a bit
|
||||
// weird to handle.
|
||||
conn, err := db.Conn(context.Background())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Check the table is complete
|
||||
row := conn.QueryRowContext(context.Background(), "select count(*) from t")
|
||||
|
||||
var count int
|
||||
if err := row.Scan(&count); err != nil {
|
||||
tt.Fatal(err)
|
||||
}
|
||||
if count != 2 {
|
||||
tt.Fatal(count, 2)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("backup and restore", func(tt *testing.T) {
|
||||
type backuper interface {
|
||||
NewBackup(string) (*Backup, error)
|
||||
NewRestore(string) (*Backup, error)
|
||||
}
|
||||
|
||||
tmpDir, err := os.MkdirTemp(os.TempDir(), "storetest_")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
tmpFile := path.Join(tmpDir, "test.db")
|
||||
|
||||
withDB(func(db *sql.DB) {
|
||||
if _, err := db.Exec("create table t(b text); insert into t values (?), (?)", "text1", "text2"); err != nil {
|
||||
tt.Fatal(err)
|
||||
}
|
||||
|
||||
// Backup the DB to tmpFile
|
||||
func() {
|
||||
conn, err := db.Conn(context.Background())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer conn.Close()
|
||||
err = conn.Raw(func(driverConn any) error {
|
||||
bck, err := driverConn.(backuper).NewBackup(tmpFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for more := true; more; {
|
||||
more, err = bck.Step(-1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return bck.Finish()
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}()
|
||||
})
|
||||
|
||||
// Restore will be executed on a new in-memory DB
|
||||
withDB(func(db *sql.DB) {
|
||||
func() {
|
||||
conn, err := db.Conn(context.Background())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer conn.Close()
|
||||
err = conn.Raw(func(driverConn any) error {
|
||||
bck, err := driverConn.(backuper).NewRestore(tmpFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for more := true; more; {
|
||||
more, err = bck.Step(-1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return bck.Finish()
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}()
|
||||
|
||||
// Check the table is complete
|
||||
var count int
|
||||
row := db.QueryRow("select count(*) from t")
|
||||
if err := row.Scan(&count); err != nil {
|
||||
tt.Fatal(err)
|
||||
} else if count != 2 {
|
||||
tt.Fatal(count, 2)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("backup, commit and close", func(tt *testing.T) {
|
||||
type backuper interface {
|
||||
NewBackup(string) (*Backup, error)
|
||||
NewRestore(string) (*Backup, error)
|
||||
}
|
||||
|
||||
tmpDir, err := os.MkdirTemp(os.TempDir(), "storetest_")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
var inMemDB driver.Conn
|
||||
|
||||
withDB(func(db *sql.DB) {
|
||||
if _, err := db.Exec("create table t(b text); insert into t values (?), (?)", "text1", "text2"); err != nil {
|
||||
tt.Fatal(err)
|
||||
}
|
||||
|
||||
// Backup the DB to an in-memory instance
|
||||
func() {
|
||||
conn, err := db.Conn(context.Background())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer conn.Close()
|
||||
err = conn.Raw(func(driverConn any) error {
|
||||
bck, err := driverConn.(backuper).NewBackup(":memory:")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for more := true; more; {
|
||||
more, err = bck.Step(-1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
inMemDB, err = bck.Commit()
|
||||
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}()
|
||||
})
|
||||
|
||||
if inMemDB == nil {
|
||||
tt.Fatal("in-memory database must not be nil")
|
||||
}
|
||||
|
||||
querier, ok := (inMemDB).(interface {
|
||||
Query(query string, args []driver.Value) (dr driver.Rows, err error)
|
||||
})
|
||||
|
||||
if !ok {
|
||||
tt.Fatal(fmt.Errorf("in-memory DB does not implement a Query method: %T", inMemDB))
|
||||
}
|
||||
|
||||
// Check the table is complete
|
||||
rows, err := querier.Query("select count(*) from t", nil)
|
||||
if err != nil {
|
||||
tt.Fatal(err)
|
||||
}
|
||||
|
||||
var count int
|
||||
var values = []driver.Value{&count}
|
||||
|
||||
err = rows.Next(values)
|
||||
if err != nil {
|
||||
tt.Fatal(err)
|
||||
}
|
||||
|
||||
if (values[0]).(int64) != 2 {
|
||||
tt.Fatal(count, 2)
|
||||
}
|
||||
|
||||
err = rows.Next(values)
|
||||
if !errors.Is(err, io.EOF) {
|
||||
tt.Fatal(err)
|
||||
}
|
||||
|
||||
if err = rows.Close(); err != nil {
|
||||
tt.Fatal(err)
|
||||
}
|
||||
|
||||
if err = inMemDB.Close(); err != nil {
|
||||
tt.Fatal(err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("QueryContext with context expired", func(tt *testing.T) {
|
||||
withDB(func(db *sql.DB) {
|
||||
if _, err := db.Exec("create table t(b text); insert into t values (?), (?)", "text1", "text2"); err != nil {
|
||||
tt.Fatal(err)
|
||||
}
|
||||
|
||||
conn, err := db.Conn(context.Background())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
cancel()
|
||||
_, err = conn.QueryContext(ctx, "select count(*) from t")
|
||||
if err == nil {
|
||||
tt.Fatalf("unexpected success while context is canceled")
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("QueryContext with context expiring", func(tt *testing.T) {
|
||||
withDB(func(db *sql.DB) {
|
||||
if _, err := db.Exec("create table t(b text); insert into t values (?), (?)", "text1", "text2"); err != nil {
|
||||
tt.Fatal(err)
|
||||
}
|
||||
|
||||
conn, err := db.Conn(context.Background())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for try := 0; try < 1000; try++ {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
go func() {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
cancel()
|
||||
}()
|
||||
|
||||
for start := time.Now(); time.Since(start) < 200*time.Millisecond; {
|
||||
func() {
|
||||
rows, err := conn.QueryContext(ctx, "select count(*) from t")
|
||||
if rows != nil {
|
||||
defer rows.Close()
|
||||
}
|
||||
if err != nil && !(errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) || strings.Contains(err.Error(), "interrupted (9)")) {
|
||||
tt.Fatalf("unexpected error, was expected context or interrupted error: err=%v", err)
|
||||
} else if err == nil && !rows.Next() {
|
||||
if rowsErr := rows.Err(); rowsErr != nil && !(errors.Is(rowsErr, context.Canceled) || errors.Is(rowsErr, context.DeadlineExceeded) || strings.Contains(rowsErr.Error(), "interrupted (9)")) {
|
||||
tt.Fatalf("unexpected error, was expected context or interrupted error: err=%v", err)
|
||||
} else if rowsErr == nil {
|
||||
tt.Fatalf("success with no data (try=%d)", try)
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("ExecContext with context expired", func(tt *testing.T) {
|
||||
withDB(func(db *sql.DB) {
|
||||
if _, err := db.Exec("create table t(b text); insert into t values (?), (?)", "text1", "text2"); err != nil {
|
||||
tt.Fatal(err)
|
||||
}
|
||||
|
||||
conn, err := db.Conn(context.Background())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
cancel()
|
||||
_, err = conn.ExecContext(ctx, "insert into t values (?)", "text3")
|
||||
if err == nil {
|
||||
tt.Fatalf("unexpected success while context is canceled")
|
||||
} else if !(errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) || strings.Contains(err.Error(), "interrupted (9)")) {
|
||||
tt.Fatalf("unexpected error while context is canceled: expected context or interrupted, got %v", err)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("ExecContext with context expiring", func(tt *testing.T) {
|
||||
withDB(func(db *sql.DB) {
|
||||
if _, err := db.Exec("create table t(b text); insert into t values (?), (?)", "text1", "text2"); err != nil {
|
||||
tt.Fatal(err)
|
||||
}
|
||||
|
||||
conn, err := db.Conn(context.Background())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for try := 0; try < 1000; try++ {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
go func() {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
cancel()
|
||||
}()
|
||||
|
||||
for start := time.Now(); time.Since(start) < 200*time.Millisecond; {
|
||||
res, err := conn.ExecContext(ctx, "insert into t values (?)", "text3")
|
||||
if err != nil && !(errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) || strings.Contains(err.Error(), "interrupted (9)")) {
|
||||
tt.Fatalf("unexpected error, was expected context or interrupted error: err=%v", err)
|
||||
} else if err == nil {
|
||||
if rowsAffected, err := res.RowsAffected(); err != nil {
|
||||
tt.Fatal(err)
|
||||
} else if rowsAffected != 1 {
|
||||
tt.Fatalf("success with no inserted data (try=%d)", try)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
33
go.mod
Normal file
33
go.mod
Normal file
|
@ -0,0 +1,33 @@
|
|||
module modernc.org/sqlite
|
||||
|
||||
go 1.23.0
|
||||
|
||||
require (
|
||||
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e
|
||||
golang.org/x/sys v0.31.0
|
||||
modernc.org/fileutil v1.3.0
|
||||
modernc.org/libc v1.62.1
|
||||
modernc.org/mathutil v1.7.1
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/ncruces/go-strftime v0.1.9 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 // indirect
|
||||
modernc.org/memory v1.9.1 // indirect
|
||||
)
|
||||
|
||||
retract [v1.16.0, v1.17.2] // https://gitlab.com/cznic/sqlite/-/issues/100
|
||||
|
||||
retract v1.19.0 // module source tree too large (max size is 524288000 bytes)
|
||||
|
||||
retract v1.20.1 // https://gitlab.com/cznic/sqlite/-/issues/123
|
||||
|
||||
retract v1.29.4 // tagged accidentally w/o builders checking the commit
|
||||
|
||||
retract v1.33.0 // intended to resolve #177 but breaks clients
|
||||
|
||||
retract v1.34.3 // intended to resolve #199 but breaks clients, see #200, fix in 1fcc86e9
|
45
go.sum
Normal file
45
go.sum
Normal file
|
@ -0,0 +1,45 @@
|
|||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs=
|
||||
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
|
||||
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 h1:nDVHiLt8aIbd/VzvPWN6kSOPE7+F/fNFDSXLVYkE/Iw=
|
||||
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394/go.mod h1:sIifuuw/Yco/y6yb6+bDNfyeQ/MdPUy/hKEMYQV17cM=
|
||||
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
|
||||
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
|
||||
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
||||
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/tools v0.31.0 h1:0EedkvKDbh+qistFTd0Bcwe/YLh4vHwWEkiI0toFIBU=
|
||||
golang.org/x/tools v0.31.0/go.mod h1:naFTU+Cev749tSJRXJlna0T3WxKvb1kWEx15xA4SdmQ=
|
||||
modernc.org/cc/v4 v4.25.2 h1:T2oH7sZdGvTaie0BRNFbIYsabzCxUQg8nLqCdQ2i0ic=
|
||||
modernc.org/cc/v4 v4.25.2/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
|
||||
modernc.org/ccgo/v4 v4.25.1 h1:TFSzPrAGmDsdnhT9X2UrcPMI3N/mJ9/X9ykKXwLhDsU=
|
||||
modernc.org/ccgo/v4 v4.25.1/go.mod h1:njjuAYiPflywOOrm3B7kCB444ONP5pAVr8PIEoE0uDw=
|
||||
modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE=
|
||||
modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ=
|
||||
modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=
|
||||
modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
|
||||
modernc.org/libc v1.62.1 h1:s0+fv5E3FymN8eJVmnk0llBe6rOxCu/DEU+XygRbS8s=
|
||||
modernc.org/libc v1.62.1/go.mod h1:iXhATfJQLjG3NWy56a6WVU73lWOcdYVxsvwCgoPljuo=
|
||||
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
|
||||
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
|
||||
modernc.org/memory v1.9.1 h1:V/Z1solwAVmMW1yttq3nDdZPJqV1rM05Ccq6KMSZ34g=
|
||||
modernc.org/memory v1.9.1/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
|
||||
modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8=
|
||||
modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
|
||||
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
|
||||
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
|
||||
modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
|
||||
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
|
||||
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
||||
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
78
issue120.diff
Normal file
78
issue120.diff
Normal file
|
@ -0,0 +1,78 @@
|
|||
--- /home/jnml/tmp/test_syscall.c 2023-04-21 16:26:44.302689709 +0200
|
||||
+++ testdata/sqlite-src-3410200/src/test_syscall.c 2023-04-21 16:29:28.000869993 +0200
|
||||
@@ -110,15 +110,15 @@
|
||||
static int ts_fstat(int fd, struct stat *p);
|
||||
static int ts_ftruncate(int fd, off_t n);
|
||||
static int ts_fcntl(int fd, int cmd, ... );
|
||||
-static int ts_read(int fd, void *aBuf, size_t nBuf);
|
||||
-static int ts_pread(int fd, void *aBuf, size_t nBuf, off_t off);
|
||||
+static ssize_t ts_read(int fd, void *aBuf, size_t nBuf);
|
||||
+static ssize_t ts_pread(int fd, void *aBuf, size_t nBuf, off_t off);
|
||||
/* Note: pread64() and pwrite64() actually use off64_t as the type on their
|
||||
** last parameter. But that datatype is not defined on many systems
|
||||
** (ex: Mac, OpenBSD). So substitute a likely equivalent: sqlite3_uint64 */
|
||||
-static int ts_pread64(int fd, void *aBuf, size_t nBuf, sqlite3_uint64 off);
|
||||
-static int ts_write(int fd, const void *aBuf, size_t nBuf);
|
||||
-static int ts_pwrite(int fd, const void *aBuf, size_t nBuf, off_t off);
|
||||
-static int ts_pwrite64(int fd, const void *aBuf, size_t nBuf, sqlite3_uint64 off);
|
||||
+static ssize_t ts_pread64(int fd, void *aBuf, size_t nBuf, sqlite3_uint64 off);
|
||||
+static ssize_t ts_write(int fd, const void *aBuf, size_t nBuf);
|
||||
+static ssize_t ts_pwrite(int fd, const void *aBuf, size_t nBuf, off_t off);
|
||||
+static ssize_t ts_pwrite64(int fd, const void *aBuf, size_t nBuf, sqlite3_uint64 off);
|
||||
static int ts_fchmod(int fd, mode_t mode);
|
||||
static int ts_fallocate(int fd, off_t off, off_t len);
|
||||
static void *ts_mmap(void *, size_t, int, int, int, off_t);
|
||||
@@ -313,7 +313,7 @@
|
||||
/*
|
||||
** A wrapper around read().
|
||||
*/
|
||||
-static int ts_read(int fd, void *aBuf, size_t nBuf){
|
||||
+static ssize_t ts_read(int fd, void *aBuf, size_t nBuf){
|
||||
if( tsIsFailErrno("read") ){
|
||||
return -1;
|
||||
}
|
||||
@@ -323,7 +323,7 @@
|
||||
/*
|
||||
** A wrapper around pread().
|
||||
*/
|
||||
-static int ts_pread(int fd, void *aBuf, size_t nBuf, off_t off){
|
||||
+static ssize_t ts_pread(int fd, void *aBuf, size_t nBuf, off_t off){
|
||||
if( tsIsFailErrno("pread") ){
|
||||
return -1;
|
||||
}
|
||||
@@ -333,7 +333,7 @@
|
||||
/*
|
||||
** A wrapper around pread64().
|
||||
*/
|
||||
-static int ts_pread64(int fd, void *aBuf, size_t nBuf, sqlite3_uint64 off){
|
||||
+static ssize_t ts_pread64(int fd, void *aBuf, size_t nBuf, sqlite3_uint64 off){
|
||||
if( tsIsFailErrno("pread64") ){
|
||||
return -1;
|
||||
}
|
||||
@@ -343,7 +343,7 @@
|
||||
/*
|
||||
** A wrapper around write().
|
||||
*/
|
||||
-static int ts_write(int fd, const void *aBuf, size_t nBuf){
|
||||
+static ssize_t ts_write(int fd, const void *aBuf, size_t nBuf){
|
||||
if( tsIsFailErrno("write") ){
|
||||
if( tsErrno("write")==EINTR ) orig_write(fd, aBuf, nBuf/2);
|
||||
return -1;
|
||||
@@ -354,7 +354,7 @@
|
||||
/*
|
||||
** A wrapper around pwrite().
|
||||
*/
|
||||
-static int ts_pwrite(int fd, const void *aBuf, size_t nBuf, off_t off){
|
||||
+static ssize_t ts_pwrite(int fd, const void *aBuf, size_t nBuf, off_t off){
|
||||
if( tsIsFailErrno("pwrite") ){
|
||||
return -1;
|
||||
}
|
||||
@@ -364,7 +364,7 @@
|
||||
/*
|
||||
** A wrapper around pwrite64().
|
||||
*/
|
||||
-static int ts_pwrite64(int fd, const void *aBuf, size_t nBuf, sqlite3_uint64 off){
|
||||
+static ssize_t ts_pwrite64(int fd, const void *aBuf, size_t nBuf, sqlite3_uint64 off){
|
||||
if( tsIsFailErrno("pwrite64") ){
|
||||
return -1;
|
||||
}
|
22
issue198/go.mod
Normal file
22
issue198/go.mod
Normal file
|
@ -0,0 +1,22 @@
|
|||
module example.com/issue198
|
||||
|
||||
go 1.23.3
|
||||
|
||||
require (
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.24 // indirect
|
||||
github.com/ncruces/go-strftime v0.1.9 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
golang.org/x/sync v0.9.0 // indirect
|
||||
golang.org/x/sys v0.22.0 // indirect
|
||||
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 // indirect
|
||||
modernc.org/libc v1.55.3 // indirect
|
||||
modernc.org/mathutil v1.6.0 // indirect
|
||||
modernc.org/memory v1.8.0 // indirect
|
||||
modernc.org/sqlite v1.34.1 // indirect
|
||||
modernc.org/strutil v1.2.0 // indirect
|
||||
modernc.org/token v1.1.0 // indirect
|
||||
)
|
33
issue198/go.sum
Normal file
33
issue198/go.sum
Normal file
|
@ -0,0 +1,33 @@
|
|||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM=
|
||||
github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
|
||||
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
|
||||
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
|
||||
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 h1:5D53IMaUuA5InSeMu9eJtlQXS2NxAhyWQvkKEgXZhHI=
|
||||
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4=
|
||||
modernc.org/libc v1.55.3 h1:AzcW1mhlPNrRtjS5sS+eW2ISCgSOLLNyFzRh/V3Qj/U=
|
||||
modernc.org/libc v1.55.3/go.mod h1:qFXepLhz+JjFThQ4kzwzOjA/y/artDeg+pcYnY+Q83w=
|
||||
modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
|
||||
modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo=
|
||||
modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E=
|
||||
modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU=
|
||||
modernc.org/sqlite v1.34.1 h1:u3Yi6M0N8t9yKRDwhXcyp1eS5/ErhPTBggxWFuR6Hfk=
|
||||
modernc.org/sqlite v1.34.1/go.mod h1:pXV2xHxhzXZsgT/RtTFAPY6JJDEvOTcTdwADQCCWD4k=
|
||||
modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA=
|
||||
modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0=
|
||||
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
||||
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
158
issue198/main.go
Normal file
158
issue198/main.go
Normal file
|
@ -0,0 +1,158 @@
|
|||
// https://gitlab.com/cznic/sqlite/-/issues/198#note_2232364348
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
_ "modernc.org/sqlite"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
|
||||
defer cancel()
|
||||
|
||||
absDb, err := filepath.Abs("simple-web.db")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
dbSlash := "/" + strings.TrimPrefix(filepath.ToSlash(absDb), "/")
|
||||
connStr := "file://" + dbSlash + "?_pragma=foreign_keys(1)&_pragma=journal_mode(WAL)&_pragma=synchronous(NORMAL)&_pragma=busy_timeout(10000)"
|
||||
// connStr = "file:///" + dbSlash + "?_foreign_keys=1&_journal_mode=WAL&_synchronous=NORMAL&_busy_timeout=10000&_mutex=no"
|
||||
fmt.Printf("connecting to %s\n", connStr)
|
||||
db, err := sql.Open("sqlite", connStr)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
db.SetMaxOpenConns(20)
|
||||
db.SetMaxIdleConns(2)
|
||||
db.SetConnMaxLifetime(5 * time.Minute)
|
||||
db.SetConnMaxIdleTime(1 * time.Minute)
|
||||
defer db.Close()
|
||||
|
||||
if _, err := db.Exec(`CREATE TABLE IF NOT EXISTS data (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL DEFAULT ''
|
||||
)`); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
db.Exec(`DELETE FROM data`)
|
||||
db.Exec(`INSERT INTO data (Id, Name) VALUES (1, 'A'),(2, 'B'),(3, 'C'),(4, 'D');`)
|
||||
|
||||
http.HandleFunc("/items/{id}", func(w http.ResponseWriter, r *http.Request) {
|
||||
id := r.PathValue("id")
|
||||
type entry struct {
|
||||
Id int64 `sql:"id"`
|
||||
Name string `sql:"name"`
|
||||
}
|
||||
|
||||
scanEntry := func(ctx context.Context, id string) (entry, error) {
|
||||
ctx = context.Background()
|
||||
// modernc sqlite breaks connections when context gets cancelled
|
||||
dbConn, err := db.Conn(ctx)
|
||||
// var sqliteErr *sqlite.Error
|
||||
// for err != nil && errors.As(err, &sqliteErr) && sqliteErr.Code() == sqlite3.SQLITE_BUSY {
|
||||
// // fmt.Fprintf(os.Stderr, "failed to obtain connection. retrying %#v\n", err)
|
||||
// time.Sleep(time.Microsecond)
|
||||
// dbConn, err = db.Conn(ctx)
|
||||
// }
|
||||
if err != nil {
|
||||
return entry{}, fmt.Errorf("obtaining db conn: %w", err)
|
||||
}
|
||||
defer dbConn.Close()
|
||||
|
||||
// modernc sqlite breaks connections when context gets cancelled
|
||||
// ctx = context.Background()
|
||||
row := dbConn.QueryRowContext(ctx, "SELECT Id,Name FROM data WHERE id = ? LIMIT 1", id)
|
||||
if err := row.Err(); err != nil {
|
||||
return entry{}, fmt.Errorf("retrieving row: %w", err)
|
||||
}
|
||||
|
||||
e := entry{}
|
||||
if err := row.Scan(&e.Id, &e.Name); err != nil {
|
||||
return entry{}, fmt.Errorf("scanning entry: %w", err)
|
||||
}
|
||||
return e, nil
|
||||
}
|
||||
|
||||
const HttpClientClosedRequest = 499
|
||||
|
||||
e, err := scanEntry(r.Context(), id)
|
||||
if err != nil {
|
||||
if errors.Is(err, context.Canceled) {
|
||||
w.WriteHeader(HttpClientClosedRequest)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
fmt.Fprintf(os.Stderr, "%v - failed to retrieve row %v\n", time.Now().Format(time.RFC3339), err)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "text/plain")
|
||||
fmt.Fprintf(w, "%#v", e)
|
||||
})
|
||||
|
||||
server := http.Server{
|
||||
Addr: "127.0.0.1:8082",
|
||||
Handler: http.DefaultServeMux,
|
||||
}
|
||||
go func() {
|
||||
if err := server.ListenAndServe(); err != nil {
|
||||
fmt.Printf("server: %v\n", err)
|
||||
}
|
||||
}()
|
||||
go func() {
|
||||
RunClient(ctx)
|
||||
}()
|
||||
<-ctx.Done()
|
||||
server.Shutdown(context.Background())
|
||||
|
||||
db.Close()
|
||||
}
|
||||
|
||||
func RunClient(ctx context.Context) {
|
||||
eg, _ := errgroup.WithContext(ctx)
|
||||
|
||||
limit := 20
|
||||
eg.SetLimit(limit)
|
||||
c := &http.Client{
|
||||
Transport: &http.Transport{
|
||||
MaxIdleConns: 6,
|
||||
MaxConnsPerHost: 6,
|
||||
DisableKeepAlives: false,
|
||||
IdleConnTimeout: 10 * time.Second,
|
||||
}, Timeout: 5 * time.Second}
|
||||
|
||||
for i := 0; i < limit; i++ {
|
||||
eg.Go(func() error {
|
||||
|
||||
for ctx.Err() == nil {
|
||||
res, err := c.Get("http://127.0.0.1:8082/items/2")
|
||||
if err != nil {
|
||||
fmt.Printf("err http.Do %v\n", err)
|
||||
time.Sleep(time.Second)
|
||||
continue
|
||||
}
|
||||
io.Copy(io.Discard, res.Body)
|
||||
res.Body.Close()
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
if err := eg.Wait(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
16
lib/defs.go
Normal file
16
lib/defs.go
Normal file
|
@ -0,0 +1,16 @@
|
|||
// Copyright 2022 The Sqlite 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 sqlite3
|
||||
|
||||
const (
|
||||
SQLITE_STATIC = uintptr(0) // ((sqlite3_destructor_type)0)
|
||||
SQLITE_TRANSIENT = ^uintptr(0) // ((sqlite3_destructor_type)-1)
|
||||
)
|
||||
|
||||
type (
|
||||
Sqlite3_index_constraint = sqlite3_index_constraint
|
||||
Sqlite3_index_orderby = sqlite3_index_orderby
|
||||
Sqlite3_index_constraint_usage = sqlite3_index_constraint_usage
|
||||
)
|
20
lib/hooks.go
Normal file
20
lib/hooks.go
Normal file
|
@ -0,0 +1,20 @@
|
|||
// Copyright 2019 The Sqlite Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !(linux && arm64)
|
||||
|
||||
package sqlite3
|
||||
|
||||
import (
|
||||
"modernc.org/libc"
|
||||
)
|
||||
|
||||
// Format and write a message to the log if logging is enabled.
|
||||
func X__ccgo_sqlite3_log(t *libc.TLS, iErrCode int32, zFormat uintptr, va uintptr) { /* sqlite3.c:29405:17: */
|
||||
libc.X__ccgo_sqlite3_log(t, iErrCode, zFormat, va)
|
||||
}
|
||||
|
||||
func PatchIssue199() {
|
||||
// nop
|
||||
}
|
30
lib/hooks_linux_arm64.go
Normal file
30
lib/hooks_linux_arm64.go
Normal file
|
@ -0,0 +1,30 @@
|
|||
// Copyright 2019 The Sqlite 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 sqlite3
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"modernc.org/libc"
|
||||
)
|
||||
|
||||
// Format and write a message to the log if logging is enabled.
|
||||
func X__ccgo_sqlite3_log(t *libc.TLS, iErrCode int32, zFormat uintptr, va uintptr) { /* sqlite3.c:29405:17: */
|
||||
libc.X__ccgo_sqlite3_log(t, iErrCode, zFormat, va)
|
||||
}
|
||||
|
||||
// https://gitlab.com/cznic/sqlite/-/issues/199
|
||||
//
|
||||
// We are currently stuck on libc@v1.55.3. Until that is resolved - fix the
|
||||
// problem at runtime.
|
||||
func PatchIssue199() {
|
||||
p := unsafe.Pointer(&_aSyscall)
|
||||
*(*uintptr)(unsafe.Add(p, 608)) = __ccgo_fp(_unixGetpagesizeIssue199)
|
||||
}
|
||||
|
||||
func _unixGetpagesizeIssue199(tls *libc.TLS) (r int32) {
|
||||
return int32(syscall.Getpagesize())
|
||||
}
|
325
lib/mutex.go
Normal file
325
lib/mutex.go
Normal file
|
@ -0,0 +1,325 @@
|
|||
// Copyright 2021 The Sqlite Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !linux
|
||||
|
||||
package sqlite3
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"unsafe"
|
||||
|
||||
"modernc.org/libc"
|
||||
"modernc.org/libc/sys/types"
|
||||
)
|
||||
|
||||
func init() {
|
||||
tls := libc.NewTLS()
|
||||
if Xsqlite3_threadsafe(tls) == 0 {
|
||||
panic(fmt.Errorf("sqlite: thread safety configuration error"))
|
||||
}
|
||||
|
||||
varArgs := libc.Xmalloc(tls, types.Size_t(unsafe.Sizeof(uintptr(0))))
|
||||
if varArgs == 0 {
|
||||
panic(fmt.Errorf("cannot allocate memory"))
|
||||
}
|
||||
|
||||
// int sqlite3_config(int, ...);
|
||||
if rc := Xsqlite3_config(tls, SQLITE_CONFIG_MUTEX, libc.VaList(varArgs, uintptr(unsafe.Pointer(&mutexMethods)))); rc != SQLITE_OK {
|
||||
p := Xsqlite3_errstr(tls, rc)
|
||||
str := libc.GoString(p)
|
||||
panic(fmt.Errorf("sqlite: failed to configure mutex methods: %v", str))
|
||||
}
|
||||
|
||||
libc.Xfree(tls, varArgs)
|
||||
tls.Close()
|
||||
}
|
||||
|
||||
var (
|
||||
mutexMethods = Sqlite3_mutex_methods{
|
||||
FxMutexInit: *(*uintptr)(unsafe.Pointer(&struct{ f func(*libc.TLS) int32 }{mutexInit})),
|
||||
FxMutexEnd: *(*uintptr)(unsafe.Pointer(&struct{ f func(*libc.TLS) int32 }{mutexEnd})),
|
||||
FxMutexAlloc: *(*uintptr)(unsafe.Pointer(&struct {
|
||||
f func(*libc.TLS, int32) uintptr
|
||||
}{mutexAlloc})),
|
||||
FxMutexFree: *(*uintptr)(unsafe.Pointer(&struct{ f func(*libc.TLS, uintptr) }{mutexFree})),
|
||||
FxMutexEnter: *(*uintptr)(unsafe.Pointer(&struct{ f func(*libc.TLS, uintptr) }{mutexEnter})),
|
||||
FxMutexTry: *(*uintptr)(unsafe.Pointer(&struct {
|
||||
f func(*libc.TLS, uintptr) int32
|
||||
}{mutexTry})),
|
||||
FxMutexLeave: *(*uintptr)(unsafe.Pointer(&struct{ f func(*libc.TLS, uintptr) }{mutexLeave})),
|
||||
FxMutexHeld: *(*uintptr)(unsafe.Pointer(&struct {
|
||||
f func(*libc.TLS, uintptr) int32
|
||||
}{mutexHeld})),
|
||||
FxMutexNotheld: *(*uintptr)(unsafe.Pointer(&struct {
|
||||
f func(*libc.TLS, uintptr) int32
|
||||
}{mutexNotheld})),
|
||||
}
|
||||
|
||||
mutexApp10 mutex
|
||||
mutexApp20 mutex
|
||||
mutexApp30 mutex
|
||||
mutexLRU0 mutex
|
||||
mutexMaster0 mutex
|
||||
mutexMem0 mutex
|
||||
mutexOpen0 mutex
|
||||
mutexPMem0 mutex
|
||||
mutexPRNG0 mutex
|
||||
mutexVFS10 mutex
|
||||
mutexVFS20 mutex
|
||||
mutexVFS30 mutex
|
||||
|
||||
mutexApp1 = uintptr(unsafe.Pointer(&mutexApp10))
|
||||
mutexApp2 = uintptr(unsafe.Pointer(&mutexApp20))
|
||||
mutexApp3 = uintptr(unsafe.Pointer(&mutexApp30))
|
||||
mutexLRU = uintptr(unsafe.Pointer(&mutexLRU0))
|
||||
mutexMaster = uintptr(unsafe.Pointer(&mutexMaster0))
|
||||
mutexMem = uintptr(unsafe.Pointer(&mutexMem0))
|
||||
mutexOpen = uintptr(unsafe.Pointer(&mutexOpen0))
|
||||
mutexPMem = uintptr(unsafe.Pointer(&mutexPMem0))
|
||||
mutexPRNG = uintptr(unsafe.Pointer(&mutexPRNG0))
|
||||
mutexVFS1 = uintptr(unsafe.Pointer(&mutexVFS10))
|
||||
mutexVFS2 = uintptr(unsafe.Pointer(&mutexVFS20))
|
||||
mutexVFS3 = uintptr(unsafe.Pointer(&mutexVFS30))
|
||||
)
|
||||
|
||||
type mutex struct {
|
||||
sync.Mutex
|
||||
cnt int32
|
||||
id int32 // tls.ID
|
||||
recursive bool
|
||||
}
|
||||
|
||||
// int (*xMutexInit)(void);
|
||||
//
|
||||
// The xMutexInit method defined by this structure is invoked as part of system
|
||||
// initialization by the sqlite3_initialize() function. The xMutexInit routine
|
||||
// is called by SQLite exactly once for each effective call to
|
||||
// sqlite3_initialize().
|
||||
//
|
||||
// The xMutexInit() method must be threadsafe. It must be harmless to invoke
|
||||
// xMutexInit() multiple times within the same process and without intervening
|
||||
// calls to xMutexEnd(). Second and subsequent calls to xMutexInit() must be
|
||||
// no-ops. xMutexInit() must not use SQLite memory allocation (sqlite3_malloc()
|
||||
// and its associates).
|
||||
//
|
||||
// If xMutexInit fails in any way, it is expected to clean up after itself
|
||||
// prior to returning.
|
||||
func mutexInit(tls *libc.TLS) int32 { return SQLITE_OK }
|
||||
|
||||
// int (*xMutexEnd)(void);
|
||||
func mutexEnd(tls *libc.TLS) int32 { return SQLITE_OK }
|
||||
|
||||
// sqlite3_mutex *(*xMutexAlloc)(int);
|
||||
//
|
||||
// The sqlite3_mutex_alloc() routine allocates a new mutex and returns a
|
||||
// pointer to it. The sqlite3_mutex_alloc() routine returns NULL if it is
|
||||
// unable to allocate the requested mutex. The argument to
|
||||
// sqlite3_mutex_alloc() must one of these integer constants:
|
||||
//
|
||||
// SQLITE_MUTEX_FAST
|
||||
// SQLITE_MUTEX_RECURSIVE
|
||||
// SQLITE_MUTEX_STATIC_MASTER
|
||||
// SQLITE_MUTEX_STATIC_MEM
|
||||
// SQLITE_MUTEX_STATIC_OPEN
|
||||
// SQLITE_MUTEX_STATIC_PRNG
|
||||
// SQLITE_MUTEX_STATIC_LRU
|
||||
// SQLITE_MUTEX_STATIC_PMEM
|
||||
// SQLITE_MUTEX_STATIC_APP1
|
||||
// SQLITE_MUTEX_STATIC_APP2
|
||||
// SQLITE_MUTEX_STATIC_APP3
|
||||
// SQLITE_MUTEX_STATIC_VFS1
|
||||
// SQLITE_MUTEX_STATIC_VFS2
|
||||
// SQLITE_MUTEX_STATIC_VFS3
|
||||
//
|
||||
// The first two constants (SQLITE_MUTEX_FAST and SQLITE_MUTEX_RECURSIVE) cause
|
||||
// sqlite3_mutex_alloc() to create a new mutex. The new mutex is recursive when
|
||||
// SQLITE_MUTEX_RECURSIVE is used but not necessarily so when SQLITE_MUTEX_FAST
|
||||
// is used. The mutex implementation does not need to make a distinction
|
||||
// between SQLITE_MUTEX_RECURSIVE and SQLITE_MUTEX_FAST if it does not want to.
|
||||
// SQLite will only request a recursive mutex in cases where it really needs
|
||||
// one. If a faster non-recursive mutex implementation is available on the host
|
||||
// platform, the mutex subsystem might return such a mutex in response to
|
||||
// SQLITE_MUTEX_FAST.
|
||||
//
|
||||
// The other allowed parameters to sqlite3_mutex_alloc() (anything other than
|
||||
// SQLITE_MUTEX_FAST and SQLITE_MUTEX_RECURSIVE) each return a pointer to a
|
||||
// static preexisting mutex. Nine static mutexes are used by the current
|
||||
// version of SQLite. Future versions of SQLite may add additional static
|
||||
// mutexes. Static mutexes are for internal use by SQLite only. Applications
|
||||
// that use SQLite mutexes should use only the dynamic mutexes returned by
|
||||
// SQLITE_MUTEX_FAST or SQLITE_MUTEX_RECURSIVE.
|
||||
//
|
||||
// Note that if one of the dynamic mutex parameters (SQLITE_MUTEX_FAST or
|
||||
// SQLITE_MUTEX_RECURSIVE) is used then sqlite3_mutex_alloc() returns a
|
||||
// different mutex on every call. For the static mutex types, the same mutex is
|
||||
// returned on every call that has the same type number.
|
||||
func mutexAlloc(tls *libc.TLS, typ int32) (r uintptr) {
|
||||
switch typ {
|
||||
case SQLITE_MUTEX_FAST:
|
||||
r = libc.Xcalloc(tls, 1, types.Size_t(unsafe.Sizeof(mutex{})))
|
||||
return r
|
||||
case SQLITE_MUTEX_RECURSIVE:
|
||||
r = libc.Xcalloc(tls, 1, types.Size_t(unsafe.Sizeof(mutex{})))
|
||||
(*mutex)(unsafe.Pointer(r)).recursive = true
|
||||
return r
|
||||
case SQLITE_MUTEX_STATIC_MASTER:
|
||||
return mutexMaster
|
||||
case SQLITE_MUTEX_STATIC_MEM:
|
||||
return mutexMem
|
||||
case SQLITE_MUTEX_STATIC_OPEN:
|
||||
return mutexOpen
|
||||
case SQLITE_MUTEX_STATIC_PRNG:
|
||||
return mutexPRNG
|
||||
case SQLITE_MUTEX_STATIC_LRU:
|
||||
return mutexLRU
|
||||
case SQLITE_MUTEX_STATIC_PMEM:
|
||||
return mutexPMem
|
||||
case SQLITE_MUTEX_STATIC_APP1:
|
||||
return mutexApp1
|
||||
case SQLITE_MUTEX_STATIC_APP2:
|
||||
return mutexApp2
|
||||
case SQLITE_MUTEX_STATIC_APP3:
|
||||
return mutexApp3
|
||||
case SQLITE_MUTEX_STATIC_VFS1:
|
||||
return mutexVFS1
|
||||
case SQLITE_MUTEX_STATIC_VFS2:
|
||||
return mutexVFS2
|
||||
case SQLITE_MUTEX_STATIC_VFS3:
|
||||
return mutexVFS3
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
// void (*xMutexFree)(sqlite3_mutex *);
|
||||
func mutexFree(tls *libc.TLS, m uintptr) {
|
||||
libc.Xfree(tls, m)
|
||||
}
|
||||
|
||||
// The sqlite3_mutex_enter() and sqlite3_mutex_try() routines attempt to enter
|
||||
// a mutex. If another thread is already within the mutex,
|
||||
// sqlite3_mutex_enter() will block and sqlite3_mutex_try() will return
|
||||
// SQLITE_BUSY. The sqlite3_mutex_try() interface returns SQLITE_OK upon
|
||||
// successful entry. Mutexes created using SQLITE_MUTEX_RECURSIVE can be
|
||||
// entered multiple times by the same thread. In such cases, the mutex must be
|
||||
// exited an equal number of times before another thread can enter. If the same
|
||||
// thread tries to enter any mutex other than an SQLITE_MUTEX_RECURSIVE more
|
||||
// than once, the behavior is undefined.
|
||||
//
|
||||
// If the argument to sqlite3_mutex_enter(), sqlite3_mutex_try(), or
|
||||
// sqlite3_mutex_leave() is a NULL pointer, then all three routines behave as
|
||||
// no-ops.
|
||||
|
||||
// void (*xMutexEnter)(sqlite3_mutex *);
|
||||
func mutexEnter(tls *libc.TLS, m uintptr) {
|
||||
if m == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if !(*mutex)(unsafe.Pointer(m)).recursive {
|
||||
(*mutex)(unsafe.Pointer(m)).Lock()
|
||||
(*mutex)(unsafe.Pointer(m)).id = tls.ID
|
||||
return
|
||||
}
|
||||
|
||||
id := tls.ID
|
||||
if atomic.CompareAndSwapInt32(&(*mutex)(unsafe.Pointer(m)).id, 0, id) {
|
||||
(*mutex)(unsafe.Pointer(m)).cnt = 1
|
||||
(*mutex)(unsafe.Pointer(m)).Lock()
|
||||
return
|
||||
}
|
||||
|
||||
if atomic.LoadInt32(&(*mutex)(unsafe.Pointer(m)).id) == id {
|
||||
(*mutex)(unsafe.Pointer(m)).cnt++
|
||||
return
|
||||
}
|
||||
|
||||
for {
|
||||
(*mutex)(unsafe.Pointer(m)).Lock()
|
||||
if atomic.CompareAndSwapInt32(&(*mutex)(unsafe.Pointer(m)).id, 0, id) {
|
||||
(*mutex)(unsafe.Pointer(m)).cnt = 1
|
||||
return
|
||||
}
|
||||
|
||||
(*mutex)(unsafe.Pointer(m)).Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
// int (*xMutexTry)(sqlite3_mutex *);
|
||||
func mutexTry(tls *libc.TLS, m uintptr) int32 {
|
||||
if m == 0 {
|
||||
return SQLITE_OK
|
||||
}
|
||||
|
||||
if !(*mutex)(unsafe.Pointer(m)).recursive {
|
||||
if (*mutex)(unsafe.Pointer(m)).TryLock() {
|
||||
return SQLITE_OK
|
||||
}
|
||||
}
|
||||
|
||||
return SQLITE_BUSY
|
||||
}
|
||||
|
||||
// void (*xMutexLeave)(sqlite3_mutex *);
|
||||
func mutexLeave(tls *libc.TLS, m uintptr) {
|
||||
if m == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if !(*mutex)(unsafe.Pointer(m)).recursive {
|
||||
(*mutex)(unsafe.Pointer(m)).id = 0
|
||||
(*mutex)(unsafe.Pointer(m)).Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
if atomic.AddInt32(&(*mutex)(unsafe.Pointer(m)).cnt, -1) == 0 {
|
||||
atomic.StoreInt32(&(*mutex)(unsafe.Pointer(m)).id, 0)
|
||||
(*mutex)(unsafe.Pointer(m)).Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
// The sqlite3_mutex_held() and sqlite3_mutex_notheld() routines are intended
|
||||
// for use inside assert() statements. The SQLite core never uses these
|
||||
// routines except inside an assert() and applications are advised to follow
|
||||
// the lead of the core. The SQLite core only provides implementations for
|
||||
// these routines when it is compiled with the SQLITE_DEBUG flag. External
|
||||
// mutex implementations are only required to provide these routines if
|
||||
// SQLITE_DEBUG is defined and if NDEBUG is not defined.
|
||||
//
|
||||
// These routines should return true if the mutex in their argument is held or
|
||||
// not held, respectively, by the calling thread.
|
||||
//
|
||||
// The implementation is not required to provide versions of these routines
|
||||
// that actually work. If the implementation does not provide working versions
|
||||
// of these routines, it should at least provide stubs that always return true
|
||||
// so that one does not get spurious assertion failures.
|
||||
//
|
||||
// If the argument to sqlite3_mutex_held() is a NULL pointer then the routine
|
||||
// should return 1. This seems counter-intuitive since clearly the mutex cannot
|
||||
// be held if it does not exist. But the reason the mutex does not exist is
|
||||
// because the build is not using mutexes. And we do not want the assert()
|
||||
// containing the call to sqlite3_mutex_held() to fail, so a non-zero return is
|
||||
// the appropriate thing to do. The sqlite3_mutex_notheld() interface should
|
||||
// also return 1 when given a NULL pointer.
|
||||
|
||||
// int (*xMutexHeld)(sqlite3_mutex *);
|
||||
func mutexHeld(tls *libc.TLS, m uintptr) int32 {
|
||||
if m == 0 {
|
||||
return 1
|
||||
}
|
||||
|
||||
return libc.Bool32(atomic.LoadInt32(&(*mutex)(unsafe.Pointer(m)).id) == tls.ID)
|
||||
}
|
||||
|
||||
// int (*xMutexNotheld)(sqlite3_mutex *);
|
||||
func mutexNotheld(tls *libc.TLS, m uintptr) int32 {
|
||||
if m == 0 {
|
||||
return 1
|
||||
}
|
||||
|
||||
return libc.Bool32(atomic.LoadInt32(&(*mutex)(unsafe.Pointer(m)).id) != tls.ID)
|
||||
}
|
235840
lib/sqlite_darwin_amd64.go
Normal file
235840
lib/sqlite_darwin_amd64.go
Normal file
File diff suppressed because one or more lines are too long
235384
lib/sqlite_darwin_arm64.go
Normal file
235384
lib/sqlite_darwin_arm64.go
Normal file
File diff suppressed because one or more lines are too long
153539
lib/sqlite_freebsd_386.go
Normal file
153539
lib/sqlite_freebsd_386.go
Normal file
File diff suppressed because one or more lines are too long
229709
lib/sqlite_freebsd_amd64.go
Normal file
229709
lib/sqlite_freebsd_amd64.go
Normal file
File diff suppressed because one or more lines are too long
153633
lib/sqlite_freebsd_arm.go
Normal file
153633
lib/sqlite_freebsd_arm.go
Normal file
File diff suppressed because one or more lines are too long
229722
lib/sqlite_freebsd_arm64.go
Normal file
229722
lib/sqlite_freebsd_arm64.go
Normal file
File diff suppressed because one or more lines are too long
229883
lib/sqlite_linux_386.go
Normal file
229883
lib/sqlite_linux_386.go
Normal file
File diff suppressed because one or more lines are too long
229890
lib/sqlite_linux_amd64.go
Normal file
229890
lib/sqlite_linux_amd64.go
Normal file
File diff suppressed because one or more lines are too long
230379
lib/sqlite_linux_arm.go
Normal file
230379
lib/sqlite_linux_arm.go
Normal file
File diff suppressed because one or more lines are too long
229880
lib/sqlite_linux_arm64.go
Normal file
229880
lib/sqlite_linux_arm64.go
Normal file
File diff suppressed because one or more lines are too long
229979
lib/sqlite_linux_loong64.go
Normal file
229979
lib/sqlite_linux_loong64.go
Normal file
File diff suppressed because one or more lines are too long
229914
lib/sqlite_linux_ppc64le.go
Normal file
229914
lib/sqlite_linux_ppc64le.go
Normal file
File diff suppressed because one or more lines are too long
229873
lib/sqlite_linux_riscv64.go
Normal file
229873
lib/sqlite_linux_riscv64.go
Normal file
File diff suppressed because one or more lines are too long
229817
lib/sqlite_linux_s390x.go
Normal file
229817
lib/sqlite_linux_s390x.go
Normal file
File diff suppressed because one or more lines are too long
151230
lib/sqlite_netbsd_amd64.go
Normal file
151230
lib/sqlite_netbsd_amd64.go
Normal file
File diff suppressed because one or more lines are too long
153058
lib/sqlite_openbsd_amd64.go
Normal file
153058
lib/sqlite_openbsd_amd64.go
Normal file
File diff suppressed because one or more lines are too long
153063
lib/sqlite_openbsd_arm64.go
Normal file
153063
lib/sqlite_openbsd_arm64.go
Normal file
File diff suppressed because one or more lines are too long
304364
lib/sqlite_windows.go
Normal file
304364
lib/sqlite_windows.go
Normal file
File diff suppressed because one or more lines are too long
304468
lib/sqlite_windows_386.go
Normal file
304468
lib/sqlite_windows_386.go
Normal file
File diff suppressed because one or more lines are too long
BIN
logo.png
Normal file
BIN
logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 39 KiB |
23
mutex.go
Normal file
23
mutex.go
Normal file
|
@ -0,0 +1,23 @@
|
|||
// Copyright 2019 The Sqlite 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 sqlite // import "modernc.org/sqlite"
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"unsafe"
|
||||
|
||||
"modernc.org/libc"
|
||||
"modernc.org/libc/sys/types"
|
||||
)
|
||||
|
||||
type mutex struct {
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
func mutexAlloc(tls *libc.TLS) uintptr {
|
||||
return libc.Xcalloc(tls, 1, types.Size_t(unsafe.Sizeof(mutex{})))
|
||||
}
|
||||
|
||||
func mutexFree(tls *libc.TLS, m uintptr) { libc.Xfree(tls, m) }
|
12
nodmesg.go
Normal file
12
nodmesg.go
Normal file
|
@ -0,0 +1,12 @@
|
|||
// Copyright 2023 The Sqlite Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !sqlite.dmesg
|
||||
// +build !sqlite.dmesg
|
||||
|
||||
package sqlite // import "modernc.org/sqlite"
|
||||
|
||||
const dmesgs = false
|
||||
|
||||
func dmesg(s string, args ...interface{}) {}
|
10
norlimit.go
Normal file
10
norlimit.go
Normal file
|
@ -0,0 +1,10 @@
|
|||
// Copyright 2021 The Sqlite Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package sqlite // import "modernc.org/sqlite"
|
||||
|
||||
func setMaxOpenFiles(n int) error { return nil }
|
26
null_test.go
Normal file
26
null_test.go
Normal file
|
@ -0,0 +1,26 @@
|
|||
package sqlite
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNullBinding(t *testing.T) {
|
||||
db, err := sql.Open("sqlite", "file::memory:")
|
||||
if err != nil {
|
||||
t.Errorf("cannot open: %v", err)
|
||||
return
|
||||
}
|
||||
_, err = db.Exec(`
|
||||
CREATE TABLE table1 (field1 varchar NULL);
|
||||
INSERT INTO table1 (field1) VALUES (?);
|
||||
`, sql.NullString{})
|
||||
if err != nil {
|
||||
t.Errorf("Error binding null: %v", err)
|
||||
}
|
||||
err = db.Close()
|
||||
if err != nil {
|
||||
t.Errorf("cannot close: %v", err)
|
||||
return
|
||||
}
|
||||
}
|
19
rlimit.go
Normal file
19
rlimit.go
Normal file
|
@ -0,0 +1,19 @@
|
|||
// Copyright 2021 The Sqlite Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build freebsd
|
||||
// +build freebsd
|
||||
|
||||
package sqlite // import "modernc.org/sqlite"
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func setMaxOpenFiles(n int64) error {
|
||||
var rLimit unix.Rlimit
|
||||
rLimit.Max = n
|
||||
rLimit.Cur = n
|
||||
return unix.Setrlimit(unix.RLIMIT_NOFILE, &rLimit)
|
||||
}
|
19
rulimit.go
Normal file
19
rulimit.go
Normal file
|
@ -0,0 +1,19 @@
|
|||
// Copyright 2021 The Sqlite Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build linux || darwin || netbsd || openbsd
|
||||
// +build linux darwin netbsd openbsd
|
||||
|
||||
package sqlite // import "modernc.org/sqlite"
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func setMaxOpenFiles(n int64) error {
|
||||
var rLimit unix.Rlimit
|
||||
rLimit.Max = uint64(n)
|
||||
rLimit.Cur = uint64(n)
|
||||
return unix.Setrlimit(unix.RLIMIT_NOFILE, &rLimit)
|
||||
}
|
84
sqlite_go18.go
Normal file
84
sqlite_go18.go
Normal file
|
@ -0,0 +1,84 @@
|
|||
// Copyright 2017 The Sqlite Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build go1.8
|
||||
// +build go1.8
|
||||
|
||||
package sqlite // import "modernc.org/sqlite"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql/driver"
|
||||
)
|
||||
|
||||
// Ping implements driver.Pinger
|
||||
func (c *conn) Ping(ctx context.Context) (err error) {
|
||||
if dmesgs {
|
||||
defer func() {
|
||||
dmesg("conn %p, ctx %p: err %v", c, ctx, err)
|
||||
}()
|
||||
}
|
||||
_, err = c.ExecContext(ctx, "select 1", nil)
|
||||
return err
|
||||
}
|
||||
|
||||
// BeginTx implements driver.ConnBeginTx
|
||||
func (c *conn) BeginTx(ctx context.Context, opts driver.TxOptions) (dt driver.Tx, err error) {
|
||||
if dmesgs {
|
||||
defer func() {
|
||||
dmesg("conn %p, ctx %p, opts %+v: (driver.Tx %v, err %v)", c, ctx, opts, dt, err)
|
||||
}()
|
||||
}
|
||||
return c.begin(ctx, opts)
|
||||
}
|
||||
|
||||
// PrepareContext implements driver.ConnPrepareContext
|
||||
func (c *conn) PrepareContext(ctx context.Context, query string) (ds driver.Stmt, err error) {
|
||||
if dmesgs {
|
||||
defer func() {
|
||||
dmesg("conn %p, ctx %p, query %q: (driver.Stmt %v, err %v)", c, ctx, query, ds, err)
|
||||
}()
|
||||
}
|
||||
return c.prepare(ctx, query)
|
||||
}
|
||||
|
||||
// ExecContext implements driver.ExecerContext
|
||||
func (c *conn) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (dr driver.Result, err error) {
|
||||
if dmesgs {
|
||||
defer func() {
|
||||
dmesg("conn %p, ctx %p, query %q, args %v: (driver.Result %p, err %v)", c, ctx, query, args, dr, err)
|
||||
}()
|
||||
}
|
||||
return c.exec(ctx, query, args)
|
||||
}
|
||||
|
||||
// QueryContext implements driver.QueryerContext
|
||||
func (c *conn) QueryContext(ctx context.Context, query string, args []driver.NamedValue) (dr driver.Rows, err error) {
|
||||
if dmesgs {
|
||||
defer func() {
|
||||
dmesg("conn %p, ctx %p, query %q, args %v: (driver.Rows %p, err %v)", c, ctx, query, args, dr, err)
|
||||
}()
|
||||
}
|
||||
return c.query(ctx, query, args)
|
||||
}
|
||||
|
||||
// ExecContext implements driver.StmtExecContext
|
||||
func (s *stmt) ExecContext(ctx context.Context, args []driver.NamedValue) (dr driver.Result, err error) {
|
||||
if dmesgs {
|
||||
defer func() {
|
||||
dmesg("stmt %p, ctx %p, args %v: (driver.Result %p, err %v)", s, ctx, args, dr, err)
|
||||
}()
|
||||
}
|
||||
return s.exec(ctx, args)
|
||||
}
|
||||
|
||||
// QueryContext implements driver.StmtQueryContext
|
||||
func (s *stmt) QueryContext(ctx context.Context, args []driver.NamedValue) (dr driver.Rows, err error) {
|
||||
if dmesgs {
|
||||
defer func() {
|
||||
dmesg("stmt %p, ctx %p, args %v: (driver.Rows %p, err %v)", s, ctx, args, dr, err)
|
||||
}()
|
||||
}
|
||||
return s.query(ctx, args)
|
||||
}
|
49
sqlite_go18_test.go
Normal file
49
sqlite_go18_test.go
Normal file
|
@ -0,0 +1,49 @@
|
|||
// Copyright 2017 The Sqlite Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build go1.8
|
||||
// +build go1.8
|
||||
|
||||
package sqlite // import "modernc.org/sqlite"
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNamedParameters(t *testing.T) {
|
||||
dir, db := tempDB(t)
|
||||
defer func() {
|
||||
db.Close()
|
||||
os.RemoveAll(dir)
|
||||
}()
|
||||
|
||||
_, err := db.Exec(`
|
||||
create table t(s1 varchar(32), s2 varchar(32), s3 varchar(32), s4 varchar(32));
|
||||
insert into t values(?, @aa, $aa, @bb);
|
||||
`, "1", sql.Named("aa", "one"), sql.Named("bb", "two"))
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
rows, err := db.Query("select * from t")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
rec := make([]string, 4)
|
||||
for rows.Next() {
|
||||
if err := rows.Scan(&rec[0], &rec[1], &rec[2], &rec[3]); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
w := []string{"1", "one", "one", "two"}
|
||||
if !reflect.DeepEqual(rec, w) {
|
||||
t.Fatal(rec, w)
|
||||
}
|
||||
}
|
2
testdata/.gitignore
vendored
Normal file
2
testdata/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
sqlite-*
|
||||
SQLite-*
|
1474
testdata/mptest.c
vendored
Normal file
1474
testdata/mptest.c
vendored
Normal file
File diff suppressed because it is too large
Load diff
439
testdata/overlay/malloc5.test
vendored
Normal file
439
testdata/overlay/malloc5.test
vendored
Normal file
|
@ -0,0 +1,439 @@
|
|||
# 2005 November 30
|
||||
#
|
||||
# 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 contains test cases focused on the two memory-management APIs,
|
||||
# sqlite3_soft_heap_limit() and sqlite3_release_memory().
|
||||
#
|
||||
# Prior to version 3.6.2, calling sqlite3_release_memory() or exceeding
|
||||
# the configured soft heap limit could cause sqlite to upgrade database
|
||||
# locks and flush dirty pages to the file system. As of 3.6.2, this is
|
||||
# no longer the case. In version 3.6.2, sqlite3_release_memory() only
|
||||
# reclaims clean pages. This test file has been updated accordingly.
|
||||
#
|
||||
# $Id: malloc5.test,v 1.22 2009/04/11 19:09:54 drh Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
source $testdir/malloc_common.tcl
|
||||
db close
|
||||
|
||||
# Only run these tests if memory debugging is turned on.
|
||||
#
|
||||
if {!$MEMDEBUG} {
|
||||
puts "Skipping malloc5 tests: not compiled with -DSQLITE_MEMDEBUG..."
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
# Skip these tests if OMIT_MEMORY_MANAGEMENT was defined at compile time.
|
||||
ifcapable !memorymanage {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
# The sizes of memory allocations from system malloc() might vary,
|
||||
# depending on the memory allocator algorithms used. The following
|
||||
# routine is designed to support answers that fall within a range
|
||||
# of values while also supplying easy-to-understand "expected" values
|
||||
# when errors occur.
|
||||
#
|
||||
proc value_in_range {target x args} {
|
||||
set v [lindex $args 0]
|
||||
if {$v!=""} {
|
||||
if {$v<$target*$x} {return $v}
|
||||
if {$v>$target/$x} {return $v}
|
||||
}
|
||||
return "number between [expr {int($target*$x)}] and [expr {int($target/$x)}]"
|
||||
}
|
||||
set mrange 0.98 ;# plus or minus 2%
|
||||
|
||||
test_set_config_pagecache 0 100
|
||||
|
||||
sqlite3_soft_heap_limit 0
|
||||
sqlite3 db test.db
|
||||
# db eval {PRAGMA cache_size=1}
|
||||
|
||||
do_test malloc5-1.1 {
|
||||
# Simplest possible test. Call sqlite3_release_memory when there is exactly
|
||||
# one unused page in a single pager cache. The page cannot be freed, as
|
||||
# it is dirty. So sqlite3_release_memory() returns 0.
|
||||
#
|
||||
execsql {
|
||||
PRAGMA auto_vacuum=OFF;
|
||||
BEGIN;
|
||||
CREATE TABLE abc(a, b, c);
|
||||
}
|
||||
sqlite3_release_memory
|
||||
} {0}
|
||||
|
||||
do_test malloc5-1.2 {
|
||||
# Test that the transaction started in the above test is still active.
|
||||
# The lock on the database file should not have been upgraded (this was
|
||||
# not the case before version 3.6.2).
|
||||
#
|
||||
sqlite3 db2 test.db
|
||||
execsql {PRAGMA cache_size=2; SELECT * FROM sqlite_master } db2
|
||||
} {}
|
||||
do_test malloc5-1.3 {
|
||||
# Call [sqlite3_release_memory] when there is exactly one unused page
|
||||
# in the cache belonging to db2.
|
||||
#
|
||||
set ::pgalloc [sqlite3_release_memory]
|
||||
value_in_range 1288 0.75
|
||||
} [value_in_range 1288 0.75]
|
||||
|
||||
do_test malloc5-1.4 {
|
||||
# Commit the transaction and open a new one. Read 1 page into the cache.
|
||||
# Because the page is not dirty, it is eligible for collection even
|
||||
# before the transaction is concluded.
|
||||
#
|
||||
execsql {
|
||||
COMMIT;
|
||||
BEGIN;
|
||||
SELECT * FROM abc;
|
||||
}
|
||||
value_in_range $::pgalloc $::mrange [sqlite3_release_memory]
|
||||
} [value_in_range $::pgalloc $::mrange]
|
||||
|
||||
do_test malloc5-1.5 {
|
||||
# Conclude the transaction opened in the previous [do_test] block. This
|
||||
# causes another page (page 1) to become eligible for recycling.
|
||||
#
|
||||
execsql { COMMIT }
|
||||
value_in_range $::pgalloc $::mrange [sqlite3_release_memory]
|
||||
} [value_in_range $::pgalloc $::mrange]
|
||||
|
||||
do_test malloc5-1.6 {
|
||||
# Manipulate the cache so that it contains two unused pages. One requires
|
||||
# a journal-sync to free, the other does not.
|
||||
db2 close
|
||||
execsql {
|
||||
BEGIN;
|
||||
CREATE TABLE def(d, e, f);
|
||||
SELECT * FROM abc;
|
||||
}
|
||||
value_in_range $::pgalloc $::mrange [sqlite3_release_memory 500]
|
||||
} [value_in_range $::pgalloc $::mrange]
|
||||
do_test malloc5-1.7 {
|
||||
# Database should not be locked this time.
|
||||
sqlite3 db2 test.db
|
||||
catchsql { SELECT * FROM abc } db2
|
||||
} {0 {}}
|
||||
do_test malloc5-1.8 {
|
||||
# Try to release another block of memory. This will fail as the only
|
||||
# pages currently in the cache are dirty (page 3) or pinned (page 1).
|
||||
db2 close
|
||||
sqlite3_release_memory 500
|
||||
} 0
|
||||
do_test malloc5-1.8 {
|
||||
# Database is still not locked.
|
||||
#
|
||||
sqlite3 db2 test.db
|
||||
catchsql { SELECT * FROM abc } db2
|
||||
} {0 {}}
|
||||
do_test malloc5-1.9 {
|
||||
execsql {
|
||||
COMMIT;
|
||||
}
|
||||
} {}
|
||||
|
||||
do_test malloc5-2.1 {
|
||||
# Put some data in tables abc and def. Both tables are still wholly
|
||||
# contained within their root pages.
|
||||
execsql {
|
||||
INSERT INTO abc VALUES(1, 2, 3);
|
||||
INSERT INTO abc VALUES(4, 5, 6);
|
||||
INSERT INTO def VALUES(7, 8, 9);
|
||||
INSERT INTO def VALUES(10,11,12);
|
||||
}
|
||||
} {}
|
||||
do_test malloc5-2.2 {
|
||||
# Load the root-page for table def into the cache. Then query table abc.
|
||||
# Halfway through the query call sqlite3_release_memory(). The goal of this
|
||||
# test is to make sure we don't free pages that are in use (specifically,
|
||||
# the root of table abc).
|
||||
sqlite3_release_memory
|
||||
set nRelease 0
|
||||
execsql {
|
||||
BEGIN;
|
||||
SELECT * FROM def;
|
||||
}
|
||||
set data [list]
|
||||
db eval {SELECT * FROM abc} {
|
||||
incr nRelease [sqlite3_release_memory]
|
||||
lappend data $a $b $c
|
||||
}
|
||||
execsql {
|
||||
COMMIT;
|
||||
}
|
||||
value_in_range $::pgalloc $::mrange $nRelease
|
||||
} [value_in_range $::pgalloc $::mrange]
|
||||
do_test malloc5-2.2.1 {
|
||||
set data
|
||||
} {1 2 3 4 5 6}
|
||||
|
||||
do_test malloc5-3.1 {
|
||||
# Simple test to show that if two pagers are opened from within this
|
||||
# thread, memory is freed from both when sqlite3_release_memory() is
|
||||
# called.
|
||||
execsql {
|
||||
BEGIN;
|
||||
SELECT * FROM abc;
|
||||
}
|
||||
execsql {
|
||||
SELECT * FROM sqlite_master;
|
||||
BEGIN;
|
||||
SELECT * FROM def;
|
||||
} db2
|
||||
value_in_range [expr $::pgalloc*2] 0.99 [sqlite3_release_memory]
|
||||
} [value_in_range [expr $::pgalloc * 2] 0.99]
|
||||
do_test malloc5-3.2 {
|
||||
concat \
|
||||
[execsql {SELECT * FROM abc; COMMIT}] \
|
||||
[execsql {SELECT * FROM def; COMMIT} db2]
|
||||
} {1 2 3 4 5 6 7 8 9 10 11 12}
|
||||
|
||||
db2 close
|
||||
puts "Highwater mark: [sqlite3_memory_highwater]"
|
||||
|
||||
# The following two test cases each execute a transaction in which
|
||||
# 10000 rows are inserted into table abc. The first test case is used
|
||||
# to ensure that more than 1MB of dynamic memory is used to perform
|
||||
# the transaction.
|
||||
#
|
||||
# The second test case sets the "soft-heap-limit" to 100,000 bytes (0.1 MB)
|
||||
# and tests to see that this limit is not exceeded at any point during
|
||||
# transaction execution.
|
||||
#
|
||||
# Before executing malloc5-4.* we save the value of the current soft heap
|
||||
# limit in variable ::soft_limit. The original value is restored after
|
||||
# running the tests.
|
||||
#
|
||||
set ::soft_limit [sqlite3_soft_heap_limit -1]
|
||||
execsql {PRAGMA cache_size=2000}
|
||||
|
||||
# Test requires sqliteconfig.FbMemstat = 1 to measure highwater mark.
|
||||
# We are not built with that enabled, currently
|
||||
# -DSQLITE_DEFAULT_MEMSTATUS=0
|
||||
if {$::tcl_platform(platform)!="windows"} {
|
||||
|
||||
do_test malloc5-4.1 {
|
||||
execsql {BEGIN;}
|
||||
execsql {DELETE FROM abc;}
|
||||
for {set i 0} {$i < 10000} {incr i} {
|
||||
execsql "INSERT INTO abc VALUES($i, $i, '[string repeat X 100]');"
|
||||
}
|
||||
execsql {COMMIT;}
|
||||
db cache flush
|
||||
sqlite3_release_memory
|
||||
sqlite3_memory_highwater 1
|
||||
execsql {SELECT * FROM abc}
|
||||
set nMaxBytes [sqlite3_memory_highwater 1]
|
||||
puts -nonewline " (Highwater mark: $nMaxBytes) "
|
||||
expr $nMaxBytes > 1000000
|
||||
} {1}
|
||||
|
||||
do_test malloc5-4.2 {
|
||||
db eval {PRAGMA cache_size=1}
|
||||
db cache flush
|
||||
sqlite3_release_memory
|
||||
sqlite3_soft_heap_limit 200000
|
||||
sqlite3_memory_highwater 1
|
||||
execsql {SELECT * FROM abc}
|
||||
set nMaxBytes [sqlite3_memory_highwater 1]
|
||||
puts -nonewline " (Highwater mark: $nMaxBytes) "
|
||||
expr $nMaxBytes <= 210000
|
||||
} {1}
|
||||
|
||||
do_test malloc5-4.3 {
|
||||
# Check that the content of table abc is at least roughly as expected.
|
||||
execsql {
|
||||
SELECT count(*), sum(a), sum(b) FROM abc;
|
||||
}
|
||||
} [list 10000 [expr int(10000.0 * 4999.5)] [expr int(10000.0 * 4999.5)]]
|
||||
|
||||
}
|
||||
|
||||
# Restore the soft heap limit.
|
||||
sqlite3_soft_heap_limit $::soft_limit
|
||||
|
||||
# Test that there are no problems calling sqlite3_release_memory when
|
||||
# there are open in-memory databases.
|
||||
#
|
||||
# At one point these tests would cause a seg-fault.
|
||||
#
|
||||
do_test malloc5-5.1 {
|
||||
db close
|
||||
sqlite3 db :memory:
|
||||
execsql {
|
||||
BEGIN;
|
||||
CREATE TABLE abc(a, b, c);
|
||||
INSERT INTO abc VALUES('abcdefghi', 1234567890, NULL);
|
||||
INSERT INTO abc SELECT * FROM abc;
|
||||
INSERT INTO abc SELECT * FROM abc;
|
||||
INSERT INTO abc SELECT * FROM abc;
|
||||
INSERT INTO abc SELECT * FROM abc;
|
||||
INSERT INTO abc SELECT * FROM abc;
|
||||
INSERT INTO abc SELECT * FROM abc;
|
||||
INSERT INTO abc SELECT * FROM abc;
|
||||
}
|
||||
sqlite3_release_memory
|
||||
} 0
|
||||
do_test malloc5-5.2 {
|
||||
sqlite3_soft_heap_limit 5000
|
||||
execsql {
|
||||
COMMIT;
|
||||
PRAGMA temp_store = memory;
|
||||
SELECT * FROM abc ORDER BY a;
|
||||
}
|
||||
expr 1
|
||||
} {1}
|
||||
sqlite3_soft_heap_limit $::soft_limit
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# The following test cases (malloc5-6.*) test the new global LRU list
|
||||
# used to determine the pages to recycle when sqlite3_release_memory is
|
||||
# called and there is more than one pager open.
|
||||
#
|
||||
proc nPage {db} {
|
||||
set bt [btree_from_db $db]
|
||||
array set stats [btree_pager_stats $bt]
|
||||
set stats(page)
|
||||
}
|
||||
db close
|
||||
forcedelete test.db test.db-journal test2.db test2.db-journal
|
||||
|
||||
# This block of test-cases (malloc5-6.1.*) prepares two database files
|
||||
# for the subsequent tests.
|
||||
do_test malloc5-6.1.1 {
|
||||
sqlite3 db test.db
|
||||
execsql {
|
||||
PRAGMA page_size=1024;
|
||||
PRAGMA default_cache_size=2;
|
||||
}
|
||||
execsql {
|
||||
PRAGMA temp_store = memory;
|
||||
BEGIN;
|
||||
CREATE TABLE abc(a PRIMARY KEY, b, c);
|
||||
INSERT INTO abc VALUES(randstr(50,50), randstr(75,75), randstr(100,100));
|
||||
INSERT INTO abc
|
||||
SELECT randstr(50,50), randstr(75,75), randstr(100,100) FROM abc;
|
||||
INSERT INTO abc
|
||||
SELECT randstr(50,50), randstr(75,75), randstr(100,100) FROM abc;
|
||||
INSERT INTO abc
|
||||
SELECT randstr(50,50), randstr(75,75), randstr(100,100) FROM abc;
|
||||
INSERT INTO abc
|
||||
SELECT randstr(50,50), randstr(75,75), randstr(100,100) FROM abc;
|
||||
INSERT INTO abc
|
||||
SELECT randstr(50,50), randstr(75,75), randstr(100,100) FROM abc;
|
||||
INSERT INTO abc
|
||||
SELECT randstr(50,50), randstr(75,75), randstr(100,100) FROM abc;
|
||||
COMMIT;
|
||||
}
|
||||
forcecopy test.db test2.db
|
||||
sqlite3 db2 test2.db
|
||||
db2 eval {PRAGMA cache_size=2}
|
||||
list \
|
||||
[expr ([file size test.db]/1024)>20] [expr ([file size test2.db]/1024)>20]
|
||||
} {1 1}
|
||||
do_test malloc5-6.1.2 {
|
||||
list [execsql {PRAGMA cache_size}] [execsql {PRAGMA cache_size} db2]
|
||||
} {2 2}
|
||||
|
||||
do_test malloc5-6.2.1 {
|
||||
execsql {SELECT * FROM abc} db2
|
||||
execsql {SELECT * FROM abc} db
|
||||
expr [nPage db] + [nPage db2]
|
||||
} {4}
|
||||
|
||||
|
||||
# Our min-useable malloc block-size appears to be 2k (actual)
|
||||
# Because this test attempts to measure actual memory freed
|
||||
# causing 2 blocks to be freed will free 4K, failing the tests
|
||||
if {$::tcl_platform(platform)!="windows"} {
|
||||
|
||||
do_test malloc5-6.2.2 {
|
||||
# If we now try to reclaim some memory, it should come from the db2 cache.
|
||||
sqlite3_release_memory 3000
|
||||
expr [nPage db] + [nPage db2]
|
||||
} {1}
|
||||
do_test malloc5-6.2.3 {
|
||||
# Access the db2 cache again, so that all the db2 pages have been used
|
||||
# more recently than all the db pages. Then try to reclaim 3000 bytes.
|
||||
# This time, 3 pages should be pulled from the db cache.
|
||||
execsql { SELECT * FROM abc } db2
|
||||
sqlite3_release_memory 3000
|
||||
expr [nPage db] + [nPage db2]
|
||||
} {0}
|
||||
|
||||
}
|
||||
|
||||
do_test malloc5-6.3.1 {
|
||||
# Now open a transaction and update 2 pages in the db2 cache. Then
|
||||
# do a SELECT on the db cache so that all the db pages are more recently
|
||||
# used than the db2 pages. When we try to free memory, SQLite should
|
||||
# free the non-dirty db2 pages, then the db pages, then finally use
|
||||
# sync() to free up the dirty db2 pages. The only page that cannot be
|
||||
# freed is page1 of db2. Because there is an open transaction, the
|
||||
# btree layer holds a reference to page 1 in the db2 cache.
|
||||
#
|
||||
# UPDATE: No longer. As release_memory() does not cause a sync()
|
||||
execsql {
|
||||
BEGIN;
|
||||
UPDATE abc SET c = randstr(100,100)
|
||||
WHERE rowid = 1 OR rowid = (SELECT max(rowid) FROM abc);
|
||||
} db2
|
||||
execsql { SELECT * FROM abc } db
|
||||
expr [nPage db] + [nPage db2]
|
||||
} {4}
|
||||
do_test malloc5-6.3.2 {
|
||||
# Try to release 7700 bytes. This should release all the
|
||||
# non-dirty pages held by db2.
|
||||
sqlite3_release_memory [expr 7*1132]
|
||||
list [nPage db] [nPage db2]
|
||||
} {0 3}
|
||||
do_test malloc5-6.3.3 {
|
||||
# Try to release another 1000 bytes. This should come fromt the db
|
||||
# cache, since all three pages held by db2 are either in-use or diry.
|
||||
sqlite3_release_memory 1000
|
||||
list [nPage db] [nPage db2]
|
||||
} {0 3}
|
||||
do_test malloc5-6.3.4 {
|
||||
# Now release 9900 more (about 9 pages worth). This should expunge
|
||||
# the rest of the db cache. But the db2 cache remains intact, because
|
||||
# SQLite tries to avoid calling sync().
|
||||
if {$::tcl_platform(wordSize)==8} {
|
||||
sqlite3_release_memory 10500
|
||||
} else {
|
||||
sqlite3_release_memory 9900
|
||||
}
|
||||
list [nPage db] [nPage db2]
|
||||
} {0 3}
|
||||
do_test malloc5-6.3.5 {
|
||||
# But if we are really insistent, SQLite will consent to call sync()
|
||||
# if there is no other option. UPDATE: As of 3.6.2, SQLite will not
|
||||
# call sync() in this scenario. So no further memory can be reclaimed.
|
||||
sqlite3_release_memory 1000
|
||||
list [nPage db] [nPage db2]
|
||||
} {0 3}
|
||||
do_test malloc5-6.3.6 {
|
||||
# The referenced page (page 1 of the db2 cache) will not be freed no
|
||||
# matter how much memory we ask for:
|
||||
sqlite3_release_memory 31459
|
||||
list [nPage db] [nPage db2]
|
||||
} {0 3}
|
||||
|
||||
db2 close
|
||||
|
||||
sqlite3_soft_heap_limit $::soft_limit
|
||||
test_restore_config_pagecache
|
||||
finish_test
|
||||
catch {db close}
|
258
testdata/overlay/snapshot_fault.test
vendored
Normal file
258
testdata/overlay/snapshot_fault.test
vendored
Normal file
|
@ -0,0 +1,258 @@
|
|||
# 2015 December 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 regression tests for SQLite library. The focus
|
||||
# of this file is the sqlite3_snapshot_xxx() APIs.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
ifcapable !snapshot {finish_test; return}
|
||||
set testprefix snapshot_fault
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Check that an sqlite3_snapshot_open() client cannot be tricked into
|
||||
# reading a corrupt snapshot even if a second client fails while
|
||||
# checkpointing the db.
|
||||
#
|
||||
|
||||
# This test relies on a forcedelete of an open file
|
||||
# resulting in: error deleting "test.db": permission denied
|
||||
# Not possible to remove the open file
|
||||
if {$::tcl_platform(platform)!="windows"} {
|
||||
|
||||
do_faultsim_test 1.0 -prep {
|
||||
faultsim_delete_and_reopen
|
||||
sqlite3 db2 test.db
|
||||
db2 eval {
|
||||
CREATE TABLE t1(a, b UNIQUE, c UNIQUE);
|
||||
INSERT INTO t1 VALUES(1, randomblob(500), randomblob(500));
|
||||
INSERT INTO t1 VALUES(2, randomblob(500), randomblob(500));
|
||||
PRAGMA journal_mode = wal;
|
||||
INSERT INTO t1 VALUES(3, randomblob(500), randomblob(500));
|
||||
BEGIN;
|
||||
SELECT a FROM t1;
|
||||
}
|
||||
set ::snapshot [sqlite3_snapshot_get db2 main]
|
||||
db2 eval COMMIT
|
||||
db2 eval {
|
||||
UPDATE t1 SET b=randomblob(501), c=randomblob(501) WHERE a=1;
|
||||
INSERT INTO t1 VALUES(4, randomblob(500), randomblob(500));
|
||||
INSERT INTO t1 VALUES(5, randomblob(500), randomblob(500));
|
||||
INSERT INTO t1 VALUES(6, randomblob(500), randomblob(500));
|
||||
}
|
||||
} -body {
|
||||
db eval { PRAGMA wal_checkpoint }
|
||||
} -test {
|
||||
db2 eval BEGIN
|
||||
if {[catch { sqlite3_snapshot_open db2 main $::snapshot } msg]} {
|
||||
if {$msg != "SQLITE_ERROR_SNAPSHOT" && $msg != "SQLITE_BUSY"} {
|
||||
error "error is $msg"
|
||||
}
|
||||
} else {
|
||||
set res [db2 eval {
|
||||
SELECT a FROM t1;
|
||||
PRAGMA integrity_check;
|
||||
}]
|
||||
if {$res != "1 2 3 ok"} { error "res is $res" }
|
||||
}
|
||||
|
||||
sqlite3_snapshot_free $::snapshot
|
||||
}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# This test is similar to the previous one. Except, after the
|
||||
# "PRAGMA wal_checkpoint" command fails the db is closed and reopened
|
||||
# so as to require wal file recovery. It should not be possible to open
|
||||
# a snapshot that is part of the body of a recovered wal file.
|
||||
#
|
||||
do_faultsim_test 2.0 -prep {
|
||||
faultsim_delete_and_reopen
|
||||
db eval {
|
||||
CREATE TABLE t1(a, b UNIQUE, c UNIQUE);
|
||||
INSERT INTO t1 VALUES(1, randomblob(500), randomblob(500));
|
||||
INSERT INTO t1 VALUES(2, randomblob(500), randomblob(500));
|
||||
PRAGMA journal_mode = wal;
|
||||
INSERT INTO t1 VALUES(3, randomblob(500), randomblob(500));
|
||||
BEGIN;
|
||||
SELECT a FROM t1;
|
||||
}
|
||||
set ::snapshot [sqlite3_snapshot_get db main]
|
||||
db eval COMMIT
|
||||
|
||||
db eval {
|
||||
UPDATE t1 SET b=randomblob(501), c=randomblob(501) WHERE a=1;
|
||||
INSERT INTO t1 VALUES(4, randomblob(500), randomblob(500));
|
||||
INSERT INTO t1 VALUES(5, randomblob(500), randomblob(500));
|
||||
INSERT INTO t1 VALUES(6, randomblob(500), randomblob(500));
|
||||
}
|
||||
} -body {
|
||||
db eval { PRAGMA wal_checkpoint }
|
||||
} -test {
|
||||
|
||||
db_save
|
||||
db close
|
||||
db_restore_and_reopen
|
||||
db eval { SELECT * FROM t1 }
|
||||
|
||||
db eval BEGIN
|
||||
if {[catch { sqlite3_snapshot_open db main $::snapshot } msg]} {
|
||||
if {$msg != "SQLITE_ERROR_SNAPSHOT" && $msg != "SQLITE_BUSY"} {
|
||||
error "error is $msg"
|
||||
}
|
||||
} else {
|
||||
# This branch should actually never be taken. But it was useful in
|
||||
# determining whether or not this test was actually working (by
|
||||
# running a modified version of SQLite that allowed snapshots to be
|
||||
# opened following a recovery).
|
||||
error "TEST HAS FAILED"
|
||||
|
||||
set res [db eval {
|
||||
SELECT a FROM t1;
|
||||
PRAGMA integrity_check;
|
||||
}]
|
||||
if {$res != "1 2 3 ok"} { error "res is $res" }
|
||||
}
|
||||
|
||||
sqlite3_snapshot_free $::snapshot
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test the handling of faults that occur within sqlite3_snapshot_open().
|
||||
#
|
||||
do_faultsim_test 3.0 -prep {
|
||||
faultsim_delete_and_reopen
|
||||
db eval {
|
||||
CREATE TABLE t1(a, b UNIQUE, c UNIQUE);
|
||||
INSERT INTO t1 VALUES(1, randomblob(500), randomblob(500));
|
||||
INSERT INTO t1 VALUES(2, randomblob(500), randomblob(500));
|
||||
PRAGMA journal_mode = wal;
|
||||
INSERT INTO t1 VALUES(3, randomblob(500), randomblob(500));
|
||||
BEGIN;
|
||||
SELECT a FROM t1;
|
||||
}
|
||||
set ::snapshot [sqlite3_snapshot_get db main]
|
||||
db eval COMMIT
|
||||
db eval {
|
||||
UPDATE t1 SET b=randomblob(501), c=randomblob(501) WHERE a=1;
|
||||
INSERT INTO t1 VALUES(4, randomblob(500), randomblob(500));
|
||||
INSERT INTO t1 VALUES(5, randomblob(500), randomblob(500));
|
||||
INSERT INTO t1 VALUES(6, randomblob(500), randomblob(500));
|
||||
BEGIN;
|
||||
}
|
||||
} -body {
|
||||
if { [catch { sqlite3_snapshot_open db main $::snapshot } msg] } {
|
||||
error $msg
|
||||
}
|
||||
} -test {
|
||||
faultsim_test_result {0 {}} {1 SQLITE_IOERR} {1 SQLITE_NOMEM} \
|
||||
{1 SQLITE_IOERR_NOMEM} {1 SQLITE_IOERR_READ}
|
||||
if {$testrc==0} {
|
||||
set res [db eval {
|
||||
SELECT a FROM t1;
|
||||
PRAGMA integrity_check;
|
||||
}]
|
||||
if {$res != "1 2 3 ok"} { error "res is $res" }
|
||||
}
|
||||
|
||||
sqlite3_snapshot_free $::snapshot
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test the handling of faults that occur within sqlite3_snapshot_recover().
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 4.0 {
|
||||
PRAGMA journal_mode = wal;
|
||||
CREATE TABLE t1(zzz);
|
||||
INSERT INTO t1 VALUES('abc');
|
||||
INSERT INTO t1 VALUES('def');
|
||||
} {wal}
|
||||
faultsim_save_and_close
|
||||
|
||||
do_test 4.0.1 {
|
||||
faultsim_restore_and_reopen
|
||||
db eval { SELECT * FROM sqlite_master }
|
||||
sqlite3_snapshot_recover db main
|
||||
} {}
|
||||
db close
|
||||
|
||||
do_faultsim_test 4.0 -faults oom* -prep {
|
||||
faultsim_restore_and_reopen
|
||||
db eval { SELECT * FROM sqlite_master }
|
||||
} -body {
|
||||
sqlite3_snapshot_recover db main
|
||||
} -test {
|
||||
faultsim_test_result {0 {}} {1 SQLITE_NOMEM} {1 SQLITE_IOERR_NOMEM}
|
||||
}
|
||||
|
||||
# The following test cases contrive to call sqlite3_snapshot_recover()
|
||||
# before all pages of the *-shm file have been mapped. This tests an
|
||||
# extra branch of error handling logic in snapshot_recover().
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 4.1.0 {
|
||||
PRAGMA page_size = 512;
|
||||
PRAGMA journal_mode = wal;
|
||||
PRAGMA wal_autocheckpoint = 0;
|
||||
CREATE TABLE t1(zzz);
|
||||
INSERT INTO t1 VALUES(randomblob( 500 * 9500 ));
|
||||
PRAGMA user_version = 211;
|
||||
} {wal 0}
|
||||
|
||||
do_test 4.1.1 {
|
||||
list [file size test.db-shm] [file size test.db]
|
||||
} {98304 512}
|
||||
|
||||
faultsim_save_and_close
|
||||
do_faultsim_test 4.1 -faults shm* -prep {
|
||||
catch { db2 close }
|
||||
catch { db close }
|
||||
faultsim_restore_and_reopen
|
||||
sqlite3 db2 test.db
|
||||
db2 eval { SELECT * FROM sqlite_master }
|
||||
db eval BEGIN
|
||||
sqlite3_snapshot_get_blob db main
|
||||
db eval COMMIT
|
||||
} -body {
|
||||
sqlite3_snapshot_recover db main
|
||||
} -test {
|
||||
faultsim_test_result {0 {}} {1 SQLITE_IOERR}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test the handling of faults that occur within sqlite3_snapshot_get().
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 5.0 {
|
||||
PRAGMA page_size = 512;
|
||||
PRAGMA journal_mode = wal;
|
||||
PRAGMA wal_autocheckpoint = 0;
|
||||
CREATE TABLE t1(zzz);
|
||||
INSERT INTO t1 VALUES(randomblob( 5000 ));
|
||||
PRAGMA user_version = 211;
|
||||
} {wal 0}
|
||||
faultsim_save_and_close
|
||||
|
||||
do_faultsim_test 5 -prep {
|
||||
faultsim_restore_and_reopen
|
||||
execsql { SELECT count(*) FROM sqlite_master }
|
||||
execsql BEGIN
|
||||
} -body {
|
||||
sqlite3_snapshot_get_blob db main
|
||||
set {} {}
|
||||
} -test {
|
||||
execsql END
|
||||
faultsim_test_result {0 {}} {1 SQLITE_IOERR} {1 SQLITE_NOMEM}
|
||||
}
|
||||
|
||||
|
||||
finish_test
|
166
testdata/overlay/win32longpath.test
vendored
Normal file
166
testdata/overlay/win32longpath.test
vendored
Normal file
|
@ -0,0 +1,166 @@
|
|||
# 2013 August 27
|
||||
#
|
||||
# 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 regression tests for SQLite library. The
|
||||
# focus of this script is testing the file name handling provided
|
||||
# by the "win32-longpath" VFS.
|
||||
#
|
||||
|
||||
if {$tcl_platform(platform)!="windows"} return
|
||||
|
||||
proc get_goversion {} {
|
||||
if {$::tcl_platform(platform) eq "windows"} {
|
||||
if {[info exists ::env(ComSpec)]} {
|
||||
set comSpec $::env(ComSpec)
|
||||
} else {
|
||||
# NOTE: Hard-code the typical default value.
|
||||
set comSpec {C:\Windows\system32\cmd.exe}
|
||||
}
|
||||
return [string map [list \\ /] \
|
||||
[string trim [exec -- $comSpec /c go version ]]]
|
||||
} else {
|
||||
return [go version]
|
||||
}
|
||||
}
|
||||
|
||||
set goVer [get_goversion]
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix win32longpath
|
||||
|
||||
do_test 1.0 {
|
||||
file_control_vfsname db
|
||||
} win32
|
||||
|
||||
db close
|
||||
set rawPath [get_pwd]
|
||||
set path [file nativename $rawPath]
|
||||
sqlite3 db [file join $path test.db] -vfs win32-longpath
|
||||
|
||||
do_test 1.1 {
|
||||
file_control_vfsname db
|
||||
} win32-longpath
|
||||
|
||||
do_test 1.2 {
|
||||
db eval {
|
||||
BEGIN EXCLUSIVE;
|
||||
CREATE TABLE t1(x);
|
||||
INSERT INTO t1 VALUES(1);
|
||||
INSERT INTO t1 VALUES(2);
|
||||
INSERT INTO t1 VALUES(3);
|
||||
INSERT INTO t1 VALUES(4);
|
||||
SELECT x FROM t1 ORDER BY x;
|
||||
COMMIT;
|
||||
}
|
||||
} {1 2 3 4}
|
||||
|
||||
set longPath(1) \\\\?\\$path\\[pid]
|
||||
set uriPath(1a) %5C%5C%3F%5C$path\\[pid]
|
||||
set uriPath(1b) %5C%5C%3F%5C$rawPath/[pid]
|
||||
|
||||
make_win32_dir $longPath(1)
|
||||
|
||||
set longPath(2) $longPath(1)\\[string repeat X 255]
|
||||
set uriPath(2a) $uriPath(1a)\\[string repeat X 255]
|
||||
set uriPath(2b) $uriPath(1b)/[string repeat X 255]
|
||||
|
||||
make_win32_dir $longPath(2)
|
||||
|
||||
set longPath(3) $longPath(2)\\[string repeat Y 255]
|
||||
set uriPath(3a) $uriPath(2a)\\[string repeat Y 255]
|
||||
set uriPath(3b) $uriPath(2b)/[string repeat Y 255]
|
||||
|
||||
make_win32_dir $longPath(3)
|
||||
|
||||
set fileName $longPath(3)\\test.db
|
||||
|
||||
set uri(1a) file:$uriPath(3a)\\test.db
|
||||
set uri(1b) file:$uriPath(3b)/test.db
|
||||
set uri(1c) file:///$uriPath(3a)\\test.db
|
||||
set uri(1d) file:///$uriPath(3b)/test.db
|
||||
set uri(1e) file://localhost/$uriPath(3a)\\test.db
|
||||
set uri(1f) file://localhost/$uriPath(3b)/test.db
|
||||
|
||||
# Starting with Windows 10 v1607 OSBuild 14393, long paths are supported
|
||||
# Go 1.17+ utilizes this capability and a result this test will fail
|
||||
# because the path CAN be created.
|
||||
#
|
||||
# 2022-12-10: As Go 1.16 or older is no more supported, disable this test entirely.
|
||||
#
|
||||
# if { ([string first "1.17" $goVer] < 0) && ([string first "1.18" $goVer] < 0) && ([string first "1.19" $goVer] < 0) } {
|
||||
#
|
||||
# do_test 1.3 {
|
||||
# list [catch {sqlite3 db2 [string range $fileName 4 end]} msg] $msg
|
||||
# } {1 {unable to open database file}}
|
||||
# } else {
|
||||
puts "win32longpath-1.3... skipped"
|
||||
# }
|
||||
|
||||
sqlite3 db3 $fileName -vfs win32-longpath
|
||||
|
||||
do_test 1.4 {
|
||||
db3 eval {
|
||||
BEGIN EXCLUSIVE;
|
||||
CREATE TABLE t1(x);
|
||||
INSERT INTO t1 VALUES(5);
|
||||
INSERT INTO t1 VALUES(6);
|
||||
INSERT INTO t1 VALUES(7);
|
||||
INSERT INTO t1 VALUES(8);
|
||||
SELECT x FROM t1 ORDER BY x;
|
||||
COMMIT;
|
||||
}
|
||||
} {5 6 7 8}
|
||||
|
||||
db3 close
|
||||
# puts " Database exists \{[exists_win32_path $fileName]\}"
|
||||
|
||||
sqlite3 db3 $fileName -vfs win32-longpath
|
||||
|
||||
do_test 1.5 {
|
||||
db3 eval {
|
||||
PRAGMA journal_mode = WAL;
|
||||
}
|
||||
} {wal}
|
||||
|
||||
do_test 1.6 {
|
||||
db3 eval {
|
||||
BEGIN EXCLUSIVE;
|
||||
INSERT INTO t1 VALUES(9);
|
||||
INSERT INTO t1 VALUES(10);
|
||||
INSERT INTO t1 VALUES(11);
|
||||
INSERT INTO t1 VALUES(12);
|
||||
SELECT x FROM t1 ORDER BY x;
|
||||
COMMIT;
|
||||
}
|
||||
} {5 6 7 8 9 10 11 12}
|
||||
|
||||
db3 close
|
||||
# puts " Database exists \{[exists_win32_path $fileName]\}"
|
||||
|
||||
foreach tn {1a 1b 1c 1d 1e 1f} {
|
||||
sqlite3 db3 $uri($tn) -vfs win32-longpath -uri 1 -translatefilename 0
|
||||
|
||||
do_test 1.7.$tn {
|
||||
db3 eval {
|
||||
SELECT x FROM t1 ORDER BY x;
|
||||
}
|
||||
} {5 6 7 8 9 10 11 12}
|
||||
|
||||
db3 close
|
||||
}
|
||||
|
||||
do_delete_win32_file $fileName
|
||||
# puts " Files remaining \{[find_win32_file $longPath(3)\\*]\}"
|
||||
|
||||
do_remove_win32_dir $longPath(3)
|
||||
do_remove_win32_dir $longPath(2)
|
||||
do_remove_win32_dir $longPath(1)
|
||||
|
||||
finish_test
|
197
testdata/tcl/8_3_names.test
vendored
Normal file
197
testdata/tcl/8_3_names.test
vendored
Normal file
|
@ -0,0 +1,197 @@
|
|||
# 2011 May 17
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# Test cases for the SQLITE_ENABLE_8_3_NAMES feature that forces all
|
||||
# filename extensions to be limited to 3 characters. Some embedded
|
||||
# systems need this to work around microsoft FAT patents, but this
|
||||
# feature should be disabled on most deployments.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
ifcapable !8_3_names {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
db close
|
||||
sqlite3_shutdown
|
||||
sqlite3_config_uri 1
|
||||
|
||||
do_test 8_3_names-1.0 {
|
||||
forcedelete test.db test.nal test.db-journal
|
||||
sqlite3 db test.db
|
||||
db eval {
|
||||
PRAGMA cache_size=10;
|
||||
CREATE TABLE t1(x);
|
||||
INSERT INTO t1 VALUES(randomblob(20000));
|
||||
BEGIN;
|
||||
DELETE FROM t1;
|
||||
INSERT INTO t1 VALUES(randomblob(15000));
|
||||
}
|
||||
file exists test.db-journal
|
||||
} 1
|
||||
do_test 8_3_names-1.1 {
|
||||
file exists test.nal
|
||||
} 0
|
||||
do_test 8_3_names-1.2 {
|
||||
db eval {
|
||||
ROLLBACK;
|
||||
SELECT length(x) FROM t1
|
||||
}
|
||||
} 20000
|
||||
|
||||
db close
|
||||
do_test 8_3_names-2.0 {
|
||||
forcedelete test.db test.nal test.db-journal
|
||||
sqlite3 db file:./test.db?8_3_names=1
|
||||
db eval {
|
||||
PRAGMA cache_size=10;
|
||||
CREATE TABLE t1(x);
|
||||
INSERT INTO t1 VALUES(randomblob(20000));
|
||||
BEGIN;
|
||||
DELETE FROM t1;
|
||||
INSERT INTO t1 VALUES(randomblob(15000));
|
||||
}
|
||||
file exists test.db-journal
|
||||
} 0
|
||||
do_test 8_3_names-2.1 {
|
||||
file exists test.nal
|
||||
} 1
|
||||
forcedelete test2.db test2.nal test2.db-journal
|
||||
copy_file test.db test2.db
|
||||
copy_file test.nal test2.nal
|
||||
do_test 8_3_names-2.2 {
|
||||
db eval {
|
||||
COMMIT;
|
||||
SELECT length(x) FROM t1
|
||||
}
|
||||
} 15000
|
||||
do_test 8_3_names-2.3 {
|
||||
sqlite3 db2 file:./test2.db?8_3_names=1
|
||||
db2 eval {
|
||||
PRAGMA integrity_check;
|
||||
SELECT length(x) FROM t1;
|
||||
}
|
||||
} {ok 20000}
|
||||
|
||||
db close
|
||||
do_test 8_3_names-3.0 {
|
||||
forcedelete test.db test.nal test.db-journal
|
||||
sqlite3 db file:./test.db?8_3_names=0
|
||||
db eval {
|
||||
PRAGMA cache_size=10;
|
||||
CREATE TABLE t1(x);
|
||||
INSERT INTO t1 VALUES(randomblob(20000));
|
||||
BEGIN;
|
||||
DELETE FROM t1;
|
||||
INSERT INTO t1 VALUES(randomblob(15000));
|
||||
}
|
||||
file exists test.db-journal
|
||||
} 1
|
||||
do_test 8_3_names-3.1 {
|
||||
file exists test.nal
|
||||
} 0
|
||||
forcedelete test2.db test2.nal test2.db-journal
|
||||
copy_file test.db test2.db
|
||||
copy_file test.db-journal test2.db-journal
|
||||
do_test 8_3_names-3.2 {
|
||||
db eval {
|
||||
COMMIT;
|
||||
SELECT length(x) FROM t1
|
||||
}
|
||||
} 15000
|
||||
do_test 8_3_names-3.3 {
|
||||
sqlite3 db2 file:./test2.db?8_3_names=0
|
||||
db2 eval {
|
||||
PRAGMA integrity_check;
|
||||
SELECT length(x) FROM t1;
|
||||
}
|
||||
} {ok 20000}
|
||||
|
||||
##########################################################################
|
||||
# Master journals.
|
||||
#
|
||||
db close
|
||||
forcedelete test.db test2.db
|
||||
do_test 8_3_names-4.0 {
|
||||
sqlite3 db file:./test.db?8_3_names=1
|
||||
db eval {
|
||||
CREATE TABLE t1(x);
|
||||
INSERT INTO t1 VALUES(1);
|
||||
ATTACH 'file:./test2.db?8_3_names=1' AS db2;
|
||||
CREATE TABLE db2.t2(y);
|
||||
INSERT INTO t2 VALUES(2);
|
||||
BEGIN;
|
||||
INSERT INTO t1 VALUES(3);
|
||||
INSERT INTO t2 VALUES(4);
|
||||
COMMIT;
|
||||
SELECT * FROM t1, t2 ORDER BY x, y
|
||||
}
|
||||
} {1 2 1 4 3 2 3 4}
|
||||
|
||||
|
||||
##########################################################################
|
||||
# WAL mode.
|
||||
#
|
||||
ifcapable !wal {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
db close
|
||||
forcedelete test.db
|
||||
do_test 8_3_names-5.0 {
|
||||
sqlite3 db file:./test.db?8_3_names=1
|
||||
load_static_extension db wholenumber
|
||||
db eval {
|
||||
PRAGMA journal_mode=WAL;
|
||||
CREATE TABLE t1(x);
|
||||
CREATE VIRTUAL TABLE nums USING wholenumber;
|
||||
INSERT INTO t1 SELECT value FROM nums WHERE value BETWEEN 1 AND 1000;
|
||||
BEGIN;
|
||||
UPDATE t1 SET x=x*2;
|
||||
}
|
||||
sqlite3 db2 file:./test.db?8_3_names=1
|
||||
load_static_extension db2 wholenumber
|
||||
db2 eval {
|
||||
BEGIN;
|
||||
SELECT sum(x) FROM t1;
|
||||
}
|
||||
} {500500}
|
||||
|
||||
do_test 8_3_names-5.1 {
|
||||
file exists test.db-wal
|
||||
} 0
|
||||
do_test 8_3_names-5.2 {
|
||||
file exists test.wal
|
||||
} 1
|
||||
do_test 8_3_names-5.3 {
|
||||
file exists test.db-shm
|
||||
} 0
|
||||
do_test 8_3_names-5.4 {
|
||||
file exists test.shm
|
||||
} 1
|
||||
|
||||
|
||||
do_test 8_3_names-5.5 {
|
||||
db eval {
|
||||
COMMIT;
|
||||
SELECT sum(x) FROM t1;
|
||||
}
|
||||
} {1001000}
|
||||
do_test 8_3_names-5.6 {
|
||||
db2 eval {
|
||||
SELECT sum(x) FROM t1;
|
||||
}
|
||||
} {500500}
|
||||
|
||||
|
||||
finish_test
|
136
testdata/tcl/affinity2.test
vendored
Normal file
136
testdata/tcl/affinity2.test
vendored
Normal file
|
@ -0,0 +1,136 @@
|
|||
# 2015-06-02
|
||||
#
|
||||
# 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 regression tests for SQLite library. The
|
||||
# focus of this file is type affinity in comparison operations.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix affinity2
|
||||
|
||||
do_execsql_test affinity2-100 {
|
||||
CREATE TABLE t1(
|
||||
xi INTEGER,
|
||||
xr REAL,
|
||||
xb BLOB,
|
||||
xn NUMERIC,
|
||||
xt TEXT
|
||||
);
|
||||
INSERT INTO t1(rowid,xi,xr,xb,xn,xt) VALUES(1,1,1,1,1,1);
|
||||
INSERT INTO t1(rowid,xi,xr,xb,xn,xt) VALUES(2,'2','2','2','2','2');
|
||||
INSERT INTO t1(rowid,xi,xr,xb,xn,xt) VALUES(3,'03','03','03','03','03');
|
||||
|
||||
} {}
|
||||
do_execsql_test affinity2-110 {
|
||||
SELECT xi, typeof(xi) FROM t1 ORDER BY rowid;
|
||||
} {1 integer 2 integer 3 integer}
|
||||
do_execsql_test affinity2-120 {
|
||||
SELECT xr, typeof(xr) FROM t1 ORDER BY rowid;
|
||||
} {1.0 real 2.0 real 3.0 real}
|
||||
do_execsql_test affinity2-130 {
|
||||
SELECT xb, typeof(xb) FROM t1 ORDER BY rowid;
|
||||
} {1 integer 2 text 03 text}
|
||||
do_execsql_test affinity2-140 {
|
||||
SELECT xn, typeof(xn) FROM t1 ORDER BY rowid;
|
||||
} {1 integer 2 integer 3 integer}
|
||||
do_execsql_test affinity2-150 {
|
||||
SELECT xt, typeof(xt) FROM t1 ORDER BY rowid;
|
||||
} {1 text 2 text 03 text}
|
||||
|
||||
do_execsql_test affinity2-200 {
|
||||
SELECT rowid, xi==xt, xi==xb, xi==+xt FROM t1 ORDER BY rowid;
|
||||
} {1 1 1 1 2 1 1 1 3 1 1 1}
|
||||
do_execsql_test affinity2-210 {
|
||||
SELECT rowid, xr==xt, xr==xb, xr==+xt FROM t1 ORDER BY rowid;
|
||||
} {1 1 1 1 2 1 1 1 3 1 1 1}
|
||||
do_execsql_test affinity2-220 {
|
||||
SELECT rowid, xn==xt, xn==xb, xn==+xt FROM t1 ORDER BY rowid;
|
||||
} {1 1 1 1 2 1 1 1 3 1 1 1}
|
||||
|
||||
do_execsql_test affinity2-300 {
|
||||
SELECT rowid, xt==+xi, xt==xi, xt==xb FROM t1 ORDER BY rowid;
|
||||
} {1 1 1 0 2 1 1 1 3 0 1 1}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
do_execsql_test 400 {
|
||||
CREATE TABLE ttt(c0, c1);
|
||||
CREATE INDEX ii ON ttt(CAST(c0 AS NUMERIC));
|
||||
INSERT INTO ttt VALUES('abc', '-1');
|
||||
}
|
||||
do_execsql_test 410 {
|
||||
SELECT * FROM ttt WHERE CAST(c0 AS NUMERIC) > c1 GROUP BY rowid;
|
||||
} {abc -1}
|
||||
do_execsql_test 420 {
|
||||
SELECT * FROM ttt INDEXED BY ii WHERE CAST(c0 AS NUMERIC) > c1 GROUP BY rowid;
|
||||
} {abc -1}
|
||||
|
||||
do_execsql_test 430 {
|
||||
CREATE TABLE t3(a, b, c INTEGER);
|
||||
CREATE INDEX t3ac ON t3(a, c-1);
|
||||
INSERT INTO t3 VALUES(1, 1, 1);
|
||||
INSERT INTO t3 VALUES(2, 1, 0);
|
||||
INSERT INTO t3 VALUES(3, 1, 1);
|
||||
INSERT INTO t3 VALUES(4, 1, 0);
|
||||
INSERT INTO t3 VALUES(5, 1, 1);
|
||||
}
|
||||
do_execsql_test 440 {
|
||||
SELECT * FROM t3 WHERE c='0' ORDER BY a;
|
||||
} {2 1 0 4 1 0}
|
||||
|
||||
# 2019-08-22 ticket https://sqlite.org/src/info/d99f1ffe836c591ac57f
|
||||
# False positive in sqlite3ExprNeedsNoAffinityChange()
|
||||
#
|
||||
do_execsql_test 500 {
|
||||
DROP TABLE IF EXISTS t0;
|
||||
CREATE TABLE t0(c0 TEXT UNIQUE, c1);
|
||||
INSERT INTO t0(c0) VALUES (-1);
|
||||
SELECT quote(- x'ce'), quote(t0.c0), quote(- x'ce' >= t0.c0) FROM t0;
|
||||
} {0 '-1' 1}
|
||||
do_execsql_test 501 {
|
||||
SELECT * FROM t0 WHERE - x'ce' >= t0.c0;
|
||||
} {-1 {}}
|
||||
do_execsql_test 502 {
|
||||
SELECT quote(+-+x'ce'), quote(t0.c0), quote(+-+x'ce' >= t0.c0) FROM t0;
|
||||
} {0 '-1' 1}
|
||||
do_execsql_test 503 {
|
||||
SELECT * FROM t0 WHERE +-+x'ce' >= t0.c0;
|
||||
} {-1 {}}
|
||||
do_execsql_test 504 {
|
||||
SELECT quote(- 'ce'), quote(t0.c0), quote(- 'ce' >= t0.c0) FROM t0;
|
||||
} {0 '-1' 1}
|
||||
do_execsql_test 505 {
|
||||
SELECT * FROM t0 WHERE - 'ce' >= t0.c0;
|
||||
} {-1 {}}
|
||||
do_execsql_test 506 {
|
||||
SELECT quote(+-+'ce'), quote(t0.c0), quote(+-+'ce' >= t0.c0) FROM t0;
|
||||
} {0 '-1' 1}
|
||||
do_execsql_test 507 {
|
||||
SELECT * FROM t0 WHERE +-+'ce' >= t0.c0;
|
||||
} {-1 {}}
|
||||
|
||||
# 2019-08-30 ticket https://www.sqlite.org/src/info/40812aea1fde9594
|
||||
#
|
||||
# Due to some differences in floating point computations, these tests do not
|
||||
# work under valgrind.
|
||||
#
|
||||
if {![info exists ::G(valgrind)]} {
|
||||
do_execsql_test 600 {
|
||||
DROP TABLE IF EXISTS t0;
|
||||
CREATE TABLE t0(c0 REAL UNIQUE);
|
||||
INSERT INTO t0(c0) VALUES (3175546974276630385);
|
||||
SELECT 3175546974276630385 < c0 FROM t0;
|
||||
} {1}
|
||||
do_execsql_test 601 {
|
||||
SELECT 1 FROM t0 WHERE 3175546974276630385 < c0;
|
||||
} {1}
|
||||
}
|
||||
|
||||
finish_test
|
123
testdata/tcl/affinity3.test
vendored
Normal file
123
testdata/tcl/affinity3.test
vendored
Normal file
|
@ -0,0 +1,123 @@
|
|||
# 2017-01-16
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# Test cases for bugs:
|
||||
#
|
||||
# https://www.sqlite.org/src/info/91e2e8ba6ff2e2
|
||||
# https://www.sqlite.org/src/info/7ffd1ca1d2ad4ecf
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
# Ticket https://www.sqlite.org/src/info/91e2e8ba6ff2e2 (2011-09-19)
|
||||
# Automatic index causes undesired type conversions
|
||||
#
|
||||
do_execsql_test affinity3-100 {
|
||||
CREATE TABLE customer (id INT PRIMARY KEY);
|
||||
CREATE TABLE apr (id INT PRIMARY KEY, apr REAL);
|
||||
|
||||
CREATE VIEW v1 AS
|
||||
SELECT c.id, i.apr
|
||||
FROM customer c
|
||||
LEFT JOIN apr i ON i.id=c.id;
|
||||
|
||||
CREATE VIEW v1rj AS
|
||||
SELECT c.id, i.apr
|
||||
FROM apr i
|
||||
RIGHT JOIN customer c ON i.id=c.id;
|
||||
|
||||
CREATE VIEW v2 AS
|
||||
SELECT c.id, v1.apr
|
||||
FROM customer c
|
||||
LEFT JOIN v1 ON v1.id=c.id;
|
||||
|
||||
CREATE VIEW v2rj AS
|
||||
SELECT c.id, v1.apr
|
||||
FROM v1 RIGHT JOIN customer c ON v1.id=c.id;
|
||||
|
||||
CREATE VIEW v2rjrj AS
|
||||
SELECT c.id, v1rj.apr
|
||||
FROM v1rj RIGHT JOIN customer c ON v1rj.id=c.id;
|
||||
|
||||
INSERT INTO customer (id) VALUES (1);
|
||||
INSERT INTO apr (id, apr) VALUES (1, 12);
|
||||
INSERT INTO customer (id) VALUES (2);
|
||||
INSERT INTO apr (id, apr) VALUES (2, 12.01);
|
||||
}
|
||||
do_execsql_test affinity3-110 {
|
||||
PRAGMA automatic_index=ON;
|
||||
SELECT id, (apr / 100), typeof(apr) apr_type FROM v1;
|
||||
} {1 0.12 real 2 0.1201 real}
|
||||
do_execsql_test affinity3-111 {
|
||||
PRAGMA automatic_index=ON;
|
||||
SELECT id, (apr / 100), typeof(apr) apr_type FROM v1rj;
|
||||
} {1 0.12 real 2 0.1201 real}
|
||||
do_execsql_test affinity3-120 {
|
||||
SELECT id, (apr / 100), typeof(apr) apr_type FROM v2;
|
||||
} {1 0.12 real 2 0.1201 real}
|
||||
do_execsql_test affinity3-121 {
|
||||
SELECT id, (apr / 100), typeof(apr) apr_type FROM v2rj;
|
||||
} {1 0.12 real 2 0.1201 real}
|
||||
do_execsql_test affinity3-122 {
|
||||
SELECT id, (apr / 100), typeof(apr) apr_type FROM v2rjrj;
|
||||
} {1 0.12 real 2 0.1201 real}
|
||||
do_execsql_test affinity3-130 {
|
||||
PRAGMA automatic_index=OFF;
|
||||
SELECT id, (apr / 100), typeof(apr) apr_type FROM v1;
|
||||
} {1 0.12 real 2 0.1201 real}
|
||||
do_execsql_test affinity3-131 {
|
||||
SELECT id, (apr / 100), typeof(apr) apr_type FROM v1rj;
|
||||
} {1 0.12 real 2 0.1201 real}
|
||||
do_execsql_test affinity3-140 {
|
||||
SELECT id, (apr / 100), typeof(apr) apr_type FROM v2;
|
||||
} {1 0.12 real 2 0.1201 real}
|
||||
do_execsql_test affinity3-141 {
|
||||
SELECT id, (apr / 100), typeof(apr) apr_type FROM v2rj;
|
||||
} {1 0.12 real 2 0.1201 real}
|
||||
do_execsql_test affinity3-142 {
|
||||
SELECT id, (apr / 100), typeof(apr) apr_type FROM v2rjrj;
|
||||
} {1 0.12 real 2 0.1201 real}
|
||||
|
||||
# Ticket https://www.sqlite.org/src/info/7ffd1ca1d2ad4ecf (2017-01-16)
|
||||
# Incorrect affinity when using automatic indexes
|
||||
#
|
||||
do_execsql_test affinity3-200 {
|
||||
CREATE TABLE map_integer (id INT, name);
|
||||
INSERT INTO map_integer VALUES(1,'a');
|
||||
CREATE TABLE map_text (id TEXT, name);
|
||||
INSERT INTO map_text VALUES('4','e');
|
||||
CREATE TABLE data (id TEXT, name);
|
||||
INSERT INTO data VALUES(1,'abc');
|
||||
INSERT INTO data VALUES('4','xyz');
|
||||
CREATE VIEW idmap as
|
||||
SELECT * FROM map_integer
|
||||
UNION SELECT * FROM map_text;
|
||||
CREATE TABLE mzed AS SELECT * FROM idmap;
|
||||
}
|
||||
|
||||
do_execsql_test affinity3-210 {
|
||||
PRAGMA automatic_index=ON;
|
||||
SELECT * FROM data JOIN idmap USING(id);
|
||||
} {4 xyz e}
|
||||
do_execsql_test affinity3-220 {
|
||||
SELECT * FROM data JOIN mzed USING(id);
|
||||
} {4 xyz e}
|
||||
|
||||
do_execsql_test affinity3-250 {
|
||||
PRAGMA automatic_index=OFF;
|
||||
SELECT * FROM data JOIN idmap USING(id);
|
||||
} {4 xyz e}
|
||||
do_execsql_test affinity3-260 {
|
||||
SELECT * FROM data JOIN mzed USING(id);
|
||||
} {4 xyz e}
|
||||
|
||||
finish_test
|
78
testdata/tcl/aggerror.test
vendored
Normal file
78
testdata/tcl/aggerror.test
vendored
Normal file
|
@ -0,0 +1,78 @@
|
|||
# 2006 January 20
|
||||
#
|
||||
# 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 regression tests for SQLite library.
|
||||
#
|
||||
# This file implements tests for calling sqlite3_result_error()
|
||||
# from within an aggregate function implementation.
|
||||
#
|
||||
# $Id: aggerror.test,v 1.3 2006/05/03 23:34:06 drh Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
|
||||
# Add the x_count aggregate function to the database handle.
|
||||
# x_count will error out if its input is 40 or 41 or if its
|
||||
# final results is 42. Make sure that such errors are handled
|
||||
# appropriately.
|
||||
#
|
||||
do_test aggerror-1.1 {
|
||||
set DB [sqlite3_connection_pointer db]
|
||||
sqlite3_create_aggregate $DB
|
||||
execsql {
|
||||
CREATE TABLE t1(a);
|
||||
INSERT INTO t1 VALUES(1);
|
||||
INSERT INTO t1 VALUES(2);
|
||||
INSERT INTO t1 SELECT a+2 FROM t1;
|
||||
INSERT INTO t1 SELECT a+4 FROM t1;
|
||||
INSERT INTO t1 SELECT a+8 FROM t1;
|
||||
INSERT INTO t1 SELECT a+16 FROM t1;
|
||||
INSERT INTO t1 SELECT a+32 FROM t1 ORDER BY a LIMIT 7;
|
||||
SELECT x_count(*) FROM t1;
|
||||
}
|
||||
} {39}
|
||||
do_test aggerror-1.2 {
|
||||
execsql {
|
||||
INSERT INTO t1 VALUES(40);
|
||||
SELECT x_count(*) FROM t1;
|
||||
}
|
||||
} {40}
|
||||
do_test aggerror-1.3 {
|
||||
catchsql {
|
||||
SELECT x_count(a) FROM t1;
|
||||
}
|
||||
} {1 {value of 40 handed to x_count}}
|
||||
ifcapable utf16 {
|
||||
do_test aggerror-1.4 {
|
||||
execsql {
|
||||
UPDATE t1 SET a=41 WHERE a=40
|
||||
}
|
||||
catchsql {
|
||||
SELECT x_count(a) FROM t1;
|
||||
}
|
||||
} {1 abc}
|
||||
}
|
||||
do_test aggerror-1.5 {
|
||||
execsql {
|
||||
SELECT x_count(*) FROM t1
|
||||
}
|
||||
} 40
|
||||
do_test aggerror-1.6 {
|
||||
execsql {
|
||||
INSERT INTO t1 VALUES(40);
|
||||
INSERT INTO t1 VALUES(42);
|
||||
}
|
||||
catchsql {
|
||||
SELECT x_count(*) FROM t1;
|
||||
}
|
||||
} {1 {x_count totals to 42}}
|
||||
|
||||
finish_test
|
365
testdata/tcl/aggnested.test
vendored
Normal file
365
testdata/tcl/aggnested.test
vendored
Normal file
|
@ -0,0 +1,365 @@
|
|||
# 2012-08-23
|
||||
#
|
||||
# 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 regression tests for SQLite library.
|
||||
#
|
||||
# This file implements tests for processing aggregate queries with
|
||||
# subqueries in which the subqueries hold the aggregate functions
|
||||
# or in which the subqueries are themselves aggregate queries
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix aggnested
|
||||
|
||||
do_test aggnested-1.1 {
|
||||
db eval {
|
||||
CREATE TABLE t1(a1 INTEGER);
|
||||
INSERT INTO t1 VALUES(1), (2), (3);
|
||||
CREATE TABLE t2(b1 INTEGER);
|
||||
INSERT INTO t2 VALUES(4), (5);
|
||||
SELECT (SELECT group_concat(a1,'x') FROM t2) FROM t1;
|
||||
}
|
||||
} {1x2x3}
|
||||
do_test aggnested-1.2 {
|
||||
db eval {
|
||||
SELECT
|
||||
(SELECT group_concat(a1,'x') || '-' || group_concat(b1,'y') FROM t2)
|
||||
FROM t1;
|
||||
}
|
||||
} {1x2x3-4y5}
|
||||
do_test aggnested-1.3 {
|
||||
db eval {
|
||||
SELECT (SELECT group_concat(b1,a1) FROM t2) FROM t1;
|
||||
}
|
||||
} {415 425 435}
|
||||
do_test aggnested-1.4 {
|
||||
db eval {
|
||||
SELECT (SELECT group_concat(a1,b1) FROM t2) FROM t1;
|
||||
}
|
||||
} {151 252 353}
|
||||
|
||||
|
||||
# This test case is a copy of the one in
|
||||
# http://www.mail-archive.com/sqlite-users@sqlite.org/msg70787.html
|
||||
#
|
||||
do_test aggnested-2.0 {
|
||||
sqlite3 db2 :memory:
|
||||
db2 eval {
|
||||
CREATE TABLE t1 (A1 INTEGER NOT NULL,A2 INTEGER NOT NULL,A3 INTEGER NOT
|
||||
NULL,A4 INTEGER NOT NULL,PRIMARY KEY(A1));
|
||||
REPLACE INTO t1 VALUES(1,11,111,1111);
|
||||
REPLACE INTO t1 VALUES(2,22,222,2222);
|
||||
REPLACE INTO t1 VALUES(3,33,333,3333);
|
||||
CREATE TABLE t2 (B1 INTEGER NOT NULL,B2 INTEGER NOT NULL,B3 INTEGER NOT
|
||||
NULL,B4 INTEGER NOT NULL,PRIMARY KEY(B1));
|
||||
REPLACE INTO t2 VALUES(1,88,888,8888);
|
||||
REPLACE INTO t2 VALUES(2,99,999,9999);
|
||||
SELECT (SELECT GROUP_CONCAT(CASE WHEN a1=1 THEN'A' ELSE 'B' END) FROM t2),
|
||||
t1.*
|
||||
FROM t1;
|
||||
}
|
||||
} {A,B,B 1 11 111 1111}
|
||||
db2 close
|
||||
|
||||
##################### Test cases for ticket [bfbf38e5e9956ac69f] ############
|
||||
#
|
||||
# This first test case is the original problem report:
|
||||
do_test aggnested-3.0 {
|
||||
db eval {
|
||||
CREATE TABLE AAA (
|
||||
aaa_id INTEGER PRIMARY KEY AUTOINCREMENT
|
||||
);
|
||||
CREATE TABLE RRR (
|
||||
rrr_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
rrr_date INTEGER NOT NULL,
|
||||
rrr_aaa INTEGER
|
||||
);
|
||||
CREATE TABLE TTT (
|
||||
ttt_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
target_aaa INTEGER NOT NULL,
|
||||
source_aaa INTEGER NOT NULL
|
||||
);
|
||||
insert into AAA (aaa_id) values (2);
|
||||
insert into TTT (ttt_id, target_aaa, source_aaa)
|
||||
values (4469, 2, 2);
|
||||
insert into TTT (ttt_id, target_aaa, source_aaa)
|
||||
values (4476, 2, 1);
|
||||
insert into RRR (rrr_id, rrr_date, rrr_aaa)
|
||||
values (0, 0, NULL);
|
||||
insert into RRR (rrr_id, rrr_date, rrr_aaa)
|
||||
values (2, 4312, 2);
|
||||
SELECT i.aaa_id,
|
||||
(SELECT sum(CASE WHEN (t.source_aaa == i.aaa_id) THEN 1 ELSE 0 END)
|
||||
FROM TTT t
|
||||
) AS segfault
|
||||
FROM
|
||||
(SELECT curr.rrr_aaa as aaa_id
|
||||
FROM RRR curr
|
||||
-- you also can comment out the next line
|
||||
-- it causes segfault to happen after one row is outputted
|
||||
INNER JOIN AAA a ON (curr.rrr_aaa = aaa_id)
|
||||
LEFT JOIN RRR r ON (r.rrr_id <> 0 AND r.rrr_date < curr.rrr_date)
|
||||
GROUP BY curr.rrr_id
|
||||
HAVING r.rrr_date IS NULL
|
||||
) i;
|
||||
}
|
||||
} {2 1}
|
||||
|
||||
# Further variants of the test case, as found in the ticket
|
||||
#
|
||||
do_test aggnested-3.1 {
|
||||
db eval {
|
||||
DROP TABLE IF EXISTS t1;
|
||||
DROP TABLE IF EXISTS t2;
|
||||
CREATE TABLE t1 (
|
||||
id1 INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
value1 INTEGER
|
||||
);
|
||||
INSERT INTO t1 VALUES(4469,2),(4476,1);
|
||||
CREATE TABLE t2 (
|
||||
id2 INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
value2 INTEGER
|
||||
);
|
||||
INSERT INTO t2 VALUES(0,1),(2,2);
|
||||
SELECT
|
||||
(SELECT sum(value2==xyz) FROM t2)
|
||||
FROM
|
||||
(SELECT curr.value1 as xyz
|
||||
FROM t1 AS curr LEFT JOIN t1 AS other
|
||||
GROUP BY curr.id1);
|
||||
}
|
||||
} {1 1}
|
||||
do_test aggnested-3.1-rj {
|
||||
db eval {
|
||||
SELECT
|
||||
(SELECT sum(value2==xyz) FROM t2)
|
||||
FROM
|
||||
(SELECT curr.value1 as xyz
|
||||
FROM t1 AS other RIGHT JOIN t1 AS curr
|
||||
GROUP BY curr.id1);
|
||||
}
|
||||
} {1 1}
|
||||
|
||||
do_test aggnested-3.2 {
|
||||
db eval {
|
||||
DROP TABLE IF EXISTS t1;
|
||||
DROP TABLE IF EXISTS t2;
|
||||
CREATE TABLE t1 (
|
||||
id1 INTEGER,
|
||||
value1 INTEGER,
|
||||
x1 INTEGER
|
||||
);
|
||||
INSERT INTO t1 VALUES(4469,2,98),(4469,1,99),(4469,3,97);
|
||||
CREATE TABLE t2 (
|
||||
value2 INTEGER
|
||||
);
|
||||
INSERT INTO t2 VALUES(1);
|
||||
SELECT
|
||||
(SELECT sum(value2==xyz) FROM t2)
|
||||
FROM
|
||||
(SELECT value1 as xyz, max(x1) AS pqr
|
||||
FROM t1
|
||||
GROUP BY id1);
|
||||
SELECT
|
||||
(SELECT sum(value2<>xyz) FROM t2)
|
||||
FROM
|
||||
(SELECT value1 as xyz, max(x1) AS pqr
|
||||
FROM t1
|
||||
GROUP BY id1);
|
||||
}
|
||||
} {1 0}
|
||||
do_test aggnested-3.3 {
|
||||
db eval {
|
||||
DROP TABLE IF EXISTS t1;
|
||||
DROP TABLE IF EXISTS t2;
|
||||
CREATE TABLE t1(id1, value1);
|
||||
INSERT INTO t1 VALUES(4469,2),(4469,1);
|
||||
CREATE TABLE t2 (value2);
|
||||
INSERT INTO t2 VALUES(1);
|
||||
SELECT (SELECT sum(value2=value1) FROM t2), max(value1)
|
||||
FROM t1
|
||||
GROUP BY id1;
|
||||
}
|
||||
} {0 2}
|
||||
|
||||
# A batch of queries all doing approximately the same operation involving
|
||||
# two nested aggregate queries.
|
||||
#
|
||||
do_test aggnested-3.11 {
|
||||
db eval {
|
||||
DROP TABLE IF EXISTS t1;
|
||||
DROP TABLE IF EXISTS t2;
|
||||
CREATE TABLE t1(id1, value1);
|
||||
INSERT INTO t1 VALUES(4469,12),(4469,11),(4470,34);
|
||||
CREATE INDEX t1id1 ON t1(id1);
|
||||
CREATE TABLE t2 (value2);
|
||||
INSERT INTO t2 VALUES(12),(34),(34);
|
||||
INSERT INTO t2 SELECT value2 FROM t2;
|
||||
|
||||
SELECT max(value1), (SELECT count(*) FROM t2 WHERE value2=max(value1))
|
||||
FROM t1
|
||||
GROUP BY id1;
|
||||
}
|
||||
} {12 2 34 4}
|
||||
do_test aggnested-3.12 {
|
||||
db eval {
|
||||
SELECT max(value1), (SELECT count(*) FROM t2 WHERE value2=value1)
|
||||
FROM t1
|
||||
GROUP BY id1;
|
||||
}
|
||||
} {12 2 34 4}
|
||||
do_test aggnested-3.13 {
|
||||
db eval {
|
||||
SELECT value1, (SELECT sum(value2=value1) FROM t2)
|
||||
FROM t1;
|
||||
}
|
||||
} {12 2 11 0 34 4}
|
||||
do_test aggnested-3.14 {
|
||||
db eval {
|
||||
SELECT value1, (SELECT sum(value2=value1) FROM t2)
|
||||
FROM t1
|
||||
WHERE value1 IN (SELECT max(value1) FROM t1 GROUP BY id1);
|
||||
}
|
||||
} {12 2 34 4}
|
||||
do_test aggnested-3.15 {
|
||||
# FIXME: If case 3.16 works, then this case really ought to work too...
|
||||
catchsql {
|
||||
SELECT max(value1), (SELECT sum(value2=max(value1)) FROM t2)
|
||||
FROM t1
|
||||
GROUP BY id1;
|
||||
}
|
||||
} {1 {misuse of aggregate function max()}}
|
||||
do_test aggnested-3.16 {
|
||||
db eval {
|
||||
SELECT max(value1), (SELECT sum(value2=value1) FROM t2)
|
||||
FROM t1
|
||||
GROUP BY id1;
|
||||
}
|
||||
} {12 2 34 4}
|
||||
|
||||
# 2019-08-31
|
||||
# Problem found by dbsqlfuzz
|
||||
#
|
||||
do_execsql_test aggnested-4.1 {
|
||||
DROP TABLE IF EXISTS aa;
|
||||
DROP TABLE IF EXISTS bb;
|
||||
CREATE TABLE aa(x INT); INSERT INTO aa(x) VALUES(123);
|
||||
CREATE TABLE bb(y INT); INSERT INTO bb(y) VALUES(456);
|
||||
SELECT (SELECT sum(x+(SELECT y)) FROM bb) FROM aa;
|
||||
} {579}
|
||||
do_execsql_test aggnested-4.2 {
|
||||
SELECT (SELECT sum(x+y) FROM bb) FROM aa;
|
||||
} {579}
|
||||
do_execsql_test aggnested-4.3 {
|
||||
DROP TABLE IF EXISTS tx;
|
||||
DROP TABLE IF EXISTS ty;
|
||||
CREATE TABLE tx(x INT);
|
||||
INSERT INTO tx VALUES(1),(2),(3),(4),(5);
|
||||
CREATE TABLE ty(y INT);
|
||||
INSERT INTO ty VALUES(91),(92),(93);
|
||||
SELECT min((SELECT count(y) FROM ty)) FROM tx;
|
||||
} {3}
|
||||
do_execsql_test aggnested-4.4 {
|
||||
SELECT max((SELECT a FROM (SELECT count(*) AS a FROM ty) AS s)) FROM tx;
|
||||
} {3}
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 5.0 {
|
||||
CREATE TABLE x1(a, b);
|
||||
INSERT INTO x1 VALUES(1, 2);
|
||||
CREATE TABLE x2(x);
|
||||
INSERT INTO x2 VALUES(NULL), (NULL), (NULL);
|
||||
}
|
||||
|
||||
# At one point, aggregate "total()" in the query below was being processed
|
||||
# as part of the outer SELECT, not as part of the sub-select with no FROM
|
||||
# clause.
|
||||
do_execsql_test 5.1 {
|
||||
SELECT ( SELECT total( (SELECT b FROM x1) ) ) FROM x2;
|
||||
} {2.0 2.0 2.0}
|
||||
|
||||
do_execsql_test 5.2 {
|
||||
SELECT ( SELECT total( (SELECT 2 FROM x1) ) ) FROM x2;
|
||||
} {2.0 2.0 2.0}
|
||||
|
||||
do_execsql_test 5.3 {
|
||||
CREATE TABLE t1(a);
|
||||
CREATE TABLE t2(b);
|
||||
}
|
||||
|
||||
do_execsql_test 5.4 {
|
||||
SELECT(
|
||||
SELECT max(b) LIMIT (
|
||||
SELECT total( (SELECT a FROM t1) )
|
||||
)
|
||||
)
|
||||
FROM t2;
|
||||
} {{}}
|
||||
|
||||
do_execsql_test 5.5 {
|
||||
CREATE TABLE a(b);
|
||||
WITH c AS(SELECT a)
|
||||
SELECT(SELECT(SELECT group_concat(b, b)
|
||||
LIMIT(SELECT 0.100000 *
|
||||
AVG(DISTINCT(SELECT 0 FROM a ORDER BY b, b, b))))
|
||||
FROM a GROUP BY b,
|
||||
b, b) FROM a EXCEPT SELECT b FROM a ORDER BY b,
|
||||
b, b;
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# dbsqlfuzz a779227f721a834df95f4f42d0c31550a1f8b8a2
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 6.0 {
|
||||
CREATE TABLE t1(a);
|
||||
CREATE TABLE t2(b);
|
||||
|
||||
INSERT INTO t1 VALUES('x');
|
||||
INSERT INTO t2 VALUES(1);
|
||||
}
|
||||
|
||||
do_execsql_test 6.1.1 {
|
||||
SELECT (
|
||||
SELECT t2.b FROM (SELECT t2.b AS c FROM t1) GROUP BY 1 HAVING t2.b
|
||||
)
|
||||
FROM t2 GROUP BY 'constant_string';
|
||||
} {1}
|
||||
do_execsql_test 6.1.2 {
|
||||
SELECT (
|
||||
SELECT c FROM (SELECT t2.b AS c FROM t1) GROUP BY c HAVING t2.b
|
||||
)
|
||||
FROM t2 GROUP BY 'constant_string';
|
||||
} {1}
|
||||
|
||||
do_execsql_test 6.2.0 {
|
||||
UPDATE t2 SET b=0
|
||||
}
|
||||
do_execsql_test 6.2.1 {
|
||||
SELECT (
|
||||
SELECT t2.b FROM (SELECT t2.b AS c FROM t1) GROUP BY 1 HAVING t2.b
|
||||
)
|
||||
FROM t2 GROUP BY 'constant_string';
|
||||
} {{}}
|
||||
do_execsql_test 6.2.2 {
|
||||
SELECT (
|
||||
SELECT c FROM (SELECT t2.b AS c FROM t1) GROUP BY c HAVING t2.b
|
||||
)
|
||||
FROM t2 GROUP BY 'constant_string';
|
||||
} {{}}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
finish_test
|
140
testdata/tcl/alias.test
vendored
Normal file
140
testdata/tcl/alias.test
vendored
Normal file
|
@ -0,0 +1,140 @@
|
|||
# 2008 August 28
|
||||
#
|
||||
# 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 regression tests for SQLite library. The
|
||||
# focus of this script is correct code generation of aliased result-set
|
||||
# values. See ticket #3343.
|
||||
#
|
||||
# $Id: alias.test,v 1.3 2009/04/23 13:22:44 drh Exp $
|
||||
#
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
# Aliases are currently evaluated twice. We might try to change this
|
||||
# in the future. But not now.
|
||||
return
|
||||
|
||||
# A procedure to return a sequence of increasing integers.
|
||||
#
|
||||
namespace eval ::seq {
|
||||
variable counter 0
|
||||
proc value {args} {
|
||||
variable counter
|
||||
incr counter
|
||||
return $counter
|
||||
}
|
||||
proc reset {} {
|
||||
variable counter
|
||||
set counter 0
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
do_test alias-1.1 {
|
||||
db function sequence ::seq::value
|
||||
db eval {
|
||||
CREATE TABLE t1(x);
|
||||
INSERT INTO t1 VALUES(9);
|
||||
INSERT INTO t1 VALUES(8);
|
||||
INSERT INTO t1 VALUES(7);
|
||||
SELECT x, sequence() FROM t1;
|
||||
}
|
||||
} {9 1 8 2 7 3}
|
||||
do_test alias-1.2 {
|
||||
::seq::reset
|
||||
db eval {
|
||||
SELECT x, sequence() AS y FROM t1 WHERE y>0
|
||||
}
|
||||
} {9 1 8 2 7 3}
|
||||
do_test alias-1.3 {
|
||||
::seq::reset
|
||||
db eval {
|
||||
SELECT x, sequence() AS y FROM t1 WHERE y>0 AND y<99
|
||||
}
|
||||
} {9 1 8 2 7 3}
|
||||
do_test alias-1.4 {
|
||||
::seq::reset
|
||||
db eval {
|
||||
SELECT x, sequence() AS y FROM t1 WHERE y>0 AND y<99 AND y!=55
|
||||
}
|
||||
} {9 1 8 2 7 3}
|
||||
do_test alias-1.5 {
|
||||
::seq::reset
|
||||
db eval {
|
||||
SELECT x, sequence() AS y FROM t1
|
||||
WHERE y>0 AND y<99 AND y!=55 AND y NOT IN (56,57,58)
|
||||
AND y NOT LIKE 'abc%' AND y%10==2
|
||||
}
|
||||
} {8 2}
|
||||
do_test alias-1.6 {
|
||||
::seq::reset
|
||||
db eval {
|
||||
SELECT x, sequence() AS y FROM t1 WHERE y BETWEEN 0 AND 99
|
||||
}
|
||||
} {9 1 8 2 7 3}
|
||||
#do_test alias-1.7 {
|
||||
# ::seq::reset
|
||||
# db eval {
|
||||
# SELECT x, sequence() AS y FROM t1 WHERE y IN (55,66,3)
|
||||
# }
|
||||
#} {7 3}
|
||||
do_test alias-1.8 {
|
||||
::seq::reset
|
||||
db eval {
|
||||
SELECT x, 1-sequence() AS y FROM t1 ORDER BY y
|
||||
}
|
||||
} {7 -2 8 -1 9 0}
|
||||
do_test alias-1.9 {
|
||||
::seq::reset
|
||||
db eval {
|
||||
SELECT x, sequence() AS y FROM t1 ORDER BY -y
|
||||
}
|
||||
} {7 3 8 2 9 1}
|
||||
do_test alias-1.10 {
|
||||
::seq::reset
|
||||
db eval {
|
||||
SELECT x, sequence() AS y FROM t1 ORDER BY x%2, y
|
||||
}
|
||||
} {8 2 9 1 7 3}
|
||||
|
||||
unset -nocomplain random_int_list
|
||||
set random_int_list [db eval {
|
||||
SELECT random()&2147483647 AS r FROM t1, t1, t1, t1 ORDER BY r
|
||||
}]
|
||||
do_test alias-1.11 {
|
||||
lsort -integer $::random_int_list
|
||||
} $random_int_list
|
||||
|
||||
|
||||
do_test alias-2.1 {
|
||||
db eval {
|
||||
SELECT 4 UNION SELECT 1 ORDER BY 1
|
||||
}
|
||||
} {1 4}
|
||||
do_test alias-2.2 {
|
||||
db eval {
|
||||
SELECT 4 UNION SELECT 1 UNION SELECT 9 ORDER BY 1
|
||||
}
|
||||
} {1 4 9}
|
||||
|
||||
if 0 {
|
||||
# Aliases in the GROUP BY clause cause the expression to be evaluated
|
||||
# twice in the current implementation. This might change in the future.
|
||||
#
|
||||
do_test alias-3.1 {
|
||||
::seq::reset
|
||||
db eval {
|
||||
SELECT sequence(*) AS y, count(*) AS z FROM t1 GROUP BY y ORDER BY z, y
|
||||
}
|
||||
} {1 1 2 1 3 1}
|
||||
}
|
||||
|
||||
finish_test
|
51
testdata/tcl/all.test
vendored
Normal file
51
testdata/tcl/all.test
vendored
Normal file
|
@ -0,0 +1,51 @@
|
|||
# 2001 September 15
|
||||
#
|
||||
# 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 runs all tests.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/permutations.test
|
||||
|
||||
run_test_suite full
|
||||
|
||||
ifcapable rbu { run_test_suite rbu }
|
||||
run_test_suite no_optimization
|
||||
run_test_suite memsubsys1
|
||||
run_test_suite memsubsys2
|
||||
run_test_suite singlethread
|
||||
run_test_suite multithread
|
||||
run_test_suite onefile
|
||||
run_test_suite utf16
|
||||
run_test_suite exclusive
|
||||
run_test_suite persistent_journal
|
||||
run_test_suite persistent_journal_error
|
||||
run_test_suite no_journal
|
||||
run_test_suite no_journal_error
|
||||
run_test_suite autovacuum_ioerr
|
||||
run_test_suite no_mutex_try
|
||||
run_test_suite fullmutex
|
||||
run_test_suite journaltest
|
||||
run_test_suite inmemory_journal
|
||||
run_test_suite pcache0
|
||||
run_test_suite pcache10
|
||||
run_test_suite pcache50
|
||||
run_test_suite pcache90
|
||||
run_test_suite pcache100
|
||||
run_test_suite prepare
|
||||
run_test_suite mmap
|
||||
|
||||
if {$::tcl_platform(platform)=="unix"} {
|
||||
ifcapable !default_autovacuum {
|
||||
run_test_suite autovacuum_crash
|
||||
}
|
||||
}
|
||||
|
||||
finish_test
|
938
testdata/tcl/alter.test
vendored
Normal file
938
testdata/tcl/alter.test
vendored
Normal file
|
@ -0,0 +1,938 @@
|
|||
# 2004 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 regression tests for SQLite library. The
|
||||
# focus of this script is testing the ALTER TABLE statement.
|
||||
#
|
||||
# $Id: alter.test,v 1.32 2009/03/24 15:08:10 drh Exp $
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
# If SQLITE_OMIT_ALTERTABLE is defined, omit this file.
|
||||
ifcapable !altertable {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
# Test organization:
|
||||
#
|
||||
# alter-1.1.* - alter-1.7.*: Basic tests of ALTER TABLE, including tables
|
||||
# with implicit and explicit indices. These tests came from an earlier
|
||||
# fork of SQLite that also supported ALTER TABLE.
|
||||
# alter-1.8.*: Tests for ALTER TABLE when the table resides in an
|
||||
# attached database.
|
||||
# alter-1.9.*: Tests for ALTER TABLE when their is whitespace between the
|
||||
# table name and left parenthesis token. i.e:
|
||||
# "CREATE TABLE abc (a, b, c);"
|
||||
# alter-2.*: Test error conditions and messages.
|
||||
# alter-3.*: Test ALTER TABLE on tables that have TRIGGERs attached to them.
|
||||
# alter-4.*: Test ALTER TABLE on tables that have AUTOINCREMENT fields.
|
||||
# ...
|
||||
# alter-12.*: Test ALTER TABLE on views.
|
||||
#
|
||||
|
||||
# Create some tables to rename. Be sure to include some TEMP tables
|
||||
# and some tables with odd names.
|
||||
#
|
||||
do_test alter-1.1 {
|
||||
ifcapable tempdb {
|
||||
set ::temp TEMP
|
||||
} else {
|
||||
set ::temp {}
|
||||
}
|
||||
execsql [subst -nocommands {
|
||||
CREATE TABLE t1(a,b);
|
||||
INSERT INTO t1 VALUES(1,2);
|
||||
CREATE TABLE [t1'x1](c UNIQUE, b PRIMARY KEY);
|
||||
INSERT INTO [t1'x1] VALUES(3,4);
|
||||
CREATE INDEX t1i1 ON T1(B);
|
||||
CREATE INDEX t1i2 ON t1(a,b);
|
||||
CREATE INDEX i3 ON [t1'x1](b,c);
|
||||
CREATE $::temp TABLE "temp table"(e,f,g UNIQUE);
|
||||
CREATE INDEX i2 ON [temp table](f);
|
||||
INSERT INTO [temp table] VALUES(5,6,7);
|
||||
}]
|
||||
execsql {
|
||||
SELECT 't1', * FROM t1;
|
||||
SELECT 't1''x1', * FROM "t1'x1";
|
||||
SELECT * FROM [temp table];
|
||||
}
|
||||
} {t1 1 2 t1'x1 3 4 5 6 7}
|
||||
do_test alter-1.2 {
|
||||
execsql [subst {
|
||||
CREATE $::temp TABLE objlist(type, name, tbl_name);
|
||||
INSERT INTO objlist SELECT type, name, tbl_name
|
||||
FROM sqlite_master WHERE NAME!='objlist';
|
||||
}]
|
||||
ifcapable tempdb {
|
||||
execsql {
|
||||
INSERT INTO objlist SELECT type, name, tbl_name
|
||||
FROM temp.sqlite_master WHERE NAME!='objlist';
|
||||
}
|
||||
}
|
||||
|
||||
execsql {
|
||||
SELECT type, name, tbl_name FROM objlist ORDER BY tbl_name, type desc, name;
|
||||
}
|
||||
} [list \
|
||||
table t1 t1 \
|
||||
index t1i1 t1 \
|
||||
index t1i2 t1 \
|
||||
table t1'x1 t1'x1 \
|
||||
index i3 t1'x1 \
|
||||
index {sqlite_autoindex_t1'x1_1} t1'x1 \
|
||||
index {sqlite_autoindex_t1'x1_2} t1'x1 \
|
||||
table {temp table} {temp table} \
|
||||
index i2 {temp table} \
|
||||
index {sqlite_autoindex_temp table_1} {temp table} \
|
||||
]
|
||||
|
||||
# Make some changes
|
||||
#
|
||||
integrity_check alter-1.3.0
|
||||
do_test alter-1.3 {
|
||||
execsql {
|
||||
ALTER TABLE [T1] RENAME to [-t1-];
|
||||
ALTER TABLE "t1'x1" RENAME TO T2;
|
||||
ALTER TABLE [temp table] RENAME to TempTab;
|
||||
}
|
||||
} {}
|
||||
integrity_check alter-1.3.1
|
||||
do_test alter-1.4 {
|
||||
execsql {
|
||||
SELECT 't1', * FROM [-t1-];
|
||||
SELECT 't2', * FROM t2;
|
||||
SELECT * FROM temptab;
|
||||
}
|
||||
} {t1 1 2 t2 3 4 5 6 7}
|
||||
do_test alter-1.5 {
|
||||
execsql {
|
||||
DELETE FROM objlist;
|
||||
INSERT INTO objlist SELECT type, name, tbl_name
|
||||
FROM sqlite_master WHERE NAME!='objlist';
|
||||
}
|
||||
catchsql {
|
||||
INSERT INTO objlist SELECT type, name, tbl_name
|
||||
FROM sqlite_temp_master WHERE NAME!='objlist';
|
||||
}
|
||||
execsql {
|
||||
SELECT type, name, tbl_name FROM objlist ORDER BY tbl_name, type desc, name;
|
||||
}
|
||||
} [list \
|
||||
table -t1- -t1- \
|
||||
index t1i1 -t1- \
|
||||
index t1i2 -t1- \
|
||||
table T2 T2 \
|
||||
index i3 T2 \
|
||||
index {sqlite_autoindex_T2_1} T2 \
|
||||
index {sqlite_autoindex_T2_2} T2 \
|
||||
table {TempTab} {TempTab} \
|
||||
index i2 {TempTab} \
|
||||
index {sqlite_autoindex_TempTab_1} {TempTab} \
|
||||
]
|
||||
|
||||
# Make sure the changes persist after restarting the database.
|
||||
# (The TEMP table will not persist, of course.)
|
||||
#
|
||||
ifcapable tempdb {
|
||||
do_test alter-1.6 {
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
set DB [sqlite3_connection_pointer db]
|
||||
execsql {
|
||||
CREATE TEMP TABLE objlist(type, name, tbl_name);
|
||||
INSERT INTO objlist SELECT type, name, tbl_name FROM sqlite_master;
|
||||
INSERT INTO objlist
|
||||
SELECT type, name, tbl_name FROM temp.sqlite_master
|
||||
WHERE NAME!='objlist';
|
||||
SELECT type, name, tbl_name FROM objlist
|
||||
ORDER BY tbl_name, type desc, name;
|
||||
}
|
||||
} [list \
|
||||
table -t1- -t1- \
|
||||
index t1i1 -t1- \
|
||||
index t1i2 -t1- \
|
||||
table T2 T2 \
|
||||
index i3 T2 \
|
||||
index {sqlite_autoindex_T2_1} T2 \
|
||||
index {sqlite_autoindex_T2_2} T2 \
|
||||
]
|
||||
} else {
|
||||
execsql {
|
||||
DROP TABLE TempTab;
|
||||
}
|
||||
}
|
||||
|
||||
# Create bogus application-defined functions for functions used
|
||||
# internally by ALTER TABLE, to ensure that ALTER TABLE falls back
|
||||
# to the built-in functions.
|
||||
#
|
||||
proc failing_app_func {args} {error "bad function"}
|
||||
do_test alter-1.7-prep {
|
||||
db func substr failing_app_func
|
||||
db func like failing_app_func
|
||||
db func sqlite_rename_table failing_app_func
|
||||
db func sqlite_rename_trigger failing_app_func
|
||||
db func sqlite_rename_parent failing_app_func
|
||||
catchsql {SELECT substr(name,1,3) FROM sqlite_master}
|
||||
} {1 {bad function}}
|
||||
|
||||
# Make sure the ALTER TABLE statements work with the
|
||||
# non-callback API
|
||||
#
|
||||
do_test alter-1.7 {
|
||||
stepsql $DB {
|
||||
ALTER TABLE [-t1-] RENAME to [*t1*];
|
||||
ALTER TABLE T2 RENAME TO [<t2>];
|
||||
}
|
||||
execsql {
|
||||
DELETE FROM objlist;
|
||||
INSERT INTO objlist SELECT type, name, tbl_name
|
||||
FROM sqlite_master WHERE NAME!='objlist';
|
||||
}
|
||||
catchsql {
|
||||
INSERT INTO objlist SELECT type, name, tbl_name
|
||||
FROM sqlite_temp_master WHERE NAME!='objlist';
|
||||
}
|
||||
execsql {
|
||||
SELECT type, name, tbl_name FROM objlist ORDER BY tbl_name, type desc, name;
|
||||
}
|
||||
} [list \
|
||||
table *t1* *t1* \
|
||||
index t1i1 *t1* \
|
||||
index t1i2 *t1* \
|
||||
table <t2> <t2> \
|
||||
index i3 <t2> \
|
||||
index {sqlite_autoindex_<t2>_1} <t2> \
|
||||
index {sqlite_autoindex_<t2>_2} <t2> \
|
||||
]
|
||||
|
||||
# Check that ALTER TABLE works on attached databases.
|
||||
#
|
||||
ifcapable attach {
|
||||
do_test alter-1.8.1 {
|
||||
forcedelete test2.db
|
||||
forcedelete test2.db-journal
|
||||
execsql {
|
||||
ATTACH 'test2.db' AS aux;
|
||||
}
|
||||
} {}
|
||||
do_test alter-1.8.2 {
|
||||
execsql {
|
||||
CREATE TABLE t4(a PRIMARY KEY, b, c);
|
||||
CREATE TABLE aux.t4(a PRIMARY KEY, b, c);
|
||||
CREATE INDEX i4 ON t4(b);
|
||||
CREATE INDEX aux.i4 ON t4(b);
|
||||
}
|
||||
} {}
|
||||
do_test alter-1.8.3 {
|
||||
execsql {
|
||||
INSERT INTO t4 VALUES('main', 'main', 'main');
|
||||
INSERT INTO aux.t4 VALUES('aux', 'aux', 'aux');
|
||||
SELECT * FROM t4 WHERE a = 'main';
|
||||
}
|
||||
} {main main main}
|
||||
do_test alter-1.8.4 {
|
||||
execsql {
|
||||
ALTER TABLE t4 RENAME TO t5;
|
||||
SELECT * FROM t4 WHERE a = 'aux';
|
||||
}
|
||||
} {aux aux aux}
|
||||
do_test alter-1.8.5 {
|
||||
execsql {
|
||||
SELECT * FROM t5;
|
||||
}
|
||||
} {main main main}
|
||||
do_test alter-1.8.6 {
|
||||
execsql {
|
||||
SELECT * FROM t5 WHERE b = 'main';
|
||||
}
|
||||
} {main main main}
|
||||
do_test alter-1.8.7 {
|
||||
execsql {
|
||||
ALTER TABLE aux.t4 RENAME TO t5;
|
||||
SELECT * FROM aux.t5 WHERE b = 'aux';
|
||||
}
|
||||
} {aux aux aux}
|
||||
}
|
||||
|
||||
do_test alter-1.9.1 {
|
||||
execsql {
|
||||
CREATE TABLE tbl1 (a, b, c);
|
||||
INSERT INTO tbl1 VALUES(1, 2, 3);
|
||||
}
|
||||
} {}
|
||||
do_test alter-1.9.2 {
|
||||
execsql {
|
||||
SELECT * FROM tbl1;
|
||||
}
|
||||
} {1 2 3}
|
||||
do_test alter-1.9.3 {
|
||||
execsql {
|
||||
ALTER TABLE tbl1 RENAME TO tbl2;
|
||||
SELECT * FROM tbl2;
|
||||
}
|
||||
} {1 2 3}
|
||||
do_test alter-1.9.4 {
|
||||
execsql {
|
||||
DROP TABLE tbl2;
|
||||
}
|
||||
} {}
|
||||
|
||||
# Test error messages
|
||||
#
|
||||
do_test alter-2.1 {
|
||||
catchsql {
|
||||
ALTER TABLE none RENAME TO hi;
|
||||
}
|
||||
} {1 {no such table: none}}
|
||||
do_test alter-2.2 {
|
||||
execsql {
|
||||
CREATE TABLE t3(p,q,r);
|
||||
}
|
||||
catchsql {
|
||||
ALTER TABLE [<t2>] RENAME TO t3;
|
||||
}
|
||||
} {1 {there is already another table or index with this name: t3}}
|
||||
do_test alter-2.3 {
|
||||
catchsql {
|
||||
ALTER TABLE [<t2>] RENAME TO i3;
|
||||
}
|
||||
} {1 {there is already another table or index with this name: i3}}
|
||||
do_test alter-2.4 {
|
||||
catchsql {
|
||||
ALTER TABLE SqLiTe_master RENAME TO master;
|
||||
}
|
||||
} {1 {table sqlite_master may not be altered}}
|
||||
do_test alter-2.5 {
|
||||
catchsql {
|
||||
ALTER TABLE t3 RENAME TO sqlite_t3;
|
||||
}
|
||||
} {1 {object name reserved for internal use: sqlite_t3}}
|
||||
do_test alter-2.6 {
|
||||
catchsql {
|
||||
ALTER TABLE t3 ADD COLUMN (ALTER TABLE t3 ADD COLUMN);
|
||||
}
|
||||
} {1 {near "(": syntax error}}
|
||||
|
||||
# If this compilation does not include triggers, omit the alter-3.* tests.
|
||||
ifcapable trigger {
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
# Tests alter-3.* test ALTER TABLE on tables that have triggers.
|
||||
#
|
||||
# alter-3.1.*: ALTER TABLE with triggers.
|
||||
# alter-3.2.*: Test that the ON keyword cannot be used as a database,
|
||||
# table or column name unquoted. This is done because part of the
|
||||
# ALTER TABLE code (specifically the implementation of SQL function
|
||||
# "sqlite_alter_trigger") will break in this case.
|
||||
# alter-3.3.*: ALTER TABLE with TEMP triggers (todo).
|
||||
#
|
||||
|
||||
# An SQL user-function for triggers to fire, so that we know they
|
||||
# are working.
|
||||
proc trigfunc {args} {
|
||||
set ::TRIGGER $args
|
||||
}
|
||||
db func trigfunc trigfunc
|
||||
|
||||
do_test alter-3.1.0 {
|
||||
execsql {
|
||||
CREATE TABLE t6(a, b, c);
|
||||
-- Different case for the table name in the trigger.
|
||||
CREATE TRIGGER trig1 AFTER INSERT ON T6 BEGIN
|
||||
SELECT trigfunc('trig1', new.a, new.b, new.c);
|
||||
END;
|
||||
}
|
||||
} {}
|
||||
do_test alter-3.1.1 {
|
||||
execsql {
|
||||
INSERT INTO t6 VALUES(1, 2, 3);
|
||||
}
|
||||
set ::TRIGGER
|
||||
} {trig1 1 2 3}
|
||||
do_test alter-3.1.2 {
|
||||
execsql {
|
||||
ALTER TABLE t6 RENAME TO t7;
|
||||
INSERT INTO t7 VALUES(4, 5, 6);
|
||||
}
|
||||
set ::TRIGGER
|
||||
} {trig1 4 5 6}
|
||||
do_test alter-3.1.3 {
|
||||
execsql {
|
||||
DROP TRIGGER trig1;
|
||||
}
|
||||
} {}
|
||||
do_test alter-3.1.4 {
|
||||
execsql {
|
||||
CREATE TRIGGER trig2 AFTER INSERT ON main.t7 BEGIN
|
||||
SELECT trigfunc('trig2', new.a, new.b, new.c);
|
||||
END;
|
||||
INSERT INTO t7 VALUES(1, 2, 3);
|
||||
}
|
||||
set ::TRIGGER
|
||||
} {trig2 1 2 3}
|
||||
do_test alter-3.1.5 {
|
||||
execsql {
|
||||
ALTER TABLE t7 RENAME TO t8;
|
||||
INSERT INTO t8 VALUES(4, 5, 6);
|
||||
}
|
||||
set ::TRIGGER
|
||||
} {trig2 4 5 6}
|
||||
do_test alter-3.1.6 {
|
||||
execsql {
|
||||
DROP TRIGGER trig2;
|
||||
}
|
||||
} {}
|
||||
do_test alter-3.1.7 {
|
||||
execsql {
|
||||
CREATE TRIGGER trig3 AFTER INSERT ON main.'t8'BEGIN
|
||||
SELECT trigfunc('trig3', new.a, new.b, new.c);
|
||||
END;
|
||||
INSERT INTO t8 VALUES(1, 2, 3);
|
||||
}
|
||||
set ::TRIGGER
|
||||
} {trig3 1 2 3}
|
||||
do_test alter-3.1.8 {
|
||||
execsql {
|
||||
ALTER TABLE t8 RENAME TO t9;
|
||||
INSERT INTO t9 VALUES(4, 5, 6);
|
||||
}
|
||||
set ::TRIGGER
|
||||
} {trig3 4 5 6}
|
||||
|
||||
# Make sure "ON" cannot be used as a database, table or column name without
|
||||
# quoting. Otherwise the sqlite_alter_trigger() function might not work.
|
||||
forcedelete test3.db
|
||||
forcedelete test3.db-journal
|
||||
ifcapable attach {
|
||||
do_test alter-3.2.1 {
|
||||
catchsql {
|
||||
ATTACH 'test3.db' AS ON;
|
||||
}
|
||||
} {1 {near "ON": syntax error}}
|
||||
do_test alter-3.2.2 {
|
||||
catchsql {
|
||||
ATTACH 'test3.db' AS 'ON';
|
||||
}
|
||||
} {0 {}}
|
||||
do_test alter-3.2.3 {
|
||||
catchsql {
|
||||
CREATE TABLE ON.t1(a, b, c);
|
||||
}
|
||||
} {1 {near "ON": syntax error}}
|
||||
do_test alter-3.2.4 {
|
||||
catchsql {
|
||||
CREATE TABLE 'ON'.t1(a, b, c);
|
||||
}
|
||||
} {0 {}}
|
||||
do_test alter-3.2.4 {
|
||||
catchsql {
|
||||
CREATE TABLE 'ON'.ON(a, b, c);
|
||||
}
|
||||
} {1 {near "ON": syntax error}}
|
||||
do_test alter-3.2.5 {
|
||||
catchsql {
|
||||
CREATE TABLE 'ON'.'ON'(a, b, c);
|
||||
}
|
||||
} {0 {}}
|
||||
}
|
||||
do_test alter-3.2.6 {
|
||||
catchsql {
|
||||
CREATE TABLE t10(a, ON, c);
|
||||
}
|
||||
} {1 {near "ON": syntax error}}
|
||||
do_test alter-3.2.7 {
|
||||
catchsql {
|
||||
CREATE TABLE t10(a, 'ON', c);
|
||||
}
|
||||
} {0 {}}
|
||||
do_test alter-3.2.8 {
|
||||
catchsql {
|
||||
CREATE TRIGGER trig4 AFTER INSERT ON ON BEGIN SELECT 1; END;
|
||||
}
|
||||
} {1 {near "ON": syntax error}}
|
||||
ifcapable attach {
|
||||
do_test alter-3.2.9 {
|
||||
catchsql {
|
||||
CREATE TRIGGER 'on'.trig4 AFTER INSERT ON 'ON' BEGIN SELECT 1; END;
|
||||
}
|
||||
} {0 {}}
|
||||
}
|
||||
do_test alter-3.2.10 {
|
||||
execsql {
|
||||
DROP TABLE t10;
|
||||
}
|
||||
} {}
|
||||
|
||||
do_test alter-3.3.1 {
|
||||
execsql [subst {
|
||||
CREATE TABLE tbl1(a, b, c);
|
||||
CREATE $::temp TRIGGER trig1 AFTER INSERT ON tbl1 BEGIN
|
||||
SELECT trigfunc('trig1', new.a, new.b, new.c);
|
||||
END;
|
||||
}]
|
||||
} {}
|
||||
do_test alter-3.3.2 {
|
||||
execsql {
|
||||
INSERT INTO tbl1 VALUES('a', 'b', 'c');
|
||||
}
|
||||
set ::TRIGGER
|
||||
} {trig1 a b c}
|
||||
do_test alter-3.3.3 {
|
||||
execsql {
|
||||
ALTER TABLE tbl1 RENAME TO tbl2;
|
||||
INSERT INTO tbl2 VALUES('d', 'e', 'f');
|
||||
}
|
||||
set ::TRIGGER
|
||||
} {trig1 d e f}
|
||||
do_test alter-3.3.4 {
|
||||
execsql [subst {
|
||||
CREATE $::temp TRIGGER trig2 AFTER UPDATE ON tbl2 BEGIN
|
||||
SELECT trigfunc('trig2', new.a, new.b, new.c);
|
||||
END;
|
||||
}]
|
||||
} {}
|
||||
do_test alter-3.3.5 {
|
||||
execsql {
|
||||
ALTER TABLE tbl2 RENAME TO tbl3;
|
||||
INSERT INTO tbl3 VALUES('g', 'h', 'i');
|
||||
}
|
||||
set ::TRIGGER
|
||||
} {trig1 g h i}
|
||||
do_test alter-3.3.6 {
|
||||
execsql {
|
||||
UPDATE tbl3 SET a = 'G' where a = 'g';
|
||||
}
|
||||
set ::TRIGGER
|
||||
} {trig2 G h i}
|
||||
do_test alter-3.3.7 {
|
||||
execsql {
|
||||
DROP TABLE tbl3;
|
||||
}
|
||||
} {}
|
||||
ifcapable tempdb {
|
||||
do_test alter-3.3.8 {
|
||||
execsql {
|
||||
SELECT * FROM temp.sqlite_master WHERE type = 'trigger';
|
||||
}
|
||||
} {}
|
||||
}
|
||||
|
||||
} ;# ifcapable trigger
|
||||
|
||||
# If the build does not include AUTOINCREMENT fields, omit alter-4.*.
|
||||
ifcapable autoinc {
|
||||
|
||||
do_test alter-4.1 {
|
||||
execsql {
|
||||
CREATE TABLE tbl1(a INTEGER PRIMARY KEY AUTOINCREMENT);
|
||||
INSERT INTO tbl1 VALUES(10);
|
||||
}
|
||||
} {}
|
||||
do_test alter-4.2 {
|
||||
execsql {
|
||||
INSERT INTO tbl1 VALUES(NULL);
|
||||
SELECT a FROM tbl1;
|
||||
}
|
||||
} {10 11}
|
||||
do_test alter-4.3 {
|
||||
execsql {
|
||||
ALTER TABLE tbl1 RENAME TO tbl2;
|
||||
DELETE FROM tbl2;
|
||||
INSERT INTO tbl2 VALUES(NULL);
|
||||
SELECT a FROM tbl2;
|
||||
}
|
||||
} {12}
|
||||
do_test alter-4.4 {
|
||||
execsql {
|
||||
DROP TABLE tbl2;
|
||||
}
|
||||
} {}
|
||||
|
||||
} ;# ifcapable autoinc
|
||||
|
||||
# Test that it is Ok to execute an ALTER TABLE immediately after
|
||||
# opening a database.
|
||||
do_test alter-5.1 {
|
||||
execsql {
|
||||
CREATE TABLE tbl1(a, b, c);
|
||||
INSERT INTO tbl1 VALUES('x', 'y', 'z');
|
||||
}
|
||||
} {}
|
||||
do_test alter-5.2 {
|
||||
sqlite3 db2 test.db
|
||||
execsql {
|
||||
ALTER TABLE tbl1 RENAME TO tbl2;
|
||||
SELECT * FROM tbl2;
|
||||
} db2
|
||||
} {x y z}
|
||||
do_test alter-5.3 {
|
||||
db2 close
|
||||
} {}
|
||||
|
||||
foreach tblname [execsql {
|
||||
SELECT name FROM sqlite_master
|
||||
WHERE type='table' AND name NOT GLOB 'sqlite*'
|
||||
}] {
|
||||
execsql "DROP TABLE \"$tblname\""
|
||||
}
|
||||
|
||||
set ::tbl_name "abc\uABCDdef"
|
||||
do_test alter-6.1 {
|
||||
string length $::tbl_name
|
||||
} {7}
|
||||
do_test alter-6.2 {
|
||||
execsql "
|
||||
CREATE TABLE ${tbl_name}(a, b, c);
|
||||
"
|
||||
set ::oid [execsql {SELECT max(oid) FROM sqlite_master}]
|
||||
execsql "
|
||||
SELECT sql FROM sqlite_master WHERE oid = $::oid;
|
||||
"
|
||||
} "{CREATE TABLE ${::tbl_name}(a, b, c)}"
|
||||
execsql "
|
||||
SELECT * FROM ${::tbl_name}
|
||||
"
|
||||
set ::tbl_name2 "abcXdef"
|
||||
do_test alter-6.3 {
|
||||
execsql "
|
||||
ALTER TABLE $::tbl_name RENAME TO $::tbl_name2
|
||||
"
|
||||
execsql "
|
||||
SELECT sql FROM sqlite_master WHERE oid = $::oid
|
||||
"
|
||||
} "{CREATE TABLE \"${::tbl_name2}\"(a, b, c)}"
|
||||
do_test alter-6.4 {
|
||||
execsql "
|
||||
ALTER TABLE $::tbl_name2 RENAME TO $::tbl_name
|
||||
"
|
||||
execsql "
|
||||
SELECT sql FROM sqlite_master WHERE oid = $::oid
|
||||
"
|
||||
} "{CREATE TABLE \"${::tbl_name}\"(a, b, c)}"
|
||||
set ::col_name ghi\1234\jkl
|
||||
do_test alter-6.5 {
|
||||
execsql "
|
||||
ALTER TABLE $::tbl_name ADD COLUMN $::col_name VARCHAR
|
||||
"
|
||||
execsql "
|
||||
SELECT sql FROM sqlite_master WHERE oid = $::oid
|
||||
"
|
||||
} "{CREATE TABLE \"${::tbl_name}\"(a, b, c, $::col_name VARCHAR)}"
|
||||
set ::col_name2 B\3421\A
|
||||
do_test alter-6.6 {
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
execsql "
|
||||
ALTER TABLE $::tbl_name ADD COLUMN $::col_name2
|
||||
"
|
||||
execsql "
|
||||
SELECT sql FROM sqlite_master WHERE oid = $::oid
|
||||
"
|
||||
} "{CREATE TABLE \"${::tbl_name}\"(a, b, c, $::col_name VARCHAR, $::col_name2)}"
|
||||
do_test alter-6.7 {
|
||||
execsql "
|
||||
INSERT INTO ${::tbl_name} VALUES(1, 2, 3, 4, 5);
|
||||
SELECT $::col_name, $::col_name2 FROM $::tbl_name;
|
||||
"
|
||||
} {4 5}
|
||||
|
||||
# Ticket #1665: Make sure ALTER TABLE ADD COLUMN works on a table
|
||||
# that includes a COLLATE clause.
|
||||
#
|
||||
do_realnum_test alter-7.1 {
|
||||
execsql {
|
||||
CREATE TABLE t1(a TEXT COLLATE BINARY);
|
||||
ALTER TABLE t1 ADD COLUMN b INTEGER COLLATE NOCASE;
|
||||
INSERT INTO t1 VALUES(1,'-2');
|
||||
INSERT INTO t1 VALUES(5.4e-08,'5.4e-08');
|
||||
SELECT typeof(a), a, typeof(b), b FROM t1;
|
||||
}
|
||||
} {text 1 integer -2 text 5.4e-08 real 5.4e-08}
|
||||
|
||||
# Make sure that when a column is added by ALTER TABLE ADD COLUMN and has
|
||||
# a default value that the default value is used by aggregate functions.
|
||||
#
|
||||
do_test alter-8.1 {
|
||||
execsql {
|
||||
CREATE TABLE t2(a INTEGER);
|
||||
INSERT INTO t2 VALUES(1);
|
||||
INSERT INTO t2 VALUES(1);
|
||||
INSERT INTO t2 VALUES(2);
|
||||
ALTER TABLE t2 ADD COLUMN b INTEGER DEFAULT 9;
|
||||
SELECT sum(b) FROM t2;
|
||||
}
|
||||
} {27}
|
||||
do_test alter-8.2 {
|
||||
execsql {
|
||||
SELECT a, sum(b) FROM t2 GROUP BY a;
|
||||
}
|
||||
} {1 18 2 9}
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
# alter-9.X - Special test: Make sure the sqlite_rename_column() and
|
||||
# rename_table() functions do not crash when handed bad input.
|
||||
#
|
||||
sqlite3_test_control SQLITE_TESTCTRL_INTERNAL_FUNCTIONS db
|
||||
do_test alter-9.1 {
|
||||
execsql {SELECT SQLITE_RENAME_COLUMN(0,0,0,0,0,0,0,0,0)}
|
||||
} {{}}
|
||||
foreach {tn sql} {
|
||||
1 { SELECT SQLITE_RENAME_TABLE(0,0,0,0,0,0,0) }
|
||||
2 { SELECT SQLITE_RENAME_TABLE(10,20,30,40,50,60,70) }
|
||||
3 { SELECT SQLITE_RENAME_TABLE('foo','foo','foo','foo','foo','foo','foo') }
|
||||
} {
|
||||
do_test alter-9.2.$tn {
|
||||
catch { execsql $sql }
|
||||
} 1
|
||||
}
|
||||
sqlite3_test_control SQLITE_TESTCTRL_INTERNAL_FUNCTIONS db
|
||||
|
||||
# If the INTERNAL_FUNCTIONS test-control is disabled (which is the default),
|
||||
# then the sqlite_rename_table() SQL function is not accessible to ordinary SQL.
|
||||
#
|
||||
do_catchsql_test alter-9.3 {
|
||||
SELECT sqlite_rename_table(0,0,0,0,0,0,0);
|
||||
} {1 {no such function: sqlite_rename_table}}
|
||||
|
||||
#------------------------------------------------------------------------
|
||||
# alter-10.X - Make sure ALTER TABLE works with multi-byte UTF-8 characters
|
||||
# in the names.
|
||||
#
|
||||
do_test alter-10.1 {
|
||||
execsql "CREATE TABLE xyz(x UNIQUE)"
|
||||
execsql "ALTER TABLE xyz RENAME TO xyz\u1234abc"
|
||||
execsql {SELECT name FROM sqlite_master WHERE name GLOB 'xyz*'}
|
||||
} [list xyz\u1234abc]
|
||||
do_test alter-10.2 {
|
||||
execsql {SELECT name FROM sqlite_master WHERE name GLOB 'sqlite_autoindex*'}
|
||||
} [list sqlite_autoindex_xyz\u1234abc_1]
|
||||
do_test alter-10.3 {
|
||||
execsql "ALTER TABLE xyz\u1234abc RENAME TO xyzabc"
|
||||
execsql {SELECT name FROM sqlite_master WHERE name GLOB 'xyz*'}
|
||||
} [list xyzabc]
|
||||
do_test alter-10.4 {
|
||||
execsql {SELECT name FROM sqlite_master WHERE name GLOB 'sqlite_autoindex*'}
|
||||
} [list sqlite_autoindex_xyzabc_1]
|
||||
|
||||
do_test alter-11.1 {
|
||||
sqlite3_exec db {CREATE TABLE t11(%c6%c6)}
|
||||
execsql {
|
||||
ALTER TABLE t11 ADD COLUMN abc;
|
||||
}
|
||||
catchsql {
|
||||
ALTER TABLE t11 ADD COLUMN abc;
|
||||
}
|
||||
} {1 {duplicate column name: abc}}
|
||||
set isutf16 [regexp 16 [db one {PRAGMA encoding}]]
|
||||
if {!$isutf16} {
|
||||
do_test alter-11.2 {
|
||||
execsql {INSERT INTO t11 VALUES(1,2)}
|
||||
sqlite3_exec db {SELECT %c6%c6 AS xyz, abc FROM t11}
|
||||
} {0 {xyz abc 1 2}}
|
||||
}
|
||||
do_test alter-11.3 {
|
||||
sqlite3_exec db {CREATE TABLE t11b("%81%82%83" text)}
|
||||
execsql {
|
||||
ALTER TABLE t11b ADD COLUMN abc;
|
||||
}
|
||||
catchsql {
|
||||
ALTER TABLE t11b ADD COLUMN abc;
|
||||
}
|
||||
} {1 {duplicate column name: abc}}
|
||||
if {!$isutf16} {
|
||||
do_test alter-11.4 {
|
||||
execsql {INSERT INTO t11b VALUES(3,4)}
|
||||
sqlite3_exec db {SELECT %81%82%83 AS xyz, abc FROM t11b}
|
||||
} {0 {xyz abc 3 4}}
|
||||
do_test alter-11.5 {
|
||||
sqlite3_exec db {SELECT [%81%82%83] AS xyz, abc FROM t11b}
|
||||
} {0 {xyz abc 3 4}}
|
||||
do_test alter-11.6 {
|
||||
sqlite3_exec db {SELECT "%81%82%83" AS xyz, abc FROM t11b}
|
||||
} {0 {xyz abc 3 4}}
|
||||
}
|
||||
do_test alter-11.7 {
|
||||
sqlite3_exec db {CREATE TABLE t11c(%81%82%83 text)}
|
||||
execsql {
|
||||
ALTER TABLE t11c ADD COLUMN abc;
|
||||
}
|
||||
catchsql {
|
||||
ALTER TABLE t11c ADD COLUMN abc;
|
||||
}
|
||||
} {1 {duplicate column name: abc}}
|
||||
if {!$isutf16} {
|
||||
do_test alter-11.8 {
|
||||
execsql {INSERT INTO t11c VALUES(5,6)}
|
||||
sqlite3_exec db {SELECT %81%82%83 AS xyz, abc FROM t11c}
|
||||
} {0 {xyz abc 5 6}}
|
||||
do_test alter-11.9 {
|
||||
sqlite3_exec db {SELECT [%81%82%83] AS xyz, abc FROM t11c}
|
||||
} {0 {xyz abc 5 6}}
|
||||
do_test alter-11.10 {
|
||||
sqlite3_exec db {SELECT "%81%82%83" AS xyz, abc FROM t11c}
|
||||
} {0 {xyz abc 5 6}}
|
||||
}
|
||||
|
||||
do_test alter-12.1 {
|
||||
execsql {
|
||||
CREATE TABLE t12(a, b, c);
|
||||
CREATE VIEW v1 AS SELECT * FROM t12;
|
||||
}
|
||||
} {}
|
||||
do_test alter-12.2 {
|
||||
catchsql {
|
||||
ALTER TABLE v1 RENAME TO v2;
|
||||
}
|
||||
} {1 {view v1 may not be altered}}
|
||||
do_test alter-12.3 {
|
||||
execsql { SELECT * FROM v1; }
|
||||
} {}
|
||||
do_test alter-12.4 {
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
execsql { SELECT * FROM v1; }
|
||||
} {}
|
||||
do_test alter-12.5 {
|
||||
catchsql {
|
||||
ALTER TABLE v1 ADD COLUMN new_column;
|
||||
}
|
||||
} {1 {Cannot add a column to a view}}
|
||||
|
||||
# Ticket #3102:
|
||||
# Verify that comments do not interfere with the table rename
|
||||
# algorithm.
|
||||
#
|
||||
do_test alter-13.1 {
|
||||
execsql {
|
||||
CREATE TABLE /* hi */ t3102a(x);
|
||||
CREATE TABLE t3102b -- comment
|
||||
(y);
|
||||
CREATE INDEX t3102c ON t3102a(x);
|
||||
SELECT name FROM sqlite_master WHERE name GLOB 't3102*' ORDER BY 1;
|
||||
}
|
||||
} {t3102a t3102b t3102c}
|
||||
do_test alter-13.2 {
|
||||
execsql {
|
||||
ALTER TABLE t3102a RENAME TO t3102a_rename;
|
||||
SELECT name FROM sqlite_master WHERE name GLOB 't3102*' ORDER BY 1;
|
||||
}
|
||||
} {t3102a_rename t3102b t3102c}
|
||||
do_test alter-13.3 {
|
||||
execsql {
|
||||
ALTER TABLE t3102b RENAME TO t3102b_rename;
|
||||
SELECT name FROM sqlite_master WHERE name GLOB 't3102*' ORDER BY 1;
|
||||
}
|
||||
} {t3102a_rename t3102b_rename t3102c}
|
||||
|
||||
# Ticket #3651
|
||||
do_test alter-14.1 {
|
||||
catchsql {
|
||||
CREATE TABLE t3651(a UNIQUE);
|
||||
INSERT INTO t3651 VALUES(5);
|
||||
ALTER TABLE t3651 ADD COLUMN b UNIQUE;
|
||||
}
|
||||
} {1 {Cannot add a UNIQUE column}}
|
||||
do_test alter-14.2 {
|
||||
catchsql {
|
||||
ALTER TABLE t3651 ADD COLUMN b PRIMARY KEY;
|
||||
}
|
||||
} {1 {Cannot add a PRIMARY KEY column}}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test that it is not possible to use ALTER TABLE on any system table.
|
||||
#
|
||||
set system_table_list {1 sqlite_master}
|
||||
catchsql ANALYZE
|
||||
ifcapable analyze { lappend system_table_list 2 sqlite_stat1 }
|
||||
ifcapable stat4 { lappend system_table_list 4 sqlite_stat4 }
|
||||
|
||||
foreach {tn tbl} $system_table_list {
|
||||
do_test alter-15.$tn.1 {
|
||||
catchsql "ALTER TABLE $tbl RENAME TO xyz"
|
||||
} [list 1 "table $tbl may not be altered"]
|
||||
|
||||
do_test alter-15.$tn.2 {
|
||||
catchsql "ALTER TABLE $tbl ADD COLUMN xyz"
|
||||
} [list 1 "table $tbl may not be altered"]
|
||||
}
|
||||
|
||||
#------------------------------------------------------------------------
|
||||
# Verify that ALTER TABLE works on tables with the WITHOUT rowid option.
|
||||
#
|
||||
do_execsql_test alter-16.1 {
|
||||
CREATE TABLE t16a(a TEXT, b REAL, c INT, PRIMARY KEY(a,b)) WITHOUT rowid;
|
||||
INSERT INTO t16a VALUES('abc',1.25,99);
|
||||
ALTER TABLE t16a ADD COLUMN d TEXT DEFAULT 'xyzzy';
|
||||
INSERT INTO t16a VALUES('cba',5.5,98,'fizzle');
|
||||
SELECT * FROM t16a ORDER BY a;
|
||||
} {abc 1.25 99 xyzzy cba 5.5 98 fizzle}
|
||||
do_execsql_test alter-16.2 {
|
||||
ALTER TABLE t16a RENAME TO t16a_rn;
|
||||
SELECT * FROM t16a_rn ORDER BY a;
|
||||
} {abc 1.25 99 xyzzy cba 5.5 98 fizzle}
|
||||
|
||||
# 2018-09-16 ticket b41031ea2b5372378cb3d2d43cf9fe2a4a5c2510
|
||||
#
|
||||
ifcapable rtree {
|
||||
db close
|
||||
sqlite3 db :memory:
|
||||
do_execsql_test alter-17.100 {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
|
||||
CREATE VIRTUAL TABLE t2 USING rtree(id,x0,x1);
|
||||
INSERT INTO t1 VALUES(1,'apple'),(2,'fig'),(3,'pear');
|
||||
INSERT INTO t2 VALUES(1,1.0,2.0),(2,2.0,3.0),(3,1.5,3.5);
|
||||
CREATE TRIGGER r1 AFTER UPDATE ON t1 BEGIN
|
||||
DELETE FROM t2 WHERE id = OLD.a;
|
||||
END;
|
||||
ALTER TABLE t1 RENAME TO t3;
|
||||
UPDATE t3 SET b='peach' WHERE a=2;
|
||||
SELECT * FROM t2 ORDER BY 1;
|
||||
} {1 1.0 2.0 3 1.5 3.5}
|
||||
}
|
||||
|
||||
# 2021-03-08 dbsqlfuzz 3f0a7245b69cd08617d7d7781ebaedb0fe765a93
|
||||
reset_db
|
||||
do_catchsql_test alter-18.1 {
|
||||
CREATE TABLE t1(a,b,c);
|
||||
CREATE TABLE log(a INTEGER PRIMARY KEY,b,c);
|
||||
CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN
|
||||
INSERT INTO logx(a,b,c) VALUES(new.a,new.b,new.c)
|
||||
ON CONFLICT(a) DO UPDATE SET c=excluded.c, b=new.b;
|
||||
END;
|
||||
ALTER TABLE log RENAME COLUMN a TO x;
|
||||
} {1 {error in trigger tr1: no such table: main.logx}}
|
||||
|
||||
# 2021-10-13 dbsqlfuzz e89174cbfad2d904f06b5e24df0a22510b6a1c1e
|
||||
reset_db
|
||||
do_execsql_test alter-19.1 {
|
||||
CREATE TABLE t1(x);
|
||||
CREATE TABLE t2(c);
|
||||
CREATE TRIGGER r1 AFTER INSERT ON t2 BEGIN
|
||||
UPDATE t2 SET (c)=(
|
||||
EXISTS(SELECT 1 WHERE (WITH cte1(a) AS (SELECT 1 FROM t1 WHERE (SELECT 1 WHERE (WITH cte2(b) AS (VALUES(1))SELECT b FROM cte2)))SELECT a FROM cte1))
|
||||
);
|
||||
END;
|
||||
ALTER TABLE t2 RENAME TO t3;
|
||||
} {}
|
||||
do_execsql_test alter-19.2 {
|
||||
SELECT name FROM sqlite_schema WHERE sql LIKE '%t2%';
|
||||
} {}
|
||||
do_execsql_test alter-19.3 {
|
||||
SELECT name FROM sqlite_schema WHERE sql LIKE '%t3%' ORDER BY name;
|
||||
} {r1 t3}
|
||||
|
||||
|
||||
finish_test
|
470
testdata/tcl/alter2.test
vendored
Normal file
470
testdata/tcl/alter2.test
vendored
Normal file
|
@ -0,0 +1,470 @@
|
|||
# 2005 February 18
|
||||
#
|
||||
# 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 regression tests for SQLite library. The
|
||||
# focus of this script is testing that SQLite can handle a subtle
|
||||
# file format change that may be used in the future to implement
|
||||
# "ALTER TABLE ... ADD COLUMN".
|
||||
#
|
||||
# $Id: alter2.test,v 1.14 2009/04/07 14:14:22 danielk1977 Exp $
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
# We have to have pragmas in order to do this test
|
||||
ifcapable {!pragma} return
|
||||
|
||||
# Do not use a codec for tests in this file, as the database file is
|
||||
# manipulated directly using tcl scripts. See proc [set_file_format].
|
||||
#
|
||||
do_not_use_codec
|
||||
|
||||
# The file format change affects the way row-records stored in tables (but
|
||||
# not indices) are interpreted. Before version 3.1.3, a row-record for a
|
||||
# table with N columns was guaranteed to contain exactly N fields. As
|
||||
# of version 3.1.3, the record may contain up to N fields. In this case
|
||||
# the M fields that are present are the values for the left-most M
|
||||
# columns. The (N-M) rightmost columns contain NULL.
|
||||
#
|
||||
# If any records in the database contain less fields than their table
|
||||
# has columns, then the file-format meta value should be set to (at least) 2.
|
||||
#
|
||||
|
||||
# This procedure sets the value of the file-format in file 'test.db'
|
||||
# to $newval. Also, the schema cookie is incremented.
|
||||
#
|
||||
proc set_file_format {newval} {
|
||||
hexio_write test.db 44 [hexio_render_int32 $newval]
|
||||
set schemacookie [hexio_get_int [hexio_read test.db 40 4]]
|
||||
incr schemacookie
|
||||
hexio_write test.db 40 [hexio_render_int32 $schemacookie]
|
||||
return {}
|
||||
}
|
||||
|
||||
# This procedure returns the value of the file-format in file 'test.db'.
|
||||
#
|
||||
proc get_file_format {{fname test.db}} {
|
||||
return [hexio_get_int [hexio_read $fname 44 4]]
|
||||
}
|
||||
|
||||
# This procedure sets the SQL statement stored for table $tbl in the
|
||||
# sqlite_master table of file 'test.db' to $sql. Also set the file format
|
||||
# to the supplied value. This is 2 if the added column has a default that is
|
||||
# NULL, or 3 otherwise.
|
||||
#
|
||||
proc alter_table {tbl sql {file_format 2}} {
|
||||
sqlite3 dbat test.db
|
||||
set s [string map {' ''} $sql]
|
||||
set t [string map {' ''} $tbl]
|
||||
sqlite3_db_config dbat DEFENSIVE 0
|
||||
dbat eval [subst {
|
||||
PRAGMA writable_schema = 1;
|
||||
UPDATE sqlite_master SET sql = '$s' WHERE name = '$t' AND type = 'table';
|
||||
PRAGMA writable_schema = 0;
|
||||
}]
|
||||
dbat close
|
||||
set_file_format 2
|
||||
}
|
||||
|
||||
# Create bogus application-defined functions for functions used
|
||||
# internally by ALTER TABLE, to ensure that ALTER TABLE falls back
|
||||
# to the built-in functions.
|
||||
#
|
||||
proc failing_app_func {args} {error "bad function"}
|
||||
do_test alter2-1.0 {
|
||||
db func substr failing_app_func
|
||||
db func like failing_app_func
|
||||
db func sqlite_rename_table failing_app_func
|
||||
db func sqlite_rename_trigger failing_app_func
|
||||
db func sqlite_rename_parent failing_app_func
|
||||
catchsql {SELECT substr('abcdefg',1,3)}
|
||||
} {1 {bad function}}
|
||||
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
# Some basic tests to make sure short rows are handled.
|
||||
#
|
||||
sqlite3_db_config db DEFENSIVE 0
|
||||
do_test alter2-1.1 {
|
||||
execsql {
|
||||
CREATE TABLE abc(a, b);
|
||||
INSERT INTO abc VALUES(1, 2);
|
||||
INSERT INTO abc VALUES(3, 4);
|
||||
INSERT INTO abc VALUES(5, 6);
|
||||
}
|
||||
} {}
|
||||
do_test alter2-1.2 {
|
||||
# ALTER TABLE abc ADD COLUMN c;
|
||||
alter_table abc {CREATE TABLE abc(a, b, c);}
|
||||
} {}
|
||||
do_test alter2-1.3 {
|
||||
execsql {
|
||||
SELECT * FROM abc;
|
||||
}
|
||||
} {1 2 {} 3 4 {} 5 6 {}}
|
||||
do_test alter2-1.4 {
|
||||
execsql {
|
||||
UPDATE abc SET c = 10 WHERE a = 1;
|
||||
SELECT * FROM abc;
|
||||
}
|
||||
} {1 2 10 3 4 {} 5 6 {}}
|
||||
do_test alter2-1.5 {
|
||||
execsql {
|
||||
CREATE INDEX abc_i ON abc(c);
|
||||
}
|
||||
} {}
|
||||
do_test alter2-1.6 {
|
||||
execsql {
|
||||
SELECT c FROM abc ORDER BY c;
|
||||
}
|
||||
} {{} {} 10}
|
||||
do_test alter2-1.7 {
|
||||
execsql {
|
||||
SELECT * FROM abc WHERE c = 10;
|
||||
}
|
||||
} {1 2 10}
|
||||
do_test alter2-1.8 {
|
||||
execsql {
|
||||
SELECT sum(a), c FROM abc GROUP BY c;
|
||||
}
|
||||
} {8 {} 1 10}
|
||||
do_test alter2-1.9 {
|
||||
# ALTER TABLE abc ADD COLUMN d;
|
||||
alter_table abc {CREATE TABLE abc(a, b, c, d);}
|
||||
if {[permutation] == "prepare"} { db cache flush }
|
||||
execsql { SELECT * FROM abc; }
|
||||
execsql {
|
||||
UPDATE abc SET d = 11 WHERE c IS NULL AND a<4;
|
||||
SELECT * FROM abc;
|
||||
}
|
||||
} {1 2 10 {} 3 4 {} 11 5 6 {} {}}
|
||||
do_test alter2-1.10 {
|
||||
execsql {
|
||||
SELECT typeof(d) FROM abc;
|
||||
}
|
||||
} {null integer null}
|
||||
do_test alter2-1.99 {
|
||||
execsql {
|
||||
DROP TABLE abc;
|
||||
}
|
||||
} {}
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
# Test that views work when the underlying table structure is changed.
|
||||
#
|
||||
ifcapable view {
|
||||
do_test alter2-2.1 {
|
||||
execsql {
|
||||
CREATE TABLE abc2(a, b, c);
|
||||
INSERT INTO abc2 VALUES(1, 2, 10);
|
||||
INSERT INTO abc2 VALUES(3, 4, NULL);
|
||||
INSERT INTO abc2 VALUES(5, 6, NULL);
|
||||
CREATE VIEW abc2_v AS SELECT * FROM abc2;
|
||||
SELECT * FROM abc2_v;
|
||||
}
|
||||
} {1 2 10 3 4 {} 5 6 {}}
|
||||
do_test alter2-2.2 {
|
||||
# ALTER TABLE abc ADD COLUMN d;
|
||||
alter_table abc2 {CREATE TABLE abc2(a, b, c, d);}
|
||||
execsql {
|
||||
SELECT * FROM abc2_v;
|
||||
}
|
||||
} {1 2 10 {} 3 4 {} {} 5 6 {} {}}
|
||||
do_test alter2-2.3 {
|
||||
execsql {
|
||||
DROP TABLE abc2;
|
||||
DROP VIEW abc2_v;
|
||||
}
|
||||
} {}
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
# Test that triggers work when a short row is copied to the old.*
|
||||
# trigger pseudo-table.
|
||||
#
|
||||
ifcapable trigger {
|
||||
do_test alter2-3.1 {
|
||||
execsql {
|
||||
CREATE TABLE abc3(a, b);
|
||||
CREATE TABLE blog(o, n);
|
||||
CREATE TRIGGER abc3_t AFTER UPDATE OF b ON abc3 BEGIN
|
||||
INSERT INTO blog VALUES(old.b, new.b);
|
||||
END;
|
||||
}
|
||||
} {}
|
||||
do_test alter2-3.2 {
|
||||
execsql {
|
||||
INSERT INTO abc3 VALUES(1, 4);
|
||||
UPDATE abc3 SET b = 2 WHERE b = 4;
|
||||
SELECT * FROM blog;
|
||||
}
|
||||
} {4 2}
|
||||
do_test alter2-3.3 {
|
||||
execsql {
|
||||
INSERT INTO abc3 VALUES(3, 4);
|
||||
INSERT INTO abc3 VALUES(5, 6);
|
||||
}
|
||||
alter_table abc3 {CREATE TABLE abc3(a, b, c);}
|
||||
execsql {
|
||||
SELECT * FROM abc3;
|
||||
}
|
||||
} {1 2 {} 3 4 {} 5 6 {}}
|
||||
do_test alter2-3.4 {
|
||||
execsql {
|
||||
UPDATE abc3 SET b = b*2 WHERE a<4;
|
||||
SELECT * FROM abc3;
|
||||
}
|
||||
} {1 4 {} 3 8 {} 5 6 {}}
|
||||
do_test alter2-3.5 {
|
||||
execsql {
|
||||
SELECT * FROM blog;
|
||||
}
|
||||
} {4 2 2 4 4 8}
|
||||
|
||||
do_test alter2-3.6 {
|
||||
execsql {
|
||||
CREATE TABLE clog(o, n);
|
||||
CREATE TRIGGER abc3_t2 AFTER UPDATE OF c ON abc3 BEGIN
|
||||
INSERT INTO clog VALUES(old.c, new.c);
|
||||
END;
|
||||
UPDATE abc3 SET c = a*2;
|
||||
SELECT * FROM clog;
|
||||
}
|
||||
} {{} 2 {} 6 {} 10}
|
||||
} else {
|
||||
execsql { CREATE TABLE abc3(a, b); }
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
# Check that an error occurs if the database is upgraded to a file
|
||||
# format that SQLite does not support (in this case 5). Note: The
|
||||
# file format is checked each time the schema is read, so changing the
|
||||
# file format requires incrementing the schema cookie.
|
||||
#
|
||||
do_test alter2-4.1 {
|
||||
db close
|
||||
set_file_format 5
|
||||
catch { sqlite3 db test.db }
|
||||
set {} {}
|
||||
} {}
|
||||
do_test alter2-4.2 {
|
||||
# We have to run two queries here because the Tcl interface uses
|
||||
# sqlite3_prepare_v2(). In this case, the first query encounters an
|
||||
# SQLITE_SCHEMA error. Then, when trying to recompile the statement, the
|
||||
# "unsupported file format" error is encountered. So the error code
|
||||
# returned is SQLITE_SCHEMA, not SQLITE_ERROR as required by the following
|
||||
# test case.
|
||||
#
|
||||
# When the query is attempted a second time, the same error message is
|
||||
# returned but the error code is SQLITE_ERROR, because the unsupported
|
||||
# file format was detected during a call to sqlite3_prepare(), not
|
||||
# sqlite3_step().
|
||||
#
|
||||
catchsql { SELECT * FROM sqlite_master; }
|
||||
catchsql { SELECT * FROM sqlite_master; }
|
||||
} {1 {unsupported file format}}
|
||||
do_test alter2-4.3 {
|
||||
sqlite3_errcode db
|
||||
} {SQLITE_ERROR}
|
||||
do_test alter2-4.4 {
|
||||
set ::DB [sqlite3_connection_pointer db]
|
||||
catchsql {
|
||||
SELECT * FROM sqlite_master;
|
||||
}
|
||||
} {1 {unsupported file format}}
|
||||
do_test alter2-4.5 {
|
||||
sqlite3_errcode db
|
||||
} {SQLITE_ERROR}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
# Check that executing VACUUM on a file with file-format version 2
|
||||
# resets the file format to 1.
|
||||
#
|
||||
set default_file_format [expr $SQLITE_DEFAULT_FILE_FORMAT==4 ? 4 : 1]
|
||||
ifcapable vacuum {
|
||||
do_test alter2-5.1 {
|
||||
set_file_format 2
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
execsql {SELECT 1 FROM sqlite_master LIMIT 1;}
|
||||
get_file_format
|
||||
} {2}
|
||||
do_test alter2-5.2 {
|
||||
execsql { VACUUM }
|
||||
} {}
|
||||
do_test alter2-5.3 {
|
||||
get_file_format
|
||||
} $default_file_format
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
# Test that when a database with file-format 2 is opened, new
|
||||
# databases are still created with file-format 1.
|
||||
#
|
||||
do_test alter2-6.1 {
|
||||
db close
|
||||
set_file_format 2
|
||||
sqlite3 db test.db
|
||||
get_file_format
|
||||
} {2}
|
||||
ifcapable attach {
|
||||
do_test alter2-6.2 {
|
||||
forcedelete test2.db-journal
|
||||
forcedelete test2.db
|
||||
execsql {
|
||||
ATTACH 'test2.db' AS aux;
|
||||
CREATE TABLE aux.t1(a, b);
|
||||
}
|
||||
get_file_format test2.db
|
||||
} $default_file_format
|
||||
}
|
||||
do_test alter2-6.3 {
|
||||
execsql {
|
||||
CREATE TABLE t1(a, b);
|
||||
}
|
||||
get_file_format
|
||||
} {2}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
# Test that types and values for columns added with default values
|
||||
# other than NULL work with SELECT statements.
|
||||
#
|
||||
do_test alter2-7.1 {
|
||||
execsql {
|
||||
DROP TABLE t1;
|
||||
CREATE TABLE t1(a);
|
||||
INSERT INTO t1 VALUES(1);
|
||||
INSERT INTO t1 VALUES(2);
|
||||
INSERT INTO t1 VALUES(3);
|
||||
INSERT INTO t1 VALUES(4);
|
||||
SELECT * FROM t1;
|
||||
}
|
||||
} {1 2 3 4}
|
||||
do_test alter2-7.2 {
|
||||
set sql {CREATE TABLE t1(a, b DEFAULT '123', c INTEGER DEFAULT '123')}
|
||||
alter_table t1 $sql 3
|
||||
execsql {
|
||||
SELECT * FROM t1 LIMIT 1;
|
||||
}
|
||||
} {1 123 123}
|
||||
do_test alter2-7.3 {
|
||||
execsql {
|
||||
SELECT a, typeof(a), b, typeof(b), c, typeof(c) FROM t1 LIMIT 1;
|
||||
}
|
||||
} {1 integer 123 text 123 integer}
|
||||
do_test alter2-7.4 {
|
||||
execsql {
|
||||
SELECT a, typeof(a), b, typeof(b), c, typeof(c) FROM t1 LIMIT 1;
|
||||
}
|
||||
} {1 integer 123 text 123 integer}
|
||||
do_test alter2-7.5 {
|
||||
set sql {CREATE TABLE t1(a, b DEFAULT -123.0, c VARCHAR(10) default 5)}
|
||||
alter_table t1 $sql 3
|
||||
execsql {
|
||||
SELECT a, typeof(a), b, typeof(b), c, typeof(c) FROM t1 LIMIT 1;
|
||||
}
|
||||
} {1 integer -123 integer 5 text}
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
# Test that UPDATE trigger tables work with default values, and that when
|
||||
# a row is updated the default values are correctly transfered to the
|
||||
# new row.
|
||||
#
|
||||
ifcapable trigger {
|
||||
db function set_val {set ::val}
|
||||
do_test alter2-8.1 {
|
||||
execsql {
|
||||
CREATE TRIGGER trig1 BEFORE UPDATE ON t1 BEGIN
|
||||
SELECT set_val(
|
||||
old.b||' '||typeof(old.b)||' '||old.c||' '||typeof(old.c)||' '||
|
||||
new.b||' '||typeof(new.b)||' '||new.c||' '||typeof(new.c)
|
||||
);
|
||||
END;
|
||||
}
|
||||
list
|
||||
} {}
|
||||
}
|
||||
do_test alter2-8.2 {
|
||||
execsql {
|
||||
UPDATE t1 SET c = 10 WHERE a = 1;
|
||||
SELECT a, typeof(a), b, typeof(b), c, typeof(c) FROM t1 LIMIT 1;
|
||||
}
|
||||
} {1 integer -123 integer 10 text}
|
||||
ifcapable trigger {
|
||||
do_test alter2-8.3 {
|
||||
set ::val
|
||||
} {-123 integer 5 text -123 integer 10 text}
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
# Test that DELETE trigger tables work with default values, and that when
|
||||
# a row is updated the default values are correctly transfered to the
|
||||
# new row.
|
||||
#
|
||||
ifcapable trigger {
|
||||
do_test alter2-9.1 {
|
||||
execsql {
|
||||
CREATE TRIGGER trig2 BEFORE DELETE ON t1 BEGIN
|
||||
SELECT set_val(
|
||||
old.b||' '||typeof(old.b)||' '||old.c||' '||typeof(old.c)
|
||||
);
|
||||
END;
|
||||
}
|
||||
list
|
||||
} {}
|
||||
do_test alter2-9.2 {
|
||||
execsql {
|
||||
DELETE FROM t1 WHERE a = 2;
|
||||
}
|
||||
set ::val
|
||||
} {-123 integer 5 text}
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
# Test creating an index on a column added with a default value.
|
||||
#
|
||||
ifcapable bloblit {
|
||||
do_test alter2-10.1 {
|
||||
execsql {
|
||||
CREATE TABLE t2(a);
|
||||
INSERT INTO t2 VALUES('a');
|
||||
INSERT INTO t2 VALUES('b');
|
||||
INSERT INTO t2 VALUES('c');
|
||||
INSERT INTO t2 VALUES('d');
|
||||
}
|
||||
alter_table t2 {CREATE TABLE t2(a, b DEFAULT X'ABCD', c DEFAULT NULL);} 3
|
||||
catchsql {
|
||||
SELECT * FROM sqlite_master;
|
||||
}
|
||||
execsql {
|
||||
SELECT quote(a), quote(b), quote(c) FROM t2 LIMIT 1;
|
||||
}
|
||||
} {'a' X'ABCD' NULL}
|
||||
do_test alter2-10.2 {
|
||||
execsql {
|
||||
CREATE INDEX i1 ON t2(b);
|
||||
SELECT a FROM t2 WHERE b = X'ABCD';
|
||||
}
|
||||
} {a b c d}
|
||||
do_test alter2-10.3 {
|
||||
execsql {
|
||||
DELETE FROM t2 WHERE a = 'c';
|
||||
SELECT a FROM t2 WHERE b = X'ABCD';
|
||||
}
|
||||
} {a b d}
|
||||
do_test alter2-10.4 {
|
||||
execsql {
|
||||
SELECT count(b) FROM t2 WHERE b = X'ABCD';
|
||||
}
|
||||
} {3}
|
||||
}
|
||||
|
||||
finish_test
|
443
testdata/tcl/alter3.test
vendored
Normal file
443
testdata/tcl/alter3.test
vendored
Normal file
|
@ -0,0 +1,443 @@
|
|||
# 2005 February 19
|
||||
#
|
||||
# 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 regression tests for SQLite library. The
|
||||
# focus of this script is testing that SQLite can handle a subtle
|
||||
# file format change that may be used in the future to implement
|
||||
# "ALTER TABLE ... ADD COLUMN".
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
|
||||
source $testdir/tester.tcl
|
||||
|
||||
# If SQLITE_OMIT_ALTERTABLE is defined, omit this file.
|
||||
ifcapable !altertable {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
# Determine if there is a codec available on this test.
|
||||
#
|
||||
if {[catch {sqlite3 -has-codec} r] || $r} {
|
||||
set has_codec 1
|
||||
} else {
|
||||
set has_codec 0
|
||||
}
|
||||
|
||||
|
||||
# Test Organisation:
|
||||
# ------------------
|
||||
#
|
||||
# alter3-1.*: Test that ALTER TABLE correctly modifies the CREATE TABLE sql.
|
||||
# alter3-2.*: Test error messages.
|
||||
# alter3-3.*: Test adding columns with default value NULL.
|
||||
# alter3-4.*: Test adding columns with default values other than NULL.
|
||||
# alter3-5.*: Test adding columns to tables in ATTACHed databases.
|
||||
# alter3-6.*: Test that temp triggers are not accidentally dropped.
|
||||
# alter3-7.*: Test that VACUUM resets the file-format.
|
||||
#
|
||||
|
||||
# This procedure returns the value of the file-format in file 'test.db'.
|
||||
#
|
||||
proc get_file_format {{fname test.db}} {
|
||||
return [hexio_get_int [hexio_read $fname 44 4]]
|
||||
}
|
||||
|
||||
do_test alter3-1.1 {
|
||||
sqlite3_db_config db LEGACY_FILE_FORMAT 1
|
||||
execsql {
|
||||
CREATE TABLE abc(a, b, c);
|
||||
SELECT sql FROM sqlite_master;
|
||||
}
|
||||
} {{CREATE TABLE abc(a, b, c)}}
|
||||
do_test alter3-1.2 {
|
||||
execsql {ALTER TABLE abc ADD d INTEGER;}
|
||||
execsql {
|
||||
SELECT sql FROM sqlite_master;
|
||||
}
|
||||
} {{CREATE TABLE abc(a, b, c, d INTEGER)}}
|
||||
do_test alter3-1.3 {
|
||||
execsql {ALTER TABLE abc ADD e}
|
||||
execsql {
|
||||
SELECT sql FROM sqlite_master;
|
||||
}
|
||||
} {{CREATE TABLE abc(a, b, c, d INTEGER, e)}}
|
||||
do_test alter3-1.4 {
|
||||
execsql {
|
||||
CREATE TABLE main.t1(a, b);
|
||||
ALTER TABLE t1 ADD c;
|
||||
SELECT sql FROM sqlite_master WHERE tbl_name = 't1';
|
||||
}
|
||||
} {{CREATE TABLE t1(a, b, c)}}
|
||||
do_test alter3-1.5 {
|
||||
execsql {
|
||||
ALTER TABLE t1 ADD d CHECK (a>d);
|
||||
SELECT sql FROM sqlite_master WHERE tbl_name = 't1';
|
||||
}
|
||||
} {{CREATE TABLE t1(a, b, c, d CHECK (a>d))}}
|
||||
ifcapable foreignkey {
|
||||
do_test alter3-1.6 {
|
||||
execsql {
|
||||
CREATE TABLE t2(a, b, UNIQUE(a, b));
|
||||
ALTER TABLE t2 ADD c REFERENCES t1(c) ;
|
||||
SELECT sql FROM sqlite_master WHERE tbl_name = 't2' AND type = 'table';
|
||||
}
|
||||
} {{CREATE TABLE t2(a, b, c REFERENCES t1(c), UNIQUE(a, b))}}
|
||||
}
|
||||
do_test alter3-1.7 {
|
||||
execsql {
|
||||
CREATE TABLE t3(a, b, UNIQUE(a, b));
|
||||
ALTER TABLE t3 ADD COLUMN c VARCHAR(10, 20);
|
||||
SELECT sql FROM sqlite_master WHERE tbl_name = 't3' AND type = 'table';
|
||||
}
|
||||
} {{CREATE TABLE t3(a, b, c VARCHAR(10, 20), UNIQUE(a, b))}}
|
||||
do_test alter3-1.99 {
|
||||
catchsql {
|
||||
# May not exist if foriegn-keys are omitted at compile time.
|
||||
DROP TABLE t2;
|
||||
}
|
||||
execsql {
|
||||
DROP TABLE abc;
|
||||
DROP TABLE t1;
|
||||
DROP TABLE t3;
|
||||
}
|
||||
} {}
|
||||
|
||||
do_test alter3-2.1 {
|
||||
execsql {
|
||||
CREATE TABLE t1(a, b);
|
||||
INSERT INTO t1 VALUES(1,2);
|
||||
}
|
||||
catchsql {
|
||||
ALTER TABLE t1 ADD c PRIMARY KEY;
|
||||
}
|
||||
} {1 {Cannot add a PRIMARY KEY column}}
|
||||
do_test alter3-2.2 {
|
||||
catchsql {
|
||||
ALTER TABLE t1 ADD c UNIQUE
|
||||
}
|
||||
} {1 {Cannot add a UNIQUE column}}
|
||||
do_test alter3-2.3 {
|
||||
catchsql {
|
||||
ALTER TABLE t1 ADD b VARCHAR(10)
|
||||
}
|
||||
} {1 {duplicate column name: b}}
|
||||
do_test alter3-2.3 {
|
||||
catchsql {
|
||||
ALTER TABLE t1 ADD c NOT NULL;
|
||||
}
|
||||
} {1 {Cannot add a NOT NULL column with default value NULL}}
|
||||
do_test alter3-2.4 {
|
||||
catchsql {
|
||||
ALTER TABLE t1 ADD c NOT NULL DEFAULT 10;
|
||||
}
|
||||
} {0 {}}
|
||||
ifcapable view {
|
||||
do_test alter3-2.5 {
|
||||
execsql {
|
||||
CREATE VIEW v1 AS SELECT * FROM t1;
|
||||
}
|
||||
catchsql {
|
||||
alter table v1 add column d;
|
||||
}
|
||||
} {1 {Cannot add a column to a view}}
|
||||
}
|
||||
do_test alter3-2.6 {
|
||||
catchsql {
|
||||
alter table t1 add column d DEFAULT CURRENT_TIME;
|
||||
}
|
||||
} {1 {Cannot add a column with non-constant default}}
|
||||
do_test alter3-2.99 {
|
||||
execsql {
|
||||
DROP TABLE t1;
|
||||
}
|
||||
} {}
|
||||
|
||||
do_test alter3-3.1 {
|
||||
execsql {
|
||||
CREATE TABLE t1(a, b);
|
||||
INSERT INTO t1 VALUES(1, 100);
|
||||
INSERT INTO t1 VALUES(2, 300);
|
||||
SELECT * FROM t1;
|
||||
}
|
||||
} {1 100 2 300}
|
||||
do_test alter3-3.1 {
|
||||
execsql {
|
||||
PRAGMA schema_version = 10;
|
||||
}
|
||||
} {}
|
||||
do_test alter3-3.2 {
|
||||
execsql {
|
||||
ALTER TABLE t1 ADD c;
|
||||
SELECT * FROM t1;
|
||||
}
|
||||
} {1 100 {} 2 300 {}}
|
||||
if {!$has_codec} {
|
||||
do_test alter3-3.3 {
|
||||
get_file_format
|
||||
} {3}
|
||||
}
|
||||
ifcapable schema_version {
|
||||
do_test alter3-3.4 {
|
||||
execsql {
|
||||
PRAGMA schema_version;
|
||||
}
|
||||
} {11}
|
||||
}
|
||||
|
||||
do_test alter3-4.1 {
|
||||
db close
|
||||
forcedelete test.db
|
||||
set ::DB [sqlite3 db test.db]
|
||||
sqlite3_db_config db LEGACY_FILE_FORMAT 1
|
||||
execsql {
|
||||
CREATE TABLE t1(a, b);
|
||||
INSERT INTO t1 VALUES(1, 100);
|
||||
INSERT INTO t1 VALUES(2, 300);
|
||||
SELECT * FROM t1;
|
||||
}
|
||||
} {1 100 2 300}
|
||||
do_test alter3-4.1 {
|
||||
execsql {
|
||||
PRAGMA schema_version = 20;
|
||||
}
|
||||
} {}
|
||||
do_test alter3-4.2 {
|
||||
execsql {
|
||||
ALTER TABLE t1 ADD c DEFAULT 'hello world';
|
||||
SELECT * FROM t1;
|
||||
}
|
||||
} {1 100 {hello world} 2 300 {hello world}}
|
||||
if {!$has_codec} {
|
||||
do_test alter3-4.3 {
|
||||
get_file_format
|
||||
} {3}
|
||||
}
|
||||
ifcapable schema_version {
|
||||
do_test alter3-4.4 {
|
||||
execsql {
|
||||
PRAGMA schema_version;
|
||||
}
|
||||
} {21}
|
||||
}
|
||||
do_test alter3-4.99 {
|
||||
execsql {
|
||||
DROP TABLE t1;
|
||||
}
|
||||
} {}
|
||||
|
||||
ifcapable attach {
|
||||
do_test alter3-5.1 {
|
||||
forcedelete test2.db
|
||||
forcedelete test2.db-journal
|
||||
execsql {
|
||||
CREATE TABLE t1(a, b);
|
||||
INSERT INTO t1 VALUES(1, 'one');
|
||||
INSERT INTO t1 VALUES(2, 'two');
|
||||
ATTACH 'test2.db' AS aux;
|
||||
CREATE TABLE aux.t1 AS SELECT * FROM t1;
|
||||
PRAGMA aux.schema_version = 30;
|
||||
SELECT sql FROM aux.sqlite_master;
|
||||
}
|
||||
} {{CREATE TABLE t1(a,b)}}
|
||||
do_test alter3-5.2 {
|
||||
execsql {
|
||||
ALTER TABLE aux.t1 ADD COLUMN c VARCHAR(128);
|
||||
SELECT sql FROM aux.sqlite_master;
|
||||
}
|
||||
} {{CREATE TABLE t1(a,b, c VARCHAR(128))}}
|
||||
do_test alter3-5.3 {
|
||||
execsql {
|
||||
SELECT * FROM aux.t1;
|
||||
}
|
||||
} {1 one {} 2 two {}}
|
||||
ifcapable schema_version {
|
||||
do_test alter3-5.4 {
|
||||
execsql {
|
||||
PRAGMA aux.schema_version;
|
||||
}
|
||||
} {31}
|
||||
}
|
||||
if {!$has_codec} {
|
||||
do_test alter3-5.5 {
|
||||
list [get_file_format test2.db] [get_file_format]
|
||||
} {3 3}
|
||||
}
|
||||
do_test alter3-5.6 {
|
||||
execsql {
|
||||
ALTER TABLE aux.t1 ADD COLUMN d DEFAULT 1000;
|
||||
SELECT sql FROM aux.sqlite_master;
|
||||
}
|
||||
} {{CREATE TABLE t1(a,b, c VARCHAR(128), d DEFAULT 1000)}}
|
||||
do_test alter3-5.7 {
|
||||
execsql {
|
||||
SELECT * FROM aux.t1;
|
||||
}
|
||||
} {1 one {} 1000 2 two {} 1000}
|
||||
ifcapable schema_version {
|
||||
do_test alter3-5.8 {
|
||||
execsql {
|
||||
PRAGMA aux.schema_version;
|
||||
}
|
||||
} {32}
|
||||
}
|
||||
do_test alter3-5.9 {
|
||||
execsql {
|
||||
SELECT * FROM t1;
|
||||
}
|
||||
} {1 one 2 two}
|
||||
do_test alter3-5.99 {
|
||||
execsql {
|
||||
DROP TABLE aux.t1;
|
||||
DROP TABLE t1;
|
||||
}
|
||||
} {}
|
||||
}
|
||||
|
||||
#----------------------------------------------------------------
|
||||
# Test that the table schema is correctly reloaded when a column
|
||||
# is added to a table.
|
||||
#
|
||||
ifcapable trigger&&tempdb {
|
||||
do_test alter3-6.1 {
|
||||
execsql {
|
||||
CREATE TABLE t1(a, b);
|
||||
CREATE TABLE log(trig, a, b);
|
||||
|
||||
CREATE TRIGGER t1_a AFTER INSERT ON t1 BEGIN
|
||||
INSERT INTO log VALUES('a', new.a, new.b);
|
||||
END;
|
||||
CREATE TEMP TRIGGER t1_b AFTER INSERT ON t1 BEGIN
|
||||
INSERT INTO log VALUES('b', new.a, new.b);
|
||||
END;
|
||||
|
||||
INSERT INTO t1 VALUES(1, 2);
|
||||
SELECT * FROM log;
|
||||
}
|
||||
} {b 1 2 a 1 2}
|
||||
do_test alter3-6.2 {
|
||||
execsql {
|
||||
ALTER TABLE t1 ADD COLUMN c DEFAULT 'c';
|
||||
INSERT INTO t1(a, b) VALUES(3, 4);
|
||||
SELECT * FROM log;
|
||||
}
|
||||
} {b 1 2 a 1 2 b 3 4 a 3 4}
|
||||
}
|
||||
|
||||
if {!$has_codec} {
|
||||
ifcapable vacuum {
|
||||
do_test alter3-7.1 {
|
||||
execsql {
|
||||
VACUUM;
|
||||
}
|
||||
get_file_format
|
||||
} {1}
|
||||
do_test alter3-7.2 {
|
||||
execsql {
|
||||
CREATE TABLE abc(a, b, c);
|
||||
ALTER TABLE abc ADD d DEFAULT NULL;
|
||||
}
|
||||
get_file_format
|
||||
} {3}
|
||||
do_test alter3-7.3 {
|
||||
execsql {
|
||||
ALTER TABLE abc ADD e DEFAULT 10;
|
||||
}
|
||||
get_file_format
|
||||
} {3}
|
||||
do_test alter3-7.4 {
|
||||
execsql {
|
||||
ALTER TABLE abc ADD f DEFAULT NULL;
|
||||
}
|
||||
get_file_format
|
||||
} {3}
|
||||
do_test alter3-7.5 {
|
||||
execsql {
|
||||
VACUUM;
|
||||
}
|
||||
get_file_format
|
||||
} {1}
|
||||
}
|
||||
}
|
||||
|
||||
# Ticket #1183 - Make sure adding columns to large tables does not cause
|
||||
# memory corruption (as was the case before this bug was fixed).
|
||||
do_test alter3-8.1 {
|
||||
execsql {
|
||||
CREATE TABLE t4(c1);
|
||||
}
|
||||
} {}
|
||||
set ::sql ""
|
||||
do_test alter3-8.2 {
|
||||
set cols c1
|
||||
for {set i 2} {$i < 100} {incr i} {
|
||||
execsql "
|
||||
ALTER TABLE t4 ADD c$i
|
||||
"
|
||||
lappend cols c$i
|
||||
}
|
||||
set ::sql "CREATE TABLE t4([join $cols {, }])"
|
||||
list
|
||||
} {}
|
||||
do_test alter3-8.2 {
|
||||
execsql {
|
||||
SELECT sql FROM sqlite_master WHERE name = 't4';
|
||||
}
|
||||
} [list $::sql]
|
||||
|
||||
# 2021-07-20: Add support for detecting CHECK and NOT NULL constraint
|
||||
# violations in ALTER TABLE ADD COLUMN
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test alter3-9.1 {
|
||||
CREATE TABLE t1(a,b);
|
||||
INSERT INTO t1 VALUES(1, 2), ('null!',NULL), (3,4);
|
||||
} {}
|
||||
do_catchsql_test alter3-9.2 {
|
||||
ALTER TABLE t1 ADD COLUMN c CHECK(a!=1);
|
||||
} {1 {CHECK constraint failed}}
|
||||
do_catchsql_test alter3-9.3 {
|
||||
ALTER TABLE t1 ADD COLUMN c CHECK(a!=3);
|
||||
} {1 {CHECK constraint failed}}
|
||||
do_catchsql_test alter3-9.4 {
|
||||
ALTER TABLE t1 ADD COLUMN c CHECK(a!=2);
|
||||
} {0 {}}
|
||||
do_catchsql_test alter3-9.5 {
|
||||
ALTER TABLE t1 ADD COLUMN d AS (b+1) NOT NULL;
|
||||
} {1 {NOT NULL constraint failed}}
|
||||
do_catchsql_test alter3-9.6 {
|
||||
ALTER TABLE t1 ADD COLUMN d AS (b+1) NOT NULL CHECK(a!=1);
|
||||
} {1 {CHECK constraint failed}}
|
||||
do_catchsql_test alter3-9.7 {
|
||||
ALTER TABLE t1 ADD COLUMN d AS (b+1) NOT NULL CHECK(a!=3);
|
||||
} {1 {NOT NULL constraint failed}}
|
||||
|
||||
do_execsql_test alter3-9.10 {
|
||||
CREATE TEMP TABLE t0(m,n);
|
||||
INSERT INTO t0 VALUES(1, 2), ('null!',NULL), (3,4);
|
||||
ATTACH ':memory:' AS aux1;
|
||||
CREATE TABLE aux1.t2(x,y);
|
||||
INSERT INTO t2 VALUES(1, 2), ('null!',NULL), (3,4);
|
||||
} {}
|
||||
do_catchsql_test alter3-9.11 {
|
||||
ALTER TABLE t0 ADD COLUMN xtra1 AS (n+1) NOT NULL CHECK(m!=1);
|
||||
} {1 {CHECK constraint failed}}
|
||||
do_catchsql_test alter3-9.12 {
|
||||
ALTER TABLE t0 ADD COLUMN xtra1 AS (n+1) NOT NULL CHECK(m!=3);
|
||||
} {1 {NOT NULL constraint failed}}
|
||||
do_catchsql_test alter3-9.13 {
|
||||
ALTER TABLE t2 ADD COLUMN xtra1 AS (y+1) NOT NULL CHECK(x!=1);
|
||||
} {1 {CHECK constraint failed}}
|
||||
do_catchsql_test alter3-9.14 {
|
||||
ALTER TABLE t2 ADD COLUMN xtra1 AS (y+1) NOT NULL CHECK(x!=3);
|
||||
} {1 {NOT NULL constraint failed}}
|
||||
|
||||
finish_test
|
427
testdata/tcl/alter4.test
vendored
Normal file
427
testdata/tcl/alter4.test
vendored
Normal file
|
@ -0,0 +1,427 @@
|
|||
# 2009 February 2
|
||||
#
|
||||
# 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 regression tests for SQLite library. The
|
||||
# focus of this script is testing that SQLite can handle a subtle
|
||||
# file format change that may be used in the future to implement
|
||||
# "ALTER TABLE ... ADD COLUMN".
|
||||
#
|
||||
# $Id: alter4.test,v 1.1 2009/02/02 18:03:22 drh Exp $
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
|
||||
source $testdir/tester.tcl
|
||||
|
||||
# If SQLITE_OMIT_ALTERTABLE is defined, omit this file.
|
||||
ifcapable !altertable {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
# Test Organisation:
|
||||
# ------------------
|
||||
#
|
||||
# alter4-1.*: Test that ALTER TABLE correctly modifies the CREATE TABLE sql.
|
||||
# alter4-2.*: Test error messages.
|
||||
# alter4-3.*: Test adding columns with default value NULL.
|
||||
# alter4-4.*: Test adding columns with default values other than NULL.
|
||||
# alter4-5.*: Test adding columns to tables in ATTACHed databases.
|
||||
# alter4-6.*: Test that temp triggers are not accidentally dropped.
|
||||
# alter4-7.*: Test that VACUUM resets the file-format.
|
||||
#
|
||||
|
||||
do_test alter4-1.1 {
|
||||
execsql {
|
||||
CREATE TEMP TABLE abc(a, b, c);
|
||||
SELECT sql FROM sqlite_temp_master;
|
||||
}
|
||||
} {{CREATE TABLE abc(a, b, c)}}
|
||||
do_test alter4-1.1b {
|
||||
execsql {
|
||||
SELECT sql FROM temp.sqlite_master;
|
||||
}
|
||||
} {{CREATE TABLE abc(a, b, c)}}
|
||||
do_test alter4-1.2 {
|
||||
execsql {ALTER TABLE abc ADD d INTEGER;}
|
||||
execsql {
|
||||
SELECT sql FROM sqlite_temp_master;
|
||||
}
|
||||
} {{CREATE TABLE abc(a, b, c, d INTEGER)}}
|
||||
do_test alter4-1.2b {
|
||||
execsql {
|
||||
SELECT sql FROM temp.sqlite_master;
|
||||
}
|
||||
} {{CREATE TABLE abc(a, b, c, d INTEGER)}}
|
||||
do_test alter4-1.3 {
|
||||
execsql {ALTER TABLE abc ADD e}
|
||||
execsql {
|
||||
SELECT sql FROM sqlite_temp_master;
|
||||
}
|
||||
} {{CREATE TABLE abc(a, b, c, d INTEGER, e)}}
|
||||
do_test alter4-1.3b {
|
||||
execsql {
|
||||
SELECT sql FROM temp.sqlite_master;
|
||||
}
|
||||
} {{CREATE TABLE abc(a, b, c, d INTEGER, e)}}
|
||||
do_test alter4-1.4 {
|
||||
execsql {
|
||||
CREATE TABLE temp.t1(a, b);
|
||||
ALTER TABLE t1 ADD c;
|
||||
SELECT sql FROM sqlite_temp_master WHERE tbl_name = 't1';
|
||||
}
|
||||
} {{CREATE TABLE t1(a, b, c)}}
|
||||
do_test alter4-1.4b {
|
||||
execsql {
|
||||
SELECT sql FROM temp.sqlite_master WHERE tbl_name = 't1';
|
||||
}
|
||||
} {{CREATE TABLE t1(a, b, c)}}
|
||||
do_test alter4-1.5 {
|
||||
execsql {
|
||||
ALTER TABLE t1 ADD d CHECK (a>d);
|
||||
SELECT sql FROM sqlite_temp_master WHERE tbl_name = 't1';
|
||||
}
|
||||
} {{CREATE TABLE t1(a, b, c, d CHECK (a>d))}}
|
||||
ifcapable foreignkey {
|
||||
do_test alter4-1.6 {
|
||||
execsql {
|
||||
CREATE TEMP TABLE t2(a, b, UNIQUE(a, b));
|
||||
ALTER TABLE t2 ADD c REFERENCES t1(c) ;
|
||||
SELECT sql FROM sqlite_temp_master
|
||||
WHERE tbl_name = 't2' AND type = 'table';
|
||||
}
|
||||
} {{CREATE TABLE t2(a, b, c REFERENCES t1(c), UNIQUE(a, b))}}
|
||||
}
|
||||
do_test alter4-1.7 {
|
||||
execsql {
|
||||
CREATE TEMPORARY TABLE t3(a, b, UNIQUE(a, b));
|
||||
ALTER TABLE t3 ADD COLUMN c VARCHAR(10, 20);
|
||||
SELECT sql FROM sqlite_temp_master
|
||||
WHERE tbl_name = 't3' AND type = 'table';
|
||||
}
|
||||
} {{CREATE TABLE t3(a, b, c VARCHAR(10, 20), UNIQUE(a, b))}}
|
||||
do_test alter4-1.99 {
|
||||
catchsql {
|
||||
# May not exist if foriegn-keys are omitted at compile time.
|
||||
DROP TABLE t2;
|
||||
}
|
||||
execsql {
|
||||
DROP TABLE abc;
|
||||
DROP TABLE t1;
|
||||
DROP TABLE t3;
|
||||
}
|
||||
} {}
|
||||
|
||||
do_test alter4-2.1 {
|
||||
execsql {
|
||||
CREATE TABLE temp.t1(a, b);
|
||||
INSERT INTO t1 VALUES(1,2);
|
||||
}
|
||||
catchsql {
|
||||
ALTER TABLE t1 ADD c PRIMARY KEY;
|
||||
}
|
||||
} {1 {Cannot add a PRIMARY KEY column}}
|
||||
do_test alter4-2.2 {
|
||||
catchsql {
|
||||
ALTER TABLE t1 ADD c UNIQUE
|
||||
}
|
||||
} {1 {Cannot add a UNIQUE column}}
|
||||
do_test alter4-2.3 {
|
||||
catchsql {
|
||||
ALTER TABLE t1 ADD b VARCHAR(10)
|
||||
}
|
||||
} {1 {duplicate column name: b}}
|
||||
do_test alter4-2.3 {
|
||||
catchsql {
|
||||
ALTER TABLE t1 ADD c NOT NULL;
|
||||
}
|
||||
} {1 {Cannot add a NOT NULL column with default value NULL}}
|
||||
do_test alter4-2.4 {
|
||||
catchsql {
|
||||
ALTER TABLE t1 ADD c NOT NULL DEFAULT 10;
|
||||
}
|
||||
} {0 {}}
|
||||
ifcapable view {
|
||||
do_test alter4-2.5 {
|
||||
execsql {
|
||||
CREATE TEMPORARY VIEW v1 AS SELECT * FROM t1;
|
||||
}
|
||||
catchsql {
|
||||
alter table v1 add column d;
|
||||
}
|
||||
} {1 {Cannot add a column to a view}}
|
||||
}
|
||||
do_test alter4-2.6 {
|
||||
catchsql {
|
||||
alter table t1 add column d DEFAULT CURRENT_TIME;
|
||||
}
|
||||
} {1 {Cannot add a column with non-constant default}}
|
||||
do_test alter4-2.7 {
|
||||
catchsql {
|
||||
alter table t1 add column d default (-5+1);
|
||||
}
|
||||
} {1 {Cannot add a column with non-constant default}}
|
||||
do_test alter4-2.99 {
|
||||
execsql {
|
||||
DROP TABLE t1;
|
||||
}
|
||||
} {}
|
||||
|
||||
do_test alter4-3.1 {
|
||||
execsql {
|
||||
CREATE TEMP TABLE t1(a, b);
|
||||
INSERT INTO t1 VALUES(1, 100);
|
||||
INSERT INTO t1 VALUES(2, 300);
|
||||
SELECT * FROM t1;
|
||||
}
|
||||
} {1 100 2 300}
|
||||
do_test alter4-3.1 {
|
||||
execsql {
|
||||
PRAGMA schema_version = 10;
|
||||
}
|
||||
} {}
|
||||
do_test alter4-3.2 {
|
||||
execsql {
|
||||
ALTER TABLE t1 ADD c;
|
||||
SELECT * FROM t1;
|
||||
}
|
||||
} {1 100 {} 2 300 {}}
|
||||
ifcapable schema_version {
|
||||
do_test alter4-3.4 {
|
||||
execsql {
|
||||
PRAGMA schema_version;
|
||||
}
|
||||
} {10}
|
||||
}
|
||||
|
||||
do_test alter4-4.1 {
|
||||
db close
|
||||
forcedelete test.db
|
||||
set ::DB [sqlite3 db test.db]
|
||||
execsql {
|
||||
CREATE TEMP TABLE t1(a, b);
|
||||
INSERT INTO t1 VALUES(1, 100);
|
||||
INSERT INTO t1 VALUES(2, 300);
|
||||
SELECT * FROM t1;
|
||||
}
|
||||
} {1 100 2 300}
|
||||
do_test alter4-4.1 {
|
||||
execsql {
|
||||
PRAGMA schema_version = 20;
|
||||
}
|
||||
} {}
|
||||
do_test alter4-4.2 {
|
||||
execsql {
|
||||
ALTER TABLE t1 ADD c DEFAULT 'hello world';
|
||||
SELECT * FROM t1;
|
||||
}
|
||||
} {1 100 {hello world} 2 300 {hello world}}
|
||||
ifcapable schema_version {
|
||||
do_test alter4-4.4 {
|
||||
execsql {
|
||||
PRAGMA schema_version;
|
||||
}
|
||||
} {20}
|
||||
}
|
||||
do_test alter4-4.99 {
|
||||
execsql {
|
||||
DROP TABLE t1;
|
||||
}
|
||||
} {}
|
||||
|
||||
ifcapable attach {
|
||||
do_test alter4-5.1 {
|
||||
forcedelete test2.db
|
||||
forcedelete test2.db-journal
|
||||
execsql {
|
||||
CREATE TEMP TABLE t1(a, b);
|
||||
INSERT INTO t1 VALUES(1, 'one');
|
||||
INSERT INTO t1 VALUES(2, 'two');
|
||||
ATTACH 'test2.db' AS aux;
|
||||
CREATE TABLE aux.t1 AS SELECT * FROM t1;
|
||||
PRAGMA aux.schema_version = 30;
|
||||
SELECT sql FROM aux.sqlite_master;
|
||||
}
|
||||
} {{CREATE TABLE t1(a,b)}}
|
||||
do_test alter4-5.2 {
|
||||
execsql {
|
||||
ALTER TABLE aux.t1 ADD COLUMN c VARCHAR(128);
|
||||
SELECT sql FROM aux.sqlite_master;
|
||||
}
|
||||
} {{CREATE TABLE t1(a,b, c VARCHAR(128))}}
|
||||
do_test alter4-5.3 {
|
||||
execsql {
|
||||
SELECT * FROM aux.t1;
|
||||
}
|
||||
} {1 one {} 2 two {}}
|
||||
ifcapable schema_version {
|
||||
do_test alter4-5.4 {
|
||||
execsql {
|
||||
PRAGMA aux.schema_version;
|
||||
}
|
||||
} {31}
|
||||
}
|
||||
do_test alter4-5.6 {
|
||||
execsql {
|
||||
ALTER TABLE aux.t1 ADD COLUMN d DEFAULT 1000;
|
||||
SELECT sql FROM aux.sqlite_master;
|
||||
}
|
||||
} {{CREATE TABLE t1(a,b, c VARCHAR(128), d DEFAULT 1000)}}
|
||||
do_test alter4-5.7 {
|
||||
execsql {
|
||||
SELECT * FROM aux.t1;
|
||||
}
|
||||
} {1 one {} 1000 2 two {} 1000}
|
||||
ifcapable schema_version {
|
||||
do_test alter4-5.8 {
|
||||
execsql {
|
||||
PRAGMA aux.schema_version;
|
||||
}
|
||||
} {32}
|
||||
}
|
||||
do_test alter4-5.9 {
|
||||
execsql {
|
||||
SELECT * FROM t1;
|
||||
}
|
||||
} {1 one 2 two}
|
||||
do_test alter4-5.99 {
|
||||
execsql {
|
||||
DROP TABLE aux.t1;
|
||||
DROP TABLE t1;
|
||||
}
|
||||
} {}
|
||||
}
|
||||
|
||||
#----------------------------------------------------------------
|
||||
# Test that the table schema is correctly reloaded when a column
|
||||
# is added to a table.
|
||||
#
|
||||
ifcapable trigger&&tempdb {
|
||||
do_test alter4-6.1 {
|
||||
execsql {
|
||||
CREATE TEMP TABLE t1(a, b);
|
||||
CREATE TEMP TABLE log(trig, a, b);
|
||||
|
||||
CREATE TRIGGER t1_a AFTER INSERT ON t1 BEGIN
|
||||
INSERT INTO log VALUES('a', new.a, new.b);
|
||||
END;
|
||||
CREATE TEMP TRIGGER t1_b AFTER INSERT ON t1 BEGIN
|
||||
INSERT INTO log VALUES('b', new.a, new.b);
|
||||
END;
|
||||
|
||||
INSERT INTO t1 VALUES(1, 2);
|
||||
SELECT * FROM log ORDER BY trig, a, b;
|
||||
}
|
||||
} {a 1 2 b 1 2}
|
||||
do_test alter4-6.2 {
|
||||
execsql {
|
||||
ALTER TABLE t1 ADD COLUMN c DEFAULT 'c';
|
||||
INSERT INTO t1(a, b) VALUES(3, 4);
|
||||
SELECT * FROM log ORDER BY trig, a, b;
|
||||
}
|
||||
} {a 1 2 a 3 4 b 1 2 b 3 4}
|
||||
}
|
||||
|
||||
# Ticket #1183 - Make sure adding columns to large tables does not cause
|
||||
# memory corruption (as was the case before this bug was fixed).
|
||||
do_test alter4-8.1 {
|
||||
execsql {
|
||||
CREATE TEMP TABLE t4(c1);
|
||||
}
|
||||
} {}
|
||||
set ::sql ""
|
||||
do_test alter4-8.2 {
|
||||
set cols c1
|
||||
for {set i 2} {$i < 100} {incr i} {
|
||||
execsql "
|
||||
ALTER TABLE t4 ADD c$i
|
||||
"
|
||||
lappend cols c$i
|
||||
}
|
||||
set ::sql "CREATE TABLE t4([join $cols {, }])"
|
||||
list
|
||||
} {}
|
||||
do_test alter4-8.2 {
|
||||
execsql {
|
||||
SELECT sql FROM sqlite_temp_master WHERE name = 't4';
|
||||
}
|
||||
} [list $::sql]
|
||||
|
||||
|
||||
# Test that a default value equal to -1 multipied by the smallest possible
|
||||
# 64-bit integer is correctly converted to a real.
|
||||
do_execsql_test alter4-9.1 {
|
||||
CREATE TABLE t5(
|
||||
a INTEGER DEFAULT -9223372036854775808,
|
||||
b INTEGER DEFAULT (-(-9223372036854775808))
|
||||
);
|
||||
INSERT INTO t5 DEFAULT VALUES;
|
||||
}
|
||||
|
||||
do_execsql_test alter4-9.2 { SELECT typeof(a), a, typeof(b), b FROM t5; } {
|
||||
integer -9223372036854775808
|
||||
real 9.22337203685478e+18
|
||||
}
|
||||
|
||||
do_execsql_test alter4-9.3 {
|
||||
ALTER TABLE t5 ADD COLUMN c INTEGER DEFAULT (-(-9223372036854775808));
|
||||
SELECT typeof(c), c FROM t5;
|
||||
} {real 9.22337203685478e+18}
|
||||
|
||||
# Confirm that doing an ALTER TABLE on a legacy format database
|
||||
# does not corrupt DESC indexes.
|
||||
#
|
||||
# Ticket https://www.sqlite.org/src/tktview/f68bf68513a1c
|
||||
#
|
||||
do_test alter4-10.1 {
|
||||
db close
|
||||
sqlite3 db :memory:
|
||||
sqlite3_db_config db LEGACY_FILE_FORMAT 1
|
||||
db eval {
|
||||
CREATE TABLE t1(a,b,c);
|
||||
CREATE INDEX t1a ON t1(a DESC);
|
||||
INSERT INTO t1 VALUES(1,2,3);
|
||||
INSERT INTO t1 VALUES(2,3,4);
|
||||
ALTER TABLE t1 ADD COLUMN d;
|
||||
PRAGMA integrity_check;
|
||||
}
|
||||
} {ok}
|
||||
|
||||
reset_db
|
||||
do_execsql_test alter4-11.0 {
|
||||
CREATE TABLE t1(c INTEGER PRIMARY KEY, d);
|
||||
INSERT INTO t1(c,d) VALUES(1,2);
|
||||
PRAGMA foreign_keys = on;
|
||||
ALTER TABLE t1 ADD COLUMN e;
|
||||
}
|
||||
|
||||
do_execsql_test alter4-11.1 {
|
||||
ALTER TABLE t1 ADD COLUMN f REFERENCES t1;
|
||||
}
|
||||
|
||||
do_catchsql_test alter4-11.2 {
|
||||
ALTER TABLE t1 ADD COLUMN g REFERENCES t1 DEFAULT 4;
|
||||
} {1 {Cannot add a REFERENCES column with non-NULL default value}}
|
||||
|
||||
do_catchsql_test alter4-11.3 {
|
||||
ALTER TABLE t2 ADD COLUMN g;
|
||||
} {1 {no such table: t2}}
|
||||
|
||||
ifcapable fts5 {
|
||||
do_execsql_test alter4-11.4 {
|
||||
CREATE VIRTUAL TABLE fff USING fts5(f);
|
||||
}
|
||||
do_catchsql_test alter4-11.2 {
|
||||
ALTER TABLE fff ADD COLUMN g;
|
||||
} {1 {virtual tables may not be altered}}
|
||||
}
|
||||
|
||||
finish_test
|
72
testdata/tcl/alterauth.test
vendored
Normal file
72
testdata/tcl/alterauth.test
vendored
Normal file
|
@ -0,0 +1,72 @@
|
|||
# 2018 September 2
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#*************************************************************************
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
|
||||
source $testdir/tester.tcl
|
||||
|
||||
# If SQLITE_OMIT_ALTERTABLE is defined, omit this file.
|
||||
ifcapable !altertable {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
set testprefix alterauth
|
||||
|
||||
set ::auth [list]
|
||||
proc xAuth {type args} {
|
||||
if {$type == "SQLITE_ALTER_TABLE"} {
|
||||
lappend ::auth [concat $type [lrange $args 0 3]]
|
||||
}
|
||||
return SQLITE_OK
|
||||
}
|
||||
db auth xAuth
|
||||
|
||||
do_execsql_test 1.0 { CREATE TABLE t1(a, b, c); }
|
||||
|
||||
do_test 1.1 {
|
||||
set ::auth [list]
|
||||
execsql { ALTER TABLE t1 RENAME TO t2 }
|
||||
set ::auth
|
||||
} {{SQLITE_ALTER_TABLE main t1 {} {}}}
|
||||
|
||||
do_test 1.2 {
|
||||
set ::auth [list]
|
||||
execsql { ALTER TABLE t2 RENAME c TO ccc }
|
||||
set ::auth
|
||||
} {{SQLITE_ALTER_TABLE main t2 {} {}}}
|
||||
|
||||
do_test 1.3 {
|
||||
set ::auth [list]
|
||||
execsql { ALTER TABLE t2 ADD COLUMN d }
|
||||
set ::auth
|
||||
} {{SQLITE_ALTER_TABLE main t2 {} {}}}
|
||||
|
||||
proc xAuth {type args} {
|
||||
if {$type == "SQLITE_ALTER_TABLE"} {
|
||||
return SQLITE_DENY
|
||||
}
|
||||
return SQLITE_OK
|
||||
}
|
||||
|
||||
do_test 2.1 {
|
||||
catchsql { ALTER TABLE t2 RENAME TO t3 }
|
||||
} {1 {not authorized}}
|
||||
|
||||
do_test 2.2 {
|
||||
catchsql { ALTER TABLE t2 RENAME d TO ddd }
|
||||
} {1 {not authorized}}
|
||||
|
||||
do_test 2.3 {
|
||||
catchsql { ALTER TABLE t2 ADD COLUMN e }
|
||||
} {1 {not authorized}}
|
||||
|
||||
finish_test
|
119
testdata/tcl/alterauth2.test
vendored
Normal file
119
testdata/tcl/alterauth2.test
vendored
Normal file
|
@ -0,0 +1,119 @@
|
|||
# 2018 October 6
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#*************************************************************************
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
|
||||
source $testdir/tester.tcl
|
||||
|
||||
# If SQLITE_OMIT_ALTERTABLE is defined, omit this file.
|
||||
ifcapable !altertable {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
set testprefix alterauth2
|
||||
|
||||
set ::auth [list]
|
||||
proc xAuth {type args} {
|
||||
lappend ::auth [concat $type [lrange $args 0 3]]
|
||||
if {$type=="SQLITE_READ" && [lindex $args 0] == "t2"} breakpoint
|
||||
return SQLITE_OK
|
||||
}
|
||||
db auth xAuth
|
||||
|
||||
proc do_auth_test {tn sql authcode} {
|
||||
set script "
|
||||
set ::auth \[list\]
|
||||
execsql {$sql}
|
||||
lsort -unique \[set ::auth\]
|
||||
"
|
||||
|
||||
set normal [list {*}$authcode]
|
||||
uplevel [list do_test $tn $script $normal]
|
||||
}
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE t1(a, b, c);
|
||||
CREATE VIEW v1 AS SELECT * FROM t1;
|
||||
CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN
|
||||
DELETE FROM t1 WHERE a<new.a;
|
||||
END;
|
||||
|
||||
CREATE TEMP TRIGGER tr2 AFTER UPDATE OF a, b ON t1 BEGIN
|
||||
UPDATE t1 SET a=a+1 WHERE new.b<b;
|
||||
END;
|
||||
}
|
||||
|
||||
do_auth_test 1.1 {
|
||||
ALTER TABLE t1 RENAME TO t2;
|
||||
} {
|
||||
{SQLITE_ALTER_TABLE main t1 {} {}}
|
||||
{SQLITE_FUNCTION {} like {} {}}
|
||||
{SQLITE_FUNCTION {} sqlite_rename_table {} {}}
|
||||
{SQLITE_FUNCTION {} sqlite_rename_test {} {}}
|
||||
{SQLITE_FUNCTION {} substr {} {}}
|
||||
{SQLITE_READ sqlite_master name main {}}
|
||||
{SQLITE_READ sqlite_master sql main {}}
|
||||
{SQLITE_READ sqlite_master tbl_name main {}}
|
||||
{SQLITE_READ sqlite_master type main {}}
|
||||
{SQLITE_READ sqlite_temp_master name temp {}}
|
||||
{SQLITE_READ sqlite_temp_master sql temp {}}
|
||||
{SQLITE_READ sqlite_temp_master tbl_name temp {}}
|
||||
{SQLITE_READ sqlite_temp_master type temp {}}
|
||||
{SQLITE_SELECT {} {} {} {}}
|
||||
{SQLITE_UPDATE sqlite_master name main {}}
|
||||
{SQLITE_UPDATE sqlite_master sql main {}}
|
||||
{SQLITE_UPDATE sqlite_master tbl_name main {}}
|
||||
{SQLITE_UPDATE sqlite_temp_master sql temp {}}
|
||||
{SQLITE_UPDATE sqlite_temp_master tbl_name temp {}}
|
||||
}
|
||||
|
||||
do_auth_test 1.2 {
|
||||
ALTER TABLE t2 RENAME a TO aaa;
|
||||
} {
|
||||
{SQLITE_ALTER_TABLE main t2 {} {}}
|
||||
{SQLITE_FUNCTION {} like {} {}}
|
||||
{SQLITE_FUNCTION {} sqlite_rename_column {} {}}
|
||||
{SQLITE_FUNCTION {} sqlite_rename_quotefix {} {}}
|
||||
{SQLITE_FUNCTION {} sqlite_rename_test {} {}}
|
||||
{SQLITE_READ sqlite_master name main {}}
|
||||
{SQLITE_READ sqlite_master sql main {}}
|
||||
{SQLITE_READ sqlite_master tbl_name main {}}
|
||||
{SQLITE_READ sqlite_master type main {}}
|
||||
{SQLITE_READ sqlite_temp_master name temp {}}
|
||||
{SQLITE_READ sqlite_temp_master sql temp {}}
|
||||
{SQLITE_READ sqlite_temp_master type temp {}}
|
||||
{SQLITE_SELECT {} {} {} {}}
|
||||
{SQLITE_UPDATE sqlite_master sql main {}}
|
||||
{SQLITE_UPDATE sqlite_temp_master sql temp {}}
|
||||
}
|
||||
|
||||
do_auth_test 1.3 {
|
||||
ALTER TABLE t2 DROP COLUMN c;
|
||||
} {
|
||||
{SQLITE_ALTER_TABLE main t2 c {}}
|
||||
{SQLITE_FUNCTION {} like {} {}}
|
||||
{SQLITE_FUNCTION {} sqlite_drop_column {} {}}
|
||||
{SQLITE_FUNCTION {} sqlite_rename_quotefix {} {}}
|
||||
{SQLITE_FUNCTION {} sqlite_rename_test {} {}}
|
||||
{SQLITE_READ sqlite_master name main {}}
|
||||
{SQLITE_READ sqlite_master sql main {}}
|
||||
{SQLITE_READ sqlite_master tbl_name main {}}
|
||||
{SQLITE_READ sqlite_master type main {}}
|
||||
{SQLITE_READ sqlite_temp_master name temp {}}
|
||||
{SQLITE_READ sqlite_temp_master sql temp {}}
|
||||
{SQLITE_READ sqlite_temp_master type temp {}}
|
||||
{SQLITE_SELECT {} {} {} {}}
|
||||
{SQLITE_UPDATE sqlite_master sql main {}}
|
||||
{SQLITE_UPDATE sqlite_temp_master sql temp {}}
|
||||
}
|
||||
|
||||
finish_test
|
934
testdata/tcl/altercol.test
vendored
Normal file
934
testdata/tcl/altercol.test
vendored
Normal file
|
@ -0,0 +1,934 @@
|
|||
# 2009 February 2
|
||||
#
|
||||
# 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 regression tests for SQLite library. The
|
||||
# focus of this script is testing that SQLite can handle a subtle
|
||||
# file format change that may be used in the future to implement
|
||||
# "ALTER TABLE ... RENAME COLUMN ... TO".
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix altercol
|
||||
|
||||
# If SQLITE_OMIT_ALTERTABLE is defined, omit this file.
|
||||
ifcapable !altertable {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
# Drop all the tables and views in the 'main' database of database connect
|
||||
# [db]. Sort the objects by name before dropping them.
|
||||
#
|
||||
proc drop_all_tables_and_views {db} {
|
||||
set SQL {
|
||||
SELECT name, type FROM sqlite_master
|
||||
WHERE type IN ('table', 'view') AND name NOT LIKE 'sqlite_%'
|
||||
ORDER BY 1
|
||||
}
|
||||
foreach {z t} [db eval $SQL] {
|
||||
db eval "DROP $t $z"
|
||||
}
|
||||
}
|
||||
|
||||
foreach {tn before after} {
|
||||
1 {CREATE TABLE t1(a INTEGER, b TEXT, c BLOB)}
|
||||
{CREATE TABLE t1(a INTEGER, d TEXT, c BLOB)}
|
||||
|
||||
2 {CREATE TABLE t1(a INTEGER, x TEXT, "b" BLOB)}
|
||||
{CREATE TABLE t1(a INTEGER, x TEXT, "d" BLOB)}
|
||||
|
||||
3 {CREATE TABLE t1(a INTEGER, b TEXT, c BLOB, CHECK(b!=''))}
|
||||
{CREATE TABLE t1(a INTEGER, d TEXT, c BLOB, CHECK(d!=''))}
|
||||
|
||||
4 {CREATE TABLE t1(a INTEGER, b TEXT, c BLOB, CHECK(t1.b!=''))}
|
||||
{CREATE TABLE t1(a INTEGER, d TEXT, c BLOB, CHECK(t1.d!=''))}
|
||||
|
||||
5 {CREATE TABLE t1(a INTEGER, b TEXT, c BLOB, CHECK( coalesce(b,c) ))}
|
||||
{CREATE TABLE t1(a INTEGER, d TEXT, c BLOB, CHECK( coalesce(d,c) ))}
|
||||
|
||||
6 {CREATE TABLE t1(a INTEGER, "b"TEXT, c BLOB, CHECK( coalesce(b,c) ))}
|
||||
{CREATE TABLE t1(a INTEGER, "d"TEXT, c BLOB, CHECK( coalesce(d,c) ))}
|
||||
|
||||
7 {CREATE TABLE t1(a INTEGER, b TEXT, c BLOB, PRIMARY KEY(b, c))}
|
||||
{CREATE TABLE t1(a INTEGER, d TEXT, c BLOB, PRIMARY KEY(d, c))}
|
||||
|
||||
8 {CREATE TABLE t1(a INTEGER, b TEXT PRIMARY KEY, c BLOB)}
|
||||
{CREATE TABLE t1(a INTEGER, d TEXT PRIMARY KEY, c BLOB)}
|
||||
|
||||
9 {CREATE TABLE t1(a, b TEXT, c, PRIMARY KEY(a, b), UNIQUE("B"))}
|
||||
{CREATE TABLE t1(a, d TEXT, c, PRIMARY KEY(a, d), UNIQUE("d"))}
|
||||
|
||||
10 {CREATE TABLE t1(a, b, c); CREATE INDEX t1i ON t1(a, c)}
|
||||
{{CREATE TABLE t1(a, d, c)} {CREATE INDEX t1i ON t1(a, c)}}
|
||||
|
||||
11 {CREATE TABLE t1(a, b, c); CREATE INDEX t1i ON t1(b, c)}
|
||||
{{CREATE TABLE t1(a, d, c)} {CREATE INDEX t1i ON t1(d, c)}}
|
||||
|
||||
12 {CREATE TABLE t1(a, b, c); CREATE INDEX t1i ON t1(b+b+b+b, c) WHERE b>0}
|
||||
{{CREATE TABLE t1(a, d, c)} {CREATE INDEX t1i ON t1(d+d+d+d, c) WHERE d>0}}
|
||||
|
||||
13 {CREATE TABLE t1(a, b, c, FOREIGN KEY (b) REFERENCES t2)}
|
||||
{CREATE TABLE t1(a, d, c, FOREIGN KEY (d) REFERENCES t2)}
|
||||
|
||||
14 {CREATE TABLE t1(a INTEGER, b TEXT, c BLOB, PRIMARY KEY(b))}
|
||||
{CREATE TABLE t1(a INTEGER, d TEXT, c BLOB, PRIMARY KEY(d))}
|
||||
|
||||
15 {CREATE TABLE t1(a INTEGER, b INTEGER, c BLOB, PRIMARY KEY(b))}
|
||||
{CREATE TABLE t1(a INTEGER, d INTEGER, c BLOB, PRIMARY KEY(d))}
|
||||
|
||||
16 {CREATE TABLE t1(a INTEGER, b INTEGER PRIMARY KEY, c BLOB)}
|
||||
{CREATE TABLE t1(a INTEGER, d INTEGER PRIMARY KEY, c BLOB)}
|
||||
|
||||
17 {CREATE TABLE t1(a INTEGER, b INTEGER PRIMARY KEY, c BLOB, FOREIGN KEY (b) REFERENCES t2)}
|
||||
{CREATE TABLE t1(a INTEGER, d INTEGER PRIMARY KEY, c BLOB, FOREIGN KEY (d) REFERENCES t2)}
|
||||
|
||||
} {
|
||||
reset_db
|
||||
do_execsql_test 1.$tn.0 $before
|
||||
|
||||
do_execsql_test 1.$tn.1 {
|
||||
INSERT INTO t1 VALUES(1, 2, 3);
|
||||
}
|
||||
|
||||
do_execsql_test 1.$tn.2 {
|
||||
ALTER TABLE t1 RENAME COLUMN b TO d;
|
||||
}
|
||||
|
||||
do_execsql_test 1.$tn.3 {
|
||||
SELECT * FROM t1;
|
||||
} {1 2 3}
|
||||
|
||||
if {[string first INDEX $before]>0} {
|
||||
set res $after
|
||||
} else {
|
||||
set res [list $after]
|
||||
}
|
||||
do_execsql_test 1.$tn.4 {
|
||||
SELECT sql FROM sqlite_master WHERE tbl_name='t1' AND sql!=''
|
||||
} $res
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
do_execsql_test 2.0 {
|
||||
CREATE TABLE t3(a, b, c, d, e, f, g, h, i, j, k, l, m, FOREIGN KEY (b, c, d, e, f, g, h, i, j, k, l, m) REFERENCES t4);
|
||||
}
|
||||
|
||||
sqlite3 db2 test.db
|
||||
do_execsql_test -db db2 2.1 { SELECT b FROM t3 }
|
||||
|
||||
do_execsql_test 2.2 {
|
||||
ALTER TABLE t3 RENAME b TO biglongname;
|
||||
SELECT sql FROM sqlite_master WHERE name='t3';
|
||||
} {{CREATE TABLE t3(a, biglongname, c, d, e, f, g, h, i, j, k, l, m, FOREIGN KEY (biglongname, c, d, e, f, g, h, i, j, k, l, m) REFERENCES t4)}}
|
||||
|
||||
do_execsql_test -db db2 2.3 { SELECT biglongname FROM t3 }
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
do_execsql_test 3.0 {
|
||||
CREATE TABLE t4(x, y, z);
|
||||
CREATE TRIGGER ttt AFTER INSERT ON t4 WHEN new.y<0 BEGIN
|
||||
SELECT x, y, z FROM t4;
|
||||
DELETE FROM t4 WHERE y=32;
|
||||
UPDATE t4 SET x=y+1, y=0 WHERE y=32;
|
||||
INSERT INTO t4(x, y, z) SELECT 4, 5, 6 WHERE 0;
|
||||
END;
|
||||
INSERT INTO t4 VALUES(3, 2, 1);
|
||||
}
|
||||
|
||||
do_execsql_test 3.1 {
|
||||
ALTER TABLE t4 RENAME y TO abc;
|
||||
SELECT sql FROM sqlite_master WHERE name='t4';
|
||||
} {{CREATE TABLE t4(x, abc, z)}}
|
||||
|
||||
do_execsql_test 3.2 {
|
||||
SELECT * FROM t4;
|
||||
} {3 2 1}
|
||||
|
||||
do_execsql_test 3.3 { INSERT INTO t4 VALUES(6, 5, 4); } {}
|
||||
|
||||
do_execsql_test 3.4 { SELECT sql FROM sqlite_master WHERE type='trigger' } {
|
||||
{CREATE TRIGGER ttt AFTER INSERT ON t4 WHEN new.abc<0 BEGIN
|
||||
SELECT x, abc, z FROM t4;
|
||||
DELETE FROM t4 WHERE abc=32;
|
||||
UPDATE t4 SET x=abc+1, abc=0 WHERE abc=32;
|
||||
INSERT INTO t4(x, abc, z) SELECT 4, 5, 6 WHERE 0;
|
||||
END}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
do_execsql_test 4.0 {
|
||||
CREATE TABLE c1(a, b, FOREIGN KEY (a, b) REFERENCES p1(c, d));
|
||||
CREATE TABLE p1(c, d, PRIMARY KEY(c, d));
|
||||
PRAGMA foreign_keys = 1;
|
||||
INSERT INTO p1 VALUES(1, 2);
|
||||
INSERT INTO p1 VALUES(3, 4);
|
||||
}
|
||||
|
||||
do_execsql_test 4.1 {
|
||||
ALTER TABLE p1 RENAME d TO "silly name";
|
||||
SELECT sql FROM sqlite_master WHERE name IN ('c1', 'p1');
|
||||
} {
|
||||
{CREATE TABLE c1(a, b, FOREIGN KEY (a, b) REFERENCES p1(c, "silly name"))}
|
||||
{CREATE TABLE p1(c, "silly name", PRIMARY KEY(c, "silly name"))}
|
||||
}
|
||||
|
||||
do_execsql_test 4.2 { INSERT INTO c1 VALUES(1, 2); }
|
||||
|
||||
do_execsql_test 4.3 {
|
||||
CREATE TABLE c2(a, b, FOREIGN KEY (a, b) REFERENCES p1);
|
||||
}
|
||||
|
||||
do_execsql_test 4.4 {
|
||||
ALTER TABLE p1 RENAME "silly name" TO reasonable;
|
||||
SELECT sql FROM sqlite_master WHERE name IN ('c1', 'c2', 'p1');
|
||||
} {
|
||||
{CREATE TABLE c1(a, b, FOREIGN KEY (a, b) REFERENCES p1(c, "reasonable"))}
|
||||
{CREATE TABLE p1(c, "reasonable", PRIMARY KEY(c, "reasonable"))}
|
||||
{CREATE TABLE c2(a, b, FOREIGN KEY (a, b) REFERENCES p1)}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
do_execsql_test 5.0 {
|
||||
CREATE TABLE t5(a, b, c);
|
||||
CREATE INDEX t5a ON t5(a);
|
||||
INSERT INTO t5 VALUES(1, 2, 3), (4, 5, 6);
|
||||
ANALYZE;
|
||||
}
|
||||
|
||||
do_execsql_test 5.1 {
|
||||
ALTER TABLE t5 RENAME b TO big;
|
||||
SELECT big FROM t5;
|
||||
} {2 5}
|
||||
|
||||
do_catchsql_test 6.1 {
|
||||
ALTER TABLE sqlite_stat1 RENAME tbl TO thetable;
|
||||
} {1 {table sqlite_stat1 may not be altered}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
do_execsql_test 6.0 {
|
||||
CREATE TABLE blob(
|
||||
rid INTEGER PRIMARY KEY,
|
||||
rcvid INTEGER,
|
||||
size INTEGER,
|
||||
uuid TEXT UNIQUE NOT NULL,
|
||||
content BLOB,
|
||||
CHECK( length(uuid)>=40 AND rid>0 )
|
||||
);
|
||||
}
|
||||
|
||||
do_execsql_test 6.1 {
|
||||
ALTER TABLE "blob" RENAME COLUMN "rid" TO "a1";
|
||||
}
|
||||
|
||||
do_catchsql_test 6.2 {
|
||||
ALTER TABLE "blob" RENAME COLUMN "a1" TO [where];
|
||||
} {0 {}}
|
||||
|
||||
do_execsql_test 6.3 {
|
||||
SELECT "where" FROM blob;
|
||||
} {}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Triggers.
|
||||
#
|
||||
db close
|
||||
db2 close
|
||||
reset_db
|
||||
do_execsql_test 7.0 {
|
||||
CREATE TABLE c(x);
|
||||
INSERT INTO c VALUES(0);
|
||||
CREATE TABLE t6("col a", "col b", "col c");
|
||||
CREATE TRIGGER zzz AFTER UPDATE OF "col a", "col c" ON t6 BEGIN
|
||||
UPDATE c SET x=x+1;
|
||||
END;
|
||||
}
|
||||
|
||||
do_execsql_test 7.1.1 {
|
||||
INSERT INTO t6 VALUES(0, 0, 0);
|
||||
UPDATE t6 SET "col c" = 1;
|
||||
SELECT * FROM c;
|
||||
} {1}
|
||||
|
||||
do_execsql_test 7.1.2 {
|
||||
ALTER TABLE t6 RENAME "col c" TO "col 3";
|
||||
}
|
||||
|
||||
do_execsql_test 7.1.3 {
|
||||
UPDATE t6 SET "col 3" = 0;
|
||||
SELECT * FROM c;
|
||||
} {2}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Views.
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 8.0 {
|
||||
CREATE TABLE a1(x INTEGER, y TEXT, z BLOB, PRIMARY KEY(x));
|
||||
CREATE TABLE a2(a, b, c);
|
||||
CREATE VIEW v1 AS SELECT x, y, z FROM a1;
|
||||
}
|
||||
|
||||
do_execsql_test 8.1 {
|
||||
ALTER TABLE a1 RENAME y TO yyy;
|
||||
SELECT sql FROM sqlite_master WHERE type='view';
|
||||
} {{CREATE VIEW v1 AS SELECT x, yyy, z FROM a1}}
|
||||
|
||||
do_execsql_test 8.2.1 {
|
||||
DROP VIEW v1;
|
||||
CREATE VIEW v2 AS SELECT x, x+x, a, a+a FROM a1, a2;
|
||||
} {}
|
||||
do_execsql_test 8.2.2 {
|
||||
ALTER TABLE a1 RENAME x TO xxx;
|
||||
}
|
||||
do_execsql_test 8.2.3 {
|
||||
SELECT sql FROM sqlite_master WHERE type='view';
|
||||
} {{CREATE VIEW v2 AS SELECT xxx, xxx+xxx, a, a+a FROM a1, a2}}
|
||||
|
||||
do_execsql_test 8.3.1 {
|
||||
DROP TABLE a2;
|
||||
DROP VIEW v2;
|
||||
CREATE TABLE a2(a INTEGER PRIMARY KEY, b, c);
|
||||
CREATE VIEW v2 AS SELECT xxx, xxx+xxx, a, a+a FROM a1, a2;
|
||||
} {}
|
||||
do_execsql_test 8.3.2 {
|
||||
ALTER TABLE a1 RENAME xxx TO x;
|
||||
}
|
||||
do_execsql_test 8.3.3 {
|
||||
SELECT sql FROM sqlite_master WHERE type='view';
|
||||
} {{CREATE VIEW v2 AS SELECT x, x+x, a, a+a FROM a1, a2}}
|
||||
|
||||
do_execsql_test 8.4.0 {
|
||||
CREATE TABLE b1(a, b, c);
|
||||
CREATE TABLE b2(x, y, z);
|
||||
}
|
||||
|
||||
do_execsql_test 8.4.1 {
|
||||
CREATE VIEW vvv AS SELECT c+c || coalesce(c, c) FROM b1, b2 WHERE x=c GROUP BY c HAVING c>0;
|
||||
ALTER TABLE b1 RENAME c TO "a;b";
|
||||
SELECT sql FROM sqlite_master WHERE name='vvv';
|
||||
} {{CREATE VIEW vvv AS SELECT "a;b"+"a;b" || coalesce("a;b", "a;b") FROM b1, b2 WHERE x="a;b" GROUP BY "a;b" HAVING "a;b">0}}
|
||||
|
||||
do_execsql_test 8.4.2 {
|
||||
CREATE VIEW www AS SELECT b FROM b1 UNION ALL SELECT y FROM b2;
|
||||
ALTER TABLE b1 RENAME b TO bbb;
|
||||
SELECT sql FROM sqlite_master WHERE name='www';
|
||||
} {{CREATE VIEW www AS SELECT bbb FROM b1 UNION ALL SELECT y FROM b2}}
|
||||
|
||||
db collate nocase {string compare}
|
||||
|
||||
do_execsql_test 8.4.3 {
|
||||
CREATE VIEW xxx AS SELECT a FROM b1 UNION SELECT x FROM b2 ORDER BY 1 COLLATE nocase;
|
||||
}
|
||||
|
||||
do_execsql_test 8.4.4 {
|
||||
ALTER TABLE b2 RENAME x TO hello;
|
||||
SELECT sql FROM sqlite_master WHERE name='xxx';
|
||||
} {{CREATE VIEW xxx AS SELECT a FROM b1 UNION SELECT hello FROM b2 ORDER BY 1 COLLATE nocase}}
|
||||
|
||||
do_catchsql_test 8.4.5 {
|
||||
CREATE VIEW zzz AS SELECT george, ringo FROM b1;
|
||||
ALTER TABLE b1 RENAME a TO aaa;
|
||||
} {1 {error in view zzz: no such column: george}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# More triggers.
|
||||
#
|
||||
proc do_rename_column_test {tn old new lSchema} {
|
||||
for {set i 0} {$i < 2} {incr i} {
|
||||
drop_all_tables_and_views db
|
||||
|
||||
set lSorted [list]
|
||||
foreach sql $lSchema {
|
||||
execsql $sql
|
||||
lappend lSorted [string trim $sql]
|
||||
}
|
||||
set lSorted [lsort $lSorted]
|
||||
|
||||
do_execsql_test $tn.$i.1 {
|
||||
SELECT sql FROM sqlite_master WHERE sql!='' ORDER BY 1
|
||||
} $lSorted
|
||||
|
||||
if {$i==1} {
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
}
|
||||
|
||||
do_execsql_test $tn.$i.2 "ALTER TABLE t1 RENAME $old TO $new"
|
||||
|
||||
do_execsql_test $tn.$i.3 {
|
||||
SELECT sql FROM sqlite_master ORDER BY 1
|
||||
} [string map [list $old $new] $lSorted]
|
||||
}
|
||||
}
|
||||
|
||||
foreach {tn old new lSchema} {
|
||||
1 _x_ _xxx_ {
|
||||
{ CREATE TABLE t1(a, b, _x_) }
|
||||
{ CREATE TRIGGER AFTER INSERT ON t1 BEGIN
|
||||
SELECT _x_ FROM t1;
|
||||
END }
|
||||
}
|
||||
|
||||
2 _x_ _xxx_ {
|
||||
{ CREATE TABLE t1(a, b, _x_) }
|
||||
{ CREATE TABLE t2(c, d, e) }
|
||||
{ CREATE TRIGGER ttt AFTER INSERT ON t2 BEGIN
|
||||
SELECT _x_ FROM t1;
|
||||
END }
|
||||
}
|
||||
|
||||
3 _x_ _xxx_ {
|
||||
{ CREATE TABLE t1(a, b, _x_ INTEGER, PRIMARY KEY(_x_), CHECK(_x_>0)) }
|
||||
{ CREATE TABLE t2(c, d, e) }
|
||||
{ CREATE TRIGGER ttt AFTER UPDATE ON t1 BEGIN
|
||||
INSERT INTO t2 VALUES(new.a, new.b, new._x_);
|
||||
END }
|
||||
}
|
||||
|
||||
4 _x_ _xxx_ {
|
||||
{ CREATE TABLE t1(a, b, _x_ INTEGER, PRIMARY KEY(_x_), CHECK(_x_>0)) }
|
||||
{ CREATE TRIGGER ttt AFTER UPDATE ON t1 BEGIN
|
||||
INSERT INTO t1 VALUES(new.a, new.b, new._x_)
|
||||
ON CONFLICT (_x_) WHERE _x_>10 DO UPDATE SET _x_ = _x_+1;
|
||||
END }
|
||||
}
|
||||
|
||||
4 _x_ _xxx_ {
|
||||
{ CREATE TABLE t1(a, b, _x_ INTEGER, PRIMARY KEY(_x_), CHECK(_x_>0)) }
|
||||
{ CREATE TRIGGER ttt AFTER UPDATE ON t1 BEGIN
|
||||
INSERT INTO t1 VALUES(new.a, new.b, new._x_)
|
||||
ON CONFLICT (_x_) WHERE _x_>10 DO NOTHING;
|
||||
END }
|
||||
}
|
||||
} {
|
||||
do_rename_column_test 9.$tn $old $new $lSchema
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test that views can be edited even if there are missing collation
|
||||
# sequences or user defined functions.
|
||||
#
|
||||
reset_db
|
||||
|
||||
ifcapable vtab {
|
||||
foreach {tn old new lSchema} {
|
||||
1 _x_ _xxx_ {
|
||||
{ CREATE TABLE t1(a, b, _x_) }
|
||||
{ CREATE VIEW s1 AS SELECT a, b, _x_ FROM t1 WHERE _x_='abc' COLLATE xyz }
|
||||
}
|
||||
|
||||
2 _x_ _xxx_ {
|
||||
{ CREATE TABLE t1(a, b, _x_) }
|
||||
{ CREATE VIEW v1 AS SELECT a, b, _x_ FROM t1 WHERE scalar(_x_) }
|
||||
}
|
||||
|
||||
3 _x_ _xxx_ {
|
||||
{ CREATE TABLE t1(a, b, _x_) }
|
||||
{ CREATE VIEW v1 AS SELECT a, b, _x_ FROM t1 WHERE _x_ = unicode(1, 2, 3) }
|
||||
}
|
||||
|
||||
4 _x_ _xxx_ {
|
||||
{ CREATE TABLE t1(a, b, _x_) }
|
||||
{ CREATE VIRTUAL TABLE e1 USING echo(t1) }
|
||||
}
|
||||
} {
|
||||
register_echo_module db
|
||||
do_rename_column_test 10.$tn $old $new $lSchema
|
||||
}
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
# Test that if a view or trigger refers to a virtual table for which the
|
||||
# module is not available, RENAME COLUMN cannot proceed.
|
||||
#
|
||||
reset_db
|
||||
register_echo_module db
|
||||
do_execsql_test 11.0 {
|
||||
CREATE TABLE x1(a, b, c);
|
||||
CREATE VIRTUAL TABLE e1 USING echo(x1);
|
||||
}
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
|
||||
do_execsql_test 11.1 {
|
||||
ALTER TABLE x1 RENAME b TO bbb;
|
||||
SELECT sql FROM sqlite_master;
|
||||
} { {CREATE TABLE x1(a, bbb, c)} {CREATE VIRTUAL TABLE e1 USING echo(x1)} }
|
||||
|
||||
do_execsql_test 11.2 {
|
||||
CREATE VIEW v1 AS SELECT e1.*, x1.c FROM e1, x1;
|
||||
}
|
||||
|
||||
do_catchsql_test 11.3 {
|
||||
ALTER TABLE x1 RENAME c TO ccc;
|
||||
} {1 {error in view v1: no such module: echo}}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test some error conditions:
|
||||
#
|
||||
# 1. Renaming a column of a system table,
|
||||
# 2. Renaming a column of a VIEW,
|
||||
# 3. Renaming a column of a virtual table.
|
||||
# 4. Renaming a column that does not exist.
|
||||
# 5. Renaming a column of a table that does not exist.
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 12.1.1 {
|
||||
CREATE TABLE t1(a, b);
|
||||
CREATE INDEX t1a ON t1(a);
|
||||
INSERT INTO t1 VALUES(1, 1), (2, 2), (3, 4);
|
||||
ANALYZE;
|
||||
}
|
||||
do_catchsql_test 12.1.2 {
|
||||
ALTER TABLE sqlite_stat1 RENAME idx TO theindex;
|
||||
} {1 {table sqlite_stat1 may not be altered}}
|
||||
do_execsql_test 12.1.3 {
|
||||
SELECT sql FROM sqlite_master WHERE tbl_name = 'sqlite_stat1'
|
||||
} {{CREATE TABLE sqlite_stat1(tbl,idx,stat)}}
|
||||
|
||||
do_execsql_test 12.2.1 {
|
||||
CREATE VIEW v1 AS SELECT * FROM t1;
|
||||
CREATE VIEW v2(c, d) AS SELECT * FROM t1;
|
||||
}
|
||||
do_catchsql_test 12.2.2 {
|
||||
ALTER TABLE v1 RENAME a TO z;
|
||||
} {1 {cannot rename columns of view "v1"}}
|
||||
do_catchsql_test 12.2.3 {
|
||||
ALTER TABLE v2 RENAME c TO y;
|
||||
} {1 {cannot rename columns of view "v2"}}
|
||||
|
||||
ifcapable fts5 {
|
||||
do_execsql_test 12.3.1 {
|
||||
CREATE VIRTUAL TABLE ft USING fts5(a, b, c);
|
||||
}
|
||||
do_catchsql_test 12.3.2 {
|
||||
ALTER TABLE ft RENAME a TO z;
|
||||
} {1 {cannot rename columns of virtual table "ft"}}
|
||||
}
|
||||
|
||||
do_execsql_test 12.4.1 {
|
||||
CREATE TABLE t2(x, y, z);
|
||||
}
|
||||
do_catchsql_test 12.4.2 {
|
||||
ALTER TABLE t2 RENAME COLUMN a TO b;
|
||||
} {1 {no such column: "a"}}
|
||||
|
||||
do_catchsql_test 12.5.1 {
|
||||
ALTER TABLE t3 RENAME COLUMN a TO b;
|
||||
} {1 {no such table: t3}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test the effect of some parse/resolve errors.
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 13.1.1 {
|
||||
CREATE TABLE x1(i INTEGER, t TEXT UNIQUE);
|
||||
CREATE TRIGGER tr1 AFTER INSERT ON x1 BEGIN
|
||||
SELECT * FROM nosuchtable;
|
||||
END;
|
||||
}
|
||||
|
||||
do_catchsql_test 13.1.2 {
|
||||
ALTER TABLE x1 RENAME COLUMN t TO ttt;
|
||||
} {1 {error in trigger tr1: no such table: main.nosuchtable}}
|
||||
|
||||
do_execsql_test 13.1.3 {
|
||||
DROP TRIGGER tr1;
|
||||
CREATE INDEX x1i ON x1(i);
|
||||
SELECT sql FROM sqlite_master WHERE name='x1i';
|
||||
} {{CREATE INDEX x1i ON x1(i)}}
|
||||
|
||||
sqlite3_db_config db DEFENSIVE 0
|
||||
do_execsql_test 13.1.4 {
|
||||
PRAGMA writable_schema = ON;
|
||||
UPDATE sqlite_master SET sql = 'CREATE INDEX x1i ON x1(j)' WHERE name='x1i';
|
||||
PRAGMA writable_schema = OFF;
|
||||
} {}
|
||||
|
||||
do_catchsql_test 13.1.5 {
|
||||
ALTER TABLE x1 RENAME COLUMN t TO ttt;
|
||||
} {1 {error in index x1i: no such column: j}}
|
||||
|
||||
do_execsql_test 13.1.6 {
|
||||
PRAGMA writable_schema = ON;
|
||||
UPDATE sqlite_master SET sql = '' WHERE name='x1i';
|
||||
PRAGMA writable_schema = OFF;
|
||||
} {}
|
||||
|
||||
do_catchsql_test 13.1.7 {
|
||||
ALTER TABLE x1 RENAME COLUMN t TO ttt;
|
||||
} {1 {error in index x1i: }}
|
||||
|
||||
do_execsql_test 13.1.8 {
|
||||
PRAGMA writable_schema = ON;
|
||||
DELETE FROM sqlite_master WHERE name = 'x1i';
|
||||
PRAGMA writable_schema = OFF;
|
||||
}
|
||||
|
||||
do_execsql_test 13.2.0 {
|
||||
CREATE TABLE data(x UNIQUE, y, z);
|
||||
}
|
||||
foreach {tn trigger error} {
|
||||
1 {
|
||||
CREATE TRIGGER tr1 AFTER INSERT ON x1 BEGIN
|
||||
UPDATE data SET x=x+1 WHERE zzz=new.i;
|
||||
END;
|
||||
} {no such column: zzz}
|
||||
|
||||
2 {
|
||||
CREATE TRIGGER tr1 AFTER INSERT ON x1 BEGIN
|
||||
INSERT INTO data(x, y) VALUES(new.i, new.t, 1)
|
||||
ON CONFLICT (x) DO UPDATE SET z=zz+1;
|
||||
END;
|
||||
} {no such column: zz}
|
||||
|
||||
3 {
|
||||
CREATE TRIGGER tr1 AFTER INSERT ON x1 BEGIN
|
||||
INSERT INTO x1(i, t) VALUES(new.i+1, new.t||'1')
|
||||
ON CONFLICT (tttttt) DO UPDATE SET t=i+1;
|
||||
END;
|
||||
} {no such column: tttttt}
|
||||
|
||||
4 {
|
||||
CREATE TRIGGER tr1 AFTER INSERT ON x1 BEGIN
|
||||
INSERT INTO nosuchtable VALUES(new.i, new.t);
|
||||
END;
|
||||
} {no such table: main.nosuchtable}
|
||||
} {
|
||||
do_execsql_test 13.2.$tn.1 "
|
||||
DROP TRIGGER IF EXISTS tr1;
|
||||
$trigger
|
||||
"
|
||||
|
||||
do_catchsql_test 13.2.$tn.2 {
|
||||
ALTER TABLE x1 RENAME COLUMN t TO ttt;
|
||||
} "1 {error in trigger tr1: $error}"
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Passing invalid parameters directly to sqlite_rename_column().
|
||||
#
|
||||
sqlite3_test_control SQLITE_TESTCTRL_INTERNAL_FUNCTIONS db
|
||||
do_execsql_test 14.1 {
|
||||
CREATE TABLE ddd(sql, type, object, db, tbl, icol, znew, bquote);
|
||||
INSERT INTO ddd VALUES(
|
||||
'CREATE TABLE x1(i INTEGER, t TEXT)',
|
||||
'table', 'x1', 'main', 'x1', -1, 'zzz', 0
|
||||
), (
|
||||
'CREATE TABLE x1(i INTEGER, t TEXT)',
|
||||
'table', 'x1', 'main', 'x1', 2, 'zzz', 0
|
||||
), (
|
||||
'CREATE TABLE x1(i INTEGER, t TEXT)',
|
||||
'table', 'x1', 'main', 'notable', 0, 'zzz', 0
|
||||
), (
|
||||
'CREATE TABLE x1(i INTEGER, t TEXT)',
|
||||
'table', 'x1', 'main', 'ddd', -1, 'zzz', 0
|
||||
);
|
||||
} {}
|
||||
|
||||
do_execsql_test 14.2 {
|
||||
SELECT
|
||||
sqlite_rename_column(sql, type, object, db, tbl, icol, znew, bquote, 0)
|
||||
FROM ddd;
|
||||
} {{} {} {} {}}
|
||||
sqlite3_test_control SQLITE_TESTCTRL_INTERNAL_FUNCTIONS db
|
||||
|
||||
# If the INTERNAL_FUNCTIONS test-control is disabled (which is the default)
|
||||
# then the sqlite_rename_table() SQL function is not accessible to
|
||||
# ordinary SQL.
|
||||
#
|
||||
do_catchsql_test 14.3 {
|
||||
SELECT sqlite_rename_column(0,0,0,0,0,0,0,0,0);
|
||||
} {1 {no such function: sqlite_rename_column}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 15.0 {
|
||||
CREATE TABLE xxx(a, b, c);
|
||||
SELECT a AS d FROM xxx WHERE d=0;
|
||||
}
|
||||
|
||||
do_execsql_test 15.1 {
|
||||
CREATE VIEW vvv AS SELECT a AS d FROM xxx WHERE d=0;
|
||||
ALTER TABLE xxx RENAME a TO xyz;
|
||||
}
|
||||
|
||||
do_execsql_test 15.2 {
|
||||
SELECT sql FROM sqlite_master WHERE type='view';
|
||||
} {{CREATE VIEW vvv AS SELECT xyz AS d FROM xxx WHERE d=0}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
do_execsql_test 16.1.0 {
|
||||
CREATE TABLE t1(a,b,c);
|
||||
CREATE TABLE t2(d,e,f);
|
||||
INSERT INTO t1 VALUES(1,2,3);
|
||||
INSERT INTO t2 VALUES(4,5,6);
|
||||
CREATE VIEW v4 AS SELECT a, d FROM t1, t2;
|
||||
SELECT * FROM v4;
|
||||
} {1 4}
|
||||
|
||||
do_catchsql_test 16.1.1 {
|
||||
ALTER TABLE t2 RENAME d TO a;
|
||||
} {1 {error in view v4 after rename: ambiguous column name: a}}
|
||||
|
||||
do_execsql_test 16.1.2 {
|
||||
SELECT * FROM v4;
|
||||
} {1 4}
|
||||
|
||||
do_execsql_test 16.1.3 {
|
||||
CREATE UNIQUE INDEX t2d ON t2(d);
|
||||
CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN
|
||||
INSERT INTO t2 VALUES(new.a, new.b, new.c)
|
||||
ON CONFLICT(d) DO UPDATE SET f = excluded.f;
|
||||
END;
|
||||
}
|
||||
|
||||
do_execsql_test 16.1.4 {
|
||||
INSERT INTO t1 VALUES(4, 8, 456);
|
||||
SELECT * FROM t2;
|
||||
} {4 5 456}
|
||||
|
||||
do_execsql_test 16.1.5 {
|
||||
ALTER TABLE t2 RENAME COLUMN f TO "big f";
|
||||
INSERT INTO t1 VALUES(4, 0, 20456);
|
||||
SELECT * FROM t2;
|
||||
} {4 5 20456}
|
||||
|
||||
do_execsql_test 16.1.6 {
|
||||
ALTER TABLE t1 RENAME COLUMN c TO "big c";
|
||||
INSERT INTO t1 VALUES(4, 0, 0);
|
||||
SELECT * FROM t2;
|
||||
} {4 5 0}
|
||||
|
||||
do_execsql_test 16.2.1 {
|
||||
CREATE VIEW temp.v5 AS SELECT "big c" FROM t1;
|
||||
SELECT * FROM v5;
|
||||
} {3 456 20456 0}
|
||||
|
||||
do_execsql_test 16.2.2 {
|
||||
ALTER TABLE t1 RENAME COLUMN "big c" TO reallybigc;
|
||||
} {}
|
||||
|
||||
do_execsql_test 16.2.3 {
|
||||
SELECT * FROM v5;
|
||||
} {3 456 20456 0}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
do_execsql_test 17.0 {
|
||||
CREATE TABLE u7(x, y, z);
|
||||
CREATE TRIGGER u7t AFTER INSERT ON u7 BEGIN
|
||||
INSERT INTO u8 VALUES(new.x, new.y, new.z);
|
||||
END;
|
||||
} {}
|
||||
do_catchsql_test 17.1 {
|
||||
ALTER TABLE u7 RENAME x TO xxx;
|
||||
} {1 {error in trigger u7t: no such table: main.u8}}
|
||||
|
||||
do_execsql_test 17.2 {
|
||||
CREATE TEMP TABLE uu7(x, y, z);
|
||||
CREATE TRIGGER uu7t AFTER INSERT ON uu7 BEGIN
|
||||
INSERT INTO u8 VALUES(new.x, new.y, new.z);
|
||||
END;
|
||||
} {}
|
||||
do_catchsql_test 17.3 {
|
||||
ALTER TABLE uu7 RENAME x TO xxx;
|
||||
} {1 {error in trigger uu7t: no such table: u8}}
|
||||
|
||||
reset_db
|
||||
forcedelete test.db2
|
||||
do_execsql_test 18.0 {
|
||||
ATTACH 'test.db2' AS aux;
|
||||
CREATE TABLE t1(a);
|
||||
CREATE TABLE aux.log(v);
|
||||
CREATE TEMP TRIGGER tr1 AFTER INSERT ON t1 BEGIN
|
||||
INSERT INTO log VALUES(new.a);
|
||||
END;
|
||||
INSERT INTO t1 VALUES(111);
|
||||
SELECT v FROM log;
|
||||
} {111}
|
||||
|
||||
do_execsql_test 18.1 {
|
||||
ALTER TABLE t1 RENAME a TO b;
|
||||
}
|
||||
|
||||
reset_db
|
||||
do_execsql_test 19.0 {
|
||||
CREATE TABLE t1(a, b);
|
||||
CREATE TABLE t2(c, d);
|
||||
CREATE VIEW v2(e) AS SELECT coalesce(t2.c,t1.a) FROM t1, t2 WHERE t1.b=t2.d;
|
||||
}
|
||||
|
||||
do_execsql_test 19.1 {
|
||||
ALTER TABLE t1 RENAME a TO f;
|
||||
SELECT sql FROM sqlite_master WHERE name = 'v2';
|
||||
} {
|
||||
{CREATE VIEW v2(e) AS SELECT coalesce(t2.c,t1.f) FROM t1, t2 WHERE t1.b=t2.d}
|
||||
}
|
||||
|
||||
# 2019-01-08: https://www.sqlite.org/src/tktview/bc8d94f0fbd633fd9a051e3
|
||||
#
|
||||
# ALTER TABLE RENAME COLUMN does not work for tables that have redundant
|
||||
# UNIQUE constraints.
|
||||
#
|
||||
sqlite3 db :memory:
|
||||
do_execsql_test 20.100 {
|
||||
CREATE TABLE t1(aaa,b,c,UNIQUE(aaA),PRIMARY KEY(aAa),UNIQUE(aAA));
|
||||
ALTER TABLE t1 RENAME aaa TO bbb;
|
||||
SELECT sql FROM sqlite_master WHERE name='t1';
|
||||
} {{CREATE TABLE t1(bbb,b,c,UNIQUE(bbb),PRIMARY KEY(bbb),UNIQUE(bbb))}}
|
||||
do_execsql_test 20.105 {
|
||||
DROP TABLE t1;
|
||||
CREATE TABLE t1(aaa,b,c,UNIQUE(aaA),PRIMARY KEY(aAa),UNIQUE(aAA))WITHOUT ROWID;
|
||||
ALTER TABLE t1 RENAME aaa TO bbb;
|
||||
SELECT sql FROM sqlite_master WHERE name='t1';
|
||||
} {{CREATE TABLE t1(bbb,b,c,UNIQUE(bbb),PRIMARY KEY(bbb),UNIQUE(bbb))WITHOUT ROWID}}
|
||||
do_execsql_test 20.110 {
|
||||
DROP TABLE t1;
|
||||
CREATE TABLE t1(aa UNIQUE,bb UNIQUE,cc UNIQUE,UNIQUE(aA),PRIMARY KEY(bB),UNIQUE(cC));
|
||||
ALTER TABLE t1 RENAME aa TO xx;
|
||||
ALTER TABLE t1 RENAME bb TO yy;
|
||||
ALTER TABLE t1 RENAME cc TO zz;
|
||||
SELECT sql FROM sqlite_master WHERE name='t1';
|
||||
} {{CREATE TABLE t1(xx UNIQUE,yy UNIQUE,zz UNIQUE,UNIQUE(xx),PRIMARY KEY(yy),UNIQUE(zz))}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 21.0 {
|
||||
CREATE TABLE t1(a, b, c NOT NULL);
|
||||
CREATE TRIGGER tr1 AFTER INSERT ON t1 WHEN new.c IS NOT NULL BEGIN
|
||||
SELECT c NOT NULL FROM t1;
|
||||
END;
|
||||
}
|
||||
|
||||
do_execsql_test 21.1 {
|
||||
ALTER TABLE t1 RENAME c TO d;
|
||||
}
|
||||
|
||||
do_execsql_test 21.2 {
|
||||
SELECT sql FROM sqlite_schema WHERE name IS 'tr1'
|
||||
} {{CREATE TRIGGER tr1 AFTER INSERT ON t1 WHEN new.d IS NOT NULL BEGIN
|
||||
SELECT d NOT NULL FROM t1;
|
||||
END}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 22.0 {
|
||||
CREATE TABLE t1(a, b);
|
||||
CREATE TABLE t2(c, othername, extra AS (c + 1));
|
||||
ALTER TABLE t1 RENAME a to othername;
|
||||
SELECT sql FROM sqlite_schema;
|
||||
} {
|
||||
{CREATE TABLE t1(othername, b)}
|
||||
{CREATE TABLE t2(c, othername, extra AS (c + 1))}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
sqlite3_db_config db SQLITE_DBCONFIG_DQS_DDL 1
|
||||
sqlite3_db_config db SQLITE_DBCONFIG_DQS_DML 1
|
||||
do_execsql_test 22.0 {
|
||||
CREATE TABLE t1(a, b);
|
||||
CREATE INDEX x1 on t1("c"=b);
|
||||
INSERT INTO t1 VALUES('a', 'a');
|
||||
INSERT INTO t1 VALUES('b', 'b');
|
||||
INSERT INTO t1 VALUES('c', 'c');
|
||||
ALTER TABLE t1 RENAME COLUMN a TO "c";
|
||||
PRAGMA integrity_check;
|
||||
} {ok}
|
||||
|
||||
reset_db
|
||||
do_execsql_test 23.0 {
|
||||
CREATE TABLE t1('a'"b",c);
|
||||
CREATE INDEX i1 ON t1('a');
|
||||
INSERT INTO t1 VALUES(1,2), (3,4);
|
||||
ALTER TABLE t1 RENAME COLUMN a TO x;
|
||||
PRAGMA integrity_check;
|
||||
SELECT sql FROM sqlite_schema WHERE name='t1';
|
||||
|
||||
} {ok {CREATE TABLE t1("x" "b",c)}}
|
||||
|
||||
# 2022-02-04
|
||||
# Do not complain about syntax errors in the schema if
|
||||
# in PRAGMA writable_schema=ON mode.
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 23.0 {
|
||||
CREATE TABLE t1(a INT, b REAL, c TEXT, d BLOB, e ANY);
|
||||
CREATE INDEX t1abx ON t1(a, b, a+b) WHERE c IS NOT NULL;
|
||||
CREATE VIEW t2 AS SELECT a+10, b*5.0, xyz FROM t1; -- unknown column "xyz"
|
||||
CREATE TABLE schema_copy(name TEXT, sql TEXT);
|
||||
INSERT INTO schema_copy(name,sql) SELECT name, sql FROM sqlite_schema WHERE sql IS NOT NULL;
|
||||
} {}
|
||||
do_catchsql_test 23.1 {
|
||||
ALTER TABLE t1 RENAME COLUMN e TO eeee;
|
||||
} {1 {error in view t2: no such column: xyz}}
|
||||
do_execsql_test 23.2 {
|
||||
SELECT name, sql FROM sqlite_master
|
||||
EXCEPT SELECT name, sql FROM schema_copy;
|
||||
} {}
|
||||
do_execsql_test 23.3 {
|
||||
BEGIN;
|
||||
PRAGMA writable_schema=ON;
|
||||
ALTER TABLE t1 RENAME COLUMN e TO eeee;
|
||||
PRAGMA writable_schema=OFF;
|
||||
SELECT name FROM sqlite_master
|
||||
WHERE (name, sql) NOT IN (SELECT name, sql FROM schema_copy);
|
||||
ROLLBACK;
|
||||
} {t1}
|
||||
do_execsql_test 23.10 {
|
||||
DROP VIEW t2;
|
||||
CREATE TRIGGER r3 AFTER INSERT ON t1 BEGIN
|
||||
INSERT INTO t3(x,y) VALUES(new.a, new.b);
|
||||
INSERT INTO t4(p) VALUES(new.c); -- no such table "t4"
|
||||
END;
|
||||
DELETE FROM schema_copy;
|
||||
INSERT INTO schema_copy(name,sql) SELECT name, sql FROM sqlite_schema WHERE sql IS NOT NULL;
|
||||
} {}
|
||||
do_catchsql_test 23.11 {
|
||||
ALTER TABLE t1 RENAME COLUMN e TO eeee;
|
||||
} {1 {error in trigger r3: no such table: main.t3}}
|
||||
do_execsql_test 23.12 {
|
||||
SELECT name, sql FROM sqlite_master
|
||||
EXCEPT SELECT name, sql FROM schema_copy;
|
||||
} {}
|
||||
do_execsql_test 23.13 {
|
||||
BEGIN;
|
||||
PRAGMA writable_schema=ON;
|
||||
ALTER TABLE t1 RENAME COLUMN e TO eeee;
|
||||
PRAGMA writable_schema=OFF;
|
||||
SELECT name FROM sqlite_master
|
||||
WHERE (name, sql) NOT IN (SELECT name, sql FROM schema_copy);
|
||||
ROLLBACK;
|
||||
} {t1}
|
||||
do_execsql_test 23.20 {
|
||||
CREATE TABLE t4(id INTEGER PRIMARY KEY, c1 INT, c2 INT);
|
||||
CREATE VIEW t4v1 AS SELECT id, c1, c99 FROM t4;
|
||||
DELETE FROM schema_copy;
|
||||
INSERT INTO schema_copy SELECT name, sql FROM sqlite_schema;
|
||||
BEGIN;
|
||||
PRAGMA writable_schema=ON;
|
||||
ALTER TABLE t4 RENAME to t4new;
|
||||
SELECT name FROM sqlite_schema WHERE (name,sql) NOT IN (SELECT * FROM schema_copy);
|
||||
ROLLBACK;
|
||||
} {t4new}
|
||||
|
||||
finish_test
|
181
testdata/tcl/altercorrupt.test
vendored
Normal file
181
testdata/tcl/altercorrupt.test
vendored
Normal file
|
@ -0,0 +1,181 @@
|
|||
# 2019-01-11
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix altercorrupt
|
||||
|
||||
# If SQLITE_OMIT_ALTERTABLE is defined, omit this file.
|
||||
ifcapable !altertable {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
database_may_be_corrupt
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_test 1.0 {
|
||||
sqlite3 db {}
|
||||
db deserialize [decode_hexdb {
|
||||
.open --hexdb
|
||||
| size 24576 pagesize 4096 filename crash-685346d89b5e5f.db
|
||||
| page 1 offset 0
|
||||
| 0: 53 51 4c 69 74 65 20 66 6f 72 6d 61 74 20 33 00 SQLite format 3.
|
||||
| 16: 10 00 01 01 00 40 20 20 00 00 00 00 00 00 00 06 .....@ ........
|
||||
| 32: 00 00 63 00 00 05 f0 00 00 00 00 04 10 00 00 04 ..c.............
|
||||
| 48: 00 00 00 00 00 00 0f f0 00 00 00 01 00 00 00 00 ................
|
||||
| 64: 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||
| 96: 00 00 00 00 0d 0f f8 00 05 0e cf 00 0f 79 0f d3 .............y..
|
||||
| 112: 0f 2e 0e f3 0e cf 00 00 00 00 00 00 00 00 00 00 ................
|
||||
| 3776: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 22 ................
|
||||
| 3792: 05 06 17 11 11 01 31 74 61 62 6c 65 74 34 74 34 ......1tablet4t4
|
||||
| 3808: 06 43 52 45 41 54 45 20 54 41 42 4c 45 20 74 34 .CREATE TABLE t4
|
||||
| 3824: 28 7a 29 39 04 06 17 11 11 01 5f 74 61 62 6c 65 (z)9......_table
|
||||
| 3840: 74 33 74 33 05 43 52 45 41 54 45 20 54 41 42 4c t3t3.CREATE TABL
|
||||
| 3856: 45 20 74 33 28 78 20 49 4e 54 45 47 45 52 20 50 E t3(x INTEGER P
|
||||
| 3872: 52 49 4d 41 52 59 20 4b 45 59 2c 20 79 29 49 03 RIMARY KEY, y)I.
|
||||
| 3888: 06 17 11 11 01 7f 74 61 62 6c 65 74 32 74 32 04 ......tablet2t2.
|
||||
| 3904: 43 52 45 41 54 45 20 54 41 42 4c 45 20 74 32 28 CREATE TABLE t2(
|
||||
| 3920: 61 2c 62 2c 63 20 50 52 49 4d 41 52 59 20 4b 45 a,b,c PRIMARY KE
|
||||
| 3936: 59 2c 20 64 2c 20 65 2c 20 66 29 20 57 49 54 48 Y, d, e, f) WITH
|
||||
| 3952: 4f 55 54 20 52 4f 57 49 44 58 03 07 17 11 11 01 OUT ROWIDX......
|
||||
| 3968: 81 1b 74 61 62 6c 65 74 31 74 31 02 43 52 45 41 ..tablet1t1.CREA
|
||||
| 3984: 54 45 20 54 41 42 4c 45 20 74 31 28 61 2c 62 2c TE TABLE t1(a,b,
|
||||
| 4000: 63 20 41 53 20 28 2d 62 29 20 56 49 52 54 55 41 c AS (-b) VIRTUA
|
||||
| 4016: 4c 2c 64 20 43 48 45 43 4b 28 64 3e 35 29 2c 65 L,d CHECK(d>5),e
|
||||
| 4032: 20 55 4e 49 51 55 45 2c 20 66 20 41 53 20 28 2b UNIQUE, f AS (+
|
||||
| 4048: 62 29 29 23 02 06 17 37 11 01 00 69 6e 64 65 78 b))#...7...index
|
||||
| 4064: 73 71 6c 69 74 65 5f 61 75 74 6f 69 6e 64 65 78 sqlite_autoindex
|
||||
| 4080: 5f 74 31 5f 31 74 31 03 00 00 00 08 00 00 00 00 _t1_1t1.........
|
||||
| page 2 offset 4096
|
||||
| 0: 0d 00 00 00 0a 0f 93 00 0f f6 0f eb 0f e0 0f d5 ................
|
||||
| 16: 0f ca 0f 8f 0f b4 0f a9 0f 9e 0f 93 00 00 00 00 ................
|
||||
| 3984: 00 00 00 09 0a 05 01 01 01 01 0a 64 6e 14 09 09 ...........dn...
|
||||
| 4000: 05 01 01 01 01 09 5a 6d 12 09 08 05 01 01 01 01 ......Zm........
|
||||
| 4016: 08 50 6c 10 09 07 05 01 01 01 01 07 46 6b 0e 09 .Pl.........Fk..
|
||||
| 4032: 06 05 01 01 01 01 06 3c 6a 0c 09 05 05 01 01 01 .......<j.......
|
||||
| 4048: 01 05 32 69 0a 09 04 05 01 01 01 01 04 28 68 08 ..2i.........(h.
|
||||
| 4064: 09 03 05 01 01 01 01 03 1e 67 06 09 02 05 01 01 .........g......
|
||||
| 4080: 01 01 02 14 66 04 08 01 05 09 01 01 01 0a 65 02 ....f.........e.
|
||||
| page 3 offset 8192
|
||||
| 0: 0a 00 00 00 0a 0f c5 00 0f fb 0f f5 0f ef 0f e9 ................
|
||||
| 16: 0f e3 0f dd 0f d7 0f d1 0f cb 0f c5 00 00 00 00 ................
|
||||
| 4032: 00 00 00 00 00 05 03 01 01 14 0a 05 03 01 01 12 ................
|
||||
| 4048: 09 05 03 01 01 10 08 05 03 01 01 0e 07 05 03 01 ................
|
||||
| 4064: 01 0c 06 05 03 01 01 0a 05 05 03 01 01 08 04 05 ................
|
||||
| 4080: 03 01 01 06 03 05 03 01 01 04 02 04 03 01 09 02 ................
|
||||
| page 4 offset 12288
|
||||
| 0: 0a 00 00 00 0a 0f 75 00 0f 75 0f 83 0f 91 0f 9f ......u..u......
|
||||
| 16: 0f ad 0f bb 0f 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||
| 3952: 00 00 00 00 00 0d 07 01 01 01 01 01 01 9c 0a 64 ...............d
|
||||
| 3968: 6e 14 64 0d 07 02 01 01 01 01 01 a6 09 5a 6d 12 n.d..........Zm.
|
||||
| 3984: 5a 0d 07 01 01 01 01 01 01 b0 08 50 6c 10 50 0d Z..........Pl.P.
|
||||
| 4000: 07 01 01 01 01 01 01 ba 07 46 6b 0e 46 0d 07 01 .........Fk.F...
|
||||
| 4016: 01 01 01 01 01 c4 06 3c 6a 0c 3c 0d 07 01 01 01 .......<j.<.....
|
||||
| 4032: 01 01 01 ce 05 32 69 0a 32 0d 07 01 01 01 01 01 .....2i.2.......
|
||||
| 4048: 01 d8 04 28 68 08 28 0d 07 01 01 01 01 01 01 e2 ...(h.(.........
|
||||
| 4064: 03 1e 67 06 1e 0d 07 01 01 01 01 01 01 ec 02 14 ..g.............
|
||||
| 4080: 66 04 14 0c 07 01 09 01 01 01 01 f6 0a 65 02 0a f............e..
|
||||
| page 5 offset 16384
|
||||
| 0: 0d 00 00 00 03 0f e9 00 0f e9 0f fb 0f f6 00 00 ................
|
||||
| 16: 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||
| 4064: 00 00 00 00 00 00 00 00 00 03 ff ff ff ff ff ff ................
|
||||
| 4080: ff ff 9c 03 00 00 03 64 03 00 00 03 01 03 00 00 .......d........
|
||||
| page 6 offset 20480
|
||||
| 0: 0d 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 ................
|
||||
| end crash-685346d89b5e5f.db
|
||||
}]} {}
|
||||
|
||||
do_catchsql_test 1.1 {
|
||||
ALTER TABLE t2 DROP COLUMN e;
|
||||
ALTER TABLE t1 DROP COLUMN f;
|
||||
} {1 {database disk image is malformed}}
|
||||
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_test 2.0 {
|
||||
sqlite3 db {}
|
||||
db deserialize [decode_hexdb {
|
||||
.open --hexdb
|
||||
| size 24576 pagesize 4096 filename crash-0572db8f391431.db
|
||||
| page 1 offset 0
|
||||
| 0: 53 51 4c 69 74 65 20 66 6f 72 6d 61 74 20 33 00 SQLite format 3.
|
||||
| 16: 10 00 01 01 00 40 20 20 00 00 00 00 00 00 00 06 .....@ ........
|
||||
| 32: 00 00 63 00 10 05 f0 00 00 00 00 04 10 00 00 04 ..c.............
|
||||
| 48: 00 00 00 00 00 00 0f f0 00 00 00 00 00 00 00 00 ................
|
||||
| 96: 00 00 00 00 0d 0f f8 00 05 0e cf 00 0f 79 0f d3 .............y..
|
||||
| 112: 0f 2e 0e f3 0e cf 00 00 00 00 00 00 00 00 00 00 ................
|
||||
| 3776: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 22 ................
|
||||
| 3792: 05 06 17 11 11 01 31 74 61 62 6c 65 74 34 74 34 ......1tablet4t4
|
||||
| 3808: 06 43 52 45 41 54 45 20 54 41 42 4c 45 20 74 34 .CREATE TABLE t4
|
||||
| 3824: 28 7a 29 39 04 06 17 11 11 01 5f 74 61 62 6c 65 (z)9......_table
|
||||
| 3840: 74 33 74 33 05 43 52 45 41 54 45 20 54 41 42 4c t3t3.CREATE TABL
|
||||
| 3856: 45 20 74 33 28 78 20 49 4e 54 55 47 45 52 20 50 E t3(x INTUGER P
|
||||
| 3872: 52 49 4d 41 52 59 20 4b 45 59 2c 20 79 29 49 03 RIMARY KEY, y)I.
|
||||
| 3888: 06 17 11 11 01 7f 74 61 62 6c 65 74 32 74 32 04 ......tablet2t2.
|
||||
| 3904: 43 52 45 41 54 45 20 54 41 42 4c 45 20 74 32 28 CREATE TABLE t2(
|
||||
| 3920: 61 2c 62 2c 63 20 50 52 49 4d 41 52 59 20 4b 45 a,b,c PRIMARY KE
|
||||
| 3936: 59 2c 20 64 2c 20 65 2c 20 66 29 20 57 49 54 48 Y, d, e, f) WITH
|
||||
| 3952: 4f 55 54 20 52 4f 57 49 44 58 05 07 17 11 11 01 OUT ROWIDX......
|
||||
| 3968: 81 1b 74 61 62 6c 65 74 31 74 31 02 43 52 45 41 ..tablet1t1.CREA
|
||||
| 3984: 54 45 20 54 41 42 4c 45 20 74 31 28 61 2c 62 2c TE TABLE t1(a,b,
|
||||
| 4000: 63 20 41 53 20 28 2d 62 29 20 56 49 52 54 55 41 c AS (-b) VIRTUA
|
||||
| 4016: 4c 2c 64 20 43 48 45 43 4b 28 64 3e 35 29 2c 65 L,d CHECK(d>5),e
|
||||
| 4032: 20 55 4e 49 51 55 45 2c 20 66 20 41 53 20 28 2b UNIQUE, f AS (+
|
||||
| 4048: 62 29 29 23 02 06 17 37 11 01 00 69 6e 64 65 78 b))#...7...index
|
||||
| 4064: 73 71 6c 69 74 65 5f 61 75 74 6f 69 6e 64 65 78 sqlite_autoindex
|
||||
| 4080: 5f 74 31 5f 31 84 31 03 01 00 00 08 00 00 00 00 _t1_1.1.........
|
||||
| page 2 offset 4096
|
||||
| 0: 0d 00 00 00 0a 0f 93 00 0f f6 0f eb 0f e0 0f d5 ................
|
||||
| 16: 0f ca 0f 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||
| 3984: 00 00 00 09 0a 05 01 01 01 01 0a 64 6e 14 09 09 ...........dn...
|
||||
| 4000: 05 01 01 01 01 09 5a 6d 12 09 08 05 01 00 f1 01 ......Zm........
|
||||
| 4016: 08 50 6c 10 09 07 05 01 01 01 01 07 46 6b 0e 09 .Pl.........Fk..
|
||||
| 4032: 06 05 01 00 f1 01 06 3c 6a 0c 09 05 05 01 01 01 .......<j.......
|
||||
| 4048: 01 05 32 69 0a 09 04 05 01 01 01 01 04 28 68 08 ..2i.........(h.
|
||||
| 4064: 57 03 05 01 01 01 01 03 1e 67 06 09 02 05 01 01 W........g......
|
||||
| 4080: 01 01 02 14 66 04 08 01 05 09 01 01 01 0a 65 02 ....f.........e.
|
||||
| page 3 offset 8192
|
||||
| 0: 09 ff ff ff fa 0f c5 00 0f fb 0f f5 0f ef 0f e9 ................
|
||||
| 16: 0f e3 0f dd 0f d7 00 00 00 00 00 00 00 00 00 00 ................
|
||||
| 4032: 00 00 00 00 00 05 03 01 01 14 0a 05 03 01 01 12 ................
|
||||
| 4048: 09 05 03 01 01 10 08 05 03 01 01 0e 07 05 03 01 ................
|
||||
| 4064: 01 0c 06 05 03 01 01 0a 05 05 03 01 01 08 04 05 ................
|
||||
| 4080: 03 01 01 06 03 05 03 01 01 04 02 04 03 01 09 02 ................
|
||||
| page 4 offset 12288
|
||||
| 0: 0a 00 00 00 0a 0f 75 00 0f 75 0f 83 0f 91 0f 9f ......u..u......
|
||||
| 16: 0f ad 0f bb 0f 00 00 00 00 00 01 00 00 00 00 00 ................
|
||||
| 3952: 00 00 00 00 00 0d 07 01 01 01 01 01 01 9c 0a 64 ...............d
|
||||
| 3968: 6e 14 64 0d 07 02 01 01 01 01 01 a6 09 5a 6d 12 n.d..........Zm.
|
||||
| 3984: 5a 0d 07 01 01 01 01 d4 01 b0 08 50 6c 10 50 0d Z..........Pl.P.
|
||||
| 4000: 07 01 01 01 01 01 01 ba 07 46 6b 0e 46 0d 07 00 .........Fk.F...
|
||||
| 4016: 01 01 01 01 01 c4 06 3c 6a 0c 3c 0d 07 01 01 01 .......<j.<.....
|
||||
| 4032: 01 01 01 ce 05 32 69 0a 32 0d 07 01 01 01 01 01 .....2i.2.......
|
||||
| 4048: 01 d8 04 28 68 08 28 0d 07 01 01 01 01 01 01 e2 ...(h.(.........
|
||||
| 4064: 03 1e 67 06 1e 0d 07 01 01 01 01 01 01 ec 02 14 ..g.............
|
||||
| 4080: 66 04 14 0c 07 01 09 01 01 00 f1 f6 0a 65 02 0a f............e..
|
||||
| page 5 offset 16384
|
||||
| 0: 0d 00 00 00 03 0f e9 00 0f e9 0f fb 0f f6 00 00 ................
|
||||
| 4064: 00 00 00 00 00 00 00 00 00 03 ff ff ff ff ff ff ................
|
||||
| 4080: ff ff 9c 03 00 00 03 64 03 00 01 03 01 03 00 00 .......d........
|
||||
| page 6 offset 20480
|
||||
| 0: 0d 00 10 00 00 10 01 00 00 00 00 00 00 00 00 00 ................
|
||||
| end crash-0572db8f391431.db
|
||||
}]} {}
|
||||
|
||||
do_catchsql_test 2.1 {
|
||||
ALTER TABLE t1 DROP COLUMN a;
|
||||
} {1 {database disk image is malformed}}
|
||||
|
||||
finish_test
|
339
testdata/tcl/alterdropcol.test
vendored
Normal file
339
testdata/tcl/alterdropcol.test
vendored
Normal file
|
@ -0,0 +1,339 @@
|
|||
# 2021 February 16
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#*************************************************************************
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix alterdropcol
|
||||
|
||||
# If SQLITE_OMIT_ALTERTABLE is defined, omit this file.
|
||||
ifcapable !altertable {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE t1(a, b, c);
|
||||
CREATE VIEW v1 AS SELECT * FROM t1;
|
||||
|
||||
CREATE TABLE t2(x INTEGER PRIMARY KEY, y, z UNIQUE);
|
||||
CREATE INDEX t2y ON t2(y);
|
||||
|
||||
CREATE TABLE t3(q, r, s);
|
||||
CREATE INDEX t3rs ON t3(r+s);
|
||||
}
|
||||
|
||||
do_catchsql_test 1.1 {
|
||||
ALTER TABLE nosuch DROP COLUMN z;
|
||||
} {1 {no such table: nosuch}}
|
||||
|
||||
do_catchsql_test 1.2 {
|
||||
ALTER TABLE v1 DROP COLUMN c;
|
||||
} {1 {cannot drop column from view "v1"}}
|
||||
|
||||
ifcapable fts5 {
|
||||
do_execsql_test 1.3.1 {
|
||||
CREATE VIRTUAL TABLE ft1 USING fts5(one, two);
|
||||
}
|
||||
do_catchsql_test 1.3.2 {
|
||||
ALTER TABLE ft1 DROP COLUMN two;
|
||||
} {1 {cannot drop column from virtual table "ft1"}}
|
||||
}
|
||||
|
||||
do_catchsql_test 1.4 {
|
||||
ALTER TABLE sqlite_schema DROP COLUMN sql;
|
||||
} {1 {table sqlite_master may not be altered}}
|
||||
|
||||
do_catchsql_test 1.5 {
|
||||
ALTER TABLE t1 DROP COLUMN d;
|
||||
} {1 {no such column: "d"}}
|
||||
|
||||
do_execsql_test 1.6.1 {
|
||||
ALTER TABLE t1 DROP COLUMN b;
|
||||
}
|
||||
do_execsql_test 1.6.2 {
|
||||
SELECT sql FROM sqlite_schema WHERE name = 't1'
|
||||
} {{CREATE TABLE t1(a, c)}}
|
||||
|
||||
do_execsql_test 1.7.1 {
|
||||
ALTER TABLE t1 DROP COLUMN c;
|
||||
}
|
||||
do_execsql_test 1.7.2 {
|
||||
SELECT sql FROM sqlite_schema WHERE name = 't1'
|
||||
} {{CREATE TABLE t1(a)}}
|
||||
|
||||
do_catchsql_test 1.7.3 {
|
||||
ALTER TABLE t1 DROP COLUMN a;
|
||||
} {1 {cannot drop column "a": no other columns exist}}
|
||||
|
||||
|
||||
do_catchsql_test 1.8 {
|
||||
ALTER TABLE t2 DROP COLUMN z
|
||||
} {1 {cannot drop UNIQUE column: "z"}}
|
||||
|
||||
do_catchsql_test 1.9 {
|
||||
ALTER TABLE t2 DROP COLUMN x
|
||||
} {1 {cannot drop PRIMARY KEY column: "x"}}
|
||||
|
||||
do_catchsql_test 1.10 {
|
||||
ALTER TABLE t2 DROP COLUMN y
|
||||
} {1 {error in index t2y after drop column: no such column: y}}
|
||||
|
||||
do_catchsql_test 1.11 {
|
||||
ALTER TABLE t3 DROP COLUMN s
|
||||
} {1 {error in index t3rs after drop column: no such column: s}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
foreach {tn wo} {
|
||||
1 {}
|
||||
2 {WITHOUT ROWID}
|
||||
} { eval [string map [list %TN% $tn %WO% $wo] {
|
||||
|
||||
reset_db
|
||||
do_execsql_test 2.%TN%.0 {
|
||||
CREATE TABLE t1(x, y INTEGER PRIMARY KEY, z) %WO% ;
|
||||
INSERT INTO t1 VALUES(1, 2, 3);
|
||||
INSERT INTO t1 VALUES(4, 5, 6);
|
||||
INSERT INTO t1 VALUES(7, 8, 9);
|
||||
}
|
||||
|
||||
do_execsql_test 2.%TN%.1 {
|
||||
ALTER TABLE t1 DROP COLUMN x;
|
||||
SELECT * FROM t1;
|
||||
} {
|
||||
2 3 5 6 8 9
|
||||
}
|
||||
do_execsql_test 2.%TN%.2 {
|
||||
ALTER TABLE t1 DROP COLUMN z;
|
||||
SELECT * FROM t1;
|
||||
} {
|
||||
2 5 8
|
||||
}
|
||||
}]}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
|
||||
do_execsql_test 3.0 {
|
||||
CREATE TABLE t12(a, b, c, CHECK(c>10));
|
||||
CREATE TABLE t13(a, b, c CHECK(c>10));
|
||||
}
|
||||
do_catchsql_test 3.1 {
|
||||
ALTER TABLE t12 DROP COLUMN c;
|
||||
} {1 {error in table t12 after drop column: no such column: c}}
|
||||
|
||||
do_catchsql_test 3.2 {
|
||||
ALTER TABLE t13 DROP COLUMN c;
|
||||
} {0 {}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test that generated columns can be dropped. And that other columns from
|
||||
# tables that contain generated columns can be dropped.
|
||||
#
|
||||
foreach {tn wo vs} {
|
||||
1 "" ""
|
||||
2 "" VIRTUAL
|
||||
3 "" STORED
|
||||
4 "WITHOUT ROWID" STORED
|
||||
5 "WITHOUT ROWID" VIRTUAL
|
||||
} {
|
||||
reset_db
|
||||
|
||||
do_execsql_test 4.$tn.0 "
|
||||
CREATE TABLE 'my table'(a, b PRIMARY KEY, c AS (a+b) $vs, d) $wo
|
||||
"
|
||||
do_execsql_test 4.$tn.1 {
|
||||
INSERT INTO "my table"(a, b, d) VALUES(1, 2, 'hello');
|
||||
INSERT INTO "my table"(a, b, d) VALUES(3, 4, 'world');
|
||||
|
||||
SELECT * FROM "my table"
|
||||
} {
|
||||
1 2 3 hello
|
||||
3 4 7 world
|
||||
}
|
||||
|
||||
do_execsql_test 4.$tn.2 {
|
||||
ALTER TABLE "my table" DROP COLUMN c;
|
||||
}
|
||||
do_execsql_test 4.$tn.3 {
|
||||
SELECT * FROM "my table"
|
||||
} {
|
||||
1 2 hello
|
||||
3 4 world
|
||||
}
|
||||
|
||||
do_execsql_test 4.$tn.4 "
|
||||
CREATE TABLE x1(a, b, c PRIMARY KEY, d AS (b+c) $vs, e) $wo
|
||||
"
|
||||
do_execsql_test 4.$tn.5 {
|
||||
INSERT INTO x1(a, b, c, e) VALUES(1, 2, 3, 4);
|
||||
INSERT INTO x1(a, b, c, e) VALUES(5, 6, 7, 8);
|
||||
INSERT INTO x1(a, b, c, e) VALUES(9, 10, 11, 12);
|
||||
SELECT * FROM x1;
|
||||
} {
|
||||
1 2 3 5 4
|
||||
5 6 7 13 8
|
||||
9 10 11 21 12
|
||||
}
|
||||
|
||||
do_execsql_test 4.$tn.6 {
|
||||
ALTER TABLE x1 DROP COLUMN a
|
||||
}
|
||||
do_execsql_test 4.$tn.7 {
|
||||
SELECT * FROM x1
|
||||
} {
|
||||
2 3 5 4
|
||||
6 7 13 8
|
||||
10 11 21 12
|
||||
}
|
||||
do_execsql_test 4.$tn.8 {
|
||||
ALTER TABLE x1 DROP COLUMN e
|
||||
}
|
||||
do_execsql_test 4.$tn.9 {
|
||||
SELECT * FROM x1
|
||||
} {
|
||||
2 3 5
|
||||
6 7 13
|
||||
10 11 21
|
||||
}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 5.0 {
|
||||
CREATE TABLE p1(a PRIMARY KEY, b UNIQUE);
|
||||
CREATE TABLE c1(x, y, z REFERENCES p1(c));
|
||||
CREATE TABLE c2(x, y, z, w REFERENCES p1(b));
|
||||
}
|
||||
do_execsql_test 5.1 {
|
||||
ALTER TABLE c1 DROP COLUMN z;
|
||||
ALTER TABLE c2 DROP COLUMN z;
|
||||
SELECT sql FROM sqlite_schema WHERE name IN ('c1', 'c2');
|
||||
} {
|
||||
{CREATE TABLE c1(x, y)}
|
||||
{CREATE TABLE c2(x, y, w REFERENCES p1(b))}
|
||||
}
|
||||
|
||||
do_execsql_test 5.2.1 {
|
||||
CREATE VIEW v1 AS SELECT d, e FROM p1
|
||||
}
|
||||
do_catchsql_test 5.2.2 {
|
||||
ALTER TABLE c1 DROP COLUMN x
|
||||
} {1 {error in view v1: no such column: d}}
|
||||
do_execsql_test 5.3.1 {
|
||||
DROP VIEW v1;
|
||||
CREATE VIEW v1 AS SELECT x, y FROM c1;
|
||||
}
|
||||
do_catchsql_test 5.3.2 {
|
||||
ALTER TABLE c1 DROP COLUMN x
|
||||
} {1 {error in view v1 after drop column: no such column: x}}
|
||||
|
||||
do_execsql_test 5.4.1 {
|
||||
CREATE TRIGGER tr AFTER INSERT ON c1 BEGIN
|
||||
INSERT INTO p1 VALUES(new.y, new.xyz);
|
||||
END;
|
||||
}
|
||||
do_catchsql_test 5.4.2 {
|
||||
ALTER TABLE c1 DROP COLUMN y
|
||||
} {1 {error in trigger tr: no such column: new.xyz}}
|
||||
do_execsql_test 5.5.1 {
|
||||
DROP TRIGGER tr;
|
||||
CREATE TRIGGER tr AFTER INSERT ON c1 BEGIN
|
||||
INSERT INTO p1 VALUES(new.y, new.z);
|
||||
END;
|
||||
}
|
||||
do_catchsql_test 5.5.2 {
|
||||
ALTER TABLE c1 DROP COLUMN y
|
||||
} {1 {error in trigger tr: no such column: new.z}}
|
||||
|
||||
# 2021-03-06 dbsqlfuzz crash-419aa525df93db6e463772c686ac6da27b46da9e
|
||||
reset_db
|
||||
do_catchsql_test 6.0 {
|
||||
CREATE TABLE t1(a,b,c);
|
||||
CREATE TABLE t2(x,y,z);
|
||||
PRAGMA writable_schema=ON;
|
||||
UPDATE sqlite_schema SET sql='CREATE INDEX t1b ON t1(b)' WHERE name='t2';
|
||||
PRAGMA writable_schema=OFF;
|
||||
ALTER TABLE t2 DROP COLUMN z;
|
||||
} {1 {database disk image is malformed}}
|
||||
reset_db
|
||||
do_catchsql_test 6.1 {
|
||||
CREATE TABLE t1(a,b,c);
|
||||
CREATE TABLE t2(x,y,z);
|
||||
PRAGMA writable_schema=ON;
|
||||
UPDATE sqlite_schema SET sql='CREATE VIEW t2(x,y,z) AS SELECT b,a,c FROM t1'
|
||||
WHERE name='t2';
|
||||
PRAGMA writable_schema=OFF;
|
||||
ALTER TABLE t2 DROP COLUMN z;
|
||||
} {1 {database disk image is malformed}}
|
||||
|
||||
# 2021-04-06 dbsqlfuzz crash-331c5c29bb76257b198f1318eef3288f9624c8ce
|
||||
reset_db
|
||||
do_execsql_test 7.0 {
|
||||
CREATE TABLE t1(a, b, c, PRIMARY KEY(a COLLATE nocase, a)) WITHOUT ROWID;
|
||||
INSERT INTO t1 VALUES(1, 2, 3);
|
||||
INSERT INTO t1 VALUES(4, 5, 6);
|
||||
}
|
||||
do_execsql_test 7.1 {
|
||||
ALTER TABLE t1 DROP COLUMN c;
|
||||
}
|
||||
do_execsql_test 7.2 {
|
||||
SELECT sql FROM sqlite_schema;
|
||||
} {{CREATE TABLE t1(a, b, PRIMARY KEY(a COLLATE nocase, a)) WITHOUT ROWID}}
|
||||
do_execsql_test 7.3 {
|
||||
SELECT * FROM t1;
|
||||
} {1 2 4 5}
|
||||
|
||||
reset_db
|
||||
do_execsql_test 8.0 {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
|
||||
PRAGMA writable_schema = 1;
|
||||
UPDATE sqlite_schema
|
||||
SET sql = 'CREATE TABLE t1(a INTEGER PRIMARY KEY AUTOINCREMENT, b)'
|
||||
}
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
do_execsql_test 8.1 {
|
||||
ALTER TABLE t1 DROP COLUMN b;
|
||||
}
|
||||
do_execsql_test 8.2 {
|
||||
SELECT sql FROM sqlite_schema;
|
||||
} {{CREATE TABLE t1(a INTEGER PRIMARY KEY AUTOINCREMENT)}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
foreach {tn wo} {
|
||||
1 {}
|
||||
2 {WITHOUT ROWID}
|
||||
} {
|
||||
reset_db
|
||||
do_execsql_test 9.$tn.0 "
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c) $wo;
|
||||
"
|
||||
do_execsql_test 9.$tn.1 {
|
||||
WITH s(i) AS (
|
||||
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<50000
|
||||
)
|
||||
INSERT INTO t1(a, b, c) SELECT i, 123, 456 FROM s;
|
||||
}
|
||||
do_execsql_test 9.$tn.2 {
|
||||
ALTER TABLE t1 DROP COLUMN b;
|
||||
}
|
||||
|
||||
do_execsql_test 9.$tn.3 {
|
||||
SELECT count(*), c FROM t1 GROUP BY c;
|
||||
} {50000 456}
|
||||
}
|
||||
|
||||
|
||||
|
||||
finish_test
|
222
testdata/tcl/alterdropcol2.test
vendored
Normal file
222
testdata/tcl/alterdropcol2.test
vendored
Normal file
|
@ -0,0 +1,222 @@
|
|||
# 2021 February 19
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#*************************************************************************
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix alterdropcol2
|
||||
|
||||
# If SQLITE_OMIT_ALTERTABLE is defined, omit this file.
|
||||
ifcapable !altertable {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-58318-35349 The DROP COLUMN syntax is used to remove an
|
||||
# existing column from a table.
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE t1(c, b, a, PRIMARY KEY(b, a)) WITHOUT ROWID;
|
||||
INSERT INTO t1 VALUES(1, 2, 3), (4, 5, 6);
|
||||
}
|
||||
do_execsql_test 1.1 {
|
||||
ALTER TABLE t1 DROP c;
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: The DROP COLUMN command removes the named column from the table,
|
||||
# and also rewrites the entire table to purge the data associated with that
|
||||
# column.
|
||||
do_execsql_test 1.2.1 {
|
||||
SELECT * FROM t1;
|
||||
} {2 3 5 6}
|
||||
|
||||
do_execsql_test 1.2.2 {
|
||||
SELECT sql FROM sqlite_schema;
|
||||
} {
|
||||
{CREATE TABLE t1(b, a, PRIMARY KEY(b, a)) WITHOUT ROWID}
|
||||
}
|
||||
|
||||
proc do_atdc_error_test {tn schema atdc error} {
|
||||
reset_db
|
||||
execsql $schema
|
||||
uplevel [list do_catchsql_test $tn $atdc [list 1 [string trim $error]]]
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test cases 2.* attempt to verify the following:
|
||||
#
|
||||
# EVIDENCE-OF: R-24098-10282 The DROP COLUMN command only works if the column
|
||||
# is not referenced by any other parts of the schema and is not a PRIMARY KEY
|
||||
# and does not have a UNIQUE constraint.
|
||||
#
|
||||
|
||||
# EVIDENCE-OF: R-52436-31752 The column is a PRIMARY KEY or part of one.
|
||||
#
|
||||
do_atdc_error_test 2.1.1 {
|
||||
CREATE TABLE x1(a PRIMARY KEY, b, c);
|
||||
} {
|
||||
ALTER TABLE x1 DROP COLUMN a
|
||||
} {
|
||||
cannot drop PRIMARY KEY column: "a"
|
||||
}
|
||||
do_atdc_error_test 2.1.2 {
|
||||
CREATE TABLE x1(a,b,c,d,e, PRIMARY KEY(b,c,d));
|
||||
} {
|
||||
ALTER TABLE x1 DROP COLUMN c
|
||||
} {
|
||||
cannot drop PRIMARY KEY column: "c"
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-43412-16016 The column has a UNIQUE constraint.
|
||||
#
|
||||
do_atdc_error_test 2.2.1 {
|
||||
CREATE TABLE x1(a PRIMARY KEY, b, c UNIQUE);
|
||||
} {
|
||||
ALTER TABLE x1 DROP COLUMN c
|
||||
} {
|
||||
cannot drop UNIQUE column: "c"
|
||||
}
|
||||
do_atdc_error_test 2.2.2 {
|
||||
CREATE TABLE x1(a PRIMARY KEY, b, c, UNIQUE(b, c));
|
||||
} {
|
||||
ALTER TABLE x1 DROP COLUMN c
|
||||
} {
|
||||
error in table x1 after drop column: no such column: c
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-46731-08965 The column is indexed.
|
||||
#
|
||||
do_atdc_error_test 2.3.1 {
|
||||
CREATE TABLE 'one two'('x y', 'z 1', 'a b');
|
||||
CREATE INDEX idx ON 'one two'('z 1');
|
||||
} {
|
||||
ALTER TABLE 'one two' DROP COLUMN 'z 1'
|
||||
} {
|
||||
error in index idx after drop column: no such column: z 1
|
||||
}
|
||||
do_atdc_error_test 2.3.2 {
|
||||
CREATE TABLE x1(a, b, c);
|
||||
CREATE INDEX idx ON x1(a);
|
||||
} {
|
||||
ALTER TABLE x1 DROP COLUMN a;
|
||||
} {
|
||||
error in index idx after drop column: no such column: a
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-46731-08965 The column is indexed.
|
||||
#
|
||||
do_atdc_error_test 2.4.1 {
|
||||
CREATE TABLE x1234(a, b, c PRIMARY KEY) WITHOUT ROWID;
|
||||
CREATE INDEX i1 ON x1234(b) WHERE ((a+5) % 10)==0;
|
||||
} {
|
||||
ALTER TABLE x1234 DROP a
|
||||
} {
|
||||
error in index i1 after drop column: no such column: a
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-47838-03249 The column is named in a table or column
|
||||
# CHECK constraint not associated with the column being dropped.
|
||||
#
|
||||
do_atdc_error_test 2.5.1 {
|
||||
CREATE TABLE x1234(a, b, c PRIMARY KEY, CHECK(((a+5)%10)!=0)) WITHOUT ROWID;
|
||||
} {
|
||||
ALTER TABLE x1234 DROP a
|
||||
} {
|
||||
error in table x1234 after drop column: no such column: a
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-55640-01652 The column is used in a foreign key constraint.
|
||||
#
|
||||
do_atdc_error_test 2.6.1 {
|
||||
CREATE TABLE p1(x, y UNIQUE);
|
||||
CREATE TABLE c1(u, v, FOREIGN KEY (v) REFERENCES p1(y))
|
||||
} {
|
||||
ALTER TABLE c1 DROP v
|
||||
} {
|
||||
error in table c1 after drop column: unknown column "v" in foreign key definition
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-20795-39479 The column is used in the expression of a
|
||||
# generated column.
|
||||
do_atdc_error_test 2.7.1 {
|
||||
CREATE TABLE c1(u, v, w AS (u+v));
|
||||
} {
|
||||
ALTER TABLE c1 DROP v
|
||||
} {
|
||||
error in table c1 after drop column: no such column: v
|
||||
}
|
||||
do_atdc_error_test 2.7.2 {
|
||||
CREATE TABLE c1(u, v, w AS (u+v) STORED);
|
||||
} {
|
||||
ALTER TABLE c1 DROP u
|
||||
} {
|
||||
error in table c1 after drop column: no such column: u
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-01515-49025 The column appears in a trigger or view.
|
||||
#
|
||||
do_atdc_error_test 2.8.1 {
|
||||
CREATE TABLE log(l);
|
||||
CREATE TABLE c1(u, v, w);
|
||||
CREATE TRIGGER tr1 AFTER INSERT ON c1 BEGIN
|
||||
INSERT INTO log VALUES(new.w);
|
||||
END;
|
||||
} {
|
||||
ALTER TABLE c1 DROP w
|
||||
} {
|
||||
error in trigger tr1 after drop column: no such column: new.w
|
||||
}
|
||||
do_atdc_error_test 2.8.2 {
|
||||
CREATE TABLE c1(u, v, w);
|
||||
CREATE VIEW v1 AS SELECT u, v, w FROM c1;
|
||||
} {
|
||||
ALTER TABLE c1 DROP w
|
||||
} {
|
||||
error in view v1 after drop column: no such column: w
|
||||
}
|
||||
do_atdc_error_test 2.8.3 {
|
||||
CREATE TABLE c1(u, v, w);
|
||||
CREATE VIEW v1 AS SELECT * FROM c1 WHERE w IS NOT NULL;
|
||||
} {
|
||||
ALTER TABLE c1 DROP w
|
||||
} {
|
||||
error in view v1 after drop column: no such column: w
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Verify that a column that is part of a CHECK constraint may be dropped
|
||||
# if the CHECK constraint was specified as part of the column definition.
|
||||
#
|
||||
|
||||
# STALE-EVIDENCE: R-60924-11170 However, the column being deleted can be used in a
|
||||
# column CHECK constraint because the column CHECK constraint is dropped
|
||||
# together with the column itself.
|
||||
do_execsql_test 3.0 {
|
||||
CREATE TABLE yyy(q, w, e CHECK (e > 0), r);
|
||||
INSERT INTO yyy VALUES(1,1,1,1), (2,2,2,2);
|
||||
|
||||
CREATE TABLE zzz(q, w, e, r, CHECK (e > 0));
|
||||
INSERT INTO zzz VALUES(1,1,1,1), (2,2,2,2);
|
||||
}
|
||||
do_catchsql_test 3.1.1 {
|
||||
INSERT INTO yyy VALUES(0,0,0,0);
|
||||
} {1 {CHECK constraint failed: e > 0}}
|
||||
do_catchsql_test 3.1.2 {
|
||||
INSERT INTO yyy VALUES(0,0,0,0);
|
||||
} {1 {CHECK constraint failed: e > 0}}
|
||||
|
||||
do_execsql_test 3.2.1 {
|
||||
ALTER TABLE yyy DROP e;
|
||||
}
|
||||
do_catchsql_test 3.2.2 {
|
||||
ALTER TABLE zzz DROP e;
|
||||
} {1 {error in table zzz after drop column: no such column: e}}
|
||||
|
||||
finish_test
|
41
testdata/tcl/alterfault.test
vendored
Normal file
41
testdata/tcl/alterfault.test
vendored
Normal file
|
@ -0,0 +1,41 @@
|
|||
# 2021 November 16
|
||||
#
|
||||
# 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 regression tests for SQLite library.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix alterfault
|
||||
|
||||
# If SQLITE_OMIT_ALTERTABLE is defined, omit this file.
|
||||
ifcapable !altertable {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE t1(a);
|
||||
}
|
||||
faultsim_save_and_close
|
||||
|
||||
do_faultsim_test 1.1 -faults oom* -prep {
|
||||
faultsim_restore_and_reopen
|
||||
} -body {
|
||||
execsql {
|
||||
ALTER TABLE t1 ADD COLUMN b CHECK (a!=1)
|
||||
}
|
||||
} -test {
|
||||
faultsim_test_result {0 {}}
|
||||
}
|
||||
|
||||
|
||||
|
||||
finish_test
|
469
testdata/tcl/alterlegacy.test
vendored
Normal file
469
testdata/tcl/alterlegacy.test
vendored
Normal file
|
@ -0,0 +1,469 @@
|
|||
# 2018 September 20
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#*************************************************************************
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix alterlegacy
|
||||
|
||||
# If SQLITE_OMIT_ALTERTABLE is defined, omit this file.
|
||||
ifcapable !altertable {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
PRAGMA legacy_alter_table = 1;
|
||||
CREATE TABLE t1(a, b, CHECK(t1.a != t1.b));
|
||||
CREATE TABLE t2(a, b);
|
||||
CREATE INDEX t2expr ON t2(a) WHERE t2.b>0;
|
||||
}
|
||||
|
||||
do_execsql_test 1.1 {
|
||||
SELECT sql FROM sqlite_master
|
||||
} {
|
||||
{CREATE TABLE t1(a, b, CHECK(t1.a != t1.b))}
|
||||
{CREATE TABLE t2(a, b)}
|
||||
{CREATE INDEX t2expr ON t2(a) WHERE t2.b>0}
|
||||
}
|
||||
|
||||
# Legacy behavior is to corrupt the schema in this case, as the table name in
|
||||
# the CHECK constraint is incorrect after "t1" is renamed. This version is
|
||||
# slightly different - it rejects the change and rolls back the transaction.
|
||||
do_catchsql_test 1.2 {
|
||||
ALTER TABLE t1 RENAME TO t1new;
|
||||
} {1 {error in table t1new after rename: no such column: t1.a}}
|
||||
|
||||
do_execsql_test 1.3 {
|
||||
CREATE TABLE t3(c, d);
|
||||
ALTER TABLE t3 RENAME TO t3new;
|
||||
DROP TABLE t3new;
|
||||
}
|
||||
|
||||
do_execsql_test 1.4 {
|
||||
SELECT sql FROM sqlite_master
|
||||
} {
|
||||
{CREATE TABLE t1(a, b, CHECK(t1.a != t1.b))}
|
||||
{CREATE TABLE t2(a, b)}
|
||||
{CREATE INDEX t2expr ON t2(a) WHERE t2.b>0}
|
||||
}
|
||||
|
||||
|
||||
do_catchsql_test 1.3 {
|
||||
ALTER TABLE t2 RENAME TO t2new;
|
||||
} {1 {error in index t2expr after rename: no such column: t2.b}}
|
||||
do_execsql_test 1.4 {
|
||||
SELECT sql FROM sqlite_master
|
||||
} {
|
||||
{CREATE TABLE t1(a, b, CHECK(t1.a != t1.b))}
|
||||
{CREATE TABLE t2(a, b)}
|
||||
{CREATE INDEX t2expr ON t2(a) WHERE t2.b>0}
|
||||
}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
ifcapable vtab {
|
||||
register_echo_module db
|
||||
|
||||
do_execsql_test 2.0 {
|
||||
PRAGMA legacy_alter_table = 1;
|
||||
CREATE TABLE abc(a, b, c);
|
||||
INSERT INTO abc VALUES(1, 2, 3);
|
||||
CREATE VIRTUAL TABLE eee USING echo('abc');
|
||||
SELECT * FROM eee;
|
||||
} {1 2 3}
|
||||
|
||||
do_execsql_test 2.1 {
|
||||
ALTER TABLE eee RENAME TO fff;
|
||||
SELECT * FROM fff;
|
||||
} {1 2 3}
|
||||
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
|
||||
do_catchsql_test 2.2 {
|
||||
ALTER TABLE fff RENAME TO ggg;
|
||||
} {1 {no such module: echo}}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
|
||||
do_execsql_test 3.0 {
|
||||
PRAGMA legacy_alter_table = 1;
|
||||
CREATE TABLE txx(a, b, c);
|
||||
INSERT INTO txx VALUES(1, 2, 3);
|
||||
CREATE VIEW vvv AS SELECT main.txx.a, txx.b, c FROM txx;
|
||||
CREATE VIEW uuu AS SELECT main.one.a, one.b, c FROM txx AS one;
|
||||
CREATE VIEW temp.ttt AS SELECT main.txx.a, txx.b, one.b, main.one.a FROM txx AS one, txx;
|
||||
}
|
||||
|
||||
do_execsql_test 3.1.1 {
|
||||
SELECT * FROM vvv;
|
||||
} {1 2 3}
|
||||
do_execsql_test 3.1.2a {
|
||||
ALTER TABLE txx RENAME TO "t xx";
|
||||
}
|
||||
do_catchsql_test 3.1.2b {
|
||||
SELECT * FROM vvv;
|
||||
} {1 {no such table: main.txx}}
|
||||
do_execsql_test 3.1.3 {
|
||||
SELECT sql FROM sqlite_master WHERE name='vvv';
|
||||
} {{CREATE VIEW vvv AS SELECT main.txx.a, txx.b, c FROM txx}}
|
||||
|
||||
|
||||
do_catchsql_test 3.2.1 {
|
||||
SELECT * FROM uuu;
|
||||
} {1 {no such table: main.txx}}
|
||||
do_execsql_test 3.2.2 {
|
||||
SELECT sql FROM sqlite_master WHERE name='uuu';;
|
||||
} {{CREATE VIEW uuu AS SELECT main.one.a, one.b, c FROM txx AS one}}
|
||||
|
||||
do_catchsql_test 3.3.1 {
|
||||
SELECT * FROM ttt;
|
||||
} {1 {no such table: txx}}
|
||||
do_execsql_test 3.3.2 {
|
||||
SELECT sql FROM sqlite_temp_master WHERE name='ttt';
|
||||
} {{CREATE VIEW ttt AS SELECT main.txx.a, txx.b, one.b, main.one.a FROM txx AS one, txx}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 4.0 {
|
||||
PRAGMA legacy_alter_table = 1;
|
||||
CREATE table t1(x, y);
|
||||
CREATE table t2(a, b);
|
||||
|
||||
CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN
|
||||
SELECT t1.x, * FROM t1, t2;
|
||||
INSERT INTO t2 VALUES(new.x, new.y);
|
||||
END;
|
||||
}
|
||||
|
||||
do_execsql_test 4.1 {
|
||||
INSERT INTO t1 VALUES(1, 1);
|
||||
ALTER TABLE t1 RENAME TO t11;
|
||||
}
|
||||
do_catchsql_test 4.1a {
|
||||
INSERT INTO t11 VALUES(2, 2);
|
||||
} {1 {no such table: main.t1}}
|
||||
do_execsql_test 4.1b {
|
||||
ALTER TABLE t11 RENAME TO t1;
|
||||
ALTER TABLE t2 RENAME TO t22;
|
||||
}
|
||||
do_catchsql_test 4.1c {
|
||||
INSERT INTO t1 VALUES(3, 3);
|
||||
} {1 {no such table: main.t2}}
|
||||
|
||||
proc squish {a} {
|
||||
string trim [regsub -all {[[:space:]][[:space:]]*} $a { }]
|
||||
}
|
||||
db func squish squish
|
||||
do_test 4.2 {
|
||||
execsql { SELECT squish(sql) FROM sqlite_master WHERE name = 'tr1' }
|
||||
} [list [squish {
|
||||
CREATE TRIGGER tr1 AFTER INSERT ON "t1" BEGIN
|
||||
SELECT t1.x, * FROM t1, t2;
|
||||
INSERT INTO t2 VALUES(new.x, new.y);
|
||||
END
|
||||
}]]
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 5.0 {
|
||||
PRAGMA legacy_alter_table = 1;
|
||||
CREATE TABLE t9(a, b, c);
|
||||
CREATE TABLE t10(a, b, c);
|
||||
CREATE TEMP TABLE t9(a, b, c);
|
||||
|
||||
CREATE TRIGGER temp.t9t AFTER INSERT ON temp.t9 BEGIN
|
||||
INSERT INTO t10 VALUES(new.a, new.b, new.c);
|
||||
END;
|
||||
|
||||
INSERT INTO temp.t9 VALUES(1, 2, 3);
|
||||
SELECT * FROM t10;
|
||||
} {1 2 3}
|
||||
|
||||
do_execsql_test 5.1 {
|
||||
ALTER TABLE temp.t9 RENAME TO 't1234567890'
|
||||
}
|
||||
|
||||
do_execsql_test 5.2 {
|
||||
CREATE TABLE t1(a, b);
|
||||
CREATE TABLE t2(a, b);
|
||||
INSERT INTO t1 VALUES(1, 2);
|
||||
INSERT INTO t2 VALUES(3, 4);
|
||||
CREATE VIEW v AS SELECT one.a, one.b, t2.a, t2.b FROM t1 AS one, t2;
|
||||
SELECT * FROM v;
|
||||
} {1 2 3 4}
|
||||
|
||||
do_execsql_test 5.3 {
|
||||
ALTER TABLE t2 RENAME TO one;
|
||||
} {}
|
||||
|
||||
do_catchsql_test 5.4 {
|
||||
SELECT * FROM v
|
||||
} {1 {no such table: main.t2}}
|
||||
|
||||
do_execsql_test 5.5 {
|
||||
ALTER TABLE one RENAME TO t2;
|
||||
DROP VIEW v;
|
||||
CREATE VIEW temp.vv AS SELECT one.a, one.b, t2.a, t2.b FROM t1 AS one, t2;
|
||||
SELECT * FROM vv;
|
||||
} {1 2 3 4}
|
||||
|
||||
do_execsql_test 5.6 {
|
||||
ALTER TABLE t2 RENAME TO one;
|
||||
} {}
|
||||
do_catchsql_test 5.7 {
|
||||
SELECT * FROM vv
|
||||
} {1 {no such table: t2}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
ifcapable vtab {
|
||||
register_tcl_module db
|
||||
proc tcl_command {method args} {
|
||||
switch -- $method {
|
||||
xConnect {
|
||||
return "CREATE TABLE t1(a, b, c)"
|
||||
}
|
||||
}
|
||||
return {}
|
||||
}
|
||||
|
||||
do_execsql_test 6.0 {
|
||||
CREATE VIRTUAL TABLE x1 USING tcl(tcl_command);
|
||||
}
|
||||
|
||||
do_execsql_test 6.1 {
|
||||
ALTER TABLE x1 RENAME TO x2;
|
||||
SELECT sql FROM sqlite_master WHERE name = 'x2'
|
||||
} {{CREATE VIRTUAL TABLE "x2" USING tcl(tcl_command)}}
|
||||
|
||||
do_execsql_test 7.1 {
|
||||
CREATE TABLE ddd(db, sql, zOld, zNew, bTemp);
|
||||
INSERT INTO ddd VALUES(
|
||||
'main', 'CREATE TABLE x1(i INTEGER, t TEXT)', 'ddd', NULL, 0
|
||||
), (
|
||||
'main', 'CREATE TABLE x1(i INTEGER, t TEXT)', NULL, 'eee', 0
|
||||
), (
|
||||
'main', NULL, 'ddd', 'eee', 0
|
||||
);
|
||||
} {}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
forcedelete test.db2
|
||||
do_execsql_test 8.1 {
|
||||
PRAGMA legacy_alter_table = 1;
|
||||
ATTACH 'test.db2' AS aux;
|
||||
PRAGMA foreign_keys = on;
|
||||
CREATE TABLE aux.p1(a INTEGER PRIMARY KEY, b);
|
||||
CREATE TABLE aux.c1(x INTEGER PRIMARY KEY, y REFERENCES p1(a));
|
||||
INSERT INTO aux.p1 VALUES(1, 1);
|
||||
INSERT INTO aux.p1 VALUES(2, 2);
|
||||
INSERT INTO aux.c1 VALUES(NULL, 2);
|
||||
CREATE TABLE aux.c2(x INTEGER PRIMARY KEY, y REFERENCES c1(a));
|
||||
}
|
||||
|
||||
do_execsql_test 8.2 {
|
||||
ALTER TABLE aux.p1 RENAME TO ppp;
|
||||
}
|
||||
|
||||
do_execsql_test 8.2 {
|
||||
INSERT INTO aux.c1 VALUES(NULL, 1);
|
||||
SELECT sql FROM aux.sqlite_master WHERE name = 'c1';
|
||||
} {{CREATE TABLE c1(x INTEGER PRIMARY KEY, y REFERENCES "ppp"(a))}}
|
||||
|
||||
reset_db
|
||||
do_execsql_test 9.0 {
|
||||
PRAGMA legacy_alter_table = 1;
|
||||
CREATE TABLE t1(a, b, c);
|
||||
CREATE VIEW v1 AS SELECT * FROM t2;
|
||||
}
|
||||
do_execsql_test 9.1 {
|
||||
ALTER TABLE t1 RENAME TO t3;
|
||||
} {}
|
||||
do_execsql_test 9.1b {
|
||||
ALTER TABLE t3 RENAME TO t1;
|
||||
} {}
|
||||
do_execsql_test 9.2 {
|
||||
DROP VIEW v1;
|
||||
CREATE TRIGGER tr AFTER INSERT ON t1 BEGIN
|
||||
INSERT INTO t2 VALUES(new.a);
|
||||
END;
|
||||
}
|
||||
do_execsql_test 9.3 {
|
||||
ALTER TABLE t1 RENAME TO t3;
|
||||
} {}
|
||||
|
||||
forcedelete test.db2
|
||||
do_execsql_test 9.4 {
|
||||
ALTER TABLE t3 RENAME TO t1;
|
||||
DROP TRIGGER tr;
|
||||
|
||||
ATTACH 'test.db2' AS aux;
|
||||
CREATE TRIGGER tr AFTER INSERT ON t1 WHEN new.a IS NULL BEGIN SELECT 1, 2, 3; END;
|
||||
|
||||
CREATE TABLE aux.t1(x);
|
||||
CREATE TEMP TRIGGER tr AFTER INSERT ON aux.t1 BEGIN SELECT 1, 2, 3; END;
|
||||
}
|
||||
do_execsql_test 9.5 {
|
||||
ALTER TABLE main.t1 RENAME TO t3;
|
||||
}
|
||||
do_execsql_test 9.6 {
|
||||
SELECT sql FROM sqlite_temp_master;
|
||||
SELECT sql FROM sqlite_master WHERE type='trigger';
|
||||
} {
|
||||
{CREATE TRIGGER tr AFTER INSERT ON aux.t1 BEGIN SELECT 1, 2, 3; END}
|
||||
{CREATE TRIGGER tr AFTER INSERT ON "t3" WHEN new.a IS NULL BEGIN SELECT 1, 2, 3; END}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
ifcapable fts5 {
|
||||
do_execsql_test 10.0 {
|
||||
PRAGMA legacy_alter_table = 1;
|
||||
CREATE VIRTUAL TABLE fff USING fts5(x, y, z);
|
||||
}
|
||||
|
||||
do_execsql_test 10.1 {
|
||||
BEGIN;
|
||||
INSERT INTO fff VALUES('a', 'b', 'c');
|
||||
ALTER TABLE fff RENAME TO ggg;
|
||||
COMMIT;
|
||||
}
|
||||
|
||||
do_execsql_test 10.2 {
|
||||
SELECT * FROM ggg;
|
||||
} {a b c}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
forcedelete test.db2
|
||||
db func trigger trigger
|
||||
set ::trigger [list]
|
||||
proc trigger {args} {
|
||||
lappend ::trigger $args
|
||||
}
|
||||
do_execsql_test 11.0 {
|
||||
PRAGMA legacy_alter_table = 1;
|
||||
ATTACH 'test.db2' AS aux;
|
||||
CREATE TABLE aux.t1(a, b, c);
|
||||
CREATE TABLE main.t1(a, b, c);
|
||||
CREATE TEMP TRIGGER tr AFTER INSERT ON aux.t1 BEGIN
|
||||
SELECT trigger(new.a, new.b, new.c);
|
||||
END;
|
||||
}
|
||||
|
||||
do_execsql_test 11.1 {
|
||||
INSERT INTO main.t1 VALUES(1, 2, 3);
|
||||
INSERT INTO aux.t1 VALUES(4, 5, 6);
|
||||
}
|
||||
do_test 11.2 { set ::trigger } {{4 5 6}}
|
||||
|
||||
do_execsql_test 11.3 {
|
||||
SELECT name, tbl_name FROM sqlite_temp_master;
|
||||
} {tr t1}
|
||||
|
||||
do_execsql_test 11.4 {
|
||||
ALTER TABLE main.t1 RENAME TO t2;
|
||||
SELECT name, tbl_name FROM sqlite_temp_master;
|
||||
} {tr t1}
|
||||
|
||||
do_execsql_test 11.5 {
|
||||
ALTER TABLE aux.t1 RENAME TO t2;
|
||||
SELECT name, tbl_name FROM sqlite_temp_master;
|
||||
} {tr t2}
|
||||
|
||||
do_execsql_test 11.6 {
|
||||
INSERT INTO aux.t2 VALUES(7, 8, 9);
|
||||
}
|
||||
do_test 11.7 { set ::trigger } {{4 5 6} {7 8 9}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 12.0 {
|
||||
PRAGMA legacy_alter_table = 1;
|
||||
CREATE TABLE t1(a);
|
||||
CREATE TABLE t2(w);
|
||||
CREATE TRIGGER temp.r1 AFTER INSERT ON main.t2 BEGIN
|
||||
INSERT INTO t1(a) VALUES(new.w);
|
||||
END;
|
||||
CREATE TEMP TABLE t2(x);
|
||||
}
|
||||
|
||||
do_execsql_test 12.1 {
|
||||
ALTER TABLE main.t2 RENAME TO t3;
|
||||
}
|
||||
|
||||
do_execsql_test 12.2 {
|
||||
INSERT INTO t3 VALUES('WWW');
|
||||
SELECT * FROM t1;
|
||||
} {WWW}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
|
||||
ifcapable rtree {
|
||||
do_execsql_test 14.0 {
|
||||
PRAGMA legacy_alter_table = 1;
|
||||
CREATE VIRTUAL TABLE rt USING rtree(id, minx, maxx, miny, maxy);
|
||||
|
||||
CREATE TABLE "mytable" ( "fid" INTEGER PRIMARY KEY, "geom" BLOB);
|
||||
|
||||
CREATE TRIGGER tr1 AFTER UPDATE OF "geom" ON "mytable"
|
||||
WHEN OLD."fid" = NEW."fid" AND NEW."geom" IS NULL BEGIN
|
||||
DELETE FROM rt WHERE id = OLD."fid";
|
||||
END;
|
||||
|
||||
INSERT INTO mytable VALUES(1, X'abcd');
|
||||
}
|
||||
|
||||
do_execsql_test 14.1 {
|
||||
UPDATE mytable SET geom = X'1234'
|
||||
}
|
||||
|
||||
do_execsql_test 14.2 {
|
||||
ALTER TABLE mytable RENAME TO mytable_renamed;
|
||||
}
|
||||
|
||||
do_execsql_test 14.3 {
|
||||
CREATE TRIGGER tr2 AFTER INSERT ON mytable_renamed BEGIN
|
||||
DELETE FROM rt WHERE id=(SELECT min(id) FROM rt);
|
||||
END;
|
||||
}
|
||||
|
||||
do_execsql_test 14.4 {
|
||||
ALTER TABLE mytable_renamed RENAME TO mytable2;
|
||||
}
|
||||
}
|
||||
|
||||
reset_db
|
||||
do_execsql_test 14.5 {
|
||||
PRAGMA legacy_alter_table = 1;
|
||||
CREATE TABLE t1(a, b, c);
|
||||
CREATE VIEW v1 AS SELECT * FROM t1;
|
||||
CREATE TRIGGER xyz AFTER INSERT ON t1 BEGIN
|
||||
SELECT a, b FROM v1;
|
||||
END;
|
||||
}
|
||||
do_execsql_test 14.6 {
|
||||
ALTER TABLE t1 RENAME TO tt1;
|
||||
}
|
||||
|
||||
|
||||
finish_test
|
71
testdata/tcl/altermalloc.test
vendored
Normal file
71
testdata/tcl/altermalloc.test
vendored
Normal file
|
@ -0,0 +1,71 @@
|
|||
# 2005 September 19
|
||||
#
|
||||
# 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 regression tests for SQLite library. The
|
||||
# focus of this script is testing the ALTER TABLE statement and
|
||||
# specifically out-of-memory conditions within that command.
|
||||
#
|
||||
# $Id: altermalloc.test,v 1.10 2008/10/30 17:21:13 danielk1977 Exp $
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
# If SQLITE_OMIT_ALTERTABLE is defined, omit this file.
|
||||
ifcapable !altertable {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
source $testdir/malloc_common.tcl
|
||||
|
||||
do_malloc_test altermalloc-1 -tclprep {
|
||||
db close
|
||||
} -tclbody {
|
||||
if {[catch {sqlite3 db test.db}]} {
|
||||
error "out of memory"
|
||||
}
|
||||
sqlite3_db_config_lookaside db 0 0 0
|
||||
sqlite3_extended_result_codes db 1
|
||||
} -sqlbody {
|
||||
CREATE TABLE t1(a int);
|
||||
ALTER TABLE t1 ADD COLUMN b INTEGER DEFAULT NULL;
|
||||
ALTER TABLE t1 ADD COLUMN c TEXT DEFAULT 'default-text';
|
||||
ALTER TABLE t1 RENAME TO t2;
|
||||
ALTER TABLE t2 ADD COLUMN d BLOB DEFAULT X'ABCD';
|
||||
}
|
||||
|
||||
# Test malloc() failure on an ALTER TABLE on a virtual table.
|
||||
#
|
||||
ifcapable vtab {
|
||||
do_malloc_test altermalloc-vtab -tclprep {
|
||||
sqlite3 db2 test.db
|
||||
sqlite3_db_config_lookaside db2 0 0 0
|
||||
sqlite3_extended_result_codes db2 1
|
||||
register_echo_module [sqlite3_connection_pointer db2]
|
||||
db2 eval {
|
||||
CREATE TABLE t1(a, b VARCHAR, c INTEGER);
|
||||
CREATE VIRTUAL TABLE t1echo USING echo(t1);
|
||||
}
|
||||
db2 close
|
||||
|
||||
register_echo_module [sqlite3_connection_pointer db]
|
||||
} -tclbody {
|
||||
set rc [catch {db eval { ALTER TABLE t1echo RENAME TO t1_echo }} msg]
|
||||
if {$msg eq "vtable constructor failed: t1echo"} {
|
||||
set msg "out of memory"
|
||||
}
|
||||
if {$rc} {
|
||||
error $msg
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
finish_test
|
127
testdata/tcl/altermalloc2.test
vendored
Normal file
127
testdata/tcl/altermalloc2.test
vendored
Normal file
|
@ -0,0 +1,127 @@
|
|||
# 2018 August 20
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#*************************************************************************
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
source $testdir/malloc_common.tcl
|
||||
set testprefix altermalloc2
|
||||
|
||||
# If SQLITE_OMIT_ALTERTABLE is defined, omit this file.
|
||||
ifcapable !altertable {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE t1(abcd, efgh);
|
||||
}
|
||||
faultsim_save_and_close
|
||||
|
||||
set ::TMPDBERROR [list 1 \
|
||||
{unable to open a temporary database file for storing temporary tables}
|
||||
]
|
||||
|
||||
|
||||
do_faultsim_test 1 -prep {
|
||||
faultsim_restore_and_reopen
|
||||
} -body {
|
||||
execsql {
|
||||
ALTER TABLE t1 RENAME abcd TO dcba
|
||||
}
|
||||
} -test {
|
||||
faultsim_test_result {0 {}} $::TMPDBERROR
|
||||
}
|
||||
|
||||
catch {db close}
|
||||
forcedelete test.db
|
||||
sqlite3 db test.db
|
||||
do_execsql_test 2.0 {
|
||||
PRAGMA encoding = 'utf-16';
|
||||
CREATE TABLE t1(abcd, efgh);
|
||||
}
|
||||
faultsim_save_and_close
|
||||
|
||||
do_faultsim_test 2 -prep {
|
||||
faultsim_restore_and_reopen
|
||||
} -body {
|
||||
execsql {
|
||||
ALTER TABLE t1 RENAME abcd TO dcba
|
||||
}
|
||||
} -test {
|
||||
faultsim_test_result {0 {}} $::TMPDBERROR
|
||||
}
|
||||
|
||||
|
||||
reset_db
|
||||
do_execsql_test 3.0 {
|
||||
CREATE TABLE t1(abcd, efgh);
|
||||
CREATE VIEW v1 AS SELECT * FROM t1 WHERE abcd>efgh;
|
||||
}
|
||||
faultsim_save_and_close
|
||||
|
||||
do_faultsim_test 3 -prep {
|
||||
faultsim_restore_and_reopen
|
||||
} -body {
|
||||
execsql {
|
||||
ALTER TABLE t1 RENAME abcd TO dcba
|
||||
}
|
||||
} -test {
|
||||
faultsim_test_result {0 {}} $::TMPDBERROR
|
||||
}
|
||||
|
||||
reset_db
|
||||
do_execsql_test 4.0 {
|
||||
CREATE TABLE rr(a, b);
|
||||
CREATE VIEW vv AS SELECT * FROM rr;
|
||||
|
||||
CREATE TRIGGER vv1 INSTEAD OF INSERT ON vv BEGIN
|
||||
SELECT 1, 2, 3;
|
||||
END;
|
||||
CREATE TRIGGER tr1 AFTER INSERT ON rr BEGIN
|
||||
INSERT INTO vv VALUES(new.a, new.b);
|
||||
END;
|
||||
} {}
|
||||
|
||||
faultsim_save_and_close
|
||||
do_faultsim_test 4 -faults oom-* -prep {
|
||||
faultsim_restore_and_reopen
|
||||
execsql { SELECT * FROM sqlite_master }
|
||||
} -body {
|
||||
execsql {
|
||||
ALTER TABLE rr RENAME a TO c;
|
||||
}
|
||||
} -test {
|
||||
faultsim_test_result {0 {}} $::TMPDBERROR
|
||||
}
|
||||
|
||||
reset_db
|
||||
do_execsql_test 5.0 {
|
||||
CREATE TABLE rr(a, b);
|
||||
CREATE VIEW vv AS SELECT * FROM (
|
||||
WITH abc(d, e) AS (SELECT * FROM rr)
|
||||
SELECT * FROM abc
|
||||
);
|
||||
} {}
|
||||
|
||||
faultsim_save_and_close
|
||||
do_faultsim_test 5 -faults oom-* -prep {
|
||||
faultsim_restore_and_reopen
|
||||
execsql { SELECT * FROM sqlite_master }
|
||||
} -body {
|
||||
execsql {
|
||||
ALTER TABLE rr RENAME TO c;
|
||||
}
|
||||
} -test {
|
||||
faultsim_test_result {0 {}} $::TMPDBERROR
|
||||
}
|
||||
|
||||
finish_test
|
89
testdata/tcl/altermalloc3.test
vendored
Normal file
89
testdata/tcl/altermalloc3.test
vendored
Normal file
|
@ -0,0 +1,89 @@
|
|||
# 2021 February 18
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#*************************************************************************
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
source $testdir/malloc_common.tcl
|
||||
set testprefix altermalloc3
|
||||
|
||||
# If SQLITE_OMIT_ALTERTABLE is defined, omit this file.
|
||||
ifcapable !altertable {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
set ::TMPDBERROR [list 1 \
|
||||
{unable to open a temporary database file for storing temporary tables}
|
||||
]
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE x1(
|
||||
one, two, three, PRIMARY KEY(one),
|
||||
CHECK (three!="xyz"), CHECK (two!="one")
|
||||
) WITHOUT ROWID;
|
||||
CREATE INDEX x1i ON x1(one+"two"+"four") WHERE "five";
|
||||
CREATE TEMP TRIGGER AFTER INSERT ON x1 BEGIN
|
||||
UPDATE x1 SET two=new.three || "new" WHERE one=new.one||"";
|
||||
END;
|
||||
CREATE TABLE t1(a, b, c, d, PRIMARY KEY(d, b)) WITHOUT ROWID;
|
||||
INSERT INTO t1 VALUES(1, 2, 3, 4);
|
||||
}
|
||||
faultsim_save_and_close
|
||||
|
||||
do_faultsim_test 1 -prep {
|
||||
faultsim_restore_and_reopen
|
||||
} -body {
|
||||
execsql { ALTER TABLE t1 DROP COLUMN c }
|
||||
} -test {
|
||||
faultsim_test_result {0 {}} $::TMPDBERROR
|
||||
}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# dbsqlfuzz e3dd84cda3848016a6a6024c7249d09bc2ef2615
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 2.0 {
|
||||
CREATE TABLE t2(k,v);
|
||||
CREATE TRIGGER r2 AFTER INSERT ON t2 BEGIN
|
||||
UPDATE t2 SET (k,v)= (
|
||||
(WITH cte1(a) AS ( SELECT 1 FROM ( SELECT * FROM t2 ) )
|
||||
SELECT a FROM cte1
|
||||
), 1);
|
||||
END;
|
||||
|
||||
CREATE TRIGGER r1 AFTER INSERT ON t2 BEGIN
|
||||
UPDATE t2 SET k=1 FROM t2 AS one, t2 AS two NATURAL JOIN t2 AS three
|
||||
WHERE one.k=two.v;
|
||||
END;
|
||||
}
|
||||
|
||||
faultsim_save_and_close
|
||||
faultsim_restore_and_reopen
|
||||
|
||||
do_execsql_test 2.1 {
|
||||
ALTER TABLE t2 RENAME TO t2x;
|
||||
}
|
||||
|
||||
do_faultsim_test 2.2 -prep {
|
||||
faultsim_restore_and_reopen
|
||||
db eval { SELECT * FROM sqlite_master }
|
||||
} -body {
|
||||
execsql {
|
||||
ALTER TABLE t2 RENAME TO t2x;
|
||||
}
|
||||
} -test {
|
||||
faultsim_test_result {0 {}} $::TMPDBERROR
|
||||
}
|
||||
|
||||
finish_test
|
120
testdata/tcl/alterqf.test
vendored
Normal file
120
testdata/tcl/alterqf.test
vendored
Normal file
|
@ -0,0 +1,120 @@
|
|||
# 2021 March 16
|
||||
#
|
||||
# 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 regression tests for SQLite library. This
|
||||
# script focuses on testing internal function sqlite_rename_quotefix().
|
||||
#
|
||||
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix alterqf
|
||||
|
||||
# If SQLITE_OMIT_ALTERTABLE is defined, omit this file.
|
||||
ifcapable !altertable {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
sqlite3_test_control SQLITE_TESTCTRL_INTERNAL_FUNCTIONS db
|
||||
sqlite3_db_config db SQLITE_DBCONFIG_DQS_DDL 1
|
||||
sqlite3_db_config db SQLITE_DBCONFIG_DQS_DML 1
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE t1(a, b, c);
|
||||
}
|
||||
|
||||
foreach {tn before after} {
|
||||
1 {CREATE VIEW v1 AS SELECT "a", "b", "notacolumn!", "c" FROM t1}
|
||||
{CREATE VIEW v1 AS SELECT "a", "b", 'notacolumn!', "c" FROM t1}
|
||||
|
||||
2 {CREATE VIEW v1 AS SELECT "a", "b", "not'a'column!", "c" FROM t1}
|
||||
{CREATE VIEW v1 AS SELECT "a", "b", 'not''a''column!', "c" FROM t1}
|
||||
|
||||
3 {CREATE VIEW v1 AS SELECT "a", "b", "not""a""column!", "c" FROM t1}
|
||||
{CREATE VIEW v1 AS SELECT "a", "b", 'not"a"column!', "c" FROM t1}
|
||||
|
||||
4 {CREATE VIEW v1 AS SELECT "val", count("b") FROM t1 GROUP BY "abc"}
|
||||
{CREATE VIEW v1 AS SELECT 'val', count("b") FROM t1 GROUP BY 'abc'}
|
||||
|
||||
5 {CREATE TABLE xyz(a CHECK (a!="str"), b AS (a||"str"))}
|
||||
{CREATE TABLE xyz(a CHECK (a!='str'), b AS (a||'str'))}
|
||||
|
||||
6 {CREATE INDEX i1 ON t1(a || "str", "b", "val")}
|
||||
{CREATE INDEX i1 ON t1(a || 'str', "b", 'val')}
|
||||
|
||||
7 {CREATE TRIGGER tr AFTER INSERT ON t1 BEGIN SELECT "abcd"; END}
|
||||
{CREATE TRIGGER tr AFTER INSERT ON t1 BEGIN SELECT 'abcd'; END}
|
||||
|
||||
8 {CREATE VIEW v1 AS SELECT "string"'alias' FROM t1}
|
||||
{CREATE VIEW v1 AS SELECT 'string' 'alias' FROM t1}
|
||||
|
||||
9 {CREATE INDEX i1 ON t1(a) WHERE "b"="bb"}
|
||||
{CREATE INDEX i1 ON t1(a) WHERE "b"='bb'}
|
||||
|
||||
10 {CREATE TABLE t2(abc, xyz CHECK (xyz != "123"))}
|
||||
{CREATE TABLE t2(abc, xyz CHECK (xyz != '123'))}
|
||||
|
||||
11 {CREATE TRIGGER ott AFTER UPDATE ON t1 BEGIN
|
||||
SELECT max("str", new."a") FROM t1
|
||||
WHERE group_concat("b", ",") OVER (ORDER BY c||"str");
|
||||
UPDATE t1 SET c= b + "str";
|
||||
DELETE FROM t1 WHERE EXISTS (
|
||||
SELECT 1 FROM t1 AS o WHERE o."a" = "o.a" AND t1.b IN("t1.b")
|
||||
);
|
||||
END;
|
||||
} {CREATE TRIGGER ott AFTER UPDATE ON t1 BEGIN
|
||||
SELECT max('str', new."a") FROM t1
|
||||
WHERE group_concat("b", ',') OVER (ORDER BY c||'str');
|
||||
UPDATE t1 SET c= b + 'str';
|
||||
DELETE FROM t1 WHERE EXISTS (
|
||||
SELECT 1 FROM t1 AS o WHERE o."a" = 'o.a' AND t1.b IN('t1.b')
|
||||
);
|
||||
END;
|
||||
}
|
||||
|
||||
} {
|
||||
do_execsql_test 1.$tn {
|
||||
SELECT sqlite_rename_quotefix('main', $before)
|
||||
} [list $after]
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
sqlite3_db_config db SQLITE_DBCONFIG_DQS_DDL 1
|
||||
sqlite3_db_config db SQLITE_DBCONFIG_DQS_DML 1
|
||||
do_execsql_test 2.0 {
|
||||
CREATE TABLE x1(
|
||||
one, two, three, PRIMARY KEY(one),
|
||||
CHECK (three!="xyz"), CHECK (two!="one")
|
||||
) WITHOUT ROWID;
|
||||
CREATE INDEX x1i ON x1(one+"two"+"four") WHERE "five";
|
||||
CREATE TEMP TRIGGER AFTER INSERT ON x1 BEGIN
|
||||
UPDATE x1 SET two=new.three || "new" WHERE one=new.one||"";
|
||||
END;
|
||||
}
|
||||
|
||||
do_execsql_test 2.1 {
|
||||
ALTER TABLE x1 RENAME two TO 'four';
|
||||
SELECT sql FROM sqlite_schema;
|
||||
SELECT sql FROM sqlite_temp_schema;
|
||||
} {{CREATE TABLE x1(
|
||||
one, "four", three, PRIMARY KEY(one),
|
||||
CHECK (three!='xyz'), CHECK ("four"!="one")
|
||||
) WITHOUT ROWID}
|
||||
{CREATE INDEX x1i ON x1(one+"four"+'four') WHERE 'five'}
|
||||
{CREATE TRIGGER AFTER INSERT ON x1 BEGIN
|
||||
UPDATE x1 SET "four"=new.three || 'new' WHERE one=new.one||'';
|
||||
END}
|
||||
}
|
||||
|
||||
|
||||
finish_test
|
984
testdata/tcl/altertab.test
vendored
Normal file
984
testdata/tcl/altertab.test
vendored
Normal file
|
@ -0,0 +1,984 @@
|
|||
# 2018 August 24
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#*************************************************************************
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix altertab
|
||||
|
||||
# If SQLITE_OMIT_ALTERTABLE is defined, omit this file.
|
||||
ifcapable !altertable {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE t1(a, b, CHECK(t1.a != t1.b));
|
||||
|
||||
CREATE TABLE t2(a, b);
|
||||
CREATE INDEX t2expr ON t2(a) WHERE t2.b>0;
|
||||
}
|
||||
|
||||
do_execsql_test 1.1 {
|
||||
SELECT sql FROM sqlite_master
|
||||
} {
|
||||
{CREATE TABLE t1(a, b, CHECK(t1.a != t1.b))}
|
||||
{CREATE TABLE t2(a, b)}
|
||||
{CREATE INDEX t2expr ON t2(a) WHERE t2.b>0}
|
||||
}
|
||||
|
||||
do_execsql_test 1.2 {
|
||||
ALTER TABLE t1 RENAME TO t1new;
|
||||
}
|
||||
|
||||
do_execsql_test 1.3 {
|
||||
CREATE TABLE t3(c, d);
|
||||
ALTER TABLE t3 RENAME TO t3new;
|
||||
DROP TABLE t3new;
|
||||
}
|
||||
|
||||
do_execsql_test 1.4 {
|
||||
SELECT sql FROM sqlite_master
|
||||
} {
|
||||
{CREATE TABLE "t1new"(a, b, CHECK("t1new".a != "t1new".b))}
|
||||
{CREATE TABLE t2(a, b)}
|
||||
{CREATE INDEX t2expr ON t2(a) WHERE t2.b>0}
|
||||
}
|
||||
|
||||
|
||||
do_execsql_test 1.3 {
|
||||
ALTER TABLE t2 RENAME TO t2new;
|
||||
}
|
||||
do_execsql_test 1.4 {
|
||||
SELECT sql FROM sqlite_master
|
||||
} {
|
||||
{CREATE TABLE "t1new"(a, b, CHECK("t1new".a != "t1new".b))}
|
||||
{CREATE TABLE "t2new"(a, b)}
|
||||
{CREATE INDEX t2expr ON "t2new"(a) WHERE "t2new".b>0}
|
||||
}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
ifcapable vtab {
|
||||
register_echo_module db
|
||||
|
||||
do_execsql_test 2.0 {
|
||||
CREATE TABLE abc(a, b, c);
|
||||
INSERT INTO abc VALUES(1, 2, 3);
|
||||
CREATE VIRTUAL TABLE eee USING echo('abc');
|
||||
SELECT * FROM eee;
|
||||
} {1 2 3}
|
||||
|
||||
do_execsql_test 2.1 {
|
||||
ALTER TABLE eee RENAME TO fff;
|
||||
SELECT * FROM fff;
|
||||
} {1 2 3}
|
||||
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
|
||||
do_catchsql_test 2.2 {
|
||||
ALTER TABLE fff RENAME TO ggg;
|
||||
} {1 {no such module: echo}}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
|
||||
do_execsql_test 3.0 {
|
||||
CREATE TABLE txx(a, b, c);
|
||||
INSERT INTO txx VALUES(1, 2, 3);
|
||||
CREATE VIEW vvv AS SELECT main.txx.a, txx.b, c FROM txx;
|
||||
CREATE VIEW uuu AS SELECT main.one.a, one.b, c FROM txx AS one;
|
||||
CREATE VIEW temp.ttt AS SELECT main.txx.a, txx.b, one.b, main.one.a FROM txx AS one, txx;
|
||||
}
|
||||
|
||||
do_execsql_test 3.1.1 {
|
||||
SELECT * FROM vvv;
|
||||
} {1 2 3}
|
||||
do_execsql_test 3.1.2 {
|
||||
ALTER TABLE txx RENAME TO "t xx";
|
||||
SELECT * FROM vvv;
|
||||
} {1 2 3}
|
||||
do_execsql_test 3.1.3 {
|
||||
SELECT sql FROM sqlite_master WHERE name='vvv';
|
||||
} {{CREATE VIEW vvv AS SELECT main."t xx".a, "t xx".b, c FROM "t xx"}}
|
||||
|
||||
|
||||
do_execsql_test 3.2.1 {
|
||||
SELECT * FROM uuu;
|
||||
} {1 2 3}
|
||||
do_execsql_test 3.2.2 {
|
||||
SELECT sql FROM sqlite_master WHERE name='uuu';;
|
||||
} {{CREATE VIEW uuu AS SELECT main.one.a, one.b, c FROM "t xx" AS one}}
|
||||
|
||||
do_execsql_test 3.3.1 {
|
||||
SELECT * FROM ttt;
|
||||
} {1 2 2 1}
|
||||
do_execsql_test 3.3.2 {
|
||||
SELECT sql FROM sqlite_temp_master WHERE name='ttt';
|
||||
} {{CREATE VIEW ttt AS SELECT main."t xx".a, "t xx".b, one.b, main.one.a FROM "t xx" AS one, "t xx"}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 4.0 {
|
||||
CREATE table t1(x, y);
|
||||
CREATE table t2(a, b);
|
||||
|
||||
CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN
|
||||
SELECT t1.x, * FROM t1, t2;
|
||||
INSERT INTO t2 VALUES(new.x, new.y);
|
||||
END;
|
||||
}
|
||||
|
||||
do_execsql_test 4.1 {
|
||||
INSERT INTO t1 VALUES(1, 1);
|
||||
ALTER TABLE t1 RENAME TO t11;
|
||||
INSERT INTO t11 VALUES(2, 2);
|
||||
ALTER TABLE t2 RENAME TO t22;
|
||||
INSERT INTO t11 VALUES(3, 3);
|
||||
}
|
||||
|
||||
proc squish {a} {
|
||||
string trim [regsub -all {[[:space:]][[:space:]]*} $a { }]
|
||||
}
|
||||
db func squish squish
|
||||
do_test 4.2 {
|
||||
execsql { SELECT squish(sql) FROM sqlite_master WHERE name = 'tr1' }
|
||||
} [list [squish {
|
||||
CREATE TRIGGER tr1 AFTER INSERT ON "t11" BEGIN
|
||||
SELECT "t11".x, * FROM "t11", "t22";
|
||||
INSERT INTO "t22" VALUES(new.x, new.y);
|
||||
END
|
||||
}]]
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 5.0 {
|
||||
CREATE TABLE t9(a, b, c);
|
||||
CREATE TABLE t10(a, b, c);
|
||||
CREATE TEMP TABLE t9(a, b, c);
|
||||
|
||||
CREATE TRIGGER temp.t9t AFTER INSERT ON temp.t9 BEGIN
|
||||
INSERT INTO t10 VALUES(new.a, new.b, new.c);
|
||||
END;
|
||||
|
||||
INSERT INTO temp.t9 VALUES(1, 2, 3);
|
||||
SELECT * FROM t10;
|
||||
} {1 2 3}
|
||||
|
||||
do_execsql_test 5.1 {
|
||||
ALTER TABLE temp.t9 RENAME TO 't1234567890'
|
||||
}
|
||||
|
||||
do_execsql_test 5.2 {
|
||||
CREATE TABLE t1(a, b);
|
||||
CREATE TABLE t2(a, b);
|
||||
INSERT INTO t1 VALUES(1, 2);
|
||||
INSERT INTO t2 VALUES(3, 4);
|
||||
CREATE VIEW v AS SELECT one.a, one.b, t2.a, t2.b FROM t1 AS one, t2;
|
||||
SELECT * FROM v;
|
||||
} {1 2 3 4}
|
||||
|
||||
do_catchsql_test 5.3 {
|
||||
ALTER TABLE t2 RENAME TO one;
|
||||
} {1 {error in view v after rename: ambiguous column name: one.a}}
|
||||
|
||||
do_execsql_test 5.4 {
|
||||
SELECT * FROM v
|
||||
} {1 2 3 4}
|
||||
|
||||
do_execsql_test 5.5 {
|
||||
DROP VIEW v;
|
||||
CREATE VIEW temp.vv AS SELECT one.a, one.b, t2.a, t2.b FROM t1 AS one, t2;
|
||||
SELECT * FROM vv;
|
||||
} {1 2 3 4}
|
||||
|
||||
do_catchsql_test 5.6 {
|
||||
ALTER TABLE t2 RENAME TO one;
|
||||
} {1 {error in view vv after rename: ambiguous column name: one.a}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
ifcapable vtab {
|
||||
register_tcl_module db
|
||||
proc tcl_command {method args} {
|
||||
switch -- $method {
|
||||
xConnect {
|
||||
return "CREATE TABLE t1(a, b, c)"
|
||||
}
|
||||
}
|
||||
return {}
|
||||
}
|
||||
|
||||
do_execsql_test 6.0 {
|
||||
CREATE VIRTUAL TABLE x1 USING tcl(tcl_command);
|
||||
}
|
||||
|
||||
do_execsql_test 6.1 {
|
||||
ALTER TABLE x1 RENAME TO x2;
|
||||
SELECT sql FROM sqlite_master WHERE name = 'x2'
|
||||
} {{CREATE VIRTUAL TABLE "x2" USING tcl(tcl_command)}}
|
||||
|
||||
do_execsql_test 7.1 {
|
||||
CREATE TABLE ddd(db, sql, zOld, zNew, bTemp);
|
||||
INSERT INTO ddd VALUES(
|
||||
'main', 'CREATE TABLE x1(i INTEGER, t TEXT)', 'ddd', NULL, 0
|
||||
), (
|
||||
'main', 'CREATE TABLE x1(i INTEGER, t TEXT)', NULL, 'eee', 0
|
||||
), (
|
||||
'main', NULL, 'ddd', 'eee', 0
|
||||
);
|
||||
} {}
|
||||
|
||||
sqlite3_test_control SQLITE_TESTCTRL_INTERNAL_FUNCTIONS db
|
||||
do_execsql_test 7.2 {
|
||||
SELECT
|
||||
sqlite_rename_table(db, 0, 0, sql, zOld, zNew, bTemp)
|
||||
FROM ddd;
|
||||
} {{} {} {}}
|
||||
sqlite3_test_control SQLITE_TESTCTRL_INTERNAL_FUNCTIONS db
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
forcedelete test.db2
|
||||
do_execsql_test 8.1 {
|
||||
ATTACH 'test.db2' AS aux;
|
||||
PRAGMA foreign_keys = on;
|
||||
CREATE TABLE aux.p1(a INTEGER PRIMARY KEY, b);
|
||||
CREATE TABLE aux.c1(x INTEGER PRIMARY KEY, y REFERENCES p1(a));
|
||||
INSERT INTO aux.p1 VALUES(1, 1);
|
||||
INSERT INTO aux.p1 VALUES(2, 2);
|
||||
INSERT INTO aux.c1 VALUES(NULL, 2);
|
||||
CREATE TABLE aux.c2(x INTEGER PRIMARY KEY, y REFERENCES c1(a));
|
||||
}
|
||||
|
||||
do_execsql_test 8.2 {
|
||||
ALTER TABLE aux.p1 RENAME TO ppp;
|
||||
}
|
||||
|
||||
do_execsql_test 8.2 {
|
||||
INSERT INTO aux.c1 VALUES(NULL, 1);
|
||||
SELECT sql FROM aux.sqlite_master WHERE name = 'c1';
|
||||
} {{CREATE TABLE c1(x INTEGER PRIMARY KEY, y REFERENCES "ppp"(a))}}
|
||||
|
||||
reset_db
|
||||
do_execsql_test 9.0 {
|
||||
CREATE TABLE t1(a, b, c);
|
||||
CREATE VIEW v1 AS SELECT * FROM t2;
|
||||
}
|
||||
do_catchsql_test 9.1 {
|
||||
ALTER TABLE t1 RENAME TO t3;
|
||||
} {1 {error in view v1: no such table: main.t2}}
|
||||
do_execsql_test 9.2 {
|
||||
DROP VIEW v1;
|
||||
CREATE TRIGGER tr AFTER INSERT ON t1 BEGIN
|
||||
INSERT INTO t2 VALUES(new.a);
|
||||
END;
|
||||
}
|
||||
do_catchsql_test 9.3 {
|
||||
ALTER TABLE t1 RENAME TO t3;
|
||||
} {1 {error in trigger tr: no such table: main.t2}}
|
||||
|
||||
forcedelete test.db2
|
||||
do_execsql_test 9.4 {
|
||||
DROP TRIGGER tr;
|
||||
|
||||
ATTACH 'test.db2' AS aux;
|
||||
CREATE TRIGGER tr AFTER INSERT ON t1 WHEN new.a IS NULL BEGIN SELECT 1, 2, 3; END;
|
||||
|
||||
CREATE TABLE aux.t1(x);
|
||||
CREATE TEMP TRIGGER tr AFTER INSERT ON aux.t1 BEGIN SELECT 1, 2, 3; END;
|
||||
}
|
||||
do_execsql_test 9.5 {
|
||||
ALTER TABLE main.t1 RENAME TO t3;
|
||||
}
|
||||
do_execsql_test 9.6 {
|
||||
SELECT sql FROM sqlite_temp_master;
|
||||
SELECT sql FROM sqlite_master WHERE type='trigger';
|
||||
} {
|
||||
{CREATE TRIGGER tr AFTER INSERT ON aux.t1 BEGIN SELECT 1, 2, 3; END}
|
||||
{CREATE TRIGGER tr AFTER INSERT ON "t3" WHEN new.a IS NULL BEGIN SELECT 1, 2, 3; END}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
ifcapable fts5 {
|
||||
do_execsql_test 10.0 {
|
||||
CREATE VIRTUAL TABLE fff USING fts5(x, y, z);
|
||||
}
|
||||
|
||||
do_execsql_test 10.1 {
|
||||
BEGIN;
|
||||
INSERT INTO fff VALUES('a', 'b', 'c');
|
||||
ALTER TABLE fff RENAME TO ggg;
|
||||
COMMIT;
|
||||
}
|
||||
|
||||
do_execsql_test 10.2 {
|
||||
SELECT * FROM ggg;
|
||||
} {a b c}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
forcedelete test.db2
|
||||
db func trigger trigger
|
||||
set ::trigger [list]
|
||||
proc trigger {args} {
|
||||
lappend ::trigger $args
|
||||
}
|
||||
do_execsql_test 11.0 {
|
||||
ATTACH 'test.db2' AS aux;
|
||||
CREATE TABLE aux.t1(a, b, c);
|
||||
CREATE TABLE main.t1(a, b, c);
|
||||
CREATE TEMP TRIGGER tr AFTER INSERT ON aux.t1 BEGIN
|
||||
SELECT trigger(new.a, new.b, new.c);
|
||||
END;
|
||||
}
|
||||
|
||||
do_execsql_test 11.1 {
|
||||
INSERT INTO main.t1 VALUES(1, 2, 3);
|
||||
INSERT INTO aux.t1 VALUES(4, 5, 6);
|
||||
}
|
||||
do_test 11.2 { set ::trigger } {{4 5 6}}
|
||||
|
||||
do_execsql_test 11.3 {
|
||||
SELECT name, tbl_name FROM sqlite_temp_master;
|
||||
} {tr t1}
|
||||
|
||||
do_execsql_test 11.4 {
|
||||
ALTER TABLE main.t1 RENAME TO t2;
|
||||
SELECT name, tbl_name FROM sqlite_temp_master;
|
||||
} {tr t1}
|
||||
|
||||
do_execsql_test 11.5 {
|
||||
ALTER TABLE aux.t1 RENAME TO t2;
|
||||
SELECT name, tbl_name FROM sqlite_temp_master;
|
||||
} {tr t2}
|
||||
|
||||
do_execsql_test 11.6 {
|
||||
INSERT INTO aux.t2 VALUES(7, 8, 9);
|
||||
}
|
||||
do_test 11.7 { set ::trigger } {{4 5 6} {7 8 9}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 12.0 {
|
||||
CREATE TABLE t1(a);
|
||||
CREATE TABLE t2(w);
|
||||
CREATE TRIGGER temp.r1 AFTER INSERT ON main.t2 BEGIN
|
||||
INSERT INTO t1(a) VALUES(new.w);
|
||||
END;
|
||||
CREATE TEMP TABLE t2(x);
|
||||
}
|
||||
|
||||
do_execsql_test 12.1 {
|
||||
ALTER TABLE main.t2 RENAME TO t3;
|
||||
}
|
||||
|
||||
do_execsql_test 12.2 {
|
||||
INSERT INTO t3 VALUES('WWW');
|
||||
SELECT * FROM t1;
|
||||
} {WWW}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 13.0 {
|
||||
CREATE TABLE t1(x, y);
|
||||
CREATE TABLE t2(a, b);
|
||||
CREATE TABLE log(c);
|
||||
CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN
|
||||
INSERT INTO log SELECT y FROM t1, t2;
|
||||
END;
|
||||
}
|
||||
|
||||
do_execsql_test 13.1 {
|
||||
INSERT INTO t1 VALUES(1, 2);
|
||||
}
|
||||
|
||||
do_catchsql_test 13.2 {
|
||||
ALTER TABLE t2 RENAME b TO y;
|
||||
} {1 {error in trigger tr1 after rename: ambiguous column name: y}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
|
||||
ifcapable rtree {
|
||||
do_execsql_test 14.0 {
|
||||
CREATE VIRTUAL TABLE rt USING rtree(id, minx, maxx, miny, maxy);
|
||||
|
||||
CREATE TABLE "mytable" ( "fid" INTEGER PRIMARY KEY, "geom" BLOB);
|
||||
|
||||
CREATE TRIGGER tr1 AFTER UPDATE OF "geom" ON "mytable"
|
||||
WHEN OLD."fid" = NEW."fid" AND NEW."geom" IS NULL BEGIN
|
||||
DELETE FROM rt WHERE id = OLD."fid";
|
||||
END;
|
||||
|
||||
INSERT INTO mytable VALUES(1, X'abcd');
|
||||
}
|
||||
|
||||
do_execsql_test 14.1 {
|
||||
UPDATE mytable SET geom = X'1234'
|
||||
}
|
||||
|
||||
do_execsql_test 14.2 {
|
||||
ALTER TABLE mytable RENAME TO mytable_renamed;
|
||||
}
|
||||
|
||||
do_execsql_test 14.3 {
|
||||
CREATE TRIGGER tr2 AFTER INSERT ON mytable_renamed BEGIN
|
||||
DELETE FROM rt WHERE id=(SELECT min(id) FROM rt);
|
||||
END;
|
||||
}
|
||||
|
||||
do_execsql_test 14.4 {
|
||||
ALTER TABLE mytable_renamed RENAME TO mytable2;
|
||||
}
|
||||
}
|
||||
|
||||
reset_db
|
||||
do_execsql_test 14.5 {
|
||||
CREATE TABLE t1(a, b, c);
|
||||
CREATE VIEW v1 AS SELECT * FROM t1;
|
||||
CREATE TRIGGER xyz AFTER INSERT ON t1 BEGIN
|
||||
SELECT a, b FROM v1;
|
||||
END;
|
||||
}
|
||||
do_execsql_test 14.6 {
|
||||
ALTER TABLE t1 RENAME TO tt1;
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 15.0 {
|
||||
CREATE TABLE t1(a integer NOT NULL PRIMARY KEY);
|
||||
CREATE VIEW v1 AS SELECT a FROM t1;
|
||||
CREATE TRIGGER tr1 INSTEAD OF INSERT ON v1 BEGIN
|
||||
UPDATE t1 SET a = NEW.a;
|
||||
END;
|
||||
CREATE TRIGGER tr2 INSTEAD OF INSERT ON v1 BEGIN
|
||||
SELECT new.a;
|
||||
END;
|
||||
CREATE TABLE t2 (b);
|
||||
}
|
||||
|
||||
do_execsql_test 15.1 {
|
||||
INSERT INTO v1 VALUES(1);
|
||||
ALTER TABLE t2 RENAME TO t3;
|
||||
}
|
||||
|
||||
do_execsql_test 15.2 {
|
||||
CREATE TABLE x(f1 integer NOT NULL);
|
||||
CREATE VIEW y AS SELECT f1 AS f1 FROM x;
|
||||
CREATE TRIGGER t INSTEAD OF UPDATE OF f1 ON y BEGIN
|
||||
UPDATE x SET f1 = NEW.f1;
|
||||
END;
|
||||
CREATE TABLE z (f1 integer NOT NULL PRIMARY KEY);
|
||||
ALTER TABLE z RENAME TO z2;
|
||||
}
|
||||
|
||||
do_execsql_test 15.3 {
|
||||
INSERT INTO x VALUES(1), (2), (3);
|
||||
ALTER TABLE x RENAME f1 TO f2;
|
||||
SELECT * FROM x;
|
||||
} {1 2 3}
|
||||
|
||||
do_execsql_test 15.4 {
|
||||
UPDATE y SET f1 = 'x' WHERE f1 = 1;
|
||||
SELECT * FROM x;
|
||||
} {x x x}
|
||||
|
||||
do_execsql_test 15.5 {
|
||||
SELECT sql FROM sqlite_master WHERE name = 'y';
|
||||
} {{CREATE VIEW y AS SELECT f2 AS f1 FROM x}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test that it is not possible to rename a shadow table in DEFENSIVE mode.
|
||||
#
|
||||
ifcapable fts3 {
|
||||
proc vtab_command {method args} {
|
||||
switch -- $method {
|
||||
xConnect {
|
||||
if {[info exists ::vtab_connect_sql]} {
|
||||
execsql $::vtab_connect_sql
|
||||
}
|
||||
return "CREATE TABLE t1(a, b, c)"
|
||||
}
|
||||
|
||||
xBestIndex {
|
||||
set clist [lindex $args 0]
|
||||
if {[llength $clist]!=1} { error "unexpected constraint list" }
|
||||
catch { array unset C }
|
||||
array set C [lindex $clist 0]
|
||||
if {$C(usable)} {
|
||||
return "omit 0 cost 0 rows 1 idxnum 555 idxstr eq!"
|
||||
} else {
|
||||
return "cost 1000000 rows 0 idxnum 0 idxstr scan..."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {}
|
||||
}
|
||||
|
||||
register_tcl_module db
|
||||
|
||||
sqlite3_db_config db DEFENSIVE 1
|
||||
|
||||
do_execsql_test 16.0 {
|
||||
CREATE VIRTUAL TABLE y1 USING fts3;
|
||||
VACUUM;
|
||||
}
|
||||
|
||||
do_catchsql_test 16.10 {
|
||||
INSERT INTO y1_segments VALUES(1, X'1234567890');
|
||||
} {1 {table y1_segments may not be modified}}
|
||||
|
||||
do_catchsql_test 16.20 {
|
||||
DROP TABLE y1_segments;
|
||||
} {1 {table y1_segments may not be dropped}}
|
||||
|
||||
do_catchsql_test 16.20 {
|
||||
ALTER TABLE y1_segments RENAME TO abc;
|
||||
} {1 {table y1_segments may not be altered}}
|
||||
sqlite3_db_config db DEFENSIVE 0
|
||||
do_catchsql_test 16.22 {
|
||||
ALTER TABLE y1_segments RENAME TO abc;
|
||||
} {0 {}}
|
||||
sqlite3_db_config db DEFENSIVE 1
|
||||
do_catchsql_test 16.23 {
|
||||
CREATE TABLE y1_segments AS SELECT * FROM abc;
|
||||
} {1 {object name reserved for internal use: y1_segments}}
|
||||
do_catchsql_test 16.24 {
|
||||
CREATE VIEW y1_segments AS SELECT * FROM abc;
|
||||
} {1 {object name reserved for internal use: y1_segments}}
|
||||
sqlite3_db_config db DEFENSIVE 0
|
||||
do_catchsql_test 16.25 {
|
||||
ALTER TABLE abc RENAME TO y1_segments;
|
||||
} {0 {}}
|
||||
sqlite3_db_config db DEFENSIVE 1
|
||||
|
||||
do_execsql_test 16.30 {
|
||||
ALTER TABLE y1 RENAME TO z1;
|
||||
}
|
||||
|
||||
do_execsql_test 16.40 {
|
||||
SELECT * FROM z1_segments;
|
||||
}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 17.0 {
|
||||
CREATE TABLE sqlite1234 (id integer);
|
||||
ALTER TABLE sqlite1234 RENAME TO User;
|
||||
SELECT name, sql FROM sqlite_master WHERE sql IS NOT NULL;
|
||||
} {
|
||||
User {CREATE TABLE "User" (id integer)}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 18.1.0 {
|
||||
CREATE TABLE t0 (c0 INTEGER, PRIMARY KEY(c0)) WITHOUT ROWID;
|
||||
}
|
||||
do_execsql_test 18.1.1 {
|
||||
ALTER TABLE t0 RENAME COLUMN c0 TO c1;
|
||||
}
|
||||
do_execsql_test 18.1.2 {
|
||||
SELECT sql FROM sqlite_master;
|
||||
} {{CREATE TABLE t0 (c1 INTEGER, PRIMARY KEY(c1)) WITHOUT ROWID}}
|
||||
|
||||
reset_db
|
||||
do_execsql_test 18.2.0 {
|
||||
CREATE TABLE t0 (c0 INTEGER, PRIMARY KEY(c0));
|
||||
}
|
||||
do_execsql_test 18.2.1 {
|
||||
ALTER TABLE t0 RENAME COLUMN c0 TO c1;
|
||||
}
|
||||
do_execsql_test 18.2.2 {
|
||||
SELECT sql FROM sqlite_master;
|
||||
} {{CREATE TABLE t0 (c1 INTEGER, PRIMARY KEY(c1))}}
|
||||
|
||||
# 2020-02-23 ticket f50af3e8a565776b
|
||||
reset_db
|
||||
do_execsql_test 19.100 {
|
||||
CREATE TABLE t1(x);
|
||||
CREATE VIEW t2 AS SELECT 1 FROM t1, (t1 AS a0, t1);
|
||||
ALTER TABLE t1 RENAME TO t3;
|
||||
SELECT sql FROM sqlite_master;
|
||||
} {{CREATE TABLE "t3"(x)} {CREATE VIEW t2 AS SELECT 1 FROM "t3", ("t3" AS a0, "t3")}}
|
||||
do_execsql_test 19.110 {
|
||||
INSERT INTO t3(x) VALUES(123);
|
||||
SELECT * FROM t2;
|
||||
} {1}
|
||||
do_execsql_test 19.120 {
|
||||
INSERT INTO t3(x) VALUES('xyz');
|
||||
SELECT * FROM t2;
|
||||
} {1 1 1 1 1 1 1 1}
|
||||
|
||||
# Ticket 4722bdab08cb14
|
||||
reset_db
|
||||
do_execsql_test 20.0 {
|
||||
CREATE TABLE a(a);
|
||||
CREATE VIEW b AS SELECT(SELECT *FROM c JOIN a USING(d, a, a, a) JOIN a) IN();
|
||||
}
|
||||
do_execsql_test 20.1 {
|
||||
ALTER TABLE a RENAME a TO e;
|
||||
} {}
|
||||
|
||||
reset_db
|
||||
do_execsql_test 21.0 {
|
||||
CREATE TABLE a(b);
|
||||
CREATE VIEW c AS
|
||||
SELECT NULL INTERSECT
|
||||
SELECT NULL ORDER BY
|
||||
likelihood(NULL, (d, (SELECT c)));
|
||||
} {}
|
||||
do_catchsql_test 21.1 {
|
||||
SELECT likelihood(NULL, (d, (SELECT c)));
|
||||
} {1 {second argument to likelihood() must be a constant between 0.0 and 1.0}}
|
||||
do_catchsql_test 21.2 {
|
||||
SELECT * FROM c;
|
||||
} {1 {1st ORDER BY term does not match any column in the result set}}
|
||||
|
||||
do_catchsql_test 21.3 {
|
||||
ALTER TABLE a RENAME TO e;
|
||||
} {1 {error in view c: 1st ORDER BY term does not match any column in the result set}}
|
||||
|
||||
# After forum thread https://sqlite.org/forum/forumpost/ddbe1c7efa
|
||||
# Ensure that PRAGMA schema_version=N causes a full schema reload.
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 22.0 {
|
||||
CREATE TABLE t1(a INT, b TEXT NOT NULL);
|
||||
INSERT INTO t1 VALUES(1,2),('a','b');
|
||||
BEGIN;
|
||||
PRAGMA writable_schema=ON;
|
||||
UPDATE sqlite_schema SET sql='CREATE TABLE t1(a INT, b TEXT)' WHERE name LIKE 't1';
|
||||
PRAGMA schema_version=1234;
|
||||
COMMIT;
|
||||
PRAGMA integrity_check;
|
||||
} {ok}
|
||||
do_execsql_test 22.1 {
|
||||
ALTER TABLE t1 ADD COLUMN c INT DEFAULT 78;
|
||||
SELECT * FROM t1;
|
||||
} {1 2 78 a b 78}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
db collate compare64 compare64
|
||||
|
||||
do_execsql_test 23.1 {
|
||||
CREATE TABLE gigo(a text);
|
||||
CREATE TABLE idx(x text COLLATE compare64);
|
||||
CREATE VIEW v1 AS SELECT * FROM idx WHERE x='abc';
|
||||
}
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
|
||||
do_execsql_test 23.2 {
|
||||
alter table gigo rename to ggiiggoo;
|
||||
alter table idx rename to idx2;
|
||||
}
|
||||
|
||||
do_execsql_test 23.3 {
|
||||
SELECT sql FROM sqlite_master;
|
||||
} {
|
||||
{CREATE TABLE "ggiiggoo"(a text)}
|
||||
{CREATE TABLE "idx2"(x text COLLATE compare64)}
|
||||
{CREATE VIEW v1 AS SELECT * FROM "idx2" WHERE x='abc'}
|
||||
}
|
||||
|
||||
do_execsql_test 23.4 {
|
||||
ALTER TABLE idx2 RENAME x TO y;
|
||||
SELECT sql FROM sqlite_master;
|
||||
} {
|
||||
{CREATE TABLE "ggiiggoo"(a text)}
|
||||
{CREATE TABLE "idx2"(y text COLLATE compare64)}
|
||||
{CREATE VIEW v1 AS SELECT * FROM "idx2" WHERE y='abc'}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 24.1.0 {
|
||||
CREATE TABLE t1(a, b);
|
||||
CREATE TRIGGER AFTER INSERT ON t1 BEGIN
|
||||
INSERT INTO nosuchtable VALUES(new.a) ON CONFLICT(a) DO NOTHING;
|
||||
END;
|
||||
}
|
||||
do_catchsql_test 24.1.1 {
|
||||
ALTER TABLE t1 RENAME TO t2;
|
||||
} {1 {error in trigger AFTER: no such table: main.nosuchtable}}
|
||||
|
||||
reset_db
|
||||
do_execsql_test 24.2.0 {
|
||||
CREATE TABLE t1(a, b);
|
||||
CREATE TRIGGER AFTER INSERT ON t1 BEGIN
|
||||
INSERT INTO v1 VALUES(new.a) ON CONFLICT(a) DO NOTHING;
|
||||
END;
|
||||
CREATE VIEW v1 AS SELECT * FROM nosuchtable;
|
||||
}
|
||||
do_catchsql_test 24.2.1 {
|
||||
ALTER TABLE t1 RENAME TO t2;
|
||||
} {1 {error in trigger AFTER: no such table: main.nosuchtable}}
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 25.1 {
|
||||
CREATE TABLE xx(x);
|
||||
CREATE VIEW v3(b) AS WITH b AS (SELECT b FROM (SELECT * FROM t2)) VALUES(1);
|
||||
}
|
||||
|
||||
ifcapable json1&&vtab {
|
||||
do_catchsql_test 25.2 {
|
||||
ALTER TABLE json_each RENAME TO t4;
|
||||
} {1 {table json_each may not be altered}}
|
||||
}
|
||||
|
||||
# 2021-05-01 dbsqlfuzz bc17a306a09329bba0ecc61547077f6178bcf321
|
||||
# Remove a NEVER() inserted on 2019-12-09 that is reachable after all.
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 26.1 {
|
||||
CREATE TABLE t1(k,v);
|
||||
CREATE TABLE t2_a(k,v);
|
||||
CREATE VIEW t2 AS SELECT * FROM t2_a;
|
||||
CREATE TRIGGER r2 AFTER INSERT ON t1 BEGIN
|
||||
UPDATE t1
|
||||
SET (k,v)=((WITH cte1(a) AS (SELECT 1 FROM t2) SELECT t2.k FROM t2, cte1),1);
|
||||
END;
|
||||
ALTER TABLE t1 RENAME TO t1x;
|
||||
INSERT INTO t2_a VALUES(2,3);
|
||||
INSERT INTO t1x VALUES(98,99);
|
||||
SELECT * FROM t1x;
|
||||
} {2 1}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
|
||||
do_execsql_test 27.1 {
|
||||
|
||||
create table t_sa (
|
||||
c_muyat INTEGER NOT NULL,
|
||||
c_d4u TEXT
|
||||
);
|
||||
|
||||
create table t2 ( abc );
|
||||
|
||||
CREATE TRIGGER trig AFTER DELETE ON t_sa
|
||||
BEGIN
|
||||
DELETE FROM t_sa WHERE (
|
||||
SELECT 123 FROM t2
|
||||
WINDOW oamat7fzf AS ( PARTITION BY t_sa.c_d4u )
|
||||
);
|
||||
END;
|
||||
}
|
||||
|
||||
do_execsql_test 27.2 {
|
||||
alter table t_sa rename column c_muyat to c_dg;
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 29.1 {
|
||||
CREATE TABLE t1(a, b, c);
|
||||
INSERT INTO t1 VALUES('a', 'b', 'c');
|
||||
|
||||
CREATE VIEW v0 AS
|
||||
WITH p AS ( SELECT 1 FROM t1 ),
|
||||
g AS ( SELECT 1 FROM p, t1 )
|
||||
SELECT 1 FROM g;
|
||||
}
|
||||
|
||||
do_execsql_test 29.2 {
|
||||
SELECT * FROM v0
|
||||
} 1
|
||||
|
||||
do_execsql_test 29.2 {
|
||||
ALTER TABLE t1 RENAME TO t2
|
||||
}
|
||||
|
||||
do_execsql_test 29.3 {
|
||||
SELECT sql FROM sqlite_schema WHERE name='v0'
|
||||
} {{CREATE VIEW v0 AS
|
||||
WITH p AS ( SELECT 1 FROM "t2" ),
|
||||
g AS ( SELECT 1 FROM p, "t2" )
|
||||
SELECT 1 FROM g}}
|
||||
|
||||
do_execsql_test 29.4 {
|
||||
CREATE VIEW v2 AS
|
||||
WITH p AS ( SELECT 1 FROM t2 ),
|
||||
g AS ( SELECT 1 FROM (
|
||||
WITH i AS (SELECT 1 FROM p, t2)
|
||||
SELECT * FROM i
|
||||
)
|
||||
)
|
||||
SELECT 1 FROM g;
|
||||
}
|
||||
|
||||
do_execsql_test 29.4 {
|
||||
SELECT * FROM v2;
|
||||
} 1
|
||||
|
||||
do_execsql_test 29.5 {
|
||||
ALTER TABLE t2 RENAME TO t3;
|
||||
}
|
||||
|
||||
do_execsql_test 29.5 {
|
||||
SELECT sql FROM sqlite_schema WHERE name='v2'
|
||||
} {{CREATE VIEW v2 AS
|
||||
WITH p AS ( SELECT 1 FROM "t3" ),
|
||||
g AS ( SELECT 1 FROM (
|
||||
WITH i AS (SELECT 1 FROM p, "t3")
|
||||
SELECT * FROM i
|
||||
)
|
||||
)
|
||||
SELECT 1 FROM g}}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 28.1 {
|
||||
CREATE TABLE t1(a);
|
||||
CREATE TABLE t2(b,c);
|
||||
CREATE TABLE t4(b,c);
|
||||
INSERT INTO t2 VALUES(1,2),(1,3),(2,5);
|
||||
INSERT INTO t4 VALUES(1,2),(1,3),(2,5);
|
||||
|
||||
CREATE VIEW v3 AS
|
||||
WITH RECURSIVE t3(x,y,z) AS (
|
||||
SELECT b,c,NULL FROM t4
|
||||
UNION
|
||||
SELECT x,y,NULL FROM t3, t2
|
||||
)
|
||||
SELECT * FROM t3 AS xyz;
|
||||
}
|
||||
|
||||
do_execsql_test 28.2 {
|
||||
SELECT * FROM v3
|
||||
} {
|
||||
1 2 {} 1 3 {} 2 5 {}
|
||||
}
|
||||
|
||||
do_execsql_test 28.3 {
|
||||
ALTER TABLE t1 RENAME a TO a2; -- fails in v3
|
||||
}
|
||||
|
||||
do_execsql_test 28.4 {
|
||||
ALTER TABLE t2 RENAME TO t5;
|
||||
}
|
||||
|
||||
do_execsql_test 28.5 {
|
||||
SELECT sql FROM sqlite_schema WHERE name='v3'
|
||||
} {{CREATE VIEW v3 AS
|
||||
WITH RECURSIVE t3(x,y,z) AS (
|
||||
SELECT b,c,NULL FROM t4
|
||||
UNION
|
||||
SELECT x,y,NULL FROM t3, "t5"
|
||||
)
|
||||
SELECT * FROM t3 AS xyz}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 30.0 {
|
||||
CREATE TABLE t1(a,b,c,d,e,f);
|
||||
CREATE TABLE t2(a,b,c);
|
||||
CREATE INDEX t1abc ON t1(a,b,c+d+e);
|
||||
CREATE VIEW v1(x,y) AS
|
||||
SELECT t1.b,t2.b FROM t1,t2 WHERE t1.a=t2.a
|
||||
GROUP BY 1 HAVING t2.c NOT NULL LIMIT 10;
|
||||
CREATE TRIGGER r1 AFTER INSERT ON t1 WHEN 'no' NOT NULL BEGIN
|
||||
INSERT INTO t2(a,a,b,c) VALUES(new.b,new.a,new.c-7);
|
||||
WITH c1(x) AS (
|
||||
VALUES(0)
|
||||
UNION ALL
|
||||
SELECT current_time+x FROM c1 WHERE x
|
||||
UNION ALL
|
||||
SELECT 1+x FROM c1 WHERE x<1
|
||||
), c2(x) AS (VALUES(0),(1))
|
||||
SELECT * FROM c1 AS x1, c2 AS x2, (
|
||||
SELECT x+1 FROM c1 WHERE x IS NOT TRUE
|
||||
UNION ALL
|
||||
SELECT 1+x FROM c1 WHERE 1<x
|
||||
) AS x3, c2 x5;
|
||||
END;
|
||||
}
|
||||
|
||||
do_execsql_test 30.1 {
|
||||
ALTER TABLE t1 RENAME TO t1x;
|
||||
}
|
||||
|
||||
do_execsql_test 30.2 {
|
||||
SELECT sql FROM sqlite_schema ORDER BY rowid
|
||||
} {
|
||||
{CREATE TABLE "t1x"(a,b,c,d,e,f)}
|
||||
{CREATE TABLE t2(a,b,c)}
|
||||
{CREATE INDEX t1abc ON "t1x"(a,b,c+d+e)}
|
||||
{CREATE VIEW v1(x,y) AS
|
||||
SELECT "t1x".b,t2.b FROM "t1x",t2 WHERE "t1x".a=t2.a
|
||||
GROUP BY 1 HAVING t2.c NOT NULL LIMIT 10}
|
||||
{CREATE TRIGGER r1 AFTER INSERT ON "t1x" WHEN 'no' NOT NULL BEGIN
|
||||
INSERT INTO t2(a,a,b,c) VALUES(new.b,new.a,new.c-7);
|
||||
WITH c1(x) AS (
|
||||
VALUES(0)
|
||||
UNION ALL
|
||||
SELECT current_time+x FROM c1 WHERE x
|
||||
UNION ALL
|
||||
SELECT 1+x FROM c1 WHERE x<1
|
||||
), c2(x) AS (VALUES(0),(1))
|
||||
SELECT * FROM c1 AS x1, c2 AS x2, (
|
||||
SELECT x+1 FROM c1 WHERE x IS NOT TRUE
|
||||
UNION ALL
|
||||
SELECT 1+x FROM c1 WHERE 1<x
|
||||
) AS x3, c2 x5;
|
||||
END}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 31.0 {
|
||||
CREATE TABLE t1(q);
|
||||
CREATE VIEW vvv AS WITH x AS (WITH y AS (SELECT * FROM x) SELECT 1) SELECT 1;
|
||||
}
|
||||
|
||||
do_execsql_test 31.1 {
|
||||
SELECT * FROM vvv;
|
||||
} {1}
|
||||
|
||||
do_execsql_test 31.2 {
|
||||
ALTER TABLE t1 RENAME TO t1x;
|
||||
}
|
||||
|
||||
do_execsql_test 31.3 {
|
||||
ALTER TABLE t1x RENAME q TO x;
|
||||
}
|
||||
|
||||
# 2021-07-02 OSSFuzz https://oss-fuzz.com/testcase-detail/5517690440646656
|
||||
# Bad assert() statement
|
||||
#
|
||||
reset_db
|
||||
do_catchsql_test 32.0 {
|
||||
CREATE TABLE t1(x);
|
||||
CREATE TRIGGER r1 BEFORE INSERT ON t1 BEGIN
|
||||
UPDATE t1 SET x=x FROM (SELECT*);
|
||||
END;
|
||||
ALTER TABLE t1 RENAME TO x;
|
||||
} {1 {error in trigger r1: no tables specified}}
|
||||
|
||||
finish_test
|
363
testdata/tcl/altertab2.test
vendored
Normal file
363
testdata/tcl/altertab2.test
vendored
Normal file
|
@ -0,0 +1,363 @@
|
|||
# 2018 September 30
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#*************************************************************************
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix altertab2
|
||||
|
||||
# If SQLITE_OMIT_ALTERTABLE is defined, omit this file.
|
||||
ifcapable !altertable {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
ifcapable fts5 {
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE rr(a, b);
|
||||
CREATE VIRTUAL TABLE ff USING fts5(a, b);
|
||||
CREATE TRIGGER tr1 AFTER INSERT ON rr BEGIN
|
||||
INSERT INTO ff VALUES(new.a, new.b);
|
||||
END;
|
||||
INSERT INTO rr VALUES('hello', 'world');
|
||||
SELECT * FROM ff;
|
||||
} {hello world}
|
||||
|
||||
do_execsql_test 1.1 {
|
||||
ALTER TABLE ff RENAME TO ffff;
|
||||
}
|
||||
|
||||
do_execsql_test 1.2 {
|
||||
INSERT INTO rr VALUES('in', 'tcl');
|
||||
SELECT * FROM ffff;
|
||||
} {hello world in tcl}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Check that table names that appear in REFERENCES clauses are updated
|
||||
# when a table is renamed unless:
|
||||
#
|
||||
# a) "PRAGMA legacy_alter_table" is true, and
|
||||
# b) "PRAGMA foreign_keys" is false.
|
||||
#
|
||||
do_execsql_test 2.0 {
|
||||
CREATE TABLE p1(a PRIMARY KEY, b);
|
||||
CREATE TABLE c1(x REFERENCES p1);
|
||||
CREATE TABLE c2(x, FOREIGN KEY (x) REFERENCES p1);
|
||||
CREATE TABLE c3(x, FOREIGN KEY (x) REFERENCES p1(a));
|
||||
}
|
||||
|
||||
do_execsql_test 2.1 {
|
||||
ALTER TABLE p1 RENAME TO p2;
|
||||
SELECT sql FROM sqlite_master WHERE name LIKE 'c%';
|
||||
} {
|
||||
{CREATE TABLE c1(x REFERENCES "p2")}
|
||||
{CREATE TABLE c2(x, FOREIGN KEY (x) REFERENCES "p2")}
|
||||
{CREATE TABLE c3(x, FOREIGN KEY (x) REFERENCES "p2"(a))}
|
||||
}
|
||||
|
||||
do_execsql_test 2.2 {
|
||||
PRAGMA legacy_alter_table = 1;
|
||||
ALTER TABLE p2 RENAME TO p3;
|
||||
SELECT sql FROM sqlite_master WHERE name LIKE 'c%';
|
||||
} {
|
||||
{CREATE TABLE c1(x REFERENCES "p2")}
|
||||
{CREATE TABLE c2(x, FOREIGN KEY (x) REFERENCES "p2")}
|
||||
{CREATE TABLE c3(x, FOREIGN KEY (x) REFERENCES "p2"(a))}
|
||||
}
|
||||
|
||||
do_execsql_test 2.3 {
|
||||
ALTER TABLE p3 RENAME TO p2;
|
||||
PRAGMA foreign_keys = 1;
|
||||
ALTER TABLE p2 RENAME TO p3;
|
||||
SELECT sql FROM sqlite_master WHERE name LIKE 'c%';
|
||||
} {
|
||||
{CREATE TABLE c1(x REFERENCES "p3")}
|
||||
{CREATE TABLE c2(x, FOREIGN KEY (x) REFERENCES "p3")}
|
||||
{CREATE TABLE c3(x, FOREIGN KEY (x) REFERENCES "p3"(a))}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Table name in WITH clauses that are part of views or triggers.
|
||||
#
|
||||
foreach {tn schema} {
|
||||
1 {
|
||||
CREATE TABLE log_entry(col1, y);
|
||||
CREATE INDEX i1 ON log_entry(col1);
|
||||
}
|
||||
|
||||
2 {
|
||||
CREATE TABLE t1(a, b, c);
|
||||
CREATE TABLE t2(x);
|
||||
CREATE TABLE log_entry(col1);
|
||||
CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN
|
||||
INSERT INTO t2 SELECT col1 FROM log_entry;
|
||||
END;
|
||||
}
|
||||
|
||||
3 {
|
||||
CREATE TABLE t1(a, b, c);
|
||||
CREATE TABLE t2(x);
|
||||
CREATE TABLE log_entry(col1);
|
||||
CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN
|
||||
INSERT INTO t2
|
||||
WITH xyz(x) AS (SELECT col1 FROM log_entry)
|
||||
SELECT x FROM xyz;
|
||||
END;
|
||||
}
|
||||
|
||||
4 {
|
||||
CREATE TABLE log_entry(col1);
|
||||
CREATE VIEW ttt AS
|
||||
WITH xyz(x) AS (SELECT col1 FROM log_entry)
|
||||
SELECT x FROM xyz;
|
||||
}
|
||||
} {
|
||||
reset_db
|
||||
do_execsql_test 3.$tn.1 $schema
|
||||
set expect [db eval "SELECT sql FROM sqlite_master"]
|
||||
set expect [string map {log_entry {"newname"}} $expect]
|
||||
|
||||
do_execsql_test 3.$tn.2 {
|
||||
ALTER TABLE log_entry RENAME TO newname;
|
||||
SELECT sql FROM sqlite_master;
|
||||
} $expect
|
||||
|
||||
reset_db
|
||||
do_execsql_test 3.$tn.3 $schema
|
||||
set expect [db eval "SELECT sql FROM sqlite_master"]
|
||||
set expect [string map {col1 newname} $expect]
|
||||
|
||||
do_execsql_test 3.$tn.4 {
|
||||
ALTER TABLE log_entry RENAME col1 TO newname;
|
||||
SELECT sql FROM sqlite_master;
|
||||
} $expect
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 4.0 {
|
||||
CREATE TABLE t1(a,b,c,d,e,f);
|
||||
CREATE TRIGGER r1 AFTER INSERT ON t1 WHEN new.a NOT NULL BEGIN
|
||||
UPDATE t1 SET (c,d)=(a,b);
|
||||
END;
|
||||
}
|
||||
|
||||
do_execsql_test 4.1 {
|
||||
ALTER TABLE t1 RENAME TO t1x;
|
||||
SELECT sql FROM sqlite_master WHERE type = 'trigger';
|
||||
} {
|
||||
{CREATE TRIGGER r1 AFTER INSERT ON "t1x" WHEN new.a NOT NULL BEGIN
|
||||
UPDATE "t1x" SET (c,d)=(a,b);
|
||||
END}
|
||||
}
|
||||
|
||||
do_execsql_test 4.2 {
|
||||
ALTER TABLE t1x RENAME a TO aaa;
|
||||
SELECT sql FROM sqlite_master WHERE type = 'trigger';
|
||||
} {
|
||||
{CREATE TRIGGER r1 AFTER INSERT ON "t1x" WHEN new.aaa NOT NULL BEGIN
|
||||
UPDATE "t1x" SET (c,d)=(aaa,b);
|
||||
END}
|
||||
}
|
||||
|
||||
do_execsql_test 4.3 {
|
||||
ALTER TABLE t1x RENAME d TO ddd;
|
||||
SELECT sql FROM sqlite_master WHERE type = 'trigger';
|
||||
} {
|
||||
{CREATE TRIGGER r1 AFTER INSERT ON "t1x" WHEN new.aaa NOT NULL BEGIN
|
||||
UPDATE "t1x" SET (c,ddd)=(aaa,b);
|
||||
END}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
ifcapable windowfunc {
|
||||
do_execsql_test 5.0 {
|
||||
CREATE TABLE t2(a);
|
||||
CREATE TRIGGER r2 AFTER INSERT ON t2 WHEN new.a NOT NULL BEGIN
|
||||
SELECT a, sum(a) OVER w1 FROM t2
|
||||
WINDOW w1 AS (
|
||||
PARTITION BY a ORDER BY a
|
||||
ROWS BETWEEN 2 PRECEDING AND 3 FOLLOWING
|
||||
),
|
||||
w2 AS (
|
||||
PARTITION BY a
|
||||
ORDER BY rowid ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
|
||||
);
|
||||
END;
|
||||
} {}
|
||||
|
||||
do_execsql_test 5.0.1 {
|
||||
INSERT INTO t2 VALUES(1);
|
||||
} {}
|
||||
|
||||
do_execsql_test 5.1 {
|
||||
ALTER TABLE t2 RENAME TO t2x;
|
||||
SELECT sql FROM sqlite_master WHERE name = 'r2';
|
||||
} {
|
||||
{CREATE TRIGGER r2 AFTER INSERT ON "t2x" WHEN new.a NOT NULL BEGIN
|
||||
SELECT a, sum(a) OVER w1 FROM "t2x"
|
||||
WINDOW w1 AS (
|
||||
PARTITION BY a ORDER BY a
|
||||
ROWS BETWEEN 2 PRECEDING AND 3 FOLLOWING
|
||||
),
|
||||
w2 AS (
|
||||
PARTITION BY a
|
||||
ORDER BY rowid ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
|
||||
);
|
||||
END}
|
||||
}
|
||||
|
||||
do_execsql_test 5.2 {
|
||||
ALTER TABLE t2x RENAME a TO aaaa;
|
||||
SELECT sql FROM sqlite_master WHERE name = 'r2';
|
||||
} {
|
||||
{CREATE TRIGGER r2 AFTER INSERT ON "t2x" WHEN new.aaaa NOT NULL BEGIN
|
||||
SELECT aaaa, sum(aaaa) OVER w1 FROM "t2x"
|
||||
WINDOW w1 AS (
|
||||
PARTITION BY aaaa ORDER BY aaaa
|
||||
ROWS BETWEEN 2 PRECEDING AND 3 FOLLOWING
|
||||
),
|
||||
w2 AS (
|
||||
PARTITION BY aaaa
|
||||
ORDER BY rowid ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
|
||||
);
|
||||
END}
|
||||
}
|
||||
|
||||
do_execsql_test 5.3 {
|
||||
INSERT INTO t2x VALUES(1);
|
||||
} {}
|
||||
} ;# windowfunc
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
do_execsql_test 6.0 {
|
||||
CREATE TABLE t3(a,b,c,d);
|
||||
CREATE TRIGGER r3 AFTER INSERT ON t3 WHEN new.a NOT NULL BEGIN
|
||||
SELECT a,b,c FROM t3 EXCEPT SELECT a,b,c FROM t3 ORDER BY a;
|
||||
SELECT rowid, * FROM t3;
|
||||
END;
|
||||
} {}
|
||||
|
||||
do_execsql_test 6.1 {
|
||||
ALTER TABLE t3 RENAME TO t3x;
|
||||
SELECT sql FROM sqlite_master WHERE name = 'r3';
|
||||
} {
|
||||
{CREATE TRIGGER r3 AFTER INSERT ON "t3x" WHEN new.a NOT NULL BEGIN
|
||||
SELECT a,b,c FROM "t3x" EXCEPT SELECT a,b,c FROM "t3x" ORDER BY a;
|
||||
SELECT rowid, * FROM "t3x";
|
||||
END}
|
||||
}
|
||||
|
||||
do_execsql_test 6.2 {
|
||||
ALTER TABLE t3x RENAME a TO abcd;
|
||||
SELECT sql FROM sqlite_master WHERE name = 'r3';
|
||||
} {
|
||||
{CREATE TRIGGER r3 AFTER INSERT ON "t3x" WHEN new.abcd NOT NULL BEGIN
|
||||
SELECT abcd,b,c FROM "t3x" EXCEPT SELECT abcd,b,c FROM "t3x" ORDER BY abcd;
|
||||
SELECT rowid, * FROM "t3x";
|
||||
END}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
|
||||
do_execsql_test 7.0 {
|
||||
CREATE TABLE t1(a,b,c,d,e,f);
|
||||
INSERT INTO t1 VALUES(1,2,3,4,5,6);
|
||||
CREATE TABLE t2(x,y,z);
|
||||
}
|
||||
|
||||
do_execsql_test 7.1 {
|
||||
SELECT a,b,c FROM t1 UNION SELECT d,e,f FROM t1 ORDER BY b,c;
|
||||
} {1 2 3 4 5 6}
|
||||
|
||||
do_execsql_test 7.2 {
|
||||
CREATE TRIGGER r1 AFTER INSERT ON t1 BEGIN
|
||||
INSERT INTO t2
|
||||
SELECT a,b,c FROM t1 UNION SELECT d,e,f FROM t1 ORDER BY b,c;
|
||||
END;
|
||||
INSERT INTO t1 VALUES(2,3,4,5,6,7);
|
||||
SELECT * FROM t2;
|
||||
} {1 2 3 2 3 4 4 5 6 5 6 7}
|
||||
|
||||
do_execsql_test 7.3 {
|
||||
ALTER TABLE t1 RENAME TO xyzzy;
|
||||
SELECT sql FROM sqlite_master WHERE name='r1'
|
||||
} {
|
||||
{CREATE TRIGGER r1 AFTER INSERT ON "xyzzy" BEGIN
|
||||
INSERT INTO t2
|
||||
SELECT a,b,c FROM "xyzzy" UNION SELECT d,e,f FROM "xyzzy" ORDER BY b,c;
|
||||
END}
|
||||
}
|
||||
|
||||
do_execsql_test 7.3 {
|
||||
ALTER TABLE xyzzy RENAME c TO ccc;
|
||||
SELECT sql FROM sqlite_master WHERE name='r1'
|
||||
} {
|
||||
{CREATE TRIGGER r1 AFTER INSERT ON "xyzzy" BEGIN
|
||||
INSERT INTO t2
|
||||
SELECT a,b,ccc FROM "xyzzy" UNION SELECT d,e,f FROM "xyzzy" ORDER BY b,ccc;
|
||||
END}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 8.0 {
|
||||
CREATE TABLE t1(a, b, c);
|
||||
CREATE TABLE t2(a, b, c);
|
||||
CREATE TABLE t3(d, e, f);
|
||||
CREATE VIEW v1 AS SELECT * FROM t1;
|
||||
CREATE TRIGGER tr AFTER INSERT ON t3 BEGIN
|
||||
UPDATE t2 SET a = new.d;
|
||||
SELECT a, b, c FROM v1;
|
||||
END;
|
||||
}
|
||||
|
||||
do_execsql_test 8.1 {
|
||||
INSERT INTO t3 VALUES(1, 2, 3);
|
||||
}
|
||||
|
||||
# The following ALTER TABLE fails as if column "t1.a" is renamed the "a"
|
||||
# in the "SELECT a, b, c FROM v1" within the trigger can no longer be
|
||||
# resolved. But at one point there was a bug allowing the ALTER TABLE
|
||||
# succeed. Which meant the subsequent INSERT statement would fail.
|
||||
do_catchsql_test 8.2 {
|
||||
ALTER TABLE t1 RENAME a TO aaa;
|
||||
} {1 {error in trigger tr after rename: no such column: a}}
|
||||
do_execsql_test 8.3 {
|
||||
INSERT INTO t3 VALUES(4, 5, 6);
|
||||
}
|
||||
|
||||
do_execsql_test 8.4 {
|
||||
CREATE TABLE t4(a, b);
|
||||
CREATE VIEW v4 AS SELECT * FROM t4 WHERE (a=1 AND 0) OR b=2;
|
||||
}
|
||||
|
||||
# Branches of an expression tree that are optimized out by the AND
|
||||
# optimization are renamed.
|
||||
#
|
||||
do_execsql_test 8.5 {
|
||||
ALTER TABLE t4 RENAME a TO c;
|
||||
SELECT sql FROM sqlite_master WHERE name = 'v4'
|
||||
} {{CREATE VIEW v4 AS SELECT * FROM t4 WHERE (c=1 AND 0) OR b=2}}
|
||||
|
||||
# 2019-06-10 https://www.sqlite.org/src/info/533010b8cacebe82
|
||||
reset_db
|
||||
do_catchsql_test 8.6 {
|
||||
CREATE TABLE t0(c0);
|
||||
CREATE INDEX i0 ON t0(likelihood(1,2) AND 0);
|
||||
ALTER TABLE t0 RENAME TO t1;
|
||||
SELECT sql FROM sqlite_master WHERE name='i0';
|
||||
} {1 {error in index i0: second argument to likelihood() must be a constant between 0.0 and 1.0}}
|
||||
|
||||
finish_test
|
739
testdata/tcl/altertab3.test
vendored
Normal file
739
testdata/tcl/altertab3.test
vendored
Normal file
|
@ -0,0 +1,739 @@
|
|||
# 2019 January 23
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#*************************************************************************
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix altertab3
|
||||
|
||||
# If SQLITE_OMIT_ALTERTABLE is defined, omit this file.
|
||||
ifcapable !altertable {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
ifcapable windowfunc {
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE t1(a, b);
|
||||
CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN
|
||||
SELECT sum(b) OVER w FROM t1 WINDOW w AS (ORDER BY a);
|
||||
END;
|
||||
}
|
||||
|
||||
do_execsql_test 1.1 {
|
||||
ALTER TABLE t1 RENAME a TO aaa;
|
||||
}
|
||||
|
||||
do_execsql_test 1.2 {
|
||||
SELECT sql FROM sqlite_master WHERE name='tr1'
|
||||
} {{CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN
|
||||
SELECT sum(b) OVER w FROM t1 WINDOW w AS (ORDER BY aaa);
|
||||
END}}
|
||||
|
||||
do_execsql_test 1.3 {
|
||||
INSERT INTO t1 VALUES(1, 2);
|
||||
}
|
||||
} ;# windowfunc
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 2.0 {
|
||||
CREATE TABLE t1(a,b,c);
|
||||
CREATE TABLE t2(a,b,c);
|
||||
CREATE TRIGGER r1 AFTER INSERT ON t1 WHEN new.a NOT NULL BEGIN
|
||||
SELECT a,b, a name FROM t1
|
||||
INTERSECT
|
||||
SELECT a,b,c FROM t1 WHERE b>='d' ORDER BY name;
|
||||
SELECT new.c;
|
||||
END;
|
||||
}
|
||||
|
||||
do_execsql_test 2.1 {
|
||||
ALTER TABLE t1 RENAME TO t1x;
|
||||
SELECT sql FROM sqlite_master WHERE name = 'r1';
|
||||
} {{CREATE TRIGGER r1 AFTER INSERT ON "t1x" WHEN new.a NOT NULL BEGIN
|
||||
SELECT a,b, a name FROM "t1x"
|
||||
INTERSECT
|
||||
SELECT a,b,c FROM "t1x" WHERE b>='d' ORDER BY name;
|
||||
SELECT new.c;
|
||||
END}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 3.0 {
|
||||
CREATE TABLE t1(a, b, c, d);
|
||||
CREATE VIEW v1 AS SELECT * FROM t1 WHERE a=1 OR (b IN ());
|
||||
}
|
||||
|
||||
do_execsql_test 3.1 {
|
||||
ALTER TABLE t1 RENAME b TO bbb;
|
||||
}
|
||||
|
||||
do_execsql_test 3.2 {
|
||||
SELECT sql FROM sqlite_master WHERE name = 'v1'
|
||||
} {{CREATE VIEW v1 AS SELECT * FROM t1 WHERE a=1 OR (b IN ())}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 4.0 {
|
||||
CREATE TABLE t1(a, b);
|
||||
CREATE TABLE t3(e, f);
|
||||
CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN
|
||||
INSERT INTO t2 VALUES(new.a, new.b);
|
||||
END;
|
||||
}
|
||||
|
||||
do_catchsql_test 4.1.2 {
|
||||
BEGIN;
|
||||
ALTER TABLE t3 RENAME TO t4;
|
||||
} {1 {error in trigger tr1: no such table: main.t2}}
|
||||
do_execsql_test 4.1.2 {
|
||||
COMMIT;
|
||||
}
|
||||
do_execsql_test 4.1.3 {
|
||||
SELECT type, name, tbl_name, sql
|
||||
FROM sqlite_master WHERE type='table' AND name!='t1';
|
||||
} {table t3 t3 {CREATE TABLE t3(e, f)}}
|
||||
|
||||
|
||||
do_catchsql_test 4.2.1 {
|
||||
BEGIN;
|
||||
ALTER TABLE t3 RENAME e TO eee;
|
||||
} {1 {error in trigger tr1: no such table: main.t2}}
|
||||
do_execsql_test 4.2.2 {
|
||||
COMMIT;
|
||||
}
|
||||
do_execsql_test 4.2.3 {
|
||||
SELECT type, name, tbl_name, sql
|
||||
FROM sqlite_master WHERE type='table' AND name!='t1';
|
||||
} {table t3 t3 {CREATE TABLE t3(e, f)}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 5.0 {
|
||||
CREATE TABLE t1 (
|
||||
c1 integer, c2, PRIMARY KEY(c1 collate rtrim),
|
||||
UNIQUE(c2)
|
||||
)
|
||||
}
|
||||
do_execsql_test 5.1 {
|
||||
ALTER TABLE t1 RENAME c1 TO c3;
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 6.0 {
|
||||
CREATE TEMPORARY TABLE Table0 (
|
||||
Col0 INTEGER,
|
||||
PRIMARY KEY(Col0 COLLATE RTRIM),
|
||||
FOREIGN KEY (Col0) REFERENCES Table0
|
||||
);
|
||||
}
|
||||
|
||||
do_execsql_test 6.1 {
|
||||
ALTER TABLE Table0 RENAME Col0 TO Col0;
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 7.1.0 {
|
||||
CREATE TABLE t1(a,b,c);
|
||||
CREATE TRIGGER AFTER INSERT ON t1 BEGIN
|
||||
SELECT a, rank() OVER w1 FROM t1
|
||||
WINDOW w1 AS (PARTITION BY b, percent_rank() OVER w1);
|
||||
END;
|
||||
}
|
||||
|
||||
do_execsql_test 7.1.2 {
|
||||
ALTER TABLE t1 RENAME TO t1x;
|
||||
SELECT sql FROM sqlite_master;
|
||||
} {
|
||||
{CREATE TABLE "t1x"(a,b,c)}
|
||||
{CREATE TRIGGER AFTER INSERT ON "t1x" BEGIN
|
||||
SELECT a, rank() OVER w1 FROM "t1x"
|
||||
WINDOW w1 AS (PARTITION BY b, percent_rank() OVER w1);
|
||||
END}
|
||||
}
|
||||
|
||||
do_execsql_test 7.2.1 {
|
||||
DROP TRIGGER after;
|
||||
CREATE TRIGGER AFTER INSERT ON t1x BEGIN
|
||||
SELECT a, rank() OVER w1 FROM t1x
|
||||
WINDOW w1 AS (PARTITION BY b, percent_rank() OVER w1 ORDER BY d);
|
||||
END;
|
||||
}
|
||||
|
||||
do_catchsql_test 7.2.2 {
|
||||
ALTER TABLE t1x RENAME TO t1;
|
||||
} {1 {error in trigger AFTER: no such column: d}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 8.0 {
|
||||
CREATE TABLE t0(c0);
|
||||
CREATE INDEX i0 ON t0('1' IN ());
|
||||
}
|
||||
do_execsql_test 8.1 {
|
||||
ALTER TABLE t0 RENAME TO t1;
|
||||
SELECT sql FROM sqlite_master;
|
||||
} {
|
||||
{CREATE TABLE "t1"(c0)}
|
||||
{CREATE INDEX i0 ON "t1"('1' IN ())}
|
||||
}
|
||||
do_execsql_test 8.2.1 {
|
||||
CREATE TABLE t2 (c0);
|
||||
CREATE INDEX i2 ON t2((LIKELIHOOD(c0, 100) IN ()));
|
||||
ALTER TABLE t2 RENAME COLUMN c0 TO c1;
|
||||
}
|
||||
do_execsql_test 8.2.2 {
|
||||
SELECT sql FROM sqlite_master WHERE tbl_name = 't2';
|
||||
} {
|
||||
{CREATE TABLE t2 (c1)}
|
||||
{CREATE INDEX i2 ON t2((LIKELIHOOD(c0, 100) IN ()))}
|
||||
}
|
||||
do_test 8.2.3 {
|
||||
sqlite3 db2 test.db
|
||||
db2 eval { INSERT INTO t2 VALUES (1), (2), (3) }
|
||||
db close
|
||||
} {}
|
||||
db2 close
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 9.1 {
|
||||
CREATE TABLE t1(a,b,c);
|
||||
CREATE TRIGGER AFTER INSERT ON t1 WHEN new.a NOT NULL BEGIN
|
||||
SELECT true WHERE (SELECT a, b FROM (t1)) IN ();
|
||||
END;
|
||||
}
|
||||
do_execsql_test 9.2 {
|
||||
ALTER TABLE t1 RENAME TO t1x;
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 10.1 {
|
||||
CREATE TABLE t1(a, b, c);
|
||||
CREATE TABLE t2(a, b, c);
|
||||
CREATE VIEW v1 AS SELECT * FROM t1 WHERE (
|
||||
SELECT t1.a FROM t1, t2
|
||||
) IN () OR t1.a=5;
|
||||
}
|
||||
|
||||
do_execsql_test 10.2 {
|
||||
ALTER TABLE t2 RENAME TO t3;
|
||||
SELECT sql FROM sqlite_master WHERE name='v1';
|
||||
} {
|
||||
{CREATE VIEW v1 AS SELECT * FROM t1 WHERE (
|
||||
SELECT t1.a FROM t1, t2
|
||||
) IN () OR t1.a=5}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 11.1 {
|
||||
CREATE TABLE t1(
|
||||
a,b,c,d,e,f,g,h,j,jj,jjb,k,aa,bb,cc,dd,ee DEFAULT 3.14,
|
||||
ff DEFAULT('hiccup'),Wg NOD NULL DEFAULT(false)
|
||||
);
|
||||
|
||||
CREATE TRIGGER b AFTER INSERT ON t1 WHEN new.a BEGIN
|
||||
SELECT a, sum() w3 FROM t1
|
||||
WINDOW b AS (ORDER BY NOT EXISTS(SELECT 1 FROM abc));
|
||||
END;
|
||||
}
|
||||
|
||||
do_catchsql_test 11.2 {
|
||||
ALTER TABLE t1 RENAME TO t1x;
|
||||
} {1 {error in trigger b: no such table: main.abc}}
|
||||
|
||||
do_execsql_test 11.3 {
|
||||
DROP TRIGGER b;
|
||||
CREATE TRIGGER b AFTER INSERT ON t1 WHEN new.a BEGIN
|
||||
SELECT a, sum() w3 FROM t1
|
||||
WINDOW b AS (ORDER BY NOT EXISTS(SELECT 1 FROM t1));
|
||||
END;
|
||||
} {}
|
||||
|
||||
do_execsql_test 11.4 {
|
||||
ALTER TABLE t1 RENAME TO t1x;
|
||||
SELECT sql FROM sqlite_master WHERE name = 'b';
|
||||
} {
|
||||
{CREATE TRIGGER b AFTER INSERT ON "t1x" WHEN new.a BEGIN
|
||||
SELECT a, sum() w3 FROM "t1x"
|
||||
WINDOW b AS (ORDER BY NOT EXISTS(SELECT 1 FROM "t1x"));
|
||||
END}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 12.1 {
|
||||
CREATE TABLE t1(a,b,c,d,e,f,g,h,j,jj,Zjj,k,aQ,bb,cc,dd,ee DEFAULT 3.14,
|
||||
ff DEFAULT('hiccup'),gg NOD NULL DEFAULT(false));
|
||||
CREATE TRIGGER AFTER INSERT ON t1 WHEN new.a NOT NULL BEGIN
|
||||
|
||||
SELECT b () OVER , dense_rank() OVER d, d () OVER w1
|
||||
FROM t1
|
||||
WINDOW
|
||||
w1 AS
|
||||
( w1 ORDER BY d
|
||||
ROWS BETWEEN 2 NOT IN(SELECT a, sum(d) w2,max(d)OVER FROM t1
|
||||
WINDOW
|
||||
w1 AS
|
||||
(PARTITION BY d
|
||||
ROWS BETWEEN '' PRECEDING AND false FOLLOWING),
|
||||
d AS
|
||||
(PARTITION BY b ORDER BY d
|
||||
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
|
||||
) PRECEDING AND 1 FOLLOWING),
|
||||
w2 AS
|
||||
(PARTITION BY b ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW),
|
||||
w3 AS
|
||||
(PARTITION BY b ORDER BY d
|
||||
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
|
||||
;
|
||||
SELECT a, sum(d) w2,max(d)OVER FROM t1
|
||||
WINDOW
|
||||
w1 AS
|
||||
(PARTITION BY d
|
||||
ROWS BETWEEN '' PRECEDING AND false FOLLOWING),
|
||||
d AS
|
||||
(PARTITION BY b ORDER BY d
|
||||
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
|
||||
;
|
||||
|
||||
END;
|
||||
}
|
||||
|
||||
do_execsql_test 12.2 {
|
||||
ALTER TABLE t1 RENAME TO t1x;
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 13.1 {
|
||||
CREATE TABLE t1(a);
|
||||
CREATE TRIGGER r1 INSERT ON t1 BEGIN
|
||||
SELECT a(*) OVER (ORDER BY (SELECT 1)) FROM t1;
|
||||
END;
|
||||
}
|
||||
|
||||
do_execsql_test 13.2 {
|
||||
ALTER TABLE t1 RENAME TO t1x;
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 14.1 {
|
||||
CREATE TABLE t1(a);
|
||||
CREATE TABLE t2(b);
|
||||
CREATE TRIGGER AFTER INSERT ON t1 BEGIN
|
||||
SELECT sum() FILTER (WHERE (SELECT sum() FILTER (WHERE 0)) AND a);
|
||||
END;
|
||||
}
|
||||
|
||||
do_catchsql_test 14.2 {
|
||||
ALTER TABLE t1 RENAME TO t1x;
|
||||
} {1 {error in trigger AFTER: no such column: a}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
|
||||
do_execsql_test 16.1 {
|
||||
CREATE TABLE t1(x);
|
||||
CREATE TRIGGER AFTER INSERT ON t1 BEGIN
|
||||
SELECT (WITH t2 AS (WITH t3 AS (SELECT true)
|
||||
SELECT * FROM t3 ORDER BY true COLLATE nocase)
|
||||
SELECT 11);
|
||||
|
||||
WITH t4 AS (SELECT * FROM t1) SELECT 33;
|
||||
END;
|
||||
}
|
||||
do_execsql_test 16.2 {
|
||||
ALTER TABLE t1 RENAME TO t1x;
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 17.1 {
|
||||
CREATE TABLE t1(a,b,c);
|
||||
CREATE TRIGGER AFTER INSERT ON t1 WHEN new.a NOT NULL BEGIN
|
||||
SELECT a () FILTER (WHERE a>0) FROM t1;
|
||||
END;
|
||||
}
|
||||
|
||||
do_execsql_test 17.2 {
|
||||
ALTER TABLE t1 RENAME TO t1x;
|
||||
ALTER TABLE t1x RENAME a TO aaa;
|
||||
SELECT sql FROM sqlite_master WHERE type='trigger';
|
||||
} {
|
||||
{CREATE TRIGGER AFTER INSERT ON "t1x" WHEN new.aaa NOT NULL BEGIN
|
||||
SELECT a () FILTER (WHERE aaa>0) FROM "t1x";
|
||||
END}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 18.1 {
|
||||
CREATE TABLE t1(a,b);
|
||||
CREATE TRIGGER r1 AFTER INSERT ON t1 BEGIN
|
||||
SELECT a, b FROM t1
|
||||
INTERSECT SELECT b,a FROM t1
|
||||
ORDER BY b IN (
|
||||
SELECT a UNION SELECT b
|
||||
FROM t1
|
||||
ORDER BY b COLLATE nocase
|
||||
)
|
||||
;
|
||||
END;
|
||||
}
|
||||
|
||||
do_catchsql_test 18.2 {
|
||||
SELECT a, b FROM t1
|
||||
INTERSECT
|
||||
SELECT b,a FROM t1
|
||||
ORDER BY b IN (
|
||||
SELECT a UNION SELECT b
|
||||
FROM t1
|
||||
ORDER BY b COLLATE nocase
|
||||
);
|
||||
} {1 {1st ORDER BY term does not match any column in the result set}}
|
||||
|
||||
do_catchsql_test 18.3 {
|
||||
ALTER TABLE t1 RENAME TO t1x;
|
||||
} {1 {error in trigger r1: 1st ORDER BY term does not match any column in the result set}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 19.0 {
|
||||
CREATE TABLE a(a,h CONSTRAINT a UNIQUE ON CONFLICT FAIL,CONSTRAINT a);
|
||||
}
|
||||
|
||||
foreach {tn v res} {
|
||||
1 {
|
||||
CREATE VIEW q AS SELECT 123
|
||||
|
||||
WINDOW x AS (
|
||||
RANGE BETWEEN UNBOUNDED PRECEDING AND INDEXED() OVER(
|
||||
PARTITION BY ( WITH x AS(VALUES(col1)) VALUES(453) )
|
||||
)
|
||||
FOLLOWING
|
||||
)
|
||||
} {1 {error in view q: no such column: col1}}
|
||||
|
||||
2 {
|
||||
CREATE VIEW q AS SELECT
|
||||
CAST(CAST(CAST(CAST(CAST(CAST(CAST(CAST(CAST(CAST(CAST(RIGHT
|
||||
AS)AS)AS)AS)AS)AS)AS)AS)AS)AS)AS)WINDOW x AS(RANGE BETWEEN UNBOUNDED
|
||||
PRECEDING AND INDEXED(*)OVER(PARTITION BY
|
||||
CROSS,CROSS,NATURAL,sqlite_master(*)OVER a,(WITH a AS(VALUES(LEFT)UNION
|
||||
VALUES(LEFT)UNION VALUES(LEFT)UNION VALUES(LEFT)UNION VALUES(LEFT)UNION
|
||||
VALUES(LEFT)UNION VALUES(LEFT))VALUES(LEFT))IN
|
||||
STORED,LEFT,LEFT,LEFT,LEFT,LEFT,LEFT)*LEFT FOLLOWING)ORDER BY
|
||||
LEFT,LEFT,LEFT,LEFT,LEFT,LEFT,LEFT,LEFT,LEFT,LEFT,LEFT LIMIT
|
||||
LEFT,INDEXED(*)OVER(PARTITION BY
|
||||
CROSS,CROSS,CROSS,LEFT,INDEXED(*)OVER(PARTITION BY
|
||||
CROSS,CROSS,CROSS),INDEXED(*)OVER(PARTITION BY
|
||||
LEFT,LEFT,LEFT,LEFT,LEFT,LEFT,LEFT,LEFT,LEFT,LEFT,LEFT),
|
||||
LEFT,LEFT,INNER,CROSS,CROSS,CROSS,INNER,NATURAL ORDER BY
|
||||
OUTER,NATURAL,NATURAL,NATURAL,NATURAL,NATURAL,NATURAL,NATURAL,INNER,
|
||||
INNER,INNER NULLS LAST GROUPS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED
|
||||
FOLLOWING);
|
||||
} {1 {error in view q: no such column: LEFT}}
|
||||
|
||||
3 {
|
||||
CREATE VIEW q AS SELECT 99 WINDOW x AS (RANGE BETWEEN UNBOUNDED PRECEDING
|
||||
AND count(*)OVER(PARTITION BY (WITH a AS(VALUES(2),(x3))VALUES(0)))
|
||||
FOLLOWING)ORDER BY x2,sum(1)OVER(PARTITION BY avg(5)OVER(PARTITION BY x1));
|
||||
} {1 {error in view q: no such column: x3}}
|
||||
} {
|
||||
do_execsql_test 19.$tn.1 "
|
||||
DROP VIEW IF EXISTS q;
|
||||
$v
|
||||
" {}
|
||||
|
||||
do_catchsql_test 19.$tn.2 {
|
||||
ALTER TABLE a RENAME TO g;
|
||||
} $res
|
||||
}
|
||||
|
||||
# Verify that the "if( pParse->nErr ) return WRC_Abort" at the top of the
|
||||
# renameUnmapSelectCb() routine in alter.c (2019-12-04) is really required.
|
||||
#
|
||||
sqlite3 db :memory:
|
||||
do_catchsql_test 20.10 {
|
||||
CREATE TABLE s(a, b, c);
|
||||
CREATE INDEX k ON s( (WITH s AS( SELECT * ) VALUES(2) ) IN () );
|
||||
ALTER TABLE s RENAME a TO a2;
|
||||
} {1 {error in index k: no tables specified}}
|
||||
|
||||
#------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 21.1 {
|
||||
CREATE TABLE s(col);
|
||||
CREATE VIEW v AS SELECT (
|
||||
WITH x(a) AS(SELECT * FROM s) VALUES(RIGHT)
|
||||
) IN() ;
|
||||
CREATE TABLE a(a);
|
||||
ALTER TABLE a RENAME a TO b;
|
||||
}
|
||||
|
||||
#------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 22.1 {
|
||||
CREATE TABLE t1(a);
|
||||
CREATE VIEW v2(b) AS SELECT * FROM v2;
|
||||
}
|
||||
|
||||
do_catchsql_test 22.2 {
|
||||
ALTER TABLE t1 RENAME TO t4;
|
||||
} {1 {error in view v2: view v2 is circularly defined}}
|
||||
|
||||
do_execsql_test 22.3 {
|
||||
DROP VIEW v2;
|
||||
CREATE VIEW v2(b) AS WITH t3 AS (SELECT b FROM v2) SELECT * FROM t3;
|
||||
}
|
||||
|
||||
do_catchsql_test 22.4 {
|
||||
ALTER TABLE t1 RENAME TO t4;
|
||||
} {1 {error in view v2: view v2 is circularly defined}}
|
||||
|
||||
do_execsql_test 22.5 {
|
||||
DROP VIEW v2;
|
||||
CREATE VIEW v2(b) AS WITH t3 AS (SELECT b FROM v2) VALUES(1);
|
||||
}
|
||||
|
||||
do_catchsql_test 22.6 {
|
||||
ALTER TABLE t1 RENAME TO t4;
|
||||
} {0 {}}
|
||||
|
||||
#------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 23.1 {
|
||||
CREATE TABLE t1(x);
|
||||
CREATE TRIGGER r1 AFTER INSERT ON t1 BEGIN
|
||||
UPDATE t1 SET (c,d)=((SELECT 1 FROM t1 JOIN t2 ON b=x),1);
|
||||
END;
|
||||
}
|
||||
|
||||
do_catchsql_test 23.2 {
|
||||
ALTER TABLE t1 RENAME TO t1x;
|
||||
} {1 {error in trigger r1: no such table: main.t2}}
|
||||
|
||||
#------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 23.1 {
|
||||
CREATE TABLE v0 (a);
|
||||
CREATE VIEW v2 (v3) AS
|
||||
WITH x1 AS (SELECT * FROM v2)
|
||||
SELECT v3 AS x, v3 AS y FROM v2;
|
||||
}
|
||||
|
||||
do_catchsql_test 23.2 {
|
||||
SELECT * FROM v2
|
||||
} {1 {view v2 is circularly defined}}
|
||||
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
|
||||
do_catchsql_test 23.3 {
|
||||
ALTER TABLE v0 RENAME TO t3 ;
|
||||
} {1 {error in view v2: view v2 is circularly defined}}
|
||||
|
||||
#------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 24.1 {
|
||||
CREATE TABLE v0 (v1);
|
||||
CREATE TABLE v2 (v3 INTEGER UNIQUE ON CONFLICT ABORT);
|
||||
CREATE TRIGGER x AFTER INSERT ON v2 WHEN (
|
||||
( SELECT v1 AS PROMO_REVENUE FROM v2 JOIN v0 USING ( VALUE ) ) AND 0 )
|
||||
BEGIN
|
||||
DELETE FROM v2;
|
||||
END;
|
||||
}
|
||||
do_catchsql_test 24.2 {
|
||||
ALTER TABLE v0 RENAME TO x ;
|
||||
} {1 {error in trigger x: cannot join using column VALUE - column not present in both tables}}
|
||||
|
||||
do_execsql_test 24.3 {
|
||||
DROP TRIGGER x;
|
||||
CREATE TRIGGER x AFTER INSERT ON v2 WHEN (
|
||||
0 AND (SELECT rowid FROM v0)
|
||||
) BEGIN
|
||||
DELETE FROM v2;
|
||||
END;
|
||||
}
|
||||
|
||||
do_execsql_test 24.4 {
|
||||
ALTER TABLE v0 RENAME TO xyz;
|
||||
SELECT sql FROM sqlite_master WHERE type='trigger'
|
||||
} {{CREATE TRIGGER x AFTER INSERT ON v2 WHEN (
|
||||
0 AND (SELECT rowid FROM "xyz")
|
||||
) BEGIN
|
||||
DELETE FROM v2;
|
||||
END}}
|
||||
|
||||
#------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 25.1 {
|
||||
CREATE TABLE t1(a, b, c);
|
||||
CREATE TABLE t2(a, b, c);
|
||||
CREATE TRIGGER ttt AFTER INSERT ON t1 BEGIN
|
||||
UPDATE t1 SET a=t2.a FROM t2 WHERE t1.a=t2.a;
|
||||
END;
|
||||
}
|
||||
#do_execsql_test 25.2 {
|
||||
# ALTER TABLE t2 RENAME COLUMN a TO aaa;
|
||||
#}
|
||||
|
||||
#------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 26.1 {
|
||||
CREATE TABLE t1(x);
|
||||
|
||||
CREATE TABLE t3(y);
|
||||
CREATE TABLE t4(z);
|
||||
|
||||
CREATE TRIGGER tr1 INSERT ON t3 BEGIN
|
||||
UPDATE t3 SET y=z FROM (SELECT z FROM t4);
|
||||
END;
|
||||
|
||||
CREATE TRIGGER tr2 INSERT ON t3 BEGIN
|
||||
UPDATE t3 SET y=abc FROM (SELECT x AS abc FROM t1);
|
||||
END;
|
||||
}
|
||||
|
||||
do_execsql_test 26.2 {
|
||||
ALTER TABLE t1 RENAME TO t2;
|
||||
}
|
||||
|
||||
do_execsql_test 26.3 {
|
||||
ALTER TABLE t2 RENAME x TO xx;
|
||||
}
|
||||
|
||||
do_execsql_test 26.4 {
|
||||
SELECT sql FROM sqlite_schema WHERE name='tr2'
|
||||
} {
|
||||
{CREATE TRIGGER tr2 INSERT ON t3 BEGIN
|
||||
UPDATE t3 SET y=abc FROM (SELECT xx AS abc FROM "t2");
|
||||
END}
|
||||
}
|
||||
|
||||
# 2020-11-02 OSSFuzz
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 26.5 {
|
||||
CREATE TABLE t1(xx);
|
||||
CREATE TRIGGER xx INSERT ON t1 BEGIN
|
||||
UPDATE t1 SET xx=xx FROM(SELECT xx);
|
||||
END;
|
||||
} {}
|
||||
do_catchsql_test 26.6 {
|
||||
ALTER TABLE t1 RENAME TO t2;
|
||||
} {1 {error in trigger xx: no such column: xx}}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
|
||||
do_execsql_test 27.1 {
|
||||
CREATE TABLE t1(a, b AS ((WITH w1 (xyz) AS ( SELECT t1.b FROM t1 ) SELECT 123) IN ()), c);
|
||||
}
|
||||
|
||||
do_execsql_test 27.2 {
|
||||
ALTER TABLE t1 DROP COLUMN c;
|
||||
SELECT sql FROM sqlite_schema WHERE name = 't1';
|
||||
} {
|
||||
{CREATE TABLE t1(a, b AS ((WITH w1 (xyz) AS ( SELECT t1.b FROM t1 ) SELECT 123) IN ()))}
|
||||
}
|
||||
|
||||
do_execsql_test 27.3 {
|
||||
CREATE TABLE t0(c0 , c1 AS (CASE TRUE NOT IN () WHEN NULL THEN CASE + 0xa ISNULL WHEN NOT + 0x9 THEN t0.c1 ELSE CURRENT_TIME LIKE CAST (t0.c1 REGEXP '-([1-9]\d*.\d*|0\.\d*[1-9]\d*)'ESCAPE (c1) COLLATE BINARY BETWEEN c1 AND c1 NOT IN (WITH t4 (c0) AS (WITH t3 (c0) AS NOT MATERIALIZED (WITH RECURSIVE t2 (c0) AS (WITH RECURSIVE t1 AS (VALUES (x'717171ff71717171' ) ) SELECT DISTINCT t0.c0 FROM t0 NOT INDEXED WHERE t0.c0 =t0.c0 GROUP BY 0x9 ) SELECT DISTINCT t0.c0 FROM t0 NOT INDEXED WHERE t0.c0 =t0.c1 ) SELECT DISTINCT t0.c0 FROM t0 NOT INDEXED WHERE t0.c0 =t0.c0 GROUP BY typeof(0x9 ) ) SELECT DISTINCT t0.c0 FROM t0 NOT INDEXED WHERE t0.c0 =t0.c0 GROUP BY typeof(typeof(0x9 ) ) ) IN t0 BETWEEN typeof(typeof(typeof(hex(*) FILTER (WHERE + x'5ccd1e68' ) ) ) ) AND 1 >0xa AS BLOB (+4.4E4 , -0xe ) ) END <> c1 IN () END ) VIRTUAL , c35 PRIMARY KEY , c60 , c64 NUMERIC (-6.8 , -0xE ) ) WITHOUT ROWID ;
|
||||
} {}
|
||||
|
||||
do_execsql_test 27.4 {
|
||||
ALTER TABLE t0 DROP COLUMN c60;
|
||||
} {}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 28.1 {
|
||||
CREATE TABLE t1(a,b,c,d);
|
||||
CREATE TRIGGER AFTER INSERT ON t1 BEGIN
|
||||
UPDATE t1 SET (c,d)=(a,b);
|
||||
END;
|
||||
ALTER TABLE t1 RENAME TO t2;
|
||||
}
|
||||
|
||||
do_execsql_test 28.2 {
|
||||
SELECT sql FROM sqlite_schema WHERE type='trigger'
|
||||
} {{CREATE TRIGGER AFTER INSERT ON "t2" BEGIN
|
||||
UPDATE "t2" SET (c,d)=(a,b);
|
||||
END}}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 29.1 {
|
||||
CREATE TABLE t1(x, y);
|
||||
CREATE TRIGGER Trigger1 DELETE ON t1
|
||||
BEGIN
|
||||
SELECT t1.*, t1.x FROM t1 ORDER BY t1.x;
|
||||
END;
|
||||
}
|
||||
|
||||
|
||||
do_execsql_test 29.2 {
|
||||
ALTER TABLE t1 RENAME x TO z;
|
||||
}
|
||||
|
||||
do_execsql_test 29.3 {
|
||||
ALTER TABLE t1 RENAME TO t2;
|
||||
}
|
||||
|
||||
do_execsql_test 29.4 {
|
||||
CREATE TRIGGER tr2 AFTER DELETE ON t2 BEGIN
|
||||
SELECT z, y FROM (
|
||||
SELECT t2.* FROM t2
|
||||
);
|
||||
END;
|
||||
}
|
||||
|
||||
do_execsql_test 29.5 {
|
||||
DELETE FROM t2
|
||||
}
|
||||
|
||||
do_execsql_test 29.6 {
|
||||
ALTER TABLE t2 RENAME TO t3;
|
||||
}
|
||||
|
||||
do_execsql_test 29.7 {
|
||||
SELECT sql FROM sqlite_schema WHERE type='trigger'
|
||||
} {
|
||||
{CREATE TRIGGER Trigger1 DELETE ON "t3"
|
||||
BEGIN
|
||||
SELECT "t3".*, "t3".z FROM "t3" ORDER BY "t3".z;
|
||||
END}
|
||||
{CREATE TRIGGER tr2 AFTER DELETE ON "t3" BEGIN
|
||||
SELECT z, y FROM (
|
||||
SELECT "t3".* FROM "t3"
|
||||
);
|
||||
END}
|
||||
}
|
||||
|
||||
finish_test
|
163
testdata/tcl/altertrig.test
vendored
Normal file
163
testdata/tcl/altertrig.test
vendored
Normal file
|
@ -0,0 +1,163 @@
|
|||
# 2022 May 27
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#*************************************************************************
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix altertrig
|
||||
|
||||
# If SQLITE_OMIT_ALTERTABLE is defined, omit this file.
|
||||
ifcapable !altertable {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
proc collapse_whitespace {in} {
|
||||
regsub -all {[ \t\n]+} [string trim $in] { }
|
||||
}
|
||||
|
||||
proc do_whitespace_sql_test {tn sql res} {
|
||||
set got [execsql $sql]
|
||||
set wgot [list]
|
||||
set wres [list]
|
||||
foreach g $got { lappend wgot [collapse_whitespace $g] }
|
||||
foreach r $res { lappend wres [collapse_whitespace $r] }
|
||||
|
||||
uplevel [list do_test $tn [list set {} $wgot] $wres]
|
||||
}
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE t1(x);
|
||||
CREATE TABLE t2(y);
|
||||
CREATE TABLE t3(z);
|
||||
CREATE TABLE t4(a);
|
||||
|
||||
CREATE TRIGGER r1 INSERT ON t1 BEGIN
|
||||
UPDATE t1 SET d='xyz' FROM t2, t3;
|
||||
END;
|
||||
}
|
||||
|
||||
do_whitespace_sql_test 1.1 {
|
||||
ALTER TABLE t3 RENAME TO t5;
|
||||
SELECT sql FROM sqlite_schema WHERE type='trigger';
|
||||
} {{
|
||||
CREATE TRIGGER r1 INSERT ON t1 BEGIN
|
||||
UPDATE t1 SET d='xyz' FROM t2, "t5";
|
||||
END
|
||||
}}
|
||||
|
||||
do_execsql_test 1.2 {
|
||||
DROP TRIGGER r1;
|
||||
CREATE TRIGGER r1 INSERT ON t1 BEGIN
|
||||
UPDATE t1 SET d='xyz' FROM t2, (SELECT * FROM t5);
|
||||
END;
|
||||
}
|
||||
|
||||
do_whitespace_sql_test 1.3 {
|
||||
ALTER TABLE t5 RENAME TO t3;
|
||||
SELECT sql FROM sqlite_schema WHERE type='trigger';
|
||||
} {{
|
||||
CREATE TRIGGER r1 INSERT ON t1 BEGIN
|
||||
UPDATE t1 SET d='xyz' FROM t2, (SELECT * FROM "t3");
|
||||
END
|
||||
}}
|
||||
|
||||
foreach {tn alter update final} {
|
||||
1 {
|
||||
ALTER TABLE t3 RENAME TO t10
|
||||
} {
|
||||
UPDATE t1 SET d='xyz' FROM t2, (SELECT * FROM t3)
|
||||
} {
|
||||
UPDATE t1 SET d='xyz' FROM t2, (SELECT * FROM "t10")
|
||||
}
|
||||
|
||||
2 {
|
||||
ALTER TABLE t3 RENAME TO t10
|
||||
} {
|
||||
UPDATE t1 SET a='xyz' FROM t3, (SELECT * FROM (SELECT e FROM t3))
|
||||
} {
|
||||
UPDATE t1 SET a='xyz' FROM "t10", (SELECT * FROM (SELECT e FROM "t10"))
|
||||
}
|
||||
|
||||
3 {
|
||||
ALTER TABLE t3 RENAME e TO abc
|
||||
} {
|
||||
UPDATE t1 SET a='xyz' FROM t3, (SELECT * FROM (SELECT e FROM t3))
|
||||
} {
|
||||
UPDATE t1 SET a='xyz' FROM t3, (SELECT * FROM (SELECT abc FROM t3))
|
||||
}
|
||||
|
||||
4 {
|
||||
ALTER TABLE t2 RENAME c TO abc
|
||||
} {
|
||||
UPDATE t1 SET a='xyz' FROM t3, (SELECT 1 FROM t2 WHERE c)
|
||||
} {
|
||||
UPDATE t1 SET a='xyz' FROM t3, (SELECT 1 FROM t2 WHERE abc)
|
||||
}
|
||||
|
||||
5 {
|
||||
ALTER TABLE t2 RENAME c TO abc
|
||||
} {
|
||||
UPDATE t1 SET a=t2.c FROM t2
|
||||
} {
|
||||
UPDATE t1 SET a=t2.abc FROM t2
|
||||
}
|
||||
|
||||
6 {
|
||||
ALTER TABLE t2 RENAME c TO abc
|
||||
} {
|
||||
UPDATE t1 SET a=t2.c FROM t2, t3
|
||||
} {
|
||||
UPDATE t1 SET a=t2.abc FROM t2, t3
|
||||
}
|
||||
|
||||
7 {
|
||||
ALTER TABLE t4 RENAME e TO abc
|
||||
} {
|
||||
UPDATE t1 SET a=1 FROM t3 NATURAL JOIN t4 WHERE t4.e=a
|
||||
} {
|
||||
UPDATE t1 SET a=1 FROM t3 NATURAL JOIN t4 WHERE t4.abc=a
|
||||
}
|
||||
|
||||
8 {
|
||||
ALTER TABLE t4 RENAME TO abc
|
||||
} {
|
||||
UPDATE t1 SET a=1 FROM t3 NATURAL JOIN t4 WHERE t4.e=a
|
||||
} {
|
||||
UPDATE t1 SET a=1 FROM t3 NATURAL JOIN "abc" WHERE "abc".e=a
|
||||
}
|
||||
|
||||
} {
|
||||
reset_db
|
||||
do_execsql_test 2.$tn.1 {
|
||||
CREATE TABLE t1(a,b);
|
||||
CREATE TABLE t2(c,d);
|
||||
CREATE TABLE t3(e,f);
|
||||
CREATE TABLE t4(e,f);
|
||||
}
|
||||
do_execsql_test 2.$tn.2 "
|
||||
CREATE TRIGGER r1 INSERT ON t1 BEGIN
|
||||
$update;
|
||||
END
|
||||
"
|
||||
do_execsql_test 2.$tn.3 $alter
|
||||
|
||||
do_whitespace_sql_test 2.$tn.4 {
|
||||
SELECT sqL FROM sqlite_schema WHERE type='trigger'
|
||||
} "{
|
||||
CREATE TRIGGER r1 INSERT ON t1 BEGIN
|
||||
$final;
|
||||
END
|
||||
}"
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
117
testdata/tcl/amatch1.test
vendored
Normal file
117
testdata/tcl/amatch1.test
vendored
Normal file
|
@ -0,0 +1,117 @@
|
|||
# 2013-09-30
|
||||
#
|
||||
# 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 regression tests for "approximate_match" virtual
|
||||
# table that is in the "amatch.c" extension.
|
||||
#
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
# If SQLITE_ENABLE_FTS4 is defined, omit this file.
|
||||
ifcapable !fts3 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
# Create the fts_kjv_genesis procedure which fills and FTS3/4 table with
|
||||
# the complete text of the Book of Genesis.
|
||||
#
|
||||
source $testdir/genesis.tcl
|
||||
|
||||
|
||||
|
||||
do_test amatch1-1.0 {
|
||||
db eval {
|
||||
CREATE VIRTUAL TABLE t1 USING fts4(words); --, tokenize porter);
|
||||
}
|
||||
fts_kjv_genesis
|
||||
db eval {
|
||||
INSERT INTO t1(t1) VALUES('optimize');
|
||||
CREATE VIRTUAL TABLE temp.t1aux USING fts4aux(main, t1);
|
||||
SELECT term FROM t1aux WHERE col=0 ORDER BY 1 LIMIT 5
|
||||
}
|
||||
} {a abated abel abelmizraim abidah}
|
||||
do_test amatch1-1.1 {
|
||||
db eval {
|
||||
SELECT term FROM t1aux WHERE term>'b' AND col=0 ORDER BY 1 LIMIT 5
|
||||
}
|
||||
} {baalhanan babel back backward bad}
|
||||
do_test amatch1-1.2 {
|
||||
db eval {
|
||||
SELECT term FROM t1aux WHERE term>'b' AND col=0 LIMIT 5
|
||||
}
|
||||
} {baalhanan babel back backward bad}
|
||||
|
||||
# Load the amatch extension
|
||||
load_static_extension db amatch
|
||||
|
||||
do_execsql_test amatch1-2.0 {
|
||||
CREATE TABLE costs(iLang, cFrom, cTo, Cost);
|
||||
INSERT INTO costs VALUES(0, '', '?', 100);
|
||||
INSERT INTO costs VALUES(0, '?', '', 100);
|
||||
INSERT INTO costs VALUES(0, '?', '?', 150);
|
||||
CREATE TABLE vocab(w TEXT UNIQUE);
|
||||
INSERT OR IGNORE INTO vocab SELECT term FROM t1aux;
|
||||
CREATE VIRTUAL TABLE t2 USING approximate_match(
|
||||
vocabulary_table=t1aux,
|
||||
vocabulary_word=term,
|
||||
edit_distances=costs
|
||||
);
|
||||
CREATE VIRTUAL TABLE t3 USING approximate_match(
|
||||
vocabulary_table=vocab,
|
||||
vocabulary_word=w,
|
||||
edit_distances=costs
|
||||
);
|
||||
CREATE VIRTUAL TABLE t4 USING approximate_match(
|
||||
vocabulary_table=vtemp,
|
||||
vocabulary_word=w,
|
||||
edit_distances=costs
|
||||
);
|
||||
} {}
|
||||
puts "Query against fts4aux: [time {
|
||||
do_execsql_test amatch1-2.1 {
|
||||
SELECT word, distance FROM t2
|
||||
WHERE word MATCH 'josxph' AND distance<300;
|
||||
} {joseph 150}} 1]"
|
||||
puts "Query against ordinary table: [time {
|
||||
do_execsql_test amatch1-2.2 {
|
||||
SELECT word, distance FROM t3
|
||||
WHERE word MATCH 'josxph' AND distance<300;
|
||||
} {joseph 150}} 1]"
|
||||
puts "Temp table initialized from fts4aux: [time {
|
||||
do_execsql_test amatch1-2.3a {
|
||||
CREATE TEMP TABLE vtemp(w TEXT UNIQUE);
|
||||
INSERT OR IGNORE INTO vtemp SELECT term FROM t1aux;
|
||||
} {}} 1]"
|
||||
puts "Query against temp table: [time {
|
||||
do_execsql_test amatch1-2.3b {
|
||||
SELECT word, distance FROM t4
|
||||
WHERE word MATCH 'josxph' AND distance<300;
|
||||
} {joseph 150}} 1]"
|
||||
do_execsql_test amatch1-2.11 {
|
||||
SELECT word, distance FROM t2
|
||||
WHERE word MATCH 'joxxph' AND distance<=300;
|
||||
} {joseph 300}
|
||||
do_execsql_test amatch1-2.12 {
|
||||
SELECT word, distance FROM t3
|
||||
WHERE word MATCH 'joxxph' AND distance<=300;
|
||||
} {joseph 300}
|
||||
do_execsql_test amatch1-2.21 {
|
||||
SELECT word, distance FROM t2
|
||||
WHERE word MATCH 'joxxph' AND distance<300;
|
||||
} {}
|
||||
do_execsql_test amatch1-2.22 {
|
||||
SELECT word, distance FROM t3
|
||||
WHERE word MATCH 'joxxph' AND distance<300;
|
||||
} {}
|
||||
|
||||
finish_test
|
380
testdata/tcl/analyze.test
vendored
Normal file
380
testdata/tcl/analyze.test
vendored
Normal file
|
@ -0,0 +1,380 @@
|
|||
# 2005 July 22
|
||||
#
|
||||
# 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 regression tests for SQLite library.
|
||||
# This file implements tests for the ANALYZE command.
|
||||
#
|
||||
# $Id: analyze.test,v 1.9 2008/08/11 18:44:58 drh Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
# There is nothing to test if ANALYZE is disable for this build.
|
||||
#
|
||||
ifcapable {!analyze} {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
# Basic sanity checks.
|
||||
#
|
||||
do_test analyze-1.1 {
|
||||
catchsql {
|
||||
ANALYZE no_such_table
|
||||
}
|
||||
} {1 {no such table: no_such_table}}
|
||||
do_test analyze-1.2 {
|
||||
execsql {
|
||||
SELECT count(*) FROM sqlite_master WHERE name='sqlite_stat1'
|
||||
}
|
||||
} {0}
|
||||
do_test analyze-1.3 {
|
||||
catchsql {
|
||||
ANALYZE no_such_db.no_such_table
|
||||
}
|
||||
} {1 {unknown database no_such_db}}
|
||||
do_test analyze-1.4 {
|
||||
execsql {
|
||||
SELECT count(*) FROM sqlite_master WHERE name='sqlite_stat1'
|
||||
}
|
||||
} {0}
|
||||
do_test analyze-1.5.1 {
|
||||
catchsql {
|
||||
ANALYZE
|
||||
}
|
||||
} {0 {}}
|
||||
do_test analyze-1.5.2 {
|
||||
catchsql {
|
||||
PRAGMA empty_result_callbacks=1;
|
||||
ANALYZE
|
||||
}
|
||||
} {0 {}}
|
||||
do_test analyze-1.6 {
|
||||
execsql {
|
||||
SELECT count(*) FROM sqlite_master WHERE name='sqlite_stat1'
|
||||
}
|
||||
} {1}
|
||||
do_test analyze-1.6.2 {
|
||||
catchsql {
|
||||
CREATE INDEX stat1idx ON sqlite_stat1(idx);
|
||||
}
|
||||
} {1 {table sqlite_stat1 may not be indexed}}
|
||||
do_test analyze-1.6.3 {
|
||||
catchsql {
|
||||
CREATE INDEX main.stat1idx ON SQLite_stat1(idx);
|
||||
}
|
||||
} {1 {table sqlite_stat1 may not be indexed}}
|
||||
do_test analyze-1.7 {
|
||||
execsql {
|
||||
SELECT * FROM sqlite_stat1 WHERE idx NOT NULL
|
||||
}
|
||||
} {}
|
||||
do_test analyze-1.8 {
|
||||
catchsql {
|
||||
ANALYZE main
|
||||
}
|
||||
} {0 {}}
|
||||
do_test analyze-1.9 {
|
||||
execsql {
|
||||
SELECT * FROM sqlite_stat1 WHERE idx NOT NULL
|
||||
}
|
||||
} {}
|
||||
do_test analyze-1.10 {
|
||||
catchsql {
|
||||
CREATE TABLE t1(a,b);
|
||||
ANALYZE main.t1;
|
||||
}
|
||||
} {0 {}}
|
||||
do_test analyze-1.11 {
|
||||
execsql {
|
||||
SELECT * FROM sqlite_stat1
|
||||
}
|
||||
} {}
|
||||
do_test analyze-1.12 {
|
||||
catchsql {
|
||||
ANALYZE t1;
|
||||
}
|
||||
} {0 {}}
|
||||
do_test analyze-1.13 {
|
||||
execsql {
|
||||
SELECT * FROM sqlite_stat1
|
||||
}
|
||||
} {}
|
||||
|
||||
# Create some indices that can be analyzed. But do not yet add
|
||||
# data. Without data in the tables, no analysis is done.
|
||||
#
|
||||
do_test analyze-2.1 {
|
||||
execsql {
|
||||
CREATE INDEX t1i1 ON t1(a);
|
||||
ANALYZE main.t1;
|
||||
SELECT * FROM sqlite_stat1 ORDER BY idx;
|
||||
}
|
||||
} {}
|
||||
do_test analyze-2.2 {
|
||||
execsql {
|
||||
CREATE INDEX t1i2 ON t1(b);
|
||||
ANALYZE t1;
|
||||
SELECT * FROM sqlite_stat1 ORDER BY idx;
|
||||
}
|
||||
} {}
|
||||
do_test analyze-2.3 {
|
||||
execsql {
|
||||
CREATE INDEX t1i3 ON t1(a,b);
|
||||
ANALYZE main;
|
||||
SELECT * FROM sqlite_stat1 ORDER BY idx;
|
||||
}
|
||||
} {}
|
||||
|
||||
# Start adding data to the table. Verify that the analysis
|
||||
# is done correctly.
|
||||
#
|
||||
do_test analyze-3.1 {
|
||||
execsql {
|
||||
INSERT INTO t1 VALUES(1,2);
|
||||
INSERT INTO t1 VALUES(1,3);
|
||||
ANALYZE main.t1;
|
||||
SELECT idx, stat FROM sqlite_stat1 ORDER BY idx;
|
||||
}
|
||||
} {t1i1 {2 2} t1i2 {2 1} t1i3 {2 2 1}}
|
||||
do_test analyze-3.2 {
|
||||
execsql {
|
||||
INSERT INTO t1 VALUES(1,4);
|
||||
INSERT INTO t1 VALUES(1,5);
|
||||
ANALYZE t1;
|
||||
SELECT idx, stat FROM sqlite_stat1 ORDER BY idx;
|
||||
}
|
||||
} {t1i1 {4 4} t1i2 {4 1} t1i3 {4 4 1}}
|
||||
do_test analyze-3.3 {
|
||||
execsql {
|
||||
INSERT INTO t1 VALUES(2,5);
|
||||
ANALYZE main;
|
||||
SELECT idx, stat FROM sqlite_stat1 ORDER BY idx;
|
||||
}
|
||||
} {t1i1 {5 3} t1i2 {5 2} t1i3 {5 3 1}}
|
||||
do_test analyze-3.4 {
|
||||
execsql {
|
||||
CREATE TABLE t2 AS SELECT * FROM t1;
|
||||
CREATE INDEX t2i1 ON t2(a);
|
||||
CREATE INDEX t2i2 ON t2(b);
|
||||
CREATE INDEX t2i3 ON t2(a,b);
|
||||
ANALYZE;
|
||||
SELECT idx, stat FROM sqlite_stat1 ORDER BY idx;
|
||||
}
|
||||
} {t1i1 {5 3} t1i2 {5 2} t1i3 {5 3 1} t2i1 {5 3} t2i2 {5 2} t2i3 {5 3 1}}
|
||||
do_test analyze-3.5 {
|
||||
execsql {
|
||||
DROP INDEX t2i3;
|
||||
ANALYZE t1;
|
||||
SELECT idx, stat FROM sqlite_stat1 ORDER BY idx;
|
||||
}
|
||||
} {t1i1 {5 3} t1i2 {5 2} t1i3 {5 3 1} t2i1 {5 3} t2i2 {5 2}}
|
||||
do_test analyze-3.6 {
|
||||
execsql {
|
||||
ANALYZE t2;
|
||||
SELECT idx, stat FROM sqlite_stat1 ORDER BY idx;
|
||||
}
|
||||
} {t1i1 {5 3} t1i2 {5 2} t1i3 {5 3 1} t2i1 {5 3} t2i2 {5 2}}
|
||||
do_test analyze-3.7 {
|
||||
execsql {
|
||||
DROP INDEX t2i2;
|
||||
ANALYZE t2;
|
||||
SELECT idx, stat FROM sqlite_stat1 ORDER BY idx;
|
||||
}
|
||||
} {t1i1 {5 3} t1i2 {5 2} t1i3 {5 3 1} t2i1 {5 3}}
|
||||
do_test analyze-3.8 {
|
||||
execsql {
|
||||
CREATE TABLE t3 AS SELECT a, b, rowid AS c, 'hi' AS d FROM t1;
|
||||
CREATE INDEX t3i1 ON t3(a);
|
||||
CREATE INDEX t3i2 ON t3(a,b,c,d);
|
||||
CREATE INDEX t3i3 ON t3(d,b,c,a);
|
||||
DROP TABLE t1;
|
||||
DROP TABLE t2;
|
||||
SELECT idx, stat FROM sqlite_stat1 ORDER BY idx;
|
||||
}
|
||||
} {}
|
||||
do_test analyze-3.9 {
|
||||
execsql {
|
||||
ANALYZE;
|
||||
SELECT idx, stat FROM sqlite_stat1 ORDER BY idx;
|
||||
}
|
||||
} {t3i1 {5 3} t3i2 {5 3 1 1 1} t3i3 {5 5 2 1 1}}
|
||||
|
||||
do_test analyze-3.10 {
|
||||
execsql {
|
||||
CREATE TABLE [silly " name](a, b, c);
|
||||
CREATE INDEX 'foolish '' name' ON [silly " name](a, b);
|
||||
CREATE INDEX 'another foolish '' name' ON [silly " name](c);
|
||||
INSERT INTO [silly " name] VALUES(1, 2, 3);
|
||||
INSERT INTO [silly " name] VALUES(4, 5, 6);
|
||||
ANALYZE;
|
||||
SELECT idx, stat FROM sqlite_stat1 ORDER BY idx;
|
||||
}
|
||||
} {{another foolish ' name} {2 1} {foolish ' name} {2 1 1} t3i1 {5 3} t3i2 {5 3 1 1 1} t3i3 {5 5 2 1 1}}
|
||||
do_test analyze-3.11 {
|
||||
execsql {
|
||||
DROP INDEX "foolish ' name";
|
||||
SELECT idx, stat FROM sqlite_stat1 ORDER BY idx;
|
||||
}
|
||||
} {{another foolish ' name} {2 1} t3i1 {5 3} t3i2 {5 3 1 1 1} t3i3 {5 5 2 1 1}}
|
||||
do_test analyze-3.11 {
|
||||
execsql {
|
||||
DROP TABLE "silly "" name";
|
||||
SELECT idx, stat FROM sqlite_stat1 ORDER BY idx;
|
||||
}
|
||||
} {t3i1 {5 3} t3i2 {5 3 1 1 1} t3i3 {5 5 2 1 1}}
|
||||
|
||||
# Try corrupting the sqlite_stat1 table and make sure the
|
||||
# database is still able to function.
|
||||
#
|
||||
do_test analyze-4.0 {
|
||||
sqlite3 db2 test.db
|
||||
db2 eval {
|
||||
CREATE TABLE t4(x,y,z);
|
||||
CREATE INDEX t4i1 ON t4(x);
|
||||
CREATE INDEX t4i2 ON t4(y);
|
||||
INSERT INTO t4 SELECT a,b,c FROM t3;
|
||||
}
|
||||
db2 close
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
execsql {
|
||||
ANALYZE;
|
||||
SELECT idx, stat FROM sqlite_stat1 ORDER BY idx;
|
||||
}
|
||||
} {t3i1 {5 3} t3i2 {5 3 1 1 1} t3i3 {5 5 2 1 1} t4i1 {5 3} t4i2 {5 2}}
|
||||
do_test analyze-4.1 {
|
||||
execsql {
|
||||
PRAGMA writable_schema=on;
|
||||
INSERT INTO sqlite_stat1 VALUES(null,null,null);
|
||||
PRAGMA writable_schema=off;
|
||||
}
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
execsql {
|
||||
SELECT * FROM t4 WHERE x=1234;
|
||||
}
|
||||
} {}
|
||||
do_test analyze-4.2 {
|
||||
execsql {
|
||||
PRAGMA writable_schema=on;
|
||||
DELETE FROM sqlite_stat1;
|
||||
INSERT INTO sqlite_stat1 VALUES('t4','t4i1','nonsense');
|
||||
INSERT INTO sqlite_stat1 VALUES('t4','t4i2','120897349817238741092873198273409187234918720394817209384710928374109827172901827349871928741910');
|
||||
PRAGMA writable_schema=off;
|
||||
}
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
execsql {
|
||||
SELECT * FROM t4 WHERE x=1234;
|
||||
}
|
||||
} {}
|
||||
do_test analyze-4.3 {
|
||||
execsql {
|
||||
INSERT INTO sqlite_stat1 VALUES('t4','xyzzy','0 1 2 3');
|
||||
}
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
execsql {
|
||||
SELECT * FROM t4 WHERE x=1234;
|
||||
}
|
||||
} {}
|
||||
|
||||
# Verify that DROP TABLE and DROP INDEX remove entries from the
|
||||
# sqlite_stat1 and sqlite_stat4 tables.
|
||||
#
|
||||
do_test analyze-5.0 {
|
||||
execsql {
|
||||
DELETE FROM t3;
|
||||
DELETE FROM t4;
|
||||
INSERT INTO t3 VALUES(1,2,3,4);
|
||||
INSERT INTO t3 VALUES(5,6,7,8);
|
||||
INSERT INTO t3 SELECT a+8, b+8, c+8, d+8 FROM t3;
|
||||
INSERT INTO t3 SELECT a+16, b+16, c+16, d+16 FROM t3;
|
||||
INSERT INTO t3 SELECT a+32, b+32, c+32, d+32 FROM t3;
|
||||
INSERT INTO t3 SELECT a+64, b+64, c+64, d+64 FROM t3;
|
||||
INSERT INTO t4 SELECT a, b, c FROM t3;
|
||||
ANALYZE;
|
||||
SELECT DISTINCT idx FROM sqlite_stat1 ORDER BY 1;
|
||||
SELECT DISTINCT tbl FROM sqlite_stat1 ORDER BY 1;
|
||||
}
|
||||
} {t3i1 t3i2 t3i3 t4i1 t4i2 t3 t4}
|
||||
ifcapable stat4 {
|
||||
do_test analyze-5.1 {
|
||||
execsql {
|
||||
SELECT DISTINCT idx FROM sqlite_stat4 ORDER BY 1;
|
||||
SELECT DISTINCT tbl FROM sqlite_stat4 ORDER BY 1;
|
||||
}
|
||||
} {t3i1 t3i2 t3i3 t4i1 t4i2 t3 t4}
|
||||
}
|
||||
do_test analyze-5.2 {
|
||||
execsql {
|
||||
DROP INDEX t3i2;
|
||||
SELECT DISTINCT idx FROM sqlite_stat1 ORDER BY 1;
|
||||
SELECT DISTINCT tbl FROM sqlite_stat1 ORDER BY 1;
|
||||
}
|
||||
} {t3i1 t3i3 t4i1 t4i2 t3 t4}
|
||||
ifcapable stat4 {
|
||||
do_test analyze-5.3 {
|
||||
execsql {
|
||||
SELECT DISTINCT idx FROM sqlite_stat4 ORDER BY 1;
|
||||
SELECT DISTINCT tbl FROM sqlite_stat4 ORDER BY 1;
|
||||
}
|
||||
} {t3i1 t3i3 t4i1 t4i2 t3 t4}
|
||||
}
|
||||
do_test analyze-5.4 {
|
||||
execsql {
|
||||
DROP TABLE t3;
|
||||
SELECT DISTINCT idx FROM sqlite_stat1 ORDER BY 1;
|
||||
SELECT DISTINCT tbl FROM sqlite_stat1 ORDER BY 1;
|
||||
}
|
||||
} {t4i1 t4i2 t4}
|
||||
ifcapable stat4 {
|
||||
do_test analyze-5.5 {
|
||||
execsql {
|
||||
SELECT DISTINCT idx FROM sqlite_stat4 ORDER BY 1;
|
||||
SELECT DISTINCT tbl FROM sqlite_stat4 ORDER BY 1;
|
||||
}
|
||||
} {t4i1 t4i2 t4}
|
||||
}
|
||||
|
||||
# This test corrupts the database file so it must be the last test
|
||||
# in the series.
|
||||
#
|
||||
do_test analyze-5.99 {
|
||||
sqlite3_db_config db DEFENSIVE 0
|
||||
execsql {
|
||||
PRAGMA writable_schema=on;
|
||||
UPDATE sqlite_master SET sql='nonsense' WHERE name='sqlite_stat1';
|
||||
}
|
||||
db close
|
||||
catch { sqlite3 db test.db }
|
||||
catchsql {
|
||||
ANALYZE
|
||||
}
|
||||
} {1 {malformed database schema (sqlite_stat1)}}
|
||||
|
||||
# Verify that tables whose names begin with "sqlite" but not
|
||||
# "sqlite_" are analyzed.
|
||||
#
|
||||
db close
|
||||
sqlite3 db :memory:
|
||||
do_execsql_test analyze-6.1 {
|
||||
CREATE TABLE sqliteDemo(a);
|
||||
INSERT INTO sqliteDemo(a) VALUES(1),(2),(3),(4),(5);
|
||||
CREATE TABLE SQLiteDemo2(a INTEGER PRIMARY KEY AUTOINCREMENT);
|
||||
INSERT INTO SQLiteDemo2 SELECT * FROM sqliteDemo;
|
||||
CREATE TABLE t1(b);
|
||||
INSERT INTO t1(b) SELECT a FROM sqliteDemo;
|
||||
ANALYZE;
|
||||
SELECT tbl FROM sqlite_stat1 WHERE idx IS NULL ORDER BY tbl;
|
||||
} {SQLiteDemo2 sqliteDemo t1}
|
||||
|
||||
finish_test
|
739
testdata/tcl/analyze3.test
vendored
Normal file
739
testdata/tcl/analyze3.test
vendored
Normal file
|
@ -0,0 +1,739 @@
|
|||
# 2009 August 06
|
||||
#
|
||||
# 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 regression tests for SQLite library. This file
|
||||
# implements tests for range and LIKE constraints that use bound variables
|
||||
# instead of literal constant arguments.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix analyze3
|
||||
|
||||
ifcapable !stat4 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
# This test cannot be run with the sqlite3_prepare() permutation, as it
|
||||
# tests that stat4 data can be used to influence the plans of queries
|
||||
# based on bound variable values. And this is not possible when using
|
||||
# sqlite3_prepare() - as queries cannot be internally re-prepared after
|
||||
# binding values are available.
|
||||
if {[permutation]=="prepare"} {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
# Test Organization:
|
||||
#
|
||||
# analyze3-1.*: Test that the values of bound parameters are considered
|
||||
# in the same way as constants when planning queries that
|
||||
# use range constraints.
|
||||
#
|
||||
# analyze3-2.*: Test that the values of bound parameters are considered
|
||||
# in the same way as constants when planning queries that
|
||||
# use LIKE expressions in the WHERE clause.
|
||||
#
|
||||
# analyze3-3.*: Test that binding to a variable does not invalidate the
|
||||
# query plan when there is no way in which replanning the
|
||||
# query may produce a superior outcome.
|
||||
#
|
||||
# analyze3-4.*: Test that SQL or authorization callback errors occuring
|
||||
# within sqlite3Reprepare() are handled correctly.
|
||||
#
|
||||
# analyze3-5.*: Check that the query plans of applicable statements are
|
||||
# invalidated if the values of SQL parameter are modified
|
||||
# using the clear_bindings() or transfer_bindings() APIs.
|
||||
#
|
||||
# analyze3-6.*: Test that the problem fixed by commit [127a5b776d] is fixed.
|
||||
#
|
||||
# analyze3-7.*: Test that some memory leaks discovered by fuzz testing
|
||||
# have been fixed.
|
||||
#
|
||||
|
||||
proc getvar {varname} { uplevel #0 set $varname }
|
||||
db function var getvar
|
||||
|
||||
proc eqp {sql {db db}} {
|
||||
uplevel execsql [list "EXPLAIN QUERY PLAN $sql"] $db
|
||||
}
|
||||
|
||||
proc sf_execsql {sql {db db}} {
|
||||
set ::sqlite_search_count 0
|
||||
set r [uplevel [list execsql $sql $db]]
|
||||
|
||||
concat $::sqlite_search_count [$db status step] $r
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# analyze3-1.1.1:
|
||||
# Create a table with two columns. Populate the first column (affinity
|
||||
# INTEGER) with integer values from 100 to 1100. Create an index on this
|
||||
# column. ANALYZE the table.
|
||||
#
|
||||
# analyze3-1.1.2 - 3.1.3
|
||||
# Show that there are two possible plans for querying the table with
|
||||
# a range constraint on the indexed column - "full table scan" or "use
|
||||
# the index". When the range is specified using literal values, SQLite
|
||||
# is able to pick the best plan based on the samples in sqlite_stat3.
|
||||
#
|
||||
# analyze3-1.1.4 - 3.1.9
|
||||
# Show that using SQL variables produces the same results as using
|
||||
# literal values to constrain the range scan.
|
||||
#
|
||||
# These tests also check that the compiler code considers column
|
||||
# affinities when estimating the number of rows scanned by the "use
|
||||
# index strategy".
|
||||
#
|
||||
do_test analyze3-1.1.1 {
|
||||
execsql {
|
||||
BEGIN;
|
||||
CREATE TABLE t1(x INTEGER, y);
|
||||
CREATE INDEX i1 ON t1(x);
|
||||
}
|
||||
for {set i 0} {$i < 1000} {incr i} {
|
||||
execsql { INSERT INTO t1 VALUES($i+100, $i) }
|
||||
}
|
||||
execsql {
|
||||
COMMIT;
|
||||
ANALYZE;
|
||||
}
|
||||
|
||||
execsql { SELECT count(*)>0 FROM sqlite_stat4; }
|
||||
} {1}
|
||||
|
||||
do_execsql_test analyze3-1.1.x {
|
||||
SELECT count(*) FROM t1 WHERE x>200 AND x<300;
|
||||
SELECT count(*) FROM t1 WHERE x>0 AND x<1100;
|
||||
} {99 1000}
|
||||
|
||||
# The first of the following two SELECT statements visits 99 rows. So
|
||||
# it is better to use the index. But the second visits every row in
|
||||
# the table (1000 in total) so it is better to do a full-table scan.
|
||||
#
|
||||
do_eqp_test analyze3-1.1.2 {
|
||||
SELECT sum(y) FROM t1 WHERE x>200 AND x<300
|
||||
} {SEARCH t1 USING INDEX i1 (x>? AND x<?)}
|
||||
do_eqp_test analyze3-1.1.3 {
|
||||
SELECT sum(y) FROM t1 WHERE x>0 AND x<1100
|
||||
} {SCAN t1}
|
||||
|
||||
# 2017-06-26: Verify that the SQLITE_DBCONFIG_ENABLE_QPSG setting disables
|
||||
# the use of bound parameters by STAT4
|
||||
#
|
||||
db cache flush
|
||||
unset -nocomplain l
|
||||
unset -nocomplain u
|
||||
do_eqp_test analyze3-1.1.3.100 {
|
||||
SELECT sum(y) FROM t1 WHERE x>$l AND x<$u
|
||||
} {SEARCH t1 USING INDEX i1 (x>? AND x<?)}
|
||||
set l 200
|
||||
set u 300
|
||||
do_eqp_test analyze3-1.1.3.101 {
|
||||
SELECT sum(y) FROM t1 WHERE x>$l AND x<$u
|
||||
} {SEARCH t1 USING INDEX i1 (x>? AND x<?)}
|
||||
set l 0
|
||||
set u 1100
|
||||
do_eqp_test analyze3-1.1.3.102 {
|
||||
SELECT sum(y) FROM t1 WHERE x>$l AND x<$u
|
||||
} {SCAN t1}
|
||||
db cache flush
|
||||
sqlite3_db_config db ENABLE_QPSG 1
|
||||
do_eqp_test analyze3-1.1.3.103 {
|
||||
SELECT sum(y) FROM t1 WHERE x>$l AND x<$u
|
||||
} {SEARCH t1 USING INDEX i1 (x>? AND x<?)}
|
||||
db cache flush
|
||||
sqlite3_db_config db ENABLE_QPSG 0
|
||||
do_eqp_test analyze3-1.1.3.104 {
|
||||
SELECT sum(y) FROM t1 WHERE x>$l AND x<$u
|
||||
} {SCAN t1}
|
||||
|
||||
do_test analyze3-1.1.4 {
|
||||
sf_execsql { SELECT sum(y) FROM t1 WHERE x>200 AND x<300 }
|
||||
} {199 0 14850}
|
||||
do_test analyze3-1.1.5 {
|
||||
set l [string range "200" 0 end]
|
||||
set u [string range "300" 0 end]
|
||||
sf_execsql { SELECT sum(y) FROM t1 WHERE x>$l AND x<$u }
|
||||
} {199 0 14850}
|
||||
do_test analyze3-1.1.6 {
|
||||
set l [expr int(200)]
|
||||
set u [expr int(300)]
|
||||
sf_execsql { SELECT sum(y) FROM t1 WHERE x>$l AND x<$u }
|
||||
} {199 0 14850}
|
||||
do_test analyze3-1.1.7 {
|
||||
sf_execsql { SELECT sum(y) FROM t1 WHERE x>0 AND x<1100 }
|
||||
} {999 999 499500}
|
||||
do_test analyze3-1.1.8 {
|
||||
set l [string range "0" 0 end]
|
||||
set u [string range "1100" 0 end]
|
||||
sf_execsql { SELECT sum(y) FROM t1 WHERE x>$l AND x<$u }
|
||||
} {999 999 499500}
|
||||
do_test analyze3-1.1.9 {
|
||||
set l [expr int(0)]
|
||||
set u [expr int(1100)]
|
||||
sf_execsql { SELECT sum(y) FROM t1 WHERE x>$l AND x<$u }
|
||||
} {999 999 499500}
|
||||
|
||||
|
||||
# The following tests are similar to the block above. The difference is
|
||||
# that the indexed column has TEXT affinity in this case. In the tests
|
||||
# above the affinity is INTEGER.
|
||||
#
|
||||
do_test analyze3-1.2.1 {
|
||||
execsql {
|
||||
BEGIN;
|
||||
CREATE TABLE t2(x TEXT, y);
|
||||
INSERT INTO t2 SELECT * FROM t1;
|
||||
CREATE INDEX i2 ON t2(x);
|
||||
COMMIT;
|
||||
ANALYZE;
|
||||
}
|
||||
} {}
|
||||
do_execsql_test analyze3-2.1.x {
|
||||
SELECT count(*) FROM t2 WHERE x>1 AND x<2;
|
||||
SELECT count(*) FROM t2 WHERE x>0 AND x<99;
|
||||
} {200 990}
|
||||
do_eqp_test analyze3-1.2.2 {
|
||||
SELECT sum(y) FROM t2 WHERE x>1 AND x<2
|
||||
} {SEARCH t2 USING INDEX i2 (x>? AND x<?)}
|
||||
do_eqp_test analyze3-1.2.3 {
|
||||
SELECT sum(y) FROM t2 WHERE x>0 AND x<99
|
||||
} {SCAN t2}
|
||||
|
||||
do_test analyze3-1.2.4 {
|
||||
sf_execsql { SELECT sum(y) FROM t2 WHERE x>12 AND x<20 }
|
||||
} {161 0 4760}
|
||||
do_test analyze3-1.2.5 {
|
||||
set l [string range "12" 0 end]
|
||||
set u [string range "20" 0 end]
|
||||
sf_execsql {SELECT typeof($l), typeof($u), sum(y) FROM t2 WHERE x>$l AND x<$u}
|
||||
} {161 0 text text 4760}
|
||||
do_test analyze3-1.2.6 {
|
||||
set l [expr int(12)]
|
||||
set u [expr int(20)]
|
||||
sf_execsql {SELECT typeof($l), typeof($u), sum(y) FROM t2 WHERE x>$l AND x<$u}
|
||||
} {161 0 integer integer 4760}
|
||||
do_test analyze3-1.2.7 {
|
||||
sf_execsql { SELECT sum(y) FROM t2 WHERE x>0 AND x<99 }
|
||||
} {999 999 490555}
|
||||
do_test analyze3-1.2.8 {
|
||||
set l [string range "0" 0 end]
|
||||
set u [string range "99" 0 end]
|
||||
sf_execsql {SELECT typeof($l), typeof($u), sum(y) FROM t2 WHERE x>$l AND x<$u}
|
||||
} {999 999 text text 490555}
|
||||
do_test analyze3-1.2.9 {
|
||||
set l [expr int(0)]
|
||||
set u [expr int(99)]
|
||||
sf_execsql {SELECT typeof($l), typeof($u), sum(y) FROM t2 WHERE x>$l AND x<$u}
|
||||
} {999 999 integer integer 490555}
|
||||
|
||||
# Same tests a third time. This time, column x has INTEGER affinity and
|
||||
# is not the leftmost column of the table. This triggered a bug causing
|
||||
# SQLite to use sub-optimal query plans in 3.6.18 and earlier.
|
||||
#
|
||||
do_test analyze3-1.3.1 {
|
||||
execsql {
|
||||
BEGIN;
|
||||
CREATE TABLE t3(y TEXT, x INTEGER);
|
||||
INSERT INTO t3 SELECT y, x FROM t1;
|
||||
CREATE INDEX i3 ON t3(x);
|
||||
COMMIT;
|
||||
ANALYZE;
|
||||
}
|
||||
} {}
|
||||
do_execsql_test analyze3-1.3.x {
|
||||
SELECT count(*) FROM t3 WHERE x>200 AND x<300;
|
||||
SELECT count(*) FROM t3 WHERE x>0 AND x<1100
|
||||
} {99 1000}
|
||||
do_eqp_test analyze3-1.3.2 {
|
||||
SELECT sum(y) FROM t3 WHERE x>200 AND x<300
|
||||
} {SEARCH t3 USING INDEX i3 (x>? AND x<?)}
|
||||
do_eqp_test analyze3-1.3.3 {
|
||||
SELECT sum(y) FROM t3 WHERE x>0 AND x<1100
|
||||
} {SCAN t3}
|
||||
|
||||
do_test analyze3-1.3.4 {
|
||||
sf_execsql { SELECT sum(y) FROM t3 WHERE x>200 AND x<300 }
|
||||
} {199 0 14850}
|
||||
do_test analyze3-1.3.5 {
|
||||
set l [string range "200" 0 end]
|
||||
set u [string range "300" 0 end]
|
||||
sf_execsql { SELECT sum(y) FROM t3 WHERE x>$l AND x<$u }
|
||||
} {199 0 14850}
|
||||
do_test analyze3-1.3.6 {
|
||||
set l [expr int(200)]
|
||||
set u [expr int(300)]
|
||||
sf_execsql { SELECT sum(y) FROM t3 WHERE x>$l AND x<$u }
|
||||
} {199 0 14850}
|
||||
do_test analyze3-1.3.7 {
|
||||
sf_execsql { SELECT sum(y) FROM t3 WHERE x>0 AND x<1100 }
|
||||
} {999 999 499500}
|
||||
do_test analyze3-1.3.8 {
|
||||
set l [string range "0" 0 end]
|
||||
set u [string range "1100" 0 end]
|
||||
sf_execsql { SELECT sum(y) FROM t3 WHERE x>$l AND x<$u }
|
||||
} {999 999 499500}
|
||||
do_test analyze3-1.3.9 {
|
||||
set l [expr int(0)]
|
||||
set u [expr int(1100)]
|
||||
sf_execsql { SELECT sum(y) FROM t3 WHERE x>$l AND x<$u }
|
||||
} {999 999 499500}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test that the values of bound SQL variables may be used for the LIKE
|
||||
# optimization.
|
||||
#
|
||||
drop_all_tables
|
||||
do_test analyze3-2.1 {
|
||||
execsql {
|
||||
PRAGMA case_sensitive_like=off;
|
||||
BEGIN;
|
||||
CREATE TABLE t1(a, b TEXT COLLATE nocase);
|
||||
CREATE INDEX i1 ON t1(b);
|
||||
}
|
||||
for {set i 0} {$i < 1000} {incr i} {
|
||||
set t ""
|
||||
append t [lindex {a b c d e f g h i j} [expr $i/100]]
|
||||
append t [lindex {a b c d e f g h i j} [expr ($i/10)%10]]
|
||||
append t [lindex {a b c d e f g h i j} [expr ($i%10)]]
|
||||
execsql { INSERT INTO t1 VALUES($i, $t) }
|
||||
}
|
||||
execsql COMMIT
|
||||
} {}
|
||||
do_eqp_test analyze3-2.2 {
|
||||
SELECT count(a) FROM t1 WHERE b LIKE 'a%'
|
||||
} {SEARCH t1 USING INDEX i1 (b>? AND b<?)}
|
||||
do_eqp_test analyze3-2.3 {
|
||||
SELECT count(a) FROM t1 WHERE b LIKE '%a'
|
||||
} {SCAN t1}
|
||||
|
||||
# Return the first argument if like_match_blobs is true (the default)
|
||||
# or the second argument if not
|
||||
#
|
||||
proc ilmb {a b} {
|
||||
ifcapable like_match_blobs {return $a}
|
||||
return $b
|
||||
}
|
||||
|
||||
do_test analyze3-2.4 {
|
||||
sf_execsql { SELECT count(*) FROM t1 WHERE b LIKE 'a%' }
|
||||
} [list [ilmb 102 101] 0 100]
|
||||
do_test analyze3-2.5 {
|
||||
sf_execsql { SELECT count(*) FROM t1 WHERE b LIKE '%a' }
|
||||
} {999 999 100}
|
||||
|
||||
do_test analyze3-2.6 {
|
||||
set like "a%"
|
||||
sf_execsql { SELECT count(*) FROM t1 WHERE b LIKE $like }
|
||||
} [list [ilmb 102 101] 0 100]
|
||||
do_test analyze3-2.7 {
|
||||
set like "%a"
|
||||
sf_execsql { SELECT count(*) FROM t1 WHERE b LIKE $like }
|
||||
} {999 999 100}
|
||||
do_test analyze3-2.8 {
|
||||
set like "a"
|
||||
sf_execsql { SELECT count(*) FROM t1 WHERE b LIKE $like }
|
||||
} [list [ilmb 102 101] 0 0]
|
||||
do_test analyze3-2.9 {
|
||||
set like "ab"
|
||||
sf_execsql { SELECT count(*) FROM t1 WHERE b LIKE $like }
|
||||
} [list [ilmb 12 11] 0 0]
|
||||
do_test analyze3-2.10 {
|
||||
set like "abc"
|
||||
sf_execsql { SELECT count(*) FROM t1 WHERE b LIKE $like }
|
||||
} [list [ilmb 3 2] 0 1]
|
||||
do_test analyze3-2.11 {
|
||||
set like "a_c"
|
||||
sf_execsql { SELECT count(*) FROM t1 WHERE b LIKE $like }
|
||||
} [list [ilmb 102 101] 0 10]
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# This block of tests checks that statements are correctly marked as
|
||||
# expired when the values bound to any parameters that may affect the
|
||||
# query plan are modified.
|
||||
#
|
||||
drop_all_tables
|
||||
db auth auth
|
||||
proc auth {args} {
|
||||
set ::auth 1
|
||||
return SQLITE_OK
|
||||
}
|
||||
|
||||
do_test analyze3-3.1 {
|
||||
execsql {
|
||||
BEGIN;
|
||||
CREATE TABLE t1(a, b, c);
|
||||
CREATE INDEX i1 ON t1(b);
|
||||
}
|
||||
for {set i 0} {$i < 100} {incr i} {
|
||||
execsql { INSERT INTO t1 VALUES($i, $i, $i) }
|
||||
}
|
||||
execsql COMMIT
|
||||
execsql ANALYZE
|
||||
} {}
|
||||
do_test analyze3-3.2.1 {
|
||||
set S [sqlite3_prepare_v2 db "SELECT * FROM t1 WHERE b>?" -1 dummy]
|
||||
sqlite3_expired $S
|
||||
} {0}
|
||||
do_test analyze3-3.2.2 {
|
||||
sqlite3_bind_text $S 1 "abc" 3
|
||||
sqlite3_expired $S
|
||||
} {1}
|
||||
do_test analyze3-3.2.4 {
|
||||
sqlite3_finalize $S
|
||||
} {SQLITE_OK}
|
||||
|
||||
do_test analyze3-3.2.5 {
|
||||
set S [sqlite3_prepare_v2 db "SELECT * FROM t1 WHERE b=?" -1 dummy]
|
||||
sqlite3_expired $S
|
||||
} {0}
|
||||
do_test analyze3-3.2.6 {
|
||||
sqlite3_bind_text $S 1 "abc" 3
|
||||
sqlite3_expired $S
|
||||
} {1}
|
||||
do_test analyze3-3.2.7 {
|
||||
sqlite3_finalize $S
|
||||
} {SQLITE_OK}
|
||||
|
||||
do_test analyze3-3.4.1 {
|
||||
set S [sqlite3_prepare_v2 db "SELECT * FROM t1 WHERE a=? AND b>?" -1 dummy]
|
||||
sqlite3_expired $S
|
||||
} {0}
|
||||
do_test analyze3-3.4.2 {
|
||||
sqlite3_bind_text $S 1 "abc" 3
|
||||
sqlite3_expired $S
|
||||
} {0}
|
||||
do_test analyze3-3.4.3 {
|
||||
sqlite3_bind_text $S 2 "def" 3
|
||||
sqlite3_expired $S
|
||||
} {1}
|
||||
do_test analyze3-3.4.4 {
|
||||
sqlite3_bind_text $S 2 "ghi" 3
|
||||
sqlite3_expired $S
|
||||
} {1}
|
||||
do_test analyze3-3.4.5 {
|
||||
sqlite3_expired $S
|
||||
} {1}
|
||||
do_test analyze3-3.4.6 {
|
||||
sqlite3_finalize $S
|
||||
} {SQLITE_OK}
|
||||
|
||||
do_test analyze3-3.5.1 {
|
||||
set S [sqlite3_prepare_v2 db {
|
||||
SELECT * FROM t1 WHERE a IN (
|
||||
?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10,
|
||||
?11, ?12, ?13, ?14, ?15, ?16, ?17, ?18, ?19, ?20,
|
||||
?21, ?22, ?23, ?24, ?25, ?26, ?27, ?28, ?29, ?30, ?31
|
||||
) AND b>?32;
|
||||
} -1 dummy]
|
||||
sqlite3_expired $S
|
||||
} {0}
|
||||
do_test analyze3-3.5.2 {
|
||||
sqlite3_bind_text $S 31 "abc" 3
|
||||
sqlite3_expired $S
|
||||
} {0}
|
||||
do_test analyze3-3.5.3 {
|
||||
sqlite3_bind_text $S 32 "def" 3
|
||||
sqlite3_expired $S
|
||||
} {1}
|
||||
do_test analyze3-3.5.5 {
|
||||
sqlite3_finalize $S
|
||||
} {SQLITE_OK}
|
||||
|
||||
do_test analyze3-3.6.1 {
|
||||
set S [sqlite3_prepare_v2 db {
|
||||
SELECT * FROM t1 WHERE a IN (
|
||||
?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10,
|
||||
?11, ?12, ?13, ?14, ?15, ?16, ?17, ?18, ?19, ?20,
|
||||
?21, ?22, ?23, ?24, ?25, ?26, ?27, ?28, ?29, ?30, ?31, ?32
|
||||
) AND b>?33;
|
||||
} -1 dummy]
|
||||
sqlite3_expired $S
|
||||
} {0}
|
||||
do_test analyze3-3.6.2 {
|
||||
sqlite3_bind_text $S 32 "abc" 3
|
||||
sqlite3_expired $S
|
||||
} {1}
|
||||
do_test analyze3-3.6.3 {
|
||||
sqlite3_bind_text $S 33 "def" 3
|
||||
sqlite3_expired $S
|
||||
} {1}
|
||||
do_test analyze3-3.6.5 {
|
||||
sqlite3_finalize $S
|
||||
} {SQLITE_OK}
|
||||
|
||||
do_test analyze3-3.7.1 {
|
||||
set S [sqlite3_prepare_v2 db {
|
||||
SELECT * FROM t1 WHERE a IN (
|
||||
?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?33,
|
||||
?11, ?12, ?13, ?14, ?15, ?16, ?17, ?18, ?19, ?20,
|
||||
?21, ?22, ?23, ?24, ?25, ?26, ?27, ?28, ?29, ?30, ?31, ?32
|
||||
) AND b>?10;
|
||||
} -1 dummy]
|
||||
sqlite3_expired $S
|
||||
} {0}
|
||||
do_test analyze3-3.7.2 {
|
||||
sqlite3_bind_text $S 32 "abc" 3
|
||||
sqlite3_expired $S
|
||||
} {0}
|
||||
do_test analyze3-3.7.3 {
|
||||
sqlite3_bind_text $S 33 "def" 3
|
||||
sqlite3_expired $S
|
||||
} {0}
|
||||
do_test analyze3-3.7.4 {
|
||||
sqlite3_bind_text $S 10 "def" 3
|
||||
sqlite3_expired $S
|
||||
} {1}
|
||||
do_test analyze3-3.7.6 {
|
||||
sqlite3_finalize $S
|
||||
} {SQLITE_OK}
|
||||
|
||||
do_test analyze3-3.8.1 {
|
||||
execsql {
|
||||
CREATE TABLE t4(x, y TEXT COLLATE NOCASE);
|
||||
CREATE INDEX i4 ON t4(y);
|
||||
}
|
||||
} {}
|
||||
do_test analyze3-3.8.2 {
|
||||
set S [sqlite3_prepare_v2 db {
|
||||
SELECT * FROM t4 WHERE x != ? AND y LIKE ?
|
||||
} -1 dummy]
|
||||
sqlite3_expired $S
|
||||
} {0}
|
||||
do_test analyze3-3.8.3 {
|
||||
sqlite3_bind_text $S 1 "abc" 3
|
||||
sqlite3_expired $S
|
||||
} {0}
|
||||
do_test analyze3-3.8.4 {
|
||||
sqlite3_bind_text $S 2 "def" 3
|
||||
sqlite3_expired $S
|
||||
} {1}
|
||||
do_test analyze3-3.8.7 {
|
||||
sqlite3_bind_text $S 2 "ghi%" 4
|
||||
sqlite3_expired $S
|
||||
} {1}
|
||||
do_test analyze3-3.8.8 {
|
||||
sqlite3_expired $S
|
||||
} {1}
|
||||
do_test analyze3-3.8.9 {
|
||||
sqlite3_bind_text $S 2 "ghi%def" 7
|
||||
sqlite3_expired $S
|
||||
} {1}
|
||||
do_test analyze3-3.8.10 {
|
||||
sqlite3_expired $S
|
||||
} {1}
|
||||
do_test analyze3-3.8.11 {
|
||||
sqlite3_bind_text $S 2 "%ab" 3
|
||||
sqlite3_expired $S
|
||||
} {1}
|
||||
do_test analyze3-3.8.12 {
|
||||
sqlite3_expired $S
|
||||
} {1}
|
||||
do_test analyze3-3.8.12 {
|
||||
sqlite3_bind_text $S 2 "%de" 3
|
||||
sqlite3_expired $S
|
||||
} {1}
|
||||
do_test analyze3-3.8.13 {
|
||||
sqlite3_expired $S
|
||||
} {1}
|
||||
do_test analyze3-3.8.14 {
|
||||
sqlite3_finalize $S
|
||||
} {SQLITE_OK}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# These tests check that errors encountered while repreparing an SQL
|
||||
# statement within sqlite3Reprepare() are handled correctly.
|
||||
#
|
||||
|
||||
# Check a schema error.
|
||||
#
|
||||
do_test analyze3-4.1.1 {
|
||||
set S [sqlite3_prepare_v2 db "SELECT * FROM t1 WHERE a=? AND b>?" -1 dummy]
|
||||
sqlite3_step $S
|
||||
} {SQLITE_DONE}
|
||||
do_test analyze3-4.1.2 {
|
||||
sqlite3_reset $S
|
||||
sqlite3_bind_text $S 2 "abc" 3
|
||||
execsql { DROP TABLE t1 }
|
||||
sqlite3_step $S
|
||||
} {SQLITE_ERROR}
|
||||
do_test analyze3-4.1.3 {
|
||||
sqlite3_finalize $S
|
||||
} {SQLITE_ERROR}
|
||||
|
||||
# Check an authorization error.
|
||||
#
|
||||
do_test analyze3-4.2.1 {
|
||||
execsql {
|
||||
BEGIN;
|
||||
CREATE TABLE t1(a, b, c);
|
||||
CREATE INDEX i1 ON t1(b);
|
||||
}
|
||||
for {set i 0} {$i < 100} {incr i} {
|
||||
execsql { INSERT INTO t1 VALUES($i, $i, $i) }
|
||||
}
|
||||
execsql COMMIT
|
||||
execsql ANALYZE
|
||||
set S [sqlite3_prepare_v2 db "SELECT * FROM t1 WHERE a=? AND b>?" -1 dummy]
|
||||
sqlite3_step $S
|
||||
} {SQLITE_DONE}
|
||||
db auth auth
|
||||
proc auth {args} {
|
||||
if {[lindex $args 0] == "SQLITE_READ"} {return SQLITE_DENY}
|
||||
return SQLITE_OK
|
||||
}
|
||||
do_test analyze3-4.2.2 {
|
||||
sqlite3_reset $S
|
||||
sqlite3_bind_text $S 2 "abc" 3
|
||||
sqlite3_step $S
|
||||
} {SQLITE_AUTH}
|
||||
do_test analyze3-4.2.4 {
|
||||
sqlite3_finalize $S
|
||||
} {SQLITE_AUTH}
|
||||
|
||||
# Check the effect of an authorization error that occurs in a re-prepare
|
||||
# performed by sqlite3_step() is the same as one that occurs within
|
||||
# sqlite3Reprepare().
|
||||
#
|
||||
do_test analyze3-4.3.1 {
|
||||
db auth {}
|
||||
set S [sqlite3_prepare_v2 db "SELECT * FROM t1 WHERE a=? AND b>?" -1 dummy]
|
||||
execsql { CREATE TABLE t2(d, e, f) }
|
||||
db auth auth
|
||||
sqlite3_step $S
|
||||
} {SQLITE_AUTH}
|
||||
do_test analyze3-4.3.2 {
|
||||
sqlite3_finalize $S
|
||||
} {SQLITE_AUTH}
|
||||
db auth {}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test that modifying bound variables using the clear_bindings() or
|
||||
# transfer_bindings() APIs works.
|
||||
#
|
||||
# analyze3-5.1.*: sqlite3_clear_bindings()
|
||||
# analyze3-5.2.*: sqlite3_transfer_bindings()
|
||||
#
|
||||
do_test analyze3-5.1.1 {
|
||||
drop_all_tables
|
||||
execsql {
|
||||
CREATE TABLE t1(x TEXT COLLATE NOCASE);
|
||||
CREATE INDEX i1 ON t1(x);
|
||||
INSERT INTO t1 VALUES('aaa');
|
||||
INSERT INTO t1 VALUES('abb');
|
||||
INSERT INTO t1 VALUES('acc');
|
||||
INSERT INTO t1 VALUES('baa');
|
||||
INSERT INTO t1 VALUES('bbb');
|
||||
INSERT INTO t1 VALUES('bcc');
|
||||
}
|
||||
|
||||
set S [sqlite3_prepare_v2 db "SELECT * FROM t1 WHERE x LIKE ?" -1 dummy]
|
||||
sqlite3_bind_text $S 1 "a%" 2
|
||||
set R [list]
|
||||
while { "SQLITE_ROW" == [sqlite3_step $S] } {
|
||||
lappend R [sqlite3_column_text $S 0]
|
||||
}
|
||||
concat [sqlite3_reset $S] $R
|
||||
} {SQLITE_OK aaa abb acc}
|
||||
do_test analyze3-5.1.2 {
|
||||
sqlite3_clear_bindings $S
|
||||
set R [list]
|
||||
while { "SQLITE_ROW" == [sqlite3_step $S] } {
|
||||
lappend R [sqlite3_column_text $S 0]
|
||||
}
|
||||
concat [sqlite3_reset $S] $R
|
||||
} {SQLITE_OK}
|
||||
do_test analyze3-5.1.3 {
|
||||
sqlite3_finalize $S
|
||||
} {SQLITE_OK}
|
||||
|
||||
do_test analyze3-5.1.1 {
|
||||
set S1 [sqlite3_prepare_v2 db "SELECT * FROM t1 WHERE x LIKE ?" -1 dummy]
|
||||
sqlite3_bind_text $S1 1 "b%" 2
|
||||
set R [list]
|
||||
while { "SQLITE_ROW" == [sqlite3_step $S1] } {
|
||||
lappend R [sqlite3_column_text $S1 0]
|
||||
}
|
||||
concat [sqlite3_reset $S1] $R
|
||||
} {SQLITE_OK baa bbb bcc}
|
||||
|
||||
do_test analyze3-5.1.2 {
|
||||
set S2 [sqlite3_prepare_v2 db "SELECT * FROM t1 WHERE x = ?" -1 dummy]
|
||||
sqlite3_bind_text $S2 1 "a%" 2
|
||||
sqlite3_transfer_bindings $S2 $S1
|
||||
set R [list]
|
||||
while { "SQLITE_ROW" == [sqlite3_step $S1] } {
|
||||
lappend R [sqlite3_column_text $S1 0]
|
||||
}
|
||||
concat [sqlite3_reset $S1] $R
|
||||
} {SQLITE_OK aaa abb acc}
|
||||
do_test analyze3-5.1.3 {
|
||||
sqlite3_finalize $S2
|
||||
sqlite3_finalize $S1
|
||||
} {SQLITE_OK}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
do_test analyze3-6.1 {
|
||||
execsql { DROP TABLE IF EXISTS t1 }
|
||||
execsql BEGIN
|
||||
execsql { CREATE TABLE t1(a, b, c) }
|
||||
for {set i 0} {$i < 1000} {incr i} {
|
||||
execsql "INSERT INTO t1 VALUES([expr $i/100], 'x', [expr $i/10])"
|
||||
}
|
||||
execsql {
|
||||
CREATE INDEX i1 ON t1(a, b);
|
||||
CREATE INDEX i2 ON t1(c);
|
||||
}
|
||||
execsql COMMIT
|
||||
execsql ANALYZE
|
||||
} {}
|
||||
|
||||
do_eqp_test analyze3-6-3 {
|
||||
SELECT * FROM t1 WHERE a = 5 AND c = 13;
|
||||
} {SEARCH t1 USING INDEX i2 (c=?)}
|
||||
|
||||
do_eqp_test analyze3-6-2 {
|
||||
SELECT * FROM t1 WHERE a = 5 AND b > 'w' AND c = 13;
|
||||
} {SEARCH t1 USING INDEX i2 (c=?)}
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# 2015-04-20.
|
||||
# Memory leak in sqlite3Stat4ProbeFree(). (Discovered while fuzzing.)
|
||||
#
|
||||
do_execsql_test analyze-7.1 {
|
||||
DROP TABLE IF EXISTS t1;
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
|
||||
INSERT INTO t1 VALUES(1,1,'0000');
|
||||
CREATE INDEX t0b ON t1(b);
|
||||
ANALYZE;
|
||||
SELECT c FROM t1 WHERE b=3 AND a BETWEEN 30 AND hex(1);
|
||||
} {}
|
||||
|
||||
# At one point duplicate stat1 entries were causing a memory leak.
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 7.2 {
|
||||
CREATE TABLE t1(a,b,c);
|
||||
CREATE INDEX t1a ON t1(a);
|
||||
ANALYZE;
|
||||
SELECT * FROM sqlite_stat1;
|
||||
INSERT INTO sqlite_stat1(tbl,idx,stat) VALUES('t1','t1a','12000');
|
||||
INSERT INTO sqlite_stat1(tbl,idx,stat) VALUES('t1','t1a','12000');
|
||||
ANALYZE sqlite_master;
|
||||
}
|
||||
|
||||
finish_test
|
117
testdata/tcl/analyze4.test
vendored
Normal file
117
testdata/tcl/analyze4.test
vendored
Normal file
|
@ -0,0 +1,117 @@
|
|||
# 2011 January 04
|
||||
#
|
||||
# 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 regression tests for SQLite library. This file
|
||||
# implements tests for ANALYZE to verify that multiple rows containing
|
||||
# a NULL value count as distinct rows for the purposes of analyze
|
||||
# statistics.
|
||||
#
|
||||
# Also include test cases for collating sequences on indices.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
# If SQLITE_OMIT_ALTERTABLE is defined, omit this file.
|
||||
ifcapable !altertable {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
do_test analyze4-1.0 {
|
||||
db eval {
|
||||
CREATE TABLE t1(a,b);
|
||||
CREATE INDEX t1a ON t1(a);
|
||||
CREATE INDEX t1b ON t1(b);
|
||||
INSERT INTO t1 VALUES(1,NULL);
|
||||
INSERT INTO t1 SELECT a+1, b FROM t1;
|
||||
INSERT INTO t1 SELECT a+2, b FROM t1;
|
||||
INSERT INTO t1 SELECT a+4, b FROM t1;
|
||||
INSERT INTO t1 SELECT a+8, b FROM t1;
|
||||
INSERT INTO t1 SELECT a+16, b FROM t1;
|
||||
INSERT INTO t1 SELECT a+32, b FROM t1;
|
||||
INSERT INTO t1 SELECT a+64, b FROM t1;
|
||||
ANALYZE;
|
||||
}
|
||||
|
||||
# Should choose the t1a index since it is more specific than t1b.
|
||||
db eval {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE a=5 AND b IS NULL}
|
||||
} {/*SEARCH t1 USING INDEX t1a (a=?)*/}
|
||||
|
||||
# Verify that the t1b index shows that it does not narrow down the
|
||||
# search any at all.
|
||||
#
|
||||
do_test analyze4-1.1 {
|
||||
db eval {
|
||||
SELECT idx, stat FROM sqlite_stat1 WHERE tbl='t1' ORDER BY idx;
|
||||
}
|
||||
} {t1a {128 1} t1b {128 128}}
|
||||
|
||||
# Change half of the b values from NULL to a constant. Verify
|
||||
# that the number of rows selected in stat1 is half the total
|
||||
# number of rows.
|
||||
#
|
||||
do_test analyze4-1.2 {
|
||||
db eval {
|
||||
UPDATE t1 SET b='x' WHERE a%2;
|
||||
ANALYZE;
|
||||
SELECT idx, stat FROM sqlite_stat1 WHERE tbl='t1' ORDER BY idx;
|
||||
}
|
||||
} {t1a {128 1} t1b {128 64}}
|
||||
|
||||
# Change the t1.b values all back to NULL. Add columns t1.c and t1.d.
|
||||
# Create a multi-column indices using t1.b and verify that ANALYZE
|
||||
# processes them correctly.
|
||||
#
|
||||
do_test analyze4-1.3 {
|
||||
db eval {
|
||||
UPDATE t1 SET b=NULL;
|
||||
ALTER TABLE t1 ADD COLUMN c;
|
||||
ALTER TABLE t1 ADD COLUMN d;
|
||||
UPDATE t1 SET c=a/4, d=a/2;
|
||||
CREATE INDEX t1bcd ON t1(b,c,d);
|
||||
CREATE INDEX t1cdb ON t1(c,d,b);
|
||||
CREATE INDEX t1cbd ON t1(c,b,d);
|
||||
ANALYZE;
|
||||
SELECT idx, stat FROM sqlite_stat1 WHERE tbl='t1' ORDER BY idx;
|
||||
}
|
||||
} {t1a {128 1} t1b {128 128} t1bcd {128 128 4 2} t1cbd {128 4 4 2} t1cdb {128 4 2 2}}
|
||||
|
||||
# Verify that collating sequences are taken into account when computing
|
||||
# ANALYZE statistics.
|
||||
#
|
||||
do_test analyze4-2.0 {
|
||||
db eval {
|
||||
CREATE TABLE t2(
|
||||
x INTEGER PRIMARY KEY,
|
||||
a TEXT COLLATE nocase,
|
||||
b TEXT COLLATE rtrim,
|
||||
c TEXT COLLATE binary
|
||||
);
|
||||
CREATE INDEX t2a ON t2(a);
|
||||
CREATE INDEX t2b ON t2(b);
|
||||
CREATE INDEX t2c ON t2(c);
|
||||
CREATE INDEX t2c2 ON t2(c COLLATE nocase);
|
||||
CREATE INDEX t2c3 ON t2(c COLLATE rtrim);
|
||||
INSERT INTO t2 VALUES(1, 'abc', 'abc', 'abc');
|
||||
INSERT INTO t2 VALUES(2, 'abC', 'abC', 'abC');
|
||||
INSERT INTO t2 VALUES(3, 'abc ', 'abc ', 'abc ');
|
||||
INSERT INTO t2 VALUES(4, 'abC ', 'abC ', 'abC ');
|
||||
INSERT INTO t2 VALUES(5, 'aBc', 'aBc', 'aBc');
|
||||
INSERT INTO t2 VALUES(6, 'aBC', 'aBC', 'aBC');
|
||||
INSERT INTO t2 VALUES(7, 'aBc ', 'aBc ', 'aBc ');
|
||||
INSERT INTO t2 VALUES(8, 'aBC ', 'aBC ', 'aBC ');
|
||||
ANALYZE;
|
||||
SELECT idx, stat FROM sqlite_stat1 WHERE tbl='t2' ORDER BY idx;
|
||||
}
|
||||
} {t2a {8 4} t2b {8 2} t2c {8 1} t2c2 {8 4} t2c3 {8 2}}
|
||||
|
||||
finish_test
|
247
testdata/tcl/analyze5.test
vendored
Normal file
247
testdata/tcl/analyze5.test
vendored
Normal file
|
@ -0,0 +1,247 @@
|
|||
# 2011 January 19
|
||||
#
|
||||
# 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 tests for SQLite library. The focus of the tests
|
||||
# in this file is the use of the sqlite_stat4 histogram data on tables
|
||||
# with many repeated values and only a few distinct values.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
ifcapable !stat4 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
set testprefix analyze5
|
||||
|
||||
proc eqp {sql {db db}} {
|
||||
uplevel execsql [list "EXPLAIN QUERY PLAN $sql"] $db
|
||||
}
|
||||
|
||||
proc alpha {blob} {
|
||||
set ret ""
|
||||
foreach c [split $blob {}] {
|
||||
if {[string is alpha $c]} {append ret $c}
|
||||
}
|
||||
return $ret
|
||||
}
|
||||
db func alpha alpha
|
||||
|
||||
db func lindex lindex
|
||||
|
||||
unset -nocomplain i t u v w x y z
|
||||
do_test analyze5-1.0 {
|
||||
db eval {CREATE TABLE t1(t,u,v TEXT COLLATE nocase,w,x,y,z)}
|
||||
for {set i 0} {$i < 1000} {incr i} {
|
||||
set y [expr {$i>=25 && $i<=50}]
|
||||
set z [expr {($i>=400) + ($i>=700) + ($i>=875)}]
|
||||
set x $z
|
||||
set w $z
|
||||
set t [expr {$z+0.5}]
|
||||
switch $z {
|
||||
0 {set u "alpha"; unset x}
|
||||
1 {set u "bravo"}
|
||||
2 {set u "charlie"}
|
||||
3 {set u "delta"; unset w}
|
||||
}
|
||||
if {$i%2} {set v $u} {set v [string toupper $u]}
|
||||
db eval {INSERT INTO t1 VALUES($t,$u,$v,$w,$x,$y,$z)}
|
||||
}
|
||||
db eval {
|
||||
CREATE INDEX t1t ON t1(t); -- 0.5, 1.5, 2.5, and 3.5
|
||||
CREATE INDEX t1u ON t1(u); -- text
|
||||
CREATE INDEX t1v ON t1(v); -- mixed case text
|
||||
CREATE INDEX t1w ON t1(w); -- integers 0, 1, 2 and a few NULLs
|
||||
CREATE INDEX t1x ON t1(x); -- integers 1, 2, 3 and many NULLs
|
||||
CREATE INDEX t1y ON t1(y); -- integers 0 and very few 1s
|
||||
CREATE INDEX t1z ON t1(z); -- integers 0, 1, 2, and 3
|
||||
ANALYZE;
|
||||
}
|
||||
db eval {
|
||||
SELECT DISTINCT lindex(test_decode(sample),0)
|
||||
FROM sqlite_stat4 WHERE idx='t1u' ORDER BY nlt;
|
||||
}
|
||||
} {alpha bravo charlie delta}
|
||||
|
||||
do_test analyze5-1.1 {
|
||||
db eval {
|
||||
SELECT DISTINCT lower(lindex(test_decode(sample), 0))
|
||||
FROM sqlite_stat4 WHERE idx='t1v' ORDER BY 1
|
||||
}
|
||||
} {alpha bravo charlie delta}
|
||||
do_test analyze5-1.2 {
|
||||
db eval {SELECT idx, count(*) FROM sqlite_stat4 GROUP BY 1 ORDER BY 1}
|
||||
} {t1t 8 t1u 8 t1v 8 t1w 8 t1x 8 t1y 9 t1z 8}
|
||||
|
||||
# Verify that range queries generate the correct row count estimates
|
||||
#
|
||||
foreach {testid where index rows} {
|
||||
1 {z>=0 AND z<=0} t1z 400
|
||||
2 {z>=1 AND z<=1} t1z 300
|
||||
3 {z>=2 AND z<=2} t1z 175
|
||||
4 {z>=3 AND z<=3} t1z 125
|
||||
5 {z>=4 AND z<=4} t1z 1
|
||||
6 {z>=-1 AND z<=-1} t1z 1
|
||||
7 {z>1 AND z<3} t1z 175
|
||||
8 {z>0 AND z<100} t1z 600
|
||||
9 {z>=1 AND z<100} t1z 600
|
||||
10 {z>1 AND z<100} t1z 300
|
||||
11 {z>=2 AND z<100} t1z 300
|
||||
12 {z>2 AND z<100} t1z 125
|
||||
13 {z>=3 AND z<100} t1z 125
|
||||
14 {z>3 AND z<100} t1z 1
|
||||
15 {z>=4 AND z<100} t1z 1
|
||||
16 {z>=-100 AND z<=-1} t1z 1
|
||||
17 {z>=-100 AND z<=0} t1z 400
|
||||
18 {z>=-100 AND z<0} t1z 1
|
||||
19 {z>=-100 AND z<=1} t1z 700
|
||||
20 {z>=-100 AND z<2} t1z 700
|
||||
21 {z>=-100 AND z<=2} t1z 875
|
||||
22 {z>=-100 AND z<3} t1z 875
|
||||
|
||||
31 {z>=0.0 AND z<=0.0} t1z 400
|
||||
32 {z>=1.0 AND z<=1.0} t1z 300
|
||||
33 {z>=2.0 AND z<=2.0} t1z 175
|
||||
34 {z>=3.0 AND z<=3.0} t1z 125
|
||||
35 {z>=4.0 AND z<=4.0} t1z 1
|
||||
36 {z>=-1.0 AND z<=-1.0} t1z 1
|
||||
37 {z>1.5 AND z<3.0} t1z 174
|
||||
38 {z>0.5 AND z<100} t1z 599
|
||||
39 {z>=1.0 AND z<100} t1z 600
|
||||
40 {z>1.5 AND z<100} t1z 299
|
||||
41 {z>=2.0 AND z<100} t1z 300
|
||||
42 {z>2.1 AND z<100} t1z 124
|
||||
43 {z>=3.0 AND z<100} t1z 125
|
||||
44 {z>3.2 AND z<100} t1z 1
|
||||
45 {z>=4.0 AND z<100} t1z 1
|
||||
46 {z>=-100 AND z<=-1.0} t1z 1
|
||||
47 {z>=-100 AND z<=0.0} t1z 400
|
||||
48 {z>=-100 AND z<0.0} t1z 1
|
||||
49 {z>=-100 AND z<=1.0} t1z 700
|
||||
50 {z>=-100 AND z<2.0} t1z 700
|
||||
51 {z>=-100 AND z<=2.0} t1z 875
|
||||
52 {z>=-100 AND z<3.0} t1z 875
|
||||
|
||||
101 {z=-1} t1z 1
|
||||
102 {z=0} t1z 400
|
||||
103 {z=1} t1z 300
|
||||
104 {z=2} t1z 175
|
||||
105 {z=3} t1z 125
|
||||
106 {z=4} t1z 1
|
||||
107 {z=-10.0} t1z 1
|
||||
108 {z=0.0} t1z 400
|
||||
109 {z=1.0} t1z 300
|
||||
110 {z=2.0} t1z 175
|
||||
111 {z=3.0} t1z 125
|
||||
112 {z=4.0} t1z 1
|
||||
113 {z=1.5} t1z 1
|
||||
114 {z=2.5} t1z 1
|
||||
|
||||
201 {z IN (-1)} t1z 1
|
||||
202 {z IN (0)} t1z 400
|
||||
203 {z IN (1)} t1z 300
|
||||
204 {z IN (2)} t1z 175
|
||||
205 {z IN (3)} t1z 125
|
||||
206 {z IN (4)} t1z 1
|
||||
207 {z IN (0.5)} t1z 1
|
||||
208 {z IN (0,1)} t1z 700
|
||||
209 {z IN (0,1,2)} t1z 875
|
||||
210 {z IN (0,1,2,3)} {} 100
|
||||
211 {z IN (0,1,2,3,4,5)} {} 100
|
||||
212 {z IN (1,2)} t1z 475
|
||||
213 {z IN (2,3)} t1z 300
|
||||
214 {z=3 OR z=2} t1z 300
|
||||
215 {z IN (-1,3)} t1z 126
|
||||
216 {z=-1 OR z=3} t1z 126
|
||||
|
||||
300 {y=0} t1y 974
|
||||
301 {y=1} t1y 26
|
||||
302 {y=0.1} t1y 1
|
||||
|
||||
400 {x IS NULL} t1x 400
|
||||
|
||||
} {
|
||||
# Verify that the expected index is used with the expected row count
|
||||
# No longer valid due to an EXPLAIN QUERY PLAN output format change
|
||||
# do_test analyze5-1.${testid}a {
|
||||
# set x [lindex [eqp "SELECT * FROM t1 WHERE $where"] 3]
|
||||
# set idx {}
|
||||
# regexp {INDEX (t1.) } $x all idx
|
||||
# regexp {~([0-9]+) rows} $x all nrow
|
||||
# list $idx $nrow
|
||||
# } [list $index $rows]
|
||||
|
||||
# Verify that the same result is achieved regardless of whether or not
|
||||
# the index is used
|
||||
do_test analyze5-1.${testid}b {
|
||||
set w2 [string map {y +y z +z} $where]
|
||||
set a1 [db eval "SELECT rowid FROM t1 NOT INDEXED WHERE $w2\
|
||||
ORDER BY +rowid"]
|
||||
set a2 [db eval "SELECT rowid FROM t1 WHERE $where ORDER BY +rowid"]
|
||||
if {$a1==$a2} {
|
||||
set res ok
|
||||
} else {
|
||||
set res "a1=\[$a1\] a2=\[$a2\]"
|
||||
}
|
||||
set res
|
||||
} {ok}
|
||||
}
|
||||
|
||||
# Increase the number of NULLs in column x
|
||||
#
|
||||
db eval {
|
||||
UPDATE t1 SET x=NULL;
|
||||
UPDATE t1 SET x=rowid
|
||||
WHERE rowid IN (SELECT rowid FROM t1 ORDER BY random() LIMIT 5);
|
||||
ANALYZE;
|
||||
}
|
||||
|
||||
# Verify that range queries generate the correct row count estimates
|
||||
#
|
||||
foreach {testid where index rows} {
|
||||
500 {x IS NULL AND u='charlie'} t1u 17
|
||||
501 {x=1 AND u='charlie'} t1x 1
|
||||
502 {x IS NULL} t1x 995
|
||||
503 {x=1} t1x 1
|
||||
504 {x IS NOT NULL} t1x 2
|
||||
505 {+x IS NOT NULL} {} 500
|
||||
506 {upper(x) IS NOT NULL} {} 500
|
||||
|
||||
} {
|
||||
# Verify that the expected index is used with the expected row count
|
||||
# No longer valid due to an EXPLAIN QUERY PLAN format change
|
||||
# do_test analyze5-1.${testid}a {
|
||||
# set x [lindex [eqp "SELECT * FROM t1 WHERE $where"] 3]
|
||||
# set idx {}
|
||||
# regexp {INDEX (t1.) } $x all idx
|
||||
# regexp {~([0-9]+) rows} $x all nrow
|
||||
# list $idx $nrow
|
||||
# } [list $index $rows]
|
||||
|
||||
# Verify that the same result is achieved regardless of whether or not
|
||||
# the index is used
|
||||
do_test analyze5-1.${testid}b {
|
||||
set w2 [string map {y +y z +z} $where]
|
||||
set a1 [db eval "SELECT rowid FROM t1 NOT INDEXED WHERE $w2\
|
||||
ORDER BY +rowid"]
|
||||
set a2 [db eval "SELECT rowid FROM t1 WHERE $where ORDER BY +rowid"]
|
||||
if {$a1==$a2} {
|
||||
set res ok
|
||||
} else {
|
||||
set res "a1=\[$a1\] a2=\[$a2\]"
|
||||
}
|
||||
set res
|
||||
} {ok}
|
||||
}
|
||||
|
||||
finish_test
|
126
testdata/tcl/analyze6.test
vendored
Normal file
126
testdata/tcl/analyze6.test
vendored
Normal file
|
@ -0,0 +1,126 @@
|
|||
# 2011 March 3
|
||||
#
|
||||
# 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 tests for SQLite library. The focus of the tests
|
||||
# in this file a corner-case query planner optimization involving the
|
||||
# join order of two tables of different sizes.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
ifcapable !stat4 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
set testprefix analyze6
|
||||
|
||||
proc eqp {sql {db db}} {
|
||||
uplevel execsql [list "EXPLAIN QUERY PLAN $sql"] $db
|
||||
}
|
||||
|
||||
do_test analyze6-1.0 {
|
||||
db eval {
|
||||
CREATE TABLE cat(x INT, yz TEXT);
|
||||
CREATE UNIQUE INDEX catx ON cat(x);
|
||||
/* Give cat 16 unique integers */
|
||||
INSERT INTO cat(x) VALUES(1);
|
||||
INSERT INTO cat(x) VALUES(2);
|
||||
INSERT INTO cat(x) SELECT x+2 FROM cat;
|
||||
INSERT INTO cat(x) SELECT x+4 FROM cat;
|
||||
INSERT INTO cat(x) SELECT x+8 FROM cat;
|
||||
|
||||
CREATE TABLE ev(y INT);
|
||||
CREATE INDEX evy ON ev(y);
|
||||
/* ev will hold 32 copies of 16 integers found in cat */
|
||||
INSERT INTO ev SELECT x FROM cat;
|
||||
INSERT INTO ev SELECT x FROM cat;
|
||||
INSERT INTO ev SELECT y FROM ev;
|
||||
INSERT INTO ev SELECT y FROM ev;
|
||||
INSERT INTO ev SELECT y FROM ev;
|
||||
INSERT INTO ev SELECT y FROM ev;
|
||||
ANALYZE;
|
||||
SELECT count(*) FROM cat;
|
||||
SELECT count(*) FROM ev;
|
||||
}
|
||||
} {16 512}
|
||||
|
||||
# The lowest cost plan is to scan CAT and for each integer there, do a single
|
||||
# lookup of the first corresponding entry in EV then read off the equal values
|
||||
# in EV. (Prior to the 2011-03-04 enhancement to where.c, this query would
|
||||
# have used EV for the outer loop instead of CAT - which was about 3x slower.)
|
||||
#
|
||||
do_test analyze6-1.1 {
|
||||
eqp {SELECT count(*) FROM ev, cat WHERE x=y}
|
||||
} {/*SCAN cat USING COVERING INDEX catx*SEARCH ev USING COVERING INDEX evy (y=?)*/}
|
||||
|
||||
# The same plan is chosen regardless of the order of the tables in the
|
||||
# FROM clause.
|
||||
#
|
||||
do_eqp_test analyze6-1.2 {
|
||||
SELECT count(*) FROM cat, ev WHERE x=y
|
||||
} {
|
||||
QUERY PLAN
|
||||
|--SCAN cat USING COVERING INDEX catx
|
||||
`--SEARCH ev USING COVERING INDEX evy (y=?)
|
||||
}
|
||||
|
||||
|
||||
# Ticket [83ea97620bd3101645138b7b0e71c12c5498fe3d] 2011-03-30
|
||||
# If ANALYZE is run on an empty table, make sure indices are used
|
||||
# on the table.
|
||||
#
|
||||
do_test analyze6-2.1 {
|
||||
execsql {
|
||||
CREATE TABLE t201(x INTEGER PRIMARY KEY, y UNIQUE, z);
|
||||
CREATE INDEX t201z ON t201(z);
|
||||
ANALYZE;
|
||||
}
|
||||
eqp {SELECT * FROM t201 WHERE z=5}
|
||||
} {/*SEARCH t201 USING INDEX t201z (z=?)*/}
|
||||
do_test analyze6-2.2 {
|
||||
eqp {SELECT * FROM t201 WHERE y=5}
|
||||
} {/*SEARCH t201 USING INDEX sqlite_autoindex_t201_1 (y=?)*/}
|
||||
do_test analyze6-2.3 {
|
||||
eqp {SELECT * FROM t201 WHERE x=5}
|
||||
} {/*SEARCH t201 USING INTEGER PRIMARY KEY (rowid=?)*/}
|
||||
do_test analyze6-2.4 {
|
||||
execsql {
|
||||
INSERT INTO t201 VALUES(1,2,3),(2,3,4),(3,4,5);
|
||||
ANALYZE t201;
|
||||
}
|
||||
eqp {SELECT * FROM t201 WHERE z=5}
|
||||
} {/*SEARCH t201 USING INDEX t201z (z=?)*/}
|
||||
do_test analyze6-2.5 {
|
||||
eqp {SELECT * FROM t201 WHERE y=5}
|
||||
} {/*SEARCH t201 USING INDEX sqlite_autoindex_t201_1 (y=?)*/}
|
||||
do_test analyze6-2.6 {
|
||||
eqp {SELECT * FROM t201 WHERE x=5}
|
||||
} {/*SEARCH t201 USING INTEGER PRIMARY KEY (rowid=?)*/}
|
||||
do_test analyze6-2.7 {
|
||||
execsql {
|
||||
INSERT INTO t201 VALUES(4,5,7);
|
||||
INSERT INTO t201 SELECT x+100, y+100, z+100 FROM t201;
|
||||
INSERT INTO t201 SELECT x+200, y+200, z+200 FROM t201;
|
||||
INSERT INTO t201 SELECT x+400, y+400, z+400 FROM t201;
|
||||
ANALYZE t201;
|
||||
}
|
||||
eqp {SELECT * FROM t201 WHERE z=5}
|
||||
} {/*SEARCH t201 USING INDEX t201z (z=?)*/}
|
||||
do_test analyze6-2.8 {
|
||||
eqp {SELECT * FROM t201 WHERE y=5}
|
||||
} {/*SEARCH t201 USING INDEX sqlite_autoindex_t201_1 (y=?)*/}
|
||||
do_test analyze6-2.9 {
|
||||
eqp {SELECT * FROM t201 WHERE x=5}
|
||||
} {/*SEARCH t201 USING INTEGER PRIMARY KEY (rowid=?)*/}
|
||||
|
||||
finish_test
|
114
testdata/tcl/analyze7.test
vendored
Normal file
114
testdata/tcl/analyze7.test
vendored
Normal file
|
@ -0,0 +1,114 @@
|
|||
# 2011 April 1
|
||||
#
|
||||
# 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 regression tests for SQLite library.
|
||||
# This file implements tests for the ANALYZE command when an idnex
|
||||
# name is given as the argument.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
# There is nothing to test if ANALYZE is disable for this build.
|
||||
#
|
||||
ifcapable {!analyze||!vtab} {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
# Generate some test data
|
||||
#
|
||||
do_test analyze7-1.0 {
|
||||
load_static_extension db wholenumber
|
||||
execsql {
|
||||
CREATE TABLE t1(a,b,c,d);
|
||||
CREATE INDEX t1a ON t1(a);
|
||||
CREATE INDEX t1b ON t1(b);
|
||||
CREATE INDEX t1cd ON t1(c,d);
|
||||
CREATE VIRTUAL TABLE nums USING wholenumber;
|
||||
INSERT INTO t1 SELECT value, value, value/100, value FROM nums
|
||||
WHERE value BETWEEN 1 AND 256;
|
||||
EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE a=123;
|
||||
}
|
||||
} {/*SEARCH t1 USING INDEX t1a (a=?)*/}
|
||||
do_test analyze7-1.1 {
|
||||
execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE b=123;}
|
||||
} {/*SEARCH t1 USING INDEX t1b (b=?)*/}
|
||||
do_test analyze7-1.2 {
|
||||
execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=2;}
|
||||
} {/*SEARCH t1 USING INDEX t1cd (c=?)*/}
|
||||
|
||||
# Run an analyze on one of the three indices. Verify that this
|
||||
# effects the row-count estimate on the one query that uses that
|
||||
# one index.
|
||||
#
|
||||
do_test analyze7-2.0 {
|
||||
execsql {ANALYZE t1a;}
|
||||
db cache flush
|
||||
execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE a=123;}
|
||||
} {/*SEARCH t1 USING INDEX t1a (a=?)*/}
|
||||
do_test analyze7-2.1 {
|
||||
execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE b=123;}
|
||||
} {/*SEARCH t1 USING INDEX t1b (b=?)*/}
|
||||
do_test analyze7-2.2 {
|
||||
execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=2;}
|
||||
} {/*SEARCH t1 USING INDEX t1cd (c=?)*/}
|
||||
|
||||
# Verify that since the query planner now things that t1a is more
|
||||
# selective than t1b, it prefers to use t1a.
|
||||
#
|
||||
do_test analyze7-2.3 {
|
||||
execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE a=123 AND b=123}
|
||||
} {/*SEARCH t1 USING INDEX t1a (a=?)*/}
|
||||
|
||||
# Run an analysis on another of the three indices. Verify that this
|
||||
# new analysis works and does not disrupt the previous analysis.
|
||||
#
|
||||
do_test analyze7-3.0 {
|
||||
execsql {ANALYZE t1cd;}
|
||||
db cache flush;
|
||||
execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE a=123;}
|
||||
} {/*SEARCH t1 USING INDEX t1a (a=?)*/}
|
||||
do_test analyze7-3.1 {
|
||||
execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE b=123;}
|
||||
} {/*SEARCH t1 USING INDEX t1b (b=?)*/}
|
||||
do_test analyze7-3.2.1 {
|
||||
execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=?;}
|
||||
} {/*SEARCH t1 USING INDEX t1cd (c=?)*/}
|
||||
ifcapable stat4 {
|
||||
# If ENABLE_STAT4 is defined, SQLite comes up with a different estimated
|
||||
# row count for (c=2) than it does for (c=?).
|
||||
do_test analyze7-3.2.2 {
|
||||
execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=2;}
|
||||
} {/*SEARCH t1 USING INDEX t1cd (c=?)*/}
|
||||
} else {
|
||||
# If ENABLE_STAT4 is not defined, the expected row count for (c=2) is the
|
||||
# same as that for (c=?).
|
||||
do_test analyze7-3.2.3 {
|
||||
execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=2;}
|
||||
} {/*SEARCH t1 USING INDEX t1cd (c=?)*/}
|
||||
}
|
||||
do_test analyze7-3.3 {
|
||||
execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE a=123 AND b=123}
|
||||
} {/*SEARCH t1 USING INDEX t1a (a=?)*/}
|
||||
|
||||
ifcapable {!stat4} {
|
||||
do_test analyze7-3.4 {
|
||||
execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=123 AND b=123}
|
||||
} {/*SEARCH t1 USING INDEX t1b (b=?)*/}
|
||||
do_test analyze7-3.5 {
|
||||
execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE a=123 AND c=123}
|
||||
} {/*SEARCH t1 USING INDEX t1a (a=?)*/}
|
||||
}
|
||||
do_test analyze7-3.6 {
|
||||
execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=123 AND d=123 AND b=123}
|
||||
} {/*SEARCH t1 USING INDEX t1cd (c=? AND d=?)*/}
|
||||
|
||||
finish_test
|
115
testdata/tcl/analyze8.test
vendored
Normal file
115
testdata/tcl/analyze8.test
vendored
Normal file
|
@ -0,0 +1,115 @@
|
|||
# 2011 August 13
|
||||
#
|
||||
# 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 tests for SQLite library. The focus of the tests
|
||||
# in this file is testing the capabilities of sqlite_stat4.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
ifcapable !stat4 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
set testprefix analyze8
|
||||
|
||||
proc eqp {sql {db db}} {
|
||||
uplevel execsql [list "EXPLAIN QUERY PLAN $sql"] $db
|
||||
}
|
||||
|
||||
# Scenario:
|
||||
#
|
||||
# Two indices. One has mostly singleton entries, but for a few
|
||||
# values there are hundreds of entries. The other has 10-20
|
||||
# entries per value.
|
||||
#
|
||||
# Verify that the query planner chooses the first index for the singleton
|
||||
# entries and the second index for the others.
|
||||
#
|
||||
do_test 1.0 {
|
||||
db eval {
|
||||
CREATE TABLE t1(a,b,c,d);
|
||||
CREATE INDEX t1a ON t1(a);
|
||||
CREATE INDEX t1b ON t1(b);
|
||||
CREATE INDEX t1c ON t1(c);
|
||||
}
|
||||
for {set i 0} {$i<1000} {incr i} {
|
||||
if {$i%2==0} {set a $i} {set a [expr {($i%8)*100}]}
|
||||
set b [expr {$i/10}]
|
||||
set c [expr {$i/8}]
|
||||
set c [expr {$c*$c*$c}]
|
||||
db eval {INSERT INTO t1 VALUES($a,$b,$c,$i)}
|
||||
}
|
||||
db eval {ANALYZE}
|
||||
} {}
|
||||
|
||||
# The a==100 comparison is expensive because there are many rows
|
||||
# with a==100. And so for those cases, choose the t1b index.
|
||||
#
|
||||
# Buf ro a==99 and a==101, there are far fewer rows so choose
|
||||
# the t1a index.
|
||||
#
|
||||
do_test 1.1 {
|
||||
eqp {SELECT * FROM t1 WHERE a=100 AND b=55}
|
||||
} {/*SEARCH t1 USING INDEX t1b (b=?)*/}
|
||||
do_test 1.2 {
|
||||
eqp {SELECT * FROM t1 WHERE a=99 AND b=55}
|
||||
} {/*SEARCH t1 USING INDEX t1a (a=?)*/}
|
||||
do_test 1.3 {
|
||||
eqp {SELECT * FROM t1 WHERE a=101 AND b=55}
|
||||
} {/*SEARCH t1 USING INDEX t1a (a=?)*/}
|
||||
do_test 1.4 {
|
||||
eqp {SELECT * FROM t1 WHERE a=100 AND b=56}
|
||||
} {/*SEARCH t1 USING INDEX t1b (b=?)*/}
|
||||
do_test 1.5 {
|
||||
eqp {SELECT * FROM t1 WHERE a=99 AND b=56}
|
||||
} {/*SEARCH t1 USING INDEX t1a (a=?)*/}
|
||||
do_test 1.6 {
|
||||
eqp {SELECT * FROM t1 WHERE a=101 AND b=56}
|
||||
} {/*SEARCH t1 USING INDEX t1a (a=?)*/}
|
||||
do_test 2.1 {
|
||||
eqp {SELECT * FROM t1 WHERE a=100 AND b BETWEEN 50 AND 54}
|
||||
} {/*SEARCH t1 USING INDEX t1b (b>? AND b<?)*/}
|
||||
|
||||
# There are many more values of c between 0 and 100000 than there are
|
||||
# between 800000 and 900000. So t1c is more selective for the latter
|
||||
# range.
|
||||
#
|
||||
# Test 3.2 is a little unstable. It depends on the planner estimating
|
||||
# that (b BETWEEN 30 AND 34) will match more rows than (c BETWEEN
|
||||
# 800000 AND 900000). Which is a pretty close call (50 vs. 32), so
|
||||
# the planner could get it wrong with an unlucky set of samples. This
|
||||
# case happens to work, but others ("b BETWEEN 40 AND 44" for example)
|
||||
# will fail.
|
||||
#
|
||||
do_execsql_test 3.0 {
|
||||
SELECT count(*) FROM t1 WHERE b BETWEEN 30 AND 34;
|
||||
SELECT count(*) FROM t1 WHERE c BETWEEN 0 AND 100000;
|
||||
SELECT count(*) FROM t1 WHERE c BETWEEN 800000 AND 900000;
|
||||
} {50 376 32}
|
||||
do_test 3.1 {
|
||||
eqp {SELECT * FROM t1 WHERE b BETWEEN 30 AND 34 AND c BETWEEN 0 AND 100000}
|
||||
} {/*SEARCH t1 USING INDEX t1b (b>? AND b<?)*/}
|
||||
do_test 3.2 {
|
||||
eqp {SELECT * FROM t1
|
||||
WHERE b BETWEEN 30 AND 34 AND c BETWEEN 800000 AND 900000}
|
||||
} {/*SEARCH t1 USING INDEX t1c (c>? AND c<?)*/}
|
||||
do_test 3.3 {
|
||||
eqp {SELECT * FROM t1 WHERE a=100 AND c BETWEEN 0 AND 100000}
|
||||
} {/*SEARCH t1 USING INDEX t1a (a=?)*/}
|
||||
do_test 3.4 {
|
||||
eqp {SELECT * FROM t1
|
||||
WHERE a=100 AND c BETWEEN 800000 AND 900000}
|
||||
} {/*SEARCH t1 USING INDEX t1c (c>? AND c<?)*/}
|
||||
|
||||
finish_test
|
1235
testdata/tcl/analyze9.test
vendored
Normal file
1235
testdata/tcl/analyze9.test
vendored
Normal file
File diff suppressed because it is too large
Load diff
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue