<commit_before>part of 'implementation.dart';

class PreparedStatementImpl implements PreparedStatement {
  @override
  final String sql;
  final Pointer<sqlite3_stmt> _stmt;
  final DatabaseImpl _db;

  bool _closed = false;
  _ActiveCursorIterator? _currentCursor;

  bool _variablesBound = false;
  final List<Pointer> _allocatedWhileBinding = [];

  Bindings get _bindings => _db._bindings;

  PreparedStatementImpl(this.sql, this._stmt, this._db);

  @override
  int get parameterCount {
    return _bindings.sqlite3_bind_parameter_count(_stmt);
  }

  @override
  Pointer<void> get handle => _stmt;

  @override
  void execute([List<Object?> parameters = const <Object>[]]) {
    _ensureNotFinalized();
    _ensureMatchingParameters(parameters);

    _reset();
    _bindParams(parameters);

    _execute();
  }

  @override
  void executeMap([Map<String, Object?> parameters = const {}]) {
    _ensureNotFinalized();
    _ensureMatchingMapParameters(parameters);

    _reset();
    _bindMapParams(parameters);

    _execute();
  }

  void _execute() {
    int result;

    // Users should be able to execute statements returning rows, so we should
    // call _step() to skip past rows.
    do {
      result = _step();
    } while (result == SqlError.SQLITE_ROW);

    if (result != SqlError.SQLITE_OK && result != SqlError.SQLITE_DONE) {
      throwException(_db, result, sql);
    }
  }

  List<String> get _columnNames {
    final columnCount = _bindings.sqlite3_column_count(_stmt);

    return [
      for (var i = 0; i < columnCount; i++)
        // name pointer doesn't need to be disposed, that happens when we
        // finalize
        _bindings.sqlite3_column_name(_stmt, i).readString()
    ];
  }

  List<String?>? get _tableNames {
    final nameFunction = _db._library.columnNameFunction;
    if (nameFunction == null) {
      // unsupported
      return null;
    }
    final columnCount = _bindings.sqlite3_column_count(_stmt);
    return List.generate(columnCount, (i) {
      final pointer = nameFunction(_stmt, i);
      return pointer.isNullPointer ? null : pointer.readString();
    });
  }

  @override
  ResultSet select([List<Object?> parameters = const <Object?>[]]) {
    _ensureNotFinalized();
    _ensureMatchingParameters(parameters);

    _reset();
    _bindParams(parameters);

    return _selectResults();
  }

  @override
  ResultSet selectMap([Map<String, Object?> parameters = const {}]) {
    _ensureNotFinalized();
    _ensureMatchingMapParameters(parameters);

    _reset();
    _bindMapParams(parameters);

    return _selectResults();
  }

  ResultSet _selectResults() {
    final names = _columnNames;
    final tableNames = _tableNames;
    final columnCount = names.length;
    final rows = <List<Object?>>[];

    int resultCode;
    while ((resultCode = _step()) == SqlError.SQLITE_ROW) {
      rows.add(<Object?>[for (var i = 0; i < columnCount; i++) _readValue(i)]);
    }

    if (resultCode != SqlError.SQLITE_OK &&
        resultCode != SqlError.SQLITE_DONE) {
      throwException(_db, resultCode, sql);
    }

    return ResultSet(names, tableNames, rows);
  }

  @override
  IteratingCursor selectCursor([List<Object?> parameters = const <Object?>[]]) {
    _ensureNotFinalized();
    _ensureMatchingParameters(parameters);

    _reset();
    _bindParams(parameters);

    final names = _columnNames;
    final tableNames = _tableNames;
    return _currentCursor = _ActiveCursorIterator(this, names, tableNames);
  }

  @override
  void dispose() {
    if (!_closed) {
      _closed = true;

      _reset();
      _bindings.sqlite3_finalize(_stmt);
      _db._handleFinalized(this);
    }
  }

