simple-sql 0.2.1 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 671eec226ce28823bcb0d2e749c3e11d7990ffca
4
- data.tar.gz: f3447b03aa97ee19fd1e3fefd566ed24b77f2a85
3
+ metadata.gz: 435908aca401185b06131575ae1b5c85570c6fe4
4
+ data.tar.gz: d238e14b1d859326b0ed4d7e9799d3717d31a821
5
5
  SHA512:
6
- metadata.gz: 1cf66f360cb3d5cd721d6f978dcdc385e6fbfac3cf3aecd40083d8d5f9a9a1b849cf52f7884a29ece948ff4ca1164bf1ccabab97076ecbc789b6e45888bbefc4
7
- data.tar.gz: d2dd45b2e4822fa591286030fd9bb874fd1a23fdc5ee93946c6859e64e4cf8413f4f880801baefdf86db96cf6b0a179221e1adb6b08f2f1be9d21f3afc8b6079
6
+ metadata.gz: f8a497b87236bb8f0cfe1f6e1db9577bbd6364e8826b41097adec7b235cddbff4cac3cd6535a832f8b6d75e452ea21af6ae64ece5f8f9e826d48adbd59087213
7
+ data.tar.gz: 98d60e38cfd0b60128da263bc8372428028e4ce31fb009e324498de16024802bc65a35e9676f863e379154c76fb56066b4a1f43a6561bd629c1fce6d33b41c3b
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- simple-sql (0.2.1)
4
+ simple-sql (0.2.2)
5
5
  pg (~> 0.20)
6
6
  pg_array_parser (~> 0)
7
7
 
data/README.md CHANGED
@@ -1,21 +1,66 @@
1
1
  # simple-sql
2
2
 
3
+ The simple-sql gem defines a module `Simple::SQL`, which you can use to execute
4
+ SQL statements on a Postgresql database. Care has been taken to provide the
5
+ simplest interface we could come up with.
6
+
7
+ **Note:** Databases other than Postgresql are not supported, and there are no
8
+ plans to do so. If you deem that necessary, feel free to fork this code into a
9
+ `simple-sql-<yourdatabase>` gem to provide the same or a similar interface.
10
+
3
11
  ## Installation
4
12
 
5
- The gem is available through our private gem host:
13
+ The gem is available on rubygems as `simple-sql`. To use it add the following
14
+ line to your `Gemfile` and bundle us usual:
6
15
 
7
- ```ruby
8
- gem 'simple-sql' # + version
9
- ```
16
+ gem 'simple-sql' # + version
10
17
 
11
18
  ## Usage
12
19
 
13
- This gem defines a module `Simple::SQL`, which you can use to execute SQL statements
14
- on the current ActiveRecord connection.
20
+ ### Connecting to a database
21
+
22
+ Before you can send SQL commands to a database you need to connect first. `simple-sql`
23
+ gives you the following options:
24
+
25
+ 1. **Use the current ActiveRecord connection:** when running inside a Rails application
26
+ you typically have a connection to Postgresql configured already. In that case you
27
+ don't need to do anything; `simple-sql` will just use the current database connection.
28
+
29
+ This is usually the right thing, especially since `simple-sql`, when called from inside
30
+ a controller action, is using the connection valid in the current context.
31
+
32
+ 2. **Explicitely connect** on standalone applications you need to connect to a Postgresql
33
+ server. **Note that in this case there are no pooled connections!** simple-sql is not
34
+ thread-/fiber-safe in this mode.
35
+
36
+ You can explictely connect to a server by calling
37
+
38
+ ::Simple::SQL.connect! "postgres://user:password@server[:port]/database"
39
+
40
+ Alternatively, you can have `simple-sql` figure out the details automatically:
41
+
42
+ ::Simple::SQL.connect!
43
+
44
+ In that case we try to find connection parameters in the following places:
45
+
46
+ - the `DATABASE_URL` environment value
47
+ - the `config/database.yml` file from the current directory, taking `RAILS_ENV`/`RACK_ENV` into account.
48
+
49
+ ### Using placeholders
50
+
51
+ Note: whenever you run a query `simple-sql` takes care of sending query parameters over the wire properly. That means that you use placeholders `$1`, `$2`, etc. to use these inside your queries; the following is a correct example:
52
+
53
+ ```ruby
54
+ Simple::SQL.all "SELECT * FROM users WHERE email=$1", "foo@bar.local"
55
+ ```
56
+
57
+ Also note that it is not possible to use an array as the argument for the `IN(?)` SQL construct. Instead you want to use `ANY`, for example:
15
58
 
16
- Simple::SQL takes care of converting arguments back and forth.
59
+ ```ruby
60
+ Simple::SQL.all "SELECT * FROM users WHERE id = ANY($1)", [1,2,3]
61
+ ```
17
62
 
18
- ### Running a query
63
+ ### Simple::SQL.all: Fetching all results of a query
19
64
 
20
65
  `Simple::SQL.all` runs a query, with optional arguments, and returns the result. Usage example:
21
66
 
