Class: ActiveRecord::ConnectionAdapters::BigqueryAdapter

Inherits:
AbstractAdapter
  • Object
show all
Includes:
SchemaStatements
Defined in:
lib/active_record/connection_adapters/bigquery_adapter.rb

Defined Under Namespace

Classes: BindSubstitution, ColumnDefinition, ExplainPrettyPrinter, StatementPool, TableDefinition, Version

Instance Method Summary collapse

Constructor Details

#initialize(connection, logger, config) ⇒ BigqueryAdapter

Returns a new instance of BigqueryAdapter.



457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
# File 'lib/active_record/connection_adapters/bigquery_adapter.rb', line 457

def initialize(connection, logger, config)
  super(connection, logger)

  @active = nil
  @statements = StatementPool.new(@connection,
                                  self.class.type_cast_config_to_integer(config.fetch(:statement_limit) { 1000 }))
  @config = config

  #if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true })
  #  @prepared_statements = true
  #  @visitor = Arel::Visitors::SQLite.new self
  #else
  #use the sql without prepraded statements, as I know BQ doesn't support them.
  @visitor = unprepared_visitor
end

Instance Method Details

#active?Boolean

Returns:

  • (Boolean)


512
513
514
# File 'lib/active_record/connection_adapters/bigquery_adapter.rb', line 512

def active?
  @active != false
end

#adapter_nameObject

:nodoc:



473
474
475
# File 'lib/active_record/connection_adapters/bigquery_adapter.rb', line 473

def adapter_name #:nodoc:
  'BigQuery'
end

#add_column(table_name, column_name, type, options = {}) ⇒ Object

:nodoc:



774
775
776
777
778
779
780
781
782
# File 'lib/active_record/connection_adapters/bigquery_adapter.rb', line 774

def add_column(table_name, column_name, type, options = {}) #:nodoc:
  if supports_add_column? && valid_alter_table_options( type, options )
    super(table_name, column_name, type, options)
  else
    alter_table(table_name) do |definition|
      definition.column(column_name, type, options)
    end
  end
end

#add_reference(table_name, ref_name, options = {}) ⇒ Object



890
891
892
893
894
895
896
# File 'lib/active_record/connection_adapters/bigquery_adapter.rb', line 890

def add_reference(table_name, ref_name, options = {})
  polymorphic = options.delete(:polymorphic)
  index_options = options.delete(:index)
  add_column(table_name, "#{ref_name}_id", :string, options)
  add_column(table_name, "#{ref_name}_type", :string, polymorphic.is_a?(Hash) ? polymorphic : options) if polymorphic
  add_index(table_name, polymorphic ? %w[id type].map{ |t| "#{ref_name}_#{t}" } : "#{ref_name}_id", index_options.is_a?(Hash) ? index_options : nil) if index_options
end

#allowed_index_name_lengthObject

Returns 62. SQLite supports index names up to 64 characters. The rest is used by rails internally to perform temporary rename operations



550
551
552
# File 'lib/active_record/connection_adapters/bigquery_adapter.rb', line 550

def allowed_index_name_length
  index_name_length - 2
end

#assume_migrated_upto_version(version, migrations_paths = ActiveRecord::Migrator.migrations_paths) ⇒ Object



906
907
908
# File 'lib/active_record/connection_adapters/bigquery_adapter.rb', line 906

def assume_migrated_upto_version(version, migrations_paths = ActiveRecord::Migrator.migrations_paths)
  bypass_feature
end

#begin_db_transactionObject

:nodoc:



725
726
727
# File 'lib/active_record/connection_adapters/bigquery_adapter.rb', line 725

def begin_db_transaction #:nodoc:
  log('begin transaction',nil) {  } #@connection.transaction
end

#bypass_featureObject



861
862
863
864
865
866
867
868
# File 'lib/active_record/connection_adapters/bigquery_adapter.rb', line 861

def bypass_feature
  begin
    raise Error::NotImplementedColumnOperation
  rescue => e
    puts e.message
    logger.warn(e.message)
  end
end

#change_column(table_name, column_name, type, options = {}) ⇒ Object

:nodoc:



882
883
884
# File 'lib/active_record/connection_adapters/bigquery_adapter.rb', line 882

def change_column(table_name, column_name, type, options = {}) #:nodoc:
  bypass_feature
end

#change_column_default(table_name, column_name, default) ⇒ Object

:nodoc:



874
875
876
# File 'lib/active_record/connection_adapters/bigquery_adapter.rb', line 874

def change_column_default(table_name, column_name, default) #:nodoc:
  bypass_feature
end

#change_column_null(table_name, column_name, null, default = nil) ⇒ Object



878
879
880
# File 'lib/active_record/connection_adapters/bigquery_adapter.rb', line 878

def change_column_null(table_name, column_name, null, default = nil)
  bypass_feature
end

#change_table(table_name, options = {}) ⇒ Object

See also Table for details on all of the various column transformation.



817
818
819
820
821
822
823
824
825
# File 'lib/active_record/connection_adapters/bigquery_adapter.rb', line 817

def change_table(table_name, options = {})
  if supports_bulk_alter? && options[:bulk]
    recorder = ActiveRecord::Migration::CommandRecorder.new(self)
    yield update_table_definition(table_name, recorder)
    bulk_change_table(table_name, recorder.commands)
  else
    yield update_table_definition(table_name, self)
  end
end

#clear_cache!Object

Clears the prepared statements cache.



525
526
527
# File 'lib/active_record/connection_adapters/bigquery_adapter.rb', line 525

def clear_cache!
  @statements.clear
end

#columns(table_name) ⇒ Object

Returns an array of SQLite3Column objects for the table specified by table_name.



752
753
754
755
756
757
758
759
# File 'lib/active_record/connection_adapters/bigquery_adapter.rb', line 752

def columns(table_name) #:nodoc:
  schema = GoogleBigquery::Table.get(@config[:project], @config[:database], table_name)
  schema["schema"]["fields"].map do |field|
    mode = field['mode'].present? && field['mode'] == "REQUIRED" ? false : true
    #column expects (name, default, sql_type = nil, null = true)
    BigqueryColumn.new(field['name'], nil, field['type'], mode )
  end
end

#commit_db_transactionObject

:nodoc:



729
730
731
# File 'lib/active_record/connection_adapters/bigquery_adapter.rb', line 729

def commit_db_transaction #:nodoc:
  log('commit transaction',nil) {  } #@connection.commit
end

#create_database(database) ⇒ Object



590
591
592
593
594
# File 'lib/active_record/connection_adapters/bigquery_adapter.rb', line 590

def create_database(database)
  result = GoogleBigquery::Dataset.create(@config[:project],
    {"datasetReference"=> { "datasetId" => database }} )
  result
end

#create_table(table_name, options = {}) {|td| ... } ⇒ Object

See also TableDefinition#column for details on how to create columns.

Yields:

  • (td)


785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
# File 'lib/active_record/connection_adapters/bigquery_adapter.rb', line 785

def create_table(table_name, options = {})
  td = create_table_definition table_name, options[:temporary], options[:options]

  unless options[:id] == false
    pk = options.fetch(:primary_key) {
      Base.get_primary_key table_name.to_s.singularize
    }

    td.primary_key pk, options.fetch(:id, :primary_key), options
  end

  yield td if block_given?

  if options[:force] && table_exists?(table_name)
    drop_table(table_name, options)
  end

  hsh = td.columns.map { |c|  {"name"=> c[:name], "type"=> type_to_sql(c[:type]) }  }

  @table_body = {  "tableReference"=> {
                      "projectId"=> @config[:project],
                      "datasetId"=> @config[:database],
                      "tableId"=> td.name},
                    "schema"=> [fields: hsh]
                }

  res = GoogleBigquery::Table.create(@config[:project], @config[:database], @table_body )

  raise res["error"]["errors"].map{|o| "[#{o['domain']}]: #{o['reason']} #{o['message']}" }.join(", ") if res["error"].present?
end

#default_primary_key_typeObject



554
555
556
557
558
559
560
# File 'lib/active_record/connection_adapters/bigquery_adapter.rb', line 554

def default_primary_key_type
  if supports_autoincrement?
    'STRING'
  else
    'STRING'
  end
end

#disconnect!Object

Disconnects from the database if already connected. Otherwise, this method does nothing.



518
519
520
521
522
# File 'lib/active_record/connection_adapters/bigquery_adapter.rb', line 518

def disconnect!
  super
  @active = false
  @connection.close rescue nil
