<commit_before>require 'active_record_views/version'
require 'active_record_views/railtie' if defined? Rails
require 'active_record_views/registered_view'
require 'active_record_views/checksum_cache'
require 'active_support/core_ext/module/attribute_accessors'
require 'digest/sha1'

module ActiveRecordViews
  mattr_accessor :sql_load_path
  self.sql_load_path = []

  mattr_accessor :registered_views
  self.registered_views = []

  def self.init!
    require 'active_record_views/extension'
    ::ActiveRecord::Base.send :include, ActiveRecordViews::Extension
  end

  def self.find_sql_file(name)
    self.sql_load_path.each do |dir|
      path = "#{dir}/#{name}.sql"
      return path if File.exist?(path)
      path = path + '.erb'
      return path if File.exist?(path)
    end
    raise "could not find #{name}.sql"
  end

  def self.without_transaction(connection)
    in_transaction = if connection.respond_to? :transaction_open?
      connection.transaction_open?
    else
      !connection.outside_transaction?
    end

    begin
      recursing = Thread.current[:active_record_views_without_transaction]
      Thread.current[:active_record_views_without_transaction] = true

      if in_transaction && !recursing
        begin
          temp_connection = connection.pool.checkout
          yield temp_connection
        ensure
          connection.pool.checkin temp_connection
        end
      else
        yield connection
      end
    ensure
      Thread.current[:active_record_views_without_transaction] = nil
    end
  end

  def self.create_view(base_connection, name, class_name, sql, options = {})
    options.assert_valid_keys :materialized, :unique_columns

    without_transaction base_connection do |connection|
      cache = ActiveRecordViews::ChecksumCache.new(connection)
      data = {class_name: class_name, checksum: Digest::SHA1.hexdigest(sql), options: options}
      return if cache.get(name) == data

      drop_and_create = if options[:materialized]
        true
      else
        raise ArgumentError, 'unique_columns option requires view to be materialized' if options[:unique_columns]
        begin
          connection.execute "CREATE OR REPLACE VIEW #{connection.quote_table_name name} AS #{sql}"
          false
        rescue ActiveRecord::StatementInvalid
          true
        end
      end

      if drop_and_create
        connection.transaction :requires_new => true do
          without_dependencies connection, name do
            execute_drop_view connection, name
            execute_create_view connection, name, sql, options
          end
        end
      end

      cache.set name, data
    end
  end

  def self.drop_view(base_connection, name)
    without_transaction base_connection do |connection|
      cache = ActiveRecordViews::ChecksumCache.new(connection)
      execute_drop_view connection, name
      cache.set name, nil
    end
  end

  def self.execute_create_view(connection, name, sql, options)
    options.assert_valid_keys :materialized, :unique_columns
    sql = sql.sub(/;\s*/, '')

    if options[:materialized]
      connection.execute "CREATE MATERIALIZED VIEW #{connection.quote_table_name name} AS #{sql} WITH NO DATA;"
    else
      connection.execute "CREATE VIEW #{connection.quote_table_name name} AS #{sql};"
    end

    if options[:unique_columns]
      connection.execute <<-SQL.squish
        CREATE UNIQUE INDEX #{connection.quote_table_name "#{name}_pkey"}
        ON #{connection.quote_table_name name}(
          #{options[:unique_columns].map { |column_name| connection.quote_table_name(column_name) }.join(', ')}
        );
      SQL
    end
  end

  def self.execute_drop_view(connection, name)
    if materialized_view?(connection, name)
      connection.execute "DROP MATERIALIZED VIEW IF EXISTS #{connection.quote_table_name name};"
    else
      connection.execute "DROP VIEW IF EXISTS #{connection.quote_table_name name};"
    end
  end

  def self.view_exists?(connection, name)
    connection.select_value(<<-SQL.squish).present?
      SELECT 1
      FROM information_schema.views
      WHERE table_schema = 'public' AND table_name = #{connection.quote name}<commit_msg>Trailing semicolon stripping regex should be anchored<commit_after>require 'active_record_views/version'
