vlang

/

v Public
0 commits 39 issues 0 pull requests 0 contributors Discussions Projects CI

orm.Connection interface lacks exec() method, forcing raw SQL execution to bypass the interface #14

Describe the feature

orm.Connectiondefinesselect, insert, update, delete, create, drop, and last_id, but **not exec()**. Any code that needs to run raw SQL (DDL, DML, or arbitrary statements) must obtain the concrete driver type (e.g., mysql.DB, pg.Conn) and call its exec()` directly, rather than going through the interface.

Current workaround in application code:

// Must cast to concrete type just to call exec()
mut db := conn as mysql.DB
db.exec("INSERT INTO ...") or { ... }

Expected: the same pattern used by every Go SQL library — call exec() on the connection directly:

// Go database/sql, GORM, sqlx — all have exec() on the main type
db.Exec("INSERT INTO ...")

All four database drivers already have exec():

| Driver | Existing signature | File | |--------|------------------|------| | mysql | fn (db &DB) exec(query string) ![]Row | db/mysql/mysql.c.v | | postgres | fn (c &Conn) exec(query string) ![]db.pg.Row | db/pg/pg.c.v | | sqlite | fn (db &DB) exec(query string) ![]Row | db/sqlite/sqlite.c.v | | mssql | fn (h HStmt) exec(sql_ string) ! | db/mssql/stmt_handle.c.v |

Stronger evidence: pg/orm.v already uses c.exec() internally to implement the ORM's create() and drop() methods:

// db/pg/orm.v
pub fn (c &Conn) create(table orm.Table, fields []orm.TableField) ! {
    query := orm.orm_table_gen(...)
    for stmt in stmts {
        c.exec(stmt + ';')!    // ORM implementation already depends on exec()
    }
}

This shows exec() is a fundamental capability of every SQL driver, not a driver-specific extension.

Comparison with Go ecosystem:

| Library | Where is exec()? | |---------|-------------------| | Go database/sql | *sql.DB.Exec() — on the main type | | GORM | *gorm.DB.Exec() — on the main type | | sqlx | *sqlx.DB.Exec() — on the main type | | V ORM (current) | ❌ Not on orm.Connection — must use concrete driver type | | V ORM (proposed) | orm.Connection.exec() — add to the interfac

Use Case

Database initialization and migration scripts commonly execute raw DDL/DML that cannot be expressed through the ORM's sql { ... } DSL. For example, seeding reference data or running custom DDL:

// init_core_database.v — seeding initial data into core tables
sql_commands := [core_api, core_app, core_menu, core_role, ...]
for cmd in sql_commands {
    _ := db.exec(cmd) or { return error(...) }  // ← exec() needed here
}

The sql db { ... } DSL handles structured operations (create table, select, insert with struct mapping), but arbitrary SQL strings — for example INSERT INTO ... with hand-crafted values, or executing lists of pre-defined SQL statements — cannot go through the DSL. They require exec().

Without exec() on orm.Connection, the calling code must:

  1. Cast the connection to the concrete driver type (e.g., mysql.DB)
  2. Call a separate raw-acquisition method (acquire_raw())This couples application code to a specific database driver and adds noise that has no equivalent in other ecosystems.

Proposed Solution

  1. vlib/orm/orm.v — add one line to the interface:
    pub interface Connection {
    mut:
     select(config SelectConfig, data QueryData, where QueryData) ![][]Primitive
     insert(table Table, data QueryData) !
     update(table Table, data QueryData, where QueryData) !
     delete(table Table, where QueryData) !
     create(table Table, fields []TableField) !
     drop(table Table) !
     last_id() int
     exec(s string) !          // ← new
    }
    
    A ! (void result) return type is sufficient for DDL/DML — application code that needs query results can use select() which already exists on the interface.2. Driver ORM adapters — each driver's orm.v needs a thin wrapper to normalize the return type to !, since current return types vary (![]Row, ![]db.pg.Row, !).Compatibility:
  • Zero breaking changes — purely additive
  • All drivers already have exec() — no C code changes needed
  • Application code can call orm.Connection.exec() directly without casting to concrete types

Other Information

No response

Acknowledgements

  • [ ] I may be able to implement this feature request
  • [ ] This feature might incur a breaking change

Version used

V 0.5.1 3690f88

Environment details (OS name and version, etc.)

|V full version |V 0.5.1 e4a265a9914ea9ccb947436b277c31362141c90d.3690f88 |:-------------------|:------------------- |OS |linux, Deepin 25 |Processor |20 cpus, 64bit, little endian, 13th Gen Intel(R) Core(TM) i5-13500 |Memory |10.77GB/31.02GB | | |V executable |/home/Jengro/opt/v/v |V last modified time|2026-06-10 08:18:59 | | |V home dir |OK, value: /home/Jengro/opt/v |VMODULES |OK, value: /home/Jengro/.vmodules |VTMP |OK, value: /tmp/v_1000 |Current working dir |OK, value: /home/Jengro | | |Git version |git version 2.51.0 |V git status |0.5.1-1879-g3690f882 |.git/config present |true | | |cc version |cc (Deepin 12.3.0-17deepin17) 12.3.0 |gcc version |gcc (Deepin 12.3.0-17deepin17) 12.3.0 |clang version |Deepin clang version 17.0.6 (5deepin7) |tcc version |tcc version 0.9.28rc 2025-02-13 HEAD@f8bd136d (x86_64 Linux) |tcc git status |thirdparty-linux-amd64 696c1d84 |emcc version |N/A |glibc version |ldd (Debian GLIBC 2.38-6deepin24) 2.38

[!NOTE] You can use the 👍 reaction to increase the issue's priority for developers.

Please note that only the 👍 reaction to the issue itself counts as a vote. Other reactions and those to comments will not be taken into account.