Skeletons in /lib: Setting auto_increment in MySQL
Skeletons in /lib is explores the underbelly of Rails apps- the aspects, hacks, and extensions that don’t fit elsewhere, confound new developers, and make each beast unique.
There are a number of business requirements that almost (but don’t quite) map to simple technical implementations. In those cases, we’re often faced with a choice- do we write a separate, more complicated implementation, or do we force a fit with a hack?
Here’s a straightforward one: purchase orders (POs) are a key business document in our system, and when companies issue them they tend to refer to them by number. (“Hey, Apple, when are you planning to ship PO #453?”) As such, they’re a minor but real aspect of the face that our customers present to their suppliers, and some of them want more control over the range. Basically, they’d much rather start issuing POs at e.g. 10,001 than at 1, much like many folks don’t want their checkbooks to start at 1. Technically, though, the PO number is about what you’d expect it to be in a Rails app- the order_headers.id column. (I’m leaving out revisions here- that’s a topic for another time.)
So, how do we give users control over where the sequence starts? Simple, although it’s one of the very few places where we’re not database agnostic:
# Include on ActiveRecord models to get additional MySQL-specific
# functionality.
module CoupaLib
module MySQLHelpers
def self.included(base)
base.extend ClassMethods
end
module ClassMethods
# Returns a string containing the auto increment value (the next insert
# id) on this table.
def db_auto_increment
result = ActiveRecord::Base.connection.execute <<-SQL
SHOW TABLE STATUS WHERE name = '#{self.table_name}';
SQL
result.fetch_hash['Auto_increment'].to_i
end
# Sets the auto increment value to the argument. NOTE: it is the
# responsibility of the user to make sure the value isn't already in
# use (will potentially cause duplicate id errors)
def db_auto_increment=(num)
ActiveRecord::Base.connection.execute <<-SQL
ALTER TABLE #{self.table_name} AUTO_INCREMENT = #{num};
SQL
end
end # Class Methods
end # MySQLHelpers
end # CoupaLib
Now we can inquire what the next ID will be, and set it, in a few interesting places within our app:
- During customers’ initial self-guided setup, before any documents are created.
- During automated transactional “cleanup” when moving a test configuration into a production environment. Existing test documents are destroyed, and the sequence is reset to the initial custom value.
- During merging of potentially-updated Coupa seed data and customer data that needs to be preserved. (That’s another interesting code-meets-real-world problem to look at later.)











Company Blog
Developer Blog
News & Events