├── .github └── workflows │ └── ci.yaml ├── .gitignore ├── Build.PL ├── CONTRIBUTING.md ├── Changes ├── INSTALL ├── LICENSE ├── META.json ├── README.pod ├── dist.ini ├── examples └── blog │ ├── blog.conf │ ├── lib │ ├── Blog.pm │ └── Blog │ │ ├── Controller │ │ └── Posts.pm │ │ └── Model │ │ └── Posts.pm │ ├── migrations │ └── blog.sql │ ├── script │ └── blog │ ├── t │ └── blog.t │ └── templates │ ├── layouts │ └── blog.html.ep │ └── posts │ ├── _form.html.ep │ ├── create.html.ep │ ├── edit.html.ep │ ├── index.html.ep │ └── show.html.ep ├── lib └── Mojo │ ├── SQLite.pm │ └── SQLite │ ├── Database.pm │ ├── Migrations.pm │ ├── PubSub.pm │ ├── Results.pm │ └── Transaction.pm ├── prereqs.yml └── t ├── connection.t ├── crud.t ├── database.t ├── migrations.t ├── migrations └── test.sql ├── results.t └── sqlite_lite_app.t /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: test 2 | on: [push, pull_request] 3 | jobs: 4 | build: 5 | runs-on: ${{ matrix.os }} 6 | strategy: 7 | matrix: 8 | os: ['ubuntu-latest'] 9 | perl: 10 | - '5.16' 11 | - '5.18' 12 | - '5.20' 13 | - '5.22' 14 | - '5.24' 15 | - '5.26' 16 | - '5.28' 17 | - '5.30' 18 | name: 🐪 Perl ${{ matrix.perl }} on ${{ matrix.os }} 19 | steps: 20 | - uses: actions/checkout@v2 21 | - uses: shogo82148/actions-setup-perl@v1 22 | with: 23 | perl-version: ${{ matrix.perl }} 24 | - name: Run Perl tests 25 | run: | 26 | cpanm --with-develop --installdeps . 27 | perl Build.PL 28 | ./Build test 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /blib/ 2 | /.build/ 3 | _build/ 4 | cover_db/ 5 | inc/ 6 | Build 7 | !Build/ 8 | Build.bat 9 | .last_cover_stats 10 | /Makefile 11 | /Makefile.old 12 | /MANIFEST.bak 13 | /META.yml 14 | /MYMETA.* 15 | nytprof.out 16 | /pm_to_blib 17 | *.o 18 | *.bs 19 | /_eumm/ 20 | Mojo-SQLite-* 21 | -------------------------------------------------------------------------------- /Build.PL: -------------------------------------------------------------------------------- 1 | # This Build.PL for Mojo-SQLite was generated by 2 | # Dist::Zilla::Plugin::ModuleBuildTiny::Fallback 0.025 3 | use strict; 4 | use warnings; 5 | 6 | my %configure_requires = ( 7 | 'Module::Build::Tiny' => '0.034', 8 | ); 9 | 10 | my %errors = map { 11 | eval "require $_; $_->VERSION($configure_requires{$_}); 1"; 12 | $_ => $@, 13 | } keys %configure_requires; 14 | 15 | if (!grep { $_ } values %errors) 16 | { 17 | # This section for Mojo-SQLite was generated by Dist::Zilla::Plugin::ModuleBuildTiny 0.015. 18 | use strict; 19 | use warnings; 20 | 21 | use 5.010001; 22 | # use Module::Build::Tiny 0.034; 23 | Module::Build::Tiny::Build_PL(); 24 | } 25 | else 26 | { 27 | if (not $ENV{PERL_MB_FALLBACK_SILENCE_WARNING}) 28 | { 29 | warn <<'EOW' 30 | *** WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING *** 31 | 32 | If you're seeing this warning, your toolchain is really, really old* and you'll 33 | almost certainly have problems installing CPAN modules from this century. But 34 | never fear, dear user, for we have the technology to fix this! 35 | 36 | If you're using CPAN.pm to install things, then you can upgrade it using: 37 | 38 | cpan CPAN 39 | 40 | If you're using CPANPLUS to install things, then you can upgrade it using: 41 | 42 | cpanp CPANPLUS 43 | 44 | If you're using cpanminus, you shouldn't be seeing this message in the first 45 | place, so please file an issue on github. 46 | 47 | This public service announcement was brought to you by the Perl Toolchain 48 | Gang, the irc.perl.org #toolchain IRC channel, and the number 42. 49 | 50 | ---- 51 | 52 | * Alternatively, you are running this file manually, in which case you need 53 | to learn to first fulfill all configure requires prerequisites listed in 54 | META.yml or META.json -- or use a cpan client to install this distribution. 55 | 56 | You can also silence this warning for future installations by setting the 57 | PERL_MB_FALLBACK_SILENCE_WARNING environment variable, but please don't do 58 | that until you fix your toolchain as described above. 59 | 60 | 61 | Errors from configure prereqs: 62 | EOW 63 | . do { 64 | require Data::Dumper; Data::Dumper->new([ \%errors ])->Indent(2)->Terse(1)->Sortkeys(1)->Dump; 65 | }; 66 | 67 | sleep 10 if -t STDIN && (-t STDOUT || !(-f STDOUT || -c STDOUT)); 68 | } 69 | 70 | 71 | # This section was automatically generated by Dist::Zilla::Plugin::ModuleBuild v6.024. 72 | use strict; 73 | use warnings; 74 | 75 | require Module::Build; Module::Build->VERSION(0.28); 76 | 77 | 78 | my %module_build_args = ( 79 | "configure_requires" => { 80 | "Module::Build::Tiny" => "0.034" 81 | }, 82 | "dist_abstract" => "A tiny Mojolicious wrapper for SQLite", 83 | "dist_author" => [ 84 | "Dan Book " 85 | ], 86 | "dist_name" => "Mojo-SQLite", 87 | "dist_version" => "3.009", 88 | "license" => "artistic_2", 89 | "module_name" => "Mojo::SQLite", 90 | "recursive_test_files" => 1, 91 | "requires" => { 92 | "Carp" => 0, 93 | "DBD::SQLite" => "1.68", 94 | "DBI" => "1.627", 95 | "File::Spec::Functions" => 0, 96 | "File::Temp" => 0, 97 | "Mojolicious" => "8.03", 98 | "SQL::Abstract::Pg" => "1.0", 99 | "Scalar::Util" => 0, 100 | "URI" => "1.69", 101 | "URI::db" => "0.15", 102 | "URI::file" => "4.21", 103 | "perl" => "5.010001" 104 | }, 105 | "test_requires" => { 106 | "File::Spec" => 0, 107 | "Module::Metadata" => 0, 108 | "Test::More" => "0.96" 109 | } 110 | ); 111 | 112 | 113 | my %fallback_build_requires = ( 114 | "File::Spec" => 0, 115 | "Module::Metadata" => 0, 116 | "Test::More" => "0.96" 117 | ); 118 | 119 | 120 | unless ( eval { Module::Build->VERSION(0.4004) } ) { 121 | delete $module_build_args{test_requires}; 122 | $module_build_args{build_requires} = \%fallback_build_requires; 123 | } 124 | 125 | my $build = Module::Build->new(%module_build_args); 126 | 127 | 128 | $build->create_build_script; 129 | } 130 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # HOW TO CONTRIBUTE 2 | 3 | Thank you for considering contributing to this distribution. This file 4 | contains instructions that will help you work with the source code. 5 | 6 | The distribution is managed with [Dist::Zilla](https://metacpan.org/pod/Dist::Zilla). 7 | This means that many of the usual files you might expect are not in the 8 | repository, but are generated at release time. Some generated files are kept 9 | in the repository as a convenience (e.g. Build.PL/Makefile.PL and META.json). 10 | 11 | Generally, **you do not need Dist::Zilla to contribute patches**. You may need 12 | Dist::Zilla to create a tarball. See below for guidance. 13 | 14 | ## Getting dependencies 15 | 16 | If you have App::cpanminus 1.6 or later installed, you can use 17 | [cpanm](https://metacpan.org/pod/cpanm) to satisfy dependencies like this: 18 | 19 | $ cpanm --installdeps --with-develop . 20 | 21 | You can also run this command (or any other cpanm command) without installing 22 | App::cpanminus first, using the fatpacked `cpanm` script via curl or wget: 23 | 24 | $ curl -L https://cpanmin.us | perl - --installdeps --with-develop . 25 | $ wget -qO - https://cpanmin.us | perl - --installdeps --with-develop . 26 | 27 | Otherwise, look for either a `cpanfile`, `prereqs.json`/`prereqs.yml`, or 28 | `META.json` file for a list of dependencies to satisfy. 29 | 30 | ## Running tests 31 | 32 | You can run tests directly using the `prove` tool: 33 | 34 | $ prove -l 35 | $ prove -lv t/some_test_file.t 36 | 37 | For most of my distributions, `prove` is entirely sufficient for you to test 38 | any patches you have. I use `prove` for 99% of my testing during development. 39 | 40 | ## Code style and tidying 41 | 42 | Please try to match any existing coding style. If there is a `.perltidyrc` 43 | file, please install Perl::Tidy and use perltidy before submitting patches. 44 | 45 | ## Installing and using Dist::Zilla 46 | 47 | [Dist::Zilla](https://metacpan.org/pod/Dist::Zilla) is a very powerful 48 | authoring tool, optimized for maintaining a large number of distributions with 49 | a high degree of automation, but it has a large dependency chain, a bit of a 50 | learning curve and requires a number of author-specific plugins. 51 | 52 | To install it from CPAN, I recommend one of the following approaches for the 53 | quickest installation: 54 | 55 | # using CPAN.pm, but bypassing non-functional pod tests 56 | $ cpan TAP::Harness::Restricted 57 | $ PERL_MM_USE_DEFAULT=1 HARNESS_CLASS=TAP::Harness::Restricted cpan Dist::Zilla 58 | 59 | # using cpanm, bypassing *all* tests 60 | $ cpanm -n Dist::Zilla 61 | 62 | In either case, it's probably going to take about 10 minutes. Go for a walk, 63 | go get a cup of your favorite beverage, take a bathroom break, or whatever. 64 | When you get back, Dist::Zilla should be ready for you. 65 | 66 | Then you need to install any plugins specific to this distribution: 67 | 68 | $ dzil authordeps --missing | cpanm 69 | 70 | You can use Dist::Zilla to install the distribution's dependencies if you 71 | haven't already installed them with cpanm: 72 | 73 | $ dzil listdeps --missing --develop | cpanm 74 | 75 | You can instead combine these two steps into one command by installing 76 | Dist::Zilla::App::Command::installdeps then running: 77 | 78 | $ dzil installdeps 79 | 80 | Once everything is installed, here are some dzil commands you might try: 81 | 82 | $ dzil build 83 | $ dzil test 84 | $ dzil regenerate 85 | 86 | You can learn more about Dist::Zilla at http://dzil.org/ 87 | 88 | ## Other notes 89 | 90 | This distribution maintains the generated `META.json` and either `Makefile.PL` 91 | or `Build.PL` in the repository. This allows two things: 92 | [Travis CI](https://travis-ci.org/) can build and test the distribution without 93 | requiring Dist::Zilla, and the distribution can be installed directly from 94 | Github or a local git repository using `cpanm` for testing (again, not 95 | requiring Dist::Zilla). 96 | 97 | $ cpanm git://github.com/Author/Distribution-Name.git 98 | $ cd Distribution-Name; cpanm . 99 | 100 | Contributions are preferred in the form of a Github pull request. See 101 | [Using pull requests](https://help.github.com/articles/using-pull-requests/) 102 | for further information. You can use the Github issue tracker to report issues 103 | without an accompanying patch. 104 | 105 | # CREDITS 106 | 107 | This file was adapted from an initial `CONTRIBUTING.mkdn` file from David 108 | Golden under the terms of the [CC0](https://creativecommons.org/share-your-work/public-domain/cc0/), with inspiration from the 109 | contributing documents from [Dist::Zilla::Plugin::Author::KENTNL::CONTRIBUTING](https://metacpan.org/pod/Dist::Zilla::Plugin::Author::KENTNL::CONTRIBUTING) 110 | and [Dist::Zilla::PluginBundle::Author::ETHER](https://metacpan.org/pod/Dist::Zilla::PluginBundle::Author::ETHER). 111 | -------------------------------------------------------------------------------- /Changes: -------------------------------------------------------------------------------- 1 | {{$NEXT}} 2 | 3 | 3.009 2022-03-22 20:16:03 EDT 4 | - Fix race condition in Mojo::SQLite::Migrations where two processes could attempt to create the same migration. (#25, Oliver Kurz) 5 | 6 | 3.008 2021-11-28 11:10:24 EST 7 | - Update documentation to indicate that upsert operations are now supported via SQL::Abstract::Pg. 8 | 9 | 3.007 2021-08-01 20:17:42 EDT 10 | - Increase DBD::SQLite dependency to 1.68 to support and use sqlite_string_mode over sqlite_unicode (#22, Adam Williamson) 11 | - The default options now set sqlite_string_mode to DBD_SQLITE_STRING_MODE_UNICODE_FALLBACK, 12 | unless sqlite_unicode is specified explicitly in from_string or from_filename. This is equivalent 13 | to the previous behavior but ensures that DBD::SQLite will not return malformed strings. 14 | - Support wal_mode option to enable WAL journaling mode, which is still currently the default, 15 | but may not be set by default in a future release. 16 | 17 | 3.006 2021-06-16 00:30:47 EDT 18 | - Set default abstract generator to an SQL::Abstract::Pg object, enabling additional features. 19 | - Update IRC metadata to libera.chat 20 | 21 | 3.005 2021-02-15 21:47:59 EST 22 | - Add query_p, select_p, insert_p, update_p, delete_p Mojo::Promise-returning methods to Mojo::SQLite::Database. 23 | These are for API compatibility with Mojo::Pg and do not provide non-blocking query functionality. (#20, Stefan Adams) 24 | - Use Mojo::Promise in tests instead of the deprecated and decored Mojo::IOLoop::Delay (#20, Stefan Adams) 25 | 26 | 3.004 2020-07-22 22:50:43 EDT 27 | - The Mojo::SQLite::Migrations sqlite attribute and the 28 | Mojo::SQLite::Transaction db attribute are now weak attributes, increasing 29 | the required version of Mojolicious to 8.03. 30 | - Defer loading of Mojo::IOLoop unless the callback compatibility API is 31 | used. 32 | - Newly created mojo_migrations tables from Mojo::SQLite::Migrations will use 33 | a primary key. 34 | - Recognize -json parameters in queries. 35 | 36 | 3.003 2019-10-01 15:49:43 EDT 37 | - Increase DBD::SQLite dependency to 1.64 to support configuring 38 | double-quoted string literals. 39 | - Disable double-quoted string literals for all connections. 40 | https://www.sqlite.org/quirks.html#double_quoted_string_literals_are_accepted 41 | - Change default SQL::Abstract identifier back to double quote. 42 | 43 | 3.002 2019-06-21 17:41:35 EDT 44 | - Changed default SQL::Abstract identifier quoting character to `, as double 45 | quoted identifiers may be interpreted as string literals if they are not 46 | valid identifiers. https://www.sqlite.org/quirks.html#double_quoted_string_literals_are_accepted 47 | 48 | 3.001 2018-07-20 20:25:12 EDT 49 | - Increase DBD::SQLite dependency to 1.54 for FTS5 support. 50 | - Add db attribute to Mojo::SQLite::Results and use it to prevent connections 51 | from being cached for reuse while results are active. 52 | - Add sql_for method to Mojo::SQLite::Migrations. 53 | 54 | 3.000 2017-07-20 01:16:50 EDT 55 | - Changed default for max_connections attribute to 1. 56 | - Added support for sharing the database connection cache between multiple 57 | Mojo::SQLite objects. (based on Mojo::Pg 4.0) 58 | - Added parent attribute to Mojo::SQLite. 59 | - Fixed database connection leak with automatic migrations. 60 | - Removed deprecated Mojo::SQLite::PubSub and associated methods and attributes. 61 | SQLite's serverless nature means it does not have the ability to support 62 | client notifications, so it is not possible to implement an efficient 63 | pubsub system as in for example PostgreSQL, Redis, or websockets. 64 | 65 | 2.002 2017-06-01 14:16:34 EDT 66 | - Add no_wal option to prevent enabling WAL mode on connection. 67 | 68 | 2.001 2017-02-18 15:36:16 EST 69 | - Set name_sep in default SQL::Abstract object to support proper quoting of 70 | table and column names. 71 | 72 | 2.000 2017-02-11 17:03:53 EST 73 | - Add support for generating queries with SQL::Abstract. (based on Mojo::Pg 3.0) 74 | - Add abstract attribute to Mojo::SQLite. 75 | - Add delete, insert, select, and update methods to Mojo::SQLite::Database. 76 | 77 | 1.004 2017-01-17 00:10:51 EST 78 | - Use Mojo::File from Mojolicious 7.15 instead of deprecated 79 | Mojo::Util slurp function. (#9) 80 | 81 | 1.003 2016-12-11 16:30:31 EST 82 | - Add links to alternatives for deprecated Mojo::SQLite::PubSub. 83 | 84 | 1.002 2016-11-30 11:17:56 EST 85 | - Improved contextual caller information in query error messages. (#6) 86 | - Fix memory leak when reusing the same database handle many times. (#7) 87 | 88 | 1.001 2016-11-15 02:32:27 EST 89 | - Deprecate Mojo::SQLite::PubSub and associated methods and attributes. 90 | SQLite's serverless nature means it does not have the ability to support 91 | client notifications, so it is not possible to implement an efficient 92 | pubsub system as in for example PostgreSQL, Redis, or websockets. 93 | 94 | 1.000 2016-08-17 21:01:31 EDT 95 | - Bump version to 1.000 as this distribution is considered stable 96 | - Added database_class attribute to Mojo::SQLite and results_class attribute 97 | to Mojo::SQLite::Database to make it easier for these classes to be extended 98 | - Fixed a few fork-safety bugs and memory leaks in Mojo::SQLite::PubSub 99 | 100 | 0.022 2016-07-14 16:59:36 EDT 101 | - Improve expand performance slightly 102 | - Add support for encoding and decoding of JSON notifications 103 | - Add json method to Mojo::SQLite::PubSub 104 | - Fix bug where poll_interval in Mojo::SQLite::PubSub was not correctly used 105 | - Fix case where notifications from other connections could be lost 106 | 107 | 0.021 2016-02-15 01:58:40 EST 108 | - Lower dependency on DBI to 1.627 109 | - Remove dependency on URI::QueryParam 110 | 111 | 0.020 2016-02-14 17:53:55 EST 112 | - Bump dependency on DBD::SQLite to 1.50 for JSON1 extension support 113 | - Add support for json parameters to query method in Mojo::SQLite::Database 114 | - Add expand method to Mojo::SQLite::Results to decode json columns 115 | - Add auto_migrate attribute to Mojo::SQLite (from Mojo::Pg) 116 | - Add tables method to Mojo::SQLite::Database (from Mojo::Pg) 117 | 118 | 0.019 2015-11-25 22:45:25 EST 119 | - Added support for removing all subscribers of a channel at once in unlisten 120 | method in Mojo::SQLite::PubSub 121 | - Mojo::SQLite::Migrations now throws an error if the currently active 122 | version is greater than the latest version 123 | - Added finish method to Mojo::SQLite::Results 124 | 125 | 0.018 2015-09-21 13:44:30 EDT 126 | - Fixes for tests under win32 127 | 128 | 0.017 2015-09-20 17:24:44 EDT 129 | - Fix parsing of options from connection strings 130 | - Allow passing a hashref of options in from_filename 131 | 132 | 0.016 2015-09-20 16:51:53 EDT 133 | - Use URI::db to parse connection strings to allow sqlite: connection strings 134 | 135 | 0.015 2015-09-12 00:13:53 EDT 136 | - Minor pubsub fixes 137 | 138 | 0.014 2015-09-11 20:25:25 EDT 139 | - Add Mojo::SQLite::PubSub and listen/notify implementation in Mojo::SQLite::Database 140 | 141 | 0.013 2015-09-02 02:44:36 EDT 142 | - Cache rowid for successful inserts in Results objects as last_insert_id 143 | 144 | 0.012 2015-08-31 18:26:47 EDT 145 | - Include better context for DB error messages from $db->query and migrations 146 | - Switch installer to Module::Build::Tiny 147 | 148 | 0.011 2015-08-13 20:43:37 EDT 149 | - Simplify Results refcounting 150 | - Pass undef instead of broken Results object to callback if statement 151 | prepare failed 152 | 153 | 0.010 2015-08-08 22:50:06 EDT 154 | - Create a tempdir for temporary databases 155 | - Use immediate locking for migrations instead of exclusive 156 | - Run query callbacks using next_tick instead of immediately 157 | - Don't call finish in Results destructor while other Results objects refer 158 | to the statement handle (GH #2) 159 | 160 | 0.009 2015-08-05 12:28:48 EDT 161 | - Simplify some code and examples with Mojo::Base::tap 162 | 163 | 0.008 2015-08-03 19:23:49 EDT 164 | - Use and recommend URI to portably parse filenames into URLs 165 | - Add from_filename method 166 | 167 | 0.007 2015-08-01 15:17:22 EDT 168 | - Clarify use of URLs in connection strings 169 | 170 | 0.006 2015-07-28 23:54:45 EDT 171 | - Fix results rows() method to return statement handle rows 172 | - Make scheme optional for from_string() (and thus the constructor) 173 | 174 | 0.005 2015-07-28 21:59:23 EDT 175 | - Allow specifying bind types for query parameters 176 | 177 | 0.004 2015-07-28 20:48:04 EDT 178 | - Depend on DBI explicitly 179 | - Remove List::Util dependency 180 | - Add blog example 181 | 182 | 0.003 2015-07-28 12:56:30 EDT 183 | - Minor migrations fixes 184 | 185 | 0.002 2015-07-28 12:39:12 EDT 186 | - Allow passing callback to query() 187 | 188 | 0.001 2015-07-28 02:48:42 EDT 189 | - First release 190 | -------------------------------------------------------------------------------- /INSTALL: -------------------------------------------------------------------------------- 1 | This is the Perl distribution Mojo-SQLite. 2 | 3 | Installing Mojo-SQLite is straightforward. 4 | 5 | ## Installation with cpanm 6 | 7 | If you have cpanm, you only need one line: 8 | 9 | % cpanm Mojo::SQLite 10 | 11 | If it does not have permission to install modules to the current perl, cpanm 12 | will automatically set up and install to a local::lib in your home directory. 13 | See the local::lib documentation (https://metacpan.org/pod/local::lib) for 14 | details on enabling it in your environment. 15 | 16 | ## Installing with the CPAN shell 17 | 18 | Alternatively, if your CPAN shell is set up, you should just be able to do: 19 | 20 | % cpan Mojo::SQLite 21 | 22 | ## Manual installation 23 | 24 | As a last resort, you can manually install it. If you have not already 25 | downloaded the release tarball, you can find the download link on the module's 26 | MetaCPAN page: https://metacpan.org/pod/Mojo::SQLite 27 | 28 | Untar the tarball, install configure prerequisites (see below), then build it: 29 | 30 | % perl Build.PL 31 | % ./Build && ./Build test 32 | 33 | Then install it: 34 | 35 | % ./Build install 36 | 37 | Or the more portable variation: 38 | 39 | % perl Build.PL 40 | % perl Build 41 | % perl Build test 42 | % perl Build install 43 | 44 | If your perl is system-managed, you can create a local::lib in your home 45 | directory to install modules to. For details, see the local::lib documentation: 46 | https://metacpan.org/pod/local::lib 47 | 48 | The prerequisites of this distribution will also have to be installed manually. The 49 | prerequisites are listed in one of the files: `MYMETA.yml` or `MYMETA.json` generated 50 | by running the manual build process described above. 51 | 52 | ## Configure Prerequisites 53 | 54 | This distribution requires other modules to be installed before this 55 | distribution's installer can be run. They can be found under the 56 | "configure_requires" key of META.yml or the 57 | "{prereqs}{configure}{requires}" key of META.json. 58 | 59 | ## Other Prerequisites 60 | 61 | This distribution may require additional modules to be installed after running 62 | Build.PL. 63 | Look for prerequisites in the following phases: 64 | 65 | * to run ./Build, PHASE = build 66 | * to use the module code itself, PHASE = runtime 67 | * to run tests, PHASE = test 68 | 69 | They can all be found in the "PHASE_requires" key of MYMETA.yml or the 70 | "{prereqs}{PHASE}{requires}" key of MYMETA.json. 71 | 72 | ## Documentation 73 | 74 | Mojo-SQLite documentation is available as POD. 75 | You can run `perldoc` from a shell to read the documentation: 76 | 77 | % perldoc Mojo::SQLite 78 | 79 | For more information on installing Perl modules via CPAN, please see: 80 | https://www.cpan.org/modules/INSTALL.html 81 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This software is Copyright (c) 2015 by Dan Book. 2 | 3 | This is free software, licensed under: 4 | 5 | The Artistic License 2.0 (GPL Compatible) 6 | 7 | The Artistic License 2.0 8 | 9 | Copyright (c) 2000-2006, The Perl Foundation. 10 | 11 | Everyone is permitted to copy and distribute verbatim copies 12 | of this license document, but changing it is not allowed. 13 | 14 | Preamble 15 | 16 | This license establishes the terms under which a given free software 17 | Package may be copied, modified, distributed, and/or redistributed. 18 | The intent is that the Copyright Holder maintains some artistic 19 | control over the development of that Package while still keeping the 20 | Package available as open source and free software. 21 | 22 | You are always permitted to make arrangements wholly outside of this 23 | license directly with the Copyright Holder of a given Package. If the 24 | terms of this license do not permit the full use that you propose to 25 | make of the Package, you should contact the Copyright Holder and seek 26 | a different licensing arrangement. 27 | 28 | Definitions 29 | 30 | "Copyright Holder" means the individual(s) or organization(s) 31 | named in the copyright notice for the entire Package. 32 | 33 | "Contributor" means any party that has contributed code or other 34 | material to the Package, in accordance with the Copyright Holder's 35 | procedures. 36 | 37 | "You" and "your" means any person who would like to copy, 38 | distribute, or modify the Package. 39 | 40 | "Package" means the collection of files distributed by the 41 | Copyright Holder, and derivatives of that collection and/or of 42 | those files. A given Package may consist of either the Standard 43 | Version, or a Modified Version. 44 | 45 | "Distribute" means providing a copy of the Package or making it 46 | accessible to anyone else, or in the case of a company or 47 | organization, to others outside of your company or organization. 48 | 49 | "Distributor Fee" means any fee that you charge for Distributing 50 | this Package or providing support for this Package to another 51 | party. It does not mean licensing fees. 52 | 53 | "Standard Version" refers to the Package if it has not been 54 | modified, or has been modified only in ways explicitly requested 55 | by the Copyright Holder. 56 | 57 | "Modified Version" means the Package, if it has been changed, and 58 | such changes were not explicitly requested by the Copyright 59 | Holder. 60 | 61 | "Original License" means this Artistic License as Distributed with 62 | the Standard Version of the Package, in its current version or as 63 | it may be modified by The Perl Foundation in the future. 64 | 65 | "Source" form means the source code, documentation source, and 66 | configuration files for the Package. 67 | 68 | "Compiled" form means the compiled bytecode, object code, binary, 69 | or any other form resulting from mechanical transformation or 70 | translation of the Source form. 71 | 72 | 73 | Permission for Use and Modification Without Distribution 74 | 75 | (1) You are permitted to use the Standard Version and create and use 76 | Modified Versions for any purpose without restriction, provided that 77 | you do not Distribute the Modified Version. 78 | 79 | 80 | Permissions for Redistribution of the Standard Version 81 | 82 | (2) You may Distribute verbatim copies of the Source form of the 83 | Standard Version of this Package in any medium without restriction, 84 | either gratis or for a Distributor Fee, provided that you duplicate 85 | all of the original copyright notices and associated disclaimers. At 86 | your discretion, such verbatim copies may or may not include a 87 | Compiled form of the Package. 88 | 89 | (3) You may apply any bug fixes, portability changes, and other 90 | modifications made available from the Copyright Holder. The resulting 91 | Package will still be considered the Standard Version, and as such 92 | will be subject to the Original License. 93 | 94 | 95 | Distribution of Modified Versions of the Package as Source 96 | 97 | (4) You may Distribute your Modified Version as Source (either gratis 98 | or for a Distributor Fee, and with or without a Compiled form of the 99 | Modified Version) provided that you clearly document how it differs 100 | from the Standard Version, including, but not limited to, documenting 101 | any non-standard features, executables, or modules, and provided that 102 | you do at least ONE of the following: 103 | 104 | (a) make the Modified Version available to the Copyright Holder 105 | of the Standard Version, under the Original License, so that the 106 | Copyright Holder may include your modifications in the Standard 107 | Version. 108 | 109 | (b) ensure that installation of your Modified Version does not 110 | prevent the user installing or running the Standard Version. In 111 | addition, the Modified Version must bear a name that is different 112 | from the name of the Standard Version. 113 | 114 | (c) allow anyone who receives a copy of the Modified Version to 115 | make the Source form of the Modified Version available to others 116 | under 117 | 118 | (i) the Original License or 119 | 120 | (ii) a license that permits the licensee to freely copy, 121 | modify and redistribute the Modified Version using the same 122 | licensing terms that apply to the copy that the licensee 123 | received, and requires that the Source form of the Modified 124 | Version, and of any works derived from it, be made freely 125 | available in that license fees are prohibited but Distributor 126 | Fees are allowed. 127 | 128 | 129 | Distribution of Compiled Forms of the Standard Version 130 | or Modified Versions without the Source 131 | 132 | (5) You may Distribute Compiled forms of the Standard Version without 133 | the Source, provided that you include complete instructions on how to 134 | get the Source of the Standard Version. Such instructions must be 135 | valid at the time of your distribution. If these instructions, at any 136 | time while you are carrying out such distribution, become invalid, you 137 | must provide new instructions on demand or cease further distribution. 138 | If you provide valid instructions or cease distribution within thirty 139 | days after you become aware that the instructions are invalid, then 140 | you do not forfeit any of your rights under this license. 141 | 142 | (6) You may Distribute a Modified Version in Compiled form without 143 | the Source, provided that you comply with Section 4 with respect to 144 | the Source of the Modified Version. 145 | 146 | 147 | Aggregating or Linking the Package 148 | 149 | (7) You may aggregate the Package (either the Standard Version or 150 | Modified Version) with other packages and Distribute the resulting 151 | aggregation provided that you do not charge a licensing fee for the 152 | Package. Distributor Fees are permitted, and licensing fees for other 153 | components in the aggregation are permitted. The terms of this license 154 | apply to the use and Distribution of the Standard or Modified Versions 155 | as included in the aggregation. 156 | 157 | (8) You are permitted to link Modified and Standard Versions with 158 | other works, to embed the Package in a larger work of your own, or to 159 | build stand-alone binary or bytecode versions of applications that 160 | include the Package, and Distribute the result without restriction, 161 | provided the result does not expose a direct interface to the Package. 162 | 163 | 164 | Items That are Not Considered Part of a Modified Version 165 | 166 | (9) Works (including, but not limited to, modules and scripts) that 167 | merely extend or make use of the Package, do not, by themselves, cause 168 | the Package to be a Modified Version. In addition, such works are not 169 | considered parts of the Package itself, and are not subject to the 170 | terms of this license. 171 | 172 | 173 | General Provisions 174 | 175 | (10) Any use, modification, and distribution of the Standard or 176 | Modified Versions is governed by this Artistic License. By using, 177 | modifying or distributing the Package, you accept this license. Do not 178 | use, modify, or distribute the Package, if you do not accept this 179 | license. 180 | 181 | (11) If your Modified Version has been derived from a Modified 182 | Version made by someone other than you, you are nevertheless required 183 | to ensure that your Modified Version complies with the requirements of 184 | this license. 185 | 186 | (12) This license does not grant you the right to use any trademark, 187 | service mark, tradename, or logo of the Copyright Holder. 188 | 189 | (13) This license includes the non-exclusive, worldwide, 190 | free-of-charge patent license to make, have made, use, offer to sell, 191 | sell, import and otherwise transfer the Package with respect to any 192 | patent claims licensable by the Copyright Holder that are necessarily 193 | infringed by the Package. If you institute patent litigation 194 | (including a cross-claim or counterclaim) against any party alleging 195 | that the Package constitutes direct or contributory patent 196 | infringement, then this Artistic License to you shall terminate on the 197 | date that such litigation is filed. 198 | 199 | (14) Disclaimer of Warranty: 200 | THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS 201 | IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED 202 | WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR 203 | NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL 204 | LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR WILL 205 | BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 206 | DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE, EVEN IF 207 | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 208 | -------------------------------------------------------------------------------- /META.json: -------------------------------------------------------------------------------- 1 | { 2 | "abstract" : "A tiny Mojolicious wrapper for SQLite", 3 | "author" : [ 4 | "Dan Book " 5 | ], 6 | "dynamic_config" : 0, 7 | "generated_by" : "Dist::Zilla version 6.024, CPAN::Meta::Converter version 2.150010", 8 | "license" : [ 9 | "artistic_2" 10 | ], 11 | "meta-spec" : { 12 | "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", 13 | "version" : 2 14 | }, 15 | "name" : "Mojo-SQLite", 16 | "no_index" : { 17 | "directory" : [ 18 | "eg", 19 | "examples", 20 | "inc", 21 | "share", 22 | "t", 23 | "xt" 24 | ] 25 | }, 26 | "prereqs" : { 27 | "configure" : { 28 | "requires" : { 29 | "Module::Build::Tiny" : "0.034" 30 | } 31 | }, 32 | "develop" : { 33 | "requires" : { 34 | "Pod::Coverage::TrustPod" : "0", 35 | "Test::Pod" : "1.41", 36 | "Test::Pod::Coverage" : "1.08" 37 | } 38 | }, 39 | "runtime" : { 40 | "requires" : { 41 | "Carp" : "0", 42 | "DBD::SQLite" : "1.68", 43 | "DBI" : "1.627", 44 | "File::Spec::Functions" : "0", 45 | "File::Temp" : "0", 46 | "Mojolicious" : "8.03", 47 | "SQL::Abstract::Pg" : "1.0", 48 | "Scalar::Util" : "0", 49 | "URI" : "1.69", 50 | "URI::db" : "0.15", 51 | "URI::file" : "4.21", 52 | "perl" : "5.010001" 53 | } 54 | }, 55 | "test" : { 56 | "recommends" : { 57 | "CPAN::Meta" : "2.120900" 58 | }, 59 | "requires" : { 60 | "File::Spec" : "0", 61 | "Module::Metadata" : "0", 62 | "Test::More" : "0.96" 63 | } 64 | } 65 | }, 66 | "provides" : { 67 | "Mojo::SQLite" : { 68 | "file" : "lib/Mojo/SQLite.pm", 69 | "version" : "3.009" 70 | }, 71 | "Mojo::SQLite::Database" : { 72 | "file" : "lib/Mojo/SQLite/Database.pm", 73 | "version" : "3.009" 74 | }, 75 | "Mojo::SQLite::Migrations" : { 76 | "file" : "lib/Mojo/SQLite/Migrations.pm", 77 | "version" : "3.009" 78 | }, 79 | "Mojo::SQLite::PubSub" : { 80 | "file" : "lib/Mojo/SQLite/PubSub.pm", 81 | "version" : "3.009", 82 | "x_deprecated" : 1 83 | }, 84 | "Mojo::SQLite::Results" : { 85 | "file" : "lib/Mojo/SQLite/Results.pm", 86 | "version" : "3.009" 87 | }, 88 | "Mojo::SQLite::Transaction" : { 89 | "file" : "lib/Mojo/SQLite/Transaction.pm", 90 | "version" : "3.009" 91 | } 92 | }, 93 | "release_status" : "stable", 94 | "resources" : { 95 | "bugtracker" : { 96 | "web" : "https://github.com/Grinnz/Mojo-SQLite/issues" 97 | }, 98 | "homepage" : "https://github.com/Grinnz/Mojo-SQLite", 99 | "repository" : { 100 | "type" : "git", 101 | "url" : "https://github.com/Grinnz/Mojo-SQLite.git", 102 | "web" : "https://github.com/Grinnz/Mojo-SQLite" 103 | }, 104 | "x_IRC" : "ircs://irc.libera.chat/#mojo" 105 | }, 106 | "version" : "3.009", 107 | "x_contributors" : [ 108 | "Adam Williamson ", 109 | "Dan Book ", 110 | "Dan Book ", 111 | "Oliver Kurz ", 112 | "Stefan Adams " 113 | ], 114 | "x_generated_by_perl" : "v5.34.1", 115 | "x_serialization_backend" : "Cpanel::JSON::XS version 4.27", 116 | "x_spdx_expression" : "Artistic-2.0" 117 | } 118 | 119 | -------------------------------------------------------------------------------- /README.pod: -------------------------------------------------------------------------------- 1 | =pod 2 | 3 | =head1 NAME 4 | 5 | Mojo::SQLite - A tiny Mojolicious wrapper for SQLite 6 | 7 | =head1 SYNOPSIS 8 | 9 | use Mojo::SQLite; 10 | 11 | # Select the library version 12 | my $sql = Mojo::SQLite->new('sqlite:test.db'); 13 | say $sql->db->query('select sqlite_version() as version')->hash->{version}; 14 | 15 | # Use migrations to create a table 16 | $sql->migrations->name('my_names_app')->from_string(<migrate; 17 | -- 1 up 18 | create table names (id integer primary key autoincrement, name text); 19 | -- 1 down 20 | drop table names; 21 | EOF 22 | 23 | # Use migrations to drop and recreate the table 24 | $sql->migrations->migrate(0)->migrate; 25 | 26 | # Get a database handle from the cache for multiple queries 27 | my $db = $sql->db; 28 | 29 | # Use SQL::Abstract to generate simple CRUD queries for you 30 | $db->insert('names', {name => 'Isabel'}); 31 | my $id = $db->select('names', ['id'], {name => 'Isabel'})->hash->{id}; 32 | $db->update('names', {name => 'Bel'}, {id => $id}); 33 | $db->delete('names', {name => 'Bel'}); 34 | 35 | # Insert a few rows in a transaction with SQL and placeholders 36 | eval { 37 | my $tx = $db->begin; 38 | $db->query('insert into names (name) values (?)', 'Sara'); 39 | $db->query('insert into names (name) values (?)', 'Stefan'); 40 | $tx->commit; 41 | }; 42 | say $@ if $@; 43 | 44 | # Insert another row with SQL::Abstract and return the generated id 45 | say $db->insert('names', {name => 'Daniel'})->last_insert_id; 46 | 47 | # JSON roundtrip 48 | say $db->query('select ? as foo', {json => {bar => 'baz'}}) 49 | ->expand(json => 'foo')->hash->{foo}{bar}; 50 | 51 | # Select one row at a time 52 | my $results = $db->query('select * from names'); 53 | while (my $next = $results->hash) { 54 | say $next->{name}; 55 | } 56 | 57 | # Select all rows with SQL::Abstract 58 | say $_->{name} for $db->select('names')->hashes->each; 59 | 60 | =head1 DESCRIPTION 61 | 62 | L is a tiny wrapper around L that makes 63 | L a lot of fun to use with the 64 | L real-time web framework. Use all 65 | L SQLite has to offer, generate CRUD 66 | queries from data structures, and manage your database schema with migrations. 67 | 68 | =head1 BASICS 69 | 70 | Database and statement handles are cached automatically, so they can be reused 71 | transparently to increase performance. And you can handle connection timeouts 72 | gracefully by holding on to them only for short amounts of time. 73 | 74 | use Mojolicious::Lite; 75 | use Mojo::SQLite; 76 | 77 | helper sqlite => sub { state $sql = Mojo::SQLite->new('sqlite:test.db') }; 78 | 79 | get '/' => sub ($c) { 80 | my $db = $c->sqlite->db; 81 | $c->render(json => $db->query(q{select datetime('now','localtime') as now})->hash); 82 | }; 83 | 84 | app->start; 85 | 86 | In this example application, we create a C helper to store a 87 | L object. Our action calls that helper and uses the method 88 | L to dequeue a L object from the 89 | connection pool. Then we use the method L to 90 | execute an L 91 | statement, which returns a L object. And finally we call 92 | the method L to retrieve the first row as a hash 93 | reference. 94 | 95 | All I/O and queries are performed synchronously, and SQLite's default journal 96 | mode only supports concurrent reads from multiple processes while the database 97 | is not being written. The "Write-Ahead Log" journal mode allows multiple 98 | processes to read and write concurrently to the same database file (but only 99 | one can write at a time). WAL mode is enabled by the C option, 100 | currently enabled by default, and persists when opening that same database in 101 | the future. 102 | 103 | # Performed concurrently (concurrent with writing only with WAL journaling mode) 104 | my $pid = fork || die $!; 105 | say $sql->db->query(q{select datetime('now','localtime') as time})->hash->{time}; 106 | exit unless $pid; 107 | 108 | The C option prevents WAL mode from being enabled in new databases but 109 | doesn't affect databases where it has already been enabled. C may not 110 | be set by default in a future release. See L and 111 | L for more information. 112 | 113 | The L is 115 | disabled for all connections since Mojo::SQLite 3.003; use single quotes for 116 | string literals and double quotes for identifiers, as is normally recommended. 117 | 118 | All cached database handles will be reset automatically if a new process has 119 | been forked, this allows multiple processes to share the same L 120 | object safely. 121 | 122 | Any database errors will throw an exception as C is automatically 123 | enabled, so use C or L to catch them. This makes transactions 124 | with L easy. 125 | 126 | While passing a file path of C<:memory:> (or a custom L with 127 | C) will create a temporary database, in-memory databases cannot be 128 | shared between connections, so subsequent calls to L may return 129 | connections to completely different databases. For a temporary database that 130 | can be shared between connections and processes, pass a file path of C<:temp:> 131 | to store the database in a temporary directory (this is the default), or 132 | consider constructing a temporary directory yourself with L if you 133 | need to reuse the filename. A temporary directory allows SQLite to create 134 | L safely. 135 | 136 | use File::Spec::Functions 'catfile'; 137 | use File::Temp; 138 | use Mojo::SQLite; 139 | my $tempdir = File::Temp->newdir; # Deleted when object goes out of scope 140 | my $tempfile = catfile $tempdir, 'test.db'; 141 | my $sql = Mojo::SQLite->new->from_filename($tempfile); 142 | 143 | =head1 EXAMPLES 144 | 145 | This distribution also contains a well-structured example 146 | L 147 | you can use for inspiration. This application shows how to apply the MVC design 148 | pattern in practice. 149 | 150 | =head1 EVENTS 151 | 152 | L inherits all events from L and can emit the 153 | following new ones. 154 | 155 | =head2 connection 156 | 157 | $sql->on(connection => sub ($sql, $dbh) { 158 | $dbh->do('pragma journal_size_limit=1000000'); 159 | }); 160 | 161 | Emitted when a new database connection has been established. 162 | 163 | =head1 ATTRIBUTES 164 | 165 | L implements the following attributes. 166 | 167 | =head2 abstract 168 | 169 | my $abstract = $sql->abstract; 170 | $sql = $sql->abstract(SQL::Abstract->new); 171 | 172 | L object used to generate CRUD queries for 173 | L, defaults to a L object with 174 | C set to C<.> and C set to C<">. 175 | 176 | # Generate WHERE clause and bind values 177 | my($stmt, @bind) = $sql->abstract->where({foo => 'bar', baz => 'yada'}); 178 | 179 | L provides additional features to the L 180 | query methods in L such as C<-json> and 181 | C/C. The C feature is not applicable to SQLite queries. 182 | 183 | $sql->db->select(['some_table', ['other_table', foo_id => 'id']], 184 | ['foo', [bar => 'baz'], \q{datetime('now') as dt}], 185 | {foo => 'value'}, 186 | {order_by => 'foo', limit => 10, offset => 5, group_by => ['foo'], having => {baz => 'value'}}); 187 | 188 | # Upsert supported since SQLite 3.24.0 189 | $sql->db->insert('some_table', {name => $name, value => $value}, 190 | {on_conflict => [name => {value => \'"excluded"."value"'}]}); 191 | 192 | =head2 auto_migrate 193 | 194 | my $bool = $sql->auto_migrate; 195 | $sql = $sql->auto_migrate($bool); 196 | 197 | Automatically migrate to the latest database schema with L, as 198 | soon as L has been called for the first time. 199 | 200 | =head2 database_class 201 | 202 | my $class = $sql->database_class; 203 | $sql = $sql->database_class('MyApp::Database'); 204 | 205 | Class to be used by L, defaults to L. Note that 206 | this class needs to have already been loaded before L is called. 207 | 208 | =head2 dsn 209 | 210 | my $dsn = $sql->dsn; 211 | $sql = $sql->dsn('dbi:SQLite:uri=file:foo.db'); 212 | 213 | Data source name, defaults to C followed by a path to a 214 | temporary file. 215 | 216 | =head2 max_connections 217 | 218 | my $max = $sql->max_connections; 219 | $sql = $sql->max_connections(3); 220 | 221 | Maximum number of idle database handles to cache for future use, defaults to 222 | C<1>. 223 | 224 | =head2 migrations 225 | 226 | my $migrations = $sql->migrations; 227 | $sql = $sql->migrations(Mojo::SQLite::Migrations->new); 228 | 229 | L object you can use to change your database schema 230 | more easily. 231 | 232 | # Load migrations from file and migrate to latest version 233 | $sql->migrations->from_file('/home/dbook/migrations.sql')->migrate; 234 | 235 | =head2 options 236 | 237 | my $options = $sql->options; 238 | $sql = $sql->options({AutoCommit => 1, RaiseError => 1}); 239 | 240 | Options for database handles, defaults to setting C to 241 | C, setting C, 242 | C and C, and deactivating C. 243 | Note that C and C are considered mandatory, so 244 | deactivating them would be very dangerous. See 245 | L and 246 | L for more information on available 247 | options. 248 | 249 | =head2 parent 250 | 251 | my $parent = $sql->parent; 252 | $sql = $sql->parent(Mojo::SQLite->new); 253 | 254 | Another L object to use for connection management, instead of 255 | establishing and caching our own database connections. 256 | 257 | =head1 METHODS 258 | 259 | L inherits all methods from L and implements 260 | the following new ones. 261 | 262 | =head2 new 263 | 264 | my $sql = Mojo::SQLite->new; 265 | my $sql = Mojo::SQLite->new('file:test.db); 266 | my $sql = Mojo::SQLite->new('sqlite:test.db'); 267 | my $sql = Mojo::SQLite->new(Mojo::SQLite->new); 268 | 269 | Construct a new L object and parse connection string with 270 | L if necessary. 271 | 272 | # Customize configuration further 273 | my $sql = Mojo::SQLite->new->dsn('dbi:SQLite:dbname=test.db'); 274 | my $sql = Mojo::SQLite->new->dsn('dbi:SQLite:uri=file:test.db?mode=memory'); 275 | 276 | # Pass filename directly 277 | my $sql = Mojo::SQLite->new->from_filename($filename); 278 | 279 | =head2 db 280 | 281 | my $db = $sql->db; 282 | 283 | Get a database object based on L (which is usually 284 | L) for a cached or newly established database 285 | connection. The L database handle will be automatically cached 286 | again when that object is destroyed, so you can handle problems like connection 287 | timeouts gracefully by holding on to it only for short amounts of time. 288 | 289 | # Add up all the money 290 | say $sql->db->select('accounts') 291 | ->hashes->reduce(sub { $a->{money} + $b->{money} }); 292 | 293 | =head2 from_filename 294 | 295 | $sql = $sql->from_filename('C:\\Documents and Settings\\foo & bar.db', $options); 296 | 297 | Parse database filename directly. Unlike L, the filename is 298 | parsed as a local filename and not a URL. A hashref of L may be 299 | passed as the second argument. 300 | 301 | # Absolute filename 302 | $sql->from_filename('/home/fred/data.db'); 303 | 304 | # Relative to current directory 305 | $sql->from_filename('data.db'); 306 | 307 | # Temporary file database (default) 308 | $sql->from_filename(':temp:'); 309 | 310 | # In-memory temporary database (single connection only) 311 | my $db = $sql->from_filename(':memory:')->db; 312 | 313 | # Additional options 314 | $sql->from_filename($filename, { PrintError => 1 }); 315 | 316 | # Readonly connection without WAL mode 317 | $sql->from_filename($filename, { ReadOnly => 1, no_wal => 1 }); 318 | 319 | # Strict unicode strings and WAL mode 320 | use DBD::SQLite::Constants ':dbd_sqlite_string_mode'; 321 | $sql->from_filename($filename, { sqlite_string_mode => DBD_SQLITE_STRING_MODE_UNICODE_STRICT, wal_mode => 1 }); 322 | 323 | =head2 from_string 324 | 325 | $sql = $sql->from_string('test.db'); 326 | $sql = $sql->from_string('file:test.db'); 327 | $sql = $sql->from_string('file:///C:/foo/bar.db'); 328 | $sql = $sql->from_string('sqlite:C:%5Cfoo%5Cbar.db'); 329 | $sql = $sql->from_string(Mojo::SQLite->new); 330 | 331 | Parse configuration from connection string or use another L 332 | object as L. Connection strings are parsed as URLs, so you should 333 | construct them using a module like L, L, or L. 334 | For portability on non-Unix-like systems, either construct the URL with the 335 | C scheme, or use L to construct a URL with the C 336 | scheme. A URL with no scheme will be parsed as a C URL, and C URLs 337 | are parsed according to the current operating system. If specified, the 338 | hostname must be C. If the URL has a query string, it will be parsed 339 | and applied to L. 340 | 341 | # Absolute filename 342 | $sql->from_string('sqlite:////home/fred/data.db'); 343 | $sql->from_string('sqlite://localhost//home/fred/data.db'); 344 | $sql->from_string('sqlite:/home/fred/data.db'); 345 | $sql->from_string('file:///home/fred/data.db'); 346 | $sql->from_string('file://localhost/home/fred/data.db'); 347 | $sql->from_string('file:/home/fred/data.db'); 348 | $sql->from_string('///home/fred/data.db'); 349 | $sql->from_string('//localhost/home/fred/data.db'); 350 | $sql->from_string('/home/fred/data.db'); 351 | 352 | # Relative to current directory 353 | $sql->from_string('sqlite:data.db'); 354 | $sql->from_string('file:data.db'); 355 | $sql->from_string('data.db'); 356 | 357 | # Connection string must be a valid URL 358 | $sql->from_string(Mojo::URL->new->scheme('sqlite')->path($filename)); 359 | $sql->from_string(URI::db->new->Mojo::Base::tap(engine => 'sqlite')->Mojo::Base::tap(dbname => $filename)); 360 | $sql->from_string(URI::file->new($filename)); 361 | 362 | # Temporary file database (default) 363 | $sql->from_string(':temp:'); 364 | 365 | # In-memory temporary database (single connection only) 366 | my $db = $sql->from_string(':memory:')->db; 367 | 368 | # Additional options 369 | $sql->from_string('data.db?PrintError=1&sqlite_allow_multiple_statements=1'); 370 | $sql->from_string(Mojo::URL->new->scheme('sqlite')->path($filename)->query(sqlite_see_if_its_a_number => 1)); 371 | $sql->from_string(URI::file->new($filename)->Mojo::Base::tap(query_form => {PrintError => 1})); 372 | 373 | # Readonly connection without WAL mode 374 | $sql->from_string('data.db?ReadOnly=1&no_wal=1'); 375 | 376 | # String unicode strings and WAL mode 377 | use DBD::SQLite::Constants ':dbd_sqlite_string_mode'; 378 | $sql->from_string(Mojo::URL->new->scheme('sqlite')->path('data.db') 379 | ->query(sqlite_string_mode => DBD_SQLITE_STRING_MODE_UNICODE_STRICT, wal_mode => 1)); 380 | 381 | =head1 DEBUGGING 382 | 383 | You can set the C environment variable to get some advanced 384 | diagnostics information printed by L. 385 | 386 | DBI_TRACE=1 387 | DBI_TRACE=15 388 | DBI_TRACE=SQL 389 | 390 | =head1 REFERENCE 391 | 392 | This is the class hierarchy of the L distribution. 393 | 394 | =over 2 395 | 396 | =item * L 397 | 398 | =item * L 399 | 400 | =item * L 401 | 402 | =item * L 403 | 404 | =item * L 405 | 406 | =back 407 | 408 | =head1 BUGS 409 | 410 | Report any issues on the public bugtracker. 411 | 412 | =head1 AUTHOR 413 | 414 | Dan Book, C 415 | 416 | =head1 CREDITS 417 | 418 | Sebastian Riedel, author of L, which this distribution is based on. 419 | 420 | =head1 COPYRIGHT AND LICENSE 421 | 422 | Copyright 2015, Dan Book. 423 | 424 | This library is free software; you may redistribute it and/or modify it under 425 | the terms of the Artistic License version 2.0. 426 | 427 | =head1 SEE ALSO 428 | 429 | L, L, L 430 | 431 | =cut 432 | -------------------------------------------------------------------------------- /dist.ini: -------------------------------------------------------------------------------- 1 | name = Mojo-SQLite 2 | author = Dan Book 3 | license = Artistic_2_0 4 | copyright_holder = Dan Book 5 | copyright_year = 2015 6 | 7 | [@Author::DBOOK] 8 | :version = v1.0.3 9 | installer = ModuleBuildTiny::Fallback 10 | irc = ircs://irc.libera.chat/#mojo 11 | pod_tests = 1 12 | 13 | [Deprecated] 14 | module = Mojo::SQLite::PubSub 15 | -------------------------------------------------------------------------------- /examples/blog/blog.conf: -------------------------------------------------------------------------------- 1 | { 2 | sqlite => 'test.db', 3 | secrets => ['s3cret'], 4 | } 5 | -------------------------------------------------------------------------------- /examples/blog/lib/Blog.pm: -------------------------------------------------------------------------------- 1 | package Blog; 2 | use Mojo::Base 'Mojolicious'; 3 | 4 | use Blog::Model::Posts; 5 | use Mojo::SQLite; 6 | 7 | sub startup { 8 | my $self = shift; 9 | 10 | # Configuration 11 | $self->plugin('Config'); 12 | $self->secrets($self->config('secrets')); 13 | 14 | # Model 15 | $self->helper(sqlite => sub { state $sql = Mojo::SQLite->new->from_filename(shift->config('sqlite')) }); 16 | $self->helper( 17 | posts => sub { state $posts = Blog::Model::Posts->new(sqlite => shift->sqlite) }); 18 | 19 | # Migrate to latest version if necessary 20 | my $path = $self->home->child('migrations', 'blog.sql'); 21 | $self->sqlite->auto_migrate(1)->migrations->name('blog')->from_file($path); 22 | 23 | # Controller 24 | my $r = $self->routes; 25 | $r->get('/' => sub { shift->redirect_to('posts') }); 26 | $r->get('/posts')->to('posts#index'); 27 | $r->get('/posts/create')->to('posts#create')->name('create_post'); 28 | $r->post('/posts')->to('posts#store')->name('store_post'); 29 | $r->get('/posts/:id')->to('posts#show')->name('show_post'); 30 | $r->get('/posts/:id/edit')->to('posts#edit')->name('edit_post'); 31 | $r->put('/posts/:id')->to('posts#update')->name('update_post'); 32 | $r->delete('/posts/:id')->to('posts#remove')->name('remove_post'); 33 | } 34 | 35 | 1; 36 | -------------------------------------------------------------------------------- /examples/blog/lib/Blog/Controller/Posts.pm: -------------------------------------------------------------------------------- 1 | package Blog::Controller::Posts; 2 | use Mojo::Base 'Mojolicious::Controller'; 3 | 4 | sub create { shift->render(post => {}) } 5 | 6 | sub edit { 7 | my $self = shift; 8 | $self->render(post => $self->posts->find($self->param('id'))); 9 | } 10 | 11 | sub index { 12 | my $self = shift; 13 | $self->render(posts => $self->posts->all); 14 | } 15 | 16 | sub remove { 17 | my $self = shift; 18 | $self->posts->remove($self->param('id')); 19 | $self->redirect_to('posts'); 20 | } 21 | 22 | sub show { 23 | my $self = shift; 24 | $self->render(post => $self->posts->find($self->param('id'))); 25 | } 26 | 27 | sub store { 28 | my $self = shift; 29 | 30 | my $validation = $self->_validation; 31 | return $self->render(action => 'create', post => {}) 32 | if $validation->has_error; 33 | 34 | my $id = $self->posts->add($validation->output); 35 | $self->redirect_to('show_post', id => $id); 36 | } 37 | 38 | sub update { 39 | my $self = shift; 40 | 41 | my $validation = $self->_validation; 42 | return $self->render(action => 'edit', post => {}) if $validation->has_error; 43 | 44 | my $id = $self->param('id'); 45 | $self->posts->save($id, $validation->output); 46 | $self->redirect_to('show_post', id => $id); 47 | } 48 | 49 | sub _validation { 50 | my $self = shift; 51 | 52 | my $validation = $self->validation; 53 | $validation->required('title', 'not_empty'); 54 | $validation->required('body', 'not_empty'); 55 | 56 | return $validation; 57 | } 58 | 59 | 1; 60 | -------------------------------------------------------------------------------- /examples/blog/lib/Blog/Model/Posts.pm: -------------------------------------------------------------------------------- 1 | package Blog::Model::Posts; 2 | use Mojo::Base -base; 3 | 4 | has 'sqlite'; 5 | 6 | sub add { 7 | my ($self, $post) = @_; 8 | return $self->sqlite->db->insert('posts', $post)->last_insert_id; 9 | } 10 | 11 | sub all { shift->sqlite->db->select('posts')->hashes->to_array } 12 | 13 | sub find { 14 | my ($self, $id) = @_; 15 | return $self->sqlite->db->select('posts', undef, {id => $id})->hash; 16 | } 17 | 18 | sub remove { 19 | my ($self, $id) = @_; 20 | $self->sqlite->db->delete('posts', {id => $id}); 21 | } 22 | 23 | sub save { 24 | my ($self, $id, $post) = @_; 25 | $self->sqlite->db->update('posts', $post, {id => $id}); 26 | } 27 | 28 | 1; 29 | -------------------------------------------------------------------------------- /examples/blog/migrations/blog.sql: -------------------------------------------------------------------------------- 1 | -- 1 up 2 | create table if not exists posts ( 3 | id integer primary key autoincrement, 4 | title text, 5 | body text 6 | ); 7 | 8 | -- 1 down 9 | drop table if exists posts; 10 | -------------------------------------------------------------------------------- /examples/blog/script/blog: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use FindBin; 7 | BEGIN { unshift @INC, "$FindBin::Bin/../lib" } 8 | 9 | # Start command line interface for application 10 | require Mojolicious::Commands; 11 | Mojolicious::Commands->start_app('Blog'); 12 | -------------------------------------------------------------------------------- /examples/blog/t/blog.t: -------------------------------------------------------------------------------- 1 | use Mojo::Base -strict; 2 | 3 | use Test::More; 4 | 5 | use Mojo::SQLite; 6 | use Mojo::URL; 7 | use Test::Mojo; 8 | 9 | # Override configuration for testing 10 | my $t = Test::Mojo->new(Blog => {sqlite => ':temp:', secrets => ['test_s3cret']}); 11 | $t->ua->max_redirects(10); 12 | 13 | # No posts yet 14 | $t->get_ok('/')->status_is(200)->text_is('title' => 'Blog') 15 | ->text_is('body > a' => 'New post')->element_exists_not('h2'); 16 | 17 | # Create a new post 18 | $t->get_ok('/posts/create')->status_is(200)->text_is('title' => 'New post') 19 | ->element_exists('form input[name=title]') 20 | ->element_exists('form textarea[name=body]'); 21 | $t->post_ok('/posts' => form => {title => 'Testing', body => 'This is a test.'}) 22 | ->status_is(200)->text_is('title' => 'Testing')->text_is('h2' => 'Testing') 23 | ->text_like('p' => qr/This is a test/); 24 | 25 | # Read the post 26 | $t->get_ok('/')->status_is(200)->text_is('title' => 'Blog') 27 | ->text_is('h2 a' => 'Testing')->text_like('p' => qr/This is a test/); 28 | $t->get_ok('/posts/1')->status_is(200)->text_is('title' => 'Testing') 29 | ->text_is('h2' => 'Testing')->text_like('p' => qr/This is a test/) 30 | ->text_is('body > a' => 'Edit'); 31 | 32 | # Update the post 33 | $t->get_ok('/posts/1/edit')->status_is(200)->text_is('title' => 'Edit post') 34 | ->element_exists('form input[name=title][value=Testing]') 35 | ->text_like('form textarea[name=body]' => qr/This is a test/) 36 | ->element_exists('form input[value=Remove]'); 37 | $t->post_ok( 38 | '/posts/1?_method=PUT' => form => {title => 'Again', body => 'It works.'}) 39 | ->status_is(200)->text_is('title' => 'Again')->text_is('h2' => 'Again') 40 | ->text_like('p' => qr/It works/); 41 | $t->get_ok('/posts/1')->status_is(200)->text_is('title' => 'Again') 42 | ->text_is('h2' => 'Again')->text_like('p' => qr/It works/); 43 | 44 | # Delete the post 45 | $t->post_ok('/posts/1?_method=DELETE')->status_is(200) 46 | ->text_is('title' => 'Blog')->element_exists_not('h2'); 47 | 48 | done_testing(); 49 | -------------------------------------------------------------------------------- /examples/blog/templates/layouts/blog.html.ep: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <%= title %> 5 | 14 | 15 | 16 |

<%= link_to 'Blog' => 'posts' %>

17 | %= content 18 | 19 | 20 | -------------------------------------------------------------------------------- /examples/blog/templates/posts/_form.html.ep: -------------------------------------------------------------------------------- 1 | %= form_for $target => begin 2 | %= label_for title => 'Title' 3 |
4 | %= text_field title => $post->{title} 5 |
6 | %= label_for body => 'Body' 7 |
8 | %= text_area body => $post->{body} 9 |
10 | %= submit_button $caption 11 | % end 12 | -------------------------------------------------------------------------------- /examples/blog/templates/posts/create.html.ep: -------------------------------------------------------------------------------- 1 | % layout 'blog', title => 'New post'; 2 |

New post

3 | %= include 'posts/_form', caption => 'Create', target => 'store_post' 4 | -------------------------------------------------------------------------------- /examples/blog/templates/posts/edit.html.ep: -------------------------------------------------------------------------------- 1 | % layout 'blog', title => 'Edit post'; 2 |

Edit post

3 | %= include 'posts/_form', caption => 'Update', target => 'update_post' 4 | %= button_to Remove => remove_post => {id => $post->{id}} 5 | -------------------------------------------------------------------------------- /examples/blog/templates/posts/index.html.ep: -------------------------------------------------------------------------------- 1 | % layout 'blog', title => 'Blog'; 2 | % for my $post (@$posts) { 3 |

<%= link_to $post->{title} => show_post => {id => $post->{id}} %>

4 |

5 | %= $post->{body} 6 |

7 | % } 8 | %= link_to 'New post' => 'create_post' 9 | -------------------------------------------------------------------------------- /examples/blog/templates/posts/show.html.ep: -------------------------------------------------------------------------------- 1 | % layout 'blog', title => $post->{title}; 2 |

