Class: ActiveRecord::Tasks::DuckdbDatabaseTasks

Inherits:
Object
  • Object
show all
Defined in:
lib/active_record/tasks/duckdb_database_tasks.rb

Overview

Database tasks implementation for DuckDB adapter

Constant Summary collapse

MEMORY_MODE_KEYS =

Keys that indicate in-memory database mode

[:memory, 'memory', ':memory:', ':memory'].freeze
TRUE_VALUES =
[true, 'true', 'TRUE', 1, '1'].freeze

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(db_config, root_path = ActiveRecord::Tasks::DatabaseTasks.root) ⇒ DuckdbDatabaseTasks

Initializes a new DuckDB database tasks instance

Parameters:

  • db_config (ActiveRecord::DatabaseConfigurations::DatabaseConfig)

    Database configuration

  • root_path (String) (defaults to: ActiveRecord::Tasks::DatabaseTasks.root)

    Root path for the Rails application



23
24
25
26
27
# File 'lib/active_record/tasks/duckdb_database_tasks.rb', line 23

def initialize(db_config, root_path = ActiveRecord::Tasks::DatabaseTasks.root)
  @db_config = db_config
  @configuration_hash = db_config.configuration_hash
  @root_path = root_path
end

Class Method Details

.using_database_configurations?Boolean

Indicates whether this adapter uses database configurations

Returns:

  • (Boolean)

    always returns true



16
17
18
# File 'lib/active_record/tasks/duckdb_database_tasks.rb', line 16

def self.using_database_configurations?
  true
end

Instance Method Details

#charsetString

Returns the character set used by DuckDB

Returns:

  • (String)

    always returns ‘UTF-8’



72
73
74
# File 'lib/active_record/tasks/duckdb_database_tasks.rb', line 72

def charset
  'UTF-8'
end

#collationnil

Returns the collation used by DuckDB

Returns:

  • (nil)

    always returns nil as DuckDB doesn’t use explicit collations



78
79
80
# File 'lib/active_record/tasks/duckdb_database_tasks.rb', line 78

def collation
  nil
end

#createvoid

This method returns an undefined value.

Creates a new DuckDB database file or skips if in-memory mode

Raises:

  • (StandardError)

    if database creation fails



32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/active_record/tasks/duckdb_database_tasks.rb', line 32

def create
  # For DuckDB, creating a database means creating the file (if not in-memory)
  database_path = @configuration_hash[:database]
  return if MEMORY_MODE_KEYS.include?(database_path)

  # Ensure the directory exists
  FileUtils.mkdir_p(File.dirname(database_path)) if database_path.include?('/')

  # Create the database file by opening a connection
  DuckDB::Database.open(database_path).connect.close
rescue StandardError => e
  warn "Couldn't create '#{database_path}' database. Please check your configuration."
  warn "Error: #{e.message}"
  raise
end

#dropvoid

This method returns an undefined value.

Drops the DuckDB database by removing the database file

Raises:

  • (StandardError)

    if database drop fails



51
52
53
54
55
56
57
58
59
60
61
# File 'lib/active_record/tasks/duckdb_database_tasks.rb', line 51

def drop
  db_path = @configuration_hash[:database]
  return if MEMORY_MODE_KEYS.include?(db_path)

  db_file_path = File.absolute_path?(db_path) ? db_path : File.join(root_path, db_path)
  FileUtils.rm_f(db_file_path)
rescue StandardError => e
  warn "Couldn't drop database '#{db_path}'"
  warn "Error: #{e.message}"
  raise
end

#purgevoid

This method returns an undefined value.

Purges the database by dropping and recreating it



65
66
67
68
# File 'lib/active_record/tasks/duckdb_database_tasks.rb', line 65

def purge
  drop
  create
end

#structure_dump(filename, extra_flags = nil) ⇒ void

This method returns an undefined value.

Dumps the database structure to a SQL file

Parameters:

  • filename (String)

    The filename to write the structure dump to

  • extra_flags (String, nil) (defaults to: nil)

    Additional flags for the dump (unused)



86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# File 'lib/active_record/tasks/duckdb_database_tasks.rb', line 86

def structure_dump(filename, extra_flags = nil)
  # Export the database schema
  establish_connection

  File.open(filename, 'w') do |file|
    # Get all tables using DuckDB's information schema
    tables_sql = "SELECT table_name FROM information_schema.tables WHERE table_schema = 'main' AND table_type = 'BASE TABLE'"
    tables = connection.query(tables_sql)

    tables.each do |table_row|
      table_name = table_row[0]
      next if %w[ar_internal_metadata schema_migrations].include?(table_name)

      # Get table schema using PRAGMA (DuckDB supports SQLite-compatible PRAGMA)
      table_info = connection.query("PRAGMA table_info('#{table_name}')")

      # Build CREATE TABLE statement
      # [[0, "id", "BIGINT", true, nil, true], [1, "name", "VARCHAR", false, 'default_value', false], [2, "email", "VARCHAR", false, nil, false], [3, "created_at", "TIMESTAMP", true, nil, false], [4, "updated_at", "TIMESTAMP", true, nil, false]]
      columns = table_info.map do |col|
        col_def = "#{col[1]} #{col[2]}"
        col_def += ' NOT NULL' if TRUE_VALUES.include?(col[3])
        col_def += " DEFAULT #{col[4]}" if col[4]
        col_def += ' PRIMARY KEY' if TRUE_VALUES.include?(col[5])
        col_def
      end

      file.puts "CREATE TABLE #{table_name} ("
      file.puts "  #{columns.join(",\n  ")}"
      file.puts ');'
      file.puts
    end

    # Get list of sequences used for the table
    sequences = connection.query('SELECT sequencename, start_value, min_value, max_value, increment_by, cycle FROM pg_catalog.pg_sequences').to_a

    # Build CREATE SEQUENCE statements
    sequences.each do |seq|
      seq_def = "CREATE SEQUENCE #{seq[0]}"
      seq_def += " START WITH #{seq[1]}" if seq[1] && seq[1] != 1
      seq_def += " INCREMENT BY #{seq[4]}" if seq[4] && seq[4] != 1
      seq_def += " MINVALUE #{seq[2]}" if seq[2] != -9_223_372_036_854_775_808
      seq_def += " MAXVALUE #{seq[3]}" if seq[3] != 9_223_372_036_854_775_807
      seq_def += ' CYCLE' if seq[5]
      seq_def += ';'
      file.puts seq_def
    end
  end
end

#structure_load(filename, extra_flags = nil) ⇒ void

This method returns an undefined value.

Loads database structure from a SQL file

Parameters:

  • filename (String)

    The filename to load the structure from

  • extra_flags (String, nil) (defaults to: nil)

    Additional flags for the load (unused)

Raises:

  • (LoadError)

    if the schema file does not exist



140
141
142
143
144
145
146
# File 'lib/active_record/tasks/duckdb_database_tasks.rb', line 140

def structure_load(filename, extra_flags = nil)
  establish_connection
  raise(LoadError, 'Database scheme file does not exist') unless File.exist?(filename)

  sql = File.read(filename)
  connection.query(sql)
end