Disable double underscore behaviour for Sequel - ruby

How disable double underscore behaviour for Sequel?
I work with legacy data base schema where I have a lot of columns with "__" in name.
db[:abc].insert({vector_a__c: "356"})
Sequel::DatabaseError: PG::UndefinedColumn: ERROR: column "vector_a" of relation "abc" does not exist
LINE 1: INSERT INTO "abc" ("vector_a"."c") VALUES ('356') RETURNING ...

In general you want to wrap it in an identifier:
db[:abc].insert(Sequel.identifier(:vector_a__c) => "356")
Using a string as an identifier only works in very few cases for backwards compatibility, where it is unambiguous (i.e. where an SQL string would not be valid).

Double underscore behavior disabling when you transfer columns names as string but not as symbol.
For example:
db[:abc].insert({"vector_a__c" => "356"})

Related

Postgres, How can I deny to create a database with a werid name (trailing space, leading space, or even avlid using space in a database name)

I'm trying to add some constraints on database creation command in PostgreSQL.
Currently, I could do
psql -c "CREATE database \" x y\"\"z' \""
Then, I will get a database named literally " x y"z' " (without the double-quotes boundary).
It seems that pgsql supports any characters in it's database name, which is cool.
But it leads me headaches when I am doing automation stuff with bash script.
Yes, some additional work could be done to handle these cases in script. But I think these kind of names are actually meaningless (at least in my situation :), so, is there a way to add some constraints on database naming. For example, only allow [a-zA-Z0-9_.]+.
Just do not use double quotes, which you should avoid anyway if at all possible. See Documentation:
SQL identifiers and key words must begin with a letter (a-z, but also
letters with diacritical marks and non-Latin letters) or an underscore
(_). Subsequent characters in an identifier or key word can be
letters, underscores, digits (0-9), or dollar signs ($). Note that
dollar signs are not allowed in identifiers according to the letter of
the SQL standard, so their use might render applications less
portable. The SQL standard will not define a key word that contains
digits or starts or ends with an underscore, so identifiers of this
form are safe against possible conflict with future extensions of the
standard. ... 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. ... Quoted
identifiers can contain any character, except the character with code
zero. (To include a double quote, write two double quotes.) This
allows constructing table or column names that would otherwise not be
possible, such as ones containing spaces or ampersands.
Not doubling quoting in you examples makes those names invalid and Postgres has no problem telling about it. So just do not use them.
Alternately you could create an event trigger. Within there you can restrict object names as needed, esp useful if you have strict naming standards. This would allow for database enforcement of those standards;
create function app_validate_table_name()
returns event_trigger
language 'plpgsql'
as $$
begin
if obj.object_identity ~! '[A-Za-z$_][[A-Za-z0-9$_]{0,62}'
then
raise exception 'App Error: Request Name (%) is invalid for <Your App Name here>',obj.object_identity;
end if
return;
end ;
$$;
create event trigger app_table_event_trigger on ddl_command_end
when tag in ('ALTER TABLE', 'CREATE TABLE')
execute procedure app_validate_table_name();
While the same can be applied to other objects it unfortunately does not seem to apply to creating a database itself.
Disclamer: The above has NOT been tested.

SQLAlchemy: Force column alias quoting

I want SQLAlchemy to generate the following SQL code:
SELECT t171 AS "3Harm" FROM production
I've been playing around with something similar to this SQLAlchemy ORM snippet:
session.query(Production.t171.label('3harm'))
The problem here is that this doesn't properly quote "3harm" in the generated SQL. Instead of "3harm" this generates the unquoted 3harm, which is invalid because it starts with a numerical character and therefore raises the following Oracle exception:
ORA-00923: FROM keyword not found where expected
I can get this to work by capitalizing any character in the alias name:
session.query(Production.t171.label('3Harm'))
But I would still prefer to use all lowercase column names since the rest of my program is standardized for all lowercase. Any idea how to force quote the lowercase version?
Found the solution while looking for something else.
Any column can be forced to use quotes with column.quote = True.
So for the original example:
column = Production.t171.label('3harm')
column.quote = True
session.query(column)
Success!
The SQL you want to generate isn't valid; rather than this:
SELECT t171 AS '3Harm' FROM production
... you need the identifier to be enclosed in double quotes, not single quotes:
SELECT t171 AS "3Harm" FROM production
So it looks like you should be able to do this:
session.query(Production.t171.label('"3harm"'))
or maybe:
session.query(Production.t171.label("3harm"))
But I don't use SQLAlchemy and I don't have any way to check if either is valid; you might need to escape the double quotes in the first one, for instance, though from this perhaps the second is more likely to work... and I don't understand why 3Harm would work unquoted.

ASCII for SYS_CONNECT_BY_PATH ORACLE sql

Is there any way to use ascii code for value separator in SYS_CONNECT_BY_PATH.
For example in SYS_CONNECT_BY_PATH(columnname,'!'),
I want to use the ASCII value of !(33) instead of actual symbol. Also, can i use the ascii value of ENTER (13) as value separator?
Thank you.
You can use the chr function to replace a character with it's numeric equivalent.
SYS_CONNECT_BY_PATH(column name, chr(33))
Or to use a line feed, which should also be fine:
SYS_CONNECT_BY_PATH(column name, chr(13))
It's not strictly ASCII as it depends on your character set, but it will probably work for you. You can see the numeric values using the reverse ascii function, which also isn't really quite ASCII, but again close enough especially if you're always using the same character set. So ascii('!') would give you 33.
As you've discovered, giving anything except a fixed string literal gives:
SQL Error: ORA-30003: illegal parameter in SYS_CONNECT_BY_PATH
function
30003. 00000 - "illegal parameter in SYS_CONNECT_BY_PATH function"
*Cause:
*Action: use a non-empty constant string as the second argument,
then retry the operation.
This is why I usually test things before posting, but this seemed so simple... You can get around that with replace:
REPLACE(SYS_CONNECT_BY_PATH(column name, '/'), '/', chr(33))
Borrowing an example from the manual:
SELECT LPAD(' ', 2*level-1)
||replace(SYS_CONNECT_BY_PATH(last_name, '/'),'/',chr(33)) "Path"
FROM employees
START WITH last_name = 'Kochhar'
CONNECT BY PRIOR employee_id = manager_id;
Path
--------------------------------------------------
!Kochhar
!Kochhar!Greenberg
!Kochhar!Greenberg!Faviet
!Kochhar!Greenberg!Chen
!Kochhar!Greenberg!Sciarra
!Kochhar!Greenberg!Urman
!Kochhar!Greenberg!Popp
!Kochhar!Whalen
!Kochhar!Mavris
!Kochhar!Baer
!Kochhar!Higgins
!Kochhar!Higgins!Gietz
All the credit goes to Sanjeev Chauhan. Update 7/25/2017: This turned out to be a SQL Developer 4.2.0 and 17.2.0 bug. In SQLPlus and SQL Developer 3.2.2 the statement works fine.
Fix: set secureliterals off;
The source is https://community.oracle.com/thread/4065282
I had changed the version from 4.2.0 to 4.1.1.19 and my piece of code worked. Also be aware that I couldn't find "secureliterals" in version 4.2.0

How to insert Ruby array items into SQLite?

I want to execute my INSERT strings within a Ruby file that has the following array:
names_arr = ["Jack", "Susan", "Peter"]
In the same Ruby file, I've required the SQLite3 gem and have this code:
names_arr.each do |each_name|
db.execute('INSERT INTO users (name) VALUES ("#{each_name}");')
end
I am expecting this in my "users" table:
id name
1 Jack
2 Susan
3 Peter
Instead, I'm getting this:
id name
1 #{each_name}
2 #{each_name}
3 #{each_name}
It's clear I'm not interpolating properly, but I've tried a lot of different things and can't figure out where I'm wrong.
I am using SQLite3 and Ruby.
You have several problems in this code:
names_arr.each do |each_name|
db.execute('INSERT INTO users (name) VALUES ("#{each_name}");')
end
You're trying to use Ruby's string interpolation inside a single quoted string but string interpolation only works in double quoted strings (and heredocs, %Q{...} strings, regex literals, ...).
Standard SQL uses single quotes for string literals, not double quotes. Some databases let you get away with double quotes but it is a really bad habit to get into.
You shouldn't use string interpolation to build SQL anyway, you're not hacking PHP in 1999 so you should use placeholders and let the database library deal with the quoting.
The SQLite driver's execute understands ? placeholders so you can say this:
names_arr.each do |each_name|
db.execute('INSERT INTO users (name) VALUES (?)', each_name)
end

Regular expression use in oracle

I am trying to get the user information from a table (named userinfo) from the Oracle database on the basis of name.
In database name can be like {"Ashwani Dahiya","Ashwani kumar","ashwani dahiya","ashwani kumar","Ashwani dahiya","ashwani Dahiya","ashwani"}
So I want if I search for name "ashwani" then it should return the above whole list of users
select *
from userinfo
where regexp_like('name','Ashwani([[:space:]]* | [[:space:]]+[a-zA-Z0-9]*)','i')
I had tried this but "no result found".
This expression
regexp_like('name','Ashwani([[:space:]]* | [[:space:]]+[a-zA-Z0-9]*)','i')
searches inside the string 'name' not inside a column called name. When you want to refer to a column you don't need quotes.
So you need your expression to:
regexp_like(name,'Ashwani([[:space:]]* | [[:space:]]+[a-zA-Z0-9]*)','i')
(Note the missing single quote ' around name).
But I don't see the need for a regex here. a simple
where lower(name) like '%ashwani%'
will also do the trick (and will not be slower than the regex because neither of them will use an index)
I assume you meant to use the column called NAME and not the literal 'name' so try without the quotes around name and also lose the spaces around the "|":
select * from userinfo where regexp_like(name,'Ashwani([[:space:]]*|[[:space:]]+[a-zA-Z0-9]*)','i')
Note that for testing purposes you can try it with literals like so:
select *
from dual
where regexp_like('ashwani ','Ashwani([[:space:]]*|[[:space:]]+[a-zA-Z0-9]*)','i')
As mentioned by a_horse_with_no_name, you may get away with using the "LIKE" but I'm guessing you're looking for just "ashwani" and not "ashwaniX" (where X is some other letter) in which you could have just
lower(name) = 'ashwani'
or lower(name) LIKE 'ashwani %'
The trouble with regex functions is that they can be rather slow if you're working with a lot of data.
if you wish to cling to regexes, try
select *
from userinfo
where regexp_count(name, '^ashwani', 1, 'i') = 1
;
but there really is no need for if the matches will always start with the literal to compare against.

Resources