Module: RailsAgents::DatabaseQuery
- Defined in:
- lib/rails_agents/database_query.rb
Overview
Read-only database query helpers for Tool Bridge. Supports ActiveRecord SQL dialects and Mongoid collection finds.
Constant Summary collapse
- FORBIDDEN_SQL =
/\b(INSERT|UPDATE|DELETE|DROP|ALTER|TRUNCATE|CREATE|GRANT|REVOKE|COPY|EXECUTE|CALL|MERGE|REPLACE)\b/i
Class Method Summary collapse
- .apply_row_limit(sql, adapter) ⇒ Object
- .mongo_query(collection:, filter: nil, limit: 50, projection: nil) ⇒ Object
- .sql_query(sql, adapter: nil) ⇒ Object
- .stringify_bson(value) ⇒ Object
Class Method Details
.apply_row_limit(sql, adapter) ⇒ Object
56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
# File 'lib/rails_agents/database_query.rb', line 56 def apply_row_limit(sql, adapter) return sql if sql.match?(/\bLIMIT\b/i) || sql.match?(/\bFETCH\s+FIRST\b/i) || sql.match?(/\bTOP\s+\d+/i) case adapter.to_s when "sqlserver" # Best-effort: wrap as subquery when TOP absent "SELECT TOP 50 * FROM (#{sql}) AS rails_agents_q" when "oracle" "SELECT * FROM (#{sql}) rails_agents_q WHERE ROWNUM <= 50" else # postgresql, mysql, sqlite, trilogy, etc. "#{sql} LIMIT 50" end end |
.mongo_query(collection:, filter: nil, limit: 50, projection: nil) ⇒ Object
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
# File 'lib/rails_agents/database_query.rb', line 34 def mongo_query(collection:, filter: nil, limit: 50, projection: nil) raise ArgumentError, "Mongoid is not available" unless defined?(Mongoid) && Mongoid.respond_to?(:default_client) name = collection.to_s.strip raise ArgumentError, "collection is required" if name.empty? raise ArgumentError, "Invalid collection name" unless name.match?(/\A[A-Za-z0-9_.-]+\z/) lim = [[limit.to_i, 1].max, 50].min criteria = filter.is_a?(Hash) ? filter : {} coll = Mongoid.default_client[name] cursor = coll.find(criteria) cursor = cursor.sort(projection) if projection.is_a?(Hash) && !projection.empty? docs = cursor.limit(lim).to_a.map { |doc| stringify_bson(doc) } { ok: true, engine: "mongoid", collection: name, row_count: docs.length, rows: docs } end |
.sql_query(sql, adapter: nil) ⇒ Object
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
# File 'lib/rails_agents/database_query.rb', line 11 def sql_query(sql, adapter: nil) raise ArgumentError, "ActiveRecord is not available" unless defined?(ActiveRecord::Base) text = sql.to_s.strip raise ArgumentError, "sql is required" if text.empty? normalized = text.sub(/;\s*\z/, "") unless normalized.match?(/\A\s*(WITH|SELECT)\b/im) raise ArgumentError, "Only read-only SELECT/WITH queries are allowed" end raise ArgumentError, "Write or DDL statements are not allowed" if normalized.match?(FORBIDDEN_SQL) limited = apply_row_limit(normalized, adapter || DatabaseDiscovery.primary_adapter) rows = ActiveRecord::Base.connection.exec_query(limited).to_a { ok: true, engine: "active_record", adapter: adapter || DatabaseDiscovery.primary_adapter, row_count: rows.length, rows: rows.first(50) } end |
.stringify_bson(value) ⇒ Object
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
# File 'lib/rails_agents/database_query.rb', line 71 def stringify_bson(value) case value when Hash value.transform_keys(&:to_s).transform_values { |v| stringify_bson(v) } when Array value.map { |v| stringify_bson(v) } else if defined?(BSON::ObjectId) && value.is_a?(BSON::ObjectId) value.to_s else value end end rescue StandardError value.inspect end |