diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 7226f79..0000000 --- a/.gitignore +++ /dev/null @@ -1,15 +0,0 @@ -/target -/classes -/checkouts -pom.xml -pom.xml.asc -*.swp -*.jar -*.class -/.lein-* -/.nrepl-port -*.db -/doc/dist -\#*\# -.\#* -*~ diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index d4698e0..0000000 --- a/.travis.yml +++ /dev/null @@ -1,16 +0,0 @@ -language: clojure -lein: lein2 - -addons: - postgresql: 9.3 - -services: - - postgresql - -before_script: - - createdb test - -jdk: - - openjdk7 - - oraclejdk7 - - oraclejdk8 \ No newline at end of file diff --git a/CHANGES.adoc b/CHANGES.adoc deleted file mode 100644 index 4f2c11d..0000000 --- a/CHANGES.adoc +++ /dev/null @@ -1,289 +0,0 @@ -= Changelog - -== Version 0.9.0 - -Date: 2016-05-20 - -- Fix statement leakage on `fetch` functions. -- Remove default `fetch-size` parameter value. - - -== Version 0.8.0 - -Date: 2016-04-29 - -- Add support for `:returning` option for `execute`. -- Dependencies updates. -- Bug fixes. - - -== Version 0.7.0 - -Date: 2016-03-20 - -- Upgrade default clojure version to 1.8.0 -- Upgrade dependencies. - - -== Version 0.6.1 - -Date: 2015-08-26 - -- Remove potemkin dependency. - - -== Version 0.6.0 - -Date: 2015-08-14 - -- Remove deprecated code. -- Update dependencies. - - -== Version 0.5.1 - -Date: 2015-04-28 - -- Update dependencies versions. -- Minor fix on tests. - - -== Version 0.5.0 - -Date: 2015-03-29 - -This is an other release focused in simplify the main api. It deprecates the old api but -maintains full backward compatibility until 0.6.0. - -- New `execute` function in terms of abstraction. This method replaces the `execute!` - and `execute-prepared!` functions. -- New `fetch` function in terms of abstraction. This method replaces the `query` - function. -- New transaction api based in new `atomic` macro and `atomic-apply` function located - now on core ns. The old functions (`call-in-transaction` and `with-transaction`) are - still available in now deprecated `jdbc.transaction` namespace. -- New name to lazy-query: fetch-lazy. - -The major change of this release is make all query/statement executin functions works -in terms of generic abstractions defined as protocols. The api is highly influenced -by `suricatta` api, making it mostly compatible with it. - - -== Version 0.4.0 - -Date: 2015-02-07 - -- Fix wrong parsing boolean values in MSSQL (thanks to @vbedegi). - - -== Version 0.4.0-beta1 - -Date: 2015-01-11 - - -This release is focused to simplify the code and use more clojure native constructions -like `with-open` instead of own defined macros. - -Relevant changes: - -- Connection is not longer defined as defrecord. It breaks apps that relies on hash-map - interface of the connection. Now the connection is more extensible and it is defined - in terms of protocols. -- `with-connection` macro is marked as deprecated, now the recommended way to handle - connection resources is using clojure builtin `with-open` macro. -- The `make-query` function is replaced by `lazy-query`. It is not simple rename. - `lazy-query` function returns some kind of cursor object. That cursor allows create an - arbitrary number of lazyseqs (with new `cursor->lazyseq function) executing a query - using server side cursors if them are available. -- Removed `with-query` macro (because it depends on now also removed `make-query` function) - and the behavior is replaced by `lazy-query`. - -Other changes: - -- Set more consistent name to protocol and protocol method that responsible for create preapare - statements: `ISQLStatement/normalize` -> `IPreparedStatementConstructor/prepared-statement` -- Make connection constructor extensible and implemented as `IConnectionConstructor` protocol. -- The function responsible of creating a connection, is renamed: `make-connection` -> `connection` - (conserving an alias for backward compatibility). -- Remove the `:connection-uri` option because is almost same thing that uri. -- Deprecated dbspecs with datasource as key. Now datasource instances can be passed directly to - `connection` function without wrapping it in a hash-map. -- The `connection` function now accepts a optional second parameter in case when dbspec is not - in clojure hash-map format. -- Deprecated own connection pool extensions. Because they are less flexible that creating own - datasource instance. The documentation is updated with examples of how to create datasource - instances for all previously supported connection pool implementations. -- Move the rollback flag check to transaction strategy, instead of having it hardcoded in the - call-in-transaction function. This change simplifies a little bit creating own transaction - strategies implementations, removing unnecesary accidental complexity. - - -== Version 0.3.2 - -Date: 2014-11-20 - -- Set default java version to java7 - - -== Version 0.3.1 - -Date: 2014-10-28 - -- Add some missing typehints. -- Add documentation for new connection pool adapter: hikaricp -- Remove useless usage of apply on transaction macro. -- Restructure documentaion related to connection pool adapters. -- Move tests related to connection pool to each adapter repository. - - -== Version 0.3.0 - -Date: 2014-10-11 - -- Remove jdbc.types.connection namespace (records now on jdbc.types ns). -- Remove jdbc.types.resultset namespace (records now on jdbc.types ns). -- New jdbc.meta ns with connection metadata access functions (moved from jdbc.types.connection). -- Type hints for almost all code. -- Split types declaration, protocol defintion and implementation in different namespaces. -- Normalize connection pool parameters, making them more plugable. -- Improve documentation. - - -== Version 0.2.2 - -Date: 2014-07-18 - -- Fixed bug related to max-rows parameter on make-prepared-statement -- Fixed bug related to fetch-size that it was ignored. - - -== Version 0.2.1 - -Date: 2014-06-29 - -- `execute-prepared!` now accepts parametrized sql like other `query` methods. -- Minor fixes. - - -== Version 0.2.0b3 - -Date: 2014-06-15 - -- Move jdbc namespace vars to jdbc.core. potemkin is used for conserve - backward compatibility but it will be removed in 0.3 - - -== Version 0.2.0b2 - -Date: 2014-06-08 - -- Add query-first helper function. - - -== Version 0.2.0b1 - -Date: 2014-06-04 - -- Add optional options parameter to `with-transaction` macro. -- Improved `execute-prepared!` function. Now accepts self prepared statements like `query`. -- Improved `make-prepared-statement` function. Now accept string and parametrized vector - as sql value. -- Pretty dbspec format. -- Read only connections. -- Set schema to connection. - - -== Version 0.1.1 - -Date: 2014-04-06 - -- Connection pooling is splited to separate module (it preserves same api, but should be - declared as additional dependency if you want use it). -- Set clojure 1.6 as default clojure version. -- Add apache-commonds dbcp 2.0 connection pool support. -- Breaking change: rename transaction strategy methods to: begin! rollback! commit! -- Breaking change: call-in-transaction now accepts options map insted of named parameters. -- Fix bugs on marking transaction rollback-only. -- Allow set isolation level for transaction. -- Allow set read-only transaction. - -== Version 0.1-rc1 - -Date: 2014-02-16 - -- jdk6 support added (`java.lang.AutoCloseable` interface is replaced - with `java.io.Closeable` interface) -- Add ISQLStatement protocol for make easy extend query (and friends) function parameter. -- Performance improvements (with micro bench suite) -- Changed syntax for `with-connection` for more idiomatic way (previous behavior - mantained for backward compatibility). - -== Version 0.1-beta5 - -Date: 2014-01-21 - -Note: this should be a last beta release. - -- Query functions (make-query, query and with-query) now accepts prepared statements. -- Add ability to extend all behavior between some type is set as parameter to prepared statement - and retrieved from resultset thanks to `ISQLType` and `ISQLResultSetReadColumn` (this allows - extend not default types, making it compatible for pass as paramater to jdbc and extend sql types for - automatically convert them to custom types when are retrieved from resultset). -- Refactored jdbc.types namespace (rename QueryResult to ResultSet and put each type in their own - namespace and add additional util functions for each type). -- Move all transactions logic to separate namespace, removing it from a main jdbc namespace - (breaking change). -- Rename `execute-statement` to `execute-statement!` for more concise function naming. -- Remove untested and unused `execute-statement->query-result` function. - - -== Version 0.1-beta4 - -Date: 2013-12-14 - -- Now transaction management is extensible. ITransactionStrategy is exposed and - DefaultTransactionStrategy is a default implementation that cases with previous transaction - behavior. If you want other transaction strategy, just implement ITransactionStrategy protocol - and pass it to `call-in-transaction` function. -- Custom sql types now supported. Extend your type with ISQLType protocol and implement `as-sql-type` - function for it, that should return database compatible type. -- Backward incompatible change: `mark-as-rollback-only!`, `unmark-rollback-only!` and - `is-rollback-only?` are renamed to more concise names: `set-rollback!`, `unset-rollback!` - and `is-rollback-set?` -- Rollback behavior changed. Now rollback functions only affects a current transaction or - subtransaction and it never interferes in parent transactions. -- Ensuers inmutablity on connection instance on transaction blocks. Now transaction blocks has only one - clear defined side-effect: commit/rollback/setAutoCommit operations. Rollback flag is more limited - side-effect that only change state of connection for current transaction. -- Simplify isolation level setting. Now only can set isolation level on dbspec or on creating connection. - All global state is removed. - - -== Version 0.1-beta3 - -Date: 2013-12-08 - -- Minor code cleaning and function name consistency fixes. -- Expose more private functions as public. -- Fix wrong preconditions and some bugs introduced in previos version. -- Add more tests. - - -== Version 0.1-beta2 - -Date: 2013-11-25 - -- Remove some taken code from clojure.java.jdbc - that are licensed under epl. -- Add ability to set the isolation level. -- Add new `query` function. -- Change default behavior for querying a database: now the default - behavior is evaluate a request because of all jdbc implementation - retrieves all resulset in memory and use lazy-seq is totally useless. - - -== Version 0.1-beta1 - -Date: 2013-11-14 - -- Initial relase diff --git a/LICENSE b/LICENSE deleted file mode 100644 index d645695..0000000 --- a/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/LICENSE.epl b/LICENSE.epl deleted file mode 100644 index 0d786cd..0000000 --- a/LICENSE.epl +++ /dev/null @@ -1,182 +0,0 @@ -Eclipse Public License -v 1.0 - -THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC LICENSE ("AGREEMENT"). -ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS -AGREEMENT. - -1. DEFINITIONS - -"Contribution" means: - -a) in the case of the initial Contributor, the initial code and documentation distributed under -this Agreement, and - -b) in the case of each subsequent Contributor: - -i) changes to the Program, and - -ii) additions to the Program; - -where such changes and/or additions to the Program originate from and are distributed by that -particular Contributor. A Contribution 'originates' from a Contributor if it was added to the -Program by such Contributor itself or anyone acting on such Contributor's behalf. Contributions do -not include additions to the Program which: (i) are separate modules of software distributed in -conjunction with the Program under their own license agreement, and (ii) are not derivative works -of the Program. - -"Contributor" means any person or entity that distributes the Program. - -"Licensed Patents " mean patent claims licensable by a Contributor which are necessarily infringed -by the use or sale of its Contribution alone or when combined with the Program. - -"Program" means the Contributions distributed in accordance with this Agreement. - -"Recipient" means anyone who receives the Program under this Agreement, including all Contributors. - -2. GRANT OF RIGHTS - -a) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a -non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare derivative works of, -publicly display, publicly perform, distribute and sublicense the Contribution of such Contributor, -if any, and such derivative works, in source code and object code form. - -b) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a -non-exclusive, worldwide, royalty-free patent license under Licensed Patents to make, use, sell, -offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in -source code and object code form. This patent license shall apply to the combination of the -Contribution and the Program if, at the time the Contribution is added by the Contributor, such -addition of the Contribution causes such combination to be covered by the Licensed Patents. The -patent license shall not apply to any other combinations which include the Contribution. No -hardware per se is licensed hereunder. - -c) Recipient understands that although each Contributor grants the licenses to its Contributions -set forth herein, no assurances are provided by any Contributor that the Program does not infringe -the patent or other intellectual property rights of any other entity. Each Contributor disclaims -any liability to Recipient for claims brought by any other entity based on infringement of -intellectual property rights or otherwise. As a condition to exercising the rights and licenses -granted hereunder, each Recipient hereby assumes sole responsibility to secure any other -intellectual property rights needed, if any. For example, if a third party patent license is -required to allow Recipient to distribute the Program, it is Recipient's responsibility to acquire -that license before distributing the Program. - -d) Each Contributor represents that to its knowledge it has sufficient copyright rights in its -Contribution, if any, to grant the copyright license set forth in this Agreement. - -3. REQUIREMENTS - -A Contributor may choose to distribute the Program in object code form under its own license -agreement, provided that: - -a) it complies with the terms and conditions of this Agreement; and - -b) its license agreement: - -i) effectively disclaims on behalf of all Contributors all warranties and conditions, express and -implied, including warranties or conditions of title and non-infringement, and implied warranties -or conditions of merchantability and fitness for a particular purpose; - -ii) effectively excludes on behalf of all Contributors all liability for damages, including direct, -indirect, special, incidental and consequential damages, such as lost profits; - -iii) states that any provisions which differ from this Agreement are offered by that Contributor -alone and not by any other party; and - -iv) states that source code for the Program is available from such Contributor, and informs -licensees how to obtain it in a reasonable manner on or through a medium customarily used for -software exchange. - -When the Program is made available in source code form: - -a) it must be made available under this Agreement; and - -b) a copy of this Agreement must be included with each copy of the Program. - -Contributors may not remove or alter any copyright notices contained within the Program. - -Each Contributor must identify itself as the originator of its Contribution, if any, in a manner -that reasonably allows subsequent Recipients to identify the originator of the Contribution. - -4. COMMERCIAL DISTRIBUTION - -Commercial distributors of software may accept certain responsibilities with respect to end users, -business partners and the like. While this license is intended to facilitate the commercial use of -the Program, the Contributor who includes the Program in a commercial product offering should do so -in a manner which does not create potential liability for other Contributors. Therefore, if a -Contributor includes the Program in a commercial product offering, such Contributor ("Commercial -Contributor") hereby agrees to defend and indemnify every other Contributor ("Indemnified -Contributor") against any losses, damages and costs (collectively "Losses") arising from claims, -lawsuits and other legal actions brought by a third party against the Indemnified Contributor to -the extent caused by the acts or omissions of such Commercial Contributor in connection with its -distribution of the Program in a commercial product offering. The obligations in this section do -not apply to any claims or Losses relating to any actual or alleged intellectual property -infringement. In order to qualify, an Indemnified Contributor must: a) promptly notify the -Commercial Contributor in writing of such claim, and b) allow the Commercial Contributor to -control, and cooperate with the Commercial Contributor in, the defense and any related settlement -negotiations. The Indemnified Contributor may participate in any such claim at its own expense. - -For example, a Contributor might include the Program in a commercial product offering, Product X. -That Contributor is then a Commercial Contributor. If that Commercial Contributor then makes -performance claims, or offers warranties related to Product X, those performance claims and -warranties are such Commercial Contributor's responsibility alone. Under this section, the -Commercial Contributor would have to defend claims against the other Contributors related to those -performance claims and warranties, and if a court requires any other Contributor to pay any damages -as a result, the Commercial Contributor must pay those damages. - -5. NO WARRANTY - -EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT -LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR -A PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the appropriateness of -using and distributing the Program and assumes all risks associated with its exercise of rights -under this Agreement , including but not limited to the risks and costs of program errors, -compliance with applicable laws, damage to or loss of data, programs or equipment, and -unavailability or interruption of operations. - -6. DISCLAIMER OF LIABILITY - -EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE -ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER -IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT -OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF -ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - -7. GENERAL - -If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not -affect the validity or enforceability of the remainder of the terms of this Agreement, and without -further action by the parties hereto, such provision shall be reformed to the minimum extent -necessary to make such provision valid and enforceable. - -If Recipient institutes patent litigation against any entity (including a cross-claim or -counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program -with other software or hardware) infringes such Recipient's patent(s), then such Recipient's rights -granted under Section 2(b) shall terminate as of the date such litigation is filed. - -All Recipient's rights under this Agreement shall terminate if it fails to comply with any of the -material terms or conditions of this Agreement and does not cure such failure in a reasonable -period of time after becoming aware of such noncompliance. If all Recipient's rights under this -Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as -reasonably practicable. However, Recipient's obligations under this Agreement and any licenses -granted by Recipient relating to the Program shall continue and survive. - -Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid -inconsistency the Agreement is copyrighted and may only be modified in the following manner. The -Agreement Steward reserves the right to publish new versions (including revisions) of this -Agreement from time to time. No one other than the Agreement Steward has the right to modify this -Agreement. The Eclipse Foundation is the initial Agreement Steward. The Eclipse Foundation may -assign the responsibility to serve as the Agreement Steward to a suitable separate entity. Each new -version of the Agreement will be given a distinguishing version number. The Program (including -Contributions) may always be distributed subject to the version of the Agreement under which it was -received. In addition, after a new version of the Agreement is published, Contributor may elect to -distribute the Program (including its Contributions) under the new version. Except as expressly -stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the -intellectual property of any Contributor under this Agreement, whether expressly, by implication, -estoppel or otherwise. All rights in the Program not expressly granted under this Agreement are -reserved. - -This Agreement is governed by the laws of the State of New York and the intellectual property laws -of the United States of America. No party to this Agreement will bring a legal action under this -Agreement more than one year after the cause of action arose. Each party waives its rights to a -jury trial in any resulting litigation. diff --git a/README.md b/README.md deleted file mode 100644 index b971986..0000000 --- a/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# clojure.jdbc # - -A JDBC library for Clojure. - -[![Travis Badge](https://img.shields.io/travis/funcool/clojure.jdbc.svg?style=flat)](https://travis-ci.org/funcool/clojure.jdbc "Travis Badge") - - -[![Clojars Project](http://clojars.org/funcool/clojure.jdbc/latest-version.svg)](http://clojars.org/funcool/clojure.jdbc) - - -## Documentation ## - -- **Documentation:** http://funcool.github.io/clojure.jdbc/latest/ diff --git a/bench/jdbc/bench.clj b/bench/jdbc/bench.clj deleted file mode 100644 index 96f8795..0000000 --- a/bench/jdbc/bench.clj +++ /dev/null @@ -1,145 +0,0 @@ -(ns jdbc.bench - (:require [jdbc.core :as clojure.jdbc] - [jdbc.proto :as proto] - [jdbc.transaction :as tx] - [clojure.java.jdbc :as java.jdbc] - [criterium.core :refer [bench quick-bench]]) - (:import java.sql.Connection) - (:gen-class)) - -(def dbspec {:subprotocol "h2" - :subname "mem:"}) -(def sql "select * from system_range(0, 100);") - -(def ^:dynamic *iterations* 1000) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Benchmark 1 -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(defn bench-1-java-jdbc - [] - (println) - (println "Benchmark: One query without connection overhead.") - (println "Results for java.jdbc:") - - (java.jdbc/with-db-connection [conn dbspec] - (quick-bench - (dotimes [i *iterations*] - (java.jdbc/query conn sql))))) - -(defn bench-1-clojure-jdbc - [] - (println) - (println "Benchmark: One query without connection overhead.") - (println "Results for clojure.jdbc:") - - (with-open [conn (clojure.jdbc/connection dbspec)] - (quick-bench - (dotimes [i *iterations*] - (clojure.jdbc/fetch conn sql))))) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Benchmark 2 -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(defn bench-2-java-jdbc - [] - (println) - (println "Benchmark: One query with connection overhead.") - (println "Results for java.jdbc:") - - (quick-bench - (dotimes [i *iterations*] - (java.jdbc/with-db-connection [conn dbspec] - (java.jdbc/query conn sql))))) - -(defn bench-2-clojure-jdbc - [] - (println) - (println "Benchmark: One query with connection overhead.") - (println "Results for clojure.jdbc:") - - (quick-bench - (dotimes [i *iterations*] - (with-open [conn (clojure.jdbc/connection dbspec)] - (clojure.jdbc/fetch conn sql))))) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Benchmark 3 -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(def basic-tx-strategy - (reify - proto/ITransactionStrategy - (begin! [_ conn opts] - (let [^Connection rconn (proto/connection conn) - metadata (meta conn) - ^long depth (:depth-level metadata)] - (if depth - (with-meta conn - (assoc metadata :depth-level (inc depth))) - - (let [prev-autocommit (.getAutoCommit rconn)] - (.setAutoCommit rconn false) - (with-meta conn - (assoc metadata - :depth-level 0 - :prev-autocommit prev-autocommit)))))) - - (rollback! [_ conn opts] - (let [^Connection rconn (proto/connection conn) - metadata (meta conn) - ^long depth (:depth-level metadata)] - (when (= depth 0) - (.rollback rconn) - (.setAutoCommit rconn (:prev-autocommit metadata))))) - - (commit! [_ conn opts] - (let [^Connection rconn (proto/connection conn) - metadata (meta conn) - ^long depth (:depth-level metadata)] - (when (= depth 0) - (.commit rconn) - (.setAutoCommit rconn (:prev-autocommit metadata))))))) - -(defn bench-3-java-jdbc - [] - (println) - (println "Benchmark: Simple query in a transaction without connection overhead") - (println "Results for java.jdbc:") - - (java.jdbc/with-db-connection [conn dbspec] - (quick-bench - (dotimes [i *iterations*] - (java.jdbc/with-db-transaction [conn conn] - (java.jdbc/query conn sql)))))) - - -(defn bench-3-clojure-jdbc - [] - (println) - (println "Benchmark: Simple query in a transaction without connection overhead") - (println "Results for clojure.jdbc:") - - (with-open [conn (clojure.jdbc/connection (assoc dbspec :tx-strategy basic-tx-strategy))] - (quick-bench - (dotimes [i *iterations*] - (clojure.jdbc/atomic conn - (clojure.jdbc/fetch conn sql)))))) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Main -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(defn -main - [& args] - (bench-1-java-jdbc) - (bench-1-clojure-jdbc) - - (bench-2-java-jdbc) - (bench-2-clojure-jdbc) - - (bench-3-java-jdbc) - (bench-3-clojure-jdbc) -) diff --git a/doc/Makefile b/doc/Makefile deleted file mode 100644 index 90709bb..0000000 --- a/doc/Makefile +++ /dev/null @@ -1,13 +0,0 @@ -all: doc - -api: - cd .. - lein doc - -doc: - mkdir -p dist/latest/ - asciidoctor -a docinfo -a stylesheet! -o dist/latest/index.html content.adoc - -github: api doc - ghp-import -m "Generate documentation" -b gh-pages dist/ - git push origin gh-pages diff --git a/doc/content-docinfo.html b/doc/content-docinfo.html deleted file mode 100644 index a16e8f9..0000000 --- a/doc/content-docinfo.html +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/doc/content.adoc b/doc/content.adoc deleted file mode 100644 index fad9ddc..0000000 --- a/doc/content.adoc +++ /dev/null @@ -1,788 +0,0 @@ -= clojure.jdbc documentation -Andrey Antukh, -0.9.0 -:toc: left -:numbered: -:source-highlighter: pygments -:pygments-style: friendly -:sectlinks: -:idseparator: - -:idprefix: -:!numbered: - - -== Introduction - -_clojure.jdbc_ is a library for low level, jdbc-based database access. - - -link:api/index.html[Api reference documentation.] - - -=== Install - -The simplest way to use _clojure.jdbc_ in a clojure project, is by including it in the dependency -vector on your *_project.clj_* file: - -[source,clojure] ----- -[funcool/clojure.jdbc "0.9.0"] ----- - -You also need to add an entry for the java driver that you need. For instance, for `mysql`: - -[source, clojure] ---- -[mysql/mysql-connector-java "5.1.6"] ---- - -The project is tested under JDK7 and JDK8. - - -=== Project Maturity - -Since _clojure.jdbc_ is a young project there may be some API breakage. - - -== User Guide - -=== Connecting to database - -This section intends to explain everything that you should to know about how -connect to the database. - - -==== Connection parameters - -JDBC is the default Java abstraction/interface for SQL databases. It's like -the Python DB-API and similar abstractions in other languages. Clojure, as a -guest language on the JVM, benefits from having a good, well-tested abstraction -like that. - -Connection parameters are exposed in a simple hash-map and called *dbspec*. This is the simplest -and most idiomatic way in Clojure to define configuration parameters. - -.This is a default aspect of one dbspec. -[source,clojure] ----- -(def dbspec {:subprotocol "postgresql" - :subname "//localhost:5432/dbname" - :user "username" ;; Optional - :password "password"} ;; Optional ----- - -Also, _clojure.jdbc_ comes with alternative, more human comprehensible format, like this: - -.Pretty dbspec format -[source, clojure] ----- -(def dbspec {:vendor "postgresql" - :name "dbname" - :host "localhost" ;; Optional - :port 5432 ;; Optional - :user "username" ;; Optional - :password "password"}) ;; Optional ----- - -But it has some disadvantages: it does not supports all options of a default dbspec format. - -Also, *dbspec* can be represented as a URI. - -.Same as the previous example but using URI format. -[source,clojure] ----- -(def dbspec "postgresql://user:password@localhost:5432/dbname") ----- - -==== Creating a connection - -With _clojure.jdbc_ every function that interacts with a database explicitly requires -one connection instance as parameter (no dynamic vars are used for it). - -.Create one connection using `connection` function: -[source,clojure] ----- -(require '[jdbc.core :as jdbc]) - -(let [conn (jdbc/connection dbspec)] - (do-something-with conn) - (.close conn)) ----- - -As you can see in previous example, you should explicltly close connection for proper -resource management. You can use the `with-open` clojure macro for make code looks -more clean and idiomatic. - -[source,clojure] ----- -(with-open [conn (jdbc/connection dbspec)] - (do-something-with conn)) ----- - - -=== Execute Database Commands - -==== Execute Raw SQL Statements - -The simplest way to execute raw SQL is using the `execute` function. It requires -an active connection as the first parameter followed by SQL sentence: - -[source,clojure] ----- -(with-open [conn (jdbc/connection dbspec)] - (jdbc/execute conn "CREATE TABLE foo (id serial, name text);")) ----- - - -==== Execute Parametrized SQL Statements - -Raw SQL statements work well for creating tables and similar operations, but -when you need to insert some data, especially if the data comes from untrusted -sources. Using plain string concatenation is a very bat and unsafe approach. - -For safe parameter bindings, it there a *sqlvec* format that consists in a -vector with sql followed of parameters: - -.Example of sqlvec format -[source,clojure] ----- -["select * from foo where age < ?" 20] ----- - -Almost all functions in _clojure.jdbc_ accepts sqlvec as query parameter. -Let see an example using it in `execute` function: - -.Execute a simple insert SQL statement. -[source,clojure] ----- -(jdbc/execute conn ["insert into foo (name) values (?);" "Foo"])) ----- - - -==== Returning Inserted Keys - -In some circumstances, you want to use the "RETURNING id" or similar functionality on -your queries for getting the primary keys of newly inserted records. - -.Example executing query with "returning" statement -[source, clojure] ----- -(let [sql "insert into foo (name) values (?) returning id;" - res (jdbc/fetch conn [sql "Foo"])] - (println res)) - -;; This should print something like this to standard output: -[{:id 3}] ----- - - -=== Make Queries - -It is time to start executing queries and fetch results. For this purpose, -_clojure.jdbc_ exposes the `fetch` function: - -[source,clojure] ----- -(let [sql ["SELECT id, name FROM people WHERE age > ?", 2] - result (jdbc/fetch conn sql)] - (doseq [row result] - (println row)))) - -;; It should print somthing like this: -;; => {:id 1 :name "Foo"} -;; => {:id 2 :name "Bar"} ----- - -We are know that almost all function in _clojure.jdbc_ accepts a plain -sql and sqlvec as query parameter. But is not entirely true, it also -accepts as query parameter an instance of self crafted `PreparedStatement` -or anythig that implements the `ISQLStatement` protocol. - -.Example creating a prepared statement and executing it -[source, clojure] ----- -(let [stmt (jdbc/prepared-statement ["SELECT id, name FROM people WHERE age > ?", 2]) - result (jdbc/fetch conn stmt)] - (println "Result: " result)) ----- - -[NOTE] -==== -The `fetch` method seems useful in most cases but may not work well with -queries that returns a lot of results. For this purpose, cursor type queries exist -that are explained in the xref:cursor-queries[Advanced usage] section. -==== - - -=== Transactions - - -==== Getting Started with Transactions - -The most idiomatic way to wrap some code in a transaction, is by using the `atomic` -macro: - -[source,clojure] ----- -(jdbc/atomic conn - (do-thing-first conn) - (do-thing-second conn)) ----- - -[NOTE] -==== -_clojure.jdbc_ does not uses any dynamic thread-local vars to store the transaction state -of a connection. Instead of that, it overwrites the lexical scope value of `conn` with a new -connection that has transactional state. -==== - - -==== Low-level Transaction Primitives - -Behind the scenes of the `atomic` macro, _clojure.jdbc_ uses the `atomic-apply` function. - -Given an active connection as the first parameter and function that you want execute in a -transaction as the second parameter, it executes the function inside a database transaction. -The callback function should accept a connection as its first parameter. - -.Example executing a function in transaction context -[source,clojure] ----- -(jdbc/atomic-apply conn - (fn [conn] - (do-something-with conn))) ----- - -[NOTE] -==== -_clojure.jdbc_, in contrast to java.jdbc, handles nested transactions well. Thus making all -code wrapped in transaction blocks truly atomic independently of transaction nesting. - -If you want extend or change a default transaction strategy, see -xref:transaction-strategy[Transaction Strategy section]. -==== - - -==== Isolation Level - -_clojure.jdbc_ by default does nothing with the isolation level and keeps it to default values. - -.You can set the isolation level when creating a connection by specifying it in your dbspec. -[source,clojure] ----- -(def dbspec {:subprotocol "h2" - :subname "mem:" - :isolation-level :serializable}) - -(with-open [conn (jdbc/connection dbspec)] - ;; The just created connection has the isolation - ;; level set to :serializable - (do-things conn)) ----- - -An other way to set the isolation level is in moment of declaring a transaction, using -the `atomic-apply` function or `atomic` macro: - -[source, clojure] ----- -(jdbc/atomic-apply conn do-something {:isolation-level :serializable}) - -(jdbc/atomic conn {:isolation-level :serializable} - (do-something conn)) ----- - -This is a list of supported options: - -- `:read-uncommitted` - Set read uncommitted isolation level -- `:read-committed` - Set read committed isolation level -- `:repeatable-read` - Set repeatable reads isolation level -- `:serializable` - Set serializable isolation level -- `:none` - Use this option to indicate to _clojure.jdbc_ to do nothing and keep default behavior. - -You can read more about it on link:http://en.wikipedia.org/wiki/Isolation_(database_systems)[wikipedia]. - -WARNING: not all JDBC providers support the above isolation levels. - - -==== Read-Only Transactions - -In some circumstances, mainly when you are using the strictest isolation-level, you may want -to indicate to database that a query is actually read-only, allowing the database server to make some -optimizations for this operation. - -.You can set transaction read-only using transaction options -[source, clojure] ----- -(jdbc/atomic conn {:isolation-level :serializable - :read-only true} - (query-something conn)) ----- - - -== Advanced usage - -[[cursor-queries]] -=== Server Side Cursors - -By default, most JDBC drivers prefetch all results into memory make the use of lazy structures -totally useless for fetching data. Luckily, some databases implement server-side cursors that avoid -this behavior. - -If you have an extremely large resultset and you want retrieve it and process each item, this is -exactly what you need. - -For this purpose, _clojure.jdbc_ exposes the `fetch-lazy` function, that returns a some kind of -cursor instance. At the moment of creating cursor, no query is executed. - -The cursor can be used converting it into clojure lazyseq using `cursor->lazyseq` function: - -[source,clojure] ----- -(jdbc/atomic conn - (with-open [cursor (jdbc/fetch-lazy conn "SELECT id, name FROM people;")] - (doseq [row (jdbc/cursor->lazyseq cursor)] - (println row))) ----- - -In some databases, it requires that cursor should be evaluated in a context of one -transaction. - - -[[transaction-strategy]] -Transaction Strategy -~~~~~~~~~~~~~~~~~~~~ - -Transaction strategies in _clojure.jdbc_ are implemented using protocols having default -implementation explained in the previous sections. This approach allows an easy way to extend, -customize or completely change a transaction strategy for your application. - -If you want another strategy, you should create a new type and implement the -`ITransactionStrategy` protocol. - -.Sample dummy transaction strategy. -[source,clojure] ----- -(require '[jdbc.proto :as proto]) - -(def dummy-tx-strategy - (reify - proto/ITransactionStrategy - (begin! [_ conn opts] conn) - (rollback! [_ conn opts] conn) - (commit! [_ conn opts] conn))) ----- - - -_clojure.jdbc_ has different ways to specify that transaction strategy shouldbe used. The most -common is setting it in your dbspec: - -[source,clojure] ----- -(def dbspec {:subprotocol "postgresql" - :subname "//localhost:5432/dbname" - :tx-strategy dummy-tx-strategy}) -(with-open [conn (jdbc/connection dbspec)] - (jdbc/atomic conn - ;; In this transaction block, the dummy transaction - ;; strategy will be used. - (do-somthing conn))) ----- - -Internally, _clojure.jdbc_ maintains an instance of default transaction strategy stored -in a dynamic var. You can use the clojure facilities for alter that var for set -an other default transaction stragegy: - -.Overwritting with `alter-var-root` -[source, clojure] ----- -(alter-var-root #'jdbc/*default-tx-strategy* (fn [_] dummy-tx-strategy)) ----- - -.Overwritting it with dynamic scope -[source, clojure] ----- -(binding [jdbc/*default-tx-strategy* dummy-tx-strategy] - (some-func-that-uses-transactions)) ----- - - -=== Extend SQL Types - -If you want to extend some type/class to use it as JDBC parameter without explicit conversion -to an SQL-compatible type, you should extend your type with the `jdbc.proto/ISQLType` protocol. - -Here is an example which extends Java's String[] (string array) in order to pass it as -a query parameter that corresponds to PostgreSQL text array in the database: - -[source,clojure] ----- -(require '[jdbc.proto :as proto]) - -(extend-protocol ISQLType - ;; Obtain a class for string array - (class (into-array String [])) - - (set-stmt-parameter! [this conn stmt index] - (let [value (proto/as-sql-type this conn) - array (.createArrayOf conn "text" value)] - (.setArray stmt index array))) - - (as-sql-type [this conn] this)) ----- - -In this way you can pass a string array as a JDBC parameter that is automatically converted -to an SQL array and assigned properly in a prepared statement: - -[source,clojure] ----- -(with-open [conn (jdbc/connection pg-dbspec)] - (jdbc/execute conn "CREATE TABLE arrayfoo (id integer, data text[]);") - (let [mystringarray (into-array String ["foo" "bar"])] - (jdbc/execute conn ["INSERT INTO arrayfoo VALUES (?, ?);" 1 mystringarray]))) ----- - -_clojure.jdbc_ also exposes the `jdbc.proto/ISQLResultSetReadColumn` protocol that encapsulates -reverse conversions from SQL types to user-defined types. - -You can read more about that in this blog post: http://www.niwi.be/2014/04/13/postgresql-json-field-with-clojure-and-jdbc/ - - -[[connection-pool]] -== Connection pool - -DataSource is the preferd way to connect to the database in production enviroments, and -is usually used for implement connection pools. - -To make good use of resourses is much recommendable use some kind of connection pool -implementation. This can avoid continuosly creating and destroying connections, -that in the majority of time is a slow operation. - -Java ecosystem comes with various of it. This is a list of most used: - -- HikariCP: https://github.com/brettwooldridge/HikariCP -- c3p0: http://www.mchange.com/projects/c3p0/ -- Apache DBCP2: http://commons.apache.org/proper/commons-dbcp/ - -_clojure.jdbc_ is compatible with any other connection pool implemenetation, simply -pass a `javax.sql.DataSource` instance to `jdbc/connection` function. - - -=== c3p0 - -c3p0, a mature, highly concurrent JDBC connection pooling library for clojure.jdbc. - -.Dependency entry -[source, clojure] ----- -[com.mchange/c3p0 "0.9.5"] ----- - -In order to use this connection pool, previously you should create a DataSource instance. Here -an little example on how it can be done: - -[source, clojure] ----- -(import 'com.mchange.v2.c3p0.ComboPooledDataSource) - -(def ds (doto (ComboPooledDataSource.) - (.setJdbcUrl (str "jdbc:" - (:subprotocol dbspec) - (:subname dbspec))) - (.setUser (:user dbspec nil)) - (.setPassword (:password dbspec nil)) - - ;; Pool Size Management - (.setMinPoolSize 3) - (.setMaxPoolSize 15) - - ;; Connection eviction - (.setMaxConnectionAge 3600) ; 1 hour - (.setMaxIdleTime 1800) ; 1/2 hour - (.setMaxIdleTimeExcessConnections 120) - - ;; Connection testing - (.setTestConnectionOnCheckin false) - (.setTestConnectionOnCheckout false) - (.setIdleConnectionTestPeriod 600))) ----- - -You can found all configuration parameters here: http://www.mchange.com/projects/c3p0/#configuration - -Now, the new created datasource should be used like a plain dbspec for creating connections: - -[source, clojure] ----- -(with-open [conn (jdbc/connection ds)] - (do-stuff conn)) ----- - - -=== dbcp2 - -Apache commons DBCP (JDBC) connection pool implementation for clojure.jdbc - -.Dependency entry -[source, clojure] ----- -[org.apache.commons/commons-dbcp2 "2.0.1"] ----- - -In order to use this connection pool, previously you should create a DataSource instance. Here -an little example on how it can be done: - -[source, clojure] ----- -(import 'org.apache.commons.dbcp2.BasicDataSource) - -(def ds (doto (BasicDataSource.) - (.setJdbcUrl (str "jdbc:" - (:subprotocol dbspec) - (:subname dbspec))) - (.setUser (:user dbspec nil)) - (.setPassword (:password dbspec nil)) - - ;; Pool Size Management - (.setInitialSize 0) - (.setMaxIdle 3) - (.setMaxTotal 15) - - ;; Connection eviction - (.setMaxConnLifetimeMillis 3600000) ; 1 hour - - ;; Connection testing - (.setTestOnBorrow false) - (.setTestOnReturn false) - (.setTestWhileIdle true) - (.setTimeBetweenEvictionRunsMillis 600000) ;; 10 minutes - (.setNumTestsPerEvictionRun 4) - (.setMinEvictableIdleTimeMillis 1800000))) ;; 1/2 hours ----- - -You can found all configuration parameters here: http://commons.apache.org/proper/commons-dbcp/configuration.html - -Now, the new created datasource should be used like a plain dbspec for creating connections: - -[source, clojure] ----- -(with-open [conn (jdbc/connection ds)] - (do-stuff conn)) ----- - - -=== HikariCP - -Fast, simple, reliable. HikariCP is a "zero-overhead" production ready JDBC connection pool. - -.Dependency entry for Java8 -[source, clojure] ----- -[hikari-cp "1.2.4"] ----- - -.Dependency entry for Java7 or Java6 -[source, clojure] ----- -[hikari-cp "1.2.4" :exclusions [com.zaxxer/HikariCP]] -[com.zaxxer/HikariCP-java6 "2.3.9"] ----- - -In order to use this connection pool, previously you should create a DataSource instance. Here -an little example on how it can be done: - - -[source, clojure] ----- -(require '[hikari-cp.core :as hikari]) - -(def ds (hikari/make-datasource - {:connection-timeout 30000 - :idle-timeout 600000 - :max-lifetime 1800000 - :minimum-idle 10 - :maximum-pool-size 10 - :adapter "postgresql" - :username "username" - :password "password" - :database-name "database" - :server-name "localhost" - :port-number 5432})) ----- - -HikariCP, unlike other datasource implementations, requires to setup explicitly that adapter should -be used. This is a list of supported adapters: - -[[list-of-hikari-adapters]] -.List of adapters supported by HikariCP -[options="header"] -|============================================================================ -| Adapter | Datasource class name -| `:derby` | `org.apache.derby.jdbc.ClientDataSource` -| `:firebird` | `org.firebirdsql.pool.FBSimpleDataSource` -| `:db2` | `com.ibm.db2.jcc.DB2SimpleDataSource` -| `:h2` | `org.h2.jdbcx.JdbcDataSource` -| `:hsqldb` | `org.hsqldb.jdbc.JDBCDataSource` -| `:mariadb` | `org.mariadb.jdbc.MySQLDataSource` -| `:mysql` | `com.mysql.jdbc.jdbc2.optional.MysqlDataSource` -| `:sqlserver-jtds` | `net.sourceforge.jtds.jdbcx.JtdsDataSource` -| `:sqlserver` | `com.microsoft.sqlserver.jdbc.SQLServerDataSource` -| `:oracle` | `oracle.jdbc.pool.OracleDataSource` -| `:pgjdbc-ng` | `com.impossibl.postgres.jdbc.PGDataSource` -| `:postgresql` | `org.postgresql.ds.PGSimpleDataSource` -| `:sybase` | `com.sybase.jdbcx.SybDataSource` -|============================================================================ - - -You can found more information and documentation about hikari-cp here: -https://github.com/tomekw/hikari-cp - -Now, the new created datasource should be used like a plain dbspec for creating connections: - -[source, clojure] ----- -(with-open [conn (jdbc/connection ds)] - (do-stuff conn)) ----- - - -== FAQ - -=== Why another JDBC wrapper? - -This is an incomplete list of reasons: - -- Connection management in _clojure.jdbc_ is simple and explicit. However, java.jdbc does not make - diferentiation between a connection or dbspec hash map. At first glace it seems more flexible, - but it requires huge additional complexity and boilerplate code on each function that receives - a connection. If you are curious, take a look at the `with-db-connection` implementation of - java.jdbc and compare it with *clojure.jdbc*: it will get you a good idea of the hidden and - unnecessary complexity found in java.jdbc. -- _clojure.jdbc_ comes with proper transaction management with full support for nested transactions, - and plugable transaction strategies. In contrast, java.jdbc comes with one unique strategy, that - does not support subtransactions (it flattens all nested transactions in one unique giving you - false security when you wraps you code in a transaction). -- _clojure.jdbc_ has native support for connection pools. -- _clojure.jdbc_ has first class documentation. -- _clojure.jdbc_ has a simpler implementation than java.jdbc without unnecesary code duplication. A - good example are the crud methods of java.jdbc: all them repeats the transaction logic implicitly. - In contrast, clojure.jdbc, if you want a transaction, you should wrap your code in a transaction - context explicitly. - - -=== Does _clojure.jdbc_ have better performance than java.jdbc? - -Mostly **Yes**, _clojure.jdbc_ by default has better performance than java.jdbc. You can -run the micro benchmark code in your environment with: `lein with-profile bench run` - -In my environments, the results are: - -[source,text] ----- -[3/5.0.5]niwi@niwi:~/clojure.jdbc> lein with-profile bench run -Simple query without connection overhead. -java.jdbc: -"Elapsed time: 673.890131 msecs" -clojure.jdbc: -"Elapsed time: 450.329706 msecs" -Simple query with connection overhead. -java.jdbc: -"Elapsed time: 2490.233925 msecs" -clojure.jdbc: -"Elapsed time: 2239.524395 msecs" -Simple query with transaction. -java.jdbc: -"Elapsed time: 532.151667 msecs" -clojure.jdbc: -"Elapsed time: 602.482932 msecs" ----- - - -=== Why does _clojure.jdbc_ not include a DSL for working with SQL as java.jdbc 0.3 does? - -_clojure.jdbc_ is a wrapper for the Java JDBC interface. It doesn't intend to provide helpers -to avoid SQL usage. There are already plenty of DSLs for working with SQL. -_clojure.jdbc_ will not reinvent the wheel. - -This is an incomplete list of Clojure DSLs for SQL: - -- https://github.com/niwibe/suricatta -- https://github.com/stch-library/sql -- https://github.com/r0man/sqlingvo -- https://github.com/jkk/honeysql - - -=== Is this a fork of java.jdbc? - -No. It is an alternative implementation. - -== Developers Guide - -=== Philosophy - -Five most important rules: - -- Beautiful is better than ugly. -- Explicit is better than implicit. -- Simple is better than complex. -- Complex is better than complicated. -- Readability counts. - -All contributions to _clojure.jdbc_ should keep these important rules in mind. - - -=== Contributing - -Unlike Clojure and other Clojure contributed libraries _clojure.jdbc_ does not have many -restrictions for contributions. Just open an issue or pull request. - - -=== Source Code - -_clojure.jdbc_ is open source and can be found on -link:https://github.com/funcool/clojure.jdbc[github]. - -You can clone the public repository with this command: - -[source,text] ----- -git clone https://github.com/funcool/clojure.jdbc ----- - - -=== Run tests - -For running tests just execute this: - -[source, text] ----- -lein test ----- - -You should have postgresql up and running with a current user created with trust access mode -activated for this user and test db already created. - - -=== License - -_clojure.jdbc_ is writen from scratch and is licensed under Apache 2.0 license: - ----- -Copyright (c) 2013-2015 Andrey Antukh - -Licensed under the Apache License, Version 2.0 (the "License") -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. ----- - -You can see the full license in the LICENSE file located in the root of the project -repo. - -Additionaly, I want to give thanks to the `java.jdbc` developers for their good -initial work. Some intial ideas for _clojure.jdbc_ are taken from that project. diff --git a/latest/api/css/default.css b/latest/api/css/default.css new file mode 100644 index 0000000..257ab2c --- /dev/null +++ b/latest/api/css/default.css @@ -0,0 +1,442 @@ +@import url(http://fonts.googleapis.com/css?family=Droid+Sans+Mono:200,300,400); +@import url(http://fonts.googleapis.com/css?family=Lato:light,regular); +@import url(http://fonts.googleapis.com/css?family=Ubuntu:300,400,500); + +body { + font-family: "Lato", Helvetica, Arial, sans-serif; + font-weight: 300; + color:#585858; + font-size: 100%; + margin: 0px; +} + +pre, code { + font-family: "Droid Sans Mono","DejaVu Sans Mono","Monospace",monospace; + font-weight: 300; +} + +section.container { + display: flex; + font-size: 100%; +} + +h2 { + font-weight: normal; + font-size: 3em; + padding: 10px 0 2px 0; + margin: 0; +} + +header { + color: #333; + padding: 10px; +} + +header small { + font-style: italic; +} + +header h1 { + margin: 0; + padding: 0; + /* font-size: 12pt; */ + font-weight: lighter; + /* text-shadow: -1px -1px 0px #333; */ +} + +header h1 a { + color: #333; + /* font-size: 32px; */ + font-weight: 400; + text-decoration: none; +} + +#content { + overflow: auto; + background: #fff; + color: #333; + padding: 0 18px; + font-size: 1.3em; +} + +#namespaces { + border-right: solid 1px #cccccc; + min-width: 200px; + padding-right: 15px; +} + +#vars { + border-right: solid 1px #cccccc; + width: 200px; +} + +.sidebar { + overflow: auto; +} + +.sidebar a { + color: #333; + display: block; + text-decoration: none; +} + +.sidebar h3 { + margin: 0; + padding: 10px 10px 0 10px; + font-size: 19px; + font-weight: normal; +} + +.sidebar ul { + padding: 0.5em 0em; + margin: 0; +} + +.sidebar li { + display: block; + vertical-align: middle; +} + +.sidebar li a, .sidebar li .no-link { + border-left: 3px solid transparent; + padding: 0 15px; + white-space: nowrap; +} + +.sidebar li .no-link { + display: block; + color: #777; + font-style: italic; +} + +.sidebar li .inner { + display: inline-block; + padding-top: 7px; + height: 24px; +} + +.sidebar li a, .sidebar li .tree { + height: 31px; + /* height: 25px; */ +} + +.depth-1 .inner { padding-left: 2px; } +.depth-2 .inner { padding-left: 6px; } +.depth-3 .inner { padding-left: 20px; } +.depth-4 .inner { padding-left: 34px; } +.depth-5 .inner { padding-left: 48px; } +.depth-6 .inner { padding-left: 62px; } + +.sidebar li .tree { + display: block; + float: left; + position: relative; + top: -10px; + margin: 0 4px 0 0; + padding: 0; +} + +.sidebar li.depth-1 .tree { + display: none; +} + +.sidebar li .tree .top, .sidebar li .tree .bottom { + display: block; + margin: 0; + padding: 0; + width: 7px; +} + +.sidebar li .tree .top { + border-left: 1px solid #aaa; + border-bottom: 1px solid #aaa; + height: 19px; +} + +.sidebar li .tree .bottom { + height: 22px; +} + +.sidebar li.branch .tree .bottom { + border-left: 1px solid #aaa; +} + +#namespaces li.current a { + border-left: 3px solid #a33; + border-left: 3px solid #7a2518; + color: #a33; + color: #7a2518; + +} + +#vars li.current a { + border-left: 3px solid #33a; + color: #33a; +} + +.namespace-docs h2 { + color: #7a2518; +} + +.namespace-docs h3 a { + color: #ba3925; + font-family: "Droid Sans Mono","DejaVu Sans Mono","Monospace",monospace; + font-weight: 400; + text-decoration: none; +} + +.namespace-docs .usage code { + display: block; + color: #777; + margin: 2px 0; + font-size: 0.6em; +} + +/* .usage code:first-child { */ +/* padding-top: 10px; */ +/* } */ + + + +.namespace-index h3 a { + text-decoration: none; + color: #ba3925; + font-family: "Droid Sans Mono","DejaVu Sans Mono","Monospace",monospace; + font-weight: 300; +} + +.public h3 { + margin: 0; +} + +.public { + margin: 0; + border-top: 1px solid #efefef; + padding-top: 14px; + padding-bottom: 6px; +} + +.public:last-child { + margin-bottom: 20%; +} + +.members .public:last-child { + margin-bottom: 0; +} + +.members { + margin: 15px 0; +} + +.members h4 { + color: #555; + font-weight: normal; + font-variant: small-caps; + margin: 0 0 5px 0; +} + +.members .inner { + padding-top: 5px; + padding-left: 12px; + margin-top: 2px; + margin-left: 7px; + border-left: 1px solid #bbb; +} + +#content .members .inner h3 { + /* font-size: 12pt; */ +} + +.members .public { + border-top: none; + margin-top: 0; + padding-top: 6px; + padding-bottom: 0; +} + +.members .public:first-child { + padding-top: 0; +} + +h4.type, +h4.dynamic, +h4.added, +h4.deprecated { + margin: 3px 10px 10spx 0; + font-weight: bold; + font-variant: small-caps; +} + +.public h4.type, +.public h4.dynamic, +.public h4.added, +.public h4.deprecated { + font-weight: bold; + /* margin: 3px 0 0 10px; */ + font-size: 0.7em; +} + +.members h4.type, +.members h4.added, +.members h4.deprecated { + margin-top: 1px; +} + +h4.type { + color: #717171; +} + +h4.dynamic { + color: #9933aa; +} + +h4.added { + color: #508820; +} + +h4.deprecated { + color: #880000; +} + +.namespace { + margin-bottom: 40px; +} + +.namespace:last-child { + margin-bottom: 10%; +} + +.index { + padding: 0; + margin: 15px 0; +} + +.index * { + display: inline; +} + +.index p { + padding-right: 3px; +} + +.index li { + padding-right: 5px; +} + +.index li a { + color: #333; + font-family: "Droid Sans Mono","DejaVu Sans Mono","Monospace",monospace; + font-size: 0.8em; + text-decoration: none; + font-weight: 300; +} + +.index ul { + padding-left: 0; +} + +p { + margin: 15px 0; +} + +.public p:first-child, .public pre.plaintext { + margin-top: 12px; +} + +.doc { + margin: 0 0 26px 0; + clear: both; +} + +.public .doc { + margin: 0; +} + +.namespace-index .doc { + margin-bottom: 20px; +} + +.namespace-index .namespace .doc { + margin-bottom: 10px; +} + +.markdown { + /* line-height: 18px; */ + /* font-size: 16px; */ +} + +.doc, .public, .namespace .index { + max-width: 780px; + overflow-x: visible; +} + +.markdown code, .src-link a { + border-radius: 2px; + font-size: 0.8em; + color: #444; +} + +.markdown pre { + background: #f4f4f4; + border: 1px solid #e0e0e0; + /* border-radius: 2px; */ + padding: 5px 5px; + border-top: 1px solid #e0e0e0; + border-bottom: 1px solid #e0e0e0; +} + +.markdown pre code { + background: transparent; + border: none; +} + +.doc ul, .doc ol { + padding-left: 30px; +} + +.doc table { + border-collapse: collapse; + margin: 0 10px; +} + +.doc table td, .doc table th { + border: 1px solid #dddddd; + padding: 4px 6px; +} + +.doc table th { + background: #f2f2f2; +} + +.doc dl { + margin: 0 10px 20px 10px; +} + +.doc dl dt { + font-weight: bold; + margin: 0; + padding: 3px 0; + border-bottom: 1px solid #ddd; +} + +.doc dl dd { + padding: 5px 0; + margin: 0 0 5px 10px; +} + +.doc abbr { + border-bottom: 1px dotted #333; + font-variant: none + cursor: help; +} + +.src-link { + margin-bottom: 15px; +} + +.src-link a { + /* font-size: 70%; */ + padding: 1px 4px; + text-decoration: none; + color: #5555bb; +} \ No newline at end of file diff --git a/latest/api/index.html b/latest/api/index.html new file mode 100644 index 0000000..a42c065 --- /dev/null +++ b/latest/api/index.html @@ -0,0 +1,2 @@ + +Clojure.jdbc 0.9.0 API documentation

