So, I am trying to get redmine_backlogs to work with SQL server. [We are using SQL Server rather than SQLite, to scale better]
I am NOT a Ruby programmer, but I really want this plugin for my team, as we actively use Redmine for several projects.
After reading through some Ruby tutorials, I've managed to get make some modifications and get the plugin installed and migrated correctly [it appears].
On the plugin settings screen [in administration] in Redmine, it shows the migration wasn't successful.
Even though all the list items are green, and the migrations appeared to work.
Any ideas?
The changes I made were to bypass suspected issues with the way Active records handles direct SQL queries.
Here are the changes I've made -
ERROR #1 –
C:\Projects\Redmine\redmine-2.3.2>rake redmine:plugins:migrate
Migrating redmine_backlogs (Redmine Backlogs)...
== AddStoryPositions: migrating ==============================================
-- execute("select max(position) from issues")
-> 0.0020s
-> -1 rows
rake aborted!
An error has occurred, this and all later migrations canceled:
undefined method `each' for -1:FixnumC:/Projects/Redmine/redmine-2.3.2/plugins/redmine_backlogs/db/migrate/026_add_story_positions.rb:10:in `up'
FIX #1 –
Direct queries are not working correctly with the sqladapter [TinyTds + Active Record]
026_add_story_positions.rb
class AddStoryPositions < ActiveRecord::Migration
def self.up
# Rails doesn't support temp tables, mysql doesn't support update
# from same-table subselect
unless RbStory.trackers.size == 0
max = 0
dbconfig = YAML.load_file(File.join(File.dirname(__FILE__), '../../../../config/database.yml'))#[Rails.env]['username']
if dbconfig[Rails.env]['adapter'] == 'sqlserver' then
database = dbconfig[Rails.env]['database']
dataserver = dbconfig[Rails.env]['dataserver']
mode = dbconfig[Rails.env]['mode']
port = dbconfig[Rails.env]['port']
username = dbconfig[Rails.env]['username']
password = dbconfig[Rails.env]['password']
client = TinyTds::Client.new(
:database => database,
:dataserver => dataserver,
:mode => mode,
:port => port,
:username => username,
:password => password)
client.execute("select max(position) from issues").each{|row| max = row[0]}
client.execute "update issues
set position = #{max} + id
where position is null and tracker_id in (#{RbStory.trackers(:type=>:string)})"
else
execute("select max(position) from issues").each{|row| max = row[0]}
execute "update issues
set position = #{max} + id
where position is null and tracker_id in (#{RbStory.trackers(:type=>:string)})"
end
end
end
def self.down
puts "Reverting irreversible migration"
end
end
ERROR #2
rake aborted!
An error has occurred, this and all later migrations canceled:
TinyTds::Error: ALTER TABLE ALTER COLUMN position failed because one or more objects access this column.: ALTER TABLE [issues] ALTER COLUMN [position]
integer NOT NULLC:/Projects/Redmine/redmine-2.3.2/plugins/redmine_backlogs/db/migrate/033_unique_positions.rb:30:in `up'
FIX #2
033_unique_positions.rb
#SQLServer cannot change the type of an indexes column, so it must be dropped first
remove_index :issues, :position
change_column :issues, :position, :integer, :null => false
add_index :issues, :position
ERROR #3
rake aborted!
undefined method each' for -1:Fixnum
C:/Projects/Redmine/redmine-2.3.2/plugins/redmine_backlogs/lib/backlogs_setup.rb:155:inmigrated?'
FIX #3
def migrated?
available = Dir[File.join(File.dirname(__FILE__), '../db/migrate/*.rb')].collect{|m| Integer(File.basename(m).split('_')[0].gsub(/^0+/, ''))}.sort
return true if available.size == 0
available = available[-1]
ran = []
dbconfig = YAML.load_file(File.join(File.dirname(__FILE__), '../../../config/database.yml'))#[Rails.env]['username']
if dbconfig[Rails.env]['adapter'] == 'sqlserver' then
database = dbconfig[Rails.env]['database']
dataserver = dbconfig[Rails.env]['dataserver']
mode = dbconfig[Rails.env]['mode']
port = dbconfig[Rails.env]['port']
username = dbconfig[Rails.env]['username']
password = dbconfig[Rails.env]['password']
client = TinyTds::Client.new(
:database => database,
:dataserver => dataserver,
:mode => mode,
:port => port,
:username => username,
:password => password)
client.execute("select version from schema_migrations where version like '%-redmine_backlogs'").each{|m|
ran << Integer((m.is_a?(Hash) ? m.values : m)[0].split('-')[0])
}
else
Setting.connection.execute("select version from schema_migrations where version like '%-redmine_backlogs'").each{|m|
ran << Integer((m.is_a?(Hash) ? m.values : m)[0].split('-')[0])
}
end
return false if ran.size == 0
ran = ran.sort[-1]
return ran >= available
end
module_function :migrated?
I was using the wrong where clause -
This is the correct one, I must have overwritten when debugging.
'%-redmine_backlogs'
The above code works.
I could not answer my own question before, but now I can.
The above code was tested and works. I have been running backlogs on Windows with MS SQL successfully since.
Related
I'm trying to maintain/fix an outdated plugin (redmine_backlogs) in Redmine for my company's productivity workflow; I am not even remotely conversant in the subtleties of Ruby (let alone Rails), but by virtue of having glanced at the code, I am the company guru on the matter... so.
Upfront clarity: I'm looking for help on troubleshooting Ruby on Rails code - namely:
The application I am debugging is doing something like
SELECT COUNT(*) FROM tableA WHERE tableB.id = ? ...
whereas I am expecting of course
SELECT COUNT(*) FROM tableA,tableB WHERE tableB.id = ? ...
The log reads as follows:
ActiveRecord::StatementInvalid (Mysql2::Error: Unknown column 'releases.id' in 'where clause': SELECT COUNT(*) FROM `projects` WHERE (projects.status <> 9) AND (releases.id = 10 OR (projects.status <> 9 AND ( 'system' = 'none' OR (projects.lft >= 91 AND projects.rgt <= 92 AND 'none' = 'tree') OR (projects.lft > 91 AND projects.rgt < 92 AND 'none' IN ('hierarchy', 'descendants')) OR (projects.lft < 91 AND projects.rgt > 92 AND 'none' = 'hierarchy'))))):
plugins/redmine_backlogs/app/controllers/rb_master_backlogs_controller.rb:43:in `_menu_new'
plugins/redmine_backlogs/app/controllers/rb_master_backlogs_controller.rb:62:in `menu'
lib/redmine/sudo_mode.rb:63:in `sudo_mode'
The active record item comes from this section of code in plugins/redmine_backlogs/app/controllers/rb_master_backlogs_controller.rb
...
elsif #release #menu for release
projects = #release.shared_to_projects(#project)
else #menu for product backlog
projects = #project.projects_in_shared_product_backlog
end
#make the submenu or single link
if !projects.empty? # <<<<<<<<<<<< ----------------------- Line 43 <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
if projects.length > 1
links << {:label => l(label_new), :url => '#', :sub => []}
...
The projects object being tested for emptiness is generated by the call to #release.shared_to_project which is defined in ./app/models/rb_release.rb, whose relevant sections are:
class RbRelease < ActiveRecord::Base
...
unloadable
belongs_to :project, :inverse_of => :releases # <<<<<<<---- Is this where the association is being asserted ?
has_many :issues, :class_name => 'RbStory', :foreign_key => 'release_id', :dependent => :nullify
has_many :rb_release_burnchart_day_cache, :dependent => :delete_all, :foreign_key => 'release_id'
validates_presence_of :project_id, :name, :release_start_date, :release_end_date # <<<<------- or here ....?
validates_inclusion_of :status, :in => RELEASE_STATUSES
validates_inclusion_of :sharing, :in => RELEASE_SHARINGS
...
def shared_to_projects(scope_project)
#shared_projects ||=
begin
# Project used when fetching tree sharing
r = self.project.root? ? self.project : self.project.root
# Project used for other sharings
p = self.project
Project.visible.scoped(:include => :releases,
:conditions => ["#{RbRelease.table_name}.id = #{id}" +
" OR (#{Project.table_name}.status <> #{Project::STATUS_ARCHIVED} AND (" +
" 'system' = ? " +
" OR (#{Project.table_name}.lft >= #{r.lft} AND
...
So evidently we're relying on something from the main Project object to do something with the templated query - checking ../../app/models/project.rb I reach a dead end as to what to check for next... The RbRelease has declared its association with Project, but still the query does not seem to include that in the FROM clause before stating its conditions.
My naive question is: how would I fix the relationship association so that the SQL query is correctly built ?
==============
(previously there was also this issue as part of the question, but it turned out to be unrelated)
Started GET "/redmine/rb/master_backlog/afcd123_ghg/menu?project_id=9&authenticity_token=<REDACTED>" for 10.0.3.1 at 2017-06-21 15:21:36 +0000
Processing by RbMasterBacklogsController#menu as JSON
Parameters: {"project_id"=>"afcd123_ghg", "authenticity_token"=>"<REDACTED>"}
Current user: tai (id=125)
DEPRECATION WARNING: Relation#first with finder options is deprecated. Please build a scope and then call #first on it instead. (called from rb_project_settings at /usr/share/redmine/plugins/redmine_backlogs/lib/backlogs_project_patch.rb:193)
Completed 200 OK in 16ms (Views: 0.6ms | ActiveRecord: 7.0ms)
This issue was very application specific; I post the actual solution here as I managed to track down someone else who has been maintaining this plugin.
The relationship has to be explicitly state in a joins() method call, on to which the rest gets tacked.
That is, stating a relationship does not have any sway on this query, as it is a direct query on the Projects object.
Project.visible.joins('LEFT OUTER JOIN releases ON releases.project_id = projects.id').
includes(:releases).
where("#{RbRelease.table_name}.id = #{id}" +
...
I currently have a website that is built in WordPress, and have over 1000 customers registered. Would I be able to extract the information from the WordPress db in ruby code
Need way that's seamless for my customers? Without requiring registering again?
Note:
This code should create active record classes or models dynamically instead of manually defining active record models
With out having the Gem You can achieve this via Ruby script itself
I assumed you have rails application from there you can create rake task to fetch the data from WordPress Database
Step 1:
value = {host: "192.*.*.*", username: '', password: '', database: ''}
ActiveRecord::Base.establish_connection(
adapter: 'mysql2',
encoding: 'utf8',
pool: 5,
host: value[:host],
username: value[:username],
password: value[:password],
database: value[:database]
)
Step 2:
Then Define the database tables needs to be fetched
database_tables = {:"database_name" => ["SaveContactForm7_1", "SaveContactForm7_2","SaveContactForm7_3","SaveContactForm7_4"]}
Step 3:
tables = database_tables[key]
Key refers to the database name
Step 4:
tables.each do |table|
MODEL_CLASS= table
Object.const_set(MODEL_CLASS, Class.new(ActiveRecord::Base) { def self.name() MODEL_CLASS end;def self.table_name() MODEL_CLASS end })
records = MODEL_CLASS.constantize.all
results = []
records.each do |record|
set = {}
columns = MODEL_CLASS.constantize.column_names
p columns
columns.each do |column|
p record.send(column.to_sym)
p set[:mobile] = record.send(column.to_sym)
end
results << set
p record
end
p "Task done...."
p results
end
As a result you can see the array of hashes. you can insert into your active record models
Please Feel free to give your suggestion for this
This is an odd one. I have some ruby code on my machine which uses the tiny_tds version:
0.6.0.rc1
and everything works fine. When a co-worker attempt to run the same code he gets the following error:
TinyTds::Error: Adaptive Server connection failed
We are connecting without providing username or password as it's not needed on my machine. Any ideas? could this be a rights thing on the sql database?
Thanks in advance
It should be noted on the machine with issues we have SQL 2008 R2 and SQL Express installed. We can connect to SQL express but not SQL 2008 R2
Here's the code we are using
def self.GetTestMprsFromDB(dataServer,database,query)
mprids = Array.new
client = TinyTds::Client.new(:dataserver => dataServer, :database => database, :timeout => 1000)
When stepping into tiny_tds on initialize
def initialize(opts={})
if opts[:password] && opts[:password].to_s.strip != ''
opts[:password] = opts[:password].to_s
warn 'FreeTDS may have issues with passwords longer than 30 characters!' if opts[:password].length > 30
end
raise ArgumentError, 'missing :host option if no :dataserver given' if opts[:dataserver].to_s.empty? && opts[:host].to_s.empty?
#query_options = ##default_query_options.dup
opts[:appname] ||= 'TinyTds'
opts[:tds_version] = TDS_VERSIONS_SETTERS[opts[:tds_version].to_s] || TDS_VERSIONS_SETTERS['71']
opts[:login_timeout] ||= 60
opts[:timeout] ||= 5
opts[:encoding] = (opts[:encoding].nil? || opts[:encoding].downcase == 'utf8') ? 'UTF-8' : opts[:encoding].upcase
opts[:port] ||= 1433
opts[:dataserver] = "#{opts[:host]}:#{opts[:port]}" if opts[:dataserver].to_s.empty?
connect(opts)
end
At end it throws the error
I'm trying to run the Sequel example from http://sequel.rubyforge.org/. Everything works fine on sqlite, but fails when I switch to postgres.
This is the connection code:
DB = Sequel.connect(:adapter=>'postgres', :host=>'localhost', :database=>'testing', :user=>'postgres', :default_schema=>'sequel')
This is the error I get:
postgres.rb:145:in `async_exec': PG::Error: ERROR: relation "items" does not exist (Sequel::DatabaseError)
LINE 1: INSERT INTO "items" ("price", "name") VALUES (12.45377636338...
I'm suspecting that the issue is Sequel trying to execute INSERT INTO "items" instead "sequel.items", even though :default_schema is correctly set.
Anyone have any idea what i'm doing wrong?
Thanks in advance.
Edit - this is the code used:
require "rubygems"
require "sequel"
# connect to an in-memory database
#DB = Sequel.sqlite
DB = Sequel.connect(:adapter=>'postgres', :host=>'localhost', :database=>'testing', :user=>'postgres', :default_schema=>'sequel')
# create an items table
DB.create_table :items do
primary_key :id
String :name
Float :price
end
# create a dataset from the items table
items = DB[:items]
# populate the table
items.insert(:name => 'abc', :price => rand * 100)
items.insert(:name => 'def', :price => rand * 100)
items.insert(:name => 'ghi', :price => rand * 100)
# print out the number of records
puts "Item count: #{items.count}"
Looks like you're missing the password in the connect method (that's the only difference from the documentation example). Its common for the password to just be the username, so try that if you're not sure what the password is.
It's also suggested to use a different postgresql user with each project, which also makes naming the user intuitive (the project name.) That avoids potentially clashing names.
Anyway, see if this works:
DB = Sequel.postgres 'testing', host: 'localhost', default_schema: 'sequel',
user: 'postgres', password: 'postgres'
I'm having issues getting Sequel to connect to a MS SQL Database.
I have installed the following:
UnixODBC
FreeTDS
I have configured both software packages, and the following commands allow me to connect to my database without a problem:
isql
tsql
osql
However, when I try it from Ruby code using the Sequel.odbc command, I receive the following error:
ODBC::Error: IM003 (0) [iODBC][Driver Manager]Specified driver could not be loaded.
This is as far as I can get. I used to receive another error first, but managed to solve that by redoing the configuration part. Guess I missed something there.
EDIT
This is the code for my base talker class. It basically loads a YAML file like rails does, holding the database settings and establishes a connection to the database.
This seems to be working, trying it manually returns me a DB object from sequel:
module Talkers
require 'yaml'
require 'sequel'
class BaseTalker
# This function will load the desired settings as a hash from the database.yml file
# in the config folder. The data will be returned as a hash following the standard
# YAML structure.
def self.load_config(name)
cfg = YAML::load(File.open(File.join(ENV['API_ROOT'], 'config', 'database.yml')))
cfg.key?(name) ? cfg[name] : nil
end
# This function will establish a connection with the Florensia User database and return
# the Sequel database object, so that queries can be executed against the database.
def self.connect_to_user_db
settings = self.load_config("florensia_user_#{ENV['RACK_ENV']}")
Sequel.odbc settings['dsn'], :db_type => settings['adapter'], :user => settings['user'], :password => settings['password']
end
end
end
The class below inherits from the talker and performs certain actions for a User. It contains the DB logic specific to the game. When I call this logic, I receive the errors:
module Talkers
require 'yaml'
require 'sequel'
class LoginDbTalker < BaseTalker
#
# Bans the specified User from the game. The function requires the following information
# to be supplied in order for the ban to be properly set:
# - id : The identifier of the account.
# - gm_name : The name of the GM setting the ban.
# - punish_code : The punishment code being applied on the account.
# - days : The duration of the ban in days, starting from today.
#
# The function will return true if the ban has been properly set; otherwise the function
# will return false.
def self.ban_user(options = {})
return false if options.empty?
db = self.connect_to_user_db
ds = db[:tbl_User].filter(:id => options[:id])
ps = ds.prepare(:update, :apply_ban)
ps.call(
:punishcode => options[:punish_code],
:punishstory => "Banned by #{options[:gm_name]}",
:punishdate => Date.today,
:punishfreedate => (options[:days].to_i == -1) ? (Date.today + (30 * 265)) : (Date.today + options[:days].to_i))
true
rescue Exception => e
puts "Exception caught in ban_user: #{e.to_s}"
puts "Provided variables: id=#{options[:id]}, gm_name=#{options[:gm_name]}, punish_code=#{options[:punish_code]}, days=#{options[:days]}"
false
end
#
# Unbans the specified User from the game. The function requires the following information
# to be supplied in order for the ban to be properly lifted:
# - id : The identifier of the account.
# - gm_name : The name of the GM removing the ban.
#
# The function will return true if the ban has been properly lifted; otherwise the function
# will return false.
def self.unban_user(options = {})
db = self.connect_to_user_db
ds = db[:tbl_User].filter(:id => options[:id])
ps = ds.prepare(:update, :lift_ban)
ps.call(
:punishcode => '0',
:punishstory => "Ban removed by #{options[:gm_name]}",
:punishdate => Date.today,
:punishfreedate => Date.today
)
true
rescue Exception => e
puts "Exception caught in unban_user: #{e.to_s}"
puts "Provided variables: id=#{options[:id]}, gm_name=#{options[:gm_name]}"
false
end
#
# Kicks the specified User from the game, regardless on the server or character he currently is on.
# This requires a direct connection to the game servers so a specialized command can be sent that
# instruct the server to close the connection with the offending player.
# The function returns true if the kick succeeded; otherwise false.
def self.kick_player(id)
false
end
end
end
Calling any of the ban/unban functions results in the error message.
EDIT2
I've added the folder /Library/ODBC and linked all config files to there for iODBC. This removes the error I had before and now brings me this error:
ODBC::Error: 01000 (20002) [FreeTDS][SQL Server]Adaptive Server connection failed
So it seems I made some progress again
I recommend you use the tinytds adapter instead of the odbc adapter if you are connecting to Microsoft SQL Server from OS X. I know there are people who have got Sequel/ODBC running on non-Windows boxes, but I only have experience with Sequel/ODBC on Windows.