  void _reset() {
    if (_variablesBound) {
      _bindings.sqlite3_reset(_stmt);
      _variablesBound = false;
    }

    for (final pointer in _allocatedWhileBinding) {
      pointer.free();
    }
    _allocatedWhileBinding.clear();
    _currentCursor = null;
  }

  void _bindParams(List<Object?>? params) {
    if (params == null || params.isEmpty) return;

    // variables in sqlite are 1-indexed
    for (var i = 1; i <= params.length; i++) {
      final Object? param = params[i - 1];

      _bindParam(param, i);
    }

    _variablesBound = true;
  }

  void _bindMapParams(Map<String, Object?>? params) {
    if (params == null || params.isEmpty) return;

    // _bindings.sqlite3_clear_bindings(_stmt);

    // variables in sqlite are 1-indexed
    for (var key in params.keys) {
      final Object? param = params[key];

      final keyBytes = utf8.encode(key);
      final keyPtr = allocateBytes(keyBytes, additionalLength: 1);
      _allocatedWhileBinding.add(keyPtr);
      final i = _bindings.sqlite3_bind_parameter_index(_stmt, keyPtr.cast());
      if (i == 0) {
        throw ArgumentError.value(
            params, 'params', 'Key $key was not found in statement.');
      }
      _bindParam(param, i);
    }

    _variablesBound = true;
  }

