Skip to content

Invalid interpretation of where.not clause #367

@arthurwozniak

Description

@arthurwozniak

As I cannot find that our bug is intended for this gem, I am opening the issue.

In one place we need to search data by NOT matching specific GPS point. When using SQL condition, it works, but query is corrupted when using ActiveRecord.

some schema information

  create_table "orders", force: :cascade do |t|
    ...
    t.geography "gps", limit: {:srid=>4326, :type=>"st_point", :geographic=>true}
    ...
  end

Let's take a look at the following snippet with SQL statement.

order = Order.find(some_id)

Order.where(id: order.id).where("gps = ST_SetSRID( ST_Point( #{order.gps.x}, #{order.gps.y}), 4326)").explain
 => 
EXPLAIN for: SELECT "orders".* FROM "orders" WHERE "orders"."id" = $1 AND (gps = ST_SetSRID( ST_Point( 14.8569972, 50.8526169), 4326)) [["id", 12009736]]
                                           QUERY PLAN
-------------------------------------------------------------------------------------------------
 Index Scan using orders_id_company_branch_id_idx on orders  (cost=0.43..2.66 rows=1 width=1756)
   Index Cond: (id = '12009736'::bigint)
   Filter: (gps = '0101000020E6100000F3864556C8B62D402AC2F28C226D4940'::geography)
(3 rows)

Order.where(id: order.id).where.not("gps = ST_SetSRID( ST_Point( #{order.gps.x}, #{order.gps.y}), 4326)").explain
 => 
EXPLAIN for: SELECT "orders".* FROM "orders" WHERE "orders"."id" = $1 AND NOT (gps = ST_SetSRID( ST_Point( 14.8569972, 50.8526169), 4326)) [["id", 12009736]]
                                           QUERY PLAN
-------------------------------------------------------------------------------------------------
 Index Scan using orders_id_company_branch_id_idx on orders  (cost=0.43..2.66 rows=1 width=1756)
   Index Cond: (id = '12009736'::bigint)
   Filter: (NOT (gps = '0101000020E6100000F3864556C8B62D402AC2F28C226D4940'::geography))
(3 rows)

But when we use ActiveRecord referencing order.gps, data processed by DB are corrupted using where.not condition.

order = Order.find(some_id)

Order.where(id: order.id).where(gps: order.gps).explain
 => 
EXPLAIN for: SELECT "orders".* FROM "orders" WHERE "orders"."id" = $1 AND "orders"."gps" = $2 [["id", 12009736], ["gps", "0020000001000010e6402db6c8564586f340496d228cf2c22a"]]
                                           QUERY PLAN
-------------------------------------------------------------------------------------------------
 Index Scan using orders_id_company_branch_id_idx on orders  (cost=0.43..2.66 rows=1 width=1756)
   Index Cond: (id = '12009736'::bigint)
   Filter: (gps = '0101000020E6100000F3864556C8B62D402AC2F28C226D4940'::geography)
(3 rows)

Order.where(id: order.id).where.not(gps: order.gps).explain
 => 
EXPLAIN for: SELECT "orders".* FROM "orders" WHERE "orders"."id" = $1 AND "orders"."gps" != $2 [["id", 12009736], ["gps", "0020000001000010e6402db6c8564586f340496d228cf2c22a"]]
                                                                 QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------------------------
 Index Scan using orders_id_company_branch_id_idx on orders  (cost=0.43..2.66 rows=1 width=1756)
   Index Cond: (id = '12009736'::bigint)
   Filter: ((gps)::bytea <> '\x3030323030303030303130303030313065363430326462366338353634353836663334303439366432323863663263323261'::bytea)
(3 rows)

The result of this behavior is that each statement using AR is returning the order object. It seems to be wrong to me. Rails passes correctly serialized GPS point (0020000001000010e6402db6c8564586f340496d228cf2c22a) as filter condition is same as in case using ST_Point, but where.not looks like it performs some extra encoding of data.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions