PG::UndefinedFunction: ERROR: operator does not exist: text = boolean - ruby-on-rails

There is a JSONB information field with this structure:
{
"ignore"=>false
}
I want to get all records whose ignore field is true:
#user.posts.where("information ->> 'ignore' = TRUE")
This line throws an error:
PG::UndefinedFunction: ERROR: operator does not exist: text = boolean
And I could not find anything in Google. Everywhere we are talking about textual meanings. But there is nothing about booleans.

You must cast the result of information->>'ignore' to boolean:
#user.posts.where("(information ->> 'ignore')::boolean = TRUE")

I had the same issue in upgrading to Rails 5/6. It seems the way the pg gem casts has changed a little, but this works for me:
#user.posts.where("(information ->> 'ignore')::boolean = ?", true)
When an argument is added as the second parameter to a where method call, ActiveRecord will do the work to cast this appropriately for you. If you add .explain to the query above, you should see something like:
EXPLAIN for: SELECT ... AND ((information ->> 'ignore')::boolean = TRUE) ...

Related

Define default value for discriminated union

I would like to define a default value for a discriminated union, like this:
open System.Linq
type Result =
| Ok
| Error
let results : seq<Result> = [] |> Seq.ofList
let firstResult = results.FirstOrDefault()
// I want firstResult to be Error, currently it is null.
option<'a> works in this way (firstResult would be None), so it should be possible.
Thanks for your help.
Edit:
I'm using SQLDataProvider and would like to write code like
let userEmail =
query {
for user in dbContext.Public.Users do
where (user.Id = 42)
select (Ok user.Email)
headOrDefault
// should result in Error
// when no user with Id=42 exists
}
My actual result type looks like this:
type Result<'a> =
| Ok of 'a
| Failure of string // Expected, e. g. trying to log in with a wrong password
| Error // Unexpected
Returning an option, the caller would not be able to differentiate between failures and errors.
In general F# avoids the concept of defaults, instead making everything as explicit as possible. It's more idiomatic to return an option and for the caller to decide what to default to for a particular use case.
The FirstOrDefault method can only return .NET's default value for any type. So for any class it would return null, and for a number it would return zero.
I would recommend this approach instead assuming your desired default is Ok:
results |> Seq.tryHead |> Option.defaultValue Ok
You might be able to do this using the UseNullAsTrueValue compilation parameter. This tells the compiler to use null as an internal representation of one of the parameter-less cases. It is a bit of a hack (because this is meant mostly for performance optimization and here we are misusing it somewhat, but it might work).
I don't have SQL database setup to try that with the SQL provider, but the following works in memory and I think it should work with SQL too:
[<CompilationRepresentation(CompilationRepresentationFlags.UseNullAsTrueValue)>]
type Result<'a> =
| Ok of 'a
| Failure of string
| Error
let userEmail =
query {
for a in [1] do
where (a = 2)
select (Ok a)
headOrDefault }
If you run this, F# interactive prints that userEmail = null, but that's fine and the following is true:
userEmail = Error
If you want to represent issues with the data or the query parameters, such as not finding a particular record, as being distinct from other types of failures, such as not connecting to the database or hitting an unhandled exception, you can create a discriminated union to represent your expected data/query problems explicitly. You can then return that DU instead of string as the data for the Error case, and you won't really need the Failure case. Consider something like this:
type SqlError =
| NoMatchingRecordsFound of SqlParameter list
| CouldNotConnectToDatabase
| UserDoesNotHaveQueryPermissions
| UnhandledException of exn
I would suggest taking a look at Railway-Oriented Programming and following the pattern of defining a discriminated union for your different error cases. You can still include an error message in that DU, but I would suggest using explicit cases for all your different expected failures, and then having an "UnhandledException" or similar case for your unexpected errors, perhaps with an exn for the data.
If you're interested, I have a library on GitHub/NuGet that puts all the ROP stuff together and adds interoperability with the Task, Async, and Lazy types in one computation builder, so you don't have to include all that code in your own project.
EDIT
Here's a complete example of doing it ROP-style (using the linked framework):
open FSharp.Control
open System.Linq
// Simulate user table from type-provider
[<AllowNullLiteral>]
type User() =
member val Id = 0 with get,set
member val Name = "" with get,set
// Simulate a SQL DB
let users = [User(Id = 42, Name = "The User")].AsQueryable()
// Our possible events from the SQL query
type SqlEvent =
| FoundUser
| UserIdDoesNotExist of int
| CouldNotConnectToDatabase
| UnhandledException of exn
// Railway-Oriented function to find the user by id or return the correct error event(s)
let getUserById id =
operation {
let user =
query {
for user in users do
where (user.Id = id)
select user
headOrDefault
}
return!
if user |> isNull
then Result.failure [UserIdDoesNotExist id]
else Result.successWithEvents user [FoundUser]
}
Caling getUserById 42 would return a successfully completed operation with the user and the event FoundUser. Calling getUserById for any other number (e.g. 0) would return a failed operation with the error event UserIdDoesNotExist 0. You would add more events as necessary.

Grails Cannot get property 'id' on null object

I am getting this error: Cannot get property 'id' on null object and i can't understand the problem.
Here is my code in provionController.groovy
CreateCriteria returns one element, I verified in the database, size = 1 but when i tried to display the Id, I get this error.
def prov_model = null
def model = Provision_model.CreateCriteria{
gilt_air{
eq("air",air)
}
gilt_coo{
eq("coo",coo)
}
le("date_from", per.begin)
ge("date_to", per.end)
eq("active", 1)
}
println(model.size())
prov_model = model[0]
println(prov_model.id)
but when I am getting it directly by method get(), it hasn't no problem
prov_model = Provision_model.get(57)
println(prov_model.id)
1st: the method is called createCriteria(), not CreateCriteria()
2nd: the method itself DOES NOT invoke any db operation. You have to call list() or get() etc. on it to get the query results
If order to execute the query and store the results in model, replace this
def model = Provision_model.CreateCriteria
with
def model = Provision_model.withCriteria
#injecteer and #Donal both have very valid input. First, you need to address the syntax issue, here is an example of one way to format your criteria:
def prov_model = null
def model = Provision_model.createCriteria().get() {
gilt_air{
eq("air",air)
}
gilt_coo{
eq("coo",coo)
}
le("date_from", per.begin)
ge("date_to", per.end)
eq("active", 1)
}
Keep in mind that by using .get() you are limiting the return from the criteria to one record. Second, if you try writing the criteria both ways, using withCriteria and using the format above and it still doesn't work, your problem may be in the domain model or the configuration of the database.

Optional parameter type mismatch

I'm working with F# 3.1 and I've twice today come across a compile error I can't explain and have had no luck looking up.
In the following code:
type OtherClass(value:int, ?flag:bool) =
member this.DoSomething = (value,flag)
and
MyClass(value:int, ?flag:bool) =
let _dummy = new OtherClass(value,flag)
The compiler says that in my MyClass where I make the call to OtherClass's ctor, the type of flag is incorrect. Specifically, it says that it wants a bool, not a bool option. Yet, it is defined as a bool option according to all that I can see.
Any idea why flag is being seen as a regular bool, and not a bool option as its defined?
Edit:
Upon further reflection, I guess I know whats going on. Optional parameters are inside the method treated as option, but outside, they want the real thing. Something swaps the value out on the call to an option type. So that means that to do what i was trying to do we need something like this
type OtherClass(value:int, ?flag:bool) =
member this.DoSomething = (value,flag)
and
MyClass(value:int, ?flag:bool) =
let _dummy =
match flag with
| None -> new OtherClass(value)
| Some(p) -> new OtherClass(value, p)
This works, but seems a bit verbose. Is there a way to pass an optional directly into an optional parameter like this without resorting to different calls?
Here's the way to do this:
type
OtherClass(value:int, ?flag:bool) =
member this.DoSomething = (value,flag)
and
MyClass(value:int, ?flag:bool) =
let _dummy = new OtherClass(value, ?flag=flag)
Quoting ยง8.13.6 of the spec:
Callers may specify values for optional arguments in the following
ways:
By propagating an existing optional value by name, such as
?arg2=None or ?arg2=Some(3) or ?arg2=arg2. This can be useful when
building a method that passes optional arguments on to another method.

Strange behavior of gorm finder

In a controller I have this finder
User.findByEmail('test#test.com')
And works.
Works even if I write
User.findByEmail(null)
But if i write
User.findByEmail(session.email)
and session.email is not defined (ergo is null) it throw exception
groovy.lang.MissingMethodException: No signature of method: myapp.User.findByEmail() is applicable for argument types: () values: []
Is this behavior right?
If i evaluate "session.email" it give me null so I think it must work as it do when I write
User.findByEmail(null)
Even more strange....
If I run this code in groovy console:
import myapp.User
User.findByEmail(null)
It return a user that has null email but if I run the same code a second time it return
groovy.lang.MissingMethodException: No signature of method: myapp.User.findByEmail() is applicable for argument types: () values: []
You can't use standard findBySomething dynamic finders to search for null values, you need to use the findBySomethingIsNull version instead. Try
def user = (session.email ? User.findByEmail(session.email)
: User.findByEmailIsNull())
Note that even if User.findByEmail(null) worked correctly every time, it would not necessarily give you the correct results on all databases as a findBySomething(null) would translate to
WHERE something = null
in the underlying SQL query, and according to the SQL spec null is not equal to anything else (not even to null). You have to use something is null in SQL to match null values, which is what findBySomethingIsNull() translates to.
You could write a static utility method in the User class to gather this check into one place
public static User byOptEmail(val) {
if(val == null) {
return User.findByEmailIsNull()
}
User.findByEmail(val)
}
and then use User.byOptEmail(session.email) in your controllers.
Jeff Brown from grails nabble forum has identified my problem. It's a GORM bug. see jira
More info on this thread
This jira too
I tried with debugger and it looks it should be working, as you write. Maybe the groovy itself is a little bit confused here, try to help it this way:
User.findByEmail( session['email'] )

Check for nil warns about unexpected nil

I have the following check for nil:
client = !deal['deal']['party']['party'].nil? ? deal['deal']['party']['party']['company_id'] : ""
but still I get:
You have a nil object when you didn't expect it!
You might have expected an instance of ActiveRecord::Base.
The error occurred while evaluating nil.[]
How can I prevent this?
I don't know Ruby, but I think it goes wrong before the .nil:
deal['deal']['party']['party']
^ ^ ^
The arrows indicate possible nil indexes. For example, what if ["deal"] is nil or the first ["party"] is nil?
You might want to have a look at the andand game:
http://andand.rubyforge.org/
By checking !deal.nil? and !deal['deal'].nil? and !deal['deal']['party'].nil? and !deal['deal']['party']['party'].nil?
At each step, you can use an appropriate method built in NilClass to escape from nil, if it were array, string, or numeric. Just add to_hash to the inventory of this list and use it.
class NilClass; def to_hash; {} end end
client = deal['deal'].to_hash['party'].to_hash['party'].to_hash['company_id'].to_s
You can also do:
client = deal.fetch('deal', {}).fecth('party', {}).fetch('party', {}).fetch('company_id', '')

Resources