extralite 1.14 → 1.15
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/test.yml +2 -2
- data/CHANGELOG.md +5 -0
- data/Gemfile.lock +1 -1
- data/README.md +12 -2
- data/Rakefile +20 -0
- data/ext/extralite/common.c +5 -1
- data/ext/extralite/database.c +12 -2
- data/ext/extralite/extralite.h +1 -0
- data/ext/extralite/prepared_statement.c +45 -0
- data/lib/extralite/version.rb +1 -1
- data/lib/extralite.rb +25 -1
- data/lib/sequel/adapters/extralite.rb +13 -13
- data/test/test_database.rb +30 -0
- data/test/test_extralite.rb +1 -5
- data/test/test_prepared_statement.rb +19 -0
- metadata +7 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0275df0298c578e4b166e392a43e7edcc4566d08eccdfe8c76ef0fd4e2a0c244
|
4
|
+
data.tar.gz: 312f5218786eaeaa879703c403ace437b351f76fe1fd5660d3d64fe6592f1c8a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8b2052fc2ca18c5d1e61c5ada2a094a1ef5e56053ab8434b8fbb55da35bd2633dcae0131b383caf0b7a9f2990a05205d8788a72ae98ce79b69854f8b517e4c6e
|
7
|
+
data.tar.gz: cff930e31e7f6d0050f2467c2661fa8369ba259bdaf9359154da1ba9ba5dc67ca3c6f327a34dbac73f0edf3d145962c2e973e12bee9c2f323a07612c5b984c03
|
data/.github/workflows/test.yml
CHANGED
data/CHANGELOG.md
CHANGED
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -38,6 +38,8 @@ latest features and enhancements.
|
|
38
38
|
- A variety of methods for different data access patterns: rows as hashes, rows
|
39
39
|
as arrays, single row, single column, single value.
|
40
40
|
- Prepared statements.
|
41
|
+
- Use system-installed sqlite3, or the [bundled latest version of
|
42
|
+
SQLite3](#installing-the-extralite-sqlite3-bundle).
|
41
43
|
- Super fast - [up to 12.5x faster](#performance) than the
|
42
44
|
[sqlite3](https://github.com/sparklemotion/sqlite3-ruby) gem (see also
|
43
45
|
[comparison](#why-not-just-use-the-sqlite3-gem).)
|
@@ -63,7 +65,7 @@ gem 'extralite'
|
|
63
65
|
|
64
66
|
You can also run `gem install extralite` if you just want to check it out.
|
65
67
|
|
66
|
-
|
68
|
+
### Installing the Extralite-SQLite3 bundle
|
67
69
|
|
68
70
|
If you don't have sqlite3 installed on your system, do not want to use the
|
69
71
|
system-installed version of SQLite3, or would like to use the latest version of
|
@@ -136,6 +138,14 @@ number_of_rows_affected = db.changes
|
|
136
138
|
# get db filename
|
137
139
|
db.filename #=> "/tmp/my.db"
|
138
140
|
|
141
|
+
# get list of tables
|
142
|
+
db.tables #=> ['foo', 'bar']
|
143
|
+
|
144
|
+
# get and set pragmas
|
145
|
+
db.pragma(:journal_mode) #=> 'delete'
|
146
|
+
db.pragma(journal_mode: 'wal')
|
147
|
+
db.pragma(:journal_mode) #=> 'wal'
|
148
|
+
|
139
149
|
# load an extension
|
140
150
|
db.load_extension('/path/to/extension.so')
|
141
151
|
|
@@ -175,7 +185,7 @@ Here's a table summarizing the differences between the two gems:
|
|
175
185
|
|
176
186
|
| |sqlite3-ruby|Extralite|
|
177
187
|
|-|-|-|
|
178
|
-
|SQLite3 dependency|depends on OS-installed libsqlite3|
|
188
|
+
|SQLite3 dependency|depends on OS-installed libsqlite3|Use either system sqlite3 or [bundled latest version of SQLite3](#installing-the-extralite-sqlite3-bundle)|
|
179
189
|
|API design|multiple classes|single class|
|
180
190
|
|Query results|row as hash, row as array, single row, single value|row as hash, row as array, __single column__, single row, single value|
|
181
191
|
|Execute multiple statements|separate API (#execute_batch)|integrated|
|
data/Rakefile
CHANGED
@@ -24,3 +24,23 @@ YARD::Rake::YardocTask.new do |t|
|
|
24
24
|
t.files = YARD_FILES
|
25
25
|
t.options = %w(-o doc --readme README.md)
|
26
26
|
end
|
27
|
+
|
28
|
+
task :release do
|
29
|
+
require_relative './lib/extralite/version'
|
30
|
+
version = Extralite::VERSION
|
31
|
+
|
32
|
+
puts 'Building extralite...'
|
33
|
+
`gem build extralite.gemspec`
|
34
|
+
|
35
|
+
puts 'Building extralite-bundle...'
|
36
|
+
`gem build extralite-bundle.gemspec`
|
37
|
+
|
38
|
+
puts "Pushing extralite #{version}..."
|
39
|
+
`gem push extralite-#{version}.gem`
|
40
|
+
|
41
|
+
puts "Pushing extralite-bundle #{version}..."
|
42
|
+
`gem push extralite-bundle-#{version}.gem`
|
43
|
+
|
44
|
+
puts "Cleaning up..."
|
45
|
+
`rm *.gem`
|
46
|
+
end
|
data/ext/extralite/common.c
CHANGED
@@ -31,7 +31,7 @@ void bind_hash_parameter_values(sqlite3_stmt *stmt, VALUE hash) {
|
|
31
31
|
|
32
32
|
switch (TYPE(k)) {
|
33
33
|
case T_FIXNUM:
|
34
|
-
bind_parameter_value(stmt,
|
34
|
+
bind_parameter_value(stmt, FIX2INT(k), v);
|
35
35
|
break;
|
36
36
|
case T_SYMBOL:
|
37
37
|
k = rb_funcall(k, ID_TO_S, 0);
|
@@ -345,3 +345,7 @@ VALUE safe_query_single_value(query_ctx *ctx) {
|
|
345
345
|
RB_GC_GUARD(value);
|
346
346
|
return value;
|
347
347
|
}
|
348
|
+
|
349
|
+
VALUE safe_query_columns(query_ctx *ctx) {
|
350
|
+
return get_column_names(ctx->stmt, sqlite3_column_count(ctx->stmt));
|
351
|
+
}
|
data/ext/extralite/database.c
CHANGED
@@ -263,6 +263,15 @@ VALUE Database_query_single_value(int argc, VALUE *argv, VALUE self) {
|
|
263
263
|
return Database_perform_query(argc, argv, self, safe_query_single_value);
|
264
264
|
}
|
265
265
|
|
266
|
+
/* call-seq:
|
267
|
+
* db.columns(sql) -> columns
|
268
|
+
*
|
269
|
+
* Returns the column names for the given query, without running it.
|
270
|
+
*/
|
271
|
+
VALUE Database_columns(VALUE self, VALUE sql) {
|
272
|
+
return Database_perform_query(1, &sql, self, safe_query_columns);
|
273
|
+
}
|
274
|
+
|
266
275
|
/* call-seq:
|
267
276
|
* db.last_insert_rowid -> int
|
268
277
|
*
|
@@ -272,7 +281,7 @@ VALUE Database_last_insert_rowid(VALUE self) {
|
|
272
281
|
Database_t *db;
|
273
282
|
GetOpenDatabase(self, db);
|
274
283
|
|
275
|
-
return
|
284
|
+
return INT2FIX(sqlite3_last_insert_rowid(db->sqlite3_db));
|
276
285
|
}
|
277
286
|
|
278
287
|
/* call-seq:
|
@@ -284,7 +293,7 @@ VALUE Database_changes(VALUE self) {
|
|
284
293
|
Database_t *db;
|
285
294
|
GetOpenDatabase(self, db);
|
286
295
|
|
287
|
-
return
|
296
|
+
return INT2FIX(sqlite3_changes(db->sqlite3_db));
|
288
297
|
}
|
289
298
|
|
290
299
|
/* call-seq:
|
@@ -362,6 +371,7 @@ void Init_ExtraliteDatabase() {
|
|
362
371
|
rb_define_method(cDatabase, "query_single_row", Database_query_single_row, -1);
|
363
372
|
rb_define_method(cDatabase, "query_single_column", Database_query_single_column, -1);
|
364
373
|
rb_define_method(cDatabase, "query_single_value", Database_query_single_value, -1);
|
374
|
+
rb_define_method(cDatabase, "columns", Database_columns, 1);
|
365
375
|
|
366
376
|
rb_define_method(cDatabase, "last_insert_rowid", Database_last_insert_rowid, 0);
|
367
377
|
rb_define_method(cDatabase, "changes", Database_changes, 0);
|
data/ext/extralite/extralite.h
CHANGED
@@ -53,6 +53,7 @@ VALUE safe_query_hash(query_ctx *ctx);
|
|
53
53
|
VALUE safe_query_single_column(query_ctx *ctx);
|
54
54
|
VALUE safe_query_single_row(query_ctx *ctx);
|
55
55
|
VALUE safe_query_single_value(query_ctx *ctx);
|
56
|
+
VALUE safe_query_columns(query_ctx *ctx);
|
56
57
|
|
57
58
|
void prepare_single_stmt(sqlite3 *db, sqlite3_stmt **stmt, VALUE sql);
|
58
59
|
void prepare_multi_stmt(sqlite3 *db, sqlite3_stmt **stmt, VALUE sql);
|
@@ -63,6 +63,9 @@ static inline VALUE PreparedStatement_perform_query(int argc, VALUE *argv, VALUE
|
|
63
63
|
PreparedStatement_t *stmt;
|
64
64
|
GetPreparedStatement(self, stmt);
|
65
65
|
|
66
|
+
if (!stmt->stmt)
|
67
|
+
rb_raise(cError, "Prepared statement is closed");
|
68
|
+
|
66
69
|
sqlite3_reset(stmt->stmt);
|
67
70
|
sqlite3_clear_bindings(stmt->stmt);
|
68
71
|
bind_all_parameters(stmt->stmt, argc, argv);
|
@@ -218,6 +221,43 @@ VALUE PreparedStatement_sql(VALUE self) {
|
|
218
221
|
return stmt->sql;
|
219
222
|
}
|
220
223
|
|
224
|
+
/* call-seq:
|
225
|
+
* stmt.columns -> columns
|
226
|
+
*
|
227
|
+
* Returns the column names for the prepared statement without running it.
|
228
|
+
*/
|
229
|
+
VALUE PreparedStatement_columns(VALUE self) {
|
230
|
+
return PreparedStatement_perform_query(0, NULL, self, safe_query_columns);
|
231
|
+
}
|
232
|
+
|
233
|
+
/* call-seq:
|
234
|
+
* stmt.close -> stmt
|
235
|
+
*
|
236
|
+
* Closes the prepared statement. Running a closed prepared statement will raise
|
237
|
+
* an error.
|
238
|
+
*/
|
239
|
+
VALUE PreparedStatement_close(VALUE self) {
|
240
|
+
PreparedStatement_t *stmt;
|
241
|
+
GetPreparedStatement(self, stmt);
|
242
|
+
if (stmt->stmt) {
|
243
|
+
sqlite3_finalize(stmt->stmt);
|
244
|
+
stmt->stmt = NULL;
|
245
|
+
}
|
246
|
+
return self;
|
247
|
+
}
|
248
|
+
|
249
|
+
/* call-seq:
|
250
|
+
* stmt.closed? -> closed
|
251
|
+
*
|
252
|
+
* Returns true if the prepared statement is closed.
|
253
|
+
*/
|
254
|
+
VALUE PreparedStatement_closed_p(VALUE self) {
|
255
|
+
PreparedStatement_t *stmt;
|
256
|
+
GetPreparedStatement(self, stmt);
|
257
|
+
|
258
|
+
return stmt->stmt ? Qfalse : Qtrue;
|
259
|
+
}
|
260
|
+
|
221
261
|
void Init_ExtralitePreparedStatement() {
|
222
262
|
VALUE mExtralite = rb_define_module("Extralite");
|
223
263
|
|
@@ -235,4 +275,9 @@ void Init_ExtralitePreparedStatement() {
|
|
235
275
|
rb_define_method(cPreparedStatement, "query_single_row", PreparedStatement_query_single_row, -1);
|
236
276
|
rb_define_method(cPreparedStatement, "query_single_column", PreparedStatement_query_single_column, -1);
|
237
277
|
rb_define_method(cPreparedStatement, "query_single_value", PreparedStatement_query_single_value, -1);
|
278
|
+
|
279
|
+
rb_define_method(cPreparedStatement, "columns", PreparedStatement_columns, 0);
|
280
|
+
|
281
|
+
rb_define_method(cPreparedStatement, "close", PreparedStatement_close, 0);
|
282
|
+
rb_define_method(cPreparedStatement, "closed?", PreparedStatement_closed_p, 0);
|
238
283
|
}
|
data/lib/extralite/version.rb
CHANGED
data/lib/extralite.rb
CHANGED
@@ -17,5 +17,29 @@ module Extralite
|
|
17
17
|
|
18
18
|
# An SQLite database
|
19
19
|
class Database
|
20
|
+
alias_method :execute, :query
|
21
|
+
|
22
|
+
TABLES_SQL = <<~SQL
|
23
|
+
SELECT name FROM sqlite_schema
|
24
|
+
WHERE type ='table'
|
25
|
+
AND name NOT LIKE 'sqlite_%';
|
26
|
+
SQL
|
27
|
+
|
28
|
+
def tables
|
29
|
+
query_single_column(TABLES_SQL)
|
30
|
+
end
|
31
|
+
|
32
|
+
def pragma(value)
|
33
|
+
value.is_a?(Hash) ? pragma_set(value) : pragma_get(value)
|
34
|
+
end
|
35
|
+
|
36
|
+
def pragma_set(values)
|
37
|
+
sql = values.inject(+'') { |s, (k, v)| s += "pragma #{k}=#{v}; " }
|
38
|
+
query(sql)
|
39
|
+
end
|
40
|
+
|
41
|
+
def pragma_get(key)
|
42
|
+
query_single_value("pragma #{key}")
|
43
|
+
end
|
20
44
|
end
|
21
|
-
end
|
45
|
+
end
|
@@ -122,17 +122,17 @@ module Sequel
|
|
122
122
|
|
123
123
|
connection_pragmas.each{|s| log_connection_yield(s, db){db.query(s)}}
|
124
124
|
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
125
|
+
class << db
|
126
|
+
attr_reader :prepared_statements
|
127
|
+
end
|
128
|
+
db.instance_variable_set(:@prepared_statements, {})
|
129
129
|
|
130
130
|
db
|
131
131
|
end
|
132
132
|
|
133
133
|
# Disconnect given connections from the database.
|
134
134
|
def disconnect_connection(c)
|
135
|
-
|
135
|
+
c.prepared_statements.each_value{|v| v.first.close }
|
136
136
|
c.close
|
137
137
|
end
|
138
138
|
|
@@ -149,13 +149,13 @@ module Sequel
|
|
149
149
|
# Drop any prepared statements on the connection when executing DDL. This is because
|
150
150
|
# prepared statements lock the table in such a way that you can't drop or alter the
|
151
151
|
# table while a prepared statement that references it still exists.
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
152
|
+
def execute_ddl(sql, opts=OPTS)
|
153
|
+
synchronize(opts[:server]) do |conn|
|
154
|
+
conn.prepared_statements.values.each{|cps, s| cps.close}
|
155
|
+
conn.prepared_statements.clear
|
156
|
+
super
|
157
|
+
end
|
158
|
+
end
|
159
159
|
|
160
160
|
def execute_insert(sql, opts=OPTS)
|
161
161
|
_execute(:insert, sql, opts)
|
@@ -193,7 +193,7 @@ module Sequel
|
|
193
193
|
def _execute(type, sql, opts, &block)
|
194
194
|
begin
|
195
195
|
synchronize(opts[:server]) do |conn|
|
196
|
-
|
196
|
+
return execute_prepared_statement(conn, type, sql, opts, &block) if sql.is_a?(Symbol)
|
197
197
|
log_args = opts[:arguments]
|
198
198
|
args = {}
|
199
199
|
opts.fetch(:arguments, OPTS).each{|k, v| args[k] = prepared_statement_argument(v) }
|
data/test/test_database.rb
CHANGED
@@ -68,6 +68,11 @@ end
|
|
68
68
|
assert_nil r
|
69
69
|
end
|
70
70
|
|
71
|
+
def test_columns
|
72
|
+
r = @db.columns('select x, z from t')
|
73
|
+
assert_equal [:x, :z], r
|
74
|
+
end
|
75
|
+
|
71
76
|
def test_transaction_active?
|
72
77
|
assert_equal false, @db.transaction_active?
|
73
78
|
@db.query('begin')
|
@@ -177,6 +182,31 @@ end
|
|
177
182
|
r = @db.query_single_value("select reverse('abcd')")
|
178
183
|
assert_equal 'dcba', r
|
179
184
|
end
|
185
|
+
|
186
|
+
def test_tables
|
187
|
+
assert_equal ['t'], @db.tables
|
188
|
+
|
189
|
+
@db.query('create table foo (bar text)')
|
190
|
+
assert_equal ['t', 'foo'], @db.tables
|
191
|
+
|
192
|
+
@db.query('drop table t')
|
193
|
+
assert_equal ['foo'], @db.tables
|
194
|
+
|
195
|
+
@db.query('drop table foo')
|
196
|
+
assert_equal [], @db.tables
|
197
|
+
end
|
198
|
+
|
199
|
+
def test_pragma
|
200
|
+
assert_equal 'memory', @db.pragma('journal_mode')
|
201
|
+
assert_equal 2, @db.pragma('synchronous')
|
202
|
+
|
203
|
+
assert_equal 1, @db.pragma(:schema_version)
|
204
|
+
assert_equal 0, @db.pragma(:recursive_triggers)
|
205
|
+
|
206
|
+
assert_equal [], @db.pragma(schema_version: 33, recursive_triggers: 1)
|
207
|
+
assert_equal 33, @db.pragma(:schema_version)
|
208
|
+
assert_equal 1, @db.pragma(:recursive_triggers)
|
209
|
+
end
|
180
210
|
end
|
181
211
|
|
182
212
|
class ScenarioTest < MiniTest::Test
|
data/test/test_extralite.rb
CHANGED
@@ -4,10 +4,6 @@ require_relative 'helper'
|
|
4
4
|
|
5
5
|
class ExtraliteTest < MiniTest::Test
|
6
6
|
def test_sqlite3_version
|
7
|
-
|
8
|
-
match = reported.match(/^([\d\.]+)/)
|
9
|
-
if match
|
10
|
-
assert_equal match[1], Extralite.sqlite3_version
|
11
|
-
end
|
7
|
+
assert_match /^3\.\d+\.\d+$/, Extralite.sqlite3_version
|
12
8
|
end
|
13
9
|
end
|
@@ -162,4 +162,23 @@ end
|
|
162
162
|
r = @db.prepare('select null').query_single_value
|
163
163
|
assert_nil r
|
164
164
|
end
|
165
|
+
|
166
|
+
def test_prepared_statement_columns
|
167
|
+
r = @db.prepare("select 'abc' as a, 'def' as b").columns
|
168
|
+
assert_equal [:a, :b], r
|
169
|
+
end
|
170
|
+
|
171
|
+
def test_prepared_statement_close
|
172
|
+
p = @db.prepare("select 'abc'")
|
173
|
+
|
174
|
+
assert_equal false, p.closed?
|
175
|
+
|
176
|
+
p.close
|
177
|
+
assert_equal true, p.closed?
|
178
|
+
|
179
|
+
p.close
|
180
|
+
assert_equal true, p.closed?
|
181
|
+
|
182
|
+
assert_raises { p.query_single_value }
|
183
|
+
end
|
165
184
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: extralite
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '1.
|
4
|
+
version: '1.15'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sharon Rosner
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-10-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake-compiler
|
@@ -80,7 +80,7 @@ dependencies:
|
|
80
80
|
- - '='
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: 5.51.0
|
83
|
-
description:
|
83
|
+
description:
|
84
84
|
email: sharon@noteflakes.com
|
85
85
|
executables: []
|
86
86
|
extensions:
|
@@ -134,7 +134,7 @@ metadata:
|
|
134
134
|
documentation_uri: https://www.rubydoc.info/gems/extralite
|
135
135
|
homepage_uri: https://github.com/digital-fabric/extralite
|
136
136
|
changelog_uri: https://github.com/digital-fabric/extralite/blob/master/CHANGELOG.md
|
137
|
-
post_install_message:
|
137
|
+
post_install_message:
|
138
138
|
rdoc_options:
|
139
139
|
- "--title"
|
140
140
|
- extralite
|
@@ -153,8 +153,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
153
153
|
- !ruby/object:Gem::Version
|
154
154
|
version: '0'
|
155
155
|
requirements: []
|
156
|
-
rubygems_version: 3.3.
|
157
|
-
signing_key:
|
156
|
+
rubygems_version: 3.3.7
|
157
|
+
signing_key:
|
158
158
|
specification_version: 4
|
159
159
|
summary: Extra-lightweight SQLite3 wrapper for Ruby
|
160
160
|
test_files: []
|