@@ -78,6 +78,16 @@ _sqlite3_bind_blob(sqlite3_stmt *stmt, int n, void *p, int np) {
7878 return sqlite3_bind_blob(stmt, n, p, np, SQLITE_TRANSIENT);
7979}
8080
81+ static int
82+ _sqlite3_prepare_v2_nolen(sqlite3 *db, const char *zSql, int nBytes, sqlite3_stmt **ppStmt, const char **pzTail)
83+ {
84+ #ifdef SQLITE_ENABLE_UNLOCK_NOTIFY
85+ return _sqlite3_prepare_v2_blocking(db, zSql, nBytes, ppStmt, pzTail);
86+ #else
87+ return sqlite3_prepare_v2(db, zSql, nBytes, ppStmt, pzTail);
88+ #endif
89+ }
90+
8191typedef struct {
8292 int typ;
8393 sqlite3_int64 i64;
@@ -126,6 +136,50 @@ _sqlite3_exec(sqlite3* db, const char* pcmd, long long* rowid, long long* change
126136 return rv;
127137}
128138
139+ // Combined prepare+step+finalize for simple exec without parameters.
140+ // Reduces CGO crossings from ~6 to 1 for the common no-args exec case.
141+ static int
142+ _sqlite3_exec_no_args(sqlite3* db, const char* zSql, int nBytes, long long* rowid, long long* changes, const char** pzTail)
143+ {
144+ sqlite3_stmt *stmt = 0;
145+ const char *tail = 0;
146+ #ifdef SQLITE_ENABLE_UNLOCK_NOTIFY
147+ int rv = _sqlite3_prepare_v2_blocking(db, zSql, nBytes, &stmt, &tail);
148+ #else
149+ int rv = sqlite3_prepare_v2(db, zSql, nBytes, &stmt, &tail);
150+ #endif
151+ if (rv != SQLITE_OK) {
152+ *pzTail = 0;
153+ return rv;
154+ }
155+ if (stmt == 0) {
156+ // Empty statement
157+ *rowid = 0;
158+ *changes = 0;
159+ *pzTail = tail;
160+ return SQLITE_OK;
161+ }
162+ #ifdef SQLITE_ENABLE_UNLOCK_NOTIFY
163+ rv = _sqlite3_step_row_blocking(stmt, rowid, changes);
164+ #else
165+ rv = sqlite3_step(stmt);
166+ *rowid = (long long) sqlite3_last_insert_rowid(db);
167+ *changes = (long long) sqlite3_changes(db);
168+ #endif
169+ sqlite3_finalize(stmt);
170+ *pzTail = tail;
171+ return rv;
172+ }
173+
174+ // Combined reset + clear_bindings in a single C call to reduce CGO crossings.
175+ static int
176+ _sqlite3_reset_clear(sqlite3_stmt* stmt)
177+ {
178+ int rv = sqlite3_reset(stmt);
179+ sqlite3_clear_bindings(stmt);
180+ return rv;
181+ }
182+
129183#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY
130184extern int _sqlite3_step_blocking(sqlite3_stmt *stmt);
131185extern int _sqlite3_step_row_blocking(sqlite3_stmt* stmt, long long* rowid, long long* changes);
@@ -886,17 +940,15 @@ func lastError(db *C.sqlite3) error {
886940
887941// Exec implements Execer.
888942func (c * SQLiteConn ) Exec (query string , args []driver.Value ) (driver.Result , error ) {
889- list := make ([]driver.NamedValue , len (args ))
890- for i , v := range args {
891- list [i ] = driver.NamedValue {
892- Ordinal : i + 1 ,
893- Value : v ,
894- }
895- }
896- return c .exec (context .Background (), query , list )
943+ return c .exec (context .Background (), query , valueToNamedValue (args ))
897944}
898945
899946func (c * SQLiteConn ) exec (ctx context.Context , query string , args []driver.NamedValue ) (driver.Result , error ) {
947+ // Fast path: no args, no context cancellation → single CGO call per statement
948+ if len (args ) == 0 && ctx .Done () == nil {
949+ return c .execNoArgs (query )
950+ }
951+
900952 start := 0
901953 for {
902954 s , err := c .prepare (ctx , query )
@@ -931,16 +983,34 @@ func (c *SQLiteConn) exec(ctx context.Context, query string, args []driver.Named
931983 }
932984}
933985
934- // Query implements Queryer.
935- func (c * SQLiteConn ) Query (query string , args []driver.Value ) (driver.Rows , error ) {
936- list := make ([]driver.NamedValue , len (args ))
937- for i , v := range args {
938- list [i ] = driver.NamedValue {
939- Ordinal : i + 1 ,
940- Value : v ,
986+ // execNoArgs executes a query with no parameters in a single CGO call per statement.
987+ func (c * SQLiteConn ) execNoArgs (query string ) (driver.Result , error ) {
988+ var res * SQLiteResult
989+ for len (query ) > 0 {
990+ var rowid , changes C.longlong
991+ var tail * C.char
992+ pquery := C .CString (query )
993+ rv := C ._sqlite3_exec_no_args (c .db , pquery , C .int (len (query )), & rowid , & changes , & tail )
994+ if tail != nil && * tail != '\000' {
995+ query = strings .TrimSpace (C .GoString (tail ))
996+ } else {
997+ query = ""
941998 }
999+ C .free (unsafe .Pointer (pquery ))
1000+ if rv != C .SQLITE_ROW && rv != C .SQLITE_OK && rv != C .SQLITE_DONE {
1001+ return nil , c .lastError ()
1002+ }
1003+ res = & SQLiteResult {id : int64 (rowid ), changes : int64 (changes )}
1004+ }
1005+ if res == nil {
1006+ res = & SQLiteResult {0 , 0 }
9421007 }
943- return c .query (context .Background (), query , list )
1008+ return res , nil
1009+ }
1010+
1011+ // Query implements Queryer.
1012+ func (c * SQLiteConn ) Query (query string , args []driver.Value ) (driver.Rows , error ) {
1013+ return c .query (context .Background (), query , valueToNamedValue (args ))
9441014}
9451015
9461016func (c * SQLiteConn ) query (ctx context.Context , query string , args []driver.NamedValue ) (driver.Rows , error ) {
@@ -1830,7 +1900,7 @@ func (c *SQLiteConn) prepare(ctx context.Context, query string) (driver.Stmt, er
18301900 defer C .free (unsafe .Pointer (pquery ))
18311901 var s * C.sqlite3_stmt
18321902 var tail * C.char
1833- rv := C ._sqlite3_prepare_v2_internal (c .db , pquery , C .int (- 1 ), & s , & tail )
1903+ rv := C ._sqlite3_prepare_v2_internal (c .db , pquery , C .int (len ( query ) ), & s , & tail )
18341904 if rv != C .SQLITE_OK {
18351905 return nil , c .lastError ()
18361906 }
@@ -2002,7 +2072,12 @@ func bindValue(s *C.sqlite3_stmt, n C.int, value driver.Value) C.int {
20022072 }
20032073 return C ._sqlite3_bind_blob (s , n , unsafe .Pointer (& v [0 ]), C .int (ln ))
20042074 case time.Time :
2005- return bindText (s , n , v .Format (SQLiteTimestampFormats [0 ]))
2075+ var buf [64 ]byte
2076+ b := v .AppendFormat (buf [:0 ], SQLiteTimestampFormats [0 ])
2077+ if len (b ) == 0 {
2078+ return C ._sqlite3_bind_text (s , n , (* C .char )(unsafe .Pointer (& placeHolder [0 ])), C .int (0 ))
2079+ }
2080+ return C ._sqlite3_bind_text (s , n , (* C .char )(unsafe .Pointer (& b [0 ])), C .int (len (b )))
20062081 default :
20072082 return C .SQLITE_MISUSE
20082083 }
@@ -2015,12 +2090,17 @@ func (s *SQLiteStmt) bindNamedIndices(name string) [3]int {
20152090 return indices
20162091 }
20172092
2018- prefixes := [3 ]string {":" , "@" , "$" }
2093+ // Build ":name\0" once and rewrite prefix byte to avoid 3 C.CString allocs.
2094+ buf := make ([]byte , 1 + len (name )+ 1 ) // prefix + name + null terminator
2095+ copy (buf [1 :], name )
2096+ buf [len (buf )- 1 ] = 0
2097+ cname := (* C .char )(unsafe .Pointer (& buf [0 ]))
2098+
20192099 var indices [3 ]int
2020- for i := range prefixes {
2021- cname := C .CString (prefixes [i ] + name )
2100+ prefixes := [3 ]byte {':' , '@' , '$' }
2101+ for i , p := range prefixes {
2102+ buf [0 ] = p
20222103 indices [i ] = int (C .sqlite3_bind_parameter_index (s .s , cname ))
2023- C .free (unsafe .Pointer (cname ))
20242104 }
20252105 s .namedParams [name ] = indices
20262106 return indices
@@ -2057,13 +2137,11 @@ func stmtArgs(args []driver.NamedValue, start, na int) []driver.NamedValue {
20572137}
20582138
20592139func (s * SQLiteStmt ) bind (args []driver.NamedValue ) error {
2060- rv := C .sqlite3_reset (s .s )
2140+ rv := C ._sqlite3_reset_clear (s .s )
20612141 if rv != C .SQLITE_ROW && rv != C .SQLITE_OK && rv != C .SQLITE_DONE {
20622142 return s .c .lastError ()
20632143 }
20642144
2065- C .sqlite3_clear_bindings (s .s )
2066-
20672145 hasNamed := false
20682146 for i := range args {
20692147 if args [i ].Name != "" {
@@ -2083,23 +2161,20 @@ func (s *SQLiteStmt) bind(args []driver.NamedValue) error {
20832161 return nil
20842162 }
20852163
2086- bindIndices := make ([][4 ]int , len (args ))
2087- for i , v := range args {
2088- if v .Name == "" {
2089- bindIndices [i ][0 ] = v .Ordinal
2164+ for _ , arg := range args {
2165+ if arg .Name == "" {
2166+ rv = bindValue (s .s , C .int (arg .Ordinal ), arg .Value )
2167+ if rv != C .SQLITE_OK {
2168+ return s .c .lastError ()
2169+ }
20902170 continue
20912171 }
2092- indices := s .bindNamedIndices (v .Name )
2093- copy (bindIndices [i ][1 :], indices [:])
2094- }
2095-
2096- for i , arg := range args {
2097- for _ , idx := range bindIndices [i ] {
2172+ indices := s .bindNamedIndices (arg .Name )
2173+ for _ , idx := range indices {
20982174 if idx == 0 {
20992175 continue
21002176 }
2101- n := C .int (idx )
2102- rv = bindValue (s .s , n , arg .Value )
2177+ rv = bindValue (s .s , C .int (idx ), arg .Value )
21032178 if rv != C .SQLITE_OK {
21042179 return s .c .lastError ()
21052180 }
@@ -2110,14 +2185,7 @@ func (s *SQLiteStmt) bind(args []driver.NamedValue) error {
21102185
21112186// Query the statement with arguments. Return records.
21122187func (s * SQLiteStmt ) Query (args []driver.Value ) (driver.Rows , error ) {
2113- list := make ([]driver.NamedValue , len (args ))
2114- for i , v := range args {
2115- list [i ] = driver.NamedValue {
2116- Ordinal : i + 1 ,
2117- Value : v ,
2118- }
2119- }
2120- return s .query (context .Background (), list )
2188+ return s .query (context .Background (), valueToNamedValue (args ))
21212189}
21222190
21232191func (s * SQLiteStmt ) query (ctx context.Context , args []driver.NamedValue ) (driver.Rows , error ) {
@@ -2156,14 +2224,18 @@ func (r *SQLiteResult) RowsAffected() (int64, error) {
21562224
21572225// Exec execute the statement with arguments. Return result object.
21582226func (s * SQLiteStmt ) Exec (args []driver.Value ) (driver.Result , error ) {
2227+ return s .exec (context .Background (), valueToNamedValue (args ))
2228+ }
2229+
2230+ func valueToNamedValue (args []driver.Value ) []driver.NamedValue {
21592231 list := make ([]driver.NamedValue , len (args ))
21602232 for i , v := range args {
21612233 list [i ] = driver.NamedValue {
21622234 Ordinal : i + 1 ,
21632235 Value : v ,
21642236 }
21652237 }
2166- return s . exec ( context . Background (), list )
2238+ return list
21672239}
21682240
21692241func isInterruptErr (err error ) bool {
@@ -2180,47 +2252,43 @@ func (s *SQLiteStmt) exec(ctx context.Context, args []driver.NamedValue) (driver
21802252 return s .execSync (args )
21812253 }
21822254
2183- type result struct {
2184- r driver.Result
2185- err error
2186- }
2187- resultCh := make (chan result )
2188- defer close (resultCh )
2255+ sema := make (chan struct {})
2256+ var r driver.Result
2257+ var err error
21892258 go func () {
2190- r , err : = s .execSync (args )
2191- resultCh <- result { r , err }
2259+ r , err = s .execSync (args )
2260+ close ( sema )
21922261 }()
2193- var rv result
21942262 select {
2195- case rv = <- resultCh :
2263+ case <- sema :
2264+ return r , err
21962265 case <- ctx .Done ():
21972266 select {
2198- case rv = <- resultCh : // no need to interrupt, operation completed in db
2267+ case <- sema : // no need to interrupt, operation completed in db
2268+ return r , err
21992269 default :
22002270 // this is still racy and can be no-op if executed between sqlite3_* calls in execSync.
22012271 C .sqlite3_interrupt (s .c .db )
2202- rv = <- resultCh // wait for goroutine completed
2203- if isInterruptErr (rv . err ) {
2272+ <- sema // wait for goroutine completed
2273+ if isInterruptErr (err ) {
22042274 return nil , ctx .Err ()
22052275 }
2276+ return r , err
22062277 }
22072278 }
2208- return rv .r , rv .err
22092279}
22102280
22112281func (s * SQLiteStmt ) execSync (args []driver.NamedValue ) (driver.Result , error ) {
22122282 if err := s .bind (args ); err != nil {
2213- C .sqlite3_reset (s .s )
2214- C .sqlite3_clear_bindings (s .s )
2283+ C ._sqlite3_reset_clear (s .s )
22152284 return nil , err
22162285 }
22172286
22182287 var rowid , changes C.longlong
22192288 rv := C ._sqlite3_step_row_internal (s .s , & rowid , & changes )
22202289 if rv != C .SQLITE_ROW && rv != C .SQLITE_OK && rv != C .SQLITE_DONE {
22212290 err := s .c .lastError ()
2222- C .sqlite3_reset (s .s )
2223- C .sqlite3_clear_bindings (s .s )
2291+ C ._sqlite3_reset_clear (s .s )
22242292 return nil , err
22252293 }
22262294
@@ -2311,21 +2379,22 @@ func (rc *SQLiteRows) Next(dest []driver.Value) error {
23112379 if rc .ctx .Done () == nil {
23122380 return rc .nextSyncLocked (dest )
23132381 }
2314- resultCh := make (chan error )
2315- defer close ( resultCh )
2382+ sema := make (chan struct {} )
2383+ var err error
23162384 go func () {
2317- resultCh <- rc .nextSyncLocked (dest )
2385+ err = rc .nextSyncLocked (dest )
2386+ close (sema )
23182387 }()
23192388 select {
2320- case err := <- resultCh :
2389+ case <- sema :
23212390 return err
23222391 case <- rc .ctx .Done ():
23232392 select {
2324- case <- resultCh : // no need to interrupt
2393+ case <- sema : // no need to interrupt
23252394 default :
23262395 // this is still racy and can be no-op if executed between sqlite3_* calls in nextSyncLocked.
23272396 C .sqlite3_interrupt (rc .s .c .db )
2328- <- resultCh // ensure goroutine completed
2397+ <- sema // ensure goroutine completed
23292398 }
23302399 return rc .ctx .Err ()
23312400 }
0 commit comments