Clojure.jdbc Api Documentation

Version: 0.9.0

Clojure.jdbc

clojure.jdbc is a library for low level, jdbc-based database access.

jdbc.constants

Namespace that contains a public constants used around this library.

Public variables and functions:

jdbc.core

Alternative implementation of jdbc wrapper for clojure.

jdbc.resultset

ResultSet conversion functions.

Public variables and functions:

\ No newline at end of file diff --git a/latest/api/jdbc.constants.html b/latest/api/jdbc.constants.html new file mode 100644 index 0000000..c5f2385 --- /dev/null +++ b/latest/api/jdbc.constants.html @@ -0,0 +1,2 @@ + +jdbc.constants documentation

Clojure.jdbc Api Documentation

Version: 0.9.0

jdbc.constants

Namespace that contains a public constants used around this library.

isolation-levels

Transaction isolation levels

resultset-options

ResultSet keyword constants

\ No newline at end of file diff --git a/latest/api/jdbc.core.html b/latest/api/jdbc.core.html new file mode 100644 index 0000000..32facfa --- /dev/null +++ b/latest/api/jdbc.core.html @@ -0,0 +1,66 @@ + +jdbc.core documentation

Clojure.jdbc Api Documentation

Version: 0.9.0

jdbc.core

Alternative implementation of jdbc wrapper for clojure.

