├── .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 |
--------------------------------------------------------------------------------