I need to parse an SQL name for PostgreSQL, and to be sure it is always compliant.
I know that, for example, an SQL name/identifier can even consist of a single white space.
What I'm trying to find out is - is there a single use case within the entire PostgreSQL syntax where it is possible to pass in an empty "" SQL name/identifier and still to be considered valid?
I want to know whether I should parse "" as always invalid in PostgreSQL or not.
An empty quoted identifier is not valid as per the SQL standard.
You get the following error message when you try to create a table with "":
ERROR: zero-length delimited identifier at or near """"
And what would it identify anyways? There is no such thing like an identifier that identifies the empty identifier.
You can find out more about the syntax here: http://www.postgresql.org/docs/9.4/static/sql-syntax-lexical.html
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. The
length limitation still applies.
Related
How do we use numbers as tags for dollar quoted strings?
INSERT INTO table(user_id,user_data)
values (22176,to_jsonb($123${"name": "Helo. $ what is this $"}$123$::jsonb))
The above query fails, however if I replace numeric tags with alphabetic then it works. I didn't find anything in the documentation against using numbers for tags.
I need to make my tags as unique as possible, since I'm trying to avoid a situation where user content inside the jsonb matches my tags, for example
$abc${"name": "hello $abc$"}$abc$
I was trying to use UUIDs but it's not accepting numbers as tags.
Note: It's an example query, I have a lot of ' in my json values.
You cannot use $123$, because PostgreSQL uses $1, $2 etc. as placeholders in prepared statements. $a1$ would be ok.
To get a rare string to avoid collisions, drop on the keyboard a few times and make sure not to hit a digit first.
Let's consider the following postgres query:
SELECT *
FROM "MY_TABLE"
WHERE "bool_var"=FALSE
AND "str_var"='something';
The query fails to respond properly when I remove quotes around "str_var" but not when I do the same around "bool_var". Why? What is the proper way to write the query in that case, no quotes around the boolean column and quotes around the text column? Something else?
PostgreSQL converts all names (table name, column names etc) into lowercase if you don't prevent it by double quoting them in create table "My_Table_ABC" ( "My_Very_Upper_and_Lowercasy_Column" numeric,...). If you have names like this, you must always double quote those names in selects and other references.
I would recommend not creating tables like this and also not using chars outside a-z, 0-9 and _. You can not guarantee that every piece of software, library etc ever to be used against your database will support case-sensitivity. It's also tedious to remember and doing this double quoting.
Thanks to #TimBiegeleisen's comment, I was able to pinpoint the problem; I used a reserved keyword ("user") as a column name.
Link to reserved keywords in the doc: https://www.postgresql.org/docs/current/sql-keywords-appendix.html.
Now I know not to use quotes to query column names, but rather to avoid reserved keywords as column names.
In a PostgreSQL query, executed by FreeRADIUS, I want to do something similar to (the table names and values are just examples):
SELECT name
FROM users
WHERE city LIKE '%blahblah%';
but there is a catch: the blahblah value is contained in a FreeRADIUS variable, represented with '%{variable-name}'. It expands to 'blahblah'.
Now my question is: How do I match the %{variable-name} variable to the value stored in the table using the LIKE operator?
I tried using
SELECT name
FROM users
WHERE city LIKE '%%{variable-name}%';
but it doesn't expand correctly like that and is obviously incorrect.
The final query I want to achieve is
...
WHERE city LIKE '%blahblah%';
so it matches the longer string containing 'blahblah' stored in the table, but I want the variable to expand dynamically into the correct query. Is there a way to do it?
Thanks!
Wild guess:
Assuming that FreeRADIUS is doing dumb substitution across the entire SQL string, with no attempt to parse literals, etc, before sending the SQL to PostgreSQL then you could use:
SELECT name
FROM users
WHERE city LIKE '%'||'%{variable-name}'||'%';
Edit: To avoid the warnings caused by FreeRADIUS not parsing cleverly enough, hide the %s as hex chars:
WHERE city LIKE E'\x25%{variable-name}\x25';
Note the leading E for the string marking it as a string subject to escape processing.
SELECT name
FROM users
WHERE city LIKE '%%'||'%{variable-name}'||'%%';
Is slightly cleaner. %% is FreeRADIUS' escape sequence for percents.
Is there any way to determine in JDBC that a column or table has been created using "" notation? The metadata get columns does not seem to return such information.
PS: our customer model has been unfortunately created in such a way :-(
If the column name is returned in mixed case or all upper then it has been created using quotes.
So if columnName.equals(columnName.toLowerCase()) == true then no quotes were used. Or to be more prices: no quotes are needed.
Note that this is Postgres specific. Other databases store unquoted names in uppercase, some store them "as-is" without requiring quotes.
I have some code which utilizes parameterized queries to prevent against injection, but I also need to be able to dynamically construct the query regardless of the structure of the table. What is the proper way to do this?
Here's an example, say I have a table with columns Name, Address, Telephone. I have a web page where I run Show Columns and populate a select drop-down with them as options.
Next, I have a textbox called Search. This textbox is used as the parameter.
Currently my code looks something like this:
result = pquery('SELECT * FROM contacts WHERE `' + escape(column) + '`=?', search);
I get an icky feeling from it though. The reason I'm using parameterized queries is to avoid using escape. Also, escape is likely not designed for escaping column names.
How can I make sure this works the way I intend?
Edit:
The reason I require dynamic queries is that the schema is user-configurable, and I will not be around to fix anything hard-coded.
Instead of passing the column names, just pass an identifier that you code will translate to a column name using a hardcoded table. This means you don't need to worry about malicious data being passed, since all the data is either translated legally, or is known to be invalid. Psudoish code:
#columns = qw/Name Address Telephone/;
if ($columns[$param]) {
$query = "select * from contacts where $columns[$param] = ?";
} else {
die "Invalid column!";
}
run_sql($query, $search);
The trick is to be confident in your escaping and validating routines. I use my own SQL escape function that is overloaded for literals of different types. Nowhere do I insert expressions (as opposed to quoted literal values) directly from user input.
Still, it can be done, I recommend a separate — and strict — function for validating the column name. Allow it to accept only a single identifier, something like
/^\w[\w\d_]*$/
You'll have to rely on assumptions you can make about your own column names.
I use ADO.NET and the use of SQL Commands and SQLParameters to those commands which take care of the Escape problem. So if you are in a Microsoft-tool environment as well, I can say that I use this very sucesfully to build dynamic SQL and yet protect my parameters
best of luck
Make the column based on the results of another query to a table that enumerates the possible schema values. In that second query you can hardcode the select to the column name that is used to define the schema. if no rows are returned then the entered column is invalid.
In standard SQL, you enclose delimited identifiers in double quotes. This means that:
SELECT * FROM "SomeTable" WHERE "SomeColumn" = ?
will select from a table called SomeTable with the shown capitalization (not a case-converted version of the name), and will apply a condition to a column called SomeColumn with the shown capitalization.
Of itself, that's not very helpful, but...if you can apply the escape() technique with double quotes to the names entered via your web form, then you can build up your query reasonably confidently.
Of course, you said you wanted to avoid using escape - and indeed you don't have to use it on the parameters where you provide the ? place-holders. But where you are putting user-provided data into the query, you need to protect yourself from malicious people.
Different DBMS have different ways of providing delimited identifiers. MS SQL Server, for instance, seems to use square brackets [SomeTable] instead of double quotes.
Column names in some databases can contain spaces, which mean you'd have to quote the column name, but if your database contains no such columns, just run the column name through a regular expression or some sort of check before splicing into the SQL:
if ( $column !~ /^\w+$/ ) {
die "Bad column name [$column]";
}