<commit_before>﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using SqlNotebookScript.Core.SqliteInterop;
using static SqlNotebookScript.Core.SqliteInterop.NativeMethods;

namespace SqlNotebookScript.Core;

public sealed class PreparedStatement : IDisposable {
    private delegate void FreeDelegate(IntPtr p);
    private static readonly Lazy<(IntPtr Ptr, FreeDelegate Delegate)> _freeFunc = new(() => {
        FreeDelegate @delegate = Marshal.FreeHGlobal;
        return (Marshal.GetFunctionPointerForDelegate(@delegate), @delegate);
    });

    private readonly IntPtr _sqlite; // sqlite3* - do not dispose
    private readonly IntPtr _stmt; // sqlite3_stmt* - dispose
    private readonly int _paramCount;
    private readonly List<string> _paramNames;
    private bool _disposedValue;

    public PreparedStatement(IntPtr sqlite, string sql) {
        _sqlite = sqlite;
        using NativeString sqlNative = new(sql);
        using NativeBuffer stmtNative = new(IntPtr.Size);
        SqliteUtil.ThrowIfError(sqlite,
            sqlite3_prepare_v2(sqlite, sqlNative.Ptr, sqlNative.ByteCount, stmtNative.Ptr, IntPtr.Zero));
        _stmt = Marshal.ReadIntPtr(stmtNative.Ptr);
        if (_stmt == IntPtr.Zero) {
            throw new Exception("Invalid statement.");
        }
        _paramCount = sqlite3_bind_parameter_count(_stmt);
        _paramNames = new(_paramCount);
        for (var i = 1; i <= _paramCount; i++) {
            var paramNameNative = sqlite3_bind_parameter_name(_stmt, i);
            if (paramNameNative == IntPtr.Zero) {
                _paramNames.Add(null);
            } else {
                _paramNames.Add(Marshal.PtrToStringUTF8(paramNameNative));
            }
        }
    }

    private void Dispose(bool disposing) {
        if (!_disposedValue) {
            if (disposing) {
                // dispose managed state (managed objects)
            }

            // free unmanaged resources (unmanaged objects) and override finalizer
            // set large fields to null
            if (_stmt != IntPtr.Zero) {
                _ = sqlite3_finalize(_stmt);
            }
            _disposedValue = true;
        }
    }

    // override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources
    ~PreparedStatement() {
        // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
        Dispose(disposing: false);
    }

    public void Dispose() {
        // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
        Dispose(disposing: true);
        GC.SuppressFinalize(this);
    }

    public object[] GetArgs(IReadOnlyDictionary<string, object> dict) {
        var args = new object[_paramCount];
        var provided = new bool[_paramCount];
        foreach (var pair in dict) {
            var found = false;
            for (var paramIndex = 0; paramIndex < _paramCount; paramIndex++) {
                if (pair.Key.Equals(_paramNames[paramIndex], StringComparison.OrdinalIgnoreCase)) {
                    args[paramIndex] = pair.Value;
                    provided[paramIndex] = true;
                    found = true;
                    break;
                }
            }
            if (!found) {
                throw new Exception($"Argument \"{pair.Key}\" was provided, but there is no parameter by that name.");
            }
        }
        for (var i = 0; i < _paramCount; i++) {
            if (!provided[i]) {
                throw new Exception($"Missing value for parameter \"{_paramNames[i]}\".");
            }
        }
        return args;
    }

    public SimpleDataTable Execute(object[] args, bool returnResult, int maxRows, CancellationToken cancel) {
        Debug.Assert(maxRows != 0); // -1 is unrestricted, not zero
        if (args.Length != _paramCount) {
            throw new ArgumentOutOfRangeException($"Expected {_paramCount} argument(s), but {args.Length} were provided.");
        }
        BindArguments(args);
        ExecuteStatement(returnResult, out var columnNames, out var columnCount);
        ReadResultsIntoList(returnResult, maxRows, columnCount, out var rows, out var fullCount, cancel);
        return returnResult ? new SimpleDataTable(columnNames, rows, fullCount) : null;
    }

    public void ExecuteStream(object[] args, Action<List<string>> onHeader, Action<object[]> onRow, CancellationToken cancel) {
        if (args.Length != _paramCount) {
            throw new ArgumentOutOfRangeException($"Expected {_paramCount} argument(s), but {args.Length} were provided.");
        }
        BindArguments(args);
        ExecuteStatement(true, out var columnNames, out var columnCount);
        onHeader(columnNames);
        ReadResultsStream(columnCount, onRow, cancel);
    }
<commit_msg>Fix CSV import error<commit_after>﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using SqlNotebookScript.Core.SqliteInterop;
using static SqlNotebookScript.Core.SqliteInterop.NativeMethods;

namespace SqlNotebookScript.Core;

public sealed class PreparedStatement : IDisposable {
    private delegate void FreeDelegate(IntPtr p);
    private static readonly Lazy<(IntPtr Ptr, FreeDelegate Delegate)> _freeFunc = new(() => {
        FreeDelegate @delegate = Marshal.FreeHGlobal;
        return (Marshal.GetFunctionPointerForDelegate(@delegate), @delegate);
    });

