Declared type.

This commit is contained in:
Nuno Cruces
2023-12-01 02:38:56 +00:00
parent 9c562f5d8b
commit c667a1f469
8 changed files with 126 additions and 75 deletions

View File

@@ -47,7 +47,7 @@ and uses [wazero](https://wazero.io/) to provide `cgo`-free SQLite bindings.
- [x] [virtual tables](https://sqlite.org/vtab.html)
- [x] [custom VFSes](https://sqlite.org/vfs.html)
- [x] [online backup](https://sqlite.org/backup.html)
- [x] [JSON support](https://www.sqlite.org/json1.html)
- [x] [JSON support](https://sqlite.org/json1.html)
- [x] [Unicode support](https://sqlite.org/src/dir/ext/icu)
### Caveats

View File

@@ -458,6 +458,16 @@ func (r *rows) Columns() []string {
return columns
}
func (r *rows) ColumnTypeDatabaseTypeName(index int) string {
decltype := r.Stmt.ColumnDeclType(index)
if len := len(decltype); len > 0 && decltype[len-1] == ')' {
if i := strings.LastIndexByte(decltype, '('); i >= 0 {
decltype = decltype[:i]
}
}
return strings.ToUpper(strings.TrimSpace(decltype))
}
func (r *rows) Next(dest []driver.Value) error {
old := r.Conn.SetInterrupt(r.ctx)
defer r.Conn.SetInterrupt(old)

View File

@@ -1,95 +1,97 @@
free
malloc
malloc_destructor
sqlite3_errcode
sqlite3_errstr
sqlite3_errmsg
sqlite3_error_offset
sqlite3_open_v2
sqlite3_close
sqlite3_close_v2
sqlite3_prepare_v3
sqlite3_finalize
sqlite3_reset
sqlite3_step
sqlite3_exec
sqlite3_interrupt
sqlite3_progress_handler_go
sqlite3_clear_bindings
sqlite3_aggregate_context
sqlite3_anycollseq_init
sqlite3_backup_finish
sqlite3_backup_init
sqlite3_backup_pagecount
sqlite3_backup_remaining
sqlite3_backup_step
sqlite3_bind_blob64
sqlite3_bind_double
sqlite3_bind_int64
sqlite3_bind_null
sqlite3_bind_parameter_count
sqlite3_bind_parameter_index
sqlite3_bind_parameter_name
sqlite3_bind_null
sqlite3_bind_int64
sqlite3_bind_double
sqlite3_bind_text64
sqlite3_bind_blob64
sqlite3_bind_zeroblob64
sqlite3_bind_pointer_go
sqlite3_bind_text64
sqlite3_bind_value
sqlite3_column_count
sqlite3_column_name
sqlite3_column_type
sqlite3_column_int64
sqlite3_column_double
sqlite3_column_text
sqlite3_bind_zeroblob64
sqlite3_blob_bytes
sqlite3_blob_close
sqlite3_blob_open
sqlite3_blob_read
sqlite3_blob_reopen
sqlite3_blob_write
sqlite3_changes64
sqlite3_clear_bindings
sqlite3_close
sqlite3_close_v2
sqlite3_column_blob
sqlite3_column_bytes
sqlite3_column_count
sqlite3_column_decltype
sqlite3_column_double
sqlite3_column_int64
sqlite3_column_name
sqlite3_column_text
sqlite3_column_type
sqlite3_column_value
sqlite3_blob_open
sqlite3_blob_close
sqlite3_blob_reopen
sqlite3_blob_bytes
sqlite3_blob_read
sqlite3_blob_write
sqlite3_backup_init
sqlite3_backup_step
sqlite3_backup_finish
sqlite3_backup_remaining
sqlite3_backup_pagecount
sqlite3_uri_parameter
sqlite3_uri_key
sqlite3_changes64
sqlite3_last_insert_rowid
sqlite3_get_autocommit
sqlite3_create_aggregate_function_go
sqlite3_create_collation_go
sqlite3_create_function_go
sqlite3_create_aggregate_function_go
sqlite3_create_window_function_go
sqlite3_create_module_go
sqlite3_overload_function
sqlite3_anycollseq_init
sqlite3_aggregate_context
sqlite3_user_data
sqlite3_set_auxdata_go
sqlite3_create_window_function_go
sqlite3_declare_vtab
sqlite3_errcode
sqlite3_errmsg
sqlite3_error_offset
sqlite3_errstr
sqlite3_exec
sqlite3_finalize
sqlite3_get_autocommit
sqlite3_get_auxdata
sqlite3_value_type
sqlite3_value_int64
sqlite3_value_double
sqlite3_value_text
sqlite3_value_blob
sqlite3_value_bytes
sqlite3_value_pointer_go
sqlite3_value_nochange
sqlite3_result_null
sqlite3_result_int64
sqlite3_result_double
sqlite3_result_text64
sqlite3_interrupt
sqlite3_last_insert_rowid
sqlite3_open_v2
sqlite3_overload_function
sqlite3_prepare_v3
sqlite3_progress_handler_go
sqlite3_reset
sqlite3_result_blob64
sqlite3_result_zeroblob64
sqlite3_result_pointer_go
sqlite3_result_value
sqlite3_result_double
sqlite3_result_error
sqlite3_result_error_code
sqlite3_result_error_nomem
sqlite3_result_error_toobig
sqlite3_declare_vtab
sqlite3_vtab_config_go
sqlite3_result_int64
sqlite3_result_null
sqlite3_result_pointer_go
sqlite3_result_text64
sqlite3_result_value
sqlite3_result_zeroblob64
sqlite3_set_auxdata_go
sqlite3_step
sqlite3_stmt_readonly
sqlite3_uri_key
sqlite3_uri_parameter
sqlite3_user_data
sqlite3_value_blob
sqlite3_value_bytes
sqlite3_value_double
sqlite3_value_int64
sqlite3_value_nochange
sqlite3_value_pointer_go
sqlite3_value_text
sqlite3_value_type
sqlite3_vtab_collation
sqlite3_vtab_config_go
sqlite3_vtab_distinct
sqlite3_vtab_in
sqlite3_vtab_in_first
sqlite3_vtab_in_next
sqlite3_vtab_rhs_value
sqlite3_vtab_nochange
sqlite3_vtab_on_conflict
sqlite3_vtab_on_conflict
sqlite3_vtab_rhs_value

Binary file not shown.

View File

@@ -56,7 +56,9 @@ func (t *table) declare() error {
if tail != "" {
return fmt.Errorf("statement: multiple statements")
}
// TODO: sqlite3_stmt_readonly
if !stmt.ReadOnly() {
return fmt.Errorf("statement: statement must be read only")
}
t.inputs = stmt.BindCount()
t.outputs = stmt.ColumnCount()
@@ -68,14 +70,17 @@ func (t *table) declare() error {
str.WriteString(sep)
name := stmt.ColumnName(i)
str.WriteString(sqlite3.QuoteIdentifier(name))
// TODO: sqlite3_column_decltype
if typ := stmt.ColumnDeclType(i); typ != "" {
str.WriteByte(' ')
str.WriteString(typ)
}
sep = ","
}
for i := 1; i <= t.inputs; i++ {
str.WriteString(sep)
name := stmt.BindName(i)
if name == "" {
str.WriteByte('\'')
str.WriteString("'")
str.WriteString(strconv.Itoa(i))
str.WriteString("' HIDDEN")
} else {

View File

@@ -37,11 +37,12 @@
#define SQLITE_DEFAULT_WAL_SYNCHRONOUS 1
#define SQLITE_LIKE_DOESNT_MATCH_BLOBS
#define SQLITE_MAX_EXPR_DEPTH 0
#define SQLITE_OMIT_DECLTYPE
#define SQLITE_USE_ALLOCA
#define SQLITE_OMIT_DEPRECATED
#define SQLITE_OMIT_SHARED_CACHE
#define SQLITE_OMIT_AUTOINIT
#define SQLITE_USE_ALLOCA
// #define SQLITE_OMIT_DECLTYPE
// #define SQLITE_OMIT_PROGRESS_CALLBACK
// Other Options

22
stmt.go
View File

@@ -90,6 +90,15 @@ func (s *Stmt) Exec() error {
return s.Reset()
}
// ReadOnly returns true if and only if the statement
// makes no direct changes to the content of the database file.
//
// https://sqlite.org/c3ref/stmt_readonly.html
func (s *Stmt) ReadOnly() bool {
r := s.c.call("sqlite3_stmt_readonly", uint64(s.handle))
return r != 0
}
// BindCount returns the number of SQL parameters in the prepared statement.
//
// https://sqlite.org/c3ref/bind_parameter_count.html
@@ -344,6 +353,19 @@ func (s *Stmt) ColumnType(col int) Datatype {
return Datatype(r)
}
// ColumnDeclType returns the declared datatype of the result column.
// The leftmost column of the result set has the index 0.
//
// https://sqlite.org/c3ref/column_decltype.html
func (s *Stmt) ColumnDeclType(col int) string {
r := s.c.call("sqlite3_column_decltype",
uint64(s.handle), uint64(col))
if r == 0 {
return ""
}
return util.ReadString(s.c.mod, uint32(r), _MAX_NAME)
}
// ColumnBool returns the value of the result column as a bool.
// The leftmost column of the result set has the index 0.
// SQLite does not have a separate boolean storage class.

View File

@@ -72,6 +72,17 @@ func TestDriver(t *testing.T) {
}
defer rows.Close()
typs, err := rows.ColumnTypes()
if err != nil {
t.Fatal(err)
}
if got := typs[0].DatabaseTypeName(); got != "INT" {
t.Errorf("got %s, want INT", got)
}
if got := typs[1].DatabaseTypeName(); got != "VARCHAR" {
t.Errorf("got %s, want INT", got)
}
row := 0
ids := []int{0, 1, 2}
names := []string{"go", "zig", "whatever"}