atomic

macro

(atomic conn & body)

Creates a context that evaluates in transaction (or nested transaction). This is a more idiomatic way to execute some database operations in atomic way.

+
(jdbc/atomic conn
+  (jdbc/execute conn "DROP TABLE foo;")
+  (jdbc/execute conn "DROP TABLE bar;"))
+
+

Also, you can pass additional options to transaction:

+
(jdbc/atomic conn {:read-only true}
+  (jdbc/execute conn "DROP TABLE foo;")
+  (jdbc/execute conn "DROP TABLE bar;"))
+

atomic-apply

(atomic-apply conn func & [{:keys [savepoints strategy], :or {savepoints true}, :as opts}])

Wrap function in one transaction. This function accepts as a parameter a transaction strategy. If no one is specified, DefaultTransactionStrategy is used.

+

With DefaultTransactionStrategy, if current connection is already in transaction, it uses truly nested transactions for properly handle it. The availability of this feature depends on database support for it.

+
(with-open [conn (jdbc/connection)]
+  (atomic-apply conn (fn [conn] (execute! conn 'DROP TABLE foo;'))))
+
+

For more idiomatic code, you should use atomic macro.

+

Depending on transaction strategy you are using, this function can accept additional parameters. The default transaction strategy exposes two additional parameters:

