├── .github ├── dependabot.yml └── workflows │ ├── announce.yml │ ├── release.yml │ └── test.yml ├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── build.sh ├── contrib ├── adodb │ ├── adodb.sh │ └── usql-config ├── cassandra │ ├── podman-config │ ├── test.sql │ └── usql-config ├── charts │ ├── area_density_stacked.vl.json │ ├── area_density_stacked.vl.json.svg │ ├── area_density_stacked.vl.json.svg.export.png │ └── penguins.json ├── clickhouse │ ├── podman-config │ └── usql-config ├── cockroach │ └── podman-config ├── config.yaml ├── couchbase │ ├── README.md │ ├── podman-config │ └── usql-config ├── db2 │ ├── README.md │ ├── db2cli-validate.sh │ ├── db2cli.ini │ ├── db2dsdriver.cfg │ ├── install-dsdriver.sh │ ├── odbcinst.ini │ ├── podman-config │ ├── test.sql │ └── usql-config ├── duckdb │ └── usql-config ├── exasol │ ├── podman-config │ └── usql-config ├── firebird │ ├── podman-config │ └── usql-config ├── flightsql │ ├── podman-config │ └── usql-config ├── go-setup.sh ├── godror │ ├── fix-oob-config.sh │ ├── grab-instantclient.sh │ └── usql-config ├── h2 │ └── podman-config ├── hive │ ├── podman-config │ └── usql-config ├── ignite │ ├── README.md │ ├── activate.sh │ ├── podman-config │ └── usql-config ├── mymysql │ └── usql-config ├── mysql │ ├── podman-config │ ├── test.sql │ └── usql-config ├── oracle-enterprise │ ├── podman-config │ └── usql-config ├── oracle │ ├── init.sql │ ├── podman-config │ └── usql-config ├── pgx │ └── usql-config ├── podman-run.sh ├── podman-stop.sh ├── postgres │ ├── init.sql │ ├── odbcinst.ini │ ├── podman-config │ ├── schema.sql │ ├── test.sql │ └── usql-config ├── presto │ ├── podman-config │ └── usql-config ├── sqlite3 │ ├── build-windows-icu.sh │ ├── icu-i18n-mingw64.pc │ ├── test.sql │ └── usql-config ├── sqlserver │ ├── init.sql │ ├── podman-config │ ├── test.sql │ └── usql-config ├── trino │ ├── podman-config │ └── usql-config ├── usql-test.sh ├── usqlpass ├── usqlrc ├── vertica │ ├── podman-config │ └── usql-config └── ydb │ ├── podman-config │ └── usql-config ├── drivers ├── adodb │ └── adodb.go ├── athena │ └── athena.go ├── avatica │ └── avatica.go ├── bigquery │ └── bigquery.go ├── cassandra │ └── cassandra.go ├── chai │ └── chai.go ├── clickhouse │ ├── clickhouse.go │ ├── clickhouse_test.go │ ├── reader.go │ └── testdata │ │ └── clickhouse.sql ├── completer │ ├── completer.go │ └── completer_test.go ├── cosmos │ └── cosmos.go ├── couchbase │ └── couchbase.go ├── csvq │ └── csvq.go ├── databend │ └── databend.go ├── databricks │ └── databricks.go ├── drivers.go ├── drivers_test.go ├── duckdb │ └── duckdb.go ├── dynamodb │ └── dynamodb.go ├── errors.go ├── exasol │ └── exasol.go ├── firebird │ └── firebird.go ├── flightsql │ └── flightsql.go ├── godror │ └── godror.go ├── h2 │ └── h2.go ├── hive │ └── hive.go ├── ignite │ └── ignite.go ├── impala │ └── impala.go ├── maxcompute │ └── maxcompute.go ├── metadata │ ├── impala │ │ └── metadata.go │ ├── informationschema │ │ ├── metadata.go │ │ └── metadata_test.go │ ├── metadata.go │ ├── metadata_test.go │ ├── mysql │ │ └── metadata.go │ ├── oracle │ │ └── metadata.go │ ├── postgres │ │ ├── metadata.go │ │ └── metadata_test.go │ ├── reader.go │ └── writer.go ├── moderncsqlite │ └── moderncsqlite.go ├── mymysql │ └── mymysql.go ├── mysql │ └── mysql.go ├── netezza │ └── netezza.go ├── odbc │ └── odbc.go ├── oracle │ ├── oracle.go │ └── orshared │ │ └── orshared.go ├── ots │ └── ots.go ├── pgx │ └── pgx.go ├── postgres │ └── postgres.go ├── presto │ └── presto.go ├── ql │ └── ql.go ├── qtype.go ├── ramsql │ └── ramsql.go ├── sapase │ └── sapase.go ├── saphana │ └── saphana.go ├── snowflake │ └── snowflake.go ├── spanner │ └── spanner.go ├── sqlite3 │ ├── sqlite3.go │ └── sqshared │ │ ├── reader.go │ │ ├── reader_test.go │ │ └── sqshared.go ├── sqlserver │ ├── reader.go │ ├── sqlserver.go │ └── sqlserver_test.go ├── testdata │ ├── .gitignore │ ├── csvq │ │ ├── .gitignore │ │ └── staff.csv │ ├── docker │ │ └── Dockerfile │ ├── gen-golden.sh │ ├── mysql.descTable.expected.txt │ ├── mysql.descTable.golden.txt │ ├── mysql.listFuncs.expected.txt │ ├── mysql.listIndexes.expected.txt │ ├── mysql.listSchemas.expected.txt │ ├── mysql.listSchemas.golden.txt │ ├── mysql.listTables.expected.txt │ ├── mysql.listTables.golden.txt │ ├── pgsql.descTable.expected.txt │ ├── pgsql.descTable.golden.txt │ ├── pgsql.listDbs.golden.txt │ ├── pgsql.listFuncs.expected.txt │ ├── pgsql.listFuncs.golden.txt │ ├── pgsql.listIndexes.expected.txt │ ├── pgsql.listIndexes.golden.txt │ ├── pgsql.listSchemas.expected.txt │ ├── pgsql.listSchemas.golden.txt │ ├── pgsql.listTables.expected.txt │ ├── pgsql.listTables.golden.txt │ ├── sqlserver.descTable.expected.txt │ ├── sqlserver.listFuncs.expected.txt │ ├── sqlserver.listIndexes.expected.txt │ ├── sqlserver.listSchemas.expected.txt │ ├── sqlserver.listTables.expected.txt │ ├── trino.descTable.expected.txt │ ├── trino.listSchemas.expected.txt │ └── trino.listTables.expected.txt ├── trino │ ├── reader.go │ └── trino.go ├── vertica │ └── vertica.go ├── voltdb │ └── voltdb.go └── ydb │ └── ydb.go ├── env ├── env.go ├── list.go └── vars.go ├── gen.go ├── go.mod ├── go.sum ├── handler └── handler.go ├── internal ├── adodb.go ├── athena.go ├── avatica.go ├── bigquery.go ├── cassandra.go ├── chai.go ├── clickhouse.go ├── cosmos.go ├── couchbase.go ├── csvq.go ├── databend.go ├── databricks.go ├── duckdb.go ├── dynamodb.go ├── exasol.go ├── firebird.go ├── flightsql.go ├── godror.go ├── h2.go ├── hive.go ├── ignite.go ├── impala.go ├── internal.go ├── maxcompute.go ├── moderncsqlite.go ├── mymysql.go ├── mysql.go ├── netezza.go ├── odbc.go ├── oracle.go ├── ots.go ├── pgx.go ├── postgres.go ├── presto.go ├── ql.go ├── ramsql.go ├── sapase.go ├── saphana.go ├── snowflake.go ├── spanner.go ├── sqlite3.go ├── sqlserver.go ├── trino.go ├── vertica.go ├── voltdb.go ├── ydb.go └── z.go ├── main.go ├── main_test.go ├── metacmd ├── charts │ └── charts.go ├── cmds.go ├── descs.go └── metacmd.go ├── rline └── rline.go ├── run.go ├── stmt ├── params.go ├── params_test.go ├── parse.go ├── parse_test.go ├── stmt.go └── stmt_test.go ├── styles └── styles.go ├── testcli.go ├── testdata ├── booktest │ ├── authors.csv │ └── books.csv ├── copy.sql ├── duckdb.db ├── duckdb.db.wal ├── inc_test.sql ├── inc_test_z.sql ├── numbers.sql ├── quotes.sql └── sub │ ├── inc_test2.sql │ └── inc_test_z.sql ├── text ├── errors.go ├── license.go ├── logo.png └── text.go └── update-deps.sh /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | # Check for updates to GitHub Actions every weekday 7 | interval: "daily" 8 | -------------------------------------------------------------------------------- /.github/workflows/announce.yml: -------------------------------------------------------------------------------- 1 | name: Announce Release 2 | on: 3 | release: 4 | types: [published] 5 | env: 6 | VER: ${{ github.ref_name }} 7 | HOMEBREW_REPO: https://kenshaw:${{ secrets.HOMEBREW_TOKEN }}@github.com/xo/homebrew-xo.git 8 | AUR_REPO: aur@aur.archlinux.org:usql.git 9 | 10 | jobs: 11 | bump-aur-package: 12 | name: Bump AUR Package 13 | runs-on: ubuntu-24.04 14 | steps: 15 | - name: Add AUR SSH key 16 | uses: shimataro/ssh-key-action@v2 17 | with: 18 | key: ${{ secrets.AUR_SSH_KEY }} 19 | name: id_ed25519 20 | known_hosts: | 21 | aur.archlinux.org ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEuBKrPzbawxA/k2g6NcyV5jmqwJ2s+zpgZGZ7tpLIcN 22 | aur.archlinux.org ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDKF9vAFWdgm9Bi8uc+tYRBmXASBb5cB5iZsB7LOWWFeBrLp3r14w0/9S2vozjgqY5sJLDPONWoTTaVTbhe3vwO8CBKZTEt1AcWxuXNlRnk9FliR1/eNB9uz/7y1R0+c1Md+P98AJJSJWKN12nqIDIhjl2S1vOUvm7FNY43fU2knIhEbHybhwWeg+0wxpKwcAd/JeL5i92Uv03MYftOToUijd1pqyVFdJvQFhqD4v3M157jxS5FTOBrccAEjT+zYmFyD8WvKUa9vUclRddNllmBJdy4NyLB8SvVZULUPrP3QOlmzemeKracTlVOUG1wsDbxknF1BwSCU7CmU6UFP90kpWIyz66bP0bl67QAvlIc52Yix7pKJPbw85+zykvnfl2mdROsaT8p8R9nwCdFsBc9IiD0NhPEHcyHRwB8fokXTajk2QnGhL+zP5KnkmXnyQYOCUYo3EKMXIlVOVbPDgRYYT/XqvBuzq5S9rrU70KoI/S5lDnFfx/+lPLdtcnnEPk= 23 | aur.archlinux.org ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBLMiLrP8pVi5BFX2i3vepSUnpedeiewE5XptnUnau+ZoeUOPkpoCgZZuYfpaIQfhhJJI5qgnjJmr4hyJbe/zxow= 24 | - name: Bump AUR Package 25 | run: | 26 | export WORKDIR=$(mktemp -d /tmp/aur-usql.XXXXXX) 27 | export REPO_PATH=$WORKDIR/aur-usql 28 | wget -O $WORKDIR/archive.tar.gz https://github.com/xo/usql/archive/${VER}.tar.gz 29 | export SHA256SUM=$(sha256sum $WORKDIR/archive.tar.gz|awk '{print $1}') 30 | export CHANGELOG=$( 31 | curl \ 32 | -s \ 33 | -H 'Accept: application/vnd.github+json' \ 34 | https://api.github.com/repos/xo/usql/releases/tags/$VER \ 35 | |jq -r .body \ 36 | |sed -e 's/\\r//g' -e 's/\[VirusTotal.*//' 37 | ) 38 | git clone $AUR_REPO $REPO_PATH 39 | git -C $REPO_PATH config user.name 'Kenneth Shaw' 40 | git -C $REPO_PATH config user.email 'kenshaw@gmail.com' 41 | sed -i "s/pkgver=.*$/pkgver=${VER#v}/" $REPO_PATH/PKGBUILD 42 | sed -i "s/sha256sums=.*$/sha256sums=('$SHA256SUM')/" $REPO_PATH/PKGBUILD 43 | sed -i "s/pkgrel=.*$/pkgrel=1/" $REPO_PATH/PKGBUILD 44 | sed -i "s/pkgver =.*$/pkgver = ${VER#v}/" $REPO_PATH/.SRCINFO 45 | sed -i "s%source =.*$%source = usql-${VER#v}.tar.gz::https://github.com/xo/usql/archive/${VER}.tar.gz%" $REPO_PATH/.SRCINFO 46 | sed -i "s/sha256sums =.*$/sha256sums = $SHA256SUM/" $REPO_PATH/.SRCINFO 47 | sed -i "s/pkgrel =.*$/pkgrel = 1/" $REPO_PATH/.SRCINFO 48 | git -C $REPO_PATH add PKGBUILD .SRCINFO 49 | git -C $REPO_PATH commit -m "$(printf %b "Update usql version to ${VER}\n\n${CHANGELOG}")" 50 | git -C $REPO_PATH show -C 51 | git -C $REPO_PATH push origin master 52 | 53 | bump-homebrew-formula: 54 | name: Bump Homebrew Formula 55 | runs-on: ubuntu-24.04 56 | steps: 57 | - name: Bump Homebrew Formula 58 | run: | 59 | export WORKDIR=$(mktemp -d /tmp/homebrew-xo.XXXXXX) 60 | export REPO_PATH=$WORKDIR/homebrew-xo 61 | wget -O $WORKDIR/archive.tar.gz https://github.com/xo/usql/archive/${VER}.tar.gz 62 | export SHA256SUM=$(sha256sum $WORKDIR/archive.tar.gz|awk '{print $1}') 63 | export CHANGELOG=$( 64 | curl \ 65 | -s \ 66 | -H 'Accept: application/vnd.github+json' \ 67 | https://api.github.com/repos/xo/usql/releases/tags/$VER \ 68 | |jq -r .body \ 69 | |sed -e 's/\\r//g' -e 's/\[VirusTotal.*//' 70 | ) 71 | git clone $HOMEBREW_REPO $REPO_PATH 72 | git -C $REPO_PATH config user.name 'Kenneth Shaw' 73 | git -C $REPO_PATH config user.email 'ken@usql.app' 74 | sed -i "s%url \".*$%url \"https://github.com/xo/usql/archive/${VER}.tar.gz\"%" $REPO_PATH/Formula/usql.rb 75 | sed -i "s/sha256 \".*$/sha256 \"$SHA256SUM\"/" $REPO_PATH/Formula/usql.rb 76 | git -C $REPO_PATH add Formula/usql.rb 77 | git -C $REPO_PATH commit -m "$(printf %b "Update usql version to ${VER}\n\n${CHANGELOG}")" 78 | git -C $REPO_PATH show -C 79 | git -C $REPO_PATH push origin master 80 | 81 | announce-discord: 82 | name: Announce Discord 83 | runs-on: ubuntu-24.04 84 | steps: 85 | - name: Announce Discord 86 | run: | 87 | curl \ 88 | -H 'Content-Type: application/json' \ 89 | -d '{"username": "usql", "content": "> *usql ${{ github.ref_name }}* has been released!\n\nGet it here: https://github.com/xo/usql/releases/${{ github.ref_name }}"}' \ 90 | ${{ secrets.DISCORD_WEBHOOK_URL }} 91 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | on: [push, pull_request] 2 | name: Test usql 3 | jobs: 4 | test: 5 | name: Build and Test usql 6 | runs-on: ubuntu-24.04 7 | services: 8 | cassandra: 9 | image: docker.io/usql/cassandra:latest 10 | ports: 11 | - 9042:9042 12 | postgres: 13 | image: docker.io/usql/postgres:latest 14 | env: 15 | POSTGRES_PASSWORD: P4ssw0rd 16 | ports: 17 | - 5432:5432 18 | mysql: 19 | image: docker.io/library/mariadb 20 | env: 21 | MYSQL_ROOT_PASSWORD: P4ssw0rd 22 | ports: 23 | - 3306:3306 24 | sqlserver: 25 | image: mcr.microsoft.com/mssql/server:2022-latest 26 | env: 27 | ACCEPT_EULA: Y 28 | MSSQL_PID: Express 29 | SA_PASSWORD: Adm1nP@ssw0rd 30 | ports: 31 | - 1433:1433 32 | steps: 33 | - name: Install Go 34 | uses: actions/setup-go@v5 35 | with: 36 | go-version: stable 37 | - name: Install Packages 38 | run: | 39 | sudo apt-get -qq update 40 | sudo apt-get install -y build-essential libicu-dev unixodbc unixodbc-dev 41 | - name: Checkout code 42 | uses: actions/checkout@v4 43 | - name: Unit Tests 44 | run: | 45 | go test -v ./stmt 46 | - name: Build with all drivers 47 | run: | 48 | ./build.sh -b -t all 49 | - name: Shell Tests 50 | run: | 51 | go run testcli.go &> output.log 52 | ls -alh output.log 53 | - name: Archive output 54 | uses: actions/upload-artifact@v4 55 | if: always() 56 | with: 57 | name: output 58 | path: output.log 59 | if-no-files-found: error 60 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /usql 2 | /usql.exe 3 | /build/ 4 | /coverage.out 5 | /*.sql 6 | /*.txt 7 | 8 | .usql_history* 9 | .[a-f0-9]* 10 | 11 | *.ini 12 | *.csv 13 | *.db 14 | *.zip 15 | *.out 16 | 17 | *.sqlite3 18 | *.sqlite3-journal 19 | *.duckdb 20 | *.wal 21 | 22 | /instantclient* 23 | /*.pc 24 | 25 | .vscode/ 26 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Contributing to usql 2 | ==================== 3 | 4 | Any contributions are welcome. If you found a bug, or a missing feature, 5 | take a look at existing [issues](https://github.com/xo/usql/issues) 6 | and create a new one if needed. 7 | 8 | You can also open up a [pull request](https://github.com/xo/usql/pulls) (PR) 9 | with code or documentation changes. 10 | 11 | # Adding a new driver 12 | 13 | 1. Add a new schema in [dburl](https://github.com/xo/dburl). 14 | 1. Create a new go package in `drivers`. It should have an `init()` function, that would call `drivers.Register()`. 15 | 1. Regenerate code in the `internal` package by running `internal/gen.sh`. 16 | 1. Add any new required modules using `go get` or by editing `go.mod` manually and running `go mod tidy`. 17 | 1. Run all tests, build `usql` and see if the new driver works. 18 | 1. Update `README.md`. 19 | 20 | > Tip: check out closed PRs for examples, and/or search the codebase 21 | for names of databases you're familiar with. 22 | 23 | # Enabling metadata introspection for a driver 24 | 25 | For `\d*` commands to work, `usql` needs to know how to read the structure of a database. 26 | A driver must provide a metadata reader, by setting the `NewMetadataReader` property 27 | in the `drivers.Driver` structure passed to `drivers.Register()`. This needs to be a function 28 | that given a database and reader options, returns a reader instance for this particular driver. 29 | 30 | If the database has a `information_schema` schema, with standard tables like `tables` and `columns`, 31 | you can use an existing reader from the `drivers/informationschema` package. 32 | Since there are usually minor difference in objects defined in that schema in different databases, 33 | there's a set of options to configure this reader. Refer to 34 | the [package docs](https://pkg.go.dev/github.com/xo/usql/drivers/metadata/informationschema) for details. 35 | 36 | If you can't use the `informationschema` reader, consider implementing a new one. 37 | It should implement at least one of the following reader interfaces: 38 | * CatalogReader 39 | * SchemaReader 40 | * TableReader 41 | * ColumnReader 42 | * IndexReader 43 | * IndexColumnReader 44 | * FunctionReader 45 | * FunctionColumnReader 46 | * SequenceReader 47 | 48 | Every of these interfaces consist of a single function, that takes a `Filter` structure as an argument, 49 | and returns a set of results and an error. 50 | 51 | Example drivers using their own readers include: 52 | * `sqlite3` 53 | * `oracle` and `godror` sharing the same reader 54 | 55 | If you want to use the `informationschema` reader, but need to override one or more readers, 56 | use the `metadata.NewPluginReader(readers ...Reader)` function. It returns an object calling 57 | reader functions from the last reader passed in the arguments, that implements it. 58 | 59 | Example drivers extending an `informationschema` reader using a plugin reader: 60 | * `postgres` 61 | 62 | `\d*` commands are actually implemented by a metadata writer. There's currently only one, 63 | but it too can be replaced and/or extended. 64 | 65 | # Enabling autocomplete for a driver 66 | 67 | If a driver provides a metadata reader, the default completer will use it. 68 | A driver can provide it's own completer, by setting the `NewCompleter` property 69 | in the `drivers.Driver` structure passed to `drivers.Register()`. 70 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015-2025 Kenneth Shaw 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /contrib/adodb/adodb.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | rm -f example.csv 4 | 5 | usql "adodb://Microsoft.ACE.OLEDB.12.0/?Extended+Properties=\"Text;HDR=NO;FMT=Delimited\"" \ 6 | -c "create table example.csv(f1 text, f2 text, f3 text);" \ 7 | -c "insert into example.csv(f1, f2, f3) values ('a', 'b', 'c');" \ 8 | -c "select * from example.csv;" 9 | -------------------------------------------------------------------------------- /contrib/adodb/usql-config: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xo/usql/fe0d3cd0fc4972744460d79f936fb3fb025480c4/contrib/adodb/usql-config -------------------------------------------------------------------------------- /contrib/cassandra/podman-config: -------------------------------------------------------------------------------- 1 | NAME=cassandra 2 | IMAGE=docker.io/usql/cassandra 3 | PUBLISH=9042:9042 4 | -------------------------------------------------------------------------------- /contrib/cassandra/usql-config: -------------------------------------------------------------------------------- 1 | DB="cassandra://cassandra:cassandra@localhost" 2 | VSQL="SELECT release_version AS version FROM system.local WHERE key = 'local';" 3 | -------------------------------------------------------------------------------- /contrib/charts/area_density_stacked.vl.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://vega.github.io/schema/vega-lite/v5.json", 3 | "title": "{{ header }}", 4 | "width": 400, 5 | "height": 80, 6 | "data": { 7 | "url": "data/penguins.json" 8 | }, 9 | "mark": "area", 10 | "transform": [ 11 | { 12 | "density": "{{ x }}", 13 | "groupby": ["Species"], 14 | "extent": [2500, 6500] 15 | } 16 | ], 17 | "encoding": { 18 | "x": {"field": "value", "type": "quantitative", "title": "{{ title_x }}"}, 19 | "y": {"field": "density", "type": "quantitative", "stack": "zero"}, 20 | "color": {"field": "{{ field_x }}", "type": "nominal"} 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /contrib/charts/area_density_stacked.vl.json.svg.export.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xo/usql/fe0d3cd0fc4972744460d79f936fb3fb025480c4/contrib/charts/area_density_stacked.vl.json.svg.export.png -------------------------------------------------------------------------------- /contrib/clickhouse/podman-config: -------------------------------------------------------------------------------- 1 | NAME=clickhouse 2 | IMAGE=docker.io/clickhouse/clickhouse-server 3 | PUBLISH=9000:9000 4 | ENV="CLICKHOUSE_DEFAULT_ACCESS_MANAGEMENT=1 CLICKHOUSE_USER=clickhouse CLICKHOUSE_PASSWORD=P4ssw0rd" 5 | -------------------------------------------------------------------------------- /contrib/clickhouse/usql-config: -------------------------------------------------------------------------------- 1 | DB="clickhouse://clickhouse:P4ssw0rd@localhost" 2 | VSQL="select version() as version;" 3 | -------------------------------------------------------------------------------- /contrib/cockroach/podman-config: -------------------------------------------------------------------------------- 1 | NAME=cockroach 2 | IMAGE=docker.io/cockroachdb/cockroach:latest 3 | PUBLISH=26257:26257 4 | ENV="COCKROACH_DATABASE=cockroach COCKROACH_USER=cockroach COCKROACH_PASSWORD=P4ssw0rd" 5 | CMD=start-single-node 6 | -------------------------------------------------------------------------------- /contrib/config.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # named connections 3 | connections: 4 | my_couchbase_conn: couchbase://Administrator:P4ssw0rd@localhost 5 | my_clickhouse_conn: clickhouse://clickhouse:P4ssw0rd@localhost 6 | css: cassandra://cassandra:cassandra@localhost 7 | fsl: flightsql://flight_username:P4ssw0rd@localhost 8 | gdr: 9 | protocol: godror 10 | username: system 11 | password: P4ssw0rd 12 | hostname: localhost 13 | port: 1521 14 | database: free 15 | ign: ignite://ignite:ignite@localhost 16 | mss: sqlserver://sa:Adm1nP@ssw0rd@localhost 17 | mym: mysql://root:P4ssw0rd@localhost 18 | myz: mymysql://root:P4ssw0rd@localhost 19 | ora: oracle://system:P4ssw0rd@localhost/free 20 | ore: oracle://system:P4ssw0rd@localhost:1522/db1 21 | pgs: postgres://postgres:P4ssw0rd@localhost 22 | pgx: pgx://postgres:P4ssw0rd@localhost 23 | vrt: 24 | proto: vertica 25 | user: vertica 26 | pass: vertica 27 | host: localhost 28 | sll: 29 | file: /path/to/mydb.sqlite3 30 | mdc: modernsqlite:test.db 31 | dkd: test.duckdb 32 | zzz: ["databricks", "token:dapi*****@adb-*************.azuredatabricks.net:443/sql/protocolv1/o/*********/*******"] 33 | zz2: 34 | proto: mysql 35 | user: "my username" 36 | pass: "my password!" 37 | host: localhost 38 | opts: 39 | opt1: "😀" 40 | # init script 41 | init: | 42 | \echo welcome to the jungle `date` 43 | \set SYNTAX_HL_STYLE paraiso-dark 44 | \set PROMPT1 '\033[32m%S%M%/%R%#\033[0m ' 45 | \set bar test 46 | \set foo test 47 | -- \set SHOW_HOST_INFORMATION false 48 | -- \set SYNTAX_HL false 49 | \set 型示師 '本門台初埼本門台初埼' 50 | # charts path 51 | charts_path: charts 52 | # defined queries 53 | queries: 54 | q1: 55 | -------------------------------------------------------------------------------- /contrib/couchbase/README.md: -------------------------------------------------------------------------------- 1 | # Couchbase Notes 2 | 3 | ```sh 4 | $ podman volume create couchbase-data 5 | ``` 6 | 7 | After running the docker image, browse to http://127.0.0.1:8091/ui/index.html 8 | and manually configure database. 9 | -------------------------------------------------------------------------------- /contrib/couchbase/podman-config: -------------------------------------------------------------------------------- 1 | NAME=couchbase 2 | IMAGE=docker.io/library/couchbase 3 | PUBLISH=8091-8094:8091-8094 4 | VOLUME=couchbase-data:/opt/couchbase/var 5 | -------------------------------------------------------------------------------- /contrib/couchbase/usql-config: -------------------------------------------------------------------------------- 1 | # NOTE: this will only work after setting up a database on http://localhost:8091/ 2 | DB="couchbase://Administrator:P4ssw0rd@localhost" 3 | VSQL="select raw ds_version() as version;" 4 | -------------------------------------------------------------------------------- /contrib/db2/README.md: -------------------------------------------------------------------------------- 1 | # db2 Notes 2 | 3 | 1. Install unixodbc: 4 | 5 | ```sh 6 | $ sudo aptitude install unixodbc unixodbc-bin unixodbc-dev 7 | $ yay -S unixodbc 8 | ``` 9 | 10 | 2. Download `dsdriver` and install: 11 | 12 | ```sh 13 | $ ls ~/Downloads/ibm_data_server_driver_package_linuxx64_v11.5.tar.gz 14 | /home/ken/Downloads/ibm_data_server_driver_package_linuxx64_v11.5.tar.gz 15 | $ sudo ./install-dsdriver.sh 16 | ``` 17 | 18 | 3. Copy ODBC and CLI configs: 19 | 20 | ```sh 21 | $ cat odbcinst.ini | sudo tee -a /etc/odbcinst.ini 22 | $ sudo cp {db2cli.ini,db2dsdriver.cfg} /opt/db2/clidriver/cfg/ 23 | ``` 24 | 25 | 4. Run DB2 container: 26 | 27 | ```sh 28 | $ ../podman-run.sh db2 -u 29 | ``` 30 | 31 | 5. Verify DB2 working: 32 | 33 | ```sh 34 | $ ./db2cli-validate.sh 35 | ``` 36 | -------------------------------------------------------------------------------- /contrib/db2/db2cli-validate.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # see https://www.ibm.com/developerworks/community/blogs/ff78a96f-bf23-457e-befa-77f266844cbb/entry/db2cli_validate_command_line_tool_for_validating_and_testing_cli_environment_and_configuration?lang=en 4 | # see https://web.archive.org/web/20240715121603/https://blogs.sas.com/content/sgf/2017/11/16/connecting-sas-db2-database-via-odbc-without-tears/ 5 | 6 | CLIDRIVER=${1:-/opt/db2/clidriver} 7 | 8 | export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$CLIDRIVER/lib 9 | export DB2CLIINIPATH=$CLIDRIVER/cfg 10 | export DB2DSDRIVER_CFG_PATH=$CLIDRIVER/cfg 11 | 12 | $CLIDRIVER/bin/db2cli validate -dsn SAMPLE -connect -user db2inst1 -passwd P4ssw0rd 13 | -------------------------------------------------------------------------------- /contrib/db2/db2cli.ini: -------------------------------------------------------------------------------- 1 | [sample] 2 | Database = testdb 3 | Protocol = TCPIP 4 | Hostname = localhost 5 | ServiceName = 50000 6 | -------------------------------------------------------------------------------- /contrib/db2/db2dsdriver.cfg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /contrib/db2/install-dsdriver.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | DEST=${1:-/opt/db2} 4 | FILE=$2 5 | 6 | if [ ! -w $DEST ]; then 7 | echo "ERROR: not able to write to $DEST" 8 | exit 1 9 | fi 10 | 11 | echo "DEST: $DEST" 12 | 13 | if [ ! -e "$DEST" ]; then 14 | echo "$DEST does not exist" 15 | exit 1 16 | fi 17 | 18 | if [ -z "$FILE" ]; then 19 | FILE=$(ls $HOME/Downloads/ibm_data_server_driver_package_linuxx64_*.tar.gz||:) 20 | fi 21 | 22 | if [ -z "$FILE" ]; then 23 | echo "cannot find driver package to extract" 24 | exit 1 25 | fi 26 | 27 | set -e 28 | 29 | USER=$(whoami) 30 | 31 | # extract 32 | pushd $DEST &> /dev/null 33 | echo "EXTRACTING: $FILE" 34 | 35 | # extract 36 | tar -zxf $FILE 37 | tar -zxf dsdriver/odbc_cli_driver/linuxamd64/ibm_data_server_driver_for_odbc_cli.tar.gz 38 | 39 | # fix permissions 40 | chown $USER:$USER -R . 41 | find ./ -type d -exec chmod 0755 {} \; 42 | find ./ -type d -exec chmod -s {} \; 43 | 44 | popd &> /dev/null 45 | -------------------------------------------------------------------------------- /contrib/db2/odbcinst.ini: -------------------------------------------------------------------------------- 1 | [DB2] 2 | Description=DB2 driver 3 | Driver=/opt/db2/clidriver/lib/libdb2.so 4 | FileUsage = 1 5 | DontDLClose = 1 6 | -------------------------------------------------------------------------------- /contrib/db2/podman-config: -------------------------------------------------------------------------------- 1 | NAME=db2 2 | IMAGE=icr.io/db2_community/db2 3 | PUBLISH="50000:50000 55000:55000" 4 | ENV="LICENSE=accept DB2INSTANCE=db2inst1 DB2INST1_PASSWORD=P4ssw0rd DBNAME=testdb" 5 | VOLUME=db2-data:/database 6 | -------------------------------------------------------------------------------- /contrib/db2/test.sql: -------------------------------------------------------------------------------- 1 | \connect odbc+db2://db2inst1:P4ssw0rd@localhost/testdb 2 | 3 | create schema test; 4 | 5 | create table test.mytable ( 6 | COL1 INTEGER NOT NULL, 7 | COL2 CHAR(25), 8 | COL3 VARCHAR(25) NOT NULL, 9 | COL4 DATE, 10 | COL5 DECIMAL(10,2), 11 | PRIMARY KEY (COL1), 12 | UNIQUE (COL3) 13 | ); 14 | 15 | insert into test.mytable 16 | (col1, col2, col3, col4, col5) 17 | values 18 | (1, 'a', 'first', current date, 15.0), 19 | (2, 'b', 'second', current date, 16.0), 20 | (3, 'c', 'third', current date, 17.0) 21 | ; 22 | 23 | select * from test.mytable; 24 | -------------------------------------------------------------------------------- /contrib/db2/usql-config: -------------------------------------------------------------------------------- 1 | DB="odbc+db2://db2inst1:P4ssw0rd@localhost/testdb" 2 | VSQL="SELECT service_level AS version FROM sysibmadm.env_inst_info;" 3 | -------------------------------------------------------------------------------- /contrib/duckdb/usql-config: -------------------------------------------------------------------------------- 1 | DB="duckdb:test.duckdb" 2 | VSQL="SELECT library_version AS version FROM pragma_version();" 3 | -------------------------------------------------------------------------------- /contrib/exasol/podman-config: -------------------------------------------------------------------------------- 1 | NAME=exasol 2 | IMAGE=docker.io/exasol/docker-db 3 | PUBLISH=8563:8563 4 | -------------------------------------------------------------------------------- /contrib/exasol/usql-config: -------------------------------------------------------------------------------- 1 | DB="exasol://sys:exasol@localhost/?encryption=0" 2 | VSQL="SELECT param_value AS version FROM exa_metadata WHERE param_name = 'databaseProductVersion';" 3 | -------------------------------------------------------------------------------- /contrib/firebird/podman-config: -------------------------------------------------------------------------------- 1 | NAME=firebird 2 | IMAGE=docker.io/jacobalberty/firebird 3 | PUBLISH=3050:3050 4 | ENV="FIREBIRD_DATABASE=booktest FIREBIRD_USER=booktest FIREBIRD_PASSWORD=booktest" 5 | -------------------------------------------------------------------------------- /contrib/firebird/usql-config: -------------------------------------------------------------------------------- 1 | DB="firebird://booktest:booktest@localhost/booktest" 2 | VSQL="SELECT rdb\$get_context('SYSTEM', 'ENGINE_VERSION') AS version FROM rdb\$database;" 3 | -------------------------------------------------------------------------------- /contrib/flightsql/podman-config: -------------------------------------------------------------------------------- 1 | NAME=flightsql 2 | IMAGE=docker.io/voltrondata/flight-sql 3 | PUBLISH=31337:31337 4 | ENV="FLIGHT_PASSWORD=P4ssw0rd" 5 | -------------------------------------------------------------------------------- /contrib/flightsql/usql-config: -------------------------------------------------------------------------------- 1 | DB="flightsql://flight_username:P4ssw0rd@localhost:31337?tls=skip-verify" 2 | VSQL="SELECT version() AS version;" 3 | -------------------------------------------------------------------------------- /contrib/go-setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # trimmed down version of: 4 | # https://github.com/kenshaw/shell-config/blob/master/scripts/go-setup.sh 5 | 6 | ARCH=$(uname -m) 7 | PLATFORM=linux 8 | 9 | case $ARCH in 10 | aarch64) ARCH=arm64 ;; 11 | x86_64) ARCH=amd64 ;; 12 | esac 13 | 14 | REPO=https://go.googlesource.com/go 15 | DL=https://go.dev/dl/ 16 | EXT=tar.gz 17 | 18 | DEST=/usr/local 19 | 20 | set -e 21 | 22 | LATEST=$(curl -4 -s "$DL"|sed -E -n "/go1\.[0-9]+(\.[0-9]+)?\.$PLATFORM-$ARCH\.$EXT(.+?)<\/a.*/\1/' <<< "$LATEST") 24 | STABLE=$(sed -E -e 's/^go//' -e "s/\.$PLATFORM-$ARCH\.$EXT$//" <<< "$ARCHIVE") 25 | 26 | if ! [[ "$STABLE" =~ ^1\.[0-9\.]+$ ]]; then 27 | echo "ERROR: unable to retrieve latest Go version for $PLATFORM/$ARCH ($STABLE)" 28 | exit 1 29 | fi 30 | 31 | REMOTE=$(sed -E -e 's/.* $2" 62 | curl -4 -L -# -o $2 $1 63 | } 64 | 65 | # extract 66 | WORKDIR=$(mktemp -d /tmp/go-setup.XXXX) 67 | grab $REMOTE $WORKDIR/$ARCHIVE 68 | echo "USING: $WORKDIR/$ARCHIVE" 69 | 70 | pushd $WORKDIR &> /dev/null 71 | case $EXT in 72 | tar.gz) tar -zxf $ARCHIVE ;; 73 | zip) unzip -q $ARCHIVE ;; 74 | *) 75 | echo "ERROR: unknown extension $EXT" 76 | exit 77 | ;; 78 | esac 79 | 80 | echo "MOVING: $WORKDIR/go -> $DEST/go" 81 | mv go $DEST/go 82 | 83 | chown -R root:root $DEST/go 84 | 85 | echo "INSTALLED: $($DEST/go/bin/go version)" 86 | -------------------------------------------------------------------------------- /contrib/godror/fix-oob-config.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # adds DISABLE_OOB=on to user's .sqlnet.ora config 4 | # 5 | # See: 6 | # https://github.com/oracle/docker-images/issues/1352 7 | # https://franckpachot.medium.com/19c-instant-client-and-docker-1566630ab20e 8 | 9 | echo "DISABLE_OOB=ON" >> $HOME/.sqlnet.ora 10 | -------------------------------------------------------------------------------- /contrib/godror/grab-instantclient.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | DEST=${1:-/opt/oracle} 4 | 5 | # available versions: 6 | # 21.7.0.0.0 7 | # 21.6.0.0.0 8 | # 21.1.0.0.0 9 | # 19.9.0.0.0 10 | # 18.5.0.0.0 11 | # 12.2.0.1.0 12 | 13 | VERSION= 14 | 15 | OPTIND=1 16 | while getopts "v:" opt; do 17 | case "$opt" in 18 | v) VERSION=$OPTARG ;; 19 | esac 20 | done 21 | 22 | if [ -z "$VERSION" ]; then 23 | VERSION=$( 24 | wget --quiet -O- https://www.oracle.com/database/technologies/instant-client/linux-x86-64-downloads.html| \ 25 | sed -n -e 's/.*\/instantclient-basic-linux\.x64-\([^d]\+\)dbru\.zip.*/\1/p' | \ 26 | head -1 27 | ) 28 | fi 29 | 30 | if [[ ! "$VERSION" =~ ^[0-9\.]+$ ]]; then 31 | echo "error: invalid VERSION" 32 | exit 1 33 | fi 34 | 35 | BASE=https://download.oracle.com/otn_software/linux/instantclient/$(sed -e 's/[^0-9]//g' <<< "$VERSION") 36 | 37 | # build list of archives to retrieve 38 | declare -a ARCHIVES 39 | for i in basic sdk sqlplus; do 40 | ARCHIVES+=("$BASE/instantclient-$i-linux.x64-${VERSION}dbru.zip") 41 | done 42 | 43 | grab() { 44 | echo -n "RETRIEVING: $1 -> $2 " 45 | wget --progress=dot -O $2 $1 2>&1 |\ 46 | grep --line-buffered "%" | \ 47 | sed -u -e "s,\.,,g" | \ 48 | awk '{printf("\b\b\b\b%4s", $2)}' 49 | echo -ne "\b\b\b\b" 50 | echo " DONE." 51 | } 52 | 53 | cache() { 54 | FILE=$(basename $2) 55 | if [ ! -f $1/$FILE ]; then 56 | grab $2 $1/$FILE 57 | fi 58 | } 59 | 60 | set -e 61 | 62 | echo "DEST: $DEST" 63 | if [ ! -w $DEST ]; then 64 | echo "$DEST is not writable!" 65 | exit 1 66 | fi 67 | if [ ! -e "$DEST" ]; then 68 | echo "$DEST does not exist!" 69 | exit 1 70 | fi 71 | 72 | # retrieve archives 73 | for i in ${ARCHIVES[@]}; do 74 | cache $DEST $i 75 | done 76 | 77 | # remove existing directory, if any 78 | DVER=$(awk -F. '{print $1 "_" $2}' <<< "$VERSION") 79 | if [ -e $DEST/instantclient_$DVER ]; then 80 | echo "REMOVING: $DEST/instantclient_$DVER" 81 | rm -rf $DEST/instantclient_$DVER 82 | fi 83 | 84 | # extract 85 | pushd $DEST &> /dev/null 86 | for i in ${ARCHIVES[@]}; do 87 | unzip -qq $(basename $i) 88 | done 89 | popd &> /dev/null 90 | 91 | # write pkg-config file 92 | DATA=$(cat < $DEST/oci8.pc 110 | rm -f /etc/ld.so.conf.d/oracle-instantclient.conf 111 | echo "$DEST/instantclient_$DVER" | tee -a /etc/ld.so.conf.d/oracle-instantclient.conf 112 | ldconfig -v 113 | 114 | # write sqlnet.ora 115 | DATA=$(cat < $DEST/instantclient_${DVER}/network/admin/sqlnet.ora 125 | -------------------------------------------------------------------------------- /contrib/godror/usql-config: -------------------------------------------------------------------------------- 1 | DB="godror://system:P4ssw0rd@localhost/orasid" 2 | VSQL="SELECT version FROM v\$instance" 3 | -------------------------------------------------------------------------------- /contrib/h2/podman-config: -------------------------------------------------------------------------------- 1 | NAME=h2 2 | IMAGE=docker.io/buildo/h2database 3 | PUBLISH="8082:8082 9092:9092" 4 | -------------------------------------------------------------------------------- /contrib/hive/podman-config: -------------------------------------------------------------------------------- 1 | NAME=hive 2 | IMAGE=docker.io/apache/hive:4.0.0-beta-1 3 | PUBLISH="10000:10000 10002:10002" 4 | ENV="SERVICE_NAME=hiveserver2" 5 | -------------------------------------------------------------------------------- /contrib/hive/usql-config: -------------------------------------------------------------------------------- 1 | DB="hive://user:pass@localhost" 2 | VSQL="SELECT version() AS version;" 3 | -------------------------------------------------------------------------------- /contrib/ignite/README.md: -------------------------------------------------------------------------------- 1 | # Ignite Notes 2 | 3 | After starting the database, run `activate.sh`: 4 | 5 | ```sh 6 | $ ./activate.sh 7 | ``` 8 | -------------------------------------------------------------------------------- /contrib/ignite/activate.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | docker exec -it ignite \ 4 | /opt/ignite/apache-ignite/bin/control.sh \ 5 | --activate \ 6 | --user ignite \ 7 | --password ignite 8 | -------------------------------------------------------------------------------- /contrib/ignite/podman-config: -------------------------------------------------------------------------------- 1 | NAME=ignite 2 | IMAGE=docker.io/usql/ignite 3 | PUBLISH=10800:10800 4 | NETWORK=host 5 | -------------------------------------------------------------------------------- /contrib/ignite/usql-config: -------------------------------------------------------------------------------- 1 | DB="ignite://ignite:ignite@localhost/ExampleDB" 2 | -------------------------------------------------------------------------------- /contrib/mymysql/usql-config: -------------------------------------------------------------------------------- 1 | DB="mymysql://root:P4ssw0rd@localhost/" 2 | VSQL="SELECT version() AS version;" 3 | -------------------------------------------------------------------------------- /contrib/mysql/podman-config: -------------------------------------------------------------------------------- 1 | NAME=mysql 2 | IMAGE=docker.io/library/mariadb 3 | PUBLISH=3306:3306 4 | ENV="MYSQL_ROOT_PASSWORD=P4ssw0rd" 5 | -------------------------------------------------------------------------------- /contrib/mysql/test.sql: -------------------------------------------------------------------------------- 1 | -- mysql test script 2 | 3 | \set 4 | 5 | \set SYNTAX_HL_FORMAT terminal16m 6 | \set SYNTAX_HL true 7 | 8 | \? 9 | 10 | \copyright 11 | 12 | \set SYNTAX_HL_STYLE dracula 13 | 14 | select 'test'' 15 | ' \g 16 | 17 | \set NAME myname 18 | 19 | drop database if exists testdb; create database testdb; use testdb; 20 | 21 | SET FOREIGN_KEY_CHECKS=0; 22 | DROP TABLE IF EXISTS authors; 23 | DROP TABLE IF EXISTS books; 24 | DROP FUNCTION IF EXISTS say_hello; 25 | SET FOREIGN_KEY_CHECKS=1; 26 | 27 | CREATE TABLE authors ( 28 | author_id integer NOT NULL AUTO_INCREMENT PRIMARY KEY, 29 | name text NOT NULL DEFAULT '' 30 | ) ENGINE=InnoDB; 31 | 32 | CREATE INDEX authors_name_idx ON authors(name(255)); 33 | 34 | \set SYNTAX_HL_STYLE paraiso-dark 35 | 36 | CREATE TABLE books ( 37 | /* 38 | this is a multiline comment 39 | */ 40 | book_id integer NOT NULL AUTO_INCREMENT PRIMARY KEY, 41 | author_id integer NOT NULL, 42 | isbn varchar(255) NOT NULL DEFAULT '' UNIQUE, 43 | book_type ENUM('FICTION', 'NONFICTION') NOT NULL DEFAULT 'FICTION', 44 | title text NOT NULL DEFAULT '', 45 | year integer NOT NULL DEFAULT 2000, 46 | available datetime NOT NULL DEFAULT NOW(), 47 | tags text NOT NULL DEFAULT '', 48 | CONSTRAINT FOREIGN KEY (author_id) REFERENCES authors(author_id) 49 | ) ENGINE=InnoDB; 50 | 51 | CREATE INDEX books_title_idx ON books(title, year); 52 | 53 | insert into authors (name) values 54 | ('jk rowling'), 55 | ('author amazing') 56 | \g 57 | 58 | select * from authors; 59 | 60 | \set COLNAME name 61 | \set NAME amaz 62 | 63 | \echo `echo hello` 64 | 65 | select :"COLNAME" from authors where :COLNAME like '%' || :'NAME' || '%' 66 | 67 | \print \raw 68 | 69 | \g 70 | 71 | \gset AUTHOR_ 72 | 73 | select :'AUTHOR_name'; 74 | 75 | \begin 76 | insert into authors (name) values ('test'); 77 | \rollback 78 | 79 | insert into authors (name) values ('hello'); 80 | select * from authors; 81 | 82 | insert into books (author_id, isbn, title, year, available) values 83 | (1, '1', 'one', 2018, '2018-06-01 00:00:00'), 84 | (2, '2', 'two', 2019, '2019-06-01 00:00:00') 85 | ; 86 | 87 | select * from books b inner join authors a on a.author_id = b.author_id; 88 | 89 | CREATE FUNCTION say_hello(s text) RETURNS text 90 | DETERMINISTIC 91 | RETURN CONCAT('hello ', s); 92 | 93 | select say_hello('a name!') \G 94 | 95 | /* exiting! */ 96 | \q 97 | -------------------------------------------------------------------------------- /contrib/mysql/usql-config: -------------------------------------------------------------------------------- 1 | DB="mysql://root:P4ssw0rd@localhost/" 2 | VSQL="SELECT version() AS version;" 3 | -------------------------------------------------------------------------------- /contrib/oracle-enterprise/podman-config: -------------------------------------------------------------------------------- 1 | NAME=oracle-enterprise 2 | IMAGE=container-registry.oracle.com/database/enterprise:21.3.0.0 3 | PUBLISH=1522:1521 4 | ENV="ORACLE_PDB=db1 ORACLE_PWD=P4ssw0rd" 5 | VOLUME=oracle-enterprise-data:/opt/oracle/oradata 6 | -------------------------------------------------------------------------------- /contrib/oracle-enterprise/usql-config: -------------------------------------------------------------------------------- 1 | DB="oracle://system:P4ssw0rd@localhost:1522/db1" 2 | VSQL="SELECT version FROM v\$instance" 3 | -------------------------------------------------------------------------------- /contrib/oracle/init.sql: -------------------------------------------------------------------------------- 1 | \set ORACLE_USER system 2 | \set ORACLE_PASS oracle 3 | \set ORACLE_SVC xe 4 | \set ORACLE_HOST `docker port oracle 1521` 5 | 6 | \prompt NAME 'Create database user: ' 7 | \prompt -password PASS 'Password for "':NAME'": ' 8 | 9 | \connect 'oracle://':ORACLE_USER':':ORACLE_PASS'@':ORACLE_HOST'/':ORACLE_SVC 10 | 11 | \set DATNAME :NAME.dat 12 | 13 | CREATE 14 | TABLESPACE :NAME 15 | NOLOGGING 16 | DATAFILE :'DATNAME' 17 | SIZE 100m 18 | AUTOEXTEND ON; 19 | 20 | CREATE 21 | USER :NAME 22 | IDENTIFIED BY :NAME 23 | DEFAULT TABLESPACE :NAME; 24 | 25 | GRANT 26 | CREATE SESSION, 27 | CREATE TABLE, 28 | CREATE VIEW, 29 | CREATE SEQUENCE, 30 | CREATE PROCEDURE, 31 | CREATE TRIGGER, 32 | UNLIMITED TABLESPACE, 33 | SELECT ANY DICTIONARY 34 | TO :NAME; 35 | 36 | ALTER SYSTEM 37 | SET OPEN_CURSORS=400 38 | SCOPE=both; 39 | -------------------------------------------------------------------------------- /contrib/oracle/podman-config: -------------------------------------------------------------------------------- 1 | NAME=oracle 2 | IMAGE=container-registry.oracle.com/database/free 3 | PUBLISH=1521:1521 4 | ENV="ORACLE_PDB=db1 ORACLE_PWD=P4ssw0rd" 5 | VOLUME=oracle-free-data:/opt/oracle/oradata 6 | -------------------------------------------------------------------------------- /contrib/oracle/usql-config: -------------------------------------------------------------------------------- 1 | DB="oracle://system:P4ssw0rd@localhost/free" 2 | VSQL="SELECT version FROM v\$instance" 3 | -------------------------------------------------------------------------------- /contrib/pgx/usql-config: -------------------------------------------------------------------------------- 1 | DB="pgx://postgres:P4ssw0rd@localhost" 2 | VSQL="SELECT setting AS version FROM pg_settings WHERE name='server_version';" 3 | -------------------------------------------------------------------------------- /contrib/podman-run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # podman-run.sh: starts or restarts podman containers. 4 | # 5 | # Usage: podman-run.sh [-u] 6 | # 7 | # Where is a name of a subdirectory containing podman-config, 8 | # 'all', or 'test'. 9 | # 10 | # all -- starts all available database images. 11 | # test -- starts the primary testing images. The testing images are cassandra, mysql, postgres, sqlserver, and oracle 12 | # -u -- perform podman pull for images prior to start. 13 | # 14 | # Will stop any running podman container prior to starting. 15 | 16 | DIR=$1 17 | 18 | SRC=$(realpath $(cd -P "$(dirname "${BASH_SOURCE[0]}" )" && pwd)) 19 | 20 | if [ -z "$DIR" ]; then 21 | echo "usage: $0 [-u]" 22 | exit 1 23 | fi 24 | 25 | shift 26 | 27 | UPDATE=0 28 | 29 | OPTIND=1 30 | while getopts "u" opt; do 31 | case "$opt" in 32 | u) UPDATE=1 ;; 33 | esac 34 | done 35 | 36 | podman_run() { 37 | TARGET=$1 38 | BASE=$SRC/$TARGET 39 | if [ ! -e $BASE/podman-config ]; then 40 | echo "error: $BASE/podman-config doesn't exist" 41 | exit 1 42 | fi 43 | 44 | # load parameters from podman-config 45 | unset IMAGE NAME PUBLISH ENV VOLUME NETWORK PRIVILEGED HOSTNAME PARAMS CMD 46 | source $BASE/podman-config 47 | if [[ "$TARGET" != "$NAME" ]]; then 48 | echo "error: $BASE/podman-config is invalid" 49 | exit 1 50 | fi 51 | 52 | # default network settings 53 | if [ -z "$NETWORK" ]; then 54 | NETWORK=slirp4netns 55 | fi 56 | 57 | # setup params 58 | PARAMS=() 59 | for k in NAME PUBLISH ENV VOLUME NETWORK PRIVILEGED HOSTNAME; do 60 | n=$(tr 'A-Z' 'a-z' <<< "$k") 61 | v=$(eval echo "\$$k") 62 | if [ ! -z "$v" ]; then 63 | for p in $v; do 64 | PARAMS+=("--$n=$p") 65 | done 66 | fi 67 | done 68 | 69 | # determine if image exists 70 | EXISTS=$(podman image ls -q $IMAGE) 71 | if [[ "$UPDATE" == "0" && -z "$EXISTS" ]]; then 72 | UPDATE=1 73 | fi 74 | 75 | # show parameters 76 | echo "-------------------------------------------" 77 | echo "NAME: $NAME" 78 | echo "IMAGE: $IMAGE (update: $UPDATE)" 79 | echo "PUBLISH: $PUBLISH" 80 | echo "ENV: $ENV" 81 | echo "VOLUME: $VOLUME" 82 | echo "NETWORK: $NETWORK" 83 | echo "PRIVILEGED: $PRIVILEGED" 84 | echo "HOSTNAME: $HOSTNAME" 85 | echo "CMD: $CMD" 86 | echo 87 | 88 | # update 89 | if [[ "$UPDATE" == "1" ]]; then 90 | if [ ! -f $BASE/Dockerfile ]; then 91 | (set -ex; 92 | podman pull $IMAGE 93 | ) 94 | else 95 | pushd $BASE &> /dev/null 96 | (set -ex; 97 | podman build --pull -t $IMAGE:latest . 98 | ) 99 | popd &> /dev/null 100 | fi 101 | REF=$(awk -F: '{print $1}' <<< "$IMAGE") 102 | REMOVE=$(podman image list --filter=dangling=true --filter=reference=$IMAGE -q) 103 | if [ ! -z "$REMOVE" ]; then 104 | (set -ex; 105 | podman image rm -f $REMOVE 106 | ) 107 | fi 108 | fi 109 | 110 | # stop and remove 111 | if [ ! -z "$(podman ps -q --filter "name=$NAME")" ]; then 112 | (set -x; 113 | podman stop $NAME 114 | ) 115 | fi 116 | if [ ! -z "$(podman ps -q -a --filter "name=$NAME")" ]; then 117 | (set -x; 118 | podman rm -f $NAME 119 | ) 120 | fi 121 | 122 | # start 123 | (set -ex; 124 | podman run --detach --rm ${PARAMS[@]} $IMAGE $CMD 125 | ) 126 | echo 127 | } 128 | 129 | pushd $SRC &> /dev/null 130 | TARGETS=() 131 | case $DIR in 132 | all) 133 | TARGETS+=($(find . -type f -name podman-config|awk -F'/' '{print $2}')) 134 | ;; 135 | test) 136 | TARGETS+=(mysql postgres sqlserver oracle clickhouse cassandra) 137 | ;; 138 | *) 139 | TARGETS+=($DIR) 140 | ;; 141 | esac 142 | 143 | for TARGET in ${TARGETS[@]}; do 144 | podman_run $TARGET 145 | done 146 | popd &> /dev/null 147 | -------------------------------------------------------------------------------- /contrib/podman-stop.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SRC=$(realpath $(cd -P "$(dirname "${BASH_SOURCE[0]}" )" && pwd)) 4 | 5 | for TARGET in $SRC/*/podman-config; do 6 | NAME=$(basename $(dirname $TARGET)) 7 | if [ ! -z "$(podman ps -q --filter "name=$NAME")" ]; then 8 | (set -x; 9 | podman stop $NAME 10 | ) 11 | fi 12 | if [ ! -z "$(podman ps -q -a --filter "name=$NAME")" ]; then 13 | (set -x; 14 | podman rm -f $NAME 15 | ) 16 | fi 17 | done 18 | -------------------------------------------------------------------------------- /contrib/postgres/init.sql: -------------------------------------------------------------------------------- 1 | \set POSTGRES_USER postgres 2 | \set POSTGRES_PASS P4ssw0rd 3 | \set POSTGRES_DB postgres 4 | \set POSTGRES_HOST `docker port postgres 5432 | head -n1` 5 | 6 | \prompt NAME 'Create database user: ' 7 | \prompt -password PASS 'Password for "':NAME'": ' 8 | 9 | \connect 'postgres://':POSTGRES_USER':':POSTGRES_PASS'@':POSTGRES_HOST'/':POSTGRES_DB'?sslmode=disable' 10 | 11 | DROP USER IF EXISTS :NAME; 12 | 13 | CREATE USER :NAME PASSWORD :'PASS'; 14 | 15 | DROP DATABASE IF EXISTS :NAME; 16 | 17 | CREATE DATABASE :NAME OWNER :NAME; 18 | -------------------------------------------------------------------------------- /contrib/postgres/odbcinst.ini: -------------------------------------------------------------------------------- 1 | [PostgreSQL ANSI] 2 | Description=PostgreSQL ODBC driver (ANSI version) 3 | Driver=psqlodbca.so 4 | Setup=libodbcpsqlS.so 5 | Debug=0 6 | CommLog=1 7 | UsageCount=1 8 | 9 | [PostgreSQL Unicode] 10 | Description=PostgreSQL ODBC driver (Unicode version) 11 | Driver=psqlodbcw.so 12 | Setup=libodbcpsqlS.so 13 | Debug=0 14 | CommLog=1 15 | UsageCount=1 16 | -------------------------------------------------------------------------------- /contrib/postgres/podman-config: -------------------------------------------------------------------------------- 1 | NAME=postgres 2 | IMAGE=docker.io/usql/postgres 3 | PUBLISH=5432:5432 4 | ENV="POSTGRES_PASSWORD=P4ssw0rd" 5 | -------------------------------------------------------------------------------- /contrib/postgres/schema.sql: -------------------------------------------------------------------------------- 1 | \connect postgres://booktest:booktest@localhost/ 2 | 3 | DROP TABLE IF EXISTS books CASCADE; 4 | DROP TYPE IF EXISTS book_type CASCADE; 5 | DROP TABLE IF EXISTS authors CASCADE; 6 | DROP FUNCTION IF EXISTS say_hello(text) CASCADE; 7 | 8 | CREATE TABLE authors ( 9 | author_id SERIAL PRIMARY KEY, 10 | name text NOT NULL DEFAULT '' 11 | ); 12 | 13 | CREATE INDEX authors_name_idx ON authors(name); 14 | 15 | CREATE TYPE book_type AS ENUM ( 16 | 'FICTION', 17 | 'NONFICTION' 18 | ); 19 | 20 | CREATE TABLE books ( 21 | book_id SERIAL PRIMARY KEY, 22 | author_id integer NOT NULL REFERENCES authors(author_id), 23 | isbn text NOT NULL DEFAULT '' UNIQUE, 24 | booktype book_type NOT NULL DEFAULT 'FICTION', 25 | title text NOT NULL DEFAULT '', 26 | year integer NOT NULL DEFAULT 2000, 27 | available timestamp with time zone NOT NULL DEFAULT 'NOW()', 28 | tags varchar[] NOT NULL DEFAULT '{}' 29 | ); 30 | 31 | CREATE INDEX books_title_idx ON books(title, year); 32 | 33 | CREATE FUNCTION say_hello(text) RETURNS text AS $$ 34 | BEGIN 35 | RETURN CONCAT('hello ', $1); 36 | END; 37 | $$ LANGUAGE plpgsql; 38 | 39 | CREATE INDEX books_title_lower_idx ON books(title); 40 | -------------------------------------------------------------------------------- /contrib/postgres/test.sql: -------------------------------------------------------------------------------- 1 | -- postgres test script 2 | 3 | \set 4 | 5 | \set SYNTAX_HL_FORMAT terminal16m 6 | \set SYNTAX_HL true 7 | 8 | \? 9 | 10 | \copyright 11 | 12 | \set SYNTAX_HL_STYLE dracula 13 | 14 | select 'test'' 15 | ' \g 16 | 17 | \set NAME myname 18 | 19 | DROP TABLE IF EXISTS books; 20 | DROP TABLE IF EXISTS authors; 21 | 22 | DROP TABLE IF EXISTS books CASCADE; 23 | DROP TYPE IF EXISTS book_type CASCADE; 24 | DROP TABLE IF EXISTS authors CASCADE; 25 | DROP FUNCTION IF EXISTS say_hello(text) CASCADE; 26 | 27 | CREATE TABLE authors ( 28 | author_id SERIAL PRIMARY KEY, 29 | name text NOT NULL DEFAULT '' 30 | ); 31 | CREATE INDEX authors_name_idx ON authors(name); 32 | CREATE TYPE book_type AS ENUM ( 33 | 'FICTION', 34 | 'NONFICTION' 35 | ); 36 | 37 | CREATE INDEX authors_name_idx ON authors(name); 38 | 39 | \set SYNTAX_HL_STYLE paraiso-dark 40 | 41 | CREATE TABLE books ( 42 | /* 43 | this is a multiline comment 44 | */ 45 | book_id SERIAL PRIMARY KEY, 46 | author_id integer NOT NULL REFERENCES authors(author_id), 47 | isbn text NOT NULL DEFAULT '' UNIQUE, 48 | booktype book_type NOT NULL DEFAULT 'FICTION', 49 | title text NOT NULL DEFAULT '', 50 | year integer NOT NULL DEFAULT 2000, 51 | available timestamp with time zone NOT NULL DEFAULT 'NOW()', 52 | tags varchar[] NOT NULL DEFAULT '{}' 53 | ); 54 | 55 | CREATE INDEX books_title_idx ON books(title, year); 56 | 57 | insert into authors (name) values 58 | ('jk rowling'), 59 | ('author amazing') 60 | \g 61 | 62 | select * from authors; 63 | 64 | \set COLNAME name 65 | \set NAME amaz 66 | 67 | \echo `echo hello` 68 | 69 | select :"COLNAME" from authors where :COLNAME like '%' || :'NAME' || '%' 70 | 71 | \print \raw 72 | 73 | \g 74 | 75 | \gset AUTHOR_ 76 | 77 | select :'AUTHOR_name'; 78 | 79 | \begin 80 | insert into authors (name) values ('test'); 81 | \rollback 82 | 83 | insert into authors (name) values ('hello'); 84 | select * from authors; 85 | 86 | insert into books (author_id, isbn, title, year, available) values 87 | (1, '1', 'one', 2018, '2018-06-01 00:00:00'), 88 | (2, '2', 'two', 2019, '2019-06-01 00:00:00') 89 | ; 90 | 91 | select * from books b inner join authors a on a.author_id = b.author_id; 92 | 93 | CREATE FUNCTION say_hello(text) RETURNS text AS $$ 94 | BEGIN 95 | RETURN CONCAT('hello ', $1); 96 | END; 97 | $$ LANGUAGE plpgsql; 98 | 99 | select say_hello('a name!') \G 100 | 101 | /* exiting! */ 102 | \q 103 | -------------------------------------------------------------------------------- /contrib/postgres/usql-config: -------------------------------------------------------------------------------- 1 | DB="postgres://postgres:P4ssw0rd@localhost" 2 | VSQL="SELECT setting AS version FROM pg_settings WHERE name='server_version';" 3 | -------------------------------------------------------------------------------- /contrib/presto/podman-config: -------------------------------------------------------------------------------- 1 | NAME=presto 2 | IMAGE=docker.io/ahanaio/prestodb-sandbox 3 | PUBLISH=8080:8080 4 | -------------------------------------------------------------------------------- /contrib/presto/usql-config: -------------------------------------------------------------------------------- 1 | DB="presto://localhost" 2 | VSQL="SELECT node_version AS version FROM system.runtime.nodes LIMIT 1;" 3 | -------------------------------------------------------------------------------- /contrib/sqlite3/build-windows-icu.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ../source/runConfigureICU \ 4 | MinGW \ 5 | --host=x86_64-w64-mingw32 \ 6 | --disable-release \ 7 | --disable-debug \ 8 | --enable-static \ 9 | --prefix=/opt/local 10 | -------------------------------------------------------------------------------- /contrib/sqlite3/icu-i18n-mingw64.pc: -------------------------------------------------------------------------------- 1 | mingw64_prefix=C:\msys64\opt\local 2 | 3 | includedir="${mingw64_prefix}\include" 4 | libdir="${mingw64_prefix}\lib" 5 | 6 | Name: icu-i18n-mingw64 7 | Version: dev 8 | Description: icu-i18n 9 | Cflags: -I${includedir} 10 | Libs: -L${libdir} 11 | -------------------------------------------------------------------------------- /contrib/sqlite3/test.sql: -------------------------------------------------------------------------------- 1 | -- sqlite3 test script 2 | 3 | \set 4 | 5 | \set SYNTAX_HL_FORMAT terminal16m 6 | \set SYNTAX_HL true 7 | 8 | help 9 | 10 | \? 11 | 12 | \copyright 13 | 14 | \set SYNTAX_HL_STYLE dracula 15 | 16 | select 'test'' 17 | ' \g 18 | 19 | \set NAME myname 20 | 21 | PRAGMA foreign_keys = 1; 22 | 23 | DROP TABLE IF EXISTS books; 24 | 25 | DROP TABLE IF EXISTS authors; 26 | 27 | CREATE TABLE authors ( 28 | author_id integer NOT NULL PRIMARY KEY AUTOINCREMENT, 29 | name text NOT NULL DEFAULT '' 30 | ); 31 | 32 | CREATE INDEX authors_name_idx ON authors(name); 33 | 34 | \set SYNTAX_HL_STYLE paraiso-dark 35 | 36 | CREATE TABLE books ( 37 | /* 38 | this is a multiline comment 39 | */ 40 | book_id integer NOT NULL PRIMARY KEY AUTOINCREMENT, -- the id of the author 41 | author_id integer NOT NULL REFERENCES authors(author_id), 42 | isbn text NOT NULL DEFAULT '' UNIQUE, 43 | title text NOT NULL DEFAULT '', 44 | year integer NOT NULL DEFAULT 2000, 45 | available timestamp with time zone NOT NULL DEFAULT '', 46 | tags text NOT NULL DEFAULT '{}' 47 | ); 48 | 49 | CREATE INDEX books_title_idx ON books(title, year); 50 | 51 | insert into authors (name) values 52 | ("jk rowling"), 53 | ("author amazing") 54 | \g 55 | 56 | select * from authors; 57 | 58 | \set COLNAME name 59 | \set NAME amaz 60 | 61 | \echo `echo hello` 62 | 63 | select :"COLNAME" from authors where :COLNAME like '%' || :'NAME' || '%' 64 | 65 | \print \raw 66 | 67 | \g 68 | 69 | \gset AUTHOR_ 70 | 71 | select :'AUTHOR_name'; 72 | 73 | \begin 74 | insert into authors (name) values ('test'); 75 | \rollback 76 | 77 | insert into authors (name) values ('hello'); 78 | select * from authors; 79 | 80 | insert into books (author_id, isbn, title, year, available) values 81 | (1, '1', 'one', 2018, '2018-06-01 00:00:00'), 82 | (2, '2', 'two', 2019, '2019-06-01 00:00:00') 83 | ; 84 | 85 | select * from books b inner join authors a on a.author_id = b.author_id; 86 | 87 | /* exiting! */ 88 | \q 89 | -------------------------------------------------------------------------------- /contrib/sqlite3/usql-config: -------------------------------------------------------------------------------- 1 | DB="sqlite3:test.sqlite3" 2 | VSQL="SELECT sqlite_version() AS version;" 3 | -------------------------------------------------------------------------------- /contrib/sqlserver/init.sql: -------------------------------------------------------------------------------- 1 | EXEC sp_configure 2 | 'contained database authentication', 1; 3 | 4 | RECONFIGURE; 5 | 6 | DROP LOGIN :NAME; 7 | 8 | DROP DATABASE :NAME; 9 | 10 | CREATE DATABASE :NAME 11 | CONTAINMENT=PARTIAL; 12 | 13 | \set QNAME "''":NAME"''" 14 | 15 | \set SQL 'CREATE LOGIN ':NAME' WITH PASSWORD=':QNAME', CHECK_POLICY=OFF, DEFAULT_DATABASE=':NAME';' 16 | EXEC [:NAME].[dbo].[sp_executesql] N:'SQL' 17 | 18 | \set SQL 'CREATE USER ':NAME' FOR LOGIN ':NAME' WITH DEFAULT_SCHEMA=':NAME';' 19 | EXEC [:NAME].[dbo].[sp_executesql] N:'SQL'; 20 | 21 | \set SQL 'CREATE SCHEMA ':NAME' AUTHORIZATION ':NAME';' 22 | EXEC [:NAME].[dbo].[sp_executesql] N:'SQL'; 23 | 24 | \set SQL 'EXEC sp_addrolemember db_owner, ':QNAME';' 25 | EXEC [:NAME].[dbo].[sp_executesql] N:'SQL'; 26 | 27 | -- original reconnect version: 28 | -- 29 | --\connect 'sqlserver://localhost/':NAME 30 | -- 31 | --CREATE LOGIN :NAME 32 | -- WITH 33 | -- PASSWORD=:'PASS', 34 | -- CHECK_POLICY=OFF, 35 | -- DEFAULT_DATABASE=:NAME; 36 | -- 37 | --CREATE USER :NAME 38 | -- FOR LOGIN :NAME 39 | -- WITH DEFAULT_SCHEMA=:NAME; 40 | -- 41 | --CREATE SCHEMA :NAME AUTHORIZATION :NAME; 42 | -- 43 | --EXEC sp_addrolemember 'db_owner', :'NAME'; 44 | -------------------------------------------------------------------------------- /contrib/sqlserver/podman-config: -------------------------------------------------------------------------------- 1 | NAME=sqlserver 2 | IMAGE=mcr.microsoft.com/mssql/server:2022-latest 3 | PUBLISH=1433:1433 4 | ENV="ACCEPT_EULA=Y MSSQL_PID=Express SA_PASSWORD=Adm1nP@ssw0rd" 5 | -------------------------------------------------------------------------------- /contrib/sqlserver/test.sql: -------------------------------------------------------------------------------- 1 | -- sqlserver test script 2 | 3 | \set 4 | 5 | \set SYNTAX_HL_FORMAT terminal16m 6 | \set SYNTAX_HL true 7 | 8 | \? 9 | 10 | \copyright 11 | 12 | \set SYNTAX_HL_STYLE dracula 13 | 14 | select 'test'' 15 | ' \g 16 | 17 | \set NAME myname 18 | 19 | DROP TABLE IF EXISTS books; 20 | DROP TABLE IF EXISTS authors; 21 | 22 | CREATE TABLE authors ( 23 | author_id integer NOT NULL IDENTITY(1,1) PRIMARY KEY, 24 | name varchar(255) NOT NULL DEFAULT '' 25 | ); 26 | 27 | CREATE INDEX authors_name_idx ON authors(name); 28 | 29 | CREATE TABLE books ( 30 | book_id integer NOT NULL IDENTITY(1,1) PRIMARY KEY, 31 | author_id integer NOT NULL FOREIGN KEY REFERENCES authors(author_id), 32 | isbn varchar(255) NOT NULL DEFAULT '' UNIQUE, 33 | title varchar(255) NOT NULL DEFAULT '', 34 | year integer NOT NULL DEFAULT 2000, 35 | available datetime2 NOT NULL DEFAULT CURRENT_TIMESTAMP, 36 | tags varchar(255) NOT NULL DEFAULT '' 37 | ); 38 | 39 | CREATE INDEX books_title_idx ON books(title, year); 40 | 41 | \set SYNTAX_HL_STYLE paraiso-dark 42 | 43 | insert into authors (name) values 44 | ('jk rowling'), 45 | ('author amazing') 46 | \g 47 | 48 | select * from authors; 49 | 50 | \set COLNAME name 51 | \set NAME amaz 52 | 53 | \echo `echo hello` 54 | 55 | select :"COLNAME" from authors where :COLNAME like '%' || :'NAME' || '%' 56 | 57 | \print \raw 58 | 59 | \g 60 | 61 | \gset AUTHOR_ 62 | 63 | select :'AUTHOR_name'; 64 | 65 | \begin 66 | insert into authors (name) values ('test'); 67 | \rollback 68 | 69 | insert into authors (name) values ('hello'); 70 | select * from authors; 71 | 72 | insert into books (author_id, isbn, title, year, available) values 73 | (1, '1', 'one', 2018, '2018-06-01 00:00:00'), 74 | (2, '2', 'two', 2019, '2019-06-01 00:00:00') 75 | ; 76 | 77 | select * from books b inner join authors a on a.author_id = b.author_id; 78 | 79 | /* exiting! */ 80 | \q 81 | -------------------------------------------------------------------------------- /contrib/sqlserver/usql-config: -------------------------------------------------------------------------------- 1 | DB="sqlserver://sa:Adm1nP@ssw0rd@localhost/" 2 | VSQL="SELECT SERVERPROPERTY('productversion') AS version;" 3 | -------------------------------------------------------------------------------- /contrib/trino/podman-config: -------------------------------------------------------------------------------- 1 | NAME=trino 2 | IMAGE=docker.io/trinodb/trino 3 | PUBLISH=8080:8080 4 | -------------------------------------------------------------------------------- /contrib/trino/usql-config: -------------------------------------------------------------------------------- 1 | DB="trino://localhost" 2 | VSQL="SELECT node_version AS version FROM system.runtime.nodes LIMIT 1;" 3 | -------------------------------------------------------------------------------- /contrib/usql-test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SRC=$(realpath $(cd -P "$(dirname "${BASH_SOURCE[0]}")" && pwd)) 4 | 5 | USQL=$(which usql) 6 | if [ -f $SRC/../usql ]; then 7 | USQL=$(realpath $SRC/../usql) 8 | fi 9 | 10 | export USQL_SHOW_HOST_INFORMATION=false 11 | for TARGET in $SRC/*/usql-config; do 12 | NAME=$(basename $(dirname $TARGET)) 13 | if [[ ! -z "$(podman ps -q --filter "name=$NAME")" || "$NAME" == "duckdb" || "$NAME" == "sqlite3" ]]; then 14 | unset DB VSQL 15 | source $TARGET 16 | if [[ -z "$DB" || -z "$VSQL" ]]; then 17 | echo -e "ERROR: DB or VSQL not defined in $TARGET!\n" 18 | continue 19 | fi 20 | (set -x; 21 | $USQL "$DB" -X -J -c "$VSQL" 22 | ) 23 | echo 24 | fi 25 | done 26 | -------------------------------------------------------------------------------- /contrib/usqlpass: -------------------------------------------------------------------------------- 1 | # sample ~/.usqlpass file 2 | # 3 | # format is: 4 | # protocol:host:port:dbname:user:pass 5 | postgres:*:*:*:postgres:P4ssw0rd 6 | 7 | cql:*:*:*:cassandra:cassandra 8 | clickhouse:*:*:*:clickhouse:P4ssw0rd 9 | couchbase:*:*:*:Administrator:P4ssw0rd 10 | godror:*:*:*:system:P4ssw0rd 11 | ignite:*:*:*:ignite:ignite 12 | mymysql:*:*:*:root:P4ssw0rd 13 | mysql:*:*:*:root:P4ssw0rd 14 | oracle:*:*:*:system:P4ssw0rd 15 | pgx:*:*:*:postgres:P4ssw0rd 16 | sqlserver:*:*:*:sa:Adm1nP@ssw0rd 17 | vertica:*:*:*:vertica:P4ssw0rd 18 | flightsql:*:*:*:flight_username:P4ssw0rd 19 | -------------------------------------------------------------------------------- /contrib/usqlrc: -------------------------------------------------------------------------------- 1 | -- example usqlrc file 2 | -- put in $HOME/.usqlrc 3 | \echo welcome `echo $USER`, today is:`date` 4 | \set SYNTAX_HL_STYLE paraiso-dark 5 | -------------------------------------------------------------------------------- /contrib/vertica/podman-config: -------------------------------------------------------------------------------- 1 | NAME=vertica 2 | IMAGE=docker.io/vertica/vertica-ce:latest 3 | PUBLISH=5433:5433 4 | ENV="APP_DB_USER=vertica APP_DB_PASSWORD=P4ssw0rd" 5 | -------------------------------------------------------------------------------- /contrib/vertica/usql-config: -------------------------------------------------------------------------------- 1 | DB="vertica://vertica:P4ssw0rd@localhost/vertica" 2 | VSQL="SELECT version() AS version;" 3 | -------------------------------------------------------------------------------- /contrib/ydb/podman-config: -------------------------------------------------------------------------------- 1 | NAME=ydb 2 | IMAGE=cr.yandex/yc/yandex-docker-local-ydb 3 | PUBLISH="2135:2135 2136:2136 8765:8765" 4 | ENV="YDB_DEFAULT_LOG_LEVEL=NOTICE GRPC_TLS_PORT=2135 GRPC_PORT=2136 MON_PORT=8765" 5 | VOLUME="ydb-certs:/ydb_certs ydb-data:/ydb_data" 6 | HOSTNAME=localhost 7 | -------------------------------------------------------------------------------- /contrib/ydb/usql-config: -------------------------------------------------------------------------------- 1 | DB="ydb://localhost/local" 2 | VSQL="SELECT 'unk' as version;" 3 | -------------------------------------------------------------------------------- /drivers/adodb/adodb.go: -------------------------------------------------------------------------------- 1 | // Package adodb defines and registers usql's Microsoft ADODB driver. Requires 2 | // CGO. Windows only. 3 | // 4 | // Alias: oleodbc, OLE ODBC 5 | // 6 | // See: https://github.com/mattn/go-adodb 7 | package adodb 8 | 9 | import ( 10 | "database/sql" 11 | "regexp" 12 | "strings" 13 | 14 | _ "github.com/mattn/go-adodb" // DRIVER 15 | "github.com/xo/dburl" 16 | "github.com/xo/usql/drivers" 17 | ) 18 | 19 | func init() { 20 | endRE := regexp.MustCompile(`;?\s*$`) 21 | endAnchorRE := regexp.MustCompile(`(?i)\send\s*;\s*$`) 22 | drivers.Register("adodb", drivers.Driver{ 23 | AllowMultilineComments: true, 24 | AllowCComments: true, 25 | Process: func(u *dburl.URL, prefix string, sqlstr string) (string, string, bool, error) { 26 | // trim last ; but only when not END; 27 | if s := strings.ToLower(u.Query().Get("usql_trim")); s != "" && s != "off" && s != "0" && s != "false" { 28 | if !endAnchorRE.MatchString(sqlstr) { 29 | sqlstr = endRE.ReplaceAllString(sqlstr, "") 30 | } 31 | } 32 | typ, q := drivers.QueryExecType(prefix, sqlstr) 33 | return typ, sqlstr, q, nil 34 | }, 35 | RowsAffected: func(res sql.Result) (int64, error) { 36 | return 0, nil 37 | }, 38 | }, "oleodbc") 39 | } 40 | -------------------------------------------------------------------------------- /drivers/athena/athena.go: -------------------------------------------------------------------------------- 1 | // Package athena defines and registers usql's AWS Athena driver. 2 | // 3 | // See: https://github.com/uber/athenadriver 4 | package athena 5 | 6 | import ( 7 | "context" 8 | 9 | _ "github.com/uber/athenadriver/go" // DRIVER: awsathena 10 | "github.com/xo/usql/drivers" 11 | ) 12 | 13 | func init() { 14 | drivers.Register("awsathena", drivers.Driver{ 15 | AllowMultilineComments: true, 16 | Process: drivers.StripTrailingSemicolon, 17 | Version: func(ctx context.Context, db drivers.DB) (string, error) { 18 | var ver string 19 | err := db.QueryRowContext( 20 | ctx, 21 | `SELECT node_version FROM system.runtime.nodes LIMIT 1`, 22 | ).Scan(&ver) 23 | if err != nil { 24 | return "", err 25 | } 26 | return "Athena " + ver, nil 27 | }, 28 | }) 29 | } 30 | -------------------------------------------------------------------------------- /drivers/avatica/avatica.go: -------------------------------------------------------------------------------- 1 | // Package avatica defines and registers usql's Apache Avatica driver. 2 | // 3 | // See: https://github.com/apache/calcite-avatica-go 4 | package avatica 5 | 6 | import ( 7 | "strconv" 8 | 9 | _ "github.com/apache/calcite-avatica-go/v5" // DRIVER 10 | avaticaerrors "github.com/apache/calcite-avatica-go/v5/errors" 11 | "github.com/xo/usql/drivers" 12 | ) 13 | 14 | func init() { 15 | drivers.Register("avatica", drivers.Driver{ 16 | AllowMultilineComments: true, 17 | AllowCComments: true, 18 | Err: func(err error) (string, string) { 19 | if e, ok := err.(avaticaerrors.ResponseError); ok { 20 | return strconv.Itoa(int(e.ErrorCode)), e.ErrorMessage 21 | } 22 | return "", err.Error() 23 | }, 24 | }) 25 | } 26 | -------------------------------------------------------------------------------- /drivers/bigquery/bigquery.go: -------------------------------------------------------------------------------- 1 | // Package bigquery defines and registers usql's Google BigQuery driver. 2 | // 3 | // See: https://github.com/go-gorm/bigquery 4 | package bigquery 5 | 6 | import ( 7 | "github.com/xo/usql/drivers" 8 | _ "gorm.io/driver/bigquery/driver" // DRIVER 9 | ) 10 | 11 | func init() { 12 | drivers.Register("bigquery", drivers.Driver{}) 13 | } 14 | -------------------------------------------------------------------------------- /drivers/cassandra/cassandra.go: -------------------------------------------------------------------------------- 1 | // Package cassandra defines and registers usql's Cassandra driver. 2 | // 3 | // See: https://github.com/MichaelS11/go-cql-driver 4 | package cassandra 5 | 6 | import ( 7 | "context" 8 | "database/sql" 9 | "encoding/json" 10 | "fmt" 11 | "io" 12 | "log" 13 | "os" 14 | "regexp" 15 | "strings" 16 | 17 | cql "github.com/MichaelS11/go-cql-driver" // DRIVER: cql 18 | "github.com/gocql/gocql" 19 | "github.com/xo/dburl" 20 | "github.com/xo/usql/drivers" 21 | ) 22 | 23 | func init() { 24 | var debug bool 25 | if s := os.Getenv("CQL_DEBUG"); s != "" { 26 | log.Printf("ENABLING DEBUGGING FOR CQL") 27 | debug = true 28 | } 29 | // error regexp's 30 | authReqRE := regexp.MustCompile(`authentication required`) 31 | passwordErrRE := regexp.MustCompile(`Provided username (.*)and/or password are incorrect`) 32 | var l *logger 33 | drivers.Register("cql", drivers.Driver{ 34 | AllowDollar: true, 35 | AllowMultilineComments: true, 36 | AllowCComments: true, 37 | LexerName: "cql", 38 | ForceParams: func(u *dburl.URL) { 39 | if q := u.Query(); q.Get("timeout") == "" { 40 | q.Set("timeout", "300s") 41 | u.RawQuery = q.Encode() 42 | } 43 | }, 44 | Open: func(_ context.Context, u *dburl.URL, stdout, stderr func() io.Writer) (func(string, string) (*sql.DB, error), error) { 45 | // override cql and gocql loggers 46 | l = &logger{debug: debug} 47 | gocql.Logger, cql.CqlDriver.Logger = l, log.New(l, "", 0) 48 | return sql.Open, nil 49 | }, 50 | Version: func(ctx context.Context, db drivers.DB) (string, error) { 51 | var release, protocol, cql string 52 | err := db.QueryRowContext( 53 | ctx, 54 | `SELECT release_version, cql_version, native_protocol_version FROM system.local WHERE key = 'local'`, 55 | ).Scan(&release, &cql, &protocol) 56 | if err != nil { 57 | return "", err 58 | } 59 | return "Cassandra " + release + ", CQL " + cql + ", Protocol v" + protocol, nil 60 | }, 61 | ChangePassword: func(db drivers.DB, user, newpw, _ string) error { 62 | _, err := db.Exec(`ALTER ROLE ` + user + ` WITH PASSWORD = '` + newpw + `'`) 63 | return err 64 | }, 65 | IsPasswordErr: func(err error) bool { 66 | return passwordErrRE.MatchString(l.last) 67 | }, 68 | Err: func(err error) (string, string) { 69 | if authReqRE.MatchString(l.last) { 70 | return "", "authentication required" 71 | } 72 | if m := passwordErrRE.FindStringSubmatch(l.last); m != nil { 73 | return "", fmt.Sprintf("invalid username %sor password", m[1]) 74 | } 75 | return "", strings.TrimPrefix(strings.TrimPrefix(err.Error(), "driver: "), "gocql: ") 76 | }, 77 | RowsAffected: func(sql.Result) (int64, error) { 78 | return 0, nil 79 | }, 80 | ConvertDefault: func(v interface{}) (string, error) { 81 | buf, err := json.Marshal(v) 82 | if err != nil { 83 | return "", err 84 | } 85 | return string(buf), nil 86 | }, 87 | BatchQueryPrefixes: map[string]string{ 88 | "BEGIN BATCH": "APPLY BATCH", 89 | }, 90 | }) 91 | } 92 | 93 | // logger is a null logger that satisfies the gocql.StdLogger and the io.Writer 94 | // interfaces in order to capture the last error issued by the cql/gocql 95 | // packages, since the cql package does not (at this time) return any error 96 | // other than sql.ErrBadConn. 97 | type logger struct { 98 | debug bool 99 | last string 100 | } 101 | 102 | func (l *logger) Print(v ...interface{}) { 103 | if l.debug { 104 | log.Print(v...) 105 | } 106 | } 107 | 108 | func (l *logger) Printf(s string, v ...interface{}) { 109 | if l.debug { 110 | log.Printf(s, v...) 111 | } 112 | } 113 | 114 | func (l *logger) Println(v ...interface{}) { 115 | if l.debug { 116 | log.Println(v...) 117 | } 118 | } 119 | 120 | func (l *logger) Write(buf []byte) (int, error) { 121 | if l.debug { 122 | log.Printf("WRITE: %s", string(buf)) 123 | } 124 | l.last = string(buf) 125 | return len(buf), nil 126 | } 127 | -------------------------------------------------------------------------------- /drivers/chai/chai.go: -------------------------------------------------------------------------------- 1 | // Package chai defines and registers usql's ChaiSQL driver. 2 | // 3 | // See: https://github.com/chaisql/chai 4 | package chai 5 | 6 | import ( 7 | _ "github.com/chaisql/chai/driver" // DRIVER 8 | "github.com/xo/usql/drivers" 9 | ) 10 | 11 | func init() { 12 | drivers.Register("chai", drivers.Driver{}) 13 | } 14 | -------------------------------------------------------------------------------- /drivers/clickhouse/clickhouse.go: -------------------------------------------------------------------------------- 1 | // Package clickhouse defines and registers usql's ClickHouse driver. 2 | // 3 | // Group: base 4 | // See: https://github.com/ClickHouse/clickhouse-go 5 | package clickhouse 6 | 7 | import ( 8 | "database/sql" 9 | "strconv" 10 | "strings" 11 | 12 | "github.com/ClickHouse/clickhouse-go/v2" // DRIVER 13 | "github.com/xo/usql/drivers" 14 | ) 15 | 16 | func init() { 17 | drivers.Register("clickhouse", drivers.Driver{ 18 | AllowMultilineComments: true, 19 | RowsAffected: func(sql.Result) (int64, error) { 20 | return 0, nil 21 | }, 22 | ChangePassword: func(db drivers.DB, user, newpw, oldpw string) error { 23 | _, err := db.Exec(`ALTER USER ` + user + ` IDENTIFIED BY '` + newpw + `'`) 24 | return err 25 | }, 26 | Err: func(err error) (string, string) { 27 | if e, ok := err.(*clickhouse.Exception); ok { 28 | return strconv.Itoa(int(e.Code)), strings.TrimPrefix(e.Message, "clickhouse: ") 29 | } 30 | return "", err.Error() 31 | }, 32 | IsPasswordErr: func(err error) bool { 33 | if e, ok := err.(*clickhouse.Exception); ok { 34 | return e.Code == 516 35 | } 36 | return false 37 | }, 38 | Copy: drivers.CopyWithInsert(func(int) string { return "?" }), 39 | NewMetadataReader: NewMetadataReader, 40 | }) 41 | } 42 | -------------------------------------------------------------------------------- /drivers/cosmos/cosmos.go: -------------------------------------------------------------------------------- 1 | // Package cosmos defines and registers usql's Azure CosmosDB driver. 2 | // 3 | // See: https://github.com/btnguyen2k/gocosmos 4 | package cosmos 5 | 6 | import ( 7 | _ "github.com/btnguyen2k/gocosmos" // DRIVER 8 | "github.com/xo/usql/drivers" 9 | ) 10 | 11 | func init() { 12 | drivers.Register("cosmos", drivers.Driver{ 13 | Process: drivers.StripTrailingSemicolon, 14 | }, "gocosmos") 15 | } 16 | -------------------------------------------------------------------------------- /drivers/couchbase/couchbase.go: -------------------------------------------------------------------------------- 1 | // Package couchbase defines and registers usql's Couchbase driver. 2 | // 3 | // See: https://github.com/couchbase/go_n1ql 4 | package couchbase 5 | 6 | import ( 7 | "context" 8 | "strconv" 9 | "strings" 10 | 11 | _ "github.com/couchbase/go_n1ql" // DRIVER: n1ql 12 | "github.com/xo/usql/drivers" 13 | ) 14 | 15 | func init() { 16 | drivers.Register("n1ql", drivers.Driver{ 17 | AllowMultilineComments: true, 18 | Version: func(ctx context.Context, db drivers.DB) (string, error) { 19 | ver := "" 20 | /* 21 | var buf []byte 22 | if err := db.QueryRowContext(ctx, `SELECT ds_version() AS version`).Scan(&buf); err == nil { 23 | var m map[string]string 24 | if err := json.Unmarshal(buf, &m); err == nil { 25 | if s, ok := m["version"]; ok { 26 | ver = s 27 | } 28 | } 29 | } 30 | */ 31 | var v string 32 | if err := db.QueryRowContext(ctx, `SELECT RAW ds_version()`).Scan(&v); err == nil { 33 | if s, err := strconv.Unquote(v); err == nil { 34 | ver = s 35 | } 36 | } 37 | return "Couchbase " + ver, nil 38 | }, 39 | Err: func(err error) (string, string) { 40 | return "", strings.TrimPrefix(err.Error(), "N1QL: ") 41 | }, 42 | }) 43 | } 44 | -------------------------------------------------------------------------------- /drivers/csvq/csvq.go: -------------------------------------------------------------------------------- 1 | // Package csvq defines and registers usql's CSVQ driver. 2 | // 3 | // See: https://github.com/mithrandie/csvq-driver 4 | // Group: base 5 | package csvq 6 | 7 | import ( 8 | "context" 9 | "os" 10 | "strings" 11 | 12 | "github.com/mithrandie/csvq-driver" // DRIVER 13 | "github.com/mithrandie/csvq/lib/query" 14 | "github.com/xo/dburl" 15 | "github.com/xo/usql/drivers" 16 | ) 17 | 18 | func init() { 19 | csvq.SetStdout(query.NewDiscard()) 20 | drivers.Register("csvq", drivers.Driver{ 21 | AllowMultilineComments: true, 22 | Process: func(_ *dburl.URL, prefix string, sqlstr string) (string, string, bool, error) { 23 | typ, q := drivers.QueryExecType(prefix, sqlstr) 24 | if strings.HasPrefix(prefix, "SHOW") { 25 | csvq.SetStdout(os.Stdout) 26 | q = false 27 | } 28 | return typ, sqlstr, q, nil 29 | }, 30 | Version: func(ctx context.Context, db drivers.DB) (string, error) { 31 | var ver string 32 | if err := db.QueryRowContext(ctx, `SELECT @#VERSION`).Scan(&ver); err != nil { 33 | return "", err 34 | } 35 | return "CSVQ " + ver, nil 36 | }, 37 | Copy: drivers.CopyWithInsert(func(int) string { return "?" }), 38 | }) 39 | } 40 | -------------------------------------------------------------------------------- /drivers/databend/databend.go: -------------------------------------------------------------------------------- 1 | // Package databend defines and registers usql's Databend driver. 2 | // 3 | // See: https://github.com/datafuselabs/databend-go 4 | package databend 5 | 6 | import ( 7 | "io" 8 | 9 | _ "github.com/datafuselabs/databend-go" // DRIVER 10 | "github.com/xo/usql/drivers" 11 | "github.com/xo/usql/drivers/metadata" 12 | infos "github.com/xo/usql/drivers/metadata/informationschema" 13 | ) 14 | 15 | func init() { 16 | newReader := infos.New( 17 | infos.WithPlaceholder(func(int) string { return "?" }), 18 | infos.WithCustomClauses(map[infos.ClauseName]string{ 19 | infos.SequenceColumnsIncrement: "''", 20 | }), 21 | infos.WithFunctions(false), 22 | infos.WithIndexes(false), 23 | infos.WithConstraints(false), 24 | infos.WithColumnPrivileges(false), 25 | ) 26 | drivers.Register("databend", drivers.Driver{ 27 | UseColumnTypes: true, 28 | NewMetadataReader: newReader, 29 | NewMetadataWriter: func(db drivers.DB, w io.Writer, opts ...metadata.ReaderOption) metadata.Writer { 30 | return metadata.NewDefaultWriter(newReader(db, opts...))(db, w) 31 | }, 32 | }) 33 | } 34 | -------------------------------------------------------------------------------- /drivers/databricks/databricks.go: -------------------------------------------------------------------------------- 1 | // Package databricks defines and registers usql's Databricks driver. 2 | // 3 | // See: https://github.com/databricks/databricks-sql-go 4 | package databricks 5 | 6 | import ( 7 | "errors" 8 | 9 | _ "github.com/databricks/databricks-sql-go" // DRIVER 10 | dberrs "github.com/databricks/databricks-sql-go/errors" 11 | "github.com/xo/usql/drivers" 12 | ) 13 | 14 | func init() { 15 | drivers.Register("databricks", drivers.Driver{ 16 | Err: func(err error) (string, string) { 17 | var e dberrs.DBExecutionError 18 | if errors.As(err, &e) { 19 | return e.SqlState(), e.Error() 20 | } 21 | return "", err.Error() 22 | }, 23 | }) 24 | } 25 | -------------------------------------------------------------------------------- /drivers/duckdb/duckdb.go: -------------------------------------------------------------------------------- 1 | // Package duckdb defines and registers usql's DuckDB driver. Requires CGO. 2 | // 3 | // See: https://github.com/marcboeker/go-duckdb 4 | package duckdb 5 | 6 | import ( 7 | "context" 8 | "database/sql" 9 | "fmt" 10 | "io" 11 | "strings" 12 | 13 | _ "github.com/marcboeker/go-duckdb/v2" // DRIVER 14 | "github.com/xo/usql/drivers" 15 | "github.com/xo/usql/drivers/metadata" 16 | infos "github.com/xo/usql/drivers/metadata/informationschema" 17 | mymeta "github.com/xo/usql/drivers/metadata/mysql" 18 | ) 19 | 20 | type metaReader struct { 21 | metadata.LoggingReader 22 | } 23 | 24 | var ( 25 | _ metadata.CatalogReader = &metaReader{} 26 | _ metadata.ColumnStatReader = &metaReader{} 27 | ) 28 | 29 | func (r metaReader) Catalogs(metadata.Filter) (*metadata.CatalogSet, error) { 30 | qstr := `SHOW catalogs` 31 | rows, closeRows, err := r.Query(qstr) 32 | if err != nil { 33 | return nil, err 34 | } 35 | defer closeRows() 36 | 37 | results := []metadata.Catalog{} 38 | for rows.Next() { 39 | rec := metadata.Catalog{} 40 | err = rows.Scan(&rec.Catalog) 41 | if err != nil { 42 | return nil, err 43 | } 44 | results = append(results, rec) 45 | } 46 | if rows.Err() != nil { 47 | return nil, rows.Err() 48 | } 49 | return metadata.NewCatalogSet(results), nil 50 | } 51 | 52 | func (r metaReader) ColumnStats(f metadata.Filter) (*metadata.ColumnStatSet, error) { 53 | names := []string{} 54 | if f.Catalog != "" { 55 | names = append(names, f.Catalog+".") 56 | } 57 | if f.Schema != "" { 58 | names = append(names, f.Schema+".") 59 | } 60 | names = append(names, f.Parent) 61 | rows, closeRows, err := r.Query(fmt.Sprintf("SHOW STATS FOR %s", strings.Join(names, ""))) 62 | if err != nil { 63 | return nil, err 64 | } 65 | defer closeRows() 66 | 67 | results := []metadata.ColumnStat{} 68 | for rows.Next() { 69 | rec := metadata.ColumnStat{Catalog: f.Catalog, Schema: f.Schema, Table: f.Parent} 70 | name := sql.NullString{} 71 | avgWidth := sql.NullInt32{} 72 | numDistinct := sql.NullInt64{} 73 | nullFrac := sql.NullFloat64{} 74 | numRows := sql.NullInt64{} 75 | min := sql.NullString{} 76 | max := sql.NullString{} 77 | err = rows.Scan( 78 | &name, 79 | &avgWidth, 80 | &numDistinct, 81 | &nullFrac, 82 | &numRows, 83 | &min, 84 | &max, 85 | ) 86 | if err != nil { 87 | return nil, err 88 | } 89 | if !name.Valid { 90 | continue 91 | } 92 | rec.Name = name.String 93 | if avgWidth.Valid { 94 | rec.AvgWidth = int(avgWidth.Int32) 95 | } 96 | if numDistinct.Valid { 97 | rec.NumDistinct = numDistinct.Int64 98 | } 99 | if nullFrac.Valid { 100 | rec.NullFrac = nullFrac.Float64 101 | } 102 | if min.Valid { 103 | rec.Min = min.String 104 | } 105 | if max.Valid { 106 | rec.Max = max.String 107 | } 108 | results = append(results, rec) 109 | } 110 | if rows.Err() != nil { 111 | return nil, rows.Err() 112 | } 113 | 114 | return metadata.NewColumnStatSet(results), nil 115 | } 116 | 117 | func init() { 118 | newReader := func(db drivers.DB, opts ...metadata.ReaderOption) metadata.Reader { 119 | ir := infos.New( 120 | infos.WithPlaceholder(func(int) string { return "?" }), 121 | infos.WithCustomClauses(map[infos.ClauseName]string{ 122 | infos.ColumnsColumnSize: "0", 123 | infos.ColumnsNumericScale: "0", 124 | infos.ColumnsNumericPrecRadix: "0", 125 | infos.ColumnsCharOctetLength: "0", 126 | }), 127 | infos.WithFunctions(false), 128 | infos.WithSequences(false), 129 | infos.WithIndexes(false), 130 | infos.WithConstraints(false), 131 | infos.WithColumnPrivileges(false), 132 | infos.WithUsagePrivileges(false), 133 | )(db, opts...) 134 | mr := &metaReader{ 135 | LoggingReader: metadata.NewLoggingReader(db, opts...), 136 | } 137 | return metadata.NewPluginReader(ir, mr) 138 | } 139 | drivers.Register("duckdb", drivers.Driver{ 140 | AllowMultilineComments: true, 141 | Version: func(ctx context.Context, db drivers.DB) (string, error) { 142 | var ver string 143 | err := db.QueryRowContext(ctx, `SELECT library_version FROM pragma_version()`).Scan(&ver) 144 | if err != nil { 145 | return "", err 146 | } 147 | return "DuckDB " + ver, nil 148 | }, 149 | NewMetadataReader: newReader, 150 | NewMetadataWriter: func(db drivers.DB, w io.Writer, opts ...metadata.ReaderOption) metadata.Writer { 151 | return metadata.NewDefaultWriter(newReader(db, opts...))(db, w) 152 | }, 153 | Copy: drivers.CopyWithInsert(func(int) string { return "?" }), 154 | NewCompleter: mymeta.NewCompleter, 155 | }) 156 | } 157 | -------------------------------------------------------------------------------- /drivers/dynamodb/dynamodb.go: -------------------------------------------------------------------------------- 1 | // Package dynamodb defines and registers usql's DynamoDb driver. 2 | // 3 | // See: https://github.com/btnguyen2k/godynamo 4 | package dynamodb 5 | 6 | import ( 7 | _ "github.com/btnguyen2k/godynamo" // DRIVER 8 | "github.com/xo/usql/drivers" 9 | ) 10 | 11 | func init() { 12 | drivers.Register("godynamo", drivers.Driver{}) 13 | } 14 | -------------------------------------------------------------------------------- /drivers/errors.go: -------------------------------------------------------------------------------- 1 | package drivers 2 | 3 | import ( 4 | "strings" 5 | "unicode" 6 | ) 7 | 8 | // Error is a wrapper to standardize errors. 9 | type Error struct { 10 | Driver string 11 | Err error 12 | } 13 | 14 | // WrapErr wraps an error using the specified driver when err is not nil. 15 | func WrapErr(driver string, err error) error { 16 | if err == nil { 17 | return nil 18 | } 19 | // avoid double wrapping error 20 | if _, ok := err.(*Error); ok { 21 | return err 22 | } 23 | return &Error{driver, err} 24 | } 25 | 26 | // Error satisfies the error interface, returning simple information about the 27 | // wrapped error in standardized way. 28 | func (e *Error) Error() string { 29 | if d, ok := drivers[e.Driver]; ok { 30 | n := e.Driver 31 | if d.Name != "" { 32 | n = d.Name 33 | } 34 | s := n 35 | var msg string 36 | if d.Err != nil { 37 | var code string 38 | code, msg = d.Err(e.Err) 39 | if code != "" { 40 | s += ": " + code 41 | } 42 | } else { 43 | msg = e.Err.Error() 44 | } 45 | return s + ": " + chop(msg, n) 46 | } 47 | return e.Driver + ": " + chop(e.Err.Error(), e.Driver) 48 | } 49 | 50 | // Unwrap returns the original error. 51 | func (e *Error) Unwrap() error { 52 | return e.Err 53 | } 54 | 55 | // chop chops off a "prefix: " prefix from a string. 56 | func chop(s, prefix string) string { 57 | return strings.TrimLeftFunc(strings.TrimPrefix(strings.TrimSpace(s), prefix+":"), unicode.IsSpace) 58 | } 59 | -------------------------------------------------------------------------------- /drivers/exasol/exasol.go: -------------------------------------------------------------------------------- 1 | // Package exasol defines and registers usql's Exasol driver. 2 | // 3 | // See: https://github.com/exasol/exasol-driver-go 4 | package exasol 5 | 6 | import ( 7 | "context" 8 | "regexp" 9 | 10 | _ "github.com/exasol/exasol-driver-go" // DRIVER 11 | "github.com/xo/usql/drivers" 12 | ) 13 | 14 | func init() { 15 | errCodeRE := regexp.MustCompile(`^\[([0-9]+)]\s+`) 16 | drivers.Register("exasol", drivers.Driver{ 17 | AllowMultilineComments: true, 18 | LowerColumnNames: true, 19 | Copy: drivers.CopyWithInsert(func(int) string { return "?" }), 20 | Err: func(err error) (string, string) { 21 | code, msg := "", err.Error() 22 | if m := errCodeRE.FindStringSubmatch(msg); m != nil { 23 | code, msg = m[1], errCodeRE.ReplaceAllString(msg, "") 24 | } 25 | return code, msg 26 | }, 27 | Version: func(ctx context.Context, db drivers.DB) (string, error) { 28 | var ver string 29 | if err := db.QueryRowContext(ctx, `SELECT param_value FROM exa_metadata WHERE param_name = 'databaseProductVersion'`).Scan(&ver); err != nil { 30 | return "", err 31 | } 32 | return "Exasol " + ver, nil 33 | }, 34 | }) 35 | } 36 | -------------------------------------------------------------------------------- /drivers/firebird/firebird.go: -------------------------------------------------------------------------------- 1 | // Package firebird defines and registers usql's Firebird driver. 2 | // 3 | // See: https://github.com/nakagami/firebirdsql 4 | package firebird 5 | 6 | import ( 7 | "context" 8 | 9 | _ "github.com/nakagami/firebirdsql" // DRIVER: firebirdsql 10 | "github.com/xo/usql/drivers" 11 | ) 12 | 13 | func init() { 14 | drivers.Register("firebirdsql", drivers.Driver{ 15 | AllowMultilineComments: true, 16 | Version: func(ctx context.Context, db drivers.DB) (string, error) { 17 | var ver string 18 | err := db.QueryRowContext( 19 | ctx, 20 | `SELECT rdb$get_context('SYSTEM', 'ENGINE_VERSION') FROM rdb$database;`, 21 | ).Scan(&ver) 22 | if err != nil { 23 | return "", err 24 | } 25 | return "Firebird " + ver, nil 26 | }, 27 | }) 28 | } 29 | -------------------------------------------------------------------------------- /drivers/flightsql/flightsql.go: -------------------------------------------------------------------------------- 1 | // Package flightsql defines and registers usql's FlightSQL driver. 2 | // 3 | // See: https://github.com/apache/arrow/tree/main/go/arrow/flight/flightsql/driver 4 | package flightsql 5 | 6 | import ( 7 | _ "github.com/apache/arrow/go/v17/arrow/flight/flightsql/driver" // DRIVER 8 | "github.com/xo/usql/drivers" 9 | ) 10 | 11 | func init() { 12 | drivers.Register("flightsql", drivers.Driver{}) 13 | } 14 | -------------------------------------------------------------------------------- /drivers/godror/godror.go: -------------------------------------------------------------------------------- 1 | // Package godror defines and registers usql's GO DRiver for ORacle driver. 2 | // Requires CGO. Uses Oracle's ODPI-C (instant client) library. 3 | // 4 | // See: https://github.com/godror/godror 5 | // Group: all 6 | package godror 7 | 8 | import ( 9 | "errors" 10 | "fmt" 11 | "strings" 12 | 13 | _ "github.com/godror/godror" // DRIVER 14 | "github.com/xo/usql/drivers/oracle/orshared" 15 | ) 16 | 17 | func init() { 18 | orshared.Register( 19 | "godror", 20 | // unwrap error 21 | func(err error) (string, string) { 22 | if e := errors.Unwrap(err); e != nil { 23 | err = e 24 | } 25 | code, msg := "", err.Error() 26 | if e, ok := err.(interface { 27 | Code() int 28 | }); ok { 29 | code = fmt.Sprintf("ORA-%05d", e.Code()) 30 | } 31 | if e, ok := err.(interface { 32 | Message() string 33 | }); ok { 34 | msg = e.Message() 35 | } 36 | if i := strings.LastIndex(msg, "ORA-"); msg == "" && i != -1 { 37 | msg = msg[i:] 38 | if j := strings.Index(msg, ":"); j != -1 { 39 | msg = msg[j+1:] 40 | if code == "" { 41 | code = msg[i:j] 42 | } 43 | } 44 | } 45 | return code, strings.TrimSpace(msg) 46 | }, 47 | // is password error 48 | func(err error) bool { 49 | if e := errors.Unwrap(err); e != nil { 50 | err = e 51 | } 52 | if e, ok := err.(interface { 53 | Code() int 54 | }); ok { 55 | return e.Code() == 1017 || e.Code() == 1005 56 | } 57 | return false 58 | }, 59 | ) 60 | } 61 | -------------------------------------------------------------------------------- /drivers/h2/h2.go: -------------------------------------------------------------------------------- 1 | // Package h2 defines and registers usql's Apache H2 driver. 2 | // 3 | // See: https://github.com/jmrobles/h2go 4 | package h2 5 | 6 | import ( 7 | _ "github.com/jmrobles/h2go" // DRIVER 8 | "github.com/xo/usql/drivers" 9 | ) 10 | 11 | func init() { 12 | drivers.Register("h2", drivers.Driver{ 13 | AllowDollar: true, 14 | AllowMultilineComments: true, 15 | AllowCComments: true, 16 | }) 17 | } 18 | -------------------------------------------------------------------------------- /drivers/hive/hive.go: -------------------------------------------------------------------------------- 1 | // Package hive defines and registers usql's Apache Hive driver. 2 | // 3 | // See: https://github.com/sql-machine-learning/gohive 4 | package hive 5 | 6 | import ( 7 | "github.com/xo/dburl" 8 | "github.com/xo/usql/drivers" 9 | _ "sqlflow.org/gohive" // DRIVER 10 | ) 11 | 12 | func init() { 13 | drivers.Register("hive", drivers.Driver{ 14 | ForceParams: func(u *dburl.URL) { 15 | if u.User != nil && u.Query().Get("auth") == "" { 16 | drivers.ForceQueryParameters([]string{"auth", "PLAIN"})(u) 17 | } 18 | }, 19 | }) 20 | } 21 | -------------------------------------------------------------------------------- /drivers/ignite/ignite.go: -------------------------------------------------------------------------------- 1 | // Package ignite defines and registers usql's Apache Ignite driver. 2 | // 3 | // See: https://github.com/amsokol/ignite-go-client 4 | package ignite 5 | 6 | import ( 7 | "strconv" 8 | 9 | "github.com/amsokol/ignite-go-client/binary/errors" 10 | _ "github.com/amsokol/ignite-go-client/sql" // DRIVER 11 | "github.com/xo/usql/drivers" 12 | ) 13 | 14 | func init() { 15 | drivers.Register("ignite", drivers.Driver{ 16 | Err: func(err error) (string, string) { 17 | if e, ok := err.(*errors.IgniteError); ok { 18 | return strconv.Itoa(int(e.IgniteStatus)), e.IgniteMessage 19 | } 20 | return "", err.Error() 21 | }, 22 | }) 23 | } 24 | -------------------------------------------------------------------------------- /drivers/impala/impala.go: -------------------------------------------------------------------------------- 1 | // Package impala defines and registers usql's Apache Impala driver. 2 | // 3 | // See: https://github.com/sclgo/impala-go 4 | package impala 5 | 6 | import ( 7 | "context" 8 | "database/sql" 9 | "errors" 10 | 11 | "github.com/sclgo/impala-go" // DRIVER 12 | "github.com/xo/usql/drivers" 13 | meta "github.com/xo/usql/drivers/metadata/impala" 14 | ) 15 | 16 | func init() { 17 | drivers.Register("impala", drivers.Driver{ 18 | NewMetadataReader: meta.New, 19 | Copy: func(ctx context.Context, db *sql.DB, rows *sql.Rows, table string) (int64, error) { 20 | placeholder := func(int) string { 21 | return "?" 22 | } 23 | return drivers.FlexibleCopyWithInsert(ctx, db, rows, table, placeholder, false) 24 | }, 25 | IsPasswordErr: func(err error) bool { 26 | var authError *impala.AuthError 27 | return errors.As(err, &authError) 28 | }, 29 | }) 30 | } 31 | -------------------------------------------------------------------------------- /drivers/maxcompute/maxcompute.go: -------------------------------------------------------------------------------- 1 | // Package maxcompute defines and registers usql's Alibaba MaxCompute driver. 2 | // 3 | // See: https://github.com/sql-machine-learning/gomaxcompute 4 | package maxcompute 5 | 6 | import ( 7 | "github.com/xo/usql/drivers" 8 | _ "sqlflow.org/gomaxcompute" // DRIVER 9 | ) 10 | 11 | func init() { 12 | drivers.Register("maxcompute", drivers.Driver{}) 13 | } 14 | -------------------------------------------------------------------------------- /drivers/metadata/impala/metadata.go: -------------------------------------------------------------------------------- 1 | package impala 2 | 3 | import ( 4 | "context" 5 | "database/sql" 6 | 7 | "github.com/xo/usql/drivers" 8 | "github.com/xo/usql/drivers/metadata" 9 | 10 | driver "github.com/sclgo/impala-go" 11 | ) 12 | 13 | type MetaReader struct { 14 | meta *driver.Metadata 15 | } 16 | 17 | func (r MetaReader) Columns(filter metadata.Filter) (*metadata.ColumnSet, error) { 18 | columnIds, err := r.meta.GetColumns(context.Background(), filter.Schema, filter.Parent, filter.Name) 19 | if err != nil { 20 | return nil, err 21 | } 22 | columns := make([]metadata.Column, len(columnIds)) 23 | for i, columnId := range columnIds { 24 | columns[i] = metadata.Column{ 25 | Schema: columnId.Schema, 26 | Table: columnId.TableName, 27 | Name: columnId.ColumnName, 28 | } 29 | } 30 | return metadata.NewColumnSet(columns), nil 31 | } 32 | 33 | func (r MetaReader) Schemas(filter metadata.Filter) (*metadata.SchemaSet, error) { 34 | schemaNames, err := r.meta.GetSchemas(context.Background(), filter.Name) 35 | if err != nil { 36 | return nil, err 37 | } 38 | schemas := make([]metadata.Schema, len(schemaNames)) 39 | for i, name := range schemaNames { 40 | schemas[i] = metadata.Schema{ 41 | Schema: name, 42 | } 43 | } 44 | return metadata.NewSchemaSet(schemas), nil 45 | } 46 | 47 | func (r MetaReader) Tables(filter metadata.Filter) (*metadata.TableSet, error) { 48 | tableIds, err := r.meta.GetTables(context.Background(), filter.Schema, filter.Name) 49 | if err != nil { 50 | return nil, err 51 | } 52 | tables := make([]metadata.Table, len(tableIds)) 53 | for i, table := range tableIds { 54 | tables[i] = metadata.Table{ 55 | Schema: table.Schema, 56 | Name: table.Name, 57 | Type: table.Type, 58 | } 59 | } 60 | return metadata.NewTableSet(tables), nil 61 | } 62 | 63 | var ( 64 | _ metadata.SchemaReader = (*MetaReader)(nil) 65 | _ metadata.TableReader = (*MetaReader)(nil) 66 | _ metadata.ColumnReader = (*MetaReader)(nil) 67 | ) 68 | 69 | func New(db drivers.DB, _ ...metadata.ReaderOption) metadata.Reader { 70 | if sqlDb, ok := db.(*sql.DB); ok { 71 | return &MetaReader{ 72 | meta: driver.NewMetadata(sqlDb), 73 | } 74 | } else { 75 | return struct{}{} // reader with no capabilities 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /drivers/metadata/mysql/metadata.go: -------------------------------------------------------------------------------- 1 | package mysql 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/gohxs/readline" 7 | "github.com/xo/usql/drivers" 8 | "github.com/xo/usql/drivers/completer" 9 | "github.com/xo/usql/drivers/metadata" 10 | infos "github.com/xo/usql/drivers/metadata/informationschema" 11 | ) 12 | 13 | var ( 14 | // NewReader for MySQL databases 15 | NewReader = infos.New( 16 | infos.WithPlaceholder(func(int) string { return "?" }), 17 | infos.WithSequences(false), 18 | infos.WithCheckConstraints(false), 19 | infos.WithCustomClauses(map[infos.ClauseName]string{ 20 | infos.ColumnsDataType: "column_type", 21 | infos.ColumnsNumericPrecRadix: "10", 22 | infos.FunctionColumnsNumericPrecRadix: "10", 23 | infos.ConstraintIsDeferrable: "''", 24 | infos.ConstraintInitiallyDeferred: "''", 25 | infos.PrivilegesGrantor: "''", 26 | infos.ConstraintJoinCond: "AND r.referenced_table_name = f.table_name", 27 | }), 28 | infos.WithSystemSchemas([]string{"mysql", "information_schema", "performance_schema", "sys"}), 29 | infos.WithCurrentSchema("COALESCE(DATABASE(), '%')"), 30 | infos.WithUsagePrivileges(false), 31 | ) 32 | // NewCompleter for MySQL databases 33 | NewCompleter = func(db drivers.DB, opts ...completer.Option) readline.AutoCompleter { 34 | readerOpts := []metadata.ReaderOption{ 35 | // this needs to be relatively low, since autocomplete is very interactive 36 | metadata.WithTimeout(3 * time.Second), 37 | metadata.WithLimit(1000), 38 | } 39 | reader := NewReader(db, readerOpts...) 40 | opts = append([]completer.Option{ 41 | completer.WithReader(reader), 42 | completer.WithDB(db), 43 | completer.WithSQLStartCommands(append(completer.CommonSqlStartCommands, "USE")), 44 | completer.WithBeforeComplete(complete(reader)), 45 | }, opts...) 46 | return completer.NewDefaultCompleter(opts...) 47 | } 48 | ) 49 | 50 | func complete(reader metadata.Reader) completer.CompleteFunc { 51 | return func(previousWords []string, text []rune) [][]rune { 52 | if completer.TailMatches(completer.IGNORE_CASE, previousWords, `USE`) { 53 | return completeWithSchemas(reader, text) 54 | } 55 | return nil 56 | } 57 | } 58 | 59 | func completeWithSchemas(reader metadata.Reader, text []rune) [][]rune { 60 | schemaNames := []string{} 61 | schemas, err := reader.(metadata.SchemaReader).Schemas(metadata.Filter{WithSystem: true}) 62 | if err != nil { 63 | return nil 64 | } 65 | for schemas.Next() { 66 | schemaNames = append(schemaNames, schemas.Get().Schema) 67 | } 68 | return completer.CompleteFromList(text, schemaNames...) 69 | } 70 | -------------------------------------------------------------------------------- /drivers/moderncsqlite/moderncsqlite.go: -------------------------------------------------------------------------------- 1 | // Package moderncsqlite defines and registers usql's ModernC SQLite3 driver. 2 | // Transpilation of SQLite3 to Go. 3 | // 4 | // See: https://gitlab.com/cznic/sqlite 5 | package moderncsqlite 6 | 7 | import ( 8 | "context" 9 | "database/sql" 10 | "io" 11 | "strconv" 12 | 13 | "github.com/xo/dburl" 14 | "github.com/xo/usql/drivers" 15 | "github.com/xo/usql/drivers/sqlite3/sqshared" 16 | "modernc.org/sqlite" // DRIVER 17 | ) 18 | 19 | func init() { 20 | drivers.Register("moderncsqlite", drivers.Driver{ 21 | AllowMultilineComments: true, 22 | Open: func(_ context.Context, u *dburl.URL, stdout, stderr func() io.Writer) (func(string, string) (*sql.DB, error), error) { 23 | return func(_ string, params string) (*sql.DB, error) { 24 | return sql.Open("sqlite", params) 25 | }, nil 26 | }, 27 | Version: func(ctx context.Context, db drivers.DB) (string, error) { 28 | var ver string 29 | err := db.QueryRowContext(ctx, `SELECT sqlite_version()`).Scan(&ver) 30 | if err != nil { 31 | return "", err 32 | } 33 | return "ModernC SQLite " + ver, nil 34 | }, 35 | Err: func(err error) (string, string) { 36 | if e, ok := err.(*sqlite.Error); ok { 37 | return strconv.Itoa(e.Code()), e.Error() 38 | } 39 | return "", err.Error() 40 | }, 41 | ConvertBytes: sqshared.ConvertBytes, 42 | NewMetadataReader: sqshared.NewMetadataReader, 43 | Copy: drivers.CopyWithInsert(func(int) string { return "?" }), 44 | }) 45 | } 46 | -------------------------------------------------------------------------------- /drivers/mymysql/mymysql.go: -------------------------------------------------------------------------------- 1 | // Package mymysql defines and registers usql's MySQL MyMySQL driver. 2 | // 3 | // See: https://github.com/ziutek/mymysql 4 | package mymysql 5 | 6 | import ( 7 | "io" 8 | "strconv" 9 | 10 | "github.com/xo/usql/drivers" 11 | "github.com/xo/usql/drivers/metadata" 12 | mymeta "github.com/xo/usql/drivers/metadata/mysql" 13 | _ "github.com/ziutek/mymysql/godrv" // DRIVER 14 | "github.com/ziutek/mymysql/mysql" 15 | ) 16 | 17 | func init() { 18 | drivers.Register("mymysql", drivers.Driver{ 19 | AllowMultilineComments: true, 20 | AllowHashComments: true, 21 | LexerName: "mysql", 22 | UseColumnTypes: true, 23 | Err: func(err error) (string, string) { 24 | if e, ok := err.(*mysql.Error); ok { 25 | return strconv.Itoa(int(e.Code)), string(e.Msg) 26 | } 27 | return "", err.Error() 28 | }, 29 | IsPasswordErr: func(err error) bool { 30 | if e, ok := err.(*mysql.Error); ok { 31 | return e.Code == mysql.ER_ACCESS_DENIED_ERROR 32 | } 33 | return false 34 | }, 35 | NewMetadataReader: mymeta.NewReader, 36 | NewMetadataWriter: func(db drivers.DB, w io.Writer, opts ...metadata.ReaderOption) metadata.Writer { 37 | return metadata.NewDefaultWriter(mymeta.NewReader(db, opts...))(db, w) 38 | }, 39 | Copy: drivers.CopyWithInsert(func(int) string { return "?" }), 40 | NewCompleter: mymeta.NewCompleter, 41 | }) 42 | } 43 | -------------------------------------------------------------------------------- /drivers/mysql/mysql.go: -------------------------------------------------------------------------------- 1 | // Package mysql defines and registers usql's MySQL driver. 2 | // 3 | // Alias: memsql, SingleStore MemSQL 4 | // Alias: vitess, Vitess Database 5 | // Alias: tidb, TiDB 6 | // 7 | // See: https://github.com/go-sql-driver/mysql 8 | // Group: base 9 | package mysql 10 | 11 | import ( 12 | "io" 13 | "strconv" 14 | 15 | "github.com/go-sql-driver/mysql" // DRIVER 16 | "github.com/xo/usql/drivers" 17 | "github.com/xo/usql/drivers/metadata" 18 | mymeta "github.com/xo/usql/drivers/metadata/mysql" 19 | ) 20 | 21 | func init() { 22 | drivers.Register("mysql", drivers.Driver{ 23 | AllowMultilineComments: true, 24 | AllowHashComments: true, 25 | LexerName: "mysql", 26 | UseColumnTypes: true, 27 | ForceParams: drivers.ForceQueryParameters([]string{ 28 | "parseTime", "true", 29 | "loc", "Local", 30 | "sql_mode", "ansi", 31 | }), 32 | Err: func(err error) (string, string) { 33 | if e, ok := err.(*mysql.MySQLError); ok { 34 | return strconv.Itoa(int(e.Number)), e.Message 35 | } 36 | return "", err.Error() 37 | }, 38 | IsPasswordErr: func(err error) bool { 39 | if e, ok := err.(*mysql.MySQLError); ok { 40 | return e.Number == 1045 41 | } 42 | return false 43 | }, 44 | NewMetadataReader: mymeta.NewReader, 45 | NewMetadataWriter: func(db drivers.DB, w io.Writer, opts ...metadata.ReaderOption) metadata.Writer { 46 | return metadata.NewDefaultWriter(mymeta.NewReader(db, opts...))(db, w) 47 | }, 48 | Copy: drivers.CopyWithInsert(func(int) string { return "?" }), 49 | NewCompleter: mymeta.NewCompleter, 50 | }, "memsql", "vitess", "tidb") 51 | } 52 | -------------------------------------------------------------------------------- /drivers/netezza/netezza.go: -------------------------------------------------------------------------------- 1 | // Package netezza defines and registers usql's Netezza driver. 2 | // 3 | // See: https://github.com/IBM/nzgo 4 | package netezza 5 | 6 | import ( 7 | "context" 8 | "io" 9 | "log" 10 | 11 | "github.com/IBM/nzgo/v12" // DRIVER: nzgo 12 | "github.com/xo/usql/drivers" 13 | "github.com/xo/usql/drivers/metadata" 14 | infos "github.com/xo/usql/drivers/metadata/informationschema" 15 | ) 16 | 17 | func init() { 18 | nzgo.Debug = log.New(io.Discard, "", 0) 19 | nzgo.Info = log.New(io.Discard, "", 0) 20 | nzgo.Fatal = log.New(io.Discard, "", 0) 21 | newReader := infos.New( 22 | infos.WithPlaceholder(func(int) string { return "?" }), 23 | infos.WithIndexes(false), 24 | infos.WithConstraints(false), 25 | infos.WithCustomClauses(map[infos.ClauseName]string{ 26 | infos.ColumnsColumnSize: "COALESCE(character_maximum_length, numeric_precision, datetime_precision, interval_precision, 0)", 27 | infos.FunctionColumnsColumnSize: "COALESCE(character_maximum_length, numeric_precision, datetime_precision, interval_precision, 0)", 28 | }), 29 | infos.WithSystemSchemas([]string{"DEFINITION_SCHEMA", "INFORMATION_SCHEMA"}), 30 | infos.WithCurrentSchema("CURRENT_SCHEMA"), 31 | ) 32 | drivers.Register("nzgo", drivers.Driver{ 33 | Name: "nz", 34 | AllowDollar: true, 35 | AllowMultilineComments: true, 36 | LexerName: "postgres", 37 | Version: func(ctx context.Context, db drivers.DB) (string, error) { 38 | var ver string 39 | err := db.QueryRowContext(ctx, `SELECT version()`).Scan(&ver) 40 | if err != nil { 41 | return "", err 42 | } 43 | return "Netezza " + ver, nil 44 | }, 45 | ChangePassword: func(db drivers.DB, user, newpw, _ string) error { 46 | _, err := db.Exec(`ALTER USER ` + user + ` PASSWORD '` + newpw + `'`) 47 | return err 48 | }, 49 | Err: func(err error) (string, string) { 50 | if e, ok := err.(*nzgo.Error); ok { 51 | return string(e.Code), e.Message 52 | } 53 | return "", err.Error() 54 | }, 55 | IsPasswordErr: func(err error) bool { 56 | if e, ok := err.(*nzgo.Error); ok { 57 | return e.Code.Name() == "invalid_password" 58 | } 59 | return false 60 | }, 61 | NewMetadataReader: newReader, 62 | NewMetadataWriter: func(db drivers.DB, w io.Writer, opts ...metadata.ReaderOption) metadata.Writer { 63 | return metadata.NewDefaultWriter(newReader(db, opts...))(db, w) 64 | }, 65 | }) 66 | } 67 | -------------------------------------------------------------------------------- /drivers/odbc/odbc.go: -------------------------------------------------------------------------------- 1 | // Package odbc defines and registers usql's ODBC driver. Requires CGO. Uses 2 | // respective platform's standard ODBC packages. 3 | // 4 | // See: https://github.com/alexbrainman/odbc 5 | // Group: all 6 | package odbc 7 | 8 | import ( 9 | "regexp" 10 | "strings" 11 | 12 | "github.com/alexbrainman/odbc" // DRIVER 13 | "github.com/xo/dburl" 14 | "github.com/xo/usql/drivers" 15 | ) 16 | 17 | func init() { 18 | endRE := regexp.MustCompile(`;?\s*$`) 19 | endAnchorRE := regexp.MustCompile(`(?i)\send\s*;\s*$`) 20 | drivers.Register("odbc", drivers.Driver{ 21 | LexerName: "tsql", 22 | Process: func(u *dburl.URL, prefix string, sqlstr string) (string, string, bool, error) { 23 | // trim last ; but only when not END; 24 | if s := strings.ToLower(u.Query().Get("usql_trim")); s != "" && s != "off" && s != "0" && s != "false" { 25 | if !endAnchorRE.MatchString(sqlstr) { 26 | sqlstr = endRE.ReplaceAllString(sqlstr, "") 27 | } 28 | } 29 | typ, q := drivers.QueryExecType(prefix, sqlstr) 30 | return typ, sqlstr, q, nil 31 | }, 32 | IsPasswordErr: func(err error) bool { 33 | if e, ok := err.(*odbc.Error); ok { 34 | msg := strings.ToLower(e.Error()) 35 | return strings.Contains(msg, "failed") && 36 | (strings.Contains(msg, "login") || 37 | strings.Contains(msg, "authentication") || 38 | strings.Contains(msg, "password")) 39 | } 40 | return false 41 | }, 42 | }) 43 | } 44 | -------------------------------------------------------------------------------- /drivers/oracle/oracle.go: -------------------------------------------------------------------------------- 1 | // Package oracle defines and registers usql's Oracle Database driver. 2 | // 3 | // See: https://github.com/sijms/go-ora 4 | // Group: base 5 | package oracle 6 | 7 | import ( 8 | "errors" 9 | "fmt" 10 | "strings" 11 | 12 | _ "github.com/sijms/go-ora/v2" // DRIVER 13 | "github.com/xo/usql/drivers/oracle/orshared" 14 | ) 15 | 16 | func init() { 17 | orshared.Register( 18 | "oracle", 19 | // unwrap error 20 | func(err error) (string, string) { 21 | if e := errors.Unwrap(err); e != nil { 22 | err = e 23 | } 24 | code, msg := "", err.Error() 25 | if e, ok := err.(interface { 26 | Code() int 27 | }); ok { 28 | code = fmt.Sprintf("ORA-%05d", e.Code()) 29 | } 30 | if e, ok := err.(interface { 31 | Message() string 32 | }); ok { 33 | msg = e.Message() 34 | } 35 | if i := strings.LastIndex(msg, "ORA-"); msg == "" && i != -1 { 36 | msg = msg[i:] 37 | if j := strings.Index(msg, ":"); j != -1 { 38 | msg = msg[j+1:] 39 | if code == "" { 40 | code = msg[i:j] 41 | } 42 | } 43 | } 44 | return code, strings.TrimSpace(msg) 45 | }, 46 | // is password error 47 | func(err error) bool { 48 | if e := errors.Unwrap(err); e != nil { 49 | err = e 50 | } 51 | return strings.Contains(err.Error(), "empty password") 52 | }, 53 | ) 54 | } 55 | -------------------------------------------------------------------------------- /drivers/oracle/orshared/orshared.go: -------------------------------------------------------------------------------- 1 | // Package orshared contains shared a shared driver implementation for the 2 | // Oracle Database. Used by Oracle and Godror drivers. 3 | package orshared 4 | 5 | import ( 6 | "context" 7 | "fmt" 8 | "io" 9 | "regexp" 10 | "strings" 11 | 12 | "github.com/xo/dburl" 13 | "github.com/xo/usql/drivers" 14 | "github.com/xo/usql/drivers/metadata" 15 | orameta "github.com/xo/usql/drivers/metadata/oracle" 16 | "github.com/xo/usql/env" 17 | ) 18 | 19 | // Register registers an oracle driver. 20 | func Register(name string, err func(error) (string, string), isPasswordErr func(error) bool) { 21 | endRE := regexp.MustCompile(`;?\s*$`) 22 | endAnchorRE := regexp.MustCompile(`(?i)\send\s*;\s*$`) 23 | drivers.Register(name, drivers.Driver{ 24 | AllowMultilineComments: true, 25 | LowerColumnNames: true, 26 | ForceParams: func(u *dburl.URL) { 27 | // if the service name is not specified, use the environment 28 | // variable if present 29 | if strings.TrimPrefix(u.Path, "/") == "" { 30 | if n, ok := env.Getenv("ORACLE_SID", "ORASID"); ok && n != "" { 31 | u.Path = "/" + n 32 | if u.Host == "" { 33 | u.Host = "localhost" 34 | } 35 | } 36 | } 37 | }, 38 | Version: func(ctx context.Context, db drivers.DB) (string, error) { 39 | var ver string 40 | if err := db.QueryRowContext(ctx, `SELECT version FROM v$instance`).Scan(&ver); err != nil { 41 | return "", err 42 | } 43 | return "Oracle Database " + ver, nil 44 | }, 45 | User: func(ctx context.Context, db drivers.DB) (string, error) { 46 | var user string 47 | if err := db.QueryRowContext(ctx, `SELECT user FROM dual`).Scan(&user); err != nil { 48 | return "", err 49 | } 50 | return user, nil 51 | }, 52 | ChangePassword: func(db drivers.DB, user, newpw, _ string) error { 53 | _, err := db.Exec(`ALTER USER ` + user + ` IDENTIFIED BY ` + newpw) 54 | return err 55 | }, 56 | Err: err, 57 | IsPasswordErr: isPasswordErr, 58 | Process: func(_ *dburl.URL, prefix string, sqlstr string) (string, string, bool, error) { 59 | if !endAnchorRE.MatchString(sqlstr) { 60 | // trim last ; but only when not END; 61 | sqlstr = endRE.ReplaceAllString(sqlstr, "") 62 | } 63 | typ, q := drivers.QueryExecType(prefix, sqlstr) 64 | return typ, sqlstr, q, nil 65 | }, 66 | NewMetadataReader: orameta.NewReader(), 67 | NewMetadataWriter: func(db drivers.DB, w io.Writer, opts ...metadata.ReaderOption) metadata.Writer { 68 | return metadata.NewDefaultWriter(orameta.NewReader()(db, opts...))(db, w) 69 | }, 70 | Copy: drivers.CopyWithInsert(func(n int) string { 71 | return fmt.Sprintf(":%d", n) 72 | }), 73 | }) 74 | } 75 | -------------------------------------------------------------------------------- /drivers/ots/ots.go: -------------------------------------------------------------------------------- 1 | // Package ots defines and registers usql's Alibaba Tablestore driver. 2 | // 3 | // See: https://github.com/aliyun/aliyun-tablestore-go-sql-driver 4 | package ots 5 | 6 | import ( 7 | _ "github.com/aliyun/aliyun-tablestore-go-sql-driver" // DRIVER 8 | "github.com/xo/usql/drivers" 9 | ) 10 | 11 | func init() { 12 | drivers.Register("ots", drivers.Driver{}) 13 | } 14 | -------------------------------------------------------------------------------- /drivers/pgx/pgx.go: -------------------------------------------------------------------------------- 1 | // Package pgx defines and registers usql's PostgreSQL PGX driver. 2 | // 3 | // See: https://github.com/jackc/pgx 4 | package pgx 5 | 6 | import ( 7 | "context" 8 | "database/sql" 9 | "errors" 10 | "fmt" 11 | "io" 12 | "strings" 13 | 14 | "github.com/jackc/pgx/v5" 15 | "github.com/jackc/pgx/v5/pgconn" 16 | "github.com/jackc/pgx/v5/stdlib" // DRIVER 17 | "github.com/xo/dburl" 18 | "github.com/xo/usql/drivers" 19 | "github.com/xo/usql/drivers/metadata" 20 | pgmeta "github.com/xo/usql/drivers/metadata/postgres" 21 | "github.com/xo/usql/text" 22 | ) 23 | 24 | func init() { 25 | drivers.Register("pgx", drivers.Driver{ 26 | AllowDollar: true, 27 | AllowMultilineComments: true, 28 | LexerName: "postgres", 29 | Open: func(ctx context.Context, u *dburl.URL, stdout, stderr func() io.Writer) (func(string, string) (*sql.DB, error), error) { 30 | return func(_, dsn string) (*sql.DB, error) { 31 | config, err := pgx.ParseConfig(dsn) 32 | if err != nil { 33 | return nil, err 34 | } 35 | config.OnNotice = func(_ *pgconn.PgConn, notice *pgconn.Notice) { 36 | out := stderr() 37 | fmt.Fprintln(out, notice.Severity+": ", notice.Message) 38 | if notice.Hint != "" { 39 | fmt.Fprintln(out, "HINT: ", notice.Hint) 40 | } 41 | } 42 | config.OnNotification = func(_ *pgconn.PgConn, notification *pgconn.Notification) { 43 | var payload string 44 | if notification.Payload != "" { 45 | payload = fmt.Sprintf(text.NotificationPayload, notification.Payload) 46 | } 47 | fmt.Fprintln(stdout(), fmt.Sprintf(text.NotificationReceived, notification.Channel, payload, notification.PID)) 48 | } 49 | // NOTE: as opposed to the github.com/lib/pq driver, this 50 | // NOTE: driver has a "prefer" mode that is enabled by default. 51 | // NOTE: as such there is no logic here to try to reconnect as 52 | // NOTE: in the postgres driver. 53 | return stdlib.OpenDB(*config), nil 54 | }, nil 55 | }, 56 | Version: func(ctx context.Context, db drivers.DB) (string, error) { 57 | var ver string 58 | err := db.QueryRowContext(ctx, `SHOW server_version`).Scan(&ver) 59 | if err != nil { 60 | return "", err 61 | } 62 | return "PostgreSQL " + ver, nil 63 | }, 64 | ChangePassword: func(db drivers.DB, user, newpw, _ string) error { 65 | _, err := db.Exec(`ALTER USER ` + user + ` PASSWORD '` + newpw + `'`) 66 | return err 67 | }, 68 | Err: func(err error) (string, string) { 69 | var e *pgconn.PgError 70 | if errors.As(err, &e) { 71 | return e.Code, e.Message 72 | } 73 | return "", err.Error() 74 | }, 75 | IsPasswordErr: func(err error) bool { 76 | var e *pgconn.PgError 77 | if errors.As(err, &e) { 78 | return e.Code == "28P01" 79 | } 80 | return false 81 | }, 82 | NewMetadataReader: pgmeta.NewReader(), 83 | NewMetadataWriter: func(db drivers.DB, w io.Writer, opts ...metadata.ReaderOption) metadata.Writer { 84 | return metadata.NewDefaultWriter(pgmeta.NewReader()(db, opts...))(db, w) 85 | }, 86 | Copy: func(ctx context.Context, db *sql.DB, rows *sql.Rows, table string) (int64, error) { 87 | conn, err := db.Conn(context.Background()) 88 | if err != nil { 89 | return 0, fmt.Errorf("failed to get a connection from pool: %w", err) 90 | } 91 | 92 | leftParen := strings.IndexRune(table, '(') 93 | colQuery := "SELECT * FROM " + table + " WHERE 1=0" 94 | if leftParen != -1 { 95 | // pgx's CopyFrom needs a slice of column names and splitting them by a comma is unreliable 96 | // so evaluate the possible expressions against the target table 97 | colQuery = "SELECT " + table[leftParen+1:len(table)-1] + " FROM " + table[:leftParen] + " WHERE 1=0" 98 | table = table[:leftParen] 99 | } 100 | colStmt, err := db.PrepareContext(ctx, colQuery) 101 | if err != nil { 102 | return 0, fmt.Errorf("failed to prepare query to determine target table columns: %w", err) 103 | } 104 | colRows, err := colStmt.QueryContext(ctx) 105 | if err != nil { 106 | return 0, fmt.Errorf("failed to execute query to determine target table columns: %w", err) 107 | } 108 | columns, err := colRows.Columns() 109 | if err != nil { 110 | return 0, fmt.Errorf("failed to fetch target table columns: %w", err) 111 | } 112 | clen := len(columns) 113 | 114 | crows := ©Rows{ 115 | rows: rows, 116 | values: make([]interface{}, clen), 117 | } 118 | for i := 0; i < clen; i++ { 119 | crows.values[i] = new(interface{}) 120 | } 121 | 122 | var n int64 123 | err = conn.Raw(func(driverConn interface{}) error { 124 | conn := driverConn.(*stdlib.Conn).Conn() 125 | n, err = conn.CopyFrom(ctx, pgx.Identifier(strings.SplitN(table, ".", 2)), columns, crows) 126 | return err 127 | }) 128 | return n, err 129 | }, 130 | }) 131 | } 132 | 133 | type copyRows struct { 134 | rows *sql.Rows 135 | values []interface{} 136 | } 137 | 138 | func (r *copyRows) Next() bool { 139 | return r.rows.Next() 140 | } 141 | 142 | func (r *copyRows) Values() ([]interface{}, error) { 143 | err := r.rows.Scan(r.values...) 144 | actuals := make([]interface{}, len(r.values)) 145 | for i, v := range r.values { 146 | actuals[i] = *(v.(*interface{})) 147 | } 148 | return actuals, err 149 | } 150 | 151 | func (r *copyRows) Err() error { 152 | return r.rows.Err() 153 | } 154 | -------------------------------------------------------------------------------- /drivers/presto/presto.go: -------------------------------------------------------------------------------- 1 | // Package presto defines and registers usql's Presto driver. 2 | // 3 | // See: https://github.com/prestodb/presto-go-client 4 | package presto 5 | 6 | import ( 7 | "context" 8 | 9 | _ "github.com/prestodb/presto-go-client/presto" // DRIVER 10 | "github.com/xo/usql/drivers" 11 | ) 12 | 13 | func init() { 14 | drivers.Register("presto", drivers.Driver{ 15 | AllowMultilineComments: true, 16 | Process: drivers.StripTrailingSemicolon, 17 | Version: func(ctx context.Context, db drivers.DB) (string, error) { 18 | var ver string 19 | err := db.QueryRowContext( 20 | ctx, 21 | `SELECT node_version FROM system.runtime.nodes LIMIT 1`, 22 | ).Scan(&ver) 23 | if err != nil { 24 | return "", err 25 | } 26 | return "Presto " + ver, nil 27 | }, 28 | }) 29 | } 30 | -------------------------------------------------------------------------------- /drivers/ql/ql.go: -------------------------------------------------------------------------------- 1 | // Package ql defines and registers usql's Cznic QL driver. 2 | // 3 | // See: https://gitlab.com/cznic/ql 4 | package ql 5 | 6 | import ( 7 | "github.com/xo/usql/drivers" 8 | "modernc.org/ql" // DRIVER 9 | ) 10 | 11 | func init() { 12 | ql.RegisterDriver() 13 | // ql.RegisterMemDriver() 14 | drivers.Register("ql", drivers.Driver{ 15 | AllowMultilineComments: true, 16 | AllowCComments: true, 17 | BatchQueryPrefixes: map[string]string{ 18 | "BEGIN TRANSACTION": "COMMIT", 19 | }, 20 | BatchAsTransaction: true, 21 | }) 22 | } 23 | -------------------------------------------------------------------------------- /drivers/ramsql/ramsql.go: -------------------------------------------------------------------------------- 1 | // Package ramsql defines and registers usql's RamSQL driver. 2 | // 3 | // See: https://github.com/proullon/ramsql 4 | package ql 5 | 6 | import ( 7 | _ "github.com/proullon/ramsql/driver" // DRIVER 8 | "github.com/xo/usql/drivers" 9 | ) 10 | 11 | func init() { 12 | drivers.Register("ramsql", drivers.Driver{}) 13 | } 14 | -------------------------------------------------------------------------------- /drivers/sapase/sapase.go: -------------------------------------------------------------------------------- 1 | // Package sapase defines and registers usql's SAP ASE driver. 2 | // 3 | // See: https://github.com/thda/tds 4 | package sapase 5 | 6 | import ( 7 | "context" 8 | "errors" 9 | "strconv" 10 | "strings" 11 | 12 | "github.com/thda/tds" // DRIVER: tds 13 | "github.com/xo/usql/drivers" 14 | ) 15 | 16 | func init() { 17 | drivers.Register("tds", drivers.Driver{ 18 | AllowMultilineComments: true, 19 | RequirePreviousPassword: true, 20 | LexerName: "tsql", 21 | Version: func(ctx context.Context, db drivers.DB) (string, error) { 22 | var ver string 23 | err := db.QueryRowContext(ctx, `SELECT @@version`).Scan(&ver) 24 | if err != nil { 25 | return "", err 26 | } 27 | return ver, nil 28 | }, 29 | ChangePassword: func(db drivers.DB, user, newpw, oldpw string) error { 30 | if user != "" { 31 | return errors.New("Cannot change password for another user") 32 | } 33 | _, err := db.Exec(`exec sp_password '` + oldpw + `', '` + newpw + `'`) 34 | return err 35 | }, 36 | Err: func(err error) (string, string) { 37 | if e, ok := err.(tds.SybError); ok { 38 | return strconv.Itoa(int(e.MsgNumber)), e.Message 39 | } 40 | msg := err.Error() 41 | if i := strings.LastIndex(msg, "tds:"); i != -1 { 42 | msg = msg[i:] 43 | } 44 | return "", msg 45 | }, 46 | IsPasswordErr: func(err error) bool { 47 | return strings.Contains(err.Error(), "Login failed") 48 | }, 49 | Process: drivers.StripTrailingSemicolon, 50 | }) 51 | } 52 | -------------------------------------------------------------------------------- /drivers/saphana/saphana.go: -------------------------------------------------------------------------------- 1 | // Package saphana defines and registers usql's SAP HANA driver. 2 | // 3 | // See: https://github.com/SAP/go-hdb 4 | package saphana 5 | 6 | import ( 7 | "context" 8 | "strconv" 9 | 10 | _ "github.com/SAP/go-hdb/driver" // DRIVER: hdb 11 | "github.com/xo/usql/drivers" 12 | ) 13 | 14 | func init() { 15 | drivers.Register("hdb", drivers.Driver{ 16 | AllowMultilineComments: true, 17 | Version: func(ctx context.Context, db drivers.DB) (string, error) { 18 | var ver string 19 | if err := db.QueryRowContext(ctx, `SELECT version FROM m_database`).Scan(&ver); err != nil { 20 | return "", err 21 | } 22 | return "SAP HANA " + ver, nil 23 | }, 24 | Err: func(err error) (string, string) { 25 | code, msg := "", err.Error() 26 | if e, ok := err.(interface { 27 | Code() int 28 | }); ok { 29 | code = strconv.Itoa(e.Code()) 30 | } 31 | if e, ok := err.(interface { 32 | Text() string 33 | }); ok { 34 | msg = e.Text() 35 | } 36 | return code, msg 37 | }, 38 | }) 39 | } 40 | -------------------------------------------------------------------------------- /drivers/snowflake/snowflake.go: -------------------------------------------------------------------------------- 1 | // Package snowflake defines and registers usql's Snowflake driver. 2 | // 3 | // See: https://github.com/snowflakedb/gosnowflake 4 | package snowflake 5 | 6 | import ( 7 | "io" 8 | "strconv" 9 | 10 | "github.com/snowflakedb/gosnowflake" // DRIVER 11 | "github.com/xo/tblfmt" 12 | "github.com/xo/usql/drivers" 13 | "github.com/xo/usql/drivers/metadata" 14 | infos "github.com/xo/usql/drivers/metadata/informationschema" 15 | "github.com/xo/usql/env" 16 | ) 17 | 18 | func init() { 19 | gosnowflake.GetLogger().SetOutput(io.Discard) 20 | newReader := infos.New( 21 | infos.WithPlaceholder(func(int) string { return "?" }), 22 | infos.WithCustomClauses(map[infos.ClauseName]string{ 23 | infos.SequenceColumnsIncrement: "''", 24 | }), 25 | infos.WithFunctions(false), 26 | infos.WithIndexes(false), 27 | infos.WithConstraints(false), 28 | infos.WithColumnPrivileges(false), 29 | ) 30 | drivers.Register("snowflake", drivers.Driver{ 31 | AllowMultilineComments: true, 32 | Err: func(err error) (string, string) { 33 | if e, ok := err.(*gosnowflake.SnowflakeError); ok { 34 | return strconv.Itoa(e.Number), e.Message 35 | } 36 | return "", err.Error() 37 | }, 38 | NewMetadataReader: newReader, 39 | NewMetadataWriter: func(db drivers.DB, w io.Writer, opts ...metadata.ReaderOption) metadata.Writer { 40 | writerOpts := []metadata.WriterOption{ 41 | metadata.WithListAllDbs(func(pattern string, verbose bool) error { 42 | return listAllDbs(db, w, pattern, verbose) 43 | }), 44 | } 45 | return metadata.NewDefaultWriter(newReader(db, opts...), writerOpts...)(db, w) 46 | }, 47 | }) 48 | } 49 | 50 | func listAllDbs(db drivers.DB, w io.Writer, _ string, _ bool) error { 51 | rows, err := db.Query("SHOW databases") 52 | if err != nil { 53 | return err 54 | } 55 | defer rows.Close() 56 | params := env.Vars().Print() 57 | params["title"] = "List of databases" 58 | return tblfmt.EncodeAll(w, rows, params) 59 | } 60 | -------------------------------------------------------------------------------- /drivers/spanner/spanner.go: -------------------------------------------------------------------------------- 1 | // Package spanner defines and registers usql's Google Spanner driver. 2 | // 3 | // See: https://github.com/googleapis/go-sql-spanner 4 | package spanner 5 | 6 | import ( 7 | _ "github.com/googleapis/go-sql-spanner" // DRIVER 8 | "github.com/xo/usql/drivers" 9 | ) 10 | 11 | func init() { 12 | drivers.Register("spanner", drivers.Driver{}) 13 | } 14 | -------------------------------------------------------------------------------- /drivers/sqlite3/sqlite3.go: -------------------------------------------------------------------------------- 1 | // Package sqlite3 defines and registers usql's SQLite3 driver. Requires CGO. 2 | // 3 | // See: https://github.com/mattn/go-sqlite3 4 | // Group: base 5 | package sqlite3 6 | 7 | import ( 8 | "context" 9 | "strconv" 10 | 11 | "github.com/mattn/go-sqlite3" // DRIVER 12 | "github.com/xo/usql/drivers" 13 | "github.com/xo/usql/drivers/sqlite3/sqshared" 14 | ) 15 | 16 | func init() { 17 | drivers.Register("sqlite3", drivers.Driver{ 18 | AllowMultilineComments: true, 19 | ForceParams: drivers.ForceQueryParameters([]string{ 20 | "loc", "auto", 21 | }), 22 | Version: func(ctx context.Context, db drivers.DB) (string, error) { 23 | var ver string 24 | err := db.QueryRowContext(ctx, `SELECT sqlite_version()`).Scan(&ver) 25 | if err != nil { 26 | return "", err 27 | } 28 | return "SQLite3 " + ver, nil 29 | }, 30 | Err: func(err error) (string, string) { 31 | if e, ok := err.(sqlite3.Error); ok { 32 | return strconv.Itoa(int(e.Code)), e.Error() 33 | } 34 | code, msg := "", err.Error() 35 | if e, ok := err.(sqlite3.ErrNo); ok { 36 | code = strconv.Itoa(int(e)) 37 | } 38 | return code, msg 39 | }, 40 | ConvertBytes: sqshared.ConvertBytes, 41 | NewMetadataReader: sqshared.NewMetadataReader, 42 | Copy: drivers.CopyWithInsert(func(int) string { return "?" }), 43 | }) 44 | } 45 | -------------------------------------------------------------------------------- /drivers/sqlite3/sqshared/sqshared.go: -------------------------------------------------------------------------------- 1 | // Package sqshared contains shared types for the sqlite3 and moderncsqlite 2 | // drivers. 3 | package sqshared 4 | 5 | import ( 6 | "database/sql/driver" 7 | "errors" 8 | "fmt" 9 | "strings" 10 | "time" 11 | ) 12 | 13 | // ConvertBytes is the byte formatter func for sqlite3 databases. 14 | func ConvertBytes(buf []byte, tfmt string) (string, error) { 15 | // attempt to convert buf if it matches a time format, and if it 16 | // does, then return a formatted time string. 17 | s := string(buf) 18 | if s != "" && strings.TrimSpace(s) != "" { 19 | t := new(Time) 20 | if err := t.Scan(buf); err == nil { 21 | return time.Time(*t).Format(tfmt), nil 22 | } 23 | } 24 | return s, nil 25 | } 26 | 27 | // Time provides a type that will correctly scan the various timestamps 28 | // values stored by the github.com/mattn/go-sqlite3 driver for time.Time 29 | // values, as well as correctly satisfying the sql/driver/Valuer interface. 30 | type Time time.Time 31 | 32 | // Value satisfies the Valuer interface. 33 | func (t *Time) Value() (driver.Value, error) { 34 | return t, nil 35 | } 36 | 37 | // Scan satisfies the Scanner interface. 38 | func (t *Time) Scan(v interface{}) error { 39 | switch x := v.(type) { 40 | case time.Time: 41 | *t = Time(x) 42 | return nil 43 | case []byte: 44 | return t.Parse(string(x)) 45 | case string: 46 | return t.Parse(x) 47 | } 48 | return fmt.Errorf("cannot convert type %T to Time", v) 49 | } 50 | 51 | // Parse attempts to Parse string s to t. 52 | func (t *Time) Parse(s string) error { 53 | if s == "" { 54 | return nil 55 | } 56 | for _, f := range SQLiteTimestampFormats { 57 | if z, err := time.Parse(f, s); err == nil { 58 | *t = Time(z) 59 | return nil 60 | } 61 | } 62 | return errors.New("could not parse time") 63 | } 64 | 65 | // SQLiteTimestampFormats is timestamp formats understood by both this module 66 | // and SQLite. The first format in the slice will be used when saving time 67 | // values into the database. When parsing a string from a timestamp or datetime 68 | // column, the formats are tried in order. 69 | var SQLiteTimestampFormats = []string{ 70 | // By default, store timestamps with whatever timezone they come with. 71 | // When parsed, they will be returned with the same timezone. 72 | "2006-01-02 15:04:05.999999999-07:00", 73 | "2006-01-02T15:04:05.999999999-07:00", 74 | "2006-01-02 15:04:05.999999999", 75 | "2006-01-02T15:04:05.999999999", 76 | "2006-01-02 15:04:05", 77 | "2006-01-02T15:04:05", 78 | "2006-01-02 15:04", 79 | "2006-01-02T15:04", 80 | "2006-01-02", 81 | } 82 | -------------------------------------------------------------------------------- /drivers/sqlserver/sqlserver.go: -------------------------------------------------------------------------------- 1 | // Package sqlserver defines and registers usql's Microsoft SQL Server driver. 2 | // 3 | // See: https://github.com/microsoft/go-mssqldb 4 | // Group: base 5 | package sqlserver 6 | 7 | import ( 8 | "context" 9 | "database/sql" 10 | "fmt" 11 | "io" 12 | "strconv" 13 | "strings" 14 | 15 | mssql "github.com/microsoft/go-mssqldb" 16 | sqlserver "github.com/microsoft/go-mssqldb" // DRIVER 17 | "github.com/xo/usql/drivers" 18 | "github.com/xo/usql/drivers/metadata" 19 | 20 | // needed for azuresql authentication, named pipes, and shared memory transport protocols 21 | _ "github.com/microsoft/go-mssqldb/azuread" 22 | _ "github.com/microsoft/go-mssqldb/namedpipe" 23 | _ "github.com/microsoft/go-mssqldb/sharedmemory" 24 | ) 25 | 26 | func init() { 27 | drivers.Register("sqlserver", drivers.Driver{ 28 | AllowMultilineComments: true, 29 | RequirePreviousPassword: true, 30 | LexerName: "tsql", 31 | /* 32 | // NOTE: this has been commented out, as it is not necessary. if 33 | // NOTE: the azuread.DriverName is changed from `azuresql`, then 34 | // NOTE: this func will be necessary as dburl will never import non 35 | // NOTE: stdlib package. as is, dburl.Open will handle the call 36 | // NOTE: to sql.Open and will pass the `azuresql` driver name 37 | Open: func(_ context.Context, u *dburl.URL, _, _ func() io.Writer) (func(string, string) (*sql.DB, error), error) { 38 | return func(_ string, params string) (*sql.DB, error) { 39 | driver := "sqlserver" 40 | switch { 41 | case u.Query().Has("fedauth"), 42 | strings.Contains(strings.ToLower(u.OriginalScheme), "azuresql"): 43 | driver = azuread.DriverName 44 | } 45 | return sql.Open(driver, params) 46 | }, nil 47 | }, 48 | */ 49 | Version: func(ctx context.Context, db drivers.DB) (string, error) { 50 | var ver, level, edition string 51 | err := db.QueryRowContext( 52 | ctx, 53 | `SELECT SERVERPROPERTY('productversion'), SERVERPROPERTY ('productlevel'), SERVERPROPERTY ('edition')`, 54 | ).Scan(&ver, &level, &edition) 55 | if err != nil { 56 | return "", err 57 | } 58 | return "Microsoft SQL Server " + ver + ", " + level + ", " + edition, nil 59 | }, 60 | ChangePassword: func(db drivers.DB, user, newpw, oldpw string) error { 61 | _, err := db.Exec(`ALTER LOGIN ` + user + ` WITH password = '` + newpw + `' old_password = '` + oldpw + `'`) 62 | return err 63 | }, 64 | ColumnTypes: func(col *sql.ColumnType) (interface{}, error) { 65 | switch col.DatabaseTypeName() { 66 | case "UNIQUEIDENTIFIER": 67 | if nullable, ok := col.Nullable(); ok && nullable { 68 | return new(NullUniqueIdentifier), nil 69 | } 70 | return new(mssql.UniqueIdentifier), nil 71 | } 72 | return new(interface{}), nil 73 | }, 74 | Err: func(err error) (string, string) { 75 | if e, ok := err.(sqlserver.Error); ok { 76 | return strconv.Itoa(int(e.Number)), e.Message 77 | } 78 | msg := err.Error() 79 | if i := strings.LastIndex(msg, "sqlserver:"); i != -1 { 80 | msg = msg[i:] 81 | } 82 | return "", msg 83 | }, 84 | IsPasswordErr: func(err error) bool { 85 | return strings.Contains(err.Error(), "Login failed for") 86 | }, 87 | NewMetadataReader: NewReader, 88 | NewMetadataWriter: func(db drivers.DB, w io.Writer, opts ...metadata.ReaderOption) metadata.Writer { 89 | return metadata.NewDefaultWriter(NewReader(db, opts...))(db, w) 90 | }, 91 | Copy: drivers.CopyWithInsert(placeholder), 92 | }) 93 | } 94 | 95 | func placeholder(n int) string { 96 | return fmt.Sprintf("@p%d", n) 97 | } 98 | 99 | type NullUniqueIdentifier struct { 100 | ID mssql.UniqueIdentifier 101 | Valid bool 102 | } 103 | 104 | func (nui *NullUniqueIdentifier) Scan(v interface{}) error { 105 | nui.Valid = false 106 | if v == nil { 107 | return nil 108 | } 109 | if err := nui.ID.Scan(v); err != nil { 110 | return err 111 | } 112 | nui.Valid = true 113 | return nil 114 | } 115 | 116 | func (nui NullUniqueIdentifier) String() string { 117 | if nui.Valid { 118 | return nui.ID.String() 119 | } 120 | return "" 121 | } 122 | -------------------------------------------------------------------------------- /drivers/testdata/.gitignore: -------------------------------------------------------------------------------- 1 | *.actual.txt 2 | -------------------------------------------------------------------------------- /drivers/testdata/csvq/.gitignore: -------------------------------------------------------------------------------- 1 | *_copy 2 | -------------------------------------------------------------------------------- /drivers/testdata/csvq/staff.csv: -------------------------------------------------------------------------------- 1 | first_name,last_name,address_id,email,store_id,active,username,password,last_update 2 | John,Doe,1,john@invalid.com,1,true,jdoe,abc,2024-05-10T08:12:05.46875Z 3 | -------------------------------------------------------------------------------- /drivers/testdata/docker/Dockerfile: -------------------------------------------------------------------------------- 1 | ARG BASE_IMAGE 2 | FROM $BASE_IMAGE 3 | 4 | ARG SCHEMA_URL 5 | ARG TARGET 6 | ARG USER 7 | ADD --chown=$USER $SCHEMA_URL $TARGET/ 8 | RUN [ ! -d "$TARGET" ] || chmod -R 777 $TARGET/ || echo "failed to change perms of $TARGET, leaving as $(ls -la $TARGET/)" 9 | -------------------------------------------------------------------------------- /drivers/testdata/gen-golden.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | pgsql_in_docker=false 4 | pgsql_container=usql-pgsql 5 | 6 | if [ "$pgsql_in_docker" != true ]; then 7 | PGHOST="${PGHOST:-127.0.0.1}" 8 | port=$(docker port "$pgsql_container" 5432/tcp) 9 | PGPORT=${port##*:} 10 | else 11 | PGHOST="${PGHOST:-$pgsql_container}" 12 | PGPORT=5432 13 | fi 14 | PGUSER="${PGUSER:-postgres}" 15 | PGPASSWORD="${PGPASSWORD:-pw}" 16 | 17 | export PGHOST PGPORT PGUSER PGPASSWORD 18 | 19 | declare -A queries 20 | queries=( 21 | [descTable]="\d+ film*" 22 | [listTables]="\dtvmsE+ film*" 23 | [listFuncs]="\df+" 24 | [listIndexes]="\di+" 25 | [listSchemas]="\dn+" 26 | [listDbs]="\l+" 27 | ) 28 | 29 | for q in "${!queries[@]}"; do 30 | query="${queries[$q]}" 31 | cmd=(psql --no-psqlrc --command "$query") 32 | if [ "$pgsql_in_docker" == true ]; then 33 | docker run -it --rm -e PGHOST -e PGPORT -e PGUSER -e PGPASSWORD --link "$pgsql_container" postgres:13 "${cmd[@]}" >"pgsql.$q.golden.txt" 34 | else 35 | "${cmd[@]}" -o "pgsql.$q.golden.txt" 36 | fi 37 | done 38 | 39 | mysql_in_docker=true 40 | mysql_container=usql-mysql 41 | 42 | if [ "$mysql_in_docker" != true ]; then 43 | MYHOST="${MYHOST:-127.0.0.1}" 44 | port=$(docker port "$mysql_container" 3306/tcp) 45 | MYPORT=${port##*:} 46 | else 47 | MYHOST="${MYHOST:-$mysql_container}" 48 | MYPORT=3306 49 | fi 50 | MYUSER="${MYUSER:-root}" 51 | MYPASSWORD="${MYPASSWORD:-pw}" 52 | 53 | declare -A queries 54 | queries=( 55 | [descTable]="DESC film; SHOW INDEX FROM film; DESC film_actor; SHOW INDEX FROM film_actor; DESC film_category; SHOW INDEX FROM film_category; DESC film_list; SHOW INDEX FROM film_list; DESC film_text; SHOW INDEX FROM film_text;" 56 | [listTables]="SHOW TABLES LIKE 'film%'" 57 | [listSchemas]="SHOW DATABASES" 58 | ) 59 | 60 | for q in "${!queries[@]}"; do 61 | query="${queries[$q]}" 62 | cmd=(mysql -h "$MYHOST" -P "$MYPORT" -u "$MYUSER" --password="$MYPASSWORD" --no-auto-rehash --database sakila --execute "$query") 63 | if [ "$mysql_in_docker" == true ]; then 64 | docker run -it --rm --link "$mysql_container" mysql:8 "${cmd[@]}" 2>/dev/null >"mysql.$q.golden.txt" 65 | else 66 | "${cmd[@]}" 2>/dev/null >"mysql.$q.golden.txt" 67 | fi 68 | done 69 | -------------------------------------------------------------------------------- /drivers/testdata/mysql.listFuncs.expected.txt: -------------------------------------------------------------------------------- 1 | List of functions 2 | Schema | Name | Result data type | Argument data types | Type 3 | --------+----------------------------+------------------+---------------------------------------------------------------------------------------------+----------- 4 | sakila | film_in_stock | | p_film_id int, p_store_id int, OUT p_film_count int | PROCEDURE 5 | sakila | film_not_in_stock | | p_film_id int, p_store_id int, OUT p_film_count int | PROCEDURE 6 | sakila | get_customer_balance | decimal | p_customer_id int, p_effective_date datetime | FUNCTION 7 | sakila | inventory_held_by_customer | int | p_inventory_id int | FUNCTION 8 | sakila | inventory_in_stock | tinyint | p_inventory_id int | FUNCTION 9 | sakila | rewards_report | | min_monthly_purchases tinyint, min_dollar_amount_purchased decimal, OUT count_rewardees int | PROCEDURE 10 | (6 rows) 11 | -------------------------------------------------------------------------------- /drivers/testdata/mysql.listIndexes.expected.txt: -------------------------------------------------------------------------------- 1 | List of indexes 2 | Schema | Name | Type | Table | Primary? | Unique? 3 | --------+-----------------------------+----------+---------------+----------+--------- 4 | sakila | idx_actor_last_name | BTREE | actor | "NO" | "NO" 5 | sakila | PRIMARY | BTREE | actor | "YES" | "YES" 6 | sakila | idx_fk_city_id | BTREE | address | "NO" | "NO" 7 | sakila | PRIMARY | BTREE | address | "YES" | "YES" 8 | sakila | PRIMARY | BTREE | category | "YES" | "YES" 9 | sakila | idx_fk_country_id | BTREE | city | "NO" | "NO" 10 | sakila | PRIMARY | BTREE | city | "YES" | "YES" 11 | sakila | PRIMARY | BTREE | country | "YES" | "YES" 12 | sakila | idx_fk_address_id | BTREE | customer | "NO" | "NO" 13 | sakila | idx_fk_store_id | BTREE | customer | "NO" | "NO" 14 | sakila | idx_last_name | BTREE | customer | "NO" | "NO" 15 | sakila | PRIMARY | BTREE | customer | "YES" | "YES" 16 | sakila | idx_fk_language_id | BTREE | film | "NO" | "NO" 17 | sakila | idx_fk_original_language_id | BTREE | film | "NO" | "NO" 18 | sakila | idx_title | BTREE | film | "NO" | "NO" 19 | sakila | PRIMARY | BTREE | film | "YES" | "YES" 20 | sakila | idx_fk_film_id | BTREE | film_actor | "NO" | "NO" 21 | sakila | PRIMARY | BTREE | film_actor | "YES" | "YES" 22 | sakila | fk_film_category_category | BTREE | film_category | "NO" | "NO" 23 | sakila | PRIMARY | BTREE | film_category | "YES" | "YES" 24 | sakila | idx_title_description | FULLTEXT | film_text | "NO" | "NO" 25 | sakila | PRIMARY | BTREE | film_text | "YES" | "YES" 26 | sakila | idx_fk_film_id | BTREE | inventory | "NO" | "NO" 27 | sakila | idx_store_id_film_id | BTREE | inventory | "NO" | "NO" 28 | sakila | PRIMARY | BTREE | inventory | "YES" | "YES" 29 | sakila | PRIMARY | BTREE | language | "YES" | "YES" 30 | sakila | fk_payment_rental | BTREE | payment | "NO" | "NO" 31 | sakila | idx_fk_customer_id | BTREE | payment | "NO" | "NO" 32 | sakila | idx_fk_staff_id | BTREE | payment | "NO" | "NO" 33 | sakila | PRIMARY | BTREE | payment | "YES" | "YES" 34 | sakila | idx_fk_customer_id | BTREE | rental | "NO" | "NO" 35 | sakila | idx_fk_inventory_id | BTREE | rental | "NO" | "NO" 36 | sakila | idx_fk_staff_id | BTREE | rental | "NO" | "NO" 37 | sakila | PRIMARY | BTREE | rental | "YES" | "YES" 38 | sakila | rental_date | BTREE | rental | "NO" | "YES" 39 | sakila | idx_fk_address_id | BTREE | staff | "NO" | "NO" 40 | sakila | idx_fk_store_id | BTREE | staff | "NO" | "NO" 41 | sakila | PRIMARY | BTREE | staff | "YES" | "YES" 42 | sakila | idx_fk_address_id | BTREE | store | "NO" | "NO" 43 | sakila | idx_unique_manager | BTREE | store | "NO" | "YES" 44 | sakila | PRIMARY | BTREE | store | "YES" | "YES" 45 | (41 rows) 46 | -------------------------------------------------------------------------------- /drivers/testdata/mysql.listSchemas.expected.txt: -------------------------------------------------------------------------------- 1 | List of schemas 2 | Schema | Catalog 3 | --------+--------- 4 | sakila | def 5 | (1 row) 6 | -------------------------------------------------------------------------------- /drivers/testdata/mysql.listSchemas.golden.txt: -------------------------------------------------------------------------------- 1 | mysql: [Warning] Using a password on the command line interface can be insecure. 2 | +--------------------+ 3 | | Database | 4 | +--------------------+ 5 | | information_schema | 6 | | mysql | 7 | | performance_schema | 8 | | sakila | 9 | | sys | 10 | +--------------------+ 11 | -------------------------------------------------------------------------------- /drivers/testdata/mysql.listTables.expected.txt: -------------------------------------------------------------------------------- 1 | List of relations 2 | Schema | Name | Type | Rows | Size | Comment 3 | --------+---------------+------------+------+------+--------- 4 | sakila | film | BASE TABLE | 0 | | 5 | sakila | film_actor | BASE TABLE | 0 | | 6 | sakila | film_category | BASE TABLE | 0 | | 7 | sakila | film_text | BASE TABLE | 0 | | 8 | sakila | film_list | VIEW | 0 | | 9 | (5 rows) 10 | -------------------------------------------------------------------------------- /drivers/testdata/mysql.listTables.golden.txt: -------------------------------------------------------------------------------- 1 | mysql: [Warning] Using a password on the command line interface can be insecure. 2 | +--------------------------+ 3 | | Tables_in_sakila (film%) | 4 | +--------------------------+ 5 | | film | 6 | | film_actor | 7 | | film_category | 8 | | film_list | 9 | | film_text | 10 | +--------------------------+ 11 | -------------------------------------------------------------------------------- /drivers/testdata/pgsql.listDbs.golden.txt: -------------------------------------------------------------------------------- 1 | List of databases 2 | Name | Owner | Encoding | Collate | Ctype | Access privileges 3 | -----------+----------+----------+------------+------------+----------------------- 4 | postgres | postgres | UTF8 | en_US.utf8 | en_US.utf8 | 5 | template0 | postgres | UTF8 | en_US.utf8 | en_US.utf8 | =c/postgres + 6 | | | | | | postgres=CTc/postgres 7 | template1 | postgres | UTF8 | en_US.utf8 | en_US.utf8 | =c/postgres + 8 | | | | | | postgres=CTc/postgres 9 | (3 rows) 10 | 11 | -------------------------------------------------------------------------------- /drivers/testdata/pgsql.listFuncs.expected.txt: -------------------------------------------------------------------------------- 1 | List of functions 2 | Schema | Name | Result data type | Argument data types | Type 3 | --------+----------------------------+------------------+---------------------------------------------------------------------+---------- 4 | public | _group_concat | text | text, text | FUNCTION 5 | public | film_in_stock | integer | p_film_id integer, p_store_id integer, OUT p_film_count integer | FUNCTION 6 | public | film_not_in_stock | integer | p_film_id integer, p_store_id integer, OUT p_film_count integer | FUNCTION 7 | public | get_customer_balance | numeric | p_customer_id integer, p_effective_date timestamp without time zone | FUNCTION 8 | public | group_concat | text | text | 9 | public | inventory_held_by_customer | integer | p_inventory_id integer | FUNCTION 10 | public | inventory_in_stock | boolean | p_inventory_id integer | FUNCTION 11 | public | last_day | date | timestamp without time zone | FUNCTION 12 | public | last_updated | trigger | | FUNCTION 13 | public | rewards_report | USER-DEFINED | min_monthly_purchases integer, min_dollar_amount_purchased numeric | FUNCTION 14 | (10 rows) 15 | -------------------------------------------------------------------------------- /drivers/testdata/pgsql.listFuncs.golden.txt: -------------------------------------------------------------------------------- 1 | List of functions 2 | Schema | Name | Result data type | Argument data types | Type 3 | --------+----------------------------+------------------+---------------------------------------------------------------------+------ 4 | public | _group_concat | text | text, text | func 5 | public | film_in_stock | SETOF integer | p_film_id integer, p_store_id integer, OUT p_film_count integer | func 6 | public | film_not_in_stock | SETOF integer | p_film_id integer, p_store_id integer, OUT p_film_count integer | func 7 | public | get_customer_balance | numeric | p_customer_id integer, p_effective_date timestamp without time zone | func 8 | public | group_concat | text | text | agg 9 | public | inventory_held_by_customer | integer | p_inventory_id integer | func 10 | public | inventory_in_stock | boolean | p_inventory_id integer | func 11 | public | last_day | date | timestamp without time zone | func 12 | public | last_updated | trigger | | func 13 | public | rewards_report | SETOF customer | min_monthly_purchases integer, min_dollar_amount_purchased numeric | func 14 | (10 rows) 15 | 16 | -------------------------------------------------------------------------------- /drivers/testdata/pgsql.listIndexes.expected.txt: -------------------------------------------------------------------------------- 1 | List of indexes 2 | Schema | Name | Type | Table | Primary? | Unique? 3 | --------+-----------------------------------------------------+-------+------------------+----------+--------- 4 | public | actor_pkey | index | actor | "YES" | "YES" 5 | public | address_pkey | index | address | "YES" | "YES" 6 | public | category_pkey | index | category | "YES" | "YES" 7 | public | city_pkey | index | city | "YES" | "YES" 8 | public | country_pkey | index | country | "YES" | "YES" 9 | public | customer_pkey | index | customer | "YES" | "YES" 10 | public | film_actor_pkey | index | film_actor | "YES" | "YES" 11 | public | film_category_pkey | index | film_category | "YES" | "YES" 12 | public | film_fulltext_idx | index | film | "NO" | "NO" 13 | public | film_pkey | index | film | "YES" | "YES" 14 | public | idx_actor_last_name | index | actor | "NO" | "NO" 15 | public | idx_fk_address_id | index | customer | "NO" | "NO" 16 | public | idx_fk_city_id | index | address | "NO" | "NO" 17 | public | idx_fk_country_id | index | city | "NO" | "NO" 18 | public | idx_fk_customer_id | index | payment | "NO" | "NO" 19 | public | idx_fk_film_id | index | film_actor | "NO" | "NO" 20 | public | idx_fk_inventory_id | index | rental | "NO" | "NO" 21 | public | idx_fk_language_id | index | film | "NO" | "NO" 22 | public | idx_fk_original_language_id | index | film | "NO" | "NO" 23 | public | idx_fk_payment_p2007_01_customer_id | index | payment_p2007_01 | "NO" | "NO" 24 | public | idx_fk_payment_p2007_01_staff_id | index | payment_p2007_01 | "NO" | "NO" 25 | public | idx_fk_payment_p2007_02_customer_id | index | payment_p2007_02 | "NO" | "NO" 26 | public | idx_fk_payment_p2007_02_staff_id | index | payment_p2007_02 | "NO" | "NO" 27 | public | idx_fk_payment_p2007_03_customer_id | index | payment_p2007_03 | "NO" | "NO" 28 | public | idx_fk_payment_p2007_03_staff_id | index | payment_p2007_03 | "NO" | "NO" 29 | public | idx_fk_payment_p2007_04_customer_id | index | payment_p2007_04 | "NO" | "NO" 30 | public | idx_fk_payment_p2007_04_staff_id | index | payment_p2007_04 | "NO" | "NO" 31 | public | idx_fk_payment_p2007_05_customer_id | index | payment_p2007_05 | "NO" | "NO" 32 | public | idx_fk_payment_p2007_05_staff_id | index | payment_p2007_05 | "NO" | "NO" 33 | public | idx_fk_payment_p2007_06_customer_id | index | payment_p2007_06 | "NO" | "NO" 34 | public | idx_fk_payment_p2007_06_staff_id | index | payment_p2007_06 | "NO" | "NO" 35 | public | idx_fk_staff_id | index | payment | "NO" | "NO" 36 | public | idx_fk_store_id | index | customer | "NO" | "NO" 37 | public | idx_last_name | index | customer | "NO" | "NO" 38 | public | idx_store_id_film_id | index | inventory | "NO" | "NO" 39 | public | idx_title | index | film | "NO" | "NO" 40 | public | idx_unq_manager_staff_id | index | store | "YES" | "NO" 41 | public | idx_unq_rental_rental_date_inventory_id_customer_id | index | rental | "YES" | "NO" 42 | public | inventory_pkey | index | inventory | "YES" | "YES" 43 | public | language_pkey | index | language | "YES" | "YES" 44 | public | payment_pkey | index | payment | "YES" | "YES" 45 | public | rental_pkey | index | rental | "YES" | "YES" 46 | public | staff_pkey | index | staff | "YES" | "YES" 47 | public | store_pkey | index | store | "YES" | "YES" 48 | (44 rows) 49 | -------------------------------------------------------------------------------- /drivers/testdata/pgsql.listIndexes.golden.txt: -------------------------------------------------------------------------------- 1 | List of relations 2 | Schema | Name | Type | Owner | Table 3 | --------+-----------------------------------------------------+-------+----------+------------------ 4 | public | actor_pkey | index | postgres | actor 5 | public | address_pkey | index | postgres | address 6 | public | category_pkey | index | postgres | category 7 | public | city_pkey | index | postgres | city 8 | public | country_pkey | index | postgres | country 9 | public | customer_pkey | index | postgres | customer 10 | public | film_actor_pkey | index | postgres | film_actor 11 | public | film_category_pkey | index | postgres | film_category 12 | public | film_fulltext_idx | index | postgres | film 13 | public | film_pkey | index | postgres | film 14 | public | idx_actor_last_name | index | postgres | actor 15 | public | idx_fk_address_id | index | postgres | customer 16 | public | idx_fk_city_id | index | postgres | address 17 | public | idx_fk_country_id | index | postgres | city 18 | public | idx_fk_customer_id | index | postgres | payment 19 | public | idx_fk_film_id | index | postgres | film_actor 20 | public | idx_fk_inventory_id | index | postgres | rental 21 | public | idx_fk_language_id | index | postgres | film 22 | public | idx_fk_original_language_id | index | postgres | film 23 | public | idx_fk_payment_p2007_01_customer_id | index | postgres | payment_p2007_01 24 | public | idx_fk_payment_p2007_01_staff_id | index | postgres | payment_p2007_01 25 | public | idx_fk_payment_p2007_02_customer_id | index | postgres | payment_p2007_02 26 | public | idx_fk_payment_p2007_02_staff_id | index | postgres | payment_p2007_02 27 | public | idx_fk_payment_p2007_03_customer_id | index | postgres | payment_p2007_03 28 | public | idx_fk_payment_p2007_03_staff_id | index | postgres | payment_p2007_03 29 | public | idx_fk_payment_p2007_04_customer_id | index | postgres | payment_p2007_04 30 | public | idx_fk_payment_p2007_04_staff_id | index | postgres | payment_p2007_04 31 | public | idx_fk_payment_p2007_05_customer_id | index | postgres | payment_p2007_05 32 | public | idx_fk_payment_p2007_05_staff_id | index | postgres | payment_p2007_05 33 | public | idx_fk_payment_p2007_06_customer_id | index | postgres | payment_p2007_06 34 | public | idx_fk_payment_p2007_06_staff_id | index | postgres | payment_p2007_06 35 | public | idx_fk_staff_id | index | postgres | payment 36 | public | idx_fk_store_id | index | postgres | customer 37 | public | idx_last_name | index | postgres | customer 38 | public | idx_store_id_film_id | index | postgres | inventory 39 | public | idx_title | index | postgres | film 40 | public | idx_unq_manager_staff_id | index | postgres | store 41 | public | idx_unq_rental_rental_date_inventory_id_customer_id | index | postgres | rental 42 | public | inventory_pkey | index | postgres | inventory 43 | public | language_pkey | index | postgres | language 44 | public | payment_pkey | index | postgres | payment 45 | public | rental_pkey | index | postgres | rental 46 | public | staff_pkey | index | postgres | staff 47 | public | store_pkey | index | postgres | store 48 | (44 rows) 49 | 50 | -------------------------------------------------------------------------------- /drivers/testdata/pgsql.listSchemas.expected.txt: -------------------------------------------------------------------------------- 1 | List of schemas 2 | Schema | Catalog 3 | --------+---------- 4 | public | postgres 5 | (1 row) 6 | -------------------------------------------------------------------------------- /drivers/testdata/pgsql.listSchemas.golden.txt: -------------------------------------------------------------------------------- 1 | List of schemas 2 | Name | Owner 3 | --------+---------- 4 | public | postgres 5 | (1 row) 6 | 7 | -------------------------------------------------------------------------------- /drivers/testdata/pgsql.listTables.expected.txt: -------------------------------------------------------------------------------- 1 | List of relations 2 | Schema | Name | Type | Rows | Size | Comment 3 | --------+------------------+----------+------+------------+--------- 4 | public | film_film_id_seq | sequence | 1 | 8192 bytes | 5 | public | film | table | 0 | 8192 bytes | 6 | public | film_actor | table | 0 | 0 bytes | 7 | public | film_category | table | 0 | 0 bytes | 8 | public | film_list | view | 0 | 0 bytes | 9 | (5 rows) 10 | -------------------------------------------------------------------------------- /drivers/testdata/pgsql.listTables.golden.txt: -------------------------------------------------------------------------------- 1 | List of relations 2 | Schema | Name | Type | Owner 3 | --------+------------------+----------+---------- 4 | public | film | table | postgres 5 | public | film_actor | table | postgres 6 | public | film_category | table | postgres 7 | public | film_film_id_seq | sequence | postgres 8 | public | film_list | view | postgres 9 | (5 rows) 10 | 11 | -------------------------------------------------------------------------------- /drivers/testdata/sqlserver.descTable.expected.txt: -------------------------------------------------------------------------------- 1 | BASE TABLE "dbo.film" 2 | Name | Type | Nullable | Default | Size | Decimal Digits | Radix | Octet Length 3 | ----------------------+--------------+----------+-------------+------------+----------------+-------+-------------- 4 | film_id | int | "NO" | | 10 | 0 | 10 | 0 5 | title | varchar(255) | "NO" | | 255 | 0 | 10 | 255 6 | description | text | "YES" | (NULL) | 2147483647 | 0 | 10 | 2147483647 7 | release_year | varchar(4) | "YES" | | 4 | 0 | 10 | 4 8 | language_id | int | "NO" | | 10 | 0 | 10 | 0 9 | original_language_id | int | "YES" | (NULL) | 10 | 0 | 10 | 0 10 | rental_duration | tinyint | "NO" | ((3)) | 3 | 0 | 10 | 0 11 | rental_rate | decimal(4,2) | "NO" | ((4.99)) | 4 | 2 | 10 | 0 12 | length | smallint | "YES" | (NULL) | 5 | 0 | 10 | 0 13 | replacement_cost | decimal(5,2) | "NO" | ((19.99)) | 5 | 2 | 10 | 0 14 | rating | varchar(10) | "YES" | ('G') | 10 | 0 | 10 | 10 15 | special_features | varchar(255) | "YES" | (NULL) | 255 | 0 | 10 | 255 16 | last_update | datetime | "NO" | (getdate()) | 3 | 0 | 10 | 0 17 | Indexes: 18 | "" HEAP (language_id, original_language_id, film_id) 19 | "idx_fk_language_id" NONCLUSTERED (language_id) 20 | "idx_fk_original_language_id" NONCLUSTERED (original_language_id) 21 | "PK__film__349764A85F0D1F82" PRIMARY_KEY, UNIQUE, NONCLUSTERED (film_id) 22 | 23 | BASE TABLE "dbo.film_actor" 24 | Name | Type | Nullable | Default | Size | Decimal Digits | Radix | Octet Length 25 | -------------+----------+----------+-------------+------+----------------+-------+-------------- 26 | actor_id | int | "NO" | | 10 | 0 | 10 | 0 27 | film_id | int | "NO" | | 10 | 0 | 10 | 0 28 | last_update | datetime | "NO" | (getdate()) | 3 | 0 | 10 | 0 29 | Indexes: 30 | "" HEAP (actor_id, film_id, actor_id, film_id) 31 | "idx_fk_film_actor_actor" NONCLUSTERED (actor_id) 32 | "idx_fk_film_actor_film" NONCLUSTERED (film_id) 33 | "PK__film_act__086D31FFE010698E" PRIMARY_KEY, UNIQUE, NONCLUSTERED (actor_id, film_id) 34 | 35 | BASE TABLE "dbo.film_category" 36 | Name | Type | Nullable | Default | Size | Decimal Digits | Radix | Octet Length 37 | -------------+----------+----------+-------------+------+----------------+-------+-------------- 38 | film_id | int | "NO" | | 10 | 0 | 10 | 0 39 | category_id | int | "NO" | | 10 | 0 | 10 | 0 40 | last_update | datetime | "NO" | (getdate()) | 3 | 0 | 10 | 0 41 | Indexes: 42 | "" HEAP (category_id, film_id, film_id, category_id) 43 | "idx_fk_film_category_category" NONCLUSTERED (category_id) 44 | "idx_fk_film_category_film" NONCLUSTERED (film_id) 45 | "PK__film_cat__69C38A33EABC8336" PRIMARY_KEY, UNIQUE, NONCLUSTERED (film_id, category_id) 46 | 47 | BASE TABLE "dbo.film_text" 48 | Name | Type | Nullable | Default | Size | Decimal Digits | Radix | Octet Length 49 | -------------+--------------+----------+---------+------------+----------------+-------+-------------- 50 | film_id | int | "NO" | | 10 | 0 | 10 | 0 51 | title | varchar(255) | "NO" | | 255 | 0 | 10 | 255 52 | description | text | "YES" | | 2147483647 | 0 | 10 | 2147483647 53 | Indexes: 54 | "" HEAP (film_id) 55 | "PK__film_tex__349764A85D245C83" PRIMARY_KEY, UNIQUE, NONCLUSTERED (film_id) 56 | 57 | VIEW "dbo.film_list" 58 | Name | Type | Nullable | Default | Size | Decimal Digits | Radix | Octet Length 59 | -------------+--------------+----------+---------+------------+----------------+-------+-------------- 60 | FID | int | "YES" | | 10 | 0 | 10 | 0 61 | title | varchar(255) | "YES" | | 255 | 0 | 10 | 255 62 | description | text | "YES" | | 2147483647 | 0 | 10 | 2147483647 63 | category | varchar(25) | "NO" | | 25 | 0 | 10 | 25 64 | price | decimal(4,2) | "YES" | | 4 | 2 | 10 | 0 65 | length | smallint | "YES" | | 5 | 0 | 10 | 0 66 | rating | varchar(10) | "YES" | | 10 | 0 | 10 | 10 67 | actors | varchar(91) | "NO" | | 91 | 0 | 10 | 91 68 | 69 | -------------------------------------------------------------------------------- /drivers/testdata/sqlserver.listFuncs.expected.txt: -------------------------------------------------------------------------------- 1 | (0 rows) 2 | -------------------------------------------------------------------------------- /drivers/testdata/sqlserver.listIndexes.expected.txt: -------------------------------------------------------------------------------- 1 | List of indexes 2 | Schema | Name | Type | Table | Primary? | Unique? 3 | --------+--------------------------------+--------------+---------------+----------+--------- 4 | dbo | | HEAP | actor | "NO" | "NO" 5 | dbo | idx_actor_last_name | NONCLUSTERED | actor | "NO" | "NO" 6 | dbo | PK__actor__8B2447B565179537 | NONCLUSTERED | actor | "YES" | "YES" 7 | dbo | | HEAP | address | "NO" | "NO" 8 | dbo | idx_fk_city_id | NONCLUSTERED | address | "NO" | "NO" 9 | dbo | PK__address__CAA247C920A28CDC | NONCLUSTERED | address | "YES" | "YES" 10 | dbo | | HEAP | category | "NO" | "NO" 11 | dbo | PK__category__D54EE9B5C54BFE50 | NONCLUSTERED | category | "YES" | "YES" 12 | dbo | | HEAP | city | "NO" | "NO" 13 | dbo | idx_fk_country_id | NONCLUSTERED | city | "NO" | "NO" 14 | dbo | PK__city__031491A980EA569B | NONCLUSTERED | city | "YES" | "YES" 15 | dbo | | HEAP | country | "NO" | "NO" 16 | dbo | PK__country__7E8CD054CAE966BB | NONCLUSTERED | country | "YES" | "YES" 17 | dbo | | HEAP | customer | "NO" | "NO" 18 | dbo | idx_fk_address_id | NONCLUSTERED | customer | "NO" | "NO" 19 | dbo | idx_fk_store_id | NONCLUSTERED | customer | "NO" | "NO" 20 | dbo | idx_last_name | NONCLUSTERED | customer | "NO" | "NO" 21 | dbo | PK__customer__CD65CB84BB7D0A31 | NONCLUSTERED | customer | "YES" | "YES" 22 | dbo | | HEAP | film | "NO" | "NO" 23 | dbo | idx_fk_language_id | NONCLUSTERED | film | "NO" | "NO" 24 | dbo | idx_fk_original_language_id | NONCLUSTERED | film | "NO" | "NO" 25 | dbo | PK__film__349764A85F0D1F82 | NONCLUSTERED | film | "YES" | "YES" 26 | dbo | | HEAP | film_actor | "NO" | "NO" 27 | dbo | idx_fk_film_actor_actor | NONCLUSTERED | film_actor | "NO" | "NO" 28 | dbo | idx_fk_film_actor_film | NONCLUSTERED | film_actor | "NO" | "NO" 29 | dbo | PK__film_act__086D31FFE010698E | NONCLUSTERED | film_actor | "YES" | "YES" 30 | dbo | | HEAP | film_category | "NO" | "NO" 31 | dbo | idx_fk_film_category_category | NONCLUSTERED | film_category | "NO" | "NO" 32 | dbo | idx_fk_film_category_film | NONCLUSTERED | film_category | "NO" | "NO" 33 | dbo | PK__film_cat__69C38A33EABC8336 | NONCLUSTERED | film_category | "YES" | "YES" 34 | dbo | | HEAP | film_text | "NO" | "NO" 35 | dbo | PK__film_tex__349764A85D245C83 | NONCLUSTERED | film_text | "YES" | "YES" 36 | dbo | | HEAP | inventory | "NO" | "NO" 37 | dbo | idx_fk_film_id | NONCLUSTERED | inventory | "NO" | "NO" 38 | dbo | idx_fk_film_id_store_id | NONCLUSTERED | inventory | "NO" | "NO" 39 | dbo | PK__inventor__B59ACC48C0DED777 | NONCLUSTERED | inventory | "YES" | "YES" 40 | dbo | | HEAP | language | "NO" | "NO" 41 | dbo | PK__language__804CF6B2AD65E24B | NONCLUSTERED | language | "YES" | "YES" 42 | dbo | | HEAP | payment | "NO" | "NO" 43 | dbo | idx_fk_customer_id | NONCLUSTERED | payment | "NO" | "NO" 44 | dbo | idx_fk_staff_id | NONCLUSTERED | payment | "NO" | "NO" 45 | dbo | PK__payment__ED1FC9EBDD7F3474 | NONCLUSTERED | payment | "YES" | "YES" 46 | dbo | | HEAP | rental | "NO" | "NO" 47 | dbo | idx_fk_customer_id | NONCLUSTERED | rental | "NO" | "NO" 48 | dbo | idx_fk_inventory_id | NONCLUSTERED | rental | "NO" | "NO" 49 | dbo | idx_fk_staff_id | NONCLUSTERED | rental | "NO" | "NO" 50 | dbo | idx_uq | NONCLUSTERED | rental | "YES" | "NO" 51 | dbo | PK__rental__67DB611A79AF93E5 | NONCLUSTERED | rental | "YES" | "YES" 52 | dbo | | HEAP | staff | "NO" | "NO" 53 | dbo | idx_fk_address_id | NONCLUSTERED | staff | "NO" | "NO" 54 | dbo | idx_fk_store_id | NONCLUSTERED | staff | "NO" | "NO" 55 | dbo | PK__staff__1963DD9DFC0374BE | NONCLUSTERED | staff | "YES" | "YES" 56 | dbo | | HEAP | staff_copy | "NO" | "NO" 57 | dbo | | HEAP | store | "NO" | "NO" 58 | dbo | idx_fk_address_id | NONCLUSTERED | store | "YES" | "NO" 59 | dbo | idx_fk_store_address | NONCLUSTERED | store | "NO" | "NO" 60 | dbo | PK__store__A2F2A30D66044831 | NONCLUSTERED | store | "YES" | "YES" 61 | (57 rows) 62 | -------------------------------------------------------------------------------- /drivers/testdata/sqlserver.listSchemas.expected.txt: -------------------------------------------------------------------------------- 1 | List of schemas 2 | Schema | Catalog 3 | --------+--------- 4 | dbo | sakila 5 | guest | sakila 6 | (2 rows) 7 | -------------------------------------------------------------------------------- /drivers/testdata/sqlserver.listTables.expected.txt: -------------------------------------------------------------------------------- 1 | List of relations 2 | Schema | Name | Type | Rows | Size | Comment 3 | --------+---------------+------------+------+------+--------- 4 | dbo | film | BASE TABLE | 0 | | 5 | dbo | film_actor | BASE TABLE | 0 | | 6 | dbo | film_category | BASE TABLE | 0 | | 7 | dbo | film_text | BASE TABLE | 0 | | 8 | dbo | film_list | VIEW | 0 | | 9 | (5 rows) 10 | -------------------------------------------------------------------------------- /drivers/testdata/trino.listSchemas.expected.txt: -------------------------------------------------------------------------------- 1 | List of schemas 2 | Schema | Catalog 3 | ----------+--------- 4 | sf1 | tpch 5 | sf100 | tpch 6 | sf1000 | tpch 7 | sf10000 | tpch 8 | sf100000 | tpch 9 | sf300 | tpch 10 | sf3000 | tpch 11 | sf30000 | tpch 12 | tiny | tpch 13 | (9 rows) 14 | -------------------------------------------------------------------------------- /drivers/testdata/trino.listTables.expected.txt: -------------------------------------------------------------------------------- 1 | List of relations 2 | Schema | Name | Type | Rows | Size | Comment 3 | ----------+--------+------------+------+------+--------- 4 | sf1 | orders | BASE TABLE | 0 | | 5 | sf100 | orders | BASE TABLE | 0 | | 6 | sf1000 | orders | BASE TABLE | 0 | | 7 | sf10000 | orders | BASE TABLE | 0 | | 8 | sf100000 | orders | BASE TABLE | 0 | | 9 | sf300 | orders | BASE TABLE | 0 | | 10 | sf3000 | orders | BASE TABLE | 0 | | 11 | sf30000 | orders | BASE TABLE | 0 | | 12 | tiny | orders | BASE TABLE | 0 | | 13 | (9 rows) 14 | -------------------------------------------------------------------------------- /drivers/trino/reader.go: -------------------------------------------------------------------------------- 1 | package trino 2 | 3 | import ( 4 | "database/sql" 5 | "fmt" 6 | "strings" 7 | 8 | "github.com/xo/usql/drivers/metadata" 9 | ) 10 | 11 | type metaReader struct { 12 | metadata.LoggingReader 13 | } 14 | 15 | var _ metadata.CatalogReader = &metaReader{} 16 | var _ metadata.ColumnStatReader = &metaReader{} 17 | 18 | func (r metaReader) Catalogs(metadata.Filter) (*metadata.CatalogSet, error) { 19 | qstr := `SHOW catalogs` 20 | rows, closeRows, err := r.Query(qstr) 21 | if err != nil { 22 | return nil, err 23 | } 24 | defer closeRows() 25 | 26 | results := []metadata.Catalog{} 27 | for rows.Next() { 28 | rec := metadata.Catalog{} 29 | err = rows.Scan(&rec.Catalog) 30 | if err != nil { 31 | return nil, err 32 | } 33 | results = append(results, rec) 34 | } 35 | if rows.Err() != nil { 36 | return nil, rows.Err() 37 | } 38 | return metadata.NewCatalogSet(results), nil 39 | } 40 | 41 | func (r metaReader) ColumnStats(f metadata.Filter) (*metadata.ColumnStatSet, error) { 42 | names := []string{} 43 | if f.Catalog != "" { 44 | names = append(names, f.Catalog+".") 45 | } 46 | if f.Schema != "" { 47 | names = append(names, f.Schema+".") 48 | } 49 | names = append(names, f.Parent) 50 | rows, closeRows, err := r.Query(fmt.Sprintf("SHOW STATS FOR %s", strings.Join(names, ""))) 51 | if err != nil { 52 | return nil, err 53 | } 54 | defer closeRows() 55 | 56 | results := []metadata.ColumnStat{} 57 | for rows.Next() { 58 | rec := metadata.ColumnStat{Catalog: f.Catalog, Schema: f.Schema, Table: f.Parent} 59 | name := sql.NullString{} 60 | avgWidth := sql.NullInt32{} 61 | numDistinct := sql.NullInt64{} 62 | nullFrac := sql.NullFloat64{} 63 | numRows := sql.NullInt64{} 64 | min := sql.NullString{} 65 | max := sql.NullString{} 66 | err = rows.Scan( 67 | &name, 68 | &avgWidth, 69 | &numDistinct, 70 | &nullFrac, 71 | &numRows, 72 | &min, 73 | &max, 74 | ) 75 | if err != nil { 76 | return nil, err 77 | } 78 | if !name.Valid { 79 | continue 80 | } 81 | rec.Name = name.String 82 | if avgWidth.Valid { 83 | rec.AvgWidth = int(avgWidth.Int32) 84 | } 85 | if numDistinct.Valid { 86 | rec.NumDistinct = numDistinct.Int64 87 | } 88 | if nullFrac.Valid { 89 | rec.NullFrac = nullFrac.Float64 90 | } 91 | if min.Valid { 92 | rec.Min = min.String 93 | } 94 | if max.Valid { 95 | rec.Max = max.String 96 | } 97 | results = append(results, rec) 98 | } 99 | if rows.Err() != nil { 100 | return nil, rows.Err() 101 | } 102 | 103 | return metadata.NewColumnStatSet(results), nil 104 | } 105 | -------------------------------------------------------------------------------- /drivers/trino/trino.go: -------------------------------------------------------------------------------- 1 | // Package trino defines and registers usql's Trino driver. 2 | // 3 | // See: https://github.com/trinodb/trino-go-client 4 | package trino 5 | 6 | import ( 7 | "context" 8 | "io" 9 | 10 | _ "github.com/trinodb/trino-go-client/trino" // DRIVER 11 | "github.com/xo/usql/drivers" 12 | "github.com/xo/usql/drivers/metadata" 13 | infos "github.com/xo/usql/drivers/metadata/informationschema" 14 | ) 15 | 16 | func init() { 17 | newReader := func(db drivers.DB, opts ...metadata.ReaderOption) metadata.Reader { 18 | ir := infos.New( 19 | infos.WithPlaceholder(func(int) string { return "?" }), 20 | infos.WithCustomClauses(map[infos.ClauseName]string{ 21 | infos.ColumnsColumnSize: "0", 22 | infos.ColumnsNumericScale: "0", 23 | infos.ColumnsNumericPrecRadix: "0", 24 | infos.ColumnsCharOctetLength: "0", 25 | }), 26 | infos.WithFunctions(false), 27 | infos.WithSequences(false), 28 | infos.WithIndexes(false), 29 | infos.WithConstraints(false), 30 | infos.WithColumnPrivileges(false), 31 | infos.WithUsagePrivileges(false), 32 | )(db, opts...) 33 | mr := &metaReader{ 34 | LoggingReader: metadata.NewLoggingReader(db, opts...), 35 | } 36 | return metadata.NewPluginReader(ir, mr) 37 | } 38 | drivers.Register("trino", drivers.Driver{ 39 | AllowMultilineComments: true, 40 | Process: drivers.StripTrailingSemicolon, 41 | Version: func(ctx context.Context, db drivers.DB) (string, error) { 42 | var ver string 43 | err := db.QueryRowContext( 44 | ctx, 45 | `SELECT node_version FROM system.runtime.nodes LIMIT 1`, 46 | ).Scan(&ver) 47 | if err != nil { 48 | return "", err 49 | } 50 | return "Trino " + ver, nil 51 | }, 52 | NewMetadataReader: newReader, 53 | NewMetadataWriter: func(db drivers.DB, w io.Writer, opts ...metadata.ReaderOption) metadata.Writer { 54 | return metadata.NewDefaultWriter(newReader(db, opts...))(db, w) 55 | }, 56 | Copy: drivers.CopyWithInsert(func(int) string { return "?" }), 57 | }) 58 | } 59 | -------------------------------------------------------------------------------- /drivers/vertica/vertica.go: -------------------------------------------------------------------------------- 1 | // Package vertica defines and registers usql's Vertica driver. 2 | // 3 | // See: https://github.com/vertica/vertica-sql-go 4 | package vertica 5 | 6 | import ( 7 | "context" 8 | "crypto/tls" 9 | "crypto/x509" 10 | "database/sql" 11 | "errors" 12 | "io" 13 | "net/url" 14 | "os" 15 | "regexp" 16 | "strings" 17 | 18 | vertica "github.com/vertica/vertica-sql-go" // DRIVER 19 | "github.com/vertica/vertica-sql-go/logger" 20 | "github.com/xo/dburl" 21 | "github.com/xo/usql/drivers" 22 | ) 23 | 24 | func init() { 25 | // turn off logging 26 | if os.Getenv("VERTICA_SQL_GO_LOG_LEVEL") == "" { 27 | logger.SetLogLevel(logger.NONE) 28 | } 29 | 30 | errCodeRE := regexp.MustCompile(`(?i)^\[([0-9a-z]+)\]\s+(.+)`) 31 | drivers.Register("vertica", drivers.Driver{ 32 | AllowDollar: true, 33 | AllowMultilineComments: true, 34 | Version: func(ctx context.Context, db drivers.DB) (string, error) { 35 | var ver string 36 | if err := db.QueryRowContext(ctx, `SELECT version()`).Scan(&ver); err != nil { 37 | return "", err 38 | } 39 | return ver, nil 40 | }, 41 | Open: func(_ context.Context, u *dburl.URL, stdout, stderr func() io.Writer) (func(string, string) (*sql.DB, error), error) { 42 | return func(driver, dsn string) (*sql.DB, error) { 43 | u, err := url.Parse(dsn) 44 | if err != nil { 45 | return nil, err 46 | } 47 | q := u.Query() 48 | if name := q.Get("ca_path"); name != "" { 49 | if q.Get("tlsmode") != "server-strict" { 50 | return nil, errors.New("tlsmode must be set to server-strict: ca_path is set") 51 | } 52 | cfg := &tls.Config{ 53 | ServerName: u.Hostname(), 54 | } 55 | if err := addCA(name, cfg); err != nil { 56 | return nil, err 57 | } 58 | if err := vertica.RegisterTLSConfig("custom_tls_config", cfg); err != nil { 59 | return nil, err 60 | } 61 | q.Set("tlsmode", "custom_tls_config") 62 | } 63 | return sql.Open(driver, u.String()) 64 | }, nil 65 | }, 66 | ChangePassword: func(db drivers.DB, user, newpw, _ string) error { 67 | _, err := db.Exec(`ALTER USER ` + user + ` IDENTIFIED BY '` + newpw + `'`) 68 | return err 69 | }, 70 | Err: func(err error) (string, string) { 71 | msg := strings.TrimSpace(strings.TrimPrefix(err.Error(), "Error:")) 72 | if m := errCodeRE.FindAllStringSubmatch(msg, -1); m != nil { 73 | return m[0][1], strings.TrimSpace(m[0][2]) 74 | } 75 | return "", msg 76 | }, 77 | IsPasswordErr: func(err error) bool { 78 | return strings.HasSuffix(strings.TrimSpace(err.Error()), "Invalid username or password") 79 | }, 80 | }) 81 | } 82 | 83 | // addCA adds the specified file name as a ca to the tls config. 84 | func addCA(name string, cfg *tls.Config) error { 85 | pool := x509.NewCertPool() 86 | switch pem, err := os.ReadFile(name); { 87 | case err != nil: 88 | return err 89 | case !pool.AppendCertsFromPEM(pem): 90 | return errors.New("failed to append pem to cert pool") 91 | } 92 | cfg.RootCAs = pool 93 | return nil 94 | } 95 | -------------------------------------------------------------------------------- /drivers/voltdb/voltdb.go: -------------------------------------------------------------------------------- 1 | // Package voltdb defines and registers usql's VoltDB driver. 2 | // 3 | // See: https://github.com/VoltDB/voltdb-client-go 4 | package voltdb 5 | 6 | import ( 7 | _ "github.com/VoltDB/voltdb-client-go/voltdbclient" // DRIVER 8 | "github.com/xo/usql/drivers" 9 | ) 10 | 11 | func init() { 12 | drivers.Register("voltdb", drivers.Driver{ 13 | AllowMultilineComments: true, 14 | }) 15 | } 16 | -------------------------------------------------------------------------------- /drivers/ydb/ydb.go: -------------------------------------------------------------------------------- 1 | // Package ydb defines and registers usql's YDB driver. 2 | // 3 | // See: https://github.com/ydb-platform/ydb-go-sdk 4 | package ydb 5 | 6 | import ( 7 | "context" 8 | "errors" 9 | "strconv" 10 | 11 | "github.com/xo/usql/drivers" 12 | "github.com/ydb-platform/ydb-go-sdk/v3" // DRIVER 13 | ) 14 | 15 | func init() { 16 | drivers.Register("ydb", drivers.Driver{ 17 | Version: func(ctx context.Context, db drivers.DB) (string, error) { 18 | var ver string 19 | if err := db.QueryRowContext(ctx, `SELECT '' AS version`).Scan(&ver); err != nil { 20 | return "", err 21 | } 22 | return "YDB " + ver, nil 23 | }, 24 | Err: func(err error) (string, string) { 25 | var e ydb.Error 26 | if errors.As(err, &e) { 27 | return strconv.Itoa(int(e.Code())), e.Error() 28 | } 29 | return "", err.Error() 30 | }, 31 | }) 32 | } 33 | -------------------------------------------------------------------------------- /internal/adodb.go: -------------------------------------------------------------------------------- 1 | //go:build (all || most || adodb) && !no_adodb 2 | 3 | package internal 4 | 5 | // Code generated by gen.go. DO NOT EDIT. 6 | 7 | import ( 8 | _ "github.com/xo/usql/drivers/adodb" // Microsoft ADODB driver 9 | ) 10 | -------------------------------------------------------------------------------- /internal/athena.go: -------------------------------------------------------------------------------- 1 | //go:build (all || most || athena) && !no_athena 2 | 3 | package internal 4 | 5 | // Code generated by gen.go. DO NOT EDIT. 6 | 7 | import ( 8 | _ "github.com/xo/usql/drivers/athena" // AWS Athena driver 9 | ) 10 | -------------------------------------------------------------------------------- /internal/avatica.go: -------------------------------------------------------------------------------- 1 | //go:build (all || most || avatica) && !no_avatica 2 | 3 | package internal 4 | 5 | // Code generated by gen.go. DO NOT EDIT. 6 | 7 | import ( 8 | _ "github.com/xo/usql/drivers/avatica" // Apache Avatica driver 9 | ) 10 | -------------------------------------------------------------------------------- /internal/bigquery.go: -------------------------------------------------------------------------------- 1 | //go:build (all || most || bigquery) && !no_bigquery 2 | 3 | package internal 4 | 5 | // Code generated by gen.go. DO NOT EDIT. 6 | 7 | import ( 8 | _ "github.com/xo/usql/drivers/bigquery" // Google BigQuery driver 9 | ) 10 | -------------------------------------------------------------------------------- /internal/cassandra.go: -------------------------------------------------------------------------------- 1 | //go:build (all || most || cassandra) && !no_cassandra 2 | 3 | package internal 4 | 5 | // Code generated by gen.go. DO NOT EDIT. 6 | 7 | import ( 8 | _ "github.com/xo/usql/drivers/cassandra" // Cassandra driver 9 | ) 10 | -------------------------------------------------------------------------------- /internal/chai.go: -------------------------------------------------------------------------------- 1 | //go:build (all || most || chai) && !no_chai 2 | 3 | package internal 4 | 5 | // Code generated by gen.go. DO NOT EDIT. 6 | 7 | import ( 8 | _ "github.com/xo/usql/drivers/chai" // ChaiSQL driver 9 | ) 10 | -------------------------------------------------------------------------------- /internal/clickhouse.go: -------------------------------------------------------------------------------- 1 | //go:build (!no_base || clickhouse) && !no_clickhouse 2 | 3 | package internal 4 | 5 | // Code generated by gen.go. DO NOT EDIT. 6 | 7 | import ( 8 | _ "github.com/xo/usql/drivers/clickhouse" // ClickHouse driver 9 | ) 10 | -------------------------------------------------------------------------------- /internal/cosmos.go: -------------------------------------------------------------------------------- 1 | //go:build (all || most || cosmos) && !no_cosmos 2 | 3 | package internal 4 | 5 | // Code generated by gen.go. DO NOT EDIT. 6 | 7 | import ( 8 | _ "github.com/xo/usql/drivers/cosmos" // Azure CosmosDB driver 9 | ) 10 | -------------------------------------------------------------------------------- /internal/couchbase.go: -------------------------------------------------------------------------------- 1 | //go:build (all || most || couchbase) && !no_couchbase 2 | 3 | package internal 4 | 5 | // Code generated by gen.go. DO NOT EDIT. 6 | 7 | import ( 8 | _ "github.com/xo/usql/drivers/couchbase" // Couchbase driver 9 | ) 10 | -------------------------------------------------------------------------------- /internal/csvq.go: -------------------------------------------------------------------------------- 1 | //go:build (!no_base || csvq) && !no_csvq 2 | 3 | package internal 4 | 5 | // Code generated by gen.go. DO NOT EDIT. 6 | 7 | import ( 8 | _ "github.com/xo/usql/drivers/csvq" // CSVQ driver 9 | ) 10 | -------------------------------------------------------------------------------- /internal/databend.go: -------------------------------------------------------------------------------- 1 | //go:build (all || most || databend) && !no_databend 2 | 3 | package internal 4 | 5 | // Code generated by gen.go. DO NOT EDIT. 6 | 7 | import ( 8 | _ "github.com/xo/usql/drivers/databend" // Databend driver 9 | ) 10 | -------------------------------------------------------------------------------- /internal/databricks.go: -------------------------------------------------------------------------------- 1 | //go:build (all || most || databricks) && !no_databricks 2 | 3 | package internal 4 | 5 | // Code generated by gen.go. DO NOT EDIT. 6 | 7 | import ( 8 | _ "github.com/xo/usql/drivers/databricks" // Databricks driver 9 | ) 10 | -------------------------------------------------------------------------------- /internal/duckdb.go: -------------------------------------------------------------------------------- 1 | //go:build (all || most || duckdb) && !no_duckdb 2 | 3 | package internal 4 | 5 | // Code generated by gen.go. DO NOT EDIT. 6 | 7 | import ( 8 | _ "github.com/xo/usql/drivers/duckdb" // DuckDB driver 9 | ) 10 | -------------------------------------------------------------------------------- /internal/dynamodb.go: -------------------------------------------------------------------------------- 1 | //go:build (all || most || dynamodb) && !no_dynamodb 2 | 3 | package internal 4 | 5 | // Code generated by gen.go. DO NOT EDIT. 6 | 7 | import ( 8 | _ "github.com/xo/usql/drivers/dynamodb" // DynamoDb driver 9 | ) 10 | -------------------------------------------------------------------------------- /internal/exasol.go: -------------------------------------------------------------------------------- 1 | //go:build (all || most || exasol) && !no_exasol 2 | 3 | package internal 4 | 5 | // Code generated by gen.go. DO NOT EDIT. 6 | 7 | import ( 8 | _ "github.com/xo/usql/drivers/exasol" // Exasol driver 9 | ) 10 | -------------------------------------------------------------------------------- /internal/firebird.go: -------------------------------------------------------------------------------- 1 | //go:build (all || most || firebird) && !no_firebird 2 | 3 | package internal 4 | 5 | // Code generated by gen.go. DO NOT EDIT. 6 | 7 | import ( 8 | _ "github.com/xo/usql/drivers/firebird" // Firebird driver 9 | ) 10 | -------------------------------------------------------------------------------- /internal/flightsql.go: -------------------------------------------------------------------------------- 1 | //go:build (all || most || flightsql) && !no_flightsql 2 | 3 | package internal 4 | 5 | // Code generated by gen.go. DO NOT EDIT. 6 | 7 | import ( 8 | _ "github.com/xo/usql/drivers/flightsql" // FlightSQL driver 9 | ) 10 | -------------------------------------------------------------------------------- /internal/godror.go: -------------------------------------------------------------------------------- 1 | //go:build (all || godror) && !no_godror 2 | 3 | package internal 4 | 5 | // Code generated by gen.go. DO NOT EDIT. 6 | 7 | import ( 8 | _ "github.com/xo/usql/drivers/godror" // GO DRiver for ORacle driver 9 | ) 10 | -------------------------------------------------------------------------------- /internal/h2.go: -------------------------------------------------------------------------------- 1 | //go:build (all || most || h2) && !no_h2 2 | 3 | package internal 4 | 5 | // Code generated by gen.go. DO NOT EDIT. 6 | 7 | import ( 8 | _ "github.com/xo/usql/drivers/h2" // Apache H2 driver 9 | ) 10 | -------------------------------------------------------------------------------- /internal/hive.go: -------------------------------------------------------------------------------- 1 | //go:build (all || most || hive) && !no_hive 2 | 3 | package internal 4 | 5 | // Code generated by gen.go. DO NOT EDIT. 6 | 7 | import ( 8 | _ "github.com/xo/usql/drivers/hive" // Apache Hive driver 9 | ) 10 | -------------------------------------------------------------------------------- /internal/ignite.go: -------------------------------------------------------------------------------- 1 | //go:build (all || most || ignite) && !no_ignite 2 | 3 | package internal 4 | 5 | // Code generated by gen.go. DO NOT EDIT. 6 | 7 | import ( 8 | _ "github.com/xo/usql/drivers/ignite" // Apache Ignite driver 9 | ) 10 | -------------------------------------------------------------------------------- /internal/impala.go: -------------------------------------------------------------------------------- 1 | //go:build (all || most || impala) && !no_impala 2 | 3 | package internal 4 | 5 | // Code generated by gen.go. DO NOT EDIT. 6 | 7 | import ( 8 | _ "github.com/xo/usql/drivers/impala" // Apache Impala driver 9 | ) 10 | -------------------------------------------------------------------------------- /internal/internal.go: -------------------------------------------------------------------------------- 1 | // Package internal provides a way to obtain information about which database 2 | // drivers were included at build. 3 | package internal 4 | 5 | // Code generated by gen.go. DO NOT EDIT. 6 | 7 | // KnownBuildTags returns a map of known driver names to its respective build 8 | // tags. 9 | func KnownBuildTags() map[string]string { 10 | return map[string]string{ 11 | "adodb": "adodb", // github.com/mattn/go-adodb 12 | "athena": "awsathena", // github.com/uber/athenadriver/go 13 | "avatica": "avatica", // github.com/apache/calcite-avatica-go/v5 14 | "bigquery": "bigquery", // gorm.io/driver/bigquery/driver 15 | "cassandra": "cql", // github.com/MichaelS11/go-cql-driver 16 | "chai": "chai", // github.com/chaisql/chai/driver 17 | "clickhouse": "clickhouse", // github.com/ClickHouse/clickhouse-go/v2 18 | "cosmos": "cosmos", // github.com/btnguyen2k/gocosmos 19 | "couchbase": "n1ql", // github.com/couchbase/go_n1ql 20 | "csvq": "csvq", // github.com/mithrandie/csvq-driver 21 | "databend": "databend", // github.com/datafuselabs/databend-go 22 | "databricks": "databricks", // github.com/databricks/databricks-sql-go 23 | "duckdb": "duckdb", // github.com/marcboeker/go-duckdb/v2 24 | "dynamodb": "dynamodb", // github.com/btnguyen2k/godynamo 25 | "exasol": "exasol", // github.com/exasol/exasol-driver-go 26 | "firebird": "firebirdsql", // github.com/nakagami/firebirdsql 27 | "flightsql": "flightsql", // github.com/apache/arrow/go/v17/arrow/flight/flightsql/driver 28 | "godror": "godror", // github.com/godror/godror 29 | "h2": "h2", // github.com/jmrobles/h2go 30 | "hive": "hive", // sqlflow.org/gohive 31 | "ignite": "ignite", // github.com/amsokol/ignite-go-client/sql 32 | "impala": "impala", // github.com/sclgo/impala-go 33 | "maxcompute": "maxcompute", // sqlflow.org/gomaxcompute 34 | "moderncsqlite": "moderncsqlite", // modernc.org/sqlite 35 | "mymysql": "mymysql", // github.com/ziutek/mymysql/godrv 36 | "mysql": "mysql", // github.com/go-sql-driver/mysql 37 | "netezza": "nzgo", // github.com/IBM/nzgo/v12 38 | "odbc": "odbc", // github.com/alexbrainman/odbc 39 | "oracle": "oracle", // github.com/sijms/go-ora/v2 40 | "ots": "ots", // github.com/aliyun/aliyun-tablestore-go-sql-driver 41 | "pgx": "pgx", // github.com/jackc/pgx/v5/stdlib 42 | "postgres": "postgres", // github.com/lib/pq 43 | "presto": "presto", // github.com/prestodb/presto-go-client/presto 44 | "ql": "ql", // modernc.org/ql 45 | "ramsql": "ramsql", // github.com/proullon/ramsql/driver 46 | "sapase": "tds", // github.com/thda/tds 47 | "saphana": "hdb", // github.com/SAP/go-hdb/driver 48 | "snowflake": "snowflake", // github.com/snowflakedb/gosnowflake 49 | "spanner": "spanner", // github.com/googleapis/go-sql-spanner 50 | "sqlite3": "sqlite3", // github.com/mattn/go-sqlite3 51 | "sqlserver": "sqlserver", // github.com/microsoft/go-mssqldb 52 | "trino": "trino", // github.com/trinodb/trino-go-client/trino 53 | "vertica": "vertica", // github.com/vertica/vertica-sql-go 54 | "voltdb": "voltdb", // github.com/VoltDB/voltdb-client-go/voltdbclient 55 | "ydb": "ydb", // github.com/ydb-platform/ydb-go-sdk/v3 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /internal/maxcompute.go: -------------------------------------------------------------------------------- 1 | //go:build (all || most || maxcompute) && !no_maxcompute 2 | 3 | package internal 4 | 5 | // Code generated by gen.go. DO NOT EDIT. 6 | 7 | import ( 8 | _ "github.com/xo/usql/drivers/maxcompute" // Alibaba MaxCompute driver 9 | ) 10 | -------------------------------------------------------------------------------- /internal/moderncsqlite.go: -------------------------------------------------------------------------------- 1 | //go:build (all || most || moderncsqlite) && !no_moderncsqlite 2 | 3 | package internal 4 | 5 | // Code generated by gen.go. DO NOT EDIT. 6 | 7 | import ( 8 | _ "github.com/xo/usql/drivers/moderncsqlite" // ModernC SQLite3 driver 9 | ) 10 | -------------------------------------------------------------------------------- /internal/mymysql.go: -------------------------------------------------------------------------------- 1 | //go:build (all || most || mymysql) && !no_mymysql 2 | 3 | package internal 4 | 5 | // Code generated by gen.go. DO NOT EDIT. 6 | 7 | import ( 8 | _ "github.com/xo/usql/drivers/mymysql" // MySQL MyMySQL driver 9 | ) 10 | -------------------------------------------------------------------------------- /internal/mysql.go: -------------------------------------------------------------------------------- 1 | //go:build (!no_base || mysql) && !no_mysql 2 | 3 | package internal 4 | 5 | // Code generated by gen.go. DO NOT EDIT. 6 | 7 | import ( 8 | _ "github.com/xo/usql/drivers/mysql" // MySQL driver 9 | ) 10 | -------------------------------------------------------------------------------- /internal/netezza.go: -------------------------------------------------------------------------------- 1 | //go:build (all || most || netezza) && !no_netezza 2 | 3 | package internal 4 | 5 | // Code generated by gen.go. DO NOT EDIT. 6 | 7 | import ( 8 | _ "github.com/xo/usql/drivers/netezza" // Netezza driver 9 | ) 10 | -------------------------------------------------------------------------------- /internal/odbc.go: -------------------------------------------------------------------------------- 1 | //go:build (all || odbc) && !no_odbc 2 | 3 | package internal 4 | 5 | // Code generated by gen.go. DO NOT EDIT. 6 | 7 | import ( 8 | _ "github.com/xo/usql/drivers/odbc" // ODBC driver 9 | ) 10 | -------------------------------------------------------------------------------- /internal/oracle.go: -------------------------------------------------------------------------------- 1 | //go:build (!no_base || oracle) && !no_oracle 2 | 3 | package internal 4 | 5 | // Code generated by gen.go. DO NOT EDIT. 6 | 7 | import ( 8 | _ "github.com/xo/usql/drivers/oracle" // Oracle Database driver 9 | ) 10 | -------------------------------------------------------------------------------- /internal/ots.go: -------------------------------------------------------------------------------- 1 | //go:build (all || most || ots) && !no_ots 2 | 3 | package internal 4 | 5 | // Code generated by gen.go. DO NOT EDIT. 6 | 7 | import ( 8 | _ "github.com/xo/usql/drivers/ots" // Alibaba Tablestore driver 9 | ) 10 | -------------------------------------------------------------------------------- /internal/pgx.go: -------------------------------------------------------------------------------- 1 | //go:build (all || most || pgx) && !no_pgx 2 | 3 | package internal 4 | 5 | // Code generated by gen.go. DO NOT EDIT. 6 | 7 | import ( 8 | _ "github.com/xo/usql/drivers/pgx" // PostgreSQL PGX driver 9 | ) 10 | -------------------------------------------------------------------------------- /internal/postgres.go: -------------------------------------------------------------------------------- 1 | //go:build (!no_base || postgres) && !no_postgres 2 | 3 | package internal 4 | 5 | // Code generated by gen.go. DO NOT EDIT. 6 | 7 | import ( 8 | _ "github.com/xo/usql/drivers/postgres" // PostgreSQL driver 9 | ) 10 | -------------------------------------------------------------------------------- /internal/presto.go: -------------------------------------------------------------------------------- 1 | //go:build (all || most || presto) && !no_presto 2 | 3 | package internal 4 | 5 | // Code generated by gen.go. DO NOT EDIT. 6 | 7 | import ( 8 | _ "github.com/xo/usql/drivers/presto" // Presto driver 9 | ) 10 | -------------------------------------------------------------------------------- /internal/ql.go: -------------------------------------------------------------------------------- 1 | //go:build (all || most || ql) && !no_ql 2 | 3 | package internal 4 | 5 | // Code generated by gen.go. DO NOT EDIT. 6 | 7 | import ( 8 | _ "github.com/xo/usql/drivers/ql" // Cznic QL driver 9 | ) 10 | -------------------------------------------------------------------------------- /internal/ramsql.go: -------------------------------------------------------------------------------- 1 | //go:build (all || most || ramsql) && !no_ramsql 2 | 3 | package internal 4 | 5 | // Code generated by gen.go. DO NOT EDIT. 6 | 7 | import ( 8 | _ "github.com/xo/usql/drivers/ramsql" // RamSQL driver 9 | ) 10 | -------------------------------------------------------------------------------- /internal/sapase.go: -------------------------------------------------------------------------------- 1 | //go:build (all || most || sapase) && !no_sapase 2 | 3 | package internal 4 | 5 | // Code generated by gen.go. DO NOT EDIT. 6 | 7 | import ( 8 | _ "github.com/xo/usql/drivers/sapase" // SAP ASE driver 9 | ) 10 | -------------------------------------------------------------------------------- /internal/saphana.go: -------------------------------------------------------------------------------- 1 | //go:build (all || most || saphana) && !no_saphana 2 | 3 | package internal 4 | 5 | // Code generated by gen.go. DO NOT EDIT. 6 | 7 | import ( 8 | _ "github.com/xo/usql/drivers/saphana" // SAP HANA driver 9 | ) 10 | -------------------------------------------------------------------------------- /internal/snowflake.go: -------------------------------------------------------------------------------- 1 | //go:build (all || most || snowflake) && !no_snowflake 2 | 3 | package internal 4 | 5 | // Code generated by gen.go. DO NOT EDIT. 6 | 7 | import ( 8 | _ "github.com/xo/usql/drivers/snowflake" // Snowflake driver 9 | ) 10 | -------------------------------------------------------------------------------- /internal/spanner.go: -------------------------------------------------------------------------------- 1 | //go:build (all || most || spanner) && !no_spanner 2 | 3 | package internal 4 | 5 | // Code generated by gen.go. DO NOT EDIT. 6 | 7 | import ( 8 | _ "github.com/xo/usql/drivers/spanner" // Google Spanner driver 9 | ) 10 | -------------------------------------------------------------------------------- /internal/sqlite3.go: -------------------------------------------------------------------------------- 1 | //go:build (!no_base || sqlite3) && !no_sqlite3 2 | 3 | package internal 4 | 5 | // Code generated by gen.go. DO NOT EDIT. 6 | 7 | import ( 8 | _ "github.com/xo/usql/drivers/sqlite3" // SQLite3 driver 9 | ) 10 | -------------------------------------------------------------------------------- /internal/sqlserver.go: -------------------------------------------------------------------------------- 1 | //go:build (!no_base || sqlserver) && !no_sqlserver 2 | 3 | package internal 4 | 5 | // Code generated by gen.go. DO NOT EDIT. 6 | 7 | import ( 8 | _ "github.com/xo/usql/drivers/sqlserver" // Microsoft SQL Server driver 9 | ) 10 | -------------------------------------------------------------------------------- /internal/trino.go: -------------------------------------------------------------------------------- 1 | //go:build (all || most || trino) && !no_trino 2 | 3 | package internal 4 | 5 | // Code generated by gen.go. DO NOT EDIT. 6 | 7 | import ( 8 | _ "github.com/xo/usql/drivers/trino" // Trino driver 9 | ) 10 | -------------------------------------------------------------------------------- /internal/vertica.go: -------------------------------------------------------------------------------- 1 | //go:build (all || most || vertica) && !no_vertica 2 | 3 | package internal 4 | 5 | // Code generated by gen.go. DO NOT EDIT. 6 | 7 | import ( 8 | _ "github.com/xo/usql/drivers/vertica" // Vertica driver 9 | ) 10 | -------------------------------------------------------------------------------- /internal/voltdb.go: -------------------------------------------------------------------------------- 1 | //go:build (all || most || voltdb) && !no_voltdb 2 | 3 | package internal 4 | 5 | // Code generated by gen.go. DO NOT EDIT. 6 | 7 | import ( 8 | _ "github.com/xo/usql/drivers/voltdb" // VoltDB driver 9 | ) 10 | -------------------------------------------------------------------------------- /internal/ydb.go: -------------------------------------------------------------------------------- 1 | //go:build (all || most || ydb) && !no_ydb 2 | 3 | package internal 4 | 5 | // Code generated by gen.go. DO NOT EDIT. 6 | 7 | import ( 8 | _ "github.com/xo/usql/drivers/ydb" // YDB driver 9 | ) 10 | -------------------------------------------------------------------------------- /internal/z.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | import ( 4 | "runtime" 5 | 6 | "github.com/xo/dburl" 7 | "github.com/xo/usql/drivers" 8 | ) 9 | 10 | func init() { 11 | if runtime.GOOS == "windows" { 12 | // if no odbc driver, but we have adodb, add 'odbc' (and related 13 | // aliases) as alias for oleodbc 14 | if drivers.Registered("adodb") && !drivers.Registered("odbc") { 15 | old := dburl.Unregister("odbc") 16 | dburl.RegisterAlias("oleodbc", "odbc") 17 | for _, alias := range old.Aliases { 18 | dburl.RegisterAlias("oleodbc", alias) 19 | } 20 | } 21 | } 22 | if drivers.Registered("moderncsqlite") && !drivers.Registered("sqlite3") { 23 | old := dburl.Unregister("sqlite3") 24 | dburl.RegisterAlias("moderncsqlite", "sqlite3") 25 | for _, alias := range old.Aliases { 26 | dburl.RegisterAlias("moderncsqlite", alias) 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | // Command usql is the universal command-line interface for SQL databases. 2 | // 3 | //go:debug x509negativeserial=1 4 | package main 5 | 6 | //go:generate go run gen.go 7 | 8 | import ( 9 | "context" 10 | "errors" 11 | "fmt" 12 | "io" 13 | "os" 14 | "strings" 15 | 16 | "github.com/xo/usql/drivers" 17 | "github.com/xo/usql/handler" 18 | "github.com/xo/usql/internal" 19 | "github.com/xo/usql/rline" 20 | "github.com/xo/usql/text" 21 | ) 22 | 23 | func main() { 24 | // get available drivers and known build tags 25 | available, known := drivers.Available(), internal.KnownBuildTags() 26 | // report if database is supported 27 | if len(os.Args) == 2 && 28 | strings.HasPrefix(os.Args[1], "--has-") && 29 | strings.HasSuffix(os.Args[1], "-support") { 30 | n := os.Args[1][6 : len(os.Args[1])-8] 31 | if v, ok := known[n]; ok { 32 | n = v 33 | } 34 | var out int 35 | if _, ok := available[n]; ok { 36 | out = 1 37 | } 38 | fmt.Fprint(os.Stdout, out) 39 | return 40 | } 41 | // run 42 | if err := New(os.Args).ExecuteContext(context.Background()); err != nil && err != io.EOF && err != rline.ErrInterrupt { 43 | var he *handler.Error 44 | if !errors.As(err, &he) { 45 | fmt.Fprintf(os.Stderr, "error: %v\n", err) 46 | } 47 | var e *drivers.Error 48 | if errors.As(err, &e) && e.Err == text.ErrDriverNotAvailable { 49 | m := make(map[string]string, len(known)) 50 | for k, v := range known { 51 | m[v] = k 52 | } 53 | tag := e.Driver 54 | if t, ok := m[tag]; ok { 55 | tag = t 56 | } 57 | rev := "latest" 58 | if text.CommandVersion == "0.0.0-dev" || strings.Contains(text.CommandVersion, "-") { 59 | rev = "master" 60 | } 61 | fmt.Fprintf(os.Stderr, text.GoInstallHint, tag, rev) 62 | } 63 | switch estr := err.Error(); { 64 | case err == text.ErrWrongNumberOfArguments, 65 | strings.HasPrefix(estr, "unknown flag:"), 66 | strings.HasPrefix(estr, "unknown shorthand flag:"), 67 | strings.HasPrefix(estr, "bad flag syntax:"), 68 | strings.HasPrefix(estr, "flag needs an argument:"): 69 | fmt.Fprintln(os.Stderr, text.CommandHelpHint) 70 | } 71 | os.Exit(1) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /main_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | _ "github.com/google/goexpect" 5 | ) 6 | -------------------------------------------------------------------------------- /stmt/params.go: -------------------------------------------------------------------------------- 1 | package stmt 2 | 3 | import ( 4 | "unicode" 5 | 6 | "github.com/xo/usql/text" 7 | ) 8 | 9 | // Params holds information about command parameters. 10 | type Params struct { 11 | R []rune 12 | Len int 13 | } 14 | 15 | // NewParams creates command parameters. 16 | func NewParams(params string) *Params { 17 | r := []rune(params) 18 | return &Params{ 19 | R: r, 20 | Len: len(r), 21 | } 22 | } 23 | 24 | // Raw reads all remaining runes. No substitution or whitespace removal is 25 | // performed. 26 | func (p *Params) Raw() string { 27 | s := string(p.R) 28 | p.R, p.Len = p.R[:0], 0 29 | return s 30 | } 31 | 32 | // Next reads the next command parameter using the provided substitution func. 33 | // True indicates there are runes remaining in the command parameters to 34 | // process. 35 | func (p *Params) Next(unquote func(string, bool) (string, bool, error)) (string, bool, error) { 36 | i, _ := findNonSpace(p.R, 0, p.Len) 37 | if i >= p.Len { 38 | return "", false, nil 39 | } 40 | var ok bool 41 | var quote rune 42 | start := i 43 | loop: 44 | for ; i < p.Len; i++ { 45 | c, next := p.R[i], grab(p.R, i+1, p.Len) 46 | switch { 47 | case quote != 0: 48 | start := i - 1 49 | i, ok = readString(p.R, i, p.Len, quote, "") 50 | if !ok { 51 | break loop 52 | } 53 | switch z, ok, err := unquote(string(p.R[start:i+1]), false); { 54 | case err != nil: 55 | return "", false, err 56 | case ok: 57 | p.R, p.Len = substitute(p.R, start, p.Len, i-start+1, z) 58 | i = start + len([]rune(z)) - 1 59 | } 60 | quote = 0 61 | // start of single, double, or backtick string 62 | case c == '\'' || c == '"' || c == '`': 63 | quote = c 64 | case c == ':' && next != ':': 65 | if v := readVar(p.R, i, p.Len, next); v != nil { 66 | switch z, ok, err := unquote(v.Name, true); { 67 | case err != nil: 68 | return "", false, err 69 | case ok || v.Quote == '?': 70 | p.R, p.Len = v.Substitute(p.R, z, ok) 71 | i += v.Len - 1 72 | default: 73 | i = v.End - 1 74 | } 75 | } 76 | case unicode.IsSpace(c): 77 | break loop 78 | } 79 | } 80 | if quote != 0 { 81 | return "", false, text.ErrUnterminatedQuotedString 82 | } 83 | s := string(p.R[start:i]) 84 | p.R = p.R[i:] 85 | p.Len = len(p.R) 86 | return s, true, nil 87 | } 88 | 89 | // All retrieves all remaining command parameters using the provided 90 | // substitution func. Will return on the first encountered error. 91 | func (p *Params) All(unquote func(string, bool) (string, bool, error)) ([]string, error) { 92 | var v []string 93 | loop: 94 | for { 95 | switch s, ok, err := p.Next(unquote); { 96 | case err != nil: 97 | return v, err 98 | case !ok: 99 | break loop 100 | default: 101 | v = append(v, s) 102 | } 103 | } 104 | return v, nil 105 | } 106 | 107 | // Arg retrieves the next argument, without decoding. 108 | func (p *Params) Arg() (string, bool, error) { 109 | return p.Next(func(s string, _ bool) (string, bool, error) { 110 | return s, true, nil 111 | }) 112 | } 113 | -------------------------------------------------------------------------------- /stmt/params_test.go: -------------------------------------------------------------------------------- 1 | package stmt 2 | 3 | import ( 4 | "os/user" 5 | "reflect" 6 | "strconv" 7 | "testing" 8 | 9 | "github.com/xo/usql/env" 10 | "github.com/xo/usql/text" 11 | ) 12 | 13 | func TestParamsGetRaw(t *testing.T) { 14 | const exp = ` 'a string' "another string" ` 15 | p := NewParams(exp) 16 | s := p.Raw() 17 | if s != exp { 18 | t.Errorf("expected %q, got: %q", exp, s) 19 | } 20 | u, err := user.Current() 21 | if err != nil { 22 | t.Fatalf("expected no error, got: %v", err) 23 | } 24 | unquote := testUnquote(t, u) 25 | switch s, ok, err := p.Next(unquote); { 26 | case err != nil: 27 | t.Fatalf("expected no error, got: %v", err) 28 | case s != "": 29 | t.Errorf("expected empty string, got: %q", s) 30 | case ok: 31 | t.Errorf("expected ok=false, got: %t", ok) 32 | } 33 | switch v, err := p.All(unquote); { 34 | case err != nil: 35 | t.Fatalf("expected no error, got: %v", err) 36 | case len(v) != 0: 37 | t.Errorf("expected v to have length 0, got: %d", len(v)) 38 | } 39 | } 40 | 41 | func TestParamsGetAll(t *testing.T) { 42 | u, err := user.Current() 43 | if err != nil { 44 | t.Fatalf("expected no error, got: %v", err) 45 | } 46 | tests := []struct { 47 | s string 48 | exp []string 49 | err error 50 | }{ 51 | {``, nil, nil}, 52 | {` `, nil, nil}, 53 | {` :foo`, []string{`bar`}, nil}, 54 | {` :'foo`, nil, text.ErrUnterminatedQuotedString}, 55 | {` :'型示師`, nil, text.ErrUnterminatedQuotedString}, 56 | {` :"型示師`, nil, text.ErrUnterminatedQuotedString}, 57 | {` :'型示師 `, nil, text.ErrUnterminatedQuotedString}, 58 | {` :"型示師 `, nil, text.ErrUnterminatedQuotedString}, 59 | {`:'foo'`, []string{`'bar'`}, nil}, 60 | {` :'foo' `, []string{`'bar'`}, nil}, 61 | {`:'foo':foo`, []string{`'bar'bar`}, nil}, 62 | {`:'foo':foo:"foo"`, []string{`'bar'bar"bar"`}, nil}, 63 | {`:'foo':foo:foo`, []string{`'bar'barbar`}, nil}, 64 | {` :'foo':foo:foo`, []string{`'bar'barbar`}, nil}, 65 | {` :'foo':yes:foo`, []string{`'bar':yesbar`}, nil}, 66 | {` :foo `, []string{`bar`}, nil}, 67 | {`:foo:foo`, []string{`barbar`}, nil}, 68 | {` :foo:foo `, []string{`barbar`}, nil}, 69 | {` :foo:foo `, []string{`barbar`}, nil}, 70 | {`'hello'`, []string{`hello`}, nil}, 71 | {` 'hello''yes' `, []string{`hello'yes`}, nil}, 72 | {` 'hello\'...\'yes' `, []string{`hello'...'yes`}, nil}, 73 | {` "hello\'...\'yes" `, nil, text.ErrInvalidQuotedString}, 74 | {` "hello\"...\"yes" `, nil, text.ErrInvalidQuotedString}, 75 | {` 'hello':'yes' `, []string{`hello:'yes'`}, nil}, 76 | {` :'foo `, nil, text.ErrUnterminatedQuotedString}, 77 | {` :'foo bar`, nil, text.ErrUnterminatedQuotedString}, 78 | {` :'foo bar`, nil, text.ErrUnterminatedQuotedString}, 79 | {` :'foo bar `, nil, text.ErrUnterminatedQuotedString}, 80 | {" `foo", nil, text.ErrUnterminatedQuotedString}, 81 | {" `foo bar`", []string{"foo bar"}, nil}, 82 | {" `foo :foo`", []string{"foo :foo"}, nil}, 83 | {` :'foo':"foo"`, []string{`'bar'"bar"`}, nil}, 84 | {` :'foo' :"foo" `, []string{`'bar'`, `"bar"`}, nil}, 85 | {` :'foo' :"foo"`, []string{`'bar'`, `"bar"`}, nil}, 86 | {` :'foo' :"foo"`, []string{`'bar'`, `"bar"`}, nil}, 87 | {` :'foo' :"foo" `, []string{`'bar'`, `"bar"`}, nil}, 88 | {` :'foo' :"foo" :foo `, []string{`'bar'`, `"bar"`, `bar`}, nil}, 89 | {` :'foo':foo:"foo" `, []string{`'bar'bar"bar"`}, nil}, 90 | {` :'foo''yes':'foo' `, []string{`'bar'yes'bar'`}, nil}, 91 | {` :'foo' 'yes' :'foo' `, []string{`'bar'`, `yes`, `'bar'`}, nil}, 92 | {` 'yes':'foo':"foo"'blah''no' "\ntest" `, []string{`yes'bar'"bar"blah'no`, "\ntest"}, nil}, 93 | {`:型示師:'型示師':"型示師"`, []string{`:型示師:'型示師':"型示師"`}, nil}, 94 | {`:型示師 :'型示師' :"型示師"`, []string{`:型示師`, `:'型示師'`, `:"型示師"`}, nil}, 95 | {` :型示師 :'型示師' :"型示師" `, []string{`:型示師`, `:'型示師'`, `:"型示師"`}, nil}, 96 | {` :{?foo} `, []string{`TRUE`}, nil}, 97 | {` :{?foo_} `, []string{`FALSE`}, nil}, 98 | {` :{?型示} `, []string{`TRUE`}, nil}, 99 | {` :{?型示師} `, []string{`FALSE`}, nil}, 100 | {` :{?型示師 } `, []string{`:{?型示師`, `}`}, nil}, 101 | {` :{?foo }`, []string{`:{?foo`, `}`}, nil}, 102 | } 103 | for i, test := range tests { 104 | t.Run(strconv.Itoa(i), func(t *testing.T) { 105 | vals, err := NewParams(test.s).All(testUnquote(t, u)) 106 | if err != test.err { 107 | t.Fatalf("expected error %v, got: %v", test.err, err) 108 | } 109 | if !reflect.DeepEqual(vals, test.exp) { 110 | t.Errorf("expected %v, got: %v", test.exp, vals) 111 | } 112 | }) 113 | } 114 | } 115 | 116 | func testUnquote(t *testing.T, u *user.User) func(string, bool) (string, bool, error) { 117 | t.Helper() 118 | vars := env.NewVars() 119 | vars.Set("foo", "bar") 120 | vars.Set("型示", "yes") 121 | f := env.Untick(u, vars, false) 122 | return func(s string, isvar bool) (string, bool, error) { 123 | // t.Logf("test %d %q s: %q, isvar: %t", i, teststr, s, isvar) 124 | return f(s, isvar) 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /styles/styles.go: -------------------------------------------------------------------------------- 1 | // Package styles provides chroma styles based on the chroma styles but removing 2 | // the backgrounds. 3 | package styles 4 | 5 | import ( 6 | "sync" 7 | 8 | "github.com/alecthomas/chroma/v2" 9 | cstyles "github.com/alecthomas/chroma/v2/styles" 10 | ) 11 | 12 | // styles is the set of styles with their background colors removed. 13 | var styles = struct { 14 | styles map[string]*chroma.Style 15 | sync.Mutex 16 | }{ 17 | styles: make(map[string]*chroma.Style), 18 | } 19 | 20 | // Get retrieves the equivalent chroma style. 21 | func Get(name string) *chroma.Style { 22 | styles.Lock() 23 | defer styles.Unlock() 24 | if _, ok := styles.styles[name]; !ok { 25 | // get original style 26 | s := cstyles.Get(name) 27 | // create new entry map 28 | m := make(chroma.StyleEntries) 29 | for _, typ := range s.Types() { 30 | // skip background 31 | if typ == chroma.Background { 32 | continue 33 | } 34 | z := s.Get(typ) 35 | // unset background 36 | z.Background = chroma.Colour(0) 37 | m[typ] = z.String() 38 | } 39 | styles.styles[name] = chroma.MustNewStyle(s.Name, m) 40 | } 41 | return styles.styles[name] 42 | } 43 | -------------------------------------------------------------------------------- /testcli.go: -------------------------------------------------------------------------------- 1 | //go:build ignore 2 | 3 | // Command testcli runs goexpect tests against a built usql binary. 4 | package main 5 | 6 | import ( 7 | "bytes" 8 | "context" 9 | "flag" 10 | "fmt" 11 | "io" 12 | "log" 13 | "os" 14 | "regexp" 15 | "time" 16 | 17 | gexpect "github.com/google/goexpect" 18 | ) 19 | 20 | func main() { 21 | binpath := flag.String("binpath", "./usql", "bin path") 22 | deadline := flag.Duration("deadline", 5*time.Minute, "total execution deadline") 23 | timeout := flag.Duration("timeout", 2*time.Minute, "individual test timeout") 24 | re := flag.String("run", "", "test name regexp to run") 25 | flag.Parse() 26 | if err := run(context.Background(), *binpath, *deadline, *timeout, *re); err != nil { 27 | fmt.Fprintf(os.Stderr, "error: %v\n", err) 28 | os.Exit(1) 29 | } 30 | } 31 | 32 | func run(ctx context.Context, binpath string, deadline, timeout time.Duration, re string) error { 33 | ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(deadline)) 34 | defer cancel() 35 | tests, err := cliTests() 36 | if err != nil { 37 | return err 38 | } 39 | var nameRE *regexp.Regexp 40 | if re != "" { 41 | nameRE, err = regexp.Compile(re) 42 | if err != nil { 43 | return err 44 | } 45 | } 46 | for _, test := range tests { 47 | if nameRE != nil && !nameRE.MatchString(test.name) { 48 | log.Printf(">>> SKIPPING: %s", test.name) 49 | continue 50 | } 51 | log.Printf(">>> RUNNING: %s", test.name) 52 | if err := test.do(ctx, binpath, timeout); err != nil { 53 | return fmt.Errorf("test %s: %v", test.name, err) 54 | } 55 | log.Printf(">>> COMPLETED: %s", test.name) 56 | } 57 | return nil 58 | } 59 | 60 | type Test struct { 61 | name string 62 | script string 63 | args []string 64 | env []string 65 | } 66 | 67 | func cliTests() ([]Test, error) { 68 | env := append(os.Environ(), "TERM=xterm-256color") 69 | return []Test{ 70 | { 71 | "complex/postgres", 72 | "./contrib/postgres/test.sql", 73 | []string{"pgsql://postgres:P4ssw0rd@localhost", "--set=PAGER=''", "--pset=pager=off"}, 74 | env, 75 | }, 76 | { 77 | "complex/mysql", 78 | "./contrib/mysql/test.sql", 79 | []string{"my://root:P4ssw0rd@localhost", "--set=PAGER=''", "--pset=pager=off"}, 80 | env, 81 | }, 82 | { 83 | "complex/sqlite3", 84 | "./contrib/sqlite3/test.sql", 85 | []string{"sqlite:./testdata/sqlite3_test.db", "--set=PAGER=''", "--pset=pager=off"}, 86 | env, 87 | }, 88 | { 89 | "complex/moderncsqlite", 90 | "./contrib/sqlite3/test.sql", 91 | []string{"mq:./testdata/moderncsqlite_test.db", "--set=PAGER=''", "--pset=pager=off"}, 92 | env, 93 | }, 94 | { 95 | "complex/sqlserver", 96 | "./contrib/sqlserver/test.sql", 97 | []string{"sqlserver://sa:Adm1nP@ssw0rd@localhost/", "--set=PAGER=''", "--pset=pager=off"}, 98 | env, 99 | }, 100 | { 101 | "complex/cassandra", 102 | "./contrib/cassandra/test.sql", 103 | []string{"ca://cassandra:cassandra@localhost", "--set=PAGER=''", "--pset=pager=off"}, 104 | env, 105 | }, 106 | { 107 | "copy/a_bit_of_everything", 108 | "./testdata/copy.sql", 109 | []string{"--set=PAGER=''", "--pset=pager=off"}, 110 | env, 111 | }, 112 | }, nil 113 | } 114 | 115 | func (test Test) do(ctx context.Context, binpath string, timeout time.Duration) error { 116 | exp, errch, err := gexpect.SpawnWithArgs( 117 | append([]string{binpath}, test.args...), 118 | timeout, 119 | gexpect.SetEnv(test.env), 120 | gexpect.Tee(&noopWriteCloser{os.Stdout}), 121 | ) 122 | if err != nil { 123 | return err 124 | } 125 | buf, err := os.ReadFile(test.script) 126 | if err != nil { 127 | return err 128 | } 129 | for _, line := range bytes.Split(buf, []byte{'\n'}) { 130 | if err := exp.Send(string(line) + "\n"); err != nil { 131 | return err 132 | } 133 | } 134 | select { 135 | case <-ctx.Done(): 136 | defer exp.Close() 137 | return ctx.Err() 138 | case err := <-errch: 139 | defer exp.Close() 140 | return err 141 | } 142 | } 143 | 144 | type noopWriteCloser struct { 145 | io.Writer 146 | } 147 | 148 | func (*noopWriteCloser) Close() error { 149 | return nil 150 | } 151 | -------------------------------------------------------------------------------- /testdata/booktest/authors.csv: -------------------------------------------------------------------------------- 1 | author_id,name 2 | 1,Isaac Asimov 3 | 2,Stephen King 4 | -------------------------------------------------------------------------------- /testdata/booktest/books.csv: -------------------------------------------------------------------------------- 1 | book_id,author_id,title 2 | 1,1,I Robot 3 | 2,2,Carrie 4 | 3,2,Cujo 5 | -------------------------------------------------------------------------------- /testdata/copy.sql: -------------------------------------------------------------------------------- 1 | \set PGDB pg://postgres:P4ssw0rd@localhost 2 | \set MYDB my://root:P4ssw0rd@localhost 3 | \set SQDB sq:./testdata/copy_test.db 4 | \set MSDB ms://sa:Adm1nP@ssw0rd@localhost/ 5 | 6 | \connect :PGDB 7 | drop table if exists a_bit_of_everything; 8 | create table a_bit_of_everything ( 9 | a_id serial primary key, 10 | a_blob bytea, 11 | a_bool boolean, 12 | a_date timestamp with time zone, 13 | a_double double precision, 14 | a_int integer, 15 | a_text text 16 | ); 17 | insert into a_bit_of_everything 18 | (a_blob, a_bool, a_date, a_double, a_int, a_text) 19 | values 20 | (E'more\ntext'::bytea, true, now(), 32.0, 0, 'some text'), 21 | (E'other\ntext'::bytea, false, now()+interval '3 days', 64.0, 128, 'foobar') 22 | ; 23 | select * from a_bit_of_everything; 24 | 25 | \connect :MYDB 26 | drop database if exists testdb; 27 | create database testdb; 28 | use testdb; 29 | drop table if exists a_bit_of_everything; 30 | create table a_bit_of_everything ( 31 | a_id integer not null auto_increment primary key, 32 | a_blob blob, 33 | a_bool boolean, 34 | a_date datetime, 35 | a_double double, 36 | a_int integer, 37 | a_text text 38 | ); 39 | \copy :PGDB :MYDB/testdb 'select * from a_bit_of_everything' 'a_bit_of_everything(a_id, a_blob, a_bool, a_date, a_double, a_int, a_text)' 40 | \connect :MYDB/testdb 41 | select * from a_bit_of_everything; 42 | 43 | \! rm -f ./testdata/test3.db 44 | \connect :SQDB 45 | create table a_bit_of_everything ( 46 | a_id integer primary key autoincrement, 47 | a_blob blob, 48 | a_bool boolean, 49 | a_date datetime, 50 | a_double double precision, 51 | a_int integer, 52 | a_text text 53 | ); 54 | \copy :PGDB :SQDB 'select * from a_bit_of_everything' 'a_bit_of_everything(a_id, a_blob, a_bool, a_date, a_double, a_int, a_text)' 55 | \connect :SQDB 56 | select * from a_bit_of_everything; 57 | 58 | \connect :MSDB 59 | drop table if exists a_bit_of_everything; 60 | create table a_bit_of_everything ( 61 | -- a_id integer identity(1,1) primary key, -- doesn't work currently 62 | a_id integer, 63 | a_blob varbinary(max), 64 | a_bool bit, 65 | a_date datetime2, 66 | a_double double precision, 67 | a_int integer, 68 | a_text text 69 | ); 70 | \copy :PGDB :MSDB 'select * from a_bit_of_everything' 'a_bit_of_everything(a_id, a_blob, a_bool, a_date, a_double, a_int, a_text)' 71 | \connect :MSDB 72 | select * from a_bit_of_everything; 73 | 74 | \quit 75 | -------------------------------------------------------------------------------- /testdata/duckdb.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xo/usql/fe0d3cd0fc4972744460d79f936fb3fb025480c4/testdata/duckdb.db -------------------------------------------------------------------------------- /testdata/duckdb.db.wal: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xo/usql/fe0d3cd0fc4972744460d79f936fb3fb025480c4/testdata/duckdb.db.wal -------------------------------------------------------------------------------- /testdata/inc_test.sql: -------------------------------------------------------------------------------- 1 | select 'testdata/inc_test.sql'; 2 | 3 | \i sub/inc_test2.sql 4 | -------------------------------------------------------------------------------- /testdata/inc_test_z.sql: -------------------------------------------------------------------------------- 1 | select 'testdata/inc_test_z.sql'; 2 | -------------------------------------------------------------------------------- /testdata/numbers.sql: -------------------------------------------------------------------------------- 1 | select '1251258098.1555901285'::numeric; 2 | select '1251258098.1555901285'::float4; 3 | select '1251258098.1555901285'::float8; 4 | select '1251258098.1555901285'::double precision; 5 | -------------------------------------------------------------------------------- /testdata/quotes.sql: -------------------------------------------------------------------------------- 1 | -- echo all 2 | \set ECHO all 3 | -- conditional variables display FALSE when name is not set 4 | \unset foo 5 | \echo :{?foo} 6 | -- conditional variables display TRUE when name is set 7 | \set foo 'bar' 8 | \echo :{?foo} 9 | -- single quoted strings will decode '' as ' and decode \n, \t, \b, \r, \f, \digits octals, \xdigits (standard escapes) 10 | \set foo 'bar''bar\r\n' 11 | -- single quoted variables escape ' but does not escape special characters 12 | \echo :'foo' 13 | -- double quoted variables do not escape ' or special characters 14 | \echo :"foo" 15 | -- single quoted strings decode any other standard escape (\) as literal 16 | \set foo 'bar\'''bar' 17 | \echo :foo 18 | \echo :'foo' 19 | -- single quoted variables escape \ with E'' style strings 20 | \set foo 'bar\\\'' 21 | \echo :foo 22 | \echo :'foo' 23 | \echo :"foo" 24 | -- backticks interpolate unquoted variables 25 | \set foo 'bar' 26 | \echo `echo :foo` 27 | -- backticks interpolate single quoted variables 28 | \echo `echo :'foo'` 29 | -- backticks do not interpolate double quoted variables 30 | \echo `echo :"foo"` 31 | -- backticks have error messages for single quoted variables containing \r or \n when using :'' syntax 32 | \set foo 'bar\r\n' 33 | \echo `echo :'foo'` 34 | -------------------------------------------------------------------------------- /testdata/sub/inc_test2.sql: -------------------------------------------------------------------------------- 1 | select 'testdata/sub/inc_test2.sql'; 2 | 3 | select 'from testdata/sub/inc_test2.sql, doing: \i inc_test_z.sql'; 4 | 5 | \i inc_test_z.sql 6 | 7 | select 'from testdata/sub/inc_test2.sql, doing: \ir inc_test_z.sql'; 8 | 9 | \ir inc_test_z.sql 10 | -------------------------------------------------------------------------------- /testdata/sub/inc_test_z.sql: -------------------------------------------------------------------------------- 1 | select 'testdata/sub/inc_test_z.sql' 2 | -------------------------------------------------------------------------------- /text/license.go: -------------------------------------------------------------------------------- 1 | package text 2 | 3 | // Code generated by gen.go. DO NOT EDIT. 4 | 5 | // License contains the license text for usql. 6 | const License = `The MIT License (MIT) 7 | 8 | Copyright (c) 2015-2025 Kenneth Shaw 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE.` 27 | -------------------------------------------------------------------------------- /text/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xo/usql/fe0d3cd0fc4972744460d79f936fb3fb025480c4/text/logo.png -------------------------------------------------------------------------------- /update-deps.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SRC=$(realpath $(cd -P "$(dirname "${BASH_SOURCE[0]}")" && pwd)) 4 | 5 | set -e 6 | 7 | pushd $SRC &> /dev/null 8 | (set -x; 9 | go get -u -v -x $@ $(go list -tags 'all test' -f '{{ join .Imports "\n" }}' ./internal/...) 10 | ) 11 | PKGS=$(go list -tags 'all test' -f '{{ join .Imports "\n" }}'|grep 'github.com/xo/usql'|grep -v drivers|grep -v internal) 12 | (set -x; 13 | go get -u -v -x $@ $PKGS 14 | ) 15 | (set -x; 16 | go mod tidy 17 | ) 18 | popd &> /dev/null 19 | --------------------------------------------------------------------------------