mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-10-28 17:42:25 -05:00 
			
		
		
		
	[experiment] add alternative wasm sqlite3 implementation available via build-tag (#2863)
This allows for building GoToSocial with [SQLite transpiled to WASM](https://github.com/ncruces/go-sqlite3) and accessed through [Wazero](https://wazero.io/).
This commit is contained in:
		
					parent
					
						
							
								cce21c11cb
							
						
					
				
			
			
				commit
				
					
						1e7b32490d
					
				
			
		
					 398 changed files with 86174 additions and 684 deletions
				
			
		
							
								
								
									
										5
									
								
								go.mod
									
										
									
									
									
								
							
							
						
						
									
										5
									
								
								go.mod
									
										
									
									
									
								
							|  | @ -44,6 +44,7 @@ require ( | |||
| 	github.com/miekg/dns v1.1.59 | ||||
| 	github.com/minio/minio-go/v7 v7.0.70 | ||||
| 	github.com/mitchellh/mapstructure v1.5.0 | ||||
| 	github.com/ncruces/go-sqlite3 v0.16.0 | ||||
| 	github.com/oklog/ulid v1.3.1 | ||||
| 	github.com/prometheus/client_golang v1.19.1 | ||||
| 	github.com/spf13/cobra v1.8.0 | ||||
|  | @ -78,7 +79,7 @@ require ( | |||
| 	golang.org/x/text v0.15.0 | ||||
| 	gopkg.in/mcuadros/go-syslog.v2 v2.3.0 | ||||
| 	gopkg.in/yaml.v3 v3.0.1 | ||||
| 	modernc.org/sqlite v1.29.8 | ||||
| 	modernc.org/sqlite v0.0.0-00010101000000-000000000000 | ||||
| 	mvdan.cc/xurls/v2 v2.5.0 | ||||
| ) | ||||
| 
 | ||||
|  | @ -173,6 +174,7 @@ require ( | |||
| 	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect | ||||
| 	github.com/modern-go/reflect2 v1.0.2 // indirect | ||||
| 	github.com/ncruces/go-strftime v0.1.9 // indirect | ||||
| 	github.com/ncruces/julianday v1.0.0 // indirect | ||||
| 	github.com/opencontainers/runtime-spec v1.0.2 // indirect | ||||
| 	github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect | ||||
| 	github.com/pelletier/go-toml/v2 v2.2.2 // indirect | ||||
|  | @ -197,6 +199,7 @@ require ( | |||
| 	github.com/superseriousbusiness/go-jpeg-image-structure/v2 v2.0.0-20220321154430-d89a106fdabe // indirect | ||||
| 	github.com/superseriousbusiness/go-png-image-structure/v2 v2.0.1-SSB // indirect | ||||
| 	github.com/tdewolff/parse/v2 v2.7.14 // indirect | ||||
| 	github.com/tetratelabs/wazero v1.7.2 // indirect | ||||
| 	github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect | ||||
| 	github.com/toqueteos/webbrowser v1.2.0 // indirect | ||||
| 	github.com/twitchyliquid64/golang-asm v0.15.1 // indirect | ||||
|  |  | |||
							
								
								
									
										6
									
								
								go.sum
									
										
									
									
									
								
							
							
						
						
									
										6
									
								
								go.sum
									
										
									
									
									
								
							|  | @ -445,8 +445,12 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G | |||
| github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= | ||||
| github.com/moul/http2curl v1.0.0 h1:dRMWoAtb+ePxMlLkrCbAqh4TlPHXvoGUSQ323/9Zahs= | ||||
| github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ= | ||||
| github.com/ncruces/go-sqlite3 v0.16.0 h1:O7eULuEjvSBnS1QCN+dDL/ixLQZoUGWr466A02Gx1xc= | ||||
| github.com/ncruces/go-sqlite3 v0.16.0/go.mod h1:2TmAeD93ImsKXJRsUIKohfMvt17dZSbS6pzJ3k6YYFg= | ||||
| github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4= | ||||
| github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= | ||||
| github.com/ncruces/julianday v1.0.0 h1:fH0OKwa7NWvniGQtxdJRxAgkBMolni2BjDHaWTxqt7M= | ||||
| github.com/ncruces/julianday v1.0.0/go.mod h1:Dusn2KvZrrovOMJuOt0TNXL6tB7U2E8kvza5fFc9G7g= | ||||
| github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= | ||||
| github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= | ||||
| github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= | ||||
|  | @ -558,6 +562,8 @@ github.com/tdewolff/test v1.0.11-0.20240106005702-7de5f7df4739 h1:IkjBCtQOOjIn03 | |||
| github.com/tdewolff/test v1.0.11-0.20240106005702-7de5f7df4739/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8= | ||||
| github.com/technologize/otel-go-contrib v1.1.1 h1:wZH9aSPNWZWIkEh3vfaKfMb15AJ80jJ1aVj/4GZdqIw= | ||||
| github.com/technologize/otel-go-contrib v1.1.1/go.mod h1:dCN/wj2WyUO8aFZFdIN+6tfJHImjTML/8r2YVYAy3So= | ||||
| github.com/tetratelabs/wazero v1.7.2 h1:1+z5nXJNwMLPAWaTePFi49SSTL0IMx/i3Fg8Yc25GDc= | ||||
| github.com/tetratelabs/wazero v1.7.2/go.mod h1:ytl6Zuh20R/eROuyDaGPkp82O9C/DJfXAwJfQ3X6/7Y= | ||||
| github.com/tidwall/btree v0.0.0-20191029221954-400434d76274 h1:G6Z6HvJuPjG6XfNGi/feOATzeJrfgTNJY+rGrHbA04E= | ||||
| github.com/tidwall/btree v0.0.0-20191029221954-400434d76274/go.mod h1:huei1BkDWJ3/sLXmO+bsCNELL+Bp2Kks9OLyQFkzvA8= | ||||
| github.com/tidwall/buntdb v1.1.2 h1:noCrqQXL9EKMtcdwJcmuVKSEjqu1ua99RHHgbLTEHRo= | ||||
|  |  | |||
|  | @ -38,7 +38,6 @@ import ( | |||
| 	"github.com/superseriousbusiness/gotosocial/internal/federation" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/filter/visibility" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/log" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/media" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/oauth" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/processing" | ||||
|  | @ -77,19 +76,22 @@ type MediaCreateTestSuite struct { | |||
| 	TEST INFRASTRUCTURE | ||||
| */ | ||||
| 
 | ||||
| func (suite *MediaCreateTestSuite) SetupSuite() { | ||||
| 	suite.state.Caches.Init() | ||||
| func (suite *MediaCreateTestSuite) SetupTest() { | ||||
| 	testrig.StartNoopWorkers(&suite.state) | ||||
| 
 | ||||
| 	// setup standard items | ||||
| 	testrig.InitTestConfig() | ||||
| 	testrig.InitTestLog() | ||||
| 
 | ||||
| 	suite.db = testrig.NewTestDB(&suite.state) | ||||
| 	suite.state.DB = suite.db | ||||
| 	suite.state.Caches.Init() | ||||
| 
 | ||||
| 	suite.storage = testrig.NewInMemoryStorage() | ||||
| 	suite.state.Storage = suite.storage | ||||
| 
 | ||||
| 	suite.db = testrig.NewTestDB(&suite.state) | ||||
| 	testrig.StandardDBSetup(suite.db, nil) | ||||
| 	testrig.StandardStorageSetup(suite.storage, "../../../../testrig/media") | ||||
| 
 | ||||
| 	suite.tc = typeutils.NewConverter(&suite.state) | ||||
| 
 | ||||
| 	testrig.StartTimelines( | ||||
|  | @ -106,21 +108,8 @@ func (suite *MediaCreateTestSuite) SetupSuite() { | |||
| 
 | ||||
| 	// setup module being tested | ||||
| 	suite.mediaModule = mediamodule.New(suite.processor) | ||||
| } | ||||
| 
 | ||||
| func (suite *MediaCreateTestSuite) TearDownSuite() { | ||||
| 	if err := suite.db.Close(); err != nil { | ||||
| 		log.Panicf(nil, "error closing db connection: %s", err) | ||||
| 	} | ||||
| 	testrig.StopWorkers(&suite.state) | ||||
| } | ||||
| 
 | ||||
| func (suite *MediaCreateTestSuite) SetupTest() { | ||||
| 	suite.state.Caches.Init() | ||||
| 
 | ||||
| 	testrig.StandardDBSetup(suite.db, nil) | ||||
| 	testrig.StandardStorageSetup(suite.storage, "../../../../testrig/media") | ||||
| 
 | ||||
| 	// setup test data | ||||
| 	suite.testTokens = testrig.NewTestTokens() | ||||
| 	suite.testClients = testrig.NewTestClients() | ||||
| 	suite.testApplications = testrig.NewTestApplications() | ||||
|  | @ -132,6 +121,7 @@ func (suite *MediaCreateTestSuite) SetupTest() { | |||
| func (suite *MediaCreateTestSuite) TearDownTest() { | ||||
| 	testrig.StandardDBTeardown(suite.db) | ||||
| 	testrig.StandardStorageTeardown(suite.storage) | ||||
| 	testrig.StopWorkers(&suite.state) | ||||
| } | ||||
| 
 | ||||
| /* | ||||
|  |  | |||
|  | @ -36,7 +36,6 @@ import ( | |||
| 	"github.com/superseriousbusiness/gotosocial/internal/federation" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/filter/visibility" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/log" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/media" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/oauth" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/processing" | ||||
|  | @ -75,18 +74,22 @@ type MediaUpdateTestSuite struct { | |||
| 	TEST INFRASTRUCTURE | ||||
| */ | ||||
| 
 | ||||
| func (suite *MediaUpdateTestSuite) SetupSuite() { | ||||
| func (suite *MediaUpdateTestSuite) SetupTest() { | ||||
| 	testrig.StartNoopWorkers(&suite.state) | ||||
| 
 | ||||
| 	// setup standard items | ||||
| 	testrig.InitTestConfig() | ||||
| 	testrig.InitTestLog() | ||||
| 
 | ||||
| 	suite.db = testrig.NewTestDB(&suite.state) | ||||
| 	suite.state.DB = suite.db | ||||
| 	suite.state.Caches.Init() | ||||
| 
 | ||||
| 	suite.storage = testrig.NewInMemoryStorage() | ||||
| 	suite.state.Storage = suite.storage | ||||
| 
 | ||||
| 	suite.db = testrig.NewTestDB(&suite.state) | ||||
| 	testrig.StandardDBSetup(suite.db, nil) | ||||
| 	testrig.StandardStorageSetup(suite.storage, "../../../../testrig/media") | ||||
| 
 | ||||
| 	suite.tc = typeutils.NewConverter(&suite.state) | ||||
| 
 | ||||
| 	testrig.StartTimelines( | ||||
|  | @ -103,21 +106,8 @@ func (suite *MediaUpdateTestSuite) SetupSuite() { | |||
| 
 | ||||
| 	// setup module being tested | ||||
| 	suite.mediaModule = mediamodule.New(suite.processor) | ||||
| } | ||||
| 
 | ||||
| func (suite *MediaUpdateTestSuite) TearDownSuite() { | ||||
| 	if err := suite.db.Close(); err != nil { | ||||
| 		log.Panicf(nil, "error closing db connection: %s", err) | ||||
| 	} | ||||
| 	testrig.StopWorkers(&suite.state) | ||||
| } | ||||
| 
 | ||||
| func (suite *MediaUpdateTestSuite) SetupTest() { | ||||
| 	suite.state.Caches.Init() | ||||
| 
 | ||||
| 	testrig.StandardDBSetup(suite.db, nil) | ||||
| 	testrig.StandardStorageSetup(suite.storage, "../../../../testrig/media") | ||||
| 
 | ||||
| 	// setup test data | ||||
| 	suite.testTokens = testrig.NewTestTokens() | ||||
| 	suite.testClients = testrig.NewTestClients() | ||||
| 	suite.testApplications = testrig.NewTestApplications() | ||||
|  | @ -129,6 +119,7 @@ func (suite *MediaUpdateTestSuite) SetupTest() { | |||
| func (suite *MediaUpdateTestSuite) TearDownTest() { | ||||
| 	testrig.StandardDBTeardown(suite.db) | ||||
| 	testrig.StandardStorageTeardown(suite.storage) | ||||
| 	testrig.StopWorkers(&suite.state) | ||||
| } | ||||
| 
 | ||||
| /* | ||||
|  |  | |||
|  | @ -70,8 +70,6 @@ func (suite *FileserverTestSuite) SetupSuite() { | |||
| 	testrig.InitTestConfig() | ||||
| 	testrig.InitTestLog() | ||||
| 
 | ||||
| 	suite.db = testrig.NewTestDB(&suite.state) | ||||
| 	suite.state.DB = suite.db | ||||
| 	suite.storage = testrig.NewInMemoryStorage() | ||||
| 	suite.state.Storage = suite.storage | ||||
| 
 | ||||
|  | @ -98,8 +96,12 @@ func (suite *FileserverTestSuite) SetupTest() { | |||
| 	suite.state.Caches.Init() | ||||
| 	testrig.StartNoopWorkers(&suite.state) | ||||
| 
 | ||||
| 	suite.db = testrig.NewTestDB(&suite.state) | ||||
| 	suite.state.DB = suite.db | ||||
| 
 | ||||
| 	testrig.StandardDBSetup(suite.db, nil) | ||||
| 	testrig.StandardStorageSetup(suite.storage, "../../../testrig/media") | ||||
| 
 | ||||
| 	suite.testTokens = testrig.NewTestTokens() | ||||
| 	suite.testClients = testrig.NewTestClients() | ||||
| 	suite.testApplications = testrig.NewTestApplications() | ||||
|  |  | |||
|  | @ -52,3 +52,9 @@ func LoadEarlyFlags(cmd *cobra.Command) error { | |||
| func BindFlags(cmd *cobra.Command) error { | ||||
| 	return global.BindFlags(cmd) | ||||
| } | ||||
| 
 | ||||
| // Reset will totally clear global | ||||
| // ConfigState{}, loading defaults. | ||||
| func Reset() { | ||||
| 	global.Reset() | ||||
| } | ||||
|  |  | |||
|  | @ -37,25 +37,9 @@ type ConfigState struct { //nolint | |||
| 
 | ||||
| // NewState returns a new initialized ConfigState instance. | ||||
| func NewState() *ConfigState { | ||||
| 	viper := viper.New() | ||||
| 
 | ||||
| 	// Flag 'some-flag-name' becomes env var 'GTS_SOME_FLAG_NAME' | ||||
| 	viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_")) | ||||
| 	viper.SetEnvPrefix("gts") | ||||
| 
 | ||||
| 	// Load appropriate named vals from env | ||||
| 	viper.AutomaticEnv() | ||||
| 
 | ||||
| 	// Create new ConfigState with defaults | ||||
| 	state := &ConfigState{ | ||||
| 		viper:  viper, | ||||
| 		config: Defaults, | ||||
| 	} | ||||
| 
 | ||||
| 	// Perform initial load into viper | ||||
| 	state.reloadToViper() | ||||
| 
 | ||||
| 	return state | ||||
| 	st := new(ConfigState) | ||||
| 	st.Reset() | ||||
| 	return st | ||||
| } | ||||
| 
 | ||||
| // Config provides safe access to the ConfigState's contained Configuration, | ||||
|  | @ -116,6 +100,32 @@ func (st *ConfigState) Reload() (err error) { | |||
| 	return | ||||
| } | ||||
| 
 | ||||
| // Reset will totally clear | ||||
| // ConfigState{}, loading defaults. | ||||
| func (st *ConfigState) Reset() { | ||||
| 	// Do within lock. | ||||
| 	st.mutex.Lock() | ||||
| 	defer st.mutex.Unlock() | ||||
| 
 | ||||
| 	// Create new viper. | ||||
| 	viper := viper.New() | ||||
| 
 | ||||
| 	// Flag 'some-flag-name' becomes env var 'GTS_SOME_FLAG_NAME' | ||||
| 	viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_")) | ||||
| 	viper.SetEnvPrefix("gts") | ||||
| 
 | ||||
| 	// Load appropriate | ||||
| 	// named vals from env. | ||||
| 	viper.AutomaticEnv() | ||||
| 
 | ||||
| 	// Reset variables. | ||||
| 	st.viper = viper | ||||
| 	st.config = Defaults | ||||
| 
 | ||||
| 	// Load into viper. | ||||
| 	st.reloadToViper() | ||||
| } | ||||
| 
 | ||||
| // reloadToViper will reload Configuration{} values into viper. | ||||
| func (st *ConfigState) reloadToViper() { | ||||
| 	raw, err := st.config.MarshalMap() | ||||
|  |  | |||
|  | @ -74,6 +74,7 @@ func (suite *AdminTestSuite) TestCreateInstanceAccount() { | |||
| 	// we need to take an empty db for this... | ||||
| 	testrig.StandardDBTeardown(suite.db) | ||||
| 	// ...with tables created but no data | ||||
| 	suite.db = testrig.NewTestDB(&suite.state) | ||||
| 	testrig.CreateTestTables(suite.db) | ||||
| 
 | ||||
| 	// make sure there's no instance account in the db yet | ||||
|  |  | |||
|  | @ -48,8 +48,6 @@ import ( | |||
| 	"github.com/uptrace/bun/dialect/pgdialect" | ||||
| 	"github.com/uptrace/bun/dialect/sqlitedialect" | ||||
| 	"github.com/uptrace/bun/migrate" | ||||
| 
 | ||||
| 	"modernc.org/sqlite" | ||||
| ) | ||||
| 
 | ||||
| // DBService satisfies the DB interface | ||||
|  | @ -133,12 +131,12 @@ func NewBunDBService(ctx context.Context, state *state.State) (db.DB, error) { | |||
| 
 | ||||
| 	switch t { | ||||
| 	case "postgres": | ||||
| 		db, err = pgConn(ctx, state) | ||||
| 		db, err = pgConn(ctx) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 	case "sqlite": | ||||
| 		db, err = sqliteConn(ctx, state) | ||||
| 		db, err = sqliteConn(ctx) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
|  | @ -295,7 +293,7 @@ func NewBunDBService(ctx context.Context, state *state.State) (db.DB, error) { | |||
| 	return ps, nil | ||||
| } | ||||
| 
 | ||||
| func pgConn(ctx context.Context, state *state.State) (*bun.DB, error) { | ||||
| func pgConn(ctx context.Context) (*bun.DB, error) { | ||||
| 	opts, err := deriveBunDBPGOptions() //nolint:contextcheck | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("could not create bundb postgres options: %w", err) | ||||
|  | @ -326,7 +324,7 @@ func pgConn(ctx context.Context, state *state.State) (*bun.DB, error) { | |||
| 	return db, nil | ||||
| } | ||||
| 
 | ||||
| func sqliteConn(ctx context.Context, state *state.State) (*bun.DB, error) { | ||||
| func sqliteConn(ctx context.Context) (*bun.DB, error) { | ||||
| 	// validate db address has actually been set | ||||
| 	address := config.GetDbAddress() | ||||
| 	if address == "" { | ||||
|  | @ -339,9 +337,6 @@ func sqliteConn(ctx context.Context, state *state.State) (*bun.DB, error) { | |||
| 	// Open new DB instance | ||||
| 	sqldb, err := sql.Open("sqlite-gts", address) | ||||
| 	if err != nil { | ||||
| 		if errWithCode, ok := err.(*sqlite.Error); ok { | ||||
| 			err = errors.New(sqlite.ErrorCodeString[errWithCode.Code()]) | ||||
| 		} | ||||
| 		return nil, fmt.Errorf("could not open sqlite db with address %s: %w", address, err) | ||||
| 	} | ||||
| 
 | ||||
|  | @ -356,11 +351,9 @@ func sqliteConn(ctx context.Context, state *state.State) (*bun.DB, error) { | |||
| 
 | ||||
| 	// ping to check the db is there and listening | ||||
| 	if err := db.PingContext(ctx); err != nil { | ||||
| 		if errWithCode, ok := err.(*sqlite.Error); ok { | ||||
| 			err = errors.New(sqlite.ErrorCodeString[errWithCode.Code()]) | ||||
| 		} | ||||
| 		return nil, fmt.Errorf("sqlite ping: %w", err) | ||||
| 	} | ||||
| 
 | ||||
| 	log.Infof(ctx, "connected to SQLITE database with address %s", address) | ||||
| 
 | ||||
| 	return db, nil | ||||
|  | @ -528,12 +521,8 @@ func buildSQLiteAddress(addr string) string { | |||
| 
 | ||||
| 		// Use random name for in-memory instead of ':memory:', so | ||||
| 		// multiple in-mem databases can be created without conflict. | ||||
| 		addr = uuid.NewString() | ||||
| 
 | ||||
| 		// in-mem-specific preferences | ||||
| 		// (shared cache so that tests don't fail) | ||||
| 		prefs.Add("mode", "memory") | ||||
| 		prefs.Add("cache", "shared") | ||||
| 		addr = "/" + uuid.NewString() | ||||
| 		prefs.Add("vfs", "memdb") | ||||
| 	} | ||||
| 
 | ||||
| 	if dur := config.GetDbSqliteBusyTimeout(); dur > 0 { | ||||
|  |  | |||
|  | @ -18,350 +18,14 @@ | |||
| package bundb | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"database/sql" | ||||
| 	"database/sql/driver" | ||||
| 	"time" | ||||
| 	_ "unsafe" // linkname shenanigans | ||||
| 
 | ||||
| 	pgx "github.com/jackc/pgx/v5/stdlib" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/gtserror" | ||||
| 	"modernc.org/sqlite" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db/postgres" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db/sqlite" | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| 	// global SQL driver instances. | ||||
| 	postgresDriver = pgx.GetDefaultDriver() | ||||
| 	sqliteDriver   = getSQLiteDriver() | ||||
| 
 | ||||
| 	// check the postgres connection | ||||
| 	// conforms to our conn{} interface. | ||||
| 	// (note SQLite doesn't export their | ||||
| 	// conn type, and gets checked in | ||||
| 	// tests very regularly anywho). | ||||
| 	_ conn = (*pgx.Conn)(nil) | ||||
| ) | ||||
| 
 | ||||
| //go:linkname getSQLiteDriver modernc.org/sqlite.newDriver | ||||
| func getSQLiteDriver() *sqlite.Driver | ||||
| 
 | ||||
| func init() { | ||||
| 	sql.Register("pgx-gts", &PostgreSQLDriver{}) | ||||
| 	sql.Register("sqlite-gts", &SQLiteDriver{}) | ||||
| } | ||||
| 
 | ||||
| // PostgreSQLDriver is our own wrapper around the | ||||
| // pgx/stdlib.Driver{} type in order to wrap further | ||||
| // SQL driver types with our own err processing. | ||||
| type PostgreSQLDriver struct{} | ||||
| 
 | ||||
| func (d *PostgreSQLDriver) Open(name string) (driver.Conn, error) { | ||||
| 	c, err := postgresDriver.Open(name) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return &PostgreSQLConn{conn: c.(conn)}, nil | ||||
| } | ||||
| 
 | ||||
| type PostgreSQLConn struct{ conn } | ||||
| 
 | ||||
| func (c *PostgreSQLConn) Begin() (driver.Tx, error) { | ||||
| 	return c.BeginTx(context.Background(), driver.TxOptions{}) | ||||
| } | ||||
| 
 | ||||
| func (c *PostgreSQLConn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, error) { | ||||
| 	tx, err := c.conn.BeginTx(ctx, opts) | ||||
| 	err = processPostgresError(err) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return &PostgreSQLTx{tx}, nil | ||||
| } | ||||
| 
 | ||||
| func (c *PostgreSQLConn) Prepare(query string) (driver.Stmt, error) { | ||||
| 	return c.PrepareContext(context.Background(), query) | ||||
| } | ||||
| 
 | ||||
| func (c *PostgreSQLConn) PrepareContext(ctx context.Context, query string) (driver.Stmt, error) { | ||||
| 	st, err := c.conn.PrepareContext(ctx, query) | ||||
| 	err = processPostgresError(err) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return &PostgreSQLStmt{stmt: st.(stmt)}, nil | ||||
| } | ||||
| 
 | ||||
| func (c *PostgreSQLConn) Exec(query string, args []driver.Value) (driver.Result, error) { | ||||
| 	return c.ExecContext(context.Background(), query, toNamedValues(args)) | ||||
| } | ||||
| 
 | ||||
| func (c *PostgreSQLConn) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Result, error) { | ||||
| 	result, err := c.conn.ExecContext(ctx, query, args) | ||||
| 	err = processPostgresError(err) | ||||
| 	return result, err | ||||
| } | ||||
| 
 | ||||
| func (c *PostgreSQLConn) Query(query string, args []driver.Value) (driver.Rows, error) { | ||||
| 	return c.QueryContext(context.Background(), query, toNamedValues(args)) | ||||
| } | ||||
| 
 | ||||
| func (c *PostgreSQLConn) QueryContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Rows, error) { | ||||
| 	rows, err := c.conn.QueryContext(ctx, query, args) | ||||
| 	err = processPostgresError(err) | ||||
| 	return rows, err | ||||
| } | ||||
| 
 | ||||
| func (c *PostgreSQLConn) Close() error { | ||||
| 	return c.conn.Close() | ||||
| } | ||||
| 
 | ||||
| type PostgreSQLTx struct{ driver.Tx } | ||||
| 
 | ||||
| func (tx *PostgreSQLTx) Commit() error { | ||||
| 	err := tx.Tx.Commit() | ||||
| 	return processPostgresError(err) | ||||
| } | ||||
| 
 | ||||
| func (tx *PostgreSQLTx) Rollback() error { | ||||
| 	err := tx.Tx.Rollback() | ||||
| 	return processPostgresError(err) | ||||
| } | ||||
| 
 | ||||
| type PostgreSQLStmt struct{ stmt } | ||||
| 
 | ||||
| func (stmt *PostgreSQLStmt) Exec(args []driver.Value) (driver.Result, error) { | ||||
| 	return stmt.ExecContext(context.Background(), toNamedValues(args)) | ||||
| } | ||||
| 
 | ||||
| func (stmt *PostgreSQLStmt) ExecContext(ctx context.Context, args []driver.NamedValue) (driver.Result, error) { | ||||
| 	res, err := stmt.stmt.ExecContext(ctx, args) | ||||
| 	err = processPostgresError(err) | ||||
| 	return res, err | ||||
| } | ||||
| 
 | ||||
| func (stmt *PostgreSQLStmt) Query(args []driver.Value) (driver.Rows, error) { | ||||
| 	return stmt.QueryContext(context.Background(), toNamedValues(args)) | ||||
| } | ||||
| 
 | ||||
| func (stmt *PostgreSQLStmt) QueryContext(ctx context.Context, args []driver.NamedValue) (driver.Rows, error) { | ||||
| 	rows, err := stmt.stmt.QueryContext(ctx, args) | ||||
| 	err = processPostgresError(err) | ||||
| 	return rows, err | ||||
| } | ||||
| 
 | ||||
| // SQLiteDriver is our own wrapper around the | ||||
| // sqlite.Driver{} type in order to wrap further | ||||
| // SQL driver types with our own functionality, | ||||
| // e.g. hooks, retries and err processing. | ||||
| type SQLiteDriver struct{} | ||||
| 
 | ||||
| func (d *SQLiteDriver) Open(name string) (driver.Conn, error) { | ||||
| 	c, err := sqliteDriver.Open(name) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return &SQLiteConn{conn: c.(conn)}, nil | ||||
| } | ||||
| 
 | ||||
| type SQLiteConn struct{ conn } | ||||
| 
 | ||||
| func (c *SQLiteConn) Begin() (driver.Tx, error) { | ||||
| 	return c.BeginTx(context.Background(), driver.TxOptions{}) | ||||
| } | ||||
| 
 | ||||
| func (c *SQLiteConn) BeginTx(ctx context.Context, opts driver.TxOptions) (tx driver.Tx, err error) { | ||||
| 	err = retryOnBusy(ctx, func() error { | ||||
| 		tx, err = c.conn.BeginTx(ctx, opts) | ||||
| 		err = processSQLiteError(err) | ||||
| 		return err | ||||
| 	}) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return &SQLiteTx{Context: ctx, Tx: tx}, nil | ||||
| } | ||||
| 
 | ||||
| func (c *SQLiteConn) Prepare(query string) (driver.Stmt, error) { | ||||
| 	return c.PrepareContext(context.Background(), query) | ||||
| } | ||||
| 
 | ||||
| func (c *SQLiteConn) PrepareContext(ctx context.Context, query string) (st driver.Stmt, err error) { | ||||
| 	err = retryOnBusy(ctx, func() error { | ||||
| 		st, err = c.conn.PrepareContext(ctx, query) | ||||
| 		err = processSQLiteError(err) | ||||
| 		return err | ||||
| 	}) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return &SQLiteStmt{st.(stmt)}, nil | ||||
| } | ||||
| 
 | ||||
| func (c *SQLiteConn) Exec(query string, args []driver.Value) (driver.Result, error) { | ||||
| 	return c.ExecContext(context.Background(), query, toNamedValues(args)) | ||||
| } | ||||
| 
 | ||||
| func (c *SQLiteConn) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (result driver.Result, err error) { | ||||
| 	err = retryOnBusy(ctx, func() error { | ||||
| 		result, err = c.conn.ExecContext(ctx, query, args) | ||||
| 		err = processSQLiteError(err) | ||||
| 		return err | ||||
| 	}) | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| func (c *SQLiteConn) Query(query string, args []driver.Value) (driver.Rows, error) { | ||||
| 	return c.QueryContext(context.Background(), query, toNamedValues(args)) | ||||
| } | ||||
| 
 | ||||
| func (c *SQLiteConn) QueryContext(ctx context.Context, query string, args []driver.NamedValue) (rows driver.Rows, err error) { | ||||
| 	err = retryOnBusy(ctx, func() error { | ||||
| 		rows, err = c.conn.QueryContext(ctx, query, args) | ||||
| 		err = processSQLiteError(err) | ||||
| 		return err | ||||
| 	}) | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| func (c *SQLiteConn) Close() error { | ||||
| 	// see: https://www.sqlite.org/pragma.html#pragma_optimize | ||||
| 	const onClose = "PRAGMA analysis_limit=1000; PRAGMA optimize;" | ||||
| 	_, _ = c.conn.ExecContext(context.Background(), onClose, nil) | ||||
| 	return c.conn.Close() | ||||
| } | ||||
| 
 | ||||
| type SQLiteTx struct { | ||||
| 	context.Context | ||||
| 	driver.Tx | ||||
| } | ||||
| 
 | ||||
| func (tx *SQLiteTx) Commit() (err error) { | ||||
| 	err = retryOnBusy(tx.Context, func() error { | ||||
| 		err = tx.Tx.Commit() | ||||
| 		err = processSQLiteError(err) | ||||
| 		return err | ||||
| 	}) | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| func (tx *SQLiteTx) Rollback() (err error) { | ||||
| 	err = retryOnBusy(tx.Context, func() error { | ||||
| 		err = tx.Tx.Rollback() | ||||
| 		err = processSQLiteError(err) | ||||
| 		return err | ||||
| 	}) | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| type SQLiteStmt struct{ stmt } | ||||
| 
 | ||||
| func (stmt *SQLiteStmt) Exec(args []driver.Value) (driver.Result, error) { | ||||
| 	return stmt.ExecContext(context.Background(), toNamedValues(args)) | ||||
| } | ||||
| 
 | ||||
| func (stmt *SQLiteStmt) ExecContext(ctx context.Context, args []driver.NamedValue) (res driver.Result, err error) { | ||||
| 	err = retryOnBusy(ctx, func() error { | ||||
| 		res, err = stmt.stmt.ExecContext(ctx, args) | ||||
| 		err = processSQLiteError(err) | ||||
| 		return err | ||||
| 	}) | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| func (stmt *SQLiteStmt) Query(args []driver.Value) (driver.Rows, error) { | ||||
| 	return stmt.QueryContext(context.Background(), toNamedValues(args)) | ||||
| } | ||||
| 
 | ||||
| func (stmt *SQLiteStmt) QueryContext(ctx context.Context, args []driver.NamedValue) (rows driver.Rows, err error) { | ||||
| 	err = retryOnBusy(ctx, func() error { | ||||
| 		rows, err = stmt.stmt.QueryContext(ctx, args) | ||||
| 		err = processSQLiteError(err) | ||||
| 		return err | ||||
| 	}) | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| type conn interface { | ||||
| 	driver.Conn | ||||
| 	driver.ConnPrepareContext | ||||
| 	driver.ExecerContext | ||||
| 	driver.QueryerContext | ||||
| 	driver.ConnBeginTx | ||||
| } | ||||
| 
 | ||||
| type stmt interface { | ||||
| 	driver.Stmt | ||||
| 	driver.StmtExecContext | ||||
| 	driver.StmtQueryContext | ||||
| } | ||||
| 
 | ||||
| // retryOnBusy will retry given function on returned 'errBusy'. | ||||
| func retryOnBusy(ctx context.Context, fn func() error) error { | ||||
| 	if err := fn(); err != errBusy { | ||||
| 		return err | ||||
| 	} | ||||
| 	return retryOnBusySlow(ctx, fn) | ||||
| } | ||||
| 
 | ||||
| // retryOnBusySlow is the outlined form of retryOnBusy, to allow the fast path (i.e. only | ||||
| // 1 attempt) to be inlined, leaving the slow retry loop to be a separate function call. | ||||
| func retryOnBusySlow(ctx context.Context, fn func() error) error { | ||||
| 	var backoff time.Duration | ||||
| 
 | ||||
| 	for i := 0; ; i++ { | ||||
| 		// backoff according to a multiplier of 2ms * 2^2n, | ||||
| 		// up to a maximum possible backoff time of 5 minutes. | ||||
| 		// | ||||
| 		// this works out as the following: | ||||
| 		// 4ms | ||||
| 		// 16ms | ||||
| 		// 64ms | ||||
| 		// 256ms | ||||
| 		// 1.024s | ||||
| 		// 4.096s | ||||
| 		// 16.384s | ||||
| 		// 1m5.536s | ||||
| 		// 4m22.144s | ||||
| 		backoff = 2 * time.Millisecond * (1 << (2*i + 1)) | ||||
| 		if backoff >= 5*time.Minute { | ||||
| 			break | ||||
| 		} | ||||
| 
 | ||||
| 		select { | ||||
| 		// Context cancelled. | ||||
| 		case <-ctx.Done(): | ||||
| 			return ctx.Err() | ||||
| 
 | ||||
| 		// Backoff for some time. | ||||
| 		case <-time.After(backoff): | ||||
| 		} | ||||
| 
 | ||||
| 		// Perform func. | ||||
| 		err := fn() | ||||
| 
 | ||||
| 		if err != errBusy { | ||||
| 			// May be nil, or may be | ||||
| 			// some other error, either | ||||
| 			// way return here. | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return gtserror.Newf("%w (waited > %s)", db.ErrBusyTimeout, backoff) | ||||
| } | ||||
| 
 | ||||
| // toNamedValues converts older driver.Value types to driver.NamedValue types. | ||||
| func toNamedValues(args []driver.Value) []driver.NamedValue { | ||||
| 	if args == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 	args2 := make([]driver.NamedValue, len(args)) | ||||
| 	for i := range args { | ||||
| 		args2[i] = driver.NamedValue{ | ||||
| 			Ordinal: i + 1, | ||||
| 			Value:   args[i], | ||||
| 		} | ||||
| 	} | ||||
| 	return args2 | ||||
| 	// register our SQL driver implementations. | ||||
| 	sql.Register("pgx-gts", &postgres.Driver{}) | ||||
| 	sql.Register("sqlite-gts", &sqlite.Driver{}) | ||||
| } | ||||
|  |  | |||
|  | @ -1,105 +0,0 @@ | |||
| // GoToSocial | ||||
| // Copyright (C) GoToSocial Authors admin@gotosocial.org | ||||
| // SPDX-License-Identifier: AGPL-3.0-or-later | ||||
| // | ||||
| // This program is free software: you can redistribute it and/or modify | ||||
| // it under the terms of the GNU Affero General Public License as published by | ||||
| // the Free Software Foundation, either version 3 of the License, or | ||||
| // (at your option) any later version. | ||||
| // | ||||
| // This program is distributed in the hope that it will be useful, | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| // GNU Affero General Public License for more details. | ||||
| // | ||||
| // You should have received a copy of the GNU Affero General Public License | ||||
| // along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
| 
 | ||||
| package bundb | ||||
| 
 | ||||
| import ( | ||||
| 	"database/sql/driver" | ||||
| 	"errors" | ||||
| 
 | ||||
| 	"github.com/jackc/pgx/v5/pgconn" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db" | ||||
| 	"modernc.org/sqlite" | ||||
| 	sqlite3 "modernc.org/sqlite/lib" | ||||
| ) | ||||
| 
 | ||||
| // errBusy is a sentinel error indicating | ||||
| // busy database (e.g. retry needed). | ||||
| var errBusy = errors.New("busy") | ||||
| 
 | ||||
| // processPostgresError processes an error, replacing any postgres specific errors with our own error type | ||||
| func processPostgresError(err error) error { | ||||
| 	// Catch nil errs. | ||||
| 	if err == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	// Attempt to cast as postgres | ||||
| 	pgErr, ok := err.(*pgconn.PgError) | ||||
| 	if !ok { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	// Handle supplied error code: | ||||
| 	// (https://www.postgresql.org/docs/10/errcodes-appendix.html) | ||||
| 	switch pgErr.Code { //nolint | ||||
| 	case "23505" /* unique_violation */ : | ||||
| 		return db.ErrAlreadyExists | ||||
| 	} | ||||
| 
 | ||||
| 	return err | ||||
| } | ||||
| 
 | ||||
| // processSQLiteError processes an error, replacing any sqlite specific errors with our own error type | ||||
| func processSQLiteError(err error) error { | ||||
| 	// Catch nil errs. | ||||
| 	if err == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	// Attempt to cast as sqlite | ||||
| 	sqliteErr, ok := err.(*sqlite.Error) | ||||
| 	if !ok { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	// Handle supplied error code: | ||||
| 	switch sqliteErr.Code() { | ||||
| 	case sqlite3.SQLITE_CONSTRAINT_UNIQUE, | ||||
| 		sqlite3.SQLITE_CONSTRAINT_PRIMARYKEY: | ||||
| 		return db.ErrAlreadyExists | ||||
| 	case sqlite3.SQLITE_BUSY, | ||||
| 		sqlite3.SQLITE_BUSY_SNAPSHOT, | ||||
| 		sqlite3.SQLITE_BUSY_RECOVERY: | ||||
| 		return errBusy | ||||
| 	case sqlite3.SQLITE_BUSY_TIMEOUT: | ||||
| 		return db.ErrBusyTimeout | ||||
| 
 | ||||
| 	// WORKAROUND: | ||||
| 	// text copied from matrix dev chat: | ||||
| 	// | ||||
| 	// okay i've found a workaround for now. so between | ||||
| 	// v1.29.0 and v1.29.2 (modernc.org/sqlite) is that | ||||
| 	// slightly tweaked interruptOnDone() behaviour, which | ||||
| 	// causes interrupt to (imo, correctly) get called when | ||||
| 	// a context is cancelled to cancel the running query. the | ||||
| 	// issue is that every single query after that point seems | ||||
| 	// to still then return interrupted. so as you thought, | ||||
| 	// maybe that query count isn't being decremented. i don't | ||||
| 	// think it's our code, but i haven't ruled it out yet. | ||||
| 	// | ||||
| 	// the workaround for now is adding to our sqlite error | ||||
| 	// processor to replace an SQLITE_INTERRUPTED code with | ||||
| 	// driver.ErrBadConn, which hints to the golang sql package | ||||
| 	// that the conn needs to be closed and a new one opened | ||||
| 	// | ||||
| 	case sqlite3.SQLITE_INTERRUPT: | ||||
| 		return driver.ErrBadConn | ||||
| 	} | ||||
| 
 | ||||
| 	return err | ||||
| } | ||||
|  | @ -19,6 +19,7 @@ package bundb_test | |||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"errors" | ||||
| 	"testing" | ||||
| 
 | ||||
| 	"github.com/stretchr/testify/suite" | ||||
|  | @ -82,10 +83,20 @@ func (suite *TagTestSuite) TestPutTag() { | |||
| 
 | ||||
| 		// Subsequent inserts should fail | ||||
| 		// since all these tags are equivalent. | ||||
| 		suite.ErrorIs(err, db.ErrAlreadyExists) | ||||
| 		if !suite.ErrorIs(err, db.ErrAlreadyExists) { | ||||
| 			suite.T().Logf("%T(%v) %v", err, err, unwrap(err)) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func TestTagTestSuite(t *testing.T) { | ||||
| 	suite.Run(t, new(TagTestSuite)) | ||||
| } | ||||
| 
 | ||||
| func unwrap(err error) (errs []error) { | ||||
| 	for err != nil { | ||||
| 		errs = append(errs, err) | ||||
| 		err = errors.Unwrap(err) | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
|  |  | |||
|  | @ -29,8 +29,4 @@ var ( | |||
| 
 | ||||
| 	// ErrAlreadyExists is returned when a conflict was encountered in the db when doing an insert. | ||||
| 	ErrAlreadyExists = errors.New("already exists") | ||||
| 
 | ||||
| 	// ErrBusyTimeout is returned if the database connection indicates the connection is too busy | ||||
| 	// to complete the supplied query. This is generally intended to be handled internally by the DB. | ||||
| 	ErrBusyTimeout = errors.New("busy timeout") | ||||
| ) | ||||
|  |  | |||
							
								
								
									
										209
									
								
								internal/db/postgres/driver.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										209
									
								
								internal/db/postgres/driver.go
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,209 @@ | |||
| // GoToSocial | ||||
| // Copyright (C) GoToSocial Authors admin@gotosocial.org | ||||
| // SPDX-License-Identifier: AGPL-3.0-or-later | ||||
| // | ||||
| // This program is free software: you can redistribute it and/or modify | ||||
| // it under the terms of the GNU Affero General Public License as published by | ||||
| // the Free Software Foundation, either version 3 of the License, or | ||||
| // (at your option) any later version. | ||||
| // | ||||
| // This program is distributed in the hope that it will be useful, | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| // GNU Affero General Public License for more details. | ||||
| // | ||||
| // You should have received a copy of the GNU Affero General Public License | ||||
| // along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
| 
 | ||||
| package postgres | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"database/sql/driver" | ||||
| 
 | ||||
| 	pgx "github.com/jackc/pgx/v5/stdlib" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db" | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| 	// global PostgreSQL driver instances. | ||||
| 	postgresDriver = pgx.GetDefaultDriver().(*pgx.Driver) | ||||
| 
 | ||||
| 	// check the postgres driver types | ||||
| 	// conforms to our interface types. | ||||
| 	// (note SQLite doesn't export their | ||||
| 	// driver types, and gets checked in | ||||
| 	// tests very regularly anywho). | ||||
| 	_ connIface = (*pgx.Conn)(nil) | ||||
| 	_ stmtIface = (*pgx.Stmt)(nil) | ||||
| 	_ rowsIface = (*pgx.Rows)(nil) | ||||
| ) | ||||
| 
 | ||||
| // Driver is our own wrapper around the | ||||
| // pgx/stdlib.Driver{} type in order to wrap further | ||||
| // SQL driver types with our own err processing. | ||||
| type Driver struct{} | ||||
| 
 | ||||
| func (d *Driver) Open(name string) (driver.Conn, error) { | ||||
| 	conn, err := postgresDriver.Open(name) | ||||
| 	if err != nil { | ||||
| 		err = processPostgresError(err) | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return &postgresConn{conn.(connIface)}, nil | ||||
| } | ||||
| 
 | ||||
| func (d *Driver) OpenConnector(name string) (driver.Connector, error) { | ||||
| 	cc, err := postgresDriver.OpenConnector(name) | ||||
| 	if err != nil { | ||||
| 		err = processPostgresError(err) | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return &postgresConnector{driver: d, Connector: cc}, nil | ||||
| } | ||||
| 
 | ||||
| type postgresConnector struct { | ||||
| 	driver *Driver | ||||
| 	driver.Connector | ||||
| } | ||||
| 
 | ||||
| func (c *postgresConnector) Driver() driver.Driver { return c.driver } | ||||
| 
 | ||||
| func (c *postgresConnector) Connect(ctx context.Context) (driver.Conn, error) { | ||||
| 	conn, err := c.Connector.Connect(ctx) | ||||
| 	if err != nil { | ||||
| 		err = processPostgresError(err) | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return &postgresConn{conn.(connIface)}, nil | ||||
| } | ||||
| 
 | ||||
| type postgresConn struct{ connIface } | ||||
| 
 | ||||
| func (c *postgresConn) Begin() (driver.Tx, error) { | ||||
| 	return c.BeginTx(context.Background(), driver.TxOptions{}) | ||||
| } | ||||
| 
 | ||||
| func (c *postgresConn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, error) { | ||||
| 	tx, err := c.connIface.BeginTx(ctx, opts) | ||||
| 	err = processPostgresError(err) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return &postgresTx{tx}, nil | ||||
| } | ||||
| 
 | ||||
| func (c *postgresConn) Prepare(query string) (driver.Stmt, error) { | ||||
| 	return c.PrepareContext(context.Background(), query) | ||||
| } | ||||
| 
 | ||||
| func (c *postgresConn) PrepareContext(ctx context.Context, query string) (driver.Stmt, error) { | ||||
| 	st, err := c.connIface.PrepareContext(ctx, query) | ||||
| 	err = processPostgresError(err) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return &postgresStmt{st.(stmtIface)}, nil | ||||
| } | ||||
| 
 | ||||
| func (c *postgresConn) Exec(query string, args []driver.Value) (driver.Result, error) { | ||||
| 	return c.ExecContext(context.Background(), query, db.ToNamedValues(args)) | ||||
| } | ||||
| 
 | ||||
| func (c *postgresConn) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Result, error) { | ||||
| 	result, err := c.connIface.ExecContext(ctx, query, args) | ||||
| 	err = processPostgresError(err) | ||||
| 	return result, err | ||||
| } | ||||
| 
 | ||||
| func (c *postgresConn) Query(query string, args []driver.Value) (driver.Rows, error) { | ||||
| 	return c.QueryContext(context.Background(), query, db.ToNamedValues(args)) | ||||
| } | ||||
| 
 | ||||
| func (c *postgresConn) QueryContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Rows, error) { | ||||
| 	rows, err := c.connIface.QueryContext(ctx, query, args) | ||||
| 	err = processPostgresError(err) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return &postgresRows{rows.(rowsIface)}, nil | ||||
| } | ||||
| 
 | ||||
| func (c *postgresConn) Close() error { | ||||
| 	err := c.connIface.Close() | ||||
| 	return processPostgresError(err) | ||||
| } | ||||
| 
 | ||||
| type postgresTx struct{ driver.Tx } | ||||
| 
 | ||||
| func (tx *postgresTx) Commit() error { | ||||
| 	err := tx.Tx.Commit() | ||||
| 	return processPostgresError(err) | ||||
| } | ||||
| 
 | ||||
| func (tx *postgresTx) Rollback() error { | ||||
| 	err := tx.Tx.Rollback() | ||||
| 	return processPostgresError(err) | ||||
| } | ||||
| 
 | ||||
| type postgresStmt struct{ stmtIface } | ||||
| 
 | ||||
| func (stmt *postgresStmt) Exec(args []driver.Value) (driver.Result, error) { | ||||
| 	return stmt.ExecContext(context.Background(), db.ToNamedValues(args)) | ||||
| } | ||||
| 
 | ||||
| func (stmt *postgresStmt) ExecContext(ctx context.Context, args []driver.NamedValue) (driver.Result, error) { | ||||
| 	res, err := stmt.stmtIface.ExecContext(ctx, args) | ||||
| 	err = processPostgresError(err) | ||||
| 	return res, err | ||||
| } | ||||
| 
 | ||||
| func (stmt *postgresStmt) Query(args []driver.Value) (driver.Rows, error) { | ||||
| 	return stmt.QueryContext(context.Background(), db.ToNamedValues(args)) | ||||
| } | ||||
| 
 | ||||
| func (stmt *postgresStmt) QueryContext(ctx context.Context, args []driver.NamedValue) (driver.Rows, error) { | ||||
| 	rows, err := stmt.stmtIface.QueryContext(ctx, args) | ||||
| 	err = processPostgresError(err) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return &postgresRows{rows.(rowsIface)}, nil | ||||
| } | ||||
| 
 | ||||
| type postgresRows struct{ rowsIface } | ||||
| 
 | ||||
| func (r *postgresRows) Next(dest []driver.Value) error { | ||||
| 	err := r.rowsIface.Next(dest) | ||||
| 	err = processPostgresError(err) | ||||
| 	return err | ||||
| } | ||||
| 
 | ||||
| func (r *postgresRows) Close() error { | ||||
| 	err := r.rowsIface.Close() | ||||
| 	err = processPostgresError(err) | ||||
| 	return err | ||||
| } | ||||
| 
 | ||||
| type connIface interface { | ||||
| 	driver.Conn | ||||
| 	driver.ConnPrepareContext | ||||
| 	driver.ExecerContext | ||||
| 	driver.QueryerContext | ||||
| 	driver.ConnBeginTx | ||||
| } | ||||
| 
 | ||||
| type stmtIface interface { | ||||
| 	driver.Stmt | ||||
| 	driver.StmtExecContext | ||||
| 	driver.StmtQueryContext | ||||
| } | ||||
| 
 | ||||
| type rowsIface interface { | ||||
| 	driver.Rows | ||||
| 	driver.RowsColumnTypeDatabaseTypeName | ||||
| 	driver.RowsColumnTypeLength | ||||
| 	driver.RowsColumnTypePrecisionScale | ||||
| 	driver.RowsColumnTypeScanType | ||||
| 	driver.RowsColumnTypeScanType | ||||
| } | ||||
							
								
								
									
										46
									
								
								internal/db/postgres/errors.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								internal/db/postgres/errors.go
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,46 @@ | |||
| // GoToSocial | ||||
| // Copyright (C) GoToSocial Authors admin@gotosocial.org | ||||
| // SPDX-License-Identifier: AGPL-3.0-or-later | ||||
| // | ||||
| // This program is free software: you can redistribute it and/or modify | ||||
| // it under the terms of the GNU Affero General Public License as published by | ||||
| // the Free Software Foundation, either version 3 of the License, or | ||||
| // (at your option) any later version. | ||||
| // | ||||
| // This program is distributed in the hope that it will be useful, | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| // GNU Affero General Public License for more details. | ||||
| // | ||||
| // You should have received a copy of the GNU Affero General Public License | ||||
| // along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
| 
 | ||||
| package postgres | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 
 | ||||
| 	"github.com/jackc/pgx/v5/pgconn" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db" | ||||
| ) | ||||
| 
 | ||||
| // processPostgresError processes an error, replacing any | ||||
| // postgres specific errors with our own error type | ||||
| func processPostgresError(err error) error { | ||||
| 	// Attempt to cast as postgres | ||||
| 	pgErr, ok := err.(*pgconn.PgError) | ||||
| 	if !ok { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	// Handle supplied error code: | ||||
| 	// (https://www.postgresql.org/docs/10/errcodes-appendix.html) | ||||
| 	switch pgErr.Code { //nolint | ||||
| 	case "23505" /* unique_violation */ : | ||||
| 		return db.ErrAlreadyExists | ||||
| 	} | ||||
| 
 | ||||
| 	// Wrap the returned error with the code and | ||||
| 	// extended code for easier debugging later. | ||||
| 	return fmt.Errorf("%w (code=%s)", err, pgErr.Code) | ||||
| } | ||||
							
								
								
									
										197
									
								
								internal/db/sqlite/driver.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										197
									
								
								internal/db/sqlite/driver.go
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,197 @@ | |||
| // GoToSocial | ||||
| // Copyright (C) GoToSocial Authors admin@gotosocial.org | ||||
| // SPDX-License-Identifier: AGPL-3.0-or-later | ||||
| // | ||||
| // This program is free software: you can redistribute it and/or modify | ||||
| // it under the terms of the GNU Affero General Public License as published by | ||||
| // the Free Software Foundation, either version 3 of the License, or | ||||
| // (at your option) any later version. | ||||
| // | ||||
| // This program is distributed in the hope that it will be useful, | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| // GNU Affero General Public License for more details. | ||||
| // | ||||
| // You should have received a copy of the GNU Affero General Public License | ||||
| // along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
| 
 | ||||
| //go:build !wasmsqlite3 | ||||
| 
 | ||||
| package sqlite | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"database/sql/driver" | ||||
| 
 | ||||
| 	"modernc.org/sqlite" | ||||
| 
 | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db" | ||||
| ) | ||||
| 
 | ||||
| // Driver is our own wrapper around the | ||||
| // sqlite.Driver{} type in order to wrap | ||||
| // further SQL types with our own | ||||
| // functionality, e.g. err processing. | ||||
| type Driver struct{ sqlite.Driver } | ||||
| 
 | ||||
| func (d *Driver) Open(name string) (driver.Conn, error) { | ||||
| 	conn, err := d.Driver.Open(name) | ||||
| 	if err != nil { | ||||
| 		err = processSQLiteError(err) | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return &sqliteConn{conn.(connIface)}, nil | ||||
| } | ||||
| 
 | ||||
| type sqliteConn struct{ connIface } | ||||
| 
 | ||||
| func (c *sqliteConn) Begin() (driver.Tx, error) { | ||||
| 	return c.BeginTx(context.Background(), driver.TxOptions{}) | ||||
| } | ||||
| 
 | ||||
| func (c *sqliteConn) BeginTx(ctx context.Context, opts driver.TxOptions) (tx driver.Tx, err error) { | ||||
| 	tx, err = c.connIface.BeginTx(ctx, opts) | ||||
| 	err = processSQLiteError(err) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return &sqliteTx{tx}, nil | ||||
| } | ||||
| 
 | ||||
| func (c *sqliteConn) Prepare(query string) (driver.Stmt, error) { | ||||
| 	return c.PrepareContext(context.Background(), query) | ||||
| } | ||||
| 
 | ||||
| func (c *sqliteConn) PrepareContext(ctx context.Context, query string) (stmt driver.Stmt, err error) { | ||||
| 	stmt, err = c.connIface.PrepareContext(ctx, query) | ||||
| 	err = processSQLiteError(err) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return &sqliteStmt{stmtIface: stmt.(stmtIface)}, nil | ||||
| } | ||||
| 
 | ||||
| func (c *sqliteConn) Exec(query string, args []driver.Value) (driver.Result, error) { | ||||
| 	return c.ExecContext(context.Background(), query, db.ToNamedValues(args)) | ||||
| } | ||||
| 
 | ||||
| func (c *sqliteConn) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (res driver.Result, err error) { | ||||
| 	res, err = c.connIface.ExecContext(ctx, query, args) | ||||
| 	err = processSQLiteError(err) | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| func (c *sqliteConn) Query(query string, args []driver.Value) (driver.Rows, error) { | ||||
| 	return c.QueryContext(context.Background(), query, db.ToNamedValues(args)) | ||||
| } | ||||
| 
 | ||||
| func (c *sqliteConn) QueryContext(ctx context.Context, query string, args []driver.NamedValue) (rows driver.Rows, err error) { | ||||
| 	rows, err = c.connIface.QueryContext(ctx, query, args) | ||||
| 	err = processSQLiteError(err) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return &sqliteRows{rows.(rowsIface)}, nil | ||||
| } | ||||
| 
 | ||||
| func (c *sqliteConn) Close() (err error) { | ||||
| 	// see: https://www.sqlite.org/pragma.html#pragma_optimize | ||||
| 	const onClose = "PRAGMA analysis_limit=1000; PRAGMA optimize;" | ||||
| 	_, _ = c.connIface.ExecContext(context.Background(), onClose, nil) | ||||
| 
 | ||||
| 	// Finally, close the conn. | ||||
| 	err = c.connIface.Close() | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| type sqliteTx struct{ driver.Tx } | ||||
| 
 | ||||
| func (tx *sqliteTx) Commit() (err error) { | ||||
| 	err = tx.Tx.Commit() | ||||
| 	err = processSQLiteError(err) | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| func (tx *sqliteTx) Rollback() (err error) { | ||||
| 	err = tx.Tx.Rollback() | ||||
| 	err = processSQLiteError(err) | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| type sqliteStmt struct{ stmtIface } | ||||
| 
 | ||||
| func (stmt *sqliteStmt) Exec(args []driver.Value) (driver.Result, error) { | ||||
| 	return stmt.ExecContext(context.Background(), db.ToNamedValues(args)) | ||||
| } | ||||
| 
 | ||||
| func (stmt *sqliteStmt) ExecContext(ctx context.Context, args []driver.NamedValue) (res driver.Result, err error) { | ||||
| 	res, err = stmt.stmtIface.ExecContext(ctx, args) | ||||
| 	err = processSQLiteError(err) | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| func (stmt *sqliteStmt) Query(args []driver.Value) (driver.Rows, error) { | ||||
| 	return stmt.QueryContext(context.Background(), db.ToNamedValues(args)) | ||||
| } | ||||
| 
 | ||||
| func (stmt *sqliteStmt) QueryContext(ctx context.Context, args []driver.NamedValue) (rows driver.Rows, err error) { | ||||
| 	rows, err = stmt.stmtIface.QueryContext(ctx, args) | ||||
| 	err = processSQLiteError(err) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return &sqliteRows{rows.(rowsIface)}, nil | ||||
| } | ||||
| 
 | ||||
| func (stmt *sqliteStmt) Close() (err error) { | ||||
| 	err = stmt.stmtIface.Close() | ||||
| 	err = processSQLiteError(err) | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| type sqliteRows struct{ rowsIface } | ||||
| 
 | ||||
| func (r *sqliteRows) Next(dest []driver.Value) (err error) { | ||||
| 	err = r.rowsIface.Next(dest) | ||||
| 	err = processSQLiteError(err) | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| func (r *sqliteRows) Close() (err error) { | ||||
| 	err = r.rowsIface.Close() | ||||
| 	err = processSQLiteError(err) | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| // connIface is the driver.Conn interface | ||||
| // types (and the like) that modernc.org/sqlite.conn | ||||
| // conforms to. Useful so you don't need | ||||
| // to repeatedly perform checks yourself. | ||||
| type connIface interface { | ||||
| 	driver.Conn | ||||
| 	driver.ConnBeginTx | ||||
| 	driver.ConnPrepareContext | ||||
| 	driver.ExecerContext | ||||
| 	driver.QueryerContext | ||||
| } | ||||
| 
 | ||||
| // StmtIface is the driver.Stmt interface | ||||
| // types (and the like) that modernc.org/sqlite.stmt | ||||
| // conforms to. Useful so you don't need | ||||
| // to repeatedly perform checks yourself. | ||||
| type stmtIface interface { | ||||
| 	driver.Stmt | ||||
| 	driver.StmtExecContext | ||||
| 	driver.StmtQueryContext | ||||
| } | ||||
| 
 | ||||
| // RowsIface is the driver.Rows interface | ||||
| // types (and the like) that modernc.org/sqlite.rows | ||||
| // conforms to. Useful so you don't need | ||||
| // to repeatedly perform checks yourself. | ||||
| type rowsIface interface { | ||||
| 	driver.Rows | ||||
| 	driver.RowsColumnTypeDatabaseTypeName | ||||
| 	driver.RowsColumnTypeLength | ||||
| 	driver.RowsColumnTypeScanType | ||||
| } | ||||
							
								
								
									
										211
									
								
								internal/db/sqlite/driver_wasmsqlite3.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										211
									
								
								internal/db/sqlite/driver_wasmsqlite3.go
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,211 @@ | |||
| // GoToSocial | ||||
| // Copyright (C) GoToSocial Authors admin@gotosocial.org | ||||
| // SPDX-License-Identifier: AGPL-3.0-or-later | ||||
| // | ||||
| // This program is free software: you can redistribute it and/or modify | ||||
| // it under the terms of the GNU Affero General Public License as published by | ||||
| // the Free Software Foundation, either version 3 of the License, or | ||||
| // (at your option) any later version. | ||||
| // | ||||
| // This program is distributed in the hope that it will be useful, | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| // GNU Affero General Public License for more details. | ||||
| // | ||||
| // You should have received a copy of the GNU Affero General Public License | ||||
| // along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
| 
 | ||||
| //go:build wasmsqlite3 | ||||
| 
 | ||||
| package sqlite | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"database/sql/driver" | ||||
| 
 | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db" | ||||
| 
 | ||||
| 	"github.com/ncruces/go-sqlite3" | ||||
| 	sqlite3driver "github.com/ncruces/go-sqlite3/driver" | ||||
| 	_ "github.com/ncruces/go-sqlite3/embed"     // embed wasm binary | ||||
| 	_ "github.com/ncruces/go-sqlite3/vfs/memdb" // include memdb vfs | ||||
| ) | ||||
| 
 | ||||
| // Driver is our own wrapper around the | ||||
| // driver.SQLite{} type in order to wrap | ||||
| // further SQL types with our own | ||||
| // functionality, e.g. err processing. | ||||
| type Driver struct{ sqlite3driver.SQLite } | ||||
| 
 | ||||
| func (d *Driver) Open(name string) (driver.Conn, error) { | ||||
| 	conn, err := d.SQLite.Open(name) | ||||
| 	if err != nil { | ||||
| 		err = processSQLiteError(err) | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return &sqliteConn{conn.(connIface)}, nil | ||||
| } | ||||
| 
 | ||||
| func (d *Driver) OpenConnector(name string) (driver.Connector, error) { | ||||
| 	cc, err := d.SQLite.OpenConnector(name) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return &sqliteConnector{driver: d, Connector: cc}, nil | ||||
| } | ||||
| 
 | ||||
| type sqliteConnector struct { | ||||
| 	driver *Driver | ||||
| 	driver.Connector | ||||
| } | ||||
| 
 | ||||
| func (c *sqliteConnector) Driver() driver.Driver { return c.driver } | ||||
| 
 | ||||
| func (c *sqliteConnector) Connect(ctx context.Context) (driver.Conn, error) { | ||||
| 	conn, err := c.Connector.Connect(ctx) | ||||
| 	err = processSQLiteError(err) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return &sqliteConn{conn.(connIface)}, nil | ||||
| } | ||||
| 
 | ||||
| type sqliteConn struct{ connIface } | ||||
| 
 | ||||
| func (c *sqliteConn) Begin() (driver.Tx, error) { | ||||
| 	return c.BeginTx(context.Background(), driver.TxOptions{}) | ||||
| } | ||||
| 
 | ||||
| func (c *sqliteConn) BeginTx(ctx context.Context, opts driver.TxOptions) (tx driver.Tx, err error) { | ||||
| 	tx, err = c.connIface.BeginTx(ctx, opts) | ||||
| 	err = processSQLiteError(err) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return &sqliteTx{tx}, nil | ||||
| } | ||||
| 
 | ||||
| func (c *sqliteConn) Prepare(query string) (driver.Stmt, error) { | ||||
| 	return c.PrepareContext(context.Background(), query) | ||||
| } | ||||
| 
 | ||||
| func (c *sqliteConn) PrepareContext(ctx context.Context, query string) (stmt driver.Stmt, err error) { | ||||
| 	stmt, err = c.connIface.PrepareContext(ctx, query) | ||||
| 	err = processSQLiteError(err) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return &sqliteStmt{stmtIface: stmt.(stmtIface)}, nil | ||||
| } | ||||
| 
 | ||||
| func (c *sqliteConn) Exec(query string, args []driver.Value) (driver.Result, error) { | ||||
| 	return c.ExecContext(context.Background(), query, db.ToNamedValues(args)) | ||||
| } | ||||
| 
 | ||||
| func (c *sqliteConn) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (res driver.Result, err error) { | ||||
| 	res, err = c.connIface.ExecContext(ctx, query, args) | ||||
| 	err = processSQLiteError(err) | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| func (c *sqliteConn) Close() (err error) { | ||||
| 	// Get acces the underlying raw sqlite3 conn. | ||||
| 	raw := c.connIface.(sqlite3.DriverConn).Raw() | ||||
| 
 | ||||
| 	// see: https://www.sqlite.org/pragma.html#pragma_optimize | ||||
| 	const onClose = "PRAGMA analysis_limit=1000; PRAGMA optimize;" | ||||
| 	_ = raw.Exec(onClose) | ||||
| 
 | ||||
| 	// Finally, close. | ||||
| 	err = raw.Close() | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| type sqliteTx struct{ driver.Tx } | ||||
| 
 | ||||
| func (tx *sqliteTx) Commit() (err error) { | ||||
| 	err = tx.Tx.Commit() | ||||
| 	err = processSQLiteError(err) | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| func (tx *sqliteTx) Rollback() (err error) { | ||||
| 	err = tx.Tx.Rollback() | ||||
| 	err = processSQLiteError(err) | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| type sqliteStmt struct{ stmtIface } | ||||
| 
 | ||||
| func (stmt *sqliteStmt) Exec(args []driver.Value) (driver.Result, error) { | ||||
| 	return stmt.ExecContext(context.Background(), db.ToNamedValues(args)) | ||||
| } | ||||
| 
 | ||||
| func (stmt *sqliteStmt) ExecContext(ctx context.Context, args []driver.NamedValue) (res driver.Result, err error) { | ||||
| 	res, err = stmt.stmtIface.ExecContext(ctx, args) | ||||
| 	err = processSQLiteError(err) | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| func (stmt *sqliteStmt) Query(args []driver.Value) (driver.Rows, error) { | ||||
| 	return stmt.QueryContext(context.Background(), db.ToNamedValues(args)) | ||||
| } | ||||
| 
 | ||||
| func (stmt *sqliteStmt) QueryContext(ctx context.Context, args []driver.NamedValue) (rows driver.Rows, err error) { | ||||
| 	rows, err = stmt.stmtIface.QueryContext(ctx, args) | ||||
| 	err = processSQLiteError(err) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return &sqliteRows{rows.(rowsIface)}, nil | ||||
| } | ||||
| 
 | ||||
| func (stmt *sqliteStmt) Close() (err error) { | ||||
| 	err = stmt.stmtIface.Close() | ||||
| 	err = processSQLiteError(err) | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| type sqliteRows struct{ rowsIface } | ||||
| 
 | ||||
| func (r *sqliteRows) Next(dest []driver.Value) (err error) { | ||||
| 	err = r.rowsIface.Next(dest) | ||||
| 	err = processSQLiteError(err) | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| func (r *sqliteRows) Close() (err error) { | ||||
| 	err = r.rowsIface.Close() | ||||
| 	err = processSQLiteError(err) | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| // connIface is the driver.Conn interface | ||||
| // types (and the like) that go-sqlite3/driver.conn | ||||
| // conforms to. Useful so you don't need | ||||
| // to repeatedly perform checks yourself. | ||||
| type connIface interface { | ||||
| 	driver.Conn | ||||
| 	driver.ConnBeginTx | ||||
| 	driver.ConnPrepareContext | ||||
| 	driver.ExecerContext | ||||
| } | ||||
| 
 | ||||
| // StmtIface is the driver.Stmt interface | ||||
| // types (and the like) that go-sqlite3/driver.stmt | ||||
| // conforms to. Useful so you don't need | ||||
| // to repeatedly perform checks yourself. | ||||
| type stmtIface interface { | ||||
| 	driver.Stmt | ||||
| 	driver.StmtExecContext | ||||
| 	driver.StmtQueryContext | ||||
| } | ||||
| 
 | ||||
| // RowsIface is the driver.Rows interface | ||||
| // types (and the like) that go-sqlite3/driver.rows | ||||
| // conforms to. Useful so you don't need | ||||
| // to repeatedly perform checks yourself. | ||||
| type rowsIface interface { | ||||
| 	driver.Rows | ||||
| 	driver.RowsColumnTypeDatabaseTypeName | ||||
| } | ||||
							
								
								
									
										62
									
								
								internal/db/sqlite/errors.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								internal/db/sqlite/errors.go
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,62 @@ | |||
| // GoToSocial | ||||
| // Copyright (C) GoToSocial Authors admin@gotosocial.org | ||||
| // SPDX-License-Identifier: AGPL-3.0-or-later | ||||
| // | ||||
| // This program is free software: you can redistribute it and/or modify | ||||
| // it under the terms of the GNU Affero General Public License as published by | ||||
| // the Free Software Foundation, either version 3 of the License, or | ||||
| // (at your option) any later version. | ||||
| // | ||||
| // This program is distributed in the hope that it will be useful, | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| // GNU Affero General Public License for more details. | ||||
| // | ||||
| // You should have received a copy of the GNU Affero General Public License | ||||
| // along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
| 
 | ||||
| //go:build !wasmsqlite3 | ||||
| 
 | ||||
| package sqlite | ||||
| 
 | ||||
| import ( | ||||
| 	"database/sql/driver" | ||||
| 	"fmt" | ||||
| 
 | ||||
| 	"modernc.org/sqlite" | ||||
| 	sqlite3 "modernc.org/sqlite/lib" | ||||
| 
 | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db" | ||||
| ) | ||||
| 
 | ||||
| // processSQLiteError processes an sqlite3.Error to | ||||
| // handle conversion to any of our common db types. | ||||
| func processSQLiteError(err error) error { | ||||
| 	// Attempt to cast as sqlite error. | ||||
| 	sqliteErr, ok := err.(*sqlite.Error) | ||||
| 	if !ok { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	// Handle supplied error code: | ||||
| 	switch sqliteErr.Code() { | ||||
| 	case sqlite3.SQLITE_CONSTRAINT_UNIQUE, | ||||
| 		sqlite3.SQLITE_CONSTRAINT_PRIMARYKEY: | ||||
| 		return db.ErrAlreadyExists | ||||
| 
 | ||||
| 	// Busy should be very rare, but | ||||
| 	// on busy tell the database to close | ||||
| 	// the connection, re-open and re-attempt | ||||
| 	// which should give a necessary timeout. | ||||
| 	case sqlite3.SQLITE_BUSY, | ||||
| 		sqlite3.SQLITE_BUSY_RECOVERY, | ||||
| 		sqlite3.SQLITE_BUSY_SNAPSHOT: | ||||
| 		return driver.ErrBadConn | ||||
| 	} | ||||
| 
 | ||||
| 	// Wrap the returned error with the code and | ||||
| 	// extended code for easier debugging later. | ||||
| 	return fmt.Errorf("%w (code=%d)", err, | ||||
| 		sqliteErr.Code(), | ||||
| 	) | ||||
| } | ||||
							
								
								
									
										60
									
								
								internal/db/sqlite/errors_wasmsqlite3.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								internal/db/sqlite/errors_wasmsqlite3.go
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,60 @@ | |||
| // GoToSocial | ||||
| // Copyright (C) GoToSocial Authors admin@gotosocial.org | ||||
| // SPDX-License-Identifier: AGPL-3.0-or-later | ||||
| // | ||||
| // This program is free software: you can redistribute it and/or modify | ||||
| // it under the terms of the GNU Affero General Public License as published by | ||||
| // the Free Software Foundation, either version 3 of the License, or | ||||
| // (at your option) any later version. | ||||
| // | ||||
| // This program is distributed in the hope that it will be useful, | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| // GNU Affero General Public License for more details. | ||||
| // | ||||
| // You should have received a copy of the GNU Affero General Public License | ||||
| // along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
| 
 | ||||
| //go:build wasmsqlite3 | ||||
| 
 | ||||
| package sqlite | ||||
| 
 | ||||
| import ( | ||||
| 	"database/sql/driver" | ||||
| 	"fmt" | ||||
| 
 | ||||
| 	"github.com/ncruces/go-sqlite3" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db" | ||||
| ) | ||||
| 
 | ||||
| // processSQLiteError processes an sqlite3.Error to | ||||
| // handle conversion to any of our common db types. | ||||
| func processSQLiteError(err error) error { | ||||
| 	// Attempt to cast as sqlite error. | ||||
| 	sqliteErr, ok := err.(*sqlite3.Error) | ||||
| 	if !ok { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	// Handle supplied error code: | ||||
| 	switch sqliteErr.ExtendedCode() { | ||||
| 	case sqlite3.CONSTRAINT_UNIQUE, | ||||
| 		sqlite3.CONSTRAINT_PRIMARYKEY: | ||||
| 		return db.ErrAlreadyExists | ||||
| 
 | ||||
| 	// Busy should be very rare, but on | ||||
| 	// busy tell the database to close the | ||||
| 	// connection, re-open and re-attempt | ||||
| 	// which should give necessary timeout. | ||||
| 	case sqlite3.BUSY_RECOVERY, | ||||
| 		sqlite3.BUSY_SNAPSHOT: | ||||
| 		return driver.ErrBadConn | ||||
| 	} | ||||
| 
 | ||||
| 	// Wrap the returned error with the code and | ||||
| 	// extended code for easier debugging later. | ||||
| 	return fmt.Errorf("%w (code=%d extended=%d)", err, | ||||
| 		sqliteErr.Code(), | ||||
| 		sqliteErr.ExtendedCode(), | ||||
| 	) | ||||
| } | ||||
							
								
								
									
										35
									
								
								internal/db/util.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								internal/db/util.go
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,35 @@ | |||
| // GoToSocial | ||||
| // Copyright (C) GoToSocial Authors admin@gotosocial.org | ||||
| // SPDX-License-Identifier: AGPL-3.0-or-later | ||||
| // | ||||
| // This program is free software: you can redistribute it and/or modify | ||||
| // it under the terms of the GNU Affero General Public License as published by | ||||
| // the Free Software Foundation, either version 3 of the License, or | ||||
| // (at your option) any later version. | ||||
| // | ||||
| // This program is distributed in the hope that it will be useful, | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| // GNU Affero General Public License for more details. | ||||
| // | ||||
| // You should have received a copy of the GNU Affero General Public License | ||||
| // along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
| 
 | ||||
| package db | ||||
| 
 | ||||
| import "database/sql/driver" | ||||
| 
 | ||||
| // ToNamedValues converts older driver.Value types to driver.NamedValue types. | ||||
| func ToNamedValues(args []driver.Value) []driver.NamedValue { | ||||
| 	if args == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 	args2 := make([]driver.NamedValue, len(args)) | ||||
| 	for i := range args { | ||||
| 		args2[i] = driver.NamedValue{ | ||||
| 			Ordinal: i + 1, | ||||
| 			Value:   args[i], | ||||
| 		} | ||||
| 	} | ||||
| 	return args2 | ||||
| } | ||||
|  | @ -43,10 +43,6 @@ func TestMultiError(t *testing.T) { | |||
| 		t.Error("should be db.ErrAlreadyExists") | ||||
| 	} | ||||
| 
 | ||||
| 	if errors.Is(err, db.ErrBusyTimeout) { | ||||
| 		t.Error("should not be db.ErrBusyTimeout") | ||||
| 	} | ||||
| 
 | ||||
| 	errString := err.Error() | ||||
| 	expected := `sql: no rows in result set | ||||
| oopsie woopsie we did a fucky wucky etc | ||||
|  |  | |||
|  | @ -30,6 +30,7 @@ type PagingSuite struct { | |||
| } | ||||
| 
 | ||||
| func (suite *PagingSuite) TestPagingStandard() { | ||||
| 	config.SetProtocol("https") | ||||
| 	config.SetHost("example.org") | ||||
| 
 | ||||
| 	params := util.PageableResponseParams{ | ||||
|  | @ -52,6 +53,7 @@ func (suite *PagingSuite) TestPagingStandard() { | |||
| } | ||||
| 
 | ||||
| func (suite *PagingSuite) TestPagingNoLimit() { | ||||
| 	config.SetProtocol("https") | ||||
| 	config.SetHost("example.org") | ||||
| 
 | ||||
| 	params := util.PageableResponseParams{ | ||||
|  | @ -73,6 +75,7 @@ func (suite *PagingSuite) TestPagingNoLimit() { | |||
| } | ||||
| 
 | ||||
| func (suite *PagingSuite) TestPagingNoNextID() { | ||||
| 	config.SetProtocol("https") | ||||
| 	config.SetHost("example.org") | ||||
| 
 | ||||
| 	params := util.PageableResponseParams{ | ||||
|  | @ -94,6 +97,7 @@ func (suite *PagingSuite) TestPagingNoNextID() { | |||
| } | ||||
| 
 | ||||
| func (suite *PagingSuite) TestPagingNoPrevID() { | ||||
| 	config.SetProtocol("https") | ||||
| 	config.SetHost("example.org") | ||||
| 
 | ||||
| 	params := util.PageableResponseParams{ | ||||
|  | @ -115,6 +119,7 @@ func (suite *PagingSuite) TestPagingNoPrevID() { | |||
| } | ||||
| 
 | ||||
| func (suite *PagingSuite) TestPagingNoItems() { | ||||
| 	config.SetProtocol("https") | ||||
| 	config.SetHost("example.org") | ||||
| 
 | ||||
| 	params := util.PageableResponseParams{ | ||||
|  |  | |||
|  | @ -2,6 +2,11 @@ | |||
| 
 | ||||
| set -e | ||||
| 
 | ||||
| # Determine available docker binary | ||||
| _docker=$(command -v 'podman') || \ | ||||
| _docker=$(command -v 'docker') || \ | ||||
| { echo 'docker not found'; exit 1; } | ||||
| 
 | ||||
| # Ensure test args are set. | ||||
| ARGS=${@}; [ -z "$ARGS" ] && \ | ||||
| ARGS='./...' | ||||
|  | @ -10,33 +15,32 @@ ARGS='./...' | |||
| DB_NAME='postgres' | ||||
| DB_USER='postgres' | ||||
| DB_PASS='postgres' | ||||
| DB_IP='127.0.0.1' | ||||
| DB_PORT=5432 | ||||
| 
 | ||||
| # Start postgres container | ||||
| CID=$(docker run --detach \ | ||||
| CID=$($_docker run --detach \ | ||||
|     --publish "${DB_IP}:${DB_PORT}:${DB_PORT}" \ | ||||
|     --env "POSTGRES_DB=${DB_NAME}" \ | ||||
|     --env "POSTGRES_USER=${DB_USER}" \ | ||||
|     --env "POSTGRES_PASSWORD=${DB_PASS}" \ | ||||
|     --env "POSTGRES_HOST_AUTH_METHOD=trust" \ | ||||
|     --env "PGHOST=0.0.0.0" \ | ||||
|     --env "PGPORT=${DB_PORT}" \ | ||||
|     'postgres:latest') | ||||
|     'docker.io/postgres:latest') | ||||
| 
 | ||||
| # On exit kill the container | ||||
| trap "docker kill ${CID}" exit | ||||
| trap "$_docker kill ${CID}" exit | ||||
| 
 | ||||
| sleep 5 | ||||
| #docker exec "$CID" psql --user "$DB_USER" --password "$DB_PASS" -c "CREATE DATABASE \"${DB_NAME}\" WITH LOCALE \"C.UTF-8\" TEMPLATE \"template0\";" | ||||
| docker exec "$CID" psql --user "$DB_USER" --password "$DB_PASS" -c "GRANT ALL PRIVILEGES ON DATABASE \"${DB_NAME}\" TO \"${DB_USER}\";" | ||||
| 
 | ||||
| # Get running container IP | ||||
| IP=$(docker container inspect "${CID}" \ | ||||
|     --format '{{ .NetworkSettings.IPAddress }}') | ||||
| $_docker exec "$CID" psql --user "$DB_USER" --password "$DB_PASS" -c "GRANT ALL PRIVILEGES ON DATABASE \"${DB_NAME}\" TO \"${DB_USER}\";" | ||||
| 
 | ||||
| env \ | ||||
| GTS_DB_TYPE=postgres \ | ||||
| GTS_DB_ADDRESS=${IP} \ | ||||
| GTS_DB_ADDRESS=${DB_IP} \ | ||||
| GTS_DB_PORT=${DB_PORT} \ | ||||
| GTS_DB_USER=${DB_USER} \ | ||||
| GTS_DB_PASSWORD=${DB_PASS} \ | ||||
| GTS_DB_DATABASE=${DB_NAME} \ | ||||
| go test ./... -p 1 ${ARGS} | ||||
| go test -p 1 ${ARGS} | ||||
|  | @ -18,8 +18,8 @@ | |||
| package testrig | ||||
| 
 | ||||
| import ( | ||||
| 	"cmp" | ||||
| 	"os" | ||||
| 	"strconv" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"codeberg.org/gruf/go-bytesize" | ||||
|  | @ -28,15 +28,16 @@ import ( | |||
| 	"github.com/superseriousbusiness/gotosocial/internal/language" | ||||
| ) | ||||
| 
 | ||||
| // InitTestConfig initializes viper configuration with test defaults. | ||||
| // InitTestConfig initializes viper | ||||
| // configuration with test defaults. | ||||
| func InitTestConfig() { | ||||
| 	config.Config(func(cfg *config.Configuration) { | ||||
| 		*cfg = testDefaults | ||||
| 	}) | ||||
| 	config.Defaults = testDefaults() | ||||
| 	config.Reset() | ||||
| } | ||||
| 
 | ||||
| var testDefaults = config.Configuration{ | ||||
| 	LogLevel:           cmp.Or(os.Getenv("GTS_LOG_LEVEL"), "error"), | ||||
| func testDefaults() config.Configuration { | ||||
| 	return config.Configuration{ | ||||
| 		LogLevel:                 envStr("GTS_LOG_LEVEL", "error"), | ||||
| 		LogTimestampFormat:       "02/01/2006 15:04:05.000", | ||||
| 		LogDbQueries:             true, | ||||
| 		ApplicationName:          "gotosocial", | ||||
|  | @ -48,15 +49,14 @@ var testDefaults = config.Configuration{ | |||
| 		BindAddress:              "127.0.0.1", | ||||
| 		Port:                     8080, | ||||
| 		TrustedProxies:           []string{"127.0.0.1/32", "::1"}, | ||||
| 
 | ||||
| 	DbType:                   "sqlite", | ||||
| 	DbAddress:                ":memory:", | ||||
| 	DbPort:                   5432, | ||||
| 	DbUser:                   "postgres", | ||||
| 	DbPassword:               "postgres", | ||||
| 	DbDatabase:               "postgres", | ||||
| 	DbTLSMode:                "disable", | ||||
| 	DbTLSCACert:              "", | ||||
| 		DbType:                   envStr("GTS_DB_TYPE", "sqlite"), | ||||
| 		DbAddress:                envStr("GTS_DB_ADDRESS", ":memory:"), | ||||
| 		DbPort:                   envInt("GTS_DB_PORT", 0), | ||||
| 		DbUser:                   envStr("GTS_DB_USER", ""), | ||||
| 		DbPassword:               envStr("GTS_DB_PASSWORD", ""), | ||||
| 		DbDatabase:               envStr("GTS_DB_DATABASE", ""), | ||||
| 		DbTLSMode:                envStr("GTS_DB_TLS_MODE", ""), | ||||
| 		DbTLSCACert:              envStr("GTS_DB_TLS_CA_CERT", ""), | ||||
| 		DbMaxOpenConnsMultiplier: 8, | ||||
| 		DbSqliteJournalMode:      "WAL", | ||||
| 		DbSqliteSynchronous:      "NORMAL", | ||||
|  | @ -146,10 +146,31 @@ var testDefaults = config.Configuration{ | |||
| 		AdvancedRateLimitRequests:    0, // disabled | ||||
| 		AdvancedThrottlingMultiplier: 0, // disabled | ||||
| 		AdvancedSenderMultiplier:     0, // 1 sender only, regardless of CPU | ||||
| 	AdvancedHeaderFilterMode:     config.RequestHeaderFilterModeBlock, | ||||
| 
 | ||||
| 		SoftwareVersion: "0.0.0-testrig", | ||||
| 
 | ||||
| 		// simply use cache defaults. | ||||
| 		Cache: config.Defaults.Cache, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func envInt(key string, _default int) int { | ||||
| 	return env(key, _default, func(value string) int { | ||||
| 		i, _ := strconv.Atoi(value) | ||||
| 		return i | ||||
| 	}) | ||||
| } | ||||
| 
 | ||||
| func envStr(key string, _default string) string { | ||||
| 	return env(key, _default, func(value string) string { | ||||
| 		return value | ||||
| 	}) | ||||
| } | ||||
| 
 | ||||
| func env[T any](key string, _default T, parse func(string) T) T { | ||||
| 	value, ok := os.LookupEnv(key) | ||||
| 	if ok { | ||||
| 		return parse(value) | ||||
| 	} | ||||
| 	return _default | ||||
| } | ||||
|  |  | |||
|  | @ -19,10 +19,7 @@ package testrig | |||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"os" | ||||
| 	"strconv" | ||||
| 
 | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/config" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db/bundb" | ||||
| 	"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" | ||||
|  | @ -84,22 +81,6 @@ var testModels = []interface{}{ | |||
| // If the environment variable GTS_DB_PORT is set, it will take that | ||||
| // value as the port instead. | ||||
| func NewTestDB(state *state.State) db.DB { | ||||
| 	if alternateAddress := os.Getenv("GTS_DB_ADDRESS"); alternateAddress != "" { | ||||
| 		config.SetDbAddress(alternateAddress) | ||||
| 	} | ||||
| 
 | ||||
| 	if alternateDBType := os.Getenv("GTS_DB_TYPE"); alternateDBType != "" { | ||||
| 		config.SetDbType(alternateDBType) | ||||
| 	} | ||||
| 
 | ||||
| 	if alternateDBPort := os.Getenv("GTS_DB_PORT"); alternateDBPort != "" { | ||||
| 		port, err := strconv.ParseUint(alternateDBPort, 10, 16) | ||||
| 		if err != nil { | ||||
| 			panic(err) | ||||
| 		} | ||||
| 		config.SetDbPort(int(port)) | ||||
| 	} | ||||
| 
 | ||||
| 	state.Caches.Init() | ||||
| 
 | ||||
| 	testDB, err := bundb.NewBunDBService(context.Background(), state) | ||||
|  | @ -374,9 +355,10 @@ func StandardDBTeardown(db db.DB) { | |||
| 	if db == nil { | ||||
| 		return | ||||
| 	} | ||||
| 	defer db.Close() | ||||
| 	for _, m := range testModels { | ||||
| 		if err := db.DropTable(ctx, m); err != nil { | ||||
| 			log.Panic(nil, err) | ||||
| 			log.Error(ctx, err) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  |  | |||
							
								
								
									
										16
									
								
								vendor/github.com/ncruces/go-sqlite3/.gitignore
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								vendor/github.com/ncruces/go-sqlite3/.gitignore
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,16 @@ | |||
| # Binaries for programs and plugins | ||||
| *.exe | ||||
| *.exe~ | ||||
| *.dll | ||||
| *.so | ||||
| *.dylib | ||||
| 
 | ||||
| # Test binary, built with `go test -c` | ||||
| *.test | ||||
| 
 | ||||
| # Output of the go coverage tool, specifically when used with LiteIDE | ||||
| *.out | ||||
| 
 | ||||
| # Dependency directories (remove the comment below to include it) | ||||
| # vendor/ | ||||
| tools | ||||
							
								
								
									
										21
									
								
								vendor/github.com/ncruces/go-sqlite3/LICENSE
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								vendor/github.com/ncruces/go-sqlite3/LICENSE
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,21 @@ | |||
| MIT License | ||||
| 
 | ||||
| Copyright (c) 2023 Nuno Cruces | ||||
| 
 | ||||
| Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
| of this software and associated documentation files (the "Software"), to deal | ||||
| in the Software without restriction, including without limitation the rights | ||||
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
| copies of the Software, and to permit persons to whom the Software is | ||||
| furnished to do so, subject to the following conditions: | ||||
| 
 | ||||
| The above copyright notice and this permission notice shall be included in all | ||||
| copies or substantial portions of the Software. | ||||
| 
 | ||||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||
| SOFTWARE. | ||||
							
								
								
									
										113
									
								
								vendor/github.com/ncruces/go-sqlite3/README.md
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										113
									
								
								vendor/github.com/ncruces/go-sqlite3/README.md
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,113 @@ | |||
| # Go bindings to SQLite using Wazero | ||||
| 
 | ||||
| [](https://pkg.go.dev/github.com/ncruces/go-sqlite3) | ||||
| [](https://goreportcard.com/report/github.com/ncruces/go-sqlite3) | ||||
| [](https://github.com/ncruces/go-sqlite3/wiki/Test-coverage-report) | ||||
| 
 | ||||
| Go module `github.com/ncruces/go-sqlite3` is a `cgo`-free [SQLite](https://sqlite.org/) wrapper.\ | ||||
| It provides a [`database/sql`](https://pkg.go.dev/database/sql) compatible driver, | ||||
| as well as direct access to most of the [C SQLite API](https://sqlite.org/cintro.html). | ||||
| 
 | ||||
| It wraps a [Wasm](https://webassembly.org/) [build](embed/) of SQLite, | ||||
| and uses [wazero](https://wazero.io/) as the runtime.\ | ||||
| Go, wazero and [`x/sys`](https://pkg.go.dev/golang.org/x/sys) are the _only_ runtime dependencies [^1]. | ||||
| 
 | ||||
| ### Packages | ||||
| 
 | ||||
| - [`github.com/ncruces/go-sqlite3`](https://pkg.go.dev/github.com/ncruces/go-sqlite3) | ||||
|   wraps the [C SQLite API](https://sqlite.org/cintro.html) | ||||
|   ([example usage](https://pkg.go.dev/github.com/ncruces/go-sqlite3#example-package)). | ||||
| - [`github.com/ncruces/go-sqlite3/driver`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/driver) | ||||
|   provides a [`database/sql`](https://pkg.go.dev/database/sql) driver | ||||
|   ([example usage](https://pkg.go.dev/github.com/ncruces/go-sqlite3/driver#example-package)). | ||||
| - [`github.com/ncruces/go-sqlite3/embed`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/embed) | ||||
|   embeds a build of SQLite into your application. | ||||
| - [`github.com/ncruces/go-sqlite3/vfs`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/vfs) | ||||
|   wraps the [C SQLite VFS API](https://sqlite.org/vfs.html) and provides a pure Go implementation. | ||||
| - [`github.com/ncruces/go-sqlite3/gormlite`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/gormlite) | ||||
|   provides a [GORM](https://gorm.io) driver. | ||||
| 
 | ||||
| ### Extensions | ||||
| 
 | ||||
| - [`github.com/ncruces/go-sqlite3/ext/array`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/ext/array) | ||||
|   provides the [`array`](https://sqlite.org/carray.html) table-valued function. | ||||
| - [`github.com/ncruces/go-sqlite3/ext/blobio`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/ext/blobio) | ||||
|   simplifies [incremental BLOB I/O](https://sqlite.org/c3ref/blob_open.html). | ||||
| - [`github.com/ncruces/go-sqlite3/ext/csv`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/ext/csv) | ||||
|   reads [comma-separated values](https://sqlite.org/csv.html). | ||||
| - [`github.com/ncruces/go-sqlite3/ext/fileio`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/ext/fileio) | ||||
|   reads, writes and lists files. | ||||
| - [`github.com/ncruces/go-sqlite3/ext/hash`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/ext/hash) | ||||
|   provides cryptographic hash functions. | ||||
| - [`github.com/ncruces/go-sqlite3/ext/lines`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/ext/lines) | ||||
|   reads data [line-by-line](https://github.com/asg017/sqlite-lines). | ||||
| - [`github.com/ncruces/go-sqlite3/ext/pivot`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/ext/pivot) | ||||
|   creates [pivot tables](https://github.com/jakethaw/pivot_vtab). | ||||
| - [`github.com/ncruces/go-sqlite3/ext/statement`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/ext/statement) | ||||
|   creates [parameterized views](https://github.com/0x09/sqlite-statement-vtab). | ||||
| - [`github.com/ncruces/go-sqlite3/ext/stats`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/ext/stats) | ||||
|   provides [statistics](https://www.oreilly.com/library/view/sql-in-a/9780596155322/ch04s02.html) functions. | ||||
| - [`github.com/ncruces/go-sqlite3/ext/unicode`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/ext/unicode) | ||||
|   provides [Unicode aware](https://sqlite.org/src/dir/ext/icu) functions. | ||||
| - [`github.com/ncruces/go-sqlite3/ext/zorder`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/ext/zorder) | ||||
|   maps multidimensional data to one dimension. | ||||
| - [`github.com/ncruces/go-sqlite3/vfs/memdb`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/vfs/memdb) | ||||
|   implements an in-memory VFS. | ||||
| - [`github.com/ncruces/go-sqlite3/vfs/readervfs`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/vfs/readervfs) | ||||
|   implements a VFS for immutable databases. | ||||
| - [`github.com/ncruces/go-sqlite3/vfs/adiantum`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/vfs/adiantum) | ||||
|   wraps a VFS to offer encryption at rest. | ||||
| 
 | ||||
| ### Advanced features | ||||
| 
 | ||||
| - [incremental BLOB I/O](https://sqlite.org/c3ref/blob_open.html) | ||||
| - [nested transactions](https://sqlite.org/lang_savepoint.html) | ||||
| - [custom functions](https://sqlite.org/c3ref/create_function.html) | ||||
| - [virtual tables](https://sqlite.org/vtab.html) | ||||
| - [custom VFSes](https://sqlite.org/vfs.html) | ||||
| - [online backup](https://sqlite.org/backup.html) | ||||
| - [JSON support](https://sqlite.org/json1.html) | ||||
| - [math functions](https://sqlite.org/lang_mathfunc.html) | ||||
| - [full-text search](https://sqlite.org/fts5.html) | ||||
| - [geospatial search](https://sqlite.org/geopoly.html) | ||||
| - [encryption at rest](vfs/adiantum/README.md) | ||||
| - [and more…](embed/README.md) | ||||
| 
 | ||||
| ### Caveats | ||||
| 
 | ||||
| This module replaces the SQLite [OS Interface](https://sqlite.org/vfs.html) | ||||
| (aka VFS) with a [pure Go](vfs/) implementation, | ||||
| which has advantages and disadvantages. | ||||
| 
 | ||||
| Read more about the Go VFS design [here](vfs/README.md). | ||||
| 
 | ||||
| ### Testing | ||||
| 
 | ||||
| This project aims for [high test coverage](https://github.com/ncruces/go-sqlite3/wiki/Test-coverage-report). | ||||
| It also benefits greatly from [SQLite's](https://sqlite.org/testing.html) and | ||||
| [wazero's](https://tetrate.io/blog/introducing-wazero-from-tetrate/#:~:text=Rock%2Dsolid%20test%20approach) thorough testing. | ||||
| 
 | ||||
| Every commit is [tested](.github/workflows/test.yml) on | ||||
| Linux (amd64/arm64/386/riscv64/s390x), macOS (amd64/arm64), | ||||
| Windows (amd64), FreeBSD (amd64), illumos (amd64), and Solaris (amd64). | ||||
| 
 | ||||
| The Go VFS is tested by running SQLite's | ||||
| [mptest](https://github.com/sqlite/sqlite/blob/master/mptest/mptest.c). | ||||
| 
 | ||||
| ### Performance | ||||
| 
 | ||||
| Perfomance of the [`database/sql`](https://pkg.go.dev/database/sql) driver is | ||||
| [competitive](https://github.com/cvilsmeier/go-sqlite-bench) with alternatives. | ||||
| 
 | ||||
| The Wasm and VFS layers are also tested by running SQLite's | ||||
| [speedtest1](https://github.com/sqlite/sqlite/blob/master/test/speedtest1.c). | ||||
| 
 | ||||
| ### Alternatives | ||||
| 
 | ||||
| - [`modernc.org/sqlite`](https://pkg.go.dev/modernc.org/sqlite) | ||||
| - [`crawshaw.io/sqlite`](https://pkg.go.dev/crawshaw.io/sqlite) | ||||
| - [`github.com/mattn/go-sqlite3`](https://pkg.go.dev/github.com/mattn/go-sqlite3) | ||||
| - [`github.com/zombiezen/go-sqlite`](https://pkg.go.dev/github.com/zombiezen/go-sqlite) | ||||
| 
 | ||||
| [^1]: anything else you find in `go.mod` is either a test dependency, | ||||
|       or needed by one of the extensions. | ||||
							
								
								
									
										134
									
								
								vendor/github.com/ncruces/go-sqlite3/backup.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										134
									
								
								vendor/github.com/ncruces/go-sqlite3/backup.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,134 @@ | |||
| package sqlite3 | ||||
| 
 | ||||
| // Backup is an handle to an ongoing online backup operation. | ||||
| // | ||||
| // https://sqlite.org/c3ref/backup.html | ||||
| type Backup struct { | ||||
| 	c      *Conn | ||||
| 	handle uint32 | ||||
| 	otherc uint32 | ||||
| } | ||||
| 
 | ||||
| // Backup backs up srcDB on the src connection to the "main" database in dstURI. | ||||
| // | ||||
| // Backup opens the SQLite database file dstURI, | ||||
| // and blocks until the entire backup is complete. | ||||
| // Use [Conn.BackupInit] for incremental backup. | ||||
| // | ||||
| // https://sqlite.org/backup.html | ||||
| func (src *Conn) Backup(srcDB, dstURI string) error { | ||||
| 	b, err := src.BackupInit(srcDB, dstURI) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	defer b.Close() | ||||
| 	_, err = b.Step(-1) | ||||
| 	return err | ||||
| } | ||||
| 
 | ||||
| // Restore restores dstDB on the dst connection from the "main" database in srcURI. | ||||
| // | ||||
| // Restore opens the SQLite database file srcURI, | ||||
| // and blocks until the entire restore is complete. | ||||
| // | ||||
| // https://sqlite.org/backup.html | ||||
| func (dst *Conn) Restore(dstDB, srcURI string) error { | ||||
| 	src, err := dst.openDB(srcURI, OPEN_READONLY|OPEN_URI) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	b, err := dst.backupInit(dst.handle, dstDB, src, "main") | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	defer b.Close() | ||||
| 	_, err = b.Step(-1) | ||||
| 	return err | ||||
| } | ||||
| 
 | ||||
| // BackupInit initializes a backup operation to copy the content of one database into another. | ||||
| // | ||||
| // BackupInit opens the SQLite database file dstURI, | ||||
| // then initializes a backup that copies the contents of srcDB on the src connection | ||||
| // to the "main" database in dstURI. | ||||
| // | ||||
| // https://sqlite.org/c3ref/backup_finish.html#sqlite3backupinit | ||||
| func (src *Conn) BackupInit(srcDB, dstURI string) (*Backup, error) { | ||||
| 	dst, err := src.openDB(dstURI, OPEN_READWRITE|OPEN_CREATE|OPEN_URI) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return src.backupInit(dst, "main", src.handle, srcDB) | ||||
| } | ||||
| 
 | ||||
| func (c *Conn) backupInit(dst uint32, dstName string, src uint32, srcName string) (*Backup, error) { | ||||
| 	defer c.arena.mark()() | ||||
| 	dstPtr := c.arena.string(dstName) | ||||
| 	srcPtr := c.arena.string(srcName) | ||||
| 
 | ||||
| 	other := dst | ||||
| 	if c.handle == dst { | ||||
| 		other = src | ||||
| 	} | ||||
| 
 | ||||
| 	r := c.call("sqlite3_backup_init", | ||||
| 		uint64(dst), uint64(dstPtr), | ||||
| 		uint64(src), uint64(srcPtr)) | ||||
| 	if r == 0 { | ||||
| 		defer c.closeDB(other) | ||||
| 		r = c.call("sqlite3_errcode", uint64(dst)) | ||||
| 		return nil, c.sqlite.error(r, dst) | ||||
| 	} | ||||
| 
 | ||||
| 	return &Backup{ | ||||
| 		c:      c, | ||||
| 		otherc: other, | ||||
| 		handle: uint32(r), | ||||
| 	}, nil | ||||
| } | ||||
| 
 | ||||
| // Close finishes a backup operation. | ||||
| // | ||||
| // It is safe to close a nil, zero or closed Backup. | ||||
| // | ||||
| // https://sqlite.org/c3ref/backup_finish.html#sqlite3backupfinish | ||||
| func (b *Backup) Close() error { | ||||
| 	if b == nil || b.handle == 0 { | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	r := b.c.call("sqlite3_backup_finish", uint64(b.handle)) | ||||
| 	b.c.closeDB(b.otherc) | ||||
| 	b.handle = 0 | ||||
| 	return b.c.error(r) | ||||
| } | ||||
| 
 | ||||
| // Step copies up to nPage pages between the source and destination databases. | ||||
| // If nPage is negative, all remaining source pages are copied. | ||||
| // | ||||
| // https://sqlite.org/c3ref/backup_finish.html#sqlite3backupstep | ||||
| func (b *Backup) Step(nPage int) (done bool, err error) { | ||||
| 	r := b.c.call("sqlite3_backup_step", uint64(b.handle), uint64(nPage)) | ||||
| 	if r == _DONE { | ||||
| 		return true, nil | ||||
| 	} | ||||
| 	return false, b.c.error(r) | ||||
| } | ||||
| 
 | ||||
| // Remaining returns the number of pages still to be backed up | ||||
| // at the conclusion of the most recent [Backup.Step]. | ||||
| // | ||||
| // https://sqlite.org/c3ref/backup_finish.html#sqlite3backupremaining | ||||
| func (b *Backup) Remaining() int { | ||||
| 	r := b.c.call("sqlite3_backup_remaining", uint64(b.handle)) | ||||
| 	return int(int32(r)) | ||||
| } | ||||
| 
 | ||||
| // PageCount returns the total number of pages in the source database | ||||
| // at the conclusion of the most recent [Backup.Step]. | ||||
| // | ||||
| // https://sqlite.org/c3ref/backup_finish.html#sqlite3backuppagecount | ||||
| func (b *Backup) PageCount() int { | ||||
| 	r := b.c.call("sqlite3_backup_pagecount", uint64(b.handle)) | ||||
| 	return int(int32(r)) | ||||
| } | ||||
							
								
								
									
										250
									
								
								vendor/github.com/ncruces/go-sqlite3/blob.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										250
									
								
								vendor/github.com/ncruces/go-sqlite3/blob.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,250 @@ | |||
| package sqlite3 | ||||
| 
 | ||||
| import ( | ||||
| 	"io" | ||||
| 
 | ||||
| 	"github.com/ncruces/go-sqlite3/internal/util" | ||||
| ) | ||||
| 
 | ||||
| // ZeroBlob represents a zero-filled, length n BLOB | ||||
| // that can be used as an argument to | ||||
| // [database/sql.DB.Exec] and similar methods. | ||||
| type ZeroBlob int64 | ||||
| 
 | ||||
| // Blob is an handle to an open BLOB. | ||||
| // | ||||
| // It implements [io.ReadWriteSeeker] for incremental BLOB I/O. | ||||
| // | ||||
| // https://sqlite.org/c3ref/blob.html | ||||
| type Blob struct { | ||||
| 	c      *Conn | ||||
| 	bytes  int64 | ||||
| 	offset int64 | ||||
| 	handle uint32 | ||||
| } | ||||
| 
 | ||||
| var _ io.ReadWriteSeeker = &Blob{} | ||||
| 
 | ||||
| // OpenBlob opens a BLOB for incremental I/O. | ||||
| // | ||||
| // https://sqlite.org/c3ref/blob_open.html | ||||
| func (c *Conn) OpenBlob(db, table, column string, row int64, write bool) (*Blob, error) { | ||||
| 	c.checkInterrupt() | ||||
| 	defer c.arena.mark()() | ||||
| 	blobPtr := c.arena.new(ptrlen) | ||||
| 	dbPtr := c.arena.string(db) | ||||
| 	tablePtr := c.arena.string(table) | ||||
| 	columnPtr := c.arena.string(column) | ||||
| 
 | ||||
| 	var flags uint64 | ||||
| 	if write { | ||||
| 		flags = 1 | ||||
| 	} | ||||
| 
 | ||||
| 	r := c.call("sqlite3_blob_open", uint64(c.handle), | ||||
| 		uint64(dbPtr), uint64(tablePtr), uint64(columnPtr), | ||||
| 		uint64(row), flags, uint64(blobPtr)) | ||||
| 
 | ||||
| 	if err := c.error(r); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	blob := Blob{c: c} | ||||
| 	blob.handle = util.ReadUint32(c.mod, blobPtr) | ||||
| 	blob.bytes = int64(c.call("sqlite3_blob_bytes", uint64(blob.handle))) | ||||
| 	return &blob, nil | ||||
| } | ||||
| 
 | ||||
| // Close closes a BLOB handle. | ||||
| // | ||||
| // It is safe to close a nil, zero or closed Blob. | ||||
| // | ||||
| // https://sqlite.org/c3ref/blob_close.html | ||||
| func (b *Blob) Close() error { | ||||
| 	if b == nil || b.handle == 0 { | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	r := b.c.call("sqlite3_blob_close", uint64(b.handle)) | ||||
| 
 | ||||
| 	b.handle = 0 | ||||
| 	return b.c.error(r) | ||||
| } | ||||
| 
 | ||||
| // Size returns the size of the BLOB in bytes. | ||||
| // | ||||
| // https://sqlite.org/c3ref/blob_bytes.html | ||||
| func (b *Blob) Size() int64 { | ||||
| 	return b.bytes | ||||
| } | ||||
| 
 | ||||
| // Read implements the [io.Reader] interface. | ||||
| // | ||||
| // https://sqlite.org/c3ref/blob_read.html | ||||
| func (b *Blob) Read(p []byte) (n int, err error) { | ||||
| 	if b.offset >= b.bytes { | ||||
| 		return 0, io.EOF | ||||
| 	} | ||||
| 
 | ||||
| 	avail := b.bytes - b.offset | ||||
| 	want := int64(len(p)) | ||||
| 	if want > avail { | ||||
| 		want = avail | ||||
| 	} | ||||
| 
 | ||||
| 	defer b.c.arena.mark()() | ||||
| 	ptr := b.c.arena.new(uint64(want)) | ||||
| 
 | ||||
| 	r := b.c.call("sqlite3_blob_read", uint64(b.handle), | ||||
| 		uint64(ptr), uint64(want), uint64(b.offset)) | ||||
| 	err = b.c.error(r) | ||||
| 	if err != nil { | ||||
| 		return 0, err | ||||
| 	} | ||||
| 	b.offset += want | ||||
| 	if b.offset >= b.bytes { | ||||
| 		err = io.EOF | ||||
| 	} | ||||
| 
 | ||||
| 	copy(p, util.View(b.c.mod, ptr, uint64(want))) | ||||
| 	return int(want), err | ||||
| } | ||||
| 
 | ||||
| // WriteTo implements the [io.WriterTo] interface. | ||||
| // | ||||
| // https://sqlite.org/c3ref/blob_read.html | ||||
| func (b *Blob) WriteTo(w io.Writer) (n int64, err error) { | ||||
| 	if b.offset >= b.bytes { | ||||
| 		return 0, nil | ||||
| 	} | ||||
| 
 | ||||
| 	want := int64(1024 * 1024) | ||||
| 	avail := b.bytes - b.offset | ||||
| 	if want > avail { | ||||
| 		want = avail | ||||
| 	} | ||||
| 
 | ||||
| 	defer b.c.arena.mark()() | ||||
| 	ptr := b.c.arena.new(uint64(want)) | ||||
| 
 | ||||
| 	for want > 0 { | ||||
| 		r := b.c.call("sqlite3_blob_read", uint64(b.handle), | ||||
| 			uint64(ptr), uint64(want), uint64(b.offset)) | ||||
| 		err = b.c.error(r) | ||||
| 		if err != nil { | ||||
| 			return n, err | ||||
| 		} | ||||
| 
 | ||||
| 		mem := util.View(b.c.mod, ptr, uint64(want)) | ||||
| 		m, err := w.Write(mem[:want]) | ||||
| 		b.offset += int64(m) | ||||
| 		n += int64(m) | ||||
| 		if err != nil { | ||||
| 			return n, err | ||||
| 		} | ||||
| 		if int64(m) != want { | ||||
| 			return n, io.ErrShortWrite | ||||
| 		} | ||||
| 
 | ||||
| 		avail = b.bytes - b.offset | ||||
| 		if want > avail { | ||||
| 			want = avail | ||||
| 		} | ||||
| 	} | ||||
| 	return n, nil | ||||
| } | ||||
| 
 | ||||
| // Write implements the [io.Writer] interface. | ||||
| // | ||||
| // https://sqlite.org/c3ref/blob_write.html | ||||
| func (b *Blob) Write(p []byte) (n int, err error) { | ||||
| 	defer b.c.arena.mark()() | ||||
| 	ptr := b.c.arena.bytes(p) | ||||
| 
 | ||||
| 	r := b.c.call("sqlite3_blob_write", uint64(b.handle), | ||||
| 		uint64(ptr), uint64(len(p)), uint64(b.offset)) | ||||
| 	err = b.c.error(r) | ||||
| 	if err != nil { | ||||
| 		return 0, err | ||||
| 	} | ||||
| 	b.offset += int64(len(p)) | ||||
| 	return len(p), nil | ||||
| } | ||||
| 
 | ||||
| // ReadFrom implements the [io.ReaderFrom] interface. | ||||
| // | ||||
| // https://sqlite.org/c3ref/blob_write.html | ||||
| func (b *Blob) ReadFrom(r io.Reader) (n int64, err error) { | ||||
| 	want := int64(1024 * 1024) | ||||
| 	avail := b.bytes - b.offset | ||||
| 	if l, ok := r.(*io.LimitedReader); ok && want > l.N { | ||||
| 		want = l.N | ||||
| 	} | ||||
| 	if want > avail { | ||||
| 		want = avail | ||||
| 	} | ||||
| 	if want < 1 { | ||||
| 		want = 1 | ||||
| 	} | ||||
| 
 | ||||
| 	defer b.c.arena.mark()() | ||||
| 	ptr := b.c.arena.new(uint64(want)) | ||||
| 
 | ||||
| 	for { | ||||
| 		mem := util.View(b.c.mod, ptr, uint64(want)) | ||||
| 		m, err := r.Read(mem[:want]) | ||||
| 		if m > 0 { | ||||
| 			r := b.c.call("sqlite3_blob_write", uint64(b.handle), | ||||
| 				uint64(ptr), uint64(m), uint64(b.offset)) | ||||
| 			err := b.c.error(r) | ||||
| 			if err != nil { | ||||
| 				return n, err | ||||
| 			} | ||||
| 			b.offset += int64(m) | ||||
| 			n += int64(m) | ||||
| 		} | ||||
| 		if err == io.EOF { | ||||
| 			return n, nil | ||||
| 		} | ||||
| 		if err != nil { | ||||
| 			return n, err | ||||
| 		} | ||||
| 
 | ||||
| 		avail = b.bytes - b.offset | ||||
| 		if want > avail { | ||||
| 			want = avail | ||||
| 		} | ||||
| 		if want < 1 { | ||||
| 			want = 1 | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Seek implements the [io.Seeker] interface. | ||||
| func (b *Blob) Seek(offset int64, whence int) (int64, error) { | ||||
| 	switch whence { | ||||
| 	default: | ||||
| 		return 0, util.WhenceErr | ||||
| 	case io.SeekStart: | ||||
| 		break | ||||
| 	case io.SeekCurrent: | ||||
| 		offset += b.offset | ||||
| 	case io.SeekEnd: | ||||
| 		offset += b.bytes | ||||
| 	} | ||||
| 	if offset < 0 { | ||||
| 		return 0, util.OffsetErr | ||||
| 	} | ||||
| 	b.offset = offset | ||||
| 	return offset, nil | ||||
| } | ||||
| 
 | ||||
| // Reopen moves a BLOB handle to a new row of the same database table. | ||||
| // | ||||
| // https://sqlite.org/c3ref/blob_reopen.html | ||||
| func (b *Blob) Reopen(row int64) error { | ||||
| 	err := b.c.error(b.c.call("sqlite3_blob_reopen", uint64(b.handle), uint64(row))) | ||||
| 	b.bytes = int64(b.c.call("sqlite3_blob_bytes", uint64(b.handle))) | ||||
| 	b.offset = 0 | ||||
| 	return err | ||||
| } | ||||
							
								
								
									
										164
									
								
								vendor/github.com/ncruces/go-sqlite3/config.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										164
									
								
								vendor/github.com/ncruces/go-sqlite3/config.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,164 @@ | |||
| package sqlite3 | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 
 | ||||
| 	"github.com/ncruces/go-sqlite3/internal/util" | ||||
| 	"github.com/tetratelabs/wazero/api" | ||||
| ) | ||||
| 
 | ||||
| // Config makes configuration changes to a database connection. | ||||
| // Only boolean configuration options are supported. | ||||
| // Called with no arg reads the current configuration value, | ||||
| // called with one arg sets and returns the new value. | ||||
| // | ||||
| // https://sqlite.org/c3ref/db_config.html | ||||
| func (c *Conn) Config(op DBConfig, arg ...bool) (bool, error) { | ||||
| 	defer c.arena.mark()() | ||||
| 	argsPtr := c.arena.new(2 * ptrlen) | ||||
| 
 | ||||
| 	var flag int | ||||
| 	switch { | ||||
| 	case len(arg) == 0: | ||||
| 		flag = -1 | ||||
| 	case arg[0]: | ||||
| 		flag = 1 | ||||
| 	} | ||||
| 
 | ||||
| 	util.WriteUint32(c.mod, argsPtr+0*ptrlen, uint32(flag)) | ||||
| 	util.WriteUint32(c.mod, argsPtr+1*ptrlen, argsPtr) | ||||
| 
 | ||||
| 	r := c.call("sqlite3_db_config", uint64(c.handle), | ||||
| 		uint64(op), uint64(argsPtr)) | ||||
| 	return util.ReadUint32(c.mod, argsPtr) != 0, c.error(r) | ||||
| } | ||||
| 
 | ||||
| // ConfigLog sets up the error logging callback for the connection. | ||||
| // | ||||
| // https://sqlite.org/errlog.html | ||||
| func (c *Conn) ConfigLog(cb func(code ExtendedErrorCode, msg string)) error { | ||||
| 	var enable uint64 | ||||
| 	if cb != nil { | ||||
| 		enable = 1 | ||||
| 	} | ||||
| 	r := c.call("sqlite3_config_log_go", enable) | ||||
| 	if err := c.error(r); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	c.log = cb | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func logCallback(ctx context.Context, mod api.Module, _, iCode, zMsg uint32) { | ||||
| 	if c, ok := ctx.Value(connKey{}).(*Conn); ok && c.log != nil { | ||||
| 		msg := util.ReadString(mod, zMsg, _MAX_LENGTH) | ||||
| 		c.log(xErrorCode(iCode), msg) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Limit allows the size of various constructs to be | ||||
| // limited on a connection by connection basis. | ||||
| // | ||||
| // https://sqlite.org/c3ref/limit.html | ||||
| func (c *Conn) Limit(id LimitCategory, value int) int { | ||||
| 	r := c.call("sqlite3_limit", uint64(c.handle), uint64(id), uint64(value)) | ||||
| 	return int(int32(r)) | ||||
| } | ||||
| 
 | ||||
| // SetAuthorizer registers an authorizer callback with the database connection. | ||||
| // | ||||
| // https://sqlite.org/c3ref/set_authorizer.html | ||||
| func (c *Conn) SetAuthorizer(cb func(action AuthorizerActionCode, name3rd, name4th, schema, nameInner string) AuthorizerReturnCode) error { | ||||
| 	var enable uint64 | ||||
| 	if cb != nil { | ||||
| 		enable = 1 | ||||
| 	} | ||||
| 	r := c.call("sqlite3_set_authorizer_go", uint64(c.handle), enable) | ||||
| 	if err := c.error(r); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	c.authorizer = cb | ||||
| 	return nil | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| func authorizerCallback(ctx context.Context, mod api.Module, pDB uint32, action AuthorizerActionCode, zName3rd, zName4th, zSchema, zNameInner uint32) (rc AuthorizerReturnCode) { | ||||
| 	if c, ok := ctx.Value(connKey{}).(*Conn); ok && c.handle == pDB && c.authorizer != nil { | ||||
| 		var name3rd, name4th, schema, nameInner string | ||||
| 		if zName3rd != 0 { | ||||
| 			name3rd = util.ReadString(mod, zName3rd, _MAX_NAME) | ||||
| 		} | ||||
| 		if zName4th != 0 { | ||||
| 			name4th = util.ReadString(mod, zName4th, _MAX_NAME) | ||||
| 		} | ||||
| 		if zSchema != 0 { | ||||
| 			schema = util.ReadString(mod, zSchema, _MAX_NAME) | ||||
| 		} | ||||
| 		if zNameInner != 0 { | ||||
| 			nameInner = util.ReadString(mod, zNameInner, _MAX_NAME) | ||||
| 		} | ||||
| 		rc = c.authorizer(action, name3rd, name4th, schema, nameInner) | ||||
| 	} | ||||
| 	return rc | ||||
| } | ||||
| 
 | ||||
| // WalCheckpoint checkpoints a WAL database. | ||||
| // | ||||
| // https://sqlite.org/c3ref/wal_checkpoint_v2.html | ||||
| func (c *Conn) WalCheckpoint(schema string, mode CheckpointMode) (nLog, nCkpt int, err error) { | ||||
| 	defer c.arena.mark()() | ||||
| 	nLogPtr := c.arena.new(ptrlen) | ||||
| 	nCkptPtr := c.arena.new(ptrlen) | ||||
| 	schemaPtr := c.arena.string(schema) | ||||
| 	r := c.call("sqlite3_wal_checkpoint_v2", | ||||
| 		uint64(c.handle), uint64(schemaPtr), uint64(mode), | ||||
| 		uint64(nLogPtr), uint64(nCkptPtr)) | ||||
| 	nLog = int(int32(util.ReadUint32(c.mod, nLogPtr))) | ||||
| 	nCkpt = int(int32(util.ReadUint32(c.mod, nCkptPtr))) | ||||
| 	return nLog, nCkpt, c.error(r) | ||||
| } | ||||
| 
 | ||||
| // WalAutoCheckpoint configures WAL auto-checkpoints. | ||||
| // | ||||
| // https://sqlite.org/c3ref/wal_autocheckpoint.html | ||||
| func (c *Conn) WalAutoCheckpoint(pages int) error { | ||||
| 	r := c.call("sqlite3_wal_autocheckpoint", uint64(c.handle), uint64(pages)) | ||||
| 	return c.error(r) | ||||
| } | ||||
| 
 | ||||
| // WalHook registers a callback function to be invoked | ||||
| // each time data is committed to a database in WAL mode. | ||||
| // | ||||
| // https://sqlite.org/c3ref/wal_hook.html | ||||
| func (c *Conn) WalHook(cb func(db *Conn, schema string, pages int) error) { | ||||
| 	var enable uint64 | ||||
| 	if cb != nil { | ||||
| 		enable = 1 | ||||
| 	} | ||||
| 	c.call("sqlite3_wal_hook_go", uint64(c.handle), enable) | ||||
| 	c.wal = cb | ||||
| } | ||||
| 
 | ||||
| func walCallback(ctx context.Context, mod api.Module, _, pDB, zSchema uint32, pages int32) (rc uint32) { | ||||
| 	if c, ok := ctx.Value(connKey{}).(*Conn); ok && c.handle == pDB && c.wal != nil { | ||||
| 		schema := util.ReadString(mod, zSchema, _MAX_NAME) | ||||
| 		err := c.wal(c, schema, int(pages)) | ||||
| 		_, rc = errorCode(err, ERROR) | ||||
| 	} | ||||
| 	return rc | ||||
| } | ||||
| 
 | ||||
| // AutoVacuumPages registers a autovacuum compaction amount callback. | ||||
| // | ||||
| // https://sqlite.org/c3ref/autovacuum_pages.html | ||||
| func (c *Conn) AutoVacuumPages(cb func(schema string, dbPages, freePages, bytesPerPage uint) uint) error { | ||||
| 	funcPtr := util.AddHandle(c.ctx, cb) | ||||
| 	r := c.call("sqlite3_autovacuum_pages_go", uint64(c.handle), uint64(funcPtr)) | ||||
| 	return c.error(r) | ||||
| } | ||||
| 
 | ||||
| func autoVacuumCallback(ctx context.Context, mod api.Module, pApp, zSchema, nDbPage, nFreePage, nBytePerPage uint32) uint32 { | ||||
| 	fn := util.GetHandle(ctx, pApp).(func(schema string, dbPages, freePages, bytesPerPage uint) uint) | ||||
| 	schema := util.ReadString(mod, zSchema, _MAX_NAME) | ||||
| 	return uint32(fn(schema, uint(nDbPage), uint(nFreePage), uint(nBytePerPage))) | ||||
| } | ||||
							
								
								
									
										426
									
								
								vendor/github.com/ncruces/go-sqlite3/conn.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										426
									
								
								vendor/github.com/ncruces/go-sqlite3/conn.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,426 @@ | |||
| package sqlite3 | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"fmt" | ||||
| 	"math" | ||||
| 	"net/url" | ||||
| 	"strings" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/ncruces/go-sqlite3/internal/util" | ||||
| 	"github.com/ncruces/go-sqlite3/vfs" | ||||
| 	"github.com/tetratelabs/wazero/api" | ||||
| ) | ||||
| 
 | ||||
| // Conn is a database connection handle. | ||||
| // A Conn is not safe for concurrent use by multiple goroutines. | ||||
| // | ||||
| // https://sqlite.org/c3ref/sqlite3.html | ||||
| type Conn struct { | ||||
| 	*sqlite | ||||
| 
 | ||||
| 	interrupt  context.Context | ||||
| 	pending    *Stmt | ||||
| 	busy       func(int) bool | ||||
| 	log        func(xErrorCode, string) | ||||
| 	collation  func(*Conn, string) | ||||
| 	authorizer func(AuthorizerActionCode, string, string, string, string) AuthorizerReturnCode | ||||
| 	update     func(AuthorizerActionCode, string, string, int64) | ||||
| 	commit     func() bool | ||||
| 	rollback   func() | ||||
| 	wal        func(*Conn, string, int) error | ||||
| 	arena      arena | ||||
| 
 | ||||
| 	handle uint32 | ||||
| } | ||||
| 
 | ||||
| // Open calls [OpenFlags] with [OPEN_READWRITE], [OPEN_CREATE], [OPEN_URI] and [OPEN_NOFOLLOW]. | ||||
| func Open(filename string) (*Conn, error) { | ||||
| 	return newConn(filename, OPEN_READWRITE|OPEN_CREATE|OPEN_URI|OPEN_NOFOLLOW) | ||||
| } | ||||
| 
 | ||||
| // OpenFlags opens an SQLite database file as specified by the filename argument. | ||||
| // | ||||
| // If none of the required flags is used, a combination of [OPEN_READWRITE] and [OPEN_CREATE] is used. | ||||
| // If a URI filename is used, PRAGMA statements to execute can be specified using "_pragma": | ||||
| // | ||||
| //	sqlite3.Open("file:demo.db?_pragma=busy_timeout(10000)") | ||||
| // | ||||
| // https://sqlite.org/c3ref/open.html | ||||
| func OpenFlags(filename string, flags OpenFlag) (*Conn, error) { | ||||
| 	if flags&(OPEN_READONLY|OPEN_READWRITE|OPEN_CREATE) == 0 { | ||||
| 		flags |= OPEN_READWRITE | OPEN_CREATE | ||||
| 	} | ||||
| 	return newConn(filename, flags) | ||||
| } | ||||
| 
 | ||||
| type connKey struct{} | ||||
| 
 | ||||
| func newConn(filename string, flags OpenFlag) (conn *Conn, err error) { | ||||
| 	sqlite, err := instantiateSQLite() | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	defer func() { | ||||
| 		if conn == nil { | ||||
| 			sqlite.close() | ||||
| 		} | ||||
| 	}() | ||||
| 
 | ||||
| 	c := &Conn{sqlite: sqlite} | ||||
| 	c.arena = c.newArena(1024) | ||||
| 	c.ctx = context.WithValue(c.ctx, connKey{}, c) | ||||
| 	c.handle, err = c.openDB(filename, flags) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return c, nil | ||||
| } | ||||
| 
 | ||||
| func (c *Conn) openDB(filename string, flags OpenFlag) (uint32, error) { | ||||
| 	defer c.arena.mark()() | ||||
| 	connPtr := c.arena.new(ptrlen) | ||||
| 	namePtr := c.arena.string(filename) | ||||
| 
 | ||||
| 	flags |= OPEN_EXRESCODE | ||||
| 	r := c.call("sqlite3_open_v2", uint64(namePtr), uint64(connPtr), uint64(flags), 0) | ||||
| 
 | ||||
| 	handle := util.ReadUint32(c.mod, connPtr) | ||||
| 	if err := c.sqlite.error(r, handle); err != nil { | ||||
| 		c.closeDB(handle) | ||||
| 		return 0, err | ||||
| 	} | ||||
| 
 | ||||
| 	if flags|OPEN_URI != 0 && strings.HasPrefix(filename, "file:") { | ||||
| 		var pragmas strings.Builder | ||||
| 		if _, after, ok := strings.Cut(filename, "?"); ok { | ||||
| 			query, _ := url.ParseQuery(after) | ||||
| 			for _, p := range query["_pragma"] { | ||||
| 				pragmas.WriteString(`PRAGMA `) | ||||
| 				pragmas.WriteString(p) | ||||
| 				pragmas.WriteString(`;`) | ||||
| 			} | ||||
| 		} | ||||
| 		if pragmas.Len() != 0 { | ||||
| 			pragmaPtr := c.arena.string(pragmas.String()) | ||||
| 			r := c.call("sqlite3_exec", uint64(handle), uint64(pragmaPtr), 0, 0, 0) | ||||
| 			if err := c.sqlite.error(r, handle, pragmas.String()); err != nil { | ||||
| 				err = fmt.Errorf("sqlite3: invalid _pragma: %w", err) | ||||
| 				c.closeDB(handle) | ||||
| 				return 0, err | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	c.call("sqlite3_progress_handler_go", uint64(handle), 100) | ||||
| 	return handle, nil | ||||
| } | ||||
| 
 | ||||
| func (c *Conn) closeDB(handle uint32) { | ||||
| 	r := c.call("sqlite3_close_v2", uint64(handle)) | ||||
| 	if err := c.sqlite.error(r, handle); err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Close closes the database connection. | ||||
| // | ||||
| // If the database connection is associated with unfinalized prepared statements, | ||||
| // open blob handles, and/or unfinished backup objects, | ||||
| // Close will leave the database connection open and return [BUSY]. | ||||
| // | ||||
| // It is safe to close a nil, zero or closed Conn. | ||||
| // | ||||
| // https://sqlite.org/c3ref/close.html | ||||
| func (c *Conn) Close() error { | ||||
| 	if c == nil || c.handle == 0 { | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	c.pending.Close() | ||||
| 	c.pending = nil | ||||
| 
 | ||||
| 	r := c.call("sqlite3_close", uint64(c.handle)) | ||||
| 	if err := c.error(r); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	c.handle = 0 | ||||
| 	return c.close() | ||||
| } | ||||
| 
 | ||||
| // Exec is a convenience function that allows an application to run | ||||
| // multiple statements of SQL without having to use a lot of code. | ||||
| // | ||||
| // https://sqlite.org/c3ref/exec.html | ||||
| func (c *Conn) Exec(sql string) error { | ||||
| 	c.checkInterrupt() | ||||
| 	defer c.arena.mark()() | ||||
| 	sqlPtr := c.arena.string(sql) | ||||
| 
 | ||||
| 	r := c.call("sqlite3_exec", uint64(c.handle), uint64(sqlPtr), 0, 0, 0) | ||||
| 	return c.error(r, sql) | ||||
| } | ||||
| 
 | ||||
| // Prepare calls [Conn.PrepareFlags] with no flags. | ||||
| func (c *Conn) Prepare(sql string) (stmt *Stmt, tail string, err error) { | ||||
| 	return c.PrepareFlags(sql, 0) | ||||
| } | ||||
| 
 | ||||
| // PrepareFlags compiles the first SQL statement in sql; | ||||
| // tail is left pointing to what remains uncompiled. | ||||
| // If the input text contains no SQL (if the input is an empty string or a comment), | ||||
| // both stmt and err will be nil. | ||||
| // | ||||
| // https://sqlite.org/c3ref/prepare.html | ||||
| func (c *Conn) PrepareFlags(sql string, flags PrepareFlag) (stmt *Stmt, tail string, err error) { | ||||
| 	if len(sql) > _MAX_SQL_LENGTH { | ||||
| 		return nil, "", TOOBIG | ||||
| 	} | ||||
| 
 | ||||
| 	defer c.arena.mark()() | ||||
| 	stmtPtr := c.arena.new(ptrlen) | ||||
| 	tailPtr := c.arena.new(ptrlen) | ||||
| 	sqlPtr := c.arena.string(sql) | ||||
| 
 | ||||
| 	r := c.call("sqlite3_prepare_v3", uint64(c.handle), | ||||
| 		uint64(sqlPtr), uint64(len(sql)+1), uint64(flags), | ||||
| 		uint64(stmtPtr), uint64(tailPtr)) | ||||
| 
 | ||||
| 	stmt = &Stmt{c: c} | ||||
| 	stmt.handle = util.ReadUint32(c.mod, stmtPtr) | ||||
| 	if sql := sql[util.ReadUint32(c.mod, tailPtr)-sqlPtr:]; sql != "" { | ||||
| 		tail = sql | ||||
| 	} | ||||
| 
 | ||||
| 	if err := c.error(r, sql); err != nil { | ||||
| 		return nil, "", err | ||||
| 	} | ||||
| 	if stmt.handle == 0 { | ||||
| 		return nil, "", nil | ||||
| 	} | ||||
| 	return stmt, tail, nil | ||||
| } | ||||
| 
 | ||||
| // DBName returns the schema name for n-th database on the database connection. | ||||
| // | ||||
| // https://sqlite.org/c3ref/db_name.html | ||||
| func (c *Conn) DBName(n int) string { | ||||
| 	r := c.call("sqlite3_db_name", uint64(c.handle), uint64(n)) | ||||
| 
 | ||||
| 	ptr := uint32(r) | ||||
| 	if ptr == 0 { | ||||
| 		return "" | ||||
| 	} | ||||
| 	return util.ReadString(c.mod, ptr, _MAX_NAME) | ||||
| } | ||||
| 
 | ||||
| // Filename returns the filename for a database. | ||||
| // | ||||
| // https://sqlite.org/c3ref/db_filename.html | ||||
| func (c *Conn) Filename(schema string) *vfs.Filename { | ||||
| 	var ptr uint32 | ||||
| 	if schema != "" { | ||||
| 		defer c.arena.mark()() | ||||
| 		ptr = c.arena.string(schema) | ||||
| 	} | ||||
| 
 | ||||
| 	r := c.call("sqlite3_db_filename", uint64(c.handle), uint64(ptr)) | ||||
| 	return vfs.OpenFilename(c.ctx, c.mod, uint32(r), vfs.OPEN_MAIN_DB) | ||||
| } | ||||
| 
 | ||||
| // ReadOnly determines if a database is read-only. | ||||
| // | ||||
| // https://sqlite.org/c3ref/db_readonly.html | ||||
| func (c *Conn) ReadOnly(schema string) (ro bool, ok bool) { | ||||
| 	var ptr uint32 | ||||
| 	if schema != "" { | ||||
| 		defer c.arena.mark()() | ||||
| 		ptr = c.arena.string(schema) | ||||
| 	} | ||||
| 	r := c.call("sqlite3_db_readonly", uint64(c.handle), uint64(ptr)) | ||||
| 	return int32(r) > 0, int32(r) < 0 | ||||
| } | ||||
| 
 | ||||
| // GetAutocommit tests the connection for auto-commit mode. | ||||
| // | ||||
| // https://sqlite.org/c3ref/get_autocommit.html | ||||
| func (c *Conn) GetAutocommit() bool { | ||||
| 	r := c.call("sqlite3_get_autocommit", uint64(c.handle)) | ||||
| 	return r != 0 | ||||
| } | ||||
| 
 | ||||
| // LastInsertRowID returns the rowid of the most recent successful INSERT | ||||
| // on the database connection. | ||||
| // | ||||
| // https://sqlite.org/c3ref/last_insert_rowid.html | ||||
| func (c *Conn) LastInsertRowID() int64 { | ||||
| 	r := c.call("sqlite3_last_insert_rowid", uint64(c.handle)) | ||||
| 	return int64(r) | ||||
| } | ||||
| 
 | ||||
| // SetLastInsertRowID allows the application to set the value returned by | ||||
| // [Conn.LastInsertRowID]. | ||||
| // | ||||
| // https://sqlite.org/c3ref/set_last_insert_rowid.html | ||||
| func (c *Conn) SetLastInsertRowID(id int64) { | ||||
| 	c.call("sqlite3_set_last_insert_rowid", uint64(c.handle), uint64(id)) | ||||
| } | ||||
| 
 | ||||
| // Changes returns the number of rows modified, inserted or deleted | ||||
| // by the most recently completed INSERT, UPDATE or DELETE statement | ||||
| // on the database connection. | ||||
| // | ||||
| // https://sqlite.org/c3ref/changes.html | ||||
| func (c *Conn) Changes() int64 { | ||||
| 	r := c.call("sqlite3_changes64", uint64(c.handle)) | ||||
| 	return int64(r) | ||||
| } | ||||
| 
 | ||||
| // TotalChanges returns the number of rows modified, inserted or deleted | ||||
| // by all INSERT, UPDATE or DELETE statements completed | ||||
| // since the database connection was opened. | ||||
| // | ||||
| // https://sqlite.org/c3ref/total_changes.html | ||||
| func (c *Conn) TotalChanges() int64 { | ||||
| 	r := c.call("sqlite3_total_changes64", uint64(c.handle)) | ||||
| 	return int64(r) | ||||
| } | ||||
| 
 | ||||
| // ReleaseMemory frees memory used by a database connection. | ||||
| // | ||||
| // https://sqlite.org/c3ref/db_release_memory.html | ||||
| func (c *Conn) ReleaseMemory() error { | ||||
| 	r := c.call("sqlite3_db_release_memory", uint64(c.handle)) | ||||
| 	return c.error(r) | ||||
| } | ||||
| 
 | ||||
| // GetInterrupt gets the context set with [Conn.SetInterrupt], | ||||
| // or nil if none was set. | ||||
| func (c *Conn) GetInterrupt() context.Context { | ||||
| 	return c.interrupt | ||||
| } | ||||
| 
 | ||||
| // SetInterrupt interrupts a long-running query when a context is done. | ||||
| // | ||||
| // Subsequent uses of the connection will return [INTERRUPT] | ||||
| // until the context is reset by another call to SetInterrupt. | ||||
| // | ||||
| // To associate a timeout with a connection: | ||||
| // | ||||
| //	ctx, cancel := context.WithTimeout(context.TODO(), 100*time.Millisecond) | ||||
| //	conn.SetInterrupt(ctx) | ||||
| //	defer cancel() | ||||
| // | ||||
| // SetInterrupt returns the old context assigned to the connection. | ||||
| // | ||||
| // https://sqlite.org/c3ref/interrupt.html | ||||
| func (c *Conn) SetInterrupt(ctx context.Context) (old context.Context) { | ||||
| 	// Is it the same context? | ||||
| 	if ctx == c.interrupt { | ||||
| 		return ctx | ||||
| 	} | ||||
| 
 | ||||
| 	// A busy SQL statement prevents SQLite from ignoring an interrupt | ||||
| 	// that comes before any other statements are started. | ||||
| 	if c.pending == nil { | ||||
| 		c.pending, _, _ = c.Prepare(`WITH RECURSIVE c(x) AS (VALUES(0) UNION ALL SELECT x FROM c) SELECT x FROM c`) | ||||
| 	} | ||||
| 
 | ||||
| 	old = c.interrupt | ||||
| 	c.interrupt = ctx | ||||
| 
 | ||||
| 	if old != nil && old.Done() != nil && (ctx == nil || ctx.Err() == nil) { | ||||
| 		c.pending.Reset() | ||||
| 	} | ||||
| 	if ctx != nil && ctx.Done() != nil { | ||||
| 		c.pending.Step() | ||||
| 	} | ||||
| 	return old | ||||
| } | ||||
| 
 | ||||
| func (c *Conn) checkInterrupt() { | ||||
| 	if c.interrupt != nil && c.interrupt.Err() != nil { | ||||
| 		c.call("sqlite3_interrupt", uint64(c.handle)) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func progressCallback(ctx context.Context, mod api.Module, pDB uint32) (interrupt uint32) { | ||||
| 	if c, ok := ctx.Value(connKey{}).(*Conn); ok && c.handle == pDB && | ||||
| 		c.interrupt != nil && c.interrupt.Err() != nil { | ||||
| 		interrupt = 1 | ||||
| 	} | ||||
| 	return interrupt | ||||
| } | ||||
| 
 | ||||
| // BusyTimeout sets a busy timeout. | ||||
| // | ||||
| // https://sqlite.org/c3ref/busy_timeout.html | ||||
| func (c *Conn) BusyTimeout(timeout time.Duration) error { | ||||
| 	ms := min((timeout+time.Millisecond-1)/time.Millisecond, math.MaxInt32) | ||||
| 	r := c.call("sqlite3_busy_timeout", uint64(c.handle), uint64(ms)) | ||||
| 	return c.error(r) | ||||
| } | ||||
| 
 | ||||
| func timeoutCallback(ctx context.Context, mod api.Module, pDB uint32, count, tmout int32) (retry uint32) { | ||||
| 	if c, ok := ctx.Value(connKey{}).(*Conn); ok && | ||||
| 		(c.interrupt == nil || c.interrupt.Err() == nil) { | ||||
| 		const delays = "\x01\x02\x05\x0a\x0f\x14\x19\x19\x19\x32\x32\x64" | ||||
| 		const totals = "\x00\x01\x03\x08\x12\x21\x35\x4e\x67\x80\xb2\xe4" | ||||
| 		const ndelay = int32(len(delays) - 1) | ||||
| 
 | ||||
| 		var delay, prior int32 | ||||
| 		if count <= ndelay { | ||||
| 			delay = int32(delays[count]) | ||||
| 			prior = int32(totals[count]) | ||||
| 		} else { | ||||
| 			delay = int32(delays[ndelay]) | ||||
| 			prior = int32(totals[ndelay]) + delay*(count-ndelay) | ||||
| 		} | ||||
| 
 | ||||
| 		if delay = min(delay, tmout-prior); delay > 0 { | ||||
| 			time.Sleep(time.Duration(delay) * time.Millisecond) | ||||
| 			retry = 1 | ||||
| 		} | ||||
| 	} | ||||
| 	return retry | ||||
| } | ||||
| 
 | ||||
| // BusyHandler registers a callback to handle [BUSY] errors. | ||||
| // | ||||
| // https://sqlite.org/c3ref/busy_handler.html | ||||
| func (c *Conn) BusyHandler(cb func(count int) (retry bool)) error { | ||||
| 	var enable uint64 | ||||
| 	if cb != nil { | ||||
| 		enable = 1 | ||||
| 	} | ||||
| 	r := c.call("sqlite3_busy_handler_go", uint64(c.handle), enable) | ||||
| 	if err := c.error(r); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	c.busy = cb | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func busyCallback(ctx context.Context, mod api.Module, pDB uint32, count int32) (retry uint32) { | ||||
| 	if c, ok := ctx.Value(connKey{}).(*Conn); ok && c.handle == pDB && c.busy != nil && | ||||
| 		(c.interrupt == nil || c.interrupt.Err() == nil) { | ||||
| 		if c.busy(int(count)) { | ||||
| 			retry = 1 | ||||
| 		} | ||||
| 	} | ||||
| 	return retry | ||||
| } | ||||
| 
 | ||||
| func (c *Conn) error(rc uint64, sql ...string) error { | ||||
| 	return c.sqlite.error(rc, c.handle, sql...) | ||||
| } | ||||
| 
 | ||||
| // DriverConn is implemented by the SQLite [database/sql] driver connection. | ||||
| // | ||||
| // It can be used to access SQLite features like [online backup]. | ||||
| // | ||||
| // [online backup]: https://sqlite.org/backup.html | ||||
| type DriverConn interface { | ||||
| 	Raw() *Conn | ||||
| } | ||||
							
								
								
									
										360
									
								
								vendor/github.com/ncruces/go-sqlite3/const.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										360
									
								
								vendor/github.com/ncruces/go-sqlite3/const.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,360 @@ | |||
| package sqlite3 | ||||
| 
 | ||||
| import "strconv" | ||||
| 
 | ||||
| const ( | ||||
| 	_OK   = 0   /* Successful result */ | ||||
| 	_ROW  = 100 /* sqlite3_step() has another row ready */ | ||||
| 	_DONE = 101 /* sqlite3_step() has finished executing */ | ||||
| 
 | ||||
| 	_UTF8 = 1 | ||||
| 
 | ||||
| 	_MAX_NAME            = 1e6 // Self-imposed limit for most NUL terminated strings. | ||||
| 	_MAX_LENGTH          = 1e9 | ||||
| 	_MAX_SQL_LENGTH      = 1e9 | ||||
| 	_MAX_ALLOCATION_SIZE = 0x7ffffeff | ||||
| 	_MAX_FUNCTION_ARG    = 100 | ||||
| 
 | ||||
| 	ptrlen = 4 | ||||
| ) | ||||
| 
 | ||||
| // ErrorCode is a result code that [Error.Code] might return. | ||||
| // | ||||
| // https://sqlite.org/rescode.html | ||||
| type ErrorCode uint8 | ||||
| 
 | ||||
| const ( | ||||
| 	ERROR      ErrorCode = 1  /* Generic error */ | ||||
| 	INTERNAL   ErrorCode = 2  /* Internal logic error in SQLite */ | ||||
| 	PERM       ErrorCode = 3  /* Access permission denied */ | ||||
| 	ABORT      ErrorCode = 4  /* Callback routine requested an abort */ | ||||
| 	BUSY       ErrorCode = 5  /* The database file is locked */ | ||||
| 	LOCKED     ErrorCode = 6  /* A table in the database is locked */ | ||||
| 	NOMEM      ErrorCode = 7  /* A malloc() failed */ | ||||
| 	READONLY   ErrorCode = 8  /* Attempt to write a readonly database */ | ||||
| 	INTERRUPT  ErrorCode = 9  /* Operation terminated by sqlite3_interrupt() */ | ||||
| 	IOERR      ErrorCode = 10 /* Some kind of disk I/O error occurred */ | ||||
| 	CORRUPT    ErrorCode = 11 /* The database disk image is malformed */ | ||||
| 	NOTFOUND   ErrorCode = 12 /* Unknown opcode in sqlite3_file_control() */ | ||||
| 	FULL       ErrorCode = 13 /* Insertion failed because database is full */ | ||||
| 	CANTOPEN   ErrorCode = 14 /* Unable to open the database file */ | ||||
| 	PROTOCOL   ErrorCode = 15 /* Database lock protocol error */ | ||||
| 	EMPTY      ErrorCode = 16 /* Internal use only */ | ||||
| 	SCHEMA     ErrorCode = 17 /* The database schema changed */ | ||||
| 	TOOBIG     ErrorCode = 18 /* String or BLOB exceeds size limit */ | ||||
| 	CONSTRAINT ErrorCode = 19 /* Abort due to constraint violation */ | ||||
| 	MISMATCH   ErrorCode = 20 /* Data type mismatch */ | ||||
| 	MISUSE     ErrorCode = 21 /* Library used incorrectly */ | ||||
| 	NOLFS      ErrorCode = 22 /* Uses OS features not supported on host */ | ||||
| 	AUTH       ErrorCode = 23 /* Authorization denied */ | ||||
| 	FORMAT     ErrorCode = 24 /* Not used */ | ||||
| 	RANGE      ErrorCode = 25 /* 2nd parameter to sqlite3_bind out of range */ | ||||
| 	NOTADB     ErrorCode = 26 /* File opened that is not a database file */ | ||||
| 	NOTICE     ErrorCode = 27 /* Notifications from sqlite3_log() */ | ||||
| 	WARNING    ErrorCode = 28 /* Warnings from sqlite3_log() */ | ||||
| ) | ||||
| 
 | ||||
| // ExtendedErrorCode is a result code that [Error.ExtendedCode] might return. | ||||
| // | ||||
| // https://sqlite.org/rescode.html | ||||
| type ( | ||||
| 	ExtendedErrorCode uint16 | ||||
| 	xErrorCode        = ExtendedErrorCode | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
| 	ERROR_MISSING_COLLSEQ   ExtendedErrorCode = xErrorCode(ERROR) | (1 << 8) | ||||
| 	ERROR_RETRY             ExtendedErrorCode = xErrorCode(ERROR) | (2 << 8) | ||||
| 	ERROR_SNAPSHOT          ExtendedErrorCode = xErrorCode(ERROR) | (3 << 8) | ||||
| 	IOERR_READ              ExtendedErrorCode = xErrorCode(IOERR) | (1 << 8) | ||||
| 	IOERR_SHORT_READ        ExtendedErrorCode = xErrorCode(IOERR) | (2 << 8) | ||||
| 	IOERR_WRITE             ExtendedErrorCode = xErrorCode(IOERR) | (3 << 8) | ||||
| 	IOERR_FSYNC             ExtendedErrorCode = xErrorCode(IOERR) | (4 << 8) | ||||
| 	IOERR_DIR_FSYNC         ExtendedErrorCode = xErrorCode(IOERR) | (5 << 8) | ||||
| 	IOERR_TRUNCATE          ExtendedErrorCode = xErrorCode(IOERR) | (6 << 8) | ||||
| 	IOERR_FSTAT             ExtendedErrorCode = xErrorCode(IOERR) | (7 << 8) | ||||
| 	IOERR_UNLOCK            ExtendedErrorCode = xErrorCode(IOERR) | (8 << 8) | ||||
| 	IOERR_RDLOCK            ExtendedErrorCode = xErrorCode(IOERR) | (9 << 8) | ||||
| 	IOERR_DELETE            ExtendedErrorCode = xErrorCode(IOERR) | (10 << 8) | ||||
| 	IOERR_BLOCKED           ExtendedErrorCode = xErrorCode(IOERR) | (11 << 8) | ||||
| 	IOERR_NOMEM             ExtendedErrorCode = xErrorCode(IOERR) | (12 << 8) | ||||
| 	IOERR_ACCESS            ExtendedErrorCode = xErrorCode(IOERR) | (13 << 8) | ||||
| 	IOERR_CHECKRESERVEDLOCK ExtendedErrorCode = xErrorCode(IOERR) | (14 << 8) | ||||
| 	IOERR_LOCK              ExtendedErrorCode = xErrorCode(IOERR) | (15 << 8) | ||||
| 	IOERR_CLOSE             ExtendedErrorCode = xErrorCode(IOERR) | (16 << 8) | ||||
| 	IOERR_DIR_CLOSE         ExtendedErrorCode = xErrorCode(IOERR) | (17 << 8) | ||||
| 	IOERR_SHMOPEN           ExtendedErrorCode = xErrorCode(IOERR) | (18 << 8) | ||||
| 	IOERR_SHMSIZE           ExtendedErrorCode = xErrorCode(IOERR) | (19 << 8) | ||||
| 	IOERR_SHMLOCK           ExtendedErrorCode = xErrorCode(IOERR) | (20 << 8) | ||||
| 	IOERR_SHMMAP            ExtendedErrorCode = xErrorCode(IOERR) | (21 << 8) | ||||
| 	IOERR_SEEK              ExtendedErrorCode = xErrorCode(IOERR) | (22 << 8) | ||||
| 	IOERR_DELETE_NOENT      ExtendedErrorCode = xErrorCode(IOERR) | (23 << 8) | ||||
| 	IOERR_MMAP              ExtendedErrorCode = xErrorCode(IOERR) | (24 << 8) | ||||
| 	IOERR_GETTEMPPATH       ExtendedErrorCode = xErrorCode(IOERR) | (25 << 8) | ||||
| 	IOERR_CONVPATH          ExtendedErrorCode = xErrorCode(IOERR) | (26 << 8) | ||||
| 	IOERR_VNODE             ExtendedErrorCode = xErrorCode(IOERR) | (27 << 8) | ||||
| 	IOERR_AUTH              ExtendedErrorCode = xErrorCode(IOERR) | (28 << 8) | ||||
| 	IOERR_BEGIN_ATOMIC      ExtendedErrorCode = xErrorCode(IOERR) | (29 << 8) | ||||
| 	IOERR_COMMIT_ATOMIC     ExtendedErrorCode = xErrorCode(IOERR) | (30 << 8) | ||||
| 	IOERR_ROLLBACK_ATOMIC   ExtendedErrorCode = xErrorCode(IOERR) | (31 << 8) | ||||
| 	IOERR_DATA              ExtendedErrorCode = xErrorCode(IOERR) | (32 << 8) | ||||
| 	IOERR_CORRUPTFS         ExtendedErrorCode = xErrorCode(IOERR) | (33 << 8) | ||||
| 	IOERR_IN_PAGE           ExtendedErrorCode = xErrorCode(IOERR) | (34 << 8) | ||||
| 	LOCKED_SHAREDCACHE      ExtendedErrorCode = xErrorCode(LOCKED) | (1 << 8) | ||||
| 	LOCKED_VTAB             ExtendedErrorCode = xErrorCode(LOCKED) | (2 << 8) | ||||
| 	BUSY_RECOVERY           ExtendedErrorCode = xErrorCode(BUSY) | (1 << 8) | ||||
| 	BUSY_SNAPSHOT           ExtendedErrorCode = xErrorCode(BUSY) | (2 << 8) | ||||
| 	BUSY_TIMEOUT            ExtendedErrorCode = xErrorCode(BUSY) | (3 << 8) | ||||
| 	CANTOPEN_NOTEMPDIR      ExtendedErrorCode = xErrorCode(CANTOPEN) | (1 << 8) | ||||
| 	CANTOPEN_ISDIR          ExtendedErrorCode = xErrorCode(CANTOPEN) | (2 << 8) | ||||
| 	CANTOPEN_FULLPATH       ExtendedErrorCode = xErrorCode(CANTOPEN) | (3 << 8) | ||||
| 	CANTOPEN_CONVPATH       ExtendedErrorCode = xErrorCode(CANTOPEN) | (4 << 8) | ||||
| 	CANTOPEN_DIRTYWAL       ExtendedErrorCode = xErrorCode(CANTOPEN) | (5 << 8) /* Not Used */ | ||||
| 	CANTOPEN_SYMLINK        ExtendedErrorCode = xErrorCode(CANTOPEN) | (6 << 8) | ||||
| 	CORRUPT_VTAB            ExtendedErrorCode = xErrorCode(CORRUPT) | (1 << 8) | ||||
| 	CORRUPT_SEQUENCE        ExtendedErrorCode = xErrorCode(CORRUPT) | (2 << 8) | ||||
| 	CORRUPT_INDEX           ExtendedErrorCode = xErrorCode(CORRUPT) | (3 << 8) | ||||
| 	READONLY_RECOVERY       ExtendedErrorCode = xErrorCode(READONLY) | (1 << 8) | ||||
| 	READONLY_CANTLOCK       ExtendedErrorCode = xErrorCode(READONLY) | (2 << 8) | ||||
| 	READONLY_ROLLBACK       ExtendedErrorCode = xErrorCode(READONLY) | (3 << 8) | ||||
| 	READONLY_DBMOVED        ExtendedErrorCode = xErrorCode(READONLY) | (4 << 8) | ||||
| 	READONLY_CANTINIT       ExtendedErrorCode = xErrorCode(READONLY) | (5 << 8) | ||||
| 	READONLY_DIRECTORY      ExtendedErrorCode = xErrorCode(READONLY) | (6 << 8) | ||||
| 	ABORT_ROLLBACK          ExtendedErrorCode = xErrorCode(ABORT) | (2 << 8) | ||||
| 	CONSTRAINT_CHECK        ExtendedErrorCode = xErrorCode(CONSTRAINT) | (1 << 8) | ||||
| 	CONSTRAINT_COMMITHOOK   ExtendedErrorCode = xErrorCode(CONSTRAINT) | (2 << 8) | ||||
| 	CONSTRAINT_FOREIGNKEY   ExtendedErrorCode = xErrorCode(CONSTRAINT) | (3 << 8) | ||||
| 	CONSTRAINT_FUNCTION     ExtendedErrorCode = xErrorCode(CONSTRAINT) | (4 << 8) | ||||
| 	CONSTRAINT_NOTNULL      ExtendedErrorCode = xErrorCode(CONSTRAINT) | (5 << 8) | ||||
| 	CONSTRAINT_PRIMARYKEY   ExtendedErrorCode = xErrorCode(CONSTRAINT) | (6 << 8) | ||||
| 	CONSTRAINT_TRIGGER      ExtendedErrorCode = xErrorCode(CONSTRAINT) | (7 << 8) | ||||
| 	CONSTRAINT_UNIQUE       ExtendedErrorCode = xErrorCode(CONSTRAINT) | (8 << 8) | ||||
| 	CONSTRAINT_VTAB         ExtendedErrorCode = xErrorCode(CONSTRAINT) | (9 << 8) | ||||
| 	CONSTRAINT_ROWID        ExtendedErrorCode = xErrorCode(CONSTRAINT) | (10 << 8) | ||||
| 	CONSTRAINT_PINNED       ExtendedErrorCode = xErrorCode(CONSTRAINT) | (11 << 8) | ||||
| 	CONSTRAINT_DATATYPE     ExtendedErrorCode = xErrorCode(CONSTRAINT) | (12 << 8) | ||||
| 	NOTICE_RECOVER_WAL      ExtendedErrorCode = xErrorCode(NOTICE) | (1 << 8) | ||||
| 	NOTICE_RECOVER_ROLLBACK ExtendedErrorCode = xErrorCode(NOTICE) | (2 << 8) | ||||
| 	NOTICE_RBU              ExtendedErrorCode = xErrorCode(NOTICE) | (3 << 8) | ||||
| 	WARNING_AUTOINDEX       ExtendedErrorCode = xErrorCode(WARNING) | (1 << 8) | ||||
| 	AUTH_USER               ExtendedErrorCode = xErrorCode(AUTH) | (1 << 8) | ||||
| ) | ||||
| 
 | ||||
| // OpenFlag is a flag for the [OpenFlags] function. | ||||
| // | ||||
| // https://sqlite.org/c3ref/c_open_autoproxy.html | ||||
| type OpenFlag uint32 | ||||
| 
 | ||||
| const ( | ||||
| 	OPEN_READONLY     OpenFlag = 0x00000001 /* Ok for sqlite3_open_v2() */ | ||||
| 	OPEN_READWRITE    OpenFlag = 0x00000002 /* Ok for sqlite3_open_v2() */ | ||||
| 	OPEN_CREATE       OpenFlag = 0x00000004 /* Ok for sqlite3_open_v2() */ | ||||
| 	OPEN_URI          OpenFlag = 0x00000040 /* Ok for sqlite3_open_v2() */ | ||||
| 	OPEN_MEMORY       OpenFlag = 0x00000080 /* Ok for sqlite3_open_v2() */ | ||||
| 	OPEN_NOMUTEX      OpenFlag = 0x00008000 /* Ok for sqlite3_open_v2() */ | ||||
| 	OPEN_FULLMUTEX    OpenFlag = 0x00010000 /* Ok for sqlite3_open_v2() */ | ||||
| 	OPEN_SHAREDCACHE  OpenFlag = 0x00020000 /* Ok for sqlite3_open_v2() */ | ||||
| 	OPEN_PRIVATECACHE OpenFlag = 0x00040000 /* Ok for sqlite3_open_v2() */ | ||||
| 	OPEN_NOFOLLOW     OpenFlag = 0x01000000 /* Ok for sqlite3_open_v2() */ | ||||
| 	OPEN_EXRESCODE    OpenFlag = 0x02000000 /* Extended result codes */ | ||||
| ) | ||||
| 
 | ||||
| // PrepareFlag is a flag that can be passed to [Conn.PrepareFlags]. | ||||
| // | ||||
| // https://sqlite.org/c3ref/c_prepare_normalize.html | ||||
| type PrepareFlag uint32 | ||||
| 
 | ||||
| const ( | ||||
| 	PREPARE_PERSISTENT PrepareFlag = 0x01 | ||||
| 	PREPARE_NORMALIZE  PrepareFlag = 0x02 | ||||
| 	PREPARE_NO_VTAB    PrepareFlag = 0x04 | ||||
| ) | ||||
| 
 | ||||
| // FunctionFlag is a flag that can be passed to | ||||
| // [Conn.CreateFunction] and [Conn.CreateWindowFunction]. | ||||
| // | ||||
| // https://sqlite.org/c3ref/c_deterministic.html | ||||
| type FunctionFlag uint32 | ||||
| 
 | ||||
| const ( | ||||
| 	DETERMINISTIC  FunctionFlag = 0x000000800 | ||||
| 	DIRECTONLY     FunctionFlag = 0x000080000 | ||||
| 	SUBTYPE        FunctionFlag = 0x000100000 | ||||
| 	INNOCUOUS      FunctionFlag = 0x000200000 | ||||
| 	RESULT_SUBTYPE FunctionFlag = 0x001000000 | ||||
| ) | ||||
| 
 | ||||
| // StmtStatus name counter values associated with the [Stmt.Status] method. | ||||
| // | ||||
| // https://sqlite.org/c3ref/c_stmtstatus_counter.html | ||||
| type StmtStatus uint32 | ||||
| 
 | ||||
| const ( | ||||
| 	STMTSTATUS_FULLSCAN_STEP StmtStatus = 1 | ||||
| 	STMTSTATUS_SORT          StmtStatus = 2 | ||||
| 	STMTSTATUS_AUTOINDEX     StmtStatus = 3 | ||||
| 	STMTSTATUS_VM_STEP       StmtStatus = 4 | ||||
| 	STMTSTATUS_REPREPARE     StmtStatus = 5 | ||||
| 	STMTSTATUS_RUN           StmtStatus = 6 | ||||
| 	STMTSTATUS_FILTER_MISS   StmtStatus = 7 | ||||
| 	STMTSTATUS_FILTER_HIT    StmtStatus = 8 | ||||
| 	STMTSTATUS_MEMUSED       StmtStatus = 99 | ||||
| ) | ||||
| 
 | ||||
| // DBConfig are the available database connection configuration options. | ||||
| // | ||||
| // https://sqlite.org/c3ref/c_dbconfig_defensive.html | ||||
| type DBConfig uint32 | ||||
| 
 | ||||
| const ( | ||||
| 	// DBCONFIG_MAINDBNAME         DBConfig = 1000 | ||||
| 	// DBCONFIG_LOOKASIDE          DBConfig = 1001 | ||||
| 	DBCONFIG_ENABLE_FKEY           DBConfig = 1002 | ||||
| 	DBCONFIG_ENABLE_TRIGGER        DBConfig = 1003 | ||||
| 	DBCONFIG_ENABLE_FTS3_TOKENIZER DBConfig = 1004 | ||||
| 	DBCONFIG_ENABLE_LOAD_EXTENSION DBConfig = 1005 | ||||
| 	DBCONFIG_NO_CKPT_ON_CLOSE      DBConfig = 1006 | ||||
| 	DBCONFIG_ENABLE_QPSG           DBConfig = 1007 | ||||
| 	DBCONFIG_TRIGGER_EQP           DBConfig = 1008 | ||||
| 	DBCONFIG_RESET_DATABASE        DBConfig = 1009 | ||||
| 	DBCONFIG_DEFENSIVE             DBConfig = 1010 | ||||
| 	DBCONFIG_WRITABLE_SCHEMA       DBConfig = 1011 | ||||
| 	DBCONFIG_LEGACY_ALTER_TABLE    DBConfig = 1012 | ||||
| 	DBCONFIG_DQS_DML               DBConfig = 1013 | ||||
| 	DBCONFIG_DQS_DDL               DBConfig = 1014 | ||||
| 	DBCONFIG_ENABLE_VIEW           DBConfig = 1015 | ||||
| 	DBCONFIG_LEGACY_FILE_FORMAT    DBConfig = 1016 | ||||
| 	DBCONFIG_TRUSTED_SCHEMA        DBConfig = 1017 | ||||
| 	DBCONFIG_STMT_SCANSTATUS       DBConfig = 1018 | ||||
| 	DBCONFIG_REVERSE_SCANORDER     DBConfig = 1019 | ||||
| ) | ||||
| 
 | ||||
| // LimitCategory are the available run-time limit categories. | ||||
| // | ||||
| // https://sqlite.org/c3ref/c_limit_attached.html | ||||
| type LimitCategory uint32 | ||||
| 
 | ||||
| const ( | ||||
| 	LIMIT_LENGTH              LimitCategory = 0 | ||||
| 	LIMIT_SQL_LENGTH          LimitCategory = 1 | ||||
| 	LIMIT_COLUMN              LimitCategory = 2 | ||||
| 	LIMIT_EXPR_DEPTH          LimitCategory = 3 | ||||
| 	LIMIT_COMPOUND_SELECT     LimitCategory = 4 | ||||
| 	LIMIT_VDBE_OP             LimitCategory = 5 | ||||
| 	LIMIT_FUNCTION_ARG        LimitCategory = 6 | ||||
| 	LIMIT_ATTACHED            LimitCategory = 7 | ||||
| 	LIMIT_LIKE_PATTERN_LENGTH LimitCategory = 8 | ||||
| 	LIMIT_VARIABLE_NUMBER     LimitCategory = 9 | ||||
| 	LIMIT_TRIGGER_DEPTH       LimitCategory = 10 | ||||
| 	LIMIT_WORKER_THREADS      LimitCategory = 11 | ||||
| ) | ||||
| 
 | ||||
| // AuthorizerActionCode are the integer action codes | ||||
| // that the authorizer callback may be passed. | ||||
| // | ||||
| // https://sqlite.org/c3ref/c_alter_table.html | ||||
| type AuthorizerActionCode uint32 | ||||
| 
 | ||||
| const ( | ||||
| 	/***************************************************** 3rd ************ 4th ***********/ | ||||
| 	AUTH_CREATE_INDEX        AuthorizerActionCode = 1  /* Index Name      Table Name      */ | ||||
| 	AUTH_CREATE_TABLE        AuthorizerActionCode = 2  /* Table Name      NULL            */ | ||||
| 	AUTH_CREATE_TEMP_INDEX   AuthorizerActionCode = 3  /* Index Name      Table Name      */ | ||||
| 	AUTH_CREATE_TEMP_TABLE   AuthorizerActionCode = 4  /* Table Name      NULL            */ | ||||
| 	AUTH_CREATE_TEMP_TRIGGER AuthorizerActionCode = 5  /* Trigger Name    Table Name      */ | ||||
| 	AUTH_CREATE_TEMP_VIEW    AuthorizerActionCode = 6  /* View Name       NULL            */ | ||||
| 	AUTH_CREATE_TRIGGER      AuthorizerActionCode = 7  /* Trigger Name    Table Name      */ | ||||
| 	AUTH_CREATE_VIEW         AuthorizerActionCode = 8  /* View Name       NULL            */ | ||||
| 	AUTH_DELETE              AuthorizerActionCode = 9  /* Table Name      NULL            */ | ||||
| 	AUTH_DROP_INDEX          AuthorizerActionCode = 10 /* Index Name      Table Name      */ | ||||
| 	AUTH_DROP_TABLE          AuthorizerActionCode = 11 /* Table Name      NULL            */ | ||||
| 	AUTH_DROP_TEMP_INDEX     AuthorizerActionCode = 12 /* Index Name      Table Name      */ | ||||
| 	AUTH_DROP_TEMP_TABLE     AuthorizerActionCode = 13 /* Table Name      NULL            */ | ||||
| 	AUTH_DROP_TEMP_TRIGGER   AuthorizerActionCode = 14 /* Trigger Name    Table Name      */ | ||||
| 	AUTH_DROP_TEMP_VIEW      AuthorizerActionCode = 15 /* View Name       NULL            */ | ||||
| 	AUTH_DROP_TRIGGER        AuthorizerActionCode = 16 /* Trigger Name    Table Name      */ | ||||
| 	AUTH_DROP_VIEW           AuthorizerActionCode = 17 /* View Name       NULL            */ | ||||
| 	AUTH_INSERT              AuthorizerActionCode = 18 /* Table Name      NULL            */ | ||||
| 	AUTH_PRAGMA              AuthorizerActionCode = 19 /* Pragma Name     1st arg or NULL */ | ||||
| 	AUTH_READ                AuthorizerActionCode = 20 /* Table Name      Column Name     */ | ||||
| 	AUTH_SELECT              AuthorizerActionCode = 21 /* NULL            NULL            */ | ||||
| 	AUTH_TRANSACTION         AuthorizerActionCode = 22 /* Operation       NULL            */ | ||||
| 	AUTH_UPDATE              AuthorizerActionCode = 23 /* Table Name      Column Name     */ | ||||
| 	AUTH_ATTACH              AuthorizerActionCode = 24 /* Filename        NULL            */ | ||||
| 	AUTH_DETACH              AuthorizerActionCode = 25 /* Database Name   NULL            */ | ||||
| 	AUTH_ALTER_TABLE         AuthorizerActionCode = 26 /* Database Name   Table Name      */ | ||||
| 	AUTH_REINDEX             AuthorizerActionCode = 27 /* Index Name      NULL            */ | ||||
| 	AUTH_ANALYZE             AuthorizerActionCode = 28 /* Table Name      NULL            */ | ||||
| 	AUTH_CREATE_VTABLE       AuthorizerActionCode = 29 /* Table Name      Module Name     */ | ||||
| 	AUTH_DROP_VTABLE         AuthorizerActionCode = 30 /* Table Name      Module Name     */ | ||||
| 	AUTH_FUNCTION            AuthorizerActionCode = 31 /* NULL            Function Name   */ | ||||
| 	AUTH_SAVEPOINT           AuthorizerActionCode = 32 /* Operation       Savepoint Name  */ | ||||
| 	AUTH_COPY                AuthorizerActionCode = 0  /* No longer used */ | ||||
| 	AUTH_RECURSIVE           AuthorizerActionCode = 33 /* NULL            NULL            */ | ||||
| ) | ||||
| 
 | ||||
| // AuthorizerReturnCode are the integer codes | ||||
| // that the authorizer callback may return. | ||||
| // | ||||
| // https://sqlite.org/c3ref/c_deny.html | ||||
| type AuthorizerReturnCode uint32 | ||||
| 
 | ||||
| const ( | ||||
| 	AUTH_OK     AuthorizerReturnCode = 0 | ||||
| 	AUTH_DENY   AuthorizerReturnCode = 1 /* Abort the SQL statement with an error */ | ||||
| 	AUTH_IGNORE AuthorizerReturnCode = 2 /* Don't allow access, but don't generate an error */ | ||||
| ) | ||||
| 
 | ||||
| // CheckpointMode are all the checkpoint mode values. | ||||
| // | ||||
| // https://sqlite.org/c3ref/c_checkpoint_full.html | ||||
| type CheckpointMode uint32 | ||||
| 
 | ||||
| const ( | ||||
| 	CHECKPOINT_PASSIVE  CheckpointMode = 0 /* Do as much as possible w/o blocking */ | ||||
| 	CHECKPOINT_FULL     CheckpointMode = 1 /* Wait for writers, then checkpoint */ | ||||
| 	CHECKPOINT_RESTART  CheckpointMode = 2 /* Like FULL but wait for readers */ | ||||
| 	CHECKPOINT_TRUNCATE CheckpointMode = 3 /* Like RESTART but also truncate WAL */ | ||||
| ) | ||||
| 
 | ||||
| // TxnState are the allowed return values from [Conn.TxnState]. | ||||
| // | ||||
| // https://sqlite.org/c3ref/c_txn_none.html | ||||
| type TxnState uint32 | ||||
| 
 | ||||
| const ( | ||||
| 	TXN_NONE  TxnState = 0 | ||||
| 	TXN_READ  TxnState = 1 | ||||
| 	TXN_WRITE TxnState = 2 | ||||
| ) | ||||
| 
 | ||||
| // Datatype is a fundamental datatype of SQLite. | ||||
| // | ||||
| // https://sqlite.org/c3ref/c_blob.html | ||||
| type Datatype uint32 | ||||
| 
 | ||||
| const ( | ||||
| 	INTEGER Datatype = 1 | ||||
| 	FLOAT   Datatype = 2 | ||||
| 	TEXT    Datatype = 3 | ||||
| 	BLOB    Datatype = 4 | ||||
| 	NULL    Datatype = 5 | ||||
| ) | ||||
| 
 | ||||
| // String implements the [fmt.Stringer] interface. | ||||
| func (t Datatype) String() string { | ||||
| 	const name = "INTEGERFLOATEXTBLOBNULL" | ||||
| 	switch t { | ||||
| 	case INTEGER: | ||||
| 		return name[0:7] | ||||
| 	case FLOAT: | ||||
| 		return name[7:12] | ||||
| 	case TEXT: | ||||
| 		return name[11:15] | ||||
| 	case BLOB: | ||||
| 		return name[15:19] | ||||
| 	case NULL: | ||||
| 		return name[19:23] | ||||
| 	} | ||||
| 	return strconv.FormatUint(uint64(t), 10) | ||||
| } | ||||
							
								
								
									
										229
									
								
								vendor/github.com/ncruces/go-sqlite3/context.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										229
									
								
								vendor/github.com/ncruces/go-sqlite3/context.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,229 @@ | |||
| package sqlite3 | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"errors" | ||||
| 	"math" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/ncruces/go-sqlite3/internal/util" | ||||
| ) | ||||
| 
 | ||||
| // Context is the context in which an SQL function executes. | ||||
| // An SQLite [Context] is in no way related to a Go [context.Context]. | ||||
| // | ||||
| // https://sqlite.org/c3ref/context.html | ||||
| type Context struct { | ||||
| 	c      *Conn | ||||
| 	handle uint32 | ||||
| } | ||||
| 
 | ||||
| // Conn returns the database connection of the | ||||
| // [Conn.CreateFunction] or [Conn.CreateWindowFunction] | ||||
| // routines that originally registered the application defined function. | ||||
| // | ||||
| // https://sqlite.org/c3ref/context_db_handle.html | ||||
| func (ctx Context) Conn() *Conn { | ||||
| 	return ctx.c | ||||
| } | ||||
| 
 | ||||
| // SetAuxData saves metadata for argument n of the function. | ||||
| // | ||||
| // https://sqlite.org/c3ref/get_auxdata.html | ||||
| func (ctx Context) SetAuxData(n int, data any) { | ||||
| 	ptr := util.AddHandle(ctx.c.ctx, data) | ||||
| 	ctx.c.call("sqlite3_set_auxdata_go", uint64(ctx.handle), uint64(n), uint64(ptr)) | ||||
| } | ||||
| 
 | ||||
| // GetAuxData returns metadata for argument n of the function. | ||||
| // | ||||
| // https://sqlite.org/c3ref/get_auxdata.html | ||||
| func (ctx Context) GetAuxData(n int) any { | ||||
| 	ptr := uint32(ctx.c.call("sqlite3_get_auxdata", uint64(ctx.handle), uint64(n))) | ||||
| 	return util.GetHandle(ctx.c.ctx, ptr) | ||||
| } | ||||
| 
 | ||||
| // ResultBool sets the result of the function to a bool. | ||||
| // SQLite does not have a separate boolean storage class. | ||||
| // Instead, boolean values are stored as integers 0 (false) and 1 (true). | ||||
| // | ||||
| // https://sqlite.org/c3ref/result_blob.html | ||||
| func (ctx Context) ResultBool(value bool) { | ||||
| 	var i int64 | ||||
| 	if value { | ||||
| 		i = 1 | ||||
| 	} | ||||
| 	ctx.ResultInt64(i) | ||||
| } | ||||
| 
 | ||||
| // ResultInt sets the result of the function to an int. | ||||
| // | ||||
| // https://sqlite.org/c3ref/result_blob.html | ||||
| func (ctx Context) ResultInt(value int) { | ||||
| 	ctx.ResultInt64(int64(value)) | ||||
| } | ||||
| 
 | ||||
| // ResultInt64 sets the result of the function to an int64. | ||||
| // | ||||
| // https://sqlite.org/c3ref/result_blob.html | ||||
| func (ctx Context) ResultInt64(value int64) { | ||||
| 	ctx.c.call("sqlite3_result_int64", | ||||
| 		uint64(ctx.handle), uint64(value)) | ||||
| } | ||||
| 
 | ||||
| // ResultFloat sets the result of the function to a float64. | ||||
| // | ||||
| // https://sqlite.org/c3ref/result_blob.html | ||||
| func (ctx Context) ResultFloat(value float64) { | ||||
| 	ctx.c.call("sqlite3_result_double", | ||||
| 		uint64(ctx.handle), math.Float64bits(value)) | ||||
| } | ||||
| 
 | ||||
| // ResultText sets the result of the function to a string. | ||||
| // | ||||
| // https://sqlite.org/c3ref/result_blob.html | ||||
| func (ctx Context) ResultText(value string) { | ||||
| 	ptr := ctx.c.newString(value) | ||||
| 	ctx.c.call("sqlite3_result_text64", | ||||
| 		uint64(ctx.handle), uint64(ptr), uint64(len(value)), | ||||
| 		uint64(ctx.c.freer), _UTF8) | ||||
| } | ||||
| 
 | ||||
| // ResultRawText sets the text result of the function to a []byte. | ||||
| // | ||||
| // https://sqlite.org/c3ref/result_blob.html | ||||
| func (ctx Context) ResultRawText(value []byte) { | ||||
| 	ptr := ctx.c.newBytes(value) | ||||
| 	ctx.c.call("sqlite3_result_text64", | ||||
| 		uint64(ctx.handle), uint64(ptr), uint64(len(value)), | ||||
| 		uint64(ctx.c.freer), _UTF8) | ||||
| } | ||||
| 
 | ||||
| // ResultBlob sets the result of the function to a []byte. | ||||
| // Returning a nil slice is the same as calling [Context.ResultNull]. | ||||
| // | ||||
| // https://sqlite.org/c3ref/result_blob.html | ||||
| func (ctx Context) ResultBlob(value []byte) { | ||||
| 	ptr := ctx.c.newBytes(value) | ||||
| 	ctx.c.call("sqlite3_result_blob64", | ||||
| 		uint64(ctx.handle), uint64(ptr), uint64(len(value)), | ||||
| 		uint64(ctx.c.freer)) | ||||
| } | ||||
| 
 | ||||
| // ResultZeroBlob sets the result of the function to a zero-filled, length n BLOB. | ||||
| // | ||||
| // https://sqlite.org/c3ref/result_blob.html | ||||
| func (ctx Context) ResultZeroBlob(n int64) { | ||||
| 	ctx.c.call("sqlite3_result_zeroblob64", | ||||
| 		uint64(ctx.handle), uint64(n)) | ||||
| } | ||||
| 
 | ||||
| // ResultNull sets the result of the function to NULL. | ||||
| // | ||||
| // https://sqlite.org/c3ref/result_blob.html | ||||
| func (ctx Context) ResultNull() { | ||||
| 	ctx.c.call("sqlite3_result_null", | ||||
| 		uint64(ctx.handle)) | ||||
| } | ||||
| 
 | ||||
| // ResultTime sets the result of the function to a [time.Time]. | ||||
| // | ||||
| // https://sqlite.org/c3ref/result_blob.html | ||||
| func (ctx Context) ResultTime(value time.Time, format TimeFormat) { | ||||
| 	if format == TimeFormatDefault { | ||||
| 		ctx.resultRFC3339Nano(value) | ||||
| 		return | ||||
| 	} | ||||
| 	switch v := format.Encode(value).(type) { | ||||
| 	case string: | ||||
| 		ctx.ResultText(v) | ||||
| 	case int64: | ||||
| 		ctx.ResultInt64(v) | ||||
| 	case float64: | ||||
| 		ctx.ResultFloat(v) | ||||
| 	default: | ||||
| 		panic(util.AssertErr()) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (ctx Context) resultRFC3339Nano(value time.Time) { | ||||
| 	const maxlen = uint64(len(time.RFC3339Nano)) + 5 | ||||
| 
 | ||||
| 	ptr := ctx.c.new(maxlen) | ||||
| 	buf := util.View(ctx.c.mod, ptr, maxlen) | ||||
| 	buf = value.AppendFormat(buf[:0], time.RFC3339Nano) | ||||
| 
 | ||||
| 	ctx.c.call("sqlite3_result_text64", | ||||
| 		uint64(ctx.handle), uint64(ptr), uint64(len(buf)), | ||||
| 		uint64(ctx.c.freer), _UTF8) | ||||
| } | ||||
| 
 | ||||
| // ResultPointer sets the result of the function to NULL, just like [Context.ResultNull], | ||||
| // except that it also associates ptr with that NULL value such that it can be retrieved | ||||
| // within an application-defined SQL function using [Value.Pointer]. | ||||
| // | ||||
| // https://sqlite.org/c3ref/result_blob.html | ||||
| func (ctx Context) ResultPointer(ptr any) { | ||||
| 	valPtr := util.AddHandle(ctx.c.ctx, ptr) | ||||
| 	ctx.c.call("sqlite3_result_pointer_go", uint64(valPtr)) | ||||
| } | ||||
| 
 | ||||
| // ResultJSON sets the result of the function to the JSON encoding of value. | ||||
| // | ||||
| // https://sqlite.org/c3ref/result_blob.html | ||||
| func (ctx Context) ResultJSON(value any) { | ||||
| 	data, err := json.Marshal(value) | ||||
| 	if err != nil { | ||||
| 		ctx.ResultError(err) | ||||
| 		return | ||||
| 	} | ||||
| 	ctx.ResultRawText(data) | ||||
| } | ||||
| 
 | ||||
| // ResultValue sets the result of the function to a copy of [Value]. | ||||
| // | ||||
| // https://sqlite.org/c3ref/result_blob.html | ||||
| func (ctx Context) ResultValue(value Value) { | ||||
| 	if value.c != ctx.c { | ||||
| 		ctx.ResultError(MISUSE) | ||||
| 		return | ||||
| 	} | ||||
| 	ctx.c.call("sqlite3_result_value", | ||||
| 		uint64(ctx.handle), uint64(value.handle)) | ||||
| } | ||||
| 
 | ||||
| // ResultError sets the result of the function an error. | ||||
| // | ||||
| // https://sqlite.org/c3ref/result_blob.html | ||||
| func (ctx Context) ResultError(err error) { | ||||
| 	if errors.Is(err, NOMEM) { | ||||
| 		ctx.c.call("sqlite3_result_error_nomem", uint64(ctx.handle)) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	if errors.Is(err, TOOBIG) { | ||||
| 		ctx.c.call("sqlite3_result_error_toobig", uint64(ctx.handle)) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	msg, code := errorCode(err, _OK) | ||||
| 	if msg != "" { | ||||
| 		defer ctx.c.arena.mark()() | ||||
| 		ptr := ctx.c.arena.string(msg) | ||||
| 		ctx.c.call("sqlite3_result_error", | ||||
| 			uint64(ctx.handle), uint64(ptr), uint64(len(msg))) | ||||
| 	} | ||||
| 	if code != _OK { | ||||
| 		ctx.c.call("sqlite3_result_error_code", | ||||
| 			uint64(ctx.handle), uint64(code)) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // VTabNoChange may return true if a column is being fetched as part | ||||
| // of an update during which the column value will not change. | ||||
| // | ||||
| // https://sqlite.org/c3ref/vtab_nochange.html | ||||
| func (ctx Context) VTabNoChange() bool { | ||||
| 	r := ctx.c.call("sqlite3_vtab_nochange", uint64(ctx.handle)) | ||||
| 	return r != 0 | ||||
| } | ||||
							
								
								
									
										579
									
								
								vendor/github.com/ncruces/go-sqlite3/driver/driver.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										579
									
								
								vendor/github.com/ncruces/go-sqlite3/driver/driver.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,579 @@ | |||
| // Package driver provides a database/sql driver for SQLite. | ||||
| // | ||||
| // Importing package driver registers a [database/sql] driver named "sqlite3". | ||||
| // You may also need to import package embed. | ||||
| // | ||||
| //	import _ "github.com/ncruces/go-sqlite3/driver" | ||||
| //	import _ "github.com/ncruces/go-sqlite3/embed" | ||||
| // | ||||
| // The data source name for "sqlite3" databases can be a filename or a "file:" [URI]. | ||||
| // | ||||
| // The [TRANSACTION] mode can be specified using "_txlock": | ||||
| // | ||||
| //	sql.Open("sqlite3", "file:demo.db?_txlock=immediate") | ||||
| // | ||||
| // Possible values are: "deferred", "immediate", "exclusive". | ||||
| // A [read-only] transaction is always "deferred", regardless of "_txlock". | ||||
| // | ||||
| // The time encoding/decoding format can be specified using "_timefmt": | ||||
| // | ||||
| //	sql.Open("sqlite3", "file:demo.db?_timefmt=sqlite") | ||||
| // | ||||
| // Possible values are: "auto" (the default), "sqlite", "rfc3339"; | ||||
| // "auto" encodes as RFC 3339 and decodes any [format] supported by SQLite; | ||||
| // "sqlite" encodes as SQLite and decodes any [format] supported by SQLite; | ||||
| // "rfc3339" encodes and decodes RFC 3339 only. | ||||
| // | ||||
| // [PRAGMA] statements can be specified using "_pragma": | ||||
| // | ||||
| //	sql.Open("sqlite3", "file:demo.db?_pragma=busy_timeout(10000)") | ||||
| // | ||||
| // If no PRAGMAs are specified, a busy timeout of 1 minute is set. | ||||
| // | ||||
| // Order matters: | ||||
| // busy timeout and locking mode should be the first PRAGMAs set, in that order. | ||||
| // | ||||
| // [URI]: https://sqlite.org/uri.html | ||||
| // [PRAGMA]: https://sqlite.org/pragma.html | ||||
| // [format]: https://sqlite.org/lang_datefunc.html#time_values | ||||
| // [TRANSACTION]: https://sqlite.org/lang_transaction.html#deferred_immediate_and_exclusive_transactions | ||||
| // [read-only]: https://pkg.go.dev/database/sql#TxOptions | ||||
| package driver | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"database/sql" | ||||
| 	"database/sql/driver" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"net/url" | ||||
| 	"strings" | ||||
| 	"time" | ||||
| 	"unsafe" | ||||
| 
 | ||||
| 	"github.com/ncruces/go-sqlite3" | ||||
| 	"github.com/ncruces/go-sqlite3/internal/util" | ||||
| ) | ||||
| 
 | ||||
| // This variable can be replaced with -ldflags: | ||||
| // | ||||
| //	go build -ldflags="-X github.com/ncruces/go-sqlite3/driver.driverName=sqlite" | ||||
| var driverName = "sqlite3" | ||||
| 
 | ||||
| func init() { | ||||
| 	if driverName != "" { | ||||
| 		sql.Register(driverName, &SQLite{}) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Open opens the SQLite database specified by dataSourceName as a [database/sql.DB]. | ||||
| // | ||||
| // The init function is called by the driver on new connections. | ||||
| // The [sqlite3.Conn] can be used to execute queries, register functions, etc. | ||||
| // Any error returned closes the connection and is returned to [database/sql]. | ||||
| func Open(dataSourceName string, init func(*sqlite3.Conn) error) (*sql.DB, error) { | ||||
| 	c, err := (&SQLite{Init: init}).OpenConnector(dataSourceName) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return sql.OpenDB(c), nil | ||||
| } | ||||
| 
 | ||||
| // SQLite implements [database/sql/driver.Driver]. | ||||
| type SQLite struct { | ||||
| 	// Init function is called by the driver on new connections. | ||||
| 	// The [sqlite3.Conn] can be used to execute queries, register functions, etc. | ||||
| 	// Any error returned closes the connection and is returned to [database/sql]. | ||||
| 	Init func(*sqlite3.Conn) error | ||||
| } | ||||
| 
 | ||||
| // Open implements [database/sql/driver.Driver]. | ||||
| func (d *SQLite) Open(name string) (driver.Conn, error) { | ||||
| 	c, err := d.newConnector(name) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return c.Connect(context.Background()) | ||||
| } | ||||
| 
 | ||||
| // OpenConnector implements [database/sql/driver.DriverContext]. | ||||
| func (d *SQLite) OpenConnector(name string) (driver.Connector, error) { | ||||
| 	return d.newConnector(name) | ||||
| } | ||||
| 
 | ||||
| func (d *SQLite) newConnector(name string) (*connector, error) { | ||||
| 	c := connector{driver: d, name: name} | ||||
| 
 | ||||
| 	var txlock, timefmt string | ||||
| 	if strings.HasPrefix(name, "file:") { | ||||
| 		if _, after, ok := strings.Cut(name, "?"); ok { | ||||
| 			query, err := url.ParseQuery(after) | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 			txlock = query.Get("_txlock") | ||||
| 			timefmt = query.Get("_timefmt") | ||||
| 			c.pragmas = query.Has("_pragma") | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	switch txlock { | ||||
| 	case "": | ||||
| 		c.txBegin = "BEGIN" | ||||
| 	case "deferred", "immediate", "exclusive": | ||||
| 		c.txBegin = "BEGIN " + txlock | ||||
| 	default: | ||||
| 		return nil, fmt.Errorf("sqlite3: invalid _txlock: %s", txlock) | ||||
| 	} | ||||
| 
 | ||||
| 	switch timefmt { | ||||
| 	case "": | ||||
| 		c.tmRead = sqlite3.TimeFormatAuto | ||||
| 		c.tmWrite = sqlite3.TimeFormatDefault | ||||
| 	case "sqlite": | ||||
| 		c.tmRead = sqlite3.TimeFormatAuto | ||||
| 		c.tmWrite = sqlite3.TimeFormat3 | ||||
| 	case "rfc3339": | ||||
| 		c.tmRead = sqlite3.TimeFormatDefault | ||||
| 		c.tmWrite = sqlite3.TimeFormatDefault | ||||
| 	default: | ||||
| 		c.tmRead = sqlite3.TimeFormat(timefmt) | ||||
| 		c.tmWrite = sqlite3.TimeFormat(timefmt) | ||||
| 	} | ||||
| 	return &c, nil | ||||
| } | ||||
| 
 | ||||
| type connector struct { | ||||
| 	driver  *SQLite | ||||
| 	name    string | ||||
| 	txBegin string | ||||
| 	tmRead  sqlite3.TimeFormat | ||||
| 	tmWrite sqlite3.TimeFormat | ||||
| 	pragmas bool | ||||
| } | ||||
| 
 | ||||
| func (n *connector) Driver() driver.Driver { | ||||
| 	return n.driver | ||||
| } | ||||
| 
 | ||||
| func (n *connector) Connect(ctx context.Context) (_ driver.Conn, err error) { | ||||
| 	c := &conn{ | ||||
| 		txBegin: n.txBegin, | ||||
| 		tmRead:  n.tmRead, | ||||
| 		tmWrite: n.tmWrite, | ||||
| 	} | ||||
| 
 | ||||
| 	c.Conn, err = sqlite3.Open(n.name) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	defer func() { | ||||
| 		if err != nil { | ||||
| 			c.Close() | ||||
| 		} | ||||
| 	}() | ||||
| 
 | ||||
| 	old := c.Conn.SetInterrupt(ctx) | ||||
| 	defer c.Conn.SetInterrupt(old) | ||||
| 
 | ||||
| 	if !n.pragmas { | ||||
| 		err = c.Conn.BusyTimeout(60 * time.Second) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 	} | ||||
| 	if n.driver.Init != nil { | ||||
| 		err = n.driver.Init(c.Conn) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 	} | ||||
| 	if n.pragmas || n.driver.Init != nil { | ||||
| 		s, _, err := c.Conn.Prepare(`PRAGMA query_only`) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		if s.Step() && s.ColumnBool(0) { | ||||
| 			c.readOnly = '1' | ||||
| 		} else { | ||||
| 			c.readOnly = '0' | ||||
| 		} | ||||
| 		err = s.Close() | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 	} | ||||
| 	return c, nil | ||||
| } | ||||
| 
 | ||||
| type conn struct { | ||||
| 	*sqlite3.Conn | ||||
| 	txBegin    string | ||||
| 	txCommit   string | ||||
| 	txRollback string | ||||
| 	tmRead     sqlite3.TimeFormat | ||||
| 	tmWrite    sqlite3.TimeFormat | ||||
| 	readOnly   byte | ||||
| } | ||||
| 
 | ||||
| var ( | ||||
| 	// Ensure these interfaces are implemented: | ||||
| 	_ driver.ConnPrepareContext = &conn{} | ||||
| 	_ driver.ExecerContext      = &conn{} | ||||
| 	_ driver.ConnBeginTx        = &conn{} | ||||
| 	_ sqlite3.DriverConn        = &conn{} | ||||
| ) | ||||
| 
 | ||||
| func (c *conn) Raw() *sqlite3.Conn { | ||||
| 	return c.Conn | ||||
| } | ||||
| 
 | ||||
| func (c *conn) Begin() (driver.Tx, error) { | ||||
| 	return c.BeginTx(context.Background(), driver.TxOptions{}) | ||||
| } | ||||
| 
 | ||||
| func (c *conn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, error) { | ||||
| 	txBegin := c.txBegin | ||||
| 	c.txCommit = `COMMIT` | ||||
| 	c.txRollback = `ROLLBACK` | ||||
| 
 | ||||
| 	if opts.ReadOnly { | ||||
| 		txBegin = ` | ||||
| 			BEGIN deferred; | ||||
| 			PRAGMA query_only=on` | ||||
| 		c.txRollback = ` | ||||
| 			ROLLBACK; | ||||
| 			PRAGMA query_only=` + string(c.readOnly) | ||||
| 		c.txCommit = c.txRollback | ||||
| 	} | ||||
| 
 | ||||
| 	switch opts.Isolation { | ||||
| 	default: | ||||
| 		return nil, util.IsolationErr | ||||
| 	case | ||||
| 		driver.IsolationLevel(sql.LevelDefault), | ||||
| 		driver.IsolationLevel(sql.LevelSerializable): | ||||
| 		break | ||||
| 	} | ||||
| 
 | ||||
| 	old := c.Conn.SetInterrupt(ctx) | ||||
| 	defer c.Conn.SetInterrupt(old) | ||||
| 
 | ||||
| 	err := c.Conn.Exec(txBegin) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return c, nil | ||||
| } | ||||
| 
 | ||||
| func (c *conn) Commit() error { | ||||
| 	err := c.Conn.Exec(c.txCommit) | ||||
| 	if err != nil && !c.Conn.GetAutocommit() { | ||||
| 		c.Rollback() | ||||
| 	} | ||||
| 	return err | ||||
| } | ||||
| 
 | ||||
| func (c *conn) Rollback() error { | ||||
| 	err := c.Conn.Exec(c.txRollback) | ||||
| 	if errors.Is(err, sqlite3.INTERRUPT) { | ||||
| 		old := c.Conn.SetInterrupt(context.Background()) | ||||
| 		defer c.Conn.SetInterrupt(old) | ||||
| 		err = c.Conn.Exec(c.txRollback) | ||||
| 	} | ||||
| 	return err | ||||
| } | ||||
| 
 | ||||
| func (c *conn) Prepare(query string) (driver.Stmt, error) { | ||||
| 	return c.PrepareContext(context.Background(), query) | ||||
| } | ||||
| 
 | ||||
| func (c *conn) PrepareContext(ctx context.Context, query string) (driver.Stmt, error) { | ||||
| 	old := c.Conn.SetInterrupt(ctx) | ||||
| 	defer c.Conn.SetInterrupt(old) | ||||
| 
 | ||||
| 	s, tail, err := c.Conn.Prepare(query) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if tail != "" { | ||||
| 		s.Close() | ||||
| 		return nil, util.TailErr | ||||
| 	} | ||||
| 	return &stmt{Stmt: s, tmRead: c.tmRead, tmWrite: c.tmWrite}, nil | ||||
| } | ||||
| 
 | ||||
| func (c *conn) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Result, error) { | ||||
| 	if len(args) != 0 { | ||||
| 		// Slow path. | ||||
| 		return nil, driver.ErrSkip | ||||
| 	} | ||||
| 
 | ||||
| 	if savept, ok := ctx.(*saveptCtx); ok { | ||||
| 		// Called from driver.Savepoint. | ||||
| 		savept.Savepoint = c.Conn.Savepoint() | ||||
| 		return resultRowsAffected(0), nil | ||||
| 	} | ||||
| 
 | ||||
| 	old := c.Conn.SetInterrupt(ctx) | ||||
| 	defer c.Conn.SetInterrupt(old) | ||||
| 
 | ||||
| 	err := c.Conn.Exec(query) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	return newResult(c.Conn), nil | ||||
| } | ||||
| 
 | ||||
| func (c *conn) CheckNamedValue(arg *driver.NamedValue) error { | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| type stmt struct { | ||||
| 	*sqlite3.Stmt | ||||
| 	tmWrite sqlite3.TimeFormat | ||||
| 	tmRead  sqlite3.TimeFormat | ||||
| } | ||||
| 
 | ||||
| var ( | ||||
| 	// Ensure these interfaces are implemented: | ||||
| 	_ driver.StmtExecContext   = &stmt{} | ||||
| 	_ driver.StmtQueryContext  = &stmt{} | ||||
| 	_ driver.NamedValueChecker = &stmt{} | ||||
| ) | ||||
| 
 | ||||
| func (s *stmt) NumInput() int { | ||||
| 	n := s.Stmt.BindCount() | ||||
| 	for i := 1; i <= n; i++ { | ||||
| 		if s.Stmt.BindName(i) != "" { | ||||
| 			return -1 | ||||
| 		} | ||||
| 	} | ||||
| 	return n | ||||
| } | ||||
| 
 | ||||
| // Deprecated: use ExecContext instead. | ||||
| func (s *stmt) Exec(args []driver.Value) (driver.Result, error) { | ||||
| 	return s.ExecContext(context.Background(), namedValues(args)) | ||||
| } | ||||
| 
 | ||||
| // Deprecated: use QueryContext instead. | ||||
| func (s *stmt) Query(args []driver.Value) (driver.Rows, error) { | ||||
| 	return s.QueryContext(context.Background(), namedValues(args)) | ||||
| } | ||||
| 
 | ||||
| func (s *stmt) ExecContext(ctx context.Context, args []driver.NamedValue) (driver.Result, error) { | ||||
| 	err := s.setupBindings(args) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	old := s.Stmt.Conn().SetInterrupt(ctx) | ||||
| 	defer s.Stmt.Conn().SetInterrupt(old) | ||||
| 
 | ||||
| 	err = s.Stmt.Exec() | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	return newResult(s.Stmt.Conn()), nil | ||||
| } | ||||
| 
 | ||||
| func (s *stmt) QueryContext(ctx context.Context, args []driver.NamedValue) (driver.Rows, error) { | ||||
| 	err := s.setupBindings(args) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return &rows{ctx: ctx, stmt: s}, nil | ||||
| } | ||||
| 
 | ||||
| func (s *stmt) setupBindings(args []driver.NamedValue) error { | ||||
| 	err := s.Stmt.ClearBindings() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	var ids [3]int | ||||
| 	for _, arg := range args { | ||||
| 		ids := ids[:0] | ||||
| 		if arg.Name == "" { | ||||
| 			ids = append(ids, arg.Ordinal) | ||||
| 		} else { | ||||
| 			for _, prefix := range []string{":", "@", "$"} { | ||||
| 				if id := s.Stmt.BindIndex(prefix + arg.Name); id != 0 { | ||||
| 					ids = append(ids, id) | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		for _, id := range ids { | ||||
| 			switch a := arg.Value.(type) { | ||||
| 			case bool: | ||||
| 				err = s.Stmt.BindBool(id, a) | ||||
| 			case int: | ||||
| 				err = s.Stmt.BindInt(id, a) | ||||
| 			case int64: | ||||
| 				err = s.Stmt.BindInt64(id, a) | ||||
| 			case float64: | ||||
| 				err = s.Stmt.BindFloat(id, a) | ||||
| 			case string: | ||||
| 				err = s.Stmt.BindText(id, a) | ||||
| 			case []byte: | ||||
| 				err = s.Stmt.BindBlob(id, a) | ||||
| 			case sqlite3.ZeroBlob: | ||||
| 				err = s.Stmt.BindZeroBlob(id, int64(a)) | ||||
| 			case time.Time: | ||||
| 				err = s.Stmt.BindTime(id, a, s.tmWrite) | ||||
| 			case util.JSON: | ||||
| 				err = s.Stmt.BindJSON(id, a.Value) | ||||
| 			case util.PointerUnwrap: | ||||
| 				err = s.Stmt.BindPointer(id, util.UnwrapPointer(a)) | ||||
| 			case nil: | ||||
| 				err = s.Stmt.BindNull(id) | ||||
| 			default: | ||||
| 				panic(util.AssertErr()) | ||||
| 			} | ||||
| 		} | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (s *stmt) CheckNamedValue(arg *driver.NamedValue) error { | ||||
| 	switch arg.Value.(type) { | ||||
| 	case bool, int, int64, float64, string, []byte, | ||||
| 		time.Time, sqlite3.ZeroBlob, | ||||
| 		util.JSON, util.PointerUnwrap, | ||||
| 		nil: | ||||
| 		return nil | ||||
| 	default: | ||||
| 		return driver.ErrSkip | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func newResult(c *sqlite3.Conn) driver.Result { | ||||
| 	rows := c.Changes() | ||||
| 	if rows != 0 { | ||||
| 		id := c.LastInsertRowID() | ||||
| 		if id != 0 { | ||||
| 			return result{id, rows} | ||||
| 		} | ||||
| 	} | ||||
| 	return resultRowsAffected(rows) | ||||
| } | ||||
| 
 | ||||
| type result struct{ lastInsertId, rowsAffected int64 } | ||||
| 
 | ||||
| func (r result) LastInsertId() (int64, error) { | ||||
| 	return r.lastInsertId, nil | ||||
| } | ||||
| 
 | ||||
| func (r result) RowsAffected() (int64, error) { | ||||
| 	return r.rowsAffected, nil | ||||
| } | ||||
| 
 | ||||
| type resultRowsAffected int64 | ||||
| 
 | ||||
| func (r resultRowsAffected) LastInsertId() (int64, error) { | ||||
| 	return 0, nil | ||||
| } | ||||
| 
 | ||||
| func (r resultRowsAffected) RowsAffected() (int64, error) { | ||||
| 	return int64(r), nil | ||||
| } | ||||
| 
 | ||||
| type rows struct { | ||||
| 	ctx context.Context | ||||
| 	*stmt | ||||
| 	names []string | ||||
| 	types []string | ||||
| } | ||||
| 
 | ||||
| func (r *rows) Close() error { | ||||
| 	r.Stmt.ClearBindings() | ||||
| 	return r.Stmt.Reset() | ||||
| } | ||||
| 
 | ||||
| func (r *rows) Columns() []string { | ||||
| 	if r.names == nil { | ||||
| 		count := r.Stmt.ColumnCount() | ||||
| 		r.names = make([]string, count) | ||||
| 		for i := range r.names { | ||||
| 			r.names[i] = r.Stmt.ColumnName(i) | ||||
| 		} | ||||
| 	} | ||||
| 	return r.names | ||||
| } | ||||
| 
 | ||||
| func (r *rows) declType(index int) string { | ||||
| 	if r.types == nil { | ||||
| 		count := r.Stmt.ColumnCount() | ||||
| 		r.types = make([]string, count) | ||||
| 		for i := range r.types { | ||||
| 			r.types[i] = strings.ToUpper(r.Stmt.ColumnDeclType(i)) | ||||
| 		} | ||||
| 	} | ||||
| 	return r.types[index] | ||||
| } | ||||
| 
 | ||||
| func (r *rows) ColumnTypeDatabaseTypeName(index int) string { | ||||
| 	decltype := r.declType(index) | ||||
| 	if len := len(decltype); len > 0 && decltype[len-1] == ')' { | ||||
| 		if i := strings.LastIndexByte(decltype, '('); i >= 0 { | ||||
| 			decltype = decltype[:i] | ||||
| 		} | ||||
| 	} | ||||
| 	return strings.TrimSpace(decltype) | ||||
| } | ||||
| 
 | ||||
| func (r *rows) Next(dest []driver.Value) error { | ||||
| 	old := r.Stmt.Conn().SetInterrupt(r.ctx) | ||||
| 	defer r.Stmt.Conn().SetInterrupt(old) | ||||
| 
 | ||||
| 	if !r.Stmt.Step() { | ||||
| 		if err := r.Stmt.Err(); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		return io.EOF | ||||
| 	} | ||||
| 
 | ||||
| 	data := unsafe.Slice((*any)(unsafe.SliceData(dest)), len(dest)) | ||||
| 	err := r.Stmt.Columns(data) | ||||
| 	for i := range dest { | ||||
| 		if t, ok := r.decodeTime(i, dest[i]); ok { | ||||
| 			dest[i] = t | ||||
| 			continue | ||||
| 		} | ||||
| 		if s, ok := dest[i].(string); ok { | ||||
| 			t, ok := maybeTime(s) | ||||
| 			if ok { | ||||
| 				dest[i] = t | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return err | ||||
| } | ||||
| 
 | ||||
| func (r *rows) decodeTime(i int, v any) (_ time.Time, _ bool) { | ||||
| 	if r.tmRead == sqlite3.TimeFormatDefault { | ||||
| 		return | ||||
| 	} | ||||
| 	switch r.declType(i) { | ||||
| 	case "DATE", "TIME", "DATETIME", "TIMESTAMP": | ||||
| 		// maybe | ||||
| 	default: | ||||
| 		return | ||||
| 	} | ||||
| 	switch v.(type) { | ||||
| 	case int64, float64, string: | ||||
| 		// maybe | ||||
| 	default: | ||||
| 		return | ||||
| 	} | ||||
| 	t, err := r.tmRead.Decode(v) | ||||
| 	return t, err == nil | ||||
| } | ||||
							
								
								
									
										27
									
								
								vendor/github.com/ncruces/go-sqlite3/driver/savepoint.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								vendor/github.com/ncruces/go-sqlite3/driver/savepoint.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,27 @@ | |||
| package driver | ||||
| 
 | ||||
| import ( | ||||
| 	"database/sql" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/ncruces/go-sqlite3" | ||||
| ) | ||||
| 
 | ||||
| // Savepoint establishes a new transaction savepoint. | ||||
| // | ||||
| // https://sqlite.org/lang_savepoint.html | ||||
| func Savepoint(tx *sql.Tx) sqlite3.Savepoint { | ||||
| 	var ctx saveptCtx | ||||
| 	tx.ExecContext(&ctx, "") | ||||
| 	return ctx.Savepoint | ||||
| } | ||||
| 
 | ||||
| type saveptCtx struct{ sqlite3.Savepoint } | ||||
| 
 | ||||
| func (*saveptCtx) Deadline() (deadline time.Time, ok bool) { return } | ||||
| 
 | ||||
| func (*saveptCtx) Done() <-chan struct{} { return nil } | ||||
| 
 | ||||
| func (*saveptCtx) Err() error { return nil } | ||||
| 
 | ||||
| func (*saveptCtx) Value(key any) any { return nil } | ||||
							
								
								
									
										31
									
								
								vendor/github.com/ncruces/go-sqlite3/driver/time.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								vendor/github.com/ncruces/go-sqlite3/driver/time.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,31 @@ | |||
| package driver | ||||
| 
 | ||||
| import ( | ||||
| 	"time" | ||||
| ) | ||||
| 
 | ||||
| // Convert a string in [time.RFC3339Nano] format into a [time.Time] | ||||
| // if it roundtrips back to the same string. | ||||
| // This way times can be persisted to, and recovered from, the database, | ||||
| // but if a string is needed, [database/sql] will recover the same string. | ||||
| func maybeTime(text string) (_ time.Time, _ bool) { | ||||
| 	// Weed out (some) values that can't possibly be | ||||
| 	// [time.RFC3339Nano] timestamps. | ||||
| 	if len(text) < len("2006-01-02T15:04:05Z") { | ||||
| 		return | ||||
| 	} | ||||
| 	if len(text) > len(time.RFC3339Nano) { | ||||
| 		return | ||||
| 	} | ||||
| 	if text[4] != '-' || text[10] != 'T' || text[16] != ':' { | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	// Slow path. | ||||
| 	var buf [len(time.RFC3339Nano)]byte | ||||
| 	date, err := time.Parse(time.RFC3339Nano, text) | ||||
| 	if err == nil && text == string(date.AppendFormat(buf[:0], time.RFC3339Nano)) { | ||||
| 		return date, true | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
							
								
								
									
										14
									
								
								vendor/github.com/ncruces/go-sqlite3/driver/util.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								vendor/github.com/ncruces/go-sqlite3/driver/util.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,14 @@ | |||
| package driver | ||||
| 
 | ||||
| import "database/sql/driver" | ||||
| 
 | ||||
| func namedValues(args []driver.Value) []driver.NamedValue { | ||||
| 	named := make([]driver.NamedValue, len(args)) | ||||
| 	for i, v := range args { | ||||
| 		named[i] = driver.NamedValue{ | ||||
| 			Ordinal: i + 1, | ||||
| 			Value:   v, | ||||
| 		} | ||||
| 	} | ||||
| 	return named | ||||
| } | ||||
							
								
								
									
										27
									
								
								vendor/github.com/ncruces/go-sqlite3/embed/README.md
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								vendor/github.com/ncruces/go-sqlite3/embed/README.md
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,27 @@ | |||
| # Embeddable Wasm build of SQLite | ||||
| 
 | ||||
| This folder includes an embeddable Wasm build of SQLite 3.46.0 for use with | ||||
| [`github.com/ncruces/go-sqlite3`](https://pkg.go.dev/github.com/ncruces/go-sqlite3). | ||||
| 
 | ||||
| The following optional features are compiled in: | ||||
| - [math functions](https://sqlite.org/lang_mathfunc.html) | ||||
| - [FTS5](https://sqlite.org/fts5.html) | ||||
| - [JSON](https://sqlite.org/json1.html) | ||||
| - [R*Tree](https://sqlite.org/rtree.html) | ||||
| - [GeoPoly](https://sqlite.org/geopoly.html) | ||||
| - [soundex](https://sqlite.org/lang_corefunc.html#soundex) | ||||
| - [stat4](https://sqlite.org/compile.html#enable_stat4) | ||||
| - [base64](https://github.com/sqlite/sqlite/blob/master/ext/misc/base64.c) | ||||
| - [decimal](https://github.com/sqlite/sqlite/blob/master/ext/misc/decimal.c) | ||||
| - [ieee754](https://github.com/sqlite/sqlite/blob/master/ext/misc/ieee754.c) | ||||
| - [regexp](https://github.com/sqlite/sqlite/blob/master/ext/misc/regexp.c) | ||||
| - [series](https://github.com/sqlite/sqlite/blob/master/ext/misc/series.c) | ||||
| - [uint](https://github.com/sqlite/sqlite/blob/master/ext/misc/uint.c) | ||||
| - [uuid](https://github.com/sqlite/sqlite/blob/master/ext/misc/uuid.c) | ||||
| - [time](../sqlite3/time.c) | ||||
| 
 | ||||
| See the [configuration options](../sqlite3/sqlite_cfg.h), | ||||
| and [patches](../sqlite3) applied. | ||||
| 
 | ||||
| Built using [`wasi-sdk`](https://github.com/WebAssembly/wasi-sdk), | ||||
| and [`binaryen`](https://github.com/WebAssembly/binaryen). | ||||
							
								
								
									
										32
									
								
								vendor/github.com/ncruces/go-sqlite3/embed/build.sh
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								vendor/github.com/ncruces/go-sqlite3/embed/build.sh
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,32 @@ | |||
| #!/usr/bin/env bash | ||||
| set -euo pipefail | ||||
| 
 | ||||
| cd -P -- "$(dirname -- "$0")" | ||||
| 
 | ||||
| ROOT=../ | ||||
| BINARYEN="$ROOT/tools/binaryen-version_117/bin" | ||||
| WASI_SDK="$ROOT/tools/wasi-sdk-22.0/bin" | ||||
| 
 | ||||
| "$WASI_SDK/clang" --target=wasm32-wasi -std=c17 -flto -g0 -O2 \ | ||||
| 	-Wall -Wextra -Wno-unused-parameter -Wno-unused-function \ | ||||
| 	-o sqlite3.wasm "$ROOT/sqlite3/main.c" \ | ||||
| 	-I"$ROOT/sqlite3" \ | ||||
| 	-mexec-model=reactor \ | ||||
| 	-msimd128 -mmutable-globals \ | ||||
| 	-mbulk-memory -mreference-types \ | ||||
| 	-mnontrapping-fptoint -msign-ext \ | ||||
| 	-fno-stack-protector -fno-stack-clash-protection \ | ||||
| 	-Wl,--initial-memory=327680 \ | ||||
| 	-Wl,--stack-first \ | ||||
| 	-Wl,--import-undefined \ | ||||
| 	-D_HAVE_SQLITE_CONFIG_H \ | ||||
| 	-DSQLITE_CUSTOM_INCLUDE=sqlite_opt.h \ | ||||
| 	$(awk '{print "-Wl,--export="$0}' exports.txt) | ||||
| 
 | ||||
| trap 'rm -f sqlite3.tmp' EXIT | ||||
| "$BINARYEN/wasm-ctor-eval" -g -c _initialize sqlite3.wasm -o sqlite3.tmp | ||||
| "$BINARYEN/wasm-opt" -g --strip --strip-producers -c -O3 \ | ||||
| 	sqlite3.tmp -o sqlite3.wasm \ | ||||
| 	--enable-simd --enable-mutable-globals --enable-multivalue \ | ||||
| 	--enable-bulk-memory --enable-reference-types \ | ||||
| 	--enable-nontrapping-float-to-int --enable-sign-ext | ||||
							
								
								
									
										130
									
								
								vendor/github.com/ncruces/go-sqlite3/embed/exports.txt
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										130
									
								
								vendor/github.com/ncruces/go-sqlite3/embed/exports.txt
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,130 @@ | |||
| aligned_alloc | ||||
| free | ||||
| malloc | ||||
| malloc_destructor | ||||
| sqlite3_anycollseq_init | ||||
| sqlite3_autovacuum_pages_go | ||||
| 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_pointer_go | ||||
| sqlite3_bind_text64 | ||||
| sqlite3_bind_value | ||||
| sqlite3_bind_zeroblob64 | ||||
| sqlite3_blob_bytes | ||||
| sqlite3_blob_close | ||||
| sqlite3_blob_open | ||||
| sqlite3_blob_read | ||||
| sqlite3_blob_reopen | ||||
| sqlite3_blob_write | ||||
| sqlite3_busy_handler_go | ||||
| sqlite3_busy_timeout | ||||
| sqlite3_changes64 | ||||
| sqlite3_clear_bindings | ||||
| sqlite3_close | ||||
| sqlite3_close_v2 | ||||
| sqlite3_collation_needed_go | ||||
| sqlite3_column_blob | ||||
| sqlite3_column_bytes | ||||
| sqlite3_column_count | ||||
| sqlite3_column_database_name | ||||
| sqlite3_column_decltype | ||||
| sqlite3_column_double | ||||
| sqlite3_column_int64 | ||||
| sqlite3_column_name | ||||
| sqlite3_column_origin_name | ||||
| sqlite3_column_table_name | ||||
| sqlite3_column_text | ||||
| sqlite3_column_type | ||||
| sqlite3_column_value | ||||
| sqlite3_columns_go | ||||
| sqlite3_commit_hook_go | ||||
| sqlite3_config_log_go | ||||
| sqlite3_create_aggregate_function_go | ||||
| sqlite3_create_collation_go | ||||
| sqlite3_create_function_go | ||||
| sqlite3_create_module_go | ||||
| sqlite3_create_window_function_go | ||||
| sqlite3_database_file_object | ||||
| sqlite3_db_config | ||||
| sqlite3_db_filename | ||||
| sqlite3_db_name | ||||
| sqlite3_db_readonly | ||||
| sqlite3_db_release_memory | ||||
| sqlite3_declare_vtab | ||||
| sqlite3_errcode | ||||
| sqlite3_errmsg | ||||
| sqlite3_error_offset | ||||
| sqlite3_errstr | ||||
| sqlite3_exec | ||||
| sqlite3_filename_database | ||||
| sqlite3_filename_journal | ||||
| sqlite3_filename_wal | ||||
| sqlite3_finalize | ||||
| sqlite3_get_autocommit | ||||
| sqlite3_get_auxdata | ||||
| sqlite3_interrupt | ||||
| sqlite3_last_insert_rowid | ||||
| sqlite3_limit | ||||
| sqlite3_open_v2 | ||||
| sqlite3_overload_function | ||||
| sqlite3_prepare_v3 | ||||
| sqlite3_progress_handler_go | ||||
| sqlite3_reset | ||||
| sqlite3_result_blob64 | ||||
| sqlite3_result_double | ||||
| sqlite3_result_error | ||||
| sqlite3_result_error_code | ||||
| sqlite3_result_error_nomem | ||||
| sqlite3_result_error_toobig | ||||
| sqlite3_result_int64 | ||||
| sqlite3_result_null | ||||
| sqlite3_result_pointer_go | ||||
| sqlite3_result_text64 | ||||
| sqlite3_result_value | ||||
| sqlite3_result_zeroblob64 | ||||
| sqlite3_rollback_hook_go | ||||
| sqlite3_set_authorizer_go | ||||
| sqlite3_set_auxdata_go | ||||
| sqlite3_set_last_insert_rowid | ||||
| sqlite3_step | ||||
| sqlite3_stmt_busy | ||||
| sqlite3_stmt_readonly | ||||
| sqlite3_stmt_status | ||||
| sqlite3_total_changes64 | ||||
| sqlite3_txn_state | ||||
| sqlite3_update_hook_go | ||||
| sqlite3_uri_key | ||||
| sqlite3_uri_parameter | ||||
| sqlite3_value_blob | ||||
| sqlite3_value_bytes | ||||
| sqlite3_value_double | ||||
| sqlite3_value_dup | ||||
| sqlite3_value_free | ||||
| sqlite3_value_int64 | ||||
| sqlite3_value_nochange | ||||
| sqlite3_value_numeric_type | ||||
| 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_nochange | ||||
| sqlite3_vtab_on_conflict | ||||
| sqlite3_vtab_rhs_value | ||||
| sqlite3_wal_autocheckpoint | ||||
| sqlite3_wal_checkpoint_v2 | ||||
| sqlite3_wal_hook_go | ||||
							
								
								
									
										20
									
								
								vendor/github.com/ncruces/go-sqlite3/embed/init.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								vendor/github.com/ncruces/go-sqlite3/embed/init.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,20 @@ | |||
| // Package embed embeds SQLite into your application. | ||||
| // | ||||
| // Importing package embed initializes the [sqlite3.Binary] variable | ||||
| // with an appropriate build of SQLite: | ||||
| // | ||||
| //	import _ "github.com/ncruces/go-sqlite3/embed" | ||||
| package embed | ||||
| 
 | ||||
| import ( | ||||
| 	_ "embed" | ||||
| 
 | ||||
| 	"github.com/ncruces/go-sqlite3" | ||||
| ) | ||||
| 
 | ||||
| //go:embed sqlite3.wasm | ||||
| var binary []byte | ||||
| 
 | ||||
| func init() { | ||||
| 	sqlite3.Binary = binary | ||||
| } | ||||
							
								
								
									
										
											BIN
										
									
								
								vendor/github.com/ncruces/go-sqlite3/embed/sqlite3.wasm
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								vendor/github.com/ncruces/go-sqlite3/embed/sqlite3.wasm
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										162
									
								
								vendor/github.com/ncruces/go-sqlite3/error.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										162
									
								
								vendor/github.com/ncruces/go-sqlite3/error.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,162 @@ | |||
| package sqlite3 | ||||
| 
 | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 
 | ||||
| 	"github.com/ncruces/go-sqlite3/internal/util" | ||||
| ) | ||||
| 
 | ||||
| // Error wraps an SQLite Error Code. | ||||
| // | ||||
| // https://sqlite.org/c3ref/errcode.html | ||||
| type Error struct { | ||||
| 	str  string | ||||
| 	msg  string | ||||
| 	sql  string | ||||
| 	code uint64 | ||||
| } | ||||
| 
 | ||||
| // Code returns the primary error code for this error. | ||||
| // | ||||
| // https://sqlite.org/rescode.html | ||||
| func (e *Error) Code() ErrorCode { | ||||
| 	return ErrorCode(e.code) | ||||
| } | ||||
| 
 | ||||
| // ExtendedCode returns the extended error code for this error. | ||||
| // | ||||
| // https://sqlite.org/rescode.html | ||||
| func (e *Error) ExtendedCode() ExtendedErrorCode { | ||||
| 	return ExtendedErrorCode(e.code) | ||||
| } | ||||
| 
 | ||||
| // Error implements the error interface. | ||||
| func (e *Error) Error() string { | ||||
| 	var b strings.Builder | ||||
| 	b.WriteString("sqlite3: ") | ||||
| 
 | ||||
| 	if e.str != "" { | ||||
| 		b.WriteString(e.str) | ||||
| 	} else { | ||||
| 		b.WriteString(strconv.Itoa(int(e.code))) | ||||
| 	} | ||||
| 
 | ||||
| 	if e.msg != "" { | ||||
| 		b.WriteString(": ") | ||||
| 		b.WriteString(e.msg) | ||||
| 	} | ||||
| 
 | ||||
| 	return b.String() | ||||
| } | ||||
| 
 | ||||
| // Is tests whether this error matches a given [ErrorCode] or [ExtendedErrorCode]. | ||||
| // | ||||
| // It makes it possible to do: | ||||
| // | ||||
| //	if errors.Is(err, sqlite3.BUSY) { | ||||
| //		// ... handle BUSY | ||||
| //	} | ||||
| func (e *Error) Is(err error) bool { | ||||
| 	switch c := err.(type) { | ||||
| 	case ErrorCode: | ||||
| 		return c == e.Code() | ||||
| 	case ExtendedErrorCode: | ||||
| 		return c == e.ExtendedCode() | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
| 
 | ||||
| // As converts this error to an [ErrorCode] or [ExtendedErrorCode]. | ||||
| func (e *Error) As(err any) bool { | ||||
| 	switch c := err.(type) { | ||||
| 	case *ErrorCode: | ||||
| 		*c = e.Code() | ||||
| 		return true | ||||
| 	case *ExtendedErrorCode: | ||||
| 		*c = e.ExtendedCode() | ||||
| 		return true | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
| 
 | ||||
| // Temporary returns true for [BUSY] errors. | ||||
| func (e *Error) Temporary() bool { | ||||
| 	return e.Code() == BUSY | ||||
| } | ||||
| 
 | ||||
| // Timeout returns true for [BUSY_TIMEOUT] errors. | ||||
| func (e *Error) Timeout() bool { | ||||
| 	return e.ExtendedCode() == BUSY_TIMEOUT | ||||
| } | ||||
| 
 | ||||
| // SQL returns the SQL starting at the token that triggered a syntax error. | ||||
| func (e *Error) SQL() string { | ||||
| 	return e.sql | ||||
| } | ||||
| 
 | ||||
| // Error implements the error interface. | ||||
| func (e ErrorCode) Error() string { | ||||
| 	return util.ErrorCodeString(uint32(e)) | ||||
| } | ||||
| 
 | ||||
| // Temporary returns true for [BUSY] errors. | ||||
| func (e ErrorCode) Temporary() bool { | ||||
| 	return e == BUSY | ||||
| } | ||||
| 
 | ||||
| // Error implements the error interface. | ||||
| func (e ExtendedErrorCode) Error() string { | ||||
| 	return util.ErrorCodeString(uint32(e)) | ||||
| } | ||||
| 
 | ||||
| // Is tests whether this error matches a given [ErrorCode]. | ||||
| func (e ExtendedErrorCode) Is(err error) bool { | ||||
| 	c, ok := err.(ErrorCode) | ||||
| 	return ok && c == ErrorCode(e) | ||||
| } | ||||
| 
 | ||||
| // As converts this error to an [ErrorCode]. | ||||
| func (e ExtendedErrorCode) As(err any) bool { | ||||
| 	c, ok := err.(*ErrorCode) | ||||
| 	if ok { | ||||
| 		*c = ErrorCode(e) | ||||
| 	} | ||||
| 	return ok | ||||
| } | ||||
| 
 | ||||
| // Temporary returns true for [BUSY] errors. | ||||
| func (e ExtendedErrorCode) Temporary() bool { | ||||
| 	return ErrorCode(e) == BUSY | ||||
| } | ||||
| 
 | ||||
| // Timeout returns true for [BUSY_TIMEOUT] errors. | ||||
| func (e ExtendedErrorCode) Timeout() bool { | ||||
| 	return e == BUSY_TIMEOUT | ||||
| } | ||||
| 
 | ||||
| func errorCode(err error, def ErrorCode) (msg string, code uint32) { | ||||
| 	switch code := err.(type) { | ||||
| 	case nil: | ||||
| 		return "", _OK | ||||
| 	case ErrorCode: | ||||
| 		return "", uint32(code) | ||||
| 	case xErrorCode: | ||||
| 		return "", uint32(code) | ||||
| 	case *Error: | ||||
| 		return code.msg, uint32(code.code) | ||||
| 	} | ||||
| 
 | ||||
| 	var ecode ErrorCode | ||||
| 	var xcode xErrorCode | ||||
| 	switch { | ||||
| 	case errors.As(err, &xcode): | ||||
| 		code = uint32(xcode) | ||||
| 	case errors.As(err, &ecode): | ||||
| 		code = uint32(ecode) | ||||
| 	default: | ||||
| 		code = uint32(def) | ||||
| 	} | ||||
| 	return err.Error(), code | ||||
| } | ||||
							
								
								
									
										214
									
								
								vendor/github.com/ncruces/go-sqlite3/func.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										214
									
								
								vendor/github.com/ncruces/go-sqlite3/func.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,214 @@ | |||
| package sqlite3 | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"sync" | ||||
| 
 | ||||
| 	"github.com/ncruces/go-sqlite3/internal/util" | ||||
| 	"github.com/tetratelabs/wazero/api" | ||||
| ) | ||||
| 
 | ||||
| // CollationNeeded registers a callback to be invoked | ||||
| // whenever an unknown collation sequence is required. | ||||
| // | ||||
| // https://sqlite.org/c3ref/collation_needed.html | ||||
| func (c *Conn) CollationNeeded(cb func(db *Conn, name string)) error { | ||||
| 	var enable uint64 | ||||
| 	if cb != nil { | ||||
| 		enable = 1 | ||||
| 	} | ||||
| 	r := c.call("sqlite3_collation_needed_go", uint64(c.handle), enable) | ||||
| 	if err := c.error(r); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	c.collation = cb | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // AnyCollationNeeded uses [Conn.CollationNeeded] to register | ||||
| // a fake collating function for any unknown collating sequence. | ||||
| // The fake collating function works like BINARY. | ||||
| // | ||||
| // This can be used to load schemas that contain | ||||
| // one or more unknown collating sequences. | ||||
| func (c *Conn) AnyCollationNeeded() { | ||||
| 	c.call("sqlite3_anycollseq_init", uint64(c.handle), 0, 0) | ||||
| } | ||||
| 
 | ||||
| // CreateCollation defines a new collating sequence. | ||||
| // | ||||
| // https://sqlite.org/c3ref/create_collation.html | ||||
| func (c *Conn) CreateCollation(name string, fn func(a, b []byte) int) error { | ||||
| 	defer c.arena.mark()() | ||||
| 	namePtr := c.arena.string(name) | ||||
| 	funcPtr := util.AddHandle(c.ctx, fn) | ||||
| 	r := c.call("sqlite3_create_collation_go", | ||||
| 		uint64(c.handle), uint64(namePtr), uint64(funcPtr)) | ||||
| 	return c.error(r) | ||||
| } | ||||
| 
 | ||||
| // CreateFunction defines a new scalar SQL function. | ||||
| // | ||||
| // https://sqlite.org/c3ref/create_function.html | ||||
| func (c *Conn) CreateFunction(name string, nArg int, flag FunctionFlag, fn ScalarFunction) error { | ||||
| 	defer c.arena.mark()() | ||||
| 	namePtr := c.arena.string(name) | ||||
| 	funcPtr := util.AddHandle(c.ctx, fn) | ||||
| 	r := c.call("sqlite3_create_function_go", | ||||
| 		uint64(c.handle), uint64(namePtr), uint64(nArg), | ||||
| 		uint64(flag), uint64(funcPtr)) | ||||
| 	return c.error(r) | ||||
| } | ||||
| 
 | ||||
| // ScalarFunction is the type of a scalar SQL function. | ||||
| // Implementations must not retain arg. | ||||
| type ScalarFunction func(ctx Context, arg ...Value) | ||||
| 
 | ||||
| // CreateWindowFunction defines a new aggregate or aggregate window SQL function. | ||||
| // If fn returns a [WindowFunction], then an aggregate window function is created. | ||||
| // If fn returns an [io.Closer], it will be called to free resources. | ||||
| // | ||||
| // https://sqlite.org/c3ref/create_function.html | ||||
| func (c *Conn) CreateWindowFunction(name string, nArg int, flag FunctionFlag, fn func() AggregateFunction) error { | ||||
| 	defer c.arena.mark()() | ||||
| 	call := "sqlite3_create_aggregate_function_go" | ||||
| 	namePtr := c.arena.string(name) | ||||
| 	funcPtr := util.AddHandle(c.ctx, fn) | ||||
| 	if _, ok := fn().(WindowFunction); ok { | ||||
| 		call = "sqlite3_create_window_function_go" | ||||
| 	} | ||||
| 	r := c.call(call, | ||||
| 		uint64(c.handle), uint64(namePtr), uint64(nArg), | ||||
| 		uint64(flag), uint64(funcPtr)) | ||||
| 	return c.error(r) | ||||
| } | ||||
| 
 | ||||
| // AggregateFunction is the interface an aggregate function should implement. | ||||
| // | ||||
| // https://sqlite.org/appfunc.html | ||||
| type AggregateFunction interface { | ||||
| 	// Step is invoked to add a row to the current window. | ||||
| 	// The function arguments, if any, corresponding to the row being added, are passed to Step. | ||||
| 	// Implementations must not retain arg. | ||||
| 	Step(ctx Context, arg ...Value) | ||||
| 
 | ||||
| 	// Value is invoked to return the current (or final) value of the aggregate. | ||||
| 	Value(ctx Context) | ||||
| } | ||||
| 
 | ||||
| // WindowFunction is the interface an aggregate window function should implement. | ||||
| // | ||||
| // https://sqlite.org/windowfunctions.html | ||||
| type WindowFunction interface { | ||||
| 	AggregateFunction | ||||
| 
 | ||||
| 	// Inverse is invoked to remove the oldest presently aggregated result of Step from the current window. | ||||
| 	// The function arguments, if any, are those passed to Step for the row being removed. | ||||
| 	// Implementations must not retain arg. | ||||
| 	Inverse(ctx Context, arg ...Value) | ||||
| } | ||||
| 
 | ||||
| // OverloadFunction overloads a function for a virtual table. | ||||
| // | ||||
| // https://sqlite.org/c3ref/overload_function.html | ||||
| func (c *Conn) OverloadFunction(name string, nArg int) error { | ||||
| 	defer c.arena.mark()() | ||||
| 	namePtr := c.arena.string(name) | ||||
| 	r := c.call("sqlite3_overload_function", | ||||
| 		uint64(c.handle), uint64(namePtr), uint64(nArg)) | ||||
| 	return c.error(r) | ||||
| } | ||||
| 
 | ||||
| func destroyCallback(ctx context.Context, mod api.Module, pApp uint32) { | ||||
| 	util.DelHandle(ctx, pApp) | ||||
| } | ||||
| 
 | ||||
| func collationCallback(ctx context.Context, mod api.Module, pArg, pDB, eTextRep, zName uint32) { | ||||
| 	if c, ok := ctx.Value(connKey{}).(*Conn); ok && c.handle == pDB && c.collation != nil { | ||||
| 		name := util.ReadString(mod, zName, _MAX_NAME) | ||||
| 		c.collation(c, name) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func compareCallback(ctx context.Context, mod api.Module, pApp, nKey1, pKey1, nKey2, pKey2 uint32) uint32 { | ||||
| 	fn := util.GetHandle(ctx, pApp).(func(a, b []byte) int) | ||||
| 	return uint32(fn(util.View(mod, pKey1, uint64(nKey1)), util.View(mod, pKey2, uint64(nKey2)))) | ||||
| } | ||||
| 
 | ||||
| func funcCallback(ctx context.Context, mod api.Module, pCtx, pApp, nArg, pArg uint32) { | ||||
| 	args := getFuncArgs() | ||||
| 	defer putFuncArgs(args) | ||||
| 	db := ctx.Value(connKey{}).(*Conn) | ||||
| 	fn := util.GetHandle(db.ctx, pApp).(ScalarFunction) | ||||
| 	callbackArgs(db, args[:nArg], pArg) | ||||
| 	fn(Context{db, pCtx}, args[:nArg]...) | ||||
| } | ||||
| 
 | ||||
| func stepCallback(ctx context.Context, mod api.Module, pCtx, pAgg, pApp, nArg, pArg uint32) { | ||||
| 	args := getFuncArgs() | ||||
| 	defer putFuncArgs(args) | ||||
| 	db := ctx.Value(connKey{}).(*Conn) | ||||
| 	callbackArgs(db, args[:nArg], pArg) | ||||
| 	fn, _ := callbackAggregate(db, pAgg, pApp) | ||||
| 	fn.Step(Context{db, pCtx}, args[:nArg]...) | ||||
| } | ||||
| 
 | ||||
| func finalCallback(ctx context.Context, mod api.Module, pCtx, pAgg, pApp uint32) { | ||||
| 	db := ctx.Value(connKey{}).(*Conn) | ||||
| 	fn, handle := callbackAggregate(db, pAgg, pApp) | ||||
| 	fn.Value(Context{db, pCtx}) | ||||
| 	util.DelHandle(ctx, handle) | ||||
| } | ||||
| 
 | ||||
| func valueCallback(ctx context.Context, mod api.Module, pCtx, pAgg uint32) { | ||||
| 	db := ctx.Value(connKey{}).(*Conn) | ||||
| 	fn := util.GetHandle(db.ctx, pAgg).(AggregateFunction) | ||||
| 	fn.Value(Context{db, pCtx}) | ||||
| } | ||||
| 
 | ||||
| func inverseCallback(ctx context.Context, mod api.Module, pCtx, pAgg, nArg, pArg uint32) { | ||||
| 	args := getFuncArgs() | ||||
| 	defer putFuncArgs(args) | ||||
| 	db := ctx.Value(connKey{}).(*Conn) | ||||
| 	callbackArgs(db, args[:nArg], pArg) | ||||
| 	fn := util.GetHandle(db.ctx, pAgg).(WindowFunction) | ||||
| 	fn.Inverse(Context{db, pCtx}, args[:nArg]...) | ||||
| } | ||||
| 
 | ||||
| func callbackAggregate(db *Conn, pAgg, pApp uint32) (AggregateFunction, uint32) { | ||||
| 	if pApp == 0 { | ||||
| 		handle := util.ReadUint32(db.mod, pAgg) | ||||
| 		return util.GetHandle(db.ctx, handle).(AggregateFunction), handle | ||||
| 	} | ||||
| 
 | ||||
| 	// We need to create the aggregate. | ||||
| 	fn := util.GetHandle(db.ctx, pApp).(func() AggregateFunction)() | ||||
| 	handle := util.AddHandle(db.ctx, fn) | ||||
| 	if pAgg != 0 { | ||||
| 		util.WriteUint32(db.mod, pAgg, handle) | ||||
| 	} | ||||
| 	return fn, handle | ||||
| } | ||||
| 
 | ||||
| func callbackArgs(db *Conn, arg []Value, pArg uint32) { | ||||
| 	for i := range arg { | ||||
| 		arg[i] = Value{ | ||||
| 			c:      db, | ||||
| 			handle: util.ReadUint32(db.mod, pArg+ptrlen*uint32(i)), | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| var funcArgsPool sync.Pool | ||||
| 
 | ||||
| func putFuncArgs(p *[_MAX_FUNCTION_ARG]Value) { | ||||
| 	funcArgsPool.Put(p) | ||||
| } | ||||
| 
 | ||||
| func getFuncArgs() *[_MAX_FUNCTION_ARG]Value { | ||||
| 	if p := funcArgsPool.Get(); p == nil { | ||||
| 		return new([_MAX_FUNCTION_ARG]Value) | ||||
| 	} else { | ||||
| 		return p.(*[_MAX_FUNCTION_ARG]Value) | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										6
									
								
								vendor/github.com/ncruces/go-sqlite3/go.work
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								vendor/github.com/ncruces/go-sqlite3/go.work
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,6 @@ | |||
| go 1.21 | ||||
| 
 | ||||
| use ( | ||||
| 	. | ||||
| 	./gormlite | ||||
| ) | ||||
							
								
								
									
										9
									
								
								vendor/github.com/ncruces/go-sqlite3/go.work.sum
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								vendor/github.com/ncruces/go-sqlite3/go.work.sum
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,9 @@ | |||
| golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8= | ||||
| golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= | ||||
| golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= | ||||
| golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= | ||||
| golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q= | ||||
| golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= | ||||
| golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= | ||||
| golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= | ||||
| golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= | ||||
							
								
								
									
										9
									
								
								vendor/github.com/ncruces/go-sqlite3/internal/util/alloc_other.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								vendor/github.com/ncruces/go-sqlite3/internal/util/alloc_other.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,9 @@ | |||
| //go:build !(unix || windows) || sqlite3_nosys | ||||
| 
 | ||||
| package util | ||||
| 
 | ||||
| import "github.com/tetratelabs/wazero/experimental" | ||||
| 
 | ||||
| func virtualAlloc(cap, max uint64) experimental.LinearMemory { | ||||
| 	return sliceAlloc(cap, max) | ||||
| } | ||||
							
								
								
									
										25
									
								
								vendor/github.com/ncruces/go-sqlite3/internal/util/alloc_slice.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								vendor/github.com/ncruces/go-sqlite3/internal/util/alloc_slice.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,25 @@ | |||
| //go:build !(darwin || linux) || !(amd64 || arm64 || riscv64) || sqlite3_noshm || sqlite3_nosys | ||||
| 
 | ||||
| package util | ||||
| 
 | ||||
| import "github.com/tetratelabs/wazero/experimental" | ||||
| 
 | ||||
| func sliceAlloc(cap, max uint64) experimental.LinearMemory { | ||||
| 	return &sliceBuffer{make([]byte, cap), max} | ||||
| } | ||||
| 
 | ||||
| type sliceBuffer struct { | ||||
| 	buf []byte | ||||
| 	max uint64 | ||||
| } | ||||
| 
 | ||||
| func (b *sliceBuffer) Free() {} | ||||
| 
 | ||||
| func (b *sliceBuffer) Reallocate(size uint64) []byte { | ||||
| 	if cap := uint64(cap(b.buf)); size > cap { | ||||
| 		b.buf = append(b.buf[:cap], make([]byte, size-cap)...) | ||||
| 	} else { | ||||
| 		b.buf = b.buf[:size] | ||||
| 	} | ||||
| 	return b.buf | ||||
| } | ||||
							
								
								
									
										67
									
								
								vendor/github.com/ncruces/go-sqlite3/internal/util/alloc_unix.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								vendor/github.com/ncruces/go-sqlite3/internal/util/alloc_unix.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,67 @@ | |||
| //go:build unix && !sqlite3_nosys | ||||
| 
 | ||||
| package util | ||||
| 
 | ||||
| import ( | ||||
| 	"math" | ||||
| 
 | ||||
| 	"github.com/tetratelabs/wazero/experimental" | ||||
| 	"golang.org/x/sys/unix" | ||||
| ) | ||||
| 
 | ||||
| func virtualAlloc(cap, max uint64) experimental.LinearMemory { | ||||
| 	// Round up to the page size. | ||||
| 	rnd := uint64(unix.Getpagesize() - 1) | ||||
| 	max = (max + rnd) &^ rnd | ||||
| 
 | ||||
| 	if max > math.MaxInt { | ||||
| 		// This ensures int(max) overflows to a negative value, | ||||
| 		// and unix.Mmap returns EINVAL. | ||||
| 		max = math.MaxUint64 | ||||
| 	} | ||||
| 
 | ||||
| 	// Reserve max bytes of address space, to ensure we won't need to move it. | ||||
| 	// A protected, private, anonymous mapping should not commit memory. | ||||
| 	b, err := unix.Mmap(-1, 0, int(max), unix.PROT_NONE, unix.MAP_PRIVATE|unix.MAP_ANON) | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| 	return &mmappedMemory{buf: b[:0]} | ||||
| } | ||||
| 
 | ||||
| // The slice covers the entire mmapped memory: | ||||
| //   - len(buf) is the already committed memory, | ||||
| //   - cap(buf) is the reserved address space. | ||||
| type mmappedMemory struct { | ||||
| 	buf []byte | ||||
| } | ||||
| 
 | ||||
| func (m *mmappedMemory) Reallocate(size uint64) []byte { | ||||
| 	com := uint64(len(m.buf)) | ||||
| 	res := uint64(cap(m.buf)) | ||||
| 	if com < size && size < res { | ||||
| 		// Round up to the page size. | ||||
| 		rnd := uint64(unix.Getpagesize() - 1) | ||||
| 		new := (size + rnd) &^ rnd | ||||
| 
 | ||||
| 		// Commit additional memory up to new bytes. | ||||
| 		err := unix.Mprotect(m.buf[com:new], unix.PROT_READ|unix.PROT_WRITE) | ||||
| 		if err != nil { | ||||
| 			panic(err) | ||||
| 		} | ||||
| 
 | ||||
| 		// Update committed memory. | ||||
| 		m.buf = m.buf[:new] | ||||
| 	} | ||||
| 	// Limit returned capacity because bytes beyond | ||||
| 	// len(m.buf) have not yet been committed. | ||||
| 	return m.buf[:size:len(m.buf)] | ||||
| } | ||||
| 
 | ||||
| func (m *mmappedMemory) Free() { | ||||
| 	err := unix.Munmap(m.buf[:cap(m.buf)]) | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| 	m.buf = nil | ||||
| } | ||||
							
								
								
									
										76
									
								
								vendor/github.com/ncruces/go-sqlite3/internal/util/alloc_windows.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								vendor/github.com/ncruces/go-sqlite3/internal/util/alloc_windows.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,76 @@ | |||
| //go:build !sqlite3_nosys | ||||
| 
 | ||||
| package util | ||||
| 
 | ||||
| import ( | ||||
| 	"math" | ||||
| 	"reflect" | ||||
| 	"unsafe" | ||||
| 
 | ||||
| 	"github.com/tetratelabs/wazero/experimental" | ||||
| 	"golang.org/x/sys/windows" | ||||
| ) | ||||
| 
 | ||||
| func virtualAlloc(cap, max uint64) experimental.LinearMemory { | ||||
| 	// Round up to the page size. | ||||
| 	rnd := uint64(windows.Getpagesize() - 1) | ||||
| 	max = (max + rnd) &^ rnd | ||||
| 
 | ||||
| 	if max > math.MaxInt { | ||||
| 		// This ensures uintptr(max) overflows to a large value, | ||||
| 		// and windows.VirtualAlloc returns an error. | ||||
| 		max = math.MaxUint64 | ||||
| 	} | ||||
| 
 | ||||
| 	// Reserve max bytes of address space, to ensure we won't need to move it. | ||||
| 	// This does not commit memory. | ||||
| 	r, err := windows.VirtualAlloc(0, uintptr(max), windows.MEM_RESERVE, windows.PAGE_READWRITE) | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| 
 | ||||
| 	mem := virtualMemory{addr: r} | ||||
| 	// SliceHeader, although deprecated, avoids a go vet warning. | ||||
| 	sh := (*reflect.SliceHeader)(unsafe.Pointer(&mem.buf)) | ||||
| 	sh.Cap = int(max) // Not a bug. | ||||
| 	sh.Data = r | ||||
| 	return &mem | ||||
| } | ||||
| 
 | ||||
| // The slice covers the entire mmapped memory: | ||||
| //   - len(buf) is the already committed memory, | ||||
| //   - cap(buf) is the reserved address space. | ||||
| type virtualMemory struct { | ||||
| 	buf  []byte | ||||
| 	addr uintptr | ||||
| } | ||||
| 
 | ||||
| func (m *virtualMemory) Reallocate(size uint64) []byte { | ||||
| 	com := uint64(len(m.buf)) | ||||
| 	res := uint64(cap(m.buf)) | ||||
| 	if com < size && size < res { | ||||
| 		// Round up to the page size. | ||||
| 		rnd := uint64(windows.Getpagesize() - 1) | ||||
| 		new := (size + rnd) &^ rnd | ||||
| 
 | ||||
| 		// Commit additional memory up to new bytes. | ||||
| 		_, err := windows.VirtualAlloc(m.addr, uintptr(new), windows.MEM_COMMIT, windows.PAGE_READWRITE) | ||||
| 		if err != nil { | ||||
| 			panic(err) | ||||
| 		} | ||||
| 
 | ||||
| 		// Update committed memory. | ||||
| 		m.buf = m.buf[:new] | ||||
| 	} | ||||
| 	// Limit returned capacity because bytes beyond | ||||
| 	// len(m.buf) have not yet been committed. | ||||
| 	return m.buf[:size:len(m.buf)] | ||||
| } | ||||
| 
 | ||||
| func (m *virtualMemory) Free() { | ||||
| 	err := windows.VirtualFree(m.addr, 0, windows.MEM_RELEASE) | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| 	m.addr = 0 | ||||
| } | ||||
							
								
								
									
										22
									
								
								vendor/github.com/ncruces/go-sqlite3/internal/util/bool.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								vendor/github.com/ncruces/go-sqlite3/internal/util/bool.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,22 @@ | |||
| package util | ||||
| 
 | ||||
| import "strings" | ||||
| 
 | ||||
| func ParseBool(s string) (b, ok bool) { | ||||
| 	if len(s) == 0 { | ||||
| 		return false, false | ||||
| 	} | ||||
| 	if s[0] == '0' { | ||||
| 		return false, true | ||||
| 	} | ||||
| 	if '1' <= s[0] && s[0] <= '9' { | ||||
| 		return true, true | ||||
| 	} | ||||
| 	switch strings.ToLower(s) { | ||||
| 	case "true", "yes", "on": | ||||
| 		return true, true | ||||
| 	case "false", "no", "off": | ||||
| 		return false, true | ||||
| 	} | ||||
| 	return false, false | ||||
| } | ||||
							
								
								
									
										117
									
								
								vendor/github.com/ncruces/go-sqlite3/internal/util/const.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										117
									
								
								vendor/github.com/ncruces/go-sqlite3/internal/util/const.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,117 @@ | |||
| package util | ||||
| 
 | ||||
| // https://sqlite.com/matrix/rescode.html | ||||
| const ( | ||||
| 	OK = 0 /* Successful result */ | ||||
| 
 | ||||
| 	ERROR      = 1  /* Generic error */ | ||||
| 	INTERNAL   = 2  /* Internal logic error in SQLite */ | ||||
| 	PERM       = 3  /* Access permission denied */ | ||||
| 	ABORT      = 4  /* Callback routine requested an abort */ | ||||
| 	BUSY       = 5  /* The database file is locked */ | ||||
| 	LOCKED     = 6  /* A table in the database is locked */ | ||||
| 	NOMEM      = 7  /* A malloc() failed */ | ||||
| 	READONLY   = 8  /* Attempt to write a readonly database */ | ||||
| 	INTERRUPT  = 9  /* Operation terminated by sqlite3_interrupt() */ | ||||
| 	IOERR      = 10 /* Some kind of disk I/O error occurred */ | ||||
| 	CORRUPT    = 11 /* The database disk image is malformed */ | ||||
| 	NOTFOUND   = 12 /* Unknown opcode in sqlite3_file_control() */ | ||||
| 	FULL       = 13 /* Insertion failed because database is full */ | ||||
| 	CANTOPEN   = 14 /* Unable to open the database file */ | ||||
| 	PROTOCOL   = 15 /* Database lock protocol error */ | ||||
| 	EMPTY      = 16 /* Internal use only */ | ||||
| 	SCHEMA     = 17 /* The database schema changed */ | ||||
| 	TOOBIG     = 18 /* String or BLOB exceeds size limit */ | ||||
| 	CONSTRAINT = 19 /* Abort due to constraint violation */ | ||||
| 	MISMATCH   = 20 /* Data type mismatch */ | ||||
| 	MISUSE     = 21 /* Library used incorrectly */ | ||||
| 	NOLFS      = 22 /* Uses OS features not supported on host */ | ||||
| 	AUTH       = 23 /* Authorization denied */ | ||||
| 	FORMAT     = 24 /* Not used */ | ||||
| 	RANGE      = 25 /* 2nd parameter to sqlite3_bind out of range */ | ||||
| 	NOTADB     = 26 /* File opened that is not a database file */ | ||||
| 	NOTICE     = 27 /* Notifications from sqlite3_log() */ | ||||
| 	WARNING    = 28 /* Warnings from sqlite3_log() */ | ||||
| 
 | ||||
| 	ROW  = 100 /* sqlite3_step() has another row ready */ | ||||
| 	DONE = 101 /* sqlite3_step() has finished executing */ | ||||
| 
 | ||||
| 	ERROR_MISSING_COLLSEQ   = ERROR | (1 << 8) | ||||
| 	ERROR_RETRY             = ERROR | (2 << 8) | ||||
| 	ERROR_SNAPSHOT          = ERROR | (3 << 8) | ||||
| 	IOERR_READ              = IOERR | (1 << 8) | ||||
| 	IOERR_SHORT_READ        = IOERR | (2 << 8) | ||||
| 	IOERR_WRITE             = IOERR | (3 << 8) | ||||
| 	IOERR_FSYNC             = IOERR | (4 << 8) | ||||
| 	IOERR_DIR_FSYNC         = IOERR | (5 << 8) | ||||
| 	IOERR_TRUNCATE          = IOERR | (6 << 8) | ||||
| 	IOERR_FSTAT             = IOERR | (7 << 8) | ||||
| 	IOERR_UNLOCK            = IOERR | (8 << 8) | ||||
| 	IOERR_RDLOCK            = IOERR | (9 << 8) | ||||
| 	IOERR_DELETE            = IOERR | (10 << 8) | ||||
| 	IOERR_BLOCKED           = IOERR | (11 << 8) | ||||
| 	IOERR_NOMEM             = IOERR | (12 << 8) | ||||
| 	IOERR_ACCESS            = IOERR | (13 << 8) | ||||
| 	IOERR_CHECKRESERVEDLOCK = IOERR | (14 << 8) | ||||
| 	IOERR_LOCK              = IOERR | (15 << 8) | ||||
| 	IOERR_CLOSE             = IOERR | (16 << 8) | ||||
| 	IOERR_DIR_CLOSE         = IOERR | (17 << 8) | ||||
| 	IOERR_SHMOPEN           = IOERR | (18 << 8) | ||||
| 	IOERR_SHMSIZE           = IOERR | (19 << 8) | ||||
| 	IOERR_SHMLOCK           = IOERR | (20 << 8) | ||||
| 	IOERR_SHMMAP            = IOERR | (21 << 8) | ||||
| 	IOERR_SEEK              = IOERR | (22 << 8) | ||||
| 	IOERR_DELETE_NOENT      = IOERR | (23 << 8) | ||||
| 	IOERR_MMAP              = IOERR | (24 << 8) | ||||
| 	IOERR_GETTEMPPATH       = IOERR | (25 << 8) | ||||
| 	IOERR_CONVPATH          = IOERR | (26 << 8) | ||||
| 	IOERR_VNODE             = IOERR | (27 << 8) | ||||
| 	IOERR_AUTH              = IOERR | (28 << 8) | ||||
| 	IOERR_BEGIN_ATOMIC      = IOERR | (29 << 8) | ||||
| 	IOERR_COMMIT_ATOMIC     = IOERR | (30 << 8) | ||||
| 	IOERR_ROLLBACK_ATOMIC   = IOERR | (31 << 8) | ||||
| 	IOERR_DATA              = IOERR | (32 << 8) | ||||
| 	IOERR_CORRUPTFS         = IOERR | (33 << 8) | ||||
| 	IOERR_IN_PAGE           = IOERR | (34 << 8) | ||||
| 	LOCKED_SHAREDCACHE      = LOCKED | (1 << 8) | ||||
| 	LOCKED_VTAB             = LOCKED | (2 << 8) | ||||
| 	BUSY_RECOVERY           = BUSY | (1 << 8) | ||||
| 	BUSY_SNAPSHOT           = BUSY | (2 << 8) | ||||
| 	BUSY_TIMEOUT            = BUSY | (3 << 8) | ||||
| 	CANTOPEN_NOTEMPDIR      = CANTOPEN | (1 << 8) | ||||
| 	CANTOPEN_ISDIR          = CANTOPEN | (2 << 8) | ||||
| 	CANTOPEN_FULLPATH       = CANTOPEN | (3 << 8) | ||||
| 	CANTOPEN_CONVPATH       = CANTOPEN | (4 << 8) | ||||
| 	CANTOPEN_DIRTYWAL       = CANTOPEN | (5 << 8) /* Not Used */ | ||||
| 	CANTOPEN_SYMLINK        = CANTOPEN | (6 << 8) | ||||
| 	CORRUPT_VTAB            = CORRUPT | (1 << 8) | ||||
| 	CORRUPT_SEQUENCE        = CORRUPT | (2 << 8) | ||||
| 	CORRUPT_INDEX           = CORRUPT | (3 << 8) | ||||
| 	READONLY_RECOVERY       = READONLY | (1 << 8) | ||||
| 	READONLY_CANTLOCK       = READONLY | (2 << 8) | ||||
| 	READONLY_ROLLBACK       = READONLY | (3 << 8) | ||||
| 	READONLY_DBMOVED        = READONLY | (4 << 8) | ||||
| 	READONLY_CANTINIT       = READONLY | (5 << 8) | ||||
| 	READONLY_DIRECTORY      = READONLY | (6 << 8) | ||||
| 	ABORT_ROLLBACK          = ABORT | (2 << 8) | ||||
| 	CONSTRAINT_CHECK        = CONSTRAINT | (1 << 8) | ||||
| 	CONSTRAINT_COMMITHOOK   = CONSTRAINT | (2 << 8) | ||||
| 	CONSTRAINT_FOREIGNKEY   = CONSTRAINT | (3 << 8) | ||||
| 	CONSTRAINT_FUNCTION     = CONSTRAINT | (4 << 8) | ||||
| 	CONSTRAINT_NOTNULL      = CONSTRAINT | (5 << 8) | ||||
| 	CONSTRAINT_PRIMARYKEY   = CONSTRAINT | (6 << 8) | ||||
| 	CONSTRAINT_TRIGGER      = CONSTRAINT | (7 << 8) | ||||
| 	CONSTRAINT_UNIQUE       = CONSTRAINT | (8 << 8) | ||||
| 	CONSTRAINT_VTAB         = CONSTRAINT | (9 << 8) | ||||
| 	CONSTRAINT_ROWID        = CONSTRAINT | (10 << 8) | ||||
| 	CONSTRAINT_PINNED       = CONSTRAINT | (11 << 8) | ||||
| 	CONSTRAINT_DATATYPE     = CONSTRAINT | (12 << 8) | ||||
| 	NOTICE_RECOVER_WAL      = NOTICE | (1 << 8) | ||||
| 	NOTICE_RECOVER_ROLLBACK = NOTICE | (2 << 8) | ||||
| 	NOTICE_RBU              = NOTICE | (3 << 8) | ||||
| 	WARNING_AUTOINDEX       = WARNING | (1 << 8) | ||||
| 	AUTH_USER               = AUTH | (1 << 8) | ||||
| 
 | ||||
| 	OK_LOAD_PERMANENTLY = OK | (1 << 8) | ||||
| 	OK_SYMLINK          = OK | (2 << 8) /* internal use only */ | ||||
| ) | ||||
							
								
								
									
										106
									
								
								vendor/github.com/ncruces/go-sqlite3/internal/util/error.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								vendor/github.com/ncruces/go-sqlite3/internal/util/error.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,106 @@ | |||
| package util | ||||
| 
 | ||||
| import ( | ||||
| 	"runtime" | ||||
| 	"strconv" | ||||
| ) | ||||
| 
 | ||||
| type ErrorString string | ||||
| 
 | ||||
| func (e ErrorString) Error() string { return string(e) } | ||||
| 
 | ||||
| const ( | ||||
| 	NilErr       = ErrorString("sqlite3: invalid memory address or null pointer dereference") | ||||
| 	OOMErr       = ErrorString("sqlite3: out of memory") | ||||
| 	RangeErr     = ErrorString("sqlite3: index out of range") | ||||
| 	NoNulErr     = ErrorString("sqlite3: missing NUL terminator") | ||||
| 	NoBinaryErr  = ErrorString("sqlite3: no SQLite binary embed/set/loaded") | ||||
| 	BadBinaryErr = ErrorString("sqlite3: invalid SQLite binary embed/set/loaded") | ||||
| 	TimeErr      = ErrorString("sqlite3: invalid time value") | ||||
| 	WhenceErr    = ErrorString("sqlite3: invalid whence") | ||||
| 	OffsetErr    = ErrorString("sqlite3: invalid offset") | ||||
| 	TailErr      = ErrorString("sqlite3: multiple statements") | ||||
| 	IsolationErr = ErrorString("sqlite3: unsupported isolation level") | ||||
| 	ValueErr     = ErrorString("sqlite3: unsupported value") | ||||
| 	NoVFSErr     = ErrorString("sqlite3: no such vfs: ") | ||||
| ) | ||||
| 
 | ||||
| func AssertErr() ErrorString { | ||||
| 	msg := "sqlite3: assertion failed" | ||||
| 	if _, file, line, ok := runtime.Caller(1); ok { | ||||
| 		msg += " (" + file + ":" + strconv.Itoa(line) + ")" | ||||
| 	} | ||||
| 	return ErrorString(msg) | ||||
| } | ||||
| 
 | ||||
| func ErrorCodeString(rc uint32) string { | ||||
| 	switch rc { | ||||
| 	case ABORT_ROLLBACK: | ||||
| 		return "sqlite3: abort due to ROLLBACK" | ||||
| 	case ROW: | ||||
| 		return "sqlite3: another row available" | ||||
| 	case DONE: | ||||
| 		return "sqlite3: no more rows available" | ||||
| 	} | ||||
| 	switch rc & 0xff { | ||||
| 	case OK: | ||||
| 		return "sqlite3: not an error" | ||||
| 	case ERROR: | ||||
| 		return "sqlite3: SQL logic error" | ||||
| 	case INTERNAL: | ||||
| 		break | ||||
| 	case PERM: | ||||
| 		return "sqlite3: access permission denied" | ||||
| 	case ABORT: | ||||
| 		return "sqlite3: query aborted" | ||||
| 	case BUSY: | ||||
| 		return "sqlite3: database is locked" | ||||
| 	case LOCKED: | ||||
| 		return "sqlite3: database table is locked" | ||||
| 	case NOMEM: | ||||
| 		return "sqlite3: out of memory" | ||||
| 	case READONLY: | ||||
| 		return "sqlite3: attempt to write a readonly database" | ||||
| 	case INTERRUPT: | ||||
| 		return "sqlite3: interrupted" | ||||
| 	case IOERR: | ||||
| 		return "sqlite3: disk I/O error" | ||||
| 	case CORRUPT: | ||||
| 		return "sqlite3: database disk image is malformed" | ||||
| 	case NOTFOUND: | ||||
| 		return "sqlite3: unknown operation" | ||||
| 	case FULL: | ||||
| 		return "sqlite3: database or disk is full" | ||||
| 	case CANTOPEN: | ||||
| 		return "sqlite3: unable to open database file" | ||||
| 	case PROTOCOL: | ||||
| 		return "sqlite3: locking protocol" | ||||
| 	case FORMAT: | ||||
| 		break | ||||
| 	case SCHEMA: | ||||
| 		return "sqlite3: database schema has changed" | ||||
| 	case TOOBIG: | ||||
| 		return "sqlite3: string or blob too big" | ||||
| 	case CONSTRAINT: | ||||
| 		return "sqlite3: constraint failed" | ||||
| 	case MISMATCH: | ||||
| 		return "sqlite3: datatype mismatch" | ||||
| 	case MISUSE: | ||||
| 		return "sqlite3: bad parameter or other API misuse" | ||||
| 	case NOLFS: | ||||
| 		break | ||||
| 	case AUTH: | ||||
| 		return "sqlite3: authorization denied" | ||||
| 	case EMPTY: | ||||
| 		break | ||||
| 	case RANGE: | ||||
| 		return "sqlite3: column index out of range" | ||||
| 	case NOTADB: | ||||
| 		return "sqlite3: file is not a database" | ||||
| 	case NOTICE: | ||||
| 		return "sqlite3: notification message" | ||||
| 	case WARNING: | ||||
| 		return "sqlite3: warning message" | ||||
| 	} | ||||
| 	return "sqlite3: unknown error" | ||||
| } | ||||
							
								
								
									
										193
									
								
								vendor/github.com/ncruces/go-sqlite3/internal/util/func.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										193
									
								
								vendor/github.com/ncruces/go-sqlite3/internal/util/func.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,193 @@ | |||
| package util | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 
 | ||||
| 	"github.com/tetratelabs/wazero" | ||||
| 	"github.com/tetratelabs/wazero/api" | ||||
| ) | ||||
| 
 | ||||
| type i32 interface{ ~int32 | ~uint32 } | ||||
| type i64 interface{ ~int64 | ~uint64 } | ||||
| 
 | ||||
| type funcVI[T0 i32] func(context.Context, api.Module, T0) | ||||
| 
 | ||||
| func (fn funcVI[T0]) Call(ctx context.Context, mod api.Module, stack []uint64) { | ||||
| 	fn(ctx, mod, T0(stack[0])) | ||||
| } | ||||
| 
 | ||||
| func ExportFuncVI[T0 i32](mod wazero.HostModuleBuilder, name string, fn func(context.Context, api.Module, T0)) { | ||||
| 	mod.NewFunctionBuilder(). | ||||
| 		WithGoModuleFunction(funcVI[T0](fn), | ||||
| 			[]api.ValueType{api.ValueTypeI32}, nil). | ||||
| 		Export(name) | ||||
| } | ||||
| 
 | ||||
| type funcVII[T0, T1 i32] func(context.Context, api.Module, T0, T1) | ||||
| 
 | ||||
| func (fn funcVII[T0, T1]) Call(ctx context.Context, mod api.Module, stack []uint64) { | ||||
| 	fn(ctx, mod, T0(stack[0]), T1(stack[1])) | ||||
| } | ||||
| 
 | ||||
| func ExportFuncVII[T0, T1 i32](mod wazero.HostModuleBuilder, name string, fn func(context.Context, api.Module, T0, T1)) { | ||||
| 	mod.NewFunctionBuilder(). | ||||
| 		WithGoModuleFunction(funcVII[T0, T1](fn), | ||||
| 			[]api.ValueType{api.ValueTypeI32, api.ValueTypeI32}, nil). | ||||
| 		Export(name) | ||||
| } | ||||
| 
 | ||||
| type funcVIII[T0, T1, T2 i32] func(context.Context, api.Module, T0, T1, T2) | ||||
| 
 | ||||
| func (fn funcVIII[T0, T1, T2]) Call(ctx context.Context, mod api.Module, stack []uint64) { | ||||
| 	fn(ctx, mod, T0(stack[0]), T1(stack[1]), T2(stack[2])) | ||||
| } | ||||
| 
 | ||||
| func ExportFuncVIII[T0, T1, T2 i32](mod wazero.HostModuleBuilder, name string, fn func(context.Context, api.Module, T0, T1, T2)) { | ||||
| 	mod.NewFunctionBuilder(). | ||||
| 		WithGoModuleFunction(funcVIII[T0, T1, T2](fn), | ||||
| 			[]api.ValueType{api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32}, nil). | ||||
| 		Export(name) | ||||
| } | ||||
| 
 | ||||
| type funcVIIII[T0, T1, T2, T3 i32] func(context.Context, api.Module, T0, T1, T2, T3) | ||||
| 
 | ||||
| func (fn funcVIIII[T0, T1, T2, T3]) Call(ctx context.Context, mod api.Module, stack []uint64) { | ||||
| 	fn(ctx, mod, T0(stack[0]), T1(stack[1]), T2(stack[2]), T3(stack[3])) | ||||
| } | ||||
| 
 | ||||
| func ExportFuncVIIII[T0, T1, T2, T3 i32](mod wazero.HostModuleBuilder, name string, fn func(context.Context, api.Module, T0, T1, T2, T3)) { | ||||
| 	mod.NewFunctionBuilder(). | ||||
| 		WithGoModuleFunction(funcVIIII[T0, T1, T2, T3](fn), | ||||
| 			[]api.ValueType{api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32}, nil). | ||||
| 		Export(name) | ||||
| } | ||||
| 
 | ||||
| type funcVIIIII[T0, T1, T2, T3, T4 i32] func(context.Context, api.Module, T0, T1, T2, T3, T4) | ||||
| 
 | ||||
| func (fn funcVIIIII[T0, T1, T2, T3, T4]) Call(ctx context.Context, mod api.Module, stack []uint64) { | ||||
| 	fn(ctx, mod, T0(stack[0]), T1(stack[1]), T2(stack[2]), T3(stack[3]), T4(stack[4])) | ||||
| } | ||||
| 
 | ||||
| func ExportFuncVIIIII[T0, T1, T2, T3, T4 i32](mod wazero.HostModuleBuilder, name string, fn func(context.Context, api.Module, T0, T1, T2, T3, T4)) { | ||||
| 	mod.NewFunctionBuilder(). | ||||
| 		WithGoModuleFunction(funcVIIIII[T0, T1, T2, T3, T4](fn), | ||||
| 			[]api.ValueType{api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32}, nil). | ||||
| 		Export(name) | ||||
| } | ||||
| 
 | ||||
| type funcVIIIIJ[T0, T1, T2, T3 i32, T4 i64] func(context.Context, api.Module, T0, T1, T2, T3, T4) | ||||
| 
 | ||||
| func (fn funcVIIIIJ[T0, T1, T2, T3, T4]) Call(ctx context.Context, mod api.Module, stack []uint64) { | ||||
| 	fn(ctx, mod, T0(stack[0]), T1(stack[1]), T2(stack[2]), T3(stack[3]), T4(stack[4])) | ||||
| } | ||||
| 
 | ||||
| func ExportFuncVIIIIJ[T0, T1, T2, T3 i32, T4 i64](mod wazero.HostModuleBuilder, name string, fn func(context.Context, api.Module, T0, T1, T2, T3, T4)) { | ||||
| 	mod.NewFunctionBuilder(). | ||||
| 		WithGoModuleFunction(funcVIIIIJ[T0, T1, T2, T3, T4](fn), | ||||
| 			[]api.ValueType{api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI64}, nil). | ||||
| 		Export(name) | ||||
| } | ||||
| 
 | ||||
| type funcII[TR, T0 i32] func(context.Context, api.Module, T0) TR | ||||
| 
 | ||||
| func (fn funcII[TR, T0]) Call(ctx context.Context, mod api.Module, stack []uint64) { | ||||
| 	stack[0] = uint64(fn(ctx, mod, T0(stack[0]))) | ||||
| } | ||||
| 
 | ||||
| func ExportFuncII[TR, T0 i32](mod wazero.HostModuleBuilder, name string, fn func(context.Context, api.Module, T0) TR) { | ||||
| 	mod.NewFunctionBuilder(). | ||||
| 		WithGoModuleFunction(funcII[TR, T0](fn), | ||||
| 			[]api.ValueType{api.ValueTypeI32}, []api.ValueType{api.ValueTypeI32}). | ||||
| 		Export(name) | ||||
| } | ||||
| 
 | ||||
| type funcIII[TR, T0, T1 i32] func(context.Context, api.Module, T0, T1) TR | ||||
| 
 | ||||
| func (fn funcIII[TR, T0, T1]) Call(ctx context.Context, mod api.Module, stack []uint64) { | ||||
| 	stack[0] = uint64(fn(ctx, mod, T0(stack[0]), T1(stack[1]))) | ||||
| } | ||||
| 
 | ||||
| func ExportFuncIII[TR, T0, T1 i32](mod wazero.HostModuleBuilder, name string, fn func(context.Context, api.Module, T0, T1) TR) { | ||||
| 	mod.NewFunctionBuilder(). | ||||
| 		WithGoModuleFunction(funcIII[TR, T0, T1](fn), | ||||
| 			[]api.ValueType{api.ValueTypeI32, api.ValueTypeI32}, []api.ValueType{api.ValueTypeI32}). | ||||
| 		Export(name) | ||||
| } | ||||
| 
 | ||||
| type funcIIII[TR, T0, T1, T2 i32] func(context.Context, api.Module, T0, T1, T2) TR | ||||
| 
 | ||||
| func (fn funcIIII[TR, T0, T1, T2]) Call(ctx context.Context, mod api.Module, stack []uint64) { | ||||
| 	stack[0] = uint64(fn(ctx, mod, T0(stack[0]), T1(stack[1]), T2(stack[2]))) | ||||
| } | ||||
| 
 | ||||
| func ExportFuncIIII[TR, T0, T1, T2 i32](mod wazero.HostModuleBuilder, name string, fn func(context.Context, api.Module, T0, T1, T2) TR) { | ||||
| 	mod.NewFunctionBuilder(). | ||||
| 		WithGoModuleFunction(funcIIII[TR, T0, T1, T2](fn), | ||||
| 			[]api.ValueType{api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32}, []api.ValueType{api.ValueTypeI32}). | ||||
| 		Export(name) | ||||
| } | ||||
| 
 | ||||
| type funcIIIII[TR, T0, T1, T2, T3 i32] func(context.Context, api.Module, T0, T1, T2, T3) TR | ||||
| 
 | ||||
| func (fn funcIIIII[TR, T0, T1, T2, T3]) Call(ctx context.Context, mod api.Module, stack []uint64) { | ||||
| 	stack[0] = uint64(fn(ctx, mod, T0(stack[0]), T1(stack[1]), T2(stack[2]), T3(stack[3]))) | ||||
| } | ||||
| 
 | ||||
| func ExportFuncIIIII[TR, T0, T1, T2, T3 i32](mod wazero.HostModuleBuilder, name string, fn func(context.Context, api.Module, T0, T1, T2, T3) TR) { | ||||
| 	mod.NewFunctionBuilder(). | ||||
| 		WithGoModuleFunction(funcIIIII[TR, T0, T1, T2, T3](fn), | ||||
| 			[]api.ValueType{api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32}, []api.ValueType{api.ValueTypeI32}). | ||||
| 		Export(name) | ||||
| } | ||||
| 
 | ||||
| type funcIIIIII[TR, T0, T1, T2, T3, T4 i32] func(context.Context, api.Module, T0, T1, T2, T3, T4) TR | ||||
| 
 | ||||
| func (fn funcIIIIII[TR, T0, T1, T2, T3, T4]) Call(ctx context.Context, mod api.Module, stack []uint64) { | ||||
| 	stack[0] = uint64(fn(ctx, mod, T0(stack[0]), T1(stack[1]), T2(stack[2]), T3(stack[3]), T4(stack[4]))) | ||||
| } | ||||
| 
 | ||||
| func ExportFuncIIIIII[TR, T0, T1, T2, T3, T4 i32](mod wazero.HostModuleBuilder, name string, fn func(context.Context, api.Module, T0, T1, T2, T3, T4) TR) { | ||||
| 	mod.NewFunctionBuilder(). | ||||
| 		WithGoModuleFunction(funcIIIIII[TR, T0, T1, T2, T3, T4](fn), | ||||
| 			[]api.ValueType{api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32}, []api.ValueType{api.ValueTypeI32}). | ||||
| 		Export(name) | ||||
| } | ||||
| 
 | ||||
| type funcIIIIIII[TR, T0, T1, T2, T3, T4, T5 i32] func(context.Context, api.Module, T0, T1, T2, T3, T4, T5) TR | ||||
| 
 | ||||
| func (fn funcIIIIIII[TR, T0, T1, T2, T3, T4, T5]) Call(ctx context.Context, mod api.Module, stack []uint64) { | ||||
| 	stack[0] = uint64(fn(ctx, mod, T0(stack[0]), T1(stack[1]), T2(stack[2]), T3(stack[3]), T4(stack[4]), T5(stack[5]))) | ||||
| } | ||||
| 
 | ||||
| func ExportFuncIIIIIII[TR, T0, T1, T2, T3, T4, T5 i32](mod wazero.HostModuleBuilder, name string, fn func(context.Context, api.Module, T0, T1, T2, T3, T4, T5) TR) { | ||||
| 	mod.NewFunctionBuilder(). | ||||
| 		WithGoModuleFunction(funcIIIIIII[TR, T0, T1, T2, T3, T4, T5](fn), | ||||
| 			[]api.ValueType{api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32}, []api.ValueType{api.ValueTypeI32}). | ||||
| 		Export(name) | ||||
| } | ||||
| 
 | ||||
| type funcIIIIJ[TR, T0, T1, T2 i32, T3 i64] func(context.Context, api.Module, T0, T1, T2, T3) TR | ||||
| 
 | ||||
| func (fn funcIIIIJ[TR, T0, T1, T2, T3]) Call(ctx context.Context, mod api.Module, stack []uint64) { | ||||
| 	stack[0] = uint64(fn(ctx, mod, T0(stack[0]), T1(stack[1]), T2(stack[2]), T3(stack[3]))) | ||||
| } | ||||
| 
 | ||||
| func ExportFuncIIIIJ[TR, T0, T1, T2 i32, T3 i64](mod wazero.HostModuleBuilder, name string, fn func(context.Context, api.Module, T0, T1, T2, T3) TR) { | ||||
| 	mod.NewFunctionBuilder(). | ||||
| 		WithGoModuleFunction(funcIIIIJ[TR, T0, T1, T2, T3](fn), | ||||
| 			[]api.ValueType{api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI64}, []api.ValueType{api.ValueTypeI32}). | ||||
| 		Export(name) | ||||
| } | ||||
| 
 | ||||
| type funcIIJ[TR, T0 i32, T1 i64] func(context.Context, api.Module, T0, T1) TR | ||||
| 
 | ||||
| func (fn funcIIJ[TR, T0, T1]) Call(ctx context.Context, mod api.Module, stack []uint64) { | ||||
| 	stack[0] = uint64(fn(ctx, mod, T0(stack[0]), T1(stack[1]))) | ||||
| } | ||||
| 
 | ||||
| func ExportFuncIIJ[TR, T0 i32, T1 i64](mod wazero.HostModuleBuilder, name string, fn func(context.Context, api.Module, T0, T1) TR) { | ||||
| 	mod.NewFunctionBuilder(). | ||||
| 		WithGoModuleFunction(funcIIJ[TR, T0, T1](fn), | ||||
| 			[]api.ValueType{api.ValueTypeI32, api.ValueTypeI64}, []api.ValueType{api.ValueTypeI32}). | ||||
| 		Export(name) | ||||
| } | ||||
							
								
								
									
										65
									
								
								vendor/github.com/ncruces/go-sqlite3/internal/util/handle.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								vendor/github.com/ncruces/go-sqlite3/internal/util/handle.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,65 @@ | |||
| package util | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"io" | ||||
| ) | ||||
| 
 | ||||
| type handleState struct { | ||||
| 	handles []any | ||||
| 	holes   int | ||||
| } | ||||
| 
 | ||||
| func (s *handleState) CloseNotify(ctx context.Context, exitCode uint32) { | ||||
| 	for _, h := range s.handles { | ||||
| 		if c, ok := h.(io.Closer); ok { | ||||
| 			c.Close() | ||||
| 		} | ||||
| 	} | ||||
| 	s.handles = nil | ||||
| 	s.holes = 0 | ||||
| } | ||||
| 
 | ||||
| func GetHandle(ctx context.Context, id uint32) any { | ||||
| 	if id == 0 { | ||||
| 		return nil | ||||
| 	} | ||||
| 	s := ctx.Value(moduleKey{}).(*moduleState) | ||||
| 	return s.handles[^id] | ||||
| } | ||||
| 
 | ||||
| func DelHandle(ctx context.Context, id uint32) error { | ||||
| 	if id == 0 { | ||||
| 		return nil | ||||
| 	} | ||||
| 	s := ctx.Value(moduleKey{}).(*moduleState) | ||||
| 	a := s.handles[^id] | ||||
| 	s.handles[^id] = nil | ||||
| 	s.holes++ | ||||
| 	if c, ok := a.(io.Closer); ok { | ||||
| 		return c.Close() | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func AddHandle(ctx context.Context, a any) (id uint32) { | ||||
| 	if a == nil { | ||||
| 		panic(NilErr) | ||||
| 	} | ||||
| 	s := ctx.Value(moduleKey{}).(*moduleState) | ||||
| 
 | ||||
| 	// Find an empty slot. | ||||
| 	if s.holes > cap(s.handles)-len(s.handles) { | ||||
| 		for id, h := range s.handles { | ||||
| 			if h == nil { | ||||
| 				s.holes-- | ||||
| 				s.handles[id] = a | ||||
| 				return ^uint32(id) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// Add a new slot. | ||||
| 	s.handles = append(s.handles, a) | ||||
| 	return -uint32(len(s.handles)) | ||||
| } | ||||
							
								
								
									
										35
									
								
								vendor/github.com/ncruces/go-sqlite3/internal/util/json.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								vendor/github.com/ncruces/go-sqlite3/internal/util/json.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,35 @@ | |||
| package util | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"strconv" | ||||
| 	"time" | ||||
| 	"unsafe" | ||||
| ) | ||||
| 
 | ||||
| type JSON struct{ Value any } | ||||
| 
 | ||||
| func (j JSON) Scan(value any) error { | ||||
| 	var buf []byte | ||||
| 
 | ||||
| 	switch v := value.(type) { | ||||
| 	case []byte: | ||||
| 		buf = v | ||||
| 	case string: | ||||
| 		buf = unsafe.Slice(unsafe.StringData(v), len(v)) | ||||
| 	case int64: | ||||
| 		buf = strconv.AppendInt(nil, v, 10) | ||||
| 	case float64: | ||||
| 		buf = strconv.AppendFloat(nil, v, 'g', -1, 64) | ||||
| 	case time.Time: | ||||
| 		buf = append(buf, '"') | ||||
| 		buf = v.AppendFormat(buf, time.RFC3339Nano) | ||||
| 		buf = append(buf, '"') | ||||
| 	case nil: | ||||
| 		buf = append(buf, "null"...) | ||||
| 	default: | ||||
| 		panic(AssertErr()) | ||||
| 	} | ||||
| 
 | ||||
| 	return json.Unmarshal(buf, j.Value) | ||||
| } | ||||
							
								
								
									
										134
									
								
								vendor/github.com/ncruces/go-sqlite3/internal/util/mem.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										134
									
								
								vendor/github.com/ncruces/go-sqlite3/internal/util/mem.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,134 @@ | |||
| package util | ||||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"math" | ||||
| 
 | ||||
| 	"github.com/tetratelabs/wazero/api" | ||||
| ) | ||||
| 
 | ||||
| func View(mod api.Module, ptr uint32, size uint64) []byte { | ||||
| 	if ptr == 0 { | ||||
| 		panic(NilErr) | ||||
| 	} | ||||
| 	if size > math.MaxUint32 { | ||||
| 		panic(RangeErr) | ||||
| 	} | ||||
| 	if size == 0 { | ||||
| 		return nil | ||||
| 	} | ||||
| 	buf, ok := mod.Memory().Read(ptr, uint32(size)) | ||||
| 	if !ok { | ||||
| 		panic(RangeErr) | ||||
| 	} | ||||
| 	return buf | ||||
| } | ||||
| 
 | ||||
| func ReadUint8(mod api.Module, ptr uint32) uint8 { | ||||
| 	if ptr == 0 { | ||||
| 		panic(NilErr) | ||||
| 	} | ||||
| 	v, ok := mod.Memory().ReadByte(ptr) | ||||
| 	if !ok { | ||||
| 		panic(RangeErr) | ||||
| 	} | ||||
| 	return v | ||||
| } | ||||
| 
 | ||||
| func ReadUint32(mod api.Module, ptr uint32) uint32 { | ||||
| 	if ptr == 0 { | ||||
| 		panic(NilErr) | ||||
| 	} | ||||
| 	v, ok := mod.Memory().ReadUint32Le(ptr) | ||||
| 	if !ok { | ||||
| 		panic(RangeErr) | ||||
| 	} | ||||
| 	return v | ||||
| } | ||||
| 
 | ||||
| func WriteUint8(mod api.Module, ptr uint32, v uint8) { | ||||
| 	if ptr == 0 { | ||||
| 		panic(NilErr) | ||||
| 	} | ||||
| 	ok := mod.Memory().WriteByte(ptr, v) | ||||
| 	if !ok { | ||||
| 		panic(RangeErr) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func WriteUint32(mod api.Module, ptr uint32, v uint32) { | ||||
| 	if ptr == 0 { | ||||
| 		panic(NilErr) | ||||
| 	} | ||||
| 	ok := mod.Memory().WriteUint32Le(ptr, v) | ||||
| 	if !ok { | ||||
| 		panic(RangeErr) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func ReadUint64(mod api.Module, ptr uint32) uint64 { | ||||
| 	if ptr == 0 { | ||||
| 		panic(NilErr) | ||||
| 	} | ||||
| 	v, ok := mod.Memory().ReadUint64Le(ptr) | ||||
| 	if !ok { | ||||
| 		panic(RangeErr) | ||||
| 	} | ||||
| 	return v | ||||
| } | ||||
| 
 | ||||
| func WriteUint64(mod api.Module, ptr uint32, v uint64) { | ||||
| 	if ptr == 0 { | ||||
| 		panic(NilErr) | ||||
| 	} | ||||
| 	ok := mod.Memory().WriteUint64Le(ptr, v) | ||||
| 	if !ok { | ||||
| 		panic(RangeErr) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func ReadFloat64(mod api.Module, ptr uint32) float64 { | ||||
| 	return math.Float64frombits(ReadUint64(mod, ptr)) | ||||
| } | ||||
| 
 | ||||
| func WriteFloat64(mod api.Module, ptr uint32, v float64) { | ||||
| 	WriteUint64(mod, ptr, math.Float64bits(v)) | ||||
| } | ||||
| 
 | ||||
| func ReadString(mod api.Module, ptr, maxlen uint32) string { | ||||
| 	if ptr == 0 { | ||||
| 		panic(NilErr) | ||||
| 	} | ||||
| 	switch maxlen { | ||||
| 	case 0: | ||||
| 		return "" | ||||
| 	case math.MaxUint32: | ||||
| 		// avoid overflow | ||||
| 	default: | ||||
| 		maxlen = maxlen + 1 | ||||
| 	} | ||||
| 	mem := mod.Memory() | ||||
| 	buf, ok := mem.Read(ptr, maxlen) | ||||
| 	if !ok { | ||||
| 		buf, ok = mem.Read(ptr, mem.Size()-ptr) | ||||
| 		if !ok { | ||||
| 			panic(RangeErr) | ||||
| 		} | ||||
| 	} | ||||
| 	if i := bytes.IndexByte(buf, 0); i < 0 { | ||||
| 		panic(NoNulErr) | ||||
| 	} else { | ||||
| 		return string(buf[:i]) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func WriteBytes(mod api.Module, ptr uint32, b []byte) { | ||||
| 	buf := View(mod, ptr, uint64(len(b))) | ||||
| 	copy(buf, b) | ||||
| } | ||||
| 
 | ||||
| func WriteString(mod api.Module, ptr uint32, s string) { | ||||
| 	buf := View(mod, ptr, uint64(len(s)+1)) | ||||
| 	buf[len(s)] = 0 | ||||
| 	copy(buf, s) | ||||
| } | ||||
							
								
								
									
										97
									
								
								vendor/github.com/ncruces/go-sqlite3/internal/util/mmap.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								vendor/github.com/ncruces/go-sqlite3/internal/util/mmap.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,97 @@ | |||
| //go:build (darwin || linux) && (amd64 || arm64 || riscv64) && !(sqlite3_noshm || sqlite3_nosys) | ||||
| 
 | ||||
| package util | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"os" | ||||
| 	"unsafe" | ||||
| 
 | ||||
| 	"github.com/tetratelabs/wazero/api" | ||||
| 	"github.com/tetratelabs/wazero/experimental" | ||||
| 	"golang.org/x/sys/unix" | ||||
| ) | ||||
| 
 | ||||
| func withAllocator(ctx context.Context) context.Context { | ||||
| 	return experimental.WithMemoryAllocator(ctx, | ||||
| 		experimental.MemoryAllocatorFunc(virtualAlloc)) | ||||
| } | ||||
| 
 | ||||
| type mmapState struct { | ||||
| 	regions []*MappedRegion | ||||
| } | ||||
| 
 | ||||
| func (s *mmapState) new(ctx context.Context, mod api.Module, size int32) *MappedRegion { | ||||
| 	// Find unused region. | ||||
| 	for _, r := range s.regions { | ||||
| 		if !r.used && r.size == size { | ||||
| 			return r | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// Allocate page aligned memmory. | ||||
| 	alloc := mod.ExportedFunction("aligned_alloc") | ||||
| 	stack := [2]uint64{ | ||||
| 		uint64(unix.Getpagesize()), | ||||
| 		uint64(size), | ||||
| 	} | ||||
| 	if err := alloc.CallWithStack(ctx, stack[:]); err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| 	if stack[0] == 0 { | ||||
| 		panic(OOMErr) | ||||
| 	} | ||||
| 
 | ||||
| 	// Save the newly allocated region. | ||||
| 	ptr := uint32(stack[0]) | ||||
| 	buf := View(mod, ptr, uint64(size)) | ||||
| 	addr := uintptr(unsafe.Pointer(&buf[0])) | ||||
| 	s.regions = append(s.regions, &MappedRegion{ | ||||
| 		Ptr:  ptr, | ||||
| 		addr: addr, | ||||
| 		size: size, | ||||
| 	}) | ||||
| 	return s.regions[len(s.regions)-1] | ||||
| } | ||||
| 
 | ||||
| type MappedRegion struct { | ||||
| 	addr uintptr | ||||
| 	Ptr  uint32 | ||||
| 	size int32 | ||||
| 	used bool | ||||
| } | ||||
| 
 | ||||
| func MapRegion(ctx context.Context, mod api.Module, f *os.File, offset int64, size int32, prot int) (*MappedRegion, error) { | ||||
| 	s := ctx.Value(moduleKey{}).(*moduleState) | ||||
| 	r := s.new(ctx, mod, size) | ||||
| 	err := r.mmap(f, offset, prot) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return r, nil | ||||
| } | ||||
| 
 | ||||
| func (r *MappedRegion) Unmap() error { | ||||
| 	// We can't munmap the region, otherwise it could be remaped. | ||||
| 	// Instead, convert it to a protected, private, anonymous mapping. | ||||
| 	// If successful, it can be reused for a subsequent mmap. | ||||
| 	_, err := mmap(r.addr, uintptr(r.size), | ||||
| 		unix.PROT_NONE, unix.MAP_PRIVATE|unix.MAP_ANON|unix.MAP_FIXED, | ||||
| 		-1, 0) | ||||
| 	r.used = err != nil | ||||
| 	return err | ||||
| } | ||||
| 
 | ||||
| func (r *MappedRegion) mmap(f *os.File, offset int64, prot int) error { | ||||
| 	_, err := mmap(r.addr, uintptr(r.size), | ||||
| 		prot, unix.MAP_SHARED|unix.MAP_FIXED, | ||||
| 		int(f.Fd()), offset) | ||||
| 	r.used = err == nil | ||||
| 	return err | ||||
| } | ||||
| 
 | ||||
| // We need the low level mmap for MAP_FIXED to work. | ||||
| // Bind the syscall version hoping that it is more stable. | ||||
| 
 | ||||
| //go:linkname mmap syscall.mmap | ||||
| func mmap(addr, length uintptr, prot, flag, fd int, pos int64) (*byte, error) | ||||
							
								
								
									
										21
									
								
								vendor/github.com/ncruces/go-sqlite3/internal/util/mmap_other.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								vendor/github.com/ncruces/go-sqlite3/internal/util/mmap_other.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,21 @@ | |||
| //go:build !(darwin || linux) || !(amd64 || arm64 || riscv64) || sqlite3_noshm || sqlite3_nosys | ||||
| 
 | ||||
| package util | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 
 | ||||
| 	"github.com/tetratelabs/wazero/experimental" | ||||
| ) | ||||
| 
 | ||||
| type mmapState struct{} | ||||
| 
 | ||||
| func withAllocator(ctx context.Context) context.Context { | ||||
| 	return experimental.WithMemoryAllocator(ctx, | ||||
| 		experimental.MemoryAllocatorFunc(func(cap, max uint64) experimental.LinearMemory { | ||||
| 			if cap == max { | ||||
| 				return virtualAlloc(cap, max) | ||||
| 			} | ||||
| 			return sliceAlloc(cap, max) | ||||
| 		})) | ||||
| } | ||||
							
								
								
									
										21
									
								
								vendor/github.com/ncruces/go-sqlite3/internal/util/module.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								vendor/github.com/ncruces/go-sqlite3/internal/util/module.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,21 @@ | |||
| package util | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 
 | ||||
| 	"github.com/tetratelabs/wazero/experimental" | ||||
| ) | ||||
| 
 | ||||
| type moduleKey struct{} | ||||
| type moduleState struct { | ||||
| 	mmapState | ||||
| 	handleState | ||||
| } | ||||
| 
 | ||||
| func NewContext(ctx context.Context) context.Context { | ||||
| 	state := new(moduleState) | ||||
| 	ctx = withAllocator(ctx) | ||||
| 	ctx = experimental.WithCloseNotifier(ctx, state) | ||||
| 	ctx = context.WithValue(ctx, moduleKey{}, state) | ||||
| 	return ctx | ||||
| } | ||||
							
								
								
									
										11
									
								
								vendor/github.com/ncruces/go-sqlite3/internal/util/pointer.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								vendor/github.com/ncruces/go-sqlite3/internal/util/pointer.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,11 @@ | |||
| package util | ||||
| 
 | ||||
| type Pointer[T any] struct{ Value T } | ||||
| 
 | ||||
| func (p Pointer[T]) unwrap() any { return p.Value } | ||||
| 
 | ||||
| type PointerUnwrap interface{ unwrap() any } | ||||
| 
 | ||||
| func UnwrapPointer(p PointerUnwrap) any { | ||||
| 	return p.unwrap() | ||||
| } | ||||
							
								
								
									
										10
									
								
								vendor/github.com/ncruces/go-sqlite3/internal/util/reflect.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								vendor/github.com/ncruces/go-sqlite3/internal/util/reflect.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,10 @@ | |||
| package util | ||||
| 
 | ||||
| import "reflect" | ||||
| 
 | ||||
| func ReflectType(v reflect.Value) reflect.Type { | ||||
| 	if v.Kind() != reflect.Invalid { | ||||
| 		return v.Type() | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										11
									
								
								vendor/github.com/ncruces/go-sqlite3/json.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								vendor/github.com/ncruces/go-sqlite3/json.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,11 @@ | |||
| package sqlite3 | ||||
| 
 | ||||
| import "github.com/ncruces/go-sqlite3/internal/util" | ||||
| 
 | ||||
| // JSON returns a value that can be used as an argument to | ||||
| // [database/sql.DB.Exec], [database/sql.Row.Scan] and similar methods to | ||||
| // store value as JSON, or decode JSON into value. | ||||
| // JSON should NOT be used with [BindJSON] or [ResultJSON]. | ||||
| func JSON(value any) any { | ||||
| 	return util.JSON{Value: value} | ||||
| } | ||||
							
								
								
									
										12
									
								
								vendor/github.com/ncruces/go-sqlite3/pointer.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								vendor/github.com/ncruces/go-sqlite3/pointer.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,12 @@ | |||
| package sqlite3 | ||||
| 
 | ||||
| import "github.com/ncruces/go-sqlite3/internal/util" | ||||
| 
 | ||||
| // Pointer returns a pointer to a value that can be used as an argument to | ||||
| // [database/sql.DB.Exec] and similar methods. | ||||
| // Pointer should NOT be used with [BindPointer] or [ResultPointer]. | ||||
| // | ||||
| // https://sqlite.org/bindptr.html | ||||
| func Pointer[T any](value T) any { | ||||
| 	return util.Pointer[T]{Value: value} | ||||
| } | ||||
							
								
								
									
										112
									
								
								vendor/github.com/ncruces/go-sqlite3/quote.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								vendor/github.com/ncruces/go-sqlite3/quote.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,112 @@ | |||
| package sqlite3 | ||||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"math" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 	"time" | ||||
| 	"unsafe" | ||||
| 
 | ||||
| 	"github.com/ncruces/go-sqlite3/internal/util" | ||||
| ) | ||||
| 
 | ||||
| // Quote escapes and quotes a value | ||||
| // making it safe to embed in SQL text. | ||||
| func Quote(value any) string { | ||||
| 	switch v := value.(type) { | ||||
| 	case nil: | ||||
| 		return "NULL" | ||||
| 	case bool: | ||||
| 		if v { | ||||
| 			return "1" | ||||
| 		} else { | ||||
| 			return "0" | ||||
| 		} | ||||
| 
 | ||||
| 	case int: | ||||
| 		return strconv.Itoa(v) | ||||
| 	case int64: | ||||
| 		return strconv.FormatInt(v, 10) | ||||
| 	case float64: | ||||
| 		switch { | ||||
| 		case math.IsNaN(v): | ||||
| 			return "NULL" | ||||
| 		case math.IsInf(v, 1): | ||||
| 			return "9.0e999" | ||||
| 		case math.IsInf(v, -1): | ||||
| 			return "-9.0e999" | ||||
| 		} | ||||
| 		return strconv.FormatFloat(v, 'g', -1, 64) | ||||
| 	case time.Time: | ||||
| 		return "'" + v.Format(time.RFC3339Nano) + "'" | ||||
| 
 | ||||
| 	case string: | ||||
| 		if strings.IndexByte(v, 0) >= 0 { | ||||
| 			break | ||||
| 		} | ||||
| 
 | ||||
| 		buf := make([]byte, 2+len(v)+strings.Count(v, "'")) | ||||
| 		buf[0] = '\'' | ||||
| 		i := 1 | ||||
| 		for _, b := range []byte(v) { | ||||
| 			if b == '\'' { | ||||
| 				buf[i] = b | ||||
| 				i += 1 | ||||
| 			} | ||||
| 			buf[i] = b | ||||
| 			i += 1 | ||||
| 		} | ||||
| 		buf[i] = '\'' | ||||
| 		return unsafe.String(&buf[0], len(buf)) | ||||
| 
 | ||||
| 	case []byte: | ||||
| 		buf := make([]byte, 3+2*len(v)) | ||||
| 		buf[0] = 'x' | ||||
| 		buf[1] = '\'' | ||||
| 		i := 2 | ||||
| 		for _, b := range v { | ||||
| 			const hex = "0123456789ABCDEF" | ||||
| 			buf[i+0] = hex[b/16] | ||||
| 			buf[i+1] = hex[b%16] | ||||
| 			i += 2 | ||||
| 		} | ||||
| 		buf[i] = '\'' | ||||
| 		return unsafe.String(&buf[0], len(buf)) | ||||
| 
 | ||||
| 	case ZeroBlob: | ||||
| 		if v > ZeroBlob(1e9-3)/2 { | ||||
| 			break | ||||
| 		} | ||||
| 
 | ||||
| 		buf := bytes.Repeat([]byte("0"), int(3+2*int64(v))) | ||||
| 		buf[0] = 'x' | ||||
| 		buf[1] = '\'' | ||||
| 		buf[len(buf)-1] = '\'' | ||||
| 		return unsafe.String(&buf[0], len(buf)) | ||||
| 	} | ||||
| 
 | ||||
| 	panic(util.ValueErr) | ||||
| } | ||||
| 
 | ||||
| // QuoteIdentifier escapes and quotes an identifier | ||||
| // making it safe to embed in SQL text. | ||||
| func QuoteIdentifier(id string) string { | ||||
| 	if strings.IndexByte(id, 0) >= 0 { | ||||
| 		panic(util.ValueErr) | ||||
| 	} | ||||
| 
 | ||||
| 	buf := make([]byte, 2+len(id)+strings.Count(id, `"`)) | ||||
| 	buf[0] = '"' | ||||
| 	i := 1 | ||||
| 	for _, b := range []byte(id) { | ||||
| 		if b == '"' { | ||||
| 			buf[i] = b | ||||
| 			i += 1 | ||||
| 		} | ||||
| 		buf[i] = b | ||||
| 		i += 1 | ||||
| 	} | ||||
| 	buf[i] = '"' | ||||
| 	return unsafe.String(&buf[0], len(buf)) | ||||
| } | ||||
							
								
								
									
										341
									
								
								vendor/github.com/ncruces/go-sqlite3/sqlite.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										341
									
								
								vendor/github.com/ncruces/go-sqlite3/sqlite.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,341 @@ | |||
| // Package sqlite3 wraps the C SQLite API. | ||||
| package sqlite3 | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"math" | ||||
| 	"math/bits" | ||||
| 	"os" | ||||
| 	"sync" | ||||
| 	"unsafe" | ||||
| 
 | ||||
| 	"github.com/ncruces/go-sqlite3/internal/util" | ||||
| 	"github.com/ncruces/go-sqlite3/vfs" | ||||
| 	"github.com/tetratelabs/wazero" | ||||
| 	"github.com/tetratelabs/wazero/api" | ||||
| ) | ||||
| 
 | ||||
| // Configure SQLite Wasm. | ||||
| // | ||||
| // Importing package embed initializes [Binary] | ||||
| // with an appropriate build of SQLite: | ||||
| // | ||||
| //	import _ "github.com/ncruces/go-sqlite3/embed" | ||||
| var ( | ||||
| 	Binary []byte // Wasm binary to load. | ||||
| 	Path   string // Path to load the binary from. | ||||
| 
 | ||||
| 	RuntimeConfig wazero.RuntimeConfig | ||||
| ) | ||||
| 
 | ||||
| // Initialize decodes and compiles the SQLite Wasm binary. | ||||
| // This is called implicitly when the first connection is openned, | ||||
| // but is potentially slow, so you may want to call it at a more convenient time. | ||||
| func Initialize() error { | ||||
| 	instance.once.Do(compileSQLite) | ||||
| 	return instance.err | ||||
| } | ||||
| 
 | ||||
| var instance struct { | ||||
| 	runtime  wazero.Runtime | ||||
| 	compiled wazero.CompiledModule | ||||
| 	err      error | ||||
| 	once     sync.Once | ||||
| } | ||||
| 
 | ||||
| func compileSQLite() { | ||||
| 	if RuntimeConfig == nil { | ||||
| 		RuntimeConfig = wazero.NewRuntimeConfig() | ||||
| 	} | ||||
| 
 | ||||
| 	ctx := context.Background() | ||||
| 	instance.runtime = wazero.NewRuntimeWithConfig(ctx, RuntimeConfig) | ||||
| 
 | ||||
| 	env := instance.runtime.NewHostModuleBuilder("env") | ||||
| 	env = vfs.ExportHostFunctions(env) | ||||
| 	env = exportCallbacks(env) | ||||
| 	_, instance.err = env.Instantiate(ctx) | ||||
| 	if instance.err != nil { | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	bin := Binary | ||||
| 	if bin == nil && Path != "" { | ||||
| 		bin, instance.err = os.ReadFile(Path) | ||||
| 		if instance.err != nil { | ||||
| 			return | ||||
| 		} | ||||
| 	} | ||||
| 	if bin == nil { | ||||
| 		instance.err = util.NoBinaryErr | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	instance.compiled, instance.err = instance.runtime.CompileModule(ctx, bin) | ||||
| } | ||||
| 
 | ||||
| type sqlite struct { | ||||
| 	ctx   context.Context | ||||
| 	mod   api.Module | ||||
| 	funcs struct { | ||||
| 		fn   [32]api.Function | ||||
| 		id   [32]*byte | ||||
| 		mask uint32 | ||||
| 	} | ||||
| 	stack [8]uint64 | ||||
| 	freer uint32 | ||||
| } | ||||
| 
 | ||||
| func instantiateSQLite() (sqlt *sqlite, err error) { | ||||
| 	if err := Initialize(); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	sqlt = new(sqlite) | ||||
| 	sqlt.ctx = util.NewContext(context.Background()) | ||||
| 
 | ||||
| 	sqlt.mod, err = instance.runtime.InstantiateModule(sqlt.ctx, | ||||
| 		instance.compiled, wazero.NewModuleConfig().WithName("")) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	global := sqlt.mod.ExportedGlobal("malloc_destructor") | ||||
| 	if global == nil { | ||||
| 		return nil, util.BadBinaryErr | ||||
| 	} | ||||
| 
 | ||||
| 	sqlt.freer = util.ReadUint32(sqlt.mod, uint32(global.Get())) | ||||
| 	if sqlt.freer == 0 { | ||||
| 		return nil, util.BadBinaryErr | ||||
| 	} | ||||
| 	return sqlt, nil | ||||
| } | ||||
| 
 | ||||
| func (sqlt *sqlite) close() error { | ||||
| 	return sqlt.mod.Close(sqlt.ctx) | ||||
| } | ||||
| 
 | ||||
| func (sqlt *sqlite) error(rc uint64, handle uint32, sql ...string) error { | ||||
| 	if rc == _OK { | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	err := Error{code: rc} | ||||
| 
 | ||||
| 	if err.Code() == NOMEM || err.ExtendedCode() == IOERR_NOMEM { | ||||
| 		panic(util.OOMErr) | ||||
| 	} | ||||
| 
 | ||||
| 	if r := sqlt.call("sqlite3_errstr", rc); r != 0 { | ||||
| 		err.str = util.ReadString(sqlt.mod, uint32(r), _MAX_NAME) | ||||
| 	} | ||||
| 
 | ||||
| 	if handle != 0 { | ||||
| 		if r := sqlt.call("sqlite3_errmsg", uint64(handle)); r != 0 { | ||||
| 			err.msg = util.ReadString(sqlt.mod, uint32(r), _MAX_LENGTH) | ||||
| 		} | ||||
| 
 | ||||
| 		if sql != nil { | ||||
| 			if r := sqlt.call("sqlite3_error_offset", uint64(handle)); r != math.MaxUint32 { | ||||
| 				err.sql = sql[0][r:] | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	switch err.msg { | ||||
| 	case err.str, "not an error": | ||||
| 		err.msg = "" | ||||
| 	} | ||||
| 	return &err | ||||
| } | ||||
| 
 | ||||
| func (sqlt *sqlite) getfn(name string) api.Function { | ||||
| 	c := &sqlt.funcs | ||||
| 	p := unsafe.StringData(name) | ||||
| 	for i := range c.id { | ||||
| 		if c.id[i] == p { | ||||
| 			c.id[i] = nil | ||||
| 			c.mask &^= uint32(1) << i | ||||
| 			return c.fn[i] | ||||
| 		} | ||||
| 	} | ||||
| 	return sqlt.mod.ExportedFunction(name) | ||||
| } | ||||
| 
 | ||||
| func (sqlt *sqlite) putfn(name string, fn api.Function) { | ||||
| 	c := &sqlt.funcs | ||||
| 	p := unsafe.StringData(name) | ||||
| 	i := bits.TrailingZeros32(^c.mask) | ||||
| 	if i < 32 { | ||||
| 		c.id[i] = p | ||||
| 		c.fn[i] = fn | ||||
| 		c.mask |= uint32(1) << i | ||||
| 	} else { | ||||
| 		c.id[0] = p | ||||
| 		c.fn[0] = fn | ||||
| 		c.mask = uint32(1) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (sqlt *sqlite) call(name string, params ...uint64) uint64 { | ||||
| 	copy(sqlt.stack[:], params) | ||||
| 	fn := sqlt.getfn(name) | ||||
| 	err := fn.CallWithStack(sqlt.ctx, sqlt.stack[:]) | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| 	sqlt.putfn(name, fn) | ||||
| 	return sqlt.stack[0] | ||||
| } | ||||
| 
 | ||||
| func (sqlt *sqlite) free(ptr uint32) { | ||||
| 	if ptr == 0 { | ||||
| 		return | ||||
| 	} | ||||
| 	sqlt.call("free", uint64(ptr)) | ||||
| } | ||||
| 
 | ||||
| func (sqlt *sqlite) new(size uint64) uint32 { | ||||
| 	if size > _MAX_ALLOCATION_SIZE { | ||||
| 		panic(util.OOMErr) | ||||
| 	} | ||||
| 	ptr := uint32(sqlt.call("malloc", size)) | ||||
| 	if ptr == 0 && size != 0 { | ||||
| 		panic(util.OOMErr) | ||||
| 	} | ||||
| 	return ptr | ||||
| } | ||||
| 
 | ||||
| func (sqlt *sqlite) newBytes(b []byte) uint32 { | ||||
| 	if (*[0]byte)(b) == nil { | ||||
| 		return 0 | ||||
| 	} | ||||
| 	ptr := sqlt.new(uint64(len(b))) | ||||
| 	util.WriteBytes(sqlt.mod, ptr, b) | ||||
| 	return ptr | ||||
| } | ||||
| 
 | ||||
| func (sqlt *sqlite) newString(s string) uint32 { | ||||
| 	ptr := sqlt.new(uint64(len(s) + 1)) | ||||
| 	util.WriteString(sqlt.mod, ptr, s) | ||||
| 	return ptr | ||||
| } | ||||
| 
 | ||||
| func (sqlt *sqlite) newArena(size uint64) arena { | ||||
| 	// Ensure the arena's size is a multiple of 8. | ||||
| 	size = (size + 7) &^ 7 | ||||
| 	return arena{ | ||||
| 		sqlt: sqlt, | ||||
| 		size: uint32(size), | ||||
| 		base: sqlt.new(size), | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| type arena struct { | ||||
| 	sqlt *sqlite | ||||
| 	ptrs []uint32 | ||||
| 	base uint32 | ||||
| 	next uint32 | ||||
| 	size uint32 | ||||
| } | ||||
| 
 | ||||
| func (a *arena) free() { | ||||
| 	if a.sqlt == nil { | ||||
| 		return | ||||
| 	} | ||||
| 	for _, ptr := range a.ptrs { | ||||
| 		a.sqlt.free(ptr) | ||||
| 	} | ||||
| 	a.sqlt.free(a.base) | ||||
| 	a.sqlt = nil | ||||
| } | ||||
| 
 | ||||
| func (a *arena) mark() (reset func()) { | ||||
| 	ptrs := len(a.ptrs) | ||||
| 	next := a.next | ||||
| 	return func() { | ||||
| 		for _, ptr := range a.ptrs[ptrs:] { | ||||
| 			a.sqlt.free(ptr) | ||||
| 		} | ||||
| 		a.ptrs = a.ptrs[:ptrs] | ||||
| 		a.next = next | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (a *arena) new(size uint64) uint32 { | ||||
| 	// Align the next address, to 4 or 8 bytes. | ||||
| 	if size&7 != 0 { | ||||
| 		a.next = (a.next + 3) &^ 3 | ||||
| 	} else { | ||||
| 		a.next = (a.next + 7) &^ 7 | ||||
| 	} | ||||
| 	if size <= uint64(a.size-a.next) { | ||||
| 		ptr := a.base + a.next | ||||
| 		a.next += uint32(size) | ||||
| 		return ptr | ||||
| 	} | ||||
| 	ptr := a.sqlt.new(size) | ||||
| 	a.ptrs = append(a.ptrs, ptr) | ||||
| 	return ptr | ||||
| } | ||||
| 
 | ||||
| func (a *arena) bytes(b []byte) uint32 { | ||||
| 	if (*[0]byte)(b) == nil { | ||||
| 		return 0 | ||||
| 	} | ||||
| 	ptr := a.new(uint64(len(b))) | ||||
| 	util.WriteBytes(a.sqlt.mod, ptr, b) | ||||
| 	return ptr | ||||
| } | ||||
| 
 | ||||
| func (a *arena) string(s string) uint32 { | ||||
| 	ptr := a.new(uint64(len(s) + 1)) | ||||
| 	util.WriteString(a.sqlt.mod, ptr, s) | ||||
| 	return ptr | ||||
| } | ||||
| 
 | ||||
| func exportCallbacks(env wazero.HostModuleBuilder) wazero.HostModuleBuilder { | ||||
| 	util.ExportFuncII(env, "go_progress_handler", progressCallback) | ||||
| 	util.ExportFuncIIII(env, "go_busy_timeout", timeoutCallback) | ||||
| 	util.ExportFuncIII(env, "go_busy_handler", busyCallback) | ||||
| 	util.ExportFuncII(env, "go_commit_hook", commitCallback) | ||||
| 	util.ExportFuncVI(env, "go_rollback_hook", rollbackCallback) | ||||
| 	util.ExportFuncVIIIIJ(env, "go_update_hook", updateCallback) | ||||
| 	util.ExportFuncIIIII(env, "go_wal_hook", walCallback) | ||||
| 	util.ExportFuncIIIIII(env, "go_autovacuum_pages", autoVacuumCallback) | ||||
| 	util.ExportFuncIIIIIII(env, "go_authorizer", authorizerCallback) | ||||
| 	util.ExportFuncVIII(env, "go_log", logCallback) | ||||
| 	util.ExportFuncVI(env, "go_destroy", destroyCallback) | ||||
| 	util.ExportFuncVIIII(env, "go_func", funcCallback) | ||||
| 	util.ExportFuncVIIIII(env, "go_step", stepCallback) | ||||
| 	util.ExportFuncVIII(env, "go_final", finalCallback) | ||||
| 	util.ExportFuncVII(env, "go_value", valueCallback) | ||||
| 	util.ExportFuncVIIII(env, "go_inverse", inverseCallback) | ||||
| 	util.ExportFuncVIIII(env, "go_collation_needed", collationCallback) | ||||
| 	util.ExportFuncIIIIII(env, "go_compare", compareCallback) | ||||
| 	util.ExportFuncIIIIII(env, "go_vtab_create", vtabModuleCallback(xCreate)) | ||||
| 	util.ExportFuncIIIIII(env, "go_vtab_connect", vtabModuleCallback(xConnect)) | ||||
| 	util.ExportFuncII(env, "go_vtab_disconnect", vtabDisconnectCallback) | ||||
| 	util.ExportFuncII(env, "go_vtab_destroy", vtabDestroyCallback) | ||||
| 	util.ExportFuncIII(env, "go_vtab_best_index", vtabBestIndexCallback) | ||||
| 	util.ExportFuncIIIII(env, "go_vtab_update", vtabUpdateCallback) | ||||
| 	util.ExportFuncIII(env, "go_vtab_rename", vtabRenameCallback) | ||||
| 	util.ExportFuncIIIII(env, "go_vtab_find_function", vtabFindFuncCallback) | ||||
| 	util.ExportFuncII(env, "go_vtab_begin", vtabBeginCallback) | ||||
| 	util.ExportFuncII(env, "go_vtab_sync", vtabSyncCallback) | ||||
| 	util.ExportFuncII(env, "go_vtab_commit", vtabCommitCallback) | ||||
| 	util.ExportFuncII(env, "go_vtab_rollback", vtabRollbackCallback) | ||||
| 	util.ExportFuncIII(env, "go_vtab_savepoint", vtabSavepointCallback) | ||||
| 	util.ExportFuncIII(env, "go_vtab_release", vtabReleaseCallback) | ||||
| 	util.ExportFuncIII(env, "go_vtab_rollback_to", vtabRollbackToCallback) | ||||
| 	util.ExportFuncIIIIII(env, "go_vtab_integrity", vtabIntegrityCallback) | ||||
| 	util.ExportFuncIII(env, "go_cur_open", cursorOpenCallback) | ||||
| 	util.ExportFuncII(env, "go_cur_close", cursorCloseCallback) | ||||
| 	util.ExportFuncIIIIII(env, "go_cur_filter", cursorFilterCallback) | ||||
| 	util.ExportFuncII(env, "go_cur_next", cursorNextCallback) | ||||
| 	util.ExportFuncII(env, "go_cur_eof", cursorEOFCallback) | ||||
| 	util.ExportFuncIIII(env, "go_cur_column", cursorColumnCallback) | ||||
| 	util.ExportFuncIII(env, "go_cur_rowid", cursorRowIDCallback) | ||||
| 	return env | ||||
| } | ||||
							
								
								
									
										639
									
								
								vendor/github.com/ncruces/go-sqlite3/stmt.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										639
									
								
								vendor/github.com/ncruces/go-sqlite3/stmt.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,639 @@ | |||
| package sqlite3 | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"math" | ||||
| 	"strconv" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/ncruces/go-sqlite3/internal/util" | ||||
| ) | ||||
| 
 | ||||
| // Stmt is a prepared statement object. | ||||
| // | ||||
| // https://sqlite.org/c3ref/stmt.html | ||||
| type Stmt struct { | ||||
| 	c      *Conn | ||||
| 	err    error | ||||
| 	handle uint32 | ||||
| } | ||||
| 
 | ||||
| // Close destroys the prepared statement object. | ||||
| // | ||||
| // It is safe to close a nil, zero or closed Stmt. | ||||
| // | ||||
| // https://sqlite.org/c3ref/finalize.html | ||||
| func (s *Stmt) Close() error { | ||||
| 	if s == nil || s.handle == 0 { | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	r := s.c.call("sqlite3_finalize", uint64(s.handle)) | ||||
| 
 | ||||
| 	s.handle = 0 | ||||
| 	return s.c.error(r) | ||||
| } | ||||
| 
 | ||||
| // Conn returns the database connection to which the prepared statement belongs. | ||||
| // | ||||
| // https://sqlite.org/c3ref/db_handle.html | ||||
| func (s *Stmt) Conn() *Conn { | ||||
| 	return s.c | ||||
| } | ||||
| 
 | ||||
| // 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 | ||||
| } | ||||
| 
 | ||||
| // Reset resets the prepared statement object. | ||||
| // | ||||
| // https://sqlite.org/c3ref/reset.html | ||||
| func (s *Stmt) Reset() error { | ||||
| 	r := s.c.call("sqlite3_reset", uint64(s.handle)) | ||||
| 	s.err = nil | ||||
| 	return s.c.error(r) | ||||
| } | ||||
| 
 | ||||
| // Busy determines if a prepared statement has been reset. | ||||
| // | ||||
| // https://sqlite.org/c3ref/stmt_busy.html | ||||
| func (s *Stmt) Busy() bool { | ||||
| 	r := s.c.call("sqlite3_stmt_busy", uint64(s.handle)) | ||||
| 	return r != 0 | ||||
| } | ||||
| 
 | ||||
| // Step evaluates the SQL statement. | ||||
| // If the SQL statement being executed returns any data, | ||||
| // then true is returned each time a new row of data is ready for processing by the caller. | ||||
| // The values may be accessed using the Column access functions. | ||||
| // Step is called again to retrieve the next row of data. | ||||
| // If an error has occurred, Step returns false; | ||||
| // call [Stmt.Err] or [Stmt.Reset] to get the error. | ||||
| // | ||||
| // https://sqlite.org/c3ref/step.html | ||||
| func (s *Stmt) Step() bool { | ||||
| 	s.c.checkInterrupt() | ||||
| 	r := s.c.call("sqlite3_step", uint64(s.handle)) | ||||
| 	switch r { | ||||
| 	case _ROW: | ||||
| 		s.err = nil | ||||
| 		return true | ||||
| 	case _DONE: | ||||
| 		s.err = nil | ||||
| 	default: | ||||
| 		s.err = s.c.error(r) | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
| 
 | ||||
| // Err gets the last error occurred during [Stmt.Step]. | ||||
| // Err returns nil after [Stmt.Reset] is called. | ||||
| // | ||||
| // https://sqlite.org/c3ref/step.html | ||||
| func (s *Stmt) Err() error { | ||||
| 	return s.err | ||||
| } | ||||
| 
 | ||||
| // Exec is a convenience function that repeatedly calls [Stmt.Step] until it returns false, | ||||
| // then calls [Stmt.Reset] to reset the statement and get any error that occurred. | ||||
| func (s *Stmt) Exec() error { | ||||
| 	for s.Step() { | ||||
| 	} | ||||
| 	return s.Reset() | ||||
| } | ||||
| 
 | ||||
| // Status monitors the performance characteristics of prepared statements. | ||||
| // | ||||
| // https://sqlite.org/c3ref/stmt_status.html | ||||
| func (s *Stmt) Status(op StmtStatus, reset bool) int { | ||||
| 	if op > STMTSTATUS_FILTER_HIT && op != STMTSTATUS_MEMUSED { | ||||
| 		return 0 | ||||
| 	} | ||||
| 	var i uint64 | ||||
| 	if reset { | ||||
| 		i = 1 | ||||
| 	} | ||||
| 	r := s.c.call("sqlite3_stmt_status", uint64(s.handle), | ||||
| 		uint64(op), i) | ||||
| 	return int(int32(r)) | ||||
| } | ||||
| 
 | ||||
| // ClearBindings resets all bindings on the prepared statement. | ||||
| // | ||||
| // https://sqlite.org/c3ref/clear_bindings.html | ||||
| func (s *Stmt) ClearBindings() error { | ||||
| 	r := s.c.call("sqlite3_clear_bindings", uint64(s.handle)) | ||||
| 	return s.c.error(r) | ||||
| } | ||||
| 
 | ||||
| // BindCount returns the number of SQL parameters in the prepared statement. | ||||
| // | ||||
| // https://sqlite.org/c3ref/bind_parameter_count.html | ||||
| func (s *Stmt) BindCount() int { | ||||
| 	r := s.c.call("sqlite3_bind_parameter_count", | ||||
| 		uint64(s.handle)) | ||||
| 	return int(int32(r)) | ||||
| } | ||||
| 
 | ||||
| // BindIndex returns the index of a parameter in the prepared statement | ||||
| // given its name. | ||||
| // | ||||
| // https://sqlite.org/c3ref/bind_parameter_index.html | ||||
| func (s *Stmt) BindIndex(name string) int { | ||||
| 	defer s.c.arena.mark()() | ||||
| 	namePtr := s.c.arena.string(name) | ||||
| 	r := s.c.call("sqlite3_bind_parameter_index", | ||||
| 		uint64(s.handle), uint64(namePtr)) | ||||
| 	return int(int32(r)) | ||||
| } | ||||
| 
 | ||||
| // BindName returns the name of a parameter in the prepared statement. | ||||
| // The leftmost SQL parameter has an index of 1. | ||||
| // | ||||
| // https://sqlite.org/c3ref/bind_parameter_name.html | ||||
| func (s *Stmt) BindName(param int) string { | ||||
| 	r := s.c.call("sqlite3_bind_parameter_name", | ||||
| 		uint64(s.handle), uint64(param)) | ||||
| 
 | ||||
| 	ptr := uint32(r) | ||||
| 	if ptr == 0 { | ||||
| 		return "" | ||||
| 	} | ||||
| 	return util.ReadString(s.c.mod, ptr, _MAX_NAME) | ||||
| } | ||||
| 
 | ||||
| // BindBool binds a bool to the prepared statement. | ||||
| // The leftmost SQL parameter has an index of 1. | ||||
| // SQLite does not have a separate boolean storage class. | ||||
| // Instead, boolean values are stored as integers 0 (false) and 1 (true). | ||||
| // | ||||
| // https://sqlite.org/c3ref/bind_blob.html | ||||
| func (s *Stmt) BindBool(param int, value bool) error { | ||||
| 	var i int64 | ||||
| 	if value { | ||||
| 		i = 1 | ||||
| 	} | ||||
| 	return s.BindInt64(param, i) | ||||
| } | ||||
| 
 | ||||
| // BindInt binds an int to the prepared statement. | ||||
| // The leftmost SQL parameter has an index of 1. | ||||
| // | ||||
| // https://sqlite.org/c3ref/bind_blob.html | ||||
| func (s *Stmt) BindInt(param int, value int) error { | ||||
| 	return s.BindInt64(param, int64(value)) | ||||
| } | ||||
| 
 | ||||
| // BindInt64 binds an int64 to the prepared statement. | ||||
| // The leftmost SQL parameter has an index of 1. | ||||
| // | ||||
| // https://sqlite.org/c3ref/bind_blob.html | ||||
| func (s *Stmt) BindInt64(param int, value int64) error { | ||||
| 	r := s.c.call("sqlite3_bind_int64", | ||||
| 		uint64(s.handle), uint64(param), uint64(value)) | ||||
| 	return s.c.error(r) | ||||
| } | ||||
| 
 | ||||
| // BindFloat binds a float64 to the prepared statement. | ||||
| // The leftmost SQL parameter has an index of 1. | ||||
| // | ||||
| // https://sqlite.org/c3ref/bind_blob.html | ||||
| func (s *Stmt) BindFloat(param int, value float64) error { | ||||
| 	r := s.c.call("sqlite3_bind_double", | ||||
| 		uint64(s.handle), uint64(param), math.Float64bits(value)) | ||||
| 	return s.c.error(r) | ||||
| } | ||||
| 
 | ||||
| // BindText binds a string to the prepared statement. | ||||
| // The leftmost SQL parameter has an index of 1. | ||||
| // | ||||
| // https://sqlite.org/c3ref/bind_blob.html | ||||
| func (s *Stmt) BindText(param int, value string) error { | ||||
| 	if len(value) > _MAX_LENGTH { | ||||
| 		return TOOBIG | ||||
| 	} | ||||
| 	ptr := s.c.newString(value) | ||||
| 	r := s.c.call("sqlite3_bind_text64", | ||||
| 		uint64(s.handle), uint64(param), | ||||
| 		uint64(ptr), uint64(len(value)), | ||||
| 		uint64(s.c.freer), _UTF8) | ||||
| 	return s.c.error(r) | ||||
| } | ||||
| 
 | ||||
| // BindRawText binds a []byte to the prepared statement as text. | ||||
| // The leftmost SQL parameter has an index of 1. | ||||
| // | ||||
| // https://sqlite.org/c3ref/bind_blob.html | ||||
| func (s *Stmt) BindRawText(param int, value []byte) error { | ||||
| 	if len(value) > _MAX_LENGTH { | ||||
| 		return TOOBIG | ||||
| 	} | ||||
| 	ptr := s.c.newBytes(value) | ||||
| 	r := s.c.call("sqlite3_bind_text64", | ||||
| 		uint64(s.handle), uint64(param), | ||||
| 		uint64(ptr), uint64(len(value)), | ||||
| 		uint64(s.c.freer), _UTF8) | ||||
| 	return s.c.error(r) | ||||
| } | ||||
| 
 | ||||
| // BindBlob binds a []byte to the prepared statement. | ||||
| // The leftmost SQL parameter has an index of 1. | ||||
| // Binding a nil slice is the same as calling [Stmt.BindNull]. | ||||
| // | ||||
| // https://sqlite.org/c3ref/bind_blob.html | ||||
| func (s *Stmt) BindBlob(param int, value []byte) error { | ||||
| 	if len(value) > _MAX_LENGTH { | ||||
| 		return TOOBIG | ||||
| 	} | ||||
| 	ptr := s.c.newBytes(value) | ||||
| 	r := s.c.call("sqlite3_bind_blob64", | ||||
| 		uint64(s.handle), uint64(param), | ||||
| 		uint64(ptr), uint64(len(value)), | ||||
| 		uint64(s.c.freer)) | ||||
| 	return s.c.error(r) | ||||
| } | ||||
| 
 | ||||
| // BindZeroBlob binds a zero-filled, length n BLOB to the prepared statement. | ||||
| // The leftmost SQL parameter has an index of 1. | ||||
| // | ||||
| // https://sqlite.org/c3ref/bind_blob.html | ||||
| func (s *Stmt) BindZeroBlob(param int, n int64) error { | ||||
| 	r := s.c.call("sqlite3_bind_zeroblob64", | ||||
| 		uint64(s.handle), uint64(param), uint64(n)) | ||||
| 	return s.c.error(r) | ||||
| } | ||||
| 
 | ||||
| // BindNull binds a NULL to the prepared statement. | ||||
| // The leftmost SQL parameter has an index of 1. | ||||
| // | ||||
| // https://sqlite.org/c3ref/bind_blob.html | ||||
| func (s *Stmt) BindNull(param int) error { | ||||
| 	r := s.c.call("sqlite3_bind_null", | ||||
| 		uint64(s.handle), uint64(param)) | ||||
| 	return s.c.error(r) | ||||
| } | ||||
| 
 | ||||
| // BindTime binds a [time.Time] to the prepared statement. | ||||
| // The leftmost SQL parameter has an index of 1. | ||||
| // | ||||
| // https://sqlite.org/c3ref/bind_blob.html | ||||
| func (s *Stmt) BindTime(param int, value time.Time, format TimeFormat) error { | ||||
| 	if format == TimeFormatDefault { | ||||
| 		return s.bindRFC3339Nano(param, value) | ||||
| 	} | ||||
| 	switch v := format.Encode(value).(type) { | ||||
| 	case string: | ||||
| 		s.BindText(param, v) | ||||
| 	case int64: | ||||
| 		s.BindInt64(param, v) | ||||
| 	case float64: | ||||
| 		s.BindFloat(param, v) | ||||
| 	default: | ||||
| 		panic(util.AssertErr()) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (s *Stmt) bindRFC3339Nano(param int, value time.Time) error { | ||||
| 	const maxlen = uint64(len(time.RFC3339Nano)) + 5 | ||||
| 
 | ||||
| 	ptr := s.c.new(maxlen) | ||||
| 	buf := util.View(s.c.mod, ptr, maxlen) | ||||
| 	buf = value.AppendFormat(buf[:0], time.RFC3339Nano) | ||||
| 
 | ||||
| 	r := s.c.call("sqlite3_bind_text64", | ||||
| 		uint64(s.handle), uint64(param), | ||||
| 		uint64(ptr), uint64(len(buf)), | ||||
| 		uint64(s.c.freer), _UTF8) | ||||
| 	return s.c.error(r) | ||||
| } | ||||
| 
 | ||||
| // BindPointer binds a NULL to the prepared statement, just like [Stmt.BindNull], | ||||
| // but it also associates ptr with that NULL value such that it can be retrieved | ||||
| // within an application-defined SQL function using [Value.Pointer]. | ||||
| // The leftmost SQL parameter has an index of 1. | ||||
| // | ||||
| // https://sqlite.org/c3ref/bind_blob.html | ||||
| func (s *Stmt) BindPointer(param int, ptr any) error { | ||||
| 	valPtr := util.AddHandle(s.c.ctx, ptr) | ||||
| 	r := s.c.call("sqlite3_bind_pointer_go", | ||||
| 		uint64(s.handle), uint64(param), uint64(valPtr)) | ||||
| 	return s.c.error(r) | ||||
| } | ||||
| 
 | ||||
| // BindJSON binds the JSON encoding of value to the prepared statement. | ||||
| // The leftmost SQL parameter has an index of 1. | ||||
| // | ||||
| // https://sqlite.org/c3ref/bind_blob.html | ||||
| func (s *Stmt) BindJSON(param int, value any) error { | ||||
| 	data, err := json.Marshal(value) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	return s.BindRawText(param, data) | ||||
| } | ||||
| 
 | ||||
| // BindValue binds a copy of value to the prepared statement. | ||||
| // The leftmost SQL parameter has an index of 1. | ||||
| // | ||||
| // https://sqlite.org/c3ref/bind_blob.html | ||||
| func (s *Stmt) BindValue(param int, value Value) error { | ||||
| 	if value.c != s.c { | ||||
| 		return MISUSE | ||||
| 	} | ||||
| 	r := s.c.call("sqlite3_bind_value", | ||||
| 		uint64(s.handle), uint64(param), uint64(value.handle)) | ||||
| 	return s.c.error(r) | ||||
| } | ||||
| 
 | ||||
| // ColumnCount returns the number of columns in a result set. | ||||
| // | ||||
| // https://sqlite.org/c3ref/column_count.html | ||||
| func (s *Stmt) ColumnCount() int { | ||||
| 	r := s.c.call("sqlite3_column_count", | ||||
| 		uint64(s.handle)) | ||||
| 	return int(int32(r)) | ||||
| } | ||||
| 
 | ||||
| // ColumnName returns the name of the result column. | ||||
| // The leftmost column of the result set has the index 0. | ||||
| // | ||||
| // https://sqlite.org/c3ref/column_name.html | ||||
| func (s *Stmt) ColumnName(col int) string { | ||||
| 	r := s.c.call("sqlite3_column_name", | ||||
| 		uint64(s.handle), uint64(col)) | ||||
| 	if r == 0 { | ||||
| 		panic(util.OOMErr) | ||||
| 	} | ||||
| 	return util.ReadString(s.c.mod, uint32(r), _MAX_NAME) | ||||
| } | ||||
| 
 | ||||
| // ColumnType returns the initial [Datatype] of the result column. | ||||
| // The leftmost column of the result set has the index 0. | ||||
| // | ||||
| // https://sqlite.org/c3ref/column_blob.html | ||||
| func (s *Stmt) ColumnType(col int) Datatype { | ||||
| 	r := s.c.call("sqlite3_column_type", | ||||
| 		uint64(s.handle), uint64(col)) | ||||
| 	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) | ||||
| } | ||||
| 
 | ||||
| // ColumnDatabaseName returns the name of the database | ||||
| // that is the origin of a particular result column. | ||||
| // The leftmost column of the result set has the index 0. | ||||
| // | ||||
| // https://sqlite.org/c3ref/column_database_name.html | ||||
| func (s *Stmt) ColumnDatabaseName(col int) string { | ||||
| 	r := s.c.call("sqlite3_column_database_name", | ||||
| 		uint64(s.handle), uint64(col)) | ||||
| 	if r == 0 { | ||||
| 		return "" | ||||
| 	} | ||||
| 	return util.ReadString(s.c.mod, uint32(r), _MAX_NAME) | ||||
| } | ||||
| 
 | ||||
| // ColumnTableName returns the name of the table | ||||
| // that is the origin of a particular result column. | ||||
| // The leftmost column of the result set has the index 0. | ||||
| // | ||||
| // https://sqlite.org/c3ref/column_database_name.html | ||||
| func (s *Stmt) ColumnTableName(col int) string { | ||||
| 	r := s.c.call("sqlite3_column_table_name", | ||||
| 		uint64(s.handle), uint64(col)) | ||||
| 	if r == 0 { | ||||
| 		return "" | ||||
| 	} | ||||
| 	return util.ReadString(s.c.mod, uint32(r), _MAX_NAME) | ||||
| } | ||||
| 
 | ||||
| // ColumnOriginName returns the name of the table column | ||||
| // that is the origin of a particular result column. | ||||
| // The leftmost column of the result set has the index 0. | ||||
| // | ||||
| // https://sqlite.org/c3ref/column_database_name.html | ||||
| func (s *Stmt) ColumnOriginName(col int) string { | ||||
| 	r := s.c.call("sqlite3_column_origin_name", | ||||
| 		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. | ||||
| // Instead, boolean values are retrieved as integers, | ||||
| // with 0 converted to false and any other value to true. | ||||
| // | ||||
| // https://sqlite.org/c3ref/column_blob.html | ||||
| func (s *Stmt) ColumnBool(col int) bool { | ||||
| 	return s.ColumnInt64(col) != 0 | ||||
| } | ||||
| 
 | ||||
| // ColumnInt returns the value of the result column as an int. | ||||
| // The leftmost column of the result set has the index 0. | ||||
| // | ||||
| // https://sqlite.org/c3ref/column_blob.html | ||||
| func (s *Stmt) ColumnInt(col int) int { | ||||
| 	return int(s.ColumnInt64(col)) | ||||
| } | ||||
| 
 | ||||
| // ColumnInt64 returns the value of the result column as an int64. | ||||
| // The leftmost column of the result set has the index 0. | ||||
| // | ||||
| // https://sqlite.org/c3ref/column_blob.html | ||||
| func (s *Stmt) ColumnInt64(col int) int64 { | ||||
| 	r := s.c.call("sqlite3_column_int64", | ||||
| 		uint64(s.handle), uint64(col)) | ||||
| 	return int64(r) | ||||
| } | ||||
| 
 | ||||
| // ColumnFloat returns the value of the result column as a float64. | ||||
| // The leftmost column of the result set has the index 0. | ||||
| // | ||||
| // https://sqlite.org/c3ref/column_blob.html | ||||
| func (s *Stmt) ColumnFloat(col int) float64 { | ||||
| 	r := s.c.call("sqlite3_column_double", | ||||
| 		uint64(s.handle), uint64(col)) | ||||
| 	return math.Float64frombits(r) | ||||
| } | ||||
| 
 | ||||
| // ColumnTime returns the value of the result column as a [time.Time]. | ||||
| // The leftmost column of the result set has the index 0. | ||||
| // | ||||
| // https://sqlite.org/c3ref/column_blob.html | ||||
| func (s *Stmt) ColumnTime(col int, format TimeFormat) time.Time { | ||||
| 	var v any | ||||
| 	switch s.ColumnType(col) { | ||||
| 	case INTEGER: | ||||
| 		v = s.ColumnInt64(col) | ||||
| 	case FLOAT: | ||||
| 		v = s.ColumnFloat(col) | ||||
| 	case TEXT, BLOB: | ||||
| 		v = s.ColumnText(col) | ||||
| 	case NULL: | ||||
| 		return time.Time{} | ||||
| 	default: | ||||
| 		panic(util.AssertErr()) | ||||
| 	} | ||||
| 	t, err := format.Decode(v) | ||||
| 	if err != nil { | ||||
| 		s.err = err | ||||
| 	} | ||||
| 	return t | ||||
| } | ||||
| 
 | ||||
| // ColumnText returns the value of the result column as a string. | ||||
| // The leftmost column of the result set has the index 0. | ||||
| // | ||||
| // https://sqlite.org/c3ref/column_blob.html | ||||
| func (s *Stmt) ColumnText(col int) string { | ||||
| 	return string(s.ColumnRawText(col)) | ||||
| } | ||||
| 
 | ||||
| // ColumnBlob appends to buf and returns | ||||
| // the value of the result column as a []byte. | ||||
| // The leftmost column of the result set has the index 0. | ||||
| // | ||||
| // https://sqlite.org/c3ref/column_blob.html | ||||
| func (s *Stmt) ColumnBlob(col int, buf []byte) []byte { | ||||
| 	return append(buf, s.ColumnRawBlob(col)...) | ||||
| } | ||||
| 
 | ||||
| // ColumnRawText returns the value of the result column as a []byte. | ||||
| // The []byte is owned by SQLite and may be invalidated by | ||||
| // subsequent calls to [Stmt] methods. | ||||
| // The leftmost column of the result set has the index 0. | ||||
| // | ||||
| // https://sqlite.org/c3ref/column_blob.html | ||||
| func (s *Stmt) ColumnRawText(col int) []byte { | ||||
| 	r := s.c.call("sqlite3_column_text", | ||||
| 		uint64(s.handle), uint64(col)) | ||||
| 	return s.columnRawBytes(col, uint32(r)) | ||||
| } | ||||
| 
 | ||||
| // ColumnRawBlob returns the value of the result column as a []byte. | ||||
| // The []byte is owned by SQLite and may be invalidated by | ||||
| // subsequent calls to [Stmt] methods. | ||||
| // The leftmost column of the result set has the index 0. | ||||
| // | ||||
| // https://sqlite.org/c3ref/column_blob.html | ||||
| func (s *Stmt) ColumnRawBlob(col int) []byte { | ||||
| 	r := s.c.call("sqlite3_column_blob", | ||||
| 		uint64(s.handle), uint64(col)) | ||||
| 	return s.columnRawBytes(col, uint32(r)) | ||||
| } | ||||
| 
 | ||||
| func (s *Stmt) columnRawBytes(col int, ptr uint32) []byte { | ||||
| 	if ptr == 0 { | ||||
| 		r := s.c.call("sqlite3_errcode", uint64(s.c.handle)) | ||||
| 		s.err = s.c.error(r) | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	r := s.c.call("sqlite3_column_bytes", | ||||
| 		uint64(s.handle), uint64(col)) | ||||
| 	return util.View(s.c.mod, ptr, r) | ||||
| } | ||||
| 
 | ||||
| // ColumnJSON parses the JSON-encoded value of the result column | ||||
| // and stores it in the value pointed to by ptr. | ||||
| // The leftmost column of the result set has the index 0. | ||||
| // | ||||
| // https://sqlite.org/c3ref/column_blob.html | ||||
| func (s *Stmt) ColumnJSON(col int, ptr any) error { | ||||
| 	var data []byte | ||||
| 	switch s.ColumnType(col) { | ||||
| 	case NULL: | ||||
| 		data = append(data, "null"...) | ||||
| 	case TEXT: | ||||
| 		data = s.ColumnRawText(col) | ||||
| 	case BLOB: | ||||
| 		data = s.ColumnRawBlob(col) | ||||
| 	case INTEGER: | ||||
| 		data = strconv.AppendInt(nil, s.ColumnInt64(col), 10) | ||||
| 	case FLOAT: | ||||
| 		data = strconv.AppendFloat(nil, s.ColumnFloat(col), 'g', -1, 64) | ||||
| 	default: | ||||
| 		panic(util.AssertErr()) | ||||
| 	} | ||||
| 	return json.Unmarshal(data, ptr) | ||||
| } | ||||
| 
 | ||||
| // ColumnValue returns the unprotected value of the result column. | ||||
| // The leftmost column of the result set has the index 0. | ||||
| // | ||||
| // https://sqlite.org/c3ref/column_blob.html | ||||
| func (s *Stmt) ColumnValue(col int) Value { | ||||
| 	r := s.c.call("sqlite3_column_value", | ||||
| 		uint64(s.handle), uint64(col)) | ||||
| 	return Value{ | ||||
| 		c:      s.c, | ||||
| 		unprot: true, | ||||
| 		handle: uint32(r), | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Columns populates result columns into the provided slice. | ||||
| // The slice must have [Stmt.ColumnCount] length. | ||||
| // | ||||
| // [INTEGER] columns will be retrieved as int64 values, | ||||
| // [FLOAT] as float64, [NULL] as nil, | ||||
| // [TEXT] as string, and [BLOB] as []byte. | ||||
| // Any []byte are owned by SQLite and may be invalidated by | ||||
| // subsequent calls to [Stmt] methods. | ||||
| func (s *Stmt) Columns(dest []any) error { | ||||
| 	defer s.c.arena.mark()() | ||||
| 	count := uint64(len(dest)) | ||||
| 	typePtr := s.c.arena.new(count) | ||||
| 	dataPtr := s.c.arena.new(8 * count) | ||||
| 
 | ||||
| 	r := s.c.call("sqlite3_columns_go", | ||||
| 		uint64(s.handle), count, uint64(typePtr), uint64(dataPtr)) | ||||
| 	if err := s.c.error(r); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	types := util.View(s.c.mod, typePtr, count) | ||||
| 	for i := range dest { | ||||
| 		switch types[i] { | ||||
| 		case byte(INTEGER): | ||||
| 			dest[i] = int64(util.ReadUint64(s.c.mod, dataPtr+8*uint32(i))) | ||||
| 			continue | ||||
| 		case byte(FLOAT): | ||||
| 			dest[i] = util.ReadFloat64(s.c.mod, dataPtr+8*uint32(i)) | ||||
| 			continue | ||||
| 		case byte(NULL): | ||||
| 			dest[i] = nil | ||||
| 			continue | ||||
| 		} | ||||
| 		ptr := util.ReadUint32(s.c.mod, dataPtr+8*uint32(i)+0) | ||||
| 		len := util.ReadUint32(s.c.mod, dataPtr+8*uint32(i)+4) | ||||
| 		buf := util.View(s.c.mod, ptr, uint64(len)) | ||||
| 		if types[i] == byte(TEXT) { | ||||
| 			dest[i] = string(buf) | ||||
| 		} else { | ||||
| 			dest[i] = buf | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										354
									
								
								vendor/github.com/ncruces/go-sqlite3/time.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										354
									
								
								vendor/github.com/ncruces/go-sqlite3/time.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,354 @@ | |||
| package sqlite3 | ||||
| 
 | ||||
| import ( | ||||
| 	"math" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/ncruces/go-sqlite3/internal/util" | ||||
| 	"github.com/ncruces/julianday" | ||||
| ) | ||||
| 
 | ||||
| // TimeFormat specifies how to encode/decode time values. | ||||
| // | ||||
| // See the documentation for the [TimeFormatDefault] constant | ||||
| // for formats recognized by SQLite. | ||||
| // | ||||
| // https://sqlite.org/lang_datefunc.html | ||||
| type TimeFormat string | ||||
| 
 | ||||
| // TimeFormats recognized by SQLite to encode/decode time values. | ||||
| // | ||||
| // https://sqlite.org/lang_datefunc.html#time_values | ||||
| const ( | ||||
| 	TimeFormatDefault TimeFormat = "" // time.RFC3339Nano | ||||
| 
 | ||||
| 	// Text formats | ||||
| 	TimeFormat1  TimeFormat = "2006-01-02" | ||||
| 	TimeFormat2  TimeFormat = "2006-01-02 15:04" | ||||
| 	TimeFormat3  TimeFormat = "2006-01-02 15:04:05" | ||||
| 	TimeFormat4  TimeFormat = "2006-01-02 15:04:05.000" | ||||
| 	TimeFormat5  TimeFormat = "2006-01-02T15:04" | ||||
| 	TimeFormat6  TimeFormat = "2006-01-02T15:04:05" | ||||
| 	TimeFormat7  TimeFormat = "2006-01-02T15:04:05.000" | ||||
| 	TimeFormat8  TimeFormat = "15:04" | ||||
| 	TimeFormat9  TimeFormat = "15:04:05" | ||||
| 	TimeFormat10 TimeFormat = "15:04:05.000" | ||||
| 
 | ||||
| 	TimeFormat2TZ  = TimeFormat2 + "Z07:00" | ||||
| 	TimeFormat3TZ  = TimeFormat3 + "Z07:00" | ||||
| 	TimeFormat4TZ  = TimeFormat4 + "Z07:00" | ||||
| 	TimeFormat5TZ  = TimeFormat5 + "Z07:00" | ||||
| 	TimeFormat6TZ  = TimeFormat6 + "Z07:00" | ||||
| 	TimeFormat7TZ  = TimeFormat7 + "Z07:00" | ||||
| 	TimeFormat8TZ  = TimeFormat8 + "Z07:00" | ||||
| 	TimeFormat9TZ  = TimeFormat9 + "Z07:00" | ||||
| 	TimeFormat10TZ = TimeFormat10 + "Z07:00" | ||||
| 
 | ||||
| 	// Numeric formats | ||||
| 	TimeFormatJulianDay TimeFormat = "julianday" | ||||
| 	TimeFormatUnix      TimeFormat = "unixepoch" | ||||
| 	TimeFormatUnixFrac  TimeFormat = "unixepoch_frac" | ||||
| 	TimeFormatUnixMilli TimeFormat = "unixepoch_milli" // not an SQLite format | ||||
| 	TimeFormatUnixMicro TimeFormat = "unixepoch_micro" // not an SQLite format | ||||
| 	TimeFormatUnixNano  TimeFormat = "unixepoch_nano"  // not an SQLite format | ||||
| 
 | ||||
| 	// Auto | ||||
| 	TimeFormatAuto TimeFormat = "auto" | ||||
| ) | ||||
| 
 | ||||
| // Encode encodes a time value using this format. | ||||
| // | ||||
| // [TimeFormatDefault] and [TimeFormatAuto] encode using [time.RFC3339Nano], | ||||
| // with nanosecond accuracy, and preserving any timezone offset. | ||||
| // | ||||
| // This is the format used by the [database/sql] driver: | ||||
| // [database/sql.Row.Scan] will decode as [time.Time] | ||||
| // values encoded with [time.RFC3339Nano]. | ||||
| // | ||||
| // Time values encoded with [time.RFC3339Nano] cannot be sorted as strings | ||||
| // to produce a time-ordered sequence. | ||||
| // | ||||
| // Assuming that the time zones of the time values are the same (e.g., all in UTC), | ||||
| // and expressed using the same string (e.g., all "Z" or all "+00:00"), | ||||
| // use the TIME [collating sequence] to produce a time-ordered sequence. | ||||
| // | ||||
| // Otherwise, use [TimeFormat7] for time-ordered encoding. | ||||
| // | ||||
| // Formats [TimeFormat1] through [TimeFormat10] | ||||
| // convert time values to UTC before encoding. | ||||
| // | ||||
| // Returns a string for the text formats, | ||||
| // a float64 for [TimeFormatJulianDay] and [TimeFormatUnixFrac], | ||||
| // or an int64 for the other numeric formats. | ||||
| // | ||||
| // https://sqlite.org/lang_datefunc.html | ||||
| // | ||||
| // [collating sequence]: https://sqlite.org/datatype3.html#collating_sequences | ||||
| func (f TimeFormat) Encode(t time.Time) any { | ||||
| 	switch f { | ||||
| 	// Numeric formats | ||||
| 	case TimeFormatJulianDay: | ||||
| 		return julianday.Float(t) | ||||
| 	case TimeFormatUnix: | ||||
| 		return t.Unix() | ||||
| 	case TimeFormatUnixFrac: | ||||
| 		return float64(t.Unix()) + float64(t.Nanosecond())*1e-9 | ||||
| 	case TimeFormatUnixMilli: | ||||
| 		return t.UnixMilli() | ||||
| 	case TimeFormatUnixMicro: | ||||
| 		return t.UnixMicro() | ||||
| 	case TimeFormatUnixNano: | ||||
| 		return t.UnixNano() | ||||
| 	// Special formats. | ||||
| 	case TimeFormatDefault, TimeFormatAuto: | ||||
| 		f = time.RFC3339Nano | ||||
| 	// SQLite assumes UTC if unspecified. | ||||
| 	case | ||||
| 		TimeFormat1, TimeFormat2, | ||||
| 		TimeFormat3, TimeFormat4, | ||||
| 		TimeFormat5, TimeFormat6, | ||||
| 		TimeFormat7, TimeFormat8, | ||||
| 		TimeFormat9, TimeFormat10: | ||||
| 		t = t.UTC() | ||||
| 	} | ||||
| 	return t.Format(string(f)) | ||||
| } | ||||
| 
 | ||||
| // Decode decodes a time value using this format. | ||||
| // | ||||
| // The time value can be a string, an int64, or a float64. | ||||
| // | ||||
| // Formats [TimeFormat8] through [TimeFormat10] | ||||
| // (and [TimeFormat8TZ] through [TimeFormat10TZ]) | ||||
| // assume a date of 2000-01-01. | ||||
| // | ||||
| // The timezone indicator and fractional seconds are always optional | ||||
| // for formats [TimeFormat2] through [TimeFormat10] | ||||
| // (and [TimeFormat2TZ] through [TimeFormat10TZ]). | ||||
| // | ||||
| // [TimeFormatAuto] implements (and extends) the SQLite auto modifier. | ||||
| // Julian day numbers are safe to use for historical dates, | ||||
| // from 4712BC through 9999AD. | ||||
| // Unix timestamps (expressed in seconds, milliseconds, microseconds, or nanoseconds) | ||||
| // are safe to use for current events, from at least 1980 through at least 2260. | ||||
| // Unix timestamps before 1980 and after 9999 may be misinterpreted as julian day numbers, | ||||
| // or have the wrong time unit. | ||||
| // | ||||
| // https://sqlite.org/lang_datefunc.html | ||||
| func (f TimeFormat) Decode(v any) (time.Time, error) { | ||||
| 	switch f { | ||||
| 	// Numeric formats. | ||||
| 	case TimeFormatJulianDay: | ||||
| 		switch v := v.(type) { | ||||
| 		case string: | ||||
| 			return julianday.Parse(v) | ||||
| 		case float64: | ||||
| 			return julianday.FloatTime(v), nil | ||||
| 		case int64: | ||||
| 			return julianday.Time(v, 0), nil | ||||
| 		default: | ||||
| 			return time.Time{}, util.TimeErr | ||||
| 		} | ||||
| 
 | ||||
| 	case TimeFormatUnix, TimeFormatUnixFrac: | ||||
| 		if s, ok := v.(string); ok { | ||||
| 			f, err := strconv.ParseFloat(s, 64) | ||||
| 			if err != nil { | ||||
| 				return time.Time{}, err | ||||
| 			} | ||||
| 			v = f | ||||
| 		} | ||||
| 		switch v := v.(type) { | ||||
| 		case float64: | ||||
| 			sec, frac := math.Modf(v) | ||||
| 			nsec := math.Floor(frac * 1e9) | ||||
| 			return time.Unix(int64(sec), int64(nsec)).UTC(), nil | ||||
| 		case int64: | ||||
| 			return time.Unix(v, 0).UTC(), nil | ||||
| 		default: | ||||
| 			return time.Time{}, util.TimeErr | ||||
| 		} | ||||
| 
 | ||||
| 	case TimeFormatUnixMilli: | ||||
| 		if s, ok := v.(string); ok { | ||||
| 			i, err := strconv.ParseInt(s, 10, 64) | ||||
| 			if err != nil { | ||||
| 				return time.Time{}, err | ||||
| 			} | ||||
| 			v = i | ||||
| 		} | ||||
| 		switch v := v.(type) { | ||||
| 		case float64: | ||||
| 			return time.UnixMilli(int64(math.Floor(v))).UTC(), nil | ||||
| 		case int64: | ||||
| 			return time.UnixMilli(v).UTC(), nil | ||||
| 		default: | ||||
| 			return time.Time{}, util.TimeErr | ||||
| 		} | ||||
| 
 | ||||
| 	case TimeFormatUnixMicro: | ||||
| 		if s, ok := v.(string); ok { | ||||
| 			i, err := strconv.ParseInt(s, 10, 64) | ||||
| 			if err != nil { | ||||
| 				return time.Time{}, err | ||||
| 			} | ||||
| 			v = i | ||||
| 		} | ||||
| 		switch v := v.(type) { | ||||
| 		case float64: | ||||
| 			return time.UnixMicro(int64(math.Floor(v))).UTC(), nil | ||||
| 		case int64: | ||||
| 			return time.UnixMicro(v).UTC(), nil | ||||
| 		default: | ||||
| 			return time.Time{}, util.TimeErr | ||||
| 		} | ||||
| 
 | ||||
| 	case TimeFormatUnixNano: | ||||
| 		if s, ok := v.(string); ok { | ||||
| 			i, err := strconv.ParseInt(s, 10, 64) | ||||
| 			if err != nil { | ||||
| 				return time.Time{}, util.TimeErr | ||||
| 			} | ||||
| 			v = i | ||||
| 		} | ||||
| 		switch v := v.(type) { | ||||
| 		case float64: | ||||
| 			return time.Unix(0, int64(math.Floor(v))).UTC(), nil | ||||
| 		case int64: | ||||
| 			return time.Unix(0, v).UTC(), nil | ||||
| 		default: | ||||
| 			return time.Time{}, util.TimeErr | ||||
| 		} | ||||
| 
 | ||||
| 	// Special formats. | ||||
| 	case TimeFormatAuto: | ||||
| 		switch s := v.(type) { | ||||
| 		case string: | ||||
| 			i, err := strconv.ParseInt(s, 10, 64) | ||||
| 			if err == nil { | ||||
| 				v = i | ||||
| 				break | ||||
| 			} | ||||
| 			f, err := strconv.ParseFloat(s, 64) | ||||
| 			if err == nil { | ||||
| 				v = f | ||||
| 				break | ||||
| 			} | ||||
| 
 | ||||
| 			dates := []TimeFormat{ | ||||
| 				TimeFormat9, TimeFormat8, | ||||
| 				TimeFormat6, TimeFormat5, | ||||
| 				TimeFormat3, TimeFormat2, TimeFormat1, | ||||
| 			} | ||||
| 			for _, f := range dates { | ||||
| 				t, err := f.Decode(s) | ||||
| 				if err == nil { | ||||
| 					return t, nil | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		switch v := v.(type) { | ||||
| 		case float64: | ||||
| 			if 0 <= v && v < 5373484.5 { | ||||
| 				return TimeFormatJulianDay.Decode(v) | ||||
| 			} | ||||
| 			if v < 253402300800 { | ||||
| 				return TimeFormatUnixFrac.Decode(v) | ||||
| 			} | ||||
| 			if v < 253402300800_000 { | ||||
| 				return TimeFormatUnixMilli.Decode(v) | ||||
| 			} | ||||
| 			if v < 253402300800_000000 { | ||||
| 				return TimeFormatUnixMicro.Decode(v) | ||||
| 			} | ||||
| 			return TimeFormatUnixNano.Decode(v) | ||||
| 		case int64: | ||||
| 			if 0 <= v && v < 5373485 { | ||||
| 				return TimeFormatJulianDay.Decode(v) | ||||
| 			} | ||||
| 			if v < 253402300800 { | ||||
| 				return TimeFormatUnixFrac.Decode(v) | ||||
| 			} | ||||
| 			if v < 253402300800_000 { | ||||
| 				return TimeFormatUnixMilli.Decode(v) | ||||
| 			} | ||||
| 			if v < 253402300800_000000 { | ||||
| 				return TimeFormatUnixMicro.Decode(v) | ||||
| 			} | ||||
| 			return TimeFormatUnixNano.Decode(v) | ||||
| 		default: | ||||
| 			return time.Time{}, util.TimeErr | ||||
| 		} | ||||
| 
 | ||||
| 	case | ||||
| 		TimeFormat2, TimeFormat2TZ, | ||||
| 		TimeFormat3, TimeFormat3TZ, | ||||
| 		TimeFormat4, TimeFormat4TZ, | ||||
| 		TimeFormat5, TimeFormat5TZ, | ||||
| 		TimeFormat6, TimeFormat6TZ, | ||||
| 		TimeFormat7, TimeFormat7TZ: | ||||
| 		s, ok := v.(string) | ||||
| 		if !ok { | ||||
| 			return time.Time{}, util.TimeErr | ||||
| 		} | ||||
| 		return f.parseRelaxed(s) | ||||
| 
 | ||||
| 	case | ||||
| 		TimeFormat8, TimeFormat8TZ, | ||||
| 		TimeFormat9, TimeFormat9TZ, | ||||
| 		TimeFormat10, TimeFormat10TZ: | ||||
| 		s, ok := v.(string) | ||||
| 		if !ok { | ||||
| 			return time.Time{}, util.TimeErr | ||||
| 		} | ||||
| 		t, err := f.parseRelaxed(s) | ||||
| 		if err != nil { | ||||
| 			return time.Time{}, err | ||||
| 		} | ||||
| 		return t.AddDate(2000, 0, 0), nil | ||||
| 
 | ||||
| 	default: | ||||
| 		s, ok := v.(string) | ||||
| 		if !ok { | ||||
| 			return time.Time{}, util.TimeErr | ||||
| 		} | ||||
| 		if f == "" { | ||||
| 			f = time.RFC3339Nano | ||||
| 		} | ||||
| 		return time.Parse(string(f), s) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (f TimeFormat) parseRelaxed(s string) (time.Time, error) { | ||||
| 	fs := string(f) | ||||
| 	fs = strings.TrimSuffix(fs, "Z07:00") | ||||
| 	fs = strings.TrimSuffix(fs, ".000") | ||||
| 	t, err := time.Parse(fs+"Z07:00", s) | ||||
| 	if err != nil { | ||||
| 		return time.Parse(fs, s) | ||||
| 	} | ||||
| 	return t, nil | ||||
| } | ||||
| 
 | ||||
| // Scanner returns a [database/sql.Scanner] that can be used as an argument to | ||||
| // [database/sql.Row.Scan] and similar methods to | ||||
| // decode a time value into dest using this format. | ||||
| func (f TimeFormat) Scanner(dest *time.Time) interface{ Scan(any) error } { | ||||
| 	return timeScanner{dest, f} | ||||
| } | ||||
| 
 | ||||
| type timeScanner struct { | ||||
| 	*time.Time | ||||
| 	TimeFormat | ||||
| } | ||||
| 
 | ||||
| func (s timeScanner) Scan(src any) error { | ||||
| 	var ok bool | ||||
| 	var err error | ||||
| 	if *s.Time, ok = src.(time.Time); !ok { | ||||
| 		*s.Time, err = s.Decode(src) | ||||
| 	} | ||||
| 	return err | ||||
| } | ||||
							
								
								
									
										294
									
								
								vendor/github.com/ncruces/go-sqlite3/txn.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										294
									
								
								vendor/github.com/ncruces/go-sqlite3/txn.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,294 @@ | |||
| package sqlite3 | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"math/rand" | ||||
| 	"runtime" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 
 | ||||
| 	"github.com/ncruces/go-sqlite3/internal/util" | ||||
| 	"github.com/tetratelabs/wazero/api" | ||||
| ) | ||||
| 
 | ||||
| // Txn is an in-progress database transaction. | ||||
| // | ||||
| // https://sqlite.org/lang_transaction.html | ||||
| type Txn struct { | ||||
| 	c *Conn | ||||
| } | ||||
| 
 | ||||
| // Begin starts a deferred transaction. | ||||
| // | ||||
| // https://sqlite.org/lang_transaction.html | ||||
| func (c *Conn) Begin() Txn { | ||||
| 	// BEGIN even if interrupted. | ||||
| 	err := c.txnExecInterrupted(`BEGIN DEFERRED`) | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| 	return Txn{c} | ||||
| } | ||||
| 
 | ||||
| // BeginImmediate starts an immediate transaction. | ||||
| // | ||||
| // https://sqlite.org/lang_transaction.html | ||||
| func (c *Conn) BeginImmediate() (Txn, error) { | ||||
| 	err := c.Exec(`BEGIN IMMEDIATE`) | ||||
| 	if err != nil { | ||||
| 		return Txn{}, err | ||||
| 	} | ||||
| 	return Txn{c}, nil | ||||
| } | ||||
| 
 | ||||
| // BeginExclusive starts an exclusive transaction. | ||||
| // | ||||
| // https://sqlite.org/lang_transaction.html | ||||
| func (c *Conn) BeginExclusive() (Txn, error) { | ||||
| 	err := c.Exec(`BEGIN EXCLUSIVE`) | ||||
| 	if err != nil { | ||||
| 		return Txn{}, err | ||||
| 	} | ||||
| 	return Txn{c}, nil | ||||
| } | ||||
| 
 | ||||
| // End calls either [Txn.Commit] or [Txn.Rollback] | ||||
| // depending on whether *error points to a nil or non-nil error. | ||||
| // | ||||
| // This is meant to be deferred: | ||||
| // | ||||
| //	func doWork(db *sqlite3.Conn) (err error) { | ||||
| //		tx := db.Begin() | ||||
| //		defer tx.End(&err) | ||||
| // | ||||
| //		// ... do work in the transaction | ||||
| //	} | ||||
| // | ||||
| // https://sqlite.org/lang_transaction.html | ||||
| func (tx Txn) End(errp *error) { | ||||
| 	recovered := recover() | ||||
| 	if recovered != nil { | ||||
| 		defer panic(recovered) | ||||
| 	} | ||||
| 
 | ||||
| 	if *errp == nil && recovered == nil { | ||||
| 		// Success path. | ||||
| 		if tx.c.GetAutocommit() { // There is nothing to commit. | ||||
| 			return | ||||
| 		} | ||||
| 		*errp = tx.Commit() | ||||
| 		if *errp == nil { | ||||
| 			return | ||||
| 		} | ||||
| 		// Fall through to the error path. | ||||
| 	} | ||||
| 
 | ||||
| 	// Error path. | ||||
| 	if tx.c.GetAutocommit() { // There is nothing to rollback. | ||||
| 		return | ||||
| 	} | ||||
| 	err := tx.Rollback() | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Commit commits the transaction. | ||||
| // | ||||
| // https://sqlite.org/lang_transaction.html | ||||
| func (tx Txn) Commit() error { | ||||
| 	return tx.c.Exec(`COMMIT`) | ||||
| } | ||||
| 
 | ||||
| // Rollback rolls back the transaction, | ||||
| // even if the connection has been interrupted. | ||||
| // | ||||
| // https://sqlite.org/lang_transaction.html | ||||
| func (tx Txn) Rollback() error { | ||||
| 	return tx.c.txnExecInterrupted(`ROLLBACK`) | ||||
| } | ||||
| 
 | ||||
| // Savepoint is a marker within a transaction | ||||
| // that allows for partial rollback. | ||||
| // | ||||
| // https://sqlite.org/lang_savepoint.html | ||||
| type Savepoint struct { | ||||
| 	c    *Conn | ||||
| 	name string | ||||
| } | ||||
| 
 | ||||
| // Savepoint establishes a new transaction savepoint. | ||||
| // | ||||
| // https://sqlite.org/lang_savepoint.html | ||||
| func (c *Conn) Savepoint() Savepoint { | ||||
| 	// Names can be reused; this makes catching bugs more likely. | ||||
| 	name := saveptName() + "_" + strconv.Itoa(int(rand.Int31())) | ||||
| 
 | ||||
| 	err := c.txnExecInterrupted(fmt.Sprintf("SAVEPOINT %q;", name)) | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| 	return Savepoint{c: c, name: name} | ||||
| } | ||||
| 
 | ||||
| func saveptName() (name string) { | ||||
| 	defer func() { | ||||
| 		if name == "" { | ||||
| 			name = "sqlite3.Savepoint" | ||||
| 		} | ||||
| 	}() | ||||
| 
 | ||||
| 	var pc [8]uintptr | ||||
| 	n := runtime.Callers(3, pc[:]) | ||||
| 	if n <= 0 { | ||||
| 		return "" | ||||
| 	} | ||||
| 	frames := runtime.CallersFrames(pc[:n]) | ||||
| 	frame, more := frames.Next() | ||||
| 	for more && (strings.HasPrefix(frame.Function, "database/sql.") || | ||||
| 		strings.HasPrefix(frame.Function, "github.com/ncruces/go-sqlite3/driver.")) { | ||||
| 		frame, more = frames.Next() | ||||
| 	} | ||||
| 	return frame.Function | ||||
| } | ||||
| 
 | ||||
| // Release releases the savepoint rolling back any changes | ||||
| // if *error points to a non-nil error. | ||||
| // | ||||
| // This is meant to be deferred: | ||||
| // | ||||
| //	func doWork(db *sqlite3.Conn) (err error) { | ||||
| //		savept := db.Savepoint() | ||||
| //		defer savept.Release(&err) | ||||
| // | ||||
| //		// ... do work in the transaction | ||||
| //	} | ||||
| func (s Savepoint) Release(errp *error) { | ||||
| 	recovered := recover() | ||||
| 	if recovered != nil { | ||||
| 		defer panic(recovered) | ||||
| 	} | ||||
| 
 | ||||
| 	if *errp == nil && recovered == nil { | ||||
| 		// Success path. | ||||
| 		if s.c.GetAutocommit() { // There is nothing to commit. | ||||
| 			return | ||||
| 		} | ||||
| 		*errp = s.c.Exec(fmt.Sprintf("RELEASE %q;", s.name)) | ||||
| 		if *errp == nil { | ||||
| 			return | ||||
| 		} | ||||
| 		// Fall through to the error path. | ||||
| 	} | ||||
| 
 | ||||
| 	// Error path. | ||||
| 	if s.c.GetAutocommit() { // There is nothing to rollback. | ||||
| 		return | ||||
| 	} | ||||
| 	// ROLLBACK and RELEASE even if interrupted. | ||||
| 	err := s.c.txnExecInterrupted(fmt.Sprintf(` | ||||
| 		ROLLBACK TO %[1]q; | ||||
| 		RELEASE %[1]q; | ||||
| 	`, s.name)) | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Rollback rolls the transaction back to the savepoint, | ||||
| // even if the connection has been interrupted. | ||||
| // Rollback does not release the savepoint. | ||||
| // | ||||
| // https://sqlite.org/lang_transaction.html | ||||
| func (s Savepoint) Rollback() error { | ||||
| 	// ROLLBACK even if interrupted. | ||||
| 	return s.c.txnExecInterrupted(fmt.Sprintf("ROLLBACK TO %q;", s.name)) | ||||
| } | ||||
| 
 | ||||
| func (c *Conn) txnExecInterrupted(sql string) error { | ||||
| 	err := c.Exec(sql) | ||||
| 	if errors.Is(err, INTERRUPT) { | ||||
| 		old := c.SetInterrupt(context.Background()) | ||||
| 		defer c.SetInterrupt(old) | ||||
| 		err = c.Exec(sql) | ||||
| 	} | ||||
| 	return err | ||||
| } | ||||
| 
 | ||||
| // TxnState starts a deferred transaction. | ||||
| // | ||||
| // https://sqlite.org/c3ref/txn_state.html | ||||
| func (c *Conn) TxnState(schema string) TxnState { | ||||
| 	var ptr uint32 | ||||
| 	if schema != "" { | ||||
| 		defer c.arena.mark()() | ||||
| 		ptr = c.arena.string(schema) | ||||
| 	} | ||||
| 	r := c.call("sqlite3_txn_state", uint64(c.handle), uint64(ptr)) | ||||
| 	return TxnState(r) | ||||
| } | ||||
| 
 | ||||
| // CommitHook registers a callback function to be invoked | ||||
| // whenever a transaction is committed. | ||||
| // Return true to allow the commit operation to continue normally. | ||||
| // | ||||
| // https://sqlite.org/c3ref/commit_hook.html | ||||
| func (c *Conn) CommitHook(cb func() (ok bool)) { | ||||
| 	var enable uint64 | ||||
| 	if cb != nil { | ||||
| 		enable = 1 | ||||
| 	} | ||||
| 	c.call("sqlite3_commit_hook_go", uint64(c.handle), enable) | ||||
| 	c.commit = cb | ||||
| } | ||||
| 
 | ||||
| // RollbackHook registers a callback function to be invoked | ||||
| // whenever a transaction is rolled back. | ||||
| // | ||||
| // https://sqlite.org/c3ref/commit_hook.html | ||||
| func (c *Conn) RollbackHook(cb func()) { | ||||
| 	var enable uint64 | ||||
| 	if cb != nil { | ||||
| 		enable = 1 | ||||
| 	} | ||||
| 	c.call("sqlite3_rollback_hook_go", uint64(c.handle), enable) | ||||
| 	c.rollback = cb | ||||
| } | ||||
| 
 | ||||
| // UpdateHook registers a callback function to be invoked | ||||
| // whenever a row is updated, inserted or deleted in a rowid table. | ||||
| // | ||||
| // https://sqlite.org/c3ref/update_hook.html | ||||
| func (c *Conn) UpdateHook(cb func(action AuthorizerActionCode, schema, table string, rowid int64)) { | ||||
| 	var enable uint64 | ||||
| 	if cb != nil { | ||||
| 		enable = 1 | ||||
| 	} | ||||
| 	c.call("sqlite3_update_hook_go", uint64(c.handle), enable) | ||||
| 	c.update = cb | ||||
| } | ||||
| 
 | ||||
| func commitCallback(ctx context.Context, mod api.Module, pDB uint32) (rollback uint32) { | ||||
| 	if c, ok := ctx.Value(connKey{}).(*Conn); ok && c.handle == pDB && c.commit != nil { | ||||
| 		if !c.commit() { | ||||
| 			rollback = 1 | ||||
| 		} | ||||
| 	} | ||||
| 	return rollback | ||||
| } | ||||
| 
 | ||||
| func rollbackCallback(ctx context.Context, mod api.Module, pDB uint32) { | ||||
| 	if c, ok := ctx.Value(connKey{}).(*Conn); ok && c.handle == pDB && c.rollback != nil { | ||||
| 		c.rollback() | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func updateCallback(ctx context.Context, mod api.Module, pDB uint32, action AuthorizerActionCode, zSchema, zTabName uint32, rowid uint64) { | ||||
| 	if c, ok := ctx.Value(connKey{}).(*Conn); ok && c.handle == pDB && c.update != nil { | ||||
| 		schema := util.ReadString(mod, zSchema, _MAX_NAME) | ||||
| 		table := util.ReadString(mod, zTabName, _MAX_NAME) | ||||
| 		c.update(action, schema, table, int64(rowid)) | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										16
									
								
								vendor/github.com/ncruces/go-sqlite3/util/osutil/open.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								vendor/github.com/ncruces/go-sqlite3/util/osutil/open.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,16 @@ | |||
| //go:build !windows | ||||
| 
 | ||||
| package osutil | ||||
| 
 | ||||
| import ( | ||||
| 	"io/fs" | ||||
| 	"os" | ||||
| ) | ||||
| 
 | ||||
| // OpenFile behaves the same as [os.OpenFile], | ||||
| // except on Windows it sets [syscall.FILE_SHARE_DELETE]. | ||||
| // | ||||
| // See: https://go.dev/issue/32088#issuecomment-502850674 | ||||
| func OpenFile(name string, flag int, perm fs.FileMode) (*os.File, error) { | ||||
| 	return os.OpenFile(name, flag, perm) | ||||
| } | ||||
							
								
								
									
										112
									
								
								vendor/github.com/ncruces/go-sqlite3/util/osutil/open_windows.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								vendor/github.com/ncruces/go-sqlite3/util/osutil/open_windows.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,112 @@ | |||
| package osutil | ||||
| 
 | ||||
| import ( | ||||
| 	"io/fs" | ||||
| 	"os" | ||||
| 	. "syscall" | ||||
| 	"unsafe" | ||||
| ) | ||||
| 
 | ||||
| // OpenFile behaves the same as [os.OpenFile], | ||||
| // except on Windows it sets [syscall.FILE_SHARE_DELETE]. | ||||
| // | ||||
| // See: https://go.dev/issue/32088#issuecomment-502850674 | ||||
| func OpenFile(name string, flag int, perm fs.FileMode) (*os.File, error) { | ||||
| 	if name == "" { | ||||
| 		return nil, &os.PathError{Op: "open", Path: name, Err: ENOENT} | ||||
| 	} | ||||
| 	r, e := syscallOpen(name, flag, uint32(perm.Perm())) | ||||
| 	if e != nil { | ||||
| 		return nil, &os.PathError{Op: "open", Path: name, Err: e} | ||||
| 	} | ||||
| 	return os.NewFile(uintptr(r), name), nil | ||||
| } | ||||
| 
 | ||||
| // syscallOpen is a copy of [syscall.Open] | ||||
| // that uses [syscall.FILE_SHARE_DELETE]. | ||||
| // | ||||
| // https://go.dev/src/syscall/syscall_windows.go | ||||
| func syscallOpen(path string, mode int, perm uint32) (fd Handle, err error) { | ||||
| 	if len(path) == 0 { | ||||
| 		return InvalidHandle, ERROR_FILE_NOT_FOUND | ||||
| 	} | ||||
| 	pathp, err := UTF16PtrFromString(path) | ||||
| 	if err != nil { | ||||
| 		return InvalidHandle, err | ||||
| 	} | ||||
| 	var access uint32 | ||||
| 	switch mode & (O_RDONLY | O_WRONLY | O_RDWR) { | ||||
| 	case O_RDONLY: | ||||
| 		access = GENERIC_READ | ||||
| 	case O_WRONLY: | ||||
| 		access = GENERIC_WRITE | ||||
| 	case O_RDWR: | ||||
| 		access = GENERIC_READ | GENERIC_WRITE | ||||
| 	} | ||||
| 	if mode&O_CREAT != 0 { | ||||
| 		access |= GENERIC_WRITE | ||||
| 	} | ||||
| 	if mode&O_APPEND != 0 { | ||||
| 		access &^= GENERIC_WRITE | ||||
| 		access |= FILE_APPEND_DATA | ||||
| 	} | ||||
| 	sharemode := uint32(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE) | ||||
| 	var sa *SecurityAttributes | ||||
| 	if mode&O_CLOEXEC == 0 { | ||||
| 		sa = makeInheritSa() | ||||
| 	} | ||||
| 	var createmode uint32 | ||||
| 	switch { | ||||
| 	case mode&(O_CREAT|O_EXCL) == (O_CREAT | O_EXCL): | ||||
| 		createmode = CREATE_NEW | ||||
| 	case mode&(O_CREAT|O_TRUNC) == (O_CREAT | O_TRUNC): | ||||
| 		createmode = CREATE_ALWAYS | ||||
| 	case mode&O_CREAT == O_CREAT: | ||||
| 		createmode = OPEN_ALWAYS | ||||
| 	case mode&O_TRUNC == O_TRUNC: | ||||
| 		createmode = TRUNCATE_EXISTING | ||||
| 	default: | ||||
| 		createmode = OPEN_EXISTING | ||||
| 	} | ||||
| 	var attrs uint32 = FILE_ATTRIBUTE_NORMAL | ||||
| 	if perm&S_IWRITE == 0 { | ||||
| 		attrs = FILE_ATTRIBUTE_READONLY | ||||
| 		if createmode == CREATE_ALWAYS { | ||||
| 			const _ERROR_BAD_NETPATH = Errno(53) | ||||
| 			// We have been asked to create a read-only file. | ||||
| 			// If the file already exists, the semantics of | ||||
| 			// the Unix open system call is to preserve the | ||||
| 			// existing permissions. If we pass CREATE_ALWAYS | ||||
| 			// and FILE_ATTRIBUTE_READONLY to CreateFile, | ||||
| 			// and the file already exists, CreateFile will | ||||
| 			// change the file permissions. | ||||
| 			// Avoid that to preserve the Unix semantics. | ||||
| 			h, e := CreateFile(pathp, access, sharemode, sa, TRUNCATE_EXISTING, FILE_ATTRIBUTE_NORMAL, 0) | ||||
| 			switch e { | ||||
| 			case ERROR_FILE_NOT_FOUND, _ERROR_BAD_NETPATH, ERROR_PATH_NOT_FOUND: | ||||
| 				// File does not exist. These are the same | ||||
| 				// errors as Errno.Is checks for ErrNotExist. | ||||
| 				// Carry on to create the file. | ||||
| 			default: | ||||
| 				// Success or some different error. | ||||
| 				return h, e | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	if createmode == OPEN_EXISTING && access == GENERIC_READ { | ||||
| 		// Necessary for opening directory handles. | ||||
| 		attrs |= FILE_FLAG_BACKUP_SEMANTICS | ||||
| 	} | ||||
| 	if mode&O_SYNC != 0 { | ||||
| 		const _FILE_FLAG_WRITE_THROUGH = 0x80000000 | ||||
| 		attrs |= _FILE_FLAG_WRITE_THROUGH | ||||
| 	} | ||||
| 	return CreateFile(pathp, access, sharemode, sa, createmode, attrs, 0) | ||||
| } | ||||
| 
 | ||||
| func makeInheritSa() *SecurityAttributes { | ||||
| 	var sa SecurityAttributes | ||||
| 	sa.Length = uint32(unsafe.Sizeof(sa)) | ||||
| 	sa.InheritHandle = 1 | ||||
| 	return &sa | ||||
| } | ||||
							
								
								
									
										33
									
								
								vendor/github.com/ncruces/go-sqlite3/util/osutil/osfs.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								vendor/github.com/ncruces/go-sqlite3/util/osutil/osfs.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,33 @@ | |||
| package osutil | ||||
| 
 | ||||
| import ( | ||||
| 	"io/fs" | ||||
| 	"os" | ||||
| ) | ||||
| 
 | ||||
| // FS implements [fs.FS], [fs.StatFS], and [fs.ReadFileFS] | ||||
| // using package [os]. | ||||
| // | ||||
| // This filesystem does not respect [fs.ValidPath] rules, | ||||
| // and fails [testing/fstest.TestFS]! | ||||
| // | ||||
| // Still, it can be a useful tool to unify implementations | ||||
| // that can access either the [os] filesystem or an [fs.FS]. | ||||
| // It's OK to use this to open files, but you should avoid | ||||
| // opening directories, resolving paths, or walking the file system. | ||||
| type FS struct{} | ||||
| 
 | ||||
| // Open implements [fs.FS]. | ||||
| func (FS) Open(name string) (fs.File, error) { | ||||
| 	return OpenFile(name, os.O_RDONLY, 0) | ||||
| } | ||||
| 
 | ||||
| // ReadFileFS implements [fs.StatFS]. | ||||
| func (FS) Stat(name string) (fs.FileInfo, error) { | ||||
| 	return os.Stat(name) | ||||
| } | ||||
| 
 | ||||
| // ReadFile implements [fs.ReadFileFS]. | ||||
| func (FS) ReadFile(name string) ([]byte, error) { | ||||
| 	return os.ReadFile(name) | ||||
| } | ||||
							
								
								
									
										2
									
								
								vendor/github.com/ncruces/go-sqlite3/util/osutil/osutil.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/ncruces/go-sqlite3/util/osutil/osutil.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,2 @@ | |||
| // Package osutil implements operating system utility functions. | ||||
| package osutil | ||||
							
								
								
									
										236
									
								
								vendor/github.com/ncruces/go-sqlite3/value.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										236
									
								
								vendor/github.com/ncruces/go-sqlite3/value.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,236 @@ | |||
| package sqlite3 | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"math" | ||||
| 	"strconv" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/ncruces/go-sqlite3/internal/util" | ||||
| ) | ||||
| 
 | ||||
| // Value is any value that can be stored in a database table. | ||||
| // | ||||
| // https://sqlite.org/c3ref/value.html | ||||
| type Value struct { | ||||
| 	c      *Conn | ||||
| 	handle uint32 | ||||
| 	unprot bool | ||||
| 	copied bool | ||||
| } | ||||
| 
 | ||||
| func (v Value) protected() uint64 { | ||||
| 	if v.unprot { | ||||
| 		panic(util.ValueErr) | ||||
| 	} | ||||
| 	return uint64(v.handle) | ||||
| } | ||||
| 
 | ||||
| // Dup makes a copy of the SQL value and returns a pointer to that copy. | ||||
| // | ||||
| // https://sqlite.org/c3ref/value_dup.html | ||||
| func (v Value) Dup() *Value { | ||||
| 	r := v.c.call("sqlite3_value_dup", uint64(v.handle)) | ||||
| 	return &Value{ | ||||
| 		c:      v.c, | ||||
| 		copied: true, | ||||
| 		handle: uint32(r), | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Close frees an SQL value previously obtained by [Value.Dup]. | ||||
| // | ||||
| // https://sqlite.org/c3ref/value_dup.html | ||||
| func (dup *Value) Close() error { | ||||
| 	if !dup.copied { | ||||
| 		panic(util.ValueErr) | ||||
| 	} | ||||
| 	dup.c.call("sqlite3_value_free", uint64(dup.handle)) | ||||
| 	dup.handle = 0 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // Type returns the initial datatype of the value. | ||||
| // | ||||
| // https://sqlite.org/c3ref/value_blob.html | ||||
| func (v Value) Type() Datatype { | ||||
| 	r := v.c.call("sqlite3_value_type", v.protected()) | ||||
| 	return Datatype(r) | ||||
| } | ||||
| 
 | ||||
| // Type returns the numeric datatype of the value. | ||||
| // | ||||
| // https://sqlite.org/c3ref/value_blob.html | ||||
| func (v Value) NumericType() Datatype { | ||||
| 	r := v.c.call("sqlite3_value_numeric_type", v.protected()) | ||||
| 	return Datatype(r) | ||||
| } | ||||
| 
 | ||||
| // Bool returns the value as a bool. | ||||
| // SQLite does not have a separate boolean storage class. | ||||
| // Instead, boolean values are retrieved as integers, | ||||
| // with 0 converted to false and any other value to true. | ||||
| // | ||||
| // https://sqlite.org/c3ref/value_blob.html | ||||
| func (v Value) Bool() bool { | ||||
| 	return v.Int64() != 0 | ||||
| } | ||||
| 
 | ||||
| // Int returns the value as an int. | ||||
| // | ||||
| // https://sqlite.org/c3ref/value_blob.html | ||||
| func (v Value) Int() int { | ||||
| 	return int(v.Int64()) | ||||
| } | ||||
| 
 | ||||
| // Int64 returns the value as an int64. | ||||
| // | ||||
| // https://sqlite.org/c3ref/value_blob.html | ||||
| func (v Value) Int64() int64 { | ||||
| 	r := v.c.call("sqlite3_value_int64", v.protected()) | ||||
| 	return int64(r) | ||||
| } | ||||
| 
 | ||||
| // Float returns the value as a float64. | ||||
| // | ||||
| // https://sqlite.org/c3ref/value_blob.html | ||||
| func (v Value) Float() float64 { | ||||
| 	r := v.c.call("sqlite3_value_double", v.protected()) | ||||
| 	return math.Float64frombits(r) | ||||
| } | ||||
| 
 | ||||
| // Time returns the value as a [time.Time]. | ||||
| // | ||||
| // https://sqlite.org/c3ref/value_blob.html | ||||
| func (v Value) Time(format TimeFormat) time.Time { | ||||
| 	var a any | ||||
| 	switch v.Type() { | ||||
| 	case INTEGER: | ||||
| 		a = v.Int64() | ||||
| 	case FLOAT: | ||||
| 		a = v.Float() | ||||
| 	case TEXT, BLOB: | ||||
| 		a = v.Text() | ||||
| 	case NULL: | ||||
| 		return time.Time{} | ||||
| 	default: | ||||
| 		panic(util.AssertErr()) | ||||
| 	} | ||||
| 	t, _ := format.Decode(a) | ||||
| 	return t | ||||
| } | ||||
| 
 | ||||
| // Text returns the value as a string. | ||||
| // | ||||
| // https://sqlite.org/c3ref/value_blob.html | ||||
| func (v Value) Text() string { | ||||
| 	return string(v.RawText()) | ||||
| } | ||||
| 
 | ||||
| // Blob appends to buf and returns | ||||
| // the value as a []byte. | ||||
| // | ||||
| // https://sqlite.org/c3ref/value_blob.html | ||||
| func (v Value) Blob(buf []byte) []byte { | ||||
| 	return append(buf, v.RawBlob()...) | ||||
| } | ||||
| 
 | ||||
| // RawText returns the value as a []byte. | ||||
| // The []byte is owned by SQLite and may be invalidated by | ||||
| // subsequent calls to [Value] methods. | ||||
| // | ||||
| // https://sqlite.org/c3ref/value_blob.html | ||||
| func (v Value) RawText() []byte { | ||||
| 	r := v.c.call("sqlite3_value_text", v.protected()) | ||||
| 	return v.rawBytes(uint32(r)) | ||||
| } | ||||
| 
 | ||||
| // RawBlob returns the value as a []byte. | ||||
| // The []byte is owned by SQLite and may be invalidated by | ||||
| // subsequent calls to [Value] methods. | ||||
| // | ||||
| // https://sqlite.org/c3ref/value_blob.html | ||||
| func (v Value) RawBlob() []byte { | ||||
| 	r := v.c.call("sqlite3_value_blob", v.protected()) | ||||
| 	return v.rawBytes(uint32(r)) | ||||
| } | ||||
| 
 | ||||
| func (v Value) rawBytes(ptr uint32) []byte { | ||||
| 	if ptr == 0 { | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	r := v.c.call("sqlite3_value_bytes", v.protected()) | ||||
| 	return util.View(v.c.mod, ptr, r) | ||||
| } | ||||
| 
 | ||||
| // Pointer gets the pointer associated with this value, | ||||
| // or nil if it has no associated pointer. | ||||
| func (v Value) Pointer() any { | ||||
| 	r := v.c.call("sqlite3_value_pointer_go", v.protected()) | ||||
| 	return util.GetHandle(v.c.ctx, uint32(r)) | ||||
| } | ||||
| 
 | ||||
| // JSON parses a JSON-encoded value | ||||
| // and stores the result in the value pointed to by ptr. | ||||
| func (v Value) JSON(ptr any) error { | ||||
| 	var data []byte | ||||
| 	switch v.Type() { | ||||
| 	case NULL: | ||||
| 		data = append(data, "null"...) | ||||
| 	case TEXT: | ||||
| 		data = v.RawText() | ||||
| 	case BLOB: | ||||
| 		data = v.RawBlob() | ||||
| 	case INTEGER: | ||||
| 		data = strconv.AppendInt(nil, v.Int64(), 10) | ||||
| 	case FLOAT: | ||||
| 		data = strconv.AppendFloat(nil, v.Float(), 'g', -1, 64) | ||||
| 	default: | ||||
| 		panic(util.AssertErr()) | ||||
| 	} | ||||
| 	return json.Unmarshal(data, ptr) | ||||
| } | ||||
| 
 | ||||
| // NoChange returns true if and only if the value is unchanged | ||||
| // in a virtual table update operatiom. | ||||
| // | ||||
| // https://sqlite.org/c3ref/value_blob.html | ||||
| func (v Value) NoChange() bool { | ||||
| 	r := v.c.call("sqlite3_value_nochange", v.protected()) | ||||
| 	return r != 0 | ||||
| } | ||||
| 
 | ||||
| // InFirst returns the first element | ||||
| // on the right-hand side of an IN constraint. | ||||
| // | ||||
| // https://sqlite.org/c3ref/vtab_in_first.html | ||||
| func (v Value) InFirst() (Value, error) { | ||||
| 	defer v.c.arena.mark()() | ||||
| 	valPtr := v.c.arena.new(ptrlen) | ||||
| 	r := v.c.call("sqlite3_vtab_in_first", uint64(v.handle), uint64(valPtr)) | ||||
| 	if err := v.c.error(r); err != nil { | ||||
| 		return Value{}, err | ||||
| 	} | ||||
| 	return Value{ | ||||
| 		c:      v.c, | ||||
| 		handle: util.ReadUint32(v.c.mod, valPtr), | ||||
| 	}, nil | ||||
| } | ||||
| 
 | ||||
| // InNext returns the next element | ||||
| // on the right-hand side of an IN constraint. | ||||
| // | ||||
| // https://sqlite.org/c3ref/vtab_in_first.html | ||||
| func (v Value) InNext() (Value, error) { | ||||
| 	defer v.c.arena.mark()() | ||||
| 	valPtr := v.c.arena.new(ptrlen) | ||||
| 	r := v.c.call("sqlite3_vtab_in_next", uint64(v.handle), uint64(valPtr)) | ||||
| 	if err := v.c.error(r); err != nil { | ||||
| 		return Value{}, err | ||||
| 	} | ||||
| 	return Value{ | ||||
| 		c:      v.c, | ||||
| 		handle: util.ReadUint32(v.c.mod, valPtr), | ||||
| 	}, nil | ||||
| } | ||||
							
								
								
									
										86
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/README.md
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/README.md
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,86 @@ | |||
| # Go SQLite VFS API | ||||
| 
 | ||||
| This package implements the SQLite [OS Interface](https://sqlite.org/vfs.html) (aka VFS). | ||||
| 
 | ||||
| It replaces the default SQLite VFS with a **pure Go** implementation, | ||||
| and exposes [interfaces](https://pkg.go.dev/github.com/ncruces/go-sqlite3/vfs#VFS) | ||||
| that should allow you to implement your own custom VFSes. | ||||
| 
 | ||||
| Since it is a from scratch reimplementation, | ||||
| there are naturally some ways it deviates from the original. | ||||
| 
 | ||||
| The main differences are [file locking](#file-locking) and [WAL mode](#write-ahead-logging) support. | ||||
| 
 | ||||
| ### File Locking | ||||
| 
 | ||||
| POSIX advisory locks, which SQLite uses on Unix, are | ||||
| [broken by design](https://github.com/sqlite/sqlite/blob/b74eb0/src/os_unix.c#L1073-L1161). | ||||
| 
 | ||||
| On Linux and macOS, this module uses | ||||
| [OFD locks](https://www.gnu.org/software/libc/manual/html_node/Open-File-Description-Locks.html) | ||||
| to synchronize access to database files. | ||||
| OFD locks are fully compatible with POSIX advisory locks. | ||||
| 
 | ||||
| This module can also use | ||||
| [BSD locks](https://man.freebsd.org/cgi/man.cgi?query=flock&sektion=2), | ||||
| albeit with reduced concurrency (`BEGIN IMMEDIATE` behaves like `BEGIN EXCLUSIVE`). | ||||
| On BSD, macOS, and illumos, BSD locks are fully compatible with POSIX advisory locks; | ||||
| on Linux and z/OS, they are fully functional, but incompatible; | ||||
| elsewhere, they are very likely broken. | ||||
| BSD locks are the default on BSD and illumos, | ||||
| but you can opt into them with the `sqlite3_flock` build tag. | ||||
| 
 | ||||
| On Windows, this module uses `LockFileEx` and `UnlockFileEx`, | ||||
| like SQLite. | ||||
| 
 | ||||
| Otherwise, file locking is not supported, and you must use | ||||
| [`nolock=1`](https://sqlite.org/uri.html#urinolock) | ||||
| (or [`immutable=1`](https://sqlite.org/uri.html#uriimmutable)) | ||||
| to open database files. | ||||
| To use the [`database/sql`](https://pkg.go.dev/database/sql) driver | ||||
| with `nolock=1` you must disable connection pooling by calling | ||||
| [`db.SetMaxOpenConns(1)`](https://pkg.go.dev/database/sql#DB.SetMaxOpenConns). | ||||
| 
 | ||||
| You can use [`vfs.SupportsFileLocking`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/vfs#SupportsFileLocking) | ||||
| to check if your build supports file locking. | ||||
| 
 | ||||
| ### Write-Ahead Logging | ||||
| 
 | ||||
| On 64-bit Linux and macOS, this module uses `mmap` to implement | ||||
| [shared-memory for the WAL-index](https://sqlite.org/wal.html#implementation_of_shared_memory_for_the_wal_index), | ||||
| like SQLite. | ||||
| 
 | ||||
| To allow `mmap` to work, each connection needs to reserve up to 4GB of address space. | ||||
| To limit the address space each connection reserves, | ||||
| use [`WithMemoryLimitPages`](../tests/testcfg/testcfg.go). | ||||
| 
 | ||||
| Otherwise, [WAL support is limited](https://sqlite.org/wal.html#noshm), | ||||
| and `EXCLUSIVE` locking mode must be set to create, read, and write WAL databases. | ||||
| To use `EXCLUSIVE` locking mode with the | ||||
| [`database/sql`](https://pkg.go.dev/database/sql) driver | ||||
| you must disable connection pooling by calling | ||||
| [`db.SetMaxOpenConns(1)`](https://pkg.go.dev/database/sql#DB.SetMaxOpenConns). | ||||
| 
 | ||||
| You can use [`vfs.SupportsSharedMemory`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/vfs#SupportsSharedMemory) | ||||
| to check if your build supports shared memory. | ||||
| 
 | ||||
| ### Batch-Atomic Write | ||||
| 
 | ||||
| On 64-bit Linux, this module supports [batch-atomic writes](https://sqlite.org/cgi/src/technote/714) | ||||
| on the F2FS filesystem. | ||||
| 
 | ||||
| ### Build Tags | ||||
| 
 | ||||
| The VFS can be customized with a few build tags: | ||||
| - `sqlite3_flock` forces the use of BSD locks; it can be used on z/OS to enable locking, | ||||
|   and elsewhere to test BSD locks. | ||||
| - `sqlite3_nosys` prevents importing [`x/sys`](https://pkg.go.dev/golang.org/x/sys); | ||||
|   disables locking _and_ shared memory on all platforms. | ||||
| - `sqlite3_noshm` disables shared memory on all platforms. | ||||
| 
 | ||||
| > [!IMPORTANT] | ||||
| > The default configuration of this package is compatible with | ||||
| > the standard [Unix and Windows SQLite VFSes](https://sqlite.org/vfs.html#multiple_vfses); | ||||
| > `sqlite3_flock` is compatible with the [`unix-flock` VFS](https://sqlite.org/compile.html#enable_locking_style). | ||||
| > If incompatible file locking is used, accessing databases concurrently with _other_ SQLite libraries | ||||
| > will eventually corrupt data. | ||||
							
								
								
									
										175
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/api.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										175
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/api.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,175 @@ | |||
| // Package vfs wraps the C SQLite VFS API. | ||||
| package vfs | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"io" | ||||
| 
 | ||||
| 	"github.com/tetratelabs/wazero/api" | ||||
| ) | ||||
| 
 | ||||
| // A VFS defines the interface between the SQLite core and the underlying operating system. | ||||
| // | ||||
| // Use sqlite3.ErrorCode or sqlite3.ExtendedErrorCode to return specific error codes to SQLite. | ||||
| // | ||||
| // https://sqlite.org/c3ref/vfs.html | ||||
| type VFS interface { | ||||
| 	Open(name string, flags OpenFlag) (File, OpenFlag, error) | ||||
| 	Delete(name string, syncDir bool) error | ||||
| 	Access(name string, flags AccessFlag) (bool, error) | ||||
| 	FullPathname(name string) (string, error) | ||||
| } | ||||
| 
 | ||||
| // VFSFilename extends VFS with the ability to use Filename | ||||
| // objects for opening files. | ||||
| // | ||||
| // https://sqlite.org/c3ref/filename.html | ||||
| type VFSFilename interface { | ||||
| 	VFS | ||||
| 	OpenFilename(name *Filename, flags OpenFlag) (File, OpenFlag, error) | ||||
| } | ||||
| 
 | ||||
| // A File represents an open file in the OS interface layer. | ||||
| // | ||||
| // Use sqlite3.ErrorCode or sqlite3.ExtendedErrorCode to return specific error codes to SQLite. | ||||
| // In particular, sqlite3.BUSY is necessary to correctly implement lock methods. | ||||
| // | ||||
| // https://sqlite.org/c3ref/io_methods.html | ||||
| type File interface { | ||||
| 	Close() error | ||||
| 	ReadAt(p []byte, off int64) (n int, err error) | ||||
| 	WriteAt(p []byte, off int64) (n int, err error) | ||||
| 	Truncate(size int64) error | ||||
| 	Sync(flags SyncFlag) error | ||||
| 	Size() (int64, error) | ||||
| 	Lock(lock LockLevel) error | ||||
| 	Unlock(lock LockLevel) error | ||||
| 	CheckReservedLock() (bool, error) | ||||
| 	SectorSize() int | ||||
| 	DeviceCharacteristics() DeviceCharacteristic | ||||
| } | ||||
| 
 | ||||
| // FileLockState extends File to implement the | ||||
| // SQLITE_FCNTL_LOCKSTATE file control opcode. | ||||
| // | ||||
| // https://sqlite.org/c3ref/c_fcntl_begin_atomic_write.html#sqlitefcntllockstate | ||||
| type FileLockState interface { | ||||
| 	File | ||||
| 	LockState() LockLevel | ||||
| } | ||||
| 
 | ||||
| // FileChunkSize extends File to implement the | ||||
| // SQLITE_FCNTL_CHUNK_SIZE file control opcode. | ||||
| // | ||||
| // https://sqlite.org/c3ref/c_fcntl_begin_atomic_write.html#sqlitefcntlchunksize | ||||
| type FileChunkSize interface { | ||||
| 	File | ||||
| 	ChunkSize(size int) | ||||
| } | ||||
| 
 | ||||
| // FileSizeHint extends File to implement the | ||||
| // SQLITE_FCNTL_SIZE_HINT file control opcode. | ||||
| // | ||||
| // https://sqlite.org/c3ref/c_fcntl_begin_atomic_write.html#sqlitefcntlsizehint | ||||
| type FileSizeHint interface { | ||||
| 	File | ||||
| 	SizeHint(size int64) error | ||||
| } | ||||
| 
 | ||||
| // FileHasMoved extends File to implement the | ||||
| // SQLITE_FCNTL_HAS_MOVED file control opcode. | ||||
| // | ||||
| // https://sqlite.org/c3ref/c_fcntl_begin_atomic_write.html#sqlitefcntlhasmoved | ||||
| type FileHasMoved interface { | ||||
| 	File | ||||
| 	HasMoved() (bool, error) | ||||
| } | ||||
| 
 | ||||
| // FileOverwrite extends File to implement the | ||||
| // SQLITE_FCNTL_OVERWRITE file control opcode. | ||||
| // | ||||
| // https://sqlite.org/c3ref/c_fcntl_begin_atomic_write.html#sqlitefcntloverwrite | ||||
| type FileOverwrite interface { | ||||
| 	File | ||||
| 	Overwrite() error | ||||
| } | ||||
| 
 | ||||
| // FilePersistentWAL extends File to implement the | ||||
| // SQLITE_FCNTL_PERSIST_WAL file control opcode. | ||||
| // | ||||
| // https://sqlite.org/c3ref/c_fcntl_begin_atomic_write.html#sqlitefcntlpersistwal | ||||
| type FilePersistentWAL interface { | ||||
| 	File | ||||
| 	PersistentWAL() bool | ||||
| 	SetPersistentWAL(bool) | ||||
| } | ||||
| 
 | ||||
| // FilePowersafeOverwrite extends File to implement the | ||||
| // SQLITE_FCNTL_POWERSAFE_OVERWRITE file control opcode. | ||||
| // | ||||
| // https://sqlite.org/c3ref/c_fcntl_begin_atomic_write.html#sqlitefcntlpowersafeoverwrite | ||||
| type FilePowersafeOverwrite interface { | ||||
| 	File | ||||
| 	PowersafeOverwrite() bool | ||||
| 	SetPowersafeOverwrite(bool) | ||||
| } | ||||
| 
 | ||||
| // FileCommitPhaseTwo extends File to implement the | ||||
| // SQLITE_FCNTL_COMMIT_PHASETWO file control opcode. | ||||
| // | ||||
| // https://sqlite.org/c3ref/c_fcntl_begin_atomic_write.html#sqlitefcntlcommitphasetwo | ||||
| type FileCommitPhaseTwo interface { | ||||
| 	File | ||||
| 	CommitPhaseTwo() error | ||||
| } | ||||
| 
 | ||||
| // FileBatchAtomicWrite extends File to implement the | ||||
| // SQLITE_FCNTL_BEGIN_ATOMIC_WRITE, SQLITE_FCNTL_COMMIT_ATOMIC_WRITE | ||||
| // and SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE file control opcodes. | ||||
| // | ||||
| // https://sqlite.org/c3ref/c_fcntl_begin_atomic_write.html#sqlitefcntlbeginatomicwrite | ||||
| type FileBatchAtomicWrite interface { | ||||
| 	File | ||||
| 	BeginAtomicWrite() error | ||||
| 	CommitAtomicWrite() error | ||||
| 	RollbackAtomicWrite() error | ||||
| } | ||||
| 
 | ||||
| // FilePragma extends File to implement the | ||||
| // SQLITE_FCNTL_PRAGMA file control opcode. | ||||
| // | ||||
| // https://sqlite.org/c3ref/c_fcntl_begin_atomic_write.html#sqlitefcntlpragma | ||||
| type FilePragma interface { | ||||
| 	File | ||||
| 	Pragma(name, value string) (string, error) | ||||
| } | ||||
| 
 | ||||
| // FileCheckpoint extends File to implement the | ||||
| // SQLITE_FCNTL_CKPT_START and SQLITE_FCNTL_CKPT_DONE | ||||
| // file control opcodes. | ||||
| // | ||||
| // https://sqlite.org/c3ref/c_fcntl_begin_atomic_write.html#sqlitefcntlckptstart | ||||
| type FileCheckpoint interface { | ||||
| 	File | ||||
| 	CheckpointDone() error | ||||
| 	CheckpointStart() error | ||||
| } | ||||
| 
 | ||||
| // FileSharedMemory extends File to possibly implement | ||||
| // shared-memory for the WAL-index. | ||||
| // The same shared-memory instance must be returned | ||||
| // for the entire life of the file. | ||||
| // It's OK for SharedMemory to return nil. | ||||
| type FileSharedMemory interface { | ||||
| 	File | ||||
| 	SharedMemory() SharedMemory | ||||
| } | ||||
| 
 | ||||
| // SharedMemory is a shared-memory WAL-index implementation. | ||||
| // Use [NewSharedMemory] to create a shared-memory. | ||||
| type SharedMemory interface { | ||||
| 	shmMap(context.Context, api.Module, int32, int32, bool) (uint32, error) | ||||
| 	shmLock(int32, int32, _ShmFlag) error | ||||
| 	shmUnmap(bool) | ||||
| 	io.Closer | ||||
| } | ||||
							
								
								
									
										234
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/const.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										234
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/const.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,234 @@ | |||
| package vfs | ||||
| 
 | ||||
| import "github.com/ncruces/go-sqlite3/internal/util" | ||||
| 
 | ||||
| const ( | ||||
| 	_MAX_NAME            = 1e6 // Self-imposed limit for most NUL terminated strings. | ||||
| 	_MAX_SQL_LENGTH      = 1e9 | ||||
| 	_MAX_PATHNAME        = 1024 | ||||
| 	_DEFAULT_SECTOR_SIZE = 4096 | ||||
| 
 | ||||
| 	ptrlen = 4 | ||||
| ) | ||||
| 
 | ||||
| // https://sqlite.org/rescode.html | ||||
| type _ErrorCode uint32 | ||||
| 
 | ||||
| func (e _ErrorCode) Error() string { | ||||
| 	return util.ErrorCodeString(uint32(e)) | ||||
| } | ||||
| 
 | ||||
| const ( | ||||
| 	_OK                      _ErrorCode = util.OK | ||||
| 	_ERROR                   _ErrorCode = util.ERROR | ||||
| 	_PERM                    _ErrorCode = util.PERM | ||||
| 	_BUSY                    _ErrorCode = util.BUSY | ||||
| 	_READONLY                _ErrorCode = util.READONLY | ||||
| 	_IOERR                   _ErrorCode = util.IOERR | ||||
| 	_NOTFOUND                _ErrorCode = util.NOTFOUND | ||||
| 	_CANTOPEN                _ErrorCode = util.CANTOPEN | ||||
| 	_IOERR_READ              _ErrorCode = util.IOERR_READ | ||||
| 	_IOERR_SHORT_READ        _ErrorCode = util.IOERR_SHORT_READ | ||||
| 	_IOERR_WRITE             _ErrorCode = util.IOERR_WRITE | ||||
| 	_IOERR_FSYNC             _ErrorCode = util.IOERR_FSYNC | ||||
| 	_IOERR_DIR_FSYNC         _ErrorCode = util.IOERR_DIR_FSYNC | ||||
| 	_IOERR_TRUNCATE          _ErrorCode = util.IOERR_TRUNCATE | ||||
| 	_IOERR_FSTAT             _ErrorCode = util.IOERR_FSTAT | ||||
| 	_IOERR_UNLOCK            _ErrorCode = util.IOERR_UNLOCK | ||||
| 	_IOERR_RDLOCK            _ErrorCode = util.IOERR_RDLOCK | ||||
| 	_IOERR_DELETE            _ErrorCode = util.IOERR_DELETE | ||||
| 	_IOERR_ACCESS            _ErrorCode = util.IOERR_ACCESS | ||||
| 	_IOERR_CHECKRESERVEDLOCK _ErrorCode = util.IOERR_CHECKRESERVEDLOCK | ||||
| 	_IOERR_LOCK              _ErrorCode = util.IOERR_LOCK | ||||
| 	_IOERR_CLOSE             _ErrorCode = util.IOERR_CLOSE | ||||
| 	_IOERR_SHMOPEN           _ErrorCode = util.IOERR_SHMOPEN | ||||
| 	_IOERR_SHMSIZE           _ErrorCode = util.IOERR_SHMSIZE | ||||
| 	_IOERR_SHMLOCK           _ErrorCode = util.IOERR_SHMLOCK | ||||
| 	_IOERR_SHMMAP            _ErrorCode = util.IOERR_SHMMAP | ||||
| 	_IOERR_SEEK              _ErrorCode = util.IOERR_SEEK | ||||
| 	_IOERR_DELETE_NOENT      _ErrorCode = util.IOERR_DELETE_NOENT | ||||
| 	_IOERR_BEGIN_ATOMIC      _ErrorCode = util.IOERR_BEGIN_ATOMIC | ||||
| 	_IOERR_COMMIT_ATOMIC     _ErrorCode = util.IOERR_COMMIT_ATOMIC | ||||
| 	_IOERR_ROLLBACK_ATOMIC   _ErrorCode = util.IOERR_ROLLBACK_ATOMIC | ||||
| 	_CANTOPEN_FULLPATH       _ErrorCode = util.CANTOPEN_FULLPATH | ||||
| 	_CANTOPEN_ISDIR          _ErrorCode = util.CANTOPEN_ISDIR | ||||
| 	_READONLY_CANTINIT       _ErrorCode = util.READONLY_CANTINIT | ||||
| 	_OK_SYMLINK              _ErrorCode = util.OK_SYMLINK | ||||
| ) | ||||
| 
 | ||||
| // OpenFlag is a flag for the [VFS] Open method. | ||||
| // | ||||
| // https://sqlite.org/c3ref/c_open_autoproxy.html | ||||
| type OpenFlag uint32 | ||||
| 
 | ||||
| const ( | ||||
| 	OPEN_READONLY      OpenFlag = 0x00000001 /* Ok for sqlite3_open_v2() */ | ||||
| 	OPEN_READWRITE     OpenFlag = 0x00000002 /* Ok for sqlite3_open_v2() */ | ||||
| 	OPEN_CREATE        OpenFlag = 0x00000004 /* Ok for sqlite3_open_v2() */ | ||||
| 	OPEN_DELETEONCLOSE OpenFlag = 0x00000008 /* VFS only */ | ||||
| 	OPEN_EXCLUSIVE     OpenFlag = 0x00000010 /* VFS only */ | ||||
| 	OPEN_AUTOPROXY     OpenFlag = 0x00000020 /* VFS only */ | ||||
| 	OPEN_URI           OpenFlag = 0x00000040 /* Ok for sqlite3_open_v2() */ | ||||
| 	OPEN_MEMORY        OpenFlag = 0x00000080 /* Ok for sqlite3_open_v2() */ | ||||
| 	OPEN_MAIN_DB       OpenFlag = 0x00000100 /* VFS only */ | ||||
| 	OPEN_TEMP_DB       OpenFlag = 0x00000200 /* VFS only */ | ||||
| 	OPEN_TRANSIENT_DB  OpenFlag = 0x00000400 /* VFS only */ | ||||
| 	OPEN_MAIN_JOURNAL  OpenFlag = 0x00000800 /* VFS only */ | ||||
| 	OPEN_TEMP_JOURNAL  OpenFlag = 0x00001000 /* VFS only */ | ||||
| 	OPEN_SUBJOURNAL    OpenFlag = 0x00002000 /* VFS only */ | ||||
| 	OPEN_SUPER_JOURNAL OpenFlag = 0x00004000 /* VFS only */ | ||||
| 	OPEN_NOMUTEX       OpenFlag = 0x00008000 /* Ok for sqlite3_open_v2() */ | ||||
| 	OPEN_FULLMUTEX     OpenFlag = 0x00010000 /* Ok for sqlite3_open_v2() */ | ||||
| 	OPEN_SHAREDCACHE   OpenFlag = 0x00020000 /* Ok for sqlite3_open_v2() */ | ||||
| 	OPEN_PRIVATECACHE  OpenFlag = 0x00040000 /* Ok for sqlite3_open_v2() */ | ||||
| 	OPEN_WAL           OpenFlag = 0x00080000 /* VFS only */ | ||||
| 	OPEN_NOFOLLOW      OpenFlag = 0x01000000 /* Ok for sqlite3_open_v2() */ | ||||
| ) | ||||
| 
 | ||||
| // AccessFlag is a flag for the [VFS] Access method. | ||||
| // | ||||
| // https://sqlite.org/c3ref/c_access_exists.html | ||||
| type AccessFlag uint32 | ||||
| 
 | ||||
| const ( | ||||
| 	ACCESS_EXISTS    AccessFlag = 0 | ||||
| 	ACCESS_READWRITE AccessFlag = 1 /* Used by PRAGMA temp_store_directory */ | ||||
| 	ACCESS_READ      AccessFlag = 2 /* Unused */ | ||||
| ) | ||||
| 
 | ||||
| // SyncFlag is a flag for the [File] Sync method. | ||||
| // | ||||
| // https://sqlite.org/c3ref/c_sync_dataonly.html | ||||
| type SyncFlag uint32 | ||||
| 
 | ||||
| const ( | ||||
| 	SYNC_NORMAL   SyncFlag = 0x00002 | ||||
| 	SYNC_FULL     SyncFlag = 0x00003 | ||||
| 	SYNC_DATAONLY SyncFlag = 0x00010 | ||||
| ) | ||||
| 
 | ||||
| // LockLevel is a value used with [File] Lock and Unlock methods. | ||||
| // | ||||
| // https://sqlite.org/c3ref/c_lock_exclusive.html | ||||
| type LockLevel uint32 | ||||
| 
 | ||||
| const ( | ||||
| 	// No locks are held on the database. | ||||
| 	// The database may be neither read nor written. | ||||
| 	// Any internally cached data is considered suspect and subject to | ||||
| 	// verification against the database file before being used. | ||||
| 	// Other processes can read or write the database as their own locking | ||||
| 	// states permit. | ||||
| 	// This is the default state. | ||||
| 	LOCK_NONE LockLevel = 0 /* xUnlock() only */ | ||||
| 
 | ||||
| 	// The database may be read but not written. | ||||
| 	// Any number of processes can hold SHARED locks at the same time, | ||||
| 	// hence there can be many simultaneous readers. | ||||
| 	// But no other thread or process is allowed to write to the database file | ||||
| 	// while one or more SHARED locks are active. | ||||
| 	LOCK_SHARED LockLevel = 1 /* xLock() or xUnlock() */ | ||||
| 
 | ||||
| 	// A RESERVED lock means that the process is planning on writing to the | ||||
| 	// database file at some point in the future but that it is currently just | ||||
| 	// reading from the file. | ||||
| 	// Only a single RESERVED lock may be active at one time, | ||||
| 	// though multiple SHARED locks can coexist with a single RESERVED lock. | ||||
| 	// RESERVED differs from PENDING in that new SHARED locks can be acquired | ||||
| 	// while there is a RESERVED lock. | ||||
| 	LOCK_RESERVED LockLevel = 2 /* xLock() only */ | ||||
| 
 | ||||
| 	// A PENDING lock means that the process holding the lock wants to write to | ||||
| 	// the database as soon as possible and is just waiting on all current | ||||
| 	// SHARED locks to clear so that it can get an EXCLUSIVE lock. | ||||
| 	// No new SHARED locks are permitted against the database if a PENDING lock | ||||
| 	// is active, though existing SHARED locks are allowed to continue. | ||||
| 	LOCK_PENDING LockLevel = 3 /* internal use only */ | ||||
| 
 | ||||
| 	// An EXCLUSIVE lock is needed in order to write to the database file. | ||||
| 	// Only one EXCLUSIVE lock is allowed on the file and no other locks of any | ||||
| 	// kind are allowed to coexist with an EXCLUSIVE lock. | ||||
| 	// In order to maximize concurrency, SQLite works to minimize the amount of | ||||
| 	// time that EXCLUSIVE locks are held. | ||||
| 	LOCK_EXCLUSIVE LockLevel = 4 /* xLock() only */ | ||||
| ) | ||||
| 
 | ||||
| // DeviceCharacteristic is a flag retuned by the [File] DeviceCharacteristics method. | ||||
| // | ||||
| // https://sqlite.org/c3ref/c_iocap_atomic.html | ||||
| type DeviceCharacteristic uint32 | ||||
| 
 | ||||
| const ( | ||||
| 	IOCAP_ATOMIC                DeviceCharacteristic = 0x00000001 | ||||
| 	IOCAP_ATOMIC512             DeviceCharacteristic = 0x00000002 | ||||
| 	IOCAP_ATOMIC1K              DeviceCharacteristic = 0x00000004 | ||||
| 	IOCAP_ATOMIC2K              DeviceCharacteristic = 0x00000008 | ||||
| 	IOCAP_ATOMIC4K              DeviceCharacteristic = 0x00000010 | ||||
| 	IOCAP_ATOMIC8K              DeviceCharacteristic = 0x00000020 | ||||
| 	IOCAP_ATOMIC16K             DeviceCharacteristic = 0x00000040 | ||||
| 	IOCAP_ATOMIC32K             DeviceCharacteristic = 0x00000080 | ||||
| 	IOCAP_ATOMIC64K             DeviceCharacteristic = 0x00000100 | ||||
| 	IOCAP_SAFE_APPEND           DeviceCharacteristic = 0x00000200 | ||||
| 	IOCAP_SEQUENTIAL            DeviceCharacteristic = 0x00000400 | ||||
| 	IOCAP_UNDELETABLE_WHEN_OPEN DeviceCharacteristic = 0x00000800 | ||||
| 	IOCAP_POWERSAFE_OVERWRITE   DeviceCharacteristic = 0x00001000 | ||||
| 	IOCAP_IMMUTABLE             DeviceCharacteristic = 0x00002000 | ||||
| 	IOCAP_BATCH_ATOMIC          DeviceCharacteristic = 0x00004000 | ||||
| ) | ||||
| 
 | ||||
| // https://sqlite.org/c3ref/c_fcntl_begin_atomic_write.html | ||||
| type _FcntlOpcode uint32 | ||||
| 
 | ||||
| const ( | ||||
| 	_FCNTL_LOCKSTATE             _FcntlOpcode = 1 | ||||
| 	_FCNTL_GET_LOCKPROXYFILE     _FcntlOpcode = 2 | ||||
| 	_FCNTL_SET_LOCKPROXYFILE     _FcntlOpcode = 3 | ||||
| 	_FCNTL_LAST_ERRNO            _FcntlOpcode = 4 | ||||
| 	_FCNTL_SIZE_HINT             _FcntlOpcode = 5 | ||||
| 	_FCNTL_CHUNK_SIZE            _FcntlOpcode = 6 | ||||
| 	_FCNTL_FILE_POINTER          _FcntlOpcode = 7 | ||||
| 	_FCNTL_SYNC_OMITTED          _FcntlOpcode = 8 | ||||
| 	_FCNTL_WIN32_AV_RETRY        _FcntlOpcode = 9 | ||||
| 	_FCNTL_PERSIST_WAL           _FcntlOpcode = 10 | ||||
| 	_FCNTL_OVERWRITE             _FcntlOpcode = 11 | ||||
| 	_FCNTL_VFSNAME               _FcntlOpcode = 12 | ||||
| 	_FCNTL_POWERSAFE_OVERWRITE   _FcntlOpcode = 13 | ||||
| 	_FCNTL_PRAGMA                _FcntlOpcode = 14 | ||||
| 	_FCNTL_BUSYHANDLER           _FcntlOpcode = 15 | ||||
| 	_FCNTL_TEMPFILENAME          _FcntlOpcode = 16 | ||||
| 	_FCNTL_MMAP_SIZE             _FcntlOpcode = 18 | ||||
| 	_FCNTL_TRACE                 _FcntlOpcode = 19 | ||||
| 	_FCNTL_HAS_MOVED             _FcntlOpcode = 20 | ||||
| 	_FCNTL_SYNC                  _FcntlOpcode = 21 | ||||
| 	_FCNTL_COMMIT_PHASETWO       _FcntlOpcode = 22 | ||||
| 	_FCNTL_WIN32_SET_HANDLE      _FcntlOpcode = 23 | ||||
| 	_FCNTL_WAL_BLOCK             _FcntlOpcode = 24 | ||||
| 	_FCNTL_ZIPVFS                _FcntlOpcode = 25 | ||||
| 	_FCNTL_RBU                   _FcntlOpcode = 26 | ||||
| 	_FCNTL_VFS_POINTER           _FcntlOpcode = 27 | ||||
| 	_FCNTL_JOURNAL_POINTER       _FcntlOpcode = 28 | ||||
| 	_FCNTL_WIN32_GET_HANDLE      _FcntlOpcode = 29 | ||||
| 	_FCNTL_PDB                   _FcntlOpcode = 30 | ||||
| 	_FCNTL_BEGIN_ATOMIC_WRITE    _FcntlOpcode = 31 | ||||
| 	_FCNTL_COMMIT_ATOMIC_WRITE   _FcntlOpcode = 32 | ||||
| 	_FCNTL_ROLLBACK_ATOMIC_WRITE _FcntlOpcode = 33 | ||||
| 	_FCNTL_LOCK_TIMEOUT          _FcntlOpcode = 34 | ||||
| 	_FCNTL_DATA_VERSION          _FcntlOpcode = 35 | ||||
| 	_FCNTL_SIZE_LIMIT            _FcntlOpcode = 36 | ||||
| 	_FCNTL_CKPT_DONE             _FcntlOpcode = 37 | ||||
| 	_FCNTL_RESERVE_BYTES         _FcntlOpcode = 38 | ||||
| 	_FCNTL_CKPT_START            _FcntlOpcode = 39 | ||||
| 	_FCNTL_EXTERNAL_READER       _FcntlOpcode = 40 | ||||
| 	_FCNTL_CKSM_FILE             _FcntlOpcode = 41 | ||||
| 	_FCNTL_RESET_CACHE           _FcntlOpcode = 42 | ||||
| ) | ||||
| 
 | ||||
| // https://sqlite.org/c3ref/c_shm_exclusive.html | ||||
| type _ShmFlag uint32 | ||||
| 
 | ||||
| const ( | ||||
| 	_SHM_UNLOCK    _ShmFlag = 1 | ||||
| 	_SHM_LOCK      _ShmFlag = 2 | ||||
| 	_SHM_SHARED    _ShmFlag = 4 | ||||
| 	_SHM_EXCLUSIVE _ShmFlag = 8 | ||||
| ) | ||||
							
								
								
									
										217
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/file.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										217
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/file.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,217 @@ | |||
| package vfs | ||||
| 
 | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"io" | ||||
| 	"io/fs" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| 	"runtime" | ||||
| 	"syscall" | ||||
| 
 | ||||
| 	"github.com/ncruces/go-sqlite3/util/osutil" | ||||
| ) | ||||
| 
 | ||||
| type vfsOS struct{} | ||||
| 
 | ||||
| func (vfsOS) FullPathname(path string) (string, error) { | ||||
| 	path, err := filepath.Abs(path) | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
| 	fi, err := os.Lstat(path) | ||||
| 	if err != nil { | ||||
| 		if errors.Is(err, fs.ErrNotExist) { | ||||
| 			return path, nil | ||||
| 		} | ||||
| 		return "", err | ||||
| 	} | ||||
| 	if fi.Mode()&fs.ModeSymlink != 0 { | ||||
| 		err = _OK_SYMLINK | ||||
| 	} | ||||
| 	return path, err | ||||
| } | ||||
| 
 | ||||
| func (vfsOS) Delete(path string, syncDir bool) error { | ||||
| 	err := os.Remove(path) | ||||
| 	if err != nil { | ||||
| 		if errors.Is(err, fs.ErrNotExist) { | ||||
| 			return _IOERR_DELETE_NOENT | ||||
| 		} | ||||
| 		return err | ||||
| 	} | ||||
| 	if runtime.GOOS != "windows" && syncDir { | ||||
| 		f, err := os.Open(filepath.Dir(path)) | ||||
| 		if err != nil { | ||||
| 			return _OK | ||||
| 		} | ||||
| 		defer f.Close() | ||||
| 		err = osSync(f, false, false) | ||||
| 		if err != nil { | ||||
| 			return _IOERR_DIR_FSYNC | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (vfsOS) Access(name string, flags AccessFlag) (bool, error) { | ||||
| 	err := osAccess(name, flags) | ||||
| 	if flags == ACCESS_EXISTS { | ||||
| 		if errors.Is(err, fs.ErrNotExist) { | ||||
| 			return false, nil | ||||
| 		} | ||||
| 	} else { | ||||
| 		if errors.Is(err, fs.ErrPermission) { | ||||
| 			return false, nil | ||||
| 		} | ||||
| 	} | ||||
| 	return err == nil, err | ||||
| } | ||||
| 
 | ||||
| func (vfsOS) Open(name string, flags OpenFlag) (File, OpenFlag, error) { | ||||
| 	return nil, 0, _CANTOPEN | ||||
| } | ||||
| 
 | ||||
| func (vfsOS) OpenFilename(name *Filename, flags OpenFlag) (File, OpenFlag, error) { | ||||
| 	var oflags int | ||||
| 	if flags&OPEN_EXCLUSIVE != 0 { | ||||
| 		oflags |= os.O_EXCL | ||||
| 	} | ||||
| 	if flags&OPEN_CREATE != 0 { | ||||
| 		oflags |= os.O_CREATE | ||||
| 	} | ||||
| 	if flags&OPEN_READONLY != 0 { | ||||
| 		oflags |= os.O_RDONLY | ||||
| 	} | ||||
| 	if flags&OPEN_READWRITE != 0 { | ||||
| 		oflags |= os.O_RDWR | ||||
| 	} | ||||
| 
 | ||||
| 	var err error | ||||
| 	var f *os.File | ||||
| 	if name == nil { | ||||
| 		f, err = os.CreateTemp("", "*.db") | ||||
| 	} else { | ||||
| 		f, err = osutil.OpenFile(name.String(), oflags, 0666) | ||||
| 	} | ||||
| 	if err != nil { | ||||
| 		if errors.Is(err, syscall.EISDIR) { | ||||
| 			return nil, flags, _CANTOPEN_ISDIR | ||||
| 		} | ||||
| 		return nil, flags, err | ||||
| 	} | ||||
| 
 | ||||
| 	if modeof := name.URIParameter("modeof"); modeof != "" { | ||||
| 		if err = osSetMode(f, modeof); err != nil { | ||||
| 			f.Close() | ||||
| 			return nil, flags, _IOERR_FSTAT | ||||
| 		} | ||||
| 	} | ||||
| 	if flags&OPEN_DELETEONCLOSE != 0 { | ||||
| 		os.Remove(f.Name()) | ||||
| 	} | ||||
| 
 | ||||
| 	file := vfsFile{ | ||||
| 		File:     f, | ||||
| 		psow:     true, | ||||
| 		readOnly: flags&OPEN_READONLY != 0, | ||||
| 		syncDir: runtime.GOOS != "windows" && | ||||
| 			flags&(OPEN_CREATE) != 0 && | ||||
| 			flags&(OPEN_MAIN_JOURNAL|OPEN_SUPER_JOURNAL|OPEN_WAL) != 0, | ||||
| 		shm: NewSharedMemory(name.String()+"-shm", flags), | ||||
| 	} | ||||
| 	return &file, flags, nil | ||||
| } | ||||
| 
 | ||||
| type vfsFile struct { | ||||
| 	*os.File | ||||
| 	shm      SharedMemory | ||||
| 	lock     LockLevel | ||||
| 	readOnly bool | ||||
| 	keepWAL  bool | ||||
| 	syncDir  bool | ||||
| 	psow     bool | ||||
| } | ||||
| 
 | ||||
| var ( | ||||
| 	// Ensure these interfaces are implemented: | ||||
| 	_ FileLockState          = &vfsFile{} | ||||
| 	_ FileHasMoved           = &vfsFile{} | ||||
| 	_ FileSizeHint           = &vfsFile{} | ||||
| 	_ FilePersistentWAL      = &vfsFile{} | ||||
| 	_ FilePowersafeOverwrite = &vfsFile{} | ||||
| ) | ||||
| 
 | ||||
| func (f *vfsFile) Close() error { | ||||
| 	if f.shm != nil { | ||||
| 		f.shm.Close() | ||||
| 	} | ||||
| 	return f.File.Close() | ||||
| } | ||||
| 
 | ||||
| func (f *vfsFile) Sync(flags SyncFlag) error { | ||||
| 	dataonly := (flags & SYNC_DATAONLY) != 0 | ||||
| 	fullsync := (flags & 0x0f) == SYNC_FULL | ||||
| 
 | ||||
| 	err := osSync(f.File, fullsync, dataonly) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if runtime.GOOS != "windows" && f.syncDir { | ||||
| 		f.syncDir = false | ||||
| 		d, err := os.Open(filepath.Dir(f.File.Name())) | ||||
| 		if err != nil { | ||||
| 			return nil | ||||
| 		} | ||||
| 		defer d.Close() | ||||
| 		err = osSync(d, false, false) | ||||
| 		if err != nil { | ||||
| 			return _IOERR_DIR_FSYNC | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (f *vfsFile) Size() (int64, error) { | ||||
| 	return f.Seek(0, io.SeekEnd) | ||||
| } | ||||
| 
 | ||||
| func (f *vfsFile) SectorSize() int { | ||||
| 	return _DEFAULT_SECTOR_SIZE | ||||
| } | ||||
| 
 | ||||
| func (f *vfsFile) DeviceCharacteristics() DeviceCharacteristic { | ||||
| 	var res DeviceCharacteristic | ||||
| 	if osBatchAtomic(f.File) { | ||||
| 		res |= IOCAP_BATCH_ATOMIC | ||||
| 	} | ||||
| 	if f.psow { | ||||
| 		res |= IOCAP_POWERSAFE_OVERWRITE | ||||
| 	} | ||||
| 	return res | ||||
| } | ||||
| 
 | ||||
| func (f *vfsFile) SizeHint(size int64) error { | ||||
| 	return osAllocate(f.File, size) | ||||
| } | ||||
| 
 | ||||
| func (f *vfsFile) HasMoved() (bool, error) { | ||||
| 	fi, err := f.Stat() | ||||
| 	if err != nil { | ||||
| 		return false, err | ||||
| 	} | ||||
| 	pi, err := os.Stat(f.Name()) | ||||
| 	if err != nil { | ||||
| 		if errors.Is(err, fs.ErrNotExist) { | ||||
| 			return true, nil | ||||
| 		} | ||||
| 		return false, err | ||||
| 	} | ||||
| 	return !os.SameFile(fi, pi), nil | ||||
| } | ||||
| 
 | ||||
| func (f *vfsFile) LockState() LockLevel            { return f.lock } | ||||
| func (f *vfsFile) PowersafeOverwrite() bool        { return f.psow } | ||||
| func (f *vfsFile) PersistentWAL() bool             { return f.keepWAL } | ||||
| func (f *vfsFile) SetPowersafeOverwrite(psow bool) { f.psow = psow } | ||||
| func (f *vfsFile) SetPersistentWAL(keepWAL bool)   { f.keepWAL = keepWAL } | ||||
							
								
								
									
										174
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/filename.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										174
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/filename.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,174 @@ | |||
| package vfs | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"net/url" | ||||
| 
 | ||||
| 	"github.com/ncruces/go-sqlite3/internal/util" | ||||
| 	"github.com/tetratelabs/wazero/api" | ||||
| ) | ||||
| 
 | ||||
| // Filename is used by SQLite to pass filenames | ||||
| // to the Open method of a VFS. | ||||
| // | ||||
| // https://sqlite.org/c3ref/filename.html | ||||
| type Filename struct { | ||||
| 	ctx   context.Context | ||||
| 	mod   api.Module | ||||
| 	zPath uint32 | ||||
| 	flags OpenFlag | ||||
| 	stack [2]uint64 | ||||
| } | ||||
| 
 | ||||
| // OpenFilename is an internal API users should not call directly. | ||||
| func OpenFilename(ctx context.Context, mod api.Module, id uint32, flags OpenFlag) *Filename { | ||||
| 	if id == 0 { | ||||
| 		return nil | ||||
| 	} | ||||
| 	return &Filename{ | ||||
| 		ctx:   ctx, | ||||
| 		mod:   mod, | ||||
| 		zPath: id, | ||||
| 		flags: flags, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // String returns this filename as a string. | ||||
| func (n *Filename) String() string { | ||||
| 	if n == nil || n.zPath == 0 { | ||||
| 		return "" | ||||
| 	} | ||||
| 	return util.ReadString(n.mod, n.zPath, _MAX_PATHNAME) | ||||
| } | ||||
| 
 | ||||
| // Database returns the name of the corresponding database file. | ||||
| // | ||||
| // https://sqlite.org/c3ref/filename_database.html | ||||
| func (n *Filename) Database() string { | ||||
| 	return n.path("sqlite3_filename_database") | ||||
| } | ||||
| 
 | ||||
| // Journal returns the name of the corresponding rollback journal file. | ||||
| // | ||||
| // https://sqlite.org/c3ref/filename_database.html | ||||
| func (n *Filename) Journal() string { | ||||
| 	return n.path("sqlite3_filename_journal") | ||||
| } | ||||
| 
 | ||||
| // Journal returns the name of the corresponding WAL file. | ||||
| // | ||||
| // https://sqlite.org/c3ref/filename_database.html | ||||
| func (n *Filename) WAL() string { | ||||
| 	return n.path("sqlite3_filename_wal") | ||||
| } | ||||
| 
 | ||||
| func (n *Filename) path(method string) string { | ||||
| 	if n == nil || n.zPath == 0 { | ||||
| 		return "" | ||||
| 	} | ||||
| 	n.stack[0] = uint64(n.zPath) | ||||
| 	fn := n.mod.ExportedFunction(method) | ||||
| 	if err := fn.CallWithStack(n.ctx, n.stack[:]); err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| 	return util.ReadString(n.mod, uint32(n.stack[0]), _MAX_PATHNAME) | ||||
| } | ||||
| 
 | ||||
| // DatabaseFile returns the main database [File] corresponding to a journal. | ||||
| // | ||||
| // https://sqlite.org/c3ref/database_file_object.html | ||||
| func (n *Filename) DatabaseFile() File { | ||||
| 	if n == nil || n.zPath == 0 { | ||||
| 		return nil | ||||
| 	} | ||||
| 	if n.flags&(OPEN_MAIN_DB|OPEN_MAIN_JOURNAL|OPEN_WAL) == 0 { | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	n.stack[0] = uint64(n.zPath) | ||||
| 	fn := n.mod.ExportedFunction("sqlite3_database_file_object") | ||||
| 	if err := fn.CallWithStack(n.ctx, n.stack[:]); err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| 	file, _ := vfsFileGet(n.ctx, n.mod, uint32(n.stack[0])).(File) | ||||
| 	return file | ||||
| } | ||||
| 
 | ||||
| // URIParameter returns the value of a URI parameter. | ||||
| // | ||||
| // https://sqlite.org/c3ref/uri_boolean.html | ||||
| func (n *Filename) URIParameter(key string) string { | ||||
| 	if n == nil || n.zPath == 0 { | ||||
| 		return "" | ||||
| 	} | ||||
| 
 | ||||
| 	uriKey := n.mod.ExportedFunction("sqlite3_uri_key") | ||||
| 	n.stack[0] = uint64(n.zPath) | ||||
| 	n.stack[1] = uint64(0) | ||||
| 	if err := uriKey.CallWithStack(n.ctx, n.stack[:]); err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| 
 | ||||
| 	ptr := uint32(n.stack[0]) | ||||
| 	if ptr == 0 { | ||||
| 		return "" | ||||
| 	} | ||||
| 
 | ||||
| 	// Parse the format from: | ||||
| 	// https://github.com/sqlite/sqlite/blob/b74eb0/src/pager.c#L4797-L4840 | ||||
| 	// This avoids having to alloc/free the key just to find a value. | ||||
| 	for { | ||||
| 		k := util.ReadString(n.mod, ptr, _MAX_NAME) | ||||
| 		if k == "" { | ||||
| 			return "" | ||||
| 		} | ||||
| 		ptr += uint32(len(k)) + 1 | ||||
| 
 | ||||
| 		v := util.ReadString(n.mod, ptr, _MAX_NAME) | ||||
| 		if k == key { | ||||
| 			return v | ||||
| 		} | ||||
| 		ptr += uint32(len(v)) + 1 | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // URIParameters obtains values for URI parameters. | ||||
| // | ||||
| // https://sqlite.org/c3ref/uri_boolean.html | ||||
| func (n *Filename) URIParameters() url.Values { | ||||
| 	if n == nil || n.zPath == 0 { | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	uriKey := n.mod.ExportedFunction("sqlite3_uri_key") | ||||
| 	n.stack[0] = uint64(n.zPath) | ||||
| 	n.stack[1] = uint64(0) | ||||
| 	if err := uriKey.CallWithStack(n.ctx, n.stack[:]); err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| 
 | ||||
| 	ptr := uint32(n.stack[0]) | ||||
| 	if ptr == 0 { | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	var params url.Values | ||||
| 
 | ||||
| 	// Parse the format from: | ||||
| 	// https://github.com/sqlite/sqlite/blob/b74eb0/src/pager.c#L4797-L4840 | ||||
| 	// This is the only way to support multiple valued keys. | ||||
| 	for { | ||||
| 		k := util.ReadString(n.mod, ptr, _MAX_NAME) | ||||
| 		if k == "" { | ||||
| 			return params | ||||
| 		} | ||||
| 		ptr += uint32(len(k)) + 1 | ||||
| 
 | ||||
| 		v := util.ReadString(n.mod, ptr, _MAX_NAME) | ||||
| 		if params == nil { | ||||
| 			params = url.Values{} | ||||
| 		} | ||||
| 		params.Add(k, v) | ||||
| 		ptr += uint32(len(v)) + 1 | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										144
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/lock.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										144
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/lock.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,144 @@ | |||
| //go:build (linux || darwin || windows || freebsd || openbsd || netbsd || dragonfly || illumos || sqlite3_flock) && !sqlite3_nosys | ||||
| 
 | ||||
| package vfs | ||||
| 
 | ||||
| import "github.com/ncruces/go-sqlite3/internal/util" | ||||
| 
 | ||||
| // SupportsFileLocking is false on platforms that do not support file locking. | ||||
| // To open a database file on those platforms, | ||||
| // you need to use the [nolock] or [immutable] URI parameters. | ||||
| // | ||||
| // [nolock]: https://sqlite.org/uri.html#urinolock | ||||
| // [immutable]: https://sqlite.org/uri.html#uriimmutable | ||||
| const SupportsFileLocking = true | ||||
| 
 | ||||
| const ( | ||||
| 	_PENDING_BYTE  = 0x40000000 | ||||
| 	_RESERVED_BYTE = (_PENDING_BYTE + 1) | ||||
| 	_SHARED_FIRST  = (_PENDING_BYTE + 2) | ||||
| 	_SHARED_SIZE   = 510 | ||||
| ) | ||||
| 
 | ||||
| func (f *vfsFile) Lock(lock LockLevel) error { | ||||
| 	// Argument check. SQLite never explicitly requests a pending lock. | ||||
| 	if lock != LOCK_SHARED && lock != LOCK_RESERVED && lock != LOCK_EXCLUSIVE { | ||||
| 		panic(util.AssertErr()) | ||||
| 	} | ||||
| 
 | ||||
| 	switch { | ||||
| 	case f.lock < LOCK_NONE || f.lock > LOCK_EXCLUSIVE: | ||||
| 		// Connection state check. | ||||
| 		panic(util.AssertErr()) | ||||
| 	case f.lock == LOCK_NONE && lock > LOCK_SHARED: | ||||
| 		// We never move from unlocked to anything higher than a shared lock. | ||||
| 		panic(util.AssertErr()) | ||||
| 	case f.lock != LOCK_SHARED && lock == LOCK_RESERVED: | ||||
| 		// A shared lock is always held when a reserved lock is requested. | ||||
| 		panic(util.AssertErr()) | ||||
| 	} | ||||
| 
 | ||||
| 	// If we already have an equal or more restrictive lock, do nothing. | ||||
| 	if f.lock >= lock { | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	// Do not allow any kind of write-lock on a read-only database. | ||||
| 	if f.readOnly && lock >= LOCK_RESERVED { | ||||
| 		return _IOERR_LOCK | ||||
| 	} | ||||
| 
 | ||||
| 	switch lock { | ||||
| 	case LOCK_SHARED: | ||||
| 		// Must be unlocked to get SHARED. | ||||
| 		if f.lock != LOCK_NONE { | ||||
| 			panic(util.AssertErr()) | ||||
| 		} | ||||
| 		if rc := osGetSharedLock(f.File); rc != _OK { | ||||
| 			return rc | ||||
| 		} | ||||
| 		f.lock = LOCK_SHARED | ||||
| 		return nil | ||||
| 
 | ||||
| 	case LOCK_RESERVED: | ||||
| 		// Must be SHARED to get RESERVED. | ||||
| 		if f.lock != LOCK_SHARED { | ||||
| 			panic(util.AssertErr()) | ||||
| 		} | ||||
| 		if rc := osGetReservedLock(f.File); rc != _OK { | ||||
| 			return rc | ||||
| 		} | ||||
| 		f.lock = LOCK_RESERVED | ||||
| 		return nil | ||||
| 
 | ||||
| 	case LOCK_EXCLUSIVE: | ||||
| 		// Must be SHARED, RESERVED or PENDING to get EXCLUSIVE. | ||||
| 		if f.lock <= LOCK_NONE || f.lock >= LOCK_EXCLUSIVE { | ||||
| 			panic(util.AssertErr()) | ||||
| 		} | ||||
| 		reserved := f.lock == LOCK_RESERVED | ||||
| 		// A PENDING lock is needed before acquiring an EXCLUSIVE lock. | ||||
| 		if f.lock < LOCK_PENDING { | ||||
| 			// If we're already RESERVED, we can block indefinitely, | ||||
| 			// since only new readers may briefly hold the PENDING lock. | ||||
| 			if rc := osGetPendingLock(f.File, reserved /* block */); rc != _OK { | ||||
| 				return rc | ||||
| 			} | ||||
| 			f.lock = LOCK_PENDING | ||||
| 		} | ||||
| 		// We already have PENDING, so we're just waiting for readers to leave. | ||||
| 		// If we were RESERVED, we can wait for a little while, before invoking | ||||
| 		// the busy handler; we will only do this once. | ||||
| 		if rc := osGetExclusiveLock(f.File, reserved /* wait */); rc != _OK { | ||||
| 			return rc | ||||
| 		} | ||||
| 		f.lock = LOCK_EXCLUSIVE | ||||
| 		return nil | ||||
| 
 | ||||
| 	default: | ||||
| 		panic(util.AssertErr()) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (f *vfsFile) Unlock(lock LockLevel) error { | ||||
| 	// Argument check. | ||||
| 	if lock != LOCK_NONE && lock != LOCK_SHARED { | ||||
| 		panic(util.AssertErr()) | ||||
| 	} | ||||
| 
 | ||||
| 	// Connection state check. | ||||
| 	if f.lock < LOCK_NONE || f.lock > LOCK_EXCLUSIVE { | ||||
| 		panic(util.AssertErr()) | ||||
| 	} | ||||
| 
 | ||||
| 	// If we don't have a more restrictive lock, do nothing. | ||||
| 	if f.lock <= lock { | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	switch lock { | ||||
| 	case LOCK_SHARED: | ||||
| 		rc := osDowngradeLock(f.File, f.lock) | ||||
| 		f.lock = LOCK_SHARED | ||||
| 		return rc | ||||
| 
 | ||||
| 	case LOCK_NONE: | ||||
| 		rc := osReleaseLock(f.File, f.lock) | ||||
| 		f.lock = LOCK_NONE | ||||
| 		return rc | ||||
| 
 | ||||
| 	default: | ||||
| 		panic(util.AssertErr()) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (f *vfsFile) CheckReservedLock() (bool, error) { | ||||
| 	// Connection state check. | ||||
| 	if f.lock < LOCK_NONE || f.lock > LOCK_EXCLUSIVE { | ||||
| 		panic(util.AssertErr()) | ||||
| 	} | ||||
| 
 | ||||
| 	if f.lock >= LOCK_RESERVED { | ||||
| 		return true, nil | ||||
| 	} | ||||
| 	return osCheckReservedLock(f.File) | ||||
| } | ||||
							
								
								
									
										23
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/lock_other.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/lock_other.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,23 @@ | |||
| //go:build !(linux || darwin || windows || freebsd || openbsd || netbsd || dragonfly || illumos || sqlite3_flock) || sqlite3_nosys | ||||
| 
 | ||||
| package vfs | ||||
| 
 | ||||
| // SupportsFileLocking is false on platforms that do not support file locking. | ||||
| // To open a database file on those platforms, | ||||
| // you need to use the [nolock] or [immutable] URI parameters. | ||||
| // | ||||
| // [nolock]: https://sqlite.org/uri.html#urinolock | ||||
| // [immutable]: https://sqlite.org/uri.html#uriimmutable | ||||
| const SupportsFileLocking = false | ||||
| 
 | ||||
| func (f *vfsFile) Lock(LockLevel) error { | ||||
| 	return _IOERR_LOCK | ||||
| } | ||||
| 
 | ||||
| func (f *vfsFile) Unlock(LockLevel) error { | ||||
| 	return _IOERR_UNLOCK | ||||
| } | ||||
| 
 | ||||
| func (f *vfsFile) CheckReservedLock() (bool, error) { | ||||
| 	return false, _IOERR_CHECKRESERVEDLOCK | ||||
| } | ||||
							
								
								
									
										9
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/memdb/README.md
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/memdb/README.md
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,9 @@ | |||
| # Go `"memdb"` SQLite VFS | ||||
| 
 | ||||
| This package implements the [`"memdb"`](https://sqlite.org/src/doc/tip/src/memdb.c) | ||||
| SQLite VFS in pure Go. | ||||
| 
 | ||||
| It has some benefits over the C version: | ||||
| - the memory backing the database needs not be contiguous, | ||||
| - the database can grow/shrink incrementally without copying, | ||||
| - reader-writer concurrency is slightly improved. | ||||
							
								
								
									
										68
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/memdb/api.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/memdb/api.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,68 @@ | |||
| // Package memdb implements the "memdb" SQLite VFS. | ||||
| // | ||||
| // The "memdb" [vfs.VFS] allows the same in-memory database to be shared | ||||
| // among multiple database connections in the same process, | ||||
| // as long as the database name begins with "/". | ||||
| // | ||||
| // Importing package memdb registers the VFS: | ||||
| // | ||||
| //	import _ "github.com/ncruces/go-sqlite3/vfs/memdb" | ||||
| package memdb | ||||
| 
 | ||||
| import ( | ||||
| 	"sync" | ||||
| 
 | ||||
| 	"github.com/ncruces/go-sqlite3/vfs" | ||||
| ) | ||||
| 
 | ||||
| func init() { | ||||
| 	vfs.Register("memdb", memVFS{}) | ||||
| } | ||||
| 
 | ||||
| var ( | ||||
| 	memoryMtx sync.Mutex | ||||
| 	// +checklocks:memoryMtx | ||||
| 	memoryDBs = map[string]*memDB{} | ||||
| ) | ||||
| 
 | ||||
| // Create creates a shared memory database, | ||||
| // using data as its initial contents. | ||||
| // The new database takes ownership of data, | ||||
| // and the caller should not use data after this call. | ||||
| func Create(name string, data []byte) { | ||||
| 	memoryMtx.Lock() | ||||
| 	defer memoryMtx.Unlock() | ||||
| 
 | ||||
| 	db := &memDB{ | ||||
| 		refs: 1, | ||||
| 		name: name, | ||||
| 		size: int64(len(data)), | ||||
| 	} | ||||
| 
 | ||||
| 	// Convert data from WAL to rollback journal. | ||||
| 	if len(data) >= 20 && data[18] == 2 && data[19] == 2 { | ||||
| 		data[18] = 1 | ||||
| 		data[19] = 1 | ||||
| 	} | ||||
| 
 | ||||
| 	sectors := divRoundUp(db.size, sectorSize) | ||||
| 	db.data = make([]*[sectorSize]byte, sectors) | ||||
| 	for i := range db.data { | ||||
| 		sector := data[i*sectorSize:] | ||||
| 		if len(sector) >= sectorSize { | ||||
| 			db.data[i] = (*[sectorSize]byte)(sector) | ||||
| 		} else { | ||||
| 			db.data[i] = new([sectorSize]byte) | ||||
| 			copy((*db.data[i])[:], sector) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	memoryDBs[name] = db | ||||
| } | ||||
| 
 | ||||
| // Delete deletes a shared memory database. | ||||
| func Delete(name string) { | ||||
| 	memoryMtx.Lock() | ||||
| 	defer memoryMtx.Unlock() | ||||
| 	delete(memoryDBs, name) | ||||
| } | ||||
							
								
								
									
										311
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/memdb/memdb.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										311
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/memdb/memdb.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,311 @@ | |||
| package memdb | ||||
| 
 | ||||
| import ( | ||||
| 	"io" | ||||
| 	"runtime" | ||||
| 	"sync" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/ncruces/go-sqlite3" | ||||
| 	"github.com/ncruces/go-sqlite3/vfs" | ||||
| ) | ||||
| 
 | ||||
| // Must be a multiple of 64K (the largest page size). | ||||
| const sectorSize = 65536 | ||||
| 
 | ||||
| type memVFS struct{} | ||||
| 
 | ||||
| func (memVFS) Open(name string, flags vfs.OpenFlag) (vfs.File, vfs.OpenFlag, error) { | ||||
| 	// For simplicity, we do not support reading or writing data | ||||
| 	// across "sector" boundaries. | ||||
| 	// | ||||
| 	// This is not a problem for most SQLite file types: | ||||
| 	// - databases, which only do page aligned reads/writes; | ||||
| 	// - temp journals, as used by the sorter, which does the same: | ||||
| 	//   https://github.com/sqlite/sqlite/blob/b74eb0/src/vdbesort.c#L409-L412 | ||||
| 	// | ||||
| 	// We refuse to open all other file types, | ||||
| 	// but returning OPEN_MEMORY means SQLite won't ask us to. | ||||
| 	const types = vfs.OPEN_MAIN_DB | | ||||
| 		vfs.OPEN_TEMP_DB | | ||||
| 		vfs.OPEN_TEMP_JOURNAL | ||||
| 	if flags&types == 0 { | ||||
| 		return nil, flags, sqlite3.CANTOPEN | ||||
| 	} | ||||
| 
 | ||||
| 	// A shared database has a name that begins with "/". | ||||
| 	shared := len(name) > 1 && name[0] == '/' | ||||
| 
 | ||||
| 	var db *memDB | ||||
| 	if shared { | ||||
| 		name = name[1:] | ||||
| 		memoryMtx.Lock() | ||||
| 		defer memoryMtx.Unlock() | ||||
| 		db = memoryDBs[name] | ||||
| 	} | ||||
| 	if db == nil { | ||||
| 		if flags&vfs.OPEN_CREATE == 0 { | ||||
| 			return nil, flags, sqlite3.CANTOPEN | ||||
| 		} | ||||
| 		db = &memDB{name: name} | ||||
| 	} | ||||
| 	if shared { | ||||
| 		db.refs++ // +checklocksforce: memoryMtx is held | ||||
| 		memoryDBs[name] = db | ||||
| 	} | ||||
| 
 | ||||
| 	return &memFile{ | ||||
| 		memDB:    db, | ||||
| 		readOnly: flags&vfs.OPEN_READONLY != 0, | ||||
| 	}, flags | vfs.OPEN_MEMORY, nil | ||||
| } | ||||
| 
 | ||||
| func (memVFS) Delete(name string, dirSync bool) error { | ||||
| 	return sqlite3.IOERR_DELETE | ||||
| } | ||||
| 
 | ||||
| func (memVFS) Access(name string, flag vfs.AccessFlag) (bool, error) { | ||||
| 	return false, nil | ||||
| } | ||||
| 
 | ||||
| func (memVFS) FullPathname(name string) (string, error) { | ||||
| 	return name, nil | ||||
| } | ||||
| 
 | ||||
| type memDB struct { | ||||
| 	name string | ||||
| 
 | ||||
| 	// +checklocks:lockMtx | ||||
| 	pending *memFile | ||||
| 	// +checklocks:lockMtx | ||||
| 	reserved *memFile | ||||
| 
 | ||||
| 	// +checklocks:dataMtx | ||||
| 	data []*[sectorSize]byte | ||||
| 
 | ||||
| 	// +checklocks:dataMtx | ||||
| 	size int64 | ||||
| 
 | ||||
| 	// +checklocks:lockMtx | ||||
| 	shared int | ||||
| 
 | ||||
| 	// +checklocks:memoryMtx | ||||
| 	refs int | ||||
| 
 | ||||
| 	lockMtx sync.Mutex | ||||
| 	dataMtx sync.RWMutex | ||||
| } | ||||
| 
 | ||||
| func (m *memDB) release() { | ||||
| 	memoryMtx.Lock() | ||||
| 	defer memoryMtx.Unlock() | ||||
| 	if m.refs--; m.refs == 0 && m == memoryDBs[m.name] { | ||||
| 		delete(memoryDBs, m.name) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| type memFile struct { | ||||
| 	*memDB | ||||
| 	lock     vfs.LockLevel | ||||
| 	readOnly bool | ||||
| } | ||||
| 
 | ||||
| var ( | ||||
| 	// Ensure these interfaces are implemented: | ||||
| 	_ vfs.FileLockState = &memFile{} | ||||
| 	_ vfs.FileSizeHint  = &memFile{} | ||||
| ) | ||||
| 
 | ||||
| func (m *memFile) Close() error { | ||||
| 	m.release() | ||||
| 	return m.Unlock(vfs.LOCK_NONE) | ||||
| } | ||||
| 
 | ||||
| func (m *memFile) ReadAt(b []byte, off int64) (n int, err error) { | ||||
| 	m.dataMtx.RLock() | ||||
| 	defer m.dataMtx.RUnlock() | ||||
| 
 | ||||
| 	if off >= m.size { | ||||
| 		return 0, io.EOF | ||||
| 	} | ||||
| 
 | ||||
| 	base := off / sectorSize | ||||
| 	rest := off % sectorSize | ||||
| 	have := int64(sectorSize) | ||||
| 	if base == int64(len(m.data))-1 { | ||||
| 		have = modRoundUp(m.size, sectorSize) | ||||
| 	} | ||||
| 	n = copy(b, (*m.data[base])[rest:have]) | ||||
| 	if n < len(b) { | ||||
| 		// Assume reads are page aligned. | ||||
| 		return 0, io.ErrNoProgress | ||||
| 	} | ||||
| 	return n, nil | ||||
| } | ||||
| 
 | ||||
| func (m *memFile) WriteAt(b []byte, off int64) (n int, err error) { | ||||
| 	m.dataMtx.Lock() | ||||
| 	defer m.dataMtx.Unlock() | ||||
| 
 | ||||
| 	base := off / sectorSize | ||||
| 	rest := off % sectorSize | ||||
| 	for base >= int64(len(m.data)) { | ||||
| 		m.data = append(m.data, new([sectorSize]byte)) | ||||
| 	} | ||||
| 	n = copy((*m.data[base])[rest:], b) | ||||
| 	if n < len(b) { | ||||
| 		// Assume writes are page aligned. | ||||
| 		return n, io.ErrShortWrite | ||||
| 	} | ||||
| 	if size := off + int64(len(b)); size > m.size { | ||||
| 		m.size = size | ||||
| 	} | ||||
| 	return n, nil | ||||
| } | ||||
| 
 | ||||
| func (m *memFile) Truncate(size int64) error { | ||||
| 	m.dataMtx.Lock() | ||||
| 	defer m.dataMtx.Unlock() | ||||
| 	return m.truncate(size) | ||||
| } | ||||
| 
 | ||||
| // +checklocks:m.dataMtx | ||||
| func (m *memFile) truncate(size int64) error { | ||||
| 	if size < m.size { | ||||
| 		base := size / sectorSize | ||||
| 		rest := size % sectorSize | ||||
| 		if rest != 0 { | ||||
| 			clear((*m.data[base])[rest:]) | ||||
| 		} | ||||
| 	} | ||||
| 	sectors := divRoundUp(size, sectorSize) | ||||
| 	for sectors > int64(len(m.data)) { | ||||
| 		m.data = append(m.data, new([sectorSize]byte)) | ||||
| 	} | ||||
| 	clear(m.data[sectors:]) | ||||
| 	m.data = m.data[:sectors] | ||||
| 	m.size = size | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (m *memFile) Sync(flag vfs.SyncFlag) error { | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (m *memFile) Size() (int64, error) { | ||||
| 	m.dataMtx.RLock() | ||||
| 	defer m.dataMtx.RUnlock() | ||||
| 	return m.size, nil | ||||
| } | ||||
| 
 | ||||
| const spinWait = 25 * time.Microsecond | ||||
| 
 | ||||
| func (m *memFile) Lock(lock vfs.LockLevel) error { | ||||
| 	if m.lock >= lock { | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	if m.readOnly && lock >= vfs.LOCK_RESERVED { | ||||
| 		return sqlite3.IOERR_LOCK | ||||
| 	} | ||||
| 
 | ||||
| 	m.lockMtx.Lock() | ||||
| 	defer m.lockMtx.Unlock() | ||||
| 
 | ||||
| 	switch lock { | ||||
| 	case vfs.LOCK_SHARED: | ||||
| 		if m.pending != nil { | ||||
| 			return sqlite3.BUSY | ||||
| 		} | ||||
| 		m.shared++ | ||||
| 
 | ||||
| 	case vfs.LOCK_RESERVED: | ||||
| 		if m.reserved != nil { | ||||
| 			return sqlite3.BUSY | ||||
| 		} | ||||
| 		m.reserved = m | ||||
| 
 | ||||
| 	case vfs.LOCK_EXCLUSIVE: | ||||
| 		if m.lock < vfs.LOCK_PENDING { | ||||
| 			if m.pending != nil { | ||||
| 				return sqlite3.BUSY | ||||
| 			} | ||||
| 			m.lock = vfs.LOCK_PENDING | ||||
| 			m.pending = m | ||||
| 		} | ||||
| 
 | ||||
| 		for before := time.Now(); m.shared > 1; { | ||||
| 			if time.Since(before) > spinWait { | ||||
| 				return sqlite3.BUSY | ||||
| 			} | ||||
| 			m.lockMtx.Unlock() | ||||
| 			runtime.Gosched() | ||||
| 			m.lockMtx.Lock() | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	m.lock = lock | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (m *memFile) Unlock(lock vfs.LockLevel) error { | ||||
| 	if m.lock <= lock { | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	m.lockMtx.Lock() | ||||
| 	defer m.lockMtx.Unlock() | ||||
| 
 | ||||
| 	if m.pending == m { | ||||
| 		m.pending = nil | ||||
| 	} | ||||
| 	if m.reserved == m { | ||||
| 		m.reserved = nil | ||||
| 	} | ||||
| 	if lock < vfs.LOCK_SHARED { | ||||
| 		m.shared-- | ||||
| 	} | ||||
| 	m.lock = lock | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (m *memFile) CheckReservedLock() (bool, error) { | ||||
| 	if m.lock >= vfs.LOCK_RESERVED { | ||||
| 		return true, nil | ||||
| 	} | ||||
| 	m.lockMtx.Lock() | ||||
| 	defer m.lockMtx.Unlock() | ||||
| 	return m.reserved != nil, nil | ||||
| } | ||||
| 
 | ||||
| func (m *memFile) SectorSize() int { | ||||
| 	return sectorSize | ||||
| } | ||||
| 
 | ||||
| func (m *memFile) DeviceCharacteristics() vfs.DeviceCharacteristic { | ||||
| 	return vfs.IOCAP_ATOMIC | | ||||
| 		vfs.IOCAP_SEQUENTIAL | | ||||
| 		vfs.IOCAP_SAFE_APPEND | | ||||
| 		vfs.IOCAP_POWERSAFE_OVERWRITE | ||||
| } | ||||
| 
 | ||||
| func (m *memFile) SizeHint(size int64) error { | ||||
| 	m.dataMtx.Lock() | ||||
| 	defer m.dataMtx.Unlock() | ||||
| 	if size > m.size { | ||||
| 		return m.truncate(size) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (m *memFile) LockState() vfs.LockLevel { | ||||
| 	return m.lock | ||||
| } | ||||
| 
 | ||||
| func divRoundUp(a, b int64) int64 { | ||||
| 	return (a + b - 1) / b | ||||
| } | ||||
| 
 | ||||
| func modRoundUp(a, b int64) int64 { | ||||
| 	return b - (b-a%b)%b | ||||
| } | ||||
							
								
								
									
										33
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/os_bsd.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/os_bsd.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,33 @@ | |||
| //go:build (freebsd || openbsd || netbsd || dragonfly || illumos || sqlite3_flock) && !sqlite3_nosys | ||||
| 
 | ||||
| package vfs | ||||
| 
 | ||||
| import ( | ||||
| 	"os" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"golang.org/x/sys/unix" | ||||
| ) | ||||
| 
 | ||||
| func osUnlock(file *os.File, start, len int64) _ErrorCode { | ||||
| 	if start == 0 && len == 0 { | ||||
| 		err := unix.Flock(int(file.Fd()), unix.LOCK_UN) | ||||
| 		if err != nil { | ||||
| 			return _IOERR_UNLOCK | ||||
| 		} | ||||
| 	} | ||||
| 	return _OK | ||||
| } | ||||
| 
 | ||||
| func osLock(file *os.File, how int, def _ErrorCode) _ErrorCode { | ||||
| 	err := unix.Flock(int(file.Fd()), how) | ||||
| 	return osLockErrorCode(err, def) | ||||
| } | ||||
| 
 | ||||
| func osReadLock(file *os.File, _ /*start*/, _ /*len*/ int64, _ /*timeout*/ time.Duration) _ErrorCode { | ||||
| 	return osLock(file, unix.LOCK_SH|unix.LOCK_NB, _IOERR_RDLOCK) | ||||
| } | ||||
| 
 | ||||
| func osWriteLock(file *os.File, _ /*start*/, _ /*len*/ int64, _ /*timeout*/ time.Duration) _ErrorCode { | ||||
| 	return osLock(file, unix.LOCK_EX|unix.LOCK_NB, _IOERR_LOCK) | ||||
| } | ||||
							
								
								
									
										95
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/os_darwin.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/os_darwin.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,95 @@ | |||
| //go:build !(sqlite3_flock || sqlite3_nosys) | ||||
| 
 | ||||
| package vfs | ||||
| 
 | ||||
| import ( | ||||
| 	"io" | ||||
| 	"os" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"golang.org/x/sys/unix" | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
| 	// https://github.com/apple/darwin-xnu/blob/main/bsd/sys/fcntl.h | ||||
| 	_F_OFD_SETLK         = 90 | ||||
| 	_F_OFD_SETLKW        = 91 | ||||
| 	_F_OFD_SETLKWTIMEOUT = 93 | ||||
| ) | ||||
| 
 | ||||
| type flocktimeout_t struct { | ||||
| 	fl      unix.Flock_t | ||||
| 	timeout unix.Timespec | ||||
| } | ||||
| 
 | ||||
| func osSync(file *os.File, fullsync, _ /*dataonly*/ bool) error { | ||||
| 	if fullsync { | ||||
| 		return file.Sync() | ||||
| 	} | ||||
| 	return unix.Fsync(int(file.Fd())) | ||||
| } | ||||
| 
 | ||||
| func osAllocate(file *os.File, size int64) error { | ||||
| 	off, err := file.Seek(0, io.SeekEnd) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if size <= off { | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	store := unix.Fstore_t{ | ||||
| 		Flags:   unix.F_ALLOCATEALL | unix.F_ALLOCATECONTIG, | ||||
| 		Posmode: unix.F_PEOFPOSMODE, | ||||
| 		Offset:  0, | ||||
| 		Length:  size - off, | ||||
| 	} | ||||
| 
 | ||||
| 	// Try to get a continuous chunk of disk space. | ||||
| 	err = unix.FcntlFstore(file.Fd(), unix.F_PREALLOCATE, &store) | ||||
| 	if err != nil { | ||||
| 		// OK, perhaps we are too fragmented, allocate non-continuous. | ||||
| 		store.Flags = unix.F_ALLOCATEALL | ||||
| 		unix.FcntlFstore(file.Fd(), unix.F_PREALLOCATE, &store) | ||||
| 	} | ||||
| 	return file.Truncate(size) | ||||
| } | ||||
| 
 | ||||
| func osUnlock(file *os.File, start, len int64) _ErrorCode { | ||||
| 	err := unix.FcntlFlock(file.Fd(), _F_OFD_SETLK, &unix.Flock_t{ | ||||
| 		Type:  unix.F_UNLCK, | ||||
| 		Start: start, | ||||
| 		Len:   len, | ||||
| 	}) | ||||
| 	if err != nil { | ||||
| 		return _IOERR_UNLOCK | ||||
| 	} | ||||
| 	return _OK | ||||
| } | ||||
| 
 | ||||
| func osLock(file *os.File, typ int16, start, len int64, timeout time.Duration, def _ErrorCode) _ErrorCode { | ||||
| 	lock := flocktimeout_t{fl: unix.Flock_t{ | ||||
| 		Type:  typ, | ||||
| 		Start: start, | ||||
| 		Len:   len, | ||||
| 	}} | ||||
| 	var err error | ||||
| 	switch { | ||||
| 	case timeout == 0: | ||||
| 		err = unix.FcntlFlock(file.Fd(), _F_OFD_SETLK, &lock.fl) | ||||
| 	case timeout < 0: | ||||
| 		err = unix.FcntlFlock(file.Fd(), _F_OFD_SETLKW, &lock.fl) | ||||
| 	default: | ||||
| 		lock.timeout = unix.NsecToTimespec(int64(timeout / time.Nanosecond)) | ||||
| 		err = unix.FcntlFlock(file.Fd(), _F_OFD_SETLKWTIMEOUT, &lock.fl) | ||||
| 	} | ||||
| 	return osLockErrorCode(err, def) | ||||
| } | ||||
| 
 | ||||
| func osReadLock(file *os.File, start, len int64, timeout time.Duration) _ErrorCode { | ||||
| 	return osLock(file, unix.F_RDLCK, start, len, timeout, _IOERR_RDLOCK) | ||||
| } | ||||
| 
 | ||||
| func osWriteLock(file *os.File, start, len int64, timeout time.Duration) _ErrorCode { | ||||
| 	return osLock(file, unix.F_WRLCK, start, len, timeout, _IOERR_LOCK) | ||||
| } | ||||
							
								
								
									
										34
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/os_f2fs_linux.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/os_f2fs_linux.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,34 @@ | |||
| //go:build (amd64 || arm64 || riscv64) && !sqlite3_nosys | ||||
| 
 | ||||
| package vfs | ||||
| 
 | ||||
| import ( | ||||
| 	"os" | ||||
| 
 | ||||
| 	"golang.org/x/sys/unix" | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
| 	_F2FS_IOC_START_ATOMIC_WRITE  = 62721 | ||||
| 	_F2FS_IOC_COMMIT_ATOMIC_WRITE = 62722 | ||||
| 	_F2FS_IOC_ABORT_ATOMIC_WRITE  = 62725 | ||||
| 	_F2FS_IOC_GET_FEATURES        = 2147808524 | ||||
| 	_F2FS_FEATURE_ATOMIC_WRITE    = 4 | ||||
| ) | ||||
| 
 | ||||
| func osBatchAtomic(file *os.File) bool { | ||||
| 	flags, err := unix.IoctlGetInt(int(file.Fd()), _F2FS_IOC_GET_FEATURES) | ||||
| 	return err == nil && flags&_F2FS_FEATURE_ATOMIC_WRITE != 0 | ||||
| } | ||||
| 
 | ||||
| func (f *vfsFile) BeginAtomicWrite() error { | ||||
| 	return unix.IoctlSetInt(int(f.Fd()), _F2FS_IOC_START_ATOMIC_WRITE, 0) | ||||
| } | ||||
| 
 | ||||
| func (f *vfsFile) CommitAtomicWrite() error { | ||||
| 	return unix.IoctlSetInt(int(f.Fd()), _F2FS_IOC_COMMIT_ATOMIC_WRITE, 0) | ||||
| } | ||||
| 
 | ||||
| func (f *vfsFile) RollbackAtomicWrite() error { | ||||
| 	return unix.IoctlSetInt(int(f.Fd()), _F2FS_IOC_ABORT_ATOMIC_WRITE, 0) | ||||
| } | ||||
							
								
								
									
										71
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/os_linux.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/os_linux.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,71 @@ | |||
| //go:build !(sqlite3_flock || sqlite3_nosys) | ||||
| 
 | ||||
| package vfs | ||||
| 
 | ||||
| import ( | ||||
| 	"math/rand" | ||||
| 	"os" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"golang.org/x/sys/unix" | ||||
| ) | ||||
| 
 | ||||
| func osSync(file *os.File, _ /*fullsync*/, _ /*dataonly*/ bool) error { | ||||
| 	// SQLite trusts Linux's fdatasync for all fsync's. | ||||
| 	return unix.Fdatasync(int(file.Fd())) | ||||
| } | ||||
| 
 | ||||
| func osAllocate(file *os.File, size int64) error { | ||||
| 	if size == 0 { | ||||
| 		return nil | ||||
| 	} | ||||
| 	return unix.Fallocate(int(file.Fd()), 0, 0, size) | ||||
| } | ||||
| 
 | ||||
| func osUnlock(file *os.File, start, len int64) _ErrorCode { | ||||
| 	err := unix.FcntlFlock(file.Fd(), unix.F_OFD_SETLK, &unix.Flock_t{ | ||||
| 		Type:  unix.F_UNLCK, | ||||
| 		Start: start, | ||||
| 		Len:   len, | ||||
| 	}) | ||||
| 	if err != nil { | ||||
| 		return _IOERR_UNLOCK | ||||
| 	} | ||||
| 	return _OK | ||||
| } | ||||
| 
 | ||||
| func osLock(file *os.File, typ int16, start, len int64, timeout time.Duration, def _ErrorCode) _ErrorCode { | ||||
| 	lock := unix.Flock_t{ | ||||
| 		Type:  typ, | ||||
| 		Start: start, | ||||
| 		Len:   len, | ||||
| 	} | ||||
| 	var err error | ||||
| 	switch { | ||||
| 	case timeout == 0: | ||||
| 		err = unix.FcntlFlock(file.Fd(), unix.F_OFD_SETLK, &lock) | ||||
| 	case timeout < 0: | ||||
| 		err = unix.FcntlFlock(file.Fd(), unix.F_OFD_SETLKW, &lock) | ||||
| 	default: | ||||
| 		before := time.Now() | ||||
| 		for { | ||||
| 			err = unix.FcntlFlock(file.Fd(), unix.F_OFD_SETLK, &lock) | ||||
| 			if errno, _ := err.(unix.Errno); errno != unix.EAGAIN { | ||||
| 				break | ||||
| 			} | ||||
| 			if timeout < time.Since(before) { | ||||
| 				break | ||||
| 			} | ||||
| 			osSleep(time.Duration(rand.Int63n(int64(time.Millisecond)))) | ||||
| 		} | ||||
| 	} | ||||
| 	return osLockErrorCode(err, def) | ||||
| } | ||||
| 
 | ||||
| func osReadLock(file *os.File, start, len int64, timeout time.Duration) _ErrorCode { | ||||
| 	return osLock(file, unix.F_RDLCK, start, len, timeout, _IOERR_RDLOCK) | ||||
| } | ||||
| 
 | ||||
| func osWriteLock(file *os.File, start, len int64, timeout time.Duration) _ErrorCode { | ||||
| 	return osLock(file, unix.F_WRLCK, start, len, timeout, _IOERR_LOCK) | ||||
| } | ||||
							
								
								
									
										36
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/os_std_access.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/os_std_access.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,36 @@ | |||
| //go:build !unix || sqlite3_nosys | ||||
| 
 | ||||
| package vfs | ||||
| 
 | ||||
| import ( | ||||
| 	"io/fs" | ||||
| 	"os" | ||||
| ) | ||||
| 
 | ||||
| func osAccess(path string, flags AccessFlag) error { | ||||
| 	fi, err := os.Stat(path) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if flags == ACCESS_EXISTS { | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	const ( | ||||
| 		S_IREAD  = 0400 | ||||
| 		S_IWRITE = 0200 | ||||
| 		S_IEXEC  = 0100 | ||||
| 	) | ||||
| 
 | ||||
| 	var want fs.FileMode = S_IREAD | ||||
| 	if flags == ACCESS_READWRITE { | ||||
| 		want |= S_IWRITE | ||||
| 	} | ||||
| 	if fi.IsDir() { | ||||
| 		want |= S_IEXEC | ||||
| 	} | ||||
| 	if fi.Mode()&want != want { | ||||
| 		return fs.ErrPermission | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										19
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/os_std_alloc.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/os_std_alloc.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,19 @@ | |||
| //go:build !(linux || darwin) || sqlite3_flock || sqlite3_nosys | ||||
| 
 | ||||
| package vfs | ||||
| 
 | ||||
| import ( | ||||
| 	"io" | ||||
| 	"os" | ||||
| ) | ||||
| 
 | ||||
| func osAllocate(file *os.File, size int64) error { | ||||
| 	off, err := file.Seek(0, io.SeekEnd) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if size <= off { | ||||
| 		return nil | ||||
| 	} | ||||
| 	return file.Truncate(size) | ||||
| } | ||||
							
								
								
									
										9
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/os_std_atomic.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/os_std_atomic.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,9 @@ | |||
| //go:build !linux || !(amd64 || arm64 || riscv64) || sqlite3_nosys | ||||
| 
 | ||||
| package vfs | ||||
| 
 | ||||
| import "os" | ||||
| 
 | ||||
| func osBatchAtomic(*os.File) bool { | ||||
| 	return false | ||||
| } | ||||
							
								
								
									
										14
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/os_std_mode.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/os_std_mode.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,14 @@ | |||
| //go:build !unix || sqlite3_nosys | ||||
| 
 | ||||
| package vfs | ||||
| 
 | ||||
| import "os" | ||||
| 
 | ||||
| func osSetMode(file *os.File, modeof string) error { | ||||
| 	fi, err := os.Stat(modeof) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	file.Chmod(fi.Mode()) | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										9
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/os_std_sleep.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/os_std_sleep.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,9 @@ | |||
| //go:build !windows || sqlite3_nosys | ||||
| 
 | ||||
| package vfs | ||||
| 
 | ||||
| import "time" | ||||
| 
 | ||||
| func osSleep(d time.Duration) { | ||||
| 	time.Sleep(d) | ||||
| } | ||||
							
								
								
									
										9
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/os_std_sync.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/os_std_sync.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,9 @@ | |||
| //go:build !(linux || darwin) || sqlite3_flock || sqlite3_nosys | ||||
| 
 | ||||
| package vfs | ||||
| 
 | ||||
| import "os" | ||||
| 
 | ||||
| func osSync(file *os.File, _ /*fullsync*/, _ /*dataonly*/ bool) error { | ||||
| 	return file.Sync() | ||||
| } | ||||
							
								
								
									
										33
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/os_unix.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/os_unix.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,33 @@ | |||
| //go:build unix && !sqlite3_nosys | ||||
| 
 | ||||
| package vfs | ||||
| 
 | ||||
| import ( | ||||
| 	"os" | ||||
| 	"syscall" | ||||
| 
 | ||||
| 	"golang.org/x/sys/unix" | ||||
| ) | ||||
| 
 | ||||
| func osAccess(path string, flags AccessFlag) error { | ||||
| 	var access uint32 // unix.F_OK | ||||
| 	switch flags { | ||||
| 	case ACCESS_READWRITE: | ||||
| 		access = unix.R_OK | unix.W_OK | ||||
| 	case ACCESS_READ: | ||||
| 		access = unix.R_OK | ||||
| 	} | ||||
| 	return unix.Access(path, access) | ||||
| } | ||||
| 
 | ||||
| func osSetMode(file *os.File, modeof string) error { | ||||
| 	fi, err := os.Stat(modeof) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	file.Chmod(fi.Mode()) | ||||
| 	if sys, ok := fi.Sys().(*syscall.Stat_t); ok { | ||||
| 		file.Chown(int(sys.Uid), int(sys.Gid)) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										106
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/os_unix_lock.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/os_unix_lock.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,106 @@ | |||
| //go:build (linux || darwin || freebsd || openbsd || netbsd || dragonfly || illumos || sqlite3_flock) && !sqlite3_nosys | ||||
| 
 | ||||
| package vfs | ||||
| 
 | ||||
| import ( | ||||
| 	"os" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"golang.org/x/sys/unix" | ||||
| ) | ||||
| 
 | ||||
| func osGetSharedLock(file *os.File) _ErrorCode { | ||||
| 	// Test the PENDING lock before acquiring a new SHARED lock. | ||||
| 	if lock, _ := osGetLock(file, _PENDING_BYTE, 1); lock == unix.F_WRLCK { | ||||
| 		return _BUSY | ||||
| 	} | ||||
| 	// Acquire the SHARED lock. | ||||
| 	return osReadLock(file, _SHARED_FIRST, _SHARED_SIZE, 0) | ||||
| } | ||||
| 
 | ||||
| func osGetReservedLock(file *os.File) _ErrorCode { | ||||
| 	// Acquire the RESERVED lock. | ||||
| 	return osWriteLock(file, _RESERVED_BYTE, 1, 0) | ||||
| } | ||||
| 
 | ||||
| func osGetPendingLock(file *os.File, block bool) _ErrorCode { | ||||
| 	var timeout time.Duration | ||||
| 	if block { | ||||
| 		timeout = -1 | ||||
| 	} | ||||
| 	// Acquire the PENDING lock. | ||||
| 	return osWriteLock(file, _PENDING_BYTE, 1, timeout) | ||||
| } | ||||
| 
 | ||||
| func osGetExclusiveLock(file *os.File, wait bool) _ErrorCode { | ||||
| 	var timeout time.Duration | ||||
| 	if wait { | ||||
| 		timeout = time.Millisecond | ||||
| 	} | ||||
| 	// Acquire the EXCLUSIVE lock. | ||||
| 	return osWriteLock(file, _SHARED_FIRST, _SHARED_SIZE, timeout) | ||||
| } | ||||
| 
 | ||||
| func osDowngradeLock(file *os.File, state LockLevel) _ErrorCode { | ||||
| 	if state >= LOCK_EXCLUSIVE { | ||||
| 		// Downgrade to a SHARED lock. | ||||
| 		if rc := osReadLock(file, _SHARED_FIRST, _SHARED_SIZE, 0); rc != _OK { | ||||
| 			// In theory, the downgrade to a SHARED cannot fail because another | ||||
| 			// process is holding an incompatible lock. If it does, this | ||||
| 			// indicates that the other process is not following the locking | ||||
| 			// protocol. If this happens, return _IOERR_RDLOCK. Returning | ||||
| 			// BUSY would confuse the upper layer. | ||||
| 			return _IOERR_RDLOCK | ||||
| 		} | ||||
| 	} | ||||
| 	// Release the PENDING and RESERVED locks. | ||||
| 	return osUnlock(file, _PENDING_BYTE, 2) | ||||
| } | ||||
| 
 | ||||
| func osReleaseLock(file *os.File, _ LockLevel) _ErrorCode { | ||||
| 	// Release all locks. | ||||
| 	return osUnlock(file, 0, 0) | ||||
| } | ||||
| 
 | ||||
| func osCheckReservedLock(file *os.File) (bool, _ErrorCode) { | ||||
| 	// Test the RESERVED lock. | ||||
| 	lock, rc := osGetLock(file, _RESERVED_BYTE, 1) | ||||
| 	return lock == unix.F_WRLCK, rc | ||||
| } | ||||
| 
 | ||||
| func osGetLock(file *os.File, start, len int64) (int16, _ErrorCode) { | ||||
| 	lock := unix.Flock_t{ | ||||
| 		Type:  unix.F_WRLCK, | ||||
| 		Start: start, | ||||
| 		Len:   len, | ||||
| 	} | ||||
| 	if unix.FcntlFlock(file.Fd(), unix.F_GETLK, &lock) != nil { | ||||
| 		return 0, _IOERR_CHECKRESERVEDLOCK | ||||
| 	} | ||||
| 	return lock.Type, _OK | ||||
| } | ||||
| 
 | ||||
| func osLockErrorCode(err error, def _ErrorCode) _ErrorCode { | ||||
| 	if err == nil { | ||||
| 		return _OK | ||||
| 	} | ||||
| 	if errno, ok := err.(unix.Errno); ok { | ||||
| 		switch errno { | ||||
| 		case | ||||
| 			unix.EACCES, | ||||
| 			unix.EAGAIN, | ||||
| 			unix.EBUSY, | ||||
| 			unix.EINTR, | ||||
| 			unix.ENOLCK, | ||||
| 			unix.EDEADLK, | ||||
| 			unix.ETIMEDOUT: | ||||
| 			return _BUSY | ||||
| 		case unix.EPERM: | ||||
| 			return _PERM | ||||
| 		} | ||||
| 		if errno == unix.EWOULDBLOCK && unix.EWOULDBLOCK != unix.EAGAIN { | ||||
| 			return _BUSY | ||||
| 		} | ||||
| 	} | ||||
| 	return def | ||||
| } | ||||
							
								
								
									
										186
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/os_windows.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										186
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/os_windows.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,186 @@ | |||
| //go:build !sqlite3_nosys | ||||
| 
 | ||||
| package vfs | ||||
| 
 | ||||
| import ( | ||||
| 	"math/rand" | ||||
| 	"os" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"golang.org/x/sys/windows" | ||||
| ) | ||||
| 
 | ||||
| func osGetSharedLock(file *os.File) _ErrorCode { | ||||
| 	// Acquire the PENDING lock temporarily before acquiring a new SHARED lock. | ||||
| 	rc := osReadLock(file, _PENDING_BYTE, 1, 0) | ||||
| 	if rc == _OK { | ||||
| 		// Acquire the SHARED lock. | ||||
| 		rc = osReadLock(file, _SHARED_FIRST, _SHARED_SIZE, 0) | ||||
| 
 | ||||
| 		// Release the PENDING lock. | ||||
| 		osUnlock(file, _PENDING_BYTE, 1) | ||||
| 	} | ||||
| 	return rc | ||||
| } | ||||
| 
 | ||||
| func osGetReservedLock(file *os.File) _ErrorCode { | ||||
| 	// Acquire the RESERVED lock. | ||||
| 	return osWriteLock(file, _RESERVED_BYTE, 1, 0) | ||||
| } | ||||
| 
 | ||||
| func osGetPendingLock(file *os.File, block bool) _ErrorCode { | ||||
| 	var timeout time.Duration | ||||
| 	if block { | ||||
| 		timeout = -1 | ||||
| 	} | ||||
| 
 | ||||
| 	// Acquire the PENDING lock. | ||||
| 	return osWriteLock(file, _PENDING_BYTE, 1, timeout) | ||||
| } | ||||
| 
 | ||||
| func osGetExclusiveLock(file *os.File, wait bool) _ErrorCode { | ||||
| 	var timeout time.Duration | ||||
| 	if wait { | ||||
| 		timeout = time.Millisecond | ||||
| 	} | ||||
| 
 | ||||
| 	// Release the SHARED lock. | ||||
| 	osUnlock(file, _SHARED_FIRST, _SHARED_SIZE) | ||||
| 
 | ||||
| 	// Acquire the EXCLUSIVE lock. | ||||
| 	rc := osWriteLock(file, _SHARED_FIRST, _SHARED_SIZE, timeout) | ||||
| 
 | ||||
| 	if rc != _OK { | ||||
| 		// Reacquire the SHARED lock. | ||||
| 		osReadLock(file, _SHARED_FIRST, _SHARED_SIZE, 0) | ||||
| 	} | ||||
| 	return rc | ||||
| } | ||||
| 
 | ||||
| func osDowngradeLock(file *os.File, state LockLevel) _ErrorCode { | ||||
| 	if state >= LOCK_EXCLUSIVE { | ||||
| 		// Release the EXCLUSIVE lock. | ||||
| 		osUnlock(file, _SHARED_FIRST, _SHARED_SIZE) | ||||
| 
 | ||||
| 		// Reacquire the SHARED lock. | ||||
| 		if rc := osReadLock(file, _SHARED_FIRST, _SHARED_SIZE, 0); rc != _OK { | ||||
| 			// This should never happen. | ||||
| 			// We should always be able to reacquire the read lock. | ||||
| 			return _IOERR_RDLOCK | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// Release the PENDING and RESERVED locks. | ||||
| 	if state >= LOCK_RESERVED { | ||||
| 		osUnlock(file, _RESERVED_BYTE, 1) | ||||
| 	} | ||||
| 	if state >= LOCK_PENDING { | ||||
| 		osUnlock(file, _PENDING_BYTE, 1) | ||||
| 	} | ||||
| 	return _OK | ||||
| } | ||||
| 
 | ||||
| func osReleaseLock(file *os.File, state LockLevel) _ErrorCode { | ||||
| 	// Release all locks. | ||||
| 	if state >= LOCK_RESERVED { | ||||
| 		osUnlock(file, _RESERVED_BYTE, 1) | ||||
| 	} | ||||
| 	if state >= LOCK_SHARED { | ||||
| 		osUnlock(file, _SHARED_FIRST, _SHARED_SIZE) | ||||
| 	} | ||||
| 	if state >= LOCK_PENDING { | ||||
| 		osUnlock(file, _PENDING_BYTE, 1) | ||||
| 	} | ||||
| 	return _OK | ||||
| } | ||||
| 
 | ||||
| func osCheckReservedLock(file *os.File) (bool, _ErrorCode) { | ||||
| 	// Test the RESERVED lock. | ||||
| 	rc := osLock(file, 0, _RESERVED_BYTE, 1, 0, _IOERR_CHECKRESERVEDLOCK) | ||||
| 	if rc == _BUSY { | ||||
| 		return true, _OK | ||||
| 	} | ||||
| 	if rc == _OK { | ||||
| 		// Release the RESERVED lock. | ||||
| 		osUnlock(file, _RESERVED_BYTE, 1) | ||||
| 	} | ||||
| 	return false, rc | ||||
| } | ||||
| 
 | ||||
| func osUnlock(file *os.File, start, len uint32) _ErrorCode { | ||||
| 	err := windows.UnlockFileEx(windows.Handle(file.Fd()), | ||||
| 		0, len, 0, &windows.Overlapped{Offset: start}) | ||||
| 	if err == windows.ERROR_NOT_LOCKED { | ||||
| 		return _OK | ||||
| 	} | ||||
| 	if err != nil { | ||||
| 		return _IOERR_UNLOCK | ||||
| 	} | ||||
| 	return _OK | ||||
| } | ||||
| 
 | ||||
| func osLock(file *os.File, flags, start, len uint32, timeout time.Duration, def _ErrorCode) _ErrorCode { | ||||
| 	var err error | ||||
| 	switch { | ||||
| 	case timeout == 0: | ||||
| 		err = osLockEx(file, flags|windows.LOCKFILE_FAIL_IMMEDIATELY, start, len) | ||||
| 	case timeout < 0: | ||||
| 		err = osLockEx(file, flags, start, len) | ||||
| 	default: | ||||
| 		before := time.Now() | ||||
| 		for { | ||||
| 			err = osLockEx(file, flags|windows.LOCKFILE_FAIL_IMMEDIATELY, start, len) | ||||
| 			if errno, _ := err.(windows.Errno); errno != windows.ERROR_LOCK_VIOLATION { | ||||
| 				break | ||||
| 			} | ||||
| 			if timeout < time.Since(before) { | ||||
| 				break | ||||
| 			} | ||||
| 			osSleep(time.Duration(rand.Int63n(int64(time.Millisecond)))) | ||||
| 		} | ||||
| 	} | ||||
| 	return osLockErrorCode(err, def) | ||||
| } | ||||
| 
 | ||||
| func osLockEx(file *os.File, flags, start, len uint32) error { | ||||
| 	return windows.LockFileEx(windows.Handle(file.Fd()), flags, | ||||
| 		0, len, 0, &windows.Overlapped{Offset: start}) | ||||
| } | ||||
| 
 | ||||
| func osReadLock(file *os.File, start, len uint32, timeout time.Duration) _ErrorCode { | ||||
| 	return osLock(file, 0, start, len, timeout, _IOERR_RDLOCK) | ||||
| } | ||||
| 
 | ||||
| func osWriteLock(file *os.File, start, len uint32, timeout time.Duration) _ErrorCode { | ||||
| 	return osLock(file, windows.LOCKFILE_EXCLUSIVE_LOCK, start, len, timeout, _IOERR_LOCK) | ||||
| } | ||||
| 
 | ||||
| func osLockErrorCode(err error, def _ErrorCode) _ErrorCode { | ||||
| 	if err == nil { | ||||
| 		return _OK | ||||
| 	} | ||||
| 	if errno, ok := err.(windows.Errno); ok { | ||||
| 		// https://devblogs.microsoft.com/oldnewthing/20140905-00/?p=63 | ||||
| 		switch errno { | ||||
| 		case | ||||
| 			windows.ERROR_LOCK_VIOLATION, | ||||
| 			windows.ERROR_IO_PENDING, | ||||
| 			windows.ERROR_OPERATION_ABORTED: | ||||
| 			return _BUSY | ||||
| 		} | ||||
| 	} | ||||
| 	return def | ||||
| } | ||||
| 
 | ||||
| func osSleep(d time.Duration) { | ||||
| 	if d > 0 { | ||||
| 		period := max(1, d/(5*time.Millisecond)) | ||||
| 		if period < 16 { | ||||
| 			windows.TimeBeginPeriod(uint32(period)) | ||||
| 		} | ||||
| 		time.Sleep(d) | ||||
| 		if period < 16 { | ||||
| 			windows.TimeEndPeriod(uint32(period)) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										48
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/registry.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/registry.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,48 @@ | |||
| package vfs | ||||
| 
 | ||||
| import "sync" | ||||
| 
 | ||||
| var ( | ||||
| 	// +checklocks:vfsRegistryMtx | ||||
| 	vfsRegistry    map[string]VFS | ||||
| 	vfsRegistryMtx sync.RWMutex | ||||
| ) | ||||
| 
 | ||||
| // Find returns a VFS given its name. | ||||
| // If there is no match, nil is returned. | ||||
| // If name is empty, the default VFS is returned. | ||||
| // | ||||
| // https://sqlite.org/c3ref/vfs_find.html | ||||
| func Find(name string) VFS { | ||||
| 	if name == "" || name == "os" { | ||||
| 		return vfsOS{} | ||||
| 	} | ||||
| 	vfsRegistryMtx.RLock() | ||||
| 	defer vfsRegistryMtx.RUnlock() | ||||
| 	return vfsRegistry[name] | ||||
| } | ||||
| 
 | ||||
| // Register registers a VFS. | ||||
| // Empty and "os" are reserved names. | ||||
| // | ||||
| // https://sqlite.org/c3ref/vfs_find.html | ||||
| func Register(name string, vfs VFS) { | ||||
| 	if name == "" || name == "os" { | ||||
| 		return | ||||
| 	} | ||||
| 	vfsRegistryMtx.Lock() | ||||
| 	defer vfsRegistryMtx.Unlock() | ||||
| 	if vfsRegistry == nil { | ||||
| 		vfsRegistry = map[string]VFS{} | ||||
| 	} | ||||
| 	vfsRegistry[name] = vfs | ||||
| } | ||||
| 
 | ||||
| // Unregister unregisters a VFS. | ||||
| // | ||||
| // https://sqlite.org/c3ref/vfs_find.html | ||||
| func Unregister(name string) { | ||||
| 	vfsRegistryMtx.Lock() | ||||
| 	defer vfsRegistryMtx.Unlock() | ||||
| 	delete(vfsRegistry, name) | ||||
| } | ||||
							
								
								
									
										173
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/shm.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										173
									
								
								vendor/github.com/ncruces/go-sqlite3/vfs/shm.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,173 @@ | |||
| //go:build (darwin || linux) && (amd64 || arm64 || riscv64) && !(sqlite3_flock || sqlite3_noshm || sqlite3_nosys) | ||||
| 
 | ||||
| package vfs | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"io" | ||||
| 	"os" | ||||
| 
 | ||||
| 	"github.com/ncruces/go-sqlite3/internal/util" | ||||
| 	"github.com/tetratelabs/wazero/api" | ||||
| 	"golang.org/x/sys/unix" | ||||
| ) | ||||
| 
 | ||||
| // SupportsSharedMemory is false on platforms that do not support shared memory. | ||||
| // To use [WAL without shared-memory], you need to set [EXCLUSIVE locking mode]. | ||||
| // | ||||
| // [WAL without shared-memory]: https://sqlite.org/wal.html#noshm | ||||
| // [EXCLUSIVE locking mode]: https://sqlite.org/pragma.html#pragma_locking_mode | ||||
| const SupportsSharedMemory = true | ||||
| 
 | ||||
| const ( | ||||
| 	_SHM_NLOCK = 8 | ||||
| 	_SHM_BASE  = 120 | ||||
| 	_SHM_DMS   = _SHM_BASE + _SHM_NLOCK | ||||
| ) | ||||
| 
 | ||||
| func (f *vfsFile) SharedMemory() SharedMemory { return f.shm } | ||||
| 
 | ||||
| // NewSharedMemory returns a shared-memory WAL-index | ||||
| // backed by a file with the given path. | ||||
| // It will return nil if shared-memory is not supported, | ||||
| // or not appropriate for the given flags. | ||||
| // Only [OPEN_MAIN_DB] databases may need a WAL-index. | ||||
| // You must ensure all concurrent accesses to a database | ||||
| // use shared-memory instances created with the same path. | ||||
| func NewSharedMemory(path string, flags OpenFlag) SharedMemory { | ||||
| 	if flags&OPEN_MAIN_DB == 0 || flags&(OPEN_DELETEONCLOSE|OPEN_MEMORY) != 0 { | ||||
| 		return nil | ||||
| 	} | ||||
| 	return &vfsShm{ | ||||
| 		path:     path, | ||||
| 		readOnly: flags&OPEN_READONLY != 0, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| type vfsShm struct { | ||||
| 	*os.File | ||||
| 	path     string | ||||
| 	regions  []*util.MappedRegion | ||||
| 	readOnly bool | ||||
| } | ||||
| 
 | ||||
| func (s *vfsShm) shmMap(ctx context.Context, mod api.Module, id, size int32, extend bool) (uint32, error) { | ||||
| 	// Ensure size is a multiple of the OS page size. | ||||
| 	if int(size)&(unix.Getpagesize()-1) != 0 { | ||||
| 		return 0, _IOERR_SHMMAP | ||||
| 	} | ||||
| 
 | ||||
| 	if s.File == nil { | ||||
| 		var flag int | ||||
| 		if s.readOnly { | ||||
| 			flag = unix.O_RDONLY | ||||
| 		} else { | ||||
| 			flag = unix.O_RDWR | ||||
| 		} | ||||
| 		f, err := os.OpenFile(s.path, | ||||
| 			flag|unix.O_CREAT|unix.O_NOFOLLOW, 0666) | ||||
| 		if err != nil { | ||||
| 			return 0, _CANTOPEN | ||||
| 		} | ||||
| 		s.File = f | ||||
| 	} | ||||
| 
 | ||||
| 	// Dead man's switch. | ||||
| 	if lock, rc := osGetLock(s.File, _SHM_DMS, 1); rc != _OK { | ||||
| 		return 0, _IOERR_LOCK | ||||
| 	} else if lock == unix.F_WRLCK { | ||||
| 		return 0, _BUSY | ||||
| 	} else if lock == unix.F_UNLCK { | ||||
| 		if s.readOnly { | ||||
| 			return 0, _READONLY_CANTINIT | ||||
| 		} | ||||
| 		if rc := osWriteLock(s.File, _SHM_DMS, 1, 0); rc != _OK { | ||||
| 			return 0, rc | ||||
| 		} | ||||
| 		if err := s.Truncate(0); err != nil { | ||||
| 			return 0, _IOERR_SHMOPEN | ||||
| 		} | ||||
| 	} | ||||
| 	if rc := osReadLock(s.File, _SHM_DMS, 1, 0); rc != _OK { | ||||
| 		return 0, rc | ||||
| 	} | ||||
| 
 | ||||
| 	// Check if file is big enough. | ||||
| 	o, err := s.Seek(0, io.SeekEnd) | ||||
| 	if err != nil { | ||||
| 		return 0, _IOERR_SHMSIZE | ||||
| 	} | ||||
| 	if n := (int64(id) + 1) * int64(size); n > o { | ||||
| 		if !extend { | ||||
| 			return 0, nil | ||||
| 		} | ||||
| 		err := osAllocate(s.File, n) | ||||
| 		if err != nil { | ||||
| 			return 0, _IOERR_SHMSIZE | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	var prot int | ||||
| 	if s.readOnly { | ||||
| 		prot = unix.PROT_READ | ||||
| 	} else { | ||||
| 		prot = unix.PROT_READ | unix.PROT_WRITE | ||||
| 	} | ||||
| 	r, err := util.MapRegion(ctx, mod, s.File, int64(id)*int64(size), size, prot) | ||||
| 	if err != nil { | ||||
| 		return 0, err | ||||
| 	} | ||||
| 	s.regions = append(s.regions, r) | ||||
| 	return r.Ptr, nil | ||||
| } | ||||
| 
 | ||||
| func (s *vfsShm) shmLock(offset, n int32, flags _ShmFlag) error { | ||||
| 	// Argument check. | ||||
| 	if n <= 0 || offset < 0 || offset+n > _SHM_NLOCK { | ||||
| 		panic(util.AssertErr()) | ||||
| 	} | ||||
| 	switch flags { | ||||
| 	case | ||||
| 		_SHM_LOCK | _SHM_SHARED, | ||||
| 		_SHM_LOCK | _SHM_EXCLUSIVE, | ||||
| 		_SHM_UNLOCK | _SHM_SHARED, | ||||
| 		_SHM_UNLOCK | _SHM_EXCLUSIVE: | ||||
| 		// | ||||
| 	default: | ||||
| 		panic(util.AssertErr()) | ||||
| 	} | ||||
| 	if n != 1 && flags&_SHM_EXCLUSIVE == 0 { | ||||
| 		panic(util.AssertErr()) | ||||
| 	} | ||||
| 
 | ||||
| 	switch { | ||||
| 	case flags&_SHM_UNLOCK != 0: | ||||
| 		return osUnlock(s.File, _SHM_BASE+int64(offset), int64(n)) | ||||
| 	case flags&_SHM_SHARED != 0: | ||||
| 		return osReadLock(s.File, _SHM_BASE+int64(offset), int64(n), 0) | ||||
| 	case flags&_SHM_EXCLUSIVE != 0: | ||||
| 		return osWriteLock(s.File, _SHM_BASE+int64(offset), int64(n), 0) | ||||
| 	default: | ||||
| 		panic(util.AssertErr()) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (s *vfsShm) shmUnmap(delete bool) { | ||||
| 	if s.File == nil { | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	// Unmap regions. | ||||
| 	for _, r := range s.regions { | ||||
| 		r.Unmap() | ||||
| 	} | ||||
| 	clear(s.regions) | ||||
| 	s.regions = s.regions[:0] | ||||
| 
 | ||||
| 	// Close the file. | ||||
| 	defer s.Close() | ||||
| 	if delete { | ||||
| 		os.Remove(s.Name()) | ||||
| 	} | ||||
| 	s.File = nil | ||||
| } | ||||
Some files were not shown because too many files have changed in this diff Show more
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue