コード例 #1
0
        public override bool Read()
        {
            CheckClosed("Read");

            if (!_hasRead)
            {
                _hasRead = true;

                return(NativeMethods.sqlite3_stmt_busy(_currentHandle) != 0);
            }

            Debug.Assert(_currentHandle != null && !_currentHandle.IsInvalid, "_currentHandle is null.");
            var rc = NativeMethods.sqlite3_step(_currentHandle);

            if (rc == Constants.SQLITE_DONE)
            {
                return(false);
            }
            if (rc != Constants.SQLITE_ROW)
            {
                MarshalEx.ThrowExceptionForRC(rc);
            }

            return(true);
        }
コード例 #2
0
        public override object ExecuteScalar()
        {
            if (OpenReader != null)
            {
                throw new InvalidOperationException(Strings.OpenReaderExists);
            }
            if (_connection == null ||
                _connection.State != ConnectionState.Open)
            {
                throw new InvalidOperationException(Strings.FormatCallRequiresOpenConnection("ExecuteScalar"));
            }
            if (string.IsNullOrWhiteSpace(_commandText))
            {
                throw new InvalidOperationException(Strings.FormatCallRequiresSetCommandText("ExecuteScalar"));
            }

            ValidateTransaction();
            Prepare();
            Bind();

            object result    = null;
            var    gotResult = false;

            foreach (var handle in _handles)
            {
                try
                {
                    var rc = NativeMethods.sqlite3_step(handle);
                    if (rc != Constants.SQLITE_DONE &&
                        rc != Constants.SQLITE_ROW)
                    {
                        MarshalEx.ThrowExceptionForRC(rc);
                    }

                    var hasResults = NativeMethods.sqlite3_stmt_readonly(handle) != 0;

                    if (!gotResult && hasResults)
                    {
                        if (rc == Constants.SQLITE_ROW)
                        {
                            var declaredType = NativeMethods.sqlite3_column_decltype(handle, 0);
                            var sqliteType   = (SQLiteType)NativeMethods.sqlite3_column_type(handle, 0);
                            var map          = SQLiteTypeMap.FromDeclaredType(declaredType, sqliteType);
                            var value        = ColumnReader.Read(map.SQLiteType, handle, 0);

                            result = map.FromInterop(value);
                        }

                        gotResult = true;
                    }
                }
                finally
                {
                    var rc = NativeMethods.sqlite3_reset(handle);
                    MarshalEx.ThrowExceptionForRC(rc);
                }
            }

            return(result);
        }
コード例 #3
0
        public override void Close()
        {
            if (_closed)
            {
                return;
            }

            Debug.Assert(_command.OpenReader == this, "_command.ActiveReader is not this.");

            if (_handles.Any())
            {
                foreach (var handle in _handles)
                {
                    if (handle != null &&
                        !handle.IsInvalid)
                    {
                        var rc = NativeMethods.sqlite3_reset(handle);
                        MarshalEx.ThrowExceptionForRC(rc);
                    }
                }

                _handles = _empty;
            }

            _command.OpenReader = null;
            _closed             = true;
        }
コード例 #4
0
        public override void Open()
        {
            if (_state == ConnectionState.Open)
            {
                return;
            }
            if (_connectionString == null)
            {
                throw new InvalidOperationException(Strings.OpenRequiresSetConnectionString);
            }

            Debug.Assert(_handle == null, "_handle is not null.");
            Debug.Assert(_connectionOptions != null, "_connectionOptions is null.");

            // TODO: Register transaction hooks
            var rc = NativeMethods.sqlite3_open_v2(
                _connectionOptions.Filename,
                out _handle,
                _connectionOptions.GetFlags(),
                _connectionOptions.VirtualFileSystem);

            MarshalEx.ThrowExceptionForRC(rc);

            SetState(ConnectionState.Open);
        }
コード例 #5
0
        internal void Bind(IEnumerable <StatementHandle> handles)
        {
            Debug.Assert(handles != null, "handles is null.");
            if (_parameterName == null)
            {
                throw new InvalidOperationException(Strings.FormatRequiresSet("ParameterName"));
            }
            if (_value == null)
            {
                throw new InvalidOperationException(Strings.FormatRequiresSet("Value"));
            }

            foreach (var handle in handles)
            {
                var index = NativeMethods.sqlite3_bind_parameter_index(handle, _parameterName);
                if (index != 0)
                {
                    var typeMap = SQLiteTypeMap.FromClrType(_value.GetType());
                    switch (typeMap.SQLiteType)
                    {
                    case SQLiteType.Integer:
                        var rc = NativeMethods.sqlite3_bind_int64(handle, index, (long)typeMap.ToInterop(_value));
                        MarshalEx.ThrowExceptionForRC(rc);
                        break;

                    case SQLiteType.Float:
                        rc = NativeMethods.sqlite3_bind_double(handle, index, (double)typeMap.ToInterop(_value));
                        MarshalEx.ThrowExceptionForRC(rc);
                        break;

                    case SQLiteType.Text:
                        rc = NativeMethods.sqlite3_bind_text(handle, index, (string)typeMap.ToInterop(_value));
                        MarshalEx.ThrowExceptionForRC(rc);
                        break;

                    case SQLiteType.Blob:
                        rc = NativeMethods.sqlite3_bind_blob(handle, index, (byte[])typeMap.ToInterop(_value));
                        MarshalEx.ThrowExceptionForRC(rc);
                        break;

                    case SQLiteType.Null:
                        rc = NativeMethods.sqlite3_bind_null(handle, index);
                        MarshalEx.ThrowExceptionForRC(rc);
                        break;

                    default:
                        Debug.Assert(false, "Unexpected value.");
                        break;
                    }
                }
            }

            _bound = true;
        }
コード例 #6
0
        public virtual void EnableExtensions(bool enable = true)
        {
            if (_db == null ||
                _db.IsInvalid)
            {
                throw new InvalidOperationException(Strings.FormatCallRequiresOpenConnection(nameof(EnableExtensions)));
            }

            var rc = NativeMethods.sqlite3_enable_load_extension(_db, enable ? 1 : 0);

            MarshalEx.ThrowExceptionForRC(rc, _db);
        }
コード例 #7
0
        // TODO: Honor behavior
        public new SQLiteDataReader ExecuteReader(CommandBehavior behavior)
        {
            if (OpenReader != null)
            {
                throw new InvalidOperationException(Strings.OpenReaderExists);
            }
            if (_connection == null ||
                _connection.State != ConnectionState.Open)
            {
                throw new InvalidOperationException(Strings.FormatCallRequiresOpenConnection("ExecuteReader"));
            }
            if (string.IsNullOrWhiteSpace(_commandText))
            {
                throw new InvalidOperationException(Strings.FormatCallRequiresSetCommandText("ExecuteReader"));
            }

            ValidateTransaction();
            Prepare();
            Bind();

            var changes       = 0;
            var resultHandles = new List <StatementHandle>();

            foreach (var handle in _handles)
            {
                var hasResults = NativeMethods.sqlite3_stmt_readonly(handle) != 0;

                var rc = NativeMethods.sqlite3_step(handle);
                if (rc == Constants.SQLITE_ROW ||
                    (rc == Constants.SQLITE_DONE && hasResults))
                {
                    resultHandles.Add(handle);

                    continue;
                }

                rc = NativeMethods.sqlite3_reset(handle);
                MarshalEx.ThrowExceptionForRC(rc);

                changes += NativeMethods.sqlite3_changes(_connection.Handle);
            }

            return(OpenReader = new SQLiteDataReader(this, resultHandles, changes));
        }
コード例 #8
0
        private void Bind()
        {
            Debug.Assert(_prepared, "_prepared is false.");
            Debug.Assert(_handles != null, "_handles is null.");
            Debug.Assert(OpenReader == null, "ActiveReader is not null.");
            if (_parameters == null ||
                _parameters.Bound)
            {
                return;
            }

            foreach (var handle in _handles)
            {
                var rc = NativeMethods.sqlite3_clear_bindings(handle);
                MarshalEx.ThrowExceptionForRC(rc);
            }

            _parameters.Bind(_handles);
        }
コード例 #9
0
        public override void Prepare()
        {
            if (OpenReader != null)
            {
                throw new InvalidOperationException(Strings.OpenReaderExists);
            }
            if (_connection == null ||
                _connection.State != ConnectionState.Open)
            {
                throw new InvalidOperationException(Strings.FormatCallRequiresOpenConnection("Prepare"));
            }
            if (string.IsNullOrWhiteSpace(_commandText))
            {
                throw new InvalidOperationException(Strings.FormatCallRequiresSetCommandText("Prepare"));
            }
            if (_prepared)
            {
                return;
            }

            Debug.Assert(_connection.Handle != null && !_connection.Handle.IsInvalid, "_connection.Handle is null.");
            Debug.Assert(_handles == null, "_handles is not null.");

            var handles      = new List <StatementHandle>();
            var remainingSql = _commandText;

            do
            {
                StatementHandle handle;
                var             rc = NativeMethods.sqlite3_prepare_v2(
                    _connection.Handle,
                    remainingSql,
                    out handle,
                    out remainingSql);
                MarshalEx.ThrowExceptionForRC(rc);

                handles.Add(handle);
            }while (!string.IsNullOrWhiteSpace(remainingSql));

            _handles  = handles;
            _prepared = true;
        }
コード例 #10
0
        public override void Open()
        {
            if (_state == ConnectionState.Open)
            {
                return;
            }
            if (ConnectionString == null)
            {
                throw new InvalidOperationException(Strings.OpenRequiresSetConnectionString);
            }

            var flags = Constants.SQLITE_OPEN_READWRITE | Constants.SQLITE_OPEN_CREATE;

            flags |= (ConnectionStringBuilder.CacheMode == CacheMode.Shared) ? Constants.SQLITE_OPEN_SHAREDCACHE : Constants.SQLITE_OPEN_PRIVATECACHE;

            var rc = NativeMethods.sqlite3_open_v2(ConnectionStringBuilder.DataSource, out _db, flags, vfs: null);

            MarshalEx.ThrowExceptionForRC(rc, _db);
            SetState(ConnectionState.Open);
        }
コード例 #11
0
        public override int ExecuteNonQuery()
        {
            if (OpenReader != null)
            {
                throw new InvalidOperationException(Strings.OpenReaderExists);
            }
            if (_connection == null ||
                _connection.State != ConnectionState.Open)
            {
                throw new InvalidOperationException(Strings.FormatCallRequiresOpenConnection("ExecuteNonQuery"));
            }
            if (string.IsNullOrWhiteSpace(_commandText))
            {
                throw new InvalidOperationException(Strings.FormatCallRequiresSetCommandText("ExecuteNonQuery"));
            }

            ValidateTransaction();
            Prepare();
            Bind();

            Debug.Assert(_connection.Handle != null && !_connection.Handle.IsInvalid, "_connection.Handle is null.");

            var changes = 0;

            foreach (var handle in _handles)
            {
                var hasChanges = NativeMethods.sqlite3_stmt_readonly(handle) == 0;

                NativeMethods.sqlite3_step(handle);
                var rc = NativeMethods.sqlite3_reset(handle);
                MarshalEx.ThrowExceptionForRC(rc);

                if (hasChanges)
                {
                    changes += NativeMethods.sqlite3_changes(_connection.Handle);
                }
            }

            return(changes);
        }
コード例 #12
0
        public override bool Read()
        {
            if (_closed)
            {
                throw new InvalidOperationException(Strings.FormatDataReaderClosed("Read"));
            }

            if (!_stepped)
            {
                _stepped = true;

                return(_hasRows);
            }

            var rc = NativeMethods.sqlite3_step(_stmt);

            MarshalEx.ThrowExceptionForRC(rc, _db);

            _done = rc == SQLITE_DONE;

            return(!_done);
        }
コード例 #13
0
        public override void Open()
        {
            if (State == ConnectionState.Open)
            {
                return;
            }
            if (ConnectionString == null)
            {
                throw new InvalidOperationException(Strings.OpenRequiresSetConnectionString);
            }

            var filename = ConnectionStringBuilder.DataSource;
            var flags    = 0;

            if (filename.StartsWith("file:", StringComparison.OrdinalIgnoreCase))
            {
                flags |= SQLITE_OPEN_URI;
            }

            switch (ConnectionStringBuilder.Mode)
            {
            case SqliteOpenMode.ReadOnly:
                flags |= SQLITE_OPEN_READONLY;
                break;

            case SqliteOpenMode.ReadWrite:
                flags |= SQLITE_OPEN_READWRITE;
                break;

            case SqliteOpenMode.Memory:
                flags |= SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_MEMORY;
                if ((flags & SQLITE_OPEN_URI) == 0)
                {
                    flags   |= SQLITE_OPEN_URI;
                    filename = "file:" + filename;
                }
                break;

            default:
                Debug.Assert(
                    ConnectionStringBuilder.Mode == SqliteOpenMode.ReadWriteCreate,
                    "ConnectionStringBuilder.Mode is not ReadWriteCreate");
                flags |= SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
                break;
            }

            switch (ConnectionStringBuilder.Cache)
            {
            case SqliteCacheMode.Shared:
                flags |= SQLITE_OPEN_SHAREDCACHE;
                break;

            case SqliteCacheMode.Private:
                flags |= SQLITE_OPEN_PRIVATECACHE;
                break;

            default:
                Debug.Assert(
                    ConnectionStringBuilder.Cache == SqliteCacheMode.Default,
                    "ConnectionStringBuilder.Cache is not Default.");
                break;
            }

            if ((flags & SQLITE_OPEN_URI) == 0 &&
                !filename.Equals(":memory:", StringComparison.OrdinalIgnoreCase) &&
                !Path.IsPathRooted(filename))
            {
                filename = Path.GetFullPath(Path.Combine(BaseDirectory, filename));
            }

            var rc = NativeMethods.sqlite3_open_v2(filename, out _db, flags, vfs: null);

            MarshalEx.ThrowExceptionForRC(rc, _db);

            SetState(ConnectionState.Open);
        }
コード例 #14
0
        public new virtual SqliteDataReader ExecuteReader(CommandBehavior behavior)
        {
            if ((behavior & ~(CommandBehavior.Default | CommandBehavior.SequentialAccess | CommandBehavior.SingleResult | CommandBehavior.SingleRow | CommandBehavior.CloseConnection)) != 0)
            {
                throw new ArgumentException(Strings.FormatInvalidCommandBehavior(behavior));
            }

            if (Connection == null ||
                Connection.State != ConnectionState.Open)
            {
                throw new InvalidOperationException(Strings.FormatCallRequiresOpenConnection("ExecuteReader"));
            }

            if (string.IsNullOrEmpty(CommandText))
            {
                throw new InvalidOperationException(Strings.FormatCallRequiresSetCommandText("ExecuteReader"));
            }

            if (Transaction != Connection.Transaction)
            {
                throw new InvalidOperationException(
                          Transaction == null
                        ? Strings.TransactionRequired
                        : Strings.TransactionConnectionMismatch);
            }

            /*
             * This is not a guarantee. SQLITE_BUSY can still be thrown before the command timeout.
             * This sets a timeout handler but this can be cleared by concurrent commands.
             */
            NativeMethods.sqlite3_busy_timeout(Connection.DbHandle, CommandTimeout * 1000);

            var hasChanges = false;
            var changes    = 0;
            var stmts      = new Queue <Tuple <Sqlite3StmtHandle, bool> >();
            var tail       = CommandText;

            do
            {
                Sqlite3StmtHandle stmt;
                var rc = NativeMethods.sqlite3_prepare_v2(
                    Connection.DbHandle,
                    tail,
                    out stmt,
                    out tail);
                MarshalEx.ThrowExceptionForRC(rc, Connection.DbHandle);

                // Statement was empty, white space, or a comment
                if (stmt.IsInvalid)
                {
                    if (!string.IsNullOrEmpty(tail))
                    {
                        continue;
                    }

                    break;
                }

                var boundParams = 0;

                if (_parameters.IsValueCreated)
                {
                    boundParams = _parameters.Value.Bind(stmt);
                }

                var expectedParams = NativeMethods.sqlite3_bind_parameter_count(stmt);
                if (expectedParams != boundParams)
                {
                    var unboundParams = new List <string>();
                    for (var i = 1; i <= expectedParams; i++)
                    {
                        var name = NativeMethods.sqlite3_bind_parameter_name(stmt, i);

                        if (_parameters.IsValueCreated
                            ||
                            !_parameters.Value.Cast <SqliteParameter>().Any(p => p.ParameterName == name))
                        {
                            unboundParams.Add(name);
                        }
                    }
                    throw new InvalidOperationException(Strings.FormatMissingParameters(string.Join(", ", unboundParams)));
                }

                try
                {
                    var timer = Stopwatch.StartNew();
                    while (SQLITE_LOCKED == (rc = NativeMethods.sqlite3_step(stmt)) || rc == SQLITE_BUSY)
                    {
                        if (timer.ElapsedMilliseconds >= CommandTimeout * 1000)
                        {
                            break;
                        }

                        NativeMethods.sqlite3_reset(stmt);
                    }

                    MarshalEx.ThrowExceptionForRC(rc, Connection.DbHandle);
                }
                catch
                {
                    stmt.Dispose();
                    throw;
                }

                // NB: This is only a heuristic to separate SELECT statements from INSERT/UPDATE/DELETE statements. It will result
                //     in unexpected corner cases, but it's the best we can do without re-parsing SQL
                if (NativeMethods.sqlite3_stmt_readonly(stmt) != 0)
                {
                    stmts.Enqueue(Tuple.Create(stmt, rc != SQLITE_DONE));
                }
                else
                {
                    hasChanges = true;
                    changes   += NativeMethods.sqlite3_changes(Connection.DbHandle);
                    stmt.Dispose();
                }
            }while (!string.IsNullOrEmpty(tail));

            var closeConnection = (behavior & CommandBehavior.CloseConnection) != 0;

            return(new SqliteDataReader(Connection, stmts, hasChanges ? changes : -1, closeConnection));
        }
