Class: BetterAuth::Adapters::SQL
- Includes:
- JoinSupport
- Defined in:
- lib/better_auth/adapters/sql.rb
Constant Summary
Constants inherited from Base
Instance Attribute Summary collapse
-
#connection ⇒ Object
readonly
Returns the value of attribute connection.
-
#dialect ⇒ Object
readonly
Returns the value of attribute dialect.
Attributes inherited from Base
Instance Method Summary collapse
- #consume_one(model:, where:) ⇒ Object
- #count(model:, where: nil) ⇒ Object
- #create(model:, data:, force_allow_id: false) ⇒ Object
- #create_if_absent(model:, data:, conflict_field: "id", force_allow_id: true) ⇒ Object
- #delete(model:, where:) ⇒ Object
- #delete_many(model:, where:, limit: nil) ⇒ Object
- #find_many(model:, where: [], sort_by: nil, limit: nil, offset: nil, select: nil, join: nil) ⇒ Object
- #find_one(model:, where: [], select: nil, join: nil) ⇒ Object
- #increment_one(model:, where:, increment:, set: nil, allow_server_managed: false) ⇒ Object
-
#initialize(options, connection:, dialect:) ⇒ SQL
constructor
A new instance of SQL.
- #transaction ⇒ Object
- #update(model:, where:, update:) ⇒ Object
- #update_many(model:, where:, update:, returning: false) ⇒ Object
Methods inherited from Base
Constructor Details
#initialize(options, connection:, dialect:) ⇒ SQL
Returns a new instance of SQL.
15 16 17 18 19 20 |
# File 'lib/better_auth/adapters/sql.rb', line 15 def initialize(, connection:, dialect:) super() @connection = connection @dialect = dialect.to_sym @connection_lock = Monitor.new end |
Instance Attribute Details
#connection ⇒ Object (readonly)
Returns the value of attribute connection.
13 14 15 |
# File 'lib/better_auth/adapters/sql.rb', line 13 def connection @connection end |
#dialect ⇒ Object (readonly)
Returns the value of attribute dialect.
13 14 15 |
# File 'lib/better_auth/adapters/sql.rb', line 13 def dialect @dialect end |
Instance Method Details
#consume_one(model:, where:) ⇒ Object
164 165 166 167 168 169 170 171 172 173 174 |
# File 'lib/better_auth/adapters/sql.rb', line 164 def consume_one(model:, where:) model = model.to_s case dialect when :postgres, :sqlite consume_one_with_returning(model, where) when :mssql consume_one_with_output(model, where) else transaction { consume_one_with_lock(model, where) } end end |
#count(model:, where: nil) ⇒ Object
153 154 155 156 157 158 159 160 161 162 |
# File 'lib/better_auth/adapters/sql.rb', line 153 def count(model:, where: nil) model = model.to_s params = [] where_sql = build_where(model, where || [], params) sql = +"SELECT COUNT(*) AS count FROM " sql << quote(table_for(model)) sql << " WHERE #{where_sql}" unless where_sql.empty? row = execute(sql, params).first || {} (row["count"] || row[:count] || 0).to_i end |
#create(model:, data:, force_allow_id: false) ⇒ Object
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
# File 'lib/better_auth/adapters/sql.rb', line 22 def create(model:, data:, force_allow_id: false) model = model.to_s input = transform_input(model, data, "create", force_allow_id) table = table_for(model) columns = input.keys.map { |field| storage_field(model, field) } params = input.keys.map { |field| input[field] } placeholders = params.each_index.map { |index| placeholder(index + 1) } returning = (dialect == :postgres) ? " RETURNING *" : "" sql = "INSERT INTO #{quote(table)} (#{columns.map { |column| quote(column) }.join(", ")}) VALUES (#{placeholders.join(", ")})#{returning}" rows = execute(sql, params) row = rows.first return normalize_record(model, row) if row lookup = create_lookup(model, input) lookup ? find_one(model: model, where: [lookup]) : input end |
#create_if_absent(model:, data:, conflict_field: "id", force_allow_id: true) ⇒ Object
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
# File 'lib/better_auth/adapters/sql.rb', line 39 def create_if_absent(model:, data:, conflict_field: "id", force_allow_id: true) model = model.to_s field = atomic_schema_field(schema_for(model).fetch(:fields), conflict_field) input = transform_input(model, data, "create", force_allow_id) raise APIError.new("BAD_REQUEST", message: "Missing conflict field #{conflict_field}") unless input.key?(field) columns = input.keys.map { |key| storage_field(model, key) } params = input.values values = params.each_index.map { |index| placeholder(index + 1) }.join(", ") table = quote(table_for(model)) conflict_column = quote(storage_field(model, field)) quoted_columns = columns.map { |column| quote(column) }.join(", ") case dialect when :postgres, :sqlite sql = "INSERT INTO #{table} (#{quoted_columns}) VALUES (#{values}) ON CONFLICT (#{conflict_column}) DO NOTHING RETURNING #{conflict_column}" !execute(sql, params).empty? when :mysql transaction { create_if_absent_mysql(model, field, input, table, quoted_columns, values, params) } when :mssql sql = "INSERT INTO #{table} (#{quoted_columns}) OUTPUT inserted.#{conflict_column} " \ "SELECT #{values} WHERE NOT EXISTS (SELECT 1 FROM #{table} WITH (UPDLOCK, HOLDLOCK) WHERE #{conflict_column} = #{placeholder(params.length + 1)})" !execute(sql, params + [input.fetch(field)]).empty? else raise NotImplementedError, "create_if_absent is unsupported for #{dialect}" end end |
#delete(model:, where:) ⇒ Object
136 137 138 139 |
# File 'lib/better_auth/adapters/sql.rb', line 136 def delete(model:, where:) delete_many(model: model, where: where) nil end |
#delete_many(model:, where:, limit: nil) ⇒ Object
141 142 143 144 145 146 147 148 149 150 151 |
# File 'lib/better_auth/adapters/sql.rb', line 141 def delete_many(model:, where:, limit: nil) model = model.to_s params = [] where_sql = build_where(model, where || [], params) sql = +"DELETE FROM " sql << quote(table_for(model)) sql << " WHERE #{where_sql}" unless where_sql.empty? sql << " LIMIT #{Integer(limit)}" if limit && dialect == :mysql result = execute(sql, params, affected_rows_result: true) affected_rows(result) end |
#find_many(model:, where: [], sort_by: nil, limit: nil, offset: nil, select: nil, join: nil) ⇒ Object
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
# File 'lib/better_auth/adapters/sql.rb', line 75 def find_many(model:, where: [], sort_by: nil, limit: nil, offset: nil, select: nil, join: nil) model = model.to_s params = [] sql = +"SELECT " sql << "TOP (#{Integer(limit)}) " if dialect == :mssql && limit && !offset sql << select_sql(model, select, join) sql << " FROM " sql << quote(table_for(model)) sql << join_sql(model, join) where_sql = build_where(model, where || [], params) sql << " WHERE #{where_sql}" unless where_sql.empty? sql << order_sql(model, sort_by) if sort_by append_pagination_sql(sql, model, sort_by, limit, offset) records = execute(sql, params).map { |row| normalize_record(model, row, join: join) } collection_join?(model, join) ? aggregate_collection_joins(model, records, join) : records end |
#find_one(model:, where: [], select: nil, join: nil) ⇒ Object
67 68 69 70 71 72 73 |
# File 'lib/better_auth/adapters/sql.rb', line 67 def find_one(model:, where: [], select: nil, join: nil) if collection_join?(model.to_s, join) find_many(model: model, where: where, select: select, join: join).first else find_many(model: model, where: where, select: select, join: join, limit: 1).first end end |
#increment_one(model:, where:, increment:, set: nil, allow_server_managed: false) ⇒ Object
176 177 178 179 180 181 182 183 184 185 186 187 |
# File 'lib/better_auth/adapters/sql.rb', line 176 def increment_one(model:, where:, increment:, set: nil, allow_server_managed: false) model = model.to_s increments, assignments = normalize_atomic_update(model, increment, set, allow_server_managed) case dialect when :postgres, :sqlite increment_one_with_returning(model, where, increments, assignments) when :mssql increment_one_with_output(model, where, increments, assignments) else transaction { increment_one_with_lock(model, where, increments, assignments) } end end |
#transaction ⇒ Object
189 190 191 192 193 194 195 196 197 198 199 200 201 |
# File 'lib/better_auth/adapters/sql.rb', line 189 def transaction return yield active_transaction_adapter if active_transaction_adapter @connection_lock.synchronize do execute("BEGIN", []) result = with_transaction_context(self) { yield self } execute("COMMIT", []) result rescue execute("ROLLBACK", []) raise end end |
#update(model:, where:, update:) ⇒ Object
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
# File 'lib/better_auth/adapters/sql.rb', line 93 def update(model:, where:, update:) model = model.to_s return nil if Array(where).empty? ensure_update_input_has_fields!(model, update) if dialect == :postgres records = update_many(model: model, where: where, update: update, returning: true) return records.is_a?(Array) ? records.first : records end existing = find_one(model: model, where: where) return nil unless existing updated_count = update_many(model: model, where: where, update: update) return nil unless updated_count.to_i.positive? lookup = record_lookup(model, existing) lookup ? find_one(model: model, where: [lookup]) : find_one(model: model, where: where) end |
#update_many(model:, where:, update:, returning: false) ⇒ Object
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 |
# File 'lib/better_auth/adapters/sql.rb', line 113 def update_many(model:, where:, update:, returning: false) model = model.to_s ensure_update_input_has_fields!(model, update) data = transform_input(model, update, "update", true) ensure_update_data!(data) params = [] assignments = data.each_key.map do |field| params << data[field] "#{quote(storage_field(model, field))} = #{placeholder(params.length)}" end where_sql = build_where(model, where || [], params) sql = +"UPDATE " sql << quote(table_for(model)) sql << " SET " sql << assignments.join(", ") sql << " WHERE #{where_sql}" unless where_sql.empty? sql << " RETURNING *" if dialect == :postgres && returning result = execute(sql, params, affected_rows_result: !returning) return result.map { |row| normalize_record(model, row) } if returning affected_rows(result) end |