Mojo-SQLite-v4.0.0 000755 001750 001750 0 15205022012 13732 5 ustar 00grinnz grinnz 000000 000000 README 100644 001750 001750 1633 15205022012 14676 0 ustar 00grinnz grinnz 000000 000000 Mojo-SQLite-v4.0.0 Mojo::SQLite
Mojo::SQLite is a tiny wrapper around DBD::SQLite that makes SQLite
a lot of fun to use with the Mojolicious
real-time web framework. Use all SQL features
SQLite has to offer, generate CRUD queries
from data structures, and manage your database schema with migrations.
INSTALLATION
This is a Perl module distribution. It should be installed with whichever
tool you use to manage your installation of Perl, e.g. any of
cpanm .
cpan .
cpanp -i .
Consult http://www.cpan.org/modules/INSTALL.html for further instruction.
Should you wish to install this module manually, the procedure is
perl Build.PL
./Build
./Build test
./Build install
COPYRIGHT AND LICENSE
Copyright 2015, Dan Book.
This library is free software; you may redistribute it and/or modify it
under the terms of the Artistic License version 2.0.
Changes 100644 001750 001750 20761 15205022012 15334 0 ustar 00grinnz grinnz 000000 000000 Mojo-SQLite-v4.0.0 v4.0.0 2026-05-25 06:27:21 EDT
- wal_mode is now off by default, but can be enabled with the wal_mode option.
This will only affect newly created databases, as WAL mode is persistent once set.
To disable it on an existing database, issue the query: PRAGMA journal_mode=DELETE;
- Fix issue where a transaction that failed to begin would rollback and clear the database handle error.
3.009 2022-03-22 20:16:03 EDT
- Fix race condition in Mojo::SQLite::Migrations where two processes could attempt to create the same migration. (#25, Oliver Kurz)
3.008 2021-11-28 11:10:24 EST
- Update documentation to indicate that upsert operations are now supported via SQL::Abstract::Pg.
3.007 2021-08-01 20:17:42 EDT
- Increase DBD::SQLite dependency to 1.68 to support and use sqlite_string_mode over sqlite_unicode (#22, Adam Williamson)
- The default options now set sqlite_string_mode to DBD_SQLITE_STRING_MODE_UNICODE_FALLBACK,
unless sqlite_unicode is specified explicitly in from_string or from_filename. This is equivalent
to the previous behavior but ensures that DBD::SQLite will not return malformed strings.
- Support wal_mode option to enable WAL journaling mode, which is still currently the default,
but may not be set by default in a future release.
3.006 2021-06-16 00:30:47 EDT
- Set default abstract generator to an SQL::Abstract::Pg object, enabling additional features.
- Update IRC metadata to libera.chat
3.005 2021-02-15 21:47:59 EST
- Add query_p, select_p, insert_p, update_p, delete_p Mojo::Promise-returning methods to Mojo::SQLite::Database.
These are for API compatibility with Mojo::Pg and do not provide non-blocking query functionality. (#20, Stefan Adams)
- Use Mojo::Promise in tests instead of the deprecated and decored Mojo::IOLoop::Delay (#20, Stefan Adams)
3.004 2020-07-22 22:50:43 EDT
- The Mojo::SQLite::Migrations sqlite attribute and the
Mojo::SQLite::Transaction db attribute are now weak attributes, increasing
the required version of Mojolicious to 8.03.
- Defer loading of Mojo::IOLoop unless the callback compatibility API is
used.
- Newly created mojo_migrations tables from Mojo::SQLite::Migrations will use
a primary key.
- Recognize -json parameters in queries.
3.003 2019-10-01 15:49:43 EDT
- Increase DBD::SQLite dependency to 1.64 to support configuring
double-quoted string literals.
- Disable double-quoted string literals for all connections.
https://www.sqlite.org/quirks.html#double_quoted_string_literals_are_accepted
- Change default SQL::Abstract identifier back to double quote.
3.002 2019-06-21 17:41:35 EDT
- Changed default SQL::Abstract identifier quoting character to `, as double
quoted identifiers may be interpreted as string literals if they are not
valid identifiers. https://www.sqlite.org/quirks.html#double_quoted_string_literals_are_accepted
3.001 2018-07-20 20:25:12 EDT
- Increase DBD::SQLite dependency to 1.54 for FTS5 support.
- Add db attribute to Mojo::SQLite::Results and use it to prevent connections
from being cached for reuse while results are active.
- Add sql_for method to Mojo::SQLite::Migrations.
3.000 2017-07-20 01:16:50 EDT
- Changed default for max_connections attribute to 1.
- Added support for sharing the database connection cache between multiple
Mojo::SQLite objects. (based on Mojo::Pg 4.0)
- Added parent attribute to Mojo::SQLite.
- Fixed database connection leak with automatic migrations.
- Removed deprecated Mojo::SQLite::PubSub and associated methods and attributes.
SQLite's serverless nature means it does not have the ability to support
client notifications, so it is not possible to implement an efficient
pubsub system as in for example PostgreSQL, Redis, or websockets.
2.002 2017-06-01 14:16:34 EDT
- Add no_wal option to prevent enabling WAL mode on connection.
2.001 2017-02-18 15:36:16 EST
- Set name_sep in default SQL::Abstract object to support proper quoting of
table and column names.
2.000 2017-02-11 17:03:53 EST
- Add support for generating queries with SQL::Abstract. (based on Mojo::Pg 3.0)
- Add abstract attribute to Mojo::SQLite.
- Add delete, insert, select, and update methods to Mojo::SQLite::Database.
1.004 2017-01-17 00:10:51 EST
- Use Mojo::File from Mojolicious 7.15 instead of deprecated
Mojo::Util slurp function. (#9)
1.003 2016-12-11 16:30:31 EST
- Add links to alternatives for deprecated Mojo::SQLite::PubSub.
1.002 2016-11-30 11:17:56 EST
- Improved contextual caller information in query error messages. (#6)
- Fix memory leak when reusing the same database handle many times. (#7)
1.001 2016-11-15 02:32:27 EST
- Deprecate Mojo::SQLite::PubSub and associated methods and attributes.
SQLite's serverless nature means it does not have the ability to support
client notifications, so it is not possible to implement an efficient
pubsub system as in for example PostgreSQL, Redis, or websockets.
1.000 2016-08-17 21:01:31 EDT
- Bump version to 1.000 as this distribution is considered stable
- Added database_class attribute to Mojo::SQLite and results_class attribute
to Mojo::SQLite::Database to make it easier for these classes to be extended
- Fixed a few fork-safety bugs and memory leaks in Mojo::SQLite::PubSub
0.022 2016-07-14 16:59:36 EDT
- Improve expand performance slightly
- Add support for encoding and decoding of JSON notifications
- Add json method to Mojo::SQLite::PubSub
- Fix bug where poll_interval in Mojo::SQLite::PubSub was not correctly used
- Fix case where notifications from other connections could be lost
0.021 2016-02-15 01:58:40 EST
- Lower dependency on DBI to 1.627
- Remove dependency on URI::QueryParam
0.020 2016-02-14 17:53:55 EST
- Bump dependency on DBD::SQLite to 1.50 for JSON1 extension support
- Add support for json parameters to query method in Mojo::SQLite::Database
- Add expand method to Mojo::SQLite::Results to decode json columns
- Add auto_migrate attribute to Mojo::SQLite (from Mojo::Pg)
- Add tables method to Mojo::SQLite::Database (from Mojo::Pg)
0.019 2015-11-25 22:45:25 EST
- Added support for removing all subscribers of a channel at once in unlisten
method in Mojo::SQLite::PubSub
- Mojo::SQLite::Migrations now throws an error if the currently active
version is greater than the latest version
- Added finish method to Mojo::SQLite::Results
0.018 2015-09-21 13:44:30 EDT
- Fixes for tests under win32
0.017 2015-09-20 17:24:44 EDT
- Fix parsing of options from connection strings
- Allow passing a hashref of options in from_filename
0.016 2015-09-20 16:51:53 EDT
- Use URI::db to parse connection strings to allow sqlite: connection strings
0.015 2015-09-12 00:13:53 EDT
- Minor pubsub fixes
0.014 2015-09-11 20:25:25 EDT
- Add Mojo::SQLite::PubSub and listen/notify implementation in Mojo::SQLite::Database
0.013 2015-09-02 02:44:36 EDT
- Cache rowid for successful inserts in Results objects as last_insert_id
0.012 2015-08-31 18:26:47 EDT
- Include better context for DB error messages from $db->query and migrations
- Switch installer to Module::Build::Tiny
0.011 2015-08-13 20:43:37 EDT
- Simplify Results refcounting
- Pass undef instead of broken Results object to callback if statement
prepare failed
0.010 2015-08-08 22:50:06 EDT
- Create a tempdir for temporary databases
- Use immediate locking for migrations instead of exclusive
- Run query callbacks using next_tick instead of immediately
- Don't call finish in Results destructor while other Results objects refer
to the statement handle (GH #2)
0.009 2015-08-05 12:28:48 EDT
- Simplify some code and examples with Mojo::Base::tap
0.008 2015-08-03 19:23:49 EDT
- Use and recommend URI to portably parse filenames into URLs
- Add from_filename method
0.007 2015-08-01 15:17:22 EDT
- Clarify use of URLs in connection strings
0.006 2015-07-28 23:54:45 EDT
- Fix results rows() method to return statement handle rows
- Make scheme optional for from_string() (and thus the constructor)
0.005 2015-07-28 21:59:23 EDT
- Allow specifying bind types for query parameters
0.004 2015-07-28 20:48:04 EDT
- Depend on DBI explicitly
- Remove List::Util dependency
- Add blog example
0.003 2015-07-28 12:56:30 EDT
- Minor migrations fixes
0.002 2015-07-28 12:39:12 EDT
- Allow passing callback to query()
0.001 2015-07-28 02:48:42 EDT
- First release
LICENSE 100644 001750 001750 21515 15205022012 15044 0 ustar 00grinnz grinnz 000000 000000 Mojo-SQLite-v4.0.0 This software is Copyright (c) 2015 by Dan Book.
This is free software, licensed under:
The Artistic License 2.0 (GPL Compatible)
The Artistic License 2.0
Copyright (c) 2000-2006, The Perl Foundation.
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
This license establishes the terms under which a given free software
Package may be copied, modified, distributed, and/or redistributed.
The intent is that the Copyright Holder maintains some artistic
control over the development of that Package while still keeping the
Package available as open source and free software.
You are always permitted to make arrangements wholly outside of this
license directly with the Copyright Holder of a given Package. If the
terms of this license do not permit the full use that you propose to
make of the Package, you should contact the Copyright Holder and seek
a different licensing arrangement.
Definitions
"Copyright Holder" means the individual(s) or organization(s)
named in the copyright notice for the entire Package.
"Contributor" means any party that has contributed code or other
material to the Package, in accordance with the Copyright Holder's
procedures.
"You" and "your" means any person who would like to copy,
distribute, or modify the Package.
"Package" means the collection of files distributed by the
Copyright Holder, and derivatives of that collection and/or of
those files. A given Package may consist of either the Standard
Version, or a Modified Version.
"Distribute" means providing a copy of the Package or making it
accessible to anyone else, or in the case of a company or
organization, to others outside of your company or organization.
"Distributor Fee" means any fee that you charge for Distributing
this Package or providing support for this Package to another
party. It does not mean licensing fees.
"Standard Version" refers to the Package if it has not been
modified, or has been modified only in ways explicitly requested
by the Copyright Holder.
"Modified Version" means the Package, if it has been changed, and
such changes were not explicitly requested by the Copyright
Holder.
"Original License" means this Artistic License as Distributed with
the Standard Version of the Package, in its current version or as
it may be modified by The Perl Foundation in the future.
"Source" form means the source code, documentation source, and
configuration files for the Package.
"Compiled" form means the compiled bytecode, object code, binary,
or any other form resulting from mechanical transformation or
translation of the Source form.
Permission for Use and Modification Without Distribution
(1) You are permitted to use the Standard Version and create and use
Modified Versions for any purpose without restriction, provided that
you do not Distribute the Modified Version.
Permissions for Redistribution of the Standard Version
(2) You may Distribute verbatim copies of the Source form of the
Standard Version of this Package in any medium without restriction,
either gratis or for a Distributor Fee, provided that you duplicate
all of the original copyright notices and associated disclaimers. At
your discretion, such verbatim copies may or may not include a
Compiled form of the Package.
(3) You may apply any bug fixes, portability changes, and other
modifications made available from the Copyright Holder. The resulting
Package will still be considered the Standard Version, and as such
will be subject to the Original License.
Distribution of Modified Versions of the Package as Source
(4) You may Distribute your Modified Version as Source (either gratis
or for a Distributor Fee, and with or without a Compiled form of the
Modified Version) provided that you clearly document how it differs
from the Standard Version, including, but not limited to, documenting
any non-standard features, executables, or modules, and provided that
you do at least ONE of the following:
(a) make the Modified Version available to the Copyright Holder
of the Standard Version, under the Original License, so that the
Copyright Holder may include your modifications in the Standard
Version.
(b) ensure that installation of your Modified Version does not
prevent the user installing or running the Standard Version. In
addition, the Modified Version must bear a name that is different
from the name of the Standard Version.
(c) allow anyone who receives a copy of the Modified Version to
make the Source form of the Modified Version available to others
under
(i) the Original License or
(ii) a license that permits the licensee to freely copy,
modify and redistribute the Modified Version using the same
licensing terms that apply to the copy that the licensee
received, and requires that the Source form of the Modified
Version, and of any works derived from it, be made freely
available in that license fees are prohibited but Distributor
Fees are allowed.
Distribution of Compiled Forms of the Standard Version
or Modified Versions without the Source
(5) You may Distribute Compiled forms of the Standard Version without
the Source, provided that you include complete instructions on how to
get the Source of the Standard Version. Such instructions must be
valid at the time of your distribution. If these instructions, at any
time while you are carrying out such distribution, become invalid, you
must provide new instructions on demand or cease further distribution.
If you provide valid instructions or cease distribution within thirty
days after you become aware that the instructions are invalid, then
you do not forfeit any of your rights under this license.
(6) You may Distribute a Modified Version in Compiled form without
the Source, provided that you comply with Section 4 with respect to
the Source of the Modified Version.
Aggregating or Linking the Package
(7) You may aggregate the Package (either the Standard Version or
Modified Version) with other packages and Distribute the resulting
aggregation provided that you do not charge a licensing fee for the
Package. Distributor Fees are permitted, and licensing fees for other
components in the aggregation are permitted. The terms of this license
apply to the use and Distribution of the Standard or Modified Versions
as included in the aggregation.
(8) You are permitted to link Modified and Standard Versions with
other works, to embed the Package in a larger work of your own, or to
build stand-alone binary or bytecode versions of applications that
include the Package, and Distribute the result without restriction,
provided the result does not expose a direct interface to the Package.
Items That are Not Considered Part of a Modified Version
(9) Works (including, but not limited to, modules and scripts) that
merely extend or make use of the Package, do not, by themselves, cause
the Package to be a Modified Version. In addition, such works are not
considered parts of the Package itself, and are not subject to the
terms of this license.
General Provisions
(10) Any use, modification, and distribution of the Standard or
Modified Versions is governed by this Artistic License. By using,
modifying or distributing the Package, you accept this license. Do not
use, modify, or distribute the Package, if you do not accept this
license.
(11) If your Modified Version has been derived from a Modified
Version made by someone other than you, you are nevertheless required
to ensure that your Modified Version complies with the requirements of
this license.
(12) This license does not grant you the right to use any trademark,
service mark, tradename, or logo of the Copyright Holder.
(13) This license includes the non-exclusive, worldwide,
free-of-charge patent license to make, have made, use, offer to sell,
sell, import and otherwise transfer the Package with respect to any
patent claims licensable by the Copyright Holder that are necessarily
infringed by the Package. If you institute patent litigation
(including a cross-claim or counterclaim) against any party alleging
that the Package constitutes direct or contributory patent
infringement, then this Artistic License to you shall terminate on the
date that such litigation is filed.
(14) Disclaimer of Warranty:
THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS
IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL
LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR WILL
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
dist.ini 100644 001750 001750 454 15205022012 15442 0 ustar 00grinnz grinnz 000000 000000 Mojo-SQLite-v4.0.0 name = Mojo-SQLite
author = Dan Book
license = Artistic_2_0
copyright_holder = Dan Book
copyright_year = 2015
[@Author::DBOOK]
:version = v1.0.3
installer = ModuleBuildTiny::Fallback
irc = ircs://irc.libera.chat/#mojo
pod_tests = 1
[Deprecated]
module = Mojo::SQLite::PubSub
t 000755 001750 001750 0 15205022012 14116 5 ustar 00grinnz grinnz 000000 000000 Mojo-SQLite-v4.0.0 crud.t 100644 001750 001750 6230 15205022012 15401 0 ustar 00grinnz grinnz 000000 000000 Mojo-SQLite-v4.0.0/t use Mojo::Base -strict;
BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' }
use Test::More;
use Mojo::SQLite;
use Mojo::IOLoop;
my $sql = Mojo::SQLite->new;
{
my $db = $sql->db;
$db->query(
'create table if not exists crud_test (
id integer primary key autoincrement,
name text
)'
);
subtest 'Create' => sub {
$db->insert('crud_test', {name => 'foo'});
is_deeply $db->select('crud_test')->hashes->to_array,
[{id => 1, name => 'foo'}], 'right structure';
is $db->insert('crud_test', {name => 'bar'})->last_insert_id,
2, 'right value';
is_deeply $db->select('crud_test')->hashes->to_array,
[{id => 1, name => 'foo'}, {id => 2, name => 'bar'}], 'right structure';
};
subtest 'Read' => sub {
is_deeply $db->select('crud_test')->hashes->to_array,
[{id => 1, name => 'foo'}, {id => 2, name => 'bar'}], 'right structure';
is_deeply $db->select('crud_test', ['name'])->hashes->to_array,
[{name => 'foo'}, {name => 'bar'}], 'right structure';
is_deeply $db->select('crud_test', ['name'], {name => 'foo'})->hashes->to_array,
[{name => 'foo'}], 'right structure';
is_deeply $db->select('crud_test', ['name'], undef, {-desc => 'id'})
->hashes->to_array, [{name => 'bar'}, {name => 'foo'}], 'right structure';
};
subtest 'Non-blocking read' => sub {
my $result;
$db->select_p('crud_test')->then(sub {
$result = shift->hashes->to_array;
})->wait;
is_deeply $result, [{id => 1, name => 'foo'}, {id => 2, name => 'bar'}],
'right structure';
$result = undef;
$db->select_p('crud_test', undef, undef, {-desc => 'id'})->then(sub {
$result = shift->hashes->to_array;
})->wait;
is_deeply $result, [{id => 2, name => 'bar'}, {id => 1, name => 'foo'}],
'right structure';
};
subtest 'Update' => sub {
$db->update('crud_test', {name => 'baz'}, {name => 'foo'});
is_deeply $db->select('crud_test', undef, undef, {-asc => 'id'})
->hashes->to_array, [{id => 1, name => 'baz'}, {id => 2, name => 'bar'}],
'right structure';
};
subtest 'Delete' => sub {
$db->delete('crud_test', {name => 'baz'});
is_deeply $db->select('crud_test', undef, undef, {-asc => 'id'})
->hashes->to_array, [{id => 2, name => 'bar'}], 'right structure';
$db->delete('crud_test');
is_deeply $db->select('crud_test')->hashes->to_array, [], 'right structure';
};
subtest 'Quoting' => sub {
$db->query(
'create table if not exists crud_test2 (
id integer primary key autoincrement,
"t e s t" text
)'
);
$db->insert('crud_test2', {'t e s t' => 'foo'});
$db->insert('main.crud_test2', {'t e s t' => 'bar'});
is_deeply $db->select('main.crud_test2')->hashes->to_array,
[{id => 1, 't e s t' => 'foo'}, {id => 2, 't e s t' => 'bar'}],
'right structure';
};
subtest 'Unresolved identifier' => sub {
is_deeply $db->select('main.crud_test2', undef, {'t e s t' => 'foo'})
->hashes->to_array, [{id => 1, 't e s t' => 'foo'}], 'right structure';
ok !eval { $db->select('main.crud_test2', undef, {'test' => 'foo'}); 1 },
'unknown column';
};
}
done_testing();
META.yml 100644 001750 001750 3464 15205022012 15273 0 ustar 00grinnz grinnz 000000 000000 Mojo-SQLite-v4.0.0 ---
abstract: 'A tiny Mojolicious wrapper for SQLite'
author:
- 'Dan Book '
build_requires:
File::Spec: '0'
Module::Metadata: '0'
Test::More: '0.96'
configure_requires:
Module::Build::Tiny: '0.034'
dynamic_config: 0
generated_by: 'Dist::Zilla version 6.037, CPAN::Meta::Converter version 2.150010'
license: artistic_2
meta-spec:
url: http://module-build.sourceforge.net/META-spec-v1.4.html
version: '1.4'
name: Mojo-SQLite
no_index:
directory:
- eg
- examples
- inc
- share
- t
- xt
provides:
Mojo::SQLite:
file: lib/Mojo/SQLite.pm
version: v4.0.0
Mojo::SQLite::Database:
file: lib/Mojo/SQLite/Database.pm
version: v4.0.0
Mojo::SQLite::Migrations:
file: lib/Mojo/SQLite/Migrations.pm
version: v4.0.0
Mojo::SQLite::PubSub:
file: lib/Mojo/SQLite/PubSub.pm
version: v4.0.0
x_deprecated: 1
Mojo::SQLite::Results:
file: lib/Mojo/SQLite/Results.pm
version: v4.0.0
Mojo::SQLite::Transaction:
file: lib/Mojo/SQLite/Transaction.pm
version: v4.0.0
requires:
Carp: '0'
DBD::SQLite: '1.68'
DBI: '1.627'
File::Spec::Functions: '0'
File::Temp: '0'
Mojolicious: '8.03'
SQL::Abstract::Pg: '1.0'
Scalar::Util: '0'
URI: '1.69'
URI::db: '0.15'
URI::file: '4.21'
perl: '5.010001'
resources:
IRC: ircs://irc.libera.chat/#mojo
bugtracker: https://github.com/Grinnz/Mojo-SQLite/issues
homepage: https://github.com/Grinnz/Mojo-SQLite
repository: https://github.com/Grinnz/Mojo-SQLite.git
version: v4.0.0
x_contributors:
- 'Adam Williamson '
- 'Dan Book '
- 'Dan Book '
- 'Oliver Kurz '
- 'Stefan Adams '
x_generated_by_perl: v5.42.1
x_serialization_backend: 'YAML::Tiny version 1.76'
x_spdx_expression: Artistic-2.0
MANIFEST 100644 001750 001750 1767 15205022012 15157 0 ustar 00grinnz grinnz 000000 000000 Mojo-SQLite-v4.0.0 # This file was automatically generated by Dist::Zilla::Plugin::Manifest v6.037
Build.PL
CONTRIBUTING.md
Changes
LICENSE
MANIFEST
META.json
META.yml
README
dist.ini
examples/blog/blog.conf
examples/blog/lib/Blog.pm
examples/blog/lib/Blog/Controller/Posts.pm
examples/blog/lib/Blog/Model/Posts.pm
examples/blog/migrations/blog.sql
examples/blog/script/blog
examples/blog/t/blog.t
examples/blog/templates/layouts/blog.html.ep
examples/blog/templates/posts/_form.html.ep
examples/blog/templates/posts/create.html.ep
examples/blog/templates/posts/edit.html.ep
examples/blog/templates/posts/index.html.ep
examples/blog/templates/posts/show.html.ep
lib/Mojo/SQLite.pm
lib/Mojo/SQLite/Database.pm
lib/Mojo/SQLite/Migrations.pm
lib/Mojo/SQLite/PubSub.pm
lib/Mojo/SQLite/Results.pm
lib/Mojo/SQLite/Transaction.pm
prereqs.yml
t/00-report-prereqs.dd
t/00-report-prereqs.t
t/connection.t
t/crud.t
t/database.t
t/migrations.t
t/migrations/test.sql
t/results.t
t/sqlite_lite_app.t
xt/author/pod-coverage.t
xt/author/pod-syntax.t
Build.PL 100644 001750 001750 7071 15205022012 15314 0 ustar 00grinnz grinnz 000000 000000 Mojo-SQLite-v4.0.0 # This Build.PL for Mojo-SQLite was generated by
# Dist::Zilla::Plugin::ModuleBuildTiny::Fallback 0.027
use strict;
use warnings;
my %configure_requires = (
'Module::Build::Tiny' => '0.034',
);
my %errors = map {
eval "require $_; $_->VERSION($configure_requires{$_}); 1";
$_ => $@,
} keys %configure_requires;
if (!grep $_, values %errors)
{
# This section for Mojo-SQLite was generated by Dist::Zilla::Plugin::ModuleBuildTiny 0.020.
use strict;
use warnings;
use 5.010001;
# use Module::Build::Tiny 0.034;
Module::Build::Tiny::Build_PL();
}
else
{
if (not $ENV{PERL_MB_FALLBACK_SILENCE_WARNING})
{
warn <<'EOW'
*** WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING ***
If you're seeing this warning, your toolchain is really, really old* and
you'll almost certainly have problems installing CPAN modules from this
century. But never fear, dear user, for we have the technology to fix this!
If you're using CPAN.pm to install things, then you can upgrade it using:
cpan CPAN
If you're using CPANPLUS to install things, then you can upgrade it using:
cpanp CPANPLUS
If you're using cpanminus, you shouldn't be seeing this message in the first
place, so please file an issue on github.
This public service announcement was brought to you by the Perl Toolchain
Gang, the irc.perl.org #toolchain IRC channel, and the number 42.
----
* Alternatively, you are running this file manually, in which case you need to
learn to first fulfill all configure requires prerequisites listed in META.yml
or META.json -- or use a cpan client to install this distribution.
You can also silence this warning for future installations by setting the
PERL_MB_FALLBACK_SILENCE_WARNING environment variable, but please don't do
that until you fix your toolchain as described above.
Errors from configure prereqs:
EOW
. do {
require Data::Dumper; Data::Dumper->new([ \%errors ])->Indent(2)->Terse(1)->Sortkeys(1)->Dump;
};
sleep 10 if -t STDIN && (-t STDOUT || !(-f STDOUT || -c STDOUT));
}
# This section was automatically generated by Dist::Zilla::Plugin::ModuleBuild v6.037
use strict;
use warnings;
require Module::Build; Module::Build->VERSION(0.28);
my %module_build_args = (
"configure_requires" => {
"Module::Build::Tiny" => "0.034"
},
"dist_abstract" => "A tiny Mojolicious wrapper for SQLite",
"dist_author" => [
"Dan Book "
],
"dist_name" => "Mojo-SQLite",
"dist_version" => "v4.0.0",
"license" => "artistic_2",
"module_name" => "Mojo::SQLite",
"recursive_test_files" => 1,
"requires" => {
"Carp" => 0,
"DBD::SQLite" => "1.68",
"DBI" => "1.627",
"File::Spec::Functions" => 0,
"File::Temp" => 0,
"Mojolicious" => "8.03",
"SQL::Abstract::Pg" => "1.0",
"Scalar::Util" => 0,
"URI" => "1.69",
"URI::db" => "0.15",
"URI::file" => "4.21",
"perl" => "5.010001"
},
"test_requires" => {
"File::Spec" => 0,
"Module::Metadata" => 0,
"Test::More" => "0.96"
}
);
my %fallback_build_requires = (
"File::Spec" => 0,
"Module::Metadata" => 0,
"Test::More" => "0.96"
);
unless ( eval { Module::Build->VERSION(0.4004) } ) {
delete $module_build_args{test_requires};
$module_build_args{build_requires} = \%fallback_build_requires;
}
my $build = Module::Build->new(%module_build_args);
$build->create_build_script;
}
META.json 100644 001750 001750 6173 15205022012 15443 0 ustar 00grinnz grinnz 000000 000000 Mojo-SQLite-v4.0.0 {
"abstract" : "A tiny Mojolicious wrapper for SQLite",
"author" : [
"Dan Book "
],
"dynamic_config" : 0,
"generated_by" : "Dist::Zilla version 6.037, CPAN::Meta::Converter version 2.150010",
"license" : [
"artistic_2"
],
"meta-spec" : {
"url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec",
"version" : 2
},
"name" : "Mojo-SQLite",
"no_index" : {
"directory" : [
"eg",
"examples",
"inc",
"share",
"t",
"xt"
]
},
"prereqs" : {
"configure" : {
"requires" : {
"Module::Build::Tiny" : "0.034"
}
},
"develop" : {
"requires" : {
"Pod::Coverage::TrustPod" : "0",
"Test::Pod" : "1.41",
"Test::Pod::Coverage" : "1.08"
}
},
"runtime" : {
"requires" : {
"Carp" : "0",
"DBD::SQLite" : "1.68",
"DBI" : "1.627",
"File::Spec::Functions" : "0",
"File::Temp" : "0",
"Mojolicious" : "8.03",
"SQL::Abstract::Pg" : "1.0",
"Scalar::Util" : "0",
"URI" : "1.69",
"URI::db" : "0.15",
"URI::file" : "4.21",
"perl" : "5.010001"
}
},
"test" : {
"recommends" : {
"CPAN::Meta" : "2.120900"
},
"requires" : {
"File::Spec" : "0",
"Module::Metadata" : "0",
"Test::More" : "0.96"
}
}
},
"provides" : {
"Mojo::SQLite" : {
"file" : "lib/Mojo/SQLite.pm",
"version" : "v4.0.0"
},
"Mojo::SQLite::Database" : {
"file" : "lib/Mojo/SQLite/Database.pm",
"version" : "v4.0.0"
},
"Mojo::SQLite::Migrations" : {
"file" : "lib/Mojo/SQLite/Migrations.pm",
"version" : "v4.0.0"
},
"Mojo::SQLite::PubSub" : {
"file" : "lib/Mojo/SQLite/PubSub.pm",
"version" : "v4.0.0",
"x_deprecated" : 1
},
"Mojo::SQLite::Results" : {
"file" : "lib/Mojo/SQLite/Results.pm",
"version" : "v4.0.0"
},
"Mojo::SQLite::Transaction" : {
"file" : "lib/Mojo/SQLite/Transaction.pm",
"version" : "v4.0.0"
}
},
"release_status" : "stable",
"resources" : {
"bugtracker" : {
"web" : "https://github.com/Grinnz/Mojo-SQLite/issues"
},
"homepage" : "https://github.com/Grinnz/Mojo-SQLite",
"repository" : {
"type" : "git",
"url" : "https://github.com/Grinnz/Mojo-SQLite.git",
"web" : "https://github.com/Grinnz/Mojo-SQLite"
},
"x_IRC" : "ircs://irc.libera.chat/#mojo"
},
"version" : "v4.0.0",
"x_contributors" : [
"Adam Williamson ",
"Dan Book ",
"Dan Book ",
"Oliver Kurz ",
"Stefan Adams "
],
"x_generated_by_perl" : "v5.42.1",
"x_serialization_backend" : "Cpanel::JSON::XS version 4.40",
"x_spdx_expression" : "Artistic-2.0"
}
prereqs.yml 100644 001750 001750 545 15205022012 16203 0 ustar 00grinnz grinnz 000000 000000 Mojo-SQLite-v4.0.0 runtime:
requires:
perl: '5.010001'
Carp: 0
DBI: '1.627'
DBD::SQLite: '1.68' # for JSON1, FTS5, DQS, sqlite_string_mode
File::Spec::Functions: 0
File::Temp: 0
Mojolicious: '8.03'
Scalar::Util: 0
SQL::Abstract::Pg: '1.0'
URI: '1.69'
URI::db: '0.15'
URI::file: '4.21'
test:
requires:
Test::More: '0.96'
results.t 100644 001750 001750 14225 15205022012 16170 0 ustar 00grinnz grinnz 000000 000000 Mojo-SQLite-v4.0.0/t use Mojo::Base -strict;
use Test::More;
use Mojo::SQLite;
use Mojo::IOLoop;
use Mojo::Promise;
package MojoSQLiteTest::Database;
use Mojo::Base 'Mojo::SQLite::Database';
sub results_class {'MojoSQLiteTest::Results'}
package MojoSQLiteTest::Results;
use Mojo::Base 'Mojo::SQLite::Results';
sub array_test { shift->array }
package main;
my $sql = Mojo::SQLite->new;
{
my $db = $sql->db;
$db->query(
'create table if not exists results_test (
id integer primary key autoincrement,
name text
)'
);
my $results;
$results = $db->query('insert into results_test (name) values (?)', $_) for qw(foo bar);
is $results->last_insert_id, 2, 'right last_insert_id';
is $db->query('update results_test set name=name')->rows, 2, 'two rows affected';
subtest 'Tables' => sub {
ok !!(grep {/^"main"."results_test"$/i} @{$db->tables}),
'results table exists';
ok !(grep {/^"main"."sqlite_master"$/i} @{$db->tables}),
'internal tables are hidden';
ok !(grep {/^"temp"."sqlite_temp_master"$/i} @{$db->tables}),
'internal tables are hidden';
};
subtest 'Result methods' => sub {
is_deeply $db->query('select * from results_test')->columns, ['id', 'name'],
'right structure';
is_deeply $db->query('select * from results_test')->array, [1, 'foo'],
'right structure';
my $results = $db->query('select * from results_test');
is_deeply $results->arrays->to_array,
[[1, 'foo'], [2, 'bar']], 'right structure';
is_deeply $results->rows, 2, 'two rows';
is_deeply $db->query('select * from results_test')->hash,
{id => 1, name => 'foo'}, 'right structure';
is_deeply $db->query('select * from results_test')->hashes->to_array,
[{id => 1, name => 'foo'}, {id => 2, name => 'bar'}], 'right structure';
is $sql->db->query('select * from results_test')->text, "1 foo\n2 bar\n",
'right text';
};
subtest 'Custom database and results classes' => sub {
is ref $db, 'Mojo::SQLite::Database', 'right class';
$sql->database_class('MojoSQLiteTest::Database');
$db = $sql->db;
is ref $db, 'MojoSQLiteTest::Database', 'right class';
is ref $db->query('select 1'), 'MojoSQLiteTest::Results', 'right class';
is_deeply $db->query('select * from results_test')->array_test, [1, 'foo'],
'right structure';
};
subtest 'JSON' => sub {
is_deeply $db->query('select ? as foo', {json => {bar => 'baz'}})
->expand(json => 'foo')->hash, {foo => {bar => 'baz'}}, 'right structure';
is_deeply $db->query('select ? as foo', {-json => {bar => 'baz'}})
->expand(json => 'foo')->hash, {foo => {bar => 'baz'}}, 'right structure';
is_deeply $db->query('select ? as foo', {json => {bar => 'baz'}})
->expand(json => 'foo')->array, [{bar => 'baz'}], 'right structure';
my $hashes = [{foo => {one => 1}, bar => 'a'}, {foo => {two => 2}, bar => 'b'}];
is_deeply $db->query(
"select 'a' as bar, ? as foo
union all
select 'b' as bar, ? as foo", {json => {one => 1}},
{json => {two => 2}}
)->expand(json => 'foo')->hashes->to_array, $hashes, 'right structure';
my $arrays = [['a', {one => 1}], ['b', {two => 2}]];
is_deeply $db->query(
"select 'a' as bar, ? as foo
union all
select 'b' as bar, ? as foo", {json => {one => 1}},
{json => {two => 2}}
)->expand(json => 'foo')->arrays->to_array, $arrays, 'right structure';
};
subtest 'Iterate' => sub {
my $results = $db->query('select * from results_test');
is_deeply $results->array, [1, 'foo'], 'right structure';
is_deeply $results->array, [2, 'bar'], 'right structure';
is $results->array, undef, 'no more results';
};
subtest 'Non-blocking query where not all results have been fetched' => sub {
my ($fail, $result);
my @promises = (
$db->query_p('select name from results_test')->then(sub {
my $results = shift;
push @$result, $results->array;
$results->finish;
}),
$db->query_p('select name from results_test')->then(sub {
my $results = shift;
push @$result, $results->array_test;
$results->finish;
}),
$db->query_p('select name from results_test')->then(sub {
my $results = shift;
push @$result, $results->array;
}),
);
Mojo::Promise->all(@promises)->catch(sub { $fail = 1 })->wait;
ok !$fail, 'no error';
is_deeply $result, [['foo'], ['foo'], ['foo']], 'right structure';
};
subtest 'Transactions' => sub {
{
my $tx = $db->begin;
$db->query("insert into results_test (name) values ('tx1')");
$db->query("insert into results_test (name) values ('tx1')");
$tx->commit;
}
is_deeply $db->query('select * from results_test where name = ?', 'tx1')
->hashes->to_array, [{id => 3, name => 'tx1'}, {id => 4, name => 'tx1'}],
'right structure';
{
my $tx = $db->begin;
$db->query("insert into results_test (name) values ('tx2')");
$db->query("insert into results_test (name) values ('tx2')");
}
is_deeply $db->query('select * from results_test where name = ?', 'tx2')
->hashes->to_array, [], 'no results';
eval {
my $tx = $db->begin;
$db->query("insert into results_test (name) values ('tx3')");
$db->query("insert into results_test (name) values ('tx3')");
$db->query('does_not_exist');
$tx->commit;
};
like $@, qr/does_not_exist/, 'right error';
is_deeply $db->query('select * from results_test where name = ?', 'tx3')
->hashes->to_array, [], 'no results';
{
my $timeout = $db->dbh->sqlite_busy_timeout // 0;
$db->dbh->sqlite_busy_timeout(100);
my $tx;
eval {
$tx = $db->begin('immediate');
$db->begin('immediate');
};
ok $@, 'transaction failed';
ok $db->dbh->err, 'database error code retained';
$db->dbh->sqlite_busy_timeout($timeout);
}
};
subtest 'Issue #2' => sub {
my $results1 = $db->query('select 1 as one');
is_deeply $results1->hashes, [{one => 1}], 'right structure';
my $results2 = $db->query('select 1 as one');
undef $results1;
is_deeply $results2->hashes, [{one => 1}], 'right structure';
}
}
done_testing();
database.t 100644 001750 001750 22360 15205022012 16232 0 ustar 00grinnz grinnz 000000 000000 Mojo-SQLite-v4.0.0/t use Mojo::Base -strict;
BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' }
use Test::More;
use Mojo::SQLite;
use Mojo::IOLoop;
use Mojo::JSON 'true';
use DBI ':sql_types';
use Mojo::Util 'encode';
my $sql = Mojo::SQLite->new;
subtest 'Connected' => sub {
ok $sql->db->ping, 'connected';
};
subtest 'Blocking select' => sub {
is_deeply $sql->db->query('select 1 as one, 2 as two, 3 as three')->hash,
{one => 1, two => 2, three => 3}, 'right structure';
};
subtest 'Non-blocking select' => sub {
my ($fail, $result);
my $same;
my $db = $sql->db;
$db->query(
'select 1 as one, 2 as two, 3 as three' => sub {
my ($db, $err, $results) = @_;
$fail = $err;
$result = $results->hash;
$same = $db->dbh eq $results->db->dbh;
Mojo::IOLoop->stop;
}
);
Mojo::IOLoop->start;
ok $same, 'same database handles';
ok !$fail, 'no error';
is_deeply $result, {one => 1, two => 2, three => 3}, 'right structure';
};
subtest 'Concurrent non-blocking selects' => sub {
my ($fail, $result);
my $one = $sql->db->query_p('select 1 as one');
my $two = $sql->db->query_p('select 2 as two');
my $again = $sql->db->query_p('select 2 as two');
Mojo::Promise->all($one, $again, $two)->then(sub {
$result = [map { $_->[0]->hashes->first } @_];
})->catch(sub {
$fail = 1;
})->wait;
ok !$fail, 'no error';
is_deeply $result, [{one => 1}, {two => 2}, {two => 2}], 'right structure';
};
subtest 'Sequential non-blocking selects' => sub {
my ($fail, $result);
my $db = $sql->db;
$db->query_p('select 1 as one')->then(sub {
push @$result, shift->hashes->first;
$db->query_p('select 1 as one');
})->then(sub {
push @$result, shift->hashes->first;
$db->query_p('select 2 as two');
})->then(sub {
push @$result, shift->hashes->first;
})->catch(sub {
$fail = 1;
})->wait;
ok !$fail, 'no error';
is_deeply $result, [{one => 1}, {one => 1}, {two => 2}], 'right structure';
};
subtest 'Connection cache' => sub {
is $sql->max_connections, 1, 'right default';
my @dbhs = map { $_->dbh } $sql->db, $sql->db, $sql->db, $sql->db, $sql->db;
is_deeply \@dbhs,
[map { $_->dbh } $sql->db, $sql->db, $sql->db, $sql->db, $sql->db],
'same database handles';
@dbhs = ();
my $dbh = $sql->max_connections(1)->db->dbh;
is $sql->db->dbh, $dbh, 'same database handle';
isnt $sql->db->dbh, $sql->db->dbh, 'different database handles';
is $sql->db->dbh, $dbh, 'different database handles';
$dbh = $sql->db->dbh;
is $sql->db->dbh, $dbh, 'same database handle';
$sql->db->disconnect;
isnt $sql->db->dbh, $dbh, 'different database handles';
};
subtest 'Statement cache' => sub {
my $db = $sql->db;
my $sth = $db->query('select 3 as three')->sth;
is $db->query('select 3 as three')->sth, $sth, 'same statement handle';
isnt $db->query('select 4 as four')->sth, $sth, 'different statement handles';
is $db->query('select 3 as three')->sth, $sth, 'same statement handle';
undef $db;
$db = $sql->db;
my $results = $db->query('select 3 as three');
is $results->sth, $sth, 'same statement handle';
isnt $db->query('select 3 as three')->sth, $sth, 'different statement handles';
$sth = $db->query('select 3 as three')->sth;
is $db->query('select 3 as three')->sth, $sth, 'same statement handle';
isnt $db->query('select 5 as five')->sth, $sth, 'different statement handles';
isnt $db->query('select 6 as six')->sth, $sth, 'different statement handles';
is $db->query('select 3 as three')->sth, $sth, 'same statement handle';
};
subtest 'Connection reuse' => sub {
my $db = $sql->db;
my $dbh = $db->dbh;
my $results = $db->query('select 1');
undef $db;
my $db2 = $sql->db;
isnt $db2->dbh, $dbh, 'new database handle';
undef $results;
my $db3 = $sql->db;
is $db3->dbh, $dbh, 'same database handle';
$results = $db3->query('select 2');
is $results->db->dbh, $dbh, 'same database handle';
is $results->array->[0], 2, 'right result';
};
subtest 'Bind types' => sub {
my $db = $sql->db;
is_deeply $db->query('select ? as foo', {type => SQL_VARCHAR, value => 'bar'})
->hash, {foo => 'bar'}, 'right structure';
is_deeply $db->query('select ? as foo', {type => SQL_INTEGER, value => 5})
->hash, {foo => 5}, 'right structure';
is_deeply $db->query('select ? as foo', {type => SQL_REAL, value => 2.5})
->hash, {foo => 2.5}, 'right structure';
is_deeply $db->query('select ? as foo', {type => SQL_VARCHAR, value => '☃♥'})
->hash, {foo => '☃♥'}, 'right structure';
is_deeply $db->query('select ? as foo', {type => SQL_BLOB, value => encode 'UTF-8', '☃♥'})
->hash, {foo => encode 'UTF-8', '☃♥'}, 'right structure';
};
subtest 'JSON' => sub {
my $db = $sql->db;
is_deeply $db->query('select ? as foo', {json => {bar => 'baz'}})
->expand(json => 'foo')->hash, {foo => {bar => 'baz'}}, 'right structure';
is_deeply $db->query('select ? as foo', {json => {bar => 'baz'}})
->expand(json => 'foo')->array, [{bar => 'baz'}], 'right structure';
is_deeply $db->query('select ? as foo', {json => {bar => 'baz'}})
->expand(json => 'foo')->hashes->first, {foo => {bar => 'baz'}}, 'right structure';
is_deeply $db->query('select ? as foo', {json => {bar => 'baz'}})
->expand(json => 'foo')->arrays->first, [{bar => 'baz'}], 'right structure';
is_deeply $db->query('select ? as foo', {json => {bar => 'baz'}})->hash,
{foo => '{"bar":"baz"}'}, 'right structure';
is_deeply $db->query('select ? as foo', {json => \1})
->expand(json => 'foo')->hashes->first, {foo => true}, 'right structure';
is_deeply $db->query('select ? as foo', undef)->expand(json => 'foo')->hash,
{foo => undef}, 'right structure';
is_deeply $db->query('select ? as foo', undef)->expand(json => 'foo')->array,
[undef], 'right structure';
my $results = $db->query('select ?', undef);
my $name = $results->columns->[0];
is_deeply $results->expand(json => $name)->array, [undef], 'right structure';
is_deeply $results->expand(json => $name)->array, undef, 'no more results';
is_deeply $db->query('select ? as unicode', {json => {'☃' => '♥'}})
->expand(json => 'unicode')->hash, {unicode => {'☃' => '♥'}}, 'right structure';
is_deeply $db->query("select json_object('☃', ?) as unicode", '♥')
->expand(json => 'unicode')->hash, {unicode => {'☃' => '♥'}}, 'right structure';
is_deeply $db->query('select ? as foo, ? as bar', {json => {baz => 'foo'}},
{json => {baz => 'bar'}})->expand(json => 'foo')->hash,
{foo => {baz => 'foo'}, bar => '{"baz":"bar"}'}, 'right structure';
is_deeply $db->query('select ? as foo, ? as bar', {json => {baz => 'foo'}},
{json => {baz => 'bar'}})->expand(json => ['foo','bar'])->hash,
{foo => {baz => 'foo'}, bar => {baz => 'bar'}}, 'right structure';
};
subtest 'Fork-safety' => sub {
my $dbh = $sql->db->dbh;
my ($connections, $current) = @_;
$sql->on(
connection => sub {
my ($sql, $dbh) = @_;
$connections++;
$current = $dbh;
}
);
is $sql->db->dbh, $dbh, 'same database handle';
ok !$connections, 'no new connections';
{
local $$ = -23;
isnt $sql->db->dbh, $dbh, 'different database handles';
is $sql->db->dbh, $current, 'same database handle';
is $connections, 1, 'one new connection';
}
$sql->unsubscribe('connection');
};
subtest 'Shared connection cache' => sub {
my $sql2 = Mojo::SQLite->new($sql);
is $sql2->parent, $sql, 'right parent';
my $dbh = $sql->db->dbh;
is $sql->db->dbh, $dbh, 'same database handle';
is $sql2->db->dbh, $dbh, 'same database handle';
is $sql->db->dbh, $dbh, 'same database handle';
is $sql2->db->dbh, $dbh, 'same database handle';
my $db = $sql->db;
is_deeply $db->query('select 1 as one')->hashes->to_array, [{one => 1}],
'right structure';
$dbh = $db->dbh;
$db->disconnect;
$db = $sql2->db;
is_deeply $db->query('select 1 as one')->hashes->to_array, [{one => 1}],
'right structure';
isnt $db->dbh, $dbh, 'different database handle';
};
subtest 'Blocking error' => sub {
eval { $sql->db->query('does_not_exist') };
like $@, qr/does_not_exist.*database\.t/s, 'right error';
};
subtest 'Non-blocking error' => sub {
my ($fail, $result);
my $db = $sql->db;
$db->query(
'does_not_exist' => sub {
my ($db, $err, $results) = @_;
($fail, $result) = ($err, $results);
Mojo::IOLoop->stop;
}
);
Mojo::IOLoop->start;
like $fail, qr/does_not_exist/, 'right error';
is $db->dbh->errstr, $fail, 'same error';
};
subtest 'Error context' => sub {
eval { $sql->db->query('select * from table_does_not_exist') };
like $@, qr/database\.t/, 'right error';
};
subtest 'Double-quoted literal' => sub {
ok !eval { $sql->db->query('select "does_not_exist"') }, 'no double-quoted string literals';
};
subtest 'WAL mode option' => sub {
my $journal_mode = $sql->db->query('pragma journal_mode')->arrays->first->[0];
is uc $journal_mode, 'DELETE', 'right journal mode';
my $sql = Mojo::SQLite->new;
$sql->options->{wal_mode} = 1;
$journal_mode = $sql->db->query('pragma journal_mode')->arrays->first->[0];
is uc $journal_mode, 'WAL', 'right journal mode';
$sql = Mojo::SQLite->new;
$sql->options->{no_wal} = 1;
$journal_mode = $sql->db->query('pragma journal_mode')->arrays->first->[0];
is uc $journal_mode, 'DELETE', 'right journal mode';
};
done_testing();
connection.t 100644 001750 001750 10215 15205022012 16621 0 ustar 00grinnz grinnz 000000 000000 Mojo-SQLite-v4.0.0/t use Mojo::Base -strict;
use Test::More;
use DBD::SQLite::Constants ':dbd_sqlite_string_mode';
use Mojo::SQLite;
use URI::file;
subtest 'Defaults' => sub {
my $sql = Mojo::SQLite->new;
like $sql->dsn, qr/^dbi:SQLite:dbname=/, 'right data source';
my $options = {
AutoCommit => 1,
AutoInactiveDestroy => 1,
PrintError => 0,
RaiseError => 1,
sqlite_string_mode => DBD_SQLITE_STRING_MODE_UNICODE_FALLBACK,
wal_mode => 0,
};
is_deeply $sql->options, $options, 'right options';
};
subtest 'Minimal connection string with file' => sub {
my $sql = Mojo::SQLite->new('test.db');
is $sql->dsn, 'dbi:SQLite:dbname=test.db', 'right data source';
my $options = {
AutoCommit => 1,
AutoInactiveDestroy => 1,
PrintError => 0,
RaiseError => 1,
sqlite_string_mode => DBD_SQLITE_STRING_MODE_UNICODE_FALLBACK,
wal_mode => 0,
};
is_deeply $sql->options, $options, 'right options';
};
subtest 'Minimal connection string with in-memory database and option' => sub {
my $sql = Mojo::SQLite->new('sqlite::memory:?PrintError=1');
is $sql->dsn, 'dbi:SQLite:dbname=:memory:', 'right data source';
my $options = {
AutoCommit => 1,
AutoInactiveDestroy => 1,
PrintError => 1,
RaiseError => 1,
sqlite_string_mode => DBD_SQLITE_STRING_MODE_UNICODE_FALLBACK,
wal_mode => 0,
};
is_deeply $sql->options, $options, 'right options';
};
subtest 'Connection string with absolute filename and options' => sub {
my $uri = URI::file->new('/tmp/sqlite.db?#', 'unix')
->Mojo::Base::tap(query_form => {PrintError => 1, RaiseError => 0});
my $sql;
{
# Force unix interpretation
local %URI::file::OS_CLASS = ();
$sql = Mojo::SQLite->new($uri);
}
is $sql->dsn, 'dbi:SQLite:dbname=/tmp/sqlite.db?#', 'right data source';
my $options = {
AutoCommit => 1,
AutoInactiveDestroy => 1,
PrintError => 1,
RaiseError => 0,
sqlite_string_mode => DBD_SQLITE_STRING_MODE_UNICODE_FALLBACK,
wal_mode => 0,
};
is_deeply $sql->options, $options, 'right options';
};
subtest 'Connection string with lots of zeros' => sub {
my $sql = Mojo::SQLite->new('0?RaiseError=0');
is $sql->dsn, 'dbi:SQLite:dbname=0', 'right data source';
my $options = {
AutoCommit => 1,
AutoInactiveDestroy => 1,
PrintError => 0,
RaiseError => 0,
sqlite_string_mode => DBD_SQLITE_STRING_MODE_UNICODE_FALLBACK,
wal_mode => 0,
};
is_deeply $sql->options, $options, 'right options';
};
subtest 'Parse filename' => sub {
my $sql = Mojo::SQLite->new->from_filename('/foo#/bar?.db', {PrintError => 1});
is $sql->dsn, 'dbi:SQLite:dbname=/foo#/bar?.db', 'right data source';
my $options = {
AutoCommit => 1,
AutoInactiveDestroy => 1,
PrintError => 1,
RaiseError => 1,
sqlite_string_mode => DBD_SQLITE_STRING_MODE_UNICODE_FALLBACK,
wal_mode => 0,
};
is_deeply $sql->options, $options, 'right options';
};
subtest 'Invalid connection string' => sub {
eval { Mojo::SQLite->new('http://localhost:3000/test') };
like $@, qr/Invalid SQLite connection string/, 'right error';
};
subtest 'Legacy sqlite_unicode enabled' => sub {
my $sql = Mojo::SQLite->new('test.db?sqlite_unicode=1');
like $sql->dsn, qr/^dbi:SQLite:dbname=/, 'right data source';
my $options = {
AutoCommit => 1,
AutoInactiveDestroy => 1,
PrintError => 0,
RaiseError => 1,
sqlite_unicode => 1,
wal_mode => 0,
};
is_deeply $sql->options, $options, 'right options';
};
subtest 'Legacy sqlite_unicode disabled' => sub {
my $sql = Mojo::SQLite->new('test.db?sqlite_unicode=0');
like $sql->dsn, qr/^dbi:SQLite:dbname=/, 'right data source';
my $options = {
AutoCommit => 1,
AutoInactiveDestroy => 1,
PrintError => 0,
RaiseError => 1,
sqlite_unicode => 0,
wal_mode => 0,
};
is_deeply $sql->options, $options, 'right options';
};
done_testing();
migrations.t 100644 001750 001750 16657 15205022012 16656 0 ustar 00grinnz grinnz 000000 000000 Mojo-SQLite-v4.0.0/t use Mojo::Base -strict;
use Test::More;
use File::Spec::Functions 'catfile';
use File::Temp;
use FindBin;
use Mojo::SQLite;
my $tempdir = File::Temp->newdir;
my $tempfile = catfile($tempdir, 'test.db');
# Clean up before start
my $sql = Mojo::SQLite->new->from_filename($tempfile);
$sql->db->query('drop table if exists mojo_migrations');
subtest 'Defaults' => sub {
is $sql->migrations->name, 'migrations', 'right name';
is $sql->migrations->latest, 0, 'latest version is 0';
is $sql->migrations->active, 0, 'active version is 0';
};
subtest 'Create migrations table' => sub {
ok !(grep {/^"main"."mojo_migrations"$/i} @{$sql->db->tables}),
'migrations table does not exist';
is $sql->migrations->migrate->active, 0, 'active version is 0';
ok !(grep {/^"main"."mojo_migrations"$/i} @{$sql->db->tables}),
'migrations table does not exist';
is $sql->migrations->from_string("-- 1 up\n\n")->migrate->active, 1,
'active version is 1';
ok !!(grep {/^"main"."mojo_migrations"$/i} @{$sql->db->tables}),
'migrations table exists';
is $sql->migrations->migrate(0)->active, 0, 'active version is 0';
};
subtest 'Migrations from DATA section' => sub {
is $sql->migrations->from_data->latest, 0, 'latest version is 0';
is $sql->migrations->from_data(__PACKAGE__)->latest, 0, 'latest version is 0';
is $sql->migrations->name('test1')->from_data->latest, 10,
'latest version is 10';
is $sql->migrations->name('test2')->from_data->latest, 2, 'latest version is 2';
is $sql->migrations->name('migrations')->from_data(__PACKAGE__, 'test1')
->latest, 10, 'latest version is 10';
is $sql->migrations->name('test2')->from_data(__PACKAGE__)->latest, 2,
'latest version is 2';
};
subtest 'Different syntax variations' => sub {
$sql->migrations->name('migrations_test')->from_string(<migrations->latest, 10, 'latest version is 10';
is $sql->migrations->active, 0, 'active version is 0';
is $sql->migrations->migrate->active, 10, 'active version is 10';
ok !!(grep {/^"main"."migration_test_one"$/i} @{$sql->db->tables}),
'first table exists';
ok !!(grep {/^"main"."migration_test_two"$/i} @{$sql->db->tables}),
'second table exists';
is_deeply $sql->db->query('select * from migration_test_one')->hash,
{foo => 'works ♥'}, 'right structure';
is $sql->migrations->migrate->active, 10, 'active version is 10';
is $sql->migrations->migrate(1)->active, 1, 'active version is 1';
is $sql->db->query('select * from migration_test_one')->hash, undef,
'no result';
is $sql->migrations->migrate(3)->active, 3, 'active version is 3';
is $sql->db->query('select * from migration_test_two')->hash, undef,
'no result';
is $sql->migrations->migrate->active, 10, 'active version is 10';
is_deeply $sql->db->query('select * from migration_test_two')->hash,
{bar => 'works too'}, 'right structure';
is $sql->migrations->migrate(0)->active, 0, 'active version is 0';
};
subtest 'Bad and concurrent migrations' => sub {
my $sql2 = Mojo::SQLite->new->from_filename($tempfile);
$sql2->migrations->name('migrations_test2')
->from_file(catfile($FindBin::Bin, 'migrations', 'test.sql'));
is $sql2->migrations->latest, 4, 'latest version is 4';
is $sql2->migrations->active, 0, 'active version is 0';
eval { $sql2->migrations->migrate };
like $@, qr/does_not_exist/, 'right error';
is $sql2->migrations->migrate(3)->active, 3, 'active version is 3';
is $sql2->migrations->migrate(2)->active, 2, 'active version is 2';
is $sql->migrations->active, 0, 'active version is still 0';
is $sql->migrations->migrate->active, 10, 'active version is 10';
is_deeply $sql2->db->query('select * from migration_test_three')
->hashes->to_array, [{baz => 'just'}, {baz => 'works ♥'}],
'right structure';
is $sql->migrations->migrate(0)->active, 0, 'active version is 0';
is $sql2->migrations->migrate(0)->active, 0, 'active version is 0';
};
subtest 'Migrate automatically' => sub {
my $sql3 = Mojo::SQLite->new->from_filename($tempfile);
$sql3->migrations->name('migrations_test')->from_string(<<'EOF');
-- 5 up
create table if not exists migration_test_six (foo text);
-- 6 up
insert into migration_test_six values ('works!');
-- 5 down
drop table if exists migration_test_six;
-- 6 down
delete from migration_test_six;
EOF
$sql3->auto_migrate(1)->db;
is $sql3->migrations->active, 6, 'active version is 6';
is_deeply $sql3->db->query('select * from migration_test_six')->hashes,
[{foo => 'works!'}], 'right structure';
is $sql3->migrations->migrate(5)->active, 5, 'active version is 5';
is_deeply $sql3->db->query('select * from migration_test_six')->hashes, [],
'right structure';
is $sql3->migrations->migrate(0)->active, 0, 'active version is 0';
is $sql3->migrations->sql_for(0, 5), <migrations->sql_for(6, 0), <migrations->sql_for(6, 5), <migrations->sql_for(6, 6), '', 'right SQL';
is $sql3->migrations->sql_for(2, 3), '', 'right SQL';
};
subtest 'Migrate automatically with shared connection cache' => sub {
my $sql4 = Mojo::SQLite->new->from_filename($tempfile);
my $sql5 = Mojo::SQLite->new($sql4);
$sql4->auto_migrate(1)->migrations->name('test1')->from_data;
$sql5->auto_migrate(1)->migrations->name('test3')->from_data;
is_deeply $sql5->db->query('select * from migration_test_four')
->hashes->to_array, [{test => 10}], 'right structure';
is_deeply $sql5->db->query('select * from migration_test_six')->hashes->to_array,
[], 'right structure';
};
subtest 'Unknown version' => sub {
eval { $sql->migrations->migrate(23) };
like $@, qr/Version 23 has no migration/, 'right error';
};
subtest 'Version mismatch' => sub {
my $newer = <migrations->name('migrations_test3')->from_string($newer);
is $sql->migrations->migrate->active, 2, 'active version is 2';
$sql->migrations->from_string(<migrations->migrate };
like $@, qr/Active version 2 is greater than the latest version 1/,
'right error';
eval { $sql->migrations->migrate(0) };
like $@, qr/Active version 2 is greater than the latest version 1/,
'right error';
is $sql->migrations->from_string($newer)->migrate(0)->active, 0,
'active version is 0';
};
done_testing();
__DATA__
@@ test1
-- 7 up
create table migration_test_four (test integer);
-- 10 up
insert into migration_test_four values (10);
@@ test2
-- 2 up
create table migration_test_five (test integer);
@@ test3
-- 2 up
create table migration_test_six (test integer);
CONTRIBUTING.md 100644 001750 001750 10500 15205022012 16260 0 ustar 00grinnz grinnz 000000 000000 Mojo-SQLite-v4.0.0 # HOW TO CONTRIBUTE
Thank you for considering contributing to this distribution. This file
contains instructions that will help you work with the source code.
The distribution is managed with [Dist::Zilla](https://metacpan.org/pod/Dist::Zilla).
This means that many of the usual files you might expect are not in the
repository, but are generated at release time. Some generated files are kept
in the repository as a convenience (e.g. Build.PL/Makefile.PL and META.json).
Generally, **you do not need Dist::Zilla to contribute patches**. You may need
Dist::Zilla to create a tarball. See below for guidance.
## Getting dependencies
If you have App::cpanminus 1.6 or later installed, you can use
[cpanm](https://metacpan.org/pod/cpanm) to satisfy dependencies like this:
$ cpanm --installdeps --with-develop .
You can also run this command (or any other cpanm command) without installing
App::cpanminus first, using the fatpacked `cpanm` script via curl or wget:
$ curl -L https://cpanmin.us | perl - --installdeps --with-develop .
$ wget -qO - https://cpanmin.us | perl - --installdeps --with-develop .
Otherwise, look for either a `cpanfile`, `prereqs.json`/`prereqs.yml`, or
`META.json` file for a list of dependencies to satisfy.
## Running tests
You can run tests directly using the `prove` tool:
$ prove -l
$ prove -lv t/some_test_file.t
For most of my distributions, `prove` is entirely sufficient for you to test
any patches you have. I use `prove` for 99% of my testing during development.
## Code style and tidying
Please try to match any existing coding style. If there is a `.perltidyrc`
file, please install Perl::Tidy and use perltidy before submitting patches.
## Installing and using Dist::Zilla
[Dist::Zilla](https://metacpan.org/pod/Dist::Zilla) is a very powerful
authoring tool, optimized for maintaining a large number of distributions with
a high degree of automation, but it has a large dependency chain, a bit of a
learning curve and requires a number of author-specific plugins.
To install it from CPAN, I recommend one of the following approaches for the
quickest installation:
# using CPAN.pm, but bypassing non-functional pod tests
$ cpan TAP::Harness::Restricted
$ PERL_MM_USE_DEFAULT=1 HARNESS_CLASS=TAP::Harness::Restricted cpan Dist::Zilla
# using cpanm, bypassing *all* tests
$ cpanm -n Dist::Zilla
In either case, it's probably going to take about 10 minutes. Go for a walk,
go get a cup of your favorite beverage, take a bathroom break, or whatever.
When you get back, Dist::Zilla should be ready for you.
Then you need to install any plugins specific to this distribution:
$ dzil authordeps --missing | cpanm
You can use Dist::Zilla to install the distribution's dependencies if you
haven't already installed them with cpanm:
$ dzil listdeps --missing --develop | cpanm
You can instead combine these two steps into one command by installing
Dist::Zilla::App::Command::installdeps then running:
$ dzil installdeps
Once everything is installed, here are some dzil commands you might try:
$ dzil build
$ dzil test
$ dzil regenerate
You can learn more about Dist::Zilla at http://dzil.org/
## Other notes
This distribution maintains the generated `META.json` and either `Makefile.PL`
or `Build.PL` in the repository. This allows the distribution or its
dependencies to be installed directly from GitHub or a local git repository
without needing Dist::Zilla for testing or
[CI](https://en.wikipedia.org/wiki/Continuous_integration).
$ cpanm git://github.com/Author/Distribution-Name.git
$ cd Distribution-Name; cpanm --installdeps .
Contributions are preferred in the form of a GitHub pull request. See
[Using pull requests](https://help.github.com/articles/using-pull-requests/)
for further information. You can use the GitHub issue tracker to report issues
without an accompanying patch.
# CREDITS
This file was adapted from an initial `CONTRIBUTING.mkdn` file from David
Golden under the terms of the [CC0](https://creativecommons.org/share-your-work/public-domain/cc0/), with inspiration from the
contributing documents from [Dist::Zilla::Plugin::Author::KENTNL::CONTRIBUTING](https://metacpan.org/pod/Dist::Zilla::Plugin::Author::KENTNL::CONTRIBUTING)
and [Dist::Zilla::PluginBundle::Author::ETHER](https://metacpan.org/pod/Dist::Zilla::PluginBundle::Author::ETHER).
Mojo 000755 001750 001750 0 15205022012 15325 5 ustar 00grinnz grinnz 000000 000000 Mojo-SQLite-v4.0.0/lib SQLite.pm 100644 001750 001750 45432 15205022012 17214 0 ustar 00grinnz grinnz 000000 000000 Mojo-SQLite-v4.0.0/lib/Mojo package Mojo::SQLite;
use Mojo::Base 'Mojo::EventEmitter';
use Carp 'croak';
use DBI;
use DBD::SQLite;
use DBD::SQLite::Constants qw(:database_connection_configuration_options :dbd_sqlite_string_mode);
use File::Spec::Functions 'catfile';
use File::Temp;
use Mojo::SQLite::Database;
use Mojo::SQLite::Migrations;
use Scalar::Util qw(blessed weaken);
use SQL::Abstract::Pg;
use URI;
use URI::db;
our $VERSION = 'v4.0.0';
has abstract => sub { SQL::Abstract::Pg->new(name_sep => '.', quote_char => '"') };
has 'auto_migrate';
has database_class => 'Mojo::SQLite::Database';
has dsn => sub { _url_from_file(shift->_tempfile)->dbi_dsn };
has max_connections => 1;
has migrations => sub { Mojo::SQLite::Migrations->new(sqlite => shift) };
has options => sub {
{
AutoCommit => 1,
AutoInactiveDestroy => 1,
PrintError => 0,
RaiseError => 1,
sqlite_string_mode => DBD_SQLITE_STRING_MODE_UNICODE_FALLBACK,
wal_mode => 0,
};
};
has 'parent';
sub new { @_ > 1 ? shift->SUPER::new->from_string(@_) : shift->SUPER::new }
sub db { $_[0]->database_class->new(dbh => $_[0]->_prepare, sqlite => $_[0]) }
sub from_filename { shift->from_string(_url_from_file(shift, shift)) }
sub from_string {
my ($self, $str) = @_;
return $self unless $str;
return $self->parent($str) if blessed $str and $str->isa('Mojo::SQLite');
my $url = URI->new($str);
# Options
my %options = $url->query_form;
$url->query(undef);
# don't set default string_mode if sqlite_unicode legacy option is set
delete $self->options->{sqlite_string_mode} if exists $options{sqlite_unicode};
@{$self->options}{keys %options} = values %options;
# Parse URL based on scheme
$url->scheme('file') unless $url->has_recognized_scheme;
if ($url->scheme eq 'file') {
$url = _url_from_file($url->file);
} elsif ($url->scheme ne 'db') {
$url = URI::db->new($url);
}
croak qq{Invalid SQLite connection string "$str"}
unless $url->has_recognized_engine and $url->canonical_engine eq 'sqlite'
and (($url->host // '') eq '' or $url->host eq 'localhost');
# Temp database file
$url->dbname($self->_tempfile) if $url->dbname eq ':temp:';
return $self->dsn($url->dbi_dsn);
}
sub _dequeue {
my $self = shift;
# Fork-safety
delete @$self{qw(pid queue)} unless ($self->{pid} //= $$) eq $$;
while (my $dbh = shift @{$self->{queue} || []}) { return $dbh if $dbh->ping }
my $dbh = DBI->connect($self->dsn, undef, undef, $self->options)
// croak "DBI connection to @{[$self->dsn]} failed: $DBI::errstr"; # RaiseError disabled
$dbh->sqlite_db_config(SQLITE_DBCONFIG_DQS_DDL, 0);
$dbh->sqlite_db_config(SQLITE_DBCONFIG_DQS_DML, 0);
if ($self->options->{wal_mode} and !$self->options->{no_wal}) {
$dbh->do('pragma journal_mode=WAL');
$dbh->do('pragma synchronous=NORMAL');
}
# Cache the last insert rowid on inserts
weaken(my $weakdbh = $dbh);
$dbh->sqlite_update_hook(sub {
$weakdbh->{private_mojo_last_insert_id} = $_[3] if $_[0] == DBD::SQLite::INSERT;
});
$self->emit(connection => $dbh);
return $dbh;
}
sub _enqueue {
my ($self, $dbh) = @_;
if (my $parent = $self->parent) { return $parent->_enqueue($dbh) }
my $queue = $self->{queue} ||= [];
push @$queue, $dbh if $dbh->{Active};
shift @$queue while @$queue > $self->max_connections;
}
sub _prepare {
my $self = shift;
# Automatic migrations
++$self->{migrated} and $self->migrations->migrate
if !$self->{migrated} && $self->auto_migrate;
my $parent = $self->parent;
return $parent ? $parent->_prepare : $self->_dequeue;
}
sub _tempfile { catfile(shift->{tempdir} = File::Temp->newdir, 'sqlite.db') }
sub _url_from_file {
my $url = URI::db->new;
$url->engine('sqlite');
$url->dbname(shift);
if (my $options = shift) { $url->query_form($options) }
return $url;
}
1;
=head1 NAME
Mojo::SQLite - A tiny Mojolicious wrapper for SQLite
=head1 SYNOPSIS
use Mojo::SQLite;
# Select the library version
my $sql = Mojo::SQLite->new('sqlite:test.db');
say $sql->db->query('select sqlite_version() as version')->hash->{version};
# Use migrations to create a table
$sql->migrations->name('my_names_app')->from_string(<migrate;
-- 1 up
create table names (id integer primary key autoincrement, name text);
-- 1 down
drop table names;
EOF
# Use migrations to drop and recreate the table
$sql->migrations->migrate(0)->migrate;
# Get a database handle from the cache for multiple queries
my $db = $sql->db;
# Use SQL::Abstract to generate simple CRUD queries for you
$db->insert('names', {name => 'Isabel'});
my $id = $db->select('names', ['id'], {name => 'Isabel'})->hash->{id};
$db->update('names', {name => 'Bel'}, {id => $id});
$db->delete('names', {name => 'Bel'});
# Insert a few rows in a transaction with SQL and placeholders
eval {
my $tx = $db->begin;
$db->query('insert into names (name) values (?)', 'Sara');
$db->query('insert into names (name) values (?)', 'Stefan');
$tx->commit;
};
say $@ if $@;
# Insert another row with SQL::Abstract and return the generated id
say $db->insert('names', {name => 'Daniel'})->last_insert_id;
# JSON roundtrip
say $db->query('select ? as foo', {json => {bar => 'baz'}})
->expand(json => 'foo')->hash->{foo}{bar};
# Select one row at a time
my $results = $db->query('select * from names');
while (my $next = $results->hash) {
say $next->{name};
}
# Select all rows with SQL::Abstract
say $_->{name} for $db->select('names')->hashes->each;
=head1 DESCRIPTION
L is a tiny wrapper around L that makes
L a lot of fun to use with the
L real-time web framework. Use all
L SQLite has to offer, generate CRUD
queries from data structures, and manage your database schema with migrations.
=head1 BASICS
Database and statement handles are cached automatically, so they can be reused
transparently to increase performance. And you can handle connection timeouts
gracefully by holding on to them only for short amounts of time.
use Mojolicious::Lite;
use Mojo::SQLite;
helper sqlite => sub { state $sql = Mojo::SQLite->new('sqlite:test.db') };
get '/' => sub ($c) {
my $db = $c->sqlite->db;
$c->render(json => $db->query(q{select datetime('now','localtime') as now})->hash);
};
app->start;
In this example application, we create a C helper to store a
L object. Our action calls that helper and uses the method
L to dequeue a L object from the
connection pool. Then we use the method L to
execute an L
statement, which returns a L object. And finally we call
the method L to retrieve the first row as a hash
reference.
All I/O and queries are performed synchronously, and SQLite's default journal
mode only supports concurrent reads from multiple processes while the database
is not being written.
The "Write-Ahead Log" journal mode allows multiple
processes to read and write concurrently to the same database file (but only
one can write at a time). WAL mode is enabled by the C option,
and persists when opening that same database in the future.
# Performed concurrently (concurrent with writing only with WAL journaling mode)
my $pid = fork || die $!;
say $sql->db->query(q{select datetime('now','localtime') as time})->hash->{time};
exit unless $pid;
C was enabled by default until L version v4.0.0; the
C option (supported since version 2.002) can be specified to prevent it
when creating new databases, but to disable it in an existing database the
C must be set manually back to the default of C. See
L and L for more
information.
# Disable WAL mode on a database where it was previously enabled
$sql->db->query('PRAGMA journal_mode=DELETE');
The L is
disabled for all connections since Mojo::SQLite 3.003; use single quotes for
string literals and double quotes for identifiers, as is normally recommended.
All cached database handles will be reset automatically if a new process has
been forked, this allows multiple processes to share the same L
object safely.
Any database errors will throw an exception as C is automatically
enabled, so use C or L to catch them. This makes transactions
with L easy.
While passing a file path of C<:memory:> (or a custom L"dsn"> with
C) will create a temporary database, in-memory databases cannot be
shared between connections, so subsequent calls to L"db"> may return
connections to completely different databases. For a temporary database that
can be shared between connections and processes, pass a file path of C<:temp:>
to store the database in a temporary directory (this is the default), or
consider constructing a temporary directory yourself with L if you
need to reuse the filename. A temporary directory allows SQLite to create
L safely.
use File::Spec::Functions 'catfile';
use File::Temp;
use Mojo::SQLite;
my $tempdir = File::Temp->newdir; # Deleted when object goes out of scope
my $tempfile = catfile $tempdir, 'test.db';
my $sql = Mojo::SQLite->new->from_filename($tempfile);
=head1 EXAMPLES
This distribution also contains a well-structured example
L
you can use for inspiration. This application shows how to apply the MVC design
pattern in practice.
=head1 EVENTS
L inherits all events from L and can emit the
following new ones.
=head2 connection
$sql->on(connection => sub ($sql, $dbh) {
$dbh->do('pragma journal_size_limit=1000000');
});
Emitted when a new database connection has been established.
=head1 ATTRIBUTES
L implements the following attributes.
=head2 abstract
my $abstract = $sql->abstract;
$sql = $sql->abstract(SQL::Abstract->new);
L object used to generate CRUD queries for
L, defaults to a L object with
C set to C<.> and C set to C<">.
# Generate WHERE clause and bind values
my($stmt, @bind) = $sql->abstract->where({foo => 'bar', baz => 'yada'});
L provides additional features to the L
query methods in L such as C<-json> and
C/C. The C feature is not applicable to SQLite queries.
$sql->db->select(['some_table', ['other_table', foo_id => 'id']],
['foo', [bar => 'baz'], \q{datetime('now') as dt}],
{foo => 'value'},
{order_by => 'foo', limit => 10, offset => 5, group_by => ['foo'], having => {baz => 'value'}});
# Upsert supported since SQLite 3.24.0
$sql->db->insert('some_table', {name => $name, value => $value},
{on_conflict => [name => {value => \'"excluded"."value"'}]});
=head2 auto_migrate
my $bool = $sql->auto_migrate;
$sql = $sql->auto_migrate($bool);
Automatically migrate to the latest database schema with L"migrations">, as
soon as L"db"> has been called for the first time.
=head2 database_class
my $class = $sql->database_class;
$sql = $sql->database_class('MyApp::Database');
Class to be used by L"db">, defaults to L. Note that
this class needs to have already been loaded before L"db"> is called.
=head2 dsn
my $dsn = $sql->dsn;
$sql = $sql->dsn('dbi:SQLite:uri=file:foo.db');
Data source name, defaults to C followed by a path to a
temporary file.
=head2 max_connections
my $max = $sql->max_connections;
$sql = $sql->max_connections(3);
Maximum number of idle database handles to cache for future use, defaults to
C<1>.
=head2 migrations
my $migrations = $sql->migrations;
$sql = $sql->migrations(Mojo::SQLite::Migrations->new);
L object you can use to change your database schema
more easily.
# Load migrations from file and migrate to latest version
$sql->migrations->from_file('/home/dbook/migrations.sql')->migrate;
=head2 options
my $options = $sql->options;
$sql = $sql->options({AutoCommit => 1, RaiseError => 1});
Options for database handles, defaults to setting C to
C, setting C,
C and C, and deactivating C.
Note that C and C are considered mandatory, so
deactivating them would be very dangerous. See
L and
L for more information on available
options.
=head2 parent
my $parent = $sql->parent;
$sql = $sql->parent(Mojo::SQLite->new);
Another L object to use for connection management, instead of
establishing and caching our own database connections.
=head1 METHODS
L inherits all methods from L and implements
the following new ones.
=head2 new
my $sql = Mojo::SQLite->new;
my $sql = Mojo::SQLite->new('file:test.db);
my $sql = Mojo::SQLite->new('sqlite:test.db');
my $sql = Mojo::SQLite->new(Mojo::SQLite->new);
Construct a new L object and parse connection string with
L"from_string"> if necessary.
# Customize configuration further
my $sql = Mojo::SQLite->new->dsn('dbi:SQLite:dbname=test.db');
my $sql = Mojo::SQLite->new->dsn('dbi:SQLite:uri=file:test.db?mode=memory');
# Pass filename directly
my $sql = Mojo::SQLite->new->from_filename($filename);
=head2 db
my $db = $sql->db;
Get a database object based on L"database_class"> (which is usually
L) for a cached or newly established database
connection. The L database handle will be automatically cached
again when that object is destroyed, so you can handle problems like connection
timeouts gracefully by holding on to it only for short amounts of time.
# Add up all the money
say $sql->db->select('accounts')
->hashes->reduce(sub { $a->{money} + $b->{money} });
=head2 from_filename
$sql = $sql->from_filename('C:\\Documents and Settings\\foo & bar.db', $options);
Parse database filename directly. Unlike L"from_string">, the filename is
parsed as a local filename and not a URL. A hashref of L"options"> may be
passed as the second argument.
# Absolute filename
$sql->from_filename('/home/fred/data.db');
# Relative to current directory
$sql->from_filename('data.db');
# Temporary file database (default)
$sql->from_filename(':temp:');
# In-memory temporary database (single connection only)
my $db = $sql->from_filename(':memory:')->db;
# Additional options
$sql->from_filename($filename, { PrintError => 1 });
# Readonly connection
$sql->from_filename($filename, { ReadOnly => 1 });
# Strict unicode strings and WAL mode
use DBD::SQLite::Constants ':dbd_sqlite_string_mode';
$sql->from_filename($filename, { sqlite_string_mode => DBD_SQLITE_STRING_MODE_UNICODE_STRICT, wal_mode => 1 });
=head2 from_string
$sql = $sql->from_string('test.db');
$sql = $sql->from_string('file:test.db');
$sql = $sql->from_string('file:///C:/foo/bar.db');
$sql = $sql->from_string('sqlite:C:%5Cfoo%5Cbar.db');
$sql = $sql->from_string(Mojo::SQLite->new);
Parse configuration from connection string or use another L
object as L"parent">. Connection strings are parsed as URLs, so you should
construct them using a module like L, L, or L.
For portability on non-Unix-like systems, either construct the URL with the
C scheme, or use L to construct a URL with the C
scheme. A URL with no scheme will be parsed as a C URL, and C URLs
are parsed according to the current operating system. If specified, the
hostname must be C. If the URL has a query string, it will be parsed
and applied to L"options">.
# Absolute filename
$sql->from_string('sqlite:////home/fred/data.db');
$sql->from_string('sqlite://localhost//home/fred/data.db');
$sql->from_string('sqlite:/home/fred/data.db');
$sql->from_string('file:///home/fred/data.db');
$sql->from_string('file://localhost/home/fred/data.db');
$sql->from_string('file:/home/fred/data.db');
$sql->from_string('///home/fred/data.db');
$sql->from_string('//localhost/home/fred/data.db');
$sql->from_string('/home/fred/data.db');
# Relative to current directory
$sql->from_string('sqlite:data.db');
$sql->from_string('file:data.db');
$sql->from_string('data.db');
# Connection string must be a valid URL
$sql->from_string(Mojo::URL->new->scheme('sqlite')->path($filename));
$sql->from_string(URI::db->new->Mojo::Base::tap(engine => 'sqlite')->Mojo::Base::tap(dbname => $filename));
$sql->from_string(URI::file->new($filename));
# Temporary file database (default)
$sql->from_string(':temp:');
# In-memory temporary database (single connection only)
my $db = $sql->from_string(':memory:')->db;
# Additional options
$sql->from_string('data.db?PrintError=1&sqlite_allow_multiple_statements=1');
$sql->from_string(Mojo::URL->new->scheme('sqlite')->path($filename)->query(sqlite_see_if_its_a_number => 1));
$sql->from_string(URI::file->new($filename)->Mojo::Base::tap(query_form => {PrintError => 1}));
# Readonly connection with WAL mode
$sql->from_string('data.db?ReadOnly=1&wal_mode=1');
# String unicode strings and WAL mode
use DBD::SQLite::Constants ':dbd_sqlite_string_mode';
$sql->from_string(Mojo::URL->new->scheme('sqlite')->path('data.db')
->query(sqlite_string_mode => DBD_SQLITE_STRING_MODE_UNICODE_STRICT, wal_mode => 1));
=head1 DEBUGGING
You can set the C environment variable to get some advanced
diagnostics information printed by L.
DBI_TRACE=1
DBI_TRACE=15
DBI_TRACE=SQL
=head1 REFERENCE
This is the class hierarchy of the L distribution.
=over 2
=item * L
=item * L
=item * L
=item * L
=item * L
=back
=head1 BUGS
Report any issues on the public bugtracker.
=head1 AUTHOR
Dan Book, C
=head1 CREDITS
Sebastian Riedel, author of L, which this distribution is based on.
=head1 COPYRIGHT AND LICENSE
Copyright 2015, Dan Book.
This library is free software; you may redistribute it and/or modify it under
the terms of the Artistic License version 2.0.
=head1 SEE ALSO
L, L, L
sqlite_lite_app.t 100644 001750 001750 2134 15205022012 17621 0 ustar 00grinnz grinnz 000000 000000 Mojo-SQLite-v4.0.0/t use Mojo::Base -strict;
BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' }
use Test::More;
use Mojo::SQLite;
use Mojolicious::Lite;
use Scalar::Util 'refaddr';
use Test::Mojo;
helper sqlite => sub { state $sql = Mojo::SQLite->new };
app->sqlite->db->query('create table if not exists app_test (stuff text)');
app->sqlite->db->query('insert into app_test values (?)', 'I ♥ Mojolicious!');
get '/blocking' => sub {
my $c = shift;
my $db = $c->sqlite->db;
$c->res->headers->header('X-Ref' => refaddr $db->dbh);
$c->render(text => $db->query('select * from app_test')->hash->{stuff});
};
my $t = Test::Mojo->new;
subtest 'Make sure migrations are not served as static files' => sub {
$t->get_ok('/app_test')->status_is(404);
};
subtest 'Blocking select (with connection reuse)' => sub {
$t->get_ok('/blocking')->status_is(200)->content_is('I ♥ Mojolicious!');
my $ref = $t->tx->res->headers->header('X-Ref');
$t->get_ok('/blocking')->status_is(200)->header_is('X-Ref', $ref)
->content_is('I ♥ Mojolicious!');
$t->app->sqlite->db->query('drop table app_test');
};
done_testing();
00-report-prereqs.t 100644 001750 001750 13625 15205022012 17701 0 ustar 00grinnz grinnz 000000 000000 Mojo-SQLite-v4.0.0/t #!perl
use strict;
use warnings;
# This test was generated by Dist::Zilla::Plugin::Test::ReportPrereqs 0.029
use Test::More tests => 1;
use Module::Metadata;
use File::Spec;
# from $version::LAX
my $lax_version_re =
qr/(?: undef | (?: (?:[0-9]+) (?: \. | (?:\.[0-9]+) (?:_[0-9]+)? )?
|
(?:\.[0-9]+) (?:_[0-9]+)?
) | (?:
v (?:[0-9]+) (?: (?:\.[0-9]+)+ (?:_[0-9]+)? )?
|
(?:[0-9]+)? (?:\.[0-9]+){2,} (?:_[0-9]+)?
)
)/x;
# hide optional CPAN::Meta modules from prereq scanner
# and check if they are available
my $cpan_meta = "CPAN::Meta";
my $cpan_meta_pre = "CPAN::Meta::Prereqs";
my $HAS_CPAN_META = eval "require $cpan_meta; $cpan_meta->VERSION('2.120900')" && eval "require $cpan_meta_pre"; ## no critic
# Verify requirements?
my $DO_VERIFY_PREREQS = 1;
sub _max {
my $max = shift;
$max = ( $_ > $max ) ? $_ : $max for @_;
return $max;
}
sub _merge_prereqs {
my ($collector, $prereqs) = @_;
# CPAN::Meta::Prereqs object
if (ref $collector eq $cpan_meta_pre) {
return $collector->with_merged_prereqs(
CPAN::Meta::Prereqs->new( $prereqs )
);
}
# Raw hashrefs
for my $phase ( keys %$prereqs ) {
for my $type ( keys %{ $prereqs->{$phase} } ) {
for my $module ( keys %{ $prereqs->{$phase}{$type} } ) {
$collector->{$phase}{$type}{$module} = $prereqs->{$phase}{$type}{$module};
}
}
}
return $collector;
}
my @include = qw(
);
my @exclude = qw(
);
# Add static prereqs to the included modules list
my $static_prereqs = do './t/00-report-prereqs.dd';
# Merge all prereqs (either with ::Prereqs or a hashref)
my $full_prereqs = _merge_prereqs(
( $HAS_CPAN_META ? $cpan_meta_pre->new : {} ),
$static_prereqs
);
# Add dynamic prereqs to the included modules list (if we can)
my ($source) = grep { -f } 'MYMETA.json', 'MYMETA.yml';
my $cpan_meta_error;
if ( $source && $HAS_CPAN_META
&& (my $meta = eval { CPAN::Meta->load_file($source) } )
) {
$full_prereqs = _merge_prereqs($full_prereqs, $meta->prereqs);
}
else {
$cpan_meta_error = $@; # capture error from CPAN::Meta->load_file($source)
$source = 'static metadata';
}
my @full_reports;
my @dep_errors;
my $req_hash = $HAS_CPAN_META ? $full_prereqs->as_string_hash : $full_prereqs;
# Add static includes into a fake section
for my $mod (@include) {
$req_hash->{other}{modules}{$mod} = 0;
}
for my $phase ( qw(configure build test runtime develop other) ) {
next unless $req_hash->{$phase};
next if ($phase eq 'develop' and not $ENV{AUTHOR_TESTING});
for my $type ( qw(requires recommends suggests conflicts modules) ) {
next unless $req_hash->{$phase}{$type};
my $title = ucfirst($phase).' '.ucfirst($type);
my @reports = [qw/Module Want Have/];
for my $mod ( sort keys %{ $req_hash->{$phase}{$type} } ) {
next if grep { $_ eq $mod } @exclude;
my $want = $req_hash->{$phase}{$type}{$mod};
$want = "undef" unless defined $want;
$want = "any" if !$want && $want == 0;
if ($mod eq 'perl') {
push @reports, ['perl', $want, $]];
next;
}
my $req_string = $want eq 'any' ? 'any version required' : "version '$want' required";
my $file = $mod;
$file =~ s{::}{/}g;
$file .= ".pm";
my ($prefix) = grep { -e File::Spec->catfile($_, $file) } @INC;
if ($prefix) {
my $have = Module::Metadata->new_from_file( File::Spec->catfile($prefix, $file) )->version;
$have = "undef" unless defined $have;
push @reports, [$mod, $want, $have];
if ( $DO_VERIFY_PREREQS && $HAS_CPAN_META && $type eq 'requires' ) {
if ( $have !~ /\A$lax_version_re\z/ ) {
push @dep_errors, "$mod version '$have' cannot be parsed ($req_string)";
}
elsif ( ! $full_prereqs->requirements_for( $phase, $type )->accepts_module( $mod => $have ) ) {
push @dep_errors, "$mod version '$have' is not in required range '$want'";
}
}
}
else {
push @reports, [$mod, $want, "missing"];
if ( $DO_VERIFY_PREREQS && $type eq 'requires' ) {
push @dep_errors, "$mod is not installed ($req_string)";
}
}
}
if ( @reports ) {
push @full_reports, "=== $title ===\n\n";
my $ml = _max( map { length $_->[0] } @reports );
my $wl = _max( map { length $_->[1] } @reports );
my $hl = _max( map { length $_->[2] } @reports );
if ($type eq 'modules') {
splice @reports, 1, 0, ["-" x $ml, "", "-" x $hl];
push @full_reports, map { sprintf(" %*s %*s\n", -$ml, $_->[0], $hl, $_->[2]) } @reports;
}
else {
splice @reports, 1, 0, ["-" x $ml, "-" x $wl, "-" x $hl];
push @full_reports, map { sprintf(" %*s %*s %*s\n", -$ml, $_->[0], $wl, $_->[1], $hl, $_->[2]) } @reports;
}
push @full_reports, "\n";
}
}
}
if ( @full_reports ) {
diag "\nVersions for all modules listed in $source (including optional ones):\n\n", @full_reports;
}
if ( $cpan_meta_error || @dep_errors ) {
diag "\n*** WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING ***\n";
}
if ( $cpan_meta_error ) {
my ($orig_source) = grep { -f } 'MYMETA.json', 'MYMETA.yml';
diag "\nCPAN::Meta->load_file('$orig_source') failed with: $cpan_meta_error\n";
}
if ( @dep_errors ) {
diag join("\n",
"\nThe following REQUIRED prerequisites were not satisfied:\n",
@dep_errors,
"\n"
);
}
pass('Reported prereqs');
# vim: ts=4 sts=4 sw=4 et:
migrations 000755 001750 001750 0 15205022012 16272 5 ustar 00grinnz grinnz 000000 000000 Mojo-SQLite-v4.0.0/t test.sql 100644 001750 001750 412 15205022012 20107 0 ustar 00grinnz grinnz 000000 000000 Mojo-SQLite-v4.0.0/t/migrations -- 1 up
create table if not exists migration_test_three (baz text);
-- 1 down
drop table if exists migration_test_three;
-- 2 up
insert into migration_test_three values ('just');
insert into migration_test_three values ('works ♥');
-- 3 up
-- 4 up
does_not_exist;
author 000755 001750 001750 0 15205022012 15610 5 ustar 00grinnz grinnz 000000 000000 Mojo-SQLite-v4.0.0/xt pod-syntax.t 100644 001750 001750 251 15205022012 20221 0 ustar 00grinnz grinnz 000000 000000 Mojo-SQLite-v4.0.0/xt/author #!perl
# This file was automatically generated by Dist::Zilla::Plugin::PodSyntaxTests
use strict; use warnings;
use Test::More;
use Test::Pod 1.41;
all_pod_files_ok();
00-report-prereqs.dd 100644 001750 001750 3444 15205022012 20003 0 ustar 00grinnz grinnz 000000 000000 Mojo-SQLite-v4.0.0/t do { my $x = {
'configure' => {
'requires' => {
'Module::Build::Tiny' => '0.034'
}
},
'develop' => {
'requires' => {
'Pod::Coverage::TrustPod' => '0',
'Test::Pod' => '1.41',
'Test::Pod::Coverage' => '1.08'
}
},
'runtime' => {
'requires' => {
'Carp' => '0',
'DBD::SQLite' => '1.68',
'DBI' => '1.627',
'File::Spec::Functions' => '0',
'File::Temp' => '0',
'Mojolicious' => '8.03',
'SQL::Abstract::Pg' => '1.0',
'Scalar::Util' => '0',
'URI' => '1.69',
'URI::db' => '0.15',
'URI::file' => '4.21',
'perl' => '5.010001'
}
},
'test' => {
'recommends' => {
'CPAN::Meta' => '2.120900'
},
'requires' => {
'File::Spec' => '0',
'Module::Metadata' => '0',
'Test::More' => '0.96'
}
}
};
$x;
} t 000755 001750 001750 0 15205022012 16657 5 ustar 00grinnz grinnz 000000 000000 Mojo-SQLite-v4.0.0/examples/blog blog.t 100644 001750 001750 3461 15205022012 20133 0 ustar 00grinnz grinnz 000000 000000 Mojo-SQLite-v4.0.0/examples/blog/t use Mojo::Base -strict;
use Test::More;
use Mojo::SQLite;
use Mojo::URL;
use Test::Mojo;
# Override configuration for testing
my $t = Test::Mojo->new(Blog => {sqlite => ':temp:', secrets => ['test_s3cret']});
$t->ua->max_redirects(10);
# No posts yet
$t->get_ok('/')->status_is(200)->text_is('title' => 'Blog')
->text_is('body > a' => 'New post')->element_exists_not('h2');
# Create a new post
$t->get_ok('/posts/create')->status_is(200)->text_is('title' => 'New post')
->element_exists('form input[name=title]')
->element_exists('form textarea[name=body]');
$t->post_ok('/posts' => form => {title => 'Testing', body => 'This is a test.'})
->status_is(200)->text_is('title' => 'Testing')->text_is('h2' => 'Testing')
->text_like('p' => qr/This is a test/);
# Read the post
$t->get_ok('/')->status_is(200)->text_is('title' => 'Blog')
->text_is('h2 a' => 'Testing')->text_like('p' => qr/This is a test/);
$t->get_ok('/posts/1')->status_is(200)->text_is('title' => 'Testing')
->text_is('h2' => 'Testing')->text_like('p' => qr/This is a test/)
->text_is('body > a' => 'Edit');
# Update the post
$t->get_ok('/posts/1/edit')->status_is(200)->text_is('title' => 'Edit post')
->element_exists('form input[name=title][value=Testing]')
->text_like('form textarea[name=body]' => qr/This is a test/)
->element_exists('form input[value=Remove]');
$t->post_ok(
'/posts/1?_method=PUT' => form => {title => 'Again', body => 'It works.'})
->status_is(200)->text_is('title' => 'Again')->text_is('h2' => 'Again')
->text_like('p' => qr/It works/);
$t->get_ok('/posts/1')->status_is(200)->text_is('title' => 'Again')
->text_is('h2' => 'Again')->text_like('p' => qr/It works/);
# Delete the post
$t->post_ok('/posts/1?_method=DELETE')->status_is(200)
->text_is('title' => 'Blog')->element_exists_not('h2');
done_testing();
blog 000755 001750 001750 0 15205022012 16414 5 ustar 00grinnz grinnz 000000 000000 Mojo-SQLite-v4.0.0/examples blog.conf 100644 001750 001750 65 15205022012 20307 0 ustar 00grinnz grinnz 000000 000000 Mojo-SQLite-v4.0.0/examples/blog {
sqlite => 'test.db',
secrets => ['s3cret'],
}
pod-coverage.t 100644 001750 001750 364 15205022012 20473 0 ustar 00grinnz grinnz 000000 000000 Mojo-SQLite-v4.0.0/xt/author #!perl
# This file was automatically generated by Dist::Zilla::Plugin::PodCoverageTests
use strict;
use warnings;
use Test::Pod::Coverage 1.08;
use Pod::Coverage::TrustPod;
all_pod_coverage_ok({ coverage_class => 'Pod::Coverage::TrustPod' });
lib 000755 001750 001750 0 15205022012 17162 5 ustar 00grinnz grinnz 000000 000000 Mojo-SQLite-v4.0.0/examples/blog Blog.pm 100644 001750 001750 2177 15205022012 20552 0 ustar 00grinnz grinnz 000000 000000 Mojo-SQLite-v4.0.0/examples/blog/lib package Blog;
use Mojo::Base 'Mojolicious';
use Blog::Model::Posts;
use Mojo::SQLite;
sub startup {
my $self = shift;
# Configuration
$self->plugin('Config');
$self->secrets($self->config('secrets'));
# Model
$self->helper(sqlite => sub { state $sql = Mojo::SQLite->new->from_filename(shift->config('sqlite')) });
$self->helper(
posts => sub { state $posts = Blog::Model::Posts->new(sqlite => shift->sqlite) });
# Migrate to latest version if necessary
my $path = $self->home->child('migrations', 'blog.sql');
$self->sqlite->auto_migrate(1)->migrations->name('blog')->from_file($path);
# Controller
my $r = $self->routes;
$r->get('/' => sub { shift->redirect_to('posts') });
$r->get('/posts')->to('posts#index');
$r->get('/posts/create')->to('posts#create')->name('create_post');
$r->post('/posts')->to('posts#store')->name('store_post');
$r->get('/posts/:id')->to('posts#show')->name('show_post');
$r->get('/posts/:id/edit')->to('posts#edit')->name('edit_post');
$r->put('/posts/:id')->to('posts#update')->name('update_post');
$r->delete('/posts/:id')->to('posts#remove')->name('remove_post');
}
1;
script 000755 001750 001750 0 15205022012 17720 5 ustar 00grinnz grinnz 000000 000000 Mojo-SQLite-v4.0.0/examples/blog blog 100755 001750 001750 345 15205022012 20713 0 ustar 00grinnz grinnz 000000 000000 Mojo-SQLite-v4.0.0/examples/blog/script #!/usr/bin/env perl
use strict;
use warnings;
use FindBin;
BEGIN { unshift @INC, "$FindBin::Bin/../lib" }
# Start command line interface for application
require Mojolicious::Commands;
Mojolicious::Commands->start_app('Blog');
SQLite 000755 001750 001750 0 15205022012 16466 5 ustar 00grinnz grinnz 000000 000000 Mojo-SQLite-v4.0.0/lib/Mojo PubSub.pm 100644 001750 001750 1461 15205022012 20366 0 ustar 00grinnz grinnz 000000 000000 Mojo-SQLite-v4.0.0/lib/Mojo/SQLite package Mojo::SQLite::PubSub;
use Mojo::Base -strict;
use Mojo::Util 'deprecated';
our $VERSION = 'v4.0.0';
deprecated 'Mojo::SQLite::PubSub is deprecated and should no longer be used';
1;
=encoding utf8
=head1 NAME
Mojo::SQLite::PubSub - (DEPRECATED) Publish/Subscribe
=head1 DESCRIPTION
L is DEPRECATED and now an empty package. It was
originally written as a toy following the API of L, but as
SQLite is serverless and has no ability to notify clients, it is not possible
to implement an efficient pubsub system as in for example PostgreSQL, Redis, or
websockets. Consider instead using the pubsub facilities of L,
L, or L.
=head1 SEE ALSO
L, L, L
=for Pod::Coverage *EVERYTHING*
Results.pm 100644 001750 001750 12514 15205022012 20650 0 ustar 00grinnz grinnz 000000 000000 Mojo-SQLite-v4.0.0/lib/Mojo/SQLite package Mojo::SQLite::Results;
use Mojo::Base -base;
use Mojo::Collection;
use Mojo::JSON 'from_json';
use Mojo::Util 'tablify';
our $VERSION = 'v4.0.0';
has [qw(db sth)];
sub new {
my $self = shift->SUPER::new(@_);
($self->{sth}{private_mojo_refcount} //= 0)++;
return $self;
}
sub DESTROY {
my $self = shift;
return() unless my $sth = $self->{sth};
$sth->finish unless --$sth->{private_mojo_refcount};
}
sub array { ($_[0]->_expand($_[0]->sth->fetchrow_arrayref))[0] }
sub arrays { _collect($_[0]->_expand(@{$_[0]->sth->fetchall_arrayref})) }
sub columns { shift->sth->{NAME} }
sub expand {
my ($self, %expands) = @_;
for my $type (keys %expands) {
my @cols = ref $expands{$type} eq 'ARRAY' ? @{$expands{$type}} : $expands{$type};
++$self->{expand}{$type}{$_} for @cols;
}
return $self;
}
sub finish { shift->sth->finish }
sub hash { ($_[0]->_expand($_[0]->sth->fetchrow_hashref))[0] }
sub hashes { _collect($_[0]->_expand(@{$_[0]->sth->fetchall_arrayref({})})) }
sub last_insert_id { shift->{last_insert_id} // 0 }
sub rows { shift->sth->rows }
sub text { tablify shift->arrays }
sub _collect { Mojo::Collection->new(@_) }
sub _expand {
my ($self, @rows) = @_;
return @rows unless $self->{expand} and $rows[0];
if (ref $rows[0] eq 'HASH') {
my @json_names = keys %{$self->{expand}{json}};
for my $r (@rows) { $r->{$_} = from_json $r->{$_} for grep { $r->{$_} } @json_names }
} else {
my $cols = $self->columns;
my @json_idxs = grep { $self->{expand}{json}{$cols->[$_]} } 0..$#$cols;
for my $r (@rows) { $r->[$_] = from_json $r->[$_] for grep { $r->[$_] } @json_idxs }
}
return @rows;
}
1;
=head1 NAME
Mojo::SQLite::Results - Results
=head1 SYNOPSIS
use Mojo::SQLite::Results;
my $results = Mojo::SQLite::Results->new(sth => $sth);
$results->hashes->map(sub { $_->{foo} })->shuffle->join("\n")->say;
=head1 DESCRIPTION
L is a container for L statement handles
used by L.
=head1 ATTRIBUTES
L implements the following attributes.
=head2 db
my $db = $results->db;
$results = $results->db(Mojo::SQLite::Database->new);
L object these results belong to.
=head2 sth
my $sth = $results->sth;
$results = $results->sth($sth);
L statement handle results are fetched from.
=head1 METHODS
L inherits all methods from L and implements
the following new ones.
=head2 new
my $results = Mojo::SQLite::Results->new;
my $results = Mojo::SQLite::Results->new(sth => $sth);
my $results = Mojo::SQLite::Results->new({sth => $sth});
Construct a new L object.
=head2 array
my $array = $results->array;
Fetch next row from L"sth"> and return it as an array reference. Note that
L"finish"> needs to be called if you are not fetching all the possible rows.
# Process one row at a time
while (my $next = $results->array) {
say $next->[3];
}
=head2 arrays
my $collection = $results->arrays;
Fetch all rows from L"sth"> and return them as a L object
containing array references.
# Process all rows at once
say $results->arrays->reduce(sub { $a + $b->[3] }, 0);
=head2 columns
my $columns = $results->columns;
Return column names as an array reference.
# Names of all columns
say for @{$results->columns};
=head2 expand
$results = $results->expand(json => 'some_json');
$results = $results->expand(json => ['some_json','other_json']);
Decode specified fields from a particular format to Perl values for all rows.
Currently only the C text format is recognized. The names must exactly
match the column names as returned by L"columns">; it is recommended to use
explicit aliases in the query for consistent column names.
# Expand JSON
$results->expand(json => 'json_field')->hashes->map(sub { $_->{foo}{bar} })->join("\n")->say;
=head2 finish
$results->finish;
Indicate that you are finished with L"sth"> and will not be fetching all the
remaining rows.
=head2 hash
my $hash = $results->hash;
Fetch next row from L"sth"> and return it as a hash reference. Note that
L"finish"> needs to be called if you are not fetching all the possible rows.
# Process one row at a time
while (my $next = $results->hash) {
say $next->{money};
}
=head2 hashes
my $collection = $results->hashes;
Fetch all rows from L"sth"> and return them as a L object
containing hash references.
# Process all rows at once
say $results->hashes->reduce(sub { $a + $b->{money} }, 0);
=head2 last_insert_id
my $id = $results->last_insert_id;
Returns the L of the
most recent successful C.
=head2 rows
my $num = $results->rows;
Number of rows. Note that for C