Skip to content

Commit 4c7deb9

Browse files
Merge remote-tracking branch 'upstream/master' into codex/fix-26759-implicit-cast
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2 parents 6e6d0ed + 1f24f30 commit 4c7deb9

7 files changed

Lines changed: 197 additions & 15 deletions

File tree

doc/docs.md

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5639,13 +5639,40 @@ instead:
56395639
- Fedora/RHEL: `sudo dnf -y install sqlite-devel`
56405640
- Arch: `sudo pacman -S sqlite`
56415641

5642+
### Interactive SQLite CLI
5643+
5644+
V includes a built-in SQLite CLI (`v sqlite`) as a V-native
5645+
replacement for `sqlite3`:
5646+
5647+
```sh
5648+
v sqlite mydb.db
5649+
```
5650+
5651+
It provides a full readline REPL with history and tab completion,
5652+
9 output modes, `.dump`, `.import`/`.export`, `.backup`, session
5653+
control, and schema tools. Run `.help` inside the REPL for the
5654+
full command list.
5655+
5656+
### Convenience Methods
5657+
5658+
The `db.sqlite` module includes helper methods for common queries:
5659+
5660+
```v ignore
5661+
db.tables()! // list all user table names
5662+
db.columns('users')! // column names for a table
5663+
db.schema('users')! // CREATE statement(s)
5664+
db.db_size()! // file size in bytes
5665+
```
5666+
56425667
### Using the self contained SQLite module
5643-
V also maintains a separate `sqlite` module, that wraps an SQLite amalgamation, but otherwise
5644-
has the same API as the `db.sqlite` module. Its benefit, is that with it, you do not need to
5645-
install a separate system level sqlite package/library on your system (which can be hard on
5646-
some systems like windows, or systems with musl for example).
5647-
Its negative is that it can make your compilations a bit slower (since it compiles SQLite
5648-
from C, in addition to your own code).
5668+
5669+
V also maintains a separate `sqlite` module, that wraps an SQLite
5670+
amalgamation, but otherwise has the same API as the `db.sqlite`
5671+
module. Its benefit is that you do not need to install a separate
5672+
system-level sqlite package (which can be hard on some systems
5673+
like Windows, or systems with musl for example). Its downside is
5674+
that it can make compilations a bit slower since it compiles
5675+
SQLite from C in addition to your own code.
56495676

