mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
Compare commits
44 Commits
93b3f1ca55
...
d53d95991d
Author | SHA1 | Date | |
---|---|---|---|
|
d53d95991d | ||
|
fde0d9fa91 | ||
|
d25e213aa8 | ||
|
7d6fd9dade | ||
|
94de6f62b2 | ||
|
3042857d6c | ||
|
ef59a72877 | ||
|
125c571d4a | ||
|
b35ad8a657 | ||
|
e03c0329c1 | ||
|
571469a6ac | ||
|
5ecbfb7bf1 | ||
|
44ed42ce06 | ||
|
a421e485fc | ||
|
3a91a5eceb | ||
|
fab915782d | ||
|
b29f3caeec | ||
|
c6ddbd308e | ||
|
d4bedebace | ||
|
b29a084257 | ||
|
7cec70e525 | ||
|
78681bf852 | ||
|
e1758bc0c5 | ||
|
15fdfd7bcf | ||
|
40dc80079d | ||
|
ac0ae1966a | ||
|
41f99c1abf | ||
|
7451178c45 | ||
|
466c80f80a | ||
|
30fc9380a1 | ||
|
dcbc9e0b9b | ||
|
c3ff4b2f85 | ||
|
bf00ac656f | ||
|
a3449098a9 | ||
|
ba1c5def77 | ||
|
7b306e9b8f | ||
|
4a543c5f51 | ||
|
64a8c14a3c | ||
|
36d45c6d14 | ||
|
8b4c3fa1d1 | ||
|
96ff3ce8f7 | ||
|
00619b0b3f | ||
|
3b377fc791 | ||
|
9cc24c5dac |
3
.github/workflows/debug.yml
vendored
3
.github/workflows/debug.yml
vendored
@ -11,9 +11,6 @@ jobs:
|
||||
VFLAGS: -cc msvc
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
- name: Build
|
||||
run: |
|
||||
echo %VFLAGS%
|
||||
|
15
.github/workflows/linux_ci.yml
vendored
15
.github/workflows/linux_ci.yml
vendored
@ -168,9 +168,6 @@ jobs:
|
||||
timeout-minutes: 121
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
@ -223,8 +220,10 @@ jobs:
|
||||
run: VTEST_JUST_ESSENTIAL=1 V_CI_CSTRICT=1 ./v -cc gcc -cstrict test-self
|
||||
- name: Build examples
|
||||
run: ./v build-examples
|
||||
- name: Build tetris.v with -autofree
|
||||
run: ./v -autofree -experimental -o tetris examples/tetris/tetris.v
|
||||
- name: Build tetris with -autofree
|
||||
run: ./v -autofree -o tetris examples/tetris/tetris.v
|
||||
- name: Build blog tutorial with -autofree
|
||||
run: ./v -autofree -o blog tutorials/building_a_simple_web_blog_with_vweb/code/blog
|
||||
- name: Build option_test.v with -autofree
|
||||
run: ./v -autofree vlib/v/tests/option_test.v
|
||||
- name: V self compilation with -parallel-cc
|
||||
@ -273,9 +272,6 @@ jobs:
|
||||
VFLAGS: -cc clang
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
@ -380,9 +376,6 @@ jobs:
|
||||
# V_CI_MUSL: 1
|
||||
# steps:
|
||||
# - uses: actions/checkout@v3
|
||||
# - uses: actions/setup-node@v3
|
||||
# with:
|
||||
# node-version: 16
|
||||
# - name: Install dependencies
|
||||
# run: |
|
||||
# sudo apt-get install --quiet -y musl musl-tools libssl-dev sqlite3 libsqlite3-dev valgrind
|
||||
|
11
.github/workflows/macos_ci.yml
vendored
11
.github/workflows/macos_ci.yml
vendored
@ -24,9 +24,6 @@ jobs:
|
||||
PKG_CONFIG_PATH: /usr/local/opt/pkgconfig:/usr/local/opt/libpq/lib/pkgconfig:/usr/local/opt/openssl@3/lib/pkgconfig:/opt/homebrew/lib/pkgconfig:/opt/homebrew/opt/libpq/lib/pkgconfig:/opt/homebrew/opt/openssl@3/lib/pkgconfig
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
echo "PKG_CONFIG_PATH is '$PKG_CONFIG_PATH'"
|
||||
@ -68,10 +65,10 @@ jobs:
|
||||
run: VJOBS=1 ./v test-self
|
||||
- name: Build examples
|
||||
run: ./v build-examples
|
||||
- name: Build examples with -autofree
|
||||
run: |
|
||||
./v -autofree -o tetris examples/tetris/tetris.v
|
||||
./v -autofree -o blog tutorials/building_a_simple_web_blog_with_vweb/code/blog
|
||||
- name: Build tetris with -autofree
|
||||
run: ./v -autofree -o tetris examples/tetris/tetris.v
|
||||
- name: Build blog tutorial with -autofree
|
||||
run: ./v -autofree -o blog tutorials/building_a_simple_web_blog_with_vweb/code/blog
|
||||
- name: Build examples with -prod
|
||||
run: |
|
||||
./v -prod examples/news_fetcher.v
|
||||
|
18
.github/workflows/sanitized_ci.yml
vendored
18
.github/workflows/sanitized_ci.yml
vendored
@ -84,9 +84,6 @@ jobs:
|
||||
VTEST_SHOW_START: 1
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
@ -112,9 +109,6 @@ jobs:
|
||||
VTEST_SHOW_START: 1
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt update
|
||||
@ -139,9 +133,6 @@ jobs:
|
||||
VTEST_SHOW_START: 1
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt update
|
||||
@ -171,9 +162,6 @@ jobs:
|
||||
VTEST_SHOW_START: 1
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
- name: Build
|
||||
run: |
|
||||
echo %VFLAGS%
|
||||
@ -203,9 +191,6 @@ jobs:
|
||||
VTEST_SHOW_START: 1
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt update
|
||||
@ -235,9 +220,6 @@ jobs:
|
||||
VTEST_SHOW_START: 1
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt update
|
||||
|
11
.github/workflows/windows_ci.yml
vendored
11
.github/workflows/windows_ci.yml
vendored
@ -24,9 +24,6 @@ jobs:
|
||||
VERBOSE_MAKE: 1
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
- name: Build
|
||||
run: |
|
||||
gcc --version
|
||||
@ -53,6 +50,8 @@ jobs:
|
||||
run: .\v.exe test-self
|
||||
# - name: Test
|
||||
# run: .\v.exe test-all
|
||||
- name: Build option_test.v with -autofree
|
||||
run: .\v.exe -autofree vlib/v/tests/option_test.v
|
||||
- name: Test time functions in a timezone UTC-12
|
||||
run: |
|
||||
tzutil /s "Dateline Standard Time"
|
||||
@ -87,9 +86,6 @@ jobs:
|
||||
VERBOSE_MAKE: 1
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
- name: Build
|
||||
run: |
|
||||
echo %VFLAGS%
|
||||
@ -137,9 +133,6 @@ jobs:
|
||||
VERBOSE_MAKE: 1
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
- name: Build with make.bat -tcc
|
||||
run: |
|
||||
.\make.bat -tcc
|
||||
|
72
cmd/tools/show_ancient_deprecations.v
Normal file
72
cmd/tools/show_ancient_deprecations.v
Normal file
@ -0,0 +1,72 @@
|
||||
import os
|
||||
import time
|
||||
|
||||
struct Context {
|
||||
mut:
|
||||
cut_time time.Time
|
||||
deprecations int
|
||||
}
|
||||
|
||||
fn (mut ctx Context) analyze_line(line string, position_file string, position_line int) {
|
||||
blame_for_time := os.execute('git blame -L${position_line} --porcelain -- ${position_file}')
|
||||
if blame_for_time.exit_code != 0 {
|
||||
return
|
||||
}
|
||||
ts := blame_for_time.output.all_after('committer-time').all_before('\n').trim_space().int()
|
||||
t := time.unix(ts)
|
||||
if ctx.cut_time < t {
|
||||
println('>>> SKIPPING since t: ${t} > ${ctx.cut_time}, line: ${line}')
|
||||
return
|
||||
}
|
||||
ctx.deprecations++
|
||||
blame_for_context := os.execute('git blame -L${position_line},+5 -- ${position_file}')
|
||||
context := blame_for_context.output.trim_space().split_into_lines()
|
||||
println('${position_file}:${position_line}: deprecation: ${ctx.deprecations}, timestamp: ${ts} - ${t}')
|
||||
for cline in context {
|
||||
println(' ${cline}')
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
if os.args.len < 2 {
|
||||
eprintln('Usage: v run cmd/tools/show_ancient_deprecations.v [DAYS]')
|
||||
exit(1)
|
||||
}
|
||||
cut_months := os.args[1].int()
|
||||
cut_time := time.now().add(-cut_months * 24 * time.hour)
|
||||
mut ctx := Context{
|
||||
cut_time: cut_time
|
||||
}
|
||||
println('> Deprecations that happened before ${cut_time}')
|
||||
all_v_files := os.walk_ext('.', '.v')
|
||||
for v_file in all_v_files {
|
||||
if v_file == './vlib/v/fmt/tests/attrs_keep.vv' {
|
||||
println('>>> SKIPPING deprecations attrs formatting test file ${v_file}')
|
||||
continue
|
||||
}
|
||||
if v_file.starts_with('./vlib/v/checker/tests') && v_file.contains('deprec') {
|
||||
println('>>> SKIPPING deprecations test file ${v_file}')
|
||||
continue
|
||||
}
|
||||
file_content := os.read_file(v_file)!
|
||||
if !file_content.contains('[deprecated') {
|
||||
continue
|
||||
}
|
||||
lines := file_content.split_into_lines()
|
||||
for line_num := lines.len - 1; line_num > 0; line_num-- {
|
||||
line := lines[line_num]
|
||||
mut is_deprecation_line := false
|
||||
if line.contains('[deprecated:') {
|
||||
is_deprecation_line = true
|
||||
}
|
||||
if line.contains('[deprecated]') {
|
||||
is_deprecation_line = true
|
||||
}
|
||||
if !is_deprecation_line {
|
||||
continue
|
||||
}
|
||||
ctx.analyze_line(line, v_file, line_num + 1)
|
||||
}
|
||||
}
|
||||
println('> Summary: there were ${ctx.deprecations} deprecations found, done before ${cut_time}.')
|
||||
}
|
@ -342,11 +342,19 @@ fn auto_complete(args []string) {
|
||||
exit(0)
|
||||
}
|
||||
mut lines := []string{}
|
||||
mut dirs := []string{}
|
||||
mut files := []string{}
|
||||
list := auto_complete_request(sub_args[1..])
|
||||
for entry in list {
|
||||
lines << 'compadd -U -S' + '""' + ' -- ' + "'${entry}';"
|
||||
match true {
|
||||
os.is_dir(entry) { dirs << entry }
|
||||
os.is_file(entry) { files << entry }
|
||||
else { lines << entry }
|
||||
}
|
||||
}
|
||||
println(lines.join('\n'))
|
||||
println('compadd -q -- ${lines.join(' ')}')
|
||||
println('compadd -J "dirs" -X "directory" -d -- ${dirs.join(' ')}')
|
||||
println('compadd -J "files" -X "file" -f -- ${files.join(' ')}')
|
||||
}
|
||||
'-h', '--help' {
|
||||
println(help_text)
|
||||
|
1
cmd/tools/vvet/tests/const_dynamic_array_notice.out
Normal file
1
cmd/tools/vvet/tests/const_dynamic_array_notice.out
Normal file
@ -0,0 +1 @@
|
||||
cmd/tools/vvet/tests/const_dynamic_array_notice.vv:1: notice: use a fixed array, instead of a dynamic one
|
1
cmd/tools/vvet/tests/const_dynamic_array_notice.vv
Normal file
1
cmd/tools/vvet/tests/const_dynamic_array_notice.vv
Normal file
@ -0,0 +1 @@
|
||||
const a = [1, 2, 3]
|
@ -14,9 +14,10 @@ import term
|
||||
struct Vet {
|
||||
opt Options
|
||||
mut:
|
||||
errors []vet.Error
|
||||
warns []vet.Error
|
||||
file string
|
||||
errors []vet.Error
|
||||
warns []vet.Error
|
||||
notices []vet.Error
|
||||
file string
|
||||
}
|
||||
|
||||
struct Options {
|
||||
@ -69,6 +70,9 @@ fn main() {
|
||||
}
|
||||
}
|
||||
vfmt_err_count := vt.errors.filter(it.fix == .vfmt).len
|
||||
for n in vt.notices {
|
||||
eprintln(vt.e2string(n))
|
||||
}
|
||||
if vt.opt.show_warnings {
|
||||
for w in vt.warns {
|
||||
eprintln(vt.e2string(w))
|
||||
@ -100,9 +104,10 @@ fn (mut vt Vet) vet_file(path string) {
|
||||
prefs.is_vsh = path.ends_with('.vsh')
|
||||
table := ast.new_table()
|
||||
vt.vprintln("vetting file '${path}'...")
|
||||
_, errors := parser.parse_vet_file(path, table, prefs)
|
||||
_, errors, notices := parser.parse_vet_file(path, table, prefs)
|
||||
// Transfer errors from scanner and parser
|
||||
vt.errors << errors
|
||||
vt.notices << notices
|
||||
// Scan each line in file for things to improve
|
||||
source_lines := os.read_lines(vt.file) or { []string{} }
|
||||
for lnumber, line in source_lines {
|
||||
@ -235,6 +240,7 @@ fn (vt &Vet) e2string(err vet.Error) string {
|
||||
kind = match err.kind {
|
||||
.warning { term.magenta(kind) }
|
||||
.error { term.red(kind) }
|
||||
.notice { term.yellow(kind) }
|
||||
}
|
||||
kind = term.bold(kind)
|
||||
location = term.bold(location)
|
||||
@ -275,3 +281,17 @@ fn (mut vt Vet) warn(msg string, line int, fix vet.FixKind) {
|
||||
vt.warns << w
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut vt Vet) notice(msg string, line int, fix vet.FixKind) {
|
||||
pos := token.Pos{
|
||||
line_nr: line + 1
|
||||
}
|
||||
vt.notices << vet.Error{
|
||||
message: msg
|
||||
file_path: vt.file
|
||||
pos: pos
|
||||
kind: .notice
|
||||
fix: fix
|
||||
typ: .default
|
||||
}
|
||||
}
|
||||
|
@ -95,6 +95,7 @@ mut:
|
||||
ignore_exts []string // extensions of files that will be ignored, even if they change (useful for sqlite.db files for example)
|
||||
cmd_before_run string // a command to run before each re-run
|
||||
cmd_after_run string // a command to run after each re-run
|
||||
only_watch []string // If not empty, *all* files that trigger updates, should match *at least one* of these s.match_glob() patterns. This is also triggered for vweb apps, to monitor for just *.v,*.js,*.css,*.html in vweb projects.
|
||||
}
|
||||
|
||||
[if debug_vwatch ?]
|
||||
@ -106,6 +107,33 @@ fn (context &Context) str() string {
|
||||
return 'Context{ pid: ${context.pid}, is_worker: ${context.is_worker}, check_period_ms: ${context.check_period_ms}, vexe: ${context.vexe}, opts: ${context.opts}, is_exiting: ${context.is_exiting}, vfiles: ${context.vfiles}'
|
||||
}
|
||||
|
||||
fn (mut context Context) is_ext_ignored(pf string, pf_ext string) bool {
|
||||
for ipattern in context.ignore_exts {
|
||||
if pf_ext.match_glob(ipattern) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
if pf_ext in ['', '.so', '.a'] {
|
||||
// on unix, the executables saved by compilers, usually do not have extensions at all, and shared libs are .so
|
||||
return true
|
||||
}
|
||||
if pf_ext in ['.exe', '.dll', '.def'] {
|
||||
// on windows, files with these extensions will be generated by the compiler
|
||||
return true
|
||||
}
|
||||
// ignore common backup files saved by editors like emacs/jed/vim:
|
||||
if pf_ext == '.bak' {
|
||||
return true
|
||||
}
|
||||
if pf.starts_with('.#') {
|
||||
return true
|
||||
}
|
||||
if pf.ends_with('~') {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fn (mut context Context) get_stats_for_affected_vfiles() []VFileStat {
|
||||
if context.affected_paths.len == 0 {
|
||||
mut apaths := map[string]bool{}
|
||||
@ -121,34 +149,63 @@ fn (mut context Context) get_stats_for_affected_vfiles() []VFileStat {
|
||||
vfiles := os.execute(cmd)
|
||||
if vfiles.exit_code == 0 {
|
||||
paths_trimmed := vfiles.output.trim_space()
|
||||
reported_used_files := paths_trimmed.split('\n')
|
||||
reported_used_files := paths_trimmed.split_any('\n')
|
||||
$if trace_reported_used_files ? {
|
||||
context.elog('reported_used_files: ${reported_used_files}')
|
||||
}
|
||||
paths << reported_used_files
|
||||
}
|
||||
mut is_vweb_found := false
|
||||
for vf in paths {
|
||||
apaths[os.real_path(os.dir(vf))] = true
|
||||
if vf.contains('vweb.v') {
|
||||
is_vweb_found = true
|
||||
}
|
||||
}
|
||||
|
||||
if is_vweb_found {
|
||||
if !os.args.any(it.starts_with('--only-watch')) {
|
||||
// vweb is often used with SQLite .db or .sqlite3 files right next to the executable/source,
|
||||
// that are updated by the vweb app, causing a restart of the app, which in turn causes the
|
||||
// browser to reload the current page, that probably triggered the update in the first place.
|
||||
// Note that the problem is not specific to SQLite, any database that stores its files in the
|
||||
// current (project) folder, will also cause this.
|
||||
println('`v watch` detected that you are compiling a vweb project.')
|
||||
println(' Because of that, the `--only-watch=*.v,*.html,*.css,*.js` flag was also implied.')
|
||||
println(' In result, `v watch` will ignore changes to other files.')
|
||||
println(' Add your own --only-watch filter, if you wish to override that choice.')
|
||||
println('')
|
||||
context.only_watch = '*.v,*.html,*.css,*.js'.split_any(',')
|
||||
}
|
||||
}
|
||||
context.affected_paths = apaths.keys()
|
||||
// context.elog('vfiles paths to be scanned: $context.affected_paths')
|
||||
}
|
||||
// scan all files in the found folders
|
||||
// scan all files in the found folders:
|
||||
mut newstats := []VFileStat{}
|
||||
for path in context.affected_paths {
|
||||
mut files := os.ls(path) or { []string{} }
|
||||
for pf in files {
|
||||
next_file: for pf in files {
|
||||
pf_path := os.join_path_single(path, pf)
|
||||
if context.only_watch.len > 0 {
|
||||
// in the whitelist mode, first only allow files, which match at least one of the patterns in context.only_watch:
|
||||
mut matched_pattern_idx := -1
|
||||
for ow_pattern_idx, ow_pattern in context.only_watch {
|
||||
if pf_path.match_glob(ow_pattern) {
|
||||
matched_pattern_idx = ow_pattern_idx
|
||||
context.elog('> ${@METHOD} matched --only-watch pattern: ${ow_pattern}, for file: ${pf_path}')
|
||||
break
|
||||
}
|
||||
}
|
||||
if matched_pattern_idx == -1 {
|
||||
context.elog('> ${@METHOD} --only-watch ignored file: ${pf_path}')
|
||||
continue
|
||||
}
|
||||
}
|
||||
// by default allow everything, except very specific extensions (backup files, executables etc):
|
||||
pf_ext := os.file_ext(pf).to_lower()
|
||||
if pf_ext in ['', '.bak', '.exe', '.dll', '.so', '.def'] {
|
||||
continue
|
||||
}
|
||||
if pf_ext in context.ignore_exts {
|
||||
continue
|
||||
}
|
||||
if pf.starts_with('.#') {
|
||||
continue
|
||||
}
|
||||
if pf.ends_with('~') {
|
||||
if context.is_ext_ignored(pf, pf_ext) {
|
||||
context.elog('> ${@METHOD} ignored extension: ${pf_ext}, for file: ${pf_path}')
|
||||
continue
|
||||
}
|
||||
f := os.join_path(path, pf)
|
||||
@ -323,19 +380,21 @@ fn main() {
|
||||
// Options after `run` should be ignored, since they are intended for the user program, not for the watcher.
|
||||
// For example, `v watch run x.v -a -b -k', should pass all of -a -b -k to the compiled and run program.
|
||||
only_watch_options, has_run := all_before('run', all_args_after_watch_cmd)
|
||||
|
||||
mut fp := flag.new_flag_parser(only_watch_options)
|
||||
fp.application('v watch')
|
||||
fp.version('0.0.2')
|
||||
fp.description('Collect all .v files needed for a compilation, then re-run the compilation when any of the source changes.')
|
||||
fp.arguments_description('[--silent] [--clear] [--ignore .db] [--add /path/to/a/file.v] [run] program.v')
|
||||
fp.allow_unknown_args()
|
||||
fp.limit_free_args_to_at_least(1)!
|
||||
|
||||
context.is_worker = fp.bool('vwatchworker', 0, false, 'Internal flag. Used to distinguish vwatch manager and worker processes.')
|
||||
context.silent = fp.bool('silent', `s`, false, 'Be more silent; do not print the watch timestamp before each re-run.')
|
||||
context.clear_terminal = fp.bool('clear', `c`, false, 'Clears the terminal before each re-run.')
|
||||
context.keep_running = fp.bool('keep', `k`, false, 'Keep the program running. Restart it automatically, if it exits by itself. Useful for gg/ui apps.')
|
||||
context.add_files = fp.string('add', `a`, '', 'Add more files to be watched. Useful with `v watch -add=/tmp/feature.v run cmd/v /tmp/feature.v`, if you change *both* the compiler, and the feature.v file.').split(',')
|
||||
context.ignore_exts = fp.string('ignore', `i`, '', 'Ignore files having these extensions. Useful with `v watch -ignore=.db run server.v`, if your server writes to an sqlite.db file in the same folder.').split(',')
|
||||
context.add_files = fp.string('add', `a`, '', 'Add more files to be watched. Useful with `v watch --add=/tmp/feature.v run cmd/v /tmp/feature.v`, if you change *both* the compiler, and the feature.v file.').split_any(',')
|
||||
context.ignore_exts = fp.string('ignore', `i`, '', 'Ignore files having these extensions. Useful with `v watch --ignore=.db run server.v`, if your server writes to an sqlite.db file in the same folder.').split_any(',')
|
||||
context.only_watch = fp.string('only-watch', `o`, '', 'Watch only files matching these globe patterns. Example for a markdown renderer project: `v watch --only-watch=*.v,*.md run .`').split_any(',')
|
||||
show_help := fp.bool('help', `h`, false, 'Show this help screen.')
|
||||
context.cmd_before_run = fp.string('before', 0, '', 'A command to execute *before* each re-run.')
|
||||
context.cmd_after_run = fp.string('after', 0, '', 'A command to execute *after* each re-run.')
|
||||
@ -351,6 +410,7 @@ fn main() {
|
||||
context.opts << all_args_before_watch_cmd
|
||||
context.opts << remaining_options
|
||||
if has_run {
|
||||
context.opts << 'run'
|
||||
context.opts << all_after('run', all_args_after_watch_cmd)
|
||||
}
|
||||
context.elog('>>> context.pid: ${context.pid}')
|
||||
@ -360,6 +420,7 @@ fn main() {
|
||||
context.elog('>>> context.clear_terminal: ${context.clear_terminal}')
|
||||
context.elog('>>> context.add_files: ${context.add_files}')
|
||||
context.elog('>>> context.ignore_exts: ${context.ignore_exts}')
|
||||
context.elog('>>> context.only_watch: ${context.only_watch}')
|
||||
if context.is_worker {
|
||||
context.worker_main()
|
||||
} else {
|
||||
@ -407,7 +468,7 @@ fn all_before(needle string, all []string) ([]string, bool) {
|
||||
if needle_pos == -1 {
|
||||
return all, false
|
||||
}
|
||||
return all#[..needle_pos + 1], true
|
||||
return all#[..needle_pos], true
|
||||
}
|
||||
|
||||
fn all_after(needle string, all []string) []string {
|
||||
|
14
doc/docs.md
14
doc/docs.md
@ -5583,13 +5583,13 @@ is compiled with `v -g` or `v -cg`.
|
||||
If you're using a custom ifdef, then you do need `$if option ? {}` and compile with`v -d option`.
|
||||
Full list of builtin options:
|
||||
|
||||
| OS | Compilers | Platforms | Other |
|
||||
|--------------------------------|------------------|------------------|-----------------------------------------------|
|
||||
| `windows`, `linux`, `macos` | `gcc`, `tinyc` | `amd64`, `arm64` | `debug`, `prod`, `test` |
|
||||
| `mac`, `darwin`, `ios`, | `clang`, `mingw` | `x64`, `x32` | `js`, `glibc`, `prealloc` |
|
||||
| `android`, `mach`, `dragonfly` | `msvc` | `little_endian` | `no_bounds_checking`, `freestanding` |
|
||||
| `gnu`, `hpux`, `haiku`, `qnx` | `cplusplus` | `big_endian` | `no_segfault_handler`, `no_backtrace` |
|
||||
| `solaris`, `termux` | | | `no_main` |
|
||||
| OS | Compilers | Platforms | Other |
|
||||
|--------------------------------|------------------|-------------------------------|-----------------------------------------------|
|
||||
| `windows`, `linux`, `macos` | `gcc`, `tinyc` | `amd64`, `arm64`, `aarch64` | `debug`, `prod`, `test` |
|
||||
| `mac`, `darwin`, `ios`, | `clang`, `mingw` | `i386`, `arm32` | `js`, `glibc`, `prealloc` |
|
||||
| `android`, `mach`, `dragonfly` | `msvc` | `x64`, `x32` | `no_bounds_checking`, `freestanding` |
|
||||
| `gnu`, `hpux`, `haiku`, `qnx` | `cplusplus` | `little_endian`, `big_endian` | `no_segfault_handler`, `no_backtrace` |
|
||||
| `solaris`, `termux` | | | `no_main` |
|
||||
|
||||
#### `$embed_file`
|
||||
|
||||
|
@ -134,7 +134,7 @@ fn read_wav_file_samples(fpath string) ![]f32 {
|
||||
return error('WAV should have valid length')
|
||||
}
|
||||
offset += sizeof(RIFFHeader)
|
||||
mut rf := &RIFFFormat(0)
|
||||
mut rf := &RIFFFormat(unsafe { nil })
|
||||
for {
|
||||
if offset >= bytes.len {
|
||||
break
|
||||
|
14
examples/vwatch/cli_clock/main.v
Normal file
14
examples/vwatch/cli_clock/main.v
Normal file
@ -0,0 +1,14 @@
|
||||
import time
|
||||
// This example demonstrates how to use `v watch` for simple CLI apps.
|
||||
|
||||
fn main() {
|
||||
println('Run with: `v watch run examples/vwatch/cli_clock`,')
|
||||
println('then modify timer.v in your editor.')
|
||||
println('The application will be restarted,')
|
||||
println('as soon as you save your changes.')
|
||||
println('')
|
||||
for {
|
||||
println('The time is now: ${time.now()}')
|
||||
time.sleep(1000 * time.millisecond)
|
||||
}
|
||||
}
|
4
examples/vwatch/web_server/.gitignore
vendored
Normal file
4
examples/vwatch/web_server/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
*
|
||||
!.gitignore
|
||||
!main.v
|
||||
|
65
examples/vwatch/web_server/main.v
Normal file
65
examples/vwatch/web_server/main.v
Normal file
@ -0,0 +1,65 @@
|
||||
module main
|
||||
|
||||
// This example demonstrates how to use `v watch` for vweb apps, that use sqlite .db files.
|
||||
//
|
||||
// Note 1: while developing services, it is useful to also add the `--keep` option of `v watch`,
|
||||
// which will restart the app right away, even when it exits on its own.
|
||||
//
|
||||
// Note 2: vweb supports a special live reload mode, where it will make the browser to check for server
|
||||
// restarts, and it will trigger a refresh of the current page, right after that is detected.
|
||||
//
|
||||
// The above means, that to get the most optimal prototyping experience for vweb apps, use:
|
||||
// `v -d vweb_livereload watch --only-watch=*.v,*.html,*.css,*.js --keep run .`
|
||||
import os
|
||||
import vweb
|
||||
import db.sqlite
|
||||
|
||||
fn mydb() !sqlite.DB {
|
||||
return sqlite.connect(os.resource_abs_path('app.db'))
|
||||
}
|
||||
|
||||
struct State {
|
||||
mut:
|
||||
counter int
|
||||
}
|
||||
|
||||
struct App {
|
||||
vweb.Context
|
||||
mut:
|
||||
state shared State
|
||||
}
|
||||
|
||||
pub fn (mut app App) index() vweb.Result {
|
||||
mut c := 0
|
||||
lock app.state {
|
||||
app.state.counter++
|
||||
c = app.state.counter
|
||||
}
|
||||
visits := app.update_db() or { 0 }
|
||||
return app.html('<!doctype html><html><body>
|
||||
<br/>Current request counter, after the server restart: ${c}.
|
||||
<br/>Total stored visits: ${visits}
|
||||
</body></html>')
|
||||
}
|
||||
|
||||
fn (mut app App) update_db() !int {
|
||||
mut db := mydb()!
|
||||
db.exec('INSERT INTO visits (created_at) VALUES ("")')
|
||||
visits := db.q_int('SELECT count(*) FROM visits')
|
||||
db.close()!
|
||||
return visits
|
||||
}
|
||||
|
||||
fn main() {
|
||||
println('App demonstrating the use of `vweb` & `db.sqlite` together.')
|
||||
println('For best prototyping experience, run with:')
|
||||
println('`v -d vweb_livereload watch --keep run examples/vwatch/web_server/`')
|
||||
println('')
|
||||
mut db := mydb()!
|
||||
db.exec('CREATE TABLE visits (id integer primary key AUTOINCREMENT, created_at timestamp default current_timestamp);')
|
||||
db.exec('CREATE TRIGGER INSERT_visits AFTER INSERT ON visits BEGIN
|
||||
UPDATE visits SET created_at = datetime("now", "localtime") WHERE rowid = new.rowid ;
|
||||
END')
|
||||
db.close()!
|
||||
vweb.run(&App{}, 19123)
|
||||
}
|
@ -3,8 +3,8 @@ module arrays
|
||||
import strings
|
||||
|
||||
// Common arrays functions:
|
||||
// - min / max - return the value of the minumum / maximum
|
||||
// - idx_min / idx_max - return the index of the first minumum / maximum
|
||||
// - min / max - return the value of the minimum / maximum
|
||||
// - idx_min / idx_max - return the index of the first minimum / maximum
|
||||
// - merge - combine two sorted arrays and maintain sorted order
|
||||
// - chunk - chunk array to arrays with n elements
|
||||
// - window - get snapshots of the window of the given size sliding along array with the given step, where each snapshot is an array
|
||||
@ -312,7 +312,7 @@ pub fn fold_indexed[T, R](array []T, init R, fold_op fn (idx int, acc R, elem T)
|
||||
}
|
||||
|
||||
// flatten flattens n + 1 dimensional array into n dimensional array
|
||||
// Example: arrays.flatten<int>([[1, 2, 3], [4, 5]]) // => [1, 2, 3, 4, 5]
|
||||
// Example: arrays.flatten[int]([[1, 2, 3], [4, 5]]) // => [1, 2, 3, 4, 5]
|
||||
pub fn flatten[T](array [][]T) []T {
|
||||
// calculate required capacity
|
||||
mut required_size := 0
|
||||
|
@ -10,14 +10,14 @@ import strings
|
||||
// which avoids using generics and thus without generating extra
|
||||
// code for every type.
|
||||
pub struct array {
|
||||
pub:
|
||||
element_size int // size in bytes of one element in the array.
|
||||
pub mut:
|
||||
data voidptr
|
||||
offset int // in bytes (should be `usize`), to avoid copying data while making slices, unless it starts changing
|
||||
len int // length of the array in elements.
|
||||
cap int // capacity of the array in elements.
|
||||
flags ArrayFlags
|
||||
pub:
|
||||
element_size int // size in bytes of one element in the array.
|
||||
}
|
||||
|
||||
[flag]
|
||||
|
@ -223,7 +223,7 @@ pub fn flush_stderr() {
|
||||
}
|
||||
}
|
||||
|
||||
// print prints a message to stdout. Unlike `println` stdout is not automatically flushed.
|
||||
// print prints a message to stdout. Note that unlike `eprint`, stdout is not automatically flushed.
|
||||
[manualfree]
|
||||
pub fn print(s string) {
|
||||
$if android && !termux {
|
||||
@ -238,7 +238,7 @@ pub fn print(s string) {
|
||||
}
|
||||
}
|
||||
|
||||
// println prints a message with a line end, to stdout. stdout is flushed.
|
||||
// println prints a message with a line end, to stdout. Note that unlike `eprintln`, stdout is not automatically flushed.
|
||||
[manualfree]
|
||||
pub fn println(s string) {
|
||||
if s.str == 0 {
|
||||
|
@ -354,22 +354,6 @@ pub fn (s string) trim_string_right(str string) string {
|
||||
return s.clone()
|
||||
}
|
||||
|
||||
// trim_prefix strips `str` from the start of the string.
|
||||
// Example: assert 'WorldHello V'.trim_prefix('World') == 'Hello V'
|
||||
[deprecated: 'use s.trim_string_left(x) instead']
|
||||
[deprecated_after: '2022-01-19']
|
||||
pub fn (s string) trim_prefix(str string) string {
|
||||
return s.trim_string_left(str)
|
||||
}
|
||||
|
||||
// trim_suffix strips `str` from the end of the string.
|
||||
// Example: assert 'Hello VWorld'.trim_suffix('World') == 'Hello V'
|
||||
[deprecated: 'use s.trim_string_right(x) instead']
|
||||
[deprecated_after: '2022-01-19']
|
||||
pub fn (s string) trim_suffix(str string) string {
|
||||
return s.trim_string_right(str)
|
||||
}
|
||||
|
||||
// compare_strings returns `-1` if `a < b`, `1` if `a > b` else `0`.
|
||||
pub fn compare_strings(a &string, b &string) int {
|
||||
if a < b {
|
||||
|
@ -1677,22 +1677,6 @@ pub fn (s string) trim_string_right(str string) string {
|
||||
return s.clone()
|
||||
}
|
||||
|
||||
// trim_prefix strips `str` from the start of the string.
|
||||
// Example: assert 'WorldHello V'.trim_prefix('World') == 'Hello V'
|
||||
[deprecated: 'use s.trim_string_left(x) instead']
|
||||
[deprecated_after: '2022-01-19']
|
||||
pub fn (s string) trim_prefix(str string) string {
|
||||
return s.trim_string_left(str)
|
||||
}
|
||||
|
||||
// trim_suffix strips `str` from the end of the string.
|
||||
// Example: assert 'Hello VWorld'.trim_suffix('World') == 'Hello V'
|
||||
[deprecated: 'use s.trim_string_right(x) instead']
|
||||
[deprecated_after: '2022-01-19']
|
||||
pub fn (s string) trim_suffix(str string) string {
|
||||
return s.trim_string_right(str)
|
||||
}
|
||||
|
||||
// compare_strings returns `-1` if `a < b`, `1` if `a > b` else `0`.
|
||||
pub fn compare_strings(a &string, b &string) int {
|
||||
if a < b {
|
||||
|
@ -66,19 +66,6 @@ pub fn utf32_decode_to_buffer(code u32, buf &u8) int {
|
||||
return 0
|
||||
}
|
||||
|
||||
// utf8_str_len returns the number of runes contained in the string.
|
||||
[deprecated: 'use `string.len_utf8()` instead']
|
||||
[deprecated_after: '2022-05-28']
|
||||
pub fn utf8_str_len(s string) int {
|
||||
mut l := 0
|
||||
mut i := 0
|
||||
for i < s.len {
|
||||
l++
|
||||
i += ((0xe5000000 >> ((unsafe { s.str[i] } >> 3) & 0x1e)) & 3) + 1
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
// Convert utf8 to utf32
|
||||
// the original implementation did not check for
|
||||
// valid utf8 in the string, and could result in
|
||||
|
@ -17,19 +17,6 @@ mut:
|
||||
done() chan int
|
||||
}
|
||||
|
||||
[deprecated]
|
||||
pub fn cancel(mut ctx Context) {
|
||||
match mut ctx {
|
||||
CancelContext {
|
||||
ctx.cancel(true, canceled)
|
||||
}
|
||||
TimerContext {
|
||||
ctx.cancel(true, canceled)
|
||||
}
|
||||
else {}
|
||||
}
|
||||
}
|
||||
|
||||
// A CancelContext can be canceled. When canceled, it also cancels any children
|
||||
// that implement Canceler.
|
||||
pub struct CancelContext {
|
||||
|
@ -112,21 +112,21 @@ fn pg_stmt_match(mut types []u32, mut vals []&char, mut lens []int, mut formats
|
||||
}
|
||||
u16 {
|
||||
types << u32(Oid.t_int2)
|
||||
num := conv.htn16(data)
|
||||
num := conv.hton16(data)
|
||||
vals << &char(&num)
|
||||
lens << int(sizeof(u16))
|
||||
formats << 1
|
||||
}
|
||||
u32 {
|
||||
types << u32(Oid.t_int4)
|
||||
num := conv.htn32(data)
|
||||
num := conv.hton32(data)
|
||||
vals << &char(&num)
|
||||
lens << int(sizeof(u32))
|
||||
formats << 1
|
||||
}
|
||||
u64 {
|
||||
types << u32(Oid.t_int8)
|
||||
num := conv.htn64(data)
|
||||
num := conv.hton64(data)
|
||||
vals << &char(&num)
|
||||
lens << int(sizeof(u64))
|
||||
formats << 1
|
||||
@ -139,21 +139,21 @@ fn pg_stmt_match(mut types []u32, mut vals []&char, mut lens []int, mut formats
|
||||
}
|
||||
i16 {
|
||||
types << u32(Oid.t_int2)
|
||||
num := conv.htn16(u16(data))
|
||||
num := conv.hton16(u16(data))
|
||||
vals << &char(&num)
|
||||
lens << int(sizeof(i16))
|
||||
formats << 1
|
||||
}
|
||||
int {
|
||||
types << u32(Oid.t_int4)
|
||||
num := conv.htn32(u32(data))
|
||||
num := conv.hton32(u32(data))
|
||||
vals << &char(&num)
|
||||
lens << int(sizeof(int))
|
||||
formats << 1
|
||||
}
|
||||
i64 {
|
||||
types << u32(Oid.t_int8)
|
||||
num := conv.htn64(u64(data))
|
||||
num := conv.hton64(u64(data))
|
||||
vals << &char(&num)
|
||||
lens << int(sizeof(i64))
|
||||
formats << 1
|
||||
|
101
vlib/gg/draw.c.v
101
vlib/gg/draw.c.v
@ -958,104 +958,3 @@ pub fn (ctx &Context) draw_cubic_bezier_in_steps(points []f32, steps u32, c gx.C
|
||||
|
||||
sgl.end()
|
||||
}
|
||||
|
||||
//---- deprecated
|
||||
|
||||
// Sets a pixel
|
||||
[deprecated: 'use draw_pixel() instead']
|
||||
pub fn (ctx &Context) set_pixel(x f32, y f32, c gx.Color) {
|
||||
ctx.draw_pixel(x, y, c)
|
||||
}
|
||||
|
||||
[deprecated: 'use draw_pixels() instead']
|
||||
pub fn (ctx &Context) set_pixels(points []f32, c gx.Color) {
|
||||
ctx.draw_pixels(points, c)
|
||||
}
|
||||
|
||||
[deprecated: 'use draw_poly_empty() instead']
|
||||
pub fn (ctx &Context) draw_empty_poly(points []f32, c gx.Color) {
|
||||
ctx.draw_poly_empty(points, c)
|
||||
}
|
||||
|
||||
// TODO: Fix alpha
|
||||
[deprecated: 'use draw_rect_filled() instead']
|
||||
pub fn (ctx &Context) draw_rect(x f32, y f32, w f32, h f32, c gx.Color) {
|
||||
ctx.draw_rect_filled(x, y, w, h, c)
|
||||
}
|
||||
|
||||
// Draws the outline of a rectangle
|
||||
[deprecated: 'use draw_rect_empty() instead']
|
||||
pub fn (ctx &Context) draw_empty_rect(x f32, y f32, w f32, h f32, c gx.Color) {
|
||||
ctx.draw_rect_empty(x, y, w, h, c)
|
||||
}
|
||||
|
||||
[deprecated: 'use draw_rounded_rect_empty()']
|
||||
pub fn (ctx &Context) draw_empty_rounded_rect(x f32, y f32, w f32, h f32, radius f32, c gx.Color) {
|
||||
ctx.draw_rounded_rect_empty(x, y, w, h, radius, c)
|
||||
}
|
||||
|
||||
[deprecated: 'use draw_rounded_rect_filled()']
|
||||
pub fn (ctx &Context) draw_rounded_rect(x f32, y f32, w f32, h f32, radius f32, c gx.Color) {
|
||||
ctx.draw_rounded_rect_filled(x, y, w, h, radius, c)
|
||||
}
|
||||
|
||||
// Draws the outline of a triangle
|
||||
[deprecated: 'use draw_triangle_empty() instead']
|
||||
pub fn (ctx &Context) draw_empty_triangle(x f32, y f32, x2 f32, y2 f32, x3 f32, y3 f32, c gx.Color) {
|
||||
ctx.draw_triangle_empty(x, y, x2, y2, x3, y3, c)
|
||||
}
|
||||
|
||||
// Draws a filled triangle
|
||||
[deprecated: 'use draw_triangle_filled() instead']
|
||||
pub fn (ctx &Context) draw_triangle(x f32, y f32, x2 f32, y2 f32, x3 f32, y3 f32, c gx.Color) {
|
||||
ctx.draw_triangle_filled(x, y, x2, y2, x3, y3, c)
|
||||
}
|
||||
|
||||
// Draws the outline of a square
|
||||
[deprecated: 'use draw_square_empty() instead']
|
||||
pub fn (ctx &Context) draw_empty_square(x f32, y f32, s f32, c gx.Color) {
|
||||
ctx.draw_square_empty(x, y, s, c)
|
||||
}
|
||||
|
||||
// Draws a filled square
|
||||
[deprecated: 'use draw_square_filled() instead']
|
||||
pub fn (ctx &Context) draw_square(x f32, y f32, s f32, c gx.Color) {
|
||||
ctx.draw_square_filled(x, y, s, c)
|
||||
}
|
||||
|
||||
[deprecated: 'use draw_circle_filled() instead']
|
||||
pub fn (ctx &Context) draw_circle(x f32, y f32, radius f32, c gx.Color) {
|
||||
ctx.draw_circle_filled(x, y, radius, c)
|
||||
}
|
||||
|
||||
[deprecated: 'use draw_slice_empty() instead']
|
||||
pub fn (ctx &Context) draw_empty_slice(x f32, y f32, radius f32, start_angle f32, end_angle f32, segments int, c gx.Color) {
|
||||
ctx.draw_slice_empty(x, y, radius, start_angle, end_angle, segments, c)
|
||||
}
|
||||
|
||||
[deprecated: 'use draw_slice_filled() instead']
|
||||
pub fn (ctx &Context) draw_slice(x f32, y f32, radius f32, start_angle f32, end_angle f32, segments int, c gx.Color) {
|
||||
ctx.draw_slice_filled(x, y, radius, start_angle, end_angle, segments, c)
|
||||
}
|
||||
|
||||
[deprecated: 'use draw_arc_empty() instead']
|
||||
pub fn (ctx &Context) draw_empty_arc(x f32, y f32, inner_radius f32, thickness f32, start_angle f32, end_angle f32, segments int, c gx.Color) {
|
||||
ctx.draw_arc_empty(x, y, inner_radius, thickness, start_angle, end_angle, segments,
|
||||
c)
|
||||
}
|
||||
|
||||
[deprecated: 'use draw_arc_filled() instead']
|
||||
pub fn (ctx &Context) draw_arc(x f32, y f32, inner_radius f32, thickness f32, start_angle f32, end_angle f32, segments int, c gx.Color) {
|
||||
ctx.draw_arc_filled(x, y, inner_radius, thickness, start_angle, end_angle, segments,
|
||||
c)
|
||||
}
|
||||
|
||||
[deprecated: 'use draw_ellipse_empty() instead']
|
||||
pub fn (ctx &Context) draw_empty_ellipse(x f32, y f32, rw f32, rh f32, c gx.Color) {
|
||||
ctx.draw_ellipse_empty(x, y, rw, rh, c)
|
||||
}
|
||||
|
||||
[deprecated: 'use draw_ellipse_filled() instead']
|
||||
pub fn (ctx &Context) draw_ellipse(x f32, y f32, rw f32, rh f32, c gx.Color) {
|
||||
ctx.draw_ellipse_filled(x, y, rw, rh, c)
|
||||
}
|
||||
|
@ -1,9 +0,0 @@
|
||||
module math
|
||||
|
||||
[deprecated: 'use math.abs() instead']
|
||||
pub fn fabs(x f64) f64 {
|
||||
if x > 0.0 {
|
||||
return x
|
||||
}
|
||||
return -x
|
||||
}
|
@ -573,6 +573,11 @@ fn test_div_mod() {
|
||||
assert q == eq
|
||||
assert r == er
|
||||
}
|
||||
|
||||
// an extra test for checked division by zero
|
||||
if _, _ := div_mod_test_data[0].dividend.parse().div_mod_checked(TestInteger(0).parse()) {
|
||||
assert false, 'Division by 0 should return an error'
|
||||
}
|
||||
}
|
||||
|
||||
fn test_comparison() {
|
||||
|
@ -146,6 +146,6 @@ fn random_number(length int) Integer {
|
||||
nr := numbers[i]
|
||||
stri = stri + nr.ascii_str()
|
||||
}
|
||||
res := integer_from_string(stri) or { panic('error in random_number') }
|
||||
res := integer_from_string(stri) or { panic(err) }
|
||||
return res
|
||||
}
|
||||
|
@ -158,7 +158,7 @@ pub fn integer_from_string(characters string) !Integer {
|
||||
// integer_from_radix creates a new `big.Integer` from the given string and radix.
|
||||
pub fn integer_from_radix(all_characters string, radix u32) !Integer {
|
||||
if radix < 2 || radix > 36 {
|
||||
return error('Radix must be between 2 and 36 (inclusive)')
|
||||
return error('math.big: Radix must be between 2 and 36 (inclusive)')
|
||||
}
|
||||
characters := all_characters.to_lower()
|
||||
validate_string(characters, radix)!
|
||||
@ -186,10 +186,10 @@ fn validate_string(characters string, radix u32) ! {
|
||||
value := big.digit_array.index(digit)
|
||||
|
||||
if value == -1 {
|
||||
return error('Invalid character ${digit}')
|
||||
return error('math.big: Invalid character ${digit}')
|
||||
}
|
||||
if value >= radix {
|
||||
return error('Invalid character ${digit} for base ${radix}')
|
||||
return error('math.big: Invalid character ${digit} for base ${radix}')
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -369,12 +369,16 @@ pub fn (multiplicand Integer) * (multiplier Integer) Integer {
|
||||
}
|
||||
}
|
||||
|
||||
// div_mod returns the quotient and remainder from the division of the integers `dividend` divided by `divisor`.
|
||||
pub fn (dividend Integer) div_mod(divisor Integer) (Integer, Integer) {
|
||||
// Quick exits
|
||||
if divisor.signum == 0 {
|
||||
panic('Cannot divide by zero')
|
||||
// div_mod_internal is an entirely unchecked (in terms of division by zero) method for division.
|
||||
// This should only be used for internal calculations involving a definitive non-zero
|
||||
// divisor.
|
||||
//
|
||||
// DO NOT use this method if the divisor has any chance of being 0.
|
||||
fn (dividend Integer) div_mod_internal(divisor Integer) (Integer, Integer) {
|
||||
$if debug {
|
||||
assert divisor.signum != 0
|
||||
}
|
||||
|
||||
if dividend.signum == 0 {
|
||||
return zero_int, zero_int
|
||||
}
|
||||
@ -382,11 +386,11 @@ pub fn (dividend Integer) div_mod(divisor Integer) (Integer, Integer) {
|
||||
return dividend.clone(), zero_int
|
||||
}
|
||||
if divisor.signum == -1 {
|
||||
q, r := dividend.div_mod(divisor.neg())
|
||||
q, r := dividend.div_mod_internal(divisor.neg())
|
||||
return q.neg(), r
|
||||
}
|
||||
if dividend.signum == -1 {
|
||||
q, r := dividend.neg().div_mod(divisor)
|
||||
q, r := dividend.neg().div_mod_internal(divisor)
|
||||
if r.signum == 0 {
|
||||
return q.neg(), zero_int
|
||||
} else {
|
||||
@ -408,18 +412,64 @@ pub fn (dividend Integer) div_mod(divisor Integer) (Integer, Integer) {
|
||||
return quotient, remainder
|
||||
}
|
||||
|
||||
// div_mod returns the quotient and remainder from the division of the integers `dividend`
|
||||
// divided by `divisor`.
|
||||
//
|
||||
// WARNING: this method will panic if `divisor == 0`. Refer to div_mod_checked for a safer version.
|
||||
[inline]
|
||||
pub fn (dividend Integer) div_mod(divisor Integer) (Integer, Integer) {
|
||||
if _unlikely_(divisor.signum == 0) {
|
||||
panic('math.big: Cannot divide by zero')
|
||||
}
|
||||
return dividend.div_mod_internal(divisor)
|
||||
}
|
||||
|
||||
// div_mod_checked returns the quotient and remainder from the division of the integers `dividend`
|
||||
// divided by `divisor`. An error is returned if `divisor == 0`.
|
||||
[inline]
|
||||
pub fn (dividend Integer) div_mod_checked(divisor Integer) !(Integer, Integer) {
|
||||
if _unlikely_(divisor.signum == 0) {
|
||||
return error('math.big: Cannot divide by zero')
|
||||
}
|
||||
return dividend.div_mod_internal(divisor)
|
||||
}
|
||||
|
||||
// / returns the quotient of `dividend` divided by `divisor`.
|
||||
//
|
||||
// WARNING: this method will panic if `divisor == 0`. For a division method that returns a Result
|
||||
// refer to `div_checked`.
|
||||
[inline]
|
||||
pub fn (dividend Integer) / (divisor Integer) Integer {
|
||||
q, _ := dividend.div_mod(divisor)
|
||||
return q
|
||||
}
|
||||
|
||||
// % returns the remainder of `dividend` divided by `divisor`.
|
||||
//
|
||||
// WARNING: this method will panic if `divisor == 0`. For a modular division method that
|
||||
// returns a Result refer to `mod_checked`.
|
||||
[inline]
|
||||
pub fn (dividend Integer) % (divisor Integer) Integer {
|
||||
_, r := dividend.div_mod(divisor)
|
||||
return r
|
||||
}
|
||||
|
||||
// div_checked returns the quotient of `dividend` divided by `divisor`
|
||||
// or an error if `divisor == 0`.
|
||||
[inline]
|
||||
pub fn (dividend Integer) div_checked(divisor Integer) !Integer {
|
||||
q, _ := dividend.div_mod_checked(divisor)!
|
||||
return q
|
||||
}
|
||||
|
||||
// mod_checked returns the remainder of `dividend` divided by `divisor`
|
||||
// or an error if `divisor == 0`.
|
||||
[inline]
|
||||
pub fn (dividend Integer) mod_checked(divisor Integer) !Integer {
|
||||
_, r := dividend.div_mod_checked(divisor)!
|
||||
return r
|
||||
}
|
||||
|
||||
// mask_bits is the equivalent of `a % 2^n` (only when `a >= 0`), however doing a full division
|
||||
// run for this would be a lot of work when we can simply "cut off" all bits to the left of
|
||||
// the `n`th bit.
|
||||
@ -791,7 +841,7 @@ pub fn (integer Integer) hex() string {
|
||||
|
||||
// radix_str returns the string representation of the integer `a` in the specified radix.
|
||||
pub fn (integer Integer) radix_str(radix u32) string {
|
||||
if integer.signum == 0 {
|
||||
if integer.signum == 0 || radix == 0 {
|
||||
return '0'
|
||||
}
|
||||
return match radix {
|
||||
@ -808,6 +858,9 @@ pub fn (integer Integer) radix_str(radix u32) string {
|
||||
}
|
||||
|
||||
fn (integer Integer) general_radix_str(radix u32) string {
|
||||
$if debug {
|
||||
assert radix != 0
|
||||
}
|
||||
divisor := integer_from_u32(radix)
|
||||
|
||||
mut current := integer.abs()
|
||||
@ -815,7 +868,7 @@ fn (integer Integer) general_radix_str(radix u32) string {
|
||||
mut digit := zero_int
|
||||
mut rune_array := []rune{cap: current.digits.len * 4}
|
||||
for current.signum > 0 {
|
||||
new_current, digit = current.div_mod(divisor)
|
||||
new_current, digit = current.div_mod_internal(divisor)
|
||||
rune_array << big.digit_array[digit.int()]
|
||||
unsafe { digit.free() }
|
||||
unsafe { current.free() }
|
||||
@ -919,10 +972,19 @@ pub fn (a Integer) factorial() Integer {
|
||||
return product
|
||||
}
|
||||
|
||||
// isqrt returns the closest integer square root of the given integer.
|
||||
// isqrt returns the closest integer square root of the integer `a`.
|
||||
//
|
||||
// WARNING: this method will panic if `a < 0`. Refer to isqrt_checked for a safer version.
|
||||
[inline]
|
||||
pub fn (a Integer) isqrt() Integer {
|
||||
return a.isqrt_checked() or { panic(err) }
|
||||
}
|
||||
|
||||
// isqrt returns the closest integer square root of the integer `a`.
|
||||
// An error is returned if `a < 0`.
|
||||
pub fn (a Integer) isqrt_checked() !Integer {
|
||||
if a.signum < 0 {
|
||||
panic('Cannot obtain square root of negative integer')
|
||||
return error('math.big: Cannot calculate square root of negative integer')
|
||||
}
|
||||
if a.signum == 0 {
|
||||
return a
|
||||
@ -1034,7 +1096,8 @@ fn (a Integer) mod_inv(m Integer) Integer {
|
||||
q, r := if n.bit_len() == b.bit_len() {
|
||||
one_int, n - b
|
||||
} else {
|
||||
n.div_mod(b)
|
||||
// safe because the loop terminates if b == 0
|
||||
n.div_mod_internal(b)
|
||||
}
|
||||
|
||||
n = b
|
||||
|
@ -228,7 +228,7 @@ fn toom3_multiply_digit_array(operand_a []u32, operand_b []u32, mut storage []u3
|
||||
p2 := ((ptemp + a2).left_shift(1) - a0) * ((qtemp + b2).left_shift(1) - b0)
|
||||
pinf := a2 * b2
|
||||
|
||||
mut t2 := (p2 - vm1) / three_int
|
||||
mut t2, _ := (p2 - vm1).div_mod_internal(three_int)
|
||||
mut tm1 := (p1 - vm1).right_shift(1)
|
||||
mut t1 := p1 - p0
|
||||
t2 = (t2 - t1).right_shift(1)
|
||||
|
@ -67,25 +67,25 @@ fn test_multiply_karatsuba_01() {
|
||||
karatsuba_multiply_digit_array(a, b, mut c)
|
||||
assert c == [u32(0xcaf2722e), 0x55eb2c5a, 0x3dc]
|
||||
|
||||
a_operand := integer_from_string('95484736384949495947362') or { panic('error') }
|
||||
b_operand := integer_from_string('39474638493') or { panic('error') }
|
||||
a_operand := integer_from_string('95484736384949495947362') or { panic(err) }
|
||||
b_operand := integer_from_string('39474638493') or { panic(err) }
|
||||
c = []u32{len: a_operand.digits.len + b_operand.digits.len, init: 0}
|
||||
karatsuba_multiply_digit_array(a_operand.digits, b_operand.digits, mut c)
|
||||
expected := integer_from_string('3769225450395285038584683507005466') or { panic('error') }
|
||||
expected := integer_from_string('3769225450395285038584683507005466') or { panic(err) }
|
||||
assert c == expected.digits
|
||||
}
|
||||
|
||||
fn test_multiply_karatsuba_02() {
|
||||
a := integer_from_string('53575430359313366047421252453000090528070240585276680372187519418517552556246806124659918940784792906379733645877657341259357264284615702179922887873492874019672838874121154927105373025311855709389770910765') or {
|
||||
panic('error')
|
||||
panic(err)
|
||||
}
|
||||
b := integer_from_string('977091076523237491790970633699383779582771973038531457285598238843271083830214915826312193418602834034688531898668229388286706296786321423078510899614439367') or {
|
||||
panic('error')
|
||||
panic(err)
|
||||
}
|
||||
mut c := []u32{len: a.digits.len + b.digits.len + 1, init: 0}
|
||||
karatsuba_multiply_digit_array(a.digits, b.digits, mut c)
|
||||
expected := integer_from_string('52348074924977237255285644820010078601114587486470740900886892189662650320988400136613780986308710610258879824881256666730655821800564143426560480113864123642197317383052431412305975584645367703594190956925565749714310612399025459615546540332117815550470167143256687163102859337019449165214274088466835988832405507818643018779158891710706073875995722420460085755') or {
|
||||
panic('error')
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
@ -135,10 +135,10 @@ fn test_newton_divide_06() {
|
||||
|
||||
fn test_newton_divide_07() {
|
||||
a := integer_from_string('52348074924977237255285644820010078601114587486470740900886892189662650320988400136613780986308710610258879824881256666730655821800564143426560480113864123642197317383052431412305975584645367703594190956925565749714310612399025459615546540332117815550470167143256687163102859337019449165214274088466835988832405507818643018779158891710706073875995722420460085757') or {
|
||||
panic('error')
|
||||
panic(err)
|
||||
}
|
||||
b := integer_from_string('977091076523237491790970633699383779582771973038531457285598238843271083830214915826312193418602834034688531898668229388286706296786321423078510899614439367') or {
|
||||
panic('error')
|
||||
panic(err)
|
||||
}
|
||||
mut q := []u32{cap: a.digits.len - b.digits.len + 1}
|
||||
mut r := []u32{cap: a.digits.len}
|
||||
|
@ -84,7 +84,7 @@ pub fn approximate_with_eps(val f64, eps f64) Fraction {
|
||||
if eps < 0.0 {
|
||||
panic('Epsilon value cannot be negative.')
|
||||
}
|
||||
if math.fabs(val) > math.max_i64 {
|
||||
if math.abs(val) > math.max_i64 {
|
||||
panic('Value out of range.')
|
||||
}
|
||||
// The integer part is separated first. Then we process the fractional
|
||||
@ -110,7 +110,7 @@ pub fn approximate_with_eps(val f64, eps f64) Fraction {
|
||||
// eval_cf is called often so it needs to be performant
|
||||
partial = eval_cf(whole, d)
|
||||
// Check if we're done
|
||||
if math.fabs(val - partial.f64()) < eps {
|
||||
if math.abs(val - partial.f64()) < eps {
|
||||
return partial
|
||||
}
|
||||
frac -= f64(den)
|
||||
|
@ -1,4 +0,0 @@
|
||||
# SQL Server ODBC
|
||||
|
||||
The `mssql` module has been moved to `db.mssql`.
|
||||
Update your code to do: `import db.mssql` instead.
|
@ -1,6 +0,0 @@
|
||||
module mssql
|
||||
|
||||
#flag -lodbc
|
||||
|
||||
#include <sql.h>
|
||||
#include <sqlext.h>
|
@ -1,12 +0,0 @@
|
||||
module mssql
|
||||
|
||||
// mssql module does not support tcc on windows
|
||||
|
||||
// odbc32 lib comes with windows sdk and does not need to be installed separately.
|
||||
// v builder for msvc can resolve the sdk includes search path, so no need to repeat here.
|
||||
#flag windows -lodbc32
|
||||
|
||||
// Special handling of sql headers on windows.
|
||||
// Source is in v third party folder.
|
||||
#flag windows -I@VEXEROOT/thirdparty/mssql/include
|
||||
#include <mssql.h>
|
@ -1,27 +0,0 @@
|
||||
module mssql
|
||||
|
||||
fn C.SQLAllocHandle(handle_type C.SQLSMALLINT, input_handle C.SQLHANDLE, output_handle &C.SQLHANDLE) C.SQLRETURN
|
||||
|
||||
fn C.SQLSetEnvAttr(environment_handle C.SQLHENV, attribute C.SQLINTEGER, value C.SQLPOINTER, string_length C.SQLINTEGER) C.SQLRETURN
|
||||
|
||||
fn C.SQLGetDiagRec(handle_type C.SQLSMALLINT, handle C.SQLHANDLE, rec_number C.SQLSMALLINT, sql_state &C.SQLCHAR, native_error &C.SQLINTEGER, message_text &C.SQLCHAR, buffer_length C.SQLSMALLINT, text_length &C.SQLSMALLINT) C.SQLRETURN
|
||||
|
||||
fn C.SQLSetConnectAttr(connection_handle C.SQLHDBC, attribute C.SQLINTEGER, value C.SQLPOINTER, string_length C.SQLINTEGER) C.SQLRETURN
|
||||
|
||||
fn C.SQLDriverConnect(hdbc C.SQLHDBC, hwnd C.SQLHWND, sz_conn_str_in &C.SQLCHAR, cb_conn_str_in C.SQLSMALLINT, sz_conn_str_out &C.SQLCHAR, cb_conn_str_out_max C.SQLSMALLINT, pcb_conn_str_out &C.SQLSMALLINT, f_driver_completion C.SQLUSMALLINT) C.SQLRETURN
|
||||
|
||||
fn C.SQLDisconnect(connection_handle C.SQLHDBC) C.SQLRETURN
|
||||
|
||||
fn C.SQLExecDirect(statement_handle C.SQLHSTMT, statement_text &C.SQLCHAR, text_length C.SQLINTEGER) C.SQLRETURN
|
||||
|
||||
fn C.SQLBindCol(statement_handle C.SQLHSTMT, column_number C.SQLUSMALLINT, target_type C.SQLSMALLINT, target_value C.SQLPOINTER, buffer_length C.SQLLEN, str_len_or_ind &C.SQLLEN) C.SQLRETURN
|
||||
|
||||
fn C.SQLFetch(statement_handle C.SQLHSTMT) C.SQLRETURN
|
||||
|
||||
fn C.SQLFreeHandle(handle_type C.SQLSMALLINT, handle C.SQLHANDLE) C.SQLRETURN
|
||||
|
||||
fn C.SQLNumResultCols(statement_handle C.SQLHSTMT, column_count &C.SQLSMALLINT) C.SQLRETURN
|
||||
|
||||
fn C.SQLColAttribute(statement_handle C.SQLHSTMT, column_number C.SQLUSMALLINT, field_identifier C.SQLUSMALLINT, character_attribute C.SQLPOINTER, buffer_length C.SQLSMALLINT, string_length C.SQLSMALLINT, numeric_attribute &C.SQLLEN) C.SQLRETURN
|
||||
|
||||
fn C.SQLRowCount(statement_handle C.SQLHSTMT, row_count &C.SQLLEN) C.SQLRETURN
|
@ -1,20 +0,0 @@
|
||||
module mssql
|
||||
|
||||
pub struct Config {
|
||||
pub:
|
||||
driver string
|
||||
server string
|
||||
uid string
|
||||
pwd string
|
||||
// if dbname empty, conn str will not contain Database info,
|
||||
// and it is up to the server to choose which db to connect to.
|
||||
dbname string
|
||||
}
|
||||
|
||||
pub fn (cfg Config) get_conn_str() string {
|
||||
mut str := 'Driver=${cfg.driver};Server=${cfg.server};UID=${cfg.uid};PWD=${cfg.pwd}'
|
||||
if cfg.dbname != '' {
|
||||
str += ';Database=${cfg.dbname}'
|
||||
}
|
||||
return str
|
||||
}
|
@ -1,125 +0,0 @@
|
||||
module mssql
|
||||
|
||||
pub struct Connection {
|
||||
mut:
|
||||
henv C.SQLHENV = C.SQLHENV(C.SQL_NULL_HENV) // Environment
|
||||
hdbc C.SQLHDBC = C.SQLHDBC(C.SQL_NULL_HDBC) // Connection handle
|
||||
pub mut:
|
||||
conn_str string
|
||||
}
|
||||
|
||||
// connect to db
|
||||
pub fn (mut conn Connection) connect(conn_str string) !bool {
|
||||
conn_str_c := unsafe { &C.SQLCHAR(conn_str.str) }
|
||||
mut retcode := C.SQLRETURN(C.SQL_SUCCESS)
|
||||
// Allocate environment handle
|
||||
retcode = C.SQLAllocHandle(C.SQLSMALLINT(C.SQL_HANDLE_ENV), C.SQLHANDLE(C.SQL_NULL_HANDLE),
|
||||
unsafe { &C.SQLHANDLE(&conn.henv) })
|
||||
check_error(retcode, 'SQLAllocHandle(SQL_HANDLE_ENV)', C.SQLHANDLE(conn.henv), C.SQLSMALLINT(C.SQL_HANDLE_ENV))!
|
||||
|
||||
// Set the ODBC version environment attribute
|
||||
retcode = C.SQLSetEnvAttr(conn.henv, C.SQLINTEGER(C.SQL_ATTR_ODBC_VERSION), &C.SQLPOINTER(C.SQL_OV_ODBC3),
|
||||
C.SQLINTEGER(0))
|
||||
check_error(retcode, 'SQLSetEnvAttr(SQL_ATTR_ODBC_VERSION)', C.SQLHANDLE(conn.henv),
|
||||
C.SQLSMALLINT(C.SQL_HANDLE_ENV))!
|
||||
|
||||
// Allocate connection handle
|
||||
retcode = C.SQLAllocHandle(C.SQLSMALLINT(C.SQL_HANDLE_DBC), C.SQLHANDLE(conn.henv),
|
||||
unsafe { &C.SQLHANDLE(&conn.hdbc) })
|
||||
check_error(retcode, 'SQLAllocHandle(SQL_HANDLE_DBC)', C.SQLHANDLE(conn.hdbc), C.SQLSMALLINT(C.SQL_HANDLE_DBC))!
|
||||
|
||||
// Set login timeout to 5 seconds
|
||||
retcode = C.SQLSetConnectAttr(conn.hdbc, C.SQLINTEGER(C.SQL_LOGIN_TIMEOUT), C.SQLPOINTER(5),
|
||||
C.SQLINTEGER(0))
|
||||
check_error(retcode, 'SQLSetConnectAttr(SQL_LOGIN_TIMEOUT)', C.SQLHANDLE(conn.hdbc),
|
||||
C.SQLSMALLINT(C.SQL_HANDLE_DBC))!
|
||||
|
||||
// Connect to data source
|
||||
mut outstr := [1024]char{}
|
||||
mut outstrlen := C.SQLSMALLINT(0)
|
||||
retcode = C.SQLDriverConnect(conn.hdbc, C.SQLHWND(0), conn_str_c, C.SQLSMALLINT(C.SQL_NTS),
|
||||
&C.SQLCHAR(&outstr[0]), C.SQLSMALLINT(sizeof(outstr)), &outstrlen, C.SQLUSMALLINT(C.SQL_DRIVER_NOPROMPT))
|
||||
check_error(retcode, 'SQLDriverConnect()', C.SQLHANDLE(conn.hdbc), C.SQLSMALLINT(C.SQL_HANDLE_DBC))!
|
||||
conn.conn_str = conn_str
|
||||
return true
|
||||
}
|
||||
|
||||
// close - closes the connection.
|
||||
pub fn (mut conn Connection) close() {
|
||||
// Connection
|
||||
if conn.hdbc != C.SQLHDBC(C.SQL_NULL_HDBC) {
|
||||
C.SQLDisconnect(conn.hdbc)
|
||||
C.SQLFreeHandle(C.SQLSMALLINT(C.SQL_HANDLE_DBC), C.SQLHANDLE(conn.hdbc))
|
||||
conn.hdbc = C.SQLHDBC(C.SQL_NULL_HDBC)
|
||||
}
|
||||
// Environment
|
||||
if conn.henv != C.SQLHENV(C.SQL_NULL_HENV) {
|
||||
C.SQLFreeHandle(C.SQLSMALLINT(C.SQL_HANDLE_ENV), C.SQLHANDLE(conn.henv))
|
||||
conn.henv = C.SQLHENV(C.SQL_NULL_HENV)
|
||||
}
|
||||
}
|
||||
|
||||
// query executes a sql query
|
||||
pub fn (mut conn Connection) query(q string) !Result {
|
||||
mut hstmt := new_hstmt(conn.hdbc)!
|
||||
defer {
|
||||
hstmt.close()
|
||||
}
|
||||
|
||||
hstmt.exec(q)!
|
||||
|
||||
affected := hstmt.retrieve_affected_rows()!
|
||||
|
||||
hstmt.prepare_read()!
|
||||
raw_rows := hstmt.read_rows()!
|
||||
|
||||
mut res := Result{
|
||||
rows: []Row{}
|
||||
num_rows_affected: affected
|
||||
}
|
||||
|
||||
for rr in raw_rows {
|
||||
res.rows << Row{
|
||||
vals: rr
|
||||
}
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
// check_error checks odbc return code and extract error string if available
|
||||
fn check_error(e C.SQLRETURN, s string, h C.SQLHANDLE, t C.SQLSMALLINT) ! {
|
||||
if e != C.SQLRETURN(C.SQL_SUCCESS) && e != C.SQLRETURN(C.SQL_SUCCESS_WITH_INFO) {
|
||||
err_str := extract_error(s, h, t)
|
||||
return error(err_str)
|
||||
}
|
||||
}
|
||||
|
||||
// extract_error extracts error string from odbc
|
||||
fn extract_error(fnName string, handle C.SQLHANDLE, tp C.SQLSMALLINT) string {
|
||||
mut err_str := fnName
|
||||
mut i := 0
|
||||
mut native_error := C.SQLINTEGER(0)
|
||||
mut sql_state := [7]char{}
|
||||
mut message_text := [256]char{}
|
||||
mut text_length := C.SQLSMALLINT(0)
|
||||
mut ret := C.SQLRETURN(C.SQL_SUCCESS)
|
||||
|
||||
for ret == C.SQLRETURN(C.SQL_SUCCESS) {
|
||||
i++
|
||||
ret = C.SQLGetDiagRec(tp, handle, C.SQLSMALLINT(i), &C.SQLCHAR(&sql_state[0]),
|
||||
&native_error, &C.SQLCHAR(&message_text[0]), C.SQLSMALLINT(sizeof(message_text)),
|
||||
&text_length)
|
||||
|
||||
// add driver error string
|
||||
if ret == C.SQLRETURN(C.SQL_SUCCESS) || ret == C.SQLRETURN(C.SQL_SUCCESS_WITH_INFO) {
|
||||
unsafe {
|
||||
state_str := (&sql_state[0]).vstring()
|
||||
native_error_code := int(native_error)
|
||||
txt_str := (&message_text[0]).vstring()
|
||||
err_str += '\n\todbc=${state_str}:${i}:${native_error_code}:${txt_str}'
|
||||
}
|
||||
}
|
||||
}
|
||||
return err_str
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
module mssql
|
||||
|
||||
pub struct Row {
|
||||
pub mut:
|
||||
vals []string
|
||||
}
|
||||
|
||||
pub struct Result {
|
||||
pub mut:
|
||||
rows []Row
|
||||
// the number of rows affected by sql statement
|
||||
num_rows_affected int
|
||||
}
|
@ -1,127 +0,0 @@
|
||||
module mssql
|
||||
|
||||
// HStmt is handle for sql statement
|
||||
struct HStmt {
|
||||
mut:
|
||||
// db connection reference. Owner is Connection struct.
|
||||
hdbc C.SQLHDBC = C.SQLHDBC(C.SQL_NULL_HDBC)
|
||||
// statement handle
|
||||
hstmt C.SQLHSTMT = C.SQLHSTMT(C.SQL_NULL_HSTMT)
|
||||
// fields used for computation
|
||||
column_count int = -1
|
||||
// columns
|
||||
buffers [][]char
|
||||
// indicators for each column
|
||||
indicators []C.SQLLEN
|
||||
}
|
||||
|
||||
// new_hstmt constructs a new statement handle
|
||||
fn new_hstmt(hdbc C.SQLHDBC) !HStmt {
|
||||
mut retcode := C.SQLRETURN(C.SQL_SUCCESS)
|
||||
mut hstmt := C.SQLHSTMT(C.SQL_NULL_HSTMT)
|
||||
// Allocate statement handle
|
||||
retcode = C.SQLAllocHandle(C.SQLSMALLINT(C.SQL_HANDLE_STMT), C.SQLHANDLE(hdbc), unsafe { &C.SQLHANDLE(&hstmt) })
|
||||
check_error(retcode, 'SQLAllocHandle(SQL_HANDLE_STMT)', C.SQLHANDLE(hstmt), C.SQLSMALLINT(C.SQL_HANDLE_STMT))!
|
||||
|
||||
return HStmt{
|
||||
hdbc: hdbc
|
||||
hstmt: hstmt
|
||||
}
|
||||
}
|
||||
|
||||
// close the statement handle
|
||||
fn (mut h HStmt) close() {
|
||||
// Deallocate handle
|
||||
if h.hstmt != C.SQLHSTMT(C.SQL_NULL_HSTMT) {
|
||||
// check error code?
|
||||
C.SQLFreeHandle(C.SQLSMALLINT(C.SQL_HANDLE_STMT), C.SQLHANDLE(h.hstmt))
|
||||
h.hstmt = C.SQLHSTMT(C.SQL_NULL_HSTMT)
|
||||
}
|
||||
}
|
||||
|
||||
// exec executes a Sql statement. Result is stored in odbc driver, and not yet read.
|
||||
fn (h HStmt) exec(sql string) ! {
|
||||
retcode := C.SQLExecDirect(h.hstmt, sql.str, C.SQLINTEGER(C.SQL_NTS))
|
||||
check_error(retcode, 'SQLExecDirect()', C.SQLHANDLE(h.hstmt), C.SQLSMALLINT(C.SQL_HANDLE_STMT))!
|
||||
}
|
||||
|
||||
// retrieve_affected_rows returns number of rows affected/modified by the last operation. -1 if not applicable.
|
||||
fn (h HStmt) retrieve_affected_rows() !int {
|
||||
count_ret := C.SQLLEN(0)
|
||||
retcode := C.SQLRowCount(h.hstmt, &count_ret)
|
||||
check_error(retcode, 'SQLRowCount()', C.SQLHANDLE(h.hstmt), C.SQLSMALLINT(C.SQL_HANDLE_STMT))!
|
||||
return int(count_ret)
|
||||
}
|
||||
|
||||
fn (h HStmt) retrieve_column_count() !int {
|
||||
mut retcode := C.SQLRETURN(C.SQL_SUCCESS)
|
||||
col_count_buff := C.SQLSMALLINT(0)
|
||||
retcode = C.SQLNumResultCols(h.hstmt, &col_count_buff)
|
||||
check_error(retcode, 'SQLNumResultCols()', C.SQLHANDLE(h.hstmt), C.SQLSMALLINT(C.SQL_HANDLE_STMT))!
|
||||
return int(col_count_buff)
|
||||
}
|
||||
|
||||
// allocate buffers and bind them to drivers
|
||||
fn (mut h HStmt) prepare_read() ! {
|
||||
mut retcode := C.SQLRETURN(C.SQL_SUCCESS)
|
||||
|
||||
column_count := h.retrieve_column_count()!
|
||||
h.column_count = column_count // remember the count because read will need it
|
||||
|
||||
h.buffers = [][]char{len: h.column_count}
|
||||
h.indicators = []C.SQLLEN{len: h.column_count}
|
||||
|
||||
for i := 0; i < h.column_count; i++ {
|
||||
i_col := C.SQLUSMALLINT(i + 1) // col number starts with 1
|
||||
size_ret := C.SQLLEN(0)
|
||||
// find out buffer size needed to read data in this column
|
||||
retcode = C.SQLColAttribute(h.hstmt, i_col, C.SQLUSMALLINT(C.SQL_DESC_LENGTH),
|
||||
C.SQLPOINTER(0), C.SQLSMALLINT(0), C.SQLSMALLINT(0), &size_ret)
|
||||
check_error(retcode, 'SQLColAttribute()', C.SQLHANDLE(h.hstmt), C.SQLSMALLINT(C.SQL_HANDLE_STMT))!
|
||||
|
||||
// buffer allocation is the size + 1 to include termination char, since SQL_DESC_LENGTH does not include it.
|
||||
allocate_size := size_ret + C.SQLLEN(1)
|
||||
allocate_size_int := int(allocate_size)
|
||||
buff := []char{len: allocate_size_int}
|
||||
|
||||
// bind the buffer
|
||||
retcode = C.SQLBindCol(h.hstmt, C.SQLUSMALLINT(i_col), C.SQLSMALLINT(C.SQL_C_CHAR),
|
||||
C.SQLPOINTER(&buff[0]), allocate_size, &h.indicators[i])
|
||||
check_error(retcode, 'SQLBindCol()', C.SQLHANDLE(h.hstmt), C.SQLSMALLINT(C.SQL_HANDLE_STMT))!
|
||||
|
||||
// record the buffer in HStmt
|
||||
h.buffers[i] = buff
|
||||
}
|
||||
}
|
||||
|
||||
// fetch all rows
|
||||
fn (h HStmt) read_rows() ![][]string {
|
||||
mut retcode := C.SQLRETURN(C.SQL_SUCCESS)
|
||||
|
||||
mut res := [][]string{}
|
||||
|
||||
if h.column_count <= 0 {
|
||||
// there is nothing in the driver to read from
|
||||
return res
|
||||
}
|
||||
|
||||
// Fetch and print each row of data until SQL_NO_DATA returned.
|
||||
for {
|
||||
mut row := []string{}
|
||||
retcode = C.SQLFetch(h.hstmt)
|
||||
if retcode == C.SQLRETURN(C.SQL_SUCCESS) || retcode == C.SQLRETURN(C.SQL_SUCCESS_WITH_INFO) {
|
||||
// copy buffered result to res
|
||||
for content in h.buffers {
|
||||
row << unsafe { cstring_to_vstring(content.data) }
|
||||
}
|
||||
} else {
|
||||
if retcode != C.SQLRETURN(C.SQL_NO_DATA) {
|
||||
check_error(retcode, 'SQLFetch()', C.SQLHANDLE(h.hstmt), C.SQLSMALLINT(C.SQL_HANDLE_STMT))!
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
res << row
|
||||
}
|
||||
return res
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
[deprecated: 'import db.mssql instead']
|
||||
[deprecated_after: '2023-02-01']
|
||||
module mssql
|
@ -1,4 +0,0 @@
|
||||
## Description:
|
||||
|
||||
The `mysql` module has been moved to `db.mysql`.
|
||||
Update your code to do: `import db.mysql` instead.
|
@ -1,103 +0,0 @@
|
||||
module mysql
|
||||
|
||||
[typedef]
|
||||
struct C.MYSQL {
|
||||
}
|
||||
|
||||
[typedef]
|
||||
struct C.MYSQL_RES {
|
||||
}
|
||||
|
||||
[typedef]
|
||||
struct C.MYSQL_FIELD {
|
||||
name &u8 // Name of column
|
||||
org_name &u8 // Original column name, if an alias
|
||||
table &u8 // Table of column if column was a field
|
||||
org_table &u8 // Org table name, if table was an alias
|
||||
db &u8 // Database for table
|
||||
catalog &u8 // Catalog for table
|
||||
def &u8 // Default value (set by mysql_list_fields)
|
||||
length int // Width of column (create length)
|
||||
max_length int // Max width for selected set
|
||||
name_length u32
|
||||
org_name_length u32
|
||||
table_length u32
|
||||
org_table_length u32
|
||||
db_length u32
|
||||
catalog_length u32
|
||||
def_length u32
|
||||
flags u32 // Div flags
|
||||
decimals u32 // Number of decimals in field
|
||||
charsetnr u32 // Character set
|
||||
@type int // Type of field. See mysql_com.h for types
|
||||
}
|
||||
|
||||
fn C.mysql_init(mysql &C.MYSQL) &C.MYSQL
|
||||
|
||||
fn C.mysql_real_connect(mysql &C.MYSQL, host &char, user &char, passwd &char, db &char, port u32, unix_socket &char, client_flag ConnectionFlag) &C.MYSQL
|
||||
|
||||
fn C.mysql_query(mysql &C.MYSQL, q &u8) int
|
||||
|
||||
fn C.mysql_use_result(mysql &C.MYSQL)
|
||||
|
||||
fn C.mysql_real_query(mysql &C.MYSQL, q &u8, len u32) int
|
||||
|
||||
fn C.mysql_select_db(mysql &C.MYSQL, db &u8) int
|
||||
|
||||
fn C.mysql_change_user(mysql &C.MYSQL, user &u8, password &u8, db &u8) bool
|
||||
|
||||
fn C.mysql_affected_rows(mysql &C.MYSQL) u64
|
||||
|
||||
fn C.mysql_options(mysql &C.MYSQL, option int, arg voidptr) int
|
||||
|
||||
fn C.mysql_get_option(mysql &C.MYSQL, option int, arg voidptr) int
|
||||
|
||||
fn C.mysql_list_tables(mysql &C.MYSQL, wild &u8) &C.MYSQL_RES
|
||||
|
||||
fn C.mysql_num_fields(res &C.MYSQL_RES) int
|
||||
|
||||
fn C.mysql_num_rows(res &C.MYSQL_RES) u64
|
||||
|
||||
fn C.mysql_autocommit(mysql &C.MYSQL, mode bool)
|
||||
|
||||
fn C.mysql_refresh(mysql &C.MYSQL, options u32) int
|
||||
|
||||
fn C.mysql_reset_connection(mysql &C.MYSQL) int
|
||||
|
||||
fn C.mysql_ping(mysql &C.MYSQL) int
|
||||
|
||||
fn C.mysql_store_result(mysql &C.MYSQL) &C.MYSQL_RES
|
||||
|
||||
fn C.mysql_fetch_row(res &C.MYSQL_RES) &&u8
|
||||
|
||||
fn C.mysql_fetch_fields(res &C.MYSQL_RES) &C.MYSQL_FIELD
|
||||
|
||||
fn C.mysql_free_result(res &C.MYSQL_RES)
|
||||
|
||||
fn C.mysql_real_escape_string(mysql &C.MYSQL, to &u8, from &u8, len u64) u64
|
||||
|
||||
// fn C.mysql_real_escape_string_quote(mysql &C.MYSQL, to &byte, from &byte, len u64, quote byte) u64 (Don't exist in mariadb)
|
||||
|
||||
fn C.mysql_close(sock &C.MYSQL)
|
||||
|
||||
// INFO & VERSION
|
||||
fn C.mysql_info(mysql &C.MYSQL) &u8
|
||||
|
||||
fn C.mysql_get_host_info(mysql &C.MYSQL) &u8
|
||||
|
||||
fn C.mysql_get_server_info(mysql &C.MYSQL) &u8
|
||||
|
||||
fn C.mysql_get_server_version(mysql &C.MYSQL) u64
|
||||
|
||||
fn C.mysql_get_client_version() u64
|
||||
|
||||
fn C.mysql_get_client_info() &u8
|
||||
|
||||
// DEBUG & ERROR INFO
|
||||
fn C.mysql_error(mysql &C.MYSQL) &u8
|
||||
|
||||
fn C.mysql_errno(mysql &C.MYSQL) int
|
||||
|
||||
fn C.mysql_dump_debug_info(mysql &C.MYSQL) int
|
||||
|
||||
fn C.mysql_debug(debug &u8)
|
@ -1,11 +0,0 @@
|
||||
module mysql
|
||||
|
||||
// Need to check if mysqlclient is not there and use mariadb as alternative because newer system doesn't support mysql 8.0 as default
|
||||
|
||||
$if $pkgconfig('mysqlclient') {
|
||||
#pkgconfig mysqlclient
|
||||
#include <mysql.h> # Please install the libmysqlclient-dev development headers
|
||||
} $else {
|
||||
#pkgconfig mariadb
|
||||
#include <mysql.h> # Please install the libmariadb-dev development headers
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
module mysql
|
||||
|
||||
#flag windows -I@VEXEROOT/thirdparty/mysql/include
|
||||
#flag windows @VEXEROOT/thirdparty/mysql/lib/libmysql.dll
|
||||
#include <mysql.h> # Please install https://dev.mysql.com/downloads/installer/ , then put the include/ and lib/ folders in thirdparty/mysql
|
@ -1,13 +0,0 @@
|
||||
module mysql
|
||||
|
||||
// MYSQL REFRESH FLAGS
|
||||
pub const (
|
||||
refresh_grant = u32(C.REFRESH_GRANT)
|
||||
refresh_log = u32(C.REFRESH_LOG)
|
||||
refresh_tables = u32(C.REFRESH_TABLES)
|
||||
refresh_hosts = u32(C.REFRESH_HOSTS)
|
||||
refresh_status = u32(C.REFRESH_STATUS)
|
||||
refresh_threads = u32(C.REFRESH_THREADS)
|
||||
refresh_slave = u32(C.REFRESH_SLAVE)
|
||||
refresh_master = u32(C.REFRESH_MASTER)
|
||||
)
|
@ -1,78 +0,0 @@
|
||||
module mysql
|
||||
|
||||
pub enum FieldType {
|
||||
type_decimal
|
||||
type_tiny
|
||||
type_short
|
||||
type_long
|
||||
type_float
|
||||
type_double
|
||||
type_null
|
||||
type_timestamp
|
||||
type_longlong
|
||||
type_int24
|
||||
type_date
|
||||
type_time
|
||||
type_datetime
|
||||
type_year
|
||||
type_newdate
|
||||
type_varchar
|
||||
type_bit
|
||||
type_timestamp2
|
||||
type_datetime2
|
||||
type_time2
|
||||
type_json = 245
|
||||
type_newdecimal
|
||||
type_enum
|
||||
type_set
|
||||
type_tiny_blob
|
||||
type_medium_blob
|
||||
type_long_blob
|
||||
type_blob
|
||||
type_var_string
|
||||
type_string
|
||||
type_geometry
|
||||
}
|
||||
|
||||
pub fn (f FieldType) str() string {
|
||||
return match f {
|
||||
.type_decimal { 'decimal' }
|
||||
.type_tiny { 'tiny' }
|
||||
.type_short { 'short' }
|
||||
.type_long { 'long' }
|
||||
.type_float { 'float' }
|
||||
.type_double { 'double' }
|
||||
.type_null { 'null' }
|
||||
.type_timestamp { 'timestamp' }
|
||||
.type_longlong { 'longlong' }
|
||||
.type_int24 { 'int24' }
|
||||
.type_date { 'date' }
|
||||
.type_time { 'time' }
|
||||
.type_datetime { 'datetime' }
|
||||
.type_year { 'year' }
|
||||
.type_newdate { 'newdate' }
|
||||
.type_varchar { 'varchar' }
|
||||
.type_bit { 'bit' }
|
||||
.type_timestamp2 { 'timestamp2' }
|
||||
.type_datetime2 { 'datetime2' }
|
||||
.type_time2 { 'time2' }
|
||||
.type_json { 'json' }
|
||||
.type_newdecimal { 'newdecimal' }
|
||||
.type_enum { 'enum' }
|
||||
.type_set { 'set' }
|
||||
.type_tiny_blob { 'tiny_blob' }
|
||||
.type_medium_blob { 'medium_blob' }
|
||||
.type_long_blob { 'long_blob' }
|
||||
.type_blob { 'blob' }
|
||||
.type_var_string { 'var_string' }
|
||||
.type_string { 'string' }
|
||||
.type_geometry { 'geometry' }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (f FieldType) get_len() u32 {
|
||||
return match f {
|
||||
.type_blob { 262140 }
|
||||
else { 0 }
|
||||
}
|
||||
}
|
@ -1,250 +0,0 @@
|
||||
module mysql
|
||||
|
||||
// Values for the capabilities flag bitmask used by the MySQL protocol.
|
||||
// See more on https://dev.mysql.com/doc/dev/mysql-server/latest/group__group__cs__capabilities__flags.html#details
|
||||
pub enum ConnectionFlag {
|
||||
// CAN_HANDLE_EXPIRED_PASSWORDS = C.CAN_HANDLE_EXPIRED_PASSWORDS
|
||||
client_compress = C.CLIENT_COMPRESS
|
||||
client_found_rows = C.CLIENT_FOUND_ROWS
|
||||
client_ignore_sigpipe = C.CLIENT_IGNORE_SIGPIPE
|
||||
client_ignore_space = C.CLIENT_IGNORE_SPACE
|
||||
client_interactive = C.CLIENT_INTERACTIVE
|
||||
client_local_files = C.CLIENT_LOCAL_FILES
|
||||
client_multi_results = C.CLIENT_MULTI_RESULTS
|
||||
client_multi_statements = C.CLIENT_MULTI_STATEMENTS
|
||||
client_no_schema = C.CLIENT_NO_SCHEMA
|
||||
client_odbc = C.CLIENT_ODBC
|
||||
// client_optional_resultset_metadata = C.CLIENT_OPTIONAL_RESULTSET_METADATA
|
||||
client_ssl = C.CLIENT_SSL
|
||||
client_remember_options = C.CLIENT_REMEMBER_OPTIONS
|
||||
}
|
||||
|
||||
struct SQLError {
|
||||
MessageError
|
||||
}
|
||||
|
||||
// TODO: Documentation
|
||||
pub struct Connection {
|
||||
mut:
|
||||
conn &C.MYSQL = C.mysql_init(0)
|
||||
pub mut:
|
||||
host string = '127.0.0.1'
|
||||
port u32 = 3306
|
||||
username string
|
||||
password string
|
||||
dbname string
|
||||
flag ConnectionFlag
|
||||
}
|
||||
|
||||
// connect - create a new connection to the MySQL server.
|
||||
pub fn (mut conn Connection) connect() !bool {
|
||||
instance := C.mysql_init(conn.conn)
|
||||
conn.conn = C.mysql_real_connect(instance, conn.host.str, conn.username.str, conn.password.str,
|
||||
conn.dbname.str, conn.port, 0, conn.flag)
|
||||
if isnil(conn.conn) {
|
||||
return error_with_code(get_error_msg(instance), get_errno(instance))
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// query - make an SQL query and receive the results.
|
||||
// `query()` cannot be used for statements that contain binary data;
|
||||
// Use `real_query()` instead.
|
||||
pub fn (conn Connection) query(q string) !Result {
|
||||
if C.mysql_query(conn.conn, q.str) != 0 {
|
||||
return error_with_code(get_error_msg(conn.conn), get_errno(conn.conn))
|
||||
}
|
||||
res := C.mysql_store_result(conn.conn)
|
||||
return Result{res}
|
||||
}
|
||||
|
||||
// use_result - reads the result of a query
|
||||
// used after invoking mysql_real_query() or mysql_query(),
|
||||
// for every statement that successfully produces a result set
|
||||
// (SELECT, SHOW, DESCRIBE, EXPLAIN, CHECK TABLE, and so forth).
|
||||
// This reads the result of a query directly from the server
|
||||
// without storing it in a temporary table or local buffer,
|
||||
// mysql_use_result is faster and uses much less memory than C.mysql_store_result().
|
||||
// You must mysql_free_result() after you are done with the result set.
|
||||
pub fn (conn Connection) use_result() {
|
||||
C.mysql_use_result(conn.conn)
|
||||
}
|
||||
|
||||
// real_query - make an SQL query and receive the results.
|
||||
// `real_query()` can be used for statements containing binary data.
|
||||
// (Binary data may contain the `\0` character, which `query()`
|
||||
// interprets as the end of the statement string). In addition,
|
||||
// `real_query()` is faster than `query()`.
|
||||
pub fn (mut conn Connection) real_query(q string) !Result {
|
||||
if C.mysql_real_query(conn.conn, q.str, q.len) != 0 {
|
||||
return error_with_code(get_error_msg(conn.conn), get_errno(conn.conn))
|
||||
}
|
||||
res := C.mysql_store_result(conn.conn)
|
||||
return Result{res}
|
||||
}
|
||||
|
||||
// select_db - change the default database for database queries.
|
||||
pub fn (mut conn Connection) select_db(dbname string) !bool {
|
||||
if C.mysql_select_db(conn.conn, dbname.str) != 0 {
|
||||
return error_with_code(get_error_msg(conn.conn), get_errno(conn.conn))
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// change_user - change the mysql user for the connection.
|
||||
// Passing an empty string for the `dbname` parameter, resultsg in only changing
|
||||
// the user and not changing the default database for the connection.
|
||||
pub fn (mut conn Connection) change_user(username string, password string, dbname string) !bool {
|
||||
mut ret := true
|
||||
if dbname != '' {
|
||||
ret = C.mysql_change_user(conn.conn, username.str, password.str, dbname.str)
|
||||
} else {
|
||||
ret = C.mysql_change_user(conn.conn, username.str, password.str, 0)
|
||||
}
|
||||
if !ret {
|
||||
return error_with_code(get_error_msg(conn.conn), get_errno(conn.conn))
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// affected_rows - return the number of rows changed/deleted/inserted
|
||||
// by the last `UPDATE`, `DELETE`, or `INSERT` query.
|
||||
pub fn (conn &Connection) affected_rows() u64 {
|
||||
return C.mysql_affected_rows(conn.conn)
|
||||
}
|
||||
|
||||
// autocommit - turns on/off the auto-committing mode for the connection.
|
||||
// When it is on, then each query is commited right away.
|
||||
pub fn (mut conn Connection) autocommit(mode bool) {
|
||||
C.mysql_autocommit(conn.conn, mode)
|
||||
}
|
||||
|
||||
// tables - returns a list of the names of the tables in the current database,
|
||||
// that match the simple regular expression specified by the `wildcard` parameter.
|
||||
// The `wildcard` parameter may contain the wildcard characters `%` or `_`.
|
||||
// If an empty string is passed, it will return all tables.
|
||||
// Calling `tables()` is similar to executing query `SHOW TABLES [LIKE wildcard]`.
|
||||
pub fn (conn &Connection) tables(wildcard string) ![]string {
|
||||
cres := C.mysql_list_tables(conn.conn, wildcard.str)
|
||||
if isnil(cres) {
|
||||
return error_with_code(get_error_msg(conn.conn), get_errno(conn.conn))
|
||||
}
|
||||
res := Result{cres}
|
||||
mut tables := []string{}
|
||||
for row in res.rows() {
|
||||
tables << row.vals[0]
|
||||
}
|
||||
return tables
|
||||
}
|
||||
|
||||
// escape_string - creates a legal SQL string for use in an SQL statement.
|
||||
// The `s` argument is encoded to produce an escaped SQL string,
|
||||
// taking into account the current character set of the connection.
|
||||
pub fn (conn &Connection) escape_string(s string) string {
|
||||
unsafe {
|
||||
to := malloc_noscan(2 * s.len + 1)
|
||||
C.mysql_real_escape_string(conn.conn, to, s.str, s.len)
|
||||
return to.vstring()
|
||||
}
|
||||
}
|
||||
|
||||
// set_option - sets extra connect options that affect the behavior of
|
||||
// a connection. This function may be called multiple times to set several
|
||||
// options. To retrieve the current values for an option, use `get_option()`.
|
||||
pub fn (mut conn Connection) set_option(option_type int, val voidptr) {
|
||||
C.mysql_options(conn.conn, option_type, val)
|
||||
}
|
||||
|
||||
// get_option - return the value of an option, settable by `set_option`.
|
||||
// https://dev.mysql.com/doc/c-api/5.7/en/mysql-get-option.html
|
||||
pub fn (conn &Connection) get_option(option_type int) !voidptr {
|
||||
ret := unsafe { nil }
|
||||
if C.mysql_get_option(conn.conn, option_type, &ret) != 0 {
|
||||
return error_with_code(get_error_msg(conn.conn), get_errno(conn.conn))
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// refresh - flush the tables or caches, or resets replication server
|
||||
// information. The connected user must have the `RELOAD` privilege.
|
||||
pub fn (mut conn Connection) refresh(options u32) !bool {
|
||||
if C.mysql_refresh(conn.conn, options) != 0 {
|
||||
return error_with_code(get_error_msg(conn.conn), get_errno(conn.conn))
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// reset - resets the connection, and clear the session state.
|
||||
pub fn (mut conn Connection) reset() !bool {
|
||||
if C.mysql_reset_connection(conn.conn) != 0 {
|
||||
return error_with_code(get_error_msg(conn.conn), get_errno(conn.conn))
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// ping - pings a server connection, or tries to reconnect if the connection
|
||||
// has gone down.
|
||||
pub fn (mut conn Connection) ping() !bool {
|
||||
if C.mysql_ping(conn.conn) != 0 {
|
||||
return error_with_code(get_error_msg(conn.conn), get_errno(conn.conn))
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// close - closes the connection.
|
||||
pub fn (mut conn Connection) close() {
|
||||
C.mysql_close(conn.conn)
|
||||
}
|
||||
|
||||
// info - returns information about the most recently executed query.
|
||||
// See more on https://dev.mysql.com/doc/c-api/8.0/en/mysql-info.html
|
||||
pub fn (conn &Connection) info() string {
|
||||
return resolve_nil_str(C.mysql_info(conn.conn))
|
||||
}
|
||||
|
||||
// get_host_info - returns a string describing the type of connection in use,
|
||||
// including the server host name.
|
||||
pub fn (conn &Connection) get_host_info() string {
|
||||
return unsafe { C.mysql_get_host_info(conn.conn).vstring() }
|
||||
}
|
||||
|
||||
// get_server_info - returns a string representing the MySQL server version.
|
||||
// For example, `8.0.24`.
|
||||
pub fn (conn &Connection) get_server_info() string {
|
||||
return unsafe { C.mysql_get_server_info(conn.conn).vstring() }
|
||||
}
|
||||
|
||||
// get_server_version - returns an integer, representing the MySQL server
|
||||
// version. The value has the format `XYYZZ` where `X` is the major version,
|
||||
// `YY` is the release level (or minor version), and `ZZ` is the sub-version
|
||||
// within the release level. For example, `8.0.24` is returned as `80024`.
|
||||
pub fn (conn &Connection) get_server_version() u64 {
|
||||
return C.mysql_get_server_version(conn.conn)
|
||||
}
|
||||
|
||||
// dump_debug_info - instructs the server to write debugging information
|
||||
// to the error log. The connected user must have the `SUPER` privilege.
|
||||
pub fn (mut conn Connection) dump_debug_info() !bool {
|
||||
if C.mysql_dump_debug_info(conn.conn) != 0 {
|
||||
return error_with_code(get_error_msg(conn.conn), get_errno(conn.conn))
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// get_client_info - returns client version information as a string.
|
||||
pub fn get_client_info() string {
|
||||
return unsafe { C.mysql_get_client_info().vstring() }
|
||||
}
|
||||
|
||||
// get_client_version - returns the client version information as an integer.
|
||||
pub fn get_client_version() u64 {
|
||||
return C.mysql_get_client_version()
|
||||
}
|
||||
|
||||
// debug - does a `DBUG_PUSH` with the given string.
|
||||
// `debug()` uses the Fred Fish debug library.
|
||||
// To use this function, you must compile the client library to support debugging.
|
||||
// See https://dev.mysql.com/doc/c-api/8.0/en/mysql-debug.html
|
||||
pub fn debug(debug string) {
|
||||
C.mysql_debug(debug.str)
|
||||
}
|
376
vlib/mysql/orm.v
376
vlib/mysql/orm.v
@ -1,376 +0,0 @@
|
||||
module mysql
|
||||
|
||||
import orm
|
||||
import time
|
||||
|
||||
type Prims = f32 | f64 | i16 | i64 | i8 | int | string | u16 | u32 | u64 | u8
|
||||
|
||||
// sql expr
|
||||
|
||||
pub fn (db Connection) @select(config orm.SelectConfig, data orm.QueryData, where orm.QueryData) ![][]orm.Primitive {
|
||||
query := orm.orm_select_gen(config, '`', false, '?', 0, where)
|
||||
mut ret := [][]orm.Primitive{}
|
||||
mut stmt := db.init_stmt(query)
|
||||
stmt.prepare()!
|
||||
|
||||
mysql_stmt_binder(mut stmt, where)!
|
||||
mysql_stmt_binder(mut stmt, data)!
|
||||
|
||||
if data.data.len > 0 || where.data.len > 0 {
|
||||
stmt.bind_params()!
|
||||
}
|
||||
|
||||
mut status := stmt.execute()!
|
||||
num_fields := stmt.get_field_count()
|
||||
metadata := stmt.gen_metadata()
|
||||
fields := stmt.fetch_fields(metadata)
|
||||
mut dataptr := []&u8{}
|
||||
|
||||
for i in 0 .. num_fields {
|
||||
f := unsafe { fields[i] }
|
||||
match unsafe { FieldType(f.@type) } {
|
||||
.type_tiny {
|
||||
dataptr << unsafe { malloc(1) }
|
||||
}
|
||||
.type_short {
|
||||
dataptr << unsafe { malloc(2) }
|
||||
}
|
||||
.type_long {
|
||||
dataptr << unsafe { malloc(4) }
|
||||
}
|
||||
.type_longlong {
|
||||
dataptr << unsafe { malloc(8) }
|
||||
}
|
||||
.type_float {
|
||||
dataptr << unsafe { malloc(4) }
|
||||
}
|
||||
.type_double {
|
||||
dataptr << unsafe { malloc(8) }
|
||||
}
|
||||
.type_time, .type_date, .type_datetime, .type_time2, .type_datetime2 {
|
||||
dataptr << unsafe { malloc(sizeof(C.MYSQL_TIME)) }
|
||||
}
|
||||
.type_string, .type_blob {
|
||||
dataptr << unsafe { malloc(512) }
|
||||
}
|
||||
.type_var_string {
|
||||
dataptr << unsafe { malloc(2) }
|
||||
}
|
||||
else {
|
||||
return error('\'${unsafe { FieldType(f.@type) }}\' is not yet implemented. Please create a new issue at https://github.com/vlang/v/issues/new')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lens := []u32{len: int(num_fields), init: 0}
|
||||
stmt.bind_res(fields, dataptr, lens, num_fields)
|
||||
|
||||
mut row := 0
|
||||
mut types := config.types.clone()
|
||||
mut field_types := []FieldType{}
|
||||
if config.is_count {
|
||||
types = [orm.type_idx['u64']]
|
||||
}
|
||||
|
||||
for i, mut mysql_bind in stmt.res {
|
||||
f := unsafe { fields[i] }
|
||||
field_types << unsafe { FieldType(f.@type) }
|
||||
match types[i] {
|
||||
orm.type_string {
|
||||
mysql_bind.buffer_type = C.MYSQL_TYPE_BLOB
|
||||
mysql_bind.buffer_length = FieldType.type_blob.get_len()
|
||||
}
|
||||
orm.time {
|
||||
match unsafe { FieldType(f.@type) } {
|
||||
.type_long {
|
||||
mysql_bind.buffer_type = C.MYSQL_TYPE_LONG
|
||||
}
|
||||
.type_time, .type_date, .type_datetime {
|
||||
mysql_bind.buffer_type = C.MYSQL_TYPE_BLOB
|
||||
mysql_bind.buffer_length = FieldType.type_blob.get_len()
|
||||
}
|
||||
.type_string, .type_blob {}
|
||||
else {
|
||||
return error('Unknown type ${f.@type}')
|
||||
}
|
||||
}
|
||||
}
|
||||
else {}
|
||||
}
|
||||
}
|
||||
|
||||
stmt.bind_result_buffer()!
|
||||
stmt.store_result()!
|
||||
for {
|
||||
status = stmt.fetch_stmt()!
|
||||
|
||||
if status == 1 || status == 100 {
|
||||
break
|
||||
}
|
||||
row++
|
||||
|
||||
data_list := buffer_to_primitive(dataptr, types, field_types)!
|
||||
ret << data_list
|
||||
}
|
||||
|
||||
stmt.close()!
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
// sql stmt
|
||||
|
||||
pub fn (db Connection) insert(table string, data orm.QueryData) ! {
|
||||
mut converted_primitive_array := db.factory_orm_primitive_converted_from_sql(table,
|
||||
data)!
|
||||
|
||||
converted_primitive_data := orm.QueryData{
|
||||
fields: data.fields
|
||||
data: converted_primitive_array
|
||||
types: []
|
||||
kinds: []
|
||||
is_and: []
|
||||
}
|
||||
|
||||
query, converted_data := orm.orm_stmt_gen(.default, table, '`', .insert, false, '?',
|
||||
1, converted_primitive_data, orm.QueryData{})
|
||||
mysql_stmt_worker(db, query, converted_data, orm.QueryData{})!
|
||||
}
|
||||
|
||||
pub fn (db Connection) update(table string, data orm.QueryData, where orm.QueryData) ! {
|
||||
query, _ := orm.orm_stmt_gen(.default, table, '`', .update, false, '?', 1, data, where)
|
||||
mysql_stmt_worker(db, query, data, where)!
|
||||
}
|
||||
|
||||
pub fn (db Connection) delete(table string, where orm.QueryData) ! {
|
||||
query, _ := orm.orm_stmt_gen(.default, table, '`', .delete, false, '?', 1, orm.QueryData{},
|
||||
where)
|
||||
mysql_stmt_worker(db, query, orm.QueryData{}, where)!
|
||||
}
|
||||
|
||||
pub fn (db Connection) last_id() int {
|
||||
query := 'SELECT last_insert_id();'
|
||||
id := db.query(query) or { return 0 }
|
||||
|
||||
return orm.Primitive(id.rows()[0].vals[0].int())
|
||||
}
|
||||
|
||||
// table
|
||||
pub fn (db Connection) create(table string, fields []orm.TableField) ! {
|
||||
query := orm.orm_table_gen(table, '`', true, 0, fields, mysql_type_from_v, false) or {
|
||||
return err
|
||||
}
|
||||
mysql_stmt_worker(db, query, orm.QueryData{}, orm.QueryData{})!
|
||||
}
|
||||
|
||||
pub fn (db Connection) drop(table string) ! {
|
||||
query := 'DROP TABLE `${table}`;'
|
||||
mysql_stmt_worker(db, query, orm.QueryData{}, orm.QueryData{})!
|
||||
}
|
||||
|
||||
fn mysql_stmt_worker(db Connection, query string, data orm.QueryData, where orm.QueryData) ! {
|
||||
mut stmt := db.init_stmt(query)
|
||||
stmt.prepare()!
|
||||
mysql_stmt_binder(mut stmt, data)!
|
||||
mysql_stmt_binder(mut stmt, where)!
|
||||
if data.data.len > 0 || where.data.len > 0 {
|
||||
stmt.bind_params()!
|
||||
}
|
||||
stmt.execute()!
|
||||
stmt.close()!
|
||||
}
|
||||
|
||||
fn mysql_stmt_binder(mut stmt Stmt, d orm.QueryData) ! {
|
||||
for data in d.data {
|
||||
stmt_binder_match(mut stmt, data)
|
||||
}
|
||||
}
|
||||
|
||||
fn stmt_binder_match(mut stmt Stmt, data orm.Primitive) {
|
||||
match data {
|
||||
bool {
|
||||
stmt.bind_bool(&data)
|
||||
}
|
||||
i8 {
|
||||
stmt.bind_i8(&data)
|
||||
}
|
||||
i16 {
|
||||
stmt.bind_i16(&data)
|
||||
}
|
||||
int {
|
||||
stmt.bind_int(&data)
|
||||
}
|
||||
i64 {
|
||||
stmt.bind_i64(&data)
|
||||
}
|
||||
u8 {
|
||||
stmt.bind_u8(&data)
|
||||
}
|
||||
u16 {
|
||||
stmt.bind_u16(&data)
|
||||
}
|
||||
u32 {
|
||||
stmt.bind_u32(&data)
|
||||
}
|
||||
u64 {
|
||||
stmt.bind_u64(&data)
|
||||
}
|
||||
f32 {
|
||||
stmt.bind_f32(unsafe { &f32(&data) })
|
||||
}
|
||||
f64 {
|
||||
stmt.bind_f64(unsafe { &f64(&data) })
|
||||
}
|
||||
string {
|
||||
stmt.bind_text(data)
|
||||
}
|
||||
time.Time {
|
||||
unix := int(data.unix)
|
||||
stmt_binder_match(mut stmt, unix)
|
||||
}
|
||||
orm.InfixType {
|
||||
stmt_binder_match(mut stmt, data.right)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn buffer_to_primitive(data_list []&u8, types []int, field_types []FieldType) ![]orm.Primitive {
|
||||
mut res := []orm.Primitive{}
|
||||
|
||||
for i, data in data_list {
|
||||
mut primitive := orm.Primitive(0)
|
||||
match types[i] {
|
||||
orm.type_idx['i8'] {
|
||||
primitive = *(unsafe { &i8(data) })
|
||||
}
|
||||
orm.type_idx['i16'] {
|
||||
primitive = *(unsafe { &i16(data) })
|
||||
}
|
||||
orm.type_idx['int'], orm.serial {
|
||||
primitive = *(unsafe { &int(data) })
|
||||
}
|
||||
orm.type_idx['i64'] {
|
||||
primitive = *(unsafe { &i64(data) })
|
||||
}
|
||||
orm.type_idx['u8'] {
|
||||
primitive = *(unsafe { &u8(data) })
|
||||
}
|
||||
orm.type_idx['u16'] {
|
||||
primitive = *(unsafe { &u16(data) })
|
||||
}
|
||||
orm.type_idx['u32'] {
|
||||
primitive = *(unsafe { &u32(data) })
|
||||
}
|
||||
orm.type_idx['u64'] {
|
||||
primitive = *(unsafe { &u64(data) })
|
||||
}
|
||||
orm.type_idx['f32'] {
|
||||
primitive = *(unsafe { &f32(data) })
|
||||
}
|
||||
orm.type_idx['f64'] {
|
||||
primitive = *(unsafe { &f64(data) })
|
||||
}
|
||||
orm.type_idx['bool'] {
|
||||
primitive = *(unsafe { &bool(data) })
|
||||
}
|
||||
orm.type_string {
|
||||
primitive = unsafe { cstring_to_vstring(&char(data)) }
|
||||
}
|
||||
orm.time {
|
||||
match field_types[i] {
|
||||
.type_long {
|
||||
timestamp := *(unsafe { &int(data) })
|
||||
primitive = time.unix(timestamp)
|
||||
}
|
||||
.type_datetime {
|
||||
string_time := unsafe { cstring_to_vstring(&char(data)) }
|
||||
primitive = time.parse(string_time)!
|
||||
}
|
||||
else {}
|
||||
}
|
||||
}
|
||||
else {
|
||||
return error('Unknown type ${types[i]}')
|
||||
}
|
||||
}
|
||||
res << primitive
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
fn mysql_type_from_v(typ int) !string {
|
||||
str := match typ {
|
||||
orm.type_idx['i8'], orm.type_idx['u8'] {
|
||||
'TINYINT'
|
||||
}
|
||||
orm.type_idx['i16'], orm.type_idx['u16'] {
|
||||
'SMALLINT'
|
||||
}
|
||||
orm.type_idx['int'], orm.type_idx['u32'], orm.time {
|
||||
'INT'
|
||||
}
|
||||
orm.type_idx['i64'], orm.type_idx['u64'] {
|
||||
'BIGINT'
|
||||
}
|
||||
orm.type_idx['f32'] {
|
||||
'FLOAT'
|
||||
}
|
||||
orm.type_idx['f64'] {
|
||||
'DOUBLE'
|
||||
}
|
||||
orm.type_string {
|
||||
'TEXT'
|
||||
}
|
||||
orm.serial {
|
||||
'SERIAL'
|
||||
}
|
||||
orm.type_idx['bool'] {
|
||||
'BOOLEAN'
|
||||
}
|
||||
else {
|
||||
''
|
||||
}
|
||||
}
|
||||
if str == '' {
|
||||
return error('Unknown type ${typ}')
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
fn (db Connection) factory_orm_primitive_converted_from_sql(table string, data orm.QueryData) ![]orm.Primitive {
|
||||
mut map_val := db.get_table_data_type_map(table)!
|
||||
|
||||
// adapt v type to sql time
|
||||
mut converted_data := []orm.Primitive{}
|
||||
for i, field in data.fields {
|
||||
match data.data[i].type_name() {
|
||||
'time.Time' {
|
||||
if map_val[field] == 'datetime' {
|
||||
converted_data << orm.Primitive((data.data[i] as time.Time).str())
|
||||
} else {
|
||||
converted_data << data.data[i]
|
||||
}
|
||||
}
|
||||
else {
|
||||
converted_data << data.data[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
return converted_data
|
||||
}
|
||||
|
||||
fn (db Connection) get_table_data_type_map(table string) !map[string]string {
|
||||
data_type_querys := "SELECT COLUMN_NAME, DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '${table}'"
|
||||
mut map_val := map[string]string{}
|
||||
|
||||
results := db.query(data_type_querys)!
|
||||
db.use_result()
|
||||
|
||||
for row in results.rows() {
|
||||
map_val[row.vals[0]] = row.vals[1]
|
||||
}
|
||||
|
||||
unsafe { results.free() }
|
||||
return map_val
|
||||
}
|
@ -1,153 +0,0 @@
|
||||
module mysql
|
||||
|
||||
pub struct Result {
|
||||
result &C.MYSQL_RES = unsafe { nil }
|
||||
}
|
||||
|
||||
pub struct Row {
|
||||
pub mut:
|
||||
vals []string
|
||||
}
|
||||
|
||||
pub struct Field {
|
||||
name string
|
||||
org_name string
|
||||
table string
|
||||
org_table string
|
||||
db string
|
||||
catalog string
|
||||
def string
|
||||
length int
|
||||
max_length int
|
||||
name_length u32
|
||||
org_name_length u32
|
||||
table_length u32
|
||||
org_table_length u32
|
||||
db_length u32
|
||||
catalog_length u32
|
||||
def_length u32
|
||||
flags u32
|
||||
decimals u32
|
||||
charsetnr u32
|
||||
type_ FieldType
|
||||
}
|
||||
|
||||
// fetch_row - fetches the next row from a result.
|
||||
pub fn (r Result) fetch_row() &&u8 {
|
||||
return C.mysql_fetch_row(r.result)
|
||||
}
|
||||
|
||||
// n_rows - returns the number of rows from a result.
|
||||
pub fn (r Result) n_rows() u64 {
|
||||
return C.mysql_num_rows(r.result)
|
||||
}
|
||||
|
||||
// n_fields - returns the number of columns from a result.
|
||||
pub fn (r Result) n_fields() int {
|
||||
return C.mysql_num_fields(r.result)
|
||||
}
|
||||
|
||||
// rows - returns array of rows, each containing an array of values,
|
||||
// one for each column.
|
||||
pub fn (r Result) rows() []Row {
|
||||
mut rows := []Row{}
|
||||
nr_cols := r.n_fields()
|
||||
for rr := r.fetch_row(); rr; rr = r.fetch_row() {
|
||||
mut row := Row{}
|
||||
for i in 0 .. nr_cols {
|
||||
if unsafe { rr[i] == 0 } {
|
||||
row.vals << ''
|
||||
} else {
|
||||
row.vals << mystring(unsafe { &u8(rr[i]) })
|
||||
}
|
||||
}
|
||||
rows << row
|
||||
}
|
||||
return rows
|
||||
}
|
||||
|
||||
// maps - returns an array of maps, each containing a set of
|
||||
// field name: field value pairs.
|
||||
pub fn (r Result) maps() []map[string]string {
|
||||
mut array_map := []map[string]string{}
|
||||
rows := r.rows()
|
||||
fields := r.fields()
|
||||
for i in 0 .. rows.len {
|
||||
mut map_val := map[string]string{}
|
||||
for j in 0 .. fields.len {
|
||||
map_val[fields[j].name] = rows[i].vals[j]
|
||||
}
|
||||
array_map << map_val
|
||||
}
|
||||
return array_map
|
||||
}
|
||||
|
||||
// fields - returns an array of fields/columns.
|
||||
// The definitions apply primarily for columns of results,
|
||||
// such as those produced by `SELECT` statements.
|
||||
pub fn (r Result) fields() []Field {
|
||||
mut fields := []Field{}
|
||||
nr_cols := r.n_fields()
|
||||
orig_fields := C.mysql_fetch_fields(r.result)
|
||||
for i in 0 .. nr_cols {
|
||||
unsafe {
|
||||
fields << Field{
|
||||
name: mystring(orig_fields[i].name)
|
||||
org_name: mystring(orig_fields[i].org_name)
|
||||
table: mystring(orig_fields[i].table)
|
||||
org_table: mystring(orig_fields[i].org_table)
|
||||
db: mystring(orig_fields[i].db)
|
||||
catalog: mystring(orig_fields[i].catalog)
|
||||
def: resolve_nil_str(orig_fields[i].def)
|
||||
length: orig_fields.length
|
||||
max_length: orig_fields.max_length
|
||||
name_length: orig_fields.name_length
|
||||
org_name_length: orig_fields.org_name_length
|
||||
table_length: orig_fields.table_length
|
||||
org_table_length: orig_fields.org_table_length
|
||||
db_length: orig_fields.db_length
|
||||
catalog_length: orig_fields.catalog_length
|
||||
def_length: orig_fields.def_length
|
||||
flags: orig_fields.flags
|
||||
decimals: orig_fields.decimals
|
||||
charsetnr: orig_fields.charsetnr
|
||||
type_: FieldType(orig_fields.@type)
|
||||
}
|
||||
}
|
||||
}
|
||||
return fields
|
||||
}
|
||||
|
||||
// str - serializes the field
|
||||
pub fn (f Field) str() string {
|
||||
return '
|
||||
{
|
||||
name: "${f.name}"
|
||||
org_name: "${f.org_name}"
|
||||
table: "${f.table}"
|
||||
org_table: "${f.org_table}"
|
||||
db: "${f.db}"
|
||||
catalog: "${f.catalog}"
|
||||
def: "${f.def}"
|
||||
length: ${f.length}
|
||||
max_length: ${f.max_length}
|
||||
name_length: ${f.name_length}
|
||||
org_name_length: ${f.org_name_length}
|
||||
table_length: ${f.table_length}
|
||||
org_table_length: ${f.org_table_length}
|
||||
db_length: ${f.db_length}
|
||||
catalog_length: ${f.catalog_length}
|
||||
def_length: ${f.def_length}
|
||||
flags: ${f.flags}
|
||||
decimals: ${f.decimals}
|
||||
charsetnr: ${f.charsetnr}
|
||||
type: ${f.type_.str()}
|
||||
}
|
||||
'
|
||||
}
|
||||
|
||||
// free - frees the memory used by a result
|
||||
[unsafe]
|
||||
pub fn (r &Result) free() {
|
||||
C.mysql_free_result(r.result)
|
||||
}
|
@ -1,237 +0,0 @@
|
||||
module mysql
|
||||
|
||||
[typedef]
|
||||
struct C.MYSQL_STMT {
|
||||
mysql &C.MYSQL
|
||||
stmt_id u32
|
||||
}
|
||||
|
||||
[typedef]
|
||||
struct C.MYSQL_BIND {
|
||||
mut:
|
||||
buffer_type int
|
||||
buffer voidptr
|
||||
buffer_length u32
|
||||
length &u32
|
||||
}
|
||||
|
||||
const (
|
||||
mysql_type_decimal = C.MYSQL_TYPE_DECIMAL
|
||||
mysql_type_tiny = C.MYSQL_TYPE_TINY
|
||||
mysql_type_short = C.MYSQL_TYPE_SHORT
|
||||
mysql_type_long = C.MYSQL_TYPE_LONG
|
||||
mysql_type_float = C.MYSQL_TYPE_FLOAT
|
||||
mysql_type_double = C.MYSQL_TYPE_DOUBLE
|
||||
mysql_type_null = C.MYSQL_TYPE_NULL
|
||||
mysql_type_timestamp = C.MYSQL_TYPE_TIMESTAMP
|
||||
mysql_type_longlong = C.MYSQL_TYPE_LONGLONG
|
||||
mysql_type_int24 = C.MYSQL_TYPE_INT24
|
||||
mysql_type_date = C.MYSQL_TYPE_DATE
|
||||
mysql_type_time = C.MYSQL_TYPE_TIME
|
||||
mysql_type_datetime = C.MYSQL_TYPE_DATETIME
|
||||
mysql_type_year = C.MYSQL_TYPE_YEAR
|
||||
mysql_type_varchar = C.MYSQL_TYPE_VARCHAR
|
||||
mysql_type_bit = C.MYSQL_TYPE_BIT
|
||||
mysql_type_timestamp22 = C.MYSQL_TYPE_TIMESTAMP
|
||||
mysql_type_json = C.MYSQL_TYPE_JSON
|
||||
mysql_type_newdecimal = C.MYSQL_TYPE_NEWDECIMAL
|
||||
mysql_type_enum = C.MYSQL_TYPE_ENUM
|
||||
mysql_type_set = C.MYSQL_TYPE_SET
|
||||
mysql_type_tiny_blob = C.MYSQL_TYPE_TINY_BLOB
|
||||
mysql_type_medium_blob = C.MYSQL_TYPE_MEDIUM_BLOB
|
||||
mysql_type_long_blob = C.MYSQL_TYPE_LONG_BLOB
|
||||
mysql_type_blob = C.MYSQL_TYPE_BLOB
|
||||
mysql_type_var_string = C.MYSQL_TYPE_VAR_STRING
|
||||
mysql_type_string = C.MYSQL_TYPE_STRING
|
||||
mysql_type_geometry = C.MYSQL_TYPE_GEOMETRY
|
||||
mysql_no_data = C.MYSQL_NO_DATA
|
||||
)
|
||||
|
||||
fn C.mysql_stmt_init(&C.MYSQL) &C.MYSQL_STMT
|
||||
fn C.mysql_stmt_prepare(&C.MYSQL_STMT, &char, u32) int
|
||||
fn C.mysql_stmt_bind_param(&C.MYSQL_STMT, &C.MYSQL_BIND) bool
|
||||
fn C.mysql_stmt_execute(&C.MYSQL_STMT) int
|
||||
fn C.mysql_stmt_close(&C.MYSQL_STMT) bool
|
||||
fn C.mysql_stmt_free_result(&C.MYSQL_STMT) bool
|
||||
fn C.mysql_stmt_error(&C.MYSQL_STMT) &char
|
||||
fn C.mysql_stmt_result_metadata(&C.MYSQL_STMT) &C.MYSQL_RES
|
||||
|
||||
fn C.mysql_stmt_field_count(&C.MYSQL_STMT) u16
|
||||
fn C.mysql_stmt_bind_result(&C.MYSQL_STMT, &C.MYSQL_BIND) bool
|
||||
fn C.mysql_stmt_fetch(&C.MYSQL_STMT) int
|
||||
fn C.mysql_stmt_next_result(&C.MYSQL_STMT) int
|
||||
fn C.mysql_stmt_store_result(&C.MYSQL_STMT) int
|
||||
|
||||
struct Stmt {
|
||||
stmt &C.MYSQL_STMT = &C.MYSQL_STMT(0)
|
||||
query string
|
||||
mut:
|
||||
binds []C.MYSQL_BIND
|
||||
res []C.MYSQL_BIND
|
||||
}
|
||||
|
||||
pub fn (db Connection) init_stmt(query string) Stmt {
|
||||
return Stmt{
|
||||
stmt: C.mysql_stmt_init(db.conn)
|
||||
query: query
|
||||
binds: []C.MYSQL_BIND{}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (stmt Stmt) prepare() ! {
|
||||
res := C.mysql_stmt_prepare(stmt.stmt, stmt.query.str, stmt.query.len)
|
||||
if res != 0 && stmt.get_error_msg() != '' {
|
||||
return stmt.error(res)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (stmt Stmt) bind_params() ! {
|
||||
res := C.mysql_stmt_bind_param(stmt.stmt, unsafe { &C.MYSQL_BIND(stmt.binds.data) })
|
||||
if res && stmt.get_error_msg() != '' {
|
||||
return stmt.error(1)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (stmt Stmt) execute() !int {
|
||||
res := C.mysql_stmt_execute(stmt.stmt)
|
||||
if res != 0 && stmt.get_error_msg() != '' {
|
||||
return stmt.error(res)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
pub fn (stmt Stmt) next() !int {
|
||||
res := C.mysql_stmt_next_result(stmt.stmt)
|
||||
if res > 0 && stmt.get_error_msg() != '' {
|
||||
return stmt.error(res)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
pub fn (stmt Stmt) gen_metadata() &C.MYSQL_RES {
|
||||
return C.mysql_stmt_result_metadata(stmt.stmt)
|
||||
}
|
||||
|
||||
pub fn (stmt Stmt) fetch_fields(res &C.MYSQL_RES) &C.MYSQL_FIELD {
|
||||
return C.mysql_fetch_fields(res)
|
||||
}
|
||||
|
||||
pub fn (stmt Stmt) fetch_stmt() !int {
|
||||
res := C.mysql_stmt_fetch(stmt.stmt)
|
||||
if res !in [0, 100] && stmt.get_error_msg() != '' {
|
||||
return stmt.error(res)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
pub fn (stmt Stmt) close() ! {
|
||||
if !C.mysql_stmt_close(stmt.stmt) && stmt.get_error_msg() != '' {
|
||||
return stmt.error(1)
|
||||
}
|
||||
if !C.mysql_stmt_free_result(stmt.stmt) && stmt.get_error_msg() != '' {
|
||||
return stmt.error(1)
|
||||
}
|
||||
}
|
||||
|
||||
fn (stmt Stmt) get_error_msg() string {
|
||||
return unsafe { cstring_to_vstring(&char(C.mysql_stmt_error(stmt.stmt))) }
|
||||
}
|
||||
|
||||
pub fn (stmt Stmt) error(code int) IError {
|
||||
msg := stmt.get_error_msg()
|
||||
return &SQLError{
|
||||
msg: '${msg} (${code}) (${stmt.query})'
|
||||
code: code
|
||||
}
|
||||
}
|
||||
|
||||
fn (stmt Stmt) get_field_count() u16 {
|
||||
return C.mysql_stmt_field_count(stmt.stmt)
|
||||
}
|
||||
|
||||
pub fn (mut stmt Stmt) bind_bool(b &bool) {
|
||||
stmt.bind(mysql.mysql_type_tiny, b, 0)
|
||||
}
|
||||
|
||||
pub fn (mut stmt Stmt) bind_byte(b &byte) {
|
||||
stmt.bind(mysql.mysql_type_tiny, b, 0)
|
||||
}
|
||||
|
||||
pub fn (mut stmt Stmt) bind_u8(b &u8) {
|
||||
stmt.bind(mysql.mysql_type_tiny, b, 0)
|
||||
}
|
||||
|
||||
pub fn (mut stmt Stmt) bind_i8(b &i8) {
|
||||
stmt.bind(mysql.mysql_type_tiny, b, 0)
|
||||
}
|
||||
|
||||
pub fn (mut stmt Stmt) bind_i16(b &i16) {
|
||||
stmt.bind(mysql.mysql_type_short, b, 0)
|
||||
}
|
||||
|
||||
pub fn (mut stmt Stmt) bind_u16(b &u16) {
|
||||
stmt.bind(mysql.mysql_type_short, b, 0)
|
||||
}
|
||||
|
||||
pub fn (mut stmt Stmt) bind_int(b &int) {
|
||||
stmt.bind(mysql.mysql_type_long, b, 0)
|
||||
}
|
||||
|
||||
pub fn (mut stmt Stmt) bind_u32(b &u32) {
|
||||
stmt.bind(mysql.mysql_type_long, b, 0)
|
||||
}
|
||||
|
||||
pub fn (mut stmt Stmt) bind_i64(b &i64) {
|
||||
stmt.bind(mysql.mysql_type_longlong, b, 0)
|
||||
}
|
||||
|
||||
pub fn (mut stmt Stmt) bind_u64(b &u64) {
|
||||
stmt.bind(mysql.mysql_type_longlong, b, 0)
|
||||
}
|
||||
|
||||
pub fn (mut stmt Stmt) bind_f32(b &f32) {
|
||||
stmt.bind(mysql.mysql_type_float, b, 0)
|
||||
}
|
||||
|
||||
pub fn (mut stmt Stmt) bind_f64(b &f64) {
|
||||
stmt.bind(mysql.mysql_type_double, b, 0)
|
||||
}
|
||||
|
||||
pub fn (mut stmt Stmt) bind_text(b string) {
|
||||
stmt.bind(mysql.mysql_type_string, b.str, u32(b.len))
|
||||
}
|
||||
|
||||
pub fn (mut stmt Stmt) bind(typ int, buffer voidptr, buf_len u32) {
|
||||
stmt.binds << C.MYSQL_BIND{
|
||||
buffer_type: typ
|
||||
buffer: buffer
|
||||
buffer_length: buf_len
|
||||
length: 0
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (mut stmt Stmt) bind_res(fields &C.MYSQL_FIELD, dataptr []&u8, lens []u32, num_fields int) {
|
||||
for i in 0 .. num_fields {
|
||||
len := unsafe { FieldType(fields[i].@type).get_len() }
|
||||
stmt.res << C.MYSQL_BIND{
|
||||
buffer_type: unsafe { fields[i].@type }
|
||||
buffer: dataptr[i]
|
||||
length: &lens[i]
|
||||
buffer_length: len
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (mut stmt Stmt) bind_result_buffer() ! {
|
||||
res := C.mysql_stmt_bind_result(stmt.stmt, unsafe { &C.MYSQL_BIND(stmt.res.data) })
|
||||
if res && stmt.get_error_msg() != '' {
|
||||
return stmt.error(1)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (mut stmt Stmt) store_result() ! {
|
||||
res := C.mysql_stmt_store_result(stmt.stmt)
|
||||
if res != 0 && stmt.get_error_msg() != '' {
|
||||
return stmt.error(res)
|
||||
}
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
module mysql
|
||||
|
||||
// get_error_msg - returns error message from MySQL instance.
|
||||
fn get_error_msg(conn &C.MYSQL) string {
|
||||
return unsafe { C.mysql_error(conn).vstring() }
|
||||
}
|
||||
|
||||
// get_errno - returns error number from MySQL instance.
|
||||
fn get_errno(conn &C.MYSQL) int {
|
||||
return C.mysql_errno(conn)
|
||||
}
|
||||
|
||||
// resolve_nil_str - returns an empty string if passed value is a nil pointer.
|
||||
fn resolve_nil_str(ptr &u8) string {
|
||||
if isnil(ptr) {
|
||||
return ''
|
||||
}
|
||||
return unsafe { ptr.vstring() }
|
||||
}
|
||||
|
||||
[inline]
|
||||
fn mystring(b &u8) string {
|
||||
unsafe {
|
||||
return b.vstring()
|
||||
}
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
[deprecated: 'import db.mysql instead']
|
||||
[deprecated_after: '2023-02-01']
|
||||
module mysql
|
@ -1,66 +1,102 @@
|
||||
module conv
|
||||
|
||||
// htn64 converts a the 64 bit value `host` to the net format (htonll)
|
||||
// htn64 - DON'T USE, use hton64 instead
|
||||
[deprecated: 'use hton64() instead']
|
||||
[deprecated_after: '2023-12-31']
|
||||
pub fn htn64(host u64) u64 {
|
||||
return hton64(host)
|
||||
}
|
||||
|
||||
// hton64 converts the 64 bit value `host` to the net format (htonll)
|
||||
pub fn hton64(host u64) u64 {
|
||||
$if little_endian {
|
||||
// vfmt off
|
||||
return (
|
||||
((host >> 56) & 0x00000000_000000FF) |
|
||||
((host >> 40) & 0x00000000_0000FF00) |
|
||||
((host >> 24) & 0x00000000_00FF0000) |
|
||||
((host >> 8) & 0x00000000_FF000000) |
|
||||
((host << 8) & 0x000000FF_00000000) |
|
||||
((host << 24) & 0x0000FF00_00000000) |
|
||||
((host << 40) & 0x00FF0000_00000000) |
|
||||
((host << 56) & 0xFF000000_00000000)
|
||||
)
|
||||
return ((host >> 56) & 0x00000000_000000FF) |
|
||||
((host >> 40) & 0x00000000_0000FF00) |
|
||||
((host >> 24) & 0x00000000_00FF0000) |
|
||||
((host >> 8) & 0x00000000_FF000000) |
|
||||
((host << 8) & 0x000000FF_00000000) |
|
||||
((host << 24) & 0x0000FF00_00000000) |
|
||||
((host << 40) & 0x00FF0000_00000000) |
|
||||
((host << 56) & 0xFF000000_00000000)
|
||||
// vfmt on
|
||||
} $else {
|
||||
return host
|
||||
}
|
||||
}
|
||||
|
||||
// htn32 converts the 32 bit value `host` to the net format (htonl)
|
||||
// htn32 - DON'T USE, use hton32 instead
|
||||
[deprecated: 'use hton32() instead']
|
||||
[deprecated_after: '2023-12-31']
|
||||
pub fn htn32(host u32) u32 {
|
||||
return hton32(host)
|
||||
}
|
||||
|
||||
// hton32 converts the 32 bit value `host` to the net format (htonl)
|
||||
pub fn hton32(host u32) u32 {
|
||||
$if little_endian {
|
||||
// vfmt off
|
||||
return (
|
||||
((host >> 24) & 0x0000_00FF) |
|
||||
((host >> 8) & 0x0000_FF00) |
|
||||
((host << 8) & 0x00FF_0000) |
|
||||
((host << 24) & 0xFF00_0000)
|
||||
)
|
||||
return ((host >> 24) & 0x0000_00FF) |
|
||||
((host >> 8) & 0x0000_FF00) |
|
||||
((host << 8) & 0x00FF_0000) |
|
||||
((host << 24) & 0xFF00_0000)
|
||||
// vfmt on
|
||||
} $else {
|
||||
return host
|
||||
}
|
||||
}
|
||||
|
||||
// htn16 converts the 16 bit value `host` to the net format (htons)
|
||||
// htn16 - DON'T USE, use hton16 instead
|
||||
[deprecated: 'use hton16() instead']
|
||||
[deprecated_after: '2023-12-31']
|
||||
pub fn htn16(host u16) u16 {
|
||||
return hton16(host)
|
||||
}
|
||||
|
||||
// hton16 converts the 16 bit value `host` to the net format (htons)
|
||||
pub fn hton16(host u16) u16 {
|
||||
$if little_endian {
|
||||
// vfmt off
|
||||
return (
|
||||
((host >> 8) & 0x00FF) |
|
||||
((host << 8) & 0xFF00)
|
||||
)
|
||||
return ((host >> 8) & 0x00FF) |
|
||||
((host << 8) & 0xFF00)
|
||||
// vfmt on
|
||||
} $else {
|
||||
return host
|
||||
}
|
||||
}
|
||||
|
||||
// nth64 converts the 64 bit value `net` to the host format (ntohll)
|
||||
// nth64 - DON'T USE, use ntoh64 instead
|
||||
[deprecated: 'use ntoh64() instead']
|
||||
[deprecated_after: '2023-12-31']
|
||||
pub fn nth64(net u64) u64 {
|
||||
return htn64(net)
|
||||
return ntoh64(net)
|
||||
}
|
||||
|
||||
// nth32 converts the 32 bit value `net` to the host format (ntohl)
|
||||
// ntoh64 converts the 64 bit value `net` to the host format (ntohll)
|
||||
pub fn ntoh64(net u64) u64 {
|
||||
return hton64(net)
|
||||
}
|
||||
|
||||
// nth32 - DON'T USE, use ntoh32 instead
|
||||
[deprecated: 'use ntoh32() instead']
|
||||
[deprecated_after: '2023-12-31']
|
||||
pub fn nth32(net u32) u32 {
|
||||
return htn32(net)
|
||||
return ntoh32(net)
|
||||
}
|
||||
|
||||
// nth16 converts the 16 bit value `net` to the host format (ntohs)
|
||||
pub fn nth16(net u16) u16 {
|
||||
return htn16(net)
|
||||
// ntoh32 converts the 32 bit value `net` to the host format (ntohl)
|
||||
pub fn ntoh32(net u32) u32 {
|
||||
return hton32(net)
|
||||
}
|
||||
|
||||
// nth16 - DON'T USE, use ntoh16 instead
|
||||
[deprecated: 'use ntoh16() instead']
|
||||
[deprecated_after: '2023-12-31']
|
||||
pub fn nth16(net u16) u16 {
|
||||
return ntoh16(net)
|
||||
}
|
||||
|
||||
// ntoh16 converts the 16 bit value `net` to the host format (ntohs)
|
||||
pub fn ntoh16(net u16) u16 {
|
||||
return hton16(net)
|
||||
}
|
||||
|
@ -13,32 +13,32 @@ fn check[T](f fn (a T) T, finv fn (b T) T, x T) {
|
||||
}
|
||||
}
|
||||
|
||||
fn test_htn64_nth64() {
|
||||
assert 0 == conv.htn64(0)
|
||||
assert 0 == conv.nth64(0)
|
||||
assert 0xFFFF_FFFF_FFFF_FFFF == conv.nth64(0xFFFF_FFFF_FFFF_FFFF)
|
||||
assert 0xFFFF_FFFF_FFFF_FFFF == conv.htn64(0xFFFF_FFFF_FFFF_FFFF)
|
||||
fn test_hton64_ntoh64() {
|
||||
assert 0 == conv.hton64(0)
|
||||
assert 0 == conv.ntoh64(0)
|
||||
assert 0xFFFF_FFFF_FFFF_FFFF == conv.ntoh64(0xFFFF_FFFF_FFFF_FFFF)
|
||||
assert 0xFFFF_FFFF_FFFF_FFFF == conv.hton64(0xFFFF_FFFF_FFFF_FFFF)
|
||||
for x in [u64(1), 2, 128, 65536, 2147483648] {
|
||||
check(conv.htn64, conv.nth64, x)
|
||||
check(conv.hton64, conv.ntoh64, x)
|
||||
}
|
||||
}
|
||||
|
||||
fn test_htn32_nth32() {
|
||||
assert 0 == conv.htn32(0)
|
||||
assert 0 == conv.nth32(0)
|
||||
assert 0xFFFF_FFFF == conv.nth32(0xFFFF_FFFF)
|
||||
assert 0x0101_0101 == conv.htn32(0x0101_0101)
|
||||
fn test_hton32_ntoh32() {
|
||||
assert 0 == conv.hton32(0)
|
||||
assert 0 == conv.ntoh32(0)
|
||||
assert 0xFFFF_FFFF == conv.ntoh32(0xFFFF_FFFF)
|
||||
assert 0x0101_0101 == conv.hton32(0x0101_0101)
|
||||
for x in [u32(1), 2, 128, 65536, 2147483648] {
|
||||
check(conv.htn32, conv.nth32, x)
|
||||
check(conv.hton32, conv.ntoh32, x)
|
||||
}
|
||||
}
|
||||
|
||||
fn test_htn16_nth16() {
|
||||
assert 0 == conv.htn16(0)
|
||||
assert 0 == conv.nth16(0)
|
||||
assert 0xFFFF == conv.nth16(0xFFFF)
|
||||
assert 0x0101 == conv.htn16(0x0101)
|
||||
fn test_hton16_ntoh16() {
|
||||
assert 0 == conv.hton16(0)
|
||||
assert 0 == conv.ntoh16(0)
|
||||
assert 0xFFFF == conv.ntoh16(0xFFFF)
|
||||
assert 0x0101 == conv.hton16(0x0101)
|
||||
for x in [u16(1), 2, 128, 65534] {
|
||||
check(conv.htn16, conv.nth16, x)
|
||||
check(conv.hton16, conv.ntoh16, x)
|
||||
}
|
||||
}
|
||||
|
@ -46,13 +46,16 @@ fn (mut s ChunkScanner) skip_crlf() {
|
||||
s.pos += 2
|
||||
}
|
||||
|
||||
fn (mut s ChunkScanner) read_chunk(chunksize u32) string {
|
||||
fn (mut s ChunkScanner) read_chunk(chunksize u32) !string {
|
||||
startpos := s.pos
|
||||
s.pos += int(chunksize)
|
||||
if s.pos > s.text.len {
|
||||
return error('invalid chunksize')
|
||||
}
|
||||
return s.text[startpos..s.pos]
|
||||
}
|
||||
|
||||
pub fn decode(text string) string {
|
||||
pub fn decode(text string) !string {
|
||||
mut sb := strings.new_builder(100)
|
||||
mut cscanner := ChunkScanner{
|
||||
pos: 0
|
||||
@ -64,7 +67,8 @@ pub fn decode(text string) string {
|
||||
break
|
||||
}
|
||||
cscanner.skip_crlf()
|
||||
sb.write_string(cscanner.read_chunk(csize))
|
||||
ch := cscanner.read_chunk(csize)!
|
||||
sb.write_string(ch)
|
||||
cscanner.skip_crlf()
|
||||
}
|
||||
cscanner.skip_crlf()
|
||||
|
16
vlib/net/http/chunked/dechunk_test.v
Normal file
16
vlib/net/http/chunked/dechunk_test.v
Normal file
@ -0,0 +1,16 @@
|
||||
module chunked
|
||||
|
||||
fn test_invalid_chunk() {
|
||||
mut is_failure := false
|
||||
|
||||
decode('eee') or { is_failure = true }
|
||||
|
||||
assert is_failure
|
||||
}
|
||||
|
||||
fn test_valid_chunk() {
|
||||
chunks := '4\r\nWiki\r\n7\r\npedia i\r\nB\r\nn \r\nchunks.\r\n0\r\n\r\n'
|
||||
str := decode(chunks) or { panic('uh oh') }
|
||||
|
||||
assert str == 'Wikipedia in \r\nchunks.'
|
||||
}
|
@ -179,13 +179,6 @@ pub fn url_encode_form_data(data map[string]string) string {
|
||||
return pieces.join('&')
|
||||
}
|
||||
|
||||
[deprecated: 'use fetch()']
|
||||
fn fetch_with_method(method Method, _config FetchConfig) !Response {
|
||||
mut config := _config
|
||||
config.method = method
|
||||
return fetch(config)
|
||||
}
|
||||
|
||||
fn build_url_from_fetch(config FetchConfig) !string {
|
||||
mut url := urllib.parse(config.url)!
|
||||
if config.params.len == 0 {
|
||||
@ -202,23 +195,3 @@ fn build_url_from_fetch(config FetchConfig) !string {
|
||||
url.raw_query = query
|
||||
return url.str()
|
||||
}
|
||||
|
||||
[deprecated: 'unescape_url is deprecated, use urllib.query_unescape() instead']
|
||||
pub fn unescape_url(s string) string {
|
||||
panic('http.unescape_url() was replaced with urllib.query_unescape()')
|
||||
}
|
||||
|
||||
[deprecated: 'escape_url is deprecated, use urllib.query_escape() instead']
|
||||
pub fn escape_url(s string) string {
|
||||
panic('http.escape_url() was replaced with urllib.query_escape()')
|
||||
}
|
||||
|
||||
[deprecated: 'unescape is deprecated, use urllib.query_escape() instead']
|
||||
pub fn unescape(s string) string {
|
||||
panic('http.unescape() was replaced with http.unescape_url()')
|
||||
}
|
||||
|
||||
[deprecated: 'escape is deprecated, use urllib.query_unescape() instead']
|
||||
pub fn escape(s string) string {
|
||||
panic('http.escape() was replaced with http.escape_url()')
|
||||
}
|
||||
|
@ -10,7 +10,6 @@ import strconv
|
||||
pub struct Response {
|
||||
pub mut:
|
||||
body string
|
||||
text string [deprecated: 'use Response.body instead'; deprecated_after: '2022-10-03']
|
||||
header Header
|
||||
status_code int
|
||||
status_msg string
|
||||
@ -42,7 +41,7 @@ pub fn parse_response(resp string) !Response {
|
||||
header := parse_headers(resp.substr(start_idx, end_idx))!
|
||||
mut body := resp.substr(end_idx, resp.len)
|
||||
if header.get(.transfer_encoding) or { '' } == 'chunked' {
|
||||
body = chunked.decode(body)
|
||||
body = chunked.decode(body)!
|
||||
}
|
||||
return Response{
|
||||
http_version: version
|
||||
@ -50,7 +49,6 @@ pub fn parse_response(resp string) !Response {
|
||||
status_msg: status_msg
|
||||
header: header
|
||||
body: body
|
||||
text: body // TODO: remove as depreciated
|
||||
}
|
||||
}
|
||||
|
||||
@ -118,14 +116,13 @@ pub struct ResponseConfig {
|
||||
status Status = .ok
|
||||
header Header
|
||||
body string
|
||||
text string [deprecated: 'use ResponseConfig.body instead'; deprecated_after: '2022-10-03']
|
||||
}
|
||||
|
||||
// new_response creates a Response object from the configuration. This
|
||||
// function will add a Content-Length header if body is not empty.
|
||||
pub fn new_response(conf ResponseConfig) Response {
|
||||
mut resp := Response{
|
||||
body: conf.body + conf.text
|
||||
body: conf.body
|
||||
header: conf.header
|
||||
}
|
||||
if resp.body.len > 0 && !resp.header.contains(.content_length) {
|
||||
|
@ -3,7 +3,7 @@ module http
|
||||
fn test_response_bytestr_1() {
|
||||
resp := new_response(
|
||||
status: .ok
|
||||
text: 'Foo' // TODO: replace with `body` once deprecaped
|
||||
body: 'Foo'
|
||||
)
|
||||
assert resp.bytestr() == 'HTTP/1.1 200 OK\r\n' + 'Content-Length: 3\r\n' + '\r\n' + 'Foo'
|
||||
}
|
||||
@ -43,5 +43,4 @@ fn test_parse_response() {
|
||||
assert x.header.contains(.content_length)
|
||||
assert x.header.get(.content_length)! == '3'
|
||||
assert x.body == 'Foo'
|
||||
assert x.text == 'Foo'
|
||||
}
|
||||
|
@ -1,4 +0,0 @@
|
||||
## Description:
|
||||
|
||||
The `pg` module has been moved to `db.pg`.
|
||||
Update your code to do: `import db.pg` instead.
|
@ -1,16 +0,0 @@
|
||||
|
||||
#if !defined(PG_VERSION_NUM)
|
||||
#error VERROR_MESSAGE PG_VERSION_NUM is not defined. Please install the development headers for PostgreSQL, they are usually in a package named libpq-dev
|
||||
#endif
|
||||
|
||||
#if PG_VERSION_NUM < 100000
|
||||
#define CONNECTION_CHECK_WRITABLE 9
|
||||
#endif
|
||||
|
||||
#if PG_VERSION_NUM < 100000
|
||||
#define CONNECTION_CONSUME 10
|
||||
#endif
|
||||
|
||||
#if PG_VERSION_NUM < 120000
|
||||
#define CONNECTION_GSS_STARTUP 11
|
||||
#endif
|
171
vlib/pg/oid.v
171
vlib/pg/oid.v
@ -1,171 +0,0 @@
|
||||
module pg
|
||||
|
||||
pub enum Oid {
|
||||
t_bool = 16
|
||||
t_bytea = 17
|
||||
t_char = 18
|
||||
t_name = 19
|
||||
t_int8 = 20
|
||||
t_int2 = 21
|
||||
t_int2vector = 22
|
||||
t_int4 = 23
|
||||
t_regproc = 24
|
||||
t_text = 25
|
||||
t_oid = 26
|
||||
t_tid = 27
|
||||
t_xid = 28
|
||||
t_cid = 29
|
||||
t_vector = 30
|
||||
t_pg_ddl_command = 32
|
||||
t_pg_type = 71
|
||||
t_pg_attribute = 75
|
||||
t_pg_proc = 81
|
||||
t_pg_class = 83
|
||||
t_json = 114
|
||||
t_xml = 142
|
||||
t__xml = 143
|
||||
t_pg_node_tree = 194
|
||||
t__json = 199
|
||||
t_smgr = 210
|
||||
t_index_am_handler = 325
|
||||
t_point = 600
|
||||
t_lseg = 601
|
||||
t_path = 602
|
||||
t_box = 603
|
||||
t_polygon = 604
|
||||
t_line = 628
|
||||
t__line = 629
|
||||
t_cidr = 650
|
||||
t__cidr = 651
|
||||
t_float4 = 700
|
||||
t_float8 = 701
|
||||
t_abstime = 702
|
||||
t_reltime = 703
|
||||
t_tinterval = 704
|
||||
t_unknown = 705
|
||||
t_circle = 718
|
||||
t__circle = 719
|
||||
t_money = 790
|
||||
t__money = 791
|
||||
t_macaddr = 829
|
||||
t_inet = 869
|
||||
t__bool = 1000
|
||||
t__bytea = 1001
|
||||
t__char = 1002
|
||||
t__name = 1003
|
||||
t__int2 = 1005
|
||||
t__int2vector = 1006
|
||||
t__int4 = 1007
|
||||
t__regproc = 1008
|
||||
t__text = 1009
|
||||
t__tid = 1010
|
||||
t__xid = 1011
|
||||
t__cid = 1012
|
||||
t__vector = 1013
|
||||
t__bpchar = 1014
|
||||
t__varchar = 1015
|
||||
t__int8 = 1016
|
||||
t__point = 1017
|
||||
t__lseg = 1018
|
||||
t__path = 1019
|
||||
t__box = 1020
|
||||
t__float4 = 1021
|
||||
t__float8 = 1022
|
||||
t__abstime = 1023
|
||||
t__reltime = 1024
|
||||
t__tinterval = 1025
|
||||
t__polygon = 1027
|
||||
t__ = 1028
|
||||
t_aclitem = 1033
|
||||
t__aclitem = 1034
|
||||
t__macaddr = 1040
|
||||
t__inet = 1041
|
||||
t_bpchar = 1042
|
||||
t_varchar = 1043
|
||||
t_date = 1082
|
||||
t_time = 1083
|
||||
t_timestamp = 1114
|
||||
t__timestamp = 1115
|
||||
t__date = 1182
|
||||
t__time = 1183
|
||||
t_timestamptz = 1184
|
||||
t__timestamptz = 1185
|
||||
t_interval = 1186
|
||||
t__interval = 1187
|
||||
t__numeric = 1231
|
||||
t_pg_database = 1248
|
||||
t__cstring = 1263
|
||||
t_timetz = 1266
|
||||
t__timetz = 1270
|
||||
t_bit = 1560
|
||||
t__bit = 1561
|
||||
t_varbit = 1562
|
||||
t__varbit = 1563
|
||||
t_numeric = 1700
|
||||
t_refcursor = 1790
|
||||
t__refcursor = 2201
|
||||
t_regprocedure = 2202
|
||||
t_regoper = 2203
|
||||
t_regoperator = 2204
|
||||
t_regclass = 2205
|
||||
t_regtype = 2206
|
||||
t__regprocedure = 2207
|
||||
t__regoper = 2208
|
||||
t__regoperator = 2209
|
||||
t__regclass = 2210
|
||||
t__regtype = 2211
|
||||
t_record = 2249
|
||||
t_cstring = 2275
|
||||
t_any = 2276
|
||||
t_anyarray = 2277
|
||||
t_v = 2278
|
||||
t_trigger = 2279
|
||||
t_language_handler = 2280
|
||||
t_internal = 2281
|
||||
t_opaque = 2282
|
||||
t_anyelement = 2283
|
||||
t__record = 2287
|
||||
t_anynonarray = 2776
|
||||
t_pg_authid = 2842
|
||||
t_pg_auth_members = 2843
|
||||
t__txid_snapshot = 2949
|
||||
t_uuid = 2950
|
||||
t__uuid = 2951
|
||||
t_txid_snapshot = 2970
|
||||
t_fdw_handler = 3115
|
||||
t_pg_lsn = 3220
|
||||
t__pg_lsn = 3221
|
||||
t_tsm_handler = 3310
|
||||
t_anyenum = 3500
|
||||
t_tsvector = 3614
|
||||
t_tsquery = 3615
|
||||
t_gtsvector = 3642
|
||||
t__tsvector = 3643
|
||||
t__gtsvector = 3644
|
||||
t__tsquery = 3645
|
||||
t_regconfig = 3734
|
||||
t__regconfig = 3735
|
||||
t_regdictionary = 3769
|
||||
t__regdictionary = 3770
|
||||
t_jsonb = 3802
|
||||
t__jsonb = 3807
|
||||
t_anyrange = 3831
|
||||
t_event_trigger = 3838
|
||||
t_int4range = 3904
|
||||
t__int4range = 3905
|
||||
t_numrange = 3906
|
||||
t__numrange = 3907
|
||||
t_tsrange = 3908
|
||||
t__tsrange = 3909
|
||||
t_tstzrange = 3910
|
||||
t__tstzrange = 3911
|
||||
t_daterange = 3912
|
||||
t__daterange = 3913
|
||||
t_int8range = 3926
|
||||
t__int8range = 3927
|
||||
t_pg_shseclabel = 4066
|
||||
t_regnamespace = 4089
|
||||
t__regnamespace = 4090
|
||||
t_regrole = 4096
|
||||
t__regrole = 4097
|
||||
}
|
292
vlib/pg/orm.v
292
vlib/pg/orm.v
@ -1,292 +0,0 @@
|
||||
module pg
|
||||
|
||||
import orm
|
||||
import time
|
||||
import net.conv
|
||||
|
||||
// sql expr
|
||||
|
||||
pub fn (db DB) @select(config orm.SelectConfig, data orm.QueryData, where orm.QueryData) ![][]orm.Primitive {
|
||||
query := orm.orm_select_gen(config, '"', true, '$', 1, where)
|
||||
|
||||
res := pg_stmt_worker(db, query, where, data)!
|
||||
|
||||
mut ret := [][]orm.Primitive{}
|
||||
|
||||
if config.is_count {
|
||||
}
|
||||
|
||||
for row in res {
|
||||
mut row_data := []orm.Primitive{}
|
||||
for i, val in row.vals {
|
||||
field := str_to_primitive(val, config.types[i])!
|
||||
row_data << field
|
||||
}
|
||||
ret << row_data
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
// sql stmt
|
||||
|
||||
pub fn (db DB) insert(table string, data orm.QueryData) ! {
|
||||
query, converted_data := orm.orm_stmt_gen(.default, table, '"', .insert, true, '$',
|
||||
1, data, orm.QueryData{})
|
||||
pg_stmt_worker(db, query, converted_data, orm.QueryData{})!
|
||||
}
|
||||
|
||||
pub fn (db DB) update(table string, data orm.QueryData, where orm.QueryData) ! {
|
||||
query, _ := orm.orm_stmt_gen(.default, table, '"', .update, true, '$', 1, data, where)
|
||||
pg_stmt_worker(db, query, data, where)!
|
||||
}
|
||||
|
||||
pub fn (db DB) delete(table string, where orm.QueryData) ! {
|
||||
query, _ := orm.orm_stmt_gen(.default, table, '"', .delete, true, '$', 1, orm.QueryData{},
|
||||
where)
|
||||
pg_stmt_worker(db, query, orm.QueryData{}, where)!
|
||||
}
|
||||
|
||||
pub fn (db DB) last_id() int {
|
||||
query := 'SELECT LASTVAL();'
|
||||
|
||||
return db.q_int(query) or { 0 }
|
||||
}
|
||||
|
||||
// table
|
||||
|
||||
pub fn (db DB) create(table string, fields []orm.TableField) ! {
|
||||
query := orm.orm_table_gen(table, '"', true, 0, fields, pg_type_from_v, false) or { return err }
|
||||
pg_stmt_worker(db, query, orm.QueryData{}, orm.QueryData{})!
|
||||
}
|
||||
|
||||
pub fn (db DB) drop(table string) ! {
|
||||
query := 'DROP TABLE "${table}";'
|
||||
pg_stmt_worker(db, query, orm.QueryData{}, orm.QueryData{})!
|
||||
}
|
||||
|
||||
// utils
|
||||
|
||||
fn pg_stmt_worker(db DB, query string, data orm.QueryData, where orm.QueryData) ![]Row {
|
||||
mut param_types := []u32{}
|
||||
mut param_vals := []&char{}
|
||||
mut param_lens := []int{}
|
||||
mut param_formats := []int{}
|
||||
|
||||
pg_stmt_binder(mut param_types, mut param_vals, mut param_lens, mut param_formats,
|
||||
data)
|
||||
pg_stmt_binder(mut param_types, mut param_vals, mut param_lens, mut param_formats,
|
||||
where)
|
||||
|
||||
res := C.PQexecParams(db.conn, &char(query.str), param_vals.len, param_types.data,
|
||||
param_vals.data, param_lens.data, param_formats.data, 0) // here, the last 0 means require text results, 1 - binary results
|
||||
return db.handle_error_or_result(res, 'orm_stmt_worker')
|
||||
}
|
||||
|
||||
fn pg_stmt_binder(mut types []u32, mut vals []&char, mut lens []int, mut formats []int, d orm.QueryData) {
|
||||
for data in d.data {
|
||||
pg_stmt_match(mut types, mut vals, mut lens, mut formats, data)
|
||||
}
|
||||
}
|
||||
|
||||
fn pg_stmt_match(mut types []u32, mut vals []&char, mut lens []int, mut formats []int, data orm.Primitive) {
|
||||
match data {
|
||||
bool {
|
||||
types << u32(Oid.t_bool)
|
||||
vals << &char(&data)
|
||||
lens << int(sizeof(bool))
|
||||
formats << 1
|
||||
}
|
||||
u8 {
|
||||
types << u32(Oid.t_char)
|
||||
vals << &char(&data)
|
||||
lens << int(sizeof(u8))
|
||||
formats << 1
|
||||
}
|
||||
u16 {
|
||||
types << u32(Oid.t_int2)
|
||||
num := conv.htn16(data)
|
||||
vals << &char(&num)
|
||||
lens << int(sizeof(u16))
|
||||
formats << 1
|
||||
}
|
||||
u32 {
|
||||
types << u32(Oid.t_int4)
|
||||
num := conv.htn32(data)
|
||||
vals << &char(&num)
|
||||
lens << int(sizeof(u32))
|
||||
formats << 1
|
||||
}
|
||||
u64 {
|
||||
types << u32(Oid.t_int8)
|
||||
num := conv.htn64(data)
|
||||
vals << &char(&num)
|
||||
lens << int(sizeof(u64))
|
||||
formats << 1
|
||||
}
|
||||
i8 {
|
||||
types << u32(Oid.t_char)
|
||||
vals << &char(&data)
|
||||
lens << int(sizeof(i8))
|
||||
formats << 1
|
||||
}
|
||||
i16 {
|
||||
types << u32(Oid.t_int2)
|
||||
num := conv.htn16(u16(data))
|
||||
vals << &char(&num)
|
||||
lens << int(sizeof(i16))
|
||||
formats << 1
|
||||
}
|
||||
int {
|
||||
types << u32(Oid.t_int4)
|
||||
num := conv.htn32(u32(data))
|
||||
vals << &char(&num)
|
||||
lens << int(sizeof(int))
|
||||
formats << 1
|
||||
}
|
||||
i64 {
|
||||
types << u32(Oid.t_int8)
|
||||
num := conv.htn64(u64(data))
|
||||
vals << &char(&num)
|
||||
lens << int(sizeof(i64))
|
||||
formats << 1
|
||||
}
|
||||
f32 {
|
||||
types << u32(Oid.t_float4)
|
||||
vals << &char(&data)
|
||||
lens << int(sizeof(f32))
|
||||
formats << 1
|
||||
}
|
||||
f64 {
|
||||
types << u32(Oid.t_float8)
|
||||
vals << &char(&data)
|
||||
lens << int(sizeof(f64))
|
||||
formats << 1
|
||||
}
|
||||
string {
|
||||
// If paramTypes is NULL, or any particular element in the array is zero,
|
||||
// the server infers a data type for the parameter symbol in the same way
|
||||
// it would do for an untyped literal string.
|
||||
types << u32(0)
|
||||
vals << &char(data.str)
|
||||
lens << data.len
|
||||
formats << 0
|
||||
}
|
||||
time.Time {
|
||||
datetime := data.format_ss()
|
||||
types << u32(0)
|
||||
vals << &char(datetime.str)
|
||||
lens << datetime.len
|
||||
formats << 0
|
||||
}
|
||||
orm.InfixType {
|
||||
pg_stmt_match(mut types, mut vals, mut lens, mut formats, data.right)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn pg_type_from_v(typ int) !string {
|
||||
str := match typ {
|
||||
orm.type_idx['i8'], orm.type_idx['i16'], orm.type_idx['u8'], orm.type_idx['u16'] {
|
||||
'SMALLINT'
|
||||
}
|
||||
orm.type_idx['bool'] {
|
||||
'BOOLEAN'
|
||||
}
|
||||
orm.type_idx['int'], orm.type_idx['u32'] {
|
||||
'INT'
|
||||
}
|
||||
orm.time {
|
||||
'TIMESTAMP'
|
||||
}
|
||||
orm.type_idx['i64'], orm.type_idx['u64'] {
|
||||
'BIGINT'
|
||||
}
|
||||
orm.float[0] {
|
||||
'REAL'
|
||||
}
|
||||
orm.float[1] {
|
||||
'DOUBLE PRECISION'
|
||||
}
|
||||
orm.type_string {
|
||||
'TEXT'
|
||||
}
|
||||
orm.serial {
|
||||
'SERIAL'
|
||||
}
|
||||
else {
|
||||
''
|
||||
}
|
||||
}
|
||||
if str == '' {
|
||||
return error('Unknown type ${typ}')
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
fn str_to_primitive(str string, typ int) !orm.Primitive {
|
||||
match typ {
|
||||
// bool
|
||||
orm.type_idx['bool'] {
|
||||
return orm.Primitive(str == 't')
|
||||
}
|
||||
// i8
|
||||
orm.type_idx['i8'] {
|
||||
return orm.Primitive(str.i8())
|
||||
}
|
||||
// i16
|
||||
orm.type_idx['i16'] {
|
||||
return orm.Primitive(str.i16())
|
||||
}
|
||||
// int
|
||||
orm.type_idx['int'] {
|
||||
return orm.Primitive(str.int())
|
||||
}
|
||||
// i64
|
||||
orm.type_idx['i64'] {
|
||||
return orm.Primitive(str.i64())
|
||||
}
|
||||
// u8
|
||||
orm.type_idx['u8'] {
|
||||
data := str.i8()
|
||||
return orm.Primitive(*unsafe { &u8(&data) })
|
||||
}
|
||||
// u16
|
||||
orm.type_idx['u16'] {
|
||||
data := str.i16()
|
||||
return orm.Primitive(*unsafe { &u16(&data) })
|
||||
}
|
||||
// u32
|
||||
orm.type_idx['u32'] {
|
||||
data := str.int()
|
||||
return orm.Primitive(*unsafe { &u32(&data) })
|
||||
}
|
||||
// u64
|
||||
orm.type_idx['u64'] {
|
||||
data := str.i64()
|
||||
return orm.Primitive(*unsafe { &u64(&data) })
|
||||
}
|
||||
// f32
|
||||
orm.type_idx['f32'] {
|
||||
return orm.Primitive(str.f32())
|
||||
}
|
||||
// f64
|
||||
orm.type_idx['f64'] {
|
||||
return orm.Primitive(str.f64())
|
||||
}
|
||||
orm.type_string {
|
||||
return orm.Primitive(str)
|
||||
}
|
||||
orm.time {
|
||||
if str.contains_any(' /:-') {
|
||||
date_time_str := time.parse(str)!
|
||||
return orm.Primitive(date_time_str)
|
||||
}
|
||||
|
||||
timestamp := str.int()
|
||||
return orm.Primitive(time.unix(timestamp))
|
||||
}
|
||||
else {}
|
||||
}
|
||||
return error('Unknown field type ${typ}')
|
||||
}
|
347
vlib/pg/pg.v
347
vlib/pg/pg.v
@ -1,347 +0,0 @@
|
||||
module pg
|
||||
|
||||
import io
|
||||
|
||||
$if $pkgconfig('libpq') {
|
||||
#pkgconfig --cflags --libs libpq
|
||||
} $else {
|
||||
#flag -lpq
|
||||
#flag linux -I/usr/include/postgresql
|
||||
|
||||
#flag darwin -I/opt/local/include/postgresql11
|
||||
#flag darwin -L/opt/local/lib/postgresql11
|
||||
|
||||
#flag darwin -I/usr/local/opt/libpq/include
|
||||
#flag darwin -L/usr/local/opt/libpq/lib
|
||||
|
||||
#flag darwin -I/opt/homebrew/include
|
||||
#flag darwin -L/opt/homebrew/lib
|
||||
|
||||
#flag darwin -I/opt/homebrew/opt/libpq/include
|
||||
#flag darwin -L/opt/homebrew/opt/libpq/lib
|
||||
|
||||
#flag windows -I @VEXEROOT/thirdparty/pg/include
|
||||
#flag windows -L @VEXEROOT/thirdparty/pg/win64
|
||||
}
|
||||
|
||||
// PostgreSQL Source Code
|
||||
// https://doxygen.postgresql.org/libpq-fe_8h.html
|
||||
#include <libpq-fe.h>
|
||||
|
||||
// for PG_VERSION_NUM, which is defined everywhere at least since PG 9.5
|
||||
#include <pg_config.h>
|
||||
|
||||
// for orm
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include "@VMODROOT/vlib/pg/compatibility.h"
|
||||
|
||||
pub struct DB {
|
||||
mut:
|
||||
conn voidptr = unsafe { nil }
|
||||
}
|
||||
|
||||
pub struct Row {
|
||||
pub mut:
|
||||
vals []string
|
||||
}
|
||||
|
||||
pub struct Config {
|
||||
pub:
|
||||
host string = 'localhost'
|
||||
port int = 5432
|
||||
user string
|
||||
password string
|
||||
dbname string
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
struct C.pg_result {}
|
||||
|
||||
struct C.pg_conn {}
|
||||
|
||||
[typedef]
|
||||
pub struct C.PGresult {}
|
||||
|
||||
[typedef]
|
||||
pub struct C.PGconn {}
|
||||
|
||||
pub enum ConnStatusType {
|
||||
ok = C.CONNECTION_OK
|
||||
bad = C.CONNECTION_BAD
|
||||
// Non-blocking mode only below here
|
||||
// The existence of these should never be relied upon - they should only be used for user feedback or similar purposes.
|
||||
started = C.CONNECTION_STARTED // Waiting for connection to be made.
|
||||
made = C.CONNECTION_MADE // Connection OK; waiting to send.
|
||||
awaiting_response = C.CONNECTION_AWAITING_RESPONSE // Waiting for a response from the postmaster.
|
||||
auth_ok = C.CONNECTION_AUTH_OK // Received authentication; waiting for backend startup.
|
||||
setenv = C.CONNECTION_SETENV // Negotiating environment.
|
||||
ssl_startup = C.CONNECTION_SSL_STARTUP // Negotiating SSL.
|
||||
needed = C.CONNECTION_NEEDED // Internal state: connect() needed . Available in PG 8
|
||||
check_writable = C.CONNECTION_CHECK_WRITABLE // Check if we could make a writable connection. Available since PG 10
|
||||
consume = C.CONNECTION_CONSUME // Wait for any pending message and consume them. Available since PG 10
|
||||
gss_startup = C.CONNECTION_GSS_STARTUP // Negotiating GSSAPI; available since PG 12
|
||||
}
|
||||
|
||||
[typedef]
|
||||
pub enum ExecStatusType {
|
||||
empty_query = C.PGRES_EMPTY_QUERY // empty query string was executed
|
||||
command_ok = C.PGRES_COMMAND_OK // a query command that doesn't return anything was executed properly by the backend
|
||||
tuples_ok = C.PGRES_TUPLES_OK // a query command that returns tuples was executed properly by the backend, PGresult contains the result tuples
|
||||
copy_out = C.PGRES_COPY_OUT // Copy Out data transfer in progress
|
||||
copy_in = C.PGRES_COPY_IN // Copy In data transfer in progress
|
||||
bad_response = C.PGRES_BAD_RESPONSE // an unexpected response was recv'd from the backend
|
||||
nonfatal_error = C.PGRES_NONFATAL_ERROR // notice or warning message
|
||||
fatal_error = C.PGRES_FATAL_ERROR // query failed
|
||||
copy_both = C.PGRES_COPY_BOTH // Copy In/Out data transfer in progress
|
||||
single_tuple = C.PGRES_SINGLE_TUPLE // single tuple from larger resultset
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
fn C.PQconnectdb(const_conninfo &char) &C.PGconn
|
||||
|
||||
fn C.PQstatus(const_conn &C.PGconn) int
|
||||
|
||||
fn C.PQerrorMessage(const_conn &C.PGconn) &char
|
||||
|
||||
fn C.PQexec(res &C.PGconn, const_query &char) &C.PGresult
|
||||
|
||||
//
|
||||
|
||||
fn C.PQgetvalue(const_res &C.PGresult, int, int) &char
|
||||
|
||||
fn C.PQresultStatus(const_res &C.PGresult) int
|
||||
|
||||
fn C.PQntuples(const_res &C.PGresult) int
|
||||
|
||||
fn C.PQnfields(const_res &C.PGresult) int
|
||||
|
||||
// Params:
|
||||
// const Oid *paramTypes
|
||||
// const char *const *paramValues
|
||||
// const int *paramLengths
|
||||
// const int *paramFormats
|
||||
fn C.PQexecParams(conn &C.PGconn, const_command &char, nParams int, const_paramTypes &int, const_paramValues &char, const_paramLengths &int, const_paramFormats &int, resultFormat int) &C.PGresult
|
||||
|
||||
fn C.PQputCopyData(conn &C.PGconn, const_buffer &char, nbytes int) int
|
||||
|
||||
fn C.PQputCopyEnd(conn &C.PGconn, const_errmsg &char) int
|
||||
|
||||
fn C.PQgetCopyData(conn &C.PGconn, buffer &&char, async int) int
|
||||
|
||||
// cleanup
|
||||
|
||||
fn C.PQclear(res &C.PGresult)
|
||||
|
||||
fn C.PQfreemem(ptr voidptr)
|
||||
|
||||
fn C.PQfinish(conn &C.PGconn)
|
||||
|
||||
// connect makes a new connection to the database server using
|
||||
// the parameters from the `Config` structure, returning
|
||||
// a connection error when something goes wrong
|
||||
pub fn connect(config Config) !DB {
|
||||
conninfo := 'host=${config.host} port=${config.port} user=${config.user} dbname=${config.dbname} password=${config.password}'
|
||||
conn := C.PQconnectdb(&char(conninfo.str))
|
||||
if conn == 0 {
|
||||
return error('libpq memory allocation error')
|
||||
}
|
||||
status := unsafe { ConnStatusType(C.PQstatus(conn)) }
|
||||
if status != .ok {
|
||||
// We force the construction of a new string as the
|
||||
// error message will be freed by the next `PQfinish`
|
||||
// call
|
||||
c_error_msg := unsafe { C.PQerrorMessage(conn).vstring() }
|
||||
error_msg := '${c_error_msg}'
|
||||
C.PQfinish(conn)
|
||||
return error('Connection to a PG database failed: ${error_msg}')
|
||||
}
|
||||
return DB{
|
||||
conn: conn
|
||||
}
|
||||
}
|
||||
|
||||
fn res_to_rows(res voidptr) []Row {
|
||||
nr_rows := C.PQntuples(res)
|
||||
nr_cols := C.PQnfields(res)
|
||||
|
||||
mut rows := []Row{}
|
||||
for i in 0 .. nr_rows {
|
||||
mut row := Row{}
|
||||
for j in 0 .. nr_cols {
|
||||
val := C.PQgetvalue(res, i, j)
|
||||
sval := unsafe { val.vstring() }
|
||||
row.vals << sval
|
||||
}
|
||||
rows << row
|
||||
}
|
||||
|
||||
C.PQclear(res)
|
||||
return rows
|
||||
}
|
||||
|
||||
// close frees the underlying resource allocated by the database connection
|
||||
pub fn (db DB) close() {
|
||||
C.PQfinish(db.conn)
|
||||
}
|
||||
|
||||
// q_int submit a command to the database server and
|
||||
// returns an the first field in the first tuple
|
||||
// converted to an int. If no row is found or on
|
||||
// command failure, an error is returned
|
||||
pub fn (db DB) q_int(query string) !int {
|
||||
rows := db.exec(query)!
|
||||
if rows.len == 0 {
|
||||
return error('q_int "${query}" not found')
|
||||
}
|
||||
row := rows[0]
|
||||
if row.vals.len == 0 {
|
||||
return 0
|
||||
}
|
||||
val := row.vals[0]
|
||||
return val.int()
|
||||
}
|
||||
|
||||
// q_string submit a command to the database server and
|
||||
// returns an the first field in the first tuple
|
||||
// as a string. If no row is found or on
|
||||
// command failure, an error is returned
|
||||
pub fn (db DB) q_string(query string) !string {
|
||||
rows := db.exec(query)!
|
||||
if rows.len == 0 {
|
||||
return error('q_string "${query}" not found')
|
||||
}
|
||||
row := rows[0]
|
||||
if row.vals.len == 0 {
|
||||
return ''
|
||||
}
|
||||
val := row.vals[0]
|
||||
return val
|
||||
}
|
||||
|
||||
// q_strings submit a command to the database server and
|
||||
// returns the resulting row set. Alias of `exec`
|
||||
pub fn (db DB) q_strings(query string) ![]Row {
|
||||
return db.exec(query)
|
||||
}
|
||||
|
||||
// exec submit a command to the database server and wait
|
||||
// for the result, returning an error on failure and a
|
||||
// row set on success
|
||||
pub fn (db DB) exec(query string) ![]Row {
|
||||
res := C.PQexec(db.conn, &char(query.str))
|
||||
return db.handle_error_or_result(res, 'exec')
|
||||
}
|
||||
|
||||
fn rows_first_or_empty(rows []Row) !Row {
|
||||
if rows.len == 0 {
|
||||
return error('no row')
|
||||
}
|
||||
return rows[0]
|
||||
}
|
||||
|
||||
pub fn (db DB) exec_one(query string) !Row {
|
||||
res := C.PQexec(db.conn, &char(query.str))
|
||||
e := unsafe { C.PQerrorMessage(db.conn).vstring() }
|
||||
if e != '' {
|
||||
return error('pg exec error: "${e}"')
|
||||
}
|
||||
row := rows_first_or_empty(res_to_rows(res))!
|
||||
return row
|
||||
}
|
||||
|
||||
// exec_param_many executes a query with the provided parameters
|
||||
pub fn (db DB) exec_param_many(query string, params []string) ![]Row {
|
||||
unsafe {
|
||||
mut param_vals := []&char{len: params.len}
|
||||
for i in 0 .. params.len {
|
||||
param_vals[i] = &char(params[i].str)
|
||||
}
|
||||
|
||||
res := C.PQexecParams(db.conn, &char(query.str), params.len, 0, param_vals.data,
|
||||
0, 0, 0)
|
||||
return db.handle_error_or_result(res, 'exec_param_many')
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (db DB) exec_param2(query string, param string, param2 string) ![]Row {
|
||||
return db.exec_param_many(query, [param, param2])
|
||||
}
|
||||
|
||||
pub fn (db DB) exec_param(query string, param string) ![]Row {
|
||||
return db.exec_param_many(query, [param])
|
||||
}
|
||||
|
||||
fn (db DB) handle_error_or_result(res voidptr, elabel string) ![]Row {
|
||||
e := unsafe { C.PQerrorMessage(db.conn).vstring() }
|
||||
if e != '' {
|
||||
C.PQclear(res)
|
||||
return error('pg ${elabel} error:\n${e}')
|
||||
}
|
||||
return res_to_rows(res)
|
||||
}
|
||||
|
||||
// copy_expert execute COPY commands
|
||||
// https://www.postgresql.org/docs/9.5/libpq-copy.html
|
||||
pub fn (db DB) copy_expert(query string, mut file io.ReaderWriter) !int {
|
||||
mut res := C.PQexec(db.conn, &char(query.str))
|
||||
status := unsafe { ExecStatusType(C.PQresultStatus(res)) }
|
||||
defer {
|
||||
C.PQclear(res)
|
||||
}
|
||||
|
||||
e := unsafe { C.PQerrorMessage(db.conn).vstring() }
|
||||
if e != '' {
|
||||
return error('pg copy error:\n${e}')
|
||||
}
|
||||
|
||||
if status == .copy_in {
|
||||
mut buf := []u8{len: 4 * 1024}
|
||||
for {
|
||||
n := file.read(mut buf) or {
|
||||
msg := 'pg copy error: Failed to read from input'
|
||||
C.PQputCopyEnd(db.conn, &char(msg.str))
|
||||
return err
|
||||
}
|
||||
if n <= 0 {
|
||||
break
|
||||
}
|
||||
|
||||
code := C.PQputCopyData(db.conn, buf.data, n)
|
||||
if code == -1 {
|
||||
return error('pg copy error: Failed to send data, code=${code}')
|
||||
}
|
||||
}
|
||||
|
||||
code := C.PQputCopyEnd(db.conn, &char(0))
|
||||
|
||||
if code != 1 {
|
||||
return error('pg copy error: Failed to finish copy command, code: ${code}')
|
||||
}
|
||||
} else if status == .copy_out {
|
||||
for {
|
||||
address := &char(0)
|
||||
n_bytes := C.PQgetCopyData(db.conn, &address, 0)
|
||||
if n_bytes > 0 {
|
||||
mut local_buf := []u8{len: n_bytes}
|
||||
unsafe { C.memcpy(&u8(local_buf.data), address, n_bytes) }
|
||||
file.write(local_buf) or {
|
||||
C.PQfreemem(address)
|
||||
return err
|
||||
}
|
||||
} else if n_bytes == -1 {
|
||||
break
|
||||
} else if n_bytes == -2 {
|
||||
// consult PQerrorMessage for the reason
|
||||
return error('pg copy error: read error')
|
||||
}
|
||||
if address != 0 {
|
||||
C.PQfreemem(address)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
[deprecated: 'import db.pg instead']
|
||||
[deprecated_after: '2023-02-01']
|
||||
module pg
|
@ -23,6 +23,8 @@ enum Action {
|
||||
commit_line
|
||||
delete_left
|
||||
delete_right
|
||||
delete_word_left
|
||||
delete_line
|
||||
move_cursor_left
|
||||
move_cursor_right
|
||||
move_cursor_begining
|
||||
@ -131,6 +133,10 @@ pub fn (mut r Readline) read_line_utf8(prompt string) ![]rune {
|
||||
r.disable_raw_mode()
|
||||
if r.current.len == 0 {
|
||||
return error('empty line')
|
||||
} else {
|
||||
if r.current.last() == `\n` {
|
||||
r.current.pop()
|
||||
}
|
||||
}
|
||||
return r.current
|
||||
}
|
||||
@ -188,6 +194,12 @@ fn (r Readline) analyse(c int) Action {
|
||||
27 {
|
||||
return r.analyse_control()
|
||||
} // ESC
|
||||
21 {
|
||||
return .delete_line
|
||||
} // CTRL + U
|
||||
23 {
|
||||
return .delete_word_left
|
||||
} // CTRL + W
|
||||
1 {
|
||||
return .move_cursor_begining
|
||||
} // ^A
|
||||
@ -226,27 +238,7 @@ fn (r Readline) analyse_control() Action {
|
||||
}
|
||||
else {}
|
||||
}
|
||||
/*
|
||||
//TODO
|
||||
match c {
|
||||
case `[`:
|
||||
sequence := r.read_char()?
|
||||
match sequence {
|
||||
case `C`: return .move_cursor_right
|
||||
case `D`: return .move_cursor_left
|
||||
case `B`: return .history_next
|
||||
case `A`: return .history_previous
|
||||
case `1`: return r.analyse_extended_control()
|
||||
case `2`: return r.analyse_extended_control_no_eat(sequence)
|
||||
case `3`: return r.analyse_extended_control_no_eat(sequence)
|
||||
case `9`:
|
||||
foo()
|
||||
bar()
|
||||
else:
|
||||
}
|
||||
else:
|
||||
}
|
||||
*/
|
||||
|
||||
return .nothing
|
||||
}
|
||||
|
||||
@ -294,6 +286,8 @@ fn (mut r Readline) execute(a Action, c int) bool {
|
||||
.commit_line { return r.commit_line() }
|
||||
.delete_left { r.delete_character() }
|
||||
.delete_right { r.suppr_character() }
|
||||
.delete_line { r.delete_line() }
|
||||
.delete_word_left { r.delete_word_left() }
|
||||
.move_cursor_left { r.move_cursor_left() }
|
||||
.move_cursor_right { r.move_cursor_right() }
|
||||
.move_cursor_begining { r.move_cursor_begining() }
|
||||
@ -424,6 +418,46 @@ fn (mut r Readline) delete_character() {
|
||||
r.refresh_line()
|
||||
}
|
||||
|
||||
fn (mut r Readline) delete_word_left() {
|
||||
if r.cursor == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
orig_cursor := r.cursor
|
||||
if r.cursor >= r.current.len {
|
||||
r.cursor = r.current.len - 1
|
||||
}
|
||||
|
||||
if r.current[r.cursor] != ` ` && r.current[r.cursor - 1] == ` ` {
|
||||
r.cursor--
|
||||
}
|
||||
|
||||
if r.current[r.cursor] == ` ` {
|
||||
for r.cursor > 0 && r.current[r.cursor] == ` ` {
|
||||
r.cursor--
|
||||
}
|
||||
for r.cursor > 0 && r.current[r.cursor - 1] != ` ` {
|
||||
r.cursor--
|
||||
}
|
||||
} else {
|
||||
for r.cursor > 0 {
|
||||
if r.current[r.cursor - 1] == ` ` {
|
||||
break
|
||||
}
|
||||
r.cursor--
|
||||
}
|
||||
}
|
||||
|
||||
r.current.delete_many(r.cursor, orig_cursor - r.cursor)
|
||||
r.refresh_line()
|
||||
}
|
||||
|
||||
fn (mut r Readline) delete_line() {
|
||||
r.current = []
|
||||
r.cursor = 0
|
||||
r.refresh_line()
|
||||
}
|
||||
|
||||
// suppr_character removes (suppresses) the character in front of the cursor.
|
||||
fn (mut r Readline) suppr_character() {
|
||||
if r.cursor >= r.current.len {
|
||||
|
@ -19,6 +19,10 @@ $if macos {
|
||||
#flag -framework Metal -framework Cocoa -framework MetalKit -framework QuartzCore
|
||||
}
|
||||
}
|
||||
$if linux {
|
||||
#flag -D SOKOL_GLCORE33
|
||||
}
|
||||
|
||||
$if ios {
|
||||
#flag -DSOKOL_METAL
|
||||
#flag -framework Foundation -framework Metal -framework MetalKit -framework UIKit
|
||||
|
@ -1,4 +0,0 @@
|
||||
## Description
|
||||
|
||||
The `sqlite` module has been moved to `db.sqlite`.
|
||||
Update your code to do: `import db.sqlite` instead.
|
@ -1,179 +0,0 @@
|
||||
module sqlite
|
||||
|
||||
import orm
|
||||
import time
|
||||
|
||||
// sql expr
|
||||
|
||||
pub fn (db DB) @select(config orm.SelectConfig, data orm.QueryData, where orm.QueryData) ![][]orm.Primitive {
|
||||
// 1. Create query and bind necessary data
|
||||
query := orm.orm_select_gen(config, '`', true, '?', 1, where)
|
||||
$if trace_sqlite ? {
|
||||
eprintln('> @select query: "${query}"')
|
||||
}
|
||||
stmt := db.new_init_stmt(query)!
|
||||
defer {
|
||||
stmt.finalize()
|
||||
}
|
||||
mut c := 1
|
||||
sqlite_stmt_binder(stmt, where, query, mut c)!
|
||||
sqlite_stmt_binder(stmt, data, query, mut c)!
|
||||
|
||||
mut ret := [][]orm.Primitive{}
|
||||
|
||||
if config.is_count {
|
||||
// 2. Get count of returned values & add it to ret array
|
||||
step := stmt.step()
|
||||
if step !in [sqlite_row, sqlite_ok, sqlite_done] {
|
||||
return db.error_message(step, query)
|
||||
}
|
||||
count := stmt.sqlite_select_column(0, 8)!
|
||||
ret << [count]
|
||||
return ret
|
||||
}
|
||||
for {
|
||||
// 2. Parse returned values
|
||||
step := stmt.step()
|
||||
if step == sqlite_done {
|
||||
break
|
||||
}
|
||||
if step != sqlite_ok && step != sqlite_row {
|
||||
break
|
||||
}
|
||||
mut row := []orm.Primitive{}
|
||||
for i, typ in config.types {
|
||||
primitive := stmt.sqlite_select_column(i, typ)!
|
||||
row << primitive
|
||||
}
|
||||
ret << row
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// sql stmt
|
||||
|
||||
pub fn (db DB) insert(table string, data orm.QueryData) ! {
|
||||
query, converted_data := orm.orm_stmt_gen(.sqlite, table, '`', .insert, true, '?',
|
||||
1, data, orm.QueryData{})
|
||||
sqlite_stmt_worker(db, query, converted_data, orm.QueryData{})!
|
||||
}
|
||||
|
||||
pub fn (db DB) update(table string, data orm.QueryData, where orm.QueryData) ! {
|
||||
query, _ := orm.orm_stmt_gen(.sqlite, table, '`', .update, true, '?', 1, data, where)
|
||||
sqlite_stmt_worker(db, query, data, where)!
|
||||
}
|
||||
|
||||
pub fn (db DB) delete(table string, where orm.QueryData) ! {
|
||||
query, _ := orm.orm_stmt_gen(.sqlite, table, '`', .delete, true, '?', 1, orm.QueryData{},
|
||||
where)
|
||||
sqlite_stmt_worker(db, query, orm.QueryData{}, where)!
|
||||
}
|
||||
|
||||
pub fn (db DB) last_id() int {
|
||||
query := 'SELECT last_insert_rowid();'
|
||||
|
||||
return db.q_int(query)
|
||||
}
|
||||
|
||||
// table
|
||||
pub fn (db DB) create(table string, fields []orm.TableField) ! {
|
||||
query := orm.orm_table_gen(table, '`', true, 0, fields, sqlite_type_from_v, false) or {
|
||||
return err
|
||||
}
|
||||
sqlite_stmt_worker(db, query, orm.QueryData{}, orm.QueryData{})!
|
||||
}
|
||||
|
||||
pub fn (db DB) drop(table string) ! {
|
||||
query := 'DROP TABLE `${table}`;'
|
||||
sqlite_stmt_worker(db, query, orm.QueryData{}, orm.QueryData{})!
|
||||
}
|
||||
|
||||
// helper
|
||||
|
||||
// Executes query and bind prepared statement data directly
|
||||
fn sqlite_stmt_worker(db DB, query string, data orm.QueryData, where orm.QueryData) ! {
|
||||
$if trace_sqlite ? {
|
||||
eprintln('> sqlite_stmt_worker query: "${query}"')
|
||||
}
|
||||
stmt := db.new_init_stmt(query)!
|
||||
defer {
|
||||
stmt.finalize()
|
||||
}
|
||||
mut c := 1
|
||||
sqlite_stmt_binder(stmt, data, query, mut c)!
|
||||
sqlite_stmt_binder(stmt, where, query, mut c)!
|
||||
stmt.orm_step(query)!
|
||||
}
|
||||
|
||||
// Binds all values of d in the prepared statement
|
||||
fn sqlite_stmt_binder(stmt Stmt, d orm.QueryData, query string, mut c &int) ! {
|
||||
for data in d.data {
|
||||
err := bind(stmt, c, data)
|
||||
|
||||
if err != 0 {
|
||||
return stmt.db.error_message(err, query)
|
||||
}
|
||||
c++
|
||||
}
|
||||
}
|
||||
|
||||
// Universal bind function
|
||||
fn bind(stmt Stmt, c &int, data orm.Primitive) int {
|
||||
mut err := 0
|
||||
match data {
|
||||
i8, i16, int, u8, u16, u32, bool {
|
||||
err = stmt.bind_int(c, int(data))
|
||||
}
|
||||
i64, u64 {
|
||||
err = stmt.bind_i64(c, i64(data))
|
||||
}
|
||||
f32, f64 {
|
||||
err = stmt.bind_f64(c, unsafe { *(&f64(&data)) })
|
||||
}
|
||||
string {
|
||||
err = stmt.bind_text(c, data)
|
||||
}
|
||||
time.Time {
|
||||
err = stmt.bind_int(c, int(data.unix))
|
||||
}
|
||||
orm.InfixType {
|
||||
err = bind(stmt, c, data.right)
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Selects column in result and converts it to an orm.Primitive
|
||||
fn (stmt Stmt) sqlite_select_column(idx int, typ int) !orm.Primitive {
|
||||
mut primitive := orm.Primitive(0)
|
||||
|
||||
if typ in orm.nums || typ == -1 {
|
||||
primitive = stmt.get_int(idx)
|
||||
} else if typ in orm.num64 {
|
||||
primitive = stmt.get_i64(idx)
|
||||
} else if typ in orm.float {
|
||||
primitive = stmt.get_f64(idx)
|
||||
} else if typ == orm.type_string {
|
||||
primitive = stmt.get_text(idx).clone()
|
||||
} else if typ == orm.time {
|
||||
d := stmt.get_int(idx)
|
||||
primitive = time.unix(d)
|
||||
} else {
|
||||
return error('Unknown type ${typ}')
|
||||
}
|
||||
|
||||
return primitive
|
||||
}
|
||||
|
||||
// Convert type int to sql type string
|
||||
fn sqlite_type_from_v(typ int) !string {
|
||||
return if typ in orm.nums || typ < 0 || typ in orm.num64 || typ == orm.time {
|
||||
'INTEGER'
|
||||
} else if typ in orm.float {
|
||||
'REAL'
|
||||
} else if typ == orm.type_string {
|
||||
'TEXT'
|
||||
} else {
|
||||
error('Unknown type ${typ}')
|
||||
}
|
||||
}
|
@ -1,118 +0,0 @@
|
||||
module sqlite
|
||||
|
||||
// Result represents Sqlite Result and Error Codes
|
||||
// see https://www.sqlite.org/rescode.html
|
||||
pub enum Result {
|
||||
ok = 0
|
||||
error = 1
|
||||
internal = 2
|
||||
perm = 3
|
||||
abort = 4
|
||||
busy = 5
|
||||
locked = 6
|
||||
nomem = 7
|
||||
readonly = 8
|
||||
interrupt = 9
|
||||
ioerr = 10
|
||||
corrupt = 11
|
||||
notfound = 12
|
||||
full = 13
|
||||
cantopen = 14
|
||||
protocol = 15
|
||||
empty = 16
|
||||
schema = 17
|
||||
toobig = 18
|
||||
constraint = 19
|
||||
mismatch = 20
|
||||
misuse = 21
|
||||
nolfs = 22
|
||||
auth = 23
|
||||
format = 24
|
||||
range = 25
|
||||
notadb = 26
|
||||
notice = 27
|
||||
warning = 28
|
||||
row = 100
|
||||
done = 101
|
||||
ok_load_permanently = 256
|
||||
error_missing_collseq = 257
|
||||
busy_recovery = 261
|
||||
locked_sharedcache = 262
|
||||
readonly_recovery = 264
|
||||
ioerr_read = 266
|
||||
corrupt_vtab = 267
|
||||
cantopen_notempdir = 270
|
||||
constraint_check = 275
|
||||
notice_recover_wal = 283
|
||||
warning_autoindex = 284
|
||||
error_retry = 513
|
||||
abort_rollback = 516
|
||||
busy_snapshot = 517
|
||||
locked_vtab = 518
|
||||
readonly_cantlock = 520
|
||||
ioerr_short_read = 522
|
||||
corrupt_sequence = 523
|
||||
cantopen_isdir = 526
|
||||
constraint_commithook = 531
|
||||
notice_recover_rollback = 539
|
||||
error_snapshot = 769
|
||||
busy_timeout = 773
|
||||
readonly_rollback = 776
|
||||
ioerr_write = 778
|
||||
corrupt_index = 779
|
||||
cantopen_fullpath = 782
|
||||
constraint_foreignkey = 787
|
||||
readonly_dbmoved = 1032
|
||||
ioerr_fsync = 1034
|
||||
cantopen_convpath = 1038
|
||||
constraint_function = 1043
|
||||
readonly_cantinit = 1288
|
||||
ioerr_dir_fsync = 1290
|
||||
cantopen_dirtywal = 1294
|
||||
constraint_notnull = 1299
|
||||
readonly_directory = 1544
|
||||
ioerr_truncate = 1546
|
||||
cantopen_symlink = 1550
|
||||
constraint_primarykey = 1555
|
||||
ioerr_fstat = 1802
|
||||
constraint_trigger = 1811
|
||||
ioerr_unlock = 2058
|
||||
constraint_unique = 2067
|
||||
ioerr_rdlock = 2314
|
||||
constraint_vtab = 2323
|
||||
ioerr_delete = 2570
|
||||
constraint_rowid = 2579
|
||||
ioerr_blocked = 2826
|
||||
constraint_pinned = 2835
|
||||
ioerr_nomem = 3082
|
||||
ioerr_access = 3338
|
||||
ioerr_checkreservedlock = 3594
|
||||
ioerr_lock = 3850
|
||||
ioerr_close = 4106
|
||||
ioerr_dir_close = 4362
|
||||
ioerr_shmopen = 4618
|
||||
ioerr_shmsize = 4874
|
||||
ioerr_shmlock = 5130
|
||||
ioerr_shmmap = 5386
|
||||
ioerr_seek = 5642
|
||||
ioerr_delete_noent = 5898
|
||||
ioerr_mmap = 6154
|
||||
ioerr_gettemppath = 6410
|
||||
ioerr_convpath = 6666
|
||||
ioerr_vnode = 6922
|
||||
ioerr_auth = 7178
|
||||
ioerr_begin_atomic = 7434
|
||||
ioerr_commit_atomic = 7690
|
||||
ioerr_rollback_atomic = 7946
|
||||
ioerr_data = 8202
|
||||
}
|
||||
|
||||
// is_error checks if it is an error code.
|
||||
pub fn (r Result) is_error() bool {
|
||||
return r !in [.ok, .row, .done]
|
||||
}
|
||||
|
||||
// is_error checks if `code` is an error code.
|
||||
pub fn is_error(code int) bool {
|
||||
return unsafe { Result(code).is_error() }
|
||||
}
|
@ -1,342 +0,0 @@
|
||||
module sqlite
|
||||
|
||||
$if freebsd || openbsd {
|
||||
#flag -I/usr/local/include
|
||||
#flag -L/usr/local/lib
|
||||
}
|
||||
$if windows {
|
||||
#flag windows -I@VEXEROOT/thirdparty/sqlite
|
||||
#flag windows -L@VEXEROOT/thirdparty/sqlite
|
||||
#flag windows @VEXEROOT/thirdparty/sqlite/sqlite3.o
|
||||
} $else {
|
||||
#flag -lsqlite3
|
||||
}
|
||||
|
||||
#include "sqlite3.h"
|
||||
|
||||
// https://www.sqlite.org/rescode.html
|
||||
pub const (
|
||||
sqlite_ok = 0
|
||||
sqlite_error = 1
|
||||
sqlite_row = 100
|
||||
sqlite_done = 101
|
||||
sqlite_cantopen = 14
|
||||
sqlite_ioerr_read = 266
|
||||
sqlite_ioerr_short_read = 522
|
||||
sqlite_ioerr_write = 778
|
||||
sqlite_ioerr_fsync = 1034
|
||||
sqlite_ioerr_fstat = 1802
|
||||
sqlite_ioerr_delete = 2570
|
||||
|
||||
sqlite_open_main_db = 0x00000100
|
||||
sqlite_open_temp_db = 0x00000200
|
||||
sqlite_open_transient_db = 0x00000400
|
||||
sqlite_open_main_journal = 0x00000800
|
||||
sqlite_open_temp_journal = 0x00001000
|
||||
sqlite_open_subjournal = 0x00002000
|
||||
sqlite_open_super_journal = 0x00004000
|
||||
sqlite_open_wal = 0x00080000
|
||||
)
|
||||
|
||||
pub enum SyncMode {
|
||||
off
|
||||
normal
|
||||
full
|
||||
}
|
||||
|
||||
pub enum JournalMode {
|
||||
off
|
||||
delete
|
||||
truncate
|
||||
persist
|
||||
memory
|
||||
}
|
||||
|
||||
pub struct C.sqlite3 {
|
||||
}
|
||||
|
||||
pub struct C.sqlite3_stmt {
|
||||
}
|
||||
|
||||
[heap]
|
||||
pub struct Stmt {
|
||||
stmt &C.sqlite3_stmt = unsafe { nil }
|
||||
db &DB = unsafe { nil }
|
||||
}
|
||||
|
||||
pub struct SQLError {
|
||||
MessageError
|
||||
}
|
||||
|
||||
//
|
||||
[heap]
|
||||
pub struct DB {
|
||||
pub mut:
|
||||
is_open bool
|
||||
mut:
|
||||
conn &C.sqlite3 = unsafe { nil }
|
||||
}
|
||||
|
||||
pub fn (db &DB) str() string {
|
||||
return 'sqlite.DB{ conn: ' + ptr_str(db.conn) + ' }'
|
||||
}
|
||||
|
||||
pub struct Row {
|
||||
pub mut:
|
||||
vals []string
|
||||
}
|
||||
|
||||
//
|
||||
fn C.sqlite3_open(&char, &&C.sqlite3) int
|
||||
|
||||
fn C.sqlite3_close(&C.sqlite3) int
|
||||
|
||||
fn C.sqlite3_busy_timeout(db &C.sqlite3, ms int) int
|
||||
|
||||
fn C.sqlite3_last_insert_rowid(&C.sqlite3) i64
|
||||
|
||||
//
|
||||
fn C.sqlite3_prepare_v2(&C.sqlite3, &char, int, &&C.sqlite3_stmt, &&char) int
|
||||
|
||||
fn C.sqlite3_step(&C.sqlite3_stmt) int
|
||||
|
||||
fn C.sqlite3_finalize(&C.sqlite3_stmt) int
|
||||
|
||||
//
|
||||
fn C.sqlite3_column_name(&C.sqlite3_stmt, int) &char
|
||||
|
||||
fn C.sqlite3_column_text(&C.sqlite3_stmt, int) &u8
|
||||
|
||||
fn C.sqlite3_column_int(&C.sqlite3_stmt, int) int
|
||||
|
||||
fn C.sqlite3_column_int64(&C.sqlite3_stmt, int) i64
|
||||
|
||||
fn C.sqlite3_column_double(&C.sqlite3_stmt, int) f64
|
||||
|
||||
fn C.sqlite3_column_count(&C.sqlite3_stmt) int
|
||||
|
||||
//
|
||||
fn C.sqlite3_errstr(int) &char
|
||||
|
||||
fn C.sqlite3_errmsg(&C.sqlite3) &char
|
||||
|
||||
fn C.sqlite3_free(voidptr)
|
||||
|
||||
fn C.sqlite3_changes(&C.sqlite3) int
|
||||
|
||||
// connect Opens the connection with a database.
|
||||
pub fn connect(path string) !DB {
|
||||
db := &C.sqlite3(unsafe { nil })
|
||||
code := C.sqlite3_open(&char(path.str), &db)
|
||||
if code != 0 {
|
||||
return &SQLError{
|
||||
msg: unsafe { cstring_to_vstring(&char(C.sqlite3_errstr(code))) }
|
||||
code: code
|
||||
}
|
||||
}
|
||||
return DB{
|
||||
conn: db
|
||||
is_open: true
|
||||
}
|
||||
}
|
||||
|
||||
// close Closes the DB.
|
||||
// TODO: For all functions, determine whether the connection is
|
||||
// closed first, and determine what to do if it is
|
||||
pub fn (mut db DB) close() !bool {
|
||||
code := C.sqlite3_close(db.conn)
|
||||
if code == 0 {
|
||||
db.is_open = false
|
||||
} else {
|
||||
return &SQLError{
|
||||
msg: unsafe { cstring_to_vstring(&char(C.sqlite3_errstr(code))) }
|
||||
code: code
|
||||
}
|
||||
}
|
||||
return true // successfully closed
|
||||
}
|
||||
|
||||
// Only for V ORM
|
||||
fn get_int_from_stmt(stmt &C.sqlite3_stmt) int {
|
||||
x := C.sqlite3_step(stmt)
|
||||
if x != C.SQLITE_OK && x != C.SQLITE_DONE {
|
||||
C.puts(C.sqlite3_errstr(x))
|
||||
}
|
||||
|
||||
res := C.sqlite3_column_int(stmt, 0)
|
||||
C.sqlite3_finalize(stmt)
|
||||
return res
|
||||
}
|
||||
|
||||
// last_insert_rowid returns last inserted rowid
|
||||
// https://www.sqlite.org/c3ref/last_insert_rowid.html
|
||||
pub fn (db &DB) last_insert_rowid() i64 {
|
||||
return C.sqlite3_last_insert_rowid(db.conn)
|
||||
}
|
||||
|
||||
// get_affected_rows_count returns `sqlite changes()` meaning amount of rows affected by most recent sql query
|
||||
pub fn (db &DB) get_affected_rows_count() int {
|
||||
return C.sqlite3_changes(db.conn)
|
||||
}
|
||||
|
||||
// Returns a single cell with value int.
|
||||
pub fn (db &DB) q_int(query string) int {
|
||||
stmt := &C.sqlite3_stmt(unsafe { nil })
|
||||
defer {
|
||||
C.sqlite3_finalize(stmt)
|
||||
}
|
||||
C.sqlite3_prepare_v2(db.conn, &char(query.str), query.len, &stmt, 0)
|
||||
C.sqlite3_step(stmt)
|
||||
|
||||
res := C.sqlite3_column_int(stmt, 0)
|
||||
return res
|
||||
}
|
||||
|
||||
// Returns a single cell with value string.
|
||||
pub fn (db &DB) q_string(query string) string {
|
||||
stmt := &C.sqlite3_stmt(unsafe { nil })
|
||||
defer {
|
||||
C.sqlite3_finalize(stmt)
|
||||
}
|
||||
C.sqlite3_prepare_v2(db.conn, &char(query.str), query.len, &stmt, 0)
|
||||
C.sqlite3_step(stmt)
|
||||
|
||||
val := unsafe { &u8(C.sqlite3_column_text(stmt, 0)) }
|
||||
return if val != &u8(0) { unsafe { tos_clone(val) } } else { '' }
|
||||
}
|
||||
|
||||
// Execute the query on db, return an array of all the results, alongside any result code.
|
||||
// Result codes: https://www.sqlite.org/rescode.html
|
||||
[manualfree]
|
||||
pub fn (db &DB) exec(query string) ([]Row, int) {
|
||||
stmt := &C.sqlite3_stmt(unsafe { nil })
|
||||
defer {
|
||||
C.sqlite3_finalize(stmt)
|
||||
}
|
||||
C.sqlite3_prepare_v2(db.conn, &char(query.str), query.len, &stmt, 0)
|
||||
nr_cols := C.sqlite3_column_count(stmt)
|
||||
mut res := 0
|
||||
mut rows := []Row{}
|
||||
for {
|
||||
res = C.sqlite3_step(stmt)
|
||||
// Result Code SQLITE_ROW; Another row is available
|
||||
if res != 100 {
|
||||
// C.puts(C.sqlite3_errstr(res))
|
||||
break
|
||||
}
|
||||
mut row := Row{}
|
||||
for i in 0 .. nr_cols {
|
||||
val := unsafe { &u8(C.sqlite3_column_text(stmt, i)) }
|
||||
if val == &u8(0) {
|
||||
row.vals << ''
|
||||
} else {
|
||||
row.vals << unsafe { tos_clone(val) }
|
||||
}
|
||||
}
|
||||
rows << row
|
||||
}
|
||||
return rows, res
|
||||
}
|
||||
|
||||
// Execute a query, handle error code
|
||||
// Return the first row from the resulting table
|
||||
[manualfree]
|
||||
pub fn (db &DB) exec_one(query string) !Row {
|
||||
rows, code := db.exec(query)
|
||||
defer {
|
||||
unsafe { rows.free() }
|
||||
}
|
||||
if rows.len == 0 {
|
||||
return &SQLError{
|
||||
msg: 'No rows'
|
||||
code: code
|
||||
}
|
||||
} else if code != 101 {
|
||||
return &SQLError{
|
||||
msg: unsafe { cstring_to_vstring(&char(C.sqlite3_errstr(code))) }
|
||||
code: code
|
||||
}
|
||||
}
|
||||
res := rows[0]
|
||||
return res
|
||||
}
|
||||
|
||||
[manualfree]
|
||||
pub fn (db &DB) error_message(code int, query string) IError {
|
||||
errmsg := unsafe { cstring_to_vstring(&char(C.sqlite3_errmsg(db.conn))) }
|
||||
msg := '${errmsg} (${code}) (${query})'
|
||||
unsafe { errmsg.free() }
|
||||
return SQLError{
|
||||
msg: msg
|
||||
code: code
|
||||
}
|
||||
}
|
||||
|
||||
// Execute a query returning only the result code.
|
||||
// In case you don't expect any row results, but still want a result code.
|
||||
// e.g. INSERT INTO ... VALUES (...)
|
||||
pub fn (db &DB) exec_none(query string) int {
|
||||
stmt := &C.sqlite3_stmt(unsafe { nil })
|
||||
C.sqlite3_prepare_v2(db.conn, &char(query.str), query.len, &stmt, 0)
|
||||
code := C.sqlite3_step(stmt)
|
||||
C.sqlite3_finalize(stmt)
|
||||
return code
|
||||
}
|
||||
|
||||
/*
|
||||
TODO
|
||||
pub fn (db &DB) exec_param(query string, param string) []Row {
|
||||
}
|
||||
*/
|
||||
|
||||
// Issue a "create table if not exists" command to the db.
|
||||
// Creates table named 'table_name', with columns generated from 'columns' array.
|
||||
// Default columns type will be TEXT.
|
||||
pub fn (db &DB) create_table(table_name string, columns []string) {
|
||||
db.exec('create table if not exists ${table_name} (' + columns.join(',\n') + ')')
|
||||
}
|
||||
|
||||
// Set a busy timeout in milliseconds.
|
||||
// Sleeps for a specified amount of time when a table is locked. The handler
|
||||
// will sleep multiple times until at least "ms" milliseconds of sleeping have accumulated.
|
||||
// (see https://www.sqlite.org/c3ref/busy_timeout.html)
|
||||
pub fn (db &DB) busy_timeout(ms int) int {
|
||||
return C.sqlite3_busy_timeout(db.conn, ms)
|
||||
}
|
||||
|
||||
// Sets disk synchronization mode,
|
||||
// which controls how aggressively SQLite will write data to physical storage.
|
||||
// off: No syncs at all. (fastest)
|
||||
// normal: Sync after each sequence of critical disk operations.
|
||||
// full: Sync after each critical disk operation (slowest).
|
||||
pub fn (db &DB) synchronization_mode(sync_mode SyncMode) {
|
||||
if sync_mode == .off {
|
||||
db.exec('pragma synchronous = OFF;')
|
||||
} else if sync_mode == .full {
|
||||
db.exec('pragma synchronous = FULL;')
|
||||
} else {
|
||||
db.exec('pragma synchronous = NORMAL;')
|
||||
}
|
||||
}
|
||||
|
||||
// Controls how the journal file is stored and processed.
|
||||
// off: No journal record is kept. (fastest)
|
||||
// memory: Journal record is held in memory, rather than on disk.
|
||||
// delete: At the conclusion of a transaction, journal file is deleted.
|
||||
// truncate: Journal file is truncated to a length of zero bytes.
|
||||
// persist: Journal file is left in place, but the header is overwritten to indicate journal is no longer valid.
|
||||
pub fn (db &DB) journal_mode(journal_mode JournalMode) {
|
||||
if journal_mode == .off {
|
||||
db.exec('pragma journal_mode = OFF;')
|
||||
} else if journal_mode == .delete {
|
||||
db.exec('pragma journal_mode = DELETE;')
|
||||
} else if journal_mode == .truncate {
|
||||
db.exec('pragma journal_mode = TRUNCATE;')
|
||||
} else if journal_mode == .persist {
|
||||
db.exec('pragma journal_mode = PERSIST;')
|
||||
} else if journal_mode == .memory {
|
||||
db.exec('pragma journal_mode = MEMORY;')
|
||||
} else {
|
||||
db.exec('pragma journal_mode = MEMORY;')
|
||||
}
|
||||
}
|
@ -1,78 +0,0 @@
|
||||
module sqlite
|
||||
|
||||
fn C.sqlite3_bind_double(&C.sqlite3_stmt, int, f64) int
|
||||
fn C.sqlite3_bind_int(&C.sqlite3_stmt, int, int) int
|
||||
fn C.sqlite3_bind_int64(&C.sqlite3_stmt, int, i64) int
|
||||
fn C.sqlite3_bind_text(&C.sqlite3_stmt, int, &char, int, voidptr) int
|
||||
|
||||
// Only for V ORM
|
||||
fn (db &DB) init_stmt(query string) (&C.sqlite3_stmt, int) {
|
||||
// println('init_stmt("$query")')
|
||||
stmt := &C.sqlite3_stmt(0)
|
||||
err := C.sqlite3_prepare_v2(db.conn, &char(query.str), query.len, &stmt, 0)
|
||||
return stmt, err
|
||||
}
|
||||
|
||||
fn (db &DB) new_init_stmt(query string) !Stmt {
|
||||
stmt, err := db.init_stmt(query)
|
||||
if err != sqlite_ok {
|
||||
return db.error_message(err, query)
|
||||
}
|
||||
return Stmt{stmt, db}
|
||||
}
|
||||
|
||||
fn (stmt &Stmt) bind_int(idx int, v int) int {
|
||||
return C.sqlite3_bind_int(stmt.stmt, idx, v)
|
||||
}
|
||||
|
||||
fn (stmt &Stmt) bind_i64(idx int, v i64) int {
|
||||
return C.sqlite3_bind_int64(stmt.stmt, idx, v)
|
||||
}
|
||||
|
||||
fn (stmt &Stmt) bind_f64(idx int, v f64) int {
|
||||
return C.sqlite3_bind_double(stmt.stmt, idx, v)
|
||||
}
|
||||
|
||||
fn (stmt &Stmt) bind_text(idx int, s string) int {
|
||||
return C.sqlite3_bind_text(stmt.stmt, idx, voidptr(s.str), s.len, 0)
|
||||
}
|
||||
|
||||
fn (stmt &Stmt) get_int(idx int) int {
|
||||
return C.sqlite3_column_int(stmt.stmt, idx)
|
||||
}
|
||||
|
||||
fn (stmt &Stmt) get_i64(idx int) i64 {
|
||||
return C.sqlite3_column_int64(stmt.stmt, idx)
|
||||
}
|
||||
|
||||
fn (stmt &Stmt) get_f64(idx int) f64 {
|
||||
return C.sqlite3_column_double(stmt.stmt, idx)
|
||||
}
|
||||
|
||||
fn (stmt &Stmt) get_text(idx int) string {
|
||||
b := &char(C.sqlite3_column_text(stmt.stmt, idx))
|
||||
|
||||
if b == &char(0) {
|
||||
return ''
|
||||
}
|
||||
return unsafe { b.vstring() }
|
||||
}
|
||||
|
||||
fn (stmt &Stmt) get_count() int {
|
||||
return C.sqlite3_column_count(stmt.stmt)
|
||||
}
|
||||
|
||||
fn (stmt &Stmt) step() int {
|
||||
return C.sqlite3_step(stmt.stmt)
|
||||
}
|
||||
|
||||
fn (stmt &Stmt) orm_step(query string) ! {
|
||||
res := stmt.step()
|
||||
if res != sqlite_ok && res != sqlite_done && res != sqlite_row {
|
||||
return stmt.db.error_message(res, query)
|
||||
}
|
||||
}
|
||||
|
||||
fn (stmt &Stmt) finalize() {
|
||||
C.sqlite3_finalize(stmt.stmt)
|
||||
}
|
@ -1,166 +0,0 @@
|
||||
module sqlite
|
||||
|
||||
type Sig1 = fn (&C.sqlite3_file, &i64) int // https://github.com/vlang/v/issues/16291
|
||||
|
||||
type Sig2 = fn (&Sqlite3_file, &int) int // https://github.com/vlang/v/issues/16291
|
||||
|
||||
pub type Sqlite3_file = C.sqlite3_file
|
||||
|
||||
// https://www.sqlite.org/c3ref/file.html
|
||||
pub struct C.sqlite3_file {
|
||||
pub mut:
|
||||
pMethods &C.sqlite3_io_methods // Methods for an open file
|
||||
}
|
||||
|
||||
// https://www.sqlite.org/c3ref/io_methods.html
|
||||
[heap]
|
||||
pub struct C.sqlite3_io_methods {
|
||||
mut:
|
||||
// version 1 and later fields
|
||||
iVersion int
|
||||
|
||||
xClose fn (&Sqlite3_file) int
|
||||
xRead fn (&Sqlite3_file, voidptr, int, i64) int
|
||||
xWrite fn (&Sqlite3_file, voidptr, int, i64) int
|
||||
xTruncate fn (&Sqlite3_file, i64) int
|
||||
xSync fn (&Sqlite3_file, int) int
|
||||
xFileSize Sig1
|
||||
xLock fn (&Sqlite3_file, int) int
|
||||
xUnlock fn (&Sqlite3_file, int) int
|
||||
xCheckReservedLock Sig2
|
||||
xFileControl fn (&Sqlite3_file, int, voidptr) int
|
||||
xSectorSize fn (&Sqlite3_file) int
|
||||
xDeviceCharacteristics fn (&Sqlite3_file) int
|
||||
// version 2 and later fields
|
||||
xShmMap fn (&Sqlite3_file, int, int, int, &voidptr) int
|
||||
xShmLock fn (&Sqlite3_file, int, int, int) int
|
||||
xShmBarrier fn (&Sqlite3_file)
|
||||
xShmUnmap fn (&Sqlite3_file, int) int
|
||||
// version 3 and later fields
|
||||
xFetch fn (&Sqlite3_file, i64, int, &voidptr) int
|
||||
xUnfetch fn (&Sqlite3_file, i64, voidptr) int
|
||||
}
|
||||
|
||||
pub type Sqlite3_io_methods = C.sqlite3_io_methods
|
||||
|
||||
// https://www.sqlite.org/c3ref/vfs.html
|
||||
type Fn_sqlite3_syscall_ptr = fn ()
|
||||
|
||||
pub type Sqlite3_vfs = C.sqlite3_vfs
|
||||
|
||||
[heap]
|
||||
pub struct C.sqlite3_vfs {
|
||||
pub mut:
|
||||
// version 1 and later fields
|
||||
iVersion int // Structure version number (currently 3)
|
||||
szOsFile int // Size of subclassed sqlite3_file
|
||||
mxPathname int // Maximum file pathname length
|
||||
pNext &Sqlite3_vfs // Next registered VFS
|
||||
zName &char // Name of this virtual file system
|
||||
pAppData voidptr // Pointer to application-specific data
|
||||
|
||||
xOpen fn (&Sqlite3_vfs, &char, &Sqlite3_file, int, &int) int
|
||||
xDelete fn (&Sqlite3_vfs, &char, int) int
|
||||
|
||||
xAccess fn (&Sqlite3_vfs, &char, int, &int) int
|
||||
xFullPathname fn (&Sqlite3_vfs, &char, int, &char) int
|
||||
xDlOpen fn (&Sqlite3_vfs, &char) voidptr
|
||||
xDlError fn (&Sqlite3_vfs, int, &char)
|
||||
xDlSym fn (&Sqlite3_vfs, voidptr, &char) voidptr // to fn accepting void and returning
|
||||
xDlClose fn (&Sqlite3_vfs, voidptr)
|
||||
xRandomness fn (&Sqlite3_vfs, int, &char) int
|
||||
xSleep fn (&Sqlite3_vfs, int) int
|
||||
xCurrentTime fn (&Sqlite3_vfs, &f64) int
|
||||
xGetLastError fn (&Sqlite3_vfs, int, &char) int
|
||||
// version two and later only fields
|
||||
xCurrentTimeInt64 fn (&Sqlite3_vfs, &i64) int
|
||||
// version three and later only fields
|
||||
xSetSystemCall fn (&Sqlite3_vfs, &char, Fn_sqlite3_syscall_ptr) int
|
||||
xGetSystemCall fn (&Sqlite3_vfs, &char) Fn_sqlite3_syscall_ptr
|
||||
xNextSystemCall fn (&Sqlite3_vfs, &char) &char
|
||||
}
|
||||
|
||||
// https://www.sqlite.org/c3ref/vfs_find.html
|
||||
fn C.sqlite3_vfs_find(&char) &C.sqlite3_vfs
|
||||
fn C.sqlite3_vfs_register(&C.sqlite3_vfs, int) int
|
||||
fn C.sqlite3_vfs_unregister(&C.sqlite3_vfs) int
|
||||
|
||||
// get_vfs Requests sqlite to return instance of VFS with given name.
|
||||
// when such vfs is not known, `none` is returned
|
||||
pub fn get_vfs(name string) !&Sqlite3_vfs {
|
||||
res := C.sqlite3_vfs_find(name.str)
|
||||
if res != unsafe { nil } {
|
||||
return res
|
||||
}
|
||||
return error('sqlite3_vfs_find returned 0')
|
||||
}
|
||||
|
||||
// get_default_vfs Asks sqlite for default VFS instance
|
||||
pub fn get_default_vfs() !&Sqlite3_vfs {
|
||||
res := C.sqlite3_vfs_find(unsafe { nil })
|
||||
if res != unsafe { nil } {
|
||||
return res
|
||||
}
|
||||
return error('sqlite3_vfs_find(0) returned 0')
|
||||
}
|
||||
|
||||
// register_as_nondefault Asks sqlite to register VFS passed in receiver argument as the known VFS.
|
||||
// more info about VFS: https://www.sqlite.org/c3ref/vfs.html
|
||||
// 'not TODOs' to prevent corruption: https://sqlite.org/howtocorrupt.html
|
||||
// example VFS: https://www.sqlite.org/src/doc/trunk/src/test_demovfs.c
|
||||
pub fn (mut v Sqlite3_vfs) register_as_nondefault() ! {
|
||||
res := C.sqlite3_vfs_register(v, 0)
|
||||
if sqlite_ok != res {
|
||||
return error('sqlite3_vfs_register returned ${res}')
|
||||
}
|
||||
}
|
||||
|
||||
// unregister Requests sqlite to stop using VFS as passed in receiver argument
|
||||
pub fn (mut v Sqlite3_vfs) unregister() ! {
|
||||
res := C.sqlite3_vfs_unregister(v)
|
||||
if sqlite_ok != res {
|
||||
return error('sqlite3_vfs_unregister returned ${res}')
|
||||
}
|
||||
}
|
||||
|
||||
// https://www.sqlite.org/c3ref/open.html
|
||||
fn C.sqlite3_open_v2(&char, &&C.sqlite3, int, &char) int
|
||||
|
||||
// https://www.sqlite.org/c3ref/c_open_autoproxy.html
|
||||
pub enum OpenModeFlag {
|
||||
readonly = 0x00000001
|
||||
readwrite = 0x00000002
|
||||
create = 0x00000004
|
||||
uri = 0x00000040
|
||||
memory = 0x00000080
|
||||
nomutex = 0x00008000
|
||||
fullmutex = 0x00010000
|
||||
sharedcache = 0x00020000
|
||||
privatecache = 0x00040000
|
||||
exrescode = 0x02000000
|
||||
nofollow = 0x01000000
|
||||
}
|
||||
|
||||
// connect_full Opens connection to sqlite database. It gives more control than `open`.
|
||||
// Flags give control over readonly and create decisions. Specific VFS can be chosen.
|
||||
pub fn connect_full(path string, mode_flags []OpenModeFlag, vfs_name string) !DB {
|
||||
db := &C.sqlite3(unsafe { nil })
|
||||
|
||||
mut flags := 0
|
||||
|
||||
for flag in mode_flags {
|
||||
flags = flags | int(flag)
|
||||
}
|
||||
|
||||
code := C.sqlite3_open_v2(&char(path.str), &db, flags, vfs_name.str)
|
||||
if code != 0 {
|
||||
return &SQLError{
|
||||
msg: unsafe { cstring_to_vstring(&char(C.sqlite3_errstr(code))) }
|
||||
code: code
|
||||
}
|
||||
}
|
||||
return DB{
|
||||
conn: db
|
||||
is_open: true
|
||||
}
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
[deprecated: 'import db.sqlite instead']
|
||||
[deprecated_after: '2023-02-01']
|
||||
module sqlite
|
||||
|
||||
import db.sqlite
|
||||
|
||||
const use_sqlite_ok = sqlite.sqlite_ok
|
@ -89,6 +89,7 @@ pub fn common_parse_uint2(s string, _base int, _bit_size int) (u64, int) {
|
||||
// Use compile-time constants for common cases.
|
||||
cutoff := strconv.max_u64 / u64(base) + u64(1)
|
||||
max_val := if bit_size == 64 { strconv.max_u64 } else { (u64(1) << u64(bit_size)) - u64(1) }
|
||||
basem1 := base - 1
|
||||
|
||||
mut n := u64(0)
|
||||
for i in start_index .. s.len {
|
||||
@ -96,7 +97,6 @@ pub fn common_parse_uint2(s string, _base int, _bit_size int) (u64, int) {
|
||||
|
||||
// manage underscore inside the number
|
||||
if c == `_` {
|
||||
// println("Here: ${s#[i..]}")
|
||||
if i == start_index || i >= (s.len - 1) {
|
||||
// println("_ limit")
|
||||
return u64(0), 1
|
||||
@ -109,21 +109,25 @@ pub fn common_parse_uint2(s string, _base int, _bit_size int) (u64, int) {
|
||||
continue
|
||||
}
|
||||
|
||||
mut sub_count := 0
|
||||
|
||||
// get the 0-9 digit
|
||||
c -= 48 // subtract the rune `0`
|
||||
|
||||
// check if we are in the superior base rune interval [A..Z]
|
||||
if c >= base {
|
||||
c -= 7
|
||||
}
|
||||
if c >= 17 { // (65 - 48)
|
||||
sub_count++
|
||||
c -= 7 // subtract the `A` - `0` rune to obtain the value of the digit
|
||||
|
||||
// check if we are in the superior base rune interval [a..z]
|
||||
if c >= base {
|
||||
c -= 32 // subtract the `A` - `0` rune to obtain the value of the digit
|
||||
// check if we are in the superior base rune interval [a..z]
|
||||
if c >= 42 { // (97 - 7 - 48)
|
||||
sub_count++
|
||||
c -= 32 // subtract the `a` - `0` rune to obtain the value of the digit
|
||||
}
|
||||
}
|
||||
|
||||
// check for digit over base
|
||||
if c >= base {
|
||||
if c > basem1 || (sub_count == 0 && c > 9) {
|
||||
return n, i + 1
|
||||
}
|
||||
|
||||
|
@ -27,6 +27,9 @@ fn test_atoi() {
|
||||
}
|
||||
|
||||
fn test_parse_int() {
|
||||
// symbols coverage
|
||||
assert strconv.parse_int('1234567890', 10, 32)! == 1234567890
|
||||
assert strconv.parse_int('19aAbBcCdDeEfF', 16, 64)! == 0x19aAbBcCdDeEfF
|
||||
// Different bases
|
||||
assert strconv.parse_int('16', 16, 0)! == 0x16
|
||||
assert strconv.parse_int('16', 8, 0)! == 0o16
|
||||
@ -83,6 +86,32 @@ fn test_common_parse_uint2() {
|
||||
assert error == 4
|
||||
}
|
||||
|
||||
fn test_common_parse_uint2_fail() {
|
||||
mut ascii_characters := [' ', '!', '"', '#', '\$', '%', '&', "'", '(', ')', '*', '+', ',',
|
||||
'-', '.', '/', ':', ';', '<', '=', '>', '?', '@', '[', '\\', ']', '^', '_', '`', '{', '|',
|
||||
'}', '~']
|
||||
mut special_characters := [':', ';', '<', '=', '>', '?', '@', 'X', 'Y', 'Z', '[', '\\', ']',
|
||||
'^', '_', '`']
|
||||
|
||||
num0, err0 := strconv.common_parse_uint2('1Ab', 16, 32)
|
||||
assert num0 == 427
|
||||
assert err0 == 0
|
||||
|
||||
for ch in ascii_characters {
|
||||
// println("ch: [${ch}]")
|
||||
txt_str := '${ch[0]:c}12Ab'
|
||||
num, err := strconv.common_parse_uint2(txt_str, 16, 32)
|
||||
assert err != 0
|
||||
}
|
||||
|
||||
for ch in special_characters {
|
||||
// println("ch: [${ch}]")
|
||||
txt_str := '${ch[0]:c}12Ab'
|
||||
num, err := strconv.common_parse_uint2(txt_str, 16, 32)
|
||||
assert err != 0
|
||||
}
|
||||
}
|
||||
|
||||
fn test_common_parse_uint2_compatibility() {
|
||||
test_list := [
|
||||
'1234,1234',
|
||||
|
@ -37,7 +37,7 @@ pub struct PoolProcessorConfig {
|
||||
// thread in the pool will run for each item.
|
||||
// The callback function will receive as parameters:
|
||||
// 1) the PoolProcessor instance, so it can call
|
||||
// p.get_item<int>(idx) to get the actual item at index idx
|
||||
// p.get_item[int](idx) to get the actual item at index idx
|
||||
// 2) idx - the index of the currently processed item
|
||||
// 3) task_id - the index of the worker thread in which the callback
|
||||
// function is running.
|
||||
|
@ -14,15 +14,6 @@ pub fn days_from_unix_epoch(year int, month int, day int) int {
|
||||
return era * 146097 + day_of_the_era - 719468
|
||||
}
|
||||
|
||||
// days_from_civil - return the number of days since the
|
||||
// Unix epoch 1970-01-01.
|
||||
// deprecated: use time.days_from_unix_epoch instead
|
||||
[deprecated: 'use time.days_from_unix_epoch instead']
|
||||
[deprecated_after: '2022-11-23']
|
||||
pub fn days_from_civil(year int, month int, day int) int {
|
||||
return days_from_unix_epoch(year, month, day)
|
||||
}
|
||||
|
||||
// days_from_unix_epoch - return the number of days since the Unix epoch 1970-01-01.
|
||||
// A detailed description of the algorithm here is in:
|
||||
// http://howardhinnant.github.io/date_algorithms.html
|
||||
|
@ -5,7 +5,7 @@ module time
|
||||
|
||||
import strconv
|
||||
|
||||
// parse_rfc3339 returns time from a date string in RFC 3339 datetime format.
|
||||
// parse_rfc3339 returns the time from a date string in RFC 3339 datetime format.
|
||||
// See also https://ijmacd.github.io/rfc3339-iso8601/ for a visual reference of
|
||||
// the differences between ISO-8601 and RFC 3339.
|
||||
pub fn parse_rfc3339(s string) !Time {
|
||||
@ -59,7 +59,7 @@ pub fn parse_rfc3339(s string) !Time {
|
||||
return error_invalid_time(9, 'malformed date')
|
||||
}
|
||||
|
||||
// parse returns time from a date string in "YYYY-MM-DD HH:mm:ss" format.
|
||||
// parse returns the time from a date string in "YYYY-MM-DD HH:mm:ss" format.
|
||||
pub fn parse(s string) !Time {
|
||||
if s == '' {
|
||||
return error_invalid_time(0, 'datetime string is empty')
|
||||
@ -197,7 +197,7 @@ pub fn parse_iso8601(s string) !Time {
|
||||
return t
|
||||
}
|
||||
|
||||
// parse_rfc2822 returns time from a date string in RFC 2822 datetime format.
|
||||
// parse_rfc2822 returns the time from a date string in RFC 2822 datetime format.
|
||||
pub fn parse_rfc2822(s string) !Time {
|
||||
if s == '' {
|
||||
return error_invalid_time(0, 'datetime string is empty')
|
||||
|
@ -1,6 +1,6 @@
|
||||
module time
|
||||
|
||||
// parse returns time from a date string.
|
||||
// parse returns the time from a date string.
|
||||
//
|
||||
// TODO(playX): JS Date expects iso8061 format of strings and other formats
|
||||
// are implementation dependent, we probably want to implement parsing in JS.
|
||||
|
@ -30,13 +30,13 @@ pub fn new_stopwatch(opts StopWatchOptions) StopWatch {
|
||||
}
|
||||
}
|
||||
|
||||
// start starts the stopwatch. If the timer was paused, restarts counting.
|
||||
// start starts the stopwatch. If the timer was paused, it continues counting.
|
||||
pub fn (mut t StopWatch) start() {
|
||||
t.start = sys_mono_now()
|
||||
t.end = 0
|
||||
}
|
||||
|
||||
// restart restarts the stopwatch. If the timer was paused, restarts counting.
|
||||
// restart restarts the stopwatch. If the timer was paused, it restarts counting.
|
||||
pub fn (mut t StopWatch) restart() {
|
||||
t.start = sys_mono_now()
|
||||
t.end = 0
|
||||
|
@ -20,7 +20,7 @@ fn C.gmtime(t &C.time_t) &C.tm
|
||||
fn C.gmtime_r(t &C.time_t, res &C.tm) &C.tm
|
||||
fn C.strftime(buf &char, maxsize usize, const_format &char, const_tm &C.tm) usize
|
||||
|
||||
// now returns current local time.
|
||||
// now returns the current local time.
|
||||
pub fn now() Time {
|
||||
$if macos {
|
||||
return darwin_now()
|
||||
@ -62,7 +62,7 @@ pub fn utc() Time {
|
||||
*/
|
||||
}
|
||||
|
||||
// new_time returns a time struct with calculated Unix time.
|
||||
// new_time returns a time struct with the calculated Unix time.
|
||||
pub fn new_time(t Time) Time {
|
||||
if t.unix != 0 {
|
||||
return t
|
||||
@ -82,7 +82,8 @@ pub fn new_time(t Time) Time {
|
||||
}
|
||||
}
|
||||
|
||||
// ticks returns a number of milliseconds elapsed since system start.
|
||||
// ticks returns the number of milliseconds since the UNIX epoch.
|
||||
// On Windows ticks returns the number of milliseconds elapsed since system start.
|
||||
pub fn ticks() i64 {
|
||||
$if windows {
|
||||
return C.GetTickCount()
|
||||
@ -96,7 +97,7 @@ pub fn ticks() i64 {
|
||||
// # return (double)(* (uint64_t *) &elapsedNano) / 1000000;
|
||||
}
|
||||
|
||||
// str returns time in the same format as `parse` expects ("YYYY-MM-DD HH:mm:ss").
|
||||
// str returns the time in the same format as `parse` expects ("YYYY-MM-DD HH:mm:ss").
|
||||
pub fn (t Time) str() string {
|
||||
// TODO Define common default format for
|
||||
// `str` and `parse` and use it in both ways
|
||||
|
@ -30,20 +30,21 @@ pub fn utc() Time {
|
||||
return res
|
||||
}
|
||||
|
||||
/// Returns local time
|
||||
// local returns the local time.
|
||||
pub fn (t Time) local() Time {
|
||||
// TODO: Does this actually correct? JS clock is always set to timezone or no?
|
||||
// if it is not we should try to use Intl for getting local time.
|
||||
return t
|
||||
}
|
||||
|
||||
// sleep suspends the execution for a given duration (in nanoseconds).
|
||||
pub fn sleep(dur Duration) {
|
||||
#let now = new Date().getTime()
|
||||
#let toWait = BigInt(dur.val) / BigInt(time__millisecond)
|
||||
#while (new Date().getTime() < now + Number(toWait)) {}
|
||||
}
|
||||
|
||||
// new_time returns a time struct with calculated Unix time.
|
||||
// new_time returns a time struct with the calculated Unix time.
|
||||
pub fn new_time(t Time) Time {
|
||||
if t.unix != 0 {
|
||||
return t
|
||||
|
@ -90,7 +90,7 @@ pub fn Time.new(t Time) Time {
|
||||
return new_time(t)
|
||||
}
|
||||
|
||||
// smonth returns month name abbreviation.
|
||||
// smonth returns the month name abbreviation.
|
||||
pub fn (t Time) smonth() string {
|
||||
if t.month <= 0 || t.month > 12 {
|
||||
return '---'
|
||||
@ -99,23 +99,26 @@ pub fn (t Time) smonth() string {
|
||||
return time.months_string[i * 3..(i + 1) * 3]
|
||||
}
|
||||
|
||||
// unix_time returns Unix time.
|
||||
// unix_time returns the UNIX time.
|
||||
[inline]
|
||||
pub fn (t Time) unix_time() i64 {
|
||||
return t.unix
|
||||
}
|
||||
|
||||
// unix_time_milli returns Unix time with millisecond resolution.
|
||||
// unix_time_milli returns the UNIX time with millisecond resolution.
|
||||
[inline]
|
||||
pub fn (t Time) unix_time_milli() i64 {
|
||||
return t.unix * 1000 + (t.microsecond / 1000)
|
||||
}
|
||||
|
||||
// add returns a new time that duration is added
|
||||
// add returns a new time with the given duration added.
|
||||
pub fn (t Time) add(d Duration) Time {
|
||||
microseconds := i64(t.unix) * 1_000_000 + t.microsecond + d.microseconds()
|
||||
unix := microseconds / 1_000_000
|
||||
micro := microseconds % 1_000_000
|
||||
if t.is_local {
|
||||
return unix2(unix, int(micro)).as_local()
|
||||
}
|
||||
return unix2(unix, int(micro))
|
||||
}
|
||||
|
||||
|
@ -11,6 +11,7 @@ module time
|
||||
#return t + $timeOff
|
||||
#}
|
||||
|
||||
// sys_mono_now returns a *monotonically increasing time*, NOT a time adjusted for daylight savings, location etc.
|
||||
pub fn sys_mono_now() u64 {
|
||||
$if js_browser {
|
||||
mut res := u64(0)
|
||||
|
@ -125,7 +125,7 @@ pub fn (d Duration) timespec() C.timespec {
|
||||
return ts
|
||||
}
|
||||
|
||||
// return timespec of 1970/1/1
|
||||
// zero_timespec returns the calendar time in seconds and nanoseconds of the beginning of the Unix epoch.
|
||||
pub fn zero_timespec() C.timespec {
|
||||
ts := C.timespec{
|
||||
tv_sec: 0
|
||||
@ -134,7 +134,7 @@ pub fn zero_timespec() C.timespec {
|
||||
return ts
|
||||
}
|
||||
|
||||
// sleep makes the calling thread sleep for a given duration (in nanoseconds).
|
||||
// sleep suspends the execution of the calling thread for a given duration (in nanoseconds).
|
||||
pub fn sleep(duration Duration) {
|
||||
mut req := C.timespec{duration / second, duration % second}
|
||||
rem := C.timespec{}
|
||||
|
@ -187,10 +187,15 @@ fn test_add() {
|
||||
assert t2.second == t1.second + d_seconds
|
||||
assert t2.microsecond == t1.microsecond + d_microseconds
|
||||
assert t2.unix == t1.unix + d_seconds
|
||||
assert t2.is_local == t1.is_local
|
||||
t3 := time_to_test.add(-duration)
|
||||
assert t3.second == t1.second - d_seconds
|
||||
assert t3.microsecond == t1.microsecond - d_microseconds
|
||||
assert t3.unix == t1.unix - d_seconds
|
||||
assert t3.is_local == t1.is_local
|
||||
t4 := time_to_test.as_local()
|
||||
t5 := t4.add(duration)
|
||||
assert t5.is_local == t4.is_local
|
||||
}
|
||||
|
||||
fn test_add_days() {
|
||||
|
@ -118,7 +118,7 @@ pub fn (t Time) local() Time {
|
||||
hour: st_local.hour
|
||||
minute: st_local.minute
|
||||
second: st_local.second // These are the same
|
||||
microsecond: st_local.millisecond * 1000
|
||||
microsecond: int(st_local.millisecond) * 1000
|
||||
unix: st_local.unix_time()
|
||||
}
|
||||
return t_local
|
||||
@ -141,7 +141,7 @@ fn win_now() Time {
|
||||
hour: st_local.hour
|
||||
minute: st_local.minute
|
||||
second: st_local.second
|
||||
microsecond: st_local.millisecond * 1000
|
||||
microsecond: int(st_local.millisecond) * 1000
|
||||
unix: st_local.unix_time()
|
||||
is_local: true
|
||||
}
|
||||
@ -163,7 +163,7 @@ fn win_utc() Time {
|
||||
hour: st_utc.hour
|
||||
minute: st_utc.minute
|
||||
second: st_utc.second
|
||||
microsecond: st_utc.millisecond * 1000
|
||||
microsecond: int(st_utc.millisecond) * 1000
|
||||
unix: st_utc.unix_time()
|
||||
is_local: false
|
||||
}
|
||||
|
@ -13,27 +13,6 @@ pub:
|
||||
file_path string // '/path/to/file.toml'
|
||||
}
|
||||
|
||||
// auto_config returns an, automatic determined, input Config based on heuristics
|
||||
// found in `toml`
|
||||
// One example of several of why it's deprecated:
|
||||
// https://discord.com/channels/592103645835821068/592114487759470596/954101934988615721
|
||||
[deprecated: 'will be removed and not replaced due to flaky heuristics that leads to hard to find bugs']
|
||||
[deprecated_after: '2022-06-18']
|
||||
pub fn auto_config(toml string) !Config {
|
||||
mut config := Config{}
|
||||
if !toml.contains('\n') && os.is_file(toml) {
|
||||
config = Config{
|
||||
file_path: toml
|
||||
}
|
||||
} else {
|
||||
config = Config{
|
||||
text: toml
|
||||
}
|
||||
}
|
||||
config.validate()!
|
||||
return config
|
||||
}
|
||||
|
||||
// validate returns an optional error if more than one of the fields
|
||||
// in `Config` has a non-default value (empty string).
|
||||
fn (c Config) validate() ! {
|
||||
|
@ -108,26 +108,6 @@ pub fn parse_text(text string) !Doc {
|
||||
}
|
||||
}
|
||||
|
||||
// parse parses the TOML document provided in `toml`.
|
||||
// parse automatically try to determine if the type of `toml` is a file or text.
|
||||
// For explicit parsing of input types see `parse_file` or `parse_text`.
|
||||
[deprecated: 'use parse_file or parse_text instead']
|
||||
[deprecated_after: '2022-06-18']
|
||||
pub fn parse(toml string) !Doc {
|
||||
mut input_config := input.auto_config(toml)!
|
||||
scanner_config := scanner.Config{
|
||||
input: input_config
|
||||
}
|
||||
parser_config := parser.Config{
|
||||
scanner: scanner.new_scanner(scanner_config)!
|
||||
}
|
||||
mut p := parser.new_parser(parser_config)
|
||||
ast_ := p.parse()!
|
||||
return Doc{
|
||||
ast: ast_
|
||||
}
|
||||
}
|
||||
|
||||
// parse_dotted_key converts `key` string to an array of strings.
|
||||
// parse_dotted_key preserves strings delimited by both `"` and `'`.
|
||||
pub fn parse_dotted_key(key string) ![]string {
|
||||
|
@ -458,20 +458,42 @@ fn (mut c Checker) assign_stmt(mut node ast.AssignStmt) {
|
||||
// no point to show the notice, if the old error was already shown:
|
||||
if !old_assign_error_condition {
|
||||
mut_str := if node.op == .decl_assign { 'mut ' } else { '' }
|
||||
c.note('use `${mut_str}array2 ${node.op.str()} array1.clone()` instead of `${mut_str}array2 ${node.op.str()} array1` (or use `unsafe`)',
|
||||
c.warn('use `${mut_str}array2 ${node.op.str()} array1.clone()` instead of `${mut_str}array2 ${node.op.str()} array1` (or use `unsafe`)',
|
||||
node.pos)
|
||||
}
|
||||
}
|
||||
if left_sym.kind == .array && right_sym.kind == .array && node.op == .assign {
|
||||
// `mut arr := [u8(1),2,3]`
|
||||
// `arr = [byte(4),5,6]`
|
||||
left_info := left_sym.info as ast.Array
|
||||
left_elem_type := c.table.unaliased_type(left_info.elem_type)
|
||||
if left_sym.kind == .array && right_sym.kind == .array {
|
||||
right_info := right_sym.info as ast.Array
|
||||
right_elem_type := c.table.unaliased_type(right_info.elem_type)
|
||||
if left_type_unwrapped.nr_muls() == right_type_unwrapped.nr_muls()
|
||||
&& left_info.nr_dims == right_info.nr_dims && left_elem_type == right_elem_type {
|
||||
continue
|
||||
if node.op in [.decl_assign, .assign] {
|
||||
// Do not allow `mut arr := [&immutable_object]`
|
||||
if mut left is ast.Ident && right_elem_type.is_ptr() {
|
||||
if left.is_mut() || (left.obj is ast.Var && left.obj.is_mut) {
|
||||
if mut right is ast.ArrayInit && right.exprs.len > 0 {
|
||||
elem_expr := right.exprs[0]
|
||||
if elem_expr is ast.PrefixExpr && elem_expr.op == .amp {
|
||||
r := elem_expr.right
|
||||
if r is ast.Ident {
|
||||
obj := r.obj
|
||||
if obj is ast.Var && !obj.is_mut {
|
||||
c.warn('cannot add a referenece to an immutable object to a mutable array',
|
||||
elem_expr.pos)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if node.op == .assign {
|
||||
// `mut arr := [u8(1),2,3]`
|
||||
// `arr = [byte(4),5,6]`
|
||||
left_info := left_sym.info as ast.Array
|
||||
left_elem_type := c.table.unaliased_type(left_info.elem_type)
|
||||
if left_type_unwrapped.nr_muls() == right_type_unwrapped.nr_muls()
|
||||
&& left_info.nr_dims == right_info.nr_dims && left_elem_type == right_elem_type {
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
if left_sym.kind == .array_fixed && !c.inside_unsafe && node.op in [.assign, .decl_assign]
|
||||
|
@ -350,7 +350,8 @@ fn (mut c Checker) check_basic(got ast.Type, expected ast.Type) bool {
|
||||
return true
|
||||
}
|
||||
// TODO: use sym so it can be absorbed into below [.voidptr, .any] logic
|
||||
if expected.idx() == ast.array_type_idx || got.idx() == ast.array_type_idx {
|
||||
if (expected.idx() == ast.array_type_idx && c.table.final_sym(got).kind == .array)
|
||||
|| (got.idx() == ast.array_type_idx && c.table.final_sym(expected).kind == .array) {
|
||||
return true
|
||||
}
|
||||
got_sym, exp_sym := c.table.sym(got), c.table.sym(expected)
|
||||
|
@ -1626,18 +1626,11 @@ fn (mut c Checker) const_decl(mut node ast.ConstDecl) {
|
||||
}
|
||||
node.fields[i].typ = ast.mktyp(typ)
|
||||
if mut field.expr is ast.IfExpr {
|
||||
if field.expr.branches.len == 2 {
|
||||
first_stmts := field.expr.branches[0].stmts
|
||||
second_stmts := field.expr.branches[1].stmts
|
||||
if first_stmts.len > 0 && first_stmts.last() is ast.ExprStmt
|
||||
&& first_stmts.last().typ != ast.void_type {
|
||||
for branch in field.expr.branches {
|
||||
if branch.stmts.len > 0 && branch.stmts.last() is ast.ExprStmt
|
||||
&& branch.stmts.last().typ != ast.void_type {
|
||||
field.expr.is_expr = true
|
||||
field.expr.typ = (first_stmts.last() as ast.ExprStmt).typ
|
||||
field.typ = field.expr.typ
|
||||
} else if second_stmts.len > 0 && second_stmts.last() is ast.ExprStmt
|
||||
&& second_stmts.last().typ != ast.void_type {
|
||||
field.expr.is_expr = true
|
||||
field.expr.typ = (second_stmts.last() as ast.ExprStmt).typ
|
||||
field.expr.typ = (branch.stmts.last() as ast.ExprStmt).typ
|
||||
field.typ = field.expr.typ
|
||||
}
|
||||
}
|
||||
@ -3210,7 +3203,7 @@ fn (mut c Checker) at_expr(mut node ast.AtExpr) ast.Type {
|
||||
node.val = c.table.cur_fn.mod
|
||||
}
|
||||
.struct_name {
|
||||
if c.table.cur_fn.is_method {
|
||||
if c.table.cur_fn.is_method || c.table.cur_fn.is_static_type_method {
|
||||
node.val = c.table.type_to_str(c.table.cur_fn.receiver.typ).all_after_last('.')
|
||||
} else {
|
||||
node.val = ''
|
||||
|
@ -507,8 +507,11 @@ fn (mut c Checker) call_expr(mut node ast.CallExpr) ast.Type {
|
||||
c.inside_fn_arg = old_inside_fn_arg
|
||||
// autofree: mark args that have to be freed (after saving them in tmp exprs)
|
||||
free_tmp_arg_vars := c.pref.autofree && !c.is_builtin_mod && node.args.len > 0
|
||||
&& !node.args[0].typ.has_flag(.option) && !node.args[0].typ.has_flag(.result)
|
||||
if free_tmp_arg_vars && !c.inside_const {
|
||||
&& !c.inside_const && !node.args[0].typ.has_flag(.option)
|
||||
&& !node.args[0].typ.has_flag(.result) && !(node.args[0].expr is ast.CallExpr
|
||||
&& (node.args[0].expr.return_type.has_flag(.option)
|
||||
|| node.args[0].expr.return_type.has_flag(.result)))
|
||||
if free_tmp_arg_vars {
|
||||
for i, arg in node.args {
|
||||
if arg.typ != ast.string_type {
|
||||
continue
|
||||
@ -893,16 +896,14 @@ fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool) ast.
|
||||
|
||||
if typ != 0 {
|
||||
generic_vts := c.table.final_sym(typ)
|
||||
if generic_vts.kind == .function {
|
||||
info := generic_vts.info as ast.FnType
|
||||
func = info.func
|
||||
if generic_vts.info is ast.FnType {
|
||||
func = generic_vts.info.func
|
||||
found = true
|
||||
found_in_args = true
|
||||
} else {
|
||||
vts := c.table.sym(c.unwrap_generic(typ))
|
||||
if vts.kind == .function {
|
||||
info := vts.info as ast.FnType
|
||||
func = info.func
|
||||
if vts.info is ast.FnType {
|
||||
func = vts.info.func
|
||||
found = true
|
||||
found_in_args = true
|
||||
}
|
||||
@ -914,8 +915,8 @@ fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool) ast.
|
||||
if obj := c.file.global_scope.find(fn_name) {
|
||||
if obj.typ != 0 {
|
||||
sym := c.table.sym(obj.typ)
|
||||
if sym.kind == .function {
|
||||
func = (sym.info as ast.FnType).func
|
||||
if sym.info is ast.FnType {
|
||||
func = sym.info.func
|
||||
found = true
|
||||
}
|
||||
}
|
||||
@ -931,13 +932,13 @@ fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool) ast.
|
||||
}
|
||||
if obj.typ != 0 {
|
||||
sym := c.table.sym(obj.typ)
|
||||
if sym.kind == .function {
|
||||
if sym.info is ast.FnType {
|
||||
// at this point, the const metadata should be already known,
|
||||
// and we are sure that it is just a function
|
||||
c.table.fns[qualified_const_name].usages++
|
||||
c.table.fns[func.name].usages++
|
||||
found = true
|
||||
func = (sym.info as ast.FnType).func
|
||||
func = sym.info.func
|
||||
node.is_fn_a_const = true
|
||||
node.fn_var_type = obj.typ
|
||||
node.const_name = qualified_const_name
|
||||
@ -1053,9 +1054,8 @@ fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool) ast.
|
||||
if func.is_variadic && i >= func.params.len - 1 {
|
||||
param_sym := c.table.sym(param.typ)
|
||||
mut expected_type := param.typ
|
||||
if param_sym.kind == .array {
|
||||
info := param_sym.array_info()
|
||||
expected_type = info.elem_type
|
||||
if param_sym.info is ast.Array {
|
||||
expected_type = param_sym.info.elem_type
|
||||
c.expected_type = expected_type
|
||||
}
|
||||
typ := c.expr(mut call_arg.expr)
|
||||
@ -1196,18 +1196,17 @@ fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool) ast.
|
||||
if param.typ.has_flag(.generic) {
|
||||
continue
|
||||
}
|
||||
if param_typ_sym.kind == .array && arg_typ_sym.kind == .array {
|
||||
param_info := param_typ_sym.info as ast.Array
|
||||
param_elem_type := c.table.unaliased_type(param_info.elem_type)
|
||||
arg_info := arg_typ_sym.info as ast.Array
|
||||
arg_elem_type := c.table.unaliased_type(arg_info.elem_type)
|
||||
if param_typ_sym.info is ast.Array && arg_typ_sym.info is ast.Array {
|
||||
param_elem_type := c.table.unaliased_type(param_typ_sym.info.elem_type)
|
||||
arg_elem_type := c.table.unaliased_type(arg_typ_sym.info.elem_type)
|
||||
param_nr_muls := param.typ.nr_muls()
|
||||
arg_nr_muls := if call_arg.is_mut {
|
||||
arg_typ.nr_muls() + 1
|
||||
} else {
|
||||
arg_typ.nr_muls()
|
||||
}
|
||||
if param_nr_muls == arg_nr_muls && param_info.nr_dims == arg_info.nr_dims
|
||||
if param_nr_muls == arg_nr_muls
|
||||
&& param_typ_sym.info.nr_dims == arg_typ_sym.info.nr_dims
|
||||
&& param_elem_type == arg_elem_type {
|
||||
continue
|
||||
}
|
||||
@ -1255,24 +1254,27 @@ fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool) ast.
|
||||
if param_type.is_any_kind_of_pointer() && arg_typ.is_any_kind_of_pointer() {
|
||||
continue
|
||||
}
|
||||
param_typ_sym_ := c.table.sym(c.table.unaliased_type(param_type))
|
||||
arg_typ_sym_ := c.table.sym(c.table.unaliased_type(arg_typ))
|
||||
unaliased_param_sym := c.table.sym(c.table.unaliased_type(param_type))
|
||||
unaliased_arg_sym := c.table.sym(c.table.unaliased_type(arg_typ))
|
||||
// Allow `[32]i8` as `&i8` etc
|
||||
if ((arg_typ_sym_.kind == .array_fixed || arg_typ_sym_.kind == .array)
|
||||
if ((unaliased_arg_sym.kind == .array_fixed || unaliased_arg_sym.kind == .array)
|
||||
&& (param_is_number
|
||||
|| c.table.unaliased_type(param_type).is_any_kind_of_pointer()))
|
||||
|| ((param_typ_sym_.kind == .array_fixed || param_typ_sym_.kind == .array)
|
||||
|| ((unaliased_param_sym.kind == .array_fixed
|
||||
|| unaliased_param_sym.kind == .array)
|
||||
&& (typ_is_number || c.table.unaliased_type(arg_typ).is_any_kind_of_pointer())) {
|
||||
continue
|
||||
}
|
||||
// Allow `[N]anyptr` as `[N]anyptr`
|
||||
if arg_typ_sym_.kind == .array && param_typ_sym_.kind == .array {
|
||||
if (arg_typ_sym_.info as ast.Array).elem_type.is_any_kind_of_pointer()
|
||||
&& (param_typ_sym_.info as ast.Array).elem_type.is_any_kind_of_pointer() {
|
||||
if unaliased_arg_sym.info is ast.Array && unaliased_param_sym.info is ast.Array {
|
||||
if unaliased_arg_sym.info.elem_type.is_any_kind_of_pointer()
|
||||
&& unaliased_param_sym.info.elem_type.is_any_kind_of_pointer() {
|
||||
continue
|
||||
}
|
||||
} else if arg_typ_sym_.kind == .array_fixed && param_typ_sym_.kind == .array_fixed {
|
||||
if (arg_typ_sym_.info as ast.ArrayFixed).elem_type.is_any_kind_of_pointer()&& (param_typ_sym_.info as ast.ArrayFixed).elem_type.is_any_kind_of_pointer() {
|
||||
} else if unaliased_arg_sym.info is ast.ArrayFixed
|
||||
&& unaliased_param_sym.info is ast.ArrayFixed {
|
||||
if unaliased_arg_sym.info.elem_type.is_any_kind_of_pointer()
|
||||
&& unaliased_param_sym.info.elem_type.is_any_kind_of_pointer() {
|
||||
continue
|
||||
}
|
||||
}
|
||||
@ -1417,9 +1419,9 @@ fn (mut c Checker) get_comptime_args(func ast.Fn, node_ ast.CallExpr, concrete_t
|
||||
mut ctyp := c.get_comptime_var_type(call_arg.expr)
|
||||
if ctyp != ast.void_type {
|
||||
arg_sym := c.table.sym(ctyp)
|
||||
if arg_sym.kind == .array && param_typ.has_flag(.generic)
|
||||
if arg_sym.info is ast.Array && param_typ.has_flag(.generic)
|
||||
&& c.table.final_sym(param_typ).kind == .array {
|
||||
ctyp = (arg_sym.info as ast.Array).elem_type
|
||||
ctyp = arg_sym.info.elem_type
|
||||
}
|
||||
comptime_args[i] = ctyp
|
||||
}
|
||||
@ -1432,9 +1434,9 @@ fn (mut c Checker) get_comptime_args(func ast.Fn, node_ ast.CallExpr, concrete_t
|
||||
if param_typ.has_flag(.variadic) {
|
||||
ctyp = ast.mktyp(ctyp)
|
||||
comptime_args[i] = ctyp
|
||||
} else if arg_sym.kind == .array && param_typ.has_flag(.generic)
|
||||
} else if arg_sym.info is ast.Array && param_typ.has_flag(.generic)
|
||||
&& param_typ_sym.kind == .array {
|
||||
ctyp = c.get_generic_array_element_type(arg_sym.info as ast.Array)
|
||||
ctyp = c.get_generic_array_element_type(arg_sym.info)
|
||||
comptime_args[i] = ctyp
|
||||
} else if arg_sym.kind in [.struct_, .interface_, .sum_type] {
|
||||
mut generic_types := []ast.Type{}
|
||||
@ -1458,9 +1460,9 @@ fn (mut c Checker) get_comptime_args(func ast.Fn, node_ ast.CallExpr, concrete_t
|
||||
}
|
||||
}
|
||||
} else if arg_sym.kind == .any {
|
||||
mut cparam_type_sym := c.table.sym(c.unwrap_generic(ctyp))
|
||||
if param_typ_sym.kind == .array && cparam_type_sym.kind == .array {
|
||||
ctyp = (cparam_type_sym.info as ast.Array).elem_type
|
||||
cparam_type_sym := c.table.sym(c.unwrap_generic(ctyp))
|
||||
if param_typ_sym.kind == .array && cparam_type_sym.info is ast.Array {
|
||||
ctyp = cparam_type_sym.info.elem_type
|
||||
comptime_args[i] = ctyp
|
||||
} else {
|
||||
if node_.args[i].expr.is_auto_deref_var() {
|
||||
@ -1528,20 +1530,18 @@ fn (mut c Checker) resolve_fn_generic_args(func ast.Fn, mut node ast.CallExpr) [
|
||||
|
||||
// cast_fixed_array_ret casts a ArrayFixed type created to return to a non returning one
|
||||
fn (mut c Checker) cast_fixed_array_ret(typ ast.Type, sym ast.TypeSymbol) ast.Type {
|
||||
if sym.kind == .array_fixed && (sym.info as ast.ArrayFixed).is_fn_ret {
|
||||
info := sym.info as ast.ArrayFixed
|
||||
return c.table.find_or_register_array_fixed(info.elem_type, info.size, info.size_expr,
|
||||
false)
|
||||
if sym.info is ast.ArrayFixed && sym.info.is_fn_ret {
|
||||
return c.table.find_or_register_array_fixed(sym.info.elem_type, sym.info.size,
|
||||
sym.info.size_expr, false)
|
||||
}
|
||||
return typ
|
||||
}
|
||||
|
||||
// cast_to_fixed_array_ret casts a ArrayFixed type created to do not return to a returning one
|
||||
fn (mut c Checker) cast_to_fixed_array_ret(typ ast.Type, sym ast.TypeSymbol) ast.Type {
|
||||
if sym.kind == .array_fixed && !(sym.info as ast.ArrayFixed).is_fn_ret {
|
||||
info := sym.info as ast.ArrayFixed
|
||||
return c.table.find_or_register_array_fixed(info.elem_type, info.size, info.size_expr,
|
||||
true)
|
||||
if sym.info is ast.ArrayFixed && !sym.info.is_fn_ret {
|
||||
return c.table.find_or_register_array_fixed(sym.info.elem_type, sym.info.size,
|
||||
sym.info.size_expr, true)
|
||||
}
|
||||
return typ
|
||||
}
|
||||
@ -1634,7 +1634,7 @@ fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type {
|
||||
if !c.check_types(arg_type, info.elem_type) && !c.check_types(left_type, arg_type) {
|
||||
c.error('cannot ${method_name} `${arg_sym.name}` to `${left_sym.name}`', arg_expr.pos())
|
||||
}
|
||||
} else if final_left_sym.info is ast.Array && method_name in ['first', 'last', 'pop'] {
|
||||
} else if final_left_sym.kind == .array && method_name in ['first', 'last', 'pop'] {
|
||||
return c.array_builtin_method_call(mut node, left_type, final_left_sym)
|
||||
} else if c.pref.backend.is_js() && left_sym.name.starts_with('Promise[')
|
||||
&& method_name == 'wait' {
|
||||
@ -1648,13 +1648,12 @@ fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type {
|
||||
node.return_type = info.concrete_types[0]
|
||||
node.return_type.set_flag(.option)
|
||||
return node.return_type
|
||||
} else if left_sym.kind == .thread && method_name == 'wait' {
|
||||
info := left_sym.info as ast.Thread
|
||||
} else if left_sym.info is ast.Thread && method_name == 'wait' {
|
||||
if node.args.len > 0 {
|
||||
c.error('wait() does not have any arguments', node.args[0].pos)
|
||||
}
|
||||
node.return_type = info.return_type
|
||||
return info.return_type
|
||||
node.return_type = left_sym.info.return_type
|
||||
return left_sym.info.return_type
|
||||
} else if left_sym.kind == .char && left_type.nr_muls() == 0 && method_name == 'str' {
|
||||
c.error('calling `.str()` on type `char` is not allowed, use its address or cast it to an integer instead',
|
||||
node.left.pos().extend(node.pos))
|
||||
|
@ -1,4 +1,4 @@
|
||||
vlib/v/checker/tests/array_assign_err.vv:8:8: notice: use `mut array2 := array1.clone()` instead of `mut array2 := array1` (or use `unsafe`)
|
||||
vlib/v/checker/tests/array_assign_err.vv:8:8: warning: use `mut array2 := array1.clone()` instead of `mut array2 := array1` (or use `unsafe`)
|
||||
6 | fn main() {
|
||||
7 | a := Dumb{[1, 2, 3]}
|
||||
8 | mut b := a.v // I expect a compiler error
|
||||
|
14
vlib/v/checker/tests/array_of_refs_mutability.out
Normal file
14
vlib/v/checker/tests/array_of_refs_mutability.out
Normal file
@ -0,0 +1,14 @@
|
||||
vlib/v/checker/tests/array_of_refs_mutability.vv:11:14: warning: cannot add a referenece to an immutable object to a mutable array
|
||||
9 | }
|
||||
10 |
|
||||
11 | mut arr := [&x]
|
||||
| ^
|
||||
12 | arr[0].bar = 30
|
||||
13 |
|
||||
vlib/v/checker/tests/array_of_refs_mutability.vv:15:10: warning: cannot add a referenece to an immutable object to a mutable array
|
||||
13 |
|
||||
14 | mut arr2 := [&Foo{}]
|
||||
15 | arr2 = [&x]
|
||||
| ^
|
||||
16 | arr2[0].bar = 40
|
||||
17 | }
|
17
vlib/v/checker/tests/array_of_refs_mutability.vv
Normal file
17
vlib/v/checker/tests/array_of_refs_mutability.vv
Normal file
@ -0,0 +1,17 @@
|
||||
struct Foo {
|
||||
mut:
|
||||
bar int
|
||||
}
|
||||
|
||||
fn main() {
|
||||
x := Foo{
|
||||
bar: 20
|
||||
}
|
||||
|
||||
mut arr := [&x]
|
||||
arr[0].bar = 30
|
||||
|
||||
mut arr2 := [&Foo{}]
|
||||
arr2 = [&x]
|
||||
arr2[0].bar = 40
|
||||
}
|
7
vlib/v/checker/tests/fn_call_arg_array_mismatch_err.out
Normal file
7
vlib/v/checker/tests/fn_call_arg_array_mismatch_err.out
Normal file
@ -0,0 +1,7 @@
|
||||
vlib/v/checker/tests/fn_call_arg_array_mismatch_err.vv:9:36: error: cannot use `string` as `array` in argument 2 to `os.write_file_array`
|
||||
7 |
|
||||
8 | fn main() {
|
||||
9 | os.write_file_array(service_path, service_file) or {
|
||||
| ~~~~~~~~~~~~
|
||||
10 | eprintln('Error: write file service')
|
||||
11 | exit(1)
|
13
vlib/v/checker/tests/fn_call_arg_array_mismatch_err.vv
Normal file
13
vlib/v/checker/tests/fn_call_arg_array_mismatch_err.vv
Normal file
@ -0,0 +1,13 @@
|
||||
import os
|
||||
|
||||
const (
|
||||
service_file = '[Unit]'
|
||||
service_path = 'dockerman.service'
|
||||
)
|
||||
|
||||
fn main() {
|
||||
os.write_file_array(service_path, service_file) or {
|
||||
eprintln('Error: write file service')
|
||||
exit(1)
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user