@@ -39,12 +84,9 @@ Simple::SQL.all "SELECT id, email FROM users" do |id, email|
39
84
  end
40
85
  ```
41
86
 
42
- In this case SQL.all returns `self`, which lets you chain function calls.
43
-
44
- ### Getting the first result
45
-
46
- `Simple::SQL.ask` returns runs a query, with optional arguments, and returns the first result row.
87
+ ### Simple::SQL.ask: getting the first result
47
88
 
89
+ `Simple::SQL.ask` runs a query, with optional arguments, and returns the first result row or nil, if there was no result.
48
90
 
49
91
  Simple::SQL.ask "SELECT id, email FROM users WHERE id = ANY($1) LIMIT 1", [1,2,3]
50
92
 
@@ -57,21 +99,28 @@ Simple::SQL.ask "SELECT id FROM users WHERE email=$1", "foo@local" # ret
57
99
  Simple::SQL.ask "SELECT id, email FROM users WHERE email=$?", "foo@local" # returns an array `[ <id>, <email> ]` (or `nil`)
58
100
  ```
59
101
 
60
- ## Notes
102
+ ### Simple::SQL.record/Simple::SQL.records: fetching hashes
61
103
 
62
- Remember that Postgresql uses $1, $2, etc. as placeholders; the following is correct:
104
+ While `ask` and `all` convert each result row into an Array, sometimes you might want
105
+ to use Hashes or similar objects instead. To do so, you use the record and records functions:
63
106
 
64
- ```ruby
65
- Simple::SQL.all "SELECT * FROM users WHERE email=$1", "foo@bar.local"
66
- ```
107
+ # returns a single Hash (or nil)
108
+ Simple::SQL.record("SELECT id FROM users")
67
109
 
68
- Also note that `IN(?)` is not supported by the Postgresql client library; instead you
69
- must use `= ANY`, for example:
110
+ If you want the returned record to be in a structure which is not a Hash, you can use
111
+ the `into: <klass>` option. The following would return an array of up to two `OpenStruct`
112
+ objects:
70
113
 
71
- ```ruby
72
- Simple::SQL.all "SELECT * FROM users WHERE id = ANY($1)", [1,2,3]
73
- ```
114
+ sql = "SELECT id, email FROM users WHERE id = ANY($1) LIMIT 1",
115
+ Simple::SQL.records sql, [1,2,3], into: OpenStruct
116
+
117
+ ### Transaction support
118
+
119
+ `simple-sql` has limited support for nested transactions. When running with a ActiveRecord
120
+ connection, we use ActiveRecord's transaction implementation (which uses savepoints for nested
121
+ transactions, so you might be able to rollback from inside a nested transaction).
74
122
 
123
+ When connecting via `Simple::SQL.connect!` we do not support the same level of nesting support (yet). You can still nest transactions, but raising an error terminates *all* current transactions.
75
124
 
76
125
  ## Bugs and Limitations
77
126
 
@@ -0,0 +1 @@
1
+ require "simple/sql"
@@ -2,8 +2,8 @@ require_relative "sql/version.rb"
2
2
  require_relative "sql/decoder.rb"
3
3
  require_relative "sql/encoder.rb"
4
4
  require_relative "sql/config.rb"
5
- require_relative "sql/transactions.rb"
6
5
  require_relative "sql/logging.rb"
6
+ require_relative "sql/connection.rb"
7
7
 
8
8
  require "logger"
9
9
 
@@ -11,7 +11,6 @@ module Simple
11
11
  # The Simple::SQL module
12
12
  module SQL
13
13
  extend self
14
- extend Transactions
15
14
 
16
15
  attr_accessor :logger
17
16
  self.logger = Logger.new(STDERR)
@@ -88,6 +87,9 @@ module Simple
88
87
  end
89
88
  end
90
89
 
90
+ extend Forwardable
91
+ delegate :transaction => :connection
92
+
91
93
  private
92
94
 
93
95
  def exec_logged(sql, *args)
@@ -127,7 +129,7 @@ module Simple
127
129
  }
128
130
 
129
131
  def connect_to_active_record
130
- return ActiveRecord::Base.connection.raw_connection if defined?(ActiveRecord)
132
+ return Connection.new(ActiveRecord::Base.connection) if defined?(ActiveRecord)
131
133
 
132
134
  STDERR.puts <<-SQL
133
135
  Simple::SQL works out of the box with ActiveRecord-based postgres connections, reusing the current connection.
@@ -150,7 +152,7 @@ SQL
150
152
  config = Config.parse_url(database_url)
151
153
 
152
154
  require "pg"
153
- connection = PG::Connection.new(config)
155
+ connection = Connection.new(PG::Connection.new(config))
154
156
  self.connector = lambda { connection }
155
157
  end
156
158
  end
