activerecordでレガシーテーブルにつないだ話

21
ActiveRecord でレガシーテーブル に繋いだらスタンド攻撃を受けた話 @ryonext

Upload: ryonext-shimamoto

Post on 12-Nov-2014

1.449 views

Category:

Technology


0 download

DESCRIPTION

 

TRANSCRIPT

Page 1: ActiveRecordでレガシーテーブルにつないだ話

ActiveRecordでレガシーテーブルに繋いだらスタンド攻撃を受けた話

@ryonext

Page 2: ActiveRecordでレガシーテーブルにつないだ話

• タイトルは釣りです

Page 3: ActiveRecordでレガシーテーブルにつないだ話

自己紹介

• Twitter ID @ryonext

• ハンターランク151のモンハン厨です

• 新横浜にあるOA機器メーカーでビデオ会議のシステム作ってます

• RailsでAPIとかテスト書いたりとか

Page 4: ActiveRecordでレガシーテーブルにつないだ話

ActiveRecordは便利ですね 1 class CreateUsers < ActiveRecord::Migration! 2 def change! 3 create_table :users do |t|! 4 t.string :name! 5 t.integer :age! 6 t.string :type! 7 ! 8 t.timestamps! 9 end!10 end!11 end

Page 5: ActiveRecordでレガシーテーブルにつないだ話

14 ActiveRecord::Schema.define(version: 20131116064854) do!15 !16 create_table "users", force: true do |t|!17 t.string "name"!18 t.integer "age"!19 t.string "type"!20 t.datetime "created_at"!21 t.datetime "updated_at"!22 end!23 !24 end

Page 6: ActiveRecordでレガシーテーブルにつないだ話

mysql> desc users; +------------+--------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +------------+--------------+------+-----+---------+----------------+ | id | int(11) | NO | PRI | NULL | auto_increment | | name | varchar(255) | YES | | NULL | | | age | int(11) | YES | | NULL | | | type | varchar(255) | YES | | NULL | | | created_at | datetime | YES | | NULL | | | updated_at | datetime | YES | | NULL | | +------------+--------------+------+-----+---------+----------------+

6 rows in set (0.00 sec)

Page 7: ActiveRecordでレガシーテーブルにつないだ話

ActiveRecordの規約に沿っていないテーブル

mysql> desc legacy_user; +-------------------+--------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------------------+--------------+------+-----+---------+-------+ | userID | int(11) | NO | PRI | 0 | | | name | varchar(255) | YES | | NULL | | | age | int(11) | YES | | NULL | | | someLegacyParams1 | varchar(255) | YES | | NULL | | | someLegacyParams2 | varchar(255) | YES | | NULL | | | someLegacyParams3 | varchar(255) | YES | | NULL | | +-------------------+--------------+------+-----+---------+-------+

6 rows in set (0.00 sec)

+--------------------+ | Tables_in_legacydb | +--------------------+ | legacy_user | +--------------------+

Page 8: ActiveRecordでレガシーテーブルにつないだ話

こんな感じでつなげます

14 legacy:!15 adapter: mysql2!16 database: legacyDB!17 username: root!18 password:!19 pool: 5!20 timeout: 5000

config/database.yml

1 class LegacyUser < ActiveRecord::Base!2 establish_connection(:legacy)!3 self.table_name = "legacy_user"!4 end

app/models/legacy_user.rb

Page 9: ActiveRecordでレガシーテーブルにつないだ話

ただし

Page 10: ActiveRecordでレガシーテーブルにつないだ話

こんなテーブルが合ったとする

mysql> desc hyper_legacy_tbl; +-----------+---------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-----------+---------+------+-----+---------+-------+ | legacy_id | int(11) | NO | PRI | 0 | | | id | int(11) | YES | | NULL | | +-----------+---------+------+-----+---------+-------+

2 rows in set (0.01 sec)

‘id’というカラムをもつが、主キーではなく普通のカラム

Page 11: ActiveRecordでレガシーテーブルにつないだ話

つないでみる

1 class HyperLegacyTbl < ActiveRecord::Base!2 establish_connection(:legacy)!3 self.table_name = "hyper_legacy_tbl"!4 end

Page 12: ActiveRecordでレガシーテーブルにつないだ話

繋がったようにみえる

[5] pry(main)> h = HyperLegacyTbl.new => #<HyperLegacyTbl legacy_id: 0, id: nil>

Page 13: ActiveRecordでレガシーテーブルにつないだ話

‘id’を更新してみる

[6] pry(main)> h.id = 100 => 100 [7] pry(main)> h.save

Page 14: ActiveRecordでレガシーテーブルにつないだ話

ファッ!?

(0.2ms) BEGIN SQL (0.3ms) INSERT INTO `hyper_legacy_tbl` (`legacy_id`) VALUES (100) (0.1ms) COMMIT => true [8] pry(main)> h => #<HyperLegacyTbl legacy_id: 100, id: nil>

Page 15: ActiveRecordでレガシーテーブルにつないだ話
Page 16: ActiveRecordでレガシーテーブルにつないだ話

なぜなのか

• “#id”によるアクセスはprimary_keyへのアクセスになっている

• “id”という名前のパラメータへのアクセスではない

Page 17: ActiveRecordでレガシーテーブルにつないだ話

対処:既存レコードの場合

[36] pry(main)> h = HyperLegacyTbl.first HyperLegacyTbl Load (0.4ms) SELECT `hyper_legacy_tbl`.* FROM `hyper_legacy_tbl` ORDER BY `hyper_legacy_tbl`.`legacy_id` ASC LIMIT 1 => #<HyperLegacyTbl legacy_id: 100, id: 200> [37] pry(main)> h.id => 100 [38] pry(main)> h.legacy_id => 100 [39] pry(main)> h.attributes["id"] => 200

取得には“#attributes[“id”]”を使う

Page 18: ActiveRecordでレガシーテーブルにつないだ話

対処:既存レコードの場合

[45] pry(main)> h.update_column("id", 1000) SQL (0.3ms) UPDATE `hyper_legacy_tbl` SET `hyper_legacy_tbl`.`id` = 1000 WHERE `hyper_legacy_tbl`.`legacy_id` = 100 => true

更新には“update_column”を使う

[46] pry(main)> h.update_column("id", 3000) SQL (0.3ms) UPDATE `hyper_legacy_tbl` SET `hyper_legacy_tbl`.`id` = 3000 WHERE `hyper_legacy_tbl`.`legacy_id` = 1000 => false

ただし、データを取りなおさないで2連続でやると失敗する

Page 19: ActiveRecordでレガシーテーブルにつないだ話

新規の場合

• Insertのときはupdate_columnが使えないので違う方法が必要?(未調査)

• 直接SQLを発行してしまえば

• それもうActiveRecordつかわなくていいよね説

Page 20: ActiveRecordでレガシーテーブルにつないだ話

どこで定義されているのか• #id

• lib/active_record/attribute_methods/primary_key.rb

• #define_method_attribute(attr_name)

• #id=

• lib/active_record/attribute_methods/write.rb

• #write_attribute(attr_name, value)

Page 21: ActiveRecordでレガシーテーブルにつないだ話

まとめ

• ‘id’っていう名前を持つテーブルにActiveRecordでつなぐときは注意しましょう