How do I create a CHECK constraint to check for a range of possible values in Sequel. a Ruby ORM.
All attempts seem to generate CHECK (1 = 0) as seen in the output logs.
Here's the table I'm trying to model using Sequel's DSL:
create table memberships(
id integer primary key autoincrement
, group_id integer references groups(id) on delete cascade
, user_id integer references users(id) on delete cascade
, role char check (role in ('u','a','o')) default 'u'
, unique(group_id, user_id, role)
);
and here's the Sequel schema generation code:
db.create_table(:memberships){
primary_key :id
foreign_key :user_id, :users
foreign_key :group_id, :groups
char :role, default: 'u'
check{role=='u' or role=='a'} #<-----this line generates CHECK (1 = 0)
unique [:user_id, :group_id, :role]
}
In Sequel, check constraints are handled just like a filter expression. The recommended way to handle your case would be:
check(:role=>%w[a o u])
I agree that more documentation would probably be better, though there are examples in http://sequel.jeremyevans.net/rdoc/files/doc/schema_modification_rdoc.html
The documentation on how check and add_constraint are supposed to work is rather sparse but you could try bypassing all the magic entirely, write the constraint as you would in SQL, and return that from the block; something like this:
db.create_table(:memberships) {
#...
check { "role in ('a', 'o', 'u')" }
#...
}
The language that is allowed in a CHECK constraint is quite rich and varied so I'd expect a simple string to be an option.
This would be handled elegantly with an enum:
up do
extension :pg_enum
create_enum(:role_types, %w[a b c])
create_table # ...
role_types :role, null: false
#...
down do
drop_table # :...
drop_enum :role_types
end
Related
I need to create a view with an order by-clause with sequel, tinytds and MSSQL
When I do so, I get the error
TinyTds::Error: The ORDER BY clause is invalid in views, inline functions, derived tables, subqueries, and common table expressions, unless TOP or FOR XML is also specified. (Sequel::DatabaseError)
My examplecode:
require 'sequel'
DB = Sequel.tinytds(
:host => 'server',
:database=> 'DB',
)
#Remove data from previous test
DB.drop_table(:testtab1) if DB.table_exists?(:testtab1)
DB.drop_view(:v_testtab1) rescue Sequel::DatabaseError
DB.drop_view(:v_testtab2) rescue Sequel::DatabaseError
DB.create_table(:testtab1){
primary_key :id
field :a, :type => :nvarchar, :size => 10
field :b, :type => :nvarchar, :size => 10
}
#Here the error comes up
#"SELECT * FROM `testtab1` ORDER BY `b`"
DB.create_view(:v_testtab1, DB[:testtab1].order_by(:b))
See solution on SQL-side is easy. Instead of the
SELECT * FROM `testtab1` ORDER BY `b`
I need a
SELECT top 100 percent * FROM `testtab1` ORDER BY `b`
I found a solution with an additional obsolete column (without the column dummy I get an invalid comma):
sel = DB[:testtab1].select(Sequel.lit('top 100 percent "" as dummy'), *DB[:testtab1].columns)
#SELECT top 100 percent "" as dummy, [ID], [A], [B] FROM [TESTTAB1]
DB.create_view(:v_testtab2, sel.order_by(:b))
A similar solution can be made with limit:
#Take a big number to get all entries.
#DB[:testtab1].count would take the number in moment of view creation, not usage.
sel = DB[:testtab1].limit(99999999999)
#SELECT TOP (99999999999) * FROM [TESTTAB1]
DB.create_view(:v_testtab3, sel.order_by(:b))
But I'm looking for a nicer solution. Is there another better possibility?
If it is important:
Ruby 2.1
Sequel 4.19
tiny_tds-0.6.2-x64-mingw32
MSSQL 10.50.2500.0, 64 bit
Other than dropping and recreating a table, how can I add a primary key id column of type uuid to a table that was created without an id field - a join table?
I tried:
add_column :organizations_users, :id, :uuid, primary_key: true
but the column doesn't get set as a primary key.
Looking at the new_column_definition ActiveRecord method, the code above should work:
column.primary_key = type == :primary_key || options[:primary_key]
I must be having a brain fart...
An old question, but if anyone comes across it, this worked for me in Rails 5.2. I was already using UUIDs for primary key :id columns in other tables, so you might need to set that up first if you're not.
def change
add_column :products_questions, :id, :uuid, primary_key: true, default: -> { "gen_random_uuid()" }
end
I am only using ActiveRecord to some development with a legacy Oracle database. My adapter is activerecord-oracle_enhanced-adapter (https://github.com/rsim/oracle-enhanced). So I don't want to AR handle the primary key generation. How to disable the primary key generated by a sequence?
class User < Activied::Base
self.table_name = "users"
self.primary_key = "user_id"
end
user = User.new
user.save
Then I got the error:
stmt.c:230:in oci8lib_191.so: ORA-02289: sequence does not exist (OCIError)
When I change my code to
class User < ActiveRecord::Base
self.table_name = 'users'
self.primary_key = "user_id"
self.sequence_name = nil
end
I got another error:
stmt.c:230:in oci8lib_191.so: ORA-00936: missing expression (OCIError)
So is there anyone know how to manage the primary key manually? I just want to do some simple insert.
Thanks
Maybe it's too late but finaly I found answer. Shortly:
self.sequence_name = :autogenerated
From the source code comment here:
Usage notes:
# * Key generation assumes a "${table_name}_seq" sequence is available
# for all tables; the sequence name can be changed using
# ActiveRecord::Base.set_sequence_name. When using Migrations, these
# sequences are created automatically.
# ***Use set_sequence_name :autogenerated **** with legacy tables that have
# triggers that populate primary keys automatically.**
class A
include DataMapper::Resource
def self.default_repository_name
:alt_db
end
property :aid, Integer, :key => true
# other stuff
belongs_to :b, :model => 'B', :child_key => [ :bid ]
end
class B
include DataMapper::Resource
# this one is in the default repo
property :bid, Integer, :key => true
# other stuff
belongs_to :c, :model => 'C', :child_key => [ :cid ]
end
class C
include DataMapper::Resource
# this one is in the default repo
property :cid, Integer, :key => true
# other stuff
end
If I just have A and B, this works fine. If I add C, however, I get an error:
dm-core/model/property.rb:73:in `new': wrong number of arguments (4 for 3) (ArgumentError)
If I want to make a chain of relationships with DataMapper, so that I can give an ID in one place and get a piece of data that's, say, four tables away through a series of references to subsequent tables' primary key ID field, how can I do this?
EDIT: Digging into the DM source from the stack trace:
DataMapper.repository(other_repository_name) do
properties << klass.new(self, name, options, type)
end
That's where the error is raised. Indeed, in this case klass is a DataMapper Integer property, and it's initialize method only accepts three options (model, name, and an options hash).
This whole block is only executed because I'm using more than one repository, though B and C are in the same one so I don't know if that sheds any light on why it's erroring on the cid property.
EDIT2:
I have tried all permutations, and it appears that when you're chaining, once you cross a database-boundary, that must be the end of the chain. For example, since A is :alt_db and B is :default, B is as deep as I can go, regardless of whether C is :default, :alt_db, or a third option.
If instead both A and B were :default, or both were :alt_db, and then C were the opposite one, C would be as deep as I could go.
I don't understand this behavior really, though.
You found a bug actually. It's been fixed in master. You can try grabbing sources from git and see if it works.
Your code works fine for me.
irb(main):001:0> A.first.b.c
DEBUG - "(0.001168) SELECT "aid", "bid" FROM "as" ORDER BY "aid" LIMIT 1"
DEBUG - "(0.000337) SELECT "bid", "cid" FROM "bs" WHERE "bid" = 2 LIMIT 1"
DEBUG - "(0.000046) SELECT "cid" FROM "cs" WHERE "cid" = 3 LIMIT 1"
=> #<C #cid=3>
My gem is dm-core-1.1.0, you should check your version.
It turns out this was a small issue with DataMapper chaining across repositories. Submitted to them and it's allegedly been fixed already!
http://datamapper.lighthouseapp.com/projects/20609/tickets/1506-can-only-chain-up-to-first-time-changing-default-repository#ticket-1506-1
I have two tables, nodes and terms.
The relevant fields in nodes are: nid (primary key) and value
In terms, they are: value, tid, and nid, where value and tid together are the primary key and nid is a foreign key referencing nodes.nid.
I want to add records to terms. I have the tid and nid, and the value I want to pull from the corresponding node - e.g. look up the value for a given nid in node and then put that as the value in terms.
A way to do this in SQL might be:
INSERT INTO terms(tid, nid, value)
values(mytid, mynid, (
select value from nodes where nid=mynid
));
Could somebody help me do this with DataMapper?
class Node
include DataMapper::Resource
property :nid, Serial, :key => true
property :value, Integer
end
class Term
include DataMapper::Resource
property :tid, Integer, :key => true
# how do I define nid and value?
end
# and then what do I give to Term.new or Term.create and how?
If anyone could point me to a good tutorial of DataMapper as well, I'd appreciate it. I've been using their online docs, but I've found the situations I find myself in are rarely covered there.
From your description the models you're looking for should be setup like that:
class Node
include DataMapper::Resource
property :nid, Serial
property :value, Integer
end
class Term
include DataMapper::Resource
property :tid, Integer, :key => true
property :value, Integer, :key => true
belongs_to :node, :child_key => :nid
end
You can work with these models like that:
# create a node
node = Node.create(:value => 123)
# create a term and associate it with the node
term = Term.create(:tid => 321, :node => node, :value => node.value)