How can I avoid autocoercion in Couchbase queries? - json

An N1QL query has a filter WHERE myField < $value.
From experimenting, I see that Couchbase orders the types as follows: boolean < integer < string < JsonArray, even though from my perspective they should not be comparable.
For example, any boolean evaluates as less than any integer; any integer is less than all strings. (9223372036854775807 (Long.MAX_VALUE) evaluates as less than "" (empty string).)
I want to avoid this type-coercion. I want "A" < 1 and "A" > 1 to be false and not to return such values from the filter. (And also, it seems that in Javascript, both these expressions are false, as they should be.)
What are the coercion rules? How do I prevent this?

You have discovered the collation order of N1QL. Here is a fuller explanation:
https://docs.couchbase.com/server/6.0/n1ql/n1ql-language-reference/datatypes.html
If you want to avoid this comparison across types, you can add a clause using the TYPE() function to verify that the two elements being compared are of the same type.
https://docs.couchbase.com/server/6.0/n1ql/n1ql-language-reference/typefun.html
So rather than having $A > 3 you would have ($A > 3) AND (TYPE($A) = TYPE(3)).

Related

What is the use of 'WHERE TRUE' in MySQL

I'm working on my colleague's old project and I found on her code WHERE TRUE AND ID='1' AND STAT='1'.
I've tried to removed TRUE AND so the query become WHERE ID='1' AND STAT='1' and get the same result.
I know we can use TRUEas boolean to search 'where something is true' such as WHERE FLAG = TRUE and this MySQL documentation state that
The constants TRUE and FALSE evaluate to 1 and 0, respectively. The constant names can be written in any lettercase.
I also tried SELECT * FROM MYTABLE WHERE TRUE but it's just the same as SELECT * FROM MYTABLE
what is the purpose of TRUE in her query?
It has no specific functional purpose. Your colleague may have included it if they were adhering to a specific style guide that recommends that all SELECT queries have explicit WHERE clauses. If an explicit WHERE clause is not provided, the default is to select all rows. Adding a WHERE clause that is always true will have the same effect.
Another way this query could have come about is if it was generated by a code generation tool that always had to write in a WHERE clause due to the way it was written.
for example:
myQuery = "SELECT X FROM Y WHERE " + ConditionMet(data)?" AccountID = '" + AcctId + "'" : "1=1";
This means that if ConditionMet(data) is true, then only return rows where AccountID matches the AcctId you are passing in. If it is false, then return all rows.
Adding a "dummy" 1=1 makes the code generator simpler to write. More generally, 1=1 is as legitimate a boolean clause as any other, and can be "dropped" into a conditional expression without having to special-case the query to omit the WHERE clause.
Similarly, adding a WHERE clause that is always false (e.g. "WHERE 1=0") will result in zero rows being returned.
Do note that the example code here is vulnerable to SQL Injection, so it should not be used in cases where you are dealing with AccountID's that you did not produce yourself. There are multiple ways to secure it that are beyond the scope of this answer.
If you're writing your SQLString on runtime, and you might add different "where" clausules but you don't know which of all of them will be the first, it makes it easy as all of them may start with "AND ....."
Example:
SQLString:='SELECT * FROM YOUTABLE WHERE TRUE'
If condition1 THEN SQLString:=SQLString+' AND Whatever=whatever';
If condition2 THEN SQLString:=SQLString+' AND Whatever=whatever';
If condition3 THEN SQLString:=SQLString+' AND Whatever=whatever';
If condition4 THEN SQLString:=SQLString+' AND Whatever=whatever';
otherwhise, you should add the WHERE clause not on the first SQLString:= but on the first condition that happens to be true, which you don't know will it be a priori
it is not as much relevant but if find important where adding dynamic conditions,for example in php.
$condition_stmt="";
if ($start_date !="" && $end_date!="")
{
$condition_stmt="and nos.status_date between '".$start_date."' and '".$end_date."'";
}
else if ($start_date!="")
{
$condition_stmt="and nos.status_date >='".$start_date."'";
}
else
{
$condition_stmt="and nos.status_date <='".$end_date."'";
}
$sql="select * from table where true ".$condition_stmt=.";

Return true when query gives 1

I want to save a true/false in my MySQL database. I'm saving 1/0 in an INT column to do this. When I select it, I get the 1 or 0, but I want it to return true/false to my PHP code, without having to rewrite the database.
Can I use another column type? Should I save it differently?
Update: My question is about not wanting to rewrite the returned value. I'm getting a lot of results from my database. Many of those are true/false, but some are 0s because the price is 0, so I don't want to universally rewrite all 1s and 0s. I also don't want to manually rewrite 10 columns.
To follow up my comment, here's a more detailed response which also covers the PHP side, although this probably belongs on StackOverflow.
I've always just used tinyint, although you can use bool/boolean which are synonyms for tinyint(1)
However as of MySQL 5.0.3 you can use the bit type:
As of MySQL 5.0.3, the BIT data type is used to store bit-field values. A type of BIT(M) enables storage of M-bit values. M can range from 1 to 64.
Next, assuming you have an active column, perhaps to store if a user is active, you could use PHP's automatic type conversion to handle this quite simply.
// Obviously you'd replace this with your database call
$results = [['active' => 1], ['active' => 0]];
foreach($results as $row) {
if ($row['active'] == true) {
echo "true\n";
}
else {
echo "false\n";
}
}
You don't strictly need to do anything.
PHP does not, and can not, use strongly typed variables. So, if you receive an (int) 1 from your query results, you can simply use this 1 as a boolean without rewriting or changing anything.
$intOne = (int) 1; //explicitly treat the variable as an integer
var_dump((bool) $intOne); //treat the variable as a boolean
When used in any boolean context, like if ($variable)... then any of these types will be considered to be false by PHP:
the boolean FALSE itself
the integer 0 (zero)
the float 0.0 (zero)
the empty string, and the string "0"
an array with zero elements
an object with zero member variables (PHP 4 only)
the special type NULL (including unset variables)
SimpleXML objects created from empty tags
... And, most importantly;
Every other value is considered TRUE (including any resource).
Source: PHP Manual > Booleans (english)
So while you can change the storage type of your column in mysql, this won't really change the way PHP handles the variable retrieved from your results at all.
Historically, I've always used a column of type TINYINT(1) to store boolean values in mysql, and as Tom Green points out, recent mysql versions provide a new BIT type, which might be appropriate. To the best of my knowledge, mysql does not currently have an actual boolean data type.
You could just as easily use a column of type VARCHAR(1), though, because PHP can and will use any value as a boolean, thanks to the glorious, majestic, and sometimes maddening, PHP Type Juggling.
If you're trying to use the values you're retrieving for boolean logic, just use the values you receive from mysql like booleans and it will work:
if ($valueFromResults) {
//The value was something like true
} else {
//The value was something like false
}
If you're trying to actually echo out the words "true" and "false", then you're probably best served by explicitly echoing the words out yourself, like this;
if ($valueFromResults) {
echo "true";
} else {
echo "false";
}
or, in my preferred shorthand;
echo ($valueFromResults) ? "true" : "false" ;
Update You mentioned in a comment that you want to pass the values through json_encode() and use them in javascript.
JavaScript treats any real value, like int 1, as true and any empty value, like int 0, or an empty string, as false. So if your json_encode() output gets used in actual JavaScript, the int values will still work as boolean values. So the integer values from your database should still work as they are.
Just check that your integer results are encoded as integers by PHP and not as strings - they should be encoded correctly by default - because "0" == true in javascript, but 0 == false.
For a boolean value (true/false), you should use the mySql type bit or tinyint(1).
$boolean = $mysql_data ? true : false;

Perl if statement, If value = nothing do statement if value = something do statement