コード例 #15
0
        public new virtual SqliteDataReader ExecuteReader(CommandBehavior behavior)
        {
            if (behavior != CommandBehavior.Default)
            {
                throw new ArgumentException(Strings.FormatInvalidCommandBehavior(behavior));
            }
            if (Connection == null ||
                Connection.State != ConnectionState.Open)
            {
                throw new InvalidOperationException(Strings.FormatCallRequiresOpenConnection("ExecuteReader"));
            }
            if (string.IsNullOrEmpty(CommandText))
            {
                throw new InvalidOperationException(Strings.FormatCallRequiresSetCommandText("ExecuteReader"));
            }
            if (Transaction != Connection.Transaction)
            {
                throw new InvalidOperationException(
                          Transaction == null
                        ? Strings.TransactionRequired
                        : Strings.TransactionConnectionMismatch);
            }

            //TODO not necessary to call every time a command is executed. Only on first command or when timeout changes
            NativeMethods.sqlite3_busy_timeout(Connection.DbHandle, CommandTimeout * 1000);

            var hasChanges = false;
            var changes    = 0;
            var stmts      = new Queue <Tuple <Sqlite3StmtHandle, bool> >();
            var tail       = CommandText;

            do
            {
                Sqlite3StmtHandle stmt;
                var rc = NativeMethods.sqlite3_prepare_v2(
                    Connection.DbHandle,
                    tail,
                    out stmt,
                    out tail);
                MarshalEx.ThrowExceptionForRC(rc, Connection.DbHandle);

                // Statement was empty, white space, or a comment
                if (stmt.IsInvalid)
                {
                    if (!string.IsNullOrEmpty(tail))
                    {
                        continue;
                    }

                    break;
                }

                var boundParams = 0;

                if (_parameters.IsValueCreated)
                {
                    boundParams = _parameters.Value.Bind(stmt);
                }

                var expectedParams = NativeMethods.sqlite3_bind_parameter_count(stmt);
                if (expectedParams != boundParams)
                {
                    var unboundParams = new List <string>();
                    for (var i = 1; i <= expectedParams; i++)
                    {
                        var name = NativeMethods.sqlite3_bind_parameter_name(stmt, i);

                        if (_parameters.IsValueCreated
                            ||
                            !_parameters.Value.Cast <SqliteParameter>().Any(p => p.ParameterName == name))
                        {
                            unboundParams.Add(name);
                        }
                    }
                    throw new InvalidOperationException(Strings.FormatMissingParameters(string.Join(", ", unboundParams)));
                }

                try
                {
                    rc = NativeMethods.sqlite3_step_blocking(Connection.DbHandle, stmt, CommandTimeout * 1000);
                    MarshalEx.ThrowExceptionForRC(rc, Connection.DbHandle);
                }
                catch
                {
                    stmt.Dispose();
                    throw;
                }

                // NB: This is only a heuristic to separate SELECT statements from INSERT/UPDATE/DELETE statements. It will result
                //     in unexpected corner cases, but it's the best we can do without re-parsing SQL
                if (NativeMethods.sqlite3_stmt_readonly(stmt) != 0)
                {
                    stmts.Enqueue(Tuple.Create(stmt, rc != SQLITE_DONE));
                }
                else
                {
                    hasChanges = true;
                    changes   += NativeMethods.sqlite3_changes(Connection.DbHandle);
                    stmt.Dispose();
                }
            }while (!string.IsNullOrEmpty(tail));

            return(new SqliteDataReader(Connection.DbHandle, stmts, hasChanges ? changes : -1));
        }