56505677
To use it, do:
56515678
```sh

vlib/db/sqlite/README.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,43 @@ instead:
2121
- Fedora/RHEL: `sudo dnf -y install sqlite-devel`
2222
- Arch: `sudo pacman -S sqlite`
2323

24+
# Convenience Methods
25+
26+
The `DB` struct provides several helper methods for common
27+
introspection queries:
28+
29+
```v
30+
import db.sqlite
31+
32+
db := sqlite.connect('mydb.db') or { panic(err) }
33+
34+
// List all user tables
35+
tables := db.tables()!
36+
37+
// Get column names for a table
38+
cols := db.columns('users')!
39+
40+
// Get CREATE statements (single table or all objects)
41+
s := db.schema('users')!
42+
43+
// Database file size in bytes
44+
size := db.db_size()!
45+
```
46+
47+
# Interactive CLI
48+
49+
V includes a built-in SQLite CLI as a replacement for `sqlite3`:
50+
51+
```sh
52+
v sqlite mydb.db
53+
```
54+
55+
Features include a full readline REPL with history and tab
56+
completion, 9 output modes (`table`, `box`, `markdown`, `csv`,
57+
`json`, `line`, `html`, `insert`, `quote`), `.dump`,
58+
`.import`/`.export`, `.backup`, session control, and schema tools.
59+
Run `.help` inside the REPL for the full command list.
60+
2461
# Performance Tips
2562

2663
When performing a large amount of database calls (i.e. INSERTS), significant

vlib/db/sqlite/sqlite.c.v

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -582,6 +582,43 @@ pub fn (mut db DB) release_savepoint(savepoint string) ! {
582582
db.exec('RELEASE SAVEPOINT ${savepoint};')!
583583
}
584584

585+
// tables returns the names of all user tables in the database.
586+
pub fn (db &DB) tables() ![]string {
587+
rows := db.exec("SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' ORDER BY name")!
588+
return rows.map(it.vals[0])
589+
}
590+
591+
// columns returns the column names for the given table.
592+
pub fn (db &DB) columns(table string) ![]string {
593+
escaped := table.replace('"', '""')
594+
rows := db.exec('PRAGMA table_info("${escaped}")')!
595+
return rows.map(it.vals[1])
596+
}
597+
598+
// schema returns the CREATE statement(s) for the given table, or for all
599+
// objects if table is empty.
600+
pub fn (db &DB) schema(table string) !string {
601+
filter := if table != '' {
602+
escaped := table.replace("'", "''")
603+
"AND name='${escaped}'"
604+
} else {
605+
''
606+
}
607+
rows := db.exec("SELECT sql FROM sqlite_master WHERE type IN ('table','index','view','trigger') ${filter} AND sql IS NOT NULL ORDER BY type, name")!
608+
return rows.map(it.vals[0]).join('\n\n')
609+
}
610+
611+
// db_size returns the database file size in bytes, computed from page_count
612+
// and page_size.
613+
pub fn (db &DB) db_size() !i64 {
614+
pc := db.exec('PRAGMA page_count')!
615+
ps := db.exec('PRAGMA page_size')!
616+
if pc.len == 0 || ps.len == 0 {
617+
return 0
618+
}
619+
return pc[0].vals[0].i64() * ps[0].vals[0].i64()
620+
}
621+
585622
// reset returns the connection to initial state for reuse
586623
pub fn (mut db DB) reset() ! {
587624
}

vlib/db/sqlite/sqlite_test.v

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// vtest build: present_sqlite3?
22
import db.sqlite
33
import orm
4+
import os
45

56
type Connection = sqlite.DB
67

@@ -148,6 +149,46 @@ fn test_exec_param_many2() {
148149
db.close()!
149150
}
150151

152+
fn test_tables() {
153+
mut db := sqlite.connect(':memory:') or { panic(err) }
154+
db.exec('create table alpha (id integer)')!
155+
db.exec('create table beta (id integer)')!
156+
tbl := db.tables()!
157+
assert tbl == ['alpha', 'beta']
158+
db.close()!
159+
}
160+
161+
fn test_columns() {
162+
mut db := sqlite.connect(':memory:') or { panic(err) }
163+
db.exec('create table items (id integer primary key, name text, price real)')!
164+
cols := db.columns('items')!
165+
assert cols == ['id', 'name', 'price']
166+
db.close()!
167+
}
168+
169+
fn test_schema() {
170+
mut db := sqlite.connect(':memory:') or { panic(err) }
171+
db.exec('create table things (id integer primary key, label text)')!
172+
s := db.schema('things')!
173+
assert s.contains('CREATE TABLE things')
174+
// empty table name returns all objects
175+
all := db.schema('')!
176+
assert all.contains('CREATE TABLE things')
177+
db.close()!
178+
}
179+
180+
fn test_db_size() {
181+
tmp := os.join_path(os.temp_dir(), 'test_db_size.db')
182+
defer {
183+
os.rm(tmp) or {}
184+
}
185+
mut db := sqlite.connect(tmp) or { panic(err) }
186+
db.exec('create table t (id integer)')!
187+
sz := db.db_size()!
188+
assert sz > 0
189+
db.close()!
190+
}
191+
151192
fn test_orm_transaction_interface() {
152193
mut db := sqlite.connect(':memory:') or { panic(err) }
153194
defer {

vlib/v/gen/c/fn.v

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2878,8 +2878,8 @@ fn (mut g Gen) call_args(node ast.CallExpr) {
28782878
}
28792879
}
28802880
} else {
2881-
// passing variadic arg to another call which expects same array type
2882-
if variadic_count == 1
2881+
// passing the current variadic arg to another call which expects the same array type
2882+
if variadic_count == 1 && g.is_forwarded_variadic_arg(args[arg_nr])
28832883
&& ((args[arg_nr].typ.has_flag(.variadic) && args[arg_nr].typ == varg_type)
28842884
|| (varg_type.has_flag(.variadic)
28852885
&& args[arg_nr].typ == varg_type.clear_flag(.variadic)
@@ -2919,6 +2919,18 @@ fn (mut g Gen) call_args(node ast.CallExpr) {
29192919
}
29202920
}
29212921

2922+
fn (g &Gen) is_forwarded_variadic_arg(arg ast.CallArg) bool {
2923+
if arg.typ.has_flag(.variadic) {
2924+
return true
2925+
}
2926+
if arg.expr is ast.Ident && g.cur_fn != unsafe { nil } && g.cur_fn.is_variadic
2927+
&& g.cur_fn.params.len > 0 && arg.expr.obj is ast.Var {
2928+
var_obj := arg.expr.obj as ast.Var
2929+
return var_obj.is_arg && arg.expr.name == g.cur_fn.params.last().name
2930+
}
2931+
return false
2932+
}
2933+
29222934
// similar to `autofree_call_pregen()` but only to to handle [keep_args_alive] for C functions
29232935
fn (mut g Gen) keep_alive_call_pregen(node ast.CallExpr) int {
29242936
g.empty_line = true
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
interface VariadicValue {}
2+
3+
fn variadic_capture(values ...VariadicValue) []VariadicValue {
4+
return values
5+
}
6+
7+
fn test_variadic_interface_array_variable_as_single_arg() {
8+
nested := [VariadicValue('brother')]
9+
got := variadic_capture(nested)
10+
assert got.len == 1
11+
12+
inner := got[0] as []VariadicValue
13+
assert inner.len == 1
14+
assert (inner[0] as string) == 'brother'
15+
}
16+
17+
fn test_variadic_interface_array_literal_as_single_arg() {
18+
got := variadic_capture([VariadicValue('x')])
19+
assert got.len == 1
20+
21+
inner := got[0] as []VariadicValue
22+
assert inner.len == 1
23+
assert (inner[0] as string) == 'x'
24+
}

vlib/v/tests/typeof_test.v

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -120,17 +120,21 @@ fn test_typeof_on_sumtypes_of_structs() {
120120
assert d.type_name() == 'UnaryExpr'
121121
}
122122

123-
fn raw_sumtype_typeof(v ExprType) string {
124-
return typeof(v)
125-
}
126-
127123
fn test_raw_typeof_on_sumtypes_is_printable() {
128124
a := fexpr(1)
129125
b := fexpr(2)
130126
c := fexpr(3)
131-
assert raw_sumtype_typeof(a) == 'UnaryExpr'
132-
assert raw_sumtype_typeof(b) == 'BinExpr'
133-
assert raw_sumtype_typeof(c) == 'BoolExpr'
127+
raw_a_type := typeof(a)
128+
raw_b_type := typeof(b)
129+
raw_c_type := typeof(c)
130+
// Regresses issue #26704: raw typeof(sumtype) should be usable
131+
// when returned from a helper and printable via the stored result.
132+
println(raw_a_type)
133+
println(raw_b_type)
134+
println(raw_c_type)
135+
assert raw_a_type == 'UnaryExpr'
136+
assert raw_b_type == 'BinExpr'
137+
assert raw_c_type == 'BoolExpr'
134138
}
135139

136140
fn myfn(i int) int {

0 commit comments

Comments
 (0)