simple-sql 0.2.1 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/README.md +72 -23
- data/lib/simple-sql.rb +1 -0
- data/lib/simple/sql.rb +6 -4
- data/lib/simple/sql/connection.rb +76 -0
- data/lib/simple/sql/version.rb +1 -1
- data/tasks/release.rake +1 -1
- metadata +4 -3
- data/lib/simple/sql/transactions.rb +0 -44
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 435908aca401185b06131575ae1b5c85570c6fe4
|
4
|
+
data.tar.gz: d238e14b1d859326b0ed4d7e9799d3717d31a821
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f8a497b87236bb8f0cfe1f6e1db9577bbd6364e8826b41097adec7b235cddbff4cac3cd6535a832f8b6d75e452ea21af6ae64ece5f8f9e826d48adbd59087213
|
7
|
+
data.tar.gz: 98d60e38cfd0b60128da263bc8372428028e4ce31fb009e324498de16024802bc65a35e9676f863e379154c76fb56066b4a1f43a6561bd629c1fce6d33b41c3b
|
data/Gemfile.lock
CHANGED
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
|
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
|
-
|
8
|
-
gem 'simple-sql' # + version
|
9
|
-
```
|
16
|
+
gem 'simple-sql' # + version
|
10
17
|
|
11
18
|
## Usage
|
12
19
|
|
13
|
-
|
14
|
-
|
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
|
-
|
59
|
+
```ruby
|
60
|
+
Simple::SQL.all "SELECT * FROM users WHERE id = ANY($1)", [1,2,3]
|
61
|
+
```
|
17
62
|
|
18
|
-
###
|
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
|
-
|
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
|
-
|
102
|
+
### Simple::SQL.record/Simple::SQL.records: fetching hashes
|
61
103
|
|
62
|
-
|
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
|
-
|
65
|
-
Simple::SQL.
|
66
|
-
```
|
107
|
+
# returns a single Hash (or nil)
|
108
|
+
Simple::SQL.record("SELECT id FROM users")
|
67
109
|
|
68
|
-
|
69
|
-
|
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
|
-
|
72
|
-
Simple::SQL.
|
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
|
|
data/lib/simple-sql.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "simple/sql"
|
data/lib/simple/sql.rb
CHANGED
@@ -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
|
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
|
data/lib/simple/sql/version.rb
CHANGED
data/tasks/release.rake
CHANGED
@@ -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
|
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.
|
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-
|
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
|