mirror of
https://github.com/Buriburizaem0n/nezha_domains.git
synced 2026-09-19 17:50:12 +00:00
187 lines
5.6 KiB
Go
187 lines
5.6 KiB
Go
//go:build agentcompat && linux
|
|
|
|
package singleton
|
|
|
|
import (
|
|
"context"
|
|
"database/sql/driver"
|
|
"errors"
|
|
"io"
|
|
"reflect"
|
|
)
|
|
|
|
type sqliteAttributionClassification struct {
|
|
readonly bool
|
|
hasRowDML bool
|
|
ambiguous bool
|
|
operation SQLiteOperation
|
|
table string
|
|
}
|
|
|
|
func (classification sqliteAttributionClassification) requiresAttribution() bool {
|
|
return !classification.readonly && classification.hasRowDML
|
|
}
|
|
|
|
func (classification sqliteAttributionClassification) valid() bool {
|
|
return !classification.ambiguous && classification.hasRowDML && classification.operation != "" && classification.table != ""
|
|
}
|
|
|
|
type sqliteAttributionStatement struct {
|
|
connection *sqliteAttributionConnection
|
|
statement driver.Stmt
|
|
classification sqliteAttributionClassification
|
|
}
|
|
|
|
var (
|
|
_ driver.Stmt = (*sqliteAttributionStatement)(nil)
|
|
_ driver.StmtExecContext = (*sqliteAttributionStatement)(nil)
|
|
_ driver.StmtQueryContext = (*sqliteAttributionStatement)(nil)
|
|
)
|
|
|
|
func (statement *sqliteAttributionStatement) Close() error { return statement.statement.Close() }
|
|
func (statement *sqliteAttributionStatement) NumInput() int { return statement.statement.NumInput() }
|
|
|
|
func (statement *sqliteAttributionStatement) Exec(values []driver.Value) (driver.Result, error) {
|
|
return statement.execute(func() (driver.Result, error) { return statement.statement.Exec(values) })
|
|
}
|
|
|
|
func (statement *sqliteAttributionStatement) ExecContext(ctx context.Context, values []driver.NamedValue) (driver.Result, error) {
|
|
contextStatement, ok := statement.statement.(driver.StmtExecContext)
|
|
if !ok {
|
|
return nil, driver.ErrSkip
|
|
}
|
|
return statement.execute(func() (driver.Result, error) { return contextStatement.ExecContext(ctx, values) })
|
|
}
|
|
|
|
func (statement *sqliteAttributionStatement) Query(values []driver.Value) (driver.Rows, error) {
|
|
return statement.queryOwned(func() (driver.Rows, error) { return statement.statement.Query(values) }, nil)
|
|
}
|
|
|
|
func (statement *sqliteAttributionStatement) QueryContext(ctx context.Context, values []driver.NamedValue) (driver.Rows, error) {
|
|
contextStatement, ok := statement.statement.(driver.StmtQueryContext)
|
|
if !ok {
|
|
return nil, driver.ErrSkip
|
|
}
|
|
return statement.queryOwned(func() (driver.Rows, error) { return contextStatement.QueryContext(ctx, values) }, nil)
|
|
}
|
|
|
|
func (statement *sqliteAttributionStatement) execute(run func() (driver.Result, error)) (driver.Result, error) {
|
|
if err := statement.connection.beforeWrite(statement.classification); err != nil {
|
|
return nil, err
|
|
}
|
|
result, err := run()
|
|
if err != nil {
|
|
statement.connection.discardExecution()
|
|
return nil, err
|
|
}
|
|
if rows, rowsErr := result.RowsAffected(); rowsErr != nil || rows > 0 {
|
|
if err := statement.connection.publishExecution(); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
statement.connection.discardExecution()
|
|
return result, nil
|
|
}
|
|
|
|
func (statement *sqliteAttributionStatement) queryOwned(run func() (driver.Rows, error), owner driver.Stmt) (driver.Rows, error) {
|
|
if err := statement.connection.beforeWrite(statement.classification); err != nil {
|
|
if owner != nil {
|
|
return nil, errors.Join(err, owner.Close())
|
|
}
|
|
return nil, err
|
|
}
|
|
rows, err := run()
|
|
if err != nil {
|
|
statement.connection.discardExecution()
|
|
if owner != nil {
|
|
return nil, errors.Join(err, owner.Close())
|
|
}
|
|
return nil, err
|
|
}
|
|
// Disabled attribution must preserve the stock driver rows lifecycle used during Dashboard bootstrap.
|
|
if !sqliteAttributionEnabled.Load() || !statement.classification.requiresAttribution() {
|
|
if owner == nil {
|
|
return rows, nil
|
|
}
|
|
return &sqliteAttributionRows{rows: rows, owner: owner}, nil
|
|
}
|
|
return &sqliteAttributionRows{rows: rows, owner: owner, connection: statement.connection}, nil
|
|
}
|
|
|
|
type sqliteAttributionRows struct {
|
|
rows driver.Rows
|
|
owner driver.Stmt
|
|
connection *sqliteAttributionConnection
|
|
finished bool
|
|
closed bool
|
|
}
|
|
|
|
func (rows *sqliteAttributionRows) Columns() []string { return rows.rows.Columns() }
|
|
|
|
func (rows *sqliteAttributionRows) ColumnTypeDatabaseTypeName(index int) string {
|
|
typed, ok := rows.rows.(driver.RowsColumnTypeDatabaseTypeName)
|
|
if !ok {
|
|
return ""
|
|
}
|
|
return typed.ColumnTypeDatabaseTypeName(index)
|
|
}
|
|
|
|
func (rows *sqliteAttributionRows) ColumnTypeNullable(index int) (bool, bool) {
|
|
typed, ok := rows.rows.(driver.RowsColumnTypeNullable)
|
|
if !ok {
|
|
return false, false
|
|
}
|
|
return typed.ColumnTypeNullable(index)
|
|
}
|
|
|
|
func (rows *sqliteAttributionRows) ColumnTypeScanType(index int) reflect.Type {
|
|
typed, ok := rows.rows.(driver.RowsColumnTypeScanType)
|
|
if !ok {
|
|
return nil
|
|
}
|
|
return typed.ColumnTypeScanType(index)
|
|
}
|
|
|
|
func (rows *sqliteAttributionRows) Close() error {
|
|
if rows.closed {
|
|
return nil
|
|
}
|
|
rows.closed = true
|
|
closeErr := rows.rows.Close()
|
|
if !rows.finished && rows.connection != nil {
|
|
rows.connection.poison(&SQLiteAttributionError{Cause: ErrSQLiteAttributionUnsupportedWrite})
|
|
rows.connection.discardExecution()
|
|
}
|
|
if rows.owner != nil {
|
|
return errors.Join(closeErr, rows.owner.Close())
|
|
}
|
|
return closeErr
|
|
}
|
|
|
|
func (rows *sqliteAttributionRows) Next(destination []driver.Value) error {
|
|
err := rows.rows.Next(destination)
|
|
if errors.Is(err, driver.ErrBadConn) {
|
|
// Read-only direct Query rows have no attribution connection; ErrBadConn must still propagate.
|
|
if rows.connection != nil {
|
|
rows.connection.discardExecution()
|
|
}
|
|
return err
|
|
}
|
|
if err == nil {
|
|
return nil
|
|
}
|
|
rows.finished = true
|
|
if err != io.EOF {
|
|
if rows.connection != nil {
|
|
rows.connection.discardExecution()
|
|
}
|
|
return err
|
|
}
|
|
if rows.connection != nil {
|
|
if publishErr := rows.connection.publishExecution(); publishErr != nil {
|
|
return publishErr
|
|
}
|
|
}
|
|
return io.EOF
|
|
}
|