+
    +
  • :isolation-level - set isolation level for this transaction
  • +
  • :read-only - set current transaction to read only
  • +

connection

(connection dbspec)(connection dbspec options)

Creates a connection to a database. As parameter accepts:

+
    +
  • dbspec map containing connection parameters
  • +
  • dbspec map containing a datasource (deprecated)
  • +
  • URI or string (interpreted as uri)
  • +
  • DataSource instance
  • +
+

The dbspec map has this possible variants:

+

Classic approach:

+
    +
  • :subprotocol -> (required) string that represents a vendor name (ex: postgresql)
  • +
  • :subname -> (required) string that represents a database name (ex: test) (many others options that are pased directly as driver parameters)
  • +
+

Pretty format:

+
    +
  • :vendor -> (required) string that represents a vendor name (ex: postgresql)
  • +
  • :name -> (required) string that represents a database name (ex: test)
  • +
  • :host -> (optional) string that represents a database hostname (default: 127.0.0.1)
  • +
  • :port -> (optional) long number that represents a database port (default: driver default) (many others options that are pased directly as driver parameters)
  • +
+

URI or String format: vendor://user:password@host:post/dbname?param1=value

+

Additional options:

+
    +
  • :schema -> string that represents a schema name (default: nil)
  • +
  • :read-only -> boolean for mark entire connection read only.
  • +
  • :isolation-level -> keyword that represents a isolation level (:none, :read-committed, :read-uncommitted, :repeatable-read, :serializable)
  • +
+

Opions can be passed as part of dbspec map, or as optional second argument. For more details, see documentation.

cursor->lazyseq

(cursor->lazyseq cursor)(cursor->lazyseq cursor opts)

Transform a cursor in a lazyseq.

+

The returned lazyseq will return values until a cursor is closed or all values are fetched.

execute

(execute conn q)(execute conn q opts)

Execute a query and return a number of rows affected.

+
(with-open [conn (jdbc/connection dbspec)]
+  (jdbc/execute conn "create table foo (id integer);"))
+
+

This function also accepts sqlvec format.

fetch

(fetch conn q)(fetch conn q opts)

Fetch eagerly results executing a query.

+

This function returns a vector of records (default) or rows (depending on specified opts). Resources are relased inmediatelly without specific explicit action for it.

+

It accepts a sqlvec, plain sql or prepared statement as query parameter.

fetch-lazy

(fetch-lazy conn q)(fetch-lazy conn q opts)

Fetch lazily results executing a query.

+
(with-open [cursor (jdbc/fetch-lazy conn sql)]
+  (doseq [item (jdbc/cursor->lazyseq cursor)]
+    (do-something-with item)))
+
+

This function returns a cursor instead of result. You should explicitly close the cursor at the end of iteration for release resources.

fetch-one

(fetch-one conn q)(fetch-one conn q opts)

Fetch eagerly one restult executing a query.

lazy-query

deprecated

Deprecated alias for backward compatibility.

prepared-statement

(prepared-statement conn sqlvec)(prepared-statement conn sqlvec options)

Given a string or parametrized sql in sqlvec format return an instance of prepared statement.

prepared-statement?

(prepared-statement? obj)

Check if specified object is prepared statement.

set-rollback!

(set-rollback! conn)

Mark a current connection for rollback.

+

It ensures that on the end of the current transaction instead of commit changes, rollback them.

+

This function should be used inside of a transaction block, otherwise this function does nothing.

+
(jdbc/atomic conn
+  (make-some-queries-without-changes conn)
+  (jdbc/set-rollback! conn))
+
\ No newline at end of file diff --git a/latest/api/jdbc.meta.html b/latest/api/jdbc.meta.html new file mode 100644 index 0000000..82f9de3 --- /dev/null +++ b/latest/api/jdbc.meta.html @@ -0,0 +1,2 @@ + +jdbc.meta documentation

Clojure.jdbc Api Documentation

Version: 0.9.0

jdbc.meta

Connection metadata access methods.

catalog-name

(catalog-name c)

Given a connection, get a catalog name.

db-major-version

(db-major-version c)

Given a connection, return a database major version number.

db-minor-version

(db-minor-version c)

Given a connection, return a database minor version number.

db-product-name

(db-product-name c)

Given a connection, return a database product name from it metadata.

db-product-version

(db-product-version c)

Given a connection, return a database product version from it metadata.

driver-name

(driver-name c)

Given a connection, return a current driver name used for this connection.

driver-version

(driver-version c)

Given a connection, return a current driver version used for this connection.

is-readonly?

(is-readonly? c)

Returns true if a current connection is in read-only model.

is-valid?

(is-valid? c)(is-valid? c timeout)

Given a connection, return true if connection has not ben closed it still valid.

isolation-level

(isolation-level c)

Given a connection, get a current isolation level.

network-timeout

(network-timeout c)

Given a connection, get network timeout.

schema-name

(schema-name c)

Given a connection, get a schema name.

vendor-name

(vendor-name c)

Get connection vendor name.

\ No newline at end of file diff --git a/latest/api/jdbc.proto.html b/latest/api/jdbc.proto.html new file mode 100644 index 0000000..2bf0b34 --- /dev/null +++ b/latest/api/jdbc.proto.html @@ -0,0 +1,2 @@ + +jdbc.proto documentation

Clojure.jdbc Api Documentation

Version: 0.9.0

jdbc.proto

IConnection

protocol

Represents a connection like object that wraps a raw jdbc connection with some other data.

members

connection

(connection _)

Create or obtain existing connection

IDatabaseMetadata

protocol

Allows uniform database metadata extraction.

members

get-database-metadata

(get-database-metadata _)

Get metadata instance.

IExecute

protocol

members

execute

(execute q conn opts)

Execute a query and return a number of rows affected.

IFetch

protocol

members

fetch

(fetch q conn opts)

Fetch eagerly results executing query.

IPreparedStatement

protocol

Responsible of building prepared statements.

members

prepared-statement

(prepared-statement _ connection options)

Create a prepared statement.

ISQLResultSetReadColumn

protocol

Protocol that exposes uniform way to convert values obtained from result set to user types. Default implementation available for Object, Boolean, and nil.

members

from-sql-type

(from-sql-type _ conn metadata index)

Convert sql type to user type.

ISQLType

protocol

Protocol that exposes uniform way for convert user types to sql/jdbc compatible types and uniform set parameters to prepared statement instance. Default implementation available for Object and nil values.

members

as-sql-type

(as-sql-type _ conn)

Convert user type to sql type.

set-stmt-parameter!

(set-stmt-parameter! this conn stmt index)

Set value to statement.

ITransactionStrategy

protocol

members

begin!

(begin! _ conn opts)

Starts a transaction and return a connection instance.

commit!

(commit! _ conn opts)

Commits a transaction. Returns nil.

rollback!

(rollback! _ conn opts)

Rollbacks a transaction. Returns nil.

\ No newline at end of file diff --git a/latest/api/jdbc.resultset.html b/latest/api/jdbc.resultset.html new file mode 100644 index 0000000..5d86082 --- /dev/null +++ b/latest/api/jdbc.resultset.html @@ -0,0 +1,4 @@ + +jdbc.resultset documentation

Clojure.jdbc Api Documentation

Version: 0.9.0

jdbc.resultset

ResultSet conversion functions.

result-set->lazyseq

(result-set->lazyseq conn rs {:keys [identifiers as-rows? header?], :or {identifiers str/lower-case, as-rows? false, header? false}, :as options})

Function that wraps result in a lazy seq. This function is part of public api but can not be used directly (you should pass this function as parameter to query function).

+

Required parameters: rs: ResultSet instance.

+

Optional named parameters: :identifiers -> function that is applied for column name when as-arrays? is false :as-rows? -> by default this function return a lazy seq of records (map), but in certain circumstances you need results as a lazy-seq of vectors. With this keywork parameter you can enable this behavior and return a lazy-seq of vectors instead of records (maps).

result-set->vector

(result-set->vector conn rs options)

Function that evaluates a result into one clojure persistent vector. Accept same parameters as result-set->lazyseq.

\ No newline at end of file diff --git a/latest/index.html b/latest/index.html new file mode 100644 index 0000000..919592c --- /dev/null +++ b/latest/index.html @@ -0,0 +1,1161 @@ + + + + + + + + +clojure.jdbc documentation + + + + + + +
+
+

Introduction

+
+
+

clojure.jdbc is a library for low level, jdbc-based database access.

+
+ +
+

Install

+
+

The simplest way to use clojure.jdbc in a clojure project, is by including it in the dependency +vector on your project.clj file:

+
+
+
+
[funcool/clojure.jdbc "0.9.0"]
+
+
+
+

You also need to add an entry for the java driver that you need. For instance, for mysql:

+
+
+
+
---
+[mysql/mysql-connector-java "5.1.6"]
+---
+
+
+
+

The project is tested under JDK7 and JDK8.

+
+
+
+

Project Maturity

+
+

Since clojure.jdbc is a young project there may be some API breakage.

+
+
+
+
+
+

User Guide

+
+
+

Connecting to database

+
+

This section intends to explain everything that you should to know about how +connect to the database.

+
+
+

Connection parameters

+
+

JDBC is the default Java abstraction/interface for SQL databases. It’s like +the Python DB-API and similar abstractions in other languages. Clojure, as a +guest language on the JVM, benefits from having a good, well-tested abstraction +like that.

+
+
+

Connection parameters are exposed in a simple hash-map and called dbspec. This is the simplest +and most idiomatic way in Clojure to define configuration parameters.

+
+
+
This is a default aspect of one dbspec.
+
+
(def dbspec {:subprotocol "postgresql"
+             :subname "//localhost:5432/dbname"
+             :user "username"         ;; Optional
+             :password "password"}    ;; Optional
+
+
+
+

Also, clojure.jdbc comes with alternative, more human comprehensible format, like this:

+
+
+
Pretty dbspec format
+
+
(def dbspec {:vendor "postgresql"
+             :name "dbname"
+             :host "localhost"      ;; Optional
+             :port 5432             ;; Optional
+             :user "username"       ;; Optional
+             :password "password"}) ;; Optional
+
+
+
+

But it has some disadvantages: it does not supports all options of a default dbspec format.

+
+
+

Also, dbspec can be represented as a URI.

+
+
+
Same as the previous example but using URI format.
+
+
(def dbspec "postgresql://user:password@localhost:5432/dbname")
+
+
+
+
+

Creating a connection

+
+

With clojure.jdbc every function that interacts with a database explicitly requires +one connection instance as parameter (no dynamic vars are used for it).

+
+
+
Create one connection using connection function:
+
+
(require '[jdbc.core :as jdbc])
+
+(let [conn (jdbc/connection dbspec)]
+  (do-something-with conn)
+  (.close conn))
+
+
+
+

As you can see in previous example, you should explicltly close connection for proper +resource management. You can use the with-open clojure macro for make code looks +more clean and idiomatic.

+
+
+
+
(with-open [conn (jdbc/connection dbspec)]
+  (do-something-with conn))
+
+
+
+
+
+

Execute Database Commands

+
+

Execute Raw SQL Statements

+
+

The simplest way to execute raw SQL is using the execute function. It requires +an active connection as the first parameter followed by SQL sentence:

+
+
+
+
(with-open [conn (jdbc/connection dbspec)]
+  (jdbc/execute conn "CREATE TABLE foo (id serial, name text);"))
+
+
+
+
+

Execute Parametrized SQL Statements

+
+

Raw SQL statements work well for creating tables and similar operations, but +when you need to insert some data, especially if the data comes from untrusted +sources. Using plain string concatenation is a very bat and unsafe approach.

+
+
+

For safe parameter bindings, it there a sqlvec format that consists in a +vector with sql followed of parameters:

+
+
+
Example of sqlvec format
+
+
["select * from foo where age < ?" 20]
+
+
+
+

Almost all functions in clojure.jdbc accepts sqlvec as query parameter. +Let see an example using it in execute function:

+
+
+
Execute a simple insert SQL statement.
+
+
(jdbc/execute conn ["insert into foo (name) values (?);" "Foo"]))
+
+
+
+
+

Returning Inserted Keys

+
+

In some circumstances, you want to use the "RETURNING id" or similar functionality on +your queries for getting the primary keys of newly inserted records.

+
+
+
Example executing query with "returning" statement
+
+
(let [sql "insert into foo (name) values (?) returning id;"
+      res (jdbc/fetch conn [sql "Foo"])]
+  (println res))
+
+;; This should print something like this to standard output:
+[{:id 3}]
+
+
+
+
+
+

Make Queries

+
+

It is time to start executing queries and fetch results. For this purpose, +clojure.jdbc exposes the fetch function:

+
+
+
+
(let [sql    ["SELECT id, name FROM people WHERE age > ?", 2]
+      result (jdbc/fetch conn sql)]
+  (doseq [row result]
+    (println row))))
+
+;; It should print somthing like this:
+;; => {:id 1 :name "Foo"}
+;; => {:id 2 :name "Bar"}
+
+
+
+

We are know that almost all function in clojure.jdbc accepts a plain +sql and sqlvec as query parameter. But is not entirely true, it also +accepts as query parameter an instance of self crafted PreparedStatement +or anythig that implements the ISQLStatement protocol.