I am trying to insert a value from a text box into a SQL table. Although when I leave the text box blank it is adding a blank value which then causes the rest of the form functions to not work.
I want the if statement to check if $animal is blank do nothing. And if $animal has something in it, then to perform the insert into the table.
I am not quite sure what to put where I have put.
if ($animal = " ") {
}
else
if ($animal = "???") {
insertTable("INSERT INTO $table(name) VALUE ('".$animal."')");
Line 24 - 27 is this
sub insertTable {
$statement = shift (#_);
$dbh->do($statement);
} # sub
Perl uses scalar variables, which are quite versatile little things. One of the gotchas with them is that their 'truthiness' isn't always obvious.
If you test a scalar with a Boolean test it will be false if:
It's undefined.
It's an empty string.
It's the number 0.
It's the string "0".
(Arrays test as false if they're undefined or have no elements.)
So the correct answer would be - either used defined to check if the value is defined. Or use a regular expression to pattern match a 'valid' value.
E.g.
if ( $animal =~ m/\w+/ ) {
# Do something
}
\w+ is the Perl regular expression for saying 'one or more word characters' which is alphanumeric and underscore. So your empty string, whitespace only string, or undefined string will all not match this pattern.
Quite a few problems here. Addressing the worst of them:
You're using = which is for assignment, not comparison. Use == to compare numbers and eq to compare strings.
You're comparing your variable to a single space. It's very unlikely that you'll get a single space character in $animal. You'll want to do something either far simpler (if ($animal) { ... }) or more complex (perhaps if ($animal =~ /\w/) { ... }).
You are interpolating user input directly into SQL which you then run against your database. This leads to SQL injection attacks. Far better to use a bind point in your SQL.
In Perl there isn't a null value, only an undefined!
if (defined $value)
{
# Do that
}
else
{
# Do this
}

How does string truthiness work in MySQL?

I wanted to look for records where a certain string field was not blank or null, so I simply wrote SELECT ... FROM myTable WHERE x, assuming that blank and null strings would evaluate to false, but that doesn't appear to be the case.
The string "02306" is true, whereas "N06097EIP" is somehow false.
What's going on?
Edit: I'm aware of the workarounds, I simply want to know how the casting works.
In these expression string are first converted to numbers. "02306" is converted to 2306 which is >0 and therefore considered true, while "N06097EIP" (starting with non-digit) is converted to 0, which is evaluated as false.
Compare results of:
select convert("N06097EIP",signed)
and
select convert("02306",signed)
In a boolean context, such as
WHERE x
the expression x will be evaluated as an integer value. Note that MySQL considers a BOOLEAN as a numeric type.
http://dev.mysql.com/doc/refman/5.1/en/numeric-type-overview.html
It doesn't matter what type the expression x is; it's either an INTEGER type, or it will be converted to an INTEGER type, according to the documented conversion rules.
The end result is that the expression x will be evaluated to be either NULL, integer zero, or integer non-zero.
And those correspond to the boolean "truthiness" values of NULL, FALSE and TRUE.
The reason '02306' is considered TRUE is because this string converts to integer value 2306, which is non-zero.
The reason 'N06097EIP' is considered FALSE is because this string converts to integer value 0.
To can run a simple test case to verify:
SELECT IF( 0 = 'N06097EIP', 'is zero', 'is not zero')
SELECT 0 = 'N06097EIP'
The behavior you observe is entirely expected. It's all pretty straightforward. You may have been caught unawares, because the normative pattern is for us to avoid this type of evaluation and to instead use "workarounds" (as you put it) to return a boolean.
Don't try to be too cute about this with syntactic shortcuts. If nothing else, it's always harder on the next developer who has to figure out what you were doing.
Just spell out what you want.
SELECT *
FROM myTable
WHERE x IS NOT NULL AND x <> '';
or, if you'd prefer:
SELECT *
FROM myTable
WHERE COALESCE(x, '') <> '';

JPA2 Criteria API .as(String.class) casting to char(1) - How do I work around this?

Using the criteria api, I have a query which does something like:
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<SourcePath> pathQuery = cb.createQuery(SourcePath.class);
Root<SourcePath> pathRoot = pathQuery.from(SourcePath.class);
pathQuery.where(cb.equal(cb.literal(0x00010002).as(String.class), (pathRoot.get(SourcePath_.path))));
TypedQuery<SourcePath> query = entityManager.createQuery(pathQuery);
query.getResultList();
The resulting sql query results something like:
select ...snip aliases... from SourcePath where cast(x'00010002', char(1)) = path;
(path would be some nasty old alias, but that's irrelevant).
This query is incorrect. Particularly, the cast: cast(x'00010002', char(1)) is not a cast to as string, as specified by .as(String.class), instead it should be either cast(x'00010002', char), or cast(x'00010002', char(N) where N is a suitably big enough number.
I've isolated the cause of this cast failure to the MySqlDialect provided by org.hibernate. Particularly:
public String getCastTypeName(int code) {
if ( code==Types.INTEGER ) {
return "signed";
}
else if ( code==Types.VARCHAR ) {
return "char";
}
...snip...
}
Which farther down the chain is interpreted as a char, which is registered by the dialog: registerColumnType( Types.CHAR, "char(1)" );.
Finally, to my question. How can I work around this behaviour? Do I report it as a bug to Hibernate? Do I extend the Dialog and correct the returned type from getCastTypeName? Is there an alternative to .as which will appropriately cast? Or, should I use strings everywhere I'm currently using hex-values to avoid touching edge cases of hibernate's implementation?
Thanks
idb
IMHO, you should use a String literal like cb.literal("\u0001\u0002"). Reasons:
It's the same thing as cb.literal(0x00010002).as(String.class), but less verbose.
Does not hit any "edge case"
It's clearer: does "0x00010002" be treated in Big Endian or LE? Which encoding should be used?
You can improve legibility using constants (e.g.: cb.literal(ASCII_SOH + ASCII_STX) where SOH="\u0001" and STX="\u0002").
EDIT: Adding a better description, since I didn't see the "Or, should I use strings everywhere I'm currently using hex-values"