end

#drop_database(database) ⇒ Object



596
597
598
599
600
601
602
603
604
605
606
# File 'lib/active_record/connection_adapters/bigquery_adapter.rb', line 596

def drop_database(database)
  tables = GoogleBigquery::Table.list(@config[:project], database)["tables"]
  unless tables.blank?
    tables.map!{|o| o["tableReference"]["tableId"]}
    tables.each do |table_id|
      GoogleBigquery::Table.delete(@config[:project], database, table_id)
    end
  end
  result = GoogleBigquery::Dataset.delete(@config[:project], database )
  result
end

#drop_table(table_name) ⇒ Object



898
899
900
# File 'lib/active_record/connection_adapters/bigquery_adapter.rb', line 898

def drop_table(table_name)
  GoogleBigquery::Table.delete(@config[:project], @config[:database], table_name )
end

#dump_schema_informationObject

:nodoc:



902
903
904
# File 'lib/active_record/connection_adapters/bigquery_adapter.rb', line 902

def dump_schema_information #:nodoc:
  bypass_feature
end

#encodingObject

Returns the current database encoding format as a string, eg: ‘UTF-8’



581
582
583
# File 'lib/active_record/connection_adapters/bigquery_adapter.rb', line 581

def encoding
  @connection.encoding.to_s
end

#exec_delete(sql, name = 'SQL', binds = []) ⇒ Object Also known as: exec_update



700
701
702
703
# File 'lib/active_record/connection_adapters/bigquery_adapter.rb', line 700

def exec_delete(sql, name = 'SQL', binds = [])
  exec_query(sql, name, binds)
  @connection.changes
end

#exec_query(sql, name = nil, binds = []) ⇒ Object



682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
# File 'lib/active_record/connection_adapters/bigquery_adapter.rb', line 682

def exec_query(sql, name = nil, binds = [])
  log(sql, name, binds) do

    # Don't cache statements if they are not prepared
    if without_prepared_statement?(binds)
      result = GoogleBigquery::Jobs.query(@config[:project], {"query"=> sql })
      cols    = result["schema"]["fields"].map{|o| o["name"] }
      records = result["totalRows"].to_i.zero? ? [] : result["rows"].map{|o| o["f"].map{|k,v| k["v"]} }
      stmt = records
    else
      #binding.pry
      #BQ does not support prepared statements, yiak!
    end

    ActiveRecord::Result.new(cols, stmt)
  end
end

#execute(sql, name = nil) ⇒ Object

:nodoc:



711
712
713
# File 'lib/active_record/connection_adapters/bigquery_adapter.rb', line 711

def execute(sql, name = nil) #:nodoc:
  log(sql, name) { @connection.execute(sql) }
end

#explain(arel, binds = []) ⇒ Object

DATABASE STATEMENTS ======================================



664
665
666
# File 'lib/active_record/connection_adapters/bigquery_adapter.rb', line 664

def explain(arel, binds = [])
  bypass_feature
end

#indexes(table_name, name = nil) ⇒ Object

Returns an array of indexes for the given table.



762
763
764
# File 'lib/active_record/connection_adapters/bigquery_adapter.rb', line 762

def indexes(table_name, name = nil) #:nodoc:
  []
end

#insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) ⇒ Object Also known as: create

:nodoc:



715
716
717
718
# File 'lib/active_record/connection_adapters/bigquery_adapter.rb', line 715

def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
  super
  id_value || @connection.last_insert_row_id
end

#last_inserted_id(result) ⇒ Object



707
708
709
# File 'lib/active_record/connection_adapters/bigquery_adapter.rb', line 707

def last_inserted_id(result)
  @connection.last_insert_row_id
end

#native_database_typesObject

:nodoc:



562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
# File 'lib/active_record/connection_adapters/bigquery_adapter.rb', line 562

def native_database_types #:nodoc:
  {
    :primary_key => default_primary_key_type,
    :string      => { :name => "STRING", :default=> nil },
    #:text        => { :name => "text" },
    :integer     => { :name => "INTEGER", :default=> nil },
    :float       => { :name => "FLOAT", :default=> 0.0 },
    #:decimal     => { :name => "decimal" },
    :datetime    => { :name => "TIMESTAMP" },
    #:timestamp   => { :name => "datetime" },
    :timestamp    => { name: "TIMESTAMP" },
    #:time        => { :name => "time" },
    :date        => { :name => "TIMESTAMP" },
    :record      => { :name => "RECORD" },
    :boolean     => { :name => "BOOLEAN" }
  }
