<commit_before>require 'mysql2'
require 'json'

module RubyDocumentDatabase
  class Engine
    DESC_TABLE = '_desc'
    DESC_COLUMN = 'json'

    attr_reader :desc

    def initialize(db)
      @db = db
      desc_read
    end

    def self.connect_mysql(cred)
      db = Mysql2::Client.new(cred)
      self.new(db)
    end

    # ========================================================================

    def desc_read
      if sql_table_exists(DESC_TABLE)
        desc_txt = nil
        @db.query('SELECT `#{DESC_COLUMN}` FROM `#{DESC_TABLE}`;') { |row|
          desc_txt = row[DESC_COLUMN]
        }
        if desc_txt
          @desc = JSON.load(desc_txt)
        else
          @desc = desc_new
          desc_save
        end
      else
        @db.query("CREATE TABLE `#{DESC_TABLE}` (`#{DESC_COLUMN}` MEDIUMTEXT);")
        @desc = desc_new
        desc_save
      end
    end

    def desc_new
      {
        'version' => 1,
        'entities' => {},
      }
    end

    def desc_save
      desc_txt = @db.escape(@desc.to_json)
      @db.query("TRUNCATE TABLE `#{DESC_TABLE}`;")
      @db.query("INSERT INTO `#{DESC_TABLE}` VALUES ('#{desc_txt}');")
    end

    # ========================================================================

    def entity_create(name, schema)
      validate_sql_name(name)

      raise "Duplicate entity #{name.inspect}" if @desc['entities'][name]

      @db.query("CREATE TABLE `#{name}` (#{schema2sql(schema)});")
      @db.query <<-__EOS__
      CREATE TABLE `#{name}_h` (
        hid INT NOT NULL AUTO_INCREMENT,
        _id INT NOT NULL,
        PRIMARY KEY (hid),
        INDEX _id_idx (_id),
        _data MEDIUMTEXT,
        ts DATETIME,
        user_id INT
      );
      __EOS__

      @desc['entities'][name] = schema
      desc_save
    end

    def entities
      @desc['entities'].keys
    end

    def entity_get(name)
      @desc['entities'][name]
    end

    def entity_count(name)
      validate_sql_name(name)

      cnt = nil
      @db.query("SELECT COUNT(*) AS cnt FROM `#{name}`;").each { |row|
        cnt = row['cnt']
      }
      raise "Invalid query result returned from counting rows in #{name.inspect}" if cnt.nil?

      cnt
    end

    # ========================================================================

    def insert(entity, data, user = nil)
      schema = @desc['entities'][entity]
      raise "Unknown entity #{entity.inspect}" if schema.nil?

      cols = parse_data_with_schema(data, schema)
      col_names = cols.keys.map { |x| "`#{x}`" }.join(',')
      col_vals = cols.values.join(',')

      if user.nil?
        user = 'NULL'
      elsif not user.is_a?(Fixnum)
        raise "Unable to use user ID #{user.inspect}"
      end

      @db.query("INSERT INTO `#{entity}` (#{col_names}) VALUES (#{col_vals});")
      id = @db.last_id
      @db.query("INSERT INTO `#{entity}_h` (_id, _data, ts, user_id) VALUES (#{id}, #{cols['_data']}, NOW(), #{user});")

      id
    end

    def update(entity, id, data, user = nil)
      schema = @desc['entities'][entity]
      raise "Unknown entity #{entity.inspect}" if schema.nil?

      raise "Unable to use ID #{id.inspect}" unless id.is_a?(Fixnum)

      cols = parse_data_with_schema(data, schema)
      cols['_id'] = id
      col_names = cols.keys.map { |x| "`#{x}`" }.join(',')
      col_vals = cols.values.join(',')

      if user.nil?
        user = 'NULL'
      elsif not user.is_a?(Fixednum)
        raise "Unable to use user ID #{user.inspect}"
      end

      @db.query("DELETE FROM `#{entity}` WHERE _id=#{id};")
      @db.query("INSERT INTO `#{entity}` (#{col_names}) VALUES (#{col_vals});")
      @db.query("INSERT INTO `#{entity}_h` (_id, _data, ts, user_id) VALUES (#{id}, #{cols['_data']}, NOW(), #{user});")
    end

    def get(entity, id)
      raise "ID must be integer, but got #{id.inspect}" unless id.is_a?(Fixnum)
      r = nil
      @db.query("SELECT _data FROM `#{entity}` WHERE _id=#{id};").each { |row|<commit_msg>Uniform ID check messages<commit_after>require 'mysql2'
require 'json'

module RubyDocumentDatabase
  class Engine
    DESC_TABLE = '_desc'
    DESC_COLUMN = 'json'

    attr_reader :desc

    def initialize(db)
      @db = db
      desc_read
    end

    def self.connect_mysql(cred)
      db = Mysql2::Client.new(cred)
      self.new(db)
    end

    # ========================================================================

    def desc_read
      if sql_table_exists(DESC_TABLE)
        desc_txt = nil
        @db.query('SELECT `#{DESC_COLUMN}` FROM `#{DESC_TABLE}`;') { |row|
          desc_txt = row[DESC_COLUMN]
        }
        if desc_txt
          @desc = JSON.load(desc_txt)
        else
          @desc = desc_new
          desc_save
        end
      else
        @db.query("CREATE TABLE `#{DESC_TABLE}` (`#{DESC_COLUMN}` MEDIUMTEXT);")
        @desc = desc_new
        desc_save
      end
    end

    def desc_new
      {
        'version' => 1,
        'entities' => {},
      }
    end

    def desc_save
      desc_txt = @db.escape(@desc.to_json)
      @db.query("TRUNCATE TABLE `#{DESC_TABLE}`;")
      @db.query("INSERT INTO `#{DESC_TABLE}` VALUES ('#{desc_txt}');")
    end

    # ========================================================================

    def entity_create(name, schema)
      validate_sql_name(name)

      raise "Duplicate entity #{name.inspect}" if @desc['entities'][name]

      @db.query("CREATE TABLE `#{name}` (#{schema2sql(schema)});")
      @db.query <<-__EOS__
      CREATE TABLE `#{name}_h` (
        hid INT NOT NULL AUTO_INCREMENT,
        _id INT NOT NULL,
        PRIMARY KEY (hid),
        INDEX _id_idx (_id),
        _data MEDIUMTEXT,
        ts DATETIME,
        user_id INT
      );
      __EOS__

      @desc['entities'][name] = schema
      desc_save
    end

    def entities
      @desc['entities'].keys
    end

    def entity_get(name)
      @desc['entities'][name]
    end

    def entity_count(name)
      validate_sql_name(name)

      cnt = nil
      @db.query("SELECT COUNT(*) AS cnt FROM `#{name}`;").each { |row|
        cnt = row['cnt']
      }
      raise "Invalid query result returned from counting rows in #{name.inspect}" if cnt.nil?

      cnt
    end

    # ========================================================================

    def insert(entity, data, user = nil)
      schema = @desc['entities'][entity]
      raise "Unknown entity #{entity.inspect}" if schema.nil?

      cols = parse_data_with_schema(data, schema)
      col_names = cols.keys.map { |x| "`#{x}`" }.join(',')
      col_vals = cols.values.join(',')

      if user.nil?
        user = 'NULL'
      elsif not user.is_a?(Fixnum)
        raise "Unable to use user ID #{user.inspect}"
      end

      @db.query("INSERT INTO `#{entity}` (#{col_names}) VALUES (#{col_vals});")
      id = @db.last_id
      @db.query("INSERT INTO `#{entity}_h` (_id, _data, ts, user_id) VALUES (#{id}, #{cols['_data']}, NOW(), #{user});")

      id
    end

    def update(entity, id, data, user = nil)
      schema = @desc['entities'][entity]
      raise "Unknown entity #{entity.inspect}" if schema.nil?

      raise "ID must be integer, but got #{id.inspect}" unless id.is_a?(Fixnum)

      cols = parse_data_with_schema(data, schema)
      cols['_id'] = id
      col_names = cols.keys.map { |x| "`#{x}`" }.join(',')
      col_vals = cols.values.join(',')

      if user.nil?
        user = 'NULL'
      elsif not user.is_a?(Fixednum)
        raise "Unable to use user ID #{user.inspect}"
      end

      @db.query("DELETE FROM `#{entity}` WHERE _id=#{id};")
      @db.query("INSERT INTO `#{entity}` (#{col_names}) VALUES (#{col_vals});")
      @db.query("INSERT INTO `#{entity}_h` (_id, _data, ts, user_id) VALUES (#{id}, #{cols['_data']}, NOW(), #{user});")
    end

    def get(entity, id)
      raise "ID must be integer, but got #{id.inspect}" unless id.is_a?(Fixnum)
      r = nil
      @db.query("SELECT _data FROM `#{entity}` WHERE _id=#{id};").each { |row|
{'fraction_non_alphanumeric': 0.20065089498059832, 'fraction_numerical': 0.0010013768932281887, 'mean_word_length': 2.5385296722763506, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 1, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '112141', 'n_tokens_mistral': 2683, 'n_tokens_neox': 2645, 'n_words': 632}