@@ -0,0 +1,76 @@
1
+ # private
2
+ module Simple::SQL::Connection
3
+ def self.new(connection)
4
+ if defined?(ActiveRecord)
5
+ if connection.is_a?(ActiveRecord::ConnectionAdapters::PostgreSQLAdapter)
6
+ return ActiveRecordConnection.new(connection)
7
+ end
8
+ end
9
+
10
+ SimpleConnection.new(connection)
11
+ end
12
+
13
+ class RawConnection
14
+ def initialize(raw_connection)
15
+ @raw_connection = raw_connection
16
+ end
17
+
18
+ extend Forwardable
19
+ delegate %w(exec_params exec escape) => :@raw_connection
20
+
21
+ def transaction(&block)
22
+ raise ArgumentError, "Implementation missing for #transaction"
23
+ end
24
+ end
25
+
26
+ class SimpleConnection < RawConnection
27
+ def initialize(raw_connection)
28
+ super(raw_connection)
29
+ @tx_nesting_level = 0
30
+ end
31
+
32
+ private
33
+
34
+ def transaction(&block)
35
+ # Notes: by using "ensure" (as opposed to rescue) we are rolling back
36
+ # both when an exception was raised and when a value was thrown. This
37
+ # also means we have to track whether or not to rollback. i.e. do roll
38
+ # back when we yielded to &block but not otherwise.
39
+ #
40
+ # Also the transaction support is a bit limited: you cannot rollback.
41
+ # Rolling back from inside a nested transaction would require SAVEPOINT
42
+ # support; without the code is simpler at least :)
43
+
44
+ if @tx_nesting_level == 0
45
+ exec "BEGIN"
46
+ tx_started = true
47
+ end
48
+
49
+ @tx_nesting_level += 1
50
+
51
+ return_value = yield
52
+
53
+ # Only commit if we started a transaction here.
54
+ if tx_started
55
+ exec "COMMIT"
56
+ tx_committed = true
57
+ end
58
+
59
+ return_value
60
+ ensure
61
+ @tx_nesting_level -= 1
62
+ if tx_started && !tx_committed
63
+ exec "ROLLBACK"
64
+ end
65
+ end
66
+ end
67
+
68
+ class ActiveRecordConnection < RawConnection
69
+ def initialize(connection)
70
+ super(connection.raw_connection)
71
+ @connection = connection
72
+ end
73
+
74
+ delegate :transaction => :@connection
75
+ end
76
+ end
@@ -1,5 +1,5 @@
1
1
  module Simple
2
2
  module SQL
3
- VERSION = "0.2.1"
3
+ VERSION = "0.2.2"
4
4
  end
5
5
  end
@@ -54,7 +54,7 @@ namespace :release do
54
54
  Dir.chdir(GEM_ROOT) do
55
55
  version = VersionNumberTracker.version
56
56
  sh("git add #{VERSION_FILE_PATH}")
57
- sh("git commit -m \"bump auth to v#{version}\"")
57
+ sh("git commit -m \"bump to v#{version}\"")
58
58
  sh("git tag -a v#{version} -m \"Tag\"")
59
59
  end
60
60
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: simple-sql
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - radiospiel
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2018-01-19 00:00:00.000000000 Z
12
+ date: 2018-01-20 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: pg_array_parser
@@ -165,12 +165,13 @@ files:
165
165
  - Rakefile
166
166
  - bin/rake
167
167
  - config/database.yml
168
+ - lib/simple-sql.rb
168
169
  - lib/simple/sql.rb
169
170
  - lib/simple/sql/config.rb
171
+ - lib/simple/sql/connection.rb
170
172
  - lib/simple/sql/decoder.rb
171
173
  - lib/simple/sql/encoder.rb
172
174
  - lib/simple/sql/logging.rb
173
- - lib/simple/sql/transactions.rb
174
175
  - lib/simple/sql/version.rb
175
176
  - log/.gitkeep
176
177
  - simple-sql.gemspec
@@ -1,44 +0,0 @@
1
- # private
2
- module Simple::SQL::Transactions
3
- SELF = self
4
-
5
- def self.nesting_level
6
- Thread.current[:nesting_level] ||= 0
7
- end
8
-
9
- def self.nesting_level=(nesting_level)
10
- Thread.current[:nesting_level] = nesting_level
11
- end
12
-
13
- def transaction(&block)
14
- # Notes: by using "ensure" (as opposed to rescue) we are rolling back
15
- # both when an exception was raised and when a value was thrown. This
16
- # also means we have to track whether or not to rollback. i.e. do roll
17
- # back when we yielded to &block but not otherwise.
18
- #
19
- # Also the transaction support is a bit limited: you cannot rollback.
20
- # Rolling back from inside a nested transaction would require SAVEPOINT
21
- # support; without the code is simpler at least :)
22
- if SELF.nesting_level == 0
23
- transaction_started = true
24
- ask "BEGIN"
25
- end
26
-
27
- SELF.nesting_level += 1
28
-
29
- return_value = yield
30
-
31
- # Only commit if we started a transaction here.
32
- if transaction_started
33
- ask "COMMIT"
34
- transaction_committed = true
35
- end
36
-
37
- return_value
38
- ensure
39
- SELF.nesting_level -= 1
40
- if transaction_started && !transaction_committed
41
- ask "ROLLBACK"
42
- end
43
- end
44
- end