+
+
+
Example creating a prepared statement and executing it
+
+
(let [stmt (jdbc/prepared-statement ["SELECT id, name FROM people WHERE age > ?", 2])
+      result (jdbc/fetch conn stmt)]
+  (println "Result: " result))
+
+
+
+ + + + + +
+
Note
+
+
+

The fetch method seems useful in most cases but may not work well with +queries that returns a lot of results. For this purpose, cursor type queries exist +that are explained in the Advanced usage section.

+
+
+
+
+
+

Transactions

+
+

Getting Started with Transactions

+
+

The most idiomatic way to wrap some code in a transaction, is by using the atomic +macro:

+
+
+
+
(jdbc/atomic conn
+  (do-thing-first conn)
+  (do-thing-second conn))
+
+
+
+ + + + + +
+
Note
+
+
+

clojure.jdbc does not uses any dynamic thread-local vars to store the transaction state +of a connection. Instead of that, it overwrites the lexical scope value of conn with a new +connection that has transactional state.

+
+
+
+
+
+

Low-level Transaction Primitives

+
+

Behind the scenes of the atomic macro, clojure.jdbc uses the atomic-apply function.

+
+
+

Given an active connection as the first parameter and function that you want execute in a +transaction as the second parameter, it executes the function inside a database transaction. +The callback function should accept a connection as its first parameter.

+
+
+
Example executing a function in transaction context
+
+
(jdbc/atomic-apply conn
+  (fn [conn]
+    (do-something-with conn)))
+
+
+
+ + + + + +
+
Note
+
+
+

clojure.jdbc, in contrast to java.jdbc, handles nested transactions well. Thus making all +code wrapped in transaction blocks truly atomic independently of transaction nesting.

+
+
+

If you want extend or change a default transaction strategy, see +Transaction Strategy section.

+
+
+
+
+
+

Isolation Level

+
+

clojure.jdbc by default does nothing with the isolation level and keeps it to default values.

+
+
+
You can set the isolation level when creating a connection by specifying it in your dbspec.
+
+
(def dbspec {:subprotocol "h2"
+             :subname "mem:"
+             :isolation-level :serializable})
+
+(with-open [conn (jdbc/connection dbspec)]
+  ;; The just created connection has the isolation
+  ;; level set to :serializable
+  (do-things conn))
+
+
+
+

An other way to set the isolation level is in moment of declaring a transaction, using +the atomic-apply function or atomic macro:

+
+
+
+
(jdbc/atomic-apply conn do-something {:isolation-level :serializable})
+
+(jdbc/atomic conn {:isolation-level :serializable}
+  (do-something conn))
+
+
+
+

This is a list of supported options:

+
+
+
    +
  • +

    :read-uncommitted - Set read uncommitted isolation level

    +
  • +
  • +

    :read-committed - Set read committed isolation level

    +
  • +
  • +

    :repeatable-read - Set repeatable reads isolation level

    +
  • +
  • +

    :serializable - Set serializable isolation level

    +
  • +
  • +

    :none - Use this option to indicate to clojure.jdbc to do nothing and keep default behavior.

    +
  • +
+
+
+

You can read more about it on wikipedia.

+
+
+ + + + + +
+
Warning
+
+not all JDBC providers support the above isolation levels. +
+
+
+
+

Read-Only Transactions

+
+

In some circumstances, mainly when you are using the strictest isolation-level, you may want +to indicate to database that a query is actually read-only, allowing the database server to make some +optimizations for this operation.

+
+
+
You can set transaction read-only using transaction options
+
+
(jdbc/atomic conn {:isolation-level :serializable
+                   :read-only true}
+  (query-something conn))
+
+
+
+
+
+
+
+

Advanced usage

+
+
+

Server Side Cursors

+
+

By default, most JDBC drivers prefetch all results into memory make the use of lazy structures +totally useless for fetching data. Luckily, some databases implement server-side cursors that avoid +this behavior.

+
+
+

If you have an extremely large resultset and you want retrieve it and process each item, this is +exactly what you need.

+
+
+

For this purpose, clojure.jdbc exposes the fetch-lazy function, that returns a some kind of +cursor instance. At the moment of creating cursor, no query is executed.

+
+
+

The cursor can be used converting it into clojure lazyseq using cursor→lazyseq function:

