├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── README_IMAGES │ └── android_jnilibs.png ├── dependabot.yml └── workflows │ ├── build-native.yml │ └── ci.yml ├── .gitignore ├── .hgignore ├── .settings ├── org.eclipse.jdt.core.prefs ├── org.eclipse.ltk.core.refactoring.prefs └── org.eclipse.wst.validation.prefs ├── CHANGELOG ├── CONTRIBUTING.md ├── LICENSE ├── LICENSE.zentus ├── Makefile ├── Makefile.common ├── NEWS.md ├── NOTICE ├── README.adoc ├── SECURITY.md ├── SQLiteJDBC.wiki ├── USAGE.md ├── VERSION ├── amalgamation_version.sh ├── archive ├── nestedvm-2007-06-30.tgz ├── nestedvm-2009-08-09.tgz └── regex3.8a.tar.gz ├── demo ├── AppletDemo.jar ├── Sample.java └── applet-demo.html ├── docker ├── Dockerfile.alpine-linux_x86 ├── Dockerfile.alpine-linux_x86_64 ├── Dockerfile.linux_x86 ├── Dockerfile.linux_x86_64 ├── Dockerfile.rcodesign ├── dockcross-android-arm ├── dockcross-android-arm64 ├── dockcross-android-x86 ├── dockcross-android-x86_64 ├── dockcross-arm64-lts ├── dockcross-armv5 ├── dockcross-armv6-lts ├── dockcross-armv7a-lts ├── dockcross-musl-arm64 ├── dockcross-ppc64 ├── dockcross-riscv64 ├── dockcross-windows-arm64 ├── dockcross-windows-armv7 ├── dockcross-windows-x64 ├── dockcross-windows-x86 └── updatescripts.sh ├── jreleaser.yml ├── lib ├── inc_linux │ ├── jni.h │ └── jni_md.h ├── inc_mac │ ├── jni.h │ └── jni_md.h ├── inc_win │ ├── jni.h │ └── jni_md.h └── jdbc-api-1.4.jar ├── maven-eclipse.xml ├── pom.xml ├── settings.xml └── src ├── main ├── ext │ └── extension-functions.c ├── java │ └── org │ │ └── sqlite │ │ ├── BusyHandler.java │ │ ├── Collation.java │ │ ├── ExtendedCommand.java │ │ ├── FileException.java │ │ ├── Function.java │ │ ├── JDBC.java │ │ ├── NativeLibraryNotFoundException.java │ │ ├── ProgressHandler.java │ │ ├── SQLiteCommitListener.java │ │ ├── SQLiteConfig.java │ │ ├── SQLiteConnection.java │ │ ├── SQLiteConnectionConfig.java │ │ ├── SQLiteDataSource.java │ │ ├── SQLiteErrorCode.java │ │ ├── SQLiteException.java │ │ ├── SQLiteJDBCLoader.java │ │ ├── SQLiteLimits.java │ │ ├── SQLiteOpenMode.java │ │ ├── SQLiteUpdateListener.java │ │ ├── core │ │ ├── Codes.java │ │ ├── CoreDatabaseMetaData.java │ │ ├── CorePreparedStatement.java │ │ ├── CoreResultSet.java │ │ ├── CoreStatement.java │ │ ├── DB.java │ │ ├── NativeDB.c │ │ ├── NativeDB.java │ │ └── SafeStmtPtr.java │ │ ├── date │ │ ├── DateFormatUtils.java │ │ ├── DateParser.java │ │ ├── DatePrinter.java │ │ ├── ExceptionUtils.java │ │ ├── FastDateFormat.java │ │ ├── FastDateParser.java │ │ ├── FastDatePrinter.java │ │ ├── FormatCache.java │ │ └── package-info.java │ │ ├── javax │ │ ├── SQLiteConnectionPoolDataSource.java │ │ └── SQLitePooledConnection.java │ │ ├── jdbc3 │ │ ├── JDBC3Connection.java │ │ ├── JDBC3DatabaseMetaData.java │ │ ├── JDBC3PreparedStatement.java │ │ ├── JDBC3ResultSet.java │ │ ├── JDBC3Savepoint.java │ │ └── JDBC3Statement.java │ │ ├── jdbc4 │ │ ├── JDBC4Connection.java │ │ ├── JDBC4DatabaseMetaData.java │ │ ├── JDBC4PooledConnection.java │ │ ├── JDBC4PreparedStatement.java │ │ ├── JDBC4ResultSet.java │ │ └── JDBC4Statement.java │ │ └── util │ │ ├── LibraryLoaderUtil.java │ │ ├── Logger.java │ │ ├── LoggerFactory.java │ │ ├── OSInfo.java │ │ ├── ProcessRunner.java │ │ ├── QueryUtils.java │ │ ├── ResourceFinder.java │ │ └── StringUtils.java ├── java9 │ ├── module-info.java │ └── org │ │ └── sqlite │ │ └── nativeimage │ │ └── SqliteJdbcFeature.java └── resources │ ├── META-INF │ └── native-image │ │ └── org.xerial │ │ └── sqlite-jdbc │ │ └── native-image.properties │ ├── java.sql.Driver │ ├── org │ └── sqlite │ │ └── native │ │ ├── FreeBSD │ │ ├── aarch64 │ │ │ └── libsqlitejdbc.so │ │ ├── x86 │ │ │ └── libsqlitejdbc.so │ │ └── x86_64 │ │ │ └── libsqlitejdbc.so │ │ ├── Linux-Android │ │ ├── aarch64 │ │ │ └── libsqlitejdbc.so │ │ ├── arm │ │ │ └── libsqlitejdbc.so │ │ ├── x86 │ │ │ └── libsqlitejdbc.so │ │ └── x86_64 │ │ │ └── libsqlitejdbc.so │ │ ├── Linux-Musl │ │ ├── aarch64 │ │ │ └── libsqlitejdbc.so │ │ ├── x86 │ │ │ └── libsqlitejdbc.so │ │ └── x86_64 │ │ │ └── libsqlitejdbc.so │ │ ├── Linux │ │ ├── aarch64 │ │ │ └── libsqlitejdbc.so │ │ ├── arm │ │ │ └── libsqlitejdbc.so │ │ ├── armv6 │ │ │ └── libsqlitejdbc.so │ │ ├── armv7 │ │ │ └── libsqlitejdbc.so │ │ ├── ppc64 │ │ │ └── libsqlitejdbc.so │ │ ├── riscv64 │ │ │ └── libsqlitejdbc.so │ │ ├── x86 │ │ │ └── libsqlitejdbc.so │ │ └── x86_64 │ │ │ └── libsqlitejdbc.so │ │ ├── Mac │ │ ├── aarch64 │ │ │ └── libsqlitejdbc.dylib │ │ └── x86_64 │ │ │ └── libsqlitejdbc.dylib │ │ └── Windows │ │ ├── aarch64 │ │ └── sqlitejdbc.dll │ │ ├── armv7 │ │ └── sqlitejdbc.dll │ │ ├── x86 │ │ └── sqlitejdbc.dll │ │ └── x86_64 │ │ └── sqlitejdbc.dll │ └── sqlite-jdbc.properties └── test ├── java └── org │ └── sqlite │ ├── BackupTest.java │ ├── BusyHandlerTest.java │ ├── CachedRowSetTest.java │ ├── CollationTest.java │ ├── ConnectionTest.java │ ├── DBMetaDataTest.java │ ├── ErrorMessageTest.java │ ├── ExtendedCommandTest.java │ ├── ExtensionTest.java │ ├── FetchSizeTest.java │ ├── InsertQueryTest.java │ ├── JDBCTest.java │ ├── JSON1Test.java │ ├── ListenerTest.java │ ├── MathFunctionsTest.java │ ├── MetadataLeakTest.java │ ├── MultipleClassLoaderTest.java │ ├── PrepStmtTest.java │ ├── PreparedStatementThreadTest.java │ ├── ProgressHandlerTest.java │ ├── QueryTest.java │ ├── RSMetaDataTest.java │ ├── ReadUncommittedTest.java │ ├── ResultSetTest.java │ ├── ResultSetWithoutResultsTest.java │ ├── SQLiteConfigTest.java │ ├── SQLiteConnectionPoolDataSourceTest.java │ ├── SQLiteDataSourceTest.java │ ├── SQLiteJDBCLoaderTest.java │ ├── SavepointTest.java │ ├── SerializeTest.java │ ├── StatementTest.java │ ├── TransactionTest.java │ ├── TypeMapTest.java │ ├── UDFCustomErrorTest.java │ ├── UDFTest.java │ ├── Utils.java │ ├── architecture │ ├── CodingRulesTest.java │ └── TestCodingRulesTest.java │ ├── core │ └── NativeDBHelper.java │ └── util │ └── OSInfoTest.java └── resources ├── META-INF └── native-image │ └── org.xerial │ └── sqlite-jdbc │ ├── reachability-metadata.json │ ├── reflect-config.json │ └── resource-config.json └── org └── sqlite ├── attach_test.db ├── sample.db └── testdb.jar /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: triage 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Provide a sample code that reproduces the error. 15 | 16 | **Expected behavior** 17 | A clear and concise description of what you expected to happen. 18 | 19 | **Logs** 20 | If applicable, provide logs. 21 | 22 | **Environment (please complete the following information):** 23 | - OS: [e.g. Windows 7] 24 | - CPU architecture: [e.g. x86_64, arm64] 25 | - sqlite-jdbc version [e.g. 3.39.2.0] 26 | 27 | **Additional context** 28 | Add any other context about the problem here. 29 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: triage 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/README_IMAGES/android_jnilibs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xerial/sqlite-jdbc/d54053637df62fda39bb46c3e653120c4eff9e13/.github/README_IMAGES/android_jnilibs.png -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | 4 | # Maintain dependencies for maven (pom.xml in root directory) 5 | - package-ecosystem: "maven" 6 | directory: "/" 7 | schedule: 8 | interval: "weekly" 9 | 10 | # Maintain dependencies for GitHub Actions 11 | - package-ecosystem: "github-actions" 12 | directory: "/" 13 | schedule: 14 | interval: "weekly" -------------------------------------------------------------------------------- /.github/workflows/build-native.yml: -------------------------------------------------------------------------------- 1 | name: Build Native 2 | 3 | on: 4 | workflow_dispatch: 5 | pull_request: 6 | types: [ opened ] 7 | issue_comment: 8 | types: [ created ] 9 | 10 | jobs: 11 | check: 12 | name: Check conditions 13 | runs-on: ubuntu-latest 14 | outputs: 15 | ref: ${{ steps.output.outputs.ref }} 16 | repo: ${{ steps.output.outputs.repo }} 17 | steps: 18 | - name: Check if PR comment trigger is present 19 | if: github.event_name != 'workflow_dispatch' 20 | uses: khan/pull-request-comment-trigger@v1.1.0 21 | id: check 22 | with: 23 | trigger: '/native' 24 | reaction: rocket 25 | prefix_only: true 26 | env: 27 | GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}' 28 | - name: Get PR branch 29 | uses: gotson/pull-request-comment-branch@head-repo-owner-dist 30 | if: steps.check.outputs.triggered == 'true' 31 | id: comment-branch 32 | with: 33 | repo_token: ${{ secrets.GITHUB_TOKEN }} 34 | # This will set the branch to use: 35 | # - if workflow_dispatch, use the branch that was chosen when running the workflow 36 | # - if it is a comment on a PR, and the trigger matches, use the PR branch 37 | - name: Set job output 38 | id: output 39 | run: | 40 | echo "::echo::on" 41 | if [[ "${{ github.event_name == 'workflow_dispatch' }}" == 'true' ]] 42 | then 43 | echo "ref=${{ github.ref_name }}" >> $GITHUB_OUTPUT 44 | echo "repo=${{ github.repository }}" >> $GITHUB_OUTPUT 45 | elif [[ "${{ steps.check.outputs.triggered }}" == 'true' ]] 46 | then 47 | echo "ref=${{ steps.comment-branch.outputs.head_ref }}" >> $GITHUB_OUTPUT 48 | echo "repo=${{ steps.comment-branch.outputs.head_owner }}/${{ steps.comment-branch.outputs.head_repo }}" >> $GITHUB_OUTPUT 49 | else 50 | echo "" 51 | fi 52 | 53 | matrix: 54 | name: Build matrix 55 | runs-on: ubuntu-latest 56 | needs: [check] 57 | if: needs.check.outputs.ref && needs.check.outputs.repo 58 | outputs: 59 | matrix: ${{ steps.set-matrix.outputs.matrix }} 60 | steps: 61 | - uses: actions/checkout@v4 62 | with: 63 | repository: ${{ needs.check.outputs.repo }} 64 | ref: ${{ needs.check.outputs.ref }} 65 | - name: Build matrix from Makefile 66 | id: set-matrix 67 | # parse the Makefile to retrieve the list of targets in 'native-all', without 'native' 68 | run: | 69 | matrix=$(( 70 | echo '{ "target" : [' 71 | sed -n "/^native-all *: */ { s///; p }" Makefile | sed "s/^native\s//g" | sed 's/ /, /g' | xargs -n 1 echo | sed -r 's/^([^,]*)(,?)$/"\1"\2/' 72 | echo " ]}" 73 | ) | jq -c .) 74 | echo $matrix | jq . 75 | echo "matrix=$matrix" >> $GITHUB_OUTPUT 76 | 77 | build: 78 | name: Build native libraries 79 | runs-on: ubuntu-latest 80 | needs: [check, matrix] 81 | strategy: 82 | matrix: ${{fromJson(needs.matrix.outputs.matrix)}} 83 | steps: 84 | - uses: actions/checkout@v4 85 | with: 86 | repository: ${{ needs.check.outputs.repo }} 87 | ref: ${{ needs.check.outputs.ref }} 88 | # Delete existing libs so we only upload the generated one into the artifact 89 | - name: Delete existing native libs 90 | run: rm -fr src/main/resources/org/sqlite/native 91 | - name: Build native libraries 92 | run: make ${{ matrix.target }} 93 | env: 94 | OCI_EXE: docker 95 | - name: Upload native libraries 96 | uses: actions/upload-artifact@v4 97 | with: 98 | name: native-libs-${{ matrix.target }} 99 | path: src/main/resources/org/sqlite/native/ 100 | 101 | push: 102 | name: Push new native libraries to branch 103 | runs-on: ubuntu-latest 104 | needs: [check, build] 105 | steps: 106 | - uses: actions/checkout@v4 107 | with: 108 | repository: ${{ needs.check.outputs.repo }} 109 | ref: ${{ needs.check.outputs.ref }} 110 | 111 | - name: Download native libraries 112 | uses: actions/download-artifact@v4 113 | with: 114 | pattern: native-libs-* 115 | merge-multiple: true 116 | path: src/main/resources/org/sqlite/native/ 117 | 118 | - run: git status 119 | - name: Commit and push 120 | uses: EndBug/add-and-commit@v9 121 | with: 122 | message: 'chore: update native libraries' 123 | default_author: github_actions -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | *~ 3 | sqlitejdbc/dl 4 | sqlitejdbc/build 5 | sqlitejdbc/nestedvm-* 6 | src/main/resources/org/ibex/nestedvm/*.o 7 | .idea 8 | *.iml 9 | atlassian-ide-plugin.xml 10 | .classpath 11 | .project 12 | .settings/ 13 | .DS_Store 14 | *.class 15 | .fleet/settings.json -------------------------------------------------------------------------------- /.hgignore: -------------------------------------------------------------------------------- 1 | target 2 | sqlitejdbc/build 3 | sqlitejdbc/dl 4 | sqlitejdbc/nestedvm-2009-08-09 5 | .classpath 6 | .project 7 | .idea 8 | 9 | syntax: glob 10 | src/main/resources/org/ibex/nestedvm/*.o 11 | *~ 12 | *.iml -------------------------------------------------------------------------------- /.settings/org.eclipse.jdt.core.prefs: -------------------------------------------------------------------------------- 1 | #Thu Apr 28 10:35:24 JST 2011 2 | eclipse.preferences.version=1 3 | org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled 4 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 5 | org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve 6 | org.eclipse.jdt.core.compiler.compliance=1.8 7 | org.eclipse.jdt.core.compiler.debug.lineNumber=generate 8 | org.eclipse.jdt.core.compiler.debug.localVariable=generate 9 | org.eclipse.jdt.core.compiler.debug.sourceFile=generate 10 | org.eclipse.jdt.core.compiler.problem.assertIdentifier=error 11 | org.eclipse.jdt.core.compiler.problem.enumIdentifier=error 12 | org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning 13 | org.eclipse.jdt.core.compiler.source=1.8 14 | -------------------------------------------------------------------------------- /.settings/org.eclipse.ltk.core.refactoring.prefs: -------------------------------------------------------------------------------- 1 | #Mon Jan 19 11:25:47 GMT+09:00 2009 2 | eclipse.preferences.version=1 3 | org.eclipse.ltk.core.refactoring.enable.project.refactoring.history=false 4 | -------------------------------------------------------------------------------- /.settings/org.eclipse.wst.validation.prefs: -------------------------------------------------------------------------------- 1 | #Mon Jan 19 11:25:47 GMT+09:00 2009 2 | DELEGATES_PREFERENCE=delegateValidatorList 3 | USER_BUILD_PREFERENCE=enabledBuildValidatorList 4 | USER_MANUAL_PREFERENCE=enabledManualValidatorList 5 | USER_PREFERENCE=overrideGlobalPreferencesfalse 6 | eclipse.preferences.version=1 7 | override=false 8 | suspend=false 9 | vf.version=3 10 | -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | Work in progress: 2 | 3 | * Issue #107 - DatabaseMetaData.getPrimaryKeys does not always return all primary keys for multi-line table definitions. 4 | * Issue #109 - SQLiteConfig ignores calls to setDateStringFormat/setDateClass/setDatePrecision. 5 | * Issue #116 - Move to Java 6 6 | * Created new branch refactor/JDBC4 7 | * Jar is packaged as sqlite-jdbc4* 8 | * Issue #65 - Calling Statement.setMaxRows(N) has the effect of returning N+1 rows 9 | * Issue #64 - ArrayIndexOutOfBoundsException when calling ResultSetMetaData.getScale() for VARCHAR column types 10 | * Issue #56 - Implement getBinaryStream() in RS.java 11 | * Issue #45 - Stmt.close() should not close metadata 12 | * Issue #49 - PreparedStatement.getUpdateCount should return -1 if the current result is a ResultSet or no more results exist 13 | * Issue #48 - PreparedStatement.getResultSet should return null if current result is an update 14 | * Issue #67 - Bad ResultSet returned from DatabaseMetaData.getColumns(), if a given table doesn't exist 15 | * Issue #73 - DatabaseMetaData.getTables() Memory Leak 16 | * Issue #68 - DatabaseMetaData.getTables() erroneously returns INDEX, TRIGGER, and other non-table objects 17 | * Issue #53 - getImportedKeys throws Null Exception for foreign key without explicit primary column name 18 | * Issue #18 - Add support for ConnectionPool 19 | * Implemented javax.sql.ConnectionPoolDataSource - javax.SQLiteConnectionPoolDataSource 20 | * Issue #27 - Allow user to specify busy-timeout when opening a database connection 21 | * Added support for sqlite3_busy_timeout 22 | 1. Added configuration property "busy_timeout" to SQLiteConfig. 23 | 2. Exposed setBusyTimeout and getBusyTimeout on SQLiteConnection. 24 | 3. Default busy_timeout of 3 seconds (3000ms) remains the same. 25 | * Issue #60 - ExtendedCommand (e.g backup) not supported on Stmt.execute(String) 26 | * Issue #23 - getDate() returns wrong values 27 | * Added support for specifying a date storage class. For more info 28 | * Issue #54 - Implement PreparedStatement.setBinaryStream() 29 | * Added support for InputStreams on PreparedStatement 30 | 1. setBinaryStream() 31 | 2. setAsciiStream() 32 | 3. setUnicodeStream() 33 | * Issue #60 - ExtendedCommand (e.g backup) not supported on Stmt.execute(String) 34 | * Issue #23 - getDate() returns wrong values 35 | * Added support for specifying a date storage class. For more info 36 | * Issue #54 - Implement PreparedStatement.setBinaryStream() 37 | * Added support for InputStreams on PreparedStatement 38 | 1. setBinaryStream() 39 | 2. setAsciiStream() 40 | 3. setUnicodeStream() 41 | 42 | Release 3.7.15-SNAPSHOT-2 43 | * Fixed clean target to remove previous driver builds 44 | * Issue #42 - Using shared in-memory with URI filename 45 | * Added support for URI filenames 46 | 1. examples 47 | * Issue #47 - MetaData.getExportedKeys() returns empty string for a named foreign key 48 | * Issue #46 - org.sqlite.MetaData.getExportedKeys constructs incorrect SQL 49 | * Issue #44 - getExportedKeys throws Null Exception for foreign key without explicit primary column name 50 | 1. Now returns keys for foreign key definitions like "create table referring (id, foreign key references referred)" 51 | * Implemented getPrecision(int) and getScale(int) on ResultSetMetaData. 52 | * Issue #43 - ResultSetMetaData does not return the SQL Type if there are no results from the query. 53 | 1. ResultSetMetaData.getColumnTypeName() now gets type from either 'create table' statement or CAST(expr AS TYPE) otherwise sqlite3_value_type. 54 | 2. ResultSetMetaData.getColumnType() now parses the result from getColumnTypeName() to return a type. 55 | 3. Driver recognizes the types listed at under 'Affinity Name Examples'. 56 | * Issue #36 - Fixed case where a calling PreparedStatement.clearParameters() after a ResultSet is opened, caused subsequent calls to the ResultSet to return null. 57 | 1. PreparedStatement.clearParameters() now uses sqlite3_clear_bindings instead of sqlite3_reset. 58 | 2. PreparedStatement.clearParameters() does not reset current ResultSet. 59 | 3. PreparedStatement now checks if value(s) are bound to the statement parameters before executing; Throwing an exception if they are not. 60 | * Issue #40 - Fixed case where a call to Statement.getGeneratedKeys() could lead to "database is locked" error. 61 | 1. Calling Statement.Close() on a Statement on which getGeneratedKeys() was called, now releases all resources properly. 62 | * Issue #33 - Fixed Statement.cancel() to work when called from another thread 63 | 64 | Release 3.7.15-SNAPSHOT 65 | * Issue #10 - Dropped pure java support 66 | * Issue #22 - Fixed DatabaseMetaData.supportsGetGeneratedKeys to return true since it's already supported 67 | * Issue #17 - Enabled Statement.setEscapeProcessing(boolean) method so driver can be used in Ant tasks 68 | * Issue #20 - Fixed MetaData.getExportedKeys() to return all foreign keys. 69 | * Issue #14 - Fixed DatabaseMetaData.getColumns() resultSet to populate COLUMN_DEF with column default values. 70 | * Fixed Statement.close() so that calling it is a no-op on a closed connection. 71 | * Issue #13- Added support for Java Standard Edition Service Provider mechanism 72 | * Issue #12 - Fixed MetaData.getColumns() to return "YES" or "NO" for IS_NULLABLE column 73 | * Issue #16 - Fixed Statement.executeUpdate() to return 0 for DDL and more accurate changes for DML. (Added sqlite3_total_changes to native library) 74 | * Issue #15 - Modified Connection.setReadOnly(boolean) to throw an Exception if readOnly mode is modified after opening a connection 75 | * Changed driver to include architecture name in temporary filename in order to avoid conflicts when multiple JVMs with different architectures running at the same time 76 | * Issue #11 - Enhanced MetaData.getPrimaryKeys(): 77 | 1. Return named primary keys and correct key sequence. 78 | 2. Also result set is ordered by column name as per JDBC spec. 79 | * Issue #1 - Added support for WAL JournalMode. 80 | * Issue #4 - Enhanced SQLiteDataSource, SQLiteConfig and Conn to enable setting the transaction mode. 81 | * Issue #5 - Fixed NativeDB.c errors when compiling with Visual Studio 2010. 82 | * Issue #2 - Fixed issue where SQLiteDataSource: setEncoding not working. And also enabled using UTF-8, UTF-16, UTF-16le, and UTF-16be. 83 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Development 2 | ## Prerequisites 3 | 4 | - JDK 11 5 | 6 | ## Commits 7 | 8 | Since August 2022 the commit messages follow the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) standard. This allows for easier and better release notes generation. 9 | 10 | ## How to submit a patch 11 | 12 | 1. Fork this project on GitHub 13 | 2. make some changes 14 | 3. `git commit -m 'type: what changes are made to the source'` 15 | 4. `git push` 16 | 5. Create a pull request 17 | 18 | :warning: Do not force push to your PR branch. This makes the reviewer's job more difficult because it clears what changes have already been reviewed or not. The commits will be squashed while merging anyway. 19 | 20 | # How to compile the native libraries 21 | ## Prerequisites 22 | 23 | 1. JDK 11 24 | 2. Perl 25 | 3. Maven 26 | 4. make 27 | 5. gcc 28 | 6. curl 29 | 7. unzip 30 | 8. Docker (for cross-compilation only) 31 | 32 | ## Version update 33 | 1. Edit the `VERSION` file and set the SQLite version to use. 34 | 2. Edit the version number in `pom.xml` to `${VERSION}.0-SNAPSHOT`. So if `VERSION`=`3.39.2`, the version number in `pom.xml` should be `3.39.2.0-SNAPSHOT`. 35 | 36 | ## Build for the current platform 37 | ```shell 38 | # For the current platform 39 | $ make native 40 | ``` 41 | 42 | ## Build for all platforms 43 | The native library is cross-compiled for different OS and architecture by using Docker. 44 | ```shell 45 | $ make native-all 46 | ``` 47 | 48 | On Windows it is recommended to use WSL2. 49 | 50 | You can check the `native-all` goal in `Makefile` for a list of available targets. 51 | 52 | ## Build with an external amalgamation archive 53 | 54 | By default, sqlite-jdbc will download the [SQLite amalgamation](https://www.sqlite.org/amalgamation.html) in order to build the native libraries. 55 | 56 | You can use an existing installation of SQLite instead, by passing `SQLITE_OBJ=/path/to/libsqlite3.a` and `SQLITE_HEADER=/path/to/sqlite3.h`. 57 | 58 | Example: 59 | 60 | ```shell 61 | make native SQLITE_OBJ=/usr/local/lib/libsqlite3.so SQLITE_HEADER=/usr/local/include/sqlite3.h 62 | ``` 63 | 64 | ## Build from CI 65 | 66 | The native libraries can all be built with Github Actions: 67 | - by running the **Build Native** workflow [manually](https://docs.github.com/en/actions/managing-workflow-runs/manually-running-a-workflow) 68 | - by commenting "/native" on a PR 69 | 70 | Once the build succeeds, a commit will be added to the branch or PR with the updated binaries. 71 | 72 | # Release process 73 | The project version can change by 2 means: 74 | 1. By changing the bundled version of SQLite, in which case the project version changes to align with the SQLite version. This is a manual process for now. 75 | 2. When triggering a release. This is done automatically through GitHub Actions. 76 | 77 | ## Trigger a release 78 | A release can be triggered from GitHub Actions by [manually running](https://docs.github.com/en/actions/managing-workflow-runs/manually-running-a-workflow) the **CI** workflow and ticking the **Perform release** option. 79 | 80 | ## What happens when performing a release? 81 | 82 | Multiple actions will happen in sequence, all orchestrated by the GitHub workflow: 83 | 1. The version in `pom.xml` is changed to remove the `-SNAPSHOT`. This is done by using the [Maven Versions plugin](https://www.mojohaus.org/versions-maven-plugin/). 84 | 2. Deploy to Maven Central. This is done by using the Maven `deploy` goal with the `release` profile. It relies on credentials stored in the repository's secrets. 85 | 3. Perform a release commit with the changed `pom.xml`, as well as creating a git tag with the version number. 86 | 4. Create a GitHub release. This is done via JReleaser, and will also include a changelog since the last release. 87 | 5. The version in `pom.xml` is incremented for the next snapshot. 88 | 6. Perform a commit with the version updated for the next snapshot. 89 | 90 | ## Snapshot publishing 91 | 92 | The CI workflow will also publish a new snapshot to [Sonatype's snapshots repository](https://oss.sonatype.org/content/repositories/snapshots/org/xerial/sqlite-jdbc/) whenever a change occurs. 93 | -------------------------------------------------------------------------------- /LICENSE.zentus: -------------------------------------------------------------------------------- 1 | Copyright (c) 2006, David Crawshaw. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions 5 | are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | 2. Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 14 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 17 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 | SUCH DAMAGE. 24 | 25 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | This product includes the following softwares developed by David Crawshaw. 2 | See LICENSE.zentus file. 3 | 4 | And also, NestedVM (Apache License Version 2.0) is used inside sqlite- -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | | Version | Supported | 6 | |-------------------|--------------------| 7 | | Latest version | :white_check_mark: | 8 | | Any other version | :x: | 9 | 10 | ## Reporting a Vulnerability 11 | 12 | See https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing/privately-reporting-a-security-vulnerability#privately-reporting-a-security-vulnerability 13 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | version=3.49.1 2 | -------------------------------------------------------------------------------- /amalgamation_version.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Used to generate the version for the amalgamation download zip. 3 | # https://www.sqlite.org/download.html#encoding 4 | # The version is encoded so that filenames sort in order of increasing version number when viewed using "ls". 5 | # For version 3.X.Y the filename encoding is 3XXYY00. For branch version 3.X.Y.Z, the encoding is 3XXYYZZ. 6 | version="" 7 | i=0 8 | export IFS="." 9 | for num in $1; do 10 | if [ $i -gt 0 ]; then 11 | if [ $num -le 9 ]; then 12 | eval num=0$num 13 | fi 14 | fi 15 | eval version=$version$num 16 | let i+=1 17 | done 18 | unset IFS 19 | if [ $i -gt 3 ]; then 20 | echo "$version" 21 | else 22 | echo "$version"00 23 | fi 24 | -------------------------------------------------------------------------------- /archive/nestedvm-2007-06-30.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xerial/sqlite-jdbc/d54053637df62fda39bb46c3e653120c4eff9e13/archive/nestedvm-2007-06-30.tgz -------------------------------------------------------------------------------- /archive/nestedvm-2009-08-09.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xerial/sqlite-jdbc/d54053637df62fda39bb46c3e653120c4eff9e13/archive/nestedvm-2009-08-09.tgz -------------------------------------------------------------------------------- /archive/regex3.8a.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xerial/sqlite-jdbc/d54053637df62fda39bb46c3e653120c4eff9e13/archive/regex3.8a.tar.gz -------------------------------------------------------------------------------- /demo/AppletDemo.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xerial/sqlite-jdbc/d54053637df62fda39bb46c3e653120c4eff9e13/demo/AppletDemo.jar -------------------------------------------------------------------------------- /demo/Sample.java: -------------------------------------------------------------------------------- 1 | import java.sql.Connection; 2 | import java.sql.DriverManager; 3 | import java.sql.ResultSet; 4 | import java.sql.SQLException; 5 | import java.sql.Statement; 6 | 7 | public class Sample 8 | { 9 | public static void main(String[] args) 10 | { 11 | // NOTE: Connection and Statement are AutoCloseable. 12 | // Don't forget to close them both in order to avoid leaks. 13 | try 14 | ( 15 | // create a database connection 16 | Connection connection = DriverManager.getConnection("jdbc:sqlite:sample.db"); 17 | Statement statement = connection.createStatement(); 18 | ) 19 | { 20 | statement.setQueryTimeout(30); // set timeout to 30 sec. 21 | 22 | statement.executeUpdate("drop table if exists person"); 23 | statement.executeUpdate("create table person (id integer, name string)"); 24 | statement.executeUpdate("insert into person values(1, 'leo')"); 25 | statement.executeUpdate("insert into person values(2, 'yui')"); 26 | ResultSet rs = statement.executeQuery("select * from person"); 27 | while(rs.next()) 28 | { 29 | // read the result set 30 | System.out.println("name = " + rs.getString("name")); 31 | System.out.println("id = " + rs.getInt("id")); 32 | } 33 | } 34 | catch(SQLException e) 35 | { 36 | // if the error message is "out of memory", 37 | // it probably means no database file is found 38 | e.printStackTrace(System.err); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /demo/applet-demo.html: -------------------------------------------------------------------------------- 1 | 2 | Applet Demo 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /docker/Dockerfile.alpine-linux_x86: -------------------------------------------------------------------------------- 1 | FROM i386/alpine:3.11 2 | MAINTAINER Taro L. Saito 3 | 4 | RUN apk --update add bash gcc make perl libc-dev 5 | 6 | WORKDIR /work 7 | -------------------------------------------------------------------------------- /docker/Dockerfile.alpine-linux_x86_64: -------------------------------------------------------------------------------- 1 | FROM alpine:3.11 2 | MAINTAINER Taro L. Saito 3 | 4 | RUN apk --update add bash gcc make perl libc-dev 5 | 6 | WORKDIR /work 7 | -------------------------------------------------------------------------------- /docker/Dockerfile.linux_x86: -------------------------------------------------------------------------------- 1 | FROM centos:5 2 | MAINTAINER Taro L. Saito 3 | 4 | RUN yum -y install gcc glibc-devel.i386 make perl 5 | 6 | WORKDIR /work 7 | -------------------------------------------------------------------------------- /docker/Dockerfile.linux_x86_64: -------------------------------------------------------------------------------- 1 | FROM centos:5 2 | MAINTAINER Taro L. Saito 3 | 4 | RUN yum -y install gcc make perl 5 | 6 | WORKDIR /work 7 | -------------------------------------------------------------------------------- /docker/Dockerfile.rcodesign: -------------------------------------------------------------------------------- 1 | FROM rust:latest as build 2 | RUN cargo install --git https://github.com/indygreg/PyOxidizer --branch main --bin rcodesign apple-codesign 3 | 4 | FROM ubuntu:latest 5 | COPY --from=build /usr/local/cargo/bin/rcodesign /bin 6 | WORKDIR /workdir 7 | ENTRYPOINT ["/bin/rcodesign"] -------------------------------------------------------------------------------- /docker/updatescripts.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This script prints the commands to upgrade the docker cross compilation scripts 4 | 5 | grep -F 'docker run --rm doc' "$(dirname $0)/"dockcross-* | grep -F '>' | \ 6 | sed 's/\:#//' | \ 7 | awk '{print $2" "$3" "$4" "$5" "$6" "$1}' 8 | -------------------------------------------------------------------------------- /jreleaser.yml: -------------------------------------------------------------------------------- 1 | project: 2 | versionPattern: CUSTOM 3 | copyright: Taro L. Saito 4 | release: 5 | github: 6 | owner: xerial 7 | discussionCategoryName: Announcements 8 | tagName: '{{projectVersion}}' 9 | changelog: 10 | links: true 11 | formatted: ALWAYS 12 | format: '- {{#commitIsConventional}}{{#conventionalCommitIsBreakingChange}}🚨 {{/conventionalCommitIsBreakingChange}}{{#conventionalCommitScope}}**{{conventionalCommitScope}}**: {{/conventionalCommitScope}}{{conventionalCommitDescription}}{{#conventionalCommitBreakingChangeContent}}: *{{conventionalCommitBreakingChangeContent}}*{{/conventionalCommitBreakingChangeContent}} ({{commitShortHash}}){{/commitIsConventional}}{{^commitIsConventional}}{{commitTitle}} ({{commitShortHash}}){{/commitIsConventional}}{{#commitHasIssues}}, closes{{#commitIssues}} {{issue}}{{/commitIssues}}{{/commitHasIssues}}' 13 | preset: 'conventional-commits' 14 | skipMergeCommits: true 15 | excludeLabels: 16 | - 'chore' 17 | labelers: 18 | - label: 'chore' 19 | title: '[maven-release-plugin] prepare' 20 | order: 1 21 | - label: 'perf' 22 | title: 'regex:^(?:perf(?:\(.*\))?!?):\s.*' 23 | order: 120 24 | categories: 25 | - title: '🏎 Perf' 26 | key: 'perf' 27 | labels: 28 | - 'perf' 29 | order: 25 30 | hide: 31 | uncategorized: true 32 | contributors: 33 | - '[bot]' 34 | - 'github-actions' 35 | - 'GitHub' 36 | extraProperties: 37 | categorizeScopes: true 38 | issues: 39 | enabled: true 40 | comment: '🎉 This issue has been resolved in `{{tagName}}` ([Release Notes]({{releaseNotesUrl}}))' 41 | applyMilestone: 'ALWAYS' 42 | label: 43 | name: 'released' 44 | description: 'Issue has been released' 45 | color: '#ededed' 46 | files: 47 | artifacts: 48 | - path: 'target/{{projectName}}-{{projectVersion}}.jar' -------------------------------------------------------------------------------- /lib/inc_linux/jni_md.h: -------------------------------------------------------------------------------- 1 | /* 2 | * %W% %E% 3 | * 4 | * Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved. 5 | * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 6 | */ 7 | 8 | #ifndef _JAVASOFT_JNI_MD_H_ 9 | #define _JAVASOFT_JNI_MD_H_ 10 | 11 | #define JNIEXPORT __attribute__((__visibility__("default"))) 12 | #define JNIIMPORT 13 | #define JNICALL 14 | 15 | typedef int jint; 16 | #ifdef _LP64 /* 64-bit Solaris */ 17 | typedef long jlong; 18 | #else 19 | typedef long long jlong; 20 | #endif 21 | 22 | typedef signed char jbyte; 23 | 24 | #endif /* !_JAVASOFT_JNI_MD_H_ */ 25 | -------------------------------------------------------------------------------- /lib/inc_mac/jni_md.h: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)jni_md.h 1.19 05/11/17 3 | * 4 | * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 5 | * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 6 | */ 7 | 8 | #ifndef _JAVASOFT_JNI_MD_H_ 9 | #define _JAVASOFT_JNI_MD_H_ 10 | 11 | #define JNIEXPORT __attribute__((visibility("default"))) 12 | #define JNIIMPORT 13 | #define JNICALL 14 | 15 | #if defined(__LP64__) && __LP64__ /* for -Wundef */ 16 | typedef int jint; 17 | #else 18 | typedef long jint; 19 | #endif 20 | typedef long long jlong; 21 | typedef signed char jbyte; 22 | 23 | #endif /* !_JAVASOFT_JNI_MD_H_ */ 24 | -------------------------------------------------------------------------------- /lib/inc_win/jni_md.h: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)jni_md.h 1.14 03/12/19 3 | * 4 | * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 5 | * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 6 | */ 7 | 8 | #ifndef _JAVASOFT_JNI_MD_H_ 9 | #define _JAVASOFT_JNI_MD_H_ 10 | 11 | #define JNIEXPORT __declspec(dllexport) 12 | #define JNIIMPORT __declspec(dllimport) 13 | #define JNICALL __stdcall 14 | 15 | typedef long jint; 16 | typedef __int64 jlong; 17 | typedef signed char jbyte; 18 | 19 | #endif /* !_JAVASOFT_JNI_MD_H_ */ 20 | -------------------------------------------------------------------------------- /lib/jdbc-api-1.4.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xerial/sqlite-jdbc/d54053637df62fda39bb46c3e653120c4eff9e13/lib/jdbc-api-1.4.jar -------------------------------------------------------------------------------- /maven-eclipse.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | sonatype-nexus-staging 5 | ${env.SONATYPE_USERNAME} 6 | ${env.SONATYPE_PASSWORD} 7 | 8 | 9 | sonatype-nexus-snapshots 10 | ${env.SONATYPE_USERNAME} 11 | ${env.SONATYPE_PASSWORD} 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/main/java/org/sqlite/BusyHandler.java: -------------------------------------------------------------------------------- 1 | package org.sqlite; 2 | 3 | import java.sql.Connection; 4 | import java.sql.SQLException; 5 | 6 | /** https://www.sqlite.org/c3ref/busy_handler.html */ 7 | public abstract class BusyHandler { 8 | 9 | /** 10 | * commit the busy handler for the connection. 11 | * 12 | * @param conn the SQLite connection 13 | * @param busyHandler the busyHandler 14 | * @throws SQLException 15 | */ 16 | private static void commitHandler(Connection conn, BusyHandler busyHandler) 17 | throws SQLException { 18 | 19 | if (!(conn instanceof SQLiteConnection)) { 20 | throw new SQLException("connection must be to an SQLite db"); 21 | } 22 | 23 | if (conn.isClosed()) { 24 | throw new SQLException("connection closed"); 25 | } 26 | 27 | SQLiteConnection sqliteConnection = (SQLiteConnection) conn; 28 | sqliteConnection.getDatabase().busy_handler(busyHandler); 29 | } 30 | 31 | /** 32 | * Sets a busy handler for the connection. 33 | * 34 | * @param conn the SQLite connection 35 | * @param busyHandler the busyHandler 36 | * @throws SQLException 37 | */ 38 | public static final void setHandler(Connection conn, BusyHandler busyHandler) 39 | throws SQLException { 40 | commitHandler(conn, busyHandler); 41 | } 42 | 43 | /** 44 | * Clears any busy handler registered with the connection. 45 | * 46 | * @param conn the SQLite connection 47 | * @throws SQLException 48 | */ 49 | public static final void clearHandler(Connection conn) throws SQLException { 50 | commitHandler(conn, null); 51 | } 52 | 53 | /** 54 | * https://www.sqlite.org/c3ref/busy_handler.html 55 | * 56 | * @param nbPrevInvok number of times that the busy handler has been invoked previously for the 57 | * same locking event 58 | * @throws SQLException 59 | * @return If the busy callback returns 0, then no additional attempts are made to access the 60 | * database and SQLITE_BUSY is returned to the application. If the callback returns 61 | * non-zero, then another attempt is made to access the database and the cycle repeats. 62 | */ 63 | protected abstract int callback(int nbPrevInvok) throws SQLException; 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/org/sqlite/Collation.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Gauthier Roebroeck 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | package org.sqlite; 17 | 18 | import java.sql.Connection; 19 | import java.sql.SQLException; 20 | import org.sqlite.core.Codes; 21 | import org.sqlite.core.DB; 22 | 23 | /** 24 | * Provides an interface for creating SQLite user-defined collations. 25 | * 26 | *

A subclass of org.sqlite.Collation can be registered with Collation.create() 27 | * and called by the name it was given. All collations must implement xCompare(String, 28 | * String), which is called when SQLite compares two strings using the custom collation. Eg. 29 | * 30 | *

31 |  *      Class.forName("org.sqlite.JDBC");
32 |  *      Connection conn = DriverManager.getConnection("jdbc:sqlite:");
33 |  *
34 |  *      Collation.create(conn, "REVERSE", new Collation() {
35 |  *          protected int xCompare(String str1, String str2) {
36 |  *              return str1.compareTo(str2) * -1;
37 |  *          }
38 |  *      });
39 |  *
40 |  *      conn.createStatement().execute("select c1 from t order by c1 collate REVERSE;");
41 |  *  
42 | */ 43 | public abstract class Collation { 44 | private SQLiteConnection conn; 45 | private DB db; 46 | 47 | /** 48 | * Registers a given collation with the connection. 49 | * 50 | * @param conn The connection. 51 | * @param name The name of the collation. 52 | * @param f The collation to register. 53 | */ 54 | public static final void create(Connection conn, String name, Collation f) throws SQLException { 55 | if (conn == null || !(conn instanceof SQLiteConnection)) { 56 | throw new SQLException("connection must be to an SQLite db"); 57 | } 58 | if (conn.isClosed()) { 59 | throw new SQLException("connection closed"); 60 | } 61 | 62 | f.conn = (SQLiteConnection) conn; 63 | f.db = f.conn.getDatabase(); 64 | 65 | if (f.db.create_collation(name, f) != Codes.SQLITE_OK) { 66 | throw new SQLException("error creating collation"); 67 | } 68 | } 69 | 70 | /** 71 | * Removes a named collation from the given connection. 72 | * 73 | * @param conn The connection to remove the collation from. 74 | * @param name The name of the collation. 75 | * @throws SQLException 76 | */ 77 | public static final void destroy(Connection conn, String name) throws SQLException { 78 | if (conn == null || !(conn instanceof SQLiteConnection)) { 79 | throw new SQLException("connection must be to an SQLite db"); 80 | } 81 | ((SQLiteConnection) conn).getDatabase().destroy_collation(name); 82 | } 83 | 84 | /** 85 | * Called by SQLite as a custom collation to compare two strings. 86 | * 87 | * @param str1 the first string in the comparison 88 | * @param str2 the second string in the comparison 89 | * @return an integer that is negative, zero, or positive if the first string is less than, 90 | * equal to, or greater than the second, respectively 91 | */ 92 | protected abstract int xCompare(String str1, String str2); 93 | } 94 | -------------------------------------------------------------------------------- /src/main/java/org/sqlite/ExtendedCommand.java: -------------------------------------------------------------------------------- 1 | // -------------------------------------- 2 | // sqlite-jdbc Project 3 | // 4 | // ExtendedCommand.java 5 | // Since: Mar 12, 2010 6 | // 7 | // $URL$ 8 | // $Author$ 9 | // -------------------------------------- 10 | package org.sqlite; 11 | 12 | import java.sql.SQLException; 13 | import java.util.regex.Matcher; 14 | import java.util.regex.Pattern; 15 | import org.sqlite.core.DB; 16 | 17 | /** 18 | * parsing SQLite specific extension of SQL command 19 | * 20 | * @author leo 21 | */ 22 | public class ExtendedCommand { 23 | public static interface SQLExtension { 24 | public void execute(DB db) throws SQLException; 25 | } 26 | 27 | /** 28 | * Parses extended commands of "backup" or "restore" for SQLite database. 29 | * 30 | * @param sql One of the extended commands:
31 | * backup sourceDatabaseName to destinationFileName OR restore targetDatabaseName from 32 | * sourceFileName 33 | * @return BackupCommand object if the argument is a backup command; RestoreCommand object if 34 | * the argument is a restore command; 35 | * @throws SQLException 36 | */ 37 | public static SQLExtension parse(String sql) throws SQLException { 38 | if (sql == null) return null; 39 | if (sql.length() > 5 && sql.substring(0, 6).toLowerCase().equals("backup")) 40 | return BackupCommand.parse(sql); 41 | else if (sql.length() > 6 && sql.substring(0, 7).toLowerCase().equals("restore")) 42 | return RestoreCommand.parse(sql); 43 | 44 | return null; 45 | } 46 | 47 | /** 48 | * Remove the quotation mark from string. 49 | * 50 | * @param s String with quotation mark. 51 | * @return String with quotation mark removed. 52 | */ 53 | public static String removeQuotation(String s) { 54 | if (s == null) return s; 55 | 56 | if ((s.startsWith("\"") && s.endsWith("\"")) || (s.startsWith("'") && s.endsWith("'"))) 57 | return (s.length() >= 2) ? s.substring(1, s.length() - 1) : s; 58 | else return s; 59 | } 60 | 61 | public static class BackupCommand implements SQLExtension { 62 | public final String srcDB; 63 | public final String destFile; 64 | 65 | /** 66 | * Constructs a BackupCommand instance that backup the database to a target file. 67 | * 68 | * @param srcDB Source database name. 69 | * @param destFile Target file name. 70 | */ 71 | public BackupCommand(String srcDB, String destFile) { 72 | this.srcDB = srcDB; 73 | this.destFile = destFile; 74 | } 75 | 76 | private static Pattern backupCmd = 77 | Pattern.compile( 78 | "backup(\\s+(\"[^\"]*\"|'[^\']*\'|\\S+))?\\s+to\\s+(\"[^\"]*\"|'[^\']*\'|\\S+)", 79 | Pattern.CASE_INSENSITIVE); 80 | 81 | /** 82 | * Parses SQLite database backup command and creates a BackupCommand object. 83 | * 84 | * @param sql SQLite database backup command. 85 | * @return BackupCommand object. 86 | * @throws SQLException 87 | */ 88 | public static BackupCommand parse(String sql) throws SQLException { 89 | if (sql != null) { 90 | Matcher m = backupCmd.matcher(sql); 91 | if (m.matches()) { 92 | String dbName = removeQuotation(m.group(2)); 93 | String dest = removeQuotation(m.group(3)); 94 | if (dbName == null || dbName.length() == 0) dbName = "main"; 95 | 96 | return new BackupCommand(dbName, dest); 97 | } 98 | } 99 | throw new SQLException("syntax error: " + sql); 100 | } 101 | 102 | public void execute(DB db) throws SQLException { 103 | int rc = db.backup(srcDB, destFile, null); 104 | 105 | if (rc != SQLiteErrorCode.SQLITE_OK.code) { 106 | throw DB.newSQLException(rc, "Backup failed"); 107 | } 108 | } 109 | } 110 | 111 | public static class RestoreCommand implements SQLExtension { 112 | public final String targetDB; 113 | public final String srcFile; 114 | private static Pattern restoreCmd = 115 | Pattern.compile( 116 | "restore(\\s+(\"[^\"]*\"|'[^\']*\'|\\S+))?\\s+from\\s+(\"[^\"]*\"|'[^\']*\'|\\S+)", 117 | Pattern.CASE_INSENSITIVE); 118 | 119 | /** 120 | * Constructs a RestoreCommand instance that restores the database from a given source file. 121 | * 122 | * @param targetDB Target database name 123 | * @param srcFile Source file name 124 | */ 125 | public RestoreCommand(String targetDB, String srcFile) { 126 | this.targetDB = targetDB; 127 | this.srcFile = srcFile; 128 | } 129 | 130 | /** 131 | * Parses SQLite database restore command and creates a RestoreCommand object. 132 | * 133 | * @param sql SQLite restore backup command 134 | * @return RestoreCommand object. 135 | * @throws SQLException 136 | */ 137 | public static RestoreCommand parse(String sql) throws SQLException { 138 | if (sql != null) { 139 | Matcher m = restoreCmd.matcher(sql); 140 | if (m.matches()) { 141 | String dbName = removeQuotation(m.group(2)); 142 | String dest = removeQuotation(m.group(3)); 143 | if (dbName == null || dbName.length() == 0) dbName = "main"; 144 | return new RestoreCommand(dbName, dest); 145 | } 146 | } 147 | throw new SQLException("syntax error: " + sql); 148 | } 149 | 150 | /** @see org.sqlite.ExtendedCommand.SQLExtension#execute(org.sqlite.core.DB) */ 151 | public void execute(DB db) throws SQLException { 152 | int rc = db.restore(targetDB, srcFile, null); 153 | 154 | if (rc != SQLiteErrorCode.SQLITE_OK.code) { 155 | throw DB.newSQLException(rc, "Restore failed"); 156 | } 157 | } 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /src/main/java/org/sqlite/FileException.java: -------------------------------------------------------------------------------- 1 | package org.sqlite; 2 | 3 | public class FileException extends Exception { 4 | public FileException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/org/sqlite/JDBC.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2007 David Crawshaw 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | package org.sqlite; 18 | 19 | import java.sql.*; 20 | import java.util.Properties; 21 | import org.sqlite.jdbc4.JDBC4Connection; 22 | import org.sqlite.util.Logger; 23 | import org.sqlite.util.LoggerFactory; 24 | 25 | public class JDBC implements Driver { 26 | private static final Logger logger = LoggerFactory.getLogger(JDBC.class); 27 | public static final String PREFIX = "jdbc:sqlite:"; 28 | 29 | static { 30 | try { 31 | DriverManager.registerDriver(new JDBC()); 32 | } catch (SQLException e) { 33 | logger.error(() -> "Could not register driver", e); 34 | } 35 | } 36 | 37 | /** @see java.sql.Driver#getMajorVersion() */ 38 | public int getMajorVersion() { 39 | return SQLiteJDBCLoader.getMajorVersion(); 40 | } 41 | 42 | /** @see java.sql.Driver#getMinorVersion() */ 43 | public int getMinorVersion() { 44 | return SQLiteJDBCLoader.getMinorVersion(); 45 | } 46 | 47 | /** @see java.sql.Driver#jdbcCompliant() */ 48 | public boolean jdbcCompliant() { 49 | return false; 50 | } 51 | 52 | public java.util.logging.Logger getParentLogger() throws SQLFeatureNotSupportedException { 53 | // TODO 54 | return null; 55 | } 56 | 57 | /** @see java.sql.Driver#acceptsURL(java.lang.String) */ 58 | public boolean acceptsURL(String url) { 59 | return isValidURL(url); 60 | } 61 | 62 | /** 63 | * Validates a URL 64 | * 65 | * @param url 66 | * @return true if the URL is valid, false otherwise 67 | */ 68 | public static boolean isValidURL(String url) { 69 | return url != null && url.toLowerCase().startsWith(PREFIX); 70 | } 71 | 72 | /** @see java.sql.Driver#getPropertyInfo(java.lang.String, java.util.Properties) */ 73 | public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) throws SQLException { 74 | return SQLiteConfig.getDriverPropertyInfo(); 75 | } 76 | 77 | /** @see java.sql.Driver#connect(java.lang.String, java.util.Properties) */ 78 | public Connection connect(String url, Properties info) throws SQLException { 79 | return createConnection(url, info); 80 | } 81 | 82 | /** 83 | * Gets the location to the database from a given URL. 84 | * 85 | * @param url The URL to extract the location from. 86 | * @return The location to the database. 87 | */ 88 | static String extractAddress(String url) { 89 | return url.substring(PREFIX.length()); 90 | } 91 | 92 | /** 93 | * Creates a new database connection to a given URL. 94 | * 95 | * @param url the URL 96 | * @param prop the properties 97 | * @return a Connection object that represents a connection to the URL 98 | * @throws SQLException 99 | * @see java.sql.Driver#connect(java.lang.String, java.util.Properties) 100 | */ 101 | public static SQLiteConnection createConnection(String url, Properties prop) 102 | throws SQLException { 103 | if (!isValidURL(url)) return null; 104 | 105 | url = url.trim(); 106 | return new JDBC4Connection(url, extractAddress(url), prop); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/main/java/org/sqlite/NativeLibraryNotFoundException.java: -------------------------------------------------------------------------------- 1 | package org.sqlite; 2 | 3 | public class NativeLibraryNotFoundException extends Exception { 4 | public NativeLibraryNotFoundException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/org/sqlite/ProgressHandler.java: -------------------------------------------------------------------------------- 1 | package org.sqlite; 2 | 3 | import java.sql.Connection; 4 | import java.sql.SQLException; 5 | 6 | /** https://www.sqlite.org/c3ref/progress_handler.html */ 7 | public abstract class ProgressHandler { 8 | /** 9 | * Sets a progress handler for the connection. 10 | * 11 | * @param conn the SQLite connection 12 | * @param vmCalls the approximate number of virtual machine instructions that are evaluated 13 | * between successive invocations of the progressHandler 14 | * @param progressHandler the progressHandler 15 | * @throws SQLException 16 | */ 17 | public static final void setHandler( 18 | Connection conn, int vmCalls, ProgressHandler progressHandler) throws SQLException { 19 | if (!(conn instanceof SQLiteConnection)) { 20 | throw new SQLException("connection must be to an SQLite db"); 21 | } 22 | if (conn.isClosed()) { 23 | throw new SQLException("connection closed"); 24 | } 25 | SQLiteConnection sqliteConnection = (SQLiteConnection) conn; 26 | sqliteConnection.getDatabase().register_progress_handler(vmCalls, progressHandler); 27 | } 28 | 29 | /** 30 | * Clears any progress handler registered with the connection. 31 | * 32 | * @param conn the SQLite connection 33 | * @throws SQLException 34 | */ 35 | public static final void clearHandler(Connection conn) throws SQLException { 36 | SQLiteConnection sqliteConnection = (SQLiteConnection) conn; 37 | sqliteConnection.getDatabase().clear_progress_handler(); 38 | } 39 | 40 | protected abstract int progress() throws SQLException; 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/org/sqlite/SQLiteCommitListener.java: -------------------------------------------------------------------------------- 1 | package org.sqlite; 2 | 3 | /** https://www.sqlite.org/c3ref/commit_hook.html */ 4 | public interface SQLiteCommitListener { 5 | 6 | void onCommit(); 7 | 8 | void onRollback(); 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/org/sqlite/SQLiteConnectionConfig.java: -------------------------------------------------------------------------------- 1 | package org.sqlite; 2 | 3 | import static org.sqlite.SQLiteConfig.DEFAULT_DATE_STRING_FORMAT; 4 | 5 | import java.sql.Connection; 6 | import java.util.EnumMap; 7 | import java.util.Map; 8 | import java.util.Properties; 9 | import org.sqlite.date.FastDateFormat; 10 | 11 | /** Connection local configurations */ 12 | public class SQLiteConnectionConfig implements Cloneable { 13 | private SQLiteConfig.DateClass dateClass = SQLiteConfig.DateClass.INTEGER; 14 | private SQLiteConfig.DatePrecision datePrecision = 15 | SQLiteConfig.DatePrecision.MILLISECONDS; // Calendar.SECOND or Calendar.MILLISECOND 16 | private String dateStringFormat = DEFAULT_DATE_STRING_FORMAT; 17 | private FastDateFormat dateFormat = FastDateFormat.getInstance(dateStringFormat); 18 | 19 | private int transactionIsolation = Connection.TRANSACTION_SERIALIZABLE; 20 | private SQLiteConfig.TransactionMode transactionMode = SQLiteConfig.TransactionMode.DEFERRED; 21 | private boolean autoCommit = true; 22 | private boolean getGeneratedKeys = true; 23 | 24 | public static SQLiteConnectionConfig fromPragmaTable(Properties pragmaTable) { 25 | return new SQLiteConnectionConfig( 26 | SQLiteConfig.DateClass.getDateClass( 27 | pragmaTable.getProperty( 28 | SQLiteConfig.Pragma.DATE_CLASS.pragmaName, 29 | SQLiteConfig.DateClass.INTEGER.name())), 30 | SQLiteConfig.DatePrecision.getPrecision( 31 | pragmaTable.getProperty( 32 | SQLiteConfig.Pragma.DATE_PRECISION.pragmaName, 33 | SQLiteConfig.DatePrecision.MILLISECONDS.name())), 34 | pragmaTable.getProperty( 35 | SQLiteConfig.Pragma.DATE_STRING_FORMAT.pragmaName, 36 | DEFAULT_DATE_STRING_FORMAT), 37 | Connection.TRANSACTION_SERIALIZABLE, 38 | SQLiteConfig.TransactionMode.getMode( 39 | pragmaTable.getProperty( 40 | SQLiteConfig.Pragma.TRANSACTION_MODE.pragmaName, 41 | SQLiteConfig.TransactionMode.DEFERRED.name())), 42 | true, 43 | Boolean.parseBoolean( 44 | pragmaTable.getProperty( 45 | SQLiteConfig.Pragma.JDBC_GET_GENERATED_KEYS.pragmaName, "true"))); 46 | } 47 | 48 | public SQLiteConnectionConfig( 49 | SQLiteConfig.DateClass dateClass, 50 | SQLiteConfig.DatePrecision datePrecision, 51 | String dateStringFormat, 52 | int transactionIsolation, 53 | SQLiteConfig.TransactionMode transactionMode, 54 | boolean autoCommit, 55 | boolean getGeneratedKeys) { 56 | setDateClass(dateClass); 57 | setDatePrecision(datePrecision); 58 | setDateStringFormat(dateStringFormat); 59 | setTransactionIsolation(transactionIsolation); 60 | setTransactionMode(transactionMode); 61 | setAutoCommit(autoCommit); 62 | setGetGeneratedKeys(getGeneratedKeys); 63 | } 64 | 65 | public SQLiteConnectionConfig copyConfig() { 66 | return new SQLiteConnectionConfig( 67 | dateClass, 68 | datePrecision, 69 | dateStringFormat, 70 | transactionIsolation, 71 | transactionMode, 72 | autoCommit, 73 | getGeneratedKeys); 74 | } 75 | 76 | public long getDateMultiplier() { 77 | return (datePrecision == SQLiteConfig.DatePrecision.MILLISECONDS) ? 1L : 1000L; 78 | } 79 | 80 | public SQLiteConfig.DateClass getDateClass() { 81 | return dateClass; 82 | } 83 | 84 | public void setDateClass(SQLiteConfig.DateClass dateClass) { 85 | this.dateClass = dateClass; 86 | } 87 | 88 | public SQLiteConfig.DatePrecision getDatePrecision() { 89 | return datePrecision; 90 | } 91 | 92 | public void setDatePrecision(SQLiteConfig.DatePrecision datePrecision) { 93 | this.datePrecision = datePrecision; 94 | } 95 | 96 | public String getDateStringFormat() { 97 | return dateStringFormat; 98 | } 99 | 100 | public void setDateStringFormat(String dateStringFormat) { 101 | this.dateStringFormat = dateStringFormat; 102 | this.dateFormat = FastDateFormat.getInstance(dateStringFormat); 103 | } 104 | 105 | public FastDateFormat getDateFormat() { 106 | return dateFormat; 107 | } 108 | 109 | public boolean isAutoCommit() { 110 | return autoCommit; 111 | } 112 | 113 | public void setAutoCommit(boolean autoCommit) { 114 | this.autoCommit = autoCommit; 115 | } 116 | 117 | public int getTransactionIsolation() { 118 | return transactionIsolation; 119 | } 120 | 121 | public void setTransactionIsolation(int transactionIsolation) { 122 | this.transactionIsolation = transactionIsolation; 123 | } 124 | 125 | public SQLiteConfig.TransactionMode getTransactionMode() { 126 | return transactionMode; 127 | } 128 | 129 | @SuppressWarnings("deprecation") 130 | public void setTransactionMode(SQLiteConfig.TransactionMode transactionMode) { 131 | this.transactionMode = transactionMode; 132 | } 133 | 134 | public boolean isGetGeneratedKeys() { 135 | return getGeneratedKeys; 136 | } 137 | 138 | public void setGetGeneratedKeys(boolean getGeneratedKeys) { 139 | this.getGeneratedKeys = getGeneratedKeys; 140 | } 141 | 142 | private static final Map beginCommandMap = 143 | new EnumMap<>(SQLiteConfig.TransactionMode.class); 144 | 145 | static { 146 | beginCommandMap.put(SQLiteConfig.TransactionMode.DEFERRED, "begin;"); 147 | beginCommandMap.put(SQLiteConfig.TransactionMode.IMMEDIATE, "begin immediate;"); 148 | beginCommandMap.put(SQLiteConfig.TransactionMode.EXCLUSIVE, "begin exclusive;"); 149 | } 150 | 151 | String transactionPrefix() { 152 | return beginCommandMap.get(transactionMode); 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /src/main/java/org/sqlite/SQLiteException.java: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------------------- 2 | * Copyright 2016 Magnus Reftel 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *--------------------------------------------------------------------------*/ 16 | // -------------------------------------- 17 | // sqlite-jdbc Project 18 | // 19 | // SQLiteException.java 20 | // Since: Jun 28, 2016 21 | // 22 | // $URL$ 23 | // $Author$ 24 | // -------------------------------------- 25 | package org.sqlite; 26 | 27 | import java.sql.SQLException; 28 | 29 | public class SQLiteException extends SQLException { 30 | private SQLiteErrorCode resultCode; 31 | 32 | public SQLiteException(String message, SQLiteErrorCode resultCode) { 33 | super(message, null, resultCode.code & 0xff); 34 | this.resultCode = resultCode; 35 | } 36 | 37 | public SQLiteErrorCode getResultCode() { 38 | return resultCode; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/org/sqlite/SQLiteLimits.java: -------------------------------------------------------------------------------- 1 | package org.sqlite; 2 | 3 | public enum SQLiteLimits { 4 | SQLITE_LIMIT_LENGTH(0), 5 | SQLITE_LIMIT_SQL_LENGTH(1), 6 | SQLITE_LIMIT_COLUMN(2), 7 | SQLITE_LIMIT_EXPR_DEPTH(3), 8 | SQLITE_LIMIT_COMPOUND_SELECT(4), 9 | SQLITE_LIMIT_VDBE_OP(5), 10 | SQLITE_LIMIT_FUNCTION_ARG(6), 11 | SQLITE_LIMIT_ATTACHED(7), 12 | SQLITE_LIMIT_LIKE_PATTERN_LENGTH(8), 13 | SQLITE_LIMIT_VARIABLE_NUMBER(9), 14 | SQLITE_LIMIT_TRIGGER_DEPTH(10), 15 | SQLITE_LIMIT_WORKER_THREADS(11), 16 | SQLITE_LIMIT_PAGE_COUNT(12); 17 | 18 | private final int id; 19 | 20 | private SQLiteLimits(int id) { 21 | this.id = id; 22 | } 23 | 24 | public int getId() { 25 | return id; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/org/sqlite/SQLiteOpenMode.java: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------------------- 2 | * Copyright 2009 Taro L. Saito 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *--------------------------------------------------------------------------*/ 16 | // -------------------------------------- 17 | // sqlite-jdbc Project 18 | // 19 | // SQLiteOpenMode.java 20 | // Since: Dec 8, 2009 21 | // 22 | // $URL$ 23 | // $Author$ 24 | // -------------------------------------- 25 | package org.sqlite; 26 | 27 | /** 28 | * Database file open modes of SQLite. 29 | * 30 | *

See also https://www.sqlite.org/c3ref/open.html 31 | * 32 | * @author leo 33 | */ 34 | public enum SQLiteOpenMode { 35 | READONLY(0x00000001), /* Ok for int SQLITE3_open_v2() */ 36 | READWRITE(0x00000002), /* Ok for int SQLITE3_open_v2() */ 37 | CREATE(0x00000004), /* Ok for int SQLITE3_open_v2() */ 38 | DELETEONCLOSE(0x00000008), /* VFS only */ 39 | EXCLUSIVE(0x00000010), /* VFS only */ 40 | OPEN_URI(0x00000040), /* Ok for sqlite3_open_v2() */ 41 | OPEN_MEMORY(0x00000080), /* Ok for sqlite3_open_v2() */ 42 | MAIN_DB(0x00000100), /* VFS only */ 43 | TEMP_DB(0x00000200), /* VFS only */ 44 | TRANSIENT_DB(0x00000400), /* VFS only */ 45 | MAIN_JOURNAL(0x00000800), /* VFS only */ 46 | TEMP_JOURNAL(0x00001000), /* VFS only */ 47 | SUBJOURNAL(0x00002000), /* VFS only */ 48 | MASTER_JOURNAL(0x00004000), /* VFS only */ 49 | NOMUTEX(0x00008000), /* Ok for int SQLITE3_open_v2() */ 50 | FULLMUTEX(0x00010000), /* Ok for int SQLITE3_open_v2() */ 51 | SHAREDCACHE(0x00020000), /* Ok for int SQLITE3_open_v2() */ 52 | PRIVATECACHE(0x00040000) /* Ok for sqlite3_open_v2() */; 53 | 54 | public final int flag; 55 | 56 | private SQLiteOpenMode(int flag) { 57 | this.flag = flag; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/org/sqlite/SQLiteUpdateListener.java: -------------------------------------------------------------------------------- 1 | package org.sqlite; 2 | 3 | /** https://www.sqlite.org/c3ref/update_hook.html */ 4 | public interface SQLiteUpdateListener { 5 | 6 | public enum Type { 7 | INSERT, 8 | DELETE, 9 | UPDATE 10 | } 11 | 12 | void onUpdate(Type type, String database, String table, long rowId); 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/org/sqlite/core/Codes.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2007 David Crawshaw 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | package org.sqlite.core; 17 | 18 | public interface Codes { 19 | /** Successful result */ 20 | public static final int SQLITE_OK = 0; 21 | 22 | /** SQL error or missing database */ 23 | public static final int SQLITE_ERROR = 1; 24 | 25 | /** An internal logic error in SQLite */ 26 | public static final int SQLITE_INTERNAL = 2; 27 | 28 | /** Access permission denied */ 29 | public static final int SQLITE_PERM = 3; 30 | 31 | /** Callback routine requested an abort */ 32 | public static final int SQLITE_ABORT = 4; 33 | 34 | /** The database file is locked */ 35 | public static final int SQLITE_BUSY = 5; 36 | 37 | /** A table in the database is locked */ 38 | public static final int SQLITE_LOCKED = 6; 39 | 40 | /** A malloc() failed */ 41 | public static final int SQLITE_NOMEM = 7; 42 | 43 | /** Attempt to write a readonly database */ 44 | public static final int SQLITE_READONLY = 8; 45 | 46 | /** Operation terminated by sqlite_interrupt() */ 47 | public static final int SQLITE_INTERRUPT = 9; 48 | 49 | /** Some kind of disk I/O error occurred */ 50 | public static final int SQLITE_IOERR = 10; 51 | 52 | /** The database disk image is malformed */ 53 | public static final int SQLITE_CORRUPT = 11; 54 | 55 | /** (Internal Only) Table or record not found */ 56 | public static final int SQLITE_NOTFOUND = 12; 57 | 58 | /** Insertion failed because database is full */ 59 | public static final int SQLITE_FULL = 13; 60 | 61 | /** Unable to open the database file */ 62 | public static final int SQLITE_CANTOPEN = 14; 63 | 64 | /** Database lock protocol error */ 65 | public static final int SQLITE_PROTOCOL = 15; 66 | 67 | /** (Internal Only) Database table is empty */ 68 | public static final int SQLITE_EMPTY = 16; 69 | 70 | /** The database schema changed */ 71 | public static final int SQLITE_SCHEMA = 17; 72 | 73 | /** Too much data for one row of a table */ 74 | public static final int SQLITE_TOOBIG = 18; 75 | 76 | /** Abort due to constraint violation */ 77 | public static final int SQLITE_CONSTRAINT = 19; 78 | 79 | /** Data type mismatch */ 80 | public static final int SQLITE_MISMATCH = 20; 81 | 82 | /** Library used incorrectly */ 83 | public static final int SQLITE_MISUSE = 21; 84 | 85 | /** Uses OS features not supported on host */ 86 | public static final int SQLITE_NOLFS = 22; 87 | 88 | /** Authorization denied */ 89 | public static final int SQLITE_AUTH = 23; 90 | 91 | /** sqlite_step() has another row ready */ 92 | public static final int SQLITE_ROW = 100; 93 | 94 | /** sqlite_step() has finished executing */ 95 | public static final int SQLITE_DONE = 101; 96 | 97 | // types returned by sqlite3_column_type() 98 | 99 | public static final int SQLITE_INTEGER = 1; 100 | public static final int SQLITE_FLOAT = 2; 101 | public static final int SQLITE_TEXT = 3; 102 | public static final int SQLITE_BLOB = 4; 103 | public static final int SQLITE_NULL = 5; 104 | } 105 | -------------------------------------------------------------------------------- /src/main/java/org/sqlite/core/CorePreparedStatement.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2007 David Crawshaw 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | package org.sqlite.core; 18 | 19 | import java.sql.Date; 20 | import java.sql.SQLException; 21 | import java.util.Arrays; 22 | import java.util.Calendar; 23 | import org.sqlite.SQLiteConnection; 24 | import org.sqlite.SQLiteConnectionConfig; 25 | import org.sqlite.date.FastDateFormat; 26 | import org.sqlite.jdbc3.JDBC3Connection; 27 | import org.sqlite.jdbc4.JDBC4Statement; 28 | 29 | public abstract class CorePreparedStatement extends JDBC4Statement { 30 | protected int columnCount; 31 | protected int paramCount; 32 | protected int batchQueryCount; 33 | 34 | /** 35 | * Constructs a prepared statement on a provided connection. 36 | * 37 | * @param conn Connection on which to create the prepared statement. 38 | * @param sql The SQL script to prepare. 39 | * @throws SQLException 40 | */ 41 | protected CorePreparedStatement(SQLiteConnection conn, String sql) throws SQLException { 42 | super(conn); 43 | 44 | this.sql = sql; 45 | DB db = conn.getDatabase(); 46 | db.prepare(this); 47 | rs.colsMeta = pointer.safeRun(DB::column_names); 48 | columnCount = pointer.safeRunInt(DB::column_count); 49 | paramCount = pointer.safeRunInt(DB::bind_parameter_count); 50 | batchQueryCount = 0; 51 | batch = null; 52 | batchPos = 0; 53 | } 54 | 55 | /** @see org.sqlite.jdbc3.JDBC3Statement#executeBatch() */ 56 | @Override 57 | public int[] executeBatch() throws SQLException { 58 | return Arrays.stream(executeLargeBatch()).mapToInt(l -> (int) l).toArray(); 59 | } 60 | 61 | /** @see org.sqlite.jdbc3.JDBC3Statement#executeLargeBatch() */ 62 | @Override 63 | public long[] executeLargeBatch() throws SQLException { 64 | if (batchQueryCount == 0) { 65 | return new long[] {}; 66 | } 67 | 68 | if (this.conn instanceof JDBC3Connection) { 69 | ((JDBC3Connection) this.conn).tryEnforceTransactionMode(); 70 | } 71 | 72 | return this.withConnectionTimeout( 73 | () -> { 74 | try { 75 | return conn.getDatabase() 76 | .executeBatch( 77 | pointer, batchQueryCount, batch, conn.getAutoCommit()); 78 | } finally { 79 | clearBatch(); 80 | } 81 | }); 82 | } 83 | 84 | /** @see org.sqlite.jdbc3.JDBC3Statement#clearBatch() () */ 85 | @Override 86 | public void clearBatch() throws SQLException { 87 | super.clearBatch(); 88 | batchQueryCount = 0; 89 | } 90 | 91 | // PARAMETER FUNCTIONS ////////////////////////////////////////// 92 | 93 | /** 94 | * Assigns the object value to the element at the specific position of array batch. 95 | * 96 | * @param pos 97 | * @param value 98 | * @throws SQLException 99 | */ 100 | protected void batch(int pos, Object value) throws SQLException { 101 | checkOpen(); 102 | if (batch == null) { 103 | batch = new Object[paramCount]; 104 | } 105 | batch[batchPos + pos - 1] = value; 106 | } 107 | 108 | /** Store the date in the user's preferred format (text, int, or real) */ 109 | protected void setDateByMilliseconds(int pos, Long value, Calendar calendar) 110 | throws SQLException { 111 | SQLiteConnectionConfig config = conn.getConnectionConfig(); 112 | switch (config.getDateClass()) { 113 | case TEXT: 114 | batch( 115 | pos, 116 | FastDateFormat.getInstance( 117 | config.getDateStringFormat(), calendar.getTimeZone()) 118 | .format(new Date(value))); 119 | break; 120 | 121 | case REAL: 122 | // long to Julian date 123 | batch(pos, new Double((value / 86400000.0) + 2440587.5)); 124 | break; 125 | 126 | default: // INTEGER: 127 | batch(pos, new Long(value / config.getDateMultiplier())); 128 | } 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /src/main/java/org/sqlite/core/CoreResultSet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2007 David Crawshaw 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | package org.sqlite.core; 17 | 18 | import java.sql.SQLException; 19 | import java.sql.Statement; 20 | import java.util.HashMap; 21 | import java.util.Map; 22 | import org.sqlite.SQLiteConnectionConfig; 23 | 24 | /** Implements a JDBC ResultSet. */ 25 | public abstract class CoreResultSet implements Codes { 26 | protected final CoreStatement stmt; 27 | 28 | /** If the result set does not have any rows. */ 29 | public boolean emptyResultSet = false; 30 | /** If the result set is open. Doesn't mean it has results. */ 31 | public boolean open = false; 32 | /** Maximum number of rows as set by a Statement */ 33 | public long maxRows; 34 | /** if null, the RS is closed() */ 35 | public String[] cols = null; 36 | /** same as cols, but used by Meta interface */ 37 | public String[] colsMeta = null; 38 | 39 | protected boolean[][] meta = null; 40 | 41 | /** 0 means no limit, must check against maxRows */ 42 | protected int limitRows; 43 | /** number of current row, starts at 1 (0 is for before loading data) */ 44 | protected int row = 0; 45 | 46 | protected boolean pastLastRow = false; 47 | /** last column accessed, for wasNull(). -1 if none */ 48 | protected int lastCol; 49 | 50 | public boolean closeStmt; 51 | protected Map columnNameToIndex = null; 52 | 53 | /** 54 | * Default constructor for a given statement. 55 | * 56 | * @param stmt The statement. 57 | */ 58 | protected CoreResultSet(CoreStatement stmt) { 59 | this.stmt = stmt; 60 | } 61 | 62 | // INTERNAL FUNCTIONS /////////////////////////////////////////// 63 | 64 | protected DB getDatabase() { 65 | return stmt.getDatabase(); 66 | } 67 | 68 | protected SQLiteConnectionConfig getConnectionConfig() { 69 | return stmt.getConnectionConfig(); 70 | } 71 | 72 | /** 73 | * Checks the status of the result set. 74 | * 75 | * @return True if has results and can iterate them; false otherwise. 76 | */ 77 | public boolean isOpen() { 78 | return open; 79 | } 80 | 81 | /** @throws SQLException if ResultSet is not open. */ 82 | protected void checkOpen() throws SQLException { 83 | if (!open) { 84 | throw new SQLException("ResultSet closed"); 85 | } 86 | } 87 | 88 | /** 89 | * Takes col in [1,x] form, returns in [0,x-1] form 90 | * 91 | * @param col 92 | * @return 93 | * @throws SQLException 94 | */ 95 | public int checkCol(int col) throws SQLException { 96 | if (colsMeta == null) { 97 | throw new SQLException("SQLite JDBC: inconsistent internal state"); 98 | } 99 | if (col < 1 || col > colsMeta.length) { 100 | throw new SQLException("column " + col + " out of bounds [1," + colsMeta.length + "]"); 101 | } 102 | return --col; 103 | } 104 | 105 | /** 106 | * Takes col in [1,x] form, marks it as last accessed and returns [0,x-1] 107 | * 108 | * @param col 109 | * @return 110 | * @throws SQLException 111 | */ 112 | protected int markCol(int col) throws SQLException { 113 | checkCol(col); 114 | lastCol = col; 115 | return --col; 116 | } 117 | 118 | /** @throws SQLException */ 119 | public void checkMeta() throws SQLException { 120 | checkCol(1); 121 | if (meta == null) { 122 | meta = stmt.pointer.safeRun(DB::column_metadata); 123 | } 124 | } 125 | 126 | public void close() throws SQLException { 127 | cols = null; 128 | colsMeta = null; 129 | meta = null; 130 | limitRows = 0; 131 | row = 0; 132 | pastLastRow = false; 133 | lastCol = -1; 134 | columnNameToIndex = null; 135 | emptyResultSet = false; 136 | 137 | if (stmt.pointer.isClosed() || (!open && !closeStmt)) { 138 | return; 139 | } 140 | 141 | DB db = stmt.getDatabase(); 142 | synchronized (db) { 143 | if (!stmt.pointer.isClosed()) { 144 | stmt.pointer.safeRunInt(DB::reset); 145 | 146 | if (closeStmt) { 147 | closeStmt = false; // break recursive call 148 | ((Statement) stmt).close(); 149 | } 150 | } 151 | } 152 | 153 | open = false; 154 | } 155 | 156 | protected Integer findColumnIndexInCache(String col) { 157 | if (columnNameToIndex == null) { 158 | return null; 159 | } 160 | return columnNameToIndex.get(col); 161 | } 162 | 163 | protected int addColumnIndexInCache(String col, int index) { 164 | if (columnNameToIndex == null) { 165 | columnNameToIndex = new HashMap(cols.length); 166 | } 167 | columnNameToIndex.put(col, index); 168 | return index; 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /src/main/java/org/sqlite/core/SafeStmtPtr.java: -------------------------------------------------------------------------------- 1 | package org.sqlite.core; 2 | 3 | import java.sql.SQLException; 4 | 5 | /** 6 | * A class for safely wrapping calls to a native pointer to a statement, ensuring no other thread 7 | * has access to the pointer while it is run 8 | */ 9 | public class SafeStmtPtr { 10 | // store a reference to the DB, to lock it before any safe function is called. This avoids 11 | // deadlocking by locking the DB. All calls with the raw pointer are synchronized with the DB 12 | // anyways, so making a separate lock would be pointless 13 | private final DB db; 14 | private final long ptr; 15 | 16 | private volatile boolean closed = false; 17 | // to return on subsequent calls to close() after this ptr has been closed 18 | private int closedRC; 19 | // to throw on subsequent calls to close after this ptr has been closed, if the close function 20 | // threw an exception 21 | private SQLException closeException; 22 | 23 | /** 24 | * Construct a new Safe Pointer Wrapper to ensure a pointer is properly handled 25 | * 26 | * @param db the database that made this pointer. Always locked before any safe run function is 27 | * executed to avoid deadlocks 28 | * @param ptr the raw pointer 29 | */ 30 | public SafeStmtPtr(DB db, long ptr) { 31 | this.db = db; 32 | this.ptr = ptr; 33 | } 34 | 35 | /** 36 | * Check whether this pointer has been closed 37 | * 38 | * @return whether this pointer has been closed 39 | */ 40 | public boolean isClosed() { 41 | return closed; 42 | } 43 | 44 | /** 45 | * Close this pointer 46 | * 47 | * @return the return code of the close callback function 48 | * @throws SQLException if the close callback throws an SQLException, or the pointer is locked 49 | * elsewhere 50 | */ 51 | public int close() throws SQLException { 52 | synchronized (db) { 53 | return internalClose(); 54 | } 55 | } 56 | 57 | private int internalClose() throws SQLException { 58 | try { 59 | // if this is already closed, return or throw the previous result 60 | if (closed) { 61 | if (closeException != null) throw closeException; 62 | return closedRC; 63 | } 64 | closedRC = db.finalize(this, ptr); 65 | return closedRC; 66 | } catch (SQLException ex) { 67 | this.closeException = ex; 68 | throw ex; 69 | } finally { 70 | this.closed = true; 71 | } 72 | } 73 | 74 | /** 75 | * Run a callback with the wrapped pointer safely. 76 | * 77 | * @param run the function to run 78 | * @return the return of the passed in function 79 | * @throws SQLException if the pointer is utilized elsewhere 80 | */ 81 | public int safeRunInt(SafePtrIntFunction run) throws SQLException, E { 82 | synchronized (db) { 83 | this.ensureOpen(); 84 | return run.run(db, ptr); 85 | } 86 | } 87 | 88 | /** 89 | * Run a callback with the wrapped pointer safely. 90 | * 91 | * @param run the function to run 92 | * @return the return of the passed in function 93 | * @throws SQLException if the pointer is utilized elsewhere 94 | */ 95 | public long safeRunLong(SafePtrLongFunction run) 96 | throws SQLException, E { 97 | synchronized (db) { 98 | this.ensureOpen(); 99 | return run.run(db, ptr); 100 | } 101 | } 102 | 103 | /** 104 | * Run a callback with the wrapped pointer safely. 105 | * 106 | * @param run the function to run 107 | * @return the return of the passed in function 108 | * @throws SQLException if the pointer is utilized elsewhere 109 | */ 110 | public double safeRunDouble(SafePtrDoubleFunction run) 111 | throws SQLException, E { 112 | synchronized (db) { 113 | this.ensureOpen(); 114 | return run.run(db, ptr); 115 | } 116 | } 117 | 118 | /** 119 | * Run a callback with the wrapped pointer safely. 120 | * 121 | * @param run the function to run 122 | * @return the return code of the function 123 | * @throws SQLException if the pointer is utilized elsewhere 124 | */ 125 | public T safeRun(SafePtrFunction run) throws SQLException, E { 126 | synchronized (db) { 127 | this.ensureOpen(); 128 | return run.run(db, ptr); 129 | } 130 | } 131 | 132 | /** 133 | * Run a callback with the wrapped pointer safely. 134 | * 135 | * @param run the function to run 136 | * @throws SQLException if the pointer is utilized elsewhere 137 | */ 138 | public void safeRunConsume(SafePtrConsumer run) 139 | throws SQLException, E { 140 | synchronized (db) { 141 | this.ensureOpen(); 142 | run.run(db, ptr); 143 | } 144 | } 145 | 146 | private void ensureOpen() throws SQLException { 147 | if (this.closed) { 148 | throw new SQLException("stmt pointer is closed"); 149 | } 150 | } 151 | 152 | @Override 153 | public boolean equals(Object o) { 154 | if (this == o) return true; 155 | if (o == null || getClass() != o.getClass()) return false; 156 | SafeStmtPtr that = (SafeStmtPtr) o; 157 | return ptr == that.ptr; 158 | } 159 | 160 | @Override 161 | public int hashCode() { 162 | return Long.hashCode(ptr); 163 | } 164 | 165 | @FunctionalInterface 166 | public interface SafePtrIntFunction { 167 | int run(DB db, long ptr) throws E; 168 | } 169 | 170 | @FunctionalInterface 171 | public interface SafePtrLongFunction { 172 | long run(DB db, long ptr) throws E; 173 | } 174 | 175 | @FunctionalInterface 176 | public interface SafePtrDoubleFunction { 177 | double run(DB db, long ptr) throws E; 178 | } 179 | 180 | @FunctionalInterface 181 | public interface SafePtrFunction { 182 | T run(DB db, long ptr) throws E; 183 | } 184 | 185 | @FunctionalInterface 186 | public interface SafePtrConsumer { 187 | void run(DB db, long ptr) throws E; 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /src/main/java/org/sqlite/date/DateParser.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.sqlite.date; 18 | 19 | import java.text.ParseException; 20 | import java.text.ParsePosition; 21 | import java.util.Date; 22 | import java.util.Locale; 23 | import java.util.TimeZone; 24 | 25 | /** 26 | * DateParser is the "missing" interface for the parsing methods of {@link java.text.DateFormat}. 27 | * 28 | * @since 3.2 29 | */ 30 | public interface DateParser { 31 | 32 | /** 33 | * Equivalent to DateFormat.parse(String). 34 | * 35 | *

See {@link java.text.DateFormat#parse(String)} for more information. 36 | * 37 | * @param source A String whose beginning should be parsed. 38 | * @return A Date parsed from the string 39 | * @throws ParseException if the beginning of the specified string cannot be parsed. 40 | */ 41 | Date parse(String source) throws ParseException; 42 | 43 | /** 44 | * Equivalent to DateFormat.parse(String, ParsePosition). 45 | * 46 | *

See {@link java.text.DateFormat#parse(String, ParsePosition)} for more information. 47 | * 48 | * @param source A String, part of which should be parsed. 49 | * @param pos A ParsePosition object with index and error index information as 50 | * described above. 51 | * @return A Date parsed from the string. In case of error, returns null. 52 | * @throws NullPointerException if text or pos is null. 53 | */ 54 | Date parse(String source, ParsePosition pos); 55 | 56 | // Accessors 57 | // ----------------------------------------------------------------------- 58 | /** 59 | * Get the pattern used by this parser. 60 | * 61 | * @return the pattern, {@link java.text.SimpleDateFormat} compatible 62 | */ 63 | String getPattern(); 64 | 65 | /** 66 | * Get the time zone used by this parser. 67 | * 68 | *

The default {@link TimeZone} used to create a {@link Date} when the {@link TimeZone} is 69 | * not specified by the format pattern. 70 | * 71 | * @return the time zone 72 | */ 73 | TimeZone getTimeZone(); 74 | 75 | /** 76 | * Get the locale used by this parser. 77 | * 78 | * @return the locale 79 | */ 80 | Locale getLocale(); 81 | 82 | /** 83 | * Parses text from a string to produce a Date. 84 | * 85 | * @param source A String whose beginning should be parsed. 86 | * @return a java.util.Date object 87 | * @throws ParseException if the beginning of the specified string cannot be parsed. 88 | * @see java.text.DateFormat#parseObject(String) 89 | */ 90 | Object parseObject(String source) throws ParseException; 91 | 92 | /** 93 | * Parse a date/time string according to the given parse position. 94 | * 95 | * @param source A String whose beginning should be parsed. 96 | * @param pos the parse position 97 | * @return a java.util.Date object 98 | * @see java.text.DateFormat#parseObject(String, ParsePosition) 99 | */ 100 | Object parseObject(String source, ParsePosition pos); 101 | } 102 | -------------------------------------------------------------------------------- /src/main/java/org/sqlite/date/DatePrinter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.sqlite.date; 18 | 19 | import java.text.FieldPosition; 20 | import java.util.Calendar; 21 | import java.util.Date; 22 | import java.util.Locale; 23 | import java.util.TimeZone; 24 | 25 | /** 26 | * DatePrinter is the "missing" interface for the format methods of {@link java.text.DateFormat}. 27 | * 28 | * @since 3.2 29 | */ 30 | public interface DatePrinter { 31 | 32 | /** 33 | * Formats a millisecond {@code long} value. 34 | * 35 | * @param millis the millisecond value to format 36 | * @return the formatted string 37 | * @since 2.1 38 | */ 39 | String format(long millis); 40 | 41 | /** 42 | * Formats a {@code Date} object using a {@code GregorianCalendar}. 43 | * 44 | * @param date the date to format 45 | * @return the formatted string 46 | */ 47 | String format(Date date); 48 | 49 | /** 50 | * Formats a {@code Calendar} object. 51 | * 52 | * @param calendar the calendar to format 53 | * @return the formatted string 54 | */ 55 | String format(Calendar calendar); 56 | 57 | /** 58 | * Formats a millisecond {@code long} value into the supplied {@code StringBuffer}. 59 | * 60 | * @param millis the millisecond value to format 61 | * @param buf the buffer to format into 62 | * @return the specified string buffer 63 | */ 64 | StringBuffer format(long millis, StringBuffer buf); 65 | 66 | /** 67 | * Formats a {@code Date} object into the supplied {@code StringBuffer} using a {@code 68 | * GregorianCalendar}. 69 | * 70 | * @param date the date to format 71 | * @param buf the buffer to format into 72 | * @return the specified string buffer 73 | */ 74 | StringBuffer format(Date date, StringBuffer buf); 75 | 76 | /** 77 | * Formats a {@code Calendar} object into the supplied {@code StringBuffer}. 78 | * 79 | * @param calendar the calendar to format 80 | * @param buf the buffer to format into 81 | * @return the specified string buffer 82 | */ 83 | StringBuffer format(Calendar calendar, StringBuffer buf); 84 | 85 | // Accessors 86 | // ----------------------------------------------------------------------- 87 | /** 88 | * Gets the pattern used by this printer. 89 | * 90 | * @return the pattern, {@link java.text.SimpleDateFormat} compatible 91 | */ 92 | String getPattern(); 93 | 94 | /** 95 | * Gets the time zone used by this printer. 96 | * 97 | *

This zone is always used for {@code Date} printing. 98 | * 99 | * @return the time zone 100 | */ 101 | TimeZone getTimeZone(); 102 | 103 | /** 104 | * Gets the locale used by this printer. 105 | * 106 | * @return the locale 107 | */ 108 | Locale getLocale(); 109 | 110 | /** 111 | * Formats a {@code Date}, {@code Calendar} or {@code Long} (milliseconds) object. See {@link 112 | * java.text.DateFormat#format(Object, StringBuffer, FieldPosition)} 113 | * 114 | * @param obj the object to format 115 | * @param toAppendTo the buffer to append to 116 | * @param pos the position - ignored 117 | * @return the buffer passed in 118 | */ 119 | StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos); 120 | } 121 | -------------------------------------------------------------------------------- /src/main/java/org/sqlite/date/ExceptionUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.sqlite.date; 18 | 19 | /** 20 | * Provides utilities for manipulating and examining Throwable objects. 21 | * 22 | * @since 1.0 23 | */ 24 | public class ExceptionUtils { 25 | 26 | /** 27 | * Throw a checked exception without adding the exception to the throws clause of the calling 28 | * method. This method prevents throws clause pollution and reduces the clutter of "Caused by" 29 | * exceptions in the stacktrace. 30 | * 31 | *

The use of this technique may be controversial, but exceedingly useful to library 32 | * developers. 33 | * public int propagateExample { // note that there is no throws clause 34 | * try { 35 | * return invocation(); // throws IOException 36 | * } catch (Exception e) { 37 | * return ExceptionUtils.rethrow(e); // propagates a checked exception 38 | * } 39 | * } 40 | * 41 | * 42 | *

This is an alternative to the more conservative approach of wrapping the checked exception 43 | * in a RuntimeException: 44 | * public int wrapExample { // note that there is no throws clause 45 | * try { 46 | * return invocation(); // throws IOException 47 | * } catch (Error e) { 48 | * throw e; 49 | * } catch (RuntimeException e) { 50 | * throw e; // wraps a checked exception 51 | * } catch (Exception e) { 52 | * throw new UndeclaredThrowableException(e); // wraps a checked exception 53 | * } 54 | * } 55 | * 56 | * 57 | *

One downside to using this approach is that the java compiler will not allow invoking code 58 | * to specify a checked exception in a catch clause unless there is some code path within the 59 | * try block that has invoked a method declared with that checked exception. If the invoking 60 | * site wishes to catch the shaded checked exception, it must either invoke the shaded code 61 | * through a method re-declaring the desired checked exception, or catch Exception and use the 62 | * instanceof operator. Either of these techniques are required when interacting with non-java 63 | * jvm code such as Jython, Scala, or Groovy, since these languages do not consider any 64 | * exceptions as checked. 65 | * 66 | * @since 3.5 67 | * @see {{@link #wrapAndThrow(Throwable)} 68 | * @param throwable The throwable to rethrow. 69 | * @return R Never actually returns, this generic type matches any type which the calling site 70 | * requires. "Returning" the results of this method, as done in the propagateExample above, 71 | * will satisfy the java compiler requirement that all code paths return a value. 72 | * @throws throwable 73 | */ 74 | public static R rethrow(Throwable throwable) { 75 | // claim that the typeErasure invocation throws a RuntimeException 76 | return ExceptionUtils.typeErasure(throwable); 77 | } 78 | 79 | /** 80 | * Claim a Throwable is another Exception type using type erasure. This hides a checked 81 | * exception from the java compiler, allowing a checked exception to be thrown without having 82 | * the exception in the method's throw clause. 83 | */ 84 | @SuppressWarnings("unchecked") 85 | private static R typeErasure(Throwable throwable) throws T { 86 | throw (T) throwable; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/org/sqlite/date/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | /** 18 | * Provides classes and methods to work with dates and durations. These classes are immutable (and 19 | * therefore thread-safe) apart from org.apache.commons.lang3.time.StopWatch. 20 | * 21 | *

The time package contains some basic utilities for manipulating time (a delorean, police box 22 | * and grandfather clock?). These include a org.apache.commons.lang3.time.StopWatch for simple 23 | * performance measurements and an optimised {@link org.sqlite.date.FastDateFormat} class. 24 | * 25 | *

New in Lang 2.1 is the org.apache.commons.lang3.time.DurationFormatUtils class, which provides 26 | * various methods for formatting durations. 27 | * 28 | * @since 2.0 29 | */ 30 | package org.sqlite.date; 31 | -------------------------------------------------------------------------------- /src/main/java/org/sqlite/javax/SQLiteConnectionPoolDataSource.java: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------------------- 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | *--------------------------------------------------------------------------*/ 14 | package org.sqlite.javax; 15 | 16 | import java.sql.SQLException; 17 | import javax.sql.PooledConnection; 18 | import org.sqlite.SQLiteConfig; 19 | import org.sqlite.SQLiteDataSource; 20 | 21 | public class SQLiteConnectionPoolDataSource extends SQLiteDataSource 22 | implements javax.sql.ConnectionPoolDataSource { 23 | 24 | /** Default constructor. */ 25 | public SQLiteConnectionPoolDataSource() { 26 | super(); 27 | } 28 | 29 | /** 30 | * Creates a data source based on the provided configuration. 31 | * 32 | * @param config The configuration for the data source. 33 | */ 34 | public SQLiteConnectionPoolDataSource(SQLiteConfig config) { 35 | super(config); 36 | } 37 | 38 | /** @see javax.sql.ConnectionPoolDataSource#getPooledConnection() */ 39 | public PooledConnection getPooledConnection() throws SQLException { 40 | return getPooledConnection(null, null); 41 | } 42 | 43 | /** 44 | * @see javax.sql.ConnectionPoolDataSource#getPooledConnection(java.lang.String, 45 | * java.lang.String) 46 | */ 47 | public PooledConnection getPooledConnection(String user, String password) throws SQLException { 48 | return new SQLitePooledConnection(getConnection(user, password)); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/org/sqlite/jdbc3/JDBC3Savepoint.java: -------------------------------------------------------------------------------- 1 | package org.sqlite.jdbc3; 2 | 3 | import java.sql.SQLException; 4 | import java.sql.Savepoint; 5 | 6 | public class JDBC3Savepoint implements Savepoint { 7 | 8 | final int id; 9 | 10 | final String name; 11 | 12 | JDBC3Savepoint(int id) { 13 | this.id = id; 14 | this.name = null; 15 | } 16 | 17 | JDBC3Savepoint(int id, String name) { 18 | this.id = id; 19 | this.name = name; 20 | } 21 | 22 | public int getSavepointId() throws SQLException { 23 | return id; 24 | } 25 | 26 | public String getSavepointName() throws SQLException { 27 | return name == null ? String.format("SQLITE_SAVEPOINT_%s", id) : name; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/org/sqlite/jdbc4/JDBC4Connection.java: -------------------------------------------------------------------------------- 1 | package org.sqlite.jdbc4; 2 | 3 | import java.sql.Array; 4 | import java.sql.Blob; 5 | import java.sql.Clob; 6 | import java.sql.NClob; 7 | import java.sql.PreparedStatement; 8 | import java.sql.SQLClientInfoException; 9 | import java.sql.SQLException; 10 | import java.sql.SQLFeatureNotSupportedException; 11 | import java.sql.SQLXML; 12 | import java.sql.Statement; 13 | import java.util.Properties; 14 | import org.sqlite.jdbc3.JDBC3Connection; 15 | 16 | public class JDBC4Connection extends JDBC3Connection { 17 | 18 | public JDBC4Connection(String url, String fileName, Properties prop) throws SQLException { 19 | super(url, fileName, prop); 20 | } 21 | 22 | public Statement createStatement(int rst, int rsc, int rsh) throws SQLException { 23 | checkOpen(); 24 | checkCursor(rst, rsc, rsh); 25 | 26 | return new JDBC4Statement(this); 27 | } 28 | 29 | public PreparedStatement prepareStatement(String sql, int rst, int rsc, int rsh) 30 | throws SQLException { 31 | checkOpen(); 32 | checkCursor(rst, rsc, rsh); 33 | 34 | return new JDBC4PreparedStatement(this, sql); 35 | } 36 | 37 | // JDBC 4 38 | /** @see java.sql.Connection#isClosed() */ 39 | public boolean isClosed() throws SQLException { 40 | return super.isClosed(); 41 | } 42 | 43 | public T unwrap(Class iface) throws ClassCastException { 44 | // caller should invoke isWrapperFor prior to unwrap 45 | return iface.cast(this); 46 | } 47 | 48 | public boolean isWrapperFor(Class iface) { 49 | return iface.isInstance(this); 50 | } 51 | 52 | public Clob createClob() throws SQLException { 53 | // TODO Support this 54 | throw new SQLFeatureNotSupportedException(); 55 | } 56 | 57 | public Blob createBlob() throws SQLException { 58 | // TODO Support this 59 | throw new SQLFeatureNotSupportedException(); 60 | } 61 | 62 | public NClob createNClob() throws SQLException { 63 | // TODO Support this 64 | throw new SQLFeatureNotSupportedException(); 65 | } 66 | 67 | public SQLXML createSQLXML() throws SQLException { 68 | // TODO Support this 69 | throw new SQLFeatureNotSupportedException(); 70 | } 71 | 72 | public boolean isValid(int timeout) throws SQLException { 73 | if (isClosed()) { 74 | return false; 75 | } 76 | Statement statement = createStatement(); 77 | try { 78 | return statement.execute("select 1"); 79 | } finally { 80 | statement.close(); 81 | } 82 | } 83 | 84 | public void setClientInfo(String name, String value) throws SQLClientInfoException { 85 | // TODO Auto-generated method stub 86 | 87 | } 88 | 89 | public void setClientInfo(Properties properties) throws SQLClientInfoException { 90 | // TODO Auto-generated method stub 91 | 92 | } 93 | 94 | public String getClientInfo(String name) throws SQLException { 95 | // TODO Auto-generated method stub 96 | return null; 97 | } 98 | 99 | public Properties getClientInfo() throws SQLException { 100 | // TODO Auto-generated method stub 101 | return null; 102 | } 103 | 104 | public Array createArrayOf(String typeName, Object[] elements) throws SQLException { 105 | // TODO Auto-generated method stub 106 | return null; 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/main/java/org/sqlite/jdbc4/JDBC4DatabaseMetaData.java: -------------------------------------------------------------------------------- 1 | package org.sqlite.jdbc4; 2 | 3 | import java.sql.ResultSet; 4 | import java.sql.RowIdLifetime; 5 | import java.sql.SQLException; 6 | import java.sql.SQLFeatureNotSupportedException; 7 | import org.sqlite.SQLiteConnection; 8 | import org.sqlite.jdbc3.JDBC3DatabaseMetaData; 9 | 10 | public class JDBC4DatabaseMetaData extends JDBC3DatabaseMetaData { 11 | public JDBC4DatabaseMetaData(SQLiteConnection conn) { 12 | super(conn); 13 | } 14 | 15 | // JDBC 4 16 | public T unwrap(Class iface) throws ClassCastException { 17 | return iface.cast(this); 18 | } 19 | 20 | public boolean isWrapperFor(Class iface) { 21 | return iface.isInstance(this); 22 | } 23 | 24 | public RowIdLifetime getRowIdLifetime() throws SQLException { 25 | throw new SQLFeatureNotSupportedException(); 26 | } 27 | 28 | public ResultSet getSchemas(String catalog, String schemaPattern) throws SQLException { 29 | throw new SQLFeatureNotSupportedException(); 30 | } 31 | 32 | public boolean supportsStoredFunctionsUsingCallSyntax() throws SQLException { 33 | throw new SQLFeatureNotSupportedException(); 34 | } 35 | 36 | public boolean autoCommitFailureClosesAllResultSets() throws SQLException { 37 | throw new SQLFeatureNotSupportedException(); 38 | } 39 | 40 | public ResultSet getClientInfoProperties() throws SQLException { 41 | throw new SQLFeatureNotSupportedException(); 42 | } 43 | 44 | public ResultSet getFunctions(String catalog, String schemaPattern, String functionNamePattern) 45 | throws SQLException { 46 | throw new SQLFeatureNotSupportedException(); 47 | } 48 | 49 | public ResultSet getPseudoColumns( 50 | String catalog, String schemaPattern, String tableNamePattern, String columnNamePattern) 51 | throws SQLException { 52 | throw new SQLFeatureNotSupportedException(); 53 | } 54 | 55 | public boolean generatedKeyAlwaysReturned() throws SQLException { 56 | throw new SQLFeatureNotSupportedException(); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/org/sqlite/jdbc4/JDBC4PooledConnection.java: -------------------------------------------------------------------------------- 1 | package org.sqlite.jdbc4; 2 | 3 | import javax.sql.PooledConnection; 4 | import javax.sql.StatementEventListener; 5 | 6 | public abstract class JDBC4PooledConnection implements PooledConnection { 7 | 8 | public void addStatementEventListener(StatementEventListener listener) { 9 | // TODO impl 10 | } 11 | 12 | public void removeStatementEventListener(StatementEventListener listener) { 13 | // TODO impl 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/org/sqlite/jdbc4/JDBC4PreparedStatement.java: -------------------------------------------------------------------------------- 1 | package org.sqlite.jdbc4; 2 | 3 | import java.io.InputStream; 4 | import java.io.Reader; 5 | import java.sql.NClob; 6 | import java.sql.ParameterMetaData; 7 | import java.sql.PreparedStatement; 8 | import java.sql.RowId; 9 | import java.sql.SQLException; 10 | import java.sql.SQLFeatureNotSupportedException; 11 | import java.sql.SQLXML; 12 | import java.util.Arrays; 13 | import org.sqlite.SQLiteConnection; 14 | import org.sqlite.jdbc3.JDBC3PreparedStatement; 15 | 16 | public class JDBC4PreparedStatement extends JDBC3PreparedStatement 17 | implements PreparedStatement, ParameterMetaData { 18 | 19 | @Override 20 | public String toString() { 21 | return sql + " \n parameters=" + Arrays.toString(batch); 22 | } 23 | 24 | public JDBC4PreparedStatement(SQLiteConnection conn, String sql) throws SQLException { 25 | super(conn, sql); 26 | } 27 | 28 | // JDBC 4 29 | public void setRowId(int parameterIndex, RowId x) throws SQLException { 30 | // TODO Support this 31 | throw new SQLFeatureNotSupportedException(); 32 | } 33 | 34 | public void setNString(int parameterIndex, String value) throws SQLException { 35 | // TODO Support this 36 | throw new SQLFeatureNotSupportedException(); 37 | } 38 | 39 | public void setNCharacterStream(int parameterIndex, Reader value, long length) 40 | throws SQLException { 41 | // TODO Support this 42 | throw new SQLFeatureNotSupportedException(); 43 | } 44 | 45 | public void setNClob(int parameterIndex, NClob value) throws SQLException { 46 | // TODO Support this 47 | throw new SQLFeatureNotSupportedException(); 48 | } 49 | 50 | public void setClob(int parameterIndex, Reader reader, long length) throws SQLException { 51 | // TODO Support this 52 | throw new SQLFeatureNotSupportedException(); 53 | } 54 | 55 | public void setBlob(int parameterIndex, InputStream inputStream, long length) 56 | throws SQLException { 57 | // TODO Support this 58 | throw new SQLFeatureNotSupportedException(); 59 | } 60 | 61 | public void setNClob(int parameterIndex, Reader reader, long length) throws SQLException { 62 | // TODO Support this 63 | throw new SQLFeatureNotSupportedException(); 64 | } 65 | 66 | public void setSQLXML(int parameterIndex, SQLXML xmlObject) throws SQLException { 67 | // TODO Support this 68 | throw new SQLFeatureNotSupportedException(); 69 | } 70 | 71 | public void setAsciiStream(int parameterIndex, InputStream x, long length) throws SQLException { 72 | // TODO Support this 73 | throw new SQLFeatureNotSupportedException(); 74 | } 75 | 76 | public void setBinaryStream(int parameterIndex, InputStream x, long length) 77 | throws SQLException { 78 | // TODO Support this 79 | throw new SQLFeatureNotSupportedException(); 80 | } 81 | 82 | public void setCharacterStream(int parameterIndex, Reader reader, long length) 83 | throws SQLException { 84 | // TODO Support this 85 | throw new SQLFeatureNotSupportedException(); 86 | } 87 | 88 | public void setAsciiStream(int parameterIndex, InputStream x) throws SQLException { 89 | // TODO Support this 90 | throw new SQLFeatureNotSupportedException(); 91 | } 92 | 93 | public void setBinaryStream(int parameterIndex, InputStream x) throws SQLException { 94 | // TODO Support this 95 | throw new SQLFeatureNotSupportedException(); 96 | } 97 | 98 | public void setCharacterStream(int parameterIndex, Reader reader) throws SQLException { 99 | // TODO Support this 100 | throw new SQLFeatureNotSupportedException(); 101 | } 102 | 103 | public void setNCharacterStream(int parameterIndex, Reader value) throws SQLException { 104 | // TODO Support this 105 | throw new SQLFeatureNotSupportedException(); 106 | } 107 | 108 | public void setClob(int parameterIndex, Reader reader) throws SQLException { 109 | // TODO Support this 110 | throw new SQLFeatureNotSupportedException(); 111 | } 112 | 113 | public void setBlob(int parameterIndex, InputStream inputStream) throws SQLException { 114 | // TODO Support this 115 | throw new SQLFeatureNotSupportedException(); 116 | } 117 | 118 | public void setNClob(int parameterIndex, Reader reader) throws SQLException { 119 | // TODO Support this 120 | throw new SQLFeatureNotSupportedException(); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/main/java/org/sqlite/jdbc4/JDBC4Statement.java: -------------------------------------------------------------------------------- 1 | package org.sqlite.jdbc4; 2 | 3 | import java.sql.SQLException; 4 | import java.sql.Statement; 5 | import org.sqlite.SQLiteConnection; 6 | import org.sqlite.jdbc3.JDBC3Statement; 7 | 8 | public class JDBC4Statement extends JDBC3Statement implements Statement { 9 | public JDBC4Statement(SQLiteConnection conn) { 10 | super(conn); 11 | } 12 | 13 | // JDBC 4 14 | public T unwrap(Class iface) throws ClassCastException { 15 | return iface.cast(this); 16 | } 17 | 18 | public boolean isWrapperFor(Class iface) { 19 | return iface.isInstance(this); 20 | } 21 | 22 | private boolean closed = false; 23 | 24 | @Override 25 | public void close() throws SQLException { 26 | super.close(); 27 | closed = true; // isClosed() should only return true when close() happened 28 | } 29 | 30 | public boolean isClosed() { 31 | return closed; 32 | } 33 | 34 | boolean closeOnCompletion; 35 | 36 | public void closeOnCompletion() throws SQLException { 37 | if (closed) throw new SQLException("statement is closed"); 38 | closeOnCompletion = true; 39 | } 40 | 41 | public boolean isCloseOnCompletion() throws SQLException { 42 | if (closed) throw new SQLException("statement is closed"); 43 | return closeOnCompletion; 44 | } 45 | 46 | public void setPoolable(boolean poolable) throws SQLException { 47 | // TODO Auto-generated method stub 48 | 49 | } 50 | 51 | public boolean isPoolable() throws SQLException { 52 | // TODO Auto-generated method stub 53 | return false; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/org/sqlite/util/LibraryLoaderUtil.java: -------------------------------------------------------------------------------- 1 | package org.sqlite.util; 2 | 3 | import org.sqlite.SQLiteJDBCLoader; 4 | 5 | public class LibraryLoaderUtil { 6 | 7 | public static final String NATIVE_LIB_BASE_NAME = "sqlitejdbc"; 8 | 9 | /** 10 | * Get the OS-specific resource directory within the jar, where the relevant sqlitejdbc native 11 | * library is located. 12 | */ 13 | public static String getNativeLibResourcePath() { 14 | String packagePath = SQLiteJDBCLoader.class.getPackage().getName().replace(".", "/"); 15 | return String.format( 16 | "/%s/native/%s", packagePath, OSInfo.getNativeLibFolderPathForCurrentOS()); 17 | } 18 | 19 | /** Get the OS-specific name of the sqlitejdbc native library. */ 20 | public static String getNativeLibName() { 21 | return System.mapLibraryName(NATIVE_LIB_BASE_NAME); 22 | } 23 | 24 | public static boolean hasNativeLib(String path, String libraryName) { 25 | return SQLiteJDBCLoader.class.getResource(path + "/" + libraryName) != null; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/org/sqlite/util/Logger.java: -------------------------------------------------------------------------------- 1 | package org.sqlite.util; 2 | 3 | import java.util.function.Supplier; 4 | 5 | /** A simple internal Logger interface. */ 6 | public interface Logger { 7 | void trace(Supplier message); 8 | 9 | void info(Supplier message); 10 | 11 | void warn(Supplier message); 12 | 13 | void error(Supplier message, Throwable t); 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/org/sqlite/util/LoggerFactory.java: -------------------------------------------------------------------------------- 1 | package org.sqlite.util; 2 | 3 | import java.util.function.Supplier; 4 | import java.util.logging.Level; 5 | 6 | /** 7 | * A factory for {@link Logger} instances that uses SLF4J if present, falling back on a 8 | * java.util.logging implementation otherwise. 9 | */ 10 | public class LoggerFactory { 11 | static final boolean USE_SLF4J; 12 | 13 | static { 14 | boolean useSLF4J; 15 | try { 16 | Class.forName("org.slf4j.Logger"); 17 | useSLF4J = true; 18 | } catch (Exception e) { 19 | useSLF4J = false; 20 | } 21 | USE_SLF4J = useSLF4J; 22 | } 23 | 24 | /** 25 | * Get a {@link Logger} instance for the given host class. 26 | * 27 | * @param hostClass the host class from which log messages will be issued 28 | * @return a Logger 29 | */ 30 | public static Logger getLogger(Class hostClass) { 31 | if (USE_SLF4J) { 32 | return new SLF4JLogger(hostClass); 33 | } 34 | 35 | return new JDKLogger(hostClass); 36 | } 37 | 38 | private static class JDKLogger implements Logger { 39 | final java.util.logging.Logger logger; 40 | 41 | public JDKLogger(Class hostClass) { 42 | logger = java.util.logging.Logger.getLogger(hostClass.getCanonicalName()); 43 | } 44 | 45 | @Override 46 | public void trace(Supplier message) { 47 | if (logger.isLoggable(Level.FINEST)) { 48 | logger.log(Level.FINEST, message.get()); 49 | } 50 | } 51 | 52 | @Override 53 | public void info(Supplier message) { 54 | if (logger.isLoggable(Level.INFO)) { 55 | logger.log(Level.INFO, message.get()); 56 | } 57 | } 58 | 59 | @Override 60 | public void warn(Supplier message) { 61 | if (logger.isLoggable(Level.WARNING)) { 62 | logger.log(Level.WARNING, message.get()); 63 | } 64 | } 65 | 66 | @Override 67 | public void error(Supplier message, Throwable t) { 68 | if (logger.isLoggable(Level.SEVERE)) { 69 | logger.log(Level.SEVERE, message.get(), t); 70 | } 71 | } 72 | } 73 | 74 | private static class SLF4JLogger implements Logger { 75 | final org.slf4j.Logger logger; 76 | 77 | SLF4JLogger(Class hostClass) { 78 | logger = org.slf4j.LoggerFactory.getLogger(hostClass); 79 | } 80 | 81 | @Override 82 | public void trace(Supplier message) { 83 | if (logger.isTraceEnabled()) { 84 | logger.trace(message.get()); 85 | } 86 | } 87 | 88 | @Override 89 | public void info(Supplier message) { 90 | if (logger.isInfoEnabled()) { 91 | logger.info(message.get()); 92 | } 93 | } 94 | 95 | @Override 96 | public void warn(Supplier message) { 97 | if (logger.isWarnEnabled()) { 98 | logger.warn(message.get()); 99 | } 100 | } 101 | 102 | @Override 103 | public void error(Supplier message, Throwable t) { 104 | if (logger.isErrorEnabled()) { 105 | logger.error(message.get(), t); 106 | } 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/main/java/org/sqlite/util/ProcessRunner.java: -------------------------------------------------------------------------------- 1 | package org.sqlite.util; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | import java.util.concurrent.TimeUnit; 7 | 8 | public class ProcessRunner { 9 | String runAndWaitFor(String command) throws IOException, InterruptedException { 10 | Process p = Runtime.getRuntime().exec(command); 11 | p.waitFor(); 12 | 13 | return getProcessOutput(p); 14 | } 15 | 16 | String runAndWaitFor(String command, long timeout, TimeUnit unit) 17 | throws IOException, InterruptedException { 18 | Process p = Runtime.getRuntime().exec(command); 19 | p.waitFor(timeout, unit); 20 | 21 | return getProcessOutput(p); 22 | } 23 | 24 | static String getProcessOutput(Process process) throws IOException { 25 | try (InputStream in = process.getInputStream()) { 26 | int readLen; 27 | ByteArrayOutputStream b = new ByteArrayOutputStream(); 28 | byte[] buf = new byte[32]; 29 | while ((readLen = in.read(buf, 0, buf.length)) >= 0) { 30 | b.write(buf, 0, readLen); 31 | } 32 | return b.toString(); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/org/sqlite/util/QueryUtils.java: -------------------------------------------------------------------------------- 1 | package org.sqlite.util; 2 | 3 | import java.util.List; 4 | import java.util.stream.Collectors; 5 | 6 | public class QueryUtils { 7 | /** 8 | * Build a SQLite query using the VALUES clause to return arbitrary values. 9 | * 10 | * @param columns list of column names 11 | * @param valuesList values to return as rows 12 | * @return SQL query as string 13 | */ 14 | public static String valuesQuery(List columns, List> valuesList) { 15 | valuesList.forEach( 16 | (list) -> { 17 | if (list.size() != columns.size()) 18 | throw new IllegalArgumentException( 19 | "values and columns must have the same size"); 20 | }); 21 | return "with cte(" 22 | + String.join(",", columns) 23 | + ") as (values " 24 | + valuesList.stream() 25 | .map( 26 | (values) -> 27 | "(" 28 | + values.stream() 29 | .map( 30 | (o -> { 31 | if (o instanceof String) 32 | return "'" + o + "'"; 33 | if (o == null) return "null"; 34 | return o.toString(); 35 | })) 36 | .collect(Collectors.joining(",")) 37 | + ")") 38 | .collect(Collectors.joining(",")) 39 | + ") select * from cte"; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/org/sqlite/util/ResourceFinder.java: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------------------- 2 | * Copyright 2009 Taro L. Saito 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *--------------------------------------------------------------------------*/ 16 | // -------------------------------------- 17 | // sqlite-jdbc Project 18 | // 19 | // ResourceFinder.java 20 | // Since: Apr 28, 2009 21 | // 22 | // $URL$ 23 | // $Author$ 24 | // -------------------------------------- 25 | package org.sqlite.util; 26 | 27 | import java.net.URL; 28 | 29 | /** 30 | * Resource address finder for files inside the jar file 31 | * 32 | * @author leo 33 | */ 34 | public class ResourceFinder { 35 | /** 36 | * Gets the {@link URL} of the file resource 37 | * 38 | * @param referenceClass the base class for finding resources files. This method will search the 39 | * package containing the given referenceClass. 40 | * @param resourceFileName the resource file name relative to the package of the referenceClass 41 | * @return the URL of the file resource 42 | */ 43 | public static URL find(Class referenceClass, String resourceFileName) { 44 | return find(referenceClass.getClassLoader(), referenceClass.getPackage(), resourceFileName); 45 | } 46 | 47 | /** 48 | * Finds the {@link URL} of the resource 49 | * 50 | * @param basePackage the base package to find the resource 51 | * @param resourceFileName the resource file name relative to the package folder 52 | * @return the URL of the specified resource 53 | */ 54 | public static URL find(ClassLoader classLoader, Package basePackage, String resourceFileName) { 55 | return find(classLoader, basePackage.getName(), resourceFileName); 56 | } 57 | 58 | /** 59 | * Finds the {@link URL} of the resource 60 | * 61 | * @param packageName the base package name to find the resource 62 | * @param resourceFileName the resource file name relative to the package folder 63 | * @return the URL of the specified resource 64 | */ 65 | public static URL find(ClassLoader classLoader, String packageName, String resourceFileName) { 66 | String packagePath = packagePath(packageName); 67 | String resourcePath = packagePath + resourceFileName; 68 | if (!resourcePath.startsWith("/")) resourcePath = "/" + resourcePath; 69 | 70 | return classLoader.getResource(resourcePath); 71 | } 72 | 73 | @SuppressWarnings("unused") 74 | private static String packagePath(Class referenceClass) { 75 | return packagePath(referenceClass.getPackage()); 76 | } 77 | 78 | /** 79 | * @param basePackage Package object 80 | * @return Package path String in the unix-like format. 81 | */ 82 | private static String packagePath(Package basePackage) { 83 | return packagePath(basePackage.getName()); 84 | } 85 | 86 | /** 87 | * @param packageName Package name string 88 | * @return Package path String in the unix-like format. 89 | */ 90 | private static String packagePath(String packageName) { 91 | String packageAsPath = packageName.replaceAll("\\.", "/"); 92 | return packageAsPath.endsWith("/") ? packageAsPath : packageAsPath + "/"; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/main/java/org/sqlite/util/StringUtils.java: -------------------------------------------------------------------------------- 1 | package org.sqlite.util; 2 | 3 | import java.util.List; 4 | 5 | public class StringUtils { 6 | public static String join(List list, String separator) { 7 | StringBuilder sb = new StringBuilder(); 8 | boolean first = true; 9 | for (String item : list) { 10 | if (first) first = false; 11 | else sb.append(separator); 12 | 13 | sb.append(item); 14 | } 15 | return sb.toString(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java9/module-info.java: -------------------------------------------------------------------------------- 1 | module org.xerial.sqlitejdbc { 2 | 3 | requires static org.slf4j; 4 | requires transitive java.sql; 5 | requires transitive java.sql.rowset; 6 | requires static org.graalvm.nativeimage; 7 | 8 | exports org.sqlite; 9 | exports org.sqlite.core; 10 | exports org.sqlite.date; 11 | exports org.sqlite.javax; 12 | exports org.sqlite.jdbc3; 13 | exports org.sqlite.jdbc4; 14 | exports org.sqlite.util; 15 | 16 | provides java.sql.Driver with org.sqlite.JDBC; 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/native-image/org.xerial/sqlite-jdbc/native-image.properties: -------------------------------------------------------------------------------- 1 | Args=--enable-url-protocols=jar --features=org.sqlite.nativeimage.SqliteJdbcFeature 2 | -------------------------------------------------------------------------------- /src/main/resources/java.sql.Driver: -------------------------------------------------------------------------------- 1 | org.sqlite.JDBC -------------------------------------------------------------------------------- /src/main/resources/org/sqlite/native/FreeBSD/aarch64/libsqlitejdbc.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xerial/sqlite-jdbc/d54053637df62fda39bb46c3e653120c4eff9e13/src/main/resources/org/sqlite/native/FreeBSD/aarch64/libsqlitejdbc.so -------------------------------------------------------------------------------- /src/main/resources/org/sqlite/native/FreeBSD/x86/libsqlitejdbc.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xerial/sqlite-jdbc/d54053637df62fda39bb46c3e653120c4eff9e13/src/main/resources/org/sqlite/native/FreeBSD/x86/libsqlitejdbc.so -------------------------------------------------------------------------------- /src/main/resources/org/sqlite/native/FreeBSD/x86_64/libsqlitejdbc.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xerial/sqlite-jdbc/d54053637df62fda39bb46c3e653120c4eff9e13/src/main/resources/org/sqlite/native/FreeBSD/x86_64/libsqlitejdbc.so -------------------------------------------------------------------------------- /src/main/resources/org/sqlite/native/Linux-Android/aarch64/libsqlitejdbc.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xerial/sqlite-jdbc/d54053637df62fda39bb46c3e653120c4eff9e13/src/main/resources/org/sqlite/native/Linux-Android/aarch64/libsqlitejdbc.so -------------------------------------------------------------------------------- /src/main/resources/org/sqlite/native/Linux-Android/arm/libsqlitejdbc.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xerial/sqlite-jdbc/d54053637df62fda39bb46c3e653120c4eff9e13/src/main/resources/org/sqlite/native/Linux-Android/arm/libsqlitejdbc.so -------------------------------------------------------------------------------- /src/main/resources/org/sqlite/native/Linux-Android/x86/libsqlitejdbc.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xerial/sqlite-jdbc/d54053637df62fda39bb46c3e653120c4eff9e13/src/main/resources/org/sqlite/native/Linux-Android/x86/libsqlitejdbc.so -------------------------------------------------------------------------------- /src/main/resources/org/sqlite/native/Linux-Android/x86_64/libsqlitejdbc.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xerial/sqlite-jdbc/d54053637df62fda39bb46c3e653120c4eff9e13/src/main/resources/org/sqlite/native/Linux-Android/x86_64/libsqlitejdbc.so -------------------------------------------------------------------------------- /src/main/resources/org/sqlite/native/Linux-Musl/aarch64/libsqlitejdbc.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xerial/sqlite-jdbc/d54053637df62fda39bb46c3e653120c4eff9e13/src/main/resources/org/sqlite/native/Linux-Musl/aarch64/libsqlitejdbc.so -------------------------------------------------------------------------------- /src/main/resources/org/sqlite/native/Linux-Musl/x86/libsqlitejdbc.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xerial/sqlite-jdbc/d54053637df62fda39bb46c3e653120c4eff9e13/src/main/resources/org/sqlite/native/Linux-Musl/x86/libsqlitejdbc.so -------------------------------------------------------------------------------- /src/main/resources/org/sqlite/native/Linux-Musl/x86_64/libsqlitejdbc.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xerial/sqlite-jdbc/d54053637df62fda39bb46c3e653120c4eff9e13/src/main/resources/org/sqlite/native/Linux-Musl/x86_64/libsqlitejdbc.so -------------------------------------------------------------------------------- /src/main/resources/org/sqlite/native/Linux/aarch64/libsqlitejdbc.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xerial/sqlite-jdbc/d54053637df62fda39bb46c3e653120c4eff9e13/src/main/resources/org/sqlite/native/Linux/aarch64/libsqlitejdbc.so -------------------------------------------------------------------------------- /src/main/resources/org/sqlite/native/Linux/arm/libsqlitejdbc.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xerial/sqlite-jdbc/d54053637df62fda39bb46c3e653120c4eff9e13/src/main/resources/org/sqlite/native/Linux/arm/libsqlitejdbc.so -------------------------------------------------------------------------------- /src/main/resources/org/sqlite/native/Linux/armv6/libsqlitejdbc.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xerial/sqlite-jdbc/d54053637df62fda39bb46c3e653120c4eff9e13/src/main/resources/org/sqlite/native/Linux/armv6/libsqlitejdbc.so -------------------------------------------------------------------------------- /src/main/resources/org/sqlite/native/Linux/armv7/libsqlitejdbc.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xerial/sqlite-jdbc/d54053637df62fda39bb46c3e653120c4eff9e13/src/main/resources/org/sqlite/native/Linux/armv7/libsqlitejdbc.so -------------------------------------------------------------------------------- /src/main/resources/org/sqlite/native/Linux/ppc64/libsqlitejdbc.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xerial/sqlite-jdbc/d54053637df62fda39bb46c3e653120c4eff9e13/src/main/resources/org/sqlite/native/Linux/ppc64/libsqlitejdbc.so -------------------------------------------------------------------------------- /src/main/resources/org/sqlite/native/Linux/riscv64/libsqlitejdbc.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xerial/sqlite-jdbc/d54053637df62fda39bb46c3e653120c4eff9e13/src/main/resources/org/sqlite/native/Linux/riscv64/libsqlitejdbc.so -------------------------------------------------------------------------------- /src/main/resources/org/sqlite/native/Linux/x86/libsqlitejdbc.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xerial/sqlite-jdbc/d54053637df62fda39bb46c3e653120c4eff9e13/src/main/resources/org/sqlite/native/Linux/x86/libsqlitejdbc.so -------------------------------------------------------------------------------- /src/main/resources/org/sqlite/native/Linux/x86_64/libsqlitejdbc.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xerial/sqlite-jdbc/d54053637df62fda39bb46c3e653120c4eff9e13/src/main/resources/org/sqlite/native/Linux/x86_64/libsqlitejdbc.so -------------------------------------------------------------------------------- /src/main/resources/org/sqlite/native/Mac/aarch64/libsqlitejdbc.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xerial/sqlite-jdbc/d54053637df62fda39bb46c3e653120c4eff9e13/src/main/resources/org/sqlite/native/Mac/aarch64/libsqlitejdbc.dylib -------------------------------------------------------------------------------- /src/main/resources/org/sqlite/native/Mac/x86_64/libsqlitejdbc.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xerial/sqlite-jdbc/d54053637df62fda39bb46c3e653120c4eff9e13/src/main/resources/org/sqlite/native/Mac/x86_64/libsqlitejdbc.dylib -------------------------------------------------------------------------------- /src/main/resources/org/sqlite/native/Windows/aarch64/sqlitejdbc.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xerial/sqlite-jdbc/d54053637df62fda39bb46c3e653120c4eff9e13/src/main/resources/org/sqlite/native/Windows/aarch64/sqlitejdbc.dll -------------------------------------------------------------------------------- /src/main/resources/org/sqlite/native/Windows/armv7/sqlitejdbc.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xerial/sqlite-jdbc/d54053637df62fda39bb46c3e653120c4eff9e13/src/main/resources/org/sqlite/native/Windows/armv7/sqlitejdbc.dll -------------------------------------------------------------------------------- /src/main/resources/org/sqlite/native/Windows/x86/sqlitejdbc.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xerial/sqlite-jdbc/d54053637df62fda39bb46c3e653120c4eff9e13/src/main/resources/org/sqlite/native/Windows/x86/sqlitejdbc.dll -------------------------------------------------------------------------------- /src/main/resources/org/sqlite/native/Windows/x86_64/sqlitejdbc.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xerial/sqlite-jdbc/d54053637df62fda39bb46c3e653120c4eff9e13/src/main/resources/org/sqlite/native/Windows/x86_64/sqlitejdbc.dll -------------------------------------------------------------------------------- /src/main/resources/sqlite-jdbc.properties: -------------------------------------------------------------------------------- 1 | name=${project.name} 2 | version=${project.version} 3 | -------------------------------------------------------------------------------- /src/test/java/org/sqlite/BackupTest.java: -------------------------------------------------------------------------------- 1 | // -------------------------------------- 2 | // sqlite-jdbc Project 3 | // 4 | // BackupTest.java 5 | // Since: Feb 18, 2009 6 | // 7 | // $URL$ 8 | // $Author$ 9 | // -------------------------------------- 10 | package org.sqlite; 11 | 12 | import static org.assertj.core.api.Assertions.assertThat; 13 | import static org.assertj.core.api.Assertions.assertThatCode; 14 | 15 | import java.io.File; 16 | import java.io.IOException; 17 | import java.sql.*; 18 | import java.util.Properties; 19 | import java.util.concurrent.atomic.AtomicInteger; 20 | import org.junit.jupiter.api.Test; 21 | import org.junit.jupiter.api.io.TempDir; 22 | import org.sqlite.core.DB; 23 | 24 | public class BackupTest { 25 | @TempDir File tempDir; 26 | 27 | @Test 28 | public void backupAndRestore() throws SQLException, IOException { 29 | // create a memory database 30 | File tmpFile = File.createTempFile("backup-test", ".sqlite", tempDir); 31 | 32 | try (Connection conn = DriverManager.getConnection("jdbc:sqlite:")) { 33 | // memory DB to file 34 | try (Statement stmt = conn.createStatement()) { 35 | createTableAndInsertRows(stmt); 36 | 37 | stmt.executeUpdate("backup to " + tmpFile.getAbsolutePath()); 38 | } 39 | 40 | // open another memory database 41 | try (Connection conn2 = DriverManager.getConnection("jdbc:sqlite:")) { 42 | try (Statement stmt2 = conn2.createStatement()) { 43 | stmt2.execute("restore from " + tmpFile.getAbsolutePath()); 44 | try (ResultSet rs = stmt2.executeQuery("select * from sample")) { 45 | int count = 0; 46 | while (rs.next()) { 47 | count++; 48 | } 49 | 50 | assertThat(count).isEqualTo(2); 51 | } 52 | } 53 | } 54 | } 55 | } 56 | 57 | private void createTableAndInsertRows(Statement stmt) throws SQLException { 58 | stmt.executeUpdate("create table sample(id, name)"); 59 | stmt.executeUpdate("insert into sample values(1, \"leo\")"); 60 | stmt.executeUpdate("insert into sample values(2, \"yui\")"); 61 | } 62 | 63 | @Test 64 | void testFailedBackupAndRestore() throws Exception { 65 | File tmpFile = File.createTempFile("backup-test", ".sqlite", tempDir); 66 | 67 | try (Connection conn = DriverManager.getConnection("jdbc:sqlite:")) { 68 | // memory DB to file 69 | try (Statement stmt = conn.createStatement()) { 70 | createTableAndInsertRows(stmt); 71 | // This fails because we cannot write to a file starting with "(" 72 | assertThatCode( 73 | () -> 74 | stmt.executeUpdate( 75 | "backup to (" 76 | + tmpFile.getAbsolutePath() 77 | + "doesnotexist.sqlite")) 78 | .isOfAnyClassIn(SQLiteException.class); 79 | 80 | // This fails because we read from a file that does not exist, and we should not 81 | // create it 82 | assertThatCode( 83 | () -> 84 | stmt.executeUpdate( 85 | "restore from " 86 | + tmpFile.getAbsolutePath() 87 | + "doesnotexist.sqlite")) 88 | .isOfAnyClassIn(SQLiteException.class); 89 | } 90 | } 91 | } 92 | 93 | @Test 94 | public void memoryToDisk() throws Exception { 95 | try (Connection conn = DriverManager.getConnection("jdbc:sqlite:"); 96 | Statement stmt = conn.createStatement()) { 97 | stmt.executeUpdate("create table sample(id integer primary key autoincrement, name)"); 98 | for (int i = 0; i < 10000; i++) { 99 | stmt.executeUpdate("insert into sample(name) values(\"leo\")"); 100 | } 101 | 102 | File tmpFile = File.createTempFile("backup-test2", ".sqlite", tempDir); 103 | stmt.executeUpdate("backup to " + tmpFile.getAbsolutePath()); 104 | } 105 | } 106 | 107 | @Test 108 | void testProgress() throws Exception { 109 | File tmpFile = File.createTempFile("backup-test", ".sqlite", tempDir); 110 | 111 | try (SQLiteConnection conn = JDBC.createConnection("jdbc:sqlite:", new Properties())) { 112 | // memory DB to file 113 | try (Statement stmt = conn.createStatement()) { 114 | createTableAndInsertRows(stmt); 115 | } 116 | 117 | // check that JNI updates java with progress of the DB Backup. 118 | AtomicInteger remainingStore = new AtomicInteger(-1); 119 | AtomicInteger pageCountStore = new AtomicInteger(-1); 120 | DB.ProgressObserver progressObserver = 121 | (remaining, pageCount) -> { 122 | remainingStore.set(remaining); 123 | pageCountStore.set(pageCount); 124 | }; 125 | 126 | int rc = 127 | conn.getDatabase() 128 | .backup("main", tmpFile.getAbsolutePath(), progressObserver, 1, 1, 1); 129 | assertThat(rc).isEqualTo(SQLiteErrorCode.SQLITE_OK.code); 130 | assertThat(remainingStore.get()).isEqualTo(0); 131 | assertThat(pageCountStore.get()).isGreaterThan(0); 132 | } 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/test/java/org/sqlite/CachedRowSetTest.java: -------------------------------------------------------------------------------- 1 | package org.sqlite; 2 | 3 | import java.sql.Connection; 4 | import java.sql.DriverManager; 5 | import java.sql.SQLException; 6 | import java.sql.Statement; 7 | import javax.sql.rowset.CachedRowSet; 8 | import javax.sql.rowset.RowSetFactory; 9 | import javax.sql.rowset.RowSetProvider; 10 | import org.junit.jupiter.api.Test; 11 | 12 | public class CachedRowSetTest { 13 | 14 | @Test 15 | public void gh_224() throws SQLException { 16 | Connection connection = DriverManager.getConnection("jdbc:sqlite:"); 17 | try (Statement statement = connection.createStatement()) { 18 | statement.execute("create table person (id INTEGER, name VARCHAR(50))"); 19 | statement.execute("insert into person values(1, 'leo')"); 20 | } 21 | RowSetFactory factory = RowSetProvider.newFactory(); 22 | try (CachedRowSet crs = factory.createCachedRowSet()) { 23 | crs.setCommand("select * from person"); 24 | crs.execute(connection); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/test/java/org/sqlite/ErrorMessageTest.java: -------------------------------------------------------------------------------- 1 | package org.sqlite; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | import static org.assertj.core.api.Assertions.assertThatThrownBy; 5 | import static org.assertj.core.api.Assumptions.assumeThat; 6 | 7 | import java.io.File; 8 | import java.io.IOException; 9 | import java.sql.Connection; 10 | import java.sql.DriverManager; 11 | import java.sql.SQLException; 12 | import java.sql.Statement; 13 | import org.junit.jupiter.api.Test; 14 | import org.junit.jupiter.api.condition.DisabledInNativeImage; 15 | import org.junit.jupiter.api.io.TempDir; 16 | import org.sqlite.core.DB; 17 | 18 | @DisabledInNativeImage // assertj Assumptions do not work in native-image tests 19 | public class ErrorMessageTest { 20 | @TempDir File tempDir; 21 | 22 | @Test 23 | public void moved() throws SQLException, IOException { 24 | File from = File.createTempFile("error-message-test-moved-from", ".sqlite", tempDir); 25 | 26 | try (Connection conn = 27 | DriverManager.getConnection("jdbc:sqlite:" + from.getAbsolutePath()); 28 | Statement stmt = conn.createStatement()) { 29 | stmt.executeUpdate("create table sample(id, name)"); 30 | stmt.executeUpdate("insert into sample values(1, 'foo')"); 31 | 32 | File to = File.createTempFile("error-message-test-moved-from", ".sqlite", tempDir); 33 | assumeThat(to.delete()).isTrue(); 34 | assumeThat(from.renameTo(to)).isTrue(); 35 | 36 | assertThatThrownBy(() -> stmt.executeUpdate("insert into sample values(2, 'bar')")) 37 | .isInstanceOf(SQLException.class) 38 | .hasMessageStartingWith("[SQLITE_READONLY_DBMOVED]"); 39 | } 40 | } 41 | 42 | @Test 43 | public void writeProtected() throws SQLException, IOException { 44 | File file = File.createTempFile("error-message-test-write-protected", ".sqlite", tempDir); 45 | 46 | try (Connection conn = 47 | DriverManager.getConnection("jdbc:sqlite:" + file.getAbsolutePath()); 48 | Statement stmt = conn.createStatement()) { 49 | stmt.executeUpdate("create table sample(id, name)"); 50 | stmt.executeUpdate("insert into sample values(1, 'foo')"); 51 | } 52 | 53 | assumeThat(file.setReadOnly()).isTrue(); 54 | 55 | try (Connection conn = 56 | DriverManager.getConnection("jdbc:sqlite:" + file.getAbsolutePath()); 57 | Statement stmt = conn.createStatement()) { 58 | assertThatThrownBy(() -> stmt.executeUpdate("insert into sample values(2, 'bar')")) 59 | .isInstanceOf(SQLException.class) 60 | .hasMessageStartingWith("[SQLITE_READONLY]"); 61 | } 62 | } 63 | 64 | @Test 65 | public void cantOpenDir() throws IOException { 66 | File dir = File.createTempFile("error-message-test-cant-open-dir", "", tempDir); 67 | assumeThat(dir.delete()).isTrue(); 68 | assumeThat(dir.mkdir()).isTrue(); 69 | 70 | assertThatThrownBy( 71 | () -> DriverManager.getConnection("jdbc:sqlite:" + dir.getAbsolutePath())) 72 | .isInstanceOf(SQLException.class) 73 | .hasMessageStartingWith("[SQLITE_CANTOPEN"); 74 | } 75 | 76 | @Test 77 | public void shouldUsePlainErrorCodeAsVendorCodeAndExtendedAsResultCode() 78 | throws SQLException, IOException { 79 | File from = File.createTempFile("error-message-test-plain-1", ".sqlite", tempDir); 80 | 81 | try (Connection conn = 82 | DriverManager.getConnection("jdbc:sqlite:" + from.getAbsolutePath()); 83 | Statement stmt = conn.createStatement()) { 84 | stmt.executeUpdate("create table sample(id, name)"); 85 | stmt.executeUpdate("insert into sample values(1, 'foo')"); 86 | 87 | File to = File.createTempFile("error-message-test-plain-2", ".sqlite", tempDir); 88 | assumeThat(to.delete()).isTrue(); 89 | assumeThat(from.renameTo(to)).isTrue(); 90 | 91 | assertThatThrownBy(() -> stmt.executeUpdate("insert into sample values(2, 'bar')")) 92 | .isInstanceOfSatisfying( 93 | SQLiteException.class, 94 | (ex) -> { 95 | assertThat(SQLiteErrorCode.getErrorCode(ex.getErrorCode())) 96 | .isEqualTo(SQLiteErrorCode.SQLITE_READONLY); 97 | assertThat(ex.getResultCode()) 98 | .isEqualTo(SQLiteErrorCode.SQLITE_READONLY_DBMOVED); 99 | }) 100 | .hasMessageStartingWith("[SQLITE_READONLY_DBMOVED]"); 101 | } 102 | } 103 | 104 | @Test 105 | public void unknownErrorExceptionMessageShouldContainOriginalErrorCode() { 106 | int errorCode = 1234567890; 107 | String errorMessage = "fictitious code"; 108 | 109 | SQLiteException exception = DB.newSQLException(errorCode, errorMessage); 110 | 111 | assertThat(exception.getMessage()) 112 | .contains(Integer.toString(errorCode)) 113 | .contains(errorMessage) 114 | .startsWith(SQLiteErrorCode.UNKNOWN_ERROR.toString()); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/test/java/org/sqlite/ExtendedCommandTest.java: -------------------------------------------------------------------------------- 1 | // -------------------------------------- 2 | // sqlite-jdbc Project 3 | // 4 | // ExtendedCommandTest.java 5 | // Since: Mar 12, 2010 6 | // 7 | // $URL$ 8 | // $Author$ 9 | // -------------------------------------- 10 | package org.sqlite; 11 | 12 | import static org.assertj.core.api.Assertions.assertThat; 13 | 14 | import java.sql.SQLException; 15 | import java.util.stream.Stream; 16 | import org.junit.jupiter.api.Test; 17 | import org.junit.jupiter.params.ParameterizedTest; 18 | import org.junit.jupiter.params.provider.Arguments; 19 | import org.junit.jupiter.params.provider.MethodSource; 20 | import org.sqlite.ExtendedCommand.BackupCommand; 21 | import org.sqlite.ExtendedCommand.RestoreCommand; 22 | import org.sqlite.ExtendedCommand.SQLExtension; 23 | 24 | public class ExtendedCommandTest { 25 | 26 | public static BackupCommand parseBackupCommand(String sql) throws SQLException { 27 | SQLExtension e = ExtendedCommand.parse(sql); 28 | assertThat(e instanceof BackupCommand).isTrue(); 29 | return (BackupCommand) e; 30 | } 31 | 32 | public static RestoreCommand parseRestoreCommand(String sql) throws SQLException { 33 | SQLExtension e = ExtendedCommand.parse(sql); 34 | assertThat(e instanceof RestoreCommand).isTrue(); 35 | return (RestoreCommand) e; 36 | } 37 | 38 | @Test 39 | public void parseBackupCmd() throws SQLException { 40 | BackupCommand b = parseBackupCommand("backup mydb to somewhere/backupfolder/mydb.sqlite"); 41 | assertThat(b.srcDB).isEqualTo("mydb"); 42 | assertThat(b.destFile).isEqualTo("somewhere/backupfolder/mydb.sqlite"); 43 | 44 | b = parseBackupCommand("backup main to \"tmp folder with space\""); 45 | assertThat(b.srcDB).isEqualTo("main"); 46 | assertThat(b.destFile).isEqualTo("tmp folder with space"); 47 | 48 | b = parseBackupCommand("backup main to 'tmp folder with space'"); 49 | assertThat(b.srcDB).isEqualTo("main"); 50 | assertThat(b.destFile).isEqualTo("tmp folder with space"); 51 | 52 | b = parseBackupCommand("backup to target/sample.db"); 53 | assertThat(b.srcDB).isEqualTo("main"); 54 | assertThat(b.destFile).isEqualTo("target/sample.db"); 55 | } 56 | 57 | @Test 58 | public void parseRestoreCmd() throws SQLException { 59 | RestoreCommand b = 60 | parseRestoreCommand("restore mydb from somewhere/backupfolder/mydb.sqlite"); 61 | assertThat(b.targetDB).isEqualTo("mydb"); 62 | assertThat(b.srcFile).isEqualTo("somewhere/backupfolder/mydb.sqlite"); 63 | 64 | b = parseRestoreCommand("restore main from \"tmp folder with space\""); 65 | assertThat(b.targetDB).isEqualTo("main"); 66 | assertThat(b.srcFile).isEqualTo("tmp folder with space"); 67 | 68 | b = parseRestoreCommand("restore main from 'tmp folder with space'"); 69 | assertThat(b.targetDB).isEqualTo("main"); 70 | assertThat(b.srcFile).isEqualTo("tmp folder with space"); 71 | 72 | b = parseRestoreCommand("restore from target/sample.db"); 73 | assertThat(b.targetDB).isEqualTo("main"); 74 | assertThat(b.srcFile).isEqualTo("target/sample.db"); 75 | } 76 | 77 | @ParameterizedTest 78 | @MethodSource 79 | public void removeQuotation(String input, String expected) throws SQLException { 80 | assertThat(ExtendedCommand.removeQuotation(input)).isEqualTo(expected); 81 | } 82 | 83 | private static Stream removeQuotation() { 84 | return Stream.of( 85 | Arguments.of(null, null), // Null String 86 | Arguments.of("'", "'"), // String with one single quotation only 87 | Arguments.of("\"", "\""), // String with one double quotation only 88 | Arguments.of("'Test\"", "'Test\""), // String with two mismatch quotations 89 | Arguments.of("'Test'", "Test"), // String with two matching single quotations 90 | Arguments.of("\"Test\"", "Test"), // String with two matching double quotations 91 | Arguments.of("'Te's\"t'", "Te's\"t") // String with more than two quotations 92 | ); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/test/java/org/sqlite/ExtensionTest.java: -------------------------------------------------------------------------------- 1 | package org.sqlite; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | import static org.assertj.core.api.Assumptions.assumeThat; 5 | 6 | import java.sql.Connection; 7 | import java.sql.DriverManager; 8 | import java.sql.ResultSet; 9 | import java.sql.Statement; 10 | import org.junit.jupiter.api.AfterEach; 11 | import org.junit.jupiter.api.BeforeEach; 12 | import org.junit.jupiter.api.Test; 13 | import org.junit.jupiter.api.condition.DisabledInNativeImage; 14 | 15 | public class ExtensionTest { 16 | Connection conn; 17 | Statement stat; 18 | 19 | @BeforeEach 20 | public void setUp() throws Exception { 21 | conn = DriverManager.getConnection("jdbc:sqlite:"); 22 | stat = conn.createStatement(); 23 | } 24 | 25 | @AfterEach 26 | public void tearDown() throws Exception { 27 | if (stat != null) { 28 | stat.close(); 29 | } 30 | if (conn != null) { 31 | conn.close(); 32 | } 33 | } 34 | 35 | @Test 36 | public void extFTS3() throws Exception { 37 | stat.execute("create virtual table recipe using fts3(name, ingredients)"); 38 | stat.execute( 39 | "insert into recipe (name, ingredients) values('broccoli stew', 'broccoli peppers cheese tomatoes')"); 40 | stat.execute( 41 | "insert into recipe (name, ingredients) values('pumpkin stew', 'pumpkin onions garlic celery')"); 42 | 43 | ResultSet rs = 44 | stat.executeQuery( 45 | "select rowid, name, ingredients from recipe where ingredients match 'onions'"); 46 | assertThat(rs.next()).isTrue(); 47 | assertThat(rs.getString(2)).isEqualTo("pumpkin stew"); 48 | } 49 | 50 | @Test 51 | public void extFTS5() throws Exception { 52 | stat.execute("create virtual table recipe using fts5(name, ingredients)"); 53 | stat.execute( 54 | "insert into recipe (name, ingredients) values('broccoli stew', 'broccoli peppers cheese tomatoes')"); 55 | stat.execute( 56 | "insert into recipe (name, ingredients) values('pumpkin stew', 'pumpkin onions garlic celery')"); 57 | 58 | ResultSet rs = 59 | stat.executeQuery( 60 | "select rowid, name, ingredients from recipe where recipe match 'onions'"); 61 | assertThat(rs.next()).isTrue(); 62 | assertThat(rs.getString(2)).isEqualTo("pumpkin stew"); 63 | } 64 | 65 | @Test 66 | @DisabledInNativeImage // assertj Assumptions do not work in native-image tests 67 | public void extFunctions() throws Exception { 68 | Utils.assumeJdbcExtensions(conn); 69 | 70 | { 71 | ResultSet rs = stat.executeQuery("select reverse(\"ACGT\")"); 72 | assertThat(rs.next()).isTrue(); 73 | assertThat(rs.getString(1)).isEqualTo("TGCA"); 74 | rs.close(); 75 | } 76 | } 77 | 78 | @Test 79 | @DisabledInNativeImage // assertj Assumptions do not work in native-image tests 80 | public void dbstat() throws Exception { 81 | assumeThat(Utils.getCompileOptions(conn)) 82 | .as("SQLite has to be compiled with ENABLE_DBSTAT_VTAB") 83 | .contains("ENABLE_DBSTAT_VTAB"); 84 | 85 | { 86 | boolean result = stat.execute("SELECT * FROM dbstat"); 87 | assertThat(result).isTrue(); 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/test/java/org/sqlite/FetchSizeTest.java: -------------------------------------------------------------------------------- 1 | package org.sqlite; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | 5 | import java.sql.Connection; 6 | import java.sql.DriverManager; 7 | import java.sql.PreparedStatement; 8 | import java.sql.ResultSet; 9 | import java.sql.SQLException; 10 | import org.junit.jupiter.api.AfterEach; 11 | import org.junit.jupiter.api.BeforeEach; 12 | import org.junit.jupiter.api.Test; 13 | 14 | /** 15 | * Created by IntelliJ IDEA. User: david_donn Date: 19/01/2010 Time: 11:50:24 AM To change this 16 | * template use File | Settings | File Templates. 17 | */ 18 | public class FetchSizeTest { 19 | 20 | private Connection conn; 21 | 22 | @BeforeEach 23 | public void connect() throws Exception { 24 | conn = DriverManager.getConnection("jdbc:sqlite:"); 25 | } 26 | 27 | @AfterEach 28 | public void close() throws SQLException { 29 | conn.close(); 30 | } 31 | 32 | @Test 33 | public void testFetchSize() throws SQLException { 34 | assertThat(conn.prepareStatement("create table s1 (c1)").executeUpdate()).isEqualTo(0); 35 | PreparedStatement insertPrep = conn.prepareStatement("insert into s1 values (?)"); 36 | insertPrep.setInt(1, 1); 37 | assertThat(insertPrep.executeUpdate()).isEqualTo(1); 38 | insertPrep.setInt(1, 2); 39 | assertThat(insertPrep.executeUpdate()).isEqualTo(1); 40 | insertPrep.setInt(1, 3); 41 | assertThat(insertPrep.executeUpdate()).isEqualTo(1); 42 | insertPrep.setInt(1, 4); 43 | assertThat(insertPrep.executeUpdate()).isEqualTo(1); 44 | insertPrep.setInt(1, 5); 45 | assertThat(insertPrep.executeUpdate()).isEqualTo(1); 46 | insertPrep.close(); 47 | 48 | PreparedStatement selectPrep = conn.prepareStatement("select c1 from s1"); 49 | ResultSet rs = selectPrep.executeQuery(); 50 | rs.setFetchSize(2); 51 | assertThat(rs.next()).isTrue(); 52 | assertThat(rs.next()).isTrue(); 53 | assertThat(rs.next()).isTrue(); 54 | assertThat(rs.next()).isTrue(); 55 | assertThat(rs.next()).isTrue(); 56 | assertThat(rs.next()).isFalse(); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/test/java/org/sqlite/InsertQueryTest.java: -------------------------------------------------------------------------------- 1 | // -------------------------------------- 2 | // sqlite-jdbc Project 3 | // 4 | // InsertQueryTest.java 5 | // Since: Apr 7, 2009 6 | // 7 | // $URL$ 8 | // $Author$ 9 | // -------------------------------------- 10 | package org.sqlite; 11 | 12 | import java.io.File; 13 | import java.sql.*; 14 | import java.util.ArrayList; 15 | import java.util.List; 16 | import org.junit.jupiter.api.AfterEach; 17 | import org.junit.jupiter.api.BeforeEach; 18 | import org.junit.jupiter.api.Disabled; 19 | import org.junit.jupiter.api.Test; 20 | import org.junit.jupiter.api.io.TempDir; 21 | 22 | public class InsertQueryTest { 23 | String dbName; 24 | 25 | @BeforeEach 26 | public void setUp(@TempDir File tempDir) throws Exception { 27 | File tmpFile = File.createTempFile("tmp-sqlite", ".db", tempDir); 28 | dbName = tmpFile.getAbsolutePath(); 29 | } 30 | 31 | @AfterEach 32 | public void tearDown() throws Exception {} 33 | 34 | interface ConnectionFactory { 35 | Connection getConnection() throws SQLException; 36 | 37 | void dispose() throws SQLException; 38 | } 39 | 40 | class IndependentConnectionFactory implements ConnectionFactory { 41 | // Internal bookkeeping so connections are still closed in the end. 42 | private final List connections = new ArrayList<>(); 43 | 44 | public Connection getConnection() throws SQLException { 45 | Connection conn = DriverManager.getConnection("jdbc:sqlite:" + dbName); 46 | connections.add(conn); 47 | return conn; 48 | } 49 | 50 | public void dispose() throws SQLException { 51 | for (Connection connection : connections) { 52 | connection.close(); 53 | } 54 | connections.clear(); 55 | } 56 | } 57 | 58 | class SharedConnectionFactory implements ConnectionFactory { 59 | private Connection conn = null; 60 | 61 | public Connection getConnection() throws SQLException { 62 | if (conn == null) { 63 | conn = DriverManager.getConnection("jdbc:sqlite:" + dbName); 64 | } 65 | return conn; 66 | } 67 | 68 | public void dispose() throws SQLException { 69 | if (conn != null) { 70 | conn.close(); 71 | } 72 | } 73 | } 74 | 75 | static class BD { 76 | String fullId; 77 | String type; 78 | 79 | public BD(String fullId, String type) { 80 | this.fullId = fullId; 81 | this.type = type; 82 | } 83 | 84 | public String getFullId() { 85 | return fullId; 86 | } 87 | 88 | public void setFullId(String fullId) { 89 | this.fullId = fullId; 90 | } 91 | 92 | public String getType() { 93 | return type; 94 | } 95 | 96 | public void setType(String type) { 97 | this.type = type; 98 | } 99 | 100 | public static byte[] serializeBD(BD item) { 101 | return new byte[0]; 102 | } 103 | } 104 | 105 | @Test 106 | public void insertLockTestUsingSharedConnection() throws Exception { 107 | insertAndQuery(new SharedConnectionFactory()); 108 | } 109 | 110 | @Test 111 | public void insertLockTestUsingIndependentConnection() throws Exception { 112 | insertAndQuery(new IndependentConnectionFactory()); 113 | } 114 | 115 | void insertAndQuery(ConnectionFactory factory) throws SQLException { 116 | try { 117 | Statement st = factory.getConnection().createStatement(); 118 | st.executeUpdate( 119 | "CREATE TABLE IF NOT EXISTS data (fid VARCHAR(255) PRIMARY KEY, type VARCHAR(64), data BLOB);"); 120 | st.executeUpdate( 121 | "CREATE TABLE IF NOT EXISTS ResourcesTags (bd_fid VARCHAR(255), name VARCHAR(64), version INTEGER);"); 122 | st.close(); 123 | 124 | factory.getConnection().setAutoCommit(false); 125 | 126 | // Object Serialization 127 | PreparedStatement statAddBD = 128 | factory.getConnection() 129 | .prepareStatement("INSERT OR REPLACE INTO data values (?, ?, ?)"); 130 | PreparedStatement statDelRT = 131 | factory.getConnection() 132 | .prepareStatement("DELETE FROM ResourcesTags WHERE bd_fid = ?"); 133 | PreparedStatement statAddRT = 134 | factory.getConnection() 135 | .prepareStatement("INSERT INTO ResourcesTags values (?, ?, ?)"); 136 | 137 | for (int i = 0; i < 10; i++) { 138 | BD item = new BD(Integer.toHexString(i), Integer.toString(i)); 139 | 140 | // SQLite database insertion 141 | statAddBD.setString(1, item.getFullId()); 142 | statAddBD.setString(2, item.getType()); 143 | statAddBD.setBytes(3, BD.serializeBD(item)); 144 | statAddBD.execute(); 145 | 146 | // Then, its resources tags 147 | statDelRT.setString(1, item.getFullId()); 148 | statDelRT.execute(); 149 | 150 | statAddRT.setString(1, item.getFullId()); 151 | 152 | for (int j = 0; j < 2; j++) { 153 | statAddRT.setString(2, "1"); 154 | statAddRT.setLong(3, 1L); 155 | statAddRT.execute(); 156 | } 157 | } 158 | 159 | factory.getConnection().setAutoCommit(true); 160 | 161 | statAddBD.close(); 162 | statDelRT.close(); 163 | statAddRT.close(); 164 | 165 | // 166 | PreparedStatement stat; 167 | Long result = 0L; 168 | String query = "SELECT COUNT(fid) FROM data"; 169 | 170 | stat = factory.getConnection().prepareStatement(query); 171 | ResultSet rs = stat.executeQuery(); 172 | 173 | rs.next(); 174 | result = rs.getLong(1); 175 | // System.out.println("count = " + result); 176 | 177 | rs.close(); 178 | stat.close(); 179 | } finally { 180 | factory.dispose(); 181 | } 182 | } 183 | 184 | @Disabled("Not sure this worked recently, the second query cannot find the table 'sample'") 185 | @Test 186 | public void reproduceDatabaseLocked() throws SQLException { 187 | Connection conn = DriverManager.getConnection("jdbc:sqlite:" + dbName); 188 | Connection conn2 = DriverManager.getConnection("jdbc:sqlite:" + dbName); 189 | Statement stat = conn.createStatement(); 190 | Statement stat2 = conn2.createStatement(); 191 | 192 | conn.setAutoCommit(false); 193 | 194 | stat.executeUpdate("drop table if exists sample"); 195 | stat.executeUpdate("create table sample(id, name)"); 196 | stat.executeUpdate("insert into sample values(1, 'leo')"); 197 | 198 | ResultSet rs = stat2.executeQuery("select count(*) from sample"); 199 | rs.next(); 200 | 201 | conn.commit(); // causes "database is locked" (SQLITE_BUSY) 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /src/test/java/org/sqlite/MetadataLeakTest.java: -------------------------------------------------------------------------------- 1 | package org.sqlite; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | 5 | import java.sql.Connection; 6 | import java.sql.DatabaseMetaData; 7 | import java.sql.DriverManager; 8 | import java.sql.ResultSet; 9 | import java.sql.Statement; 10 | import org.junit.jupiter.api.Test; 11 | 12 | /** A test to make sure that getMetadata is properly freed */ 13 | public class MetadataLeakTest { 14 | /** 15 | * Check that statement is properly closed when result set is closed. See 16 | * https://github.com/xerial/sqlite-jdbc/issues/423 17 | * 18 | * @throws Exception on failure 19 | */ 20 | @Test 21 | void testMemoryIsFreed() throws Exception { 22 | try (Connection con = DriverManager.getConnection("jdbc:sqlite::memory:")) { 23 | DatabaseMetaData meta = con.getMetaData(); 24 | 25 | Statement statement; 26 | try (ResultSet tables = meta.getTables(null, null, null, null); ) { 27 | statement = tables.getStatement(); 28 | assertThat(statement.isClosed()).isFalse(); 29 | } 30 | assertThat(statement.isClosed()).isTrue(); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/test/java/org/sqlite/PreparedStatementThreadTest.java: -------------------------------------------------------------------------------- 1 | package org.sqlite; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | import static org.assertj.core.api.Assertions.fail; 5 | 6 | import java.sql.Connection; 7 | import java.sql.DriverManager; 8 | import java.sql.PreparedStatement; 9 | import java.sql.ResultSet; 10 | import java.sql.SQLException; 11 | import java.util.concurrent.ExecutionException; 12 | import java.util.concurrent.ExecutorService; 13 | import java.util.concurrent.Executors; 14 | import java.util.concurrent.Future; 15 | import java.util.concurrent.atomic.AtomicInteger; 16 | import org.junit.jupiter.api.AfterAll; 17 | import org.junit.jupiter.api.BeforeAll; 18 | import org.junit.jupiter.api.Test; 19 | 20 | /** 21 | * Tests to ensure that incorrect usage of prepared statements does not result in a java 22 | * segmentation fault 23 | */ 24 | public class PreparedStatementThreadTest { 25 | private static ExecutorService executorService; 26 | private Connection conn; 27 | private PreparedStatement stat; 28 | 29 | @BeforeAll 30 | static void beforeAll() { 31 | executorService = Executors.newFixedThreadPool(2); 32 | } 33 | 34 | @AfterAll 35 | static void afterAll() { 36 | executorService.shutdownNow(); 37 | } 38 | 39 | /** 40 | * Tests to make sure that if one thread is uses a PreparedStatement which another thread is 41 | * closing, that the application throws an exception rather than crashing due to undefined C 42 | * behavior 43 | */ 44 | @Test 45 | public void testPreparedStmtConcurrentCloseSegFault() throws SQLException { 46 | connect(); 47 | try { 48 | for (int i = 0; i < 100; i++) { 49 | testRace(executorService, () -> stat.close()); 50 | } 51 | } finally { 52 | close(); 53 | } 54 | } 55 | 56 | /** 57 | * Tests to make sure that if one thread is uses a PreparedStatement which another thread closes 58 | * the underlying connection to, the application throws an exception rather than crashing due to 59 | * undefined C behavior 60 | */ 61 | @Test 62 | public void testPreparedStmtConcurrentCloseConnSegFault() throws SQLException { 63 | for (int i = 0; i < 100; i++) { 64 | connect(); 65 | testRace(executorService, conn::close); 66 | close(); 67 | } 68 | } 69 | 70 | public void connect() throws SQLException { 71 | conn = DriverManager.getConnection("jdbc:sqlite:"); 72 | } 73 | 74 | public void close() throws SQLException { 75 | conn.close(); 76 | } 77 | 78 | private void testRace(ExecutorService executorService, CloseCallback closeCallback) 79 | throws SQLException { 80 | AtomicInteger countdown = new AtomicInteger(2); 81 | stat = conn.prepareStatement("select 1,2,3,4,5"); 82 | 83 | Future queryThread = 84 | executorService.submit( 85 | () -> { 86 | waitFor(countdown); 87 | for (int i = 0; i < 100; i++) { 88 | ResultSet set = stat.executeQuery(); 89 | assertThat(set.next()).isTrue(); 90 | for (int j = 1; j <= 5; j++) assertThat(set.getInt(j)).isEqualTo(j); 91 | } 92 | return true; 93 | }); 94 | Future closeThread = 95 | executorService.submit( 96 | () -> { 97 | waitFor(countdown); 98 | closeCallback.close(); 99 | return true; 100 | }); 101 | try { 102 | assertThat(queryThread.get()).isTrue(); 103 | assertThat(closeThread.get()).isTrue(); 104 | } catch (InterruptedException e) { 105 | Thread.currentThread().interrupt(); 106 | fail("", e); 107 | } catch (ExecutionException e) { 108 | // this is OK, so long as we don't seg fault 109 | } 110 | } 111 | 112 | private void waitFor(AtomicInteger countdown) { 113 | countdown.decrementAndGet(); 114 | while (0 < countdown.get()) 115 | ; 116 | } 117 | 118 | @FunctionalInterface 119 | interface CloseCallback { 120 | void close() throws SQLException; 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/test/java/org/sqlite/ProgressHandlerTest.java: -------------------------------------------------------------------------------- 1 | package org.sqlite; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | import static org.assertj.core.api.Assertions.fail; 5 | 6 | import java.sql.Connection; 7 | import java.sql.DriverManager; 8 | import java.sql.SQLException; 9 | import java.sql.Statement; 10 | import org.junit.jupiter.api.AfterEach; 11 | import org.junit.jupiter.api.BeforeEach; 12 | import org.junit.jupiter.api.Test; 13 | import org.sqlite.core.DB; 14 | import org.sqlite.core.NativeDBHelper; 15 | 16 | public class ProgressHandlerTest { 17 | private Connection conn; 18 | private Statement stat; 19 | 20 | @BeforeEach 21 | public void connect() throws Exception { 22 | conn = DriverManager.getConnection("jdbc:sqlite:"); 23 | stat = conn.createStatement(); 24 | } 25 | 26 | @AfterEach 27 | public void close() throws SQLException { 28 | stat.close(); 29 | conn.close(); 30 | } 31 | 32 | private void workWork() throws SQLException { 33 | // Generate some work for the sqlite vm 34 | stat.executeUpdate("drop table if exists foo;"); 35 | stat.executeUpdate("create table foo (id integer);"); 36 | stat.executeUpdate("insert into foo (id) values (1);"); 37 | for (int i = 0; i < 100; i++) { 38 | stat.executeQuery("select * from foo"); 39 | } 40 | } 41 | 42 | @Test 43 | public void basicProgressHandler() throws Exception { 44 | final int[] calls = {0}; 45 | ProgressHandler.setHandler( 46 | conn, 47 | 1, 48 | new ProgressHandler() { 49 | @Override 50 | protected int progress() { 51 | calls[0]++; 52 | return 0; 53 | } 54 | }); 55 | workWork(); 56 | assertThat(calls[0]).isGreaterThan(0); 57 | } 58 | 59 | @Test 60 | public void testUnregister() throws Exception { 61 | final int[] calls = {0}; 62 | ProgressHandler.setHandler( 63 | conn, 64 | 1, 65 | new ProgressHandler() { 66 | @Override 67 | protected int progress() { 68 | calls[0]++; 69 | return 0; 70 | } 71 | }); 72 | workWork(); 73 | assertThat(calls[0]).isGreaterThan(0); 74 | int totalCalls = calls[0]; 75 | ProgressHandler.clearHandler(conn); 76 | workWork(); 77 | assertThat(calls[0]).isEqualTo(totalCalls); 78 | } 79 | 80 | @Test 81 | public void testInterrupt() { 82 | 83 | try { 84 | ProgressHandler.setHandler( 85 | conn, 86 | 1, 87 | new ProgressHandler() { 88 | @Override 89 | protected int progress() { 90 | return 1; 91 | } 92 | }); 93 | workWork(); 94 | } catch (SQLException ex) { 95 | // Expected error 96 | return; 97 | } 98 | // Progress function throws, not reached 99 | fail("Progress function throws, not reached"); 100 | } 101 | 102 | /** 103 | * Tests that clear progress helper is implemented as expected. Ensures that memory is free'd 104 | * and free'd pointers are set to null (0) 105 | * 106 | * @throws Exception on test failure 107 | */ 108 | @Test 109 | public void testClearProgressHelper() throws Exception { 110 | SQLiteConnection sqliteConnection = (SQLiteConnection) conn; 111 | final DB database = sqliteConnection.getDatabase(); 112 | setDummyHandler(); 113 | assertThat(NativeDBHelper.getProgressHandler(database)).isNotEqualTo(0); 114 | ProgressHandler.clearHandler(conn); 115 | assertThat(NativeDBHelper.getProgressHandler(database)).isEqualTo(0); 116 | ProgressHandler.clearHandler(conn); 117 | 118 | setDummyHandler(); 119 | assertThat(NativeDBHelper.getProgressHandler(database)).isNotEqualTo(0); 120 | ProgressHandler.setHandler(conn, 1, null); 121 | assertThat(NativeDBHelper.getProgressHandler(database)).isEqualTo(0); 122 | ProgressHandler.setHandler(conn, 1, null); 123 | 124 | setDummyHandler(); 125 | assertThat(NativeDBHelper.getProgressHandler(database)).isNotEqualTo(0); 126 | conn.close(); 127 | assertThat(NativeDBHelper.getProgressHandler(database)).isEqualTo(0); 128 | } 129 | 130 | private void setDummyHandler() throws SQLException { 131 | ProgressHandler.setHandler( 132 | conn, 133 | 1, 134 | new ProgressHandler() { 135 | @Override 136 | protected int progress() { 137 | return 0; 138 | } 139 | }); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/test/java/org/sqlite/ReadUncommittedTest.java: -------------------------------------------------------------------------------- 1 | // -------------------------------------- 2 | // sqlite-jdbc Project 3 | // 4 | // ReadCommitedTest.java 5 | // Since: Jan 19, 2009 6 | // 7 | // $URL$ 8 | // $Author$ 9 | // -------------------------------------- 10 | package org.sqlite; 11 | 12 | import static org.assertj.core.api.Assertions.assertThat; 13 | 14 | import java.sql.Connection; 15 | import java.sql.DriverManager; 16 | import java.sql.SQLException; 17 | import java.sql.Statement; 18 | import java.util.Properties; 19 | import org.junit.jupiter.api.AfterEach; 20 | import org.junit.jupiter.api.BeforeEach; 21 | import org.junit.jupiter.api.Test; 22 | 23 | public class ReadUncommittedTest { 24 | private Connection conn; 25 | private Statement stat; 26 | 27 | @BeforeEach 28 | public void connect() throws Exception { 29 | Properties prop = new Properties(); 30 | prop.setProperty("shared_cache", "true"); 31 | conn = DriverManager.getConnection("jdbc:sqlite:", prop); 32 | stat = conn.createStatement(); 33 | stat.executeUpdate("create table test (id integer primary key, fn, sn);"); 34 | stat.executeUpdate("create view testView as select * from test;"); 35 | } 36 | 37 | @AfterEach 38 | public void close() throws SQLException { 39 | stat.close(); 40 | conn.close(); 41 | } 42 | 43 | @Test 44 | public void setReadUncommitted() throws SQLException { 45 | conn.setTransactionIsolation(SQLiteConnection.TRANSACTION_READ_UNCOMMITTED); 46 | } 47 | 48 | @Test 49 | public void setSerializable() throws SQLException { 50 | conn.setTransactionIsolation(SQLiteConnection.TRANSACTION_SERIALIZABLE); 51 | } 52 | 53 | @Test 54 | public void setIsolationPromotedToSerializable() throws SQLException { 55 | conn.setTransactionIsolation(SQLiteConnection.TRANSACTION_REPEATABLE_READ); 56 | } 57 | 58 | @Test 59 | public void setReadUncommittedWithConfig() throws SQLException { 60 | // Override original setup 61 | Properties prop = new Properties(); 62 | prop.setProperty("shared_cache", "true"); 63 | conn = DriverManager.getConnection("jdbc:sqlite:", prop); 64 | stat = conn.createStatement(); 65 | assertThat(stat.executeQuery("PRAGMA read_uncommitted;").getString(1)) 66 | .as("Fail to set pragma read_uncommitted") 67 | .isEqualTo("0"); 68 | 69 | prop.setProperty("read_uncommitted", "true"); 70 | conn = DriverManager.getConnection("jdbc:sqlite:", prop); 71 | stat = conn.createStatement(); 72 | assertThat(stat.executeQuery("PRAGMA read_uncommitted;").getString(1)) 73 | .as("Fail to set pragma read_uncommitted") 74 | .isEqualTo("1"); 75 | 76 | prop.setProperty("read_uncommitted", "false"); 77 | conn = DriverManager.getConnection("jdbc:sqlite:", prop); 78 | stat = conn.createStatement(); 79 | assertThat(stat.executeQuery("PRAGMA read_uncommitted;").getString(1)) 80 | .as("Fail to set pragma read_uncommitted") 81 | .isEqualTo("0"); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/test/java/org/sqlite/ResultSetWithoutResultsTest.java: -------------------------------------------------------------------------------- 1 | package org.sqlite; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | 5 | import java.sql.Connection; 6 | import java.sql.DriverManager; 7 | import java.sql.ResultSet; 8 | import java.sql.SQLException; 9 | import java.sql.Statement; 10 | import org.junit.jupiter.api.Test; 11 | 12 | /** Tests to make sure we properly reset a query when there are no results */ 13 | public class ResultSetWithoutResultsTest { 14 | 15 | /** 16 | * Validates that Statements can be reused as expected even when the result set has no results 17 | * 18 | * @throws Exception 19 | */ 20 | @Test 21 | public void testQueryIsReset() throws Exception { 22 | try (Connection con = DriverManager.getConnection("jdbc:sqlite::memory:")) { 23 | try (Statement statement = con.createStatement()) { 24 | // run a few queries with no results 25 | for (int i = 0; i < 3; i++) { 26 | runEmptyStatement(statement); 27 | } 28 | // test a query that has a result. 29 | testStmtWithResult(statement); 30 | } 31 | } 32 | } 33 | 34 | private void testStmtWithResult(Statement statement) throws SQLException { 35 | try (ResultSet rs = statement.executeQuery("select 123")) { 36 | assertThat(rs.isBeforeFirst()).isTrue(); 37 | assertThat(rs.isAfterLast()).isFalse(); 38 | rs.next(); 39 | assertThat(rs.isBeforeFirst()).isFalse(); 40 | assertThat(rs.isAfterLast()).isFalse(); 41 | assertThat(rs.getInt(1)).isEqualTo(123); 42 | rs.next(); 43 | assertThat(rs.isAfterLast()).isTrue(); 44 | } 45 | } 46 | 47 | private void runEmptyStatement(Statement statement) throws SQLException { 48 | try (ResultSet rs = statement.executeQuery("select 1 where 1=0")) { 49 | assertThat(rs.isBeforeFirst()).isFalse(); 50 | assertThat(rs.isAfterLast()).isFalse(); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/test/java/org/sqlite/SQLiteConfigTest.java: -------------------------------------------------------------------------------- 1 | package org.sqlite; 2 | 3 | import static org.assertj.core.api.Assertions.*; 4 | 5 | import java.util.HashSet; 6 | import java.util.Properties; 7 | import java.util.Set; 8 | import org.junit.jupiter.api.Test; 9 | import org.sqlite.SQLiteConfig.Pragma; 10 | 11 | public class SQLiteConfigTest { 12 | 13 | @Test 14 | public void toProperties() { 15 | SQLiteConfig config = new SQLiteConfig(); 16 | 17 | config.setReadOnly(true); 18 | config.setDateStringFormat("yyyy/mm/dd"); 19 | config.setDatePrecision("seconds"); 20 | config.setDateClass("real"); 21 | config.setGetGeneratedKeys(false); 22 | 23 | Properties properties = config.toProperties(); 24 | 25 | assertThat(properties.getProperty(SQLiteConfig.Pragma.DATE_STRING_FORMAT.getPragmaName())) 26 | .isEqualTo("yyyy/mm/dd"); 27 | assertThat(properties.getProperty(SQLiteConfig.Pragma.DATE_PRECISION.getPragmaName())) 28 | .isEqualTo(SQLiteConfig.DatePrecision.SECONDS.name()); 29 | assertThat(properties.getProperty(SQLiteConfig.Pragma.DATE_CLASS.getPragmaName())) 30 | .isEqualTo(SQLiteConfig.DateClass.REAL.name()); 31 | assertThat(properties.getProperty(Pragma.JDBC_GET_GENERATED_KEYS.getPragmaName())) 32 | .isEqualTo("false"); 33 | } 34 | 35 | @Test 36 | public void setBusyTimeout() { 37 | SQLiteConfig config = new SQLiteConfig(); 38 | 39 | // verify the default is set in the pragma table and the cached value 40 | assertThat(config.toProperties().getProperty(SQLiteConfig.Pragma.BUSY_TIMEOUT.pragmaName)) 41 | .isEqualTo("3000"); 42 | assertThat(config.getBusyTimeout()).isEqualTo(3000); 43 | 44 | // verify that the default is updated in both places 45 | config.setBusyTimeout(1234); 46 | assertThat(config.toProperties().getProperty(SQLiteConfig.Pragma.BUSY_TIMEOUT.pragmaName)) 47 | .isEqualTo("1234"); 48 | assertThat(config.getBusyTimeout()).isEqualTo(1234); 49 | 50 | Properties properties = new Properties(); 51 | properties.setProperty(SQLiteConfig.Pragma.BUSY_TIMEOUT.pragmaName, "100"); 52 | config = new SQLiteConfig(properties); 53 | 54 | // verify that we can set an initial value other than the default 55 | assertThat(config.toProperties().getProperty(SQLiteConfig.Pragma.BUSY_TIMEOUT.pragmaName)) 56 | .isEqualTo("100"); 57 | assertThat(config.getBusyTimeout()).isEqualTo(100); 58 | } 59 | 60 | @Test 61 | public void pragmaSet() { 62 | Set expectedPragmaSet = new HashSet<>(); 63 | for (Pragma v : SQLiteConfig.Pragma.values()) { 64 | expectedPragmaSet.add(v.pragmaName); 65 | } 66 | 67 | assertThat(SQLiteConfig.pragmaSet).isEqualTo(expectedPragmaSet); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/test/java/org/sqlite/SQLiteConnectionPoolDataSourceTest.java: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------------------- 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | *--------------------------------------------------------------------------*/ 14 | package org.sqlite; 15 | 16 | import static org.assertj.core.api.Assertions.assertThat; 17 | import static org.assertj.core.api.Assertions.assertThatThrownBy; 18 | 19 | import java.sql.Connection; 20 | import java.sql.SQLException; 21 | import java.sql.Statement; 22 | import javax.sql.ConnectionPoolDataSource; 23 | import javax.sql.PooledConnection; 24 | import org.junit.jupiter.api.Disabled; 25 | import org.junit.jupiter.api.Test; 26 | import org.sqlite.javax.SQLiteConnectionPoolDataSource; 27 | 28 | public class SQLiteConnectionPoolDataSourceTest { 29 | 30 | @Test 31 | public void connectionTest() throws SQLException { 32 | ConnectionPoolDataSource ds = new SQLiteConnectionPoolDataSource(); 33 | 34 | PooledConnection pooledConn = ds.getPooledConnection(); 35 | 36 | Connection handle = pooledConn.getConnection(); 37 | assertThat(handle.isClosed()).isFalse(); 38 | assertThat(handle.createStatement().execute("select 1")).isTrue(); 39 | 40 | Connection handle2 = pooledConn.getConnection(); 41 | assertThat(handle.isClosed()).isTrue(); 42 | Connection finalHandle = handle; 43 | assertThatThrownBy(() -> finalHandle.createStatement().execute("select 1")) 44 | .isInstanceOf(SQLException.class) 45 | .hasMessage("Connection is closed"); 46 | 47 | assertThat(handle2.createStatement().execute("select 1")).isTrue(); 48 | handle2.close(); 49 | 50 | handle = pooledConn.getConnection(); 51 | assertThat(handle.createStatement().execute("select 1")).isTrue(); 52 | 53 | pooledConn.close(); 54 | assertThat(handle.isClosed()).isTrue(); 55 | } 56 | 57 | @Disabled 58 | @Test 59 | public void proxyConnectionCloseTest() throws SQLException { 60 | ConnectionPoolDataSource ds = new SQLiteConnectionPoolDataSource(); 61 | PooledConnection pooledConn = ds.getPooledConnection(); 62 | System.out.println("pooledConn: " + pooledConn.getClass()); 63 | 64 | Connection handle = pooledConn.getConnection(); 65 | System.out.println("pooledConn.getConnection: " + handle.getClass()); 66 | 67 | Statement st = handle.createStatement(); 68 | System.out.println("statement: " + st.getClass()); 69 | Connection stConn = handle.createStatement().getConnection(); 70 | System.out.println("statement connection:" + stConn.getClass()); 71 | stConn.close(); // This closes the physical connection, not the proxy 72 | 73 | Connection handle2 = pooledConn.getConnection(); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/test/java/org/sqlite/SQLiteDataSourceTest.java: -------------------------------------------------------------------------------- 1 | // -------------------------------------- 2 | // sqlite-jdbc Project 3 | // 4 | // SQLiteDataSourceTest.java 5 | // Since: Mar 11, 2010 6 | // 7 | // $URL$ 8 | // $Author$ 9 | // -------------------------------------- 10 | package org.sqlite; 11 | 12 | import static org.assertj.core.api.Assertions.assertThat; 13 | 14 | import java.nio.ByteOrder; 15 | import java.sql.Connection; 16 | import java.sql.ResultSet; 17 | import java.sql.SQLException; 18 | import java.sql.Statement; 19 | import org.junit.jupiter.api.AfterEach; 20 | import org.junit.jupiter.api.BeforeEach; 21 | import org.junit.jupiter.api.Test; 22 | 23 | public class SQLiteDataSourceTest { 24 | 25 | @BeforeEach 26 | public void setUp() {} 27 | 28 | @AfterEach 29 | public void tearDown() {} 30 | 31 | @Test 32 | public void enumParam() throws Exception { 33 | 34 | SQLiteDataSource ds = new SQLiteDataSource(); 35 | try (Connection conn = ds.getConnection(); 36 | Statement stat = conn.createStatement()) { 37 | 38 | stat.executeUpdate("create table A (id integer, name)"); 39 | stat.executeUpdate("insert into A values(1, 'leo')"); 40 | ResultSet rs = stat.executeQuery("select * from A"); 41 | int count = 0; 42 | while (rs.next()) { 43 | count++; 44 | int id = rs.getInt(1); 45 | String name = rs.getString(2); 46 | assertThat(id).isEqualTo(1); 47 | assertThat(name).isEqualTo("leo"); 48 | } 49 | assertThat(count).isEqualTo(1); 50 | } 51 | } 52 | 53 | @Test 54 | public void encoding() throws Exception { 55 | 56 | String[] configArray = 57 | new String[] { 58 | "UTF8", "UTF-8", "UTF_8", 59 | "UTF16", "UTF-16", "UTF_16", 60 | "UTF_16LE", "UTF-16LE", "UTF16_LITTLE_ENDIAN", 61 | "UTF_16BE", "UTF-16BE", "UTF16_BIG_ENDIAN" 62 | }; 63 | 64 | String nativeOrder; 65 | if (ByteOrder.nativeOrder().equals(ByteOrder.LITTLE_ENDIAN)) { 66 | nativeOrder = "le"; 67 | } else { 68 | nativeOrder = "be"; 69 | } 70 | String[] encodingArray = 71 | new String[] {"UTF-8", "UTF-16" + nativeOrder, "UTF-16le", "UTF-16be"}; 72 | 73 | for (int i = 0; i < configArray.length; i++) { 74 | SQLiteDataSource ds = new SQLiteDataSource(); 75 | ds.setEncoding(configArray[i]); 76 | 77 | try (Connection conn = ds.getConnection(); 78 | Statement stat = conn.createStatement()) { 79 | ResultSet rs = stat.executeQuery("pragma encoding"); 80 | assertThat(rs.getString(1)).isEqualTo(encodingArray[i / 3]); 81 | } 82 | } 83 | } 84 | 85 | @Test 86 | public void setBusyTimeout() { 87 | final SQLiteDataSource ds = new SQLiteDataSource(); 88 | ds.setBusyTimeout(1234); 89 | assertThat( 90 | ds.getConfig() 91 | .toProperties() 92 | .getProperty(SQLiteConfig.Pragma.BUSY_TIMEOUT.pragmaName)) 93 | .isEqualTo("1234"); 94 | assertThat(ds.getConfig().getBusyTimeout()).isEqualTo(1234); 95 | } 96 | 97 | @Test 98 | public void setGetGeneratedKeys() throws SQLException { 99 | final SQLiteDataSource ds = new SQLiteDataSource(); 100 | ds.setGetGeneratedKeys(false); 101 | assertThat( 102 | ds.getConfig() 103 | .toProperties() 104 | .getProperty( 105 | SQLiteConfig.Pragma.JDBC_GET_GENERATED_KEYS.pragmaName)) 106 | .isEqualTo("false"); 107 | assertThat(ds.getConfig().isGetGeneratedKeys()).isEqualTo(false); 108 | assertThat( 109 | ((SQLiteConnection) ds.getConnection()) 110 | .getConnectionConfig() 111 | .isGetGeneratedKeys()) 112 | .isFalse(); 113 | 114 | ds.setGetGeneratedKeys(true); 115 | assertThat( 116 | ds.getConfig() 117 | .toProperties() 118 | .getProperty( 119 | SQLiteConfig.Pragma.JDBC_GET_GENERATED_KEYS.pragmaName)) 120 | .isEqualTo("true"); 121 | assertThat(ds.getConfig().isGetGeneratedKeys()).isEqualTo(true); 122 | assertThat( 123 | ((SQLiteConnection) ds.getConnection()) 124 | .getConnectionConfig() 125 | .isGetGeneratedKeys()) 126 | .isTrue(); 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/test/java/org/sqlite/SQLiteJDBCLoaderTest.java: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------------------- 2 | * Copyright 2007 Taro L. Saito 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *--------------------------------------------------------------------------*/ 16 | // -------------------------------------- 17 | // sqlite-jdbc Project 18 | // 19 | // SQLiteJDBCLoaderTest.java 20 | // Since: Oct 15, 2007 21 | // 22 | // $URL$ 23 | // $Author$ 24 | // -------------------------------------- 25 | package org.sqlite; 26 | 27 | import static org.assertj.core.api.Assertions.assertThat; 28 | import static org.assertj.core.api.Assertions.assertThatNoException; 29 | 30 | import java.nio.file.Path; 31 | import java.sql.*; 32 | import java.util.concurrent.ExecutorService; 33 | import java.util.concurrent.Executors; 34 | import java.util.concurrent.TimeUnit; 35 | import java.util.concurrent.atomic.AtomicInteger; 36 | import org.junit.jupiter.api.AfterEach; 37 | import org.junit.jupiter.api.BeforeEach; 38 | import org.junit.jupiter.api.Test; 39 | import org.junit.jupiter.api.io.TempDir; 40 | 41 | public class SQLiteJDBCLoaderTest { 42 | 43 | private Connection connection = null; 44 | 45 | @BeforeEach 46 | public void setUp() throws Exception { 47 | connection = null; 48 | // create a database connection 49 | connection = DriverManager.getConnection("jdbc:sqlite::memory:"); 50 | } 51 | 52 | @AfterEach 53 | public void tearDown() throws Exception { 54 | if (connection != null) { 55 | connection.close(); 56 | } 57 | } 58 | 59 | @Test 60 | public void query() { 61 | // if e.getMessage() is "out of memory", it probably means no 62 | // database file is found 63 | assertThatNoException() 64 | .isThrownBy( 65 | () -> { 66 | Statement statement = connection.createStatement(); 67 | statement.setQueryTimeout(30); // set timeout to 30 sec. 68 | 69 | statement.executeUpdate( 70 | "create table person ( id integer, name string)"); 71 | statement.executeUpdate("insert into person values(1, 'leo')"); 72 | statement.executeUpdate("insert into person values(2, 'yui')"); 73 | 74 | ResultSet rs = 75 | statement.executeQuery("select * from person order by id"); 76 | while (rs.next()) { 77 | // read the result set 78 | rs.getInt(1); 79 | rs.getString(2); 80 | } 81 | }); 82 | } 83 | 84 | @Test 85 | public void function() throws SQLException { 86 | Function.create( 87 | connection, 88 | "total", 89 | new Function() { 90 | @Override 91 | protected void xFunc() throws SQLException { 92 | int sum = 0; 93 | for (int i = 0; i < args(); i++) { 94 | sum += value_int(i); 95 | } 96 | result(sum); 97 | } 98 | }); 99 | 100 | ResultSet rs = connection.createStatement().executeQuery("select total(1, 2, 3, 4, 5)"); 101 | assertThat(rs.next()).isTrue(); 102 | assertThat(rs.getInt(1)).isEqualTo(1 + 2 + 3 + 4 + 5); 103 | } 104 | 105 | @Test 106 | public void version() { 107 | assertThat(SQLiteJDBCLoader.getVersion()).isNotEqualTo("unknown"); 108 | } 109 | 110 | @Test 111 | public void test(@TempDir Path tmpDir) throws Throwable { 112 | final AtomicInteger completedThreads = new AtomicInteger(0); 113 | ExecutorService pool = Executors.newFixedThreadPool(32); 114 | for (int i = 0; i < 32; i++) { 115 | final String connStr = "jdbc:sqlite:" + tmpDir.resolve("sample-" + i + ".db"); 116 | final int sleepMillis = i; 117 | pool.execute( 118 | () -> { 119 | try { 120 | Thread.sleep(sleepMillis * 10); 121 | } catch (InterruptedException ignored) { 122 | } 123 | assertThatNoException() 124 | .isThrownBy( 125 | () -> { 126 | // Uncomment the synchronized block and everything 127 | // works. 128 | // synchronized (TestSqlite.class) { 129 | Connection conn = DriverManager.getConnection(connStr); 130 | conn.close(); 131 | // } 132 | }); 133 | completedThreads.incrementAndGet(); 134 | }); 135 | } 136 | pool.shutdown(); 137 | pool.awaitTermination(3, TimeUnit.SECONDS); 138 | assertThat(completedThreads.get()).isEqualTo(32); 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /src/test/java/org/sqlite/SavepointTest.java: -------------------------------------------------------------------------------- 1 | package org.sqlite; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | 5 | import java.io.File; 6 | import java.sql.Connection; 7 | import java.sql.DriverManager; 8 | import java.sql.PreparedStatement; 9 | import java.sql.ResultSet; 10 | import java.sql.SQLException; 11 | import java.sql.Savepoint; 12 | import java.sql.Statement; 13 | import java.util.Properties; 14 | import org.junit.jupiter.api.AfterEach; 15 | import org.junit.jupiter.api.BeforeEach; 16 | import org.junit.jupiter.api.Test; 17 | import org.junit.jupiter.api.io.TempDir; 18 | 19 | /** 20 | * These tests assume that Statements and PreparedStatements are working as per normal and test the 21 | * interactions of commit(), setSavepoint(), setSavepoint(String), rollback(Savepoint), and 22 | * release(Savepoint). 23 | */ 24 | public class SavepointTest { 25 | private Connection conn1, conn2; 26 | private Statement stat1, stat2; 27 | 28 | @BeforeEach 29 | public void connect(@TempDir File tempDir) throws Exception { 30 | File tmpFile = File.createTempFile("test-trans", ".db", tempDir); 31 | 32 | Properties prop = new Properties(); 33 | prop.setProperty("shared_cache", "false"); 34 | 35 | conn1 = DriverManager.getConnection("jdbc:sqlite:" + tmpFile.getAbsolutePath(), prop); 36 | conn2 = DriverManager.getConnection("jdbc:sqlite:" + tmpFile.getAbsolutePath(), prop); 37 | 38 | stat1 = conn1.createStatement(); 39 | stat2 = conn2.createStatement(); 40 | } 41 | 42 | @AfterEach 43 | public void close() throws Exception { 44 | stat1.close(); 45 | stat2.close(); 46 | conn1.close(); 47 | conn2.close(); 48 | } 49 | 50 | @Test 51 | public void insert() throws SQLException { 52 | ResultSet rs; 53 | String countSql = "select count(*) from trans;"; 54 | 55 | stat1.executeUpdate("create table trans (c1);"); 56 | conn1.setSavepoint(); 57 | 58 | assertThat(stat1.executeUpdate("insert into trans values (4);")).isEqualTo(1); 59 | 60 | // transaction not yet committed, conn1 can see, conn2 can not 61 | rs = stat1.executeQuery(countSql); 62 | assertThat(rs.next()).isTrue(); 63 | assertThat(rs.getInt(1)).isEqualTo(1); 64 | rs.close(); 65 | rs = stat2.executeQuery(countSql); 66 | assertThat(rs.next()).isTrue(); 67 | assertThat(rs.getInt(1)).isEqualTo(0); 68 | rs.close(); 69 | 70 | conn1.commit(); 71 | 72 | // all connects can see data 73 | rs = stat2.executeQuery(countSql); 74 | assertThat(rs.next()).isTrue(); 75 | assertThat(rs.getInt(1)).isEqualTo(1); 76 | rs.close(); 77 | } 78 | 79 | @Test 80 | public void rollback() throws SQLException { 81 | String select = "select * from trans;"; 82 | ResultSet rs; 83 | 84 | stat1.executeUpdate("create table trans (c1);"); 85 | Savepoint sp = conn1.setSavepoint(); 86 | stat1.executeUpdate("insert into trans values (3);"); 87 | 88 | rs = stat1.executeQuery(select); 89 | assertThat(rs.next()).isTrue(); 90 | rs.close(); 91 | 92 | conn1.rollback(sp); 93 | 94 | rs = stat1.executeQuery(select); 95 | assertThat(rs.next()).isFalse(); 96 | rs.close(); 97 | } 98 | 99 | @Test 100 | public void multiRollback() throws SQLException { 101 | ResultSet rs; 102 | 103 | stat1.executeUpdate("create table t (c1);"); 104 | conn1.setSavepoint(); 105 | stat1.executeUpdate("insert into t values (1);"); 106 | conn1.commit(); 107 | 108 | Savepoint sp = conn1.setSavepoint(); 109 | stat1.executeUpdate("insert into t values (1);"); 110 | conn1.rollback(sp); 111 | 112 | stat1.addBatch("insert into t values (2);"); 113 | stat1.addBatch("insert into t values (3);"); 114 | stat1.executeBatch(); 115 | conn1.commit(); 116 | 117 | Savepoint sp7 = conn1.setSavepoint("num7"); 118 | stat1.addBatch("insert into t values (7);"); 119 | stat1.executeBatch(); 120 | 121 | // nested savepoint 122 | Savepoint sp8 = conn1.setSavepoint("num8"); 123 | stat1.addBatch("insert into t values (8);"); 124 | stat1.executeBatch(); 125 | conn1.rollback(sp8); 126 | 127 | conn1.rollback(sp7); 128 | 129 | stat1.executeUpdate("insert into t values (4);"); 130 | 131 | conn1.setAutoCommit(true); 132 | stat1.executeUpdate("insert into t values (5);"); 133 | conn1.setAutoCommit(false); 134 | PreparedStatement p = conn1.prepareStatement("insert into t values (?);"); 135 | p.setInt(1, 6); 136 | p.executeUpdate(); 137 | p.setInt(1, 7); 138 | p.executeUpdate(); 139 | 140 | // conn1 can see (1+...+7), conn2 can see (1+...+5) 141 | rs = stat1.executeQuery("select sum(c1) from t;"); 142 | assertThat(rs.next()).isTrue(); 143 | assertThat(rs.getInt(1)).isEqualTo(1 + 2 + 3 + 4 + 5 + 6 + 7); 144 | rs.close(); 145 | 146 | rs = stat2.executeQuery("select sum(c1) from t;"); 147 | assertThat(rs.next()).isTrue(); 148 | assertThat(rs.getInt(1)).isEqualTo(1 + 2 + 3 + 4 + 5); 149 | rs.close(); 150 | } 151 | 152 | @Test 153 | public void release() throws SQLException { 154 | ResultSet rs; 155 | String countSql = "select count(*) from trans;"; 156 | 157 | stat1.executeUpdate("create table trans (c1);"); 158 | 159 | Savepoint outerSP = conn1.setSavepoint("outer_sp"); 160 | assertThat(stat1.executeUpdate("insert into trans values (4);")).isEqualTo(1); 161 | 162 | // transaction not yet commited, conn1 can see, conn2 can not 163 | rs = stat1.executeQuery(countSql); 164 | assertThat(rs.next()).isTrue(); 165 | assertThat(rs.getInt(1)).isEqualTo(1); 166 | rs.close(); 167 | rs = stat2.executeQuery(countSql); 168 | assertThat(rs.next()).isTrue(); 169 | assertThat(rs.getInt(1)).isEqualTo(0); 170 | rs.close(); 171 | 172 | Savepoint innerSP = conn1.setSavepoint("inner_sp"); 173 | assertThat(stat1.executeUpdate("insert into trans values (5);")).isEqualTo(1); 174 | 175 | // transaction not yet commited, conn1 can see, conn2 can not 176 | rs = stat1.executeQuery(countSql); 177 | assertThat(rs.next()).isTrue(); 178 | assertThat(rs.getInt(1)).isEqualTo(2); 179 | rs.close(); 180 | rs = stat2.executeQuery(countSql); 181 | assertThat(rs.next()).isTrue(); 182 | assertThat(rs.getInt(1)).isEqualTo(0); 183 | rs.close(); 184 | 185 | // releasing an inner savepoint, statements are still wrapped by the outer savepoint 186 | conn1.releaseSavepoint(innerSP); 187 | 188 | rs = stat2.executeQuery(countSql); 189 | assertThat(rs.next()).isTrue(); 190 | assertThat(rs.getInt(1)).isEqualTo(0); 191 | rs.close(); 192 | 193 | // releasing the outer savepoint is like a commit 194 | conn1.releaseSavepoint(outerSP); 195 | 196 | // all connects can see SP1 data 197 | rs = stat2.executeQuery(countSql); 198 | assertThat(rs.next()).isTrue(); 199 | assertThat(rs.getInt(1)).isEqualTo(2); 200 | rs.close(); 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /src/test/java/org/sqlite/TypeMapTest.java: -------------------------------------------------------------------------------- 1 | package org.sqlite; 2 | 3 | import java.sql.Connection; 4 | import java.sql.DriverManager; 5 | import java.sql.SQLException; 6 | import java.util.Map; 7 | import org.junit.jupiter.api.Test; 8 | 9 | public class TypeMapTest { 10 | 11 | public Connection getConnection() throws SQLException { 12 | return DriverManager.getConnection("jdbc:sqlite::memory:"); 13 | } 14 | 15 | @Test 16 | public void getTypeMap() throws Exception { 17 | Connection conn = getConnection(); 18 | 19 | Map> m = conn.getTypeMap(); 20 | 21 | conn.close(); 22 | } 23 | 24 | @Test 25 | public void setTypeMap() throws Exception { 26 | Connection conn = getConnection(); 27 | 28 | Map> m = conn.getTypeMap(); 29 | conn.setTypeMap(m); 30 | 31 | conn.close(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/test/java/org/sqlite/UDFCustomErrorTest.java: -------------------------------------------------------------------------------- 1 | package org.sqlite; 2 | 3 | import static org.assertj.core.api.Assertions.assertThatExceptionOfType; 4 | 5 | import java.sql.Connection; 6 | import java.sql.DriverManager; 7 | import java.sql.SQLException; 8 | import java.sql.Statement; 9 | import org.junit.jupiter.api.AfterEach; 10 | import org.junit.jupiter.api.BeforeEach; 11 | import org.junit.jupiter.api.Test; 12 | 13 | /** Tests User Defined Functions. */ 14 | public class UDFCustomErrorTest { 15 | private Connection conn; 16 | private Statement stat; 17 | 18 | @BeforeEach 19 | public void connect() throws Exception { 20 | conn = DriverManager.getConnection("jdbc:sqlite:"); 21 | stat = conn.createStatement(); 22 | } 23 | 24 | @AfterEach 25 | public void close() throws SQLException { 26 | stat.close(); 27 | conn.close(); 28 | } 29 | 30 | @Test 31 | public void customErr() throws SQLException { 32 | Function.create( 33 | conn, 34 | "f9", 35 | new Function() { 36 | @Override 37 | public void xFunc() throws SQLException { 38 | throw new SQLException("myErr"); 39 | } 40 | }); 41 | assertThatExceptionOfType(SQLException.class) 42 | .isThrownBy(() -> stat.executeQuery("select f9();")); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/test/java/org/sqlite/Utils.java: -------------------------------------------------------------------------------- 1 | package org.sqlite; 2 | 3 | import static org.assertj.core.api.Assumptions.assumeThat; 4 | 5 | import java.sql.Connection; 6 | import java.sql.ResultSet; 7 | import java.sql.SQLException; 8 | import java.sql.Statement; 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | public class Utils { 13 | public static List getCompileOptions(Connection conn) throws SQLException { 14 | List compileOptions = new ArrayList<>(); 15 | try (Statement stat = conn.createStatement()) { 16 | try (ResultSet rs = stat.executeQuery("pragma compile_options")) { 17 | while (rs.next()) { 18 | compileOptions.add(rs.getString(1)); 19 | } 20 | } 21 | } 22 | return compileOptions; 23 | } 24 | 25 | public static void assumeJdbcExtensions(Connection conn) throws SQLException { 26 | assumeThat(getCompileOptions(conn)) 27 | .as("SQLite has to be compiled with JDBC Extensions") 28 | .contains("JDBC_EXTENSIONS"); 29 | } 30 | 31 | public static void assumeMathFunctions(Connection conn) throws SQLException { 32 | assumeThat(getCompileOptions(conn)) 33 | .as("SQLite has to be compiled with SQLITE_ENABLE_MATH_FUNCTIONS") 34 | .contains("ENABLE_MATH_FUNCTIONS"); 35 | } 36 | 37 | public static void assumeJdbcExtensionsOrMathFunctions(Connection conn) throws SQLException { 38 | List compileOptions = getCompileOptions(conn); 39 | boolean expected = 40 | compileOptions.contains("JDBC_EXTENSIONS") 41 | || compileOptions.contains("ENABLE_MATH_FUNCTIONS"); 42 | assumeThat(expected) 43 | .as( 44 | "SQLite has to be compiled with JDBC Extensions or SQLITE_ENABLE_MATH_FUNCTIONS") 45 | .isTrue(); 46 | } 47 | 48 | public static void assumeJdbcExtensionsWithoutMathFunctions(Connection conn) 49 | throws SQLException { 50 | List compileOptions = getCompileOptions(conn); 51 | boolean expected = 52 | compileOptions.contains("JDBC_EXTENSIONS") 53 | && !compileOptions.contains("ENABLE_MATH_FUNCTIONS"); 54 | assumeThat(expected) 55 | .as( 56 | "SQLite has to be compiled with JDBC Extensions and without SQLITE_ENABLE_MATH_FUNCTIONS") 57 | .isTrue(); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/test/java/org/sqlite/architecture/CodingRulesTest.java: -------------------------------------------------------------------------------- 1 | package org.sqlite.architecture; 2 | 3 | import static com.tngtech.archunit.base.DescribedPredicate.not; 4 | import static com.tngtech.archunit.core.domain.JavaClass.Predicates.belongToAnyOf; 5 | import static com.tngtech.archunit.core.domain.JavaClass.Predicates.equivalentTo; 6 | import static com.tngtech.archunit.lang.conditions.ArchPredicates.are; 7 | import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses; 8 | import static com.tngtech.archunit.library.GeneralCodingRules.*; 9 | 10 | import com.tngtech.archunit.core.domain.JavaCall; 11 | import com.tngtech.archunit.core.domain.JavaClass; 12 | import com.tngtech.archunit.core.domain.JavaClasses; 13 | import com.tngtech.archunit.core.domain.properties.HasName; 14 | import com.tngtech.archunit.core.domain.properties.HasOwner; 15 | import com.tngtech.archunit.core.importer.ImportOption; 16 | import com.tngtech.archunit.junit.AnalyzeClasses; 17 | import com.tngtech.archunit.junit.ArchTest; 18 | import com.tngtech.archunit.lang.ArchCondition; 19 | import com.tngtech.archunit.lang.ArchRule; 20 | import com.tngtech.archunit.lang.conditions.ArchConditions; 21 | import java.sql.DriverManager; 22 | import org.sqlite.util.LoggerFactory; 23 | import org.sqlite.util.OSInfo; 24 | 25 | @AnalyzeClasses( 26 | packages = "org.sqlite", 27 | importOptions = {ImportOption.DoNotIncludeTests.class}) 28 | class CodingRulesTest { 29 | public static final ArchCondition USE_SLF4J_LOGGING; 30 | 31 | static { 32 | USE_SLF4J_LOGGING = 33 | ArchConditions.dependOnClassesThat( 34 | com.tngtech.archunit.core.domain.JavaClass.Predicates 35 | .resideInAPackage("org.slf4j")) 36 | .as("use SLF4J"); 37 | } 38 | 39 | @ArchTest 40 | void no_access_to_standard_streams(JavaClasses importedClasses) { 41 | NO_CLASSES_SHOULD_ACCESS_STANDARD_STREAMS.check( 42 | importedClasses.that(are(not(equivalentTo(OSInfo.class))))); 43 | } 44 | 45 | @ArchTest 46 | private final ArchRule no_generic_exceptions = NO_CLASSES_SHOULD_THROW_GENERIC_EXCEPTIONS; 47 | 48 | @ArchTest private final ArchRule no_jodatime = NO_CLASSES_SHOULD_USE_JODATIME; 49 | 50 | @ArchTest 51 | private final ArchRule no_loggers_except_ours = 52 | noClasses() 53 | .that(not(belongToAnyOf(LoggerFactory.class))) 54 | .should(USE_JAVA_UTIL_LOGGING) 55 | .orShould(USE_SLF4J_LOGGING); 56 | 57 | @ArchTest 58 | private final ArchRule no_driver_manager_println = 59 | noClasses() 60 | .should( 61 | ArchConditions.callMethodWhere( 62 | JavaCall.Predicates.target(HasName.Predicates.name("println")) 63 | .and( 64 | JavaCall.Predicates.target( 65 | HasOwner.Predicates.With.owner( 66 | JavaClass.Predicates 67 | .assignableTo( 68 | DriverManager 69 | .class)))))); 70 | } 71 | -------------------------------------------------------------------------------- /src/test/java/org/sqlite/architecture/TestCodingRulesTest.java: -------------------------------------------------------------------------------- 1 | package org.sqlite.architecture; 2 | 3 | import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses; 4 | 5 | import com.tngtech.archunit.core.importer.ImportOption; 6 | import com.tngtech.archunit.junit.AnalyzeClasses; 7 | import com.tngtech.archunit.junit.ArchTest; 8 | import com.tngtech.archunit.lang.ArchRule; 9 | 10 | @AnalyzeClasses( 11 | packages = "org.sqlite", 12 | importOptions = {ImportOption.OnlyIncludeTests.class}) 13 | class TestCodingRulesTest { 14 | 15 | @ArchTest 16 | public static final ArchRule no_junit_assertions = 17 | noClasses() 18 | .should() 19 | .dependOnClassesThat() 20 | .haveFullyQualifiedName("org.junit.jupiter.api.Assertions"); 21 | } 22 | -------------------------------------------------------------------------------- /src/test/java/org/sqlite/core/NativeDBHelper.java: -------------------------------------------------------------------------------- 1 | package org.sqlite.core; 2 | 3 | /** This is a helper class for exposing package local functions of NativeDB to unit tests */ 4 | public class NativeDBHelper { 5 | /** 6 | * Get the native pointer of the progress handler 7 | * 8 | * @param nativeDB the native db object 9 | * @return the pointer of the progress handler 10 | */ 11 | public static long getProgressHandler(DB nativeDB) { 12 | return ((NativeDB) nativeDB).getProgressHandler(); 13 | } 14 | 15 | /** 16 | * Get the native pointer of the busy handler 17 | * 18 | * @param nativeDB the native db object 19 | * @return the pointer of the busy handler 20 | */ 21 | public static long getBusyHandler(DB nativeDB) { 22 | return ((NativeDB) nativeDB).getBusyHandler(); 23 | } 24 | 25 | /** 26 | * Get the native pointer of the commit listener 27 | * 28 | * @param nativeDB the native db object 29 | * @return the pointer of the commit listener 30 | */ 31 | public static long getCommitListener(DB nativeDB) { 32 | return ((NativeDB) nativeDB).getCommitListener(); 33 | } 34 | 35 | /** 36 | * Get the native pointer of the update listener 37 | * 38 | * @param nativeDB the native db object 39 | * @return the pointer of the update listener 40 | */ 41 | public static long getUpdateListener(DB nativeDB) { 42 | return ((NativeDB) nativeDB).getUpdateListener(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/test/resources/META-INF/native-image/org.xerial/sqlite-jdbc/reachability-metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "comment": "New reachability metadata format introduced in GraalVM for JDK 23, this merges the other *-config.json files into one: https://www.graalvm.org/latest/reference-manual/native-image/metadata/", 3 | "resources": [ 4 | { 5 | "glob": "org/sqlite/*.jar" 6 | }, 7 | { 8 | "glob": "org/sqlite/*.db" 9 | }, 10 | { 11 | "module": "java.sql.rowset", 12 | "glob": "javax/sql/rowset/rowset.properties" 13 | } 14 | ], 15 | "bundles": [ 16 | { 17 | "name": "com.sun.rowset.RowSetResourceBundle" 18 | } 19 | ], 20 | "reflection": [ 21 | { 22 | "type": "com.sun.rowset.providers.RIOptimisticProvider", 23 | "methods": [ 24 | { 25 | "name": "", 26 | "parameterTypes": [] 27 | } 28 | ] 29 | }, 30 | { 31 | "type": "java.sql.Types", 32 | "allPublicFields": true 33 | } 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /src/test/resources/META-INF/native-image/org.xerial/sqlite-jdbc/reflect-config.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "com.sun.rowset.providers.RIOptimisticProvider", 4 | "methods": [ 5 | { 6 | "name": "", 7 | "parameterTypes": [] 8 | } 9 | ] 10 | }, 11 | { 12 | "name": "java.sql.Types", 13 | "allPublicFields": true 14 | } 15 | ] -------------------------------------------------------------------------------- /src/test/resources/META-INF/native-image/org.xerial/sqlite-jdbc/resource-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "resources": { 3 | "includes": [ 4 | { 5 | "pattern": "^org/sqlite/.+\\.(jar|db)$" 6 | }, 7 | { 8 | "pattern": "\\Qjavax/sql/rowset/rowset.properties\\E" 9 | } 10 | ] 11 | }, 12 | "bundles": [ 13 | { 14 | "name": "com.sun.rowset.RowSetResourceBundle" 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /src/test/resources/org/sqlite/attach_test.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xerial/sqlite-jdbc/d54053637df62fda39bb46c3e653120c4eff9e13/src/test/resources/org/sqlite/attach_test.db -------------------------------------------------------------------------------- /src/test/resources/org/sqlite/sample.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xerial/sqlite-jdbc/d54053637df62fda39bb46c3e653120c4eff9e13/src/test/resources/org/sqlite/sample.db -------------------------------------------------------------------------------- /src/test/resources/org/sqlite/testdb.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xerial/sqlite-jdbc/d54053637df62fda39bb46c3e653120c4eff9e13/src/test/resources/org/sqlite/testdb.jar --------------------------------------------------------------------------------