discourse / mini_sql

a minimal, fast, safe sql executor
MIT License
395 stars 18 forks source link

Allow multiple occurencies of builder statements #42

Closed xronos-i-am closed 2 years ago

SamSaffron commented 3 years ago

Mixed on supporting this, do you have a real world example where you need it?

xronos-i-am commented 3 years ago

Yes, we have the polymorhic search

class SearchFilter

  include Datagrid

  scope do
    MINI_SQL.build(<<~SQL)
      (SELECT request_id, id AS notice_log_id, 0 AS request_log_id, 0 AS record_version_id, created_at FROM notice_logs /*notice_where*/)
        union all
      (SELECT request_id, 0 AS notice_log_id, id AS request_log_id, 0 AS record_version_id, created_at FROM request_logs /*request_where*/)
        union all
      (SELECT request_id, 0 AS notice_log_id, 0 AS request_log_id, id AS record_version_id, created_at FROM record_versions /*record_where*/)
    SQL
  end

  filter :request_id, :string, header: 'Искать по request_id' do |val|
    sql_literal(
      notice_where:  "WHERE (request_id = '#{val}')",
      request_where: "WHERE (request_id = '#{val}')",
      record_where:  "WHERE (request_id = '#{val}')",
    )
  end

end
SamSaffron commented 2 years ago

Sorry, I have been thinking about this one, it is technically a breaking change, hard for me to decide what to do here.

Can you have a quick look at why tests are failing?

xronos-i-am commented 2 years ago

Can you have a quick look at why tests are failing?

The method to_set is the part of stdlib, which seems to be doesn't loaded. I have no idea why it works in 2.7

xronos-i-am commented 2 years ago

Sorry, I have been thinking about this one, it is technically a breaking change, hard for me to decide what to do here.

Why do you think so?

The current version of code gives us silent error in case of multiple occurencies:

      (SELECT request_id, id AS notice_log_id, 0 AS request_log_id FROM notice_logs WHERE request_id=1)
        UNION ALL
      (SELECT request_id, 0 AS notice_log_id, id AS request_log_id FROM request_logs /*where_request_id*/)

And if we don`t have multiple occurencies all works as now

xronos-i-am commented 2 years ago

Recent example from prod:

    builder =
      MINI_SQL.build(<<~SQL)
        UPDATE products
        SET supplier_sizes = '{}'::size_enum[]
        /*where*/ AND supplier_quantity=0
      SQL
    if brand_id
      builder.where('brand_id = :brand_id', brand_id: brand_id)
    else
      builder.where('1=1')
    end
    builder.exec

    builder =
      MINI_SQL.build(<<~SQL)
        UPDATE products
        SET storehouse_sizes = '{}'::size_enum[]
        /*where*/ AND storehouse_quantity=0
      SQL
    if brand_id
      builder.where('brand_id = :brand_id', brand_id: brand_id)
    else
      builder.where('1=1')
    end
    builder.exec

I have to separate SQL into two parts instead of running together:

    builder =
      MINI_SQL.build(<<~SQL)
        UPDATE products
        SET supplier_sizes = '{}'::size_enum[]
        /*where*/ AND supplier_quantity = 0;

        UPDATE products
        SET storehouse_sizes = '{}'::size_enum[]
        /*where*/ AND storehouse_quantity = 0;
      SQL
    if brand_id
      builder.where('brand_id = :brand_id', brand_id: brand_id)
    else
      builder.where('1=1')
    end
    builder.exec