<%= $post->{title} %>

3 |

<%= $post->{body} %>

4 | %= link_to 'Edit' => edit_post => {id => $post->{id}} 5 | -------------------------------------------------------------------------------- /lib/Mojo/SQLite.pm: -------------------------------------------------------------------------------- 1 | package Mojo::SQLite; 2 | use Mojo::Base 'Mojo::EventEmitter'; 3 | 4 | use Carp 'croak'; 5 | use DBI; 6 | use DBD::SQLite; 7 | use DBD::SQLite::Constants qw(:database_connection_configuration_options :dbd_sqlite_string_mode); 8 | use File::Spec::Functions 'catfile'; 9 | use File::Temp; 10 | use Mojo::SQLite::Database; 11 | use Mojo::SQLite::Migrations; 12 | use Scalar::Util qw(blessed weaken); 13 | use SQL::Abstract::Pg; 14 | use URI; 15 | use URI::db; 16 | 17 | our $VERSION = '3.010'; 18 | 19 | has abstract => sub { SQL::Abstract::Pg->new(name_sep => '.', quote_char => '"') }; 20 | has 'auto_migrate'; 21 | has database_class => 'Mojo::SQLite::Database'; 22 | has dsn => sub { _url_from_file(shift->_tempfile)->dbi_dsn }; 23 | has max_connections => 1; 24 | has migrations => sub { Mojo::SQLite::Migrations->new(sqlite => shift) }; 25 | has options => sub { 26 | { 27 | AutoCommit => 1, 28 | AutoInactiveDestroy => 1, 29 | PrintError => 0, 30 | RaiseError => 1, 31 | sqlite_string_mode => DBD_SQLITE_STRING_MODE_UNICODE_FALLBACK, 32 | wal_mode => 1, 33 | }; 34 | }; 35 | has 'parent'; 36 | 37 | sub new { @_ > 1 ? shift->SUPER::new->from_string(@_) : shift->SUPER::new } 38 | 39 | sub db { $_[0]->database_class->new(dbh => $_[0]->_prepare, sqlite => $_[0]) } 40 | 41 | sub from_filename { shift->from_string(_url_from_file(shift, shift)) } 42 | 43 | sub from_string { 44 | my ($self, $str) = @_; 45 | return $self unless $str; 46 | return $self->parent($str) if blessed $str and $str->isa('Mojo::SQLite'); 47 | 48 | my $url = URI->new($str); 49 | 50 | # Options 51 | my %options = $url->query_form; 52 | $url->query(undef); 53 | # don't set default string_mode if sqlite_unicode legacy option is set 54 | delete $self->options->{sqlite_string_mode} if exists $options{sqlite_unicode}; 55 | @{$self->options}{keys %options} = values %options; 56 | 57 | # Parse URL based on scheme 58 | $url->scheme('file') unless $url->has_recognized_scheme; 59 | if ($url->scheme eq 'file') { 60 | $url = _url_from_file($url->file); 61 | } elsif ($url->scheme ne 'db') { 62 | $url = URI::db->new($url); 63 | } 64 | 65 | croak qq{Invalid SQLite connection string "$str"} 66 | unless $url->has_recognized_engine and $url->canonical_engine eq 'sqlite' 67 | and (($url->host // '') eq '' or $url->host eq 'localhost'); 68 | 69 | # Temp database file 70 | $url->dbname($self->_tempfile) if $url->dbname eq ':temp:'; 71 | 72 | return $self->dsn($url->dbi_dsn); 73 | } 74 | 75 | sub _dequeue { 76 | my $self = shift; 77 | 78 | # Fork-safety 79 | delete @$self{qw(pid queue)} unless ($self->{pid} //= $$) eq $$; 80 | 81 | while (my $dbh = shift @{$self->{queue} || []}) { return $dbh if $dbh->ping } 82 | 83 | my $dbh = DBI->connect($self->dsn, undef, undef, $self->options) 84 | // croak "DBI connection to @{[$self->dsn]} failed: $DBI::errstr"; # RaiseError disabled 85 | $dbh->sqlite_db_config(SQLITE_DBCONFIG_DQS_DDL, 0); 86 | $dbh->sqlite_db_config(SQLITE_DBCONFIG_DQS_DML, 0); 87 | if ($self->options->{wal_mode} and !$self->options->{no_wal}) { 88 | $dbh->do('pragma journal_mode=WAL'); 89 | $dbh->do('pragma synchronous=NORMAL'); 90 | } 91 | 92 | # Cache the last insert rowid on inserts 93 | weaken(my $weakdbh = $dbh); 94 | $dbh->sqlite_update_hook(sub { 95 | $weakdbh->{private_mojo_last_insert_id} = $_[3] if $_[0] == DBD::SQLite::INSERT; 96 | }); 97 | 98 | $self->emit(connection => $dbh); 99 | 100 | return $dbh; 101 | } 102 | 103 | sub _enqueue { 104 | my ($self, $dbh) = @_; 105 | 106 | if (my $parent = $self->parent) { return $parent->_enqueue($dbh) } 107 | 108 | my $queue = $self->{queue} ||= []; 109 | push @$queue, $dbh if $dbh->{Active}; 110 | shift @$queue while @$queue > $self->max_connections; 111 | } 112 | 113 | sub _prepare { 114 | my $self = shift; 115 | 116 | # Automatic migrations 117 | ++$self->{migrated} and $self->migrations->migrate 118 | if !$self->{migrated} && $self->auto_migrate; 119 | 120 | my $parent = $self->parent; 121 | return $parent ? $parent->_prepare : $self->_dequeue; 122 | } 123 | 124 | sub _tempfile { catfile(shift->{tempdir} = File::Temp->newdir, 'sqlite.db') } 125 | 126 | sub _url_from_file { 127 | my $url = URI::db->new; 128 | $url->engine('sqlite'); 129 | $url->dbname(shift); 130 | if (my $options = shift) { $url->query_form($options) } 131 | return $url; 132 | } 133 | 134 | 1; 135 | 136 | =head1 NAME 137 | 138 | Mojo::SQLite - A tiny Mojolicious wrapper for SQLite 139 | 140 | =head1 SYNOPSIS 141 | 142 | use Mojo::SQLite; 143 | 144 | # Select the library version 145 | my $sql = Mojo::SQLite->new('sqlite:test.db'); 146 | say $sql->db->query('select sqlite_version() as version')->hash->{version}; 147 | 148 | # Use migrations to create a table 149 | $sql->migrations->name('my_names_app')->from_string(<migrate; 150 | -- 1 up 151 | create table names (id integer primary key autoincrement, name text); 152 | -- 1 down 153 | drop table names; 154 | EOF 155 | 156 | # Use migrations to drop and recreate the table 157 | $sql->migrations->migrate(0)->migrate; 158 | 159 | # Get a database handle from the cache for multiple queries 160 | my $db = $sql->db; 161 | 162 | # Use SQL::Abstract to generate simple CRUD queries for you 163 | $db->insert('names', {name => 'Isabel'}); 164 | my $id = $db->select('names', ['id'], {name => 'Isabel'})->hash->{id}; 165 | $db->update('names', {name => 'Bel'}, {id => $id}); 166 | $db->delete('names', {name => 'Bel'}); 167 | 168 | # Insert a few rows in a transaction with SQL and placeholders 169 | eval { 170 | my $tx = $db->begin; 171 | $db->query('insert into names (name) values (?)', 'Sara'); 172 | $db->query('insert into names (name) values (?)', 'Stefan'); 173 | $tx->commit; 174 | }; 175 | say $@ if $@; 176 | 177 | # Insert another row with SQL::Abstract and return the generated id 178 | say $db->insert('names', {name => 'Daniel'})->last_insert_id; 179 | 180 | # JSON roundtrip 181 | say $db->query('select ? as foo', {json => {bar => 'baz'}}) 182 | ->expand(json => 'foo')->hash->{foo}{bar}; 183 | 184 | # Select one row at a time 185 | my $results = $db->query('select * from names'); 186 | while (my $next = $results->hash) { 187 | say $next->{name}; 188 | } 189 | 190 | # Select all rows with SQL::Abstract 191 | say $_->{name} for $db->select('names')->hashes->each; 192 | 193 | =head1 DESCRIPTION 194 | 195 | L is a tiny wrapper around L that makes 196 | L a lot of fun to use with the 197 | L real-time web framework. Use all 198 | L SQLite has to offer, generate CRUD 199 | queries from data structures, and manage your database schema with migrations. 200 | 201 | =head1 BASICS 202 | 203 | Database and statement handles are cached automatically, so they can be reused 204 | transparently to increase performance. And you can handle connection timeouts 205 | gracefully by holding on to them only for short amounts of time. 206 | 207 | use Mojolicious::Lite; 208 | use Mojo::SQLite; 209 | 210 | helper sqlite => sub { state $sql = Mojo::SQLite->new('sqlite:test.db') }; 211 | 212 | get '/' => sub ($c) { 213 | my $db = $c->sqlite->db; 214 | $c->render(json => $db->query(q{select datetime('now','localtime') as now})->hash); 215 | }; 216 | 217 | app->start; 218 | 219 | In this example application, we create a C helper to store a 220 | L object. Our action calls that helper and uses the method 221 | L to dequeue a L object from the 222 | connection pool. Then we use the method L to 223 | execute an L 224 | statement, which returns a L object. And finally we call 225 | the method L to retrieve the first row as a hash 226 | reference. 227 | 228 | All I/O and queries are performed synchronously, and SQLite's default journal 229 | mode only supports concurrent reads from multiple processes while the database 230 | is not being written. The "Write-Ahead Log" journal mode allows multiple 231 | processes to read and write concurrently to the same database file (but only 232 | one can write at a time). WAL mode is enabled by the C option, 233 | currently enabled by default, and persists when opening that same database in 234 | the future. 235 | 236 | # Performed concurrently (concurrent with writing only with WAL journaling mode) 237 | my $pid = fork || die $!; 238 | say $sql->db->query(q{select datetime('now','localtime') as time})->hash->{time}; 239 | exit unless $pid; 240 | 241 | The C option prevents WAL mode from being enabled in new databases but 242 | doesn't affect databases where it has already been enabled. C may not 243 | be set by default in a future release. See L and 244 | L for more information. 245 | 246 | The L is 248 | disabled for all connections since Mojo::SQLite 3.003; use single quotes for 249 | string literals and double quotes for identifiers, as is normally recommended. 250 | 251 | All cached database handles will be reset automatically if a new process has 252 | been forked, this allows multiple processes to share the same L 253 | object safely. 254 | 255 | Any database errors will throw an exception as C is automatically 256 | enabled, so use C or L to catch them. This makes transactions 257 | with L easy. 258 | 259 | While passing a file path of C<:memory:> (or a custom L with 260 | C) will create a temporary database, in-memory databases cannot be 261 | shared between connections, so subsequent calls to L may return 262 | connections to completely different databases. For a temporary database that 263 | can be shared between connections and processes, pass a file path of C<:temp:> 264 | to store the database in a temporary directory (this is the default), or 265 | consider constructing a temporary directory yourself with L if you 266 | need to reuse the filename. A temporary directory allows SQLite to create 267 | L safely. 268 | 269 | use File::Spec::Functions 'catfile'; 270 | use File::Temp; 271 | use Mojo::SQLite; 272 | my $tempdir = File::Temp->newdir; # Deleted when object goes out of scope 273 | my $tempfile = catfile $tempdir, 'test.db'; 274 | my $sql = Mojo::SQLite->new->from_filename($tempfile); 275 | 276 | =head1 EXAMPLES 277 | 278 | This distribution also contains a well-structured example 279 | L 280 | you can use for inspiration. This application shows how to apply the MVC design 281 | pattern in practice. 282 | 283 | =head1 EVENTS 284 | 285 | L inherits all events from L and can emit the 286 | following new ones. 287 | 288 | =head2 connection 289 | 290 | $sql->on(connection => sub ($sql, $dbh) { 291 | $dbh->do('pragma journal_size_limit=1000000'); 292 | }); 293 | 294 | Emitted when a new database connection has been established. 295 | 296 | =head1 ATTRIBUTES 297 | 298 | L implements the following attributes. 299 | 300 | =head2 abstract 301 | 302 | my $abstract = $sql->abstract; 303 | $sql = $sql->abstract(SQL::Abstract->new); 304 | 305 | L object used to generate CRUD queries for 306 | L, defaults to a L object with 307 | C set to C<.> and C set to C<">. 308 | 309 | # Generate WHERE clause and bind values 310 | my($stmt, @bind) = $sql->abstract->where({foo => 'bar', baz => 'yada'}); 311 | 312 | L provides additional features to the L 313 | query methods in L such as C<-json> and 314 | C/C. The C feature is not applicable to SQLite queries. 315 | 316 | $sql->db->select(['some_table', ['other_table', foo_id => 'id']], 317 | ['foo', [bar => 'baz'], \q{datetime('now') as dt}], 318 | {foo => 'value'}, 319 | {order_by => 'foo', limit => 10, offset => 5, group_by => ['foo'], having => {baz => 'value'}}); 320 | 321 | # Upsert supported since SQLite 3.24.0 322 | $sql->db->insert('some_table', {name => $name, value => $value}, 323 | {on_conflict => [name => {value => \'"excluded"."value"'}]}); 324 | 325 | =head2 auto_migrate 326 | 327 | my $bool = $sql->auto_migrate; 328 | $sql = $sql->auto_migrate($bool); 329 | 330 | Automatically migrate to the latest database schema with L, as 331 | soon as L has been called for the first time. 332 | 333 | =head2 database_class 334 | 335 | my $class = $sql->database_class; 336 | $sql = $sql->database_class('MyApp::Database'); 337 | 338 | Class to be used by L, defaults to L. Note that 339 | this class needs to have already been loaded before L is called. 340 | 341 | =head2 dsn 342 | 343 | my $dsn = $sql->dsn; 344 | $sql = $sql->dsn('dbi:SQLite:uri=file:foo.db'); 345 | 346 | Data source name, defaults to C followed by a path to a 347 | temporary file. 348 | 349 | =head2 max_connections 350 | 351 | my $max = $sql->max_connections; 352 | $sql = $sql->max_connections(3); 353 | 354 | Maximum number of idle database handles to cache for future use, defaults to 355 | C<1>. 356 | 357 | =head2 migrations 358 | 359 | my $migrations = $sql->migrations; 360 | $sql = $sql->migrations(Mojo::SQLite::Migrations->new); 361 | 362 | L object you can use to change your database schema 363 | more easily. 364 | 365 | # Load migrations from file and migrate to latest version 366 | $sql->migrations->from_file('/home/dbook/migrations.sql')->migrate; 367 | 368 | =head2 options 369 | 370 | my $options = $sql->options; 371 | $sql = $sql->options({AutoCommit => 1, RaiseError => 1}); 372 | 373 | Options for database handles, defaults to setting C to 374 | C, setting C, 375 | C and C, and deactivating C. 376 | Note that C and C are considered mandatory, so 377 | deactivating them would be very dangerous. See 378 | L and 379 | L for more information on available 380 | options. 381 | 382 | =head2 parent 383 | 384 | my $parent = $sql->parent; 385 | $sql = $sql->parent(Mojo::SQLite->new); 386 | 387 | Another L object to use for connection management, instead of 388 | establishing and caching our own database connections. 389 | 390 | =head1 METHODS 391 | 392 | L inherits all methods from L and implements 393 | the following new ones. 394 | 395 | =head2 new 396 | 397 | my $sql = Mojo::SQLite->new; 398 | my $sql = Mojo::SQLite->new('file:test.db); 399 | my $sql = Mojo::SQLite->new('sqlite:test.db'); 400 | my $sql = Mojo::SQLite->new(Mojo::SQLite->new); 401 | 402 | Construct a new L object and parse connection string with 403 | L if necessary. 404 | 405 | # Customize configuration further 406 | my $sql = Mojo::SQLite->new->dsn('dbi:SQLite:dbname=test.db'); 407 | my $sql = Mojo::SQLite->new->dsn('dbi:SQLite:uri=file:test.db?mode=memory'); 408 | 409 | # Pass filename directly 410 | my $sql = Mojo::SQLite->new->from_filename($filename); 411 | 412 | =head2 db 413 | 414 | my $db = $sql->db; 415 | 416 | Get a database object based on L (which is usually 417 | L) for a cached or newly established database 418 | connection. The L database handle will be automatically cached 419 | again when that object is destroyed, so you can handle problems like connection 420 | timeouts gracefully by holding on to it only for short amounts of time. 421 | 422 | # Add up all the money 423 | say $sql->db->select('accounts') 424 | ->hashes->reduce(sub { $a->{money} + $b->{money} }); 425 | 426 | =head2 from_filename 427 | 428 | $sql = $sql->from_filename('C:\\Documents and Settings\\foo & bar.db', $options); 429 | 430 | Parse database filename directly. Unlike L, the filename is 431 | parsed as a local filename and not a URL. A hashref of L may be 432 | passed as the second argument. 433 | 434 | # Absolute filename 435 | $sql->from_filename('/home/fred/data.db'); 436 | 437 | # Relative to current directory 438 | $sql->from_filename('data.db'); 439 | 440 | # Temporary file database (default) 441 | $sql->from_filename(':temp:'); 442 | 443 | # In-memory temporary database (single connection only) 444 | my $db = $sql->from_filename(':memory:')->db; 445 | 446 | # Additional options 447 | $sql->from_filename($filename, { PrintError => 1 }); 448 | 449 | # Readonly connection without WAL mode 450 | $sql->from_filename($filename, { ReadOnly => 1, no_wal => 1 }); 451 | 452 | # Strict unicode strings and WAL mode 453 | use DBD::SQLite::Constants ':dbd_sqlite_string_mode'; 454 | $sql->from_filename($filename, { sqlite_string_mode => DBD_SQLITE_STRING_MODE_UNICODE_STRICT, wal_mode => 1 }); 455 | 456 | =head2 from_string 457 | 458 | $sql = $sql->from_string('test.db'); 459 | $sql = $sql->from_string('file:test.db'); 460 | $sql = $sql->from_string('file:///C:/foo/bar.db'); 461 | $sql = $sql->from_string('sqlite:C:%5Cfoo%5Cbar.db'); 462 | $sql = $sql->from_string(Mojo::SQLite->new); 463 | 464 | Parse configuration from connection string or use another L 465 | object as L. Connection strings are parsed as URLs, so you should 466 | construct them using a module like L, L, or L. 467 | For portability on non-Unix-like systems, either construct the URL with the 468 | C scheme, or use L to construct a URL with the C 469 | scheme. A URL with no scheme will be parsed as a C URL, and C URLs 470 | are parsed according to the current operating system. If specified, the 471 | hostname must be C. If the URL has a query string, it will be parsed 472 | and applied to L. 473 | 474 | # Absolute filename 475 | $sql->from_string('sqlite:////home/fred/data.db'); 476 | $sql->from_string('sqlite://localhost//home/fred/data.db'); 477 | $sql->from_string('sqlite:/home/fred/data.db'); 478 | $sql->from_string('file:///home/fred/data.db'); 479 | $sql->from_string('file://localhost/home/fred/data.db'); 480 | $sql->from_string('file:/home/fred/data.db'); 481 | $sql->from_string('///home/fred/data.db'); 482 | $sql->from_string('//localhost/home/fred/data.db'); 483 | $sql->from_string('/home/fred/data.db'); 484 | 485 | # Relative to current directory 486 | $sql->from_string('sqlite:data.db'); 487 | $sql->from_string('file:data.db'); 488 | $sql->from_string('data.db'); 489 | 490 | # Connection string must be a valid URL 491 | $sql->from_string(Mojo::URL->new->scheme('sqlite')->path($filename)); 492 | $sql->from_string(URI::db->new->Mojo::Base::tap(engine => 'sqlite')->Mojo::Base::tap(dbname => $filename)); 493 | $sql->from_string(URI::file->new($filename)); 494 | 495 | # Temporary file database (default) 496 | $sql->from_string(':temp:'); 497 | 498 | # In-memory temporary database (single connection only) 499 | my $db = $sql->from_string(':memory:')->db; 500 | 501 | # Additional options 502 | $sql->from_string('data.db?PrintError=1&sqlite_allow_multiple_statements=1'); 503 | $sql->from_string(Mojo::URL->new->scheme('sqlite')->path($filename)->query(sqlite_see_if_its_a_number => 1)); 504 | $sql->from_string(URI::file->new($filename)->Mojo::Base::tap(query_form => {PrintError => 1})); 505 | 506 | # Readonly connection without WAL mode 507 | $sql->from_string('data.db?ReadOnly=1&no_wal=1'); 508 | 509 | # String unicode strings and WAL mode 510 | use DBD::SQLite::Constants ':dbd_sqlite_string_mode'; 511 | $sql->from_string(Mojo::URL->new->scheme('sqlite')->path('data.db') 512 | ->query(sqlite_string_mode => DBD_SQLITE_STRING_MODE_UNICODE_STRICT, wal_mode => 1)); 513 | 514 | =head1 DEBUGGING 515 | 516 | You can set the C environment variable to get some advanced 517 | diagnostics information printed by L. 518 | 519 | DBI_TRACE=1 520 | DBI_TRACE=15 521 | DBI_TRACE=SQL 522 | 523 | =head1 REFERENCE 524 | 525 | This is the class hierarchy of the L distribution. 526 | 527 | =over 2 528 | 529 | =item * L 530 | 531 | =item * L 532 | 533 | =item * L 534 | 535 | =item * L 536 | 537 | =item * L 538 | 539 | =back 540 | 541 | =head1 BUGS 542 | 543 | Report any issues on the public bugtracker. 544 | 545 | =head1 AUTHOR 546 | 547 | Dan Book, C 548 | 549 | =head1 CREDITS 550 | 551 | Sebastian Riedel, author of L, which this distribution is based on. 552 | 553 | =head1 COPYRIGHT AND LICENSE 554 | 555 | Copyright 2015, Dan Book. 556 | 557 | This library is free software; you may redistribute it and/or modify it under 558 | the terms of the Artistic License version 2.0. 559 | 560 | =head1 SEE ALSO 561 | 562 | L, L, L 563 | -------------------------------------------------------------------------------- /lib/Mojo/SQLite/Database.pm: -------------------------------------------------------------------------------- 1 | package Mojo::SQLite::Database; 2 | use Mojo::Base -base; 3 | 4 | use Carp qw(croak shortmess); 5 | use DBI 'SQL_VARCHAR'; 6 | use Mojo::JSON 'to_json'; 7 | use Mojo::Promise; 8 | use Mojo::SQLite::Results; 9 | use Mojo::SQLite::Transaction; 10 | use Mojo::Util 'monkey_patch'; 11 | 12 | our $VERSION = '3.010'; 13 | 14 | our @CARP_NOT = qw(Mojo::SQLite::Migrations); 15 | 16 | has [qw(dbh sqlite)]; 17 | has results_class => 'Mojo::SQLite::Results'; 18 | 19 | for my $name (qw(delete insert select update)) { 20 | monkey_patch __PACKAGE__, $name, sub { 21 | my ($self, @cb) = (shift, ref $_[-1] eq 'CODE' ? pop : ()); 22 | return $self->query($self->sqlite->abstract->$name(@_), @cb); 23 | }; 24 | monkey_patch __PACKAGE__, "${name}_p", sub { 25 | my $self = shift; 26 | return $self->query_p($self->sqlite->abstract->$name(@_)); 27 | }; 28 | } 29 | 30 | sub DESTROY { 31 | my $self = shift; 32 | 33 | # Supported on Perl 5.14+ 34 | return() if defined ${^GLOBAL_PHASE} && ${^GLOBAL_PHASE} eq 'DESTRUCT'; 35 | 36 | return() unless (my $sql = $self->sqlite) && (my $dbh = $self->dbh); 37 | $sql->_enqueue($dbh); 38 | } 39 | 40 | sub begin { 41 | my ($self, $behavior) = @_; 42 | return Mojo::SQLite::Transaction->new(db => $self, behavior => $behavior); 43 | } 44 | 45 | sub disconnect { 46 | my $self = shift; 47 | $self->dbh->disconnect; 48 | } 49 | 50 | sub ping { shift->dbh->ping } 51 | 52 | sub query { 53 | my ($self, $query) = (shift, shift); 54 | my $cb = ref $_[-1] eq 'CODE' ? pop : undef; 55 | 56 | my $dbh = $self->dbh; 57 | 58 | my $prev_h = $dbh->{HandleError}; 59 | # Better context for error messages 60 | local $dbh->{HandleError} = sub { $_[0] = shortmess $_[0]; ($prev_h and $prev_h->(@_)) ? 1 : 0 }; 61 | 62 | my ($sth, $errored, $error); 63 | { 64 | local $@; 65 | unless (eval { 66 | # If RaiseError has been disabled, we might not get a handle 67 | if (defined($sth = $dbh->prepare_cached($query, undef, 3))) { 68 | _bind_params($sth, @_); 69 | $sth->execute; 70 | } 71 | 1; 72 | }) { $errored = 1; $error = $@ } 73 | } 74 | 75 | die $error if $errored and !$cb; # bail out for errored "blocking" queries 76 | 77 | # We won't have a statement handle if prepare failed in a "non-blocking" 78 | # query or with RaiseError disabled 79 | my $results; 80 | if (defined $sth) { 81 | $results = $self->results_class->new(db => $self, sth => $sth); 82 | $results->{last_insert_id} = $dbh->{private_mojo_last_insert_id}; 83 | } 84 | 85 | return $results unless $cb; # blocking 86 | 87 | # Still blocking, but call the callback on the next tick 88 | $error = $dbh->err ? $dbh->errstr : $errored ? ($error || 'Error running SQLite query') : undef; 89 | require Mojo::IOLoop; 90 | Mojo::IOLoop->next_tick(sub { $self->$cb($error, $results) }); 91 | return $self; 92 | } 93 | 94 | sub query_p { 95 | my $self = shift; 96 | my $promise = Mojo::Promise->new; 97 | $self->query(@_ => sub { $_[1] ? $promise->reject($_[1]) : $promise->resolve($_[2]) }); 98 | return $promise; 99 | } 100 | 101 | sub tables { 102 | my @tables = shift->dbh->tables(undef, undef, undef, 'TABLE,VIEW,LOCAL TEMPORARY'); 103 | my %names; # Deduplicate returned temporary table indexes 104 | return [grep { !$names{$_}++ } @tables]; 105 | } 106 | 107 | sub _bind_params { 108 | my $sth = shift; 109 | return $sth unless @_; 110 | foreach my $i (0..$#_) { 111 | my $param = $_[$i]; 112 | if (ref $param eq 'HASH') { 113 | if (exists $param->{type} && exists $param->{value}) { 114 | $sth->bind_param($i+1, $param->{value}, $param->{type}); 115 | } elsif (exists $param->{json}) { 116 | $sth->bind_param($i+1, to_json($param->{json}), SQL_VARCHAR); 117 | } elsif (exists $param->{-json}) { 118 | $sth->bind_param($i+1, to_json($param->{-json}), SQL_VARCHAR); 119 | } else { 120 | croak qq{Unknown parameter hashref (no "type"/"value", "json" or "-json")}; 121 | } 122 | } else { 123 | $sth->bind_param($i+1, $param); 124 | } 125 | } 126 | return $sth; 127 | } 128 | 129 | 1; 130 | 131 | =encoding utf8 132 | 133 | =head1 NAME 134 | 135 | Mojo::SQLite::Database - Database 136 | 137 | =head1 SYNOPSIS 138 | 139 | use Mojo::SQLite::Database; 140 | 141 | my $db = Mojo::SQLite::Database->new(sqlite => $sql, dbh => $dbh); 142 | $db->query('select * from foo') 143 | ->hashes->map(sub { $_->{bar} })->join("\n")->say; 144 | 145 | =head1 DESCRIPTION 146 | 147 | L is a container for L database handles 148 | used by L. 149 | 150 | =head1 ATTRIBUTES 151 | 152 | L implements the following attributes. 153 | 154 | =head2 dbh 155 | 156 | my $dbh = $db->dbh; 157 | $db = $db->dbh($dbh); 158 | 159 | L database handle used for all queries. 160 | 161 | # Use DBI utility methods 162 | my $quoted = $db->dbh->quote_identifier('foo.bar'); 163 | 164 | =head2 results_class 165 | 166 | my $class = $db->results_class; 167 | $db = $db->results_class('MyApp::Results'); 168 | 169 | Class to be used by L, defaults to L. Note 170 | that this class needs to have already been loaded before L is called. 171 | 172 | =head2 sqlite 173 | 174 | my $sql = $db->sqlite; 175 | $db = $db->sqlite(Mojo::SQLite->new); 176 | 177 | L object this database belongs to. 178 | 179 | =head1 METHODS 180 | 181 | L inherits all methods from L and 182 | implements the following new ones. 183 | 184 | =head2 begin 185 | 186 | my $tx = $db->begin; 187 | my $tx = $db->begin('exclusive'); 188 | 189 | Begin transaction and return L object, which will 190 | automatically roll back the transaction unless 191 | L has been called before it is destroyed. 192 | 193 | # Insert rows in a transaction 194 | eval { 195 | my $tx = $db->begin; 196 | $db->insert('frameworks', {name => 'Catalyst'}); 197 | $db->insert('frameworks', {name => 'Mojolicious'}); 198 | $tx->commit; 199 | }; 200 | say $@ if $@; 201 | 202 | A transaction locking behavior of C, C, or C 203 | may optionally be passed; the default in L is currently 204 | C. See L and 205 | L for more details. 206 | 207 | =head2 delete 208 | 209 | my $results = $db->delete($table, \%where); 210 | 211 | Generate a C statement with L (usually an 212 | L object) and execute it with L. You can also 213 | append a callback for API compatibility with L; the query is still 214 | executed in a blocking manner. 215 | 216 | $db->delete(some_table => sub ($db, $err, $results) { 217 | ... 218 | }); 219 | Mojo::IOLoop->start unless Mojo::IOLoop->is_running; 220 | 221 | Use all the same argument variations you would pass to the C method of 222 | L. 223 | 224 | # "delete from some_table" 225 | $db->delete('some_table'); 226 | 227 | # "delete from some_table where foo = 'bar'" 228 | $db->delete('some_table', {foo => 'bar'}); 229 | 230 | # "delete from some_table where foo like '%test%'" 231 | $db->delete('some_table', {foo => {-like => '%test%'}}); 232 | 233 | =head2 delete_p 234 | 235 | my $promise = $db->delete_p($table, \%where, \%options); 236 | 237 | Same as L but returns a L object instead of accepting 238 | a callback. For API compatibility with L; the query is still executed 239 | in a blocking manner. 240 | 241 | $db->delete_p('some_table')->then(sub ($results) { 242 | ... 243 | })->catch(sub ($err) { 244 | ... 245 | })->wait; 246 | 247 | =head2 disconnect 248 | 249 | $db->disconnect; 250 | 251 | Disconnect L and prevent it from getting reused. 252 | 253 | =head2 insert 254 | 255 | my $results = $db->insert($table, \@values || \%fieldvals, \%options); 256 | 257 | Generate an C statement with L (usually an 258 | L object) and execute it with L. You can also 259 | append a callback for API compatibility with L; the query is still 260 | executed in a blocking manner. 261 | 262 | $db->insert(some_table => {foo => 'bar'} => sub ($db, $err, $results) { 263 | ... 264 | }); 265 | Mojo::IOLoop->start unless Mojo::IOLoop->is_running; 266 | 267 | Use all the same argument variations you would pass to the C method of 268 | L. 269 | 270 | # "insert into some_table (foo, baz) values ('bar', 'yada')" 271 | $db->insert('some_table', {foo => 'bar', baz => 'yada'}); 272 | 273 | =head2 insert_p 274 | 275 | my $promise = $db->insert_p($table, \@values || \%fieldvals, \%options); 276 | 277 | Same as L but returns a L object instead of accepting 278 | a callback. For API compatibility with L; the query is still executed 279 | in a blocking manner. 280 | 281 | $db->insert_p(some_table => {foo => 'bar'})->then(sub ($results) { 282 | ... 283 | })->catch(sub ($err) { 284 | ... 285 | })->wait; 286 | 287 | =head2 ping 288 | 289 | my $bool = $db->ping; 290 | 291 | Check database connection. 292 | 293 | =head2 query 294 | 295 | my $results = $db->query('select * from foo'); 296 | my $results = $db->query('insert into foo values (?, ?, ?)', @values); 297 | my $results = $db->query('select ? as img', {type => SQL_BLOB, value => slurp 'img.jpg'}); 298 | my $results = $db->query('select ? as foo', {json => {bar => 'baz'}}); 299 | 300 | Execute a blocking L 301 | statement and return a results object based on L (which is 302 | usually L) with the query results. The L 303 | statement handle will be automatically reused when it is not active anymore, to 304 | increase the performance of future queries. You can also append a callback for 305 | API compatibility with L; the query is still executed in a blocking 306 | manner. 307 | 308 | $db->query('insert into foo values (?, ?, ?)' => @values => sub ($db, $err, $results) { 309 | ... 310 | }); 311 | Mojo::IOLoop->start unless Mojo::IOLoop->is_running; 312 | 313 | Hash reference arguments containing C and C elements will use the 314 | specified bind type for the parameter, using types from L; 315 | see L and the subsequent section for more information. 316 | 317 | Hash reference arguments containing a value named C or C<-json> will be 318 | encoded to L with 319 | L. To accomplish the reverse, you can use the method 320 | L to decode JSON text fields to Perl values 321 | with L. 322 | 323 | # "I ♥ SQLite!" 324 | $db->query('select ? as foo', {json => {bar => 'I ♥ SQLite!'}}) 325 | ->expand(json => 'foo')->hash->{foo}{bar}; 326 | 327 | =head2 query_p 328 | 329 | my $promise = $db->query_p('SELECT * FROM foo'); 330 | 331 | Same as L but returns a L object instead of accepting 332 | a callback. For API compatibility with L; the query is still executed 333 | in a blocking manner. 334 | 335 | $db->query_p('INSERT INTO foo VALUES (?, ?, ?)' => @values)->then(sub ($results) { 336 | ... 337 | })->catch(sub ($err) { 338 | ... 339 | })->wait; 340 | 341 | =head2 select 342 | 343 | my $results = $db->select($source, $fields, $where, $order); 344 | 345 | Generate a C method of 356 | L. 357 | 358 | # "select * from some_table" 359 | $db->select('some_table'); 360 | 361 | # "select id, foo from some_table" 362 | $db->select('some_table', ['id', 'foo']); 363 | 364 | # "select * from some_table where foo = 'bar'" 365 | $db->select('some_table', undef, {foo => 'bar'}); 366 | 367 | # "select * from some_table where foo = 'bar' order by id desc" 368 | $db->select('some_table', undef, {foo => 'bar'}, {-desc => 'id'}); 369 | 370 | # "select * from some_table where foo like '%test%'" 371 | $db->select('some_table', undef, {foo => {-like => '%test%'}}); 372 | 373 | =head2 select_p 374 | 375 | my $promise = $db->select_p($source, $fields, $where, \%options); 376 | 377 | Same as L but returns a L object instead of accepting 378 | a callback. For API compatibility with L; the query is still executed 379 | in a blocking manner. 380 | 381 | $db->select_p(some_table => ['foo'] => {bar => 'yada'})->then(sub ($results) { 382 | ... 383 | })->catch(sub ($err) { 384 | ... 385 | })->wait; 386 | 387 | =head2 tables 388 | 389 | my $tables = $db->tables; 390 | 391 | Return table and view names for this database, that are visible to the current 392 | user and not internal, as an array reference. Names will be quoted and prefixed 393 | by a schema name of C<"main"> for standard tables, C<"temp"> for temporary 394 | tables, and the appropriate schema name for 395 | L. 396 | 397 | # Names of all tables 398 | say for @{$db->tables}; 399 | 400 | =head2 update 401 | 402 | my $results = $db->update($table, \%fieldvals, \%where); 403 | 404 | Generate an C statement with L (usually an 405 | L object) and execute it with L. You can also 406 | append a callback for API compatibility with L; the query is still 407 | executed in a blocking manner. 408 | 409 | $db->update(some_table => {foo => 'baz'} => {foo => 'bar'} => sub ($db, $err, $results) { 410 | ... 411 | }); 412 | Mojo::IOLoop->start unless Mojo::IOLoop->is_running; 413 | 414 | Use all the same argument variations you would pass to the C method of 415 | L. 416 | 417 | # "update some_table set foo = 'bar' where id = 23" 418 | $db->update('some_table', {foo => 'bar'}, {id => 23}); 419 | 420 | # "update some_table set foo = 'bar' where foo like '%test%'" 421 | $db->update('some_table', {foo => 'bar'}, {foo => {-like => '%test%'}}); 422 | 423 | =head2 update_p 424 | 425 | my $promise = $db->update_p($table, \%fieldvals, \%where, \%options); 426 | 427 | Same as L but returns a L object instead of accepting 428 | a callback. For API compatibility with L; the query is still executed 429 | in a blocking manner. 430 | 431 | $db->update_p(some_table => {foo => 'baz'} => {foo => 'bar'})->then(sub ($results) { 432 | ... 433 | })->catch(sub ($err) { 434 | ... 435 | })->wait; 436 | 437 | =head1 BUGS 438 | 439 | Report any issues on the public bugtracker. 440 | 441 | =head1 AUTHOR 442 | 443 | Dan Book, C 444 | 445 | =head1 COPYRIGHT AND LICENSE 446 | 447 | Copyright 2015, Dan Book. 448 | 449 | This library is free software; you may redistribute it and/or modify it under 450 | the terms of the Artistic License version 2.0. 451 | 452 | =head1 SEE ALSO 453 | 454 | L 455 | -------------------------------------------------------------------------------- /lib/Mojo/SQLite/Migrations.pm: -------------------------------------------------------------------------------- 1 | package Mojo::SQLite::Migrations; 2 | use Mojo::Base -base; 3 | 4 | use Carp 'croak'; 5 | use Mojo::File 'path'; 6 | use Mojo::Loader 'data_section'; 7 | use Mojo::Util 'decode'; 8 | 9 | use constant DEBUG => $ENV{MOJO_MIGRATIONS_DEBUG} || 0; 10 | 11 | our $VERSION = '3.010'; 12 | 13 | has name => 'migrations'; 14 | has sqlite => undef, weak => 1; 15 | 16 | sub active { $_[0]->_active($_[0]->sqlite->db) } 17 | 18 | sub from_data { 19 | my ($self, $class, $name) = @_; 20 | return $self->from_string( 21 | data_section($class //= caller, $name // $self->name)); 22 | } 23 | 24 | sub from_file { shift->from_string(decode 'UTF-8', path(pop)->slurp) } 25 | 26 | sub from_string { 27 | my ($self, $sql) = @_; 28 | 29 | my ($version, $way); 30 | my $migrations = $self->{migrations} = {up => {}, down => {}}; 31 | for my $line (split "\n", $sql // '') { 32 | ($version, $way) = ($1, lc $2) if $line =~ /^\s*--\s*(\d+)\s*(up|down)/i; 33 | $migrations->{$way}{$version} .= "$line\n" if $version; 34 | } 35 | 36 | return $self; 37 | } 38 | 39 | sub latest { 40 | (sort { $a <=> $b } keys %{shift->{migrations}{up}})[-1] || 0; 41 | } 42 | 43 | sub migrate { 44 | my ($self, $target) = @_; 45 | 46 | # Unknown version 47 | my $latest = $self->latest; 48 | $target //= $latest; 49 | my ($up, $down) = @{$self->{migrations}}{qw(up down)}; 50 | croak "Version $target has no migration" if $target != 0 && !$up->{$target}; 51 | 52 | # Already the right version (make sure migrations table exists) 53 | my $db = $self->sqlite->db; 54 | return $self if $self->_active($db, 0) == $target; 55 | 56 | # Lock migrations table and check version again 57 | my $tx = $db->begin('exclusive'); 58 | return $self if (my $active = $self->_active($db, 1)) == $target; 59 | 60 | # Newer version 61 | croak "Active version $active is greater than the latest version $latest" 62 | if $active > $latest; 63 | 64 | my $query = $self->sql_for($active, $target); 65 | warn "-- Migrate ($active -> $target)\n$query\n" if DEBUG; 66 | local $db->dbh->{sqlite_allow_multiple_statements} = 1; 67 | 68 | # Disable update hook during migrations 69 | my $hook = $db->dbh->sqlite_update_hook(undef); 70 | 71 | # Catch the error so we can croak it 72 | my ($errored, $error, $result); 73 | { 74 | local $@; 75 | eval { $result = $db->dbh->do($query); 1 } or $errored = 1; 76 | $error = $@ if $errored; 77 | } 78 | 79 | # Re-enable update hook 80 | $db->dbh->sqlite_update_hook($hook); 81 | 82 | croak $error if $errored; 83 | return $self unless defined $result; # RaiseError disabled 84 | 85 | $db->query('update mojo_migrations set version = ? where name = ?', 86 | $target, $self->name) and $tx->commit; 87 | 88 | return $self; 89 | } 90 | 91 | sub sql_for { 92 | my ($self, $from, $to) = @_; 93 | 94 | # Up 95 | my ($up, $down) = @{$self->{migrations}}{qw(up down)}; 96 | if ($from < $to) { 97 | my @up = grep { $_ <= $to && $_ > $from } keys %$up; 98 | return join '', @$up{sort { $a <=> $b } @up}; 99 | } 100 | 101 | # Down 102 | my @down = grep { $_ > $to && $_ <= $from } keys %$down; 103 | return join '', @$down{reverse sort { $a <=> $b } @down}; 104 | } 105 | 106 | sub _active { 107 | my ($self, $db, $create) = @_; 108 | 109 | my $name = $self->name; 110 | my $results; 111 | { 112 | local $db->dbh->{RaiseError} = 0; 113 | my $query = 'select version from mojo_migrations where name = ?'; 114 | $results = $db->query($query, $name); 115 | } 116 | my $next = $results ? $results->array : undef; 117 | if ($next || !$create) { return $next->[0] || 0 } 118 | 119 | $db->query( 120 | 'create table if not exists mojo_migrations ( 121 | name text not null primary key, 122 | version integer not null check (version >= 0) 123 | )' 124 | ) if !$results or $results->sth->err; 125 | $db->query('insert into mojo_migrations values (?, ?)', $name, 0); 126 | 127 | return 0; 128 | } 129 | 130 | 1; 131 | 132 | =encoding utf8 133 | 134 | =head1 NAME 135 | 136 | Mojo::SQLite::Migrations - Migrations 137 | 138 | =head1 SYNOPSIS 139 | 140 | use Mojo::SQLite::Migrations; 141 | 142 | my $migrations = Mojo::SQLite::Migrations->new(sqlite => $sql); 143 | $migrations->from_file('/home/dbook/migrations.sql')->migrate; 144 | 145 | =head1 DESCRIPTION 146 | 147 | L is used by L to allow database 148 | schemas to evolve easily over time. A migration file is just a collection of 149 | sql blocks, with one or more statements, separated by comments of the form 150 | C<-- VERSION UP/DOWN>. 151 | 152 | -- 1 up 153 | create table messages (message text); 154 | insert into messages values ('I ♥ Mojolicious!'); 155 | -- 1 down 156 | drop table messages; 157 | 158 | -- 2 up (...you can comment freely here...) 159 | create table stuff (whatever integer); 160 | -- 2 down 161 | drop table stuff; 162 | 163 | The idea is to let you migrate from any version, to any version, up and down. 164 | Migrations are very safe, because they are performed in transactions and only 165 | one can be performed at a time. If a single statement fails, the whole 166 | migration will fail and get rolled back. Every set of migrations has a 167 | L, which is stored together with the currently active version in an 168 | automatically created table named C. 169 | 170 | =head1 ATTRIBUTES 171 | 172 | L implements the following attributes. 173 | 174 | =head2 name 175 | 176 | my $name = $migrations->name; 177 | $migrations = $migrations->name('foo'); 178 | 179 | Name for this set of migrations, defaults to C. 180 | 181 | =head2 sqlite 182 | 183 | my $sql = $migrations->sqlite; 184 | $migrations = $migrations->sqlite(Mojo::SQLite->new); 185 | 186 | L object these migrations belong to. Note that this attribute is 187 | weakened. 188 | 189 | =head1 METHODS 190 | 191 | L inherits all methods from L and 192 | implements the following new ones. 193 | 194 | =head2 active 195 | 196 | my $version = $migrations->active; 197 | 198 | Currently active version. 199 | 200 | =head2 from_data 201 | 202 | $migrations = $migrations->from_data; 203 | $migrations = $migrations->from_data('main'); 204 | $migrations = $migrations->from_data('main', 'file_name'); 205 | 206 | Extract migrations from a file in the DATA section of a class with 207 | L, defaults to using the caller class and 208 | L. 209 | 210 | __DATA__ 211 | @@ migrations 212 | -- 1 up 213 | create table messages (message text); 214 | insert into messages values ('I ♥ Mojolicious!'); 215 | -- 1 down 216 | drop table messages; 217 | 218 | =head2 from_file 219 | 220 | $migrations = $migrations->from_file('/home/dbook/migrations.sql'); 221 | 222 | Extract migrations from a file. 223 | 224 | =head2 from_string 225 | 226 | $migrations = $migrations->from_string( 227 | '-- 1 up 228 | create table foo (bar integer); 229 | -- 1 down 230 | drop table foo;' 231 | ); 232 | 233 | Extract migrations from string. 234 | 235 | =head2 latest 236 | 237 | my $version = $migrations->latest; 238 | 239 | Latest version available. 240 | 241 | =head2 migrate 242 | 243 | $migrations = $migrations->migrate; 244 | $migrations = $migrations->migrate(3); 245 | 246 | Migrate from L to a different version, up or down, defaults to using 247 | L. All version numbers need to be positive, with version C<0> 248 | representing an empty database. 249 | 250 | # Reset database 251 | $migrations->migrate(0)->migrate; 252 | 253 | =head2 sql_for 254 | 255 | my $sql = $migrations->sql_for(5, 10); 256 | 257 | Get SQL to migrate from one version to another, up or down. 258 | 259 | =head1 DEBUGGING 260 | 261 | You can set the C environment variable to get some 262 | advanced diagnostics information printed to C. 263 | 264 | MOJO_MIGRATIONS_DEBUG=1 265 | 266 | =head1 BUGS 267 | 268 | Report any issues on the public bugtracker. 269 | 270 | =head1 AUTHOR 271 | 272 | Dan Book, C 273 | 274 | =head1 COPYRIGHT AND LICENSE 275 | 276 | Copyright 2015, Dan Book. 277 | 278 | This library is free software; you may redistribute it and/or modify it under 279 | the terms of the Artistic License version 2.0. 280 | 281 | =head1 SEE ALSO 282 | 283 | L 284 | -------------------------------------------------------------------------------- /lib/Mojo/SQLite/PubSub.pm: -------------------------------------------------------------------------------- 1 | package Mojo::SQLite::PubSub; 2 | use Mojo::Base -strict; 3 | 4 | use Mojo::Util 'deprecated'; 5 | 6 | our $VERSION = '3.010'; 7 | 8 | deprecated 'Mojo::SQLite::PubSub is deprecated and should no longer be used'; 9 | 10 | 1; 11 | 12 | =encoding utf8 13 | 14 | =head1 NAME 15 | 16 | Mojo::SQLite::PubSub - (DEPRECATED) Publish/Subscribe 17 | 18 | =head1 DESCRIPTION 19 | 20 | L is DEPRECATED and now an empty package. It was 21 | originally written as a toy following the API of L, but as 22 | SQLite is serverless and has no ability to notify clients, it is not possible 23 | to implement an efficient pubsub system as in for example PostgreSQL, Redis, or 24 | websockets. Consider instead using the pubsub facilities of L, 25 | L, or L. 26 | 27 | =head1 SEE ALSO 28 | 29 | L, L, L 30 | 31 | =for Pod::Coverage *EVERYTHING* 32 | -------------------------------------------------------------------------------- /lib/Mojo/SQLite/Results.pm: -------------------------------------------------------------------------------- 1 | package Mojo::SQLite::Results; 2 | use Mojo::Base -base; 3 | 4 | use Mojo::Collection; 5 | use Mojo::JSON 'from_json'; 6 | use Mojo::Util 'tablify'; 7 | 8 | our $VERSION = '3.010'; 9 | 10 | has [qw(db sth)]; 11 | 12 | sub new { 13 | my $self = shift->SUPER::new(@_); 14 | ($self->{sth}{private_mojo_refcount} //= 0)++; 15 | return $self; 16 | } 17 | 18 | sub DESTROY { 19 | my $self = shift; 20 | return() unless my $sth = $self->{sth}; 21 | $sth->finish unless --$sth->{private_mojo_refcount}; 22 | } 23 | 24 | sub array { ($_[0]->_expand($_[0]->sth->fetchrow_arrayref))[0] } 25 | 26 | sub arrays { _collect($_[0]->_expand(@{$_[0]->sth->fetchall_arrayref})) } 27 | 28 | sub columns { shift->sth->{NAME} } 29 | 30 | sub expand { 31 | my ($self, %expands) = @_; 32 | for my $type (keys %expands) { 33 | my @cols = ref $expands{$type} eq 'ARRAY' ? @{$expands{$type}} : $expands{$type}; 34 | ++$self->{expand}{$type}{$_} for @cols; 35 | } 36 | return $self; 37 | } 38 | 39 | sub finish { shift->sth->finish } 40 | 41 | sub hash { ($_[0]->_expand($_[0]->sth->fetchrow_hashref))[0] } 42 | 43 | sub hashes { _collect($_[0]->_expand(@{$_[0]->sth->fetchall_arrayref({})})) } 44 | 45 | sub last_insert_id { shift->{last_insert_id} // 0 } 46 | 47 | sub rows { shift->sth->rows } 48 | 49 | sub text { tablify shift->arrays } 50 | 51 | sub _collect { Mojo::Collection->new(@_) } 52 | 53 | sub _expand { 54 | my ($self, @rows) = @_; 55 | 56 | return @rows unless $self->{expand} and $rows[0]; 57 | 58 | if (ref $rows[0] eq 'HASH') { 59 | my @json_names = keys %{$self->{expand}{json}}; 60 | for my $r (@rows) { $r->{$_} = from_json $r->{$_} for grep { $r->{$_} } @json_names } 61 | } else { 62 | my $cols = $self->columns; 63 | my @json_idxs = grep { $self->{expand}{json}{$cols->[$_]} } 0..$#$cols; 64 | for my $r (@rows) { $r->[$_] = from_json $r->[$_] for grep { $r->[$_] } @json_idxs } 65 | } 66 | 67 | return @rows; 68 | } 69 | 70 | 1; 71 | 72 | =head1 NAME 73 | 74 | Mojo::SQLite::Results - Results 75 | 76 | =head1 SYNOPSIS 77 | 78 | use Mojo::SQLite::Results; 79 | 80 | my $results = Mojo::SQLite::Results->new(sth => $sth); 81 | $results->hashes->map(sub { $_->{foo} })->shuffle->join("\n")->say; 82 | 83 | =head1 DESCRIPTION 84 | 85 | L is a container for L statement handles 86 | used by L. 87 | 88 | =head1 ATTRIBUTES 89 | 90 | L implements the following attributes. 91 | 92 | =head2 db 93 | 94 | my $db = $results->db; 95 | $results = $results->db(Mojo::SQLite::Database->new); 96 | 97 | L object these results belong to. 98 | 99 | =head2 sth 100 | 101 | my $sth = $results->sth; 102 | $results = $results->sth($sth); 103 | 104 | L statement handle results are fetched from. 105 | 106 | =head1 METHODS 107 | 108 | L inherits all methods from L and implements 109 | the following new ones. 110 | 111 | =head2 new 112 | 113 | my $results = Mojo::SQLite::Results->new; 114 | my $results = Mojo::SQLite::Results->new(sth => $sth); 115 | my $results = Mojo::SQLite::Results->new({sth => $sth}); 116 | 117 | Construct a new L object. 118 | 119 | =head2 array 120 | 121 | my $array = $results->array; 122 | 123 | Fetch next row from L and return it as an array reference. Note that 124 | L needs to be called if you are not fetching all the possible rows. 125 | 126 | # Process one row at a time 127 | while (my $next = $results->array) { 128 | say $next->[3]; 129 | } 130 | 131 | =head2 arrays 132 | 133 | my $collection = $results->arrays; 134 | 135 | Fetch all rows from L and return them as a L object 136 | containing array references. 137 | 138 | # Process all rows at once 139 | say $results->arrays->reduce(sub { $a + $b->[3] }, 0); 140 | 141 | =head2 columns 142 | 143 | my $columns = $results->columns; 144 | 145 | Return column names as an array reference. 146 | 147 | # Names of all columns 148 | say for @{$results->columns}; 149 | 150 | =head2 expand 151 | 152 | $results = $results->expand(json => 'some_json'); 153 | $results = $results->expand(json => ['some_json','other_json']); 154 | 155 | Decode specified fields from a particular format to Perl values for all rows. 156 | Currently only the C text format is recognized. The names must exactly 157 | match the column names as returned by L; it is recommended to use 158 | explicit aliases in the query for consistent column names. 159 | 160 | # Expand JSON 161 | $results->expand(json => 'json_field')->hashes->map(sub { $_->{foo}{bar} })->join("\n")->say; 162 | 163 | =head2 finish 164 | 165 | $results->finish; 166 | 167 | Indicate that you are finished with L and will not be fetching all the 168 | remaining rows. 169 | 170 | =head2 hash 171 | 172 | my $hash = $results->hash; 173 | 174 | Fetch next row from L and return it as a hash reference. Note that 175 | L needs to be called if you are not fetching all the possible rows. 176 | 177 | # Process one row at a time 178 | while (my $next = $results->hash) { 179 | say $next->{money}; 180 | } 181 | 182 | =head2 hashes 183 | 184 | my $collection = $results->hashes; 185 | 186 | Fetch all rows from L and return them as a L object 187 | containing hash references. 188 | 189 | # Process all rows at once 190 | say $results->hashes->reduce(sub { $a + $b->{money} }, 0); 191 | 192 | =head2 last_insert_id 193 | 194 | my $id = $results->last_insert_id; 195 | 196 | Returns the L of the 197 | most recent successful C. 198 | 199 | =head2 rows 200 | 201 | my $num = $results->rows; 202 | 203 | Number of rows. Note that for C