end

#primary_key(table_name) ⇒ Object

:nodoc:



766
767
768
# File 'lib/active_record/connection_adapters/bigquery_adapter.rb', line 766

def primary_key(table_name) #:nodoc:
  "id"
end

#quote(value, column = nil) ⇒ Object

QUOTING ==================================================



610
611
612
613
614
615
616
617
# File 'lib/active_record/connection_adapters/bigquery_adapter.rb', line 610

def quote(value, column = nil)
  if value.kind_of?(String) && column && column.type == :binary && column.class.respond_to?(:string_to_binary)
    s = column.class.string_to_binary(value).unpack("H*")[0]
    "x'#{s}'"
  else
    super
  end
end

#quote_column_name(name) ⇒ Object

:nodoc:



627
628
629
# File 'lib/active_record/connection_adapters/bigquery_adapter.rb', line 627

def quote_column_name(name) #:nodoc:
  name
end

#quote_table_name(name) ⇒ Object



619
620
621
# File 'lib/active_record/connection_adapters/bigquery_adapter.rb', line 619

def quote_table_name(name)
  "#{@config[:database]}.#{name}"
end

#quote_table_name_for_assignment(table, attr) ⇒ Object



623
624
625
# File 'lib/active_record/connection_adapters/bigquery_adapter.rb', line 623

def quote_table_name_for_assignment(table, attr)
  quote_column_name(attr)
end

#quoted_date(value) ⇒ Object

Quote date/time values for use in SQL input. Includes microseconds if the value is a Time responding to usec.



633
634
635
636
637
638
639
# File 'lib/active_record/connection_adapters/bigquery_adapter.rb', line 633

def quoted_date(value) #:nodoc:
  if value.respond_to?(:usec)
    "#{super}.#{sprintf("%06d", value.usec)}"
  else
    super
  end
end

#quoted_falseObject



645
646
647
# File 'lib/active_record/connection_adapters/bigquery_adapter.rb', line 645

def quoted_false
  "0"
end

#quoted_trueObject



641
642
643
# File 'lib/active_record/connection_adapters/bigquery_adapter.rb', line 641

def quoted_true
  "1"
end

#remove_column(table_name, column_name, type = nil, options = {}) ⇒ Object

:nodoc:



870
871
872
# File 'lib/active_record/connection_adapters/bigquery_adapter.rb', line 870

def remove_column(table_name, column_name, type = nil, options = {}) #:nodoc:
  bypass_feature
end

#remove_index!(table_name, index_name) ⇒ Object

:nodoc:



770
771
772
# File 'lib/active_record/connection_adapters/bigquery_adapter.rb', line 770

def remove_index!(table_name, index_name) #:nodoc:
  #exec_query "DROP INDEX #{quote_column_name(index_name)}"
end

#rename_column(table_name, column_name, new_column_name) ⇒ Object

:nodoc:



886
887
888
# File 'lib/active_record/connection_adapters/bigquery_adapter.rb', line 886

def rename_column(table_name, column_name, new_column_name) #:nodoc:
  bypass_feature
end

#rename_table(table_name, new_name) ⇒ Object

Renames a table.

Example:

rename_table('octopuses', 'octopi')


830
831
832
# File 'lib/active_record/connection_adapters/bigquery_adapter.rb', line 830

def rename_table(table_name, new_name)
  raise Error::PendingFeature
end

#requires_reloading?Boolean

Returns:

  • (Boolean)


504
505
506
# File 'lib/active_record/connection_adapters/bigquery_adapter.rb', line 504

def requires_reloading?
  false
end

#rollback_db_transactionObject

:nodoc:



733
734
735
# File 'lib/active_record/connection_adapters/bigquery_adapter.rb', line 733

def rollback_db_transaction #:nodoc:
  log('rollback transaction',nil) { } #@connection.rollback
end

#select_rows(sql, name = nil) ⇒ Object



721
722
723
# File 'lib/active_record/connection_adapters/bigquery_adapter.rb', line 721

def select_rows(sql, name = nil)
  exec_query(sql, name).rows
end