    private readonly IntPtr _sqlite; // sqlite3* - do not dispose
    private readonly IntPtr _stmt; // sqlite3_stmt* - dispose
    private readonly int _paramCount;
    private readonly List<string> _paramNames;
    private bool _disposedValue;

    public PreparedStatement(IntPtr sqlite, string sql) {
        _sqlite = sqlite;
        using NativeString sqlNative = new(sql);
        using NativeBuffer stmtNative = new(IntPtr.Size);
        SqliteUtil.ThrowIfError(sqlite,
            sqlite3_prepare_v2(sqlite, sqlNative.Ptr, sqlNative.ByteCount, stmtNative.Ptr, IntPtr.Zero));
        _stmt = Marshal.ReadIntPtr(stmtNative.Ptr);
        if (_stmt == IntPtr.Zero) {
            throw new Exception("Invalid statement.");
        }
        _paramCount = sqlite3_bind_parameter_count(_stmt);
        _paramNames = new(_paramCount);
        for (var i = 1; i <= _paramCount; i++) {
            var paramNameNative = sqlite3_bind_parameter_name(_stmt, i);
            if (paramNameNative == IntPtr.Zero) {
                _paramNames.Add(null);
            } else {
                _paramNames.Add(Marshal.PtrToStringUTF8(paramNameNative));
            }
        }
    }

    private void Dispose(bool disposing) {
        if (!_disposedValue) {
            if (disposing) {
                // dispose managed state (managed objects)
            }

            // free unmanaged resources (unmanaged objects) and override finalizer
            // set large fields to null
            if (_stmt != IntPtr.Zero) {
                _ = sqlite3_finalize(_stmt);
            }
            _disposedValue = true;
        }
    }

    // override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources
    ~PreparedStatement() {
        // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
        Dispose(disposing: false);
    }

    public void Dispose() {
        // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
        Dispose(disposing: true);
        GC.SuppressFinalize(this);
    }

    public object[] GetArgs(IReadOnlyDictionary<string, object> dict) {
        var args = new object[_paramCount];
        var provided = new bool[_paramCount];
        foreach (var pair in dict) {
            for (var paramIndex = 0; paramIndex < _paramCount; paramIndex++) {
                if (pair.Key.Equals(_paramNames[paramIndex], StringComparison.OrdinalIgnoreCase)) {
                    args[paramIndex] = pair.Value;
                    provided[paramIndex] = true;
                    break;
                }
            }
        }
        for (var i = 0; i < _paramCount; i++) {
            if (!provided[i]) {
                throw new Exception($"Missing value for parameter \"{_paramNames[i]}\".");
            }
        }
        return args;
    }

    public SimpleDataTable Execute(object[] args, bool returnResult, int maxRows, CancellationToken cancel) {
        Debug.Assert(maxRows != 0); // -1 is unrestricted, not zero
        if (args.Length != _paramCount) {
            throw new ArgumentOutOfRangeException($"Expected {_paramCount} argument(s), but {args.Length} were provided.");
        }
        BindArguments(args);
        ExecuteStatement(returnResult, out var columnNames, out var columnCount);
        ReadResultsIntoList(returnResult, maxRows, columnCount, out var rows, out var fullCount, cancel);
        return returnResult ? new SimpleDataTable(columnNames, rows, fullCount) : null;
    }

    public void ExecuteStream(object[] args, Action<List<string>> onHeader, Action<object[]> onRow, CancellationToken cancel) {
        if (args.Length != _paramCount) {
            throw new ArgumentOutOfRangeException($"Expected {_paramCount} argument(s), but {args.Length} were provided.");
        }
        BindArguments(args);
        ExecuteStatement(true, out var columnNames, out var columnCount);
        onHeader(columnNames);
        ReadResultsStream(columnCount, onRow, cancel);
    }

{'fraction_non_alphanumeric': 0.09644087256027555, 'fraction_numerical': 0.0027137042062415195, 'mean_word_length': 2.503473491773309, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 17, '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': '6712273', 'n_tokens_mistral': 2589, 'n_tokens_neox': 2515, 'n_words': 696}