require 'active_record_views/railtie' if defined? Rails
require 'active_record_views/registered_view'
require 'active_record_views/checksum_cache'
require 'active_support/core_ext/module/attribute_accessors'
require 'digest/sha1'

module ActiveRecordViews
  mattr_accessor :sql_load_path
  self.sql_load_path = []

  mattr_accessor :registered_views
  self.registered_views = []

  def self.init!
    require 'active_record_views/extension'
    ::ActiveRecord::Base.send :include, ActiveRecordViews::Extension
  end

  def self.find_sql_file(name)
    self.sql_load_path.each do |dir|
      path = "#{dir}/#{name}.sql"
      return path if File.exist?(path)
      path = path + '.erb'
      return path if File.exist?(path)
    end
    raise "could not find #{name}.sql"
  end

  def self.without_transaction(connection)
    in_transaction = if connection.respond_to? :transaction_open?
      connection.transaction_open?
    else
      !connection.outside_transaction?
    end

    begin
      recursing = Thread.current[:active_record_views_without_transaction]
      Thread.current[:active_record_views_without_transaction] = true

      if in_transaction && !recursing
        begin
          temp_connection = connection.pool.checkout
          yield temp_connection
        ensure
          connection.pool.checkin temp_connection
        end
      else
        yield connection
      end
    ensure
      Thread.current[:active_record_views_without_transaction] = nil
    end
  end

  def self.create_view(base_connection, name, class_name, sql, options = {})
    options.assert_valid_keys :materialized, :unique_columns

    without_transaction base_connection do |connection|
      cache = ActiveRecordViews::ChecksumCache.new(connection)
      data = {class_name: class_name, checksum: Digest::SHA1.hexdigest(sql), options: options}
      return if cache.get(name) == data

      drop_and_create = if options[:materialized]
        true
      else
        raise ArgumentError, 'unique_columns option requires view to be materialized' if options[:unique_columns]
        begin
          connection.execute "CREATE OR REPLACE VIEW #{connection.quote_table_name name} AS #{sql}"
          false
        rescue ActiveRecord::StatementInvalid
          true
        end
      end

      if drop_and_create
        connection.transaction :requires_new => true do
          without_dependencies connection, name do
            execute_drop_view connection, name
            execute_create_view connection, name, sql, options
          end
        end
      end

      cache.set name, data
    end
  end

  def self.drop_view(base_connection, name)
    without_transaction base_connection do |connection|
      cache = ActiveRecordViews::ChecksumCache.new(connection)
      execute_drop_view connection, name
      cache.set name, nil
    end
  end

  def self.execute_create_view(connection, name, sql, options)
    options.assert_valid_keys :materialized, :unique_columns
    sql = sql.sub(/;\s*\z/, '')

    if options[:materialized]
      connection.execute "CREATE MATERIALIZED VIEW #{connection.quote_table_name name} AS #{sql} WITH NO DATA;"
    else
      connection.execute "CREATE VIEW #{connection.quote_table_name name} AS #{sql};"
    end

    if options[:unique_columns]
      connection.execute <<-SQL.squish
        CREATE UNIQUE INDEX #{connection.quote_table_name "#{name}_pkey"}
        ON #{connection.quote_table_name name}(
          #{options[:unique_columns].map { |column_name| connection.quote_table_name(column_name) }.join(', ')}
        );
      SQL
    end
  end

  def self.execute_drop_view(connection, name)
    if materialized_view?(connection, name)
      connection.execute "DROP MATERIALIZED VIEW IF EXISTS #{connection.quote_table_name name};"
    else
      connection.execute "DROP VIEW IF EXISTS #{connection.quote_table_name name};"
    end
  end

  def self.view_exists?(connection, name)
    connection.select_value(<<-SQL.squish).present?
      SELECT 1
      FROM information_schema.views
      WHERE table_schema = 'public' AND table_name = #{connection.quote name}
{'fraction_non_alphanumeric': 0.0827775131014769, 'fraction_numerical': 0.0007146260123868509, 'mean_word_length': 3.297338792221085, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 5, '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': '6480951', 'n_tokens_mistral': 2425, 'n_tokens_neox': 2367, 'n_words': 571}