#supports_add_column?Boolean

Returns:

  • (Boolean)


508
509
510
# File 'lib/active_record/connection_adapters/bigquery_adapter.rb', line 508

def supports_add_column?
  true
end

#supports_autoincrement?Boolean

Returns false

Returns:

  • (Boolean)


539
540
541
# File 'lib/active_record/connection_adapters/bigquery_adapter.rb', line 539

def supports_autoincrement? #:nodoc:
  false
end

#supports_count_distinct?Boolean

Returns true

Returns:

  • (Boolean)


534
535
536
# File 'lib/active_record/connection_adapters/bigquery_adapter.rb', line 534

def supports_count_distinct? #:nodoc:
  true
end

#supports_ddl_transactions?Boolean

Returns:

  • (Boolean)


477
478
479
# File 'lib/active_record/connection_adapters/bigquery_adapter.rb', line 477

def supports_ddl_transactions?
  false
end

#supports_explain?Boolean

Returns false.

Returns:

  • (Boolean)


586
587
588
# File 'lib/active_record/connection_adapters/bigquery_adapter.rb', line 586

def supports_explain?
  false
end

#supports_index_sort_order?Boolean

Returns:

  • (Boolean)


529
530
531
# File 'lib/active_record/connection_adapters/bigquery_adapter.rb', line 529

def supports_index_sort_order?
  true
end

#supports_migrations?Boolean

Returns true, since this connection adapter supports migrations.

Returns:

  • (Boolean)


496
497
498
# File 'lib/active_record/connection_adapters/bigquery_adapter.rb', line 496

def supports_migrations? #:nodoc:
  true
end

#supports_partial_index?Boolean

Returns:

  • (Boolean)


485
486
487
# File 'lib/active_record/connection_adapters/bigquery_adapter.rb', line 485

def supports_partial_index?
  true
end

#supports_primary_key?Boolean

:nodoc:

Returns:

  • (Boolean)


500
501
502
# File 'lib/active_record/connection_adapters/bigquery_adapter.rb', line 500

def supports_primary_key? #:nodoc:
  true
end

#supports_savepoints?Boolean

Returns:

  • (Boolean)


481
482
483
# File 'lib/active_record/connection_adapters/bigquery_adapter.rb', line 481

def supports_savepoints?
  false
end

#supports_statement_cache?Boolean

Returns true, since this connection adapter supports prepared statement caching.

Returns:

  • (Boolean)


491
492
493
# File 'lib/active_record/connection_adapters/bigquery_adapter.rb', line 491

def supports_statement_cache?
  false
end

#table_exists?(table_name) ⇒ Boolean

Returns:

  • (Boolean)


747
748
749
# File 'lib/active_record/connection_adapters/bigquery_adapter.rb', line 747

def table_exists?(table_name)
  table_name && tables(nil, table_name).any?
end

#tables(name = nil, table_name = nil) ⇒ Object

SCHEMA STATEMENTS ========================================



739
740
741
742
743
744
745
# File 'lib/active_record/connection_adapters/bigquery_adapter.rb', line 739

def tables(name = nil, table_name = nil) #:nodoc:
  table = GoogleBigquery::Table.list(@config[:project], @config[:database])
  return [] if table["tables"].blank?
  table_names = table["tables"].map{|o| o["tableReference"]["tableId"]}
  table_names = table_names.select{|o| o == table_name } if table_name
  table_names
end

#type_cast(value, column) ⇒ Object

:nodoc:



649
650
651
652
653
654
655
656
657
658
659
660
# File 'lib/active_record/connection_adapters/bigquery_adapter.rb', line 649

def type_cast(value, column) # :nodoc:
  return value.to_f if BigDecimal === value
  return super unless String === value
  return super unless column && value

  value = super
  if column.type == :string && value.encoding == Encoding::ASCII_8BIT
    logger.error "Binary data inserted for `string` type on column `#{column.name}`" if logger
    value = value.encode Encoding::UTF_8
  end
  value
end

#valid_alter_table_options(type, options) ⇒ Object

See: www.sqlite.org/lang_altertable.html SQLite has an additional restriction on the ALTER TABLE statement



836
837
838
# File 'lib/active_record/connection_adapters/bigquery_adapter.rb', line 836

def valid_alter_table_options( type, options)
  type.to_sym != :primary_key
end