+
+
+
+
(jdbc/atomic conn
+  (with-open [cursor (jdbc/fetch-lazy conn "SELECT id, name FROM people;")]
+    (doseq [row (jdbc/cursor->lazyseq cursor)]
+      (println row)))
+
+
+
+

In some databases, it requires that cursor should be evaluated in a context of one +transaction.

+
+
+
+

Transaction Strategy

+
+

Transaction strategies in clojure.jdbc are implemented using protocols having default +implementation explained in the previous sections. This approach allows an easy way to extend, +customize or completely change a transaction strategy for your application.

+
+
+

If you want another strategy, you should create a new type and implement the +ITransactionStrategy protocol.

+
+
+
Sample dummy transaction strategy.
+
+
(require '[jdbc.proto :as proto])
+
+(def dummy-tx-strategy
+  (reify
+    proto/ITransactionStrategy
+    (begin! [_ conn opts] conn)
+    (rollback! [_ conn opts] conn)
+    (commit! [_ conn opts] conn)))
+
+
+
+

clojure.jdbc has different ways to specify that transaction strategy shouldbe used. The most +common is setting it in your dbspec:

+
+
+
+
(def dbspec {:subprotocol "postgresql"
+             :subname "//localhost:5432/dbname"
+             :tx-strategy dummy-tx-strategy})
+(with-open [conn (jdbc/connection dbspec)]
+  (jdbc/atomic conn
+    ;; In this transaction block, the dummy transaction
+    ;; strategy will be used.
+    (do-somthing conn)))
+
+
+
+

Internally, clojure.jdbc maintains an instance of default transaction strategy stored +in a dynamic var. You can use the clojure facilities for alter that var for set +an other default transaction stragegy:

+
+
+
Overwritting with alter-var-root
+
+
(alter-var-root #'jdbc/*default-tx-strategy* (fn [_] dummy-tx-strategy))
+
+
+
+
Overwritting it with dynamic scope
+
+
(binding [jdbc/*default-tx-strategy* dummy-tx-strategy]
+  (some-func-that-uses-transactions))
+
+
+
+
+

Extend SQL Types

+
+

If you want to extend some type/class to use it as JDBC parameter without explicit conversion +to an SQL-compatible type, you should extend your type with the jdbc.proto/ISQLType protocol.

+
+
+

Here is an example which extends Java’s String[] (string array) in order to pass it as +a query parameter that corresponds to PostgreSQL text array in the database:

+
+
+
+
(require '[jdbc.proto :as proto])
+
+(extend-protocol ISQLType
+  ;; Obtain a class for string array
+  (class (into-array String []))
+
+  (set-stmt-parameter! [this conn stmt index]
+    (let [value (proto/as-sql-type this conn)
+          array (.createArrayOf conn "text" value)]
+      (.setArray stmt index array)))
+
+  (as-sql-type [this conn] this))
+
+
+
+

In this way you can pass a string array as a JDBC parameter that is automatically converted +to an SQL array and assigned properly in a prepared statement:

+
+
+
+
(with-open [conn (jdbc/connection pg-dbspec)]
+  (jdbc/execute conn "CREATE TABLE arrayfoo (id integer, data text[]);")
+  (let [mystringarray (into-array String ["foo" "bar"])]
+    (jdbc/execute conn ["INSERT INTO arrayfoo VALUES (?, ?);" 1 mystringarray])))
+
+
+
+

clojure.jdbc also exposes the jdbc.proto/ISQLResultSetReadColumn protocol that encapsulates +reverse conversions from SQL types to user-defined types.

+
+
+

You can read more about that in this blog post: http://www.niwi.be/2014/04/13/postgresql-json-field-with-clojure-and-jdbc/

+
+
+
+
+
+

Connection pool

+
+
+

DataSource is the preferd way to connect to the database in production enviroments, and +is usually used for implement connection pools.

+
+
+

To make good use of resourses is much recommendable use some kind of connection pool +implementation. This can avoid continuosly creating and destroying connections, +that in the majority of time is a slow operation.

+
+
+

Java ecosystem comes with various of it. This is a list of most used:

+
+ +
+

clojure.jdbc is compatible with any other connection pool implemenetation, simply +pass a javax.sql.DataSource instance to jdbc/connection function.

+
+
+

c3p0

+
+

c3p0, a mature, highly concurrent JDBC connection pooling library for clojure.jdbc.

+
+
+
Dependency entry
+
+
[com.mchange/c3p0 "0.9.5"]
+
+
+
+

In order to use this connection pool, previously you should create a DataSource instance. Here +an little example on how it can be done:

+
+
+
+
(import 'com.mchange.v2.c3p0.ComboPooledDataSource)
+
+(def ds (doto (ComboPooledDataSource.)
+          (.setJdbcUrl (str "jdbc:"
+                            (:subprotocol dbspec)
+                            (:subname dbspec)))
+          (.setUser (:user dbspec nil))
+          (.setPassword (:password dbspec nil))
+
+          ;; Pool Size Management
+          (.setMinPoolSize 3)
+          (.setMaxPoolSize 15)
+
+          ;; Connection eviction
+          (.setMaxConnectionAge  3600) ; 1 hour
+          (.setMaxIdleTime 1800)       ; 1/2 hour
+          (.setMaxIdleTimeExcessConnections 120)
+
+          ;; Connection testing
+          (.setTestConnectionOnCheckin false)
+          (.setTestConnectionOnCheckout false)
+          (.setIdleConnectionTestPeriod 600)))
+
+
+
+

You can found all configuration parameters here: http://www.mchange.com/projects/c3p0/#configuration

+
+
+

Now, the new created datasource should be used like a plain dbspec for creating connections:

+
+
+
+
(with-open [conn (jdbc/connection ds)]
+  (do-stuff conn))
+
+
+
+
+

dbcp2

+
+

Apache commons DBCP (JDBC) connection pool implementation for clojure.jdbc

+
+
+
Dependency entry
+
+
[org.apache.commons/commons-dbcp2 "2.0.1"]
+
+
+
+

In order to use this connection pool, previously you should create a DataSource instance. Here +an little example on how it can be done:

+
+
+
+
(import 'org.apache.commons.dbcp2.BasicDataSource)
+
+(def ds (doto (BasicDataSource.)
+          (.setJdbcUrl (str "jdbc:"
+                            (:subprotocol dbspec)
+                            (:subname dbspec)))
+          (.setUser (:user dbspec nil))
+          (.setPassword (:password dbspec nil))
+
+          ;; Pool Size Management
+          (.setInitialSize 0)
+          (.setMaxIdle 3)
+          (.setMaxTotal 15)
+
+          ;; Connection eviction
+          (.setMaxConnLifetimeMillis 3600000) ; 1 hour
+
+          ;; Connection testing
+          (.setTestOnBorrow false)
+          (.setTestOnReturn false)
+          (.setTestWhileIdle true)
+          (.setTimeBetweenEvictionRunsMillis 600000) ;; 10 minutes
+          (.setNumTestsPerEvictionRun 4)
+          (.setMinEvictableIdleTimeMillis 1800000))) ;; 1/2 hours
+
+
+
+

You can found all configuration parameters here: http://commons.apache.org/proper/commons-dbcp/configuration.html

+
+
+

Now, the new created datasource should be used like a plain dbspec for creating connections:

+
+
+
+
(with-open [conn (jdbc/connection ds)]
+  (do-stuff conn))
+
+
+
+
+

HikariCP

+
+

Fast, simple, reliable. HikariCP is a "zero-overhead" production ready JDBC connection pool.

+
+
+
Dependency entry for Java8
+
+
[hikari-cp "1.2.4"]
+
+
+
+
Dependency entry for Java7 or Java6
+
+
[hikari-cp "1.2.4" :exclusions [com.zaxxer/HikariCP]]
+[com.zaxxer/HikariCP-java6 "2.3.9"]
+
+
+
+

In order to use this connection pool, previously you should create a DataSource instance. Here +an little example on how it can be done:

+
+
+
+
(require '[hikari-cp.core :as hikari])
+
+(def ds (hikari/make-datasource
+         {:connection-timeout 30000
+          :idle-timeout 600000
+          :max-lifetime 1800000
+          :minimum-idle 10
+          :maximum-pool-size  10
+          :adapter "postgresql"
+          :username "username"
+          :password "password"
+          :database-name "database"
+          :server-name "localhost"
+          :port-number 5432}))
+
+
+
+

HikariCP, unlike other datasource implementations, requires to setup explicitly that adapter should +be used. This is a list of supported adapters:

+
+ + ++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Table 1. List of adapters supported by HikariCP
AdapterDatasource class name

:derby

org.apache.derby.jdbc.ClientDataSource

:firebird

org.firebirdsql.pool.FBSimpleDataSource

:db2

com.ibm.db2.jcc.DB2SimpleDataSource

:h2

org.h2.jdbcx.JdbcDataSource

:hsqldb

org.hsqldb.jdbc.JDBCDataSource

:mariadb

org.mariadb.jdbc.MySQLDataSource

:mysql

com.mysql.jdbc.jdbc2.optional.MysqlDataSource

:sqlserver-jtds

net.sourceforge.jtds.jdbcx.JtdsDataSource

:sqlserver

com.microsoft.sqlserver.jdbc.SQLServerDataSource

:oracle

oracle.jdbc.pool.OracleDataSource

:pgjdbc-ng

com.impossibl.postgres.jdbc.PGDataSource

:postgresql

org.postgresql.ds.PGSimpleDataSource

:sybase

com.sybase.jdbcx.SybDataSource

+
+

You can found more information and documentation about hikari-cp here: +https://github.com/tomekw/hikari-cp

+
+
+

Now, the new created datasource should be used like a plain dbspec for creating connections:

+
+
+
+
(with-open [conn (jdbc/connection ds)]
+  (do-stuff conn))
+
+
+
+
+
+
+

FAQ

+
+
+

Why another JDBC wrapper?

+
+

This is an incomplete list of reasons:

+
+
+
    +
  • +

    Connection management in clojure.jdbc is simple and explicit. However, java.jdbc does not make +diferentiation between a connection or dbspec hash map. At first glace it seems more flexible, +but it requires huge additional complexity and boilerplate code on each function that receives +a connection. If you are curious, take a look at the with-db-connection implementation of +java.jdbc and compare it with clojure.jdbc: it will get you a good idea of the hidden and +unnecessary complexity found in java.jdbc.

    +
  • +
  • +

    clojure.jdbc comes with proper transaction management with full support for nested transactions, +and plugable transaction strategies. In contrast, java.jdbc comes with one unique strategy, that +does not support subtransactions (it flattens all nested transactions in one unique giving you +false security when you wraps you code in a transaction).

    +
  • +
  • +

    clojure.jdbc has native support for connection pools.

    +
  • +
  • +

    clojure.jdbc has first class documentation.

    +
  • +
  • +

    clojure.jdbc has a simpler implementation than java.jdbc without unnecesary code duplication. A +good example are the crud methods of java.jdbc: all them repeats the transaction logic implicitly. +In contrast, clojure.jdbc, if you want a transaction, you should wrap your code in a transaction +context explicitly.

    +
  • +
+
+
+
+

Does clojure.jdbc have better performance than java.jdbc?

+
+

Mostly Yes, clojure.jdbc by default has better performance than java.jdbc. You can +run the micro benchmark code in your environment with: lein with-profile bench run

+
+
+

In my environments, the results are:

+
+
+
+
[3/5.0.5]niwi@niwi:~/clojure.jdbc> lein with-profile bench run
+Simple query without connection overhead.
+java.jdbc:
+"Elapsed time: 673.890131 msecs"
+clojure.jdbc:
+"Elapsed time: 450.329706 msecs"
+Simple query with connection overhead.
+java.jdbc:
+"Elapsed time: 2490.233925 msecs"
+clojure.jdbc:
+"Elapsed time: 2239.524395 msecs"
+Simple query with transaction.
+java.jdbc:
+"Elapsed time: 532.151667 msecs"
+clojure.jdbc:
+"Elapsed time: 602.482932 msecs"
+
+
+
+
+

Why does clojure.jdbc not include a DSL for working with SQL as java.jdbc 0.3 does?

+
+

clojure.jdbc is a wrapper for the Java JDBC interface. It doesn’t intend to provide helpers +to avoid SQL usage. There are already plenty of DSLs for working with SQL. +clojure.jdbc will not reinvent the wheel.

+
+
+

This is an incomplete list of Clojure DSLs for SQL:

+
+ +
+
+

Is this a fork of java.jdbc?

+
+

No. It is an alternative implementation.

+
+
+
+
+
+

Developers Guide

+
+
+

Philosophy

+
+

Five most important rules:

+
+
+
    +
  • +

    Beautiful is better than ugly.

    +
  • +
  • +

    Explicit is better than implicit.

    +
  • +
  • +

    Simple is better than complex.

    +
  • +
  • +

    Complex is better than complicated.

    +
  • +
  • +

    Readability counts.

    +
  • +
+
+
+

All contributions to clojure.jdbc should keep these important rules in mind.

+
+
+
+

Contributing

+
+

Unlike Clojure and other Clojure contributed libraries clojure.jdbc does not have many +restrictions for contributions. Just open an issue or pull request.

+
+
+
+

Source Code

+
+

clojure.jdbc is open source and can be found on +github.

+
+
+

You can clone the public repository with this command:

+
+
+
+
git clone https://github.com/funcool/clojure.jdbc
+
+
+
+
+

Run tests

+
+

For running tests just execute this:

+
+
+
+
lein test
+
+
+
+

You should have postgresql up and running with a current user created with trust access mode +activated for this user and test db already created.

+
+
+
+

License

+
+

clojure.jdbc is writen from scratch and is licensed under Apache 2.0 license:

+
+
+
+
Copyright (c) 2013-2015 Andrey Antukh <niwi@niwi.nz>
+
+Licensed under the Apache License, Version 2.0 (the "License")
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+
+
+

You can see the full license in the LICENSE file located in the root of the project +repo.

+
+
+

Additionaly, I want to give thanks to the java.jdbc developers for their good +initial work. Some intial ideas for clojure.jdbc are taken from that project.

+
+
+
+
+
+ + + \ No newline at end of file diff --git a/project.clj b/project.clj deleted file mode 100644 index 5bf5587..0000000 --- a/project.clj +++ /dev/null @@ -1,30 +0,0 @@ -(defproject funcool/clojure.jdbc "0.9.0" - :description "clojure.jdbc is a library for low level, jdbc-based database access." - :url "http://github.com/niwibe/clojure.jdbc" - :license {:name "Apache 2.0" - :url "http://www.apache.org/licenses/LICENSE-2.0.txt"} - :dependencies [[org.clojure/clojure "1.8.0" :scope "provided"]] - :javac-options ["-target" "1.7" "-source" "1.7" "-Xlint:-options"] - :profiles - {:dev - {:dependencies [[com.h2database/h2 "1.4.192"] - [org.postgresql/postgresql "9.4.1209.jre7"] - [hikari-cp "1.7.3"] - [cheshire "5.6.3"]] - :codeina {:sources ["src"] - :exclude [jdbc.impl - jdbc.transaction - jdbc.types] - :reader :clojure - :target "doc/dist/latest/api" - :src-uri "http://github.com/niwibe/clojure.jdbc/blob/master/" - :src-uri-prefix "#L"} - :plugins [[lein-ancient "0.6.10"] - [funcool/codeina "0.5.0"]]} - :bench {:source-paths ["bench/"] - :main jdbc.bench - :global-vars {*warn-on-reflection* true - *unchecked-math* :warn-on-boxed} - :dependencies [[org.clojure/java.jdbc "0.5.8"] - [com.h2database/h2 "1.4.192"] - [criterium "0.4.4"]]}}) diff --git a/src/jdbc/constants.clj b/src/jdbc/constants.clj deleted file mode 100644 index bc6aed8..0000000 --- a/src/jdbc/constants.clj +++ /dev/null @@ -1,44 +0,0 @@ -;; Copyright 2014-2015 Andrey Antukh -;; -;; Licensed under the Apache License, Version 2.0 (the "License") -;; you may not use this file except in compliance with the License. -;; You may obtain a copy of the License at -;; -;; http://www.apache.org/licenses/LICENSE-2.0 -;; -;; Unless required by applicable law or agreed to in writing, software -;; distributed under the License is distributed on an "AS IS" BASIS, -;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -;; See the License for the specific language governing permissions and -;; limitations under the License. - -(ns jdbc.constants - "Namespace that contains a public constants - used around this library." - (:import java.sql.ResultSet - java.sql.Connection)) - -(def ^{:doc "ResultSet keyword constants" :static true} - resultset-options - {:forward-only ResultSet/TYPE_FORWARD_ONLY ;; Type - :scroll-insensitive ResultSet/TYPE_SCROLL_INSENSITIVE - :scroll-sensitive ResultSet/TYPE_SCROLL_SENSITIVE - - ;; Cursors - :hold ResultSet/HOLD_CURSORS_OVER_COMMIT - :close ResultSet/CLOSE_CURSORS_AT_COMMIT - - ;; Concurrency - :read-only ResultSet/CONCUR_READ_ONLY - :updatable ResultSet/CONCUR_UPDATABLE}) - -(def ^{:doc "Transaction isolation levels" :static true} - isolation-levels - {:none Connection/TRANSACTION_NONE - :read-uncommitted Connection/TRANSACTION_READ_UNCOMMITTED - :read-committed Connection/TRANSACTION_READ_COMMITTED - :repeatable-read Connection/TRANSACTION_REPEATABLE_READ - :serializable Connection/TRANSACTION_SERIALIZABLE}) - - - diff --git a/src/jdbc/core.clj b/src/jdbc/core.clj deleted file mode 100644 index 04369f4..0000000 --- a/src/jdbc/core.clj +++ /dev/null @@ -1,248 +0,0 @@ -;; Copyright 2014-2016 Andrey Antukh -;; -;; Licensed under the Apache License, Version 2.0 (the "License") -;; you may not use this file except in compliance with the License. -;; You may obtain a copy of the License at -;; -;; http://www.apache.org/licenses/LICENSE-2.0 -;; -;; Unless required by applicable law or agreed to in writing, software -;; distributed under the License is distributed on an "AS IS" BASIS, -;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -;; See the License for the specific language governing permissions and -;; limitations under the License. - -(ns jdbc.core - "Alternative implementation of jdbc wrapper for clojure." - (:require [clojure.string :as str] - [jdbc.types :as types] - [jdbc.impl :as impl] - [jdbc.proto :as proto] - [jdbc.resultset :refer [result-set->lazyseq result-set->vector]] - [jdbc.transaction :as tx] - [jdbc.constants :as constants]) - (:import java.sql.PreparedStatement - java.sql.ResultSet - java.sql.Connection)) - -(def ^{:doc "Default transaction strategy implementation." - :no-doc true - :dynamic true} - *default-tx-strategy* (impl/transaction-strategy)) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Main public api. -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(defn connection - "Creates a connection to a database. As parameter accepts: - - - dbspec map containing connection parameters - - dbspec map containing a datasource (deprecated) - - URI or string (interpreted as uri) - - DataSource instance - - The dbspec map has this possible variants: - - Classic approach: - - - `:subprotocol` -> (required) string that represents a vendor name (ex: postgresql) - - `:subname` -> (required) string that represents a database name (ex: test) - (many others options that are pased directly as driver parameters) - - Pretty format: - - - `:vendor` -> (required) string that represents a vendor name (ex: postgresql) - - `:name` -> (required) string that represents a database name (ex: test) - - `:host` -> (optional) string that represents a database hostname (default: 127.0.0.1) - - `:port` -> (optional) long number that represents a database port (default: driver default) - (many others options that are pased directly as driver parameters) - - URI or String format: `vendor://user:password@host:post/dbname?param1=value` - - Additional options: - - - `:schema` -> string that represents a schema name (default: nil) - - `:read-only` -> boolean for mark entire connection read only. - - `:isolation-level` -> keyword that represents a isolation level (`:none`, `:read-committed`, - `:read-uncommitted`, `:repeatable-read`, `:serializable`) - - Opions can be passed as part of dbspec map, or as optional second argument. - For more details, see documentation." - ([dbspec] (connection dbspec {})) - ([dbspec options] - (let [^Connection conn (proto/connection dbspec) - options (merge (when (map? dbspec) dbspec) options)] - - ;; Set readonly flag if it found on the options map - (some->> (:read-only options) - (.setReadOnly conn)) - - ;; Set the concrete isolation level if it found - ;; on the options map - (some->> (:isolation-level options) - (get constants/isolation-levels) - (.setTransactionIsolation conn)) - - ;; Set the schema if it found on the options map - (some->> (:schema options) - (.setSchema conn)) - - (let [tx-strategy (:tx-strategy options *default-tx-strategy*) - metadata {:tx-strategy tx-strategy}] - (with-meta (types/->connection conn) metadata))))) - -(defn prepared-statement? - "Check if specified object is prepared statement." - [obj] - (instance? PreparedStatement obj)) - -(defn prepared-statement - "Given a string or parametrized sql in sqlvec format - return an instance of prepared statement." - ([conn sqlvec] (prepared-statement conn sqlvec {})) - ([conn sqlvec options] - (let [conn (proto/connection conn)] - (proto/prepared-statement sqlvec conn options)))) - -(defn execute - "Execute a query and return a number of rows affected. - - (with-open [conn (jdbc/connection dbspec)] - (jdbc/execute conn \"create table foo (id integer);\")) - - This function also accepts sqlvec format." - ([conn q] (execute conn q {})) - ([conn q opts] - (let [rconn (proto/connection conn)] - (proto/execute q rconn opts)))) - -(defn fetch - "Fetch eagerly results executing a query. - - This function returns a vector of records (default) or - rows (depending on specified opts). Resources are relased - inmediatelly without specific explicit action for it. - - It accepts a sqlvec, plain sql or prepared statement - as query parameter." - ([conn q] (fetch conn q {})) - ([conn q opts] - (let [rconn (proto/connection conn)] - (proto/fetch q rconn opts)))) - -(defn fetch-one - "Fetch eagerly one restult executing a query." - ([conn q] (fetch-one conn q {})) - ([conn q opts] - (first (fetch conn q opts)))) - -(defn fetch-lazy - "Fetch lazily results executing a query. - - (with-open [cursor (jdbc/fetch-lazy conn sql)] - (doseq [item (jdbc/cursor->lazyseq cursor)] - (do-something-with item))) - - This function returns a cursor instead of result. - You should explicitly close the cursor at the end of - iteration for release resources." - ([conn q] (fetch-lazy conn q {})) - ([conn q opts] - (let [^Connection conn (proto/connection conn) - ^PreparedStatement stmt (proto/prepared-statement q conn opts)] - (types/->cursor stmt)))) - -(def ^{:doc "Deprecated alias for backward compatibility." - :deprecated true} - lazy-query fetch-lazy) - -(defn cursor->lazyseq - "Transform a cursor in a lazyseq. - - The returned lazyseq will return values until a cursor - is closed or all values are fetched." - ([cursor] (impl/cursor->lazyseq cursor {})) - ([cursor opts] (impl/cursor->lazyseq cursor opts))) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Transactions -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(defn atomic-apply - "Wrap function in one transaction. - This function accepts as a parameter a transaction strategy. If no one - is specified, `DefaultTransactionStrategy` is used. - - With `DefaultTransactionStrategy`, if current connection is already in - transaction, it uses truly nested transactions for properly handle it. - The availability of this feature depends on database support for it. - - (with-open [conn (jdbc/connection)] - (atomic-apply conn (fn [conn] (execute! conn 'DROP TABLE foo;')))) - - For more idiomatic code, you should use `atomic` macro. - - Depending on transaction strategy you are using, this function can accept - additional parameters. The default transaction strategy exposes two additional - parameters: - - - `:isolation-level` - set isolation level for this transaction - - `:read-only` - set current transaction to read only" - [conn func & [{:keys [savepoints strategy] :or {savepoints true} :as opts}]] - (let [metadata (meta conn) - tx-strategy (or strategy - (:tx-strategy metadata) - *default-tx-strategy*)] - (when (and (:transaction metadata) (not savepoints)) - (throw (RuntimeException. "Savepoints explicitly disabled."))) - - (let [conn (proto/begin! tx-strategy conn opts) - metadata (meta conn)] - (try - (let [returnvalue (func conn)] - (proto/commit! tx-strategy conn opts) - returnvalue) - (catch Throwable t - (proto/rollback! tx-strategy conn opts) - (throw t)))))) - -(defmacro atomic - "Creates a context that evaluates in transaction (or nested transaction). - This is a more idiomatic way to execute some database operations in - atomic way. - - (jdbc/atomic conn - (jdbc/execute conn \"DROP TABLE foo;\") - (jdbc/execute conn \"DROP TABLE bar;\")) - - Also, you can pass additional options to transaction: - - (jdbc/atomic conn {:read-only true} - (jdbc/execute conn \"DROP TABLE foo;\") - (jdbc/execute conn \"DROP TABLE bar;\")) - " - [conn & body] - (if (map? (first body)) - `(let [func# (fn [c#] (let [~conn c#] ~@(next body)))] - (atomic-apply ~conn func# ~(first body))) - `(let [func# (fn [c#] (let [~conn c#] ~@body))] - (atomic-apply ~conn func#)))) - -(defn set-rollback! - "Mark a current connection for rollback. - - It ensures that on the end of the current transaction - instead of commit changes, rollback them. - - This function should be used inside of a transaction - block, otherwise this function does nothing. - - (jdbc/atomic conn - (make-some-queries-without-changes conn) - (jdbc/set-rollback! conn)) - " - [conn] - (let [metadata (meta conn)] - (when-let [rollback-flag (:rollback metadata)] - (reset! rollback-flag true)))) diff --git a/src/jdbc/impl.clj b/src/jdbc/impl.clj deleted file mode 100644 index 4cfac7a..0000000 --- a/src/jdbc/impl.clj +++ /dev/null @@ -1,325 +0,0 @@ -;; Copyright 2014-2015 Andrey Antukh -;; -;; Licensed under the Apache License, Version 2.0 (the "License") -;; you may not use this file except in compliance with the License. -;; You may obtain a copy of the License at -;; -;; http://www.apache.org/licenses/LICENSE-2.0 -;; -;; Unless required by applicable law or agreed to in writing, software -;; distributed under the License is distributed on an "AS IS" BASIS, -;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -;; See the License for the specific language governing permissions and -;; limitations under the License. - -(ns jdbc.impl - "Protocol implementations. Mainly private api" - (:require [clojure.string :as str] - [clojure.walk :as walk] - [jdbc.proto :as proto] - [jdbc.types :as types] - [jdbc.resultset :refer [result-set->lazyseq result-set->vector]] - [jdbc.constants :as constants]) - (:import java.net.URI - java.util.Properties - java.sql.Connection - java.sql.DriverManager - java.sql.PreparedStatement)) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Connection constructors implementation -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(declare ^:private dbspec->connection) -(declare ^:private uri->dbspec) -(declare ^:private querystring->map) -(declare ^:private map->properties) - -(extend-protocol proto/IConnection - java.sql.Connection - (connection [this] this) - - javax.sql.DataSource - (connection [ds] - (.getConnection ds)) - - clojure.lang.IPersistentMap - (connection [dbspec] - (dbspec->connection dbspec)) - - java.net.URI - (connection [uri] - (-> (uri->dbspec uri) - (dbspec->connection))) - - java.lang.String - (connection [uri] - (proto/connection (java.net.URI. uri)))) - -(defn- dbspec->connection - "Create a connection instance from dbspec." - [{:keys [subprotocol subname user password - name vendor host port datasource classname] - :as dbspec}] - (cond - (and name vendor) - (let [host (or host "127.0.0.1") - port (if port (str ":" port) "") - dbspec (-> (dissoc dbspec :name :vendor :host :port) - (assoc :subprotocol vendor - :subname (str "//" host port "/" name)))] - (dbspec->connection dbspec)) - - (and subprotocol subname) - (let [url (format "jdbc:%s:%s" subprotocol subname) - options (dissoc dbspec :subprotocol :subname)] - - (when classname - (Class/forName classname)) - - (DriverManager/getConnection url (map->properties options))) - - ;; NOTE: only for backward compatibility - (and datasource) - (proto/connection datasource) - - :else - (throw (IllegalArgumentException. "Invalid dbspec format")))) - -(defn uri->dbspec - "Parses a dbspec as uri into a plain dbspec. This function - accepts `java.net.URI` or `String` as parameter." - [^URI uri] - (let [host (.getHost uri) - port (.getPort uri) - path (.getPath uri) - scheme (.getScheme uri) - userinfo (.getUserInfo uri)] - (merge - {:subname (if (pos? port) - (str "//" host ":" port path) - (str "//" host path)) - :subprotocol scheme} - (when userinfo - (let [[user password] (str/split userinfo #":")] - {:user user :password password})) - (querystring->map uri)))) - -(defn- querystring->map - "Given a URI instance, return its querystring as - plain map with parsed keys and values." - [^URI uri] - (when-let [^String query (.getQuery uri)] - (when-not (str/blank? query) - (->> (for [^String kvs (.split query "&")] (into [] (.split kvs "="))) - (into {}) - (walk/keywordize-keys))))) - -(defn- map->properties - "Convert hash-map to java.utils.Properties instance. This method is used - internally for convert dbspec map to properties instance, but it can - be usefull for other purposes." - [data] - (let [p (Properties.)] - (dorun (map (fn [[k v]] (.setProperty p (name k) (str v))) (seq data))) - p)) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; IExecute implementation -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(extend-protocol proto/IExecute - java.lang.String - (execute [sql conn opts] - (with-open [^PreparedStatement stmt (.createStatement ^Connection conn)] - (.addBatch stmt ^String sql) - (seq (.executeBatch stmt)))) - - clojure.lang.IPersistentVector - (execute [sqlvec conn opts] - (with-open [^PreparedStatement stmt (proto/prepared-statement sqlvec conn opts)] - (let [counts (.executeUpdate stmt)] - (if (:returning opts) - (with-open [rs (.getGeneratedKeys stmt)] - (result-set->vector conn rs opts)) - counts)))) - - PreparedStatement - (execute [^PreparedStatement stmt ^Connection conn opts] - (.executeUpdate stmt))) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; IFetch implementation -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(extend-protocol proto/IFetch - java.lang.String - (fetch [^String sql ^Connection conn opts] - (with-open [^PreparedStatement stmt (proto/prepared-statement sql conn opts)] - (let [^ResultSet rs (.executeQuery stmt)] - (result-set->vector conn rs opts)))) - - clojure.lang.IPersistentVector - (fetch [^clojure.lang.IPersistentVector sqlvec ^Connection conn opts] - (with-open [^PreparedStatement stmt (proto/prepared-statement sqlvec conn opts)] - (let [^ResultSet rs (.executeQuery stmt)] - (result-set->vector conn rs opts)))) - - PreparedStatement - (fetch [^PreparedStatement stmt ^Connection conn opts] - (let [^ResultSet rs (.executeQuery stmt)] - (result-set->vector conn rs opts)))) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; PreparedStatement constructors implementation -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(declare ^:private prepared-statement*) - -(extend-protocol proto/IPreparedStatement - String - (prepared-statement [sql conn options] - (prepared-statement* conn [sql] options)) - - clojure.lang.IPersistentVector - (prepared-statement [sql-with-params conn options] - (prepared-statement* conn sql-with-params options)) - - PreparedStatement - (prepared-statement [o _ _] o)) - -(defn- prepared-statement* - "Given connection and query, return a prepared statement." - ([^Connection conn sqlvec] (prepared-statement* conn sqlvec {})) - ([^Connection conn sqlvec {:keys [result-type result-concurency fetch-size - max-rows holdability returning] - :or {result-type :forward-only - result-concurency :read-only} - :as options}] - (let [sqlvec (if (string? sqlvec) [sqlvec] sqlvec) - ^String sql (first sqlvec) - params (rest sqlvec) - - ^PreparedStatement - stmt (cond - returning - (if (or (= :all returning) (true? returning)) - (.prepareStatement conn sql java.sql.Statement/RETURN_GENERATED_KEYS) - (.prepareStatement conn sql - #^"[Ljava.lang.String;" (into-array String (mapv name returning)))) - - holdability - (.prepareStatement conn sql - (result-type constants/resultset-options) - (result-concurency constants/resultset-options) - (holdability constants/resultset-options)) - :else - (.prepareStatement conn sql - (result-type constants/resultset-options) - (result-concurency constants/resultset-options)))] - - ;; Set fetch-size and max-rows if provided by user - (when fetch-size (.setFetchSize stmt fetch-size)) - (when max-rows (.setMaxRows stmt max-rows)) - (when (seq params) - (->> params - (map-indexed #(proto/set-stmt-parameter! %2 conn stmt (inc %1))) - (dorun))) - stmt))) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Default implementation for type conversions -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(extend-protocol proto/ISQLType - Object - (as-sql-type [this conn] this) - (set-stmt-parameter! [this conn ^PreparedStatement stmt ^Long index] - (.setObject stmt index (proto/as-sql-type this conn))) - - nil - (as-sql-type [this conn] nil) - (set-stmt-parameter! [this conn ^PreparedStatement stmt index] - (.setObject stmt index (proto/as-sql-type nil conn)))) - - -(extend-protocol proto/ISQLResultSetReadColumn - Object - (from-sql-type [this conn metadata i] this) - - Boolean - (from-sql-type [this conn metadata i] (= true this)) - - nil - (from-sql-type [this conn metadata i] nil)) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Transactions -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(defn- begin* - [conn opts] - (let [^Connection rconn (proto/connection conn) - metadata (-> (meta conn) - (assoc :rollback (atom false) - :prev-isolation (.getTransactionIsolation rconn) - :prev-readonly (.isReadOnly rconn)))] - (if (:transaction metadata) - (let [sp (.setSavepoint rconn)] - (with-meta conn - (assoc metadata :savepoint sp :transaction true))) - - (let [prev-autocommit (.getAutoCommit rconn)] - (.setAutoCommit rconn false) - (when-let [isolation (:isolation-level opts)] - (.setTransactionIsolation rconn (get constants/isolation-levels isolation))) - (when-let [read-only (:read-only opts)] - (.setReadOnly rconn read-only)) - (with-meta conn - (assoc metadata :prev-autocommit prev-autocommit :transaction true)))))) - -(defn- commit* - [ts conn opts] - (let [^Connection rconn (proto/connection conn) - metadata (meta conn)] - ;; In case on commit and rollback flag is set, commit action - ;; should be ignored and rollback will performed. - (if @(:rollback metadata) - (proto/rollback! ts conn opts) - (if-let [savepoint (:savepoint metadata)] - (.releaseSavepoint rconn savepoint) - (do - (.commit rconn) - (.setAutoCommit rconn (:prev-autocommit metadata)) - (.setTransactionIsolation rconn (:prev-isolation metadata)) - (.setReadOnly rconn (:prev-readonly metadata))))))) - -(defn- rollback* - [conn opts] - (let [^Connection rconn (proto/connection conn) - metadata (meta conn)] - (if-let [savepoint (:savepoint metadata)] - (.rollback rconn savepoint) - (do - (.rollback rconn) - (.setAutoCommit rconn (:prev-autocommit metadata)) - (.setTransactionIsolation rconn (:prev-isolation metadata)) - (.setReadOnly rconn (:prev-readonly metadata)))))) - -(defn transaction-strategy - [] - (reify proto/ITransactionStrategy - (begin! [_ conn opts] (begin* conn opts)) - (rollback! [_ conn opts] (rollback* conn opts)) - (commit! [ts conn opts] (commit* ts conn opts)))) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Lazy Query -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(defn cursor->lazyseq - [cursor opts] - (let [^PreparedStatement stmt (.-stmt cursor) - ^Connection conn (.getConnection stmt) - ^ResultSet rs (.executeQuery stmt)] - (result-set->lazyseq conn rs opts))) diff --git a/src/jdbc/meta.clj b/src/jdbc/meta.clj deleted file mode 100644 index 0f772fb..0000000 --- a/src/jdbc/meta.clj +++ /dev/null @@ -1,111 +0,0 @@ -;; Copyright 2014-2015 Andrey Antukh -;; -;; Licensed under the Apache License, Version 2.0 (the "License") -;; you may not use this file except in compliance with the License. -;; You may obtain a copy of the License at -;; -;; http://www.apache.org/licenses/LICENSE-2.0 -;; -;; Unless required by applicable law or agreed to in writing, software -;; distributed under the License is distributed on an "AS IS" BASIS, -;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -;; See the License for the specific language governing permissions and -;; limitations under the License. - -(ns jdbc.meta - "Connection metadata access methods." - (:require [jdbc.types :as types] - [jdbc.proto :as proto])) - -(defn vendor-name - "Get connection vendor name." - [c] - (let [^java.sql.DatabaseMetaData meta (:metadata c)] - (.getDatabaseProductName meta))) - -(defn catalog-name - "Given a connection, get a catalog name." - [ c] - (let [^java.sql.Connection conn (proto/connection c)] - (.getCatalog conn))) - -(defn schema-name - "Given a connection, get a schema name." - [c] - (let [^java.sql.Connection conn (proto/connection c)] - (.getSchema conn))) - -(defn is-readonly? - "Returns true if a current connection is - in read-only model." - [c] - (let [^java.sql.Connection conn (proto/connection c)] - (.isReadOnly conn))) - -(defn is-valid? - "Given a connection, return true if connection - has not ben closed it still valid." - ([c] - (is-valid? c 0)) - ([c ^long timeout] - (let [^java.sql.Connection conn (proto/connection c)] - (.isValid conn timeout)))) - -(defn network-timeout - "Given a connection, get network timeout." - [c] - (let [^java.sql.Connection conn (proto/connection c)] - (.getNetworkTimeout conn))) - -(defn isolation-level - "Given a connection, get a current isolation level." - [c] - (let [^java.sql.Connection conn (proto/connection c) - ilvalue (.getTransactionIsolation conn)] - (condp = ilvalue - java.sql.Connection/TRANSACTION_READ_UNCOMMITTED :read-commited - java.sql.Connection/TRANSACTION_REPEATABLE_READ :repeatable-read - java.sql.Connection/TRANSACTION_SERIALIZABLE :serializable - :none))) - -(defn db-major-version - "Given a connection, return a database major - version number." - [c] - (let [^java.sql.DatabaseMetaData meta (proto/get-database-metadata c)] - (.getDatabaseMajorVersion meta))) - -(defn db-minor-version - "Given a connection, return a database minor - version number." - [c] - (let [^java.sql.DatabaseMetaData meta (proto/get-database-metadata c)] - (.getDatabaseMinorVersion meta))) - -(defn db-product-name - "Given a connection, return a database product - name from it metadata." - [c] - (let [^java.sql.DatabaseMetaData meta (proto/get-database-metadata c)] - (.getDatabaseProductName meta))) - -(defn db-product-version - "Given a connection, return a database product - version from it metadata." - [c] - (let [^java.sql.DatabaseMetaData meta (proto/get-database-metadata c)] - (.getDatabaseProductVersion meta))) - -(defn driver-name - "Given a connection, return a current driver name - used for this connection." - [c] - (let [^java.sql.DatabaseMetaData meta (proto/get-database-metadata c)] - (.getDriverName meta))) - -(defn driver-version - "Given a connection, return a current driver version - used for this connection." - [c] - (let [^java.sql.DatabaseMetaData meta (proto/get-database-metadata c)] - (.getDriverVersion meta))) diff --git a/src/jdbc/proto.clj b/src/jdbc/proto.clj deleted file mode 100644 index 04fb6e8..0000000 --- a/src/jdbc/proto.clj +++ /dev/null @@ -1,63 +0,0 @@ -;; Copyright 2014-2016 Andrey Antukh -;; -;; Licensed under the Apache License, Version 2.0 (the "License") -;; you may not use this file except in compliance with the License. -;; You may obtain a copy of the License at -;; -;; http://www.apache.org/licenses/LICENSE-2.0 -;; -;; Unless required by applicable law or agreed to in writing, software -;; distributed under the License is distributed on an "AS IS" BASIS, -;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -;; See the License for the specific language governing permissions and -;; limitations under the License. - -(ns jdbc.proto) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Internal Protocols -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(defprotocol IConnection - "Represents a connection like object that wraps - a raw jdbc connection with some other data." - (connection [_] "Create or obtain existing connection")) - -(defprotocol IExecute - (execute [q conn opts] "Execute a query and return a number of rows affected.")) - -(defprotocol IFetch - (fetch [q conn opts] "Fetch eagerly results executing query.")) - -(defprotocol IDatabaseMetadata - "Allows uniform database metadata extraction." - (get-database-metadata [_] "Get metadata instance.")) - -(defprotocol IPreparedStatement - "Responsible of building prepared statements." - (prepared-statement [_ connection options] "Create a prepared statement.")) - -(defprotocol ITransactionStrategy - (begin! [_ conn opts] "Starts a transaction and return a connection instance.") - (rollback! [_ conn opts] "Rollbacks a transaction. Returns nil.") - (commit! [_ conn opts] "Commits a transaction. Returns nil.")) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; SQL Extension Protocols -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(defprotocol ISQLType - "Protocol that exposes uniform way for convert user - types to sql/jdbc compatible types and uniform set parameters - to prepared statement instance. Default implementation available - for Object and nil values." - - (as-sql-type [_ conn] "Convert user type to sql type.") - (set-stmt-parameter! [this conn stmt index] "Set value to statement.")) - -(defprotocol ISQLResultSetReadColumn - "Protocol that exposes uniform way to convert values - obtained from result set to user types. Default implementation - available for Object, Boolean, and nil." - - (from-sql-type [_ conn metadata index] "Convert sql type to user type.")) diff --git a/src/jdbc/resultset.clj b/src/jdbc/resultset.clj deleted file mode 100644 index 16c51aa..0000000 --- a/src/jdbc/resultset.clj +++ /dev/null @@ -1,72 +0,0 @@ -;; Copyright 2014-2015 Andrey Antukh -;; -;; Licensed under the Apache License, Version 2.0 (the "License") -;; you may not use this file except in compliance with the License. -;; You may obtain a copy of the License at -;; -;; http://www.apache.org/licenses/LICENSE-2.0 -;; -;; Unless required by applicable law or agreed to in writing, software -;; distributed under the License is distributed on an "AS IS" BASIS, -;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -;; See the License for the specific language governing permissions and -;; limitations under the License. - -(ns jdbc.resultset - "ResultSet conversion functions." - (:require [clojure.string :as str] - [jdbc.proto :as proto]) - (:import java.sql.PreparedStatement - java.sql.ResultSetMetaData - java.sql.ResultSet)) - -(defn result-set->lazyseq - "Function that wraps result in a lazy seq. This function - is part of public api but can not be used directly (you should pass - this function as parameter to `query` function). - - Required parameters: - rs: ResultSet instance. - - Optional named parameters: - :identifiers -> function that is applied for column name - when as-arrays? is false - :as-rows? -> by default this function return a lazy seq of - records (map), but in certain circumstances you - need results as a lazy-seq of vectors. With this keywork - parameter you can enable this behavior and return a lazy-seq - of vectors instead of records (maps). - " - [conn ^ResultSet rs {:keys [identifiers as-rows? header?] - :or {identifiers str/lower-case - as-rows? false - header? false} - :as options}] - (let [^ResultSetMetaData metadata (.getMetaData rs) - idseq (range 1 (inc (.getColumnCount metadata))) - labels (mapv (fn [^long i] (.getColumnLabel metadata i)) idseq) - keyseq (mapv (comp keyword identifiers) labels) - values (fn [] - (mapv (fn [^long i] - (-> (.getObject rs i) - (proto/from-sql-type conn metadata i))) - idseq)) - rows (fn thisfn [] - (when (.next rs) - (cons (values) (lazy-seq (thisfn))))) - records (fn thisfn [] - (when (.next rs) - (-> (zipmap keyseq (values)) - (cons (lazy-seq (thisfn)))))) - header (mapv identifiers labels)] - (if-not as-rows? - (records) - (if-not header? - (rows) - (cons header (lazy-seq (rows))))))) - -(defn result-set->vector - "Function that evaluates a result into one clojure persistent - vector. Accept same parameters as `result-set->lazyseq`." - [conn ^ResultSet rs options] - (vec (result-set->lazyseq conn rs options))) diff --git a/src/jdbc/transaction.clj b/src/jdbc/transaction.clj deleted file mode 100644 index 2c03ebb..0000000 --- a/src/jdbc/transaction.clj +++ /dev/null @@ -1,165 +0,0 @@ -;; Copyright 2014 Andrey Antukh -;; -;; Licensed under the Apache License, Version 2.0 (the "License") -;; you may not use this file except in compliance with the License. -;; You may obtain a copy of the License at -;; -;; http://www.apache.org/licenses/LICENSE-2.0 -;; -;; Unless required by applicable law or agreed to in writing, software -;; distributed under the License is distributed on an "AS IS" BASIS, -;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -;; See the License for the specific language governing permissions and -;; limitations under the License. - -(ns jdbc.transaction - "Transactions support for clojure.jdbc - - WARNING: This namespace is deprecated and will be removed in - clojure.jdbc 0.6.0." - (:require [jdbc.constants :as constants] - [jdbc.proto :as proto] - [jdbc.impl :as impl]) - (:import java.sql.Connection)) - -(def ITransactionStrategy proto/ITransactionStrategy) -(def begin! proto/begin!) -(def rollback! proto/rollback!) -(def commit! proto/commit!) - -;; (defprotocol ITransactionStrategy -;; (begin! [_ conn opts] "Starts a transaction and return a connection instance.") -;; (rollback! [_ conn opts] "Rollbacks a transaction. Returns nil.") -;; (commit! [_ conn opts] "Commits a transaction. Returns nil.")) - -(def ^{:doc "Default transaction strategy implementation." - :dynamic true} - *default-tx-strategy* (impl/transaction-strategy)) - -(defn wrap-transaction-strategy - "Simple helper function that associate a strategy - to a connection and return a new connection object - with wrapped stragy. - - Example: - - (let [conn (wrap-transaction-strategy simplecon (MyStrategy.))] - (use-your-new-conn conn)) - " - [conn strategy] - (let [metadata (meta conn)] - (with-meta conn (assoc metadata :tx-strategy strategy)))) - -(defn set-rollback! - "Mark a current connection for rollback. - - It ensures that on the end of the current transaction - instead of commit changes, rollback them. - - This function should be used inside of a transaction - block, otherwise this function does nothing. - - Example: - - (with-transaction conn - (make-some-queries-without-changes conn) - (set-rollback! conn)) - " - [conn] - (let [metadata (meta conn)] - (when-let [rollback-flag (:rollback metadata)] - (reset! rollback-flag true)))) - -(defn unset-rollback! - "Revert flag setted by `set-rollback!` function. - This function should be used inside of a transaction - block, otherwise this function does nothing." - [conn] - (let [metadata (meta conn)] - (when-let [rollback-flag (:rollback metadata)] - (reset! rollback-flag false)))) - -(defn is-rollback-set? - "Check if a current connection in one transaction - is marked for rollback. - - This should be used in one transaction, in other case this - function always return false." - [conn] - (let [metadata (meta conn)] - (if-let [rollback-flag (:rollback metadata)] - (deref rollback-flag) - false))) - -(defn call-in-transaction - "Wrap function in one transaction. - This function accepts as a parameter a transaction strategy. If no one - is specified, `DefaultTransactionStrategy` is used. - - With `DefaultTransactionStrategy`, if current connection is already in - transaction, it uses truly nested transactions for properly handle it. - The availability of this feature depends on database support for it. - - Example: - - (with-connection dbspec conn - (call-in-transaction conn (fn [conn] (execute! conn 'DROP TABLE foo;')))) - - For more idiomatic code, you should use `with-transaction` macro. - - Depending on transaction strategy you are using, this function can accept - additional parameters. The default transaction strategy exposes two additional - parameters: - - - `:isolation-level` - set isolation level for this transaction - - `:read-only` - set current transaction to read only - " - [conn func & [{:keys [savepoints strategy] :or {savepoints true} :as opts}]] - (let [metadata (meta conn) - tx-strategy (or strategy - (:tx-strategy metadata) - *default-tx-strategy*)] - (when (and (:transaction metadata) (not savepoints)) - (throw (RuntimeException. "Savepoints explicitly disabled."))) - - (let [conn (begin! tx-strategy conn opts) - metadata (meta conn)] - (try - (let [returnvalue (func conn)] - (commit! tx-strategy conn opts) - returnvalue) - (catch Throwable t - (rollback! tx-strategy conn opts) - (throw t)))))) - -(defmacro with-transaction-strategy - "Set some transaction strategy connection in the current context scope. - This method not uses thread-local dynamic variables and connection - preserves a transaction strategy throught threads." - [conn strategy & body] - `(let [~conn (wrap-transaction-strategy ~conn ~strategy)] - ~@body)) - -(defmacro with-transaction - "Creates a context that evaluates in transaction (or nested transaction). - This is a more idiomatic way to execute some database operations in - atomic way. - - Example: - - (with-transaction conn - (execute! conn 'DROP TABLE foo;') - (execute! conn 'DROP TABLE bar;')) - - Also, you can pass additional options to transaction: - - (with-transaction conn {:read-only true} - (execute! conn 'DROP TABLE foo;') - (execute! conn 'DROP TABLE bar;')) - " - [conn & body] - (if (map? (first body)) - `(let [func# (fn [c#] (let [~conn c#] ~@(next body)))] - (call-in-transaction ~conn func# ~(first body))) - `(let [func# (fn [c#] (let [~conn c#] ~@body))] - (call-in-transaction ~conn func#)))) diff --git a/src/jdbc/types.clj b/src/jdbc/types.clj deleted file mode 100644 index aa8f16b..0000000 --- a/src/jdbc/types.clj +++ /dev/null @@ -1,51 +0,0 @@ -;; Copyright 2014-2015 Andrey Antukh -;; -;; Licensed under the Apache License, Version 2.0 (the "License") -;; you may not use this file except in compliance with the License. -;; You may obtain a copy of the License at -;; -;; http://www.apache.org/licenses/LICENSE-2.0 -;; -;; Unless required by applicable law or agreed to in writing, software -;; distributed under the License is distributed on an "AS IS" BASIS, -;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -;; See the License for the specific language governing permissions and -;; limitations under the License. - -(ns jdbc.types - (:require [jdbc.proto :as proto] - [jdbc.resultset :refer [result-set->lazyseq]]) - (:import java.sql.Connection - java.sql.ResultSet - java.sql.PreparedStatement)) - -(defn ->connection - "Create a connection wrapper. - - The connection wrapper is need because it - implemens IMeta interface that is mandatory - for transaction management." - [^Connection conn] - (reify - proto/IConnection - (connection [_] conn) - - proto/IDatabaseMetadata - (get-database-metadata [_] - (.getMetaData conn)) - - java.io.Closeable - (close [_] - (.close conn)))) - -(deftype Cursor [stmt] - proto/IConnection - (connection [_] (.getConnection stmt)) - - java.io.Closeable - (close [_] - (.close stmt))) - -(defn ->cursor - [^PreparedStatement stmt] - (Cursor. stmt)) diff --git a/test/jdbc/core_tests.clj b/test/jdbc/core_tests.clj deleted file mode 100644 index e839019..0000000 --- a/test/jdbc/core_tests.clj +++ /dev/null @@ -1,339 +0,0 @@ -(ns jdbc.core-tests - (:import org.postgresql.util.PGobject) - (:require [jdbc.core :as jdbc] - [jdbc.proto :as proto] - [hikari-cp.core :as hikari] - [cheshire.core :as json] - [clojure.test :refer :all])) - -(def h2-dbspec1 {:classname "org.h2.Driver" - :subprotocol "h2" - :subname "/tmp/jdbctest.db"}) - -(def h2-dbspec2 {:subprotocol "h2" - :subname "/tmp/jdbctest.db"}) - -(def h2-dbspec3 {:subprotocol "h2" - :subname "mem:"}) - -(def h2-dbspec4 {:subprotocol "h2" - :subname "mem:" - :isolation-level :serializable}) - -(def pg-dbspec {:subprotocol "postgresql" - :subname "//localhost:5432/test"}) - -(def pg-dbspec-pretty {:vendor "postgresql" - :name "test" - :host "localhost" - :read-only true}) - -(def pg-dbspec-uri-1 "postgresql://localhost:5432/test") -(def pg-dbspec-uri-2 "postgresql://localhost:5432/test?") -(def pg-dbspec-uri-3 "postgresql://localhost:5432/test?foo=bar") - -(deftest datasource-spec - (with-open [ds (hikari/make-datasource {:adapter "h2" :url "jdbc:h2:/tmp/test"})] - (is (instance? javax.sql.DataSource ds)) - (with-open [conn (jdbc/connection ds)] - (let [result (jdbc/fetch conn "SELECT 1 + 1 as foo;")] - (is (= [{:foo 2}] result)))))) - -(deftest db-specs - (let [c1 (jdbc/connection h2-dbspec1) - c2 (jdbc/connection h2-dbspec2) - c3 (jdbc/connection h2-dbspec3) - c4 (jdbc/connection pg-dbspec-pretty) - c5 (jdbc/connection pg-dbspec-uri-1) - c6 (jdbc/connection pg-dbspec-uri-2) - c7 (jdbc/connection pg-dbspec-uri-3)] - (is (satisfies? proto/IConnection c1)) - (is (satisfies? proto/IConnection c2)) - (is (satisfies? proto/IConnection c3)) - (is (satisfies? proto/IConnection c4)) - (is (satisfies? proto/IConnection c5)) - (is (satisfies? proto/IConnection c6)) - (is (satisfies? proto/IConnection c7)))) - -(deftest db-isolation-level-1 - (let [c1 (-> (jdbc/connection h2-dbspec4) - (proto/connection)) - c2 (-> (jdbc/connection h2-dbspec3) - (proto/connection))] - (is (= (.getTransactionIsolation c1) 8)) - (is (= (.getTransactionIsolation c2) 2)))) - -(deftest db-isolation-level-2 - (let [func1 (fn [conn] - (let [conn (proto/connection conn) - isolation (.getTransactionIsolation conn)] - (is (= isolation 8))))] - (with-open [conn (jdbc/connection h2-dbspec3)] - (jdbc/atomic-apply conn func1 {:isolation-level :serializable})))) - -(deftest db-readonly-transactions - (letfn [(func [conn] - (let [raw (proto/connection conn)] - (is (true? (.isReadOnly raw)))))] - (with-open [conn (jdbc/connection pg-dbspec)] - (jdbc/atomic-apply conn func {:read-only true}) - (is (false? (.isReadOnly (proto/connection conn)))))) - - (with-open [conn (jdbc/connection pg-dbspec)] - (jdbc/atomic conn {:read-only true} - (is (true? (.isReadOnly (proto/connection conn))))) - (is (false? (.isReadOnly (proto/connection conn)))))) - -(deftest db-commands-2 - (with-open [conn (jdbc/connection pg-dbspec)] - (jdbc/atomic conn - (jdbc/set-rollback! conn) - (jdbc/execute conn "create table foo2 (id serial, age integer);") - (let [result (jdbc/fetch conn ["insert into foo2 (age) values (?) returning id" 1])] - (is (= result [{:id 1}]))))) - - (with-open [conn (jdbc/connection pg-dbspec)] - (jdbc/atomic conn - (jdbc/set-rollback! conn) - (let [sql1 "CREATE TABLE foo (id integer primary key, age integer);" - sql2 ["INSERT INTO foo (id, age) VALUES (?,?), (?,?);" 1 1 2 2]] - (jdbc/execute conn sql1) - (let [result (jdbc/execute conn sql2 {:returning true})] - (is (= result [{:id 1, :age 1} {:id 2, :age 2}]))))))) - -(deftest db-commands - ;; Simple statement - (with-open [conn (jdbc/connection h2-dbspec3)] - (let [sql "CREATE TABLE foo (name varchar(255), age integer);" - r (jdbc/execute conn sql)] - (is (= (list 0) r)))) - - ;; Statement with exception - (with-open [conn (jdbc/connection h2-dbspec3)] - (let [sql "CREATE TABLE foo (name varchar(255), age integer);"] - (jdbc/execute conn sql) - (is (thrown? org.h2.jdbc.JdbcBatchUpdateException (jdbc/execute conn sql))))) - - ;; Fetch from simple query - (with-open [conn (jdbc/connection h2-dbspec3)] - (let [result (jdbc/fetch conn "SELECT 1 + 1 as foo;")] - (is (= [{:foo 2}] result)))) - - - ;; Fetch from complex query in sqlvec format - (with-open [conn (jdbc/connection pg-dbspec)] - (let [result (jdbc/fetch conn ["SELECT * FROM generate_series(1, ?) LIMIT 1 OFFSET 3;" 10])] - (is (= (count result) 1)))) - - ;; Fetch with sqlvec format and overwriting identifiers parameter - (with-open [conn (jdbc/connection h2-dbspec3)] - (let [result (jdbc/fetch conn ["SELECT 1 + 1 as foo;"] {:identifiers identity})] - (is (= [{:FOO 2}] result)))) - - ;; Fetch returning rows - (with-open [conn (jdbc/connection h2-dbspec3)] - (let [result (jdbc/fetch conn ["SELECT 1 + 1 as foo;"] {:as-rows? true})] - (is (= [2] (first result))))) - - ;; Fetch returning rows with header - (with-open [conn (jdbc/connection h2-dbspec3)] - (let [result (jdbc/fetch conn ["SELECT 1 + 1 as foo, 2 + 2 as bar;"] {:as-rows? true :header? true})] - (is (= [["foo", "bar"] [2, 4]] result)))) - - ;; Fetch from prepared statement - (with-open [conn (jdbc/connection h2-dbspec3)] - (let [stmt (jdbc/prepared-statement conn ["select ? as foo;" 2]) - result (jdbc/fetch conn stmt)] - (is (= [{:foo 2}] result)))) -) - - -(deftest lazy-queries - (with-open [conn (jdbc/connection h2-dbspec3)] - (jdbc/atomic conn - (with-open [cursor (jdbc/fetch-lazy conn "SELECT 1 + 1 as foo;")] - (let [result (vec (jdbc/cursor->lazyseq cursor))] - (is (= [{:foo 2}] result))) - (let [result (vec (jdbc/cursor->lazyseq cursor))] - (is (= [{:foo 2}] result))))))) - -(deftest insert-bytes - (let [buffer (byte-array (map byte (range 0 10))) - inputStream (java.io.ByteArrayInputStream. buffer) - sql "CREATE TABLE foo (id integer, data bytea);"] - (with-open [conn (jdbc/connection h2-dbspec3)] - (jdbc/execute conn sql) - (let [res (jdbc/execute conn ["INSERT INTO foo (id, data) VALUES (?, ?);" 1 inputStream])] - (is (= res 1))) - (let [res (jdbc/fetch-one conn "SELECT * FROM foo")] - (is (instance? (Class/forName "[B") (:data res))) - (is (= (get (:data res) 2) 2)))))) - - -(extend-protocol proto/ISQLType - (class (into-array String [])) - (as-sql-type [this conn] this) - (set-stmt-parameter! [this conn stmt index] - (let [prepared (proto/as-sql-type this conn) - array (.createArrayOf conn "text" prepared)] - (.setArray stmt index array)))) - -(deftest insert-arrays - (with-open [conn (jdbc/connection pg-dbspec)] - (jdbc/atomic conn - (jdbc/set-rollback! conn) - (let [sql "CREATE TABLE arrayfoo (id integer, data text[]);" - dat (into-array String ["foo", "bar"])] - (jdbc/execute conn sql) - (let [res (jdbc/execute conn ["INSERT INTO arrayfoo (id, data) VALUES (?, ?);" 1, dat])] - (is (= res 1))) - - (let [res (jdbc/fetch-one conn "SELECT * FROM arrayfoo") - rr (.getArray (:data res))] - (is (= (count rr) 2)) - (is (= (get rr 0) "foo")) - (is (= (get rr 1) "bar"))))))) - -(deftest transactions-dummy-strategy - (let [sql1 "CREATE TABLE foo (name varchar(255), age integer);" - sql2 "INSERT INTO foo (name,age) VALUES (?, ?);" - sql3 "SELECT age FROM foo;" - strategy (reify proto/ITransactionStrategy - (begin! [_ conn opts] conn) - (rollback! [_ conn opts] nil) - (commit! [_ conn opts] nil)) - dbspec (assoc h2-dbspec3 :tx-strategy strategy)] - (with-open [conn (jdbc/connection dbspec)] - (is (identical? (:tx-strategy (meta conn)) strategy)) - (jdbc/execute conn sql1) - (try - (jdbc/atomic conn - (jdbc/execute conn [sql2 "foo" 1]) - (jdbc/execute conn [sql2 "bar" 2]) - (let [results (jdbc/fetch conn sql3)] - (is (= (count results) 2)) - (throw (RuntimeException. "Fooo")))) - - (catch Exception e - (let [results (jdbc/fetch conn sql3)] - (is (= (count results) 2)))))))) - - -(deftest transactions - (let [sql1 "CREATE TABLE foo (name varchar(255), age integer);" - sql2 "INSERT INTO foo (name,age) VALUES (?, ?);" - sql3 "SELECT age FROM foo;"] - - ;; Basic transaction test with exception. - (with-open [conn (jdbc/connection h2-dbspec3)] - (jdbc/execute conn sql1) - - (try - (jdbc/atomic conn - (jdbc/execute conn [sql2 "foo" 1]) - (jdbc/execute conn [sql2 "bar" 2]) - - (let [results (jdbc/fetch conn sql3)] - (is (= (count results) 2)) - (throw (RuntimeException. "Fooo")))) - (catch Exception e - (let [results (jdbc/fetch conn sql3)] - (is (= (count results) 0)))))) - - ;; Basic transaction test without exception. - (with-open [conn (jdbc/connection h2-dbspec3)] - (jdbc/execute conn sql1) - - (jdbc/atomic conn - (jdbc/execute conn [sql2 "foo" 1]) - (jdbc/execute conn [sql2 "bar" 2])) - - (jdbc/atomic conn - (let [results (jdbc/fetch conn sql3)] - (is (= (count results) 2))))) - - ;; Immutability - (with-open [conn (jdbc/connection h2-dbspec3)] - (jdbc/atomic conn - (let [metadata (meta conn)] - (is (:transaction metadata)) - (is (:rollback metadata)) - (is (false? @(:rollback metadata))) - (is (nil? (:savepoint metadata))))) - - (let [metadata (meta conn)] - (is (= (:transaction metadata) nil)) - (is (= (:rollback metadata) nil)))) - - ;; Savepoints - (with-open [conn (jdbc/connection h2-dbspec3)] - (jdbc/atomic conn - (is (:transaction (meta conn))) - (jdbc/atomic conn - (is (not (nil? (:savepoint (meta conn)))))))) - - ;; Set rollback 01 - (with-open [conn (jdbc/connection h2-dbspec3)] - (jdbc/execute conn sql1) - - (jdbc/atomic conn - (jdbc/execute conn [sql2 "foo" 1]) - (jdbc/execute conn [sql2 "bar" 2]) - (is (false? @(:rollback (meta conn)))) - - (jdbc/atomic conn - (jdbc/execute conn [sql2 "foo" 1]) - (jdbc/execute conn [sql2 "bar" 2]) - (jdbc/set-rollback! conn) - (is (true? @(:rollback (meta conn)))) - (let [results (jdbc/fetch conn sql3)] - (is (= (count results) 4)))) - - (let [results (jdbc/fetch conn [sql3])] - (is (= (count results) 2))))) - - ;; Set rollback 02 - (with-open [conn (jdbc/connection h2-dbspec3)] - (jdbc/execute conn sql1) - - (jdbc/atomic conn - (jdbc/set-rollback! conn) - (jdbc/execute conn [sql2 "foo" 1]) - (jdbc/execute conn [sql2 "bar" 2]) - - (is (true? @(:rollback (meta conn)))) - - (jdbc/atomic conn - (is (false? @(:rollback (meta conn)))) - - (jdbc/execute conn [sql2 "foo" 1]) - (jdbc/execute conn [sql2 "bar" 2]) - (let [results (jdbc/fetch conn sql3)] - (is (= (count results) 4)))) - - (let [results (jdbc/fetch conn [sql3])] - (is (= (count results) 4)))) - - (let [results (jdbc/fetch conn [sql3])] - (is (= (count results) 0)))) - - - ;; Subtransactions - (with-open [conn (jdbc/connection h2-dbspec3)] - (jdbc/execute conn sql1) - - (jdbc/atomic conn - (jdbc/execute conn [sql2 "foo" 1]) - (jdbc/execute conn [sql2 "bar" 2]) - - (try - (jdbc/atomic conn - (jdbc/execute conn [sql2 "foo" 1]) - (jdbc/execute conn [sql2 "bar" 2]) - (let [results (jdbc/fetch conn [sql3])] - (is (= (count results) 4)) - (throw (RuntimeException. "Fooo")))) - (catch Exception e - (let [results (jdbc/fetch conn [sql3])] - (is (= (count results) 2))))))) - ))