  void _bindParam(Object? param, int i) {
    if (param == null) {
      _bindings.sqlite3_bind_null(_stmt, i);
    } else if (param is int) {
      _bindings.sqlite3_bind_int64(_stmt, i, param);
    } else if (param is bool) {
      _bindings.sqlite3_bind_int64(_stmt, i, param ? 1 : 0);
    } else if (param is double) {
      _bindings.sqlite3_bind_double(_stmt, i, param.toDouble());
    } else if (param is String) {
      final bytes = utf8.encode(param);<commit_msg>make sure last byte is null<commit_after>part of 'implementation.dart';

class PreparedStatementImpl implements PreparedStatement {
  @override
  final String sql;
  final Pointer<sqlite3_stmt> _stmt;
  final DatabaseImpl _db;

  bool _closed = false;
  _ActiveCursorIterator? _currentCursor;

  bool _variablesBound = false;
  final List<Pointer> _allocatedWhileBinding = [];

  Bindings get _bindings => _db._bindings;

  PreparedStatementImpl(this.sql, this._stmt, this._db);

  @override
  int get parameterCount {
    return _bindings.sqlite3_bind_parameter_count(_stmt);
  }

  @override
  Pointer<void> get handle => _stmt;

  @override
  void execute([List<Object?> parameters = const <Object>[]]) {
    _ensureNotFinalized();
    _ensureMatchingParameters(parameters);

    _reset();
    _bindParams(parameters);

    _execute();
  }

  @override
  void executeMap([Map<String, Object?> parameters = const {}]) {
    _ensureNotFinalized();
    _ensureMatchingMapParameters(parameters);

    _reset();
    _bindMapParams(parameters);

    _execute();
  }

  void _execute() {
    int result;

    // Users should be able to execute statements returning rows, so we should
    // call _step() to skip past rows.
    do {
      result = _step();
    } while (result == SqlError.SQLITE_ROW);

    if (result != SqlError.SQLITE_OK && result != SqlError.SQLITE_DONE) {
      throwException(_db, result, sql);
    }
  }

  List<String> get _columnNames {
    final columnCount = _bindings.sqlite3_column_count(_stmt);

    return [
      for (var i = 0; i < columnCount; i++)
        // name pointer doesn't need to be disposed, that happens when we
        // finalize
        _bindings.sqlite3_column_name(_stmt, i).readString()
    ];
  }

  List<String?>? get _tableNames {
    final nameFunction = _db._library.columnNameFunction;
    if (nameFunction == null) {
      // unsupported
      return null;
    }
    final columnCount = _bindings.sqlite3_column_count(_stmt);
    return List.generate(columnCount, (i) {
      final pointer = nameFunction(_stmt, i);
      return pointer.isNullPointer ? null : pointer.readString();
    });
  }

  @override
  ResultSet select([List<Object?> parameters = const <Object?>[]]) {
    _ensureNotFinalized();
    _ensureMatchingParameters(parameters);

    _reset();
    _bindParams(parameters);

    return _selectResults();
  }

  @override
  ResultSet selectMap([Map<String, Object?> parameters = const {}]) {
    _ensureNotFinalized();
    _ensureMatchingMapParameters(parameters);

    _reset();
    _bindMapParams(parameters);

    return _selectResults();
  }

  ResultSet _selectResults() {
    final names = _columnNames;
    final tableNames = _tableNames;
    final columnCount = names.length;
    final rows = <List<Object?>>[];

    int resultCode;
    while ((resultCode = _step()) == SqlError.SQLITE_ROW) {
      rows.add(<Object?>[for (var i = 0; i < columnCount; i++) _readValue(i)]);
    }

    if (resultCode != SqlError.SQLITE_OK &&
        resultCode != SqlError.SQLITE_DONE) {
      throwException(_db, resultCode, sql);
    }

    return ResultSet(names, tableNames, rows);
  }

  @override
  IteratingCursor selectCursor([List<Object?> parameters = const <Object?>[]]) {
    _ensureNotFinalized();
    _ensureMatchingParameters(parameters);

    _reset();
    _bindParams(parameters);

    final names = _columnNames;
    final tableNames = _tableNames;
    return _currentCursor = _ActiveCursorIterator(this, names, tableNames);
  }

  @override
  void dispose() {
    if (!_closed) {
      _closed = true;

      _reset();
      _bindings.sqlite3_finalize(_stmt);
      _db._handleFinalized(this);
    }
  }

  void _reset() {
    if (_variablesBound) {
      _bindings.sqlite3_reset(_stmt);
      _variablesBound = false;
    }

    for (final pointer in _allocatedWhileBinding) {
      pointer.free();
    }
    _allocatedWhileBinding.clear();
    _currentCursor = null;
  }

  void _bindParams(List<Object?>? params) {
    if (params == null || params.isEmpty) return;

    // variables in sqlite are 1-indexed
    for (var i = 1; i <= params.length; i++) {
      final Object? param = params[i - 1];

      _bindParam(param, i);
    }

    _variablesBound = true;
  }

  void _bindMapParams(Map<String, Object?>? params) {
    if (params == null || params.isEmpty) return;

    // _bindings.sqlite3_clear_bindings(_stmt);

    // variables in sqlite are 1-indexed
    for (var key in params.keys) {
      final Object? param = params[key];

      final keyBytes = utf8.encode(key);
      final keyPtr = allocateBytes(keyBytes, additionalLength: 1);
      _allocatedWhileBinding.add(keyPtr);
      keyPtr[keyBytes.length] = 0;
      final i = _bindings.sqlite3_bind_parameter_index(_stmt, keyPtr.cast());
      if (i == 0) {
        throw ArgumentError.value(
            params, 'params', 'Key $key was not found in statement.');
      }
      _bindParam(param, i);
    }

    _variablesBound = true;
  }

  void _bindParam(Object? param, int i) {
    if (param == null) {
      _bindings.sqlite3_bind_null(_stmt, i);
    } else if (param is int) {
      _bindings.sqlite3_bind_int64(_stmt, i, param);
    } else if (param is bool) {
      _bindings.sqlite3_bind_int64(_stmt, i, param ? 1 : 0);
    } else if (param is double) {
      _bindings.sqlite3_bind_double(_stmt, i, param.toDouble());
    } else if (param is String) {
      final bytes = utf8.encode(param);
{'fraction_non_alphanumeric': 0.11836885321524127, 'fraction_numerical': 0.005443306578097611, 'mean_word_length': 3.17887432536623, 'pattern_counts': {'":': 0, '<': 45, '<?xml version=': 0, '>': 43, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1063113', 'n_tokens_mistral': 3458, 'n_tokens_neox': 3328, 'n_words': 800}