While executing a simple select - where operation using activerecord execute,
ActiveRecord::Base.connection.execute('select * from spree_variants where sku = "1SB-E4196-00";')
I got this error:
from /Users/abc/.rvm/gems/ruby-2.7.2#cboparts/gems/activerecord-6.0.3.5/lib/active_record/connection_adapters/postgresql/database_statements.rb:92:in `exec'
Caused by PG::UndefinedColumn: ERROR: column "1SB-E4196-00" does not exist
LINE 1: select * from spree_variants where sku = "1SB-E4196-00";
Why it is considering "1SB-E4196-00" as a column but not SKU? The error seems misleading.
Because PostgreSQL expects strings to be bounded in single quotes. While double quotes have a different meaning:
There is a second kind of identifier: the delimited identifier or quoted identifier. It is formed by enclosing an arbitrary sequence of characters in double-quotes ("). A delimited identifier is always an identifier, never a key word.
That means if the following query should work:
ActiveRecord::Base.connection.execute(
"select * from spree_variants where sku = '1SB-E4196-00';"
)
Btw you if you are using Rails and have a SpreeVariant model then you can see in the console how Rails formats and escapes the query like this:
puts SpreeVariant.where(sku: '1SB-E4196-00').to_sql
Related
I have the following as part of an AR query:
.having('COUNT(foo.id) > bar.maxUsers')
This generates an error:
ActiveRecord::StatementInvalid:
PG::UndefinedColumn: ERROR: column bar.maxusers does not exist
^
HINT: Perhaps you meant to reference the column "bar.maxUsers".
I am referencing the column bar.maxUsers.
So, apparently AR downcases the query. How to suppress this behavior?
Rails 4.2.10
PostgreSQL
EDIT: SQL:
SELECT ... HAVING COUNT(foo.id) > bar.maxUsers
So it is happening after the to_sql. Maybe from the execute?
This isn't an ActiveRecord or AREL issue, this is just how case sensitivity works in SQL and PostgreSQL.
Identifiers in SQL (such as table and column names) are case-insensitive unless they're quoted. Standard SQL says that unquoted identifiers are folded to upper case, PostgreSQL folds them to lower case, hence the bar.maxusers in the error message.
The solution is to quote the offending column name:
.having('COUNT(foo.id) > bar."maxUsers"')
Note that you must use double quotes for quoting the identifier as single quotes are only for string literals. Also note that identifier quoting is database-specific: standard SQL and PostgreSQL use double quotes, MySQL uses backticks, SQL Server uses brackets, ...
I am trying to insert some data in my postgres databae like this:
def insert_row(conn, row)
attendee = map_row_to_struct(row)
conn.execute(
<<-SQL
INSERT INTO tmp_attendee_import (email, first_name, last_name)
VALUES("#{attendee.email}", "#{attendee.first_name}", "#{attendee.last_name}");
SQL
)
end
The SQL is evaluated wonderfully as:
INSERT INTO tmp_attendee_import (email, first_name, last_name)
VALUES("myemail#yahoo.com", "Gigel", "Ion");
Yet I get this error:
Failure/Error:
conn.execute(
<<-SQL
INSERT INTO tmp_attendee_import (email, first_name, last_name)
VALUES("#{attendee.email}", "#{attendee.first_name}", "#{attendee.last_name}");
SQL
)
ActiveRecord::StatementInvalid:
PG::UndefinedColumn: ERROR: column "myemail#yahoo.com" does not exist
LINE 2: VALUES("myemail#yahoo.com", "Gigel", "Ion");
^
: INSERT INTO tmp_attendee_import (email, first_name, last_name)
VALUES("myemail#yahoo.com", "Gigel", "Ion");
Anyone has any clue ?
Postgres manual
A string constant in SQL is an arbitrary sequence of characters
bounded by single quotes ('), for example 'This is a string'. To
include a single-quote character within a string constant, write two
adjacent single quotes, e.g., 'Dianne''s horse'. Note that this is not
the same as a double-quote character (").
There is a second kind of identifier: the delimited identifier or
quoted identifier. It is formed by enclosing an arbitrary sequence of
characters in double-quotes ("). A delimited identifier is always an
identifier, never a key word. So "select" could be used to refer to a
column or table named "select", whereas an unquoted select would be
taken as a key word and would therefore provoke a parse error when
used where a table or column name is expected.
TL;DR: Single quotes for string constants, double quotes for table/column names.
BTW, the way you're choosing for inserting records is vulnerable to sql-injection.
I'm trying to use exec_query to run an arbitrary query, with values brought in through bindings, and am getting unexpected errors.
Running this in the console
sql = 'SELECT * FROM foobars WHERE id IN (?)'
name = 'query_name_placeholder'
binds = [FooBar.first]
ActiveRecord::Base.connection.exec_query sql, name, binds
Yields this error:
Account Load (7.9ms) SELECT "foobars".* FROM "foobars" ORDER BY "foobars"."id" ASC LIMIT 1
PG::SyntaxError: ERROR: syntax error at or near ")"
LINE 1: SELECT * FROM foobars WHERE id IN (?)
^
: SELECT * FROM foobars WHERE id IN (?)
ActiveRecord::StatementInvalid: PG::SyntaxError: ERROR: syntax error at or near ")"
LINE 1: SELECT * FROM foobars WHERE id IN (?)
^
: SELECT * FROM accounts WHERE id IN (?)
from /Users/foo_user/.rvm/gems/ruby-2.2.4#foo_project/gems/activerecord-4.2.3/lib/active_record/connection_adapters/postgresql_adapter.rb:641:in `prepare'
It appears the binding syntax is being ignored? I've tried ... WHERE id = ? as well, but to no avail.
mu is too short got you part of the way there. For reference, here is the method's documentation: https://apidock.com/rails/ActiveRecord/ConnectionAdapters/DatabaseStatements/exec_query
He's right in that you will need to use the underlying database's binds syntax to set bind variables in the SQL string. For Oracle this is :1, :2 for PostgreSQL this is $1, $2... so that's step one.
Step two is you need to build bind objects, which are QueryAttribute objects, not just values to be passed in. This is a bit clunky, but here's an example:
binds = [ ActiveRecord::Relation::QueryAttribute.new(
"id", 6, ActiveRecord::Type::Integer.new
)]
ApplicationRecord.connection.exec_query(
'SELECT * FROM users WHERE id = $1', 'sql', binds
)
I just spent a whole day going through unit tests and source code trying to figure that out.
I am currently trying to do a complicated WHERE search on a table using Rails, the trouble is I get the error:
PG::Error: ERROR: column "email" does not exist
LINE 1: SELECT "bans".* FROM "bans" WHERE (Email='' AND IP='' AND (...
^
: SELECT "bans".* FROM "bans" WHERE (Email='' AND IP='' AND (Username='NULL' ))
And I know that column actually exists, and doing a rails dbconsole gives me the following:
Jungle=> select * from bans;
id | Username | IP | Email | Reason | Length | created_at | updated_at
----+----------+----+-------+--------+--------+------------+------------
(0 rows)
So this is definatly in the database, has anyone had any experience with this?
SQL column names are case insensitive unless quoted, the standard says that identifiers should be normalized to upper case but PostgreSQL normalizes to lower case:
Quoting an identifier also makes it case-sensitive, whereas unquoted names are always folded to lower case. For example, the identifiers FOO, foo, and "foo" are considered the same by PostgreSQL, but "Foo" and "FOO" are different from these three and each other. (The folding of unquoted names to lower case in PostgreSQL is incompatible with the SQL standard, which says that unquoted names should be folded to upper case. Thus, foo should be equivalent to "FOO" not "foo" according to the standard. If you want to write portable applications you are advised to always quote a particular name or never quote it.)
You're referencing Email in your SQL:
SELECT "bans".* FROM "bans" WHERE (Email='' ...
but PostgreSQL is complaining about email:
column "email" does not exist
Your unquoted Email is being treated as email because PostgreSQL normalizes identifiers to lower case. Sounds like you created the columns with capitalized names by double quoting them:
create table "bans" (
"Email" varchar(...)
...
)
or by using :Email to identify the column in a migration. If you quote a column name when it is created, then it is not normalized to lower case (or upper case in the SQL standard case) and you'll have to double quote it and match the case forever:
SELECT "bans".* FROM "bans" WHERE ("Email"='' ...
Once you fix Email, you'll have the same problem with IP, Username, Reason, and Length: you'll have to double quote them all in any SQL that references them.
The best practise is to use lower case column and table names so that you don't have to worry about quoting things all the time. I'd recommend that you fix your table to have lower case column names.
As an aside, your 'NULL' string literal:
SELECT "bans".* FROM "bans" WHERE (Email='' AND IP='' AND (Username='NULL' ))
-- -------------------->------------------>---------->---------------^^^^^^
looks odd, are you sure that you don't mean "Username" is null? The 'NULL' string literal and the NULL value are entirely different things and you can't use = or != to compare things against NULL, you have to use is null, is not null, is distinct from, or is not distinct from (depending on your intent) when NULLs might be in play.
It doesn't look like you're error is from a test database but if so try rake db:test:prepare.
In general, be aware that you have 3 databases - Test, Development, Production. So it's easy to get them mixed up and check the wrong one.
I had the same problem here,
but as mu-is-too-short said, PostgreSql can be told to
search with case sensitivity on columns names.
So by implemented this code I managed to bypass the same error that you're facing:
Transaction.find(:all,:conditions => ["(date between ? and ?) AND \"Membership_id\" = ?", Time.now.at_beginning_of_month, Time.now.end_of_month,membership.id])
Noticed the \" sign srrounding the column name? Well.. as annoying as it is, that's the fix to this problem.
I am using AR's sum method for a query and seeing this error when using PostgreSQL:
PGError: ERROR: function sum(character varying) does not exist
LINE 1: SELECT sum("assets".asset_file_size) AS sum_asset_file_size ...
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
SELECT sum("assets".asset_file_size) AS sum_asset_file_size FROM "assets" [0m
I am using the following in my code, which works with MySQL:
Asset.sum(:asset_file_size)
I am trying to get a sum of the asset_file_size column.
What am I missing here?
OH.
In writing my question I worked out this issue.
I had accidentally made the column Varchar rather than an Integer. Hence the request to "explicitly type cast".
We apologise for the incovenience.