chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
java/PostgresTest.class
|
||||
java/postgresql-42.7.3.jar
|
||||
node/node_modules/
|
||||
elixir/_build
|
||||
elixir/mix.lock
|
||||
elixir/deps
|
||||
elixir/postgrex-test
|
||||
dotnet/bin
|
||||
dotnet/obj
|
||||
dotnet/out
|
||||
c/libaprutil-test
|
||||
c/libdbi-test
|
||||
c/postgres-c-connector-test
|
||||
ruby/Gemfile.lock
|
||||
@@ -0,0 +1,214 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
FROM golang:1.26.2-alpine AS golang_cgo
|
||||
ENV CGO_ENABLED=1
|
||||
ENV GO_LDFLAGS="-linkmode external -extldflags '-static'"
|
||||
RUN apk add --no-cache build-base
|
||||
|
||||
# --- Build doltgres binary ---
|
||||
FROM golang_cgo AS doltgres_build
|
||||
RUN apk add --no-cache icu-dev icu-static
|
||||
RUN apk update && apk add --no-cache bash
|
||||
WORKDIR /root/building
|
||||
COPY go.mod doltgresql/
|
||||
WORKDIR doltgresql
|
||||
RUN go mod download
|
||||
COPY --exclude=testing/postgres-client-tests/ . .
|
||||
WORKDIR /root/building/doltgresql/postgres/parser
|
||||
RUN sh ./build.sh
|
||||
WORKDIR /root/building/doltgresql/cmd/doltgres
|
||||
RUN go build -ldflags "-linkmode external -extldflags '-static'" -o /build/bin/doltgres .
|
||||
|
||||
# --- Build Go postgres clients (pgx, lib/pq) ---
|
||||
FROM golang:1.26.4 AS go_clients_build
|
||||
COPY testing/postgres-client-tests/go/pgx/ /build/go/pgx/
|
||||
WORKDIR /build/go/pgx
|
||||
RUN go build -o /build/bin/pgx-test .
|
||||
COPY testing/postgres-client-tests/go/libpq/ /build/go/libpq/
|
||||
WORKDIR /build/go/libpq
|
||||
RUN go build -o /build/bin/libpq-test .
|
||||
|
||||
# --- Build Rust sqlx client ---
|
||||
FROM rust:1.96-slim-bookworm AS rust_clients_build
|
||||
RUN apt-get update && apt-get install -y pkg-config libssl-dev && rm -rf /var/lib/apt/lists/*
|
||||
COPY testing/postgres-client-tests/rust/ /build/rust/
|
||||
WORKDIR /build/rust
|
||||
RUN cargo build --release
|
||||
|
||||
# --- Build C libpq client ---
|
||||
FROM debian:bookworm-slim AS c_clients_build
|
||||
RUN apt-get update && apt-get install -y \
|
||||
gcc make pkg-config \
|
||||
libpq-dev \
|
||||
libapr1-dev libaprutil1-dev libaprutil1-dbd-pgsql \
|
||||
libdbi-dev libdbd-pgsql \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
COPY testing/postgres-client-tests/c/ /build/c/
|
||||
WORKDIR /build/c
|
||||
RUN make
|
||||
|
||||
# --- Build C++ libpqxx client ---
|
||||
FROM debian:bookworm-slim AS cpp_clients_build
|
||||
RUN apt-get update && apt-get install -y g++ make libpqxx-dev pkg-config && rm -rf /var/lib/apt/lists/*
|
||||
COPY testing/postgres-client-tests/cpp/ /build/cpp/
|
||||
WORKDIR /build/cpp
|
||||
RUN make
|
||||
|
||||
# --- Build ODBC psqlODBC client ---
|
||||
FROM debian:bookworm-slim AS odbc_clients_build
|
||||
RUN apt-get update && apt-get install -y gcc make unixodbc-dev && rm -rf /var/lib/apt/lists/*
|
||||
COPY testing/postgres-client-tests/odbc/ /build/odbc/
|
||||
WORKDIR /build/odbc
|
||||
RUN make
|
||||
|
||||
# --- Install Node postgres client deps ---
|
||||
FROM node:22-bookworm-slim AS node_clients_build
|
||||
COPY testing/postgres-client-tests/node/package.json /build/node/
|
||||
COPY testing/postgres-client-tests/node/package-lock.json /build/node/
|
||||
WORKDIR /build/node
|
||||
RUN npm install
|
||||
COPY testing/postgres-client-tests/node/ /build/node/
|
||||
|
||||
# --- Install Ruby pg gem ---
|
||||
FROM ruby:4.0.5-bookworm AS ruby_clients_build
|
||||
RUN apt-get update && apt-get install -y libyaml-dev libpq-dev && rm -rf /var/lib/apt/lists/*
|
||||
COPY testing/postgres-client-tests/ruby/Gemfile /build/ruby/
|
||||
WORKDIR /build/ruby
|
||||
RUN bundle install
|
||||
COPY testing/postgres-client-tests/ruby/ /build/ruby/
|
||||
|
||||
# --- Install Python deps ---
|
||||
FROM python:3.14-slim-bookworm AS python_clients_build
|
||||
RUN pip3 install --no-cache-dir --target /build/python-deps sqlalchemy==2.0.46
|
||||
COPY testing/postgres-client-tests/python/ /build/python/
|
||||
WORKDIR /build/python/
|
||||
|
||||
# --- Build .NET Npgsql client ---
|
||||
FROM mcr.microsoft.com/dotnet/sdk:9.0-bookworm-slim AS dotnet_clients_build
|
||||
COPY testing/postgres-client-tests/dotnet/ /build/dotnet/
|
||||
WORKDIR /build/dotnet
|
||||
RUN dotnet publish -c Release -o /build/output/
|
||||
|
||||
# --- Build Java R2DBC client ---
|
||||
FROM maven:3.9-eclipse-temurin-17 AS r2dbc_clients_build
|
||||
COPY testing/postgres-client-tests/r2dbc/ /build/r2dbc/
|
||||
WORKDIR /build/r2dbc
|
||||
RUN mvn package -q
|
||||
|
||||
# --- Build Elixir Postgrex client ---
|
||||
FROM hexpm/elixir:1.16.3-erlang-24.2-debian-bookworm-20260610-slim AS elixir_clients_build
|
||||
ENV ELIXIR_ERL_OPTIONS="+fnu"
|
||||
ENV LANG=C.UTF-8
|
||||
ENV LC_ALL=C.UTF-8
|
||||
RUN apt-get update && apt-get install -y elixir && rm -rf /var/lib/apt/lists/*
|
||||
COPY testing/postgres-client-tests/elixir/ /build/elixir/
|
||||
WORKDIR /build/elixir
|
||||
RUN mix local.hex --force && \
|
||||
mix local.rebar --force && \
|
||||
mix deps.get && \
|
||||
mix escript.build
|
||||
|
||||
# --- Build Swift PostgresNIO client ---
|
||||
FROM swift:6.0-bookworm AS swift_clients_build
|
||||
COPY testing/postgres-client-tests/swift/ /build/swift/
|
||||
WORKDIR /build/swift
|
||||
RUN swift build -c release --static-swift-stdlib
|
||||
|
||||
# --- Runtime ---
|
||||
FROM debian:bookworm-slim
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
RUN apt-get update && apt-get install -y \
|
||||
curl \
|
||||
gnupg \
|
||||
lsb-release && \
|
||||
sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list' && \
|
||||
curl -fsSL https://www.postgresql.org/media/keys/ACCC4CF8.asc | gpg --dearmor -o /etc/apt/trusted.gpg.d/postgresql.gpg && \
|
||||
curl -fsSL https://deb.nodesource.com/setup_22.x | bash -
|
||||
|
||||
# java JDBC dependencies
|
||||
COPY testing/postgres-client-tests/java/ /postgres-client-tests/java/
|
||||
RUN apt-get install -y \
|
||||
openjdk-17-jdk && \
|
||||
curl -L -o /postgres-client-tests/java/postgresql-42.7.3.jar https://jdbc.postgresql.org/download/postgresql-42.7.3.jar
|
||||
|
||||
# perl dependencies
|
||||
RUN apt-get install -y \
|
||||
python3 \
|
||||
python3-pip \
|
||||
python3-psycopg2 \
|
||||
perl \
|
||||
cpanminus \
|
||||
php \
|
||||
php-pgsql \
|
||||
r-base \
|
||||
libyaml-dev \
|
||||
libpq-dev \
|
||||
odbc-postgresql \
|
||||
g++ make libpqxx-dev pkg-config \
|
||||
gcc make unixodbc-dev \
|
||||
libapr1-dev libaprutil1-dev libaprutil1-dbd-pgsql \
|
||||
libdbi-dev libdbd-pgsql \
|
||||
ca-certificates-java \
|
||||
bats \
|
||||
lsof \
|
||||
postgresql-client-15 \
|
||||
nodejs \
|
||||
elixir \
|
||||
libicu-dev \
|
||||
git && \
|
||||
update-ca-certificates -f && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# install .NET
|
||||
RUN curl -sSL https://dot.net/v1/dotnet-install.sh -o dotnet-install.sh \
|
||||
&& chmod +x dotnet-install.sh \
|
||||
&& ./dotnet-install.sh --channel 9.0 --runtime aspnetcore --install-dir /usr/share/dotnet \
|
||||
&& ln -s /usr/share/dotnet/dotnet /usr/bin/dotnet \
|
||||
&& rm dotnet-install.sh
|
||||
|
||||
# cpan dependencies
|
||||
RUN cpanm --force DBD::Pg
|
||||
|
||||
# r dependencies
|
||||
COPY testing/postgres-client-tests/r/ /postgres-client-tests/r/
|
||||
RUN Rscript -e 'install.packages(c("RPostgres", "RPostgreSQL"), repos="https://cloud.r-project.org")'
|
||||
|
||||
ENV JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64/
|
||||
ENV GEM_HOME="/usr/local/bundle"
|
||||
ENV PYTHONPATH=/usr/local/lib/python-deps
|
||||
ENV ELIXIR_ERL_OPTIONS="+fnu"
|
||||
ENV LANG=C.UTF-8
|
||||
ENV LC_ALL=C.UTF-8
|
||||
|
||||
COPY --from=ruby_clients_build /usr/local/bin/ruby /usr/local/bin/
|
||||
COPY --from=ruby_clients_build /usr/local/lib/ /usr/local/lib/
|
||||
COPY --from=ruby_clients_build /usr/local/bundle/ /usr/local/bundle/
|
||||
RUN ldconfig
|
||||
|
||||
COPY --from=doltgres_build /build/bin/doltgres /usr/local/bin/doltgres
|
||||
COPY --from=go_clients_build /build/bin/pgx-test /build/bin/go/pgx-test
|
||||
COPY --from=go_clients_build /build/bin/libpq-test /build/bin/go/libpq-test
|
||||
COPY --from=rust_clients_build /build/rust/target/release/sqlx_exists_demo /build/bin/rust/sqlx_exists_demo
|
||||
COPY --from=c_clients_build /build/c/postgres-c-connector-test /build/bin/c/postgres-c-connector-test
|
||||
COPY --from=dotnet_clients_build /build/output /build/bin/dotnet
|
||||
COPY --from=r2dbc_clients_build /build/r2dbc/target/r2dbc-test-1.0.jar /build/bin/r2dbc/r2dbc-test.jar
|
||||
COPY --from=elixir_clients_build /build/elixir/postgrex-test /build/bin/elixir/postgrex-test
|
||||
COPY --from=swift_clients_build /build/swift/.build/release/postgresnio-test /build/bin/swift/postgresnio-test
|
||||
COPY --from=node_clients_build /build/node/ /postgres-client-tests/node/
|
||||
COPY --from=python_clients_build /build/python/ /postgres-client-tests/python/
|
||||
COPY --from=python_clients_build /build/python-deps/ /usr/local/lib/python-deps/
|
||||
COPY --from=ruby_clients_build /build/ruby/ /postgres-client-tests/ruby/
|
||||
|
||||
COPY testing/postgres-client-tests/c/ /postgres-client-tests/c/
|
||||
COPY testing/postgres-client-tests/cpp/ /postgres-client-tests/cpp/
|
||||
COPY testing/postgres-client-tests/odbc/ /postgres-client-tests/odbc/
|
||||
COPY testing/postgres-client-tests/drizzle/ /postgres-client-tests/drizzle/
|
||||
COPY testing/postgres-client-tests/helpers.bash /postgres-client-tests/
|
||||
COPY testing/postgres-client-tests/php/ /postgres-client-tests/php/
|
||||
COPY testing/postgres-client-tests/perl/ /postgres-client-tests/perl/
|
||||
COPY testing/postgres-client-tests/postgres-client-tests.bats /postgres-client-tests/
|
||||
COPY testing/postgres-client-tests/postgres-client-tests-entrypoint.sh /postgres-client-tests/entrypoint.sh
|
||||
|
||||
WORKDIR /postgres-client-tests
|
||||
ENTRYPOINT ["/postgres-client-tests/entrypoint.sh"]
|
||||
@@ -0,0 +1,21 @@
|
||||
## PostgreSQL Client Tests
|
||||
We created smoke tests for Doltgres's PostgreSQL client integrations, and we run these tests through GitHub Actions
|
||||
on pull requests.
|
||||
|
||||
These tests can be run locally using Docker. From the doltgresql directory of the repo, run:
|
||||
|
||||
```bash
|
||||
$ docker build -t postgres-client-tests -f testing/postgres-client-tests/Dockerfile .
|
||||
$ docker run postgres-client-tests:latest
|
||||
```
|
||||
|
||||
The `docker build` step will take a few minutes to complete as it needs to install all the dependencies in the image.
|
||||
|
||||
Running the built container will produce output like:
|
||||
```bash
|
||||
$ docker run postgres-client-tests:latest
|
||||
Running postgres-client-tests:
|
||||
1..2
|
||||
ok 1 postgres-connector-java client
|
||||
ok 2 node postgres client
|
||||
```
|
||||
@@ -0,0 +1,20 @@
|
||||
LIBPQ_CFLAGS := $(shell pkg-config --cflags libpq)
|
||||
LIBPQ_LDFLAGS := $(shell pkg-config --libs libpq)
|
||||
APR_CFLAGS := $(shell pkg-config --cflags apr-1 apr-util-1)
|
||||
APR_LDFLAGS := $(shell pkg-config --libs apr-1 apr-util-1)
|
||||
CFLAGS="$(apr-1-config --includes) $(apu-1-config --includes)"
|
||||
|
||||
all: postgres-c-connector-test libaprutil-test libdbi-test
|
||||
|
||||
postgres-c-connector-test: postgres-c-connector-test.c
|
||||
$(CC) $(LIBPQ_CFLAGS) -o $@ $^ $(LIBPQ_LDFLAGS)
|
||||
|
||||
libaprutil-test: libaprutil-test.c
|
||||
$(CC) $(APR_CFLAGS) -o $@ $^ $(APR_LDFLAGS)
|
||||
|
||||
libdbi-test: libdbi-test.c
|
||||
$(CC) -o $@ $^ -ldbi
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -f postgres-c-connector-test libaprutil-test libdbi-test
|
||||
@@ -0,0 +1,117 @@
|
||||
#include <apr.h>
|
||||
#include <apr_pools.h>
|
||||
#include <apr_dbd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static void fail(const char *op, const char *msg) {
|
||||
fprintf(stderr, "%s: %s\n", op, msg ? msg : "(no message)");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void exec_query(const apr_dbd_driver_t *drv, apr_dbd_t *handle, const char *sql) {
|
||||
int nrows = 0;
|
||||
int rv = apr_dbd_query(drv, handle, &nrows, sql);
|
||||
if (rv != 0)
|
||||
fail(sql, apr_dbd_error(drv, handle, rv));
|
||||
}
|
||||
|
||||
static const char *exec_scalar(const apr_dbd_driver_t *drv, apr_pool_t *pool,
|
||||
apr_dbd_t *handle, const char *sql) {
|
||||
apr_dbd_results_t *res = NULL;
|
||||
int rv = apr_dbd_select(drv, pool, handle, &res, sql, 1);
|
||||
if (rv != 0)
|
||||
fail(sql, apr_dbd_error(drv, handle, rv));
|
||||
apr_dbd_row_t *row = NULL;
|
||||
if (apr_dbd_get_row(drv, pool, res, &row, -1) != 0)
|
||||
fail(sql, "no rows returned");
|
||||
return apr_dbd_get_entry(drv, row, 0);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
if (argc < 3) {
|
||||
fprintf(stderr, "Usage: %s <user> <port>\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
const char *user = argv[1];
|
||||
const char *port = argv[2];
|
||||
|
||||
apr_initialize();
|
||||
apr_pool_t *pool;
|
||||
apr_pool_create(&pool, NULL);
|
||||
|
||||
apr_status_t rv = apr_dbd_init(pool);
|
||||
if (rv != APR_SUCCESS) {
|
||||
char buf[256];
|
||||
apr_strerror(rv, buf, sizeof(buf));
|
||||
fail("apr_dbd_init", buf);
|
||||
}
|
||||
|
||||
const apr_dbd_driver_t *driver;
|
||||
rv = apr_dbd_get_driver(pool, "pgsql", &driver);
|
||||
if (rv != APR_SUCCESS) {
|
||||
char buf[256];
|
||||
apr_strerror(rv, buf, sizeof(buf));
|
||||
fail("apr_dbd_get_driver(pgsql)", buf);
|
||||
}
|
||||
|
||||
char params[256];
|
||||
snprintf(params, sizeof(params),
|
||||
"host=localhost port=%s dbname=postgres user=%s password=password",
|
||||
port, user);
|
||||
apr_dbd_t *handle = NULL;
|
||||
const char *open_err = NULL;
|
||||
rv = apr_dbd_open_ex(driver, pool, params, &handle, &open_err);
|
||||
if (rv != APR_SUCCESS)
|
||||
fail("apr_dbd_open_ex", open_err);
|
||||
|
||||
const char *pk_str = exec_scalar(driver, pool, handle, "SELECT pk FROM test_table LIMIT 1");
|
||||
if (!pk_str || atoi(pk_str) != 1) {
|
||||
fprintf(stderr, "expected pk=1, got %s\n", pk_str ? pk_str : "NULL");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
exec_query(driver, handle, "INSERT INTO test_table VALUES (2)");
|
||||
|
||||
const char *count_str = exec_scalar(driver, pool, handle, "SELECT COUNT(*) FROM test_table");
|
||||
if (!count_str || atoi(count_str) != 2) {
|
||||
fprintf(stderr, "expected count=2, got %s\n", count_str ? count_str : "NULL");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
const char *dolt_queries[] = {
|
||||
"DROP TABLE IF EXISTS test",
|
||||
"CREATE TABLE test (pk int, value int, PRIMARY KEY(pk))",
|
||||
"INSERT INTO test (pk, value) VALUES (0, 0)",
|
||||
"SELECT dolt_add('-A')",
|
||||
"SELECT dolt_commit('-m', 'added table test')",
|
||||
"SELECT dolt_checkout('-b', 'mybranch')",
|
||||
"INSERT INTO test VALUES (1, 1)",
|
||||
"SELECT dolt_commit('-a', '-m', 'updated test')",
|
||||
"SELECT dolt_checkout('main')",
|
||||
"SELECT dolt_merge('mybranch')",
|
||||
NULL
|
||||
};
|
||||
for (int i = 0; dolt_queries[i]; i++)
|
||||
exec_query(driver, handle, dolt_queries[i]);
|
||||
|
||||
const char *log_count = exec_scalar(driver, pool, handle, "SELECT COUNT(*) FROM dolt_log");
|
||||
if (!log_count || atoi(log_count) != 4) {
|
||||
fprintf(stderr, "expected 4 dolt_log entries, got %s\n", log_count ? log_count : "NULL");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
const char *test_count = exec_scalar(driver, pool, handle, "SELECT COUNT(*) FROM test");
|
||||
if (!test_count || atoi(test_count) != 2) {
|
||||
fprintf(stderr, "expected 2 rows in test, got %s\n", test_count ? test_count : "NULL");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
apr_dbd_close(driver, handle);
|
||||
apr_pool_destroy(pool);
|
||||
apr_terminate();
|
||||
|
||||
printf("libaprutil apr_dbd test passed\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>English</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.apple.xcode.dsym.libaprutil-test</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>dSYM</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
</dict>
|
||||
</plist>
|
||||
BIN
Binary file not shown.
+5
@@ -0,0 +1,5 @@
|
||||
---
|
||||
triple: 'arm64-apple-darwin'
|
||||
binary-path: libaprutil-test
|
||||
relocations: []
|
||||
...
|
||||
@@ -0,0 +1,122 @@
|
||||
#include <dbi/dbi.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static void conn_fail(dbi_conn conn, const char *op) {
|
||||
const char *errstr = NULL;
|
||||
dbi_conn_error(conn, &errstr);
|
||||
fprintf(stderr, "%s: %s\n", op, errstr ? errstr : "(no message)");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static dbi_result exec_query(dbi_conn conn, const char *sql) {
|
||||
dbi_result res = dbi_conn_query(conn, sql);
|
||||
if (!res)
|
||||
conn_fail(conn, sql);
|
||||
return res;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
if (argc < 3) {
|
||||
fprintf(stderr, "Usage: %s <user> <port>\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
const char *user = argv[1];
|
||||
int port = atoi(argv[2]);
|
||||
|
||||
dbi_inst dbi;
|
||||
if (dbi_initialize_r(NULL, &dbi) < 0) {
|
||||
fprintf(stderr, "dbi_initialize_r failed: no drivers found\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
dbi_conn conn = dbi_conn_new_r("pgsql", dbi);
|
||||
if (!conn) {
|
||||
fprintf(stderr, "dbi_conn_new_r(pgsql) failed: driver not installed?\n");
|
||||
dbi_shutdown_r(dbi);
|
||||
return 1;
|
||||
}
|
||||
|
||||
dbi_conn_set_option(conn, "host", "localhost");
|
||||
dbi_conn_set_option_numeric(conn, "port", (long)port);
|
||||
dbi_conn_set_option(conn, "dbname", "postgres");
|
||||
dbi_conn_set_option(conn, "username", user);
|
||||
dbi_conn_set_option(conn, "password", "password");
|
||||
|
||||
if (dbi_conn_connect(conn) < 0)
|
||||
conn_fail(conn, "dbi_conn_connect");
|
||||
|
||||
// SELECT pk from test_table (set up by bats setup())
|
||||
dbi_result res = exec_query(conn, "SELECT pk FROM test_table LIMIT 1");
|
||||
if (!dbi_result_next_row(res)) {
|
||||
fprintf(stderr, "expected at least one row in test_table\n");
|
||||
exit(1);
|
||||
}
|
||||
int pk = dbi_result_get_int(res, "pk");
|
||||
dbi_result_free(res);
|
||||
if (pk != 1) {
|
||||
fprintf(stderr, "expected pk=1, got %d\n", pk);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// INSERT
|
||||
res = exec_query(conn, "INSERT INTO test_table VALUES (2)");
|
||||
dbi_result_free(res);
|
||||
|
||||
// COUNT
|
||||
res = exec_query(conn, "SELECT COUNT(*) FROM test_table");
|
||||
if (!dbi_result_next_row(res)) {
|
||||
fprintf(stderr, "expected count row\n");
|
||||
exit(1);
|
||||
}
|
||||
long long count = dbi_result_get_longlong(res, "count");
|
||||
dbi_result_free(res);
|
||||
if (count != 2) {
|
||||
fprintf(stderr, "expected count=2, got %lld\n", count);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Dolt workflow
|
||||
const char *dolt_queries[] = {
|
||||
"DROP TABLE IF EXISTS test",
|
||||
"CREATE TABLE test (pk int, value int, PRIMARY KEY(pk))",
|
||||
"INSERT INTO test (pk, value) VALUES (0, 0)",
|
||||
"SELECT dolt_add('-A')",
|
||||
"SELECT dolt_commit('-m', 'added table test')",
|
||||
"SELECT dolt_checkout('-b', 'mybranch')",
|
||||
"INSERT INTO test VALUES (1, 1)",
|
||||
"SELECT dolt_commit('-a', '-m', 'updated test')",
|
||||
"SELECT dolt_checkout('main')",
|
||||
"SELECT dolt_merge('mybranch')",
|
||||
NULL
|
||||
};
|
||||
for (int i = 0; dolt_queries[i]; i++) {
|
||||
res = exec_query(conn, dolt_queries[i]);
|
||||
dbi_result_free(res);
|
||||
}
|
||||
|
||||
res = exec_query(conn, "SELECT COUNT(*) FROM dolt_log");
|
||||
dbi_result_next_row(res);
|
||||
long long log_count = dbi_result_get_longlong(res, "count");
|
||||
dbi_result_free(res);
|
||||
if (log_count != 4) {
|
||||
fprintf(stderr, "expected 4 dolt_log entries, got %lld\n", log_count);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
res = exec_query(conn, "SELECT COUNT(*) FROM test");
|
||||
dbi_result_next_row(res);
|
||||
long long test_count = dbi_result_get_longlong(res, "count");
|
||||
dbi_result_free(res);
|
||||
if (test_count != 2) {
|
||||
fprintf(stderr, "expected 2 rows in test, got %lld\n", test_count);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
dbi_conn_close(conn);
|
||||
dbi_shutdown_r(dbi);
|
||||
|
||||
printf("libdbi pgsql test passed\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <libpq-fe.h>
|
||||
|
||||
#define QUERIES_SIZE 13
|
||||
|
||||
char *queries[QUERIES_SIZE] = {
|
||||
"create table test (pk int, value int, d1 decimal(9, 3), f1 float, c1 char(10), t1 text, primary key(pk))",
|
||||
"select * from test",
|
||||
"insert into test (pk, value, d1, f1, c1, t1) values (0,0,0.0,0.0,'abc','a1')",
|
||||
"select * from test",
|
||||
"select dolt_add('-A');",
|
||||
"select dolt_commit('-m', 'my commit')",
|
||||
"select COUNT(*) FROM dolt.log",
|
||||
"select dolt_checkout('-b', 'mybranch')",
|
||||
"insert into test (pk, value, d1, f1, c1, t1) values (10,10, 123456.789, 420.42,'example','some text')",
|
||||
"select dolt_commit('-a', '-m', 'my commit2')",
|
||||
"select dolt_checkout('main')",
|
||||
"select dolt_merge('mybranch')",
|
||||
"select COUNT(*) FROM dolt.log",
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
|
||||
char* user = argv[1];
|
||||
int port = atoi(argv[2]);
|
||||
|
||||
// Connect to the database
|
||||
// conninfo is a string of keywords and values separated by spaces.
|
||||
char conninfo[100];
|
||||
sprintf(conninfo, "dbname=postgres user=%s password=password host=localhost port=%d", user, port);
|
||||
|
||||
// Create a connection
|
||||
PGconn *conn = PQconnectdb(conninfo);
|
||||
|
||||
// Check if the connection is successful
|
||||
if (PQstatus(conn) != CONNECTION_OK) {
|
||||
// If not successful, print the error message and finish the connection
|
||||
printf("Error while connecting to the database server: %s\n", PQerrorMessage(conn));
|
||||
|
||||
// Finish the connection
|
||||
PQfinish(conn);
|
||||
|
||||
// Exit the program
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// We have successfully established a connection to the database server
|
||||
printf("Connection Established\n");
|
||||
printf("Port: %s\n", PQport(conn));
|
||||
printf("Host: %s\n", PQhost(conn));
|
||||
printf("DBName: %s\n", PQdb(conn));
|
||||
|
||||
for ( int i = 0; i < QUERIES_SIZE; i++ ) {
|
||||
// Submit the query and retrieve the result
|
||||
PGresult *res = PQexec(conn, queries[i]);
|
||||
|
||||
// Check the status of the query result
|
||||
ExecStatusType resStatus = PQresultStatus(res);
|
||||
|
||||
if (resStatus == PGRES_COMMAND_OK || resStatus == PGRES_TUPLES_OK) {
|
||||
// Successful completion of a command returning no data OR data (such as a SELECT or SHOW).
|
||||
|
||||
// Clear the result
|
||||
PQclear(res);
|
||||
} else {
|
||||
printf("QUERY FAILED: %s\n", queries[i]);
|
||||
// If not successful, print the error message and finish the connection
|
||||
printf("Error while executing the query: %s\n", PQerrorMessage(conn));
|
||||
|
||||
// Clear the result
|
||||
PQclear(res);
|
||||
|
||||
// Finish the connection
|
||||
PQfinish(conn);
|
||||
|
||||
// Exit the program
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Submit the query and retrieve the result
|
||||
PGresult *res = PQexec(conn, "SELECT * FROM test WHERE pk = 10");
|
||||
|
||||
// Check the status of the query result
|
||||
ExecStatusType resStatus = PQresultStatus(res);
|
||||
|
||||
if (resStatus != PGRES_TUPLES_OK) {
|
||||
printf("QUERY FAILED: %s\n", "SELECT * FROM test WHERE pk = 10");
|
||||
// If not successful, print the error message and finish the connection
|
||||
printf("Error while executing the query: %s\n", PQerrorMessage(conn));
|
||||
|
||||
// Clear the result
|
||||
PQclear(res);
|
||||
|
||||
// Finish the connection
|
||||
PQfinish(conn);
|
||||
|
||||
// Exit the program
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
// Get the number of columns in the query result
|
||||
int cols = PQnfields(res);
|
||||
printf("Number of cols: %d\n", cols);
|
||||
assert(cols == 6);
|
||||
|
||||
char *expectedCols[6] = {"pk", "value", "d1", "f1", "c1", "t1"};
|
||||
// Assert the column names
|
||||
for (int i = 0; i < cols; i++) {
|
||||
assert(strcmp(PQfname(res, i), expectedCols[i]) == 0);
|
||||
}
|
||||
|
||||
// Get the number of rows in the query result
|
||||
int rows = PQntuples(res);
|
||||
printf("Number of rows: %d\n", rows);
|
||||
assert(rows == 1);
|
||||
|
||||
char *expectedRowResults[6] = {"10", "10", "123456.789", "420.42", "example ", "some text"};
|
||||
// Assert query result
|
||||
for (int i = 0; i < rows; i++) {
|
||||
for (int j = 0; j < cols; j++) {
|
||||
char *actual = PQgetvalue(res, i, j);
|
||||
printf("EXPECTED: '%s'\n", expectedRowResults[j]);
|
||||
printf("ACTUAL: '%s'\n", actual);
|
||||
assert(strcmp(actual, expectedRowResults[j]) == 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Clear the result
|
||||
PQclear(res);
|
||||
|
||||
// Close the connection and free the memory
|
||||
PQfinish(conn);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
CXXFLAGS := -std=c++17 $(shell pkg-config --cflags libpqxx)
|
||||
LDFLAGS := $(shell pkg-config --libs libpqxx)
|
||||
|
||||
all: libpqxx-test
|
||||
|
||||
libpqxx-test: libpqxx-test.cpp
|
||||
$(CXX) $(CXXFLAGS) -o $@ $^ $(LDFLAGS)
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -f libpqxx-test
|
||||
@@ -0,0 +1,77 @@
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <pqxx/pqxx>
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
if (argc < 3) {
|
||||
std::cerr << "Usage: " << argv[0] << " <user> <port>\n";
|
||||
return 1;
|
||||
}
|
||||
std::string user = argv[1];
|
||||
std::string port = argv[2];
|
||||
|
||||
try {
|
||||
std::string connStr = "host=localhost port=" + port + " dbname=postgres user=" + user + " password=password sslmode=disable";
|
||||
pqxx::connection conn(connStr);
|
||||
pqxx::nontransaction ntxn(conn);
|
||||
|
||||
// SELECT from test_table (set up by bats setup())
|
||||
pqxx::result r = ntxn.exec("SELECT pk FROM test_table LIMIT 1");
|
||||
if (r.empty() || r[0][0].as<int>() != 1)
|
||||
throw std::runtime_error("expected pk=1");
|
||||
|
||||
// INSERT
|
||||
ntxn.exec("INSERT INTO test_table VALUES (2)");
|
||||
|
||||
// COUNT
|
||||
r = ntxn.exec("SELECT COUNT(*) FROM test_table");
|
||||
if (r[0][0].as<long long>() != 2)
|
||||
throw std::runtime_error("expected count=2, got " + std::to_string(r[0][0].as<long long>()));
|
||||
|
||||
// Prepared statement
|
||||
conn.prepare("select_pk", "SELECT pk FROM test_table WHERE pk = $1");
|
||||
r = ntxn.exec_prepared("select_pk", 1);
|
||||
if (r.empty() || r[0][0].as<int>() != 1)
|
||||
throw std::runtime_error("expected pk=1 from prepared stmt");
|
||||
|
||||
// Dolt workflow: create table, insert, commit, branch, insert, commit, merge
|
||||
for (const char *q : {
|
||||
"DROP TABLE IF EXISTS test",
|
||||
"CREATE TABLE test (pk int, value int, PRIMARY KEY(pk))",
|
||||
"INSERT INTO test (pk, value) VALUES (0, 0)",
|
||||
"SELECT dolt_add('-A')",
|
||||
"SELECT dolt_commit('-m', 'added table test')",
|
||||
"SELECT dolt_checkout('-b', 'mybranch')",
|
||||
"INSERT INTO test VALUES (1, 1)",
|
||||
"SELECT dolt_commit('-a', '-m', 'updated test')",
|
||||
"SELECT dolt_checkout('main')",
|
||||
"SELECT dolt_merge('mybranch')",
|
||||
}) {
|
||||
ntxn.exec(q);
|
||||
}
|
||||
|
||||
conn.prepare("select_test_by_pk", "SELECT pk, value FROM test WHERE pk = $1");
|
||||
r = ntxn.exec_prepared("select_test_by_pk", 0);
|
||||
if (r.empty())
|
||||
throw std::runtime_error("no rows for select_test_by_pk");
|
||||
if (r[0]["pk"].as<int>() != 0 || r[0]["value"].as<int>() != 0)
|
||||
throw std::runtime_error("expected pk=0 value=0");
|
||||
|
||||
conn.prepare("count_dolt_log", "SELECT COUNT(*) FROM dolt_log");
|
||||
r = ntxn.exec_prepared("count_dolt_log");
|
||||
if (r[0][0].as<long long>() != 4)
|
||||
throw std::runtime_error("expected 4 dolt_log entries, got " + std::to_string(r[0][0].as<long long>()));
|
||||
|
||||
conn.prepare("count_test", "SELECT COUNT(*) FROM test");
|
||||
r = ntxn.exec_prepared("count_test");
|
||||
if (r[0][0].as<long long>() != 2)
|
||||
throw std::runtime_error("expected 2 rows in test, got " + std::to_string(r[0][0].as<long long>()));
|
||||
|
||||
std::cout << "libpqxx test passed\n";
|
||||
} catch (const std::exception &e) {
|
||||
std::cerr << "Error: " << e.what() << "\n";
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
using Npgsql;
|
||||
|
||||
var user = args[0];
|
||||
var port = args[1];
|
||||
|
||||
var connStr = $"Host=localhost;Port={port};Username={user};Password=password;Database=postgres;SSL Mode=Disable";
|
||||
await using var conn = new NpgsqlConnection(connStr);
|
||||
await conn.OpenAsync();
|
||||
|
||||
// Basic SELECT
|
||||
await using (var cmd = new NpgsqlCommand("SELECT pk FROM test_table LIMIT 1", conn))
|
||||
{
|
||||
var pk = (int)(await cmd.ExecuteScalarAsync())!;
|
||||
if (pk != 1)
|
||||
throw new Exception($"expected pk=1, got {pk}");
|
||||
}
|
||||
|
||||
// INSERT
|
||||
await using (var cmd = new NpgsqlCommand("INSERT INTO test_table VALUES (2)", conn))
|
||||
await cmd.ExecuteNonQueryAsync();
|
||||
|
||||
// COUNT
|
||||
await using (var cmd = new NpgsqlCommand("SELECT COUNT(*) FROM test_table", conn))
|
||||
{
|
||||
var count = (long)(await cmd.ExecuteScalarAsync())!;
|
||||
if (count != 2)
|
||||
throw new Exception($"expected count=2, got {count}");
|
||||
}
|
||||
|
||||
// Prepared SELECT
|
||||
await using (var cmd = new NpgsqlCommand("SELECT pk FROM test_table WHERE pk = $1", conn))
|
||||
{
|
||||
cmd.Parameters.AddWithValue(1);
|
||||
await cmd.PrepareAsync();
|
||||
var pk = (int)(await cmd.ExecuteScalarAsync())!;
|
||||
if (pk != 1)
|
||||
throw new Exception($"expected pk=1 from prepared stmt, got {pk}");
|
||||
}
|
||||
|
||||
// Dolt workflow: create table, insert, commit, branch, insert, commit, merge
|
||||
foreach (var q in new[]
|
||||
{
|
||||
"DROP TABLE IF EXISTS test",
|
||||
"CREATE TABLE test (pk int, value int, PRIMARY KEY(pk))",
|
||||
"INSERT INTO test (pk, value) VALUES (0, 0)",
|
||||
"SELECT dolt_add('-A')",
|
||||
"SELECT dolt_commit('-m', 'added table test')",
|
||||
"SELECT dolt_checkout('-b', 'mybranch')",
|
||||
"INSERT INTO test VALUES (1, 1)",
|
||||
"SELECT dolt_commit('-a', '-m', 'updated test')",
|
||||
"SELECT dolt_checkout('main')",
|
||||
"SELECT dolt_merge('mybranch')",
|
||||
})
|
||||
{
|
||||
await using var cmd = new NpgsqlCommand(q, conn);
|
||||
await cmd.ExecuteNonQueryAsync();
|
||||
}
|
||||
|
||||
await RunPreparedQuery(
|
||||
"SELECT pk, value FROM test WHERE pk = $1",
|
||||
[0],
|
||||
async r =>
|
||||
{
|
||||
if (!await r.ReadAsync()) throw new Exception("no rows");
|
||||
var pk = r.GetInt32(0);
|
||||
var value = r.GetInt32(1);
|
||||
if (pk != 0 || value != 0)
|
||||
throw new Exception($"expected pk=0 value=0, got pk={pk} value={value}");
|
||||
});
|
||||
|
||||
await RunPreparedQuery(
|
||||
"SELECT COUNT(*) FROM dolt_log",
|
||||
[],
|
||||
async r =>
|
||||
{
|
||||
if (!await r.ReadAsync()) throw new Exception("no rows");
|
||||
var size = r.GetInt64(0);
|
||||
if (size != 4)
|
||||
throw new Exception($"expected 4 dolt_log entries, got {size}");
|
||||
});
|
||||
|
||||
await RunPreparedQuery(
|
||||
"SELECT COUNT(*) FROM test",
|
||||
[],
|
||||
async r =>
|
||||
{
|
||||
if (!await r.ReadAsync()) throw new Exception("no rows");
|
||||
var size = r.GetInt64(0);
|
||||
if (size != 2)
|
||||
throw new Exception($"expected 2 rows in test, got {size}");
|
||||
});
|
||||
|
||||
Console.WriteLine("Npgsql test passed");
|
||||
|
||||
async Task RunPreparedQuery(string query, object[] queryArgs, Func<NpgsqlDataReader, Task> check)
|
||||
{
|
||||
await using var cmd = new NpgsqlCommand(query, conn);
|
||||
foreach (var arg in queryArgs)
|
||||
cmd.Parameters.AddWithValue(arg);
|
||||
await cmd.PrepareAsync();
|
||||
await using var reader = await cmd.ExecuteReaderAsync();
|
||||
await check(reader);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<AssemblyName>npgsql-test</AssemblyName>
|
||||
<InvariantGlobalization>true</InvariantGlobalization>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Npgsql" Version="9.0.3" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,11 @@
|
||||
import 'dotenv/config';
|
||||
import { defineConfig } from 'drizzle-kit';
|
||||
export default defineConfig({
|
||||
out: './drizzle',
|
||||
schema: './src/db/schema.ts',
|
||||
dialect: 'postgresql',
|
||||
dbCredentials: {
|
||||
url: process.env.DATABASE_URL!,
|
||||
},
|
||||
tablesFilter: ["!dolt_*"], // IMPORTANT to filter out dolt system tables
|
||||
});
|
||||
@@ -0,0 +1,9 @@
|
||||
import { jsonb, integer, pgTable, varchar } from 'drizzle-orm/pg-core';
|
||||
|
||||
export const usersTable = pgTable("users", {
|
||||
id: integer().primaryKey(),
|
||||
name: varchar({ length: 255 }).notNull(),
|
||||
age: integer().notNull(),
|
||||
email: varchar({ length: 255 }).notNull().unique(),
|
||||
render: jsonb('render'),
|
||||
});
|
||||
@@ -0,0 +1,53 @@
|
||||
import 'dotenv/config';
|
||||
import { Pool } from 'pg';
|
||||
import { drizzle } from 'drizzle-orm/node-postgres';
|
||||
import { usersTable } from "./db/schema";
|
||||
import { eq } from 'drizzle-orm';
|
||||
|
||||
const connectionString = process.env.DATABASE_URL!
|
||||
const pool = new Pool({ connectionString });
|
||||
|
||||
const db = drizzle(pool);
|
||||
|
||||
async function main() {
|
||||
const user: typeof usersTable.$inferInsert = {
|
||||
id: 1,
|
||||
name: 'John',
|
||||
age: 30,
|
||||
render: null,
|
||||
email: 'john@example.com',
|
||||
};
|
||||
await db.insert(usersTable).values(user);
|
||||
console.log('New user created!')
|
||||
const users = await db.select().from(usersTable);
|
||||
console.log('Getting all users from the database: ', users)
|
||||
/*
|
||||
const users: {
|
||||
id: number;
|
||||
name: string;
|
||||
age: number;
|
||||
email: string;
|
||||
render: jsonb;
|
||||
}[]
|
||||
*/
|
||||
await db
|
||||
.update(usersTable)
|
||||
.set({
|
||||
age: 31,
|
||||
})
|
||||
.where(eq(usersTable.email, user.email));
|
||||
console.log('User info updated!')
|
||||
|
||||
// Verify the update
|
||||
const result = await db
|
||||
.select()
|
||||
.from(usersTable)
|
||||
.where(eq(usersTable.age, 31));
|
||||
console.log('Retrieved updated record:', result);
|
||||
|
||||
// Clean up
|
||||
await db.delete(usersTable).where(eq(usersTable.email, user.email));
|
||||
console.log('User deleted!')
|
||||
}
|
||||
|
||||
main();
|
||||
@@ -0,0 +1,47 @@
|
||||
defmodule PostgrexTest do
|
||||
def main(args) do
|
||||
[user, port_str] = args
|
||||
port = String.to_integer(port_str)
|
||||
|
||||
{:ok, _} = Application.ensure_all_started(:postgrex)
|
||||
|
||||
{:ok, conn} = Postgrex.start_link(
|
||||
hostname: "localhost",
|
||||
port: port,
|
||||
database: "postgres",
|
||||
username: user,
|
||||
password: "password",
|
||||
ssl: false
|
||||
)
|
||||
|
||||
{:ok, %{rows: [[pk]]}} = Postgrex.query(conn, "SELECT pk FROM test_table LIMIT 1", [])
|
||||
if pk != 1, do: raise("expected pk=1, got #{pk}")
|
||||
|
||||
{:ok, _} = Postgrex.query(conn, "INSERT INTO test_table VALUES (2)", [])
|
||||
|
||||
{:ok, %{rows: [[count]]}} = Postgrex.query(conn, "SELECT COUNT(*) FROM test_table", [])
|
||||
if count != 2, do: raise("expected count=2, got #{count}")
|
||||
|
||||
# Dolt workflow: create table, insert, commit, branch, insert, commit, merge
|
||||
Enum.each([
|
||||
"DROP TABLE IF EXISTS test",
|
||||
"CREATE TABLE test (pk int, value int, PRIMARY KEY(pk))",
|
||||
"INSERT INTO test (pk, value) VALUES (0, 0)",
|
||||
"SELECT dolt_add('-A')",
|
||||
"SELECT dolt_commit('-m', 'added table test')",
|
||||
"SELECT dolt_checkout('-b', 'mybranch')",
|
||||
"INSERT INTO test VALUES (1, 1)",
|
||||
"SELECT dolt_commit('-a', '-m', 'updated test')",
|
||||
"SELECT dolt_checkout('main')",
|
||||
"SELECT dolt_merge('mybranch')"
|
||||
], fn q -> {:ok, _} = Postgrex.query(conn, q, []) end)
|
||||
|
||||
{:ok, %{rows: [[log_count]]}} = Postgrex.query(conn, "SELECT COUNT(*) FROM dolt_log", [])
|
||||
if log_count != 4, do: raise("expected 4 dolt_log entries, got #{log_count}")
|
||||
|
||||
{:ok, %{rows: [[test_count]]}} = Postgrex.query(conn, "SELECT COUNT(*) FROM test", [])
|
||||
if test_count != 2, do: raise("expected 2 rows in test, got #{test_count}")
|
||||
|
||||
IO.puts("Postgrex test passed")
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,21 @@
|
||||
defmodule PostgrexTest.MixProject do
|
||||
use Mix.Project
|
||||
|
||||
def project do
|
||||
[
|
||||
app: :postgrex_test,
|
||||
version: "0.1.0",
|
||||
elixir: "~> 1.14",
|
||||
escript: [main_module: PostgrexTest, name: "postgrex-test"],
|
||||
deps: deps()
|
||||
]
|
||||
end
|
||||
|
||||
def application do
|
||||
[extra_applications: [:logger]]
|
||||
end
|
||||
|
||||
defp deps do
|
||||
[{:postgrex, "~> 0.18"}]
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,5 @@
|
||||
module libpq-test
|
||||
|
||||
go 1.26.3
|
||||
|
||||
require github.com/lib/pq v1.12.3 // indirect
|
||||
@@ -0,0 +1,2 @@
|
||||
github.com/lib/pq v1.12.3 h1:tTWxr2YLKwIvK90ZXEw8GP7UFHtcbTtty8zsI+YjrfQ=
|
||||
github.com/lib/pq v1.12.3/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA=
|
||||
@@ -0,0 +1,191 @@
|
||||
// Copyright 2026 Dolthub, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
_ "github.com/lib/pq"
|
||||
)
|
||||
|
||||
type ResFunc func(rows *sql.Rows) error
|
||||
|
||||
type StmtTest struct {
|
||||
Query string
|
||||
Args []interface{}
|
||||
Res []ResFunc
|
||||
}
|
||||
|
||||
func main() {
|
||||
user := os.Args[1]
|
||||
port := os.Args[2]
|
||||
|
||||
connStr := fmt.Sprintf("host=localhost port=%s user=%s password=password dbname=postgres sslmode=disable", port, user)
|
||||
db, err := sql.Open("postgres", connStr)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
if err = db.Ping(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
var pk int
|
||||
err = db.QueryRow("SELECT pk FROM test_table LIMIT 1").Scan(&pk)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if pk != 1 {
|
||||
panic(fmt.Sprintf("expected pk=1, got %d", pk))
|
||||
}
|
||||
|
||||
_, err = db.Exec("INSERT INTO test_table VALUES (2)")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
var count int
|
||||
err = db.QueryRow("SELECT COUNT(*) FROM test_table").Scan(&count)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if count != 2 {
|
||||
panic(fmt.Sprintf("expected count=2, got %d", count))
|
||||
}
|
||||
|
||||
stmt, err := db.Prepare("SELECT pk FROM test_table WHERE pk = $1")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer stmt.Close()
|
||||
|
||||
err = stmt.QueryRow(1).Scan(&pk)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if pk != 1 {
|
||||
panic(fmt.Sprintf("expected pk=1 from prepared stmt, got %d", pk))
|
||||
}
|
||||
|
||||
// dolt workflow: create table, insert, commit, branch, insert, commit, merge
|
||||
for _, q := range []string{
|
||||
"DROP TABLE IF EXISTS test",
|
||||
"CREATE TABLE test (pk int, value int, PRIMARY KEY(pk))",
|
||||
"INSERT INTO test (pk, value) VALUES (0, 0)",
|
||||
"SELECT dolt_add('-A')",
|
||||
"SELECT dolt_commit('-m', 'added table test')",
|
||||
"SELECT dolt_checkout('-b', 'mybranch')",
|
||||
"INSERT INTO test VALUES (1, 1)",
|
||||
"SELECT dolt_commit('-a', '-m', 'updated test')",
|
||||
"SELECT dolt_checkout('main')",
|
||||
"SELECT dolt_merge('mybranch')",
|
||||
} {
|
||||
if _, err = db.Exec(q); err != nil {
|
||||
panic(fmt.Sprintf("failed to execute %q: %v", q, err))
|
||||
}
|
||||
}
|
||||
|
||||
stmtTests := []StmtTest{
|
||||
{
|
||||
Query: "SELECT pk, value FROM test WHERE pk = $1",
|
||||
Args: []interface{}{int64(0)},
|
||||
Res: []ResFunc{
|
||||
func(rows *sql.Rows) error {
|
||||
var pk, value int64
|
||||
if err := rows.Scan(&pk, &value); err != nil {
|
||||
return err
|
||||
}
|
||||
if pk != 0 || value != 0 {
|
||||
return fmt.Errorf("expected pk=0 value=0, got pk=%d value=%d", pk, value)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Query: "SELECT COUNT(*) FROM dolt_log",
|
||||
Res: []ResFunc{
|
||||
func(rows *sql.Rows) error {
|
||||
var size int64
|
||||
if err := rows.Scan(&size); err != nil {
|
||||
return err
|
||||
}
|
||||
if size != 4 {
|
||||
return fmt.Errorf("expected 4 dolt_log entries, got %d", size)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Query: "SELECT COUNT(*) FROM test",
|
||||
Res: []ResFunc{
|
||||
func(rows *sql.Rows) error {
|
||||
var size int64
|
||||
if err := rows.Scan(&size); err != nil {
|
||||
return err
|
||||
}
|
||||
if size != 2 {
|
||||
return fmt.Errorf("expected 2 rows in test, got %d", size)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range stmtTests {
|
||||
func() {
|
||||
stmt, err := db.Prepare(test.Query)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("prepare %q: %v", test.Query, err))
|
||||
}
|
||||
defer func() {
|
||||
if err := stmt.Close(); err != nil {
|
||||
panic(fmt.Sprintf("stmt.Close() for %q: %v", test.Query, err))
|
||||
}
|
||||
}()
|
||||
|
||||
rows, err := stmt.Query(test.Args...)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("query %q: %v", test.Query, err))
|
||||
}
|
||||
defer func() {
|
||||
if err := rows.Close(); err != nil {
|
||||
panic(fmt.Sprintf("rows.Close() for %q: %v", test.Query, err))
|
||||
}
|
||||
}()
|
||||
|
||||
i := 0
|
||||
for rows.Next() {
|
||||
if i >= len(test.Res) {
|
||||
panic(fmt.Sprintf("too many rows for %q", test.Query))
|
||||
}
|
||||
if err := test.Res[i](rows); err != nil {
|
||||
panic(fmt.Sprintf("result %d of %q: %v", i, test.Query, err))
|
||||
}
|
||||
i++
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
panic(fmt.Sprintf("rows.Err() for %q: %v", test.Query, err))
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
fmt.Println("lib/pq test passed")
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
module pgx-test
|
||||
|
||||
go 1.26.3
|
||||
|
||||
require (
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
||||
github.com/jackc/pgx/v5 v5.10.0 // indirect
|
||||
golang.org/x/text v0.29.0 // indirect
|
||||
)
|
||||
@@ -0,0 +1,15 @@
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||
github.com/jackc/pgx/v5 v5.10.0 h1:VhSvgU2jSli8o3AqIEOTJr7rZwAEUVo4E4XhR94Zfr0=
|
||||
github.com/jackc/pgx/v5 v5.10.0/go.mod h1:mal1tBGAFfLHvZzaYh77YS/eC6IX9OWbRV1QIIM0Jn4=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
|
||||
golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
@@ -0,0 +1,179 @@
|
||||
// Copyright 2026 Dolthub, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
)
|
||||
|
||||
type ResFunc func(rows pgx.Rows) error
|
||||
|
||||
type StmtTest struct {
|
||||
Name string
|
||||
Query string
|
||||
Args []any
|
||||
Res []ResFunc
|
||||
}
|
||||
|
||||
func main() {
|
||||
user := os.Args[1]
|
||||
port := os.Args[2]
|
||||
|
||||
ctx := context.Background()
|
||||
connStr := fmt.Sprintf("postgres://%s:password@localhost:%s/postgres", user, port)
|
||||
conn, err := pgx.Connect(ctx, connStr)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer conn.Close(ctx)
|
||||
|
||||
var pk int
|
||||
err = conn.QueryRow(ctx, "SELECT pk FROM test_table LIMIT 1").Scan(&pk)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if pk != 1 {
|
||||
panic(fmt.Sprintf("expected pk=1, got %d", pk))
|
||||
}
|
||||
|
||||
_, err = conn.Exec(ctx, "INSERT INTO test_table VALUES (2)")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
var count int
|
||||
err = conn.QueryRow(ctx, "SELECT COUNT(*) FROM test_table").Scan(&count)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if count != 2 {
|
||||
panic(fmt.Sprintf("expected count=2, got %d", count))
|
||||
}
|
||||
|
||||
_, err = conn.Prepare(ctx, "select_pk", "SELECT pk FROM test_table WHERE pk = $1")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
err = conn.QueryRow(ctx, "select_pk", 1).Scan(&pk)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if pk != 1 {
|
||||
panic(fmt.Sprintf("expected pk=1 from prepared stmt, got %d", pk))
|
||||
}
|
||||
|
||||
// dolt workflow: create table, insert, commit, branch, insert, commit, merge
|
||||
for _, q := range []string{
|
||||
"DROP TABLE IF EXISTS test",
|
||||
"CREATE TABLE test (pk int, value int, PRIMARY KEY(pk))",
|
||||
"INSERT INTO test (pk, value) VALUES (0, 0)",
|
||||
"SELECT dolt_add('-A')",
|
||||
"SELECT dolt_commit('-m', 'added table test')",
|
||||
"SELECT dolt_checkout('-b', 'mybranch')",
|
||||
"INSERT INTO test VALUES (1, 1)",
|
||||
"SELECT dolt_commit('-a', '-m', 'updated test')",
|
||||
"SELECT dolt_checkout('main')",
|
||||
"SELECT dolt_merge('mybranch')",
|
||||
} {
|
||||
if _, err = conn.Exec(ctx, q); err != nil {
|
||||
panic(fmt.Sprintf("failed to execute %q: %v", q, err))
|
||||
}
|
||||
}
|
||||
|
||||
stmtTests := []StmtTest{
|
||||
{
|
||||
Name: "select_test_by_pk",
|
||||
Query: "SELECT pk, value FROM test WHERE pk = $1",
|
||||
Args: []any{int64(0)},
|
||||
Res: []ResFunc{
|
||||
func(rows pgx.Rows) error {
|
||||
var pk, value int64
|
||||
if err := rows.Scan(&pk, &value); err != nil {
|
||||
return err
|
||||
}
|
||||
if pk != 0 || value != 0 {
|
||||
return fmt.Errorf("expected pk=0 value=0, got pk=%d value=%d", pk, value)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "count_dolt_log",
|
||||
Query: "SELECT COUNT(*) FROM dolt_log",
|
||||
Res: []ResFunc{
|
||||
func(rows pgx.Rows) error {
|
||||
var size int64
|
||||
if err := rows.Scan(&size); err != nil {
|
||||
return err
|
||||
}
|
||||
if size != 4 {
|
||||
return fmt.Errorf("expected 4 dolt_log entries, got %d", size)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "count_test",
|
||||
Query: "SELECT COUNT(*) FROM test",
|
||||
Res: []ResFunc{
|
||||
func(rows pgx.Rows) error {
|
||||
var size int64
|
||||
if err := rows.Scan(&size); err != nil {
|
||||
return err
|
||||
}
|
||||
if size != 2 {
|
||||
return fmt.Errorf("expected 2 rows in test, got %d", size)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range stmtTests {
|
||||
func() {
|
||||
if _, err := conn.Prepare(ctx, test.Name, test.Query); err != nil {
|
||||
panic(fmt.Sprintf("prepare %q: %v", test.Query, err))
|
||||
}
|
||||
rows, err := conn.Query(ctx, test.Name, test.Args...)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("query %q: %v", test.Query, err))
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
i := 0
|
||||
for rows.Next() {
|
||||
if i >= len(test.Res) {
|
||||
panic(fmt.Sprintf("too many rows for %q", test.Query))
|
||||
}
|
||||
if err := test.Res[i](rows); err != nil {
|
||||
panic(fmt.Sprintf("result %d of %q: %v", i, test.Query, err))
|
||||
}
|
||||
i++
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
panic(fmt.Sprintf("rows.Err() for %q: %v", test.Query, err))
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
fmt.Println("pgx test passed")
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
start_doltgres_server() {
|
||||
REPO_NAME="doltgres_repo_$$"
|
||||
mkdir $REPO_NAME
|
||||
cd $REPO_NAME
|
||||
|
||||
USER="postgres"
|
||||
PORT=$( definePORT )
|
||||
CONFIG=$( defineCONFIG $PORT )
|
||||
echo "$CONFIG" > config.yaml
|
||||
|
||||
doltgres --data-dir=. -config=config.yaml &
|
||||
SERVER_PID=$!
|
||||
# Give the server a chance to start
|
||||
sleep 2
|
||||
}
|
||||
|
||||
setup_doltgres_repo() {
|
||||
run psql --version
|
||||
if [[ ! "$output" =~ "(PostgreSQL) 15" ]] && [[ ! "$output" =~ "(PostgreSQL) 16" ]] && [[ ! "$output" =~ "(PostgreSQL) 17" ]]; then
|
||||
echo "PSQL must be version 15, got $output"
|
||||
return 1
|
||||
fi
|
||||
|
||||
start_doltgres_server
|
||||
}
|
||||
|
||||
teardown_doltgres_repo() {
|
||||
kill $SERVER_PID
|
||||
rm -rf $REPO_NAME
|
||||
}
|
||||
|
||||
query_server() {
|
||||
PGPASSWORD="password" psql -U "${USER:-postgres}" -h localhost -p $PORT "$@" postgres
|
||||
}
|
||||
|
||||
definePORT() {
|
||||
getPORT=""
|
||||
for i in {0..9}
|
||||
do
|
||||
let getPORT="($$ + $i) % 4096 + 2048"
|
||||
portinuse=$(lsof -i -P -n | grep LISTEN | grep $attemptedPORT | wc -l)
|
||||
if [ $portinuse -eq 0 ]
|
||||
then
|
||||
echo "$getPORT"
|
||||
break
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
defineCONFIG() {
|
||||
PORT=$1
|
||||
cat <<EOF
|
||||
log_level: debug
|
||||
|
||||
behavior:
|
||||
read_only: false
|
||||
disable_client_multi_statements: false
|
||||
dolt_transaction_commit: false
|
||||
|
||||
user:
|
||||
name: "postgres"
|
||||
password: "password"
|
||||
|
||||
listener:
|
||||
host: localhost
|
||||
port: $PORT
|
||||
EOF
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.sql.ResultSet;
|
||||
|
||||
public class PostgresTest {
|
||||
public String query;
|
||||
public Integer expectedUpdateCount; // for queries that don't return set of results
|
||||
public Object fieldAccessor; // can be String (i.e column name), Integer (i.e. field position) and `null` for no result set queries.
|
||||
public String[] expectedResults;
|
||||
|
||||
// Parameterized constructor
|
||||
public PostgresTest(String query, Integer expectedUpdateCount, Object fieldAccessor, String[] expectedResults) {
|
||||
this.query = query;
|
||||
this.expectedUpdateCount = expectedUpdateCount;
|
||||
this.fieldAccessor = fieldAccessor;
|
||||
this.expectedResults = expectedResults;
|
||||
}
|
||||
// Getters and Setters (optional)
|
||||
public String getQuery() {
|
||||
return this.query;
|
||||
}
|
||||
|
||||
public Integer getExpectedUpdateCount() {
|
||||
return this.expectedUpdateCount;
|
||||
}
|
||||
|
||||
public Object getFieldAccessor() {
|
||||
return this.fieldAccessor;
|
||||
}
|
||||
|
||||
public String[] getExpectedResults() {
|
||||
return this.expectedResults;
|
||||
}
|
||||
|
||||
// test queries to be run against Doltgres
|
||||
private static final PostgresTest[] tests = {
|
||||
new PostgresTest("create table test (pk int, value int, d1 decimal(4,2), c1 char(10), primary key(pk))", 0, null, null),
|
||||
new PostgresTest("select pk from test", null, "pk", new String[]{}), // the table has no rows
|
||||
new PostgresTest("insert into test (pk, value, d1, c1) values (0,1,2.3,'hi'), (2,3,4.56,'hello')", 2, null, null),
|
||||
new PostgresTest("select * from test", null, "pk", new String[]{"0","2"}),
|
||||
new PostgresTest("select * from test", null, "value", new String[]{"1","3"}),
|
||||
// TODO: doltgres DECIMAL type result is returned as "2.3", should be "2.30"
|
||||
// new PostgresTest("select * from test", null, "d1", new String[]{"2.30","4.56"}),
|
||||
new PostgresTest("select * from test", null, "c1", new String[]{"hi ","hello "}),
|
||||
new PostgresTest("select dolt_add('-A')", null, "dolt_add", new String[]{"{0}"}),
|
||||
new PostgresTest("select length(dolt_commit('-m', 'my commit')::text)", null, 1, new String[]{"34"}),
|
||||
new PostgresTest("select COUNT(*) FROM dolt.log", null, 1, new String[]{"3"}),
|
||||
new PostgresTest("select dolt_checkout('-b', 'mybranch')", null, "dolt_checkout", new String[]{"{0,\"Switched to branch 'mybranch'\"}"}),
|
||||
new PostgresTest("insert into test (pk, value, d1, c1) values (1,1,12.34,'bye')", 1, null, null),
|
||||
new PostgresTest("select length(dolt_commit('-a', '-m', 'my commit2')::text)", null, 1, new String[]{"34"}),
|
||||
new PostgresTest("select dolt_checkout('main')", null, "dolt_checkout", new String[]{"{0,\"Switched to branch 'main'\"}"}),
|
||||
new PostgresTest("select length(dolt_merge('mybranch')::text)", null, 1, new String[]{"57"}),
|
||||
new PostgresTest("select COUNT(*) FROM dolt.log", null, "COUNT", new String[]{"4"}), // returns res
|
||||
};
|
||||
|
||||
public static void main(String[] args) {
|
||||
Connection conn = null;
|
||||
|
||||
String user = args[0];
|
||||
String port = args[1];
|
||||
|
||||
try {
|
||||
String url = "jdbc:postgresql://127.0.0.1:" + port + "/postgres";
|
||||
String password = "password";
|
||||
|
||||
conn = DriverManager.getConnection(url, user, password);
|
||||
Statement st = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
|
||||
|
||||
for (int i = 0; i < tests.length; i++) {
|
||||
PostgresTest t = tests[i];
|
||||
String query = t.getQuery();
|
||||
if ( st.execute(query) ) {
|
||||
String[] expectedResults = t.getExpectedResults();
|
||||
Object fieldAccessor = t.getFieldAccessor();
|
||||
ResultSet rs = st.getResultSet();
|
||||
int rowCount = getRowCount(rs);
|
||||
// Compare the row count to the length of the array
|
||||
if (rowCount != expectedResults.length) {
|
||||
System.out.println("Row count in ResultSet: " + rowCount + " does not match length of the expected results: " + expectedResults.length);
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
Integer j = 0;
|
||||
if (rs.next()) {
|
||||
String expected = expectedResults[j];
|
||||
String result = "";
|
||||
if (fieldAccessor instanceof String) {
|
||||
result = rs.getString((String)fieldAccessor);
|
||||
} else if (fieldAccessor instanceof Integer) {
|
||||
result = rs.getString((Integer)fieldAccessor);
|
||||
} else {
|
||||
System.out.println("Unsupported field accessor value: " + fieldAccessor);
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
if (!expected.equals(result) && !(query.contains("dolt_commit")) && !(query.contains("dolt_merge"))) {
|
||||
System.out.println("Query: \n" + query);
|
||||
System.out.println("Expected:\n'" + expected + "'");
|
||||
System.out.println("Result:\n'" + result + "'");
|
||||
System.exit(1);
|
||||
}
|
||||
j++;
|
||||
}
|
||||
} else {
|
||||
Integer expectedUpdateCount = t.getExpectedUpdateCount();
|
||||
Integer result = st.getUpdateCount();
|
||||
if ( !expectedUpdateCount.equals(result) ) {
|
||||
System.out.println("Query: \n" + query);
|
||||
System.out.println("Expected:\n" + expectedUpdateCount);
|
||||
System.out.println("Rows Updated:\n" + result);
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
System.exit(0);
|
||||
} catch (SQLException ex) {
|
||||
System.out.println("An error occurred.");
|
||||
ex.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
public static int getRowCount(ResultSet rs) throws SQLException {
|
||||
int rowCount = 0;
|
||||
if (rs != null) {
|
||||
rs.last(); // Move to the last row
|
||||
rowCount = rs.getRow(); // Get the row number (which is the row count)
|
||||
rs.beforeFirst(); // Move back to the beginning of the ResultSet
|
||||
}
|
||||
return rowCount;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
# Node Client Integration Tests
|
||||
|
||||
## Install
|
||||
|
||||
```
|
||||
$ npm install
|
||||
```
|
||||
|
||||
## Run node tests
|
||||
|
||||
To run the node tests, you must make sure you have Doltgres installed on your computer and
|
||||
have run `npm install`. Then update your Doltgres config by running:
|
||||
|
||||
```shell
|
||||
sh ../postgres-client-tests-entrypoint.sh
|
||||
```
|
||||
|
||||
And then you can run the tests using the `run-tests.sh` script, which sets up the database, runs the SQL server, runs the provided `.js` test file against the running server, and then tears down the database.
|
||||
|
||||
For example, you can run the `workbench.js` tests by running:
|
||||
|
||||
```shell
|
||||
sh run-tests.sh workbench.js
|
||||
```
|
||||
|
||||
## Workbench stability tests
|
||||
|
||||
The tests in `workbenchTests` were written to enforce the stability of the [SQL
|
||||
workbench](https://github.com/dolthub/dolt-workbench). The workbench uses many Doltgres
|
||||
system tables, functions, and procedures, and any changes to these interfaces can break
|
||||
the workbench. @tbantle22 will be tagged in any GitHub PR that updates those files to
|
||||
ensure appropriate workbench updates are made for Doltgres changes that break these
|
||||
queries.
|
||||
@@ -0,0 +1,28 @@
|
||||
import pkg from "pg";
|
||||
const { Client } = pkg;
|
||||
|
||||
export class Database {
|
||||
constructor(config) {
|
||||
this.client = new Client(config);
|
||||
this.client.connect();
|
||||
}
|
||||
|
||||
query(sql, args) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.client.query(sql, args, (err, rows) => {
|
||||
if (err) return reject(err);
|
||||
return resolve(rows);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
close() {
|
||||
this.client.end((err) => {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
} else {
|
||||
console.log("db connection closed");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,591 @@
|
||||
export const countFields = [
|
||||
{
|
||||
name: "count",
|
||||
tableID: 0,
|
||||
columnID: 1,
|
||||
dataTypeID: 20,
|
||||
dataTypeSize: 20,
|
||||
dataTypeModifier: -1,
|
||||
format: "text",
|
||||
},
|
||||
];
|
||||
|
||||
export const doltAddFields = [
|
||||
{
|
||||
name: "dolt_add",
|
||||
tableID: 0,
|
||||
columnID: 1,
|
||||
dataTypeID: 1009,
|
||||
dataTypeSize: -1,
|
||||
dataTypeModifier: -1,
|
||||
format: "text",
|
||||
},
|
||||
];
|
||||
|
||||
export const doltResetFields = [
|
||||
{
|
||||
name: "dolt_reset",
|
||||
tableID: 0,
|
||||
columnID: 1,
|
||||
dataTypeID: 1009,
|
||||
dataTypeSize: -1,
|
||||
dataTypeModifier: -1,
|
||||
format: "text",
|
||||
},
|
||||
];
|
||||
|
||||
export const doltBranchFields = [
|
||||
{
|
||||
name: "dolt_branch",
|
||||
tableID: 0,
|
||||
columnID: 1,
|
||||
dataTypeID: 1009,
|
||||
dataTypeSize: -1,
|
||||
dataTypeModifier: -1,
|
||||
format: "text",
|
||||
},
|
||||
];
|
||||
|
||||
export const doltCheckoutFields = [
|
||||
{
|
||||
name: "dolt_checkout",
|
||||
tableID: 0,
|
||||
columnID: 1,
|
||||
dataTypeID: 1009,
|
||||
dataTypeSize: -1,
|
||||
dataTypeModifier: -1,
|
||||
format: "text",
|
||||
},
|
||||
];
|
||||
|
||||
export const doltCleanFields = [
|
||||
{
|
||||
name: "dolt_clean",
|
||||
tableID: 0,
|
||||
columnID: 1,
|
||||
dataTypeID: 1009,
|
||||
dataTypeSize: -1,
|
||||
dataTypeModifier: -1,
|
||||
format: "text",
|
||||
},
|
||||
];
|
||||
|
||||
export const doltCommitFields = [
|
||||
{
|
||||
name: "dolt_commit",
|
||||
tableID: 0,
|
||||
columnID: 1,
|
||||
dataTypeID: 1009,
|
||||
dataTypeSize: -1,
|
||||
dataTypeModifier: -1,
|
||||
format: "text",
|
||||
},
|
||||
];
|
||||
|
||||
export const doltSchemasFields = [
|
||||
{
|
||||
name: "type",
|
||||
tableID: 0,
|
||||
columnID: 1,
|
||||
dataTypeID: 1043,
|
||||
dataTypeSize: 256,
|
||||
dataTypeModifier: -1,
|
||||
format: "text",
|
||||
},
|
||||
{
|
||||
name: "name",
|
||||
tableID: 0,
|
||||
columnID: 2,
|
||||
dataTypeID: 1043,
|
||||
dataTypeSize: 256,
|
||||
dataTypeModifier: -1,
|
||||
format: "text",
|
||||
},
|
||||
{
|
||||
name: "fragment",
|
||||
tableID: 0,
|
||||
columnID: 3,
|
||||
dataTypeID: 25,
|
||||
dataTypeSize: -1,
|
||||
dataTypeModifier: -1,
|
||||
format: "text",
|
||||
},
|
||||
{
|
||||
name: "extra",
|
||||
tableID: 0,
|
||||
columnID: 4,
|
||||
dataTypeID: 114,
|
||||
dataTypeSize: -1,
|
||||
dataTypeModifier: -1,
|
||||
format: "text",
|
||||
},
|
||||
{
|
||||
name: "sql_mode",
|
||||
tableID: 0,
|
||||
columnID: 5,
|
||||
dataTypeID: 1043,
|
||||
dataTypeSize: 1024,
|
||||
dataTypeModifier: -1,
|
||||
format: "text",
|
||||
},
|
||||
];
|
||||
|
||||
export const doltDocsFields = [
|
||||
{
|
||||
name: "doc_name",
|
||||
tableID: 0,
|
||||
columnID: 1,
|
||||
dataTypeID: 25,
|
||||
dataTypeSize: -1,
|
||||
dataTypeModifier: -1,
|
||||
format: "text",
|
||||
},
|
||||
{
|
||||
name: "doc_text",
|
||||
tableID: 0,
|
||||
columnID: 2,
|
||||
dataTypeID: 25,
|
||||
dataTypeSize: -1,
|
||||
dataTypeModifier: -1,
|
||||
format: "text",
|
||||
},
|
||||
];
|
||||
|
||||
export const doltStatusFields = [
|
||||
{
|
||||
name: "table_name",
|
||||
tableID: 0,
|
||||
columnID: 1,
|
||||
dataTypeID: 25,
|
||||
dataTypeSize: -1,
|
||||
dataTypeModifier: -1,
|
||||
format: "text",
|
||||
},
|
||||
{
|
||||
name: "staged",
|
||||
tableID: 0,
|
||||
columnID: 2,
|
||||
dataTypeID: 16,
|
||||
dataTypeSize: 1,
|
||||
dataTypeModifier: -1,
|
||||
format: "text",
|
||||
},
|
||||
{
|
||||
name: "status",
|
||||
tableID: 0,
|
||||
columnID: 3,
|
||||
dataTypeID: 25,
|
||||
dataTypeSize: -1,
|
||||
dataTypeModifier: -1,
|
||||
format: "text",
|
||||
},
|
||||
];
|
||||
|
||||
export const doltTagFields = [
|
||||
{
|
||||
name: "dolt_tag",
|
||||
tableID: 0,
|
||||
columnID: 1,
|
||||
dataTypeID: 1009,
|
||||
dataTypeSize: -1,
|
||||
dataTypeModifier: -1,
|
||||
format: "text",
|
||||
},
|
||||
];
|
||||
|
||||
export const doltTagsFields = [
|
||||
{
|
||||
name: "tag_name",
|
||||
tableID: 0,
|
||||
columnID: 1,
|
||||
dataTypeID: 25,
|
||||
dataTypeSize: -4,
|
||||
dataTypeModifier: -1,
|
||||
format: "text",
|
||||
},
|
||||
{
|
||||
name: "tag_hash",
|
||||
tableID: 0,
|
||||
columnID: 2,
|
||||
dataTypeID: 25,
|
||||
dataTypeSize: -4,
|
||||
dataTypeModifier: -1,
|
||||
format: "text",
|
||||
},
|
||||
{
|
||||
name: "tagger",
|
||||
tableID: 0,
|
||||
columnID: 3,
|
||||
dataTypeID: 25,
|
||||
dataTypeSize: -4,
|
||||
dataTypeModifier: -1,
|
||||
format: "text",
|
||||
},
|
||||
{
|
||||
name: "email",
|
||||
tableID: 0,
|
||||
columnID: 4,
|
||||
dataTypeID: 25,
|
||||
dataTypeSize: -4,
|
||||
dataTypeModifier: -1,
|
||||
format: "text",
|
||||
},
|
||||
{
|
||||
name: "date",
|
||||
tableID: 0,
|
||||
columnID: 5,
|
||||
dataTypeID: 1114,
|
||||
dataTypeSize: 26,
|
||||
dataTypeModifier: -1,
|
||||
format: "text",
|
||||
},
|
||||
{
|
||||
name: "message",
|
||||
tableID: 0,
|
||||
columnID: 6,
|
||||
dataTypeID: 25,
|
||||
dataTypeSize: -4,
|
||||
dataTypeModifier: -1,
|
||||
format: "text",
|
||||
},
|
||||
];
|
||||
|
||||
export const infoSchemaKeyColumnUsageFields = [
|
||||
{
|
||||
name: "CONSTRAINT_CATALOG",
|
||||
tableID: 0,
|
||||
columnID: 1,
|
||||
dataTypeID: 1043,
|
||||
dataTypeSize: 192,
|
||||
dataTypeModifier: -1,
|
||||
format: "text",
|
||||
},
|
||||
{
|
||||
name: "CONSTRAINT_SCHEMA",
|
||||
tableID: 0,
|
||||
columnID: 2,
|
||||
dataTypeID: 1043,
|
||||
dataTypeSize: 192,
|
||||
dataTypeModifier: -1,
|
||||
format: "text",
|
||||
},
|
||||
{
|
||||
name: "CONSTRAINT_NAME",
|
||||
tableID: 0,
|
||||
columnID: 3,
|
||||
dataTypeID: 1043,
|
||||
dataTypeSize: 192,
|
||||
dataTypeModifier: -1,
|
||||
format: "text",
|
||||
},
|
||||
{
|
||||
name: "TABLE_CATALOG",
|
||||
tableID: 0,
|
||||
columnID: 4,
|
||||
dataTypeID: 1043,
|
||||
dataTypeSize: 192,
|
||||
dataTypeModifier: -1,
|
||||
format: "text",
|
||||
},
|
||||
{
|
||||
name: "TABLE_SCHEMA",
|
||||
tableID: 0,
|
||||
columnID: 5,
|
||||
dataTypeID: 1043,
|
||||
dataTypeSize: 192,
|
||||
dataTypeModifier: -1,
|
||||
format: "text",
|
||||
},
|
||||
{
|
||||
name: "TABLE_NAME",
|
||||
tableID: 0,
|
||||
columnID: 6,
|
||||
dataTypeID: 1043,
|
||||
dataTypeSize: 192,
|
||||
dataTypeModifier: -1,
|
||||
format: "text",
|
||||
},
|
||||
{
|
||||
name: "COLUMN_NAME",
|
||||
tableID: 0,
|
||||
columnID: 7,
|
||||
dataTypeID: 1043,
|
||||
dataTypeSize: 192,
|
||||
dataTypeModifier: -1,
|
||||
format: "text",
|
||||
},
|
||||
{
|
||||
name: "ORDINAL_POSITION",
|
||||
tableID: 0,
|
||||
columnID: 8,
|
||||
dataTypeID: 26,
|
||||
dataTypeSize: 10,
|
||||
dataTypeModifier: -1,
|
||||
format: "text",
|
||||
},
|
||||
{
|
||||
name: "POSITION_IN_UNIQUE_CONSTRAINT",
|
||||
tableID: 0,
|
||||
columnID: 9,
|
||||
dataTypeID: 26,
|
||||
dataTypeSize: 10,
|
||||
dataTypeModifier: -1,
|
||||
format: "text",
|
||||
},
|
||||
{
|
||||
name: "REFERENCED_TABLE_SCHEMA",
|
||||
tableID: 0,
|
||||
columnID: 10,
|
||||
dataTypeID: 1043,
|
||||
dataTypeSize: 192,
|
||||
dataTypeModifier: -1,
|
||||
format: "text",
|
||||
},
|
||||
{
|
||||
name: "REFERENCED_TABLE_NAME",
|
||||
tableID: 0,
|
||||
columnID: 11,
|
||||
dataTypeID: 1043,
|
||||
dataTypeSize: 192,
|
||||
dataTypeModifier: -1,
|
||||
format: "text",
|
||||
},
|
||||
{
|
||||
name: "REFERENCED_COLUMN_NAME",
|
||||
tableID: 0,
|
||||
columnID: 12,
|
||||
dataTypeID: 1043,
|
||||
dataTypeSize: 192,
|
||||
dataTypeModifier: -1,
|
||||
format: "text",
|
||||
},
|
||||
];
|
||||
|
||||
export const schemaNameField = {
|
||||
name: "schema_name",
|
||||
tableID: 0,
|
||||
columnID: 1,
|
||||
dataTypeID: 1043,
|
||||
dataTypeSize: 256,
|
||||
dataTypeModifier: 68,
|
||||
format: "text",
|
||||
};
|
||||
|
||||
export const pgTablesFields = [
|
||||
{
|
||||
name: "schemaname",
|
||||
tableID: 0,
|
||||
columnID: 1,
|
||||
dataTypeID: 19,
|
||||
dataTypeSize: 64,
|
||||
dataTypeModifier: -1,
|
||||
format: "text",
|
||||
},
|
||||
{
|
||||
name: "tablename",
|
||||
tableID: 0,
|
||||
columnID: 2,
|
||||
dataTypeID: 19,
|
||||
dataTypeSize: 64,
|
||||
dataTypeModifier: -1,
|
||||
format: "text",
|
||||
},
|
||||
];
|
||||
|
||||
export const doltDiffStatFields = [
|
||||
{
|
||||
name: "table_name",
|
||||
tableID: 0,
|
||||
columnID: 1,
|
||||
dataTypeID: 25,
|
||||
dataTypeSize: -1,
|
||||
dataTypeModifier: -1,
|
||||
format: "text",
|
||||
},
|
||||
{
|
||||
name: "rows_unmodified",
|
||||
tableID: 0,
|
||||
columnID: 2,
|
||||
dataTypeID: 20,
|
||||
dataTypeSize: 20,
|
||||
dataTypeModifier: -1,
|
||||
format: "text",
|
||||
},
|
||||
{
|
||||
name: "rows_added",
|
||||
tableID: 0,
|
||||
columnID: 3,
|
||||
dataTypeID: 20,
|
||||
dataTypeSize: 20,
|
||||
dataTypeModifier: -1,
|
||||
format: "text",
|
||||
},
|
||||
{
|
||||
name: "rows_deleted",
|
||||
tableID: 0,
|
||||
columnID: 4,
|
||||
dataTypeID: 20,
|
||||
dataTypeSize: 20,
|
||||
dataTypeModifier: -1,
|
||||
format: "text",
|
||||
},
|
||||
{
|
||||
name: "rows_modified",
|
||||
tableID: 0,
|
||||
columnID: 5,
|
||||
dataTypeID: 20,
|
||||
dataTypeSize: 20,
|
||||
dataTypeModifier: -1,
|
||||
format: "text",
|
||||
},
|
||||
{
|
||||
name: "cells_added",
|
||||
tableID: 0,
|
||||
columnID: 6,
|
||||
dataTypeID: 20,
|
||||
dataTypeSize: 20,
|
||||
dataTypeModifier: -1,
|
||||
format: "text",
|
||||
},
|
||||
{
|
||||
name: "cells_deleted",
|
||||
tableID: 0,
|
||||
columnID: 7,
|
||||
dataTypeID: 20,
|
||||
dataTypeSize: 20,
|
||||
dataTypeModifier: -1,
|
||||
format: "text",
|
||||
},
|
||||
{
|
||||
name: "cells_modified",
|
||||
tableID: 0,
|
||||
columnID: 8,
|
||||
dataTypeID: 20,
|
||||
dataTypeSize: 20,
|
||||
dataTypeModifier: -1,
|
||||
format: "text",
|
||||
},
|
||||
{
|
||||
name: "old_row_count",
|
||||
tableID: 0,
|
||||
columnID: 9,
|
||||
dataTypeID: 20,
|
||||
dataTypeSize: 20,
|
||||
dataTypeModifier: -1,
|
||||
format: "text",
|
||||
},
|
||||
{
|
||||
name: "new_row_count",
|
||||
tableID: 0,
|
||||
columnID: 10,
|
||||
dataTypeID: 20,
|
||||
dataTypeSize: 20,
|
||||
dataTypeModifier: -1,
|
||||
format: "text",
|
||||
},
|
||||
{
|
||||
name: "old_cell_count",
|
||||
tableID: 0,
|
||||
columnID: 11,
|
||||
dataTypeID: 20,
|
||||
dataTypeSize: 20,
|
||||
dataTypeModifier: -1,
|
||||
format: "text",
|
||||
},
|
||||
{
|
||||
name: "new_cell_count",
|
||||
tableID: 0,
|
||||
columnID: 12,
|
||||
dataTypeID: 20,
|
||||
dataTypeSize: 20,
|
||||
dataTypeModifier: -1,
|
||||
format: "text",
|
||||
},
|
||||
];
|
||||
|
||||
export const doltDiffSummaryFields = [
|
||||
{
|
||||
name: "from_table_name",
|
||||
tableID: 0,
|
||||
columnID: 1,
|
||||
dataTypeID: 25,
|
||||
dataTypeSize: -1,
|
||||
dataTypeModifier: -1,
|
||||
format: "text",
|
||||
},
|
||||
{
|
||||
name: "to_table_name",
|
||||
tableID: 0,
|
||||
columnID: 2,
|
||||
dataTypeID: 25,
|
||||
dataTypeSize: -1,
|
||||
dataTypeModifier: -1,
|
||||
format: "text",
|
||||
},
|
||||
{
|
||||
name: "diff_type",
|
||||
tableID: 0,
|
||||
columnID: 3,
|
||||
dataTypeID: 25,
|
||||
dataTypeSize: -4,
|
||||
dataTypeModifier: -1,
|
||||
format: "text",
|
||||
},
|
||||
{
|
||||
name: "data_change",
|
||||
tableID: 0,
|
||||
columnID: 4,
|
||||
dataTypeID: 21,
|
||||
dataTypeSize: 4,
|
||||
dataTypeModifier: -1,
|
||||
format: "text",
|
||||
},
|
||||
{
|
||||
name: "schema_change",
|
||||
tableID: 0,
|
||||
columnID: 5,
|
||||
dataTypeID: 21,
|
||||
dataTypeSize: 4,
|
||||
dataTypeModifier: -1,
|
||||
format: "text",
|
||||
},
|
||||
];
|
||||
|
||||
export const doltSchemaDiffFields = [
|
||||
{
|
||||
name: "from_table_name",
|
||||
tableID: 0,
|
||||
columnID: 1,
|
||||
dataTypeID: 25,
|
||||
dataTypeSize: -1,
|
||||
dataTypeModifier: -1,
|
||||
format: "text",
|
||||
},
|
||||
{
|
||||
name: "to_table_name",
|
||||
tableID: 0,
|
||||
columnID: 2,
|
||||
dataTypeID: 25,
|
||||
dataTypeSize: -1,
|
||||
dataTypeModifier: -1,
|
||||
format: "text",
|
||||
},
|
||||
{
|
||||
name: "from_create_statement",
|
||||
tableID: 0,
|
||||
columnID: 3,
|
||||
dataTypeID: 25,
|
||||
dataTypeSize: -4,
|
||||
dataTypeModifier: -1,
|
||||
format: "text",
|
||||
},
|
||||
{
|
||||
name: "to_create_statement",
|
||||
tableID: 0,
|
||||
columnID: 4,
|
||||
dataTypeID: 25,
|
||||
dataTypeSize: -4,
|
||||
dataTypeModifier: -1,
|
||||
format: "text",
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,64 @@
|
||||
const args = process.argv.slice(2);
|
||||
const user = args[0];
|
||||
const port = args[1];
|
||||
const version = args[2];
|
||||
|
||||
export const dbName = "postgres";
|
||||
|
||||
export function getArgs() {
|
||||
return { user, port };
|
||||
}
|
||||
|
||||
export function getDoltgresVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
export function getConfig() {
|
||||
const { user, port } = getArgs();
|
||||
return {
|
||||
host: "localhost",
|
||||
port: port,
|
||||
database: dbName,
|
||||
user: user,
|
||||
password: "password",
|
||||
};
|
||||
}
|
||||
|
||||
export function assertEqualRows(test, data) {
|
||||
const expected = test.res;
|
||||
const resultStr = JSON.stringify(data);
|
||||
const result = JSON.parse(resultStr);
|
||||
if (!assertQueryResult(test.q, expected, data, test.matcher)) {
|
||||
console.log("Query:", test.q);
|
||||
console.log("Results:", result);
|
||||
console.log("Expected:", expected);
|
||||
throw new Error("Query failed");
|
||||
} else {
|
||||
console.log("Query succeeded:", test.q);
|
||||
}
|
||||
}
|
||||
|
||||
export function assertQueryResult(q, expected, data, matcher) {
|
||||
if (matcher) {
|
||||
return matcher(data, expected);
|
||||
}
|
||||
if (q.toLowerCase().includes("dolt_commit")) {
|
||||
if (data.rows.length !== 1) return false;
|
||||
const hash = data.rows[0].dolt_commit[0];
|
||||
if (hash.length !== 32) {
|
||||
console.log("Invalid hash for dolt_commit:", hash);
|
||||
return false;
|
||||
}
|
||||
expected.rows[0].dolt_commit = data.rows[0].dolt_commit;
|
||||
}
|
||||
|
||||
// Does partial matching of actual and expected results.
|
||||
const partialRes = {
|
||||
command: data.command,
|
||||
rowCount: data.rowCount,
|
||||
oid: data.oid,
|
||||
rows: data.rows,
|
||||
fields: data.fields,
|
||||
};
|
||||
return JSON.stringify(expected) === JSON.stringify(partialRes);
|
||||
}
|
||||
@@ -0,0 +1,208 @@
|
||||
import { Database } from "./database.js";
|
||||
import { assertEqualRows, getConfig } from "./helpers.js";
|
||||
import {
|
||||
doltAddFields,
|
||||
doltCheckoutFields,
|
||||
doltCommitFields,
|
||||
countFields,
|
||||
} from "./fields.js";
|
||||
import { mergeMatcher } from "./workbenchTests/matchers.js";
|
||||
|
||||
const tests = [
|
||||
{
|
||||
q: "create table test (pk int, value int, primary key(pk))",
|
||||
res: {
|
||||
command: "CREATE",
|
||||
rowCount: null,
|
||||
oid: null,
|
||||
rows: [],
|
||||
fields: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
q: "select * from test",
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 0,
|
||||
oid: null,
|
||||
rows: [],
|
||||
fields: [
|
||||
{
|
||||
name: "pk",
|
||||
tableID: 0, // TODO: need to be filled? Got 16859 from Postgres
|
||||
columnID: 1,
|
||||
dataTypeID: 23,
|
||||
dataTypeSize: 4,
|
||||
dataTypeModifier: -1,
|
||||
format: "text",
|
||||
},
|
||||
{
|
||||
name: "value",
|
||||
tableID: 0, // TODO: need to be filled? Got 16859 from Postgres
|
||||
columnID: 2,
|
||||
dataTypeID: 23,
|
||||
dataTypeSize: 4,
|
||||
dataTypeModifier: -1,
|
||||
format: "text",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
q: "insert into test (pk, value) values (0,0)",
|
||||
res: {
|
||||
command: "INSERT",
|
||||
rowCount: 1,
|
||||
oid: 0,
|
||||
rows: [],
|
||||
fields: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
q: "select * from test",
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 1,
|
||||
oid: null,
|
||||
rows: [{ pk: 0, value: 0 }],
|
||||
fields: [
|
||||
{
|
||||
name: "pk",
|
||||
tableID: 0,
|
||||
columnID: 1,
|
||||
dataTypeID: 23,
|
||||
dataTypeSize: 4,
|
||||
dataTypeModifier: -1,
|
||||
format: "text",
|
||||
},
|
||||
{
|
||||
name: "value",
|
||||
tableID: 0,
|
||||
columnID: 2,
|
||||
dataTypeID: 23,
|
||||
dataTypeSize: 4,
|
||||
dataTypeModifier: -1,
|
||||
format: "text",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
q: "select dolt_add('-A');",
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 1,
|
||||
oid: null,
|
||||
rows: [{ dolt_add: ["0"] }],
|
||||
fields: doltAddFields,
|
||||
},
|
||||
},
|
||||
{
|
||||
q: "select dolt_commit('-m', 'my commit')",
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 1,
|
||||
oid: null,
|
||||
rows: [{ dolt_commit: [""] }],
|
||||
fields: doltCommitFields,
|
||||
},
|
||||
},
|
||||
{
|
||||
q: "select COUNT(*) FROM dolt.log",
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 1,
|
||||
oid: null,
|
||||
rows: [{ count: "3" }],
|
||||
fields: countFields,
|
||||
},
|
||||
},
|
||||
{
|
||||
q: "select dolt_checkout('-b', 'mybranch')",
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 1,
|
||||
oid: null,
|
||||
rows: [{ dolt_checkout: ["0", "Switched to branch 'mybranch'"] }],
|
||||
fields: doltCheckoutFields,
|
||||
},
|
||||
},
|
||||
{
|
||||
q: "insert into test (pk, value) values (1,1),(2,3)",
|
||||
res: {
|
||||
command: "INSERT",
|
||||
rowCount: 2,
|
||||
oid: 0,
|
||||
rows: [],
|
||||
fields: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
q: "select dolt_commit('-a', '-m', 'my commit2')",
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 1,
|
||||
oid: null,
|
||||
rows: [{ dolt_commit: [""] }],
|
||||
fields: doltCommitFields,
|
||||
},
|
||||
},
|
||||
{
|
||||
q: "select dolt_checkout('main')",
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 1,
|
||||
oid: null,
|
||||
rows: [{ dolt_checkout: ["0", "Switched to branch 'main'"] }],
|
||||
fields: doltCheckoutFields,
|
||||
},
|
||||
},
|
||||
{
|
||||
q: "select dolt_merge('mybranch')",
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 1,
|
||||
oid: null,
|
||||
rows: [
|
||||
{
|
||||
dolt_merge: ["", "1", "0", "merge successful"],
|
||||
},
|
||||
],
|
||||
fields: [],
|
||||
},
|
||||
matcher: mergeMatcher,
|
||||
},
|
||||
{
|
||||
q: "select COUNT(*) FROM dolt.log",
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 1,
|
||||
oid: null,
|
||||
rows: [{ count: "4" }],
|
||||
fields: countFields,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
async function main() {
|
||||
const database = new Database(getConfig());
|
||||
|
||||
await Promise.all(
|
||||
tests.map((test) => {
|
||||
return database
|
||||
.query(test.q)
|
||||
.then((data) => {
|
||||
assertEqualRows(test, data);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
});
|
||||
})
|
||||
);
|
||||
|
||||
database.close();
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
main();
|
||||
@@ -0,0 +1,86 @@
|
||||
import knex from "knex";
|
||||
import wtfnode from "wtfnode";
|
||||
import { Socket } from "net";
|
||||
import { getConfig, getDoltgresVersion } from "./helpers.js";
|
||||
|
||||
const db = knex({
|
||||
client: "pg",
|
||||
version: getDoltgresVersion(),
|
||||
connection: getConfig(),
|
||||
});
|
||||
|
||||
async function createTable() {
|
||||
const val = await db.schema.createTable("test2", (table) => {
|
||||
table.integer("id").primary();
|
||||
table.integer("foo");
|
||||
});
|
||||
return val;
|
||||
}
|
||||
|
||||
// upsert runs INSERT ... ON CONFLICT UPDATE statement, which is not yet supported in Doltgres. (TODO)
|
||||
async function upsert(table, data) {
|
||||
const val = await db(table).insert(data).onConflict().merge();
|
||||
return val;
|
||||
}
|
||||
|
||||
async function insert(table, data) {
|
||||
const val = await db(table).insert(data);
|
||||
return val;
|
||||
}
|
||||
|
||||
async function select() {
|
||||
const val = await db.select("id", "foo").from("test2");
|
||||
return val;
|
||||
}
|
||||
|
||||
async function main() {
|
||||
await createTable();
|
||||
await Promise.all([
|
||||
insert("test2", { id: 1, foo: 1 }),
|
||||
insert("test2", { id: 2, foo: 2 }),
|
||||
]);
|
||||
|
||||
const expectedResult = JSON.stringify([
|
||||
{ id: 1, foo: 1 },
|
||||
{ id: 2, foo: 2 },
|
||||
]);
|
||||
const result = await select();
|
||||
if (JSON.stringify(result) !== expectedResult) {
|
||||
console.log("Results:", result);
|
||||
console.log("Expected:", expectedResult);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
await db.destroy();
|
||||
|
||||
// cc: https://github.com/dolthub/dolt/issues/3752
|
||||
setTimeout(async () => {
|
||||
const sockets = await getOpenSockets();
|
||||
|
||||
if (sockets.length > 0) {
|
||||
wtfnode.dump();
|
||||
process.exit(1);
|
||||
}
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
// cc: https://github.com/myndzi/wtfnode/blob/master/index.js#L457
|
||||
async function getOpenSockets() {
|
||||
const sockets = [];
|
||||
process._getActiveHandles().forEach(function (h) {
|
||||
// handles can be null now? early exit to guard against this
|
||||
if (!h) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (h instanceof Socket) {
|
||||
if (h.fd == null && h.localAddress && !h.destroyed) {
|
||||
sockets.push(h);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return sockets;
|
||||
}
|
||||
|
||||
main();
|
||||
+771
@@ -0,0 +1,771 @@
|
||||
{
|
||||
"name": "node",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "node",
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"knex": "^2.5.1",
|
||||
"pg": "^8.12.0",
|
||||
"pg-copy-streams": "^6.0.6",
|
||||
"pg-promise": "^11.6.0",
|
||||
"wtfnode": "^0.9.2"
|
||||
}
|
||||
},
|
||||
"node_modules/assert-options": {
|
||||
"version": "0.8.1",
|
||||
"resolved": "https://registry.npmjs.org/assert-options/-/assert-options-0.8.1.tgz",
|
||||
"integrity": "sha512-5lNGRB5g5i2bGIzb+J1QQE1iKU/WEMVBReFIc5pPDWjcPj23otPL0eI6PB2v7QPi0qU6Mhym5D3y0ZiSIOf3GA==",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/colorette": {
|
||||
"version": "2.0.19",
|
||||
"resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz",
|
||||
"integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ=="
|
||||
},
|
||||
"node_modules/commander": {
|
||||
"version": "10.0.1",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz",
|
||||
"integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==",
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.3.4",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
||||
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
|
||||
"dependencies": {
|
||||
"ms": "2.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"supports-color": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/escalade": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
|
||||
"integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/esm": {
|
||||
"version": "3.2.25",
|
||||
"resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz",
|
||||
"integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/function-bind": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
|
||||
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
|
||||
},
|
||||
"node_modules/get-package-type": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz",
|
||||
"integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==",
|
||||
"engines": {
|
||||
"node": ">=8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/getopts": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/getopts/-/getopts-2.3.0.tgz",
|
||||
"integrity": "sha512-5eDf9fuSXwxBL6q5HX+dhDj+dslFGWzU5thZ9kNKUkcPtaPdatmUFKwHFrLb/uf/WpA4BHET+AX3Scl56cAjpA=="
|
||||
},
|
||||
"node_modules/has": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
|
||||
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
|
||||
"dependencies": {
|
||||
"function-bind": "^1.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/interpret": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz",
|
||||
"integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==",
|
||||
"engines": {
|
||||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/is-core-module": {
|
||||
"version": "2.12.1",
|
||||
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz",
|
||||
"integrity": "sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==",
|
||||
"dependencies": {
|
||||
"has": "^1.0.3"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/knex": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmjs.org/knex/-/knex-2.5.1.tgz",
|
||||
"integrity": "sha512-z78DgGKUr4SE/6cm7ku+jHvFT0X97aERh/f0MUKAKgFnwCYBEW4TFBqtHWFYiJFid7fMrtpZ/gxJthvz5mEByA==",
|
||||
"dependencies": {
|
||||
"colorette": "2.0.19",
|
||||
"commander": "^10.0.0",
|
||||
"debug": "4.3.4",
|
||||
"escalade": "^3.1.1",
|
||||
"esm": "^3.2.25",
|
||||
"get-package-type": "^0.1.0",
|
||||
"getopts": "2.3.0",
|
||||
"interpret": "^2.2.0",
|
||||
"lodash": "^4.17.21",
|
||||
"pg-connection-string": "2.6.1",
|
||||
"rechoir": "^0.8.0",
|
||||
"resolve-from": "^5.0.0",
|
||||
"tarn": "^3.0.2",
|
||||
"tildify": "2.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"knex": "bin/cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"better-sqlite3": {
|
||||
"optional": true
|
||||
},
|
||||
"mysql": {
|
||||
"optional": true
|
||||
},
|
||||
"mysql2": {
|
||||
"optional": true
|
||||
},
|
||||
"pg": {
|
||||
"optional": true
|
||||
},
|
||||
"pg-native": {
|
||||
"optional": true
|
||||
},
|
||||
"sqlite3": {
|
||||
"optional": true
|
||||
},
|
||||
"tedious": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/lodash": {
|
||||
"version": "4.18.1",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz",
|
||||
"integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q=="
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||
},
|
||||
"node_modules/obuf": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz",
|
||||
"integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg=="
|
||||
},
|
||||
"node_modules/path-parse": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
|
||||
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
|
||||
},
|
||||
"node_modules/pg": {
|
||||
"version": "8.12.0",
|
||||
"resolved": "https://registry.npmjs.org/pg/-/pg-8.12.0.tgz",
|
||||
"integrity": "sha512-A+LHUSnwnxrnL/tZ+OLfqR1SxLN3c/pgDztZ47Rpbsd4jUytsTtwQo/TLPRzPJMp/1pbhYVhH9cuSZLAajNfjQ==",
|
||||
"dependencies": {
|
||||
"pg-connection-string": "^2.6.4",
|
||||
"pg-pool": "^3.6.2",
|
||||
"pg-protocol": "^1.6.1",
|
||||
"pg-types": "^2.1.0",
|
||||
"pgpass": "1.x"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"pg-cloudflare": "^1.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"pg-native": ">=3.0.1"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"pg-native": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/pg-cloudflare": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz",
|
||||
"integrity": "sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/pg-connection-string": {
|
||||
"version": "2.6.1",
|
||||
"resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.1.tgz",
|
||||
"integrity": "sha512-w6ZzNu6oMmIzEAYVw+RLK0+nqHPt8K3ZnknKi+g48Ak2pr3dtljJW3o+D/n2zzCG07Zoe9VOX3aiKpj+BN0pjg=="
|
||||
},
|
||||
"node_modules/pg-copy-streams": {
|
||||
"version": "6.0.6",
|
||||
"resolved": "https://registry.npmjs.org/pg-copy-streams/-/pg-copy-streams-6.0.6.tgz",
|
||||
"integrity": "sha512-Z+Dd2C2NIDTsjyFKmc6a9QLlpM8tjpERx+43RSx0WmL7j3uNChERi3xSvZUL0hWJ1oRUn4S3fhyt3apdSrTyKQ==",
|
||||
"dependencies": {
|
||||
"obuf": "^1.1.2"
|
||||
}
|
||||
},
|
||||
"node_modules/pg-int8": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz",
|
||||
"integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==",
|
||||
"engines": {
|
||||
"node": ">=4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/pg-minify": {
|
||||
"version": "1.6.3",
|
||||
"resolved": "https://registry.npmjs.org/pg-minify/-/pg-minify-1.6.3.tgz",
|
||||
"integrity": "sha512-NoSsPqXxbkD8RIe+peQCqiea4QzXgosdTKY8p7PsbbGsh2F8TifDj/vJxfuR8qJwNYrijdSs7uf0tAe6WOyCsQ==",
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/pg-pool": {
|
||||
"version": "3.6.2",
|
||||
"resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.6.2.tgz",
|
||||
"integrity": "sha512-Htjbg8BlwXqSBQ9V8Vjtc+vzf/6fVUuak/3/XXKA9oxZprwW3IMDQTGHP+KDmVL7rtd+R1QjbnCFPuTHm3G4hg==",
|
||||
"peerDependencies": {
|
||||
"pg": ">=8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/pg-promise": {
|
||||
"version": "11.6.0",
|
||||
"resolved": "https://registry.npmjs.org/pg-promise/-/pg-promise-11.6.0.tgz",
|
||||
"integrity": "sha512-NDRPMfkv3ia89suWlJ4iGvP6X5YFrLJ2+9AIVISeBFFZ29Eb4FNXX9JaVb1p1OrpQkE2yT7igmXPL7UYQhk+6A==",
|
||||
"dependencies": {
|
||||
"assert-options": "0.8.1",
|
||||
"pg": "8.11.5",
|
||||
"pg-minify": "1.6.3",
|
||||
"spex": "3.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0"
|
||||
}
|
||||
},
|
||||
"node_modules/pg-promise/node_modules/pg": {
|
||||
"version": "8.11.5",
|
||||
"resolved": "https://registry.npmjs.org/pg/-/pg-8.11.5.tgz",
|
||||
"integrity": "sha512-jqgNHSKL5cbDjFlHyYsCXmQDrfIX/3RsNwYqpd4N0Kt8niLuNoRNH+aazv6cOd43gPh9Y4DjQCtb+X0MH0Hvnw==",
|
||||
"dependencies": {
|
||||
"pg-connection-string": "^2.6.4",
|
||||
"pg-pool": "^3.6.2",
|
||||
"pg-protocol": "^1.6.1",
|
||||
"pg-types": "^2.1.0",
|
||||
"pgpass": "1.x"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"pg-cloudflare": "^1.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"pg-native": ">=3.0.1"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"pg-native": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/pg-promise/node_modules/pg-connection-string": {
|
||||
"version": "2.6.4",
|
||||
"resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.4.tgz",
|
||||
"integrity": "sha512-v+Z7W/0EO707aNMaAEfiGnGL9sxxumwLl2fJvCQtMn9Fxsg+lPpPkdcyBSv/KFgpGdYkMfn+EI1Or2EHjpgLCA=="
|
||||
},
|
||||
"node_modules/pg-protocol": {
|
||||
"version": "1.6.1",
|
||||
"resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.1.tgz",
|
||||
"integrity": "sha512-jPIlvgoD63hrEuihvIg+tJhoGjUsLPn6poJY9N5CnlPd91c2T18T/9zBtLxZSb1EhYxBRoZJtzScCaWlYLtktg=="
|
||||
},
|
||||
"node_modules/pg-types": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz",
|
||||
"integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==",
|
||||
"dependencies": {
|
||||
"pg-int8": "1.0.1",
|
||||
"postgres-array": "~2.0.0",
|
||||
"postgres-bytea": "~1.0.0",
|
||||
"postgres-date": "~1.0.4",
|
||||
"postgres-interval": "^1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/pg/node_modules/pg-connection-string": {
|
||||
"version": "2.6.4",
|
||||
"resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.4.tgz",
|
||||
"integrity": "sha512-v+Z7W/0EO707aNMaAEfiGnGL9sxxumwLl2fJvCQtMn9Fxsg+lPpPkdcyBSv/KFgpGdYkMfn+EI1Or2EHjpgLCA=="
|
||||
},
|
||||
"node_modules/pgpass": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz",
|
||||
"integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==",
|
||||
"dependencies": {
|
||||
"split2": "^4.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/postgres-array": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz",
|
||||
"integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/postgres-bytea": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz",
|
||||
"integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/postgres-date": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz",
|
||||
"integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/postgres-interval": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz",
|
||||
"integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==",
|
||||
"dependencies": {
|
||||
"xtend": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/rechoir": {
|
||||
"version": "0.8.0",
|
||||
"resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz",
|
||||
"integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==",
|
||||
"dependencies": {
|
||||
"resolve": "^1.20.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 10.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/resolve": {
|
||||
"version": "1.22.2",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz",
|
||||
"integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==",
|
||||
"dependencies": {
|
||||
"is-core-module": "^2.11.0",
|
||||
"path-parse": "^1.0.7",
|
||||
"supports-preserve-symlinks-flag": "^1.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"resolve": "bin/resolve"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/resolve-from": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
|
||||
"integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/spex": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/spex/-/spex-3.3.0.tgz",
|
||||
"integrity": "sha512-VNiXjFp6R4ldPbVRYbpxlD35yRHceecVXlct1J4/X80KuuPnW2AXMq3sGwhnJOhKkUsOxAT6nRGfGE5pocVw5w==",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/split2": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
|
||||
"integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==",
|
||||
"engines": {
|
||||
"node": ">= 10.x"
|
||||
}
|
||||
},
|
||||
"node_modules/supports-preserve-symlinks-flag": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
|
||||
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/tarn": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/tarn/-/tarn-3.0.2.tgz",
|
||||
"integrity": "sha512-51LAVKUSZSVfI05vjPESNc5vwqqZpbXCsU+/+wxlOrUjk2SnFTt97v9ZgQrD4YmxYW1Px6w2KjaDitCfkvgxMQ==",
|
||||
"engines": {
|
||||
"node": ">=8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/tildify": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/tildify/-/tildify-2.0.0.tgz",
|
||||
"integrity": "sha512-Cc+OraorugtXNfs50hU9KS369rFXCfgGLpfCfvlc+Ud5u6VWmUQsOAa9HbTvheQdYnrdJqqv1e5oIqXppMYnSw==",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/wtfnode": {
|
||||
"version": "0.9.2",
|
||||
"resolved": "https://registry.npmjs.org/wtfnode/-/wtfnode-0.9.2.tgz",
|
||||
"integrity": "sha512-AzGy/MOGmzufyHDKHwuZeuqaqslGIPuwxDAhdmUUJ91Ak22Ynxry8quXNR/cZZIaSs5T+C+LNS3W9AcMzJXhMw==",
|
||||
"bin": {
|
||||
"wtfnode": "proxy.js"
|
||||
}
|
||||
},
|
||||
"node_modules/xtend": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
||||
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
|
||||
"engines": {
|
||||
"node": ">=0.4"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"assert-options": {
|
||||
"version": "0.8.1",
|
||||
"resolved": "https://registry.npmjs.org/assert-options/-/assert-options-0.8.1.tgz",
|
||||
"integrity": "sha512-5lNGRB5g5i2bGIzb+J1QQE1iKU/WEMVBReFIc5pPDWjcPj23otPL0eI6PB2v7QPi0qU6Mhym5D3y0ZiSIOf3GA=="
|
||||
},
|
||||
"colorette": {
|
||||
"version": "2.0.19",
|
||||
"resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz",
|
||||
"integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ=="
|
||||
},
|
||||
"commander": {
|
||||
"version": "10.0.1",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz",
|
||||
"integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug=="
|
||||
},
|
||||
"debug": {
|
||||
"version": "4.3.4",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
||||
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
|
||||
"requires": {
|
||||
"ms": "2.1.2"
|
||||
}
|
||||
},
|
||||
"escalade": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
|
||||
"integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw=="
|
||||
},
|
||||
"esm": {
|
||||
"version": "3.2.25",
|
||||
"resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz",
|
||||
"integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA=="
|
||||
},
|
||||
"function-bind": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
|
||||
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
|
||||
},
|
||||
"get-package-type": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz",
|
||||
"integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q=="
|
||||
},
|
||||
"getopts": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/getopts/-/getopts-2.3.0.tgz",
|
||||
"integrity": "sha512-5eDf9fuSXwxBL6q5HX+dhDj+dslFGWzU5thZ9kNKUkcPtaPdatmUFKwHFrLb/uf/WpA4BHET+AX3Scl56cAjpA=="
|
||||
},
|
||||
"has": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
|
||||
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
|
||||
"requires": {
|
||||
"function-bind": "^1.1.1"
|
||||
}
|
||||
},
|
||||
"interpret": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz",
|
||||
"integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw=="
|
||||
},
|
||||
"is-core-module": {
|
||||
"version": "2.12.1",
|
||||
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz",
|
||||
"integrity": "sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==",
|
||||
"requires": {
|
||||
"has": "^1.0.3"
|
||||
}
|
||||
},
|
||||
"knex": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmjs.org/knex/-/knex-2.5.1.tgz",
|
||||
"integrity": "sha512-z78DgGKUr4SE/6cm7ku+jHvFT0X97aERh/f0MUKAKgFnwCYBEW4TFBqtHWFYiJFid7fMrtpZ/gxJthvz5mEByA==",
|
||||
"requires": {
|
||||
"colorette": "2.0.19",
|
||||
"commander": "^10.0.0",
|
||||
"debug": "4.3.4",
|
||||
"escalade": "^3.1.1",
|
||||
"esm": "^3.2.25",
|
||||
"get-package-type": "^0.1.0",
|
||||
"getopts": "2.3.0",
|
||||
"interpret": "^2.2.0",
|
||||
"lodash": "^4.17.21",
|
||||
"pg-connection-string": "2.6.1",
|
||||
"rechoir": "^0.8.0",
|
||||
"resolve-from": "^5.0.0",
|
||||
"tarn": "^3.0.2",
|
||||
"tildify": "2.0.0"
|
||||
}
|
||||
},
|
||||
"lodash": {
|
||||
"version": "4.18.1",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz",
|
||||
"integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q=="
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||
},
|
||||
"obuf": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz",
|
||||
"integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg=="
|
||||
},
|
||||
"path-parse": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
|
||||
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
|
||||
},
|
||||
"pg": {
|
||||
"version": "8.12.0",
|
||||
"resolved": "https://registry.npmjs.org/pg/-/pg-8.12.0.tgz",
|
||||
"integrity": "sha512-A+LHUSnwnxrnL/tZ+OLfqR1SxLN3c/pgDztZ47Rpbsd4jUytsTtwQo/TLPRzPJMp/1pbhYVhH9cuSZLAajNfjQ==",
|
||||
"requires": {
|
||||
"pg-cloudflare": "^1.1.1",
|
||||
"pg-connection-string": "^2.6.4",
|
||||
"pg-pool": "^3.6.2",
|
||||
"pg-protocol": "^1.6.1",
|
||||
"pg-types": "^2.1.0",
|
||||
"pgpass": "1.x"
|
||||
},
|
||||
"dependencies": {
|
||||
"pg-connection-string": {
|
||||
"version": "2.6.4",
|
||||
"resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.4.tgz",
|
||||
"integrity": "sha512-v+Z7W/0EO707aNMaAEfiGnGL9sxxumwLl2fJvCQtMn9Fxsg+lPpPkdcyBSv/KFgpGdYkMfn+EI1Or2EHjpgLCA=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"pg-cloudflare": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz",
|
||||
"integrity": "sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==",
|
||||
"optional": true
|
||||
},
|
||||
"pg-connection-string": {
|
||||
"version": "2.6.1",
|
||||
"resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.1.tgz",
|
||||
"integrity": "sha512-w6ZzNu6oMmIzEAYVw+RLK0+nqHPt8K3ZnknKi+g48Ak2pr3dtljJW3o+D/n2zzCG07Zoe9VOX3aiKpj+BN0pjg=="
|
||||
},
|
||||
"pg-copy-streams": {
|
||||
"version": "6.0.6",
|
||||
"resolved": "https://registry.npmjs.org/pg-copy-streams/-/pg-copy-streams-6.0.6.tgz",
|
||||
"integrity": "sha512-Z+Dd2C2NIDTsjyFKmc6a9QLlpM8tjpERx+43RSx0WmL7j3uNChERi3xSvZUL0hWJ1oRUn4S3fhyt3apdSrTyKQ==",
|
||||
"requires": {
|
||||
"obuf": "^1.1.2"
|
||||
}
|
||||
},
|
||||
"pg-int8": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz",
|
||||
"integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw=="
|
||||
},
|
||||
"pg-minify": {
|
||||
"version": "1.6.3",
|
||||
"resolved": "https://registry.npmjs.org/pg-minify/-/pg-minify-1.6.3.tgz",
|
||||
"integrity": "sha512-NoSsPqXxbkD8RIe+peQCqiea4QzXgosdTKY8p7PsbbGsh2F8TifDj/vJxfuR8qJwNYrijdSs7uf0tAe6WOyCsQ=="
|
||||
},
|
||||
"pg-pool": {
|
||||
"version": "3.6.2",
|
||||
"resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.6.2.tgz",
|
||||
"integrity": "sha512-Htjbg8BlwXqSBQ9V8Vjtc+vzf/6fVUuak/3/XXKA9oxZprwW3IMDQTGHP+KDmVL7rtd+R1QjbnCFPuTHm3G4hg==",
|
||||
"requires": {}
|
||||
},
|
||||
"pg-promise": {
|
||||
"version": "11.6.0",
|
||||
"resolved": "https://registry.npmjs.org/pg-promise/-/pg-promise-11.6.0.tgz",
|
||||
"integrity": "sha512-NDRPMfkv3ia89suWlJ4iGvP6X5YFrLJ2+9AIVISeBFFZ29Eb4FNXX9JaVb1p1OrpQkE2yT7igmXPL7UYQhk+6A==",
|
||||
"requires": {
|
||||
"assert-options": "0.8.1",
|
||||
"pg": "8.11.5",
|
||||
"pg-minify": "1.6.3",
|
||||
"spex": "3.3.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"pg": {
|
||||
"version": "8.11.5",
|
||||
"resolved": "https://registry.npmjs.org/pg/-/pg-8.11.5.tgz",
|
||||
"integrity": "sha512-jqgNHSKL5cbDjFlHyYsCXmQDrfIX/3RsNwYqpd4N0Kt8niLuNoRNH+aazv6cOd43gPh9Y4DjQCtb+X0MH0Hvnw==",
|
||||
"requires": {
|
||||
"pg-cloudflare": "^1.1.1",
|
||||
"pg-connection-string": "^2.6.4",
|
||||
"pg-pool": "^3.6.2",
|
||||
"pg-protocol": "^1.6.1",
|
||||
"pg-types": "^2.1.0",
|
||||
"pgpass": "1.x"
|
||||
}
|
||||
},
|
||||
"pg-connection-string": {
|
||||
"version": "2.6.4",
|
||||
"resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.4.tgz",
|
||||
"integrity": "sha512-v+Z7W/0EO707aNMaAEfiGnGL9sxxumwLl2fJvCQtMn9Fxsg+lPpPkdcyBSv/KFgpGdYkMfn+EI1Or2EHjpgLCA=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"pg-protocol": {
|
||||
"version": "1.6.1",
|
||||
"resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.1.tgz",
|
||||
"integrity": "sha512-jPIlvgoD63hrEuihvIg+tJhoGjUsLPn6poJY9N5CnlPd91c2T18T/9zBtLxZSb1EhYxBRoZJtzScCaWlYLtktg=="
|
||||
},
|
||||
"pg-types": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz",
|
||||
"integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==",
|
||||
"requires": {
|
||||
"pg-int8": "1.0.1",
|
||||
"postgres-array": "~2.0.0",
|
||||
"postgres-bytea": "~1.0.0",
|
||||
"postgres-date": "~1.0.4",
|
||||
"postgres-interval": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"pgpass": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz",
|
||||
"integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==",
|
||||
"requires": {
|
||||
"split2": "^4.1.0"
|
||||
}
|
||||
},
|
||||
"postgres-array": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz",
|
||||
"integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA=="
|
||||
},
|
||||
"postgres-bytea": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz",
|
||||
"integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w=="
|
||||
},
|
||||
"postgres-date": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz",
|
||||
"integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q=="
|
||||
},
|
||||
"postgres-interval": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz",
|
||||
"integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==",
|
||||
"requires": {
|
||||
"xtend": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"rechoir": {
|
||||
"version": "0.8.0",
|
||||
"resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz",
|
||||
"integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==",
|
||||
"requires": {
|
||||
"resolve": "^1.20.0"
|
||||
}
|
||||
},
|
||||
"resolve": {
|
||||
"version": "1.22.2",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz",
|
||||
"integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==",
|
||||
"requires": {
|
||||
"is-core-module": "^2.11.0",
|
||||
"path-parse": "^1.0.7",
|
||||
"supports-preserve-symlinks-flag": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"resolve-from": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
|
||||
"integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw=="
|
||||
},
|
||||
"spex": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/spex/-/spex-3.3.0.tgz",
|
||||
"integrity": "sha512-VNiXjFp6R4ldPbVRYbpxlD35yRHceecVXlct1J4/X80KuuPnW2AXMq3sGwhnJOhKkUsOxAT6nRGfGE5pocVw5w=="
|
||||
},
|
||||
"split2": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
|
||||
"integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg=="
|
||||
},
|
||||
"supports-preserve-symlinks-flag": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
|
||||
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="
|
||||
},
|
||||
"tarn": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/tarn/-/tarn-3.0.2.tgz",
|
||||
"integrity": "sha512-51LAVKUSZSVfI05vjPESNc5vwqqZpbXCsU+/+wxlOrUjk2SnFTt97v9ZgQrD4YmxYW1Px6w2KjaDitCfkvgxMQ=="
|
||||
},
|
||||
"tildify": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/tildify/-/tildify-2.0.0.tgz",
|
||||
"integrity": "sha512-Cc+OraorugtXNfs50hU9KS369rFXCfgGLpfCfvlc+Ud5u6VWmUQsOAa9HbTvheQdYnrdJqqv1e5oIqXppMYnSw=="
|
||||
},
|
||||
"wtfnode": {
|
||||
"version": "0.9.2",
|
||||
"resolved": "https://registry.npmjs.org/wtfnode/-/wtfnode-0.9.2.tgz",
|
||||
"integrity": "sha512-AzGy/MOGmzufyHDKHwuZeuqaqslGIPuwxDAhdmUUJ91Ak22Ynxry8quXNR/cZZIaSs5T+C+LNS3W9AcMzJXhMw=="
|
||||
},
|
||||
"xtend": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
||||
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"name": "node",
|
||||
"version": "1.0.0",
|
||||
"node": ">=16.9",
|
||||
"description": "A simple node command line utility to show how to connect a node application to a Doltgres database using the PostgreSQL connector.",
|
||||
"main": "index.js",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"knex": "^2.5.1",
|
||||
"pg": "^8.12.0",
|
||||
"pg-copy-streams": "^6.0.6",
|
||||
"pg-promise": "^11.6.0",
|
||||
"wtfnode": "^0.9.2"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#!/bin/sh
|
||||
source ../helpers.bash
|
||||
|
||||
echo "Running $1 tests"
|
||||
start_doltgres_server
|
||||
query_server -c "CREATE TABLE IF NOT EXISTS test_table(pk int)" -t
|
||||
query_server -c "DELETE FROM test_table" -t
|
||||
query_server -c "INSERT INTO test_table VALUES (1)" -t
|
||||
|
||||
cd ..
|
||||
DOLTGRES_VERSION=$( doltgres --version | sed -nre 's/^[^0-9]*(([0-9]+\.)*[0-9]+).*/\1/p' )
|
||||
node $1 $USER $PORT $DOLTGRES_VERSION $PWD/testdata
|
||||
teardown_doltgres_repo
|
||||
@@ -0,0 +1,4 @@
|
||||
id,info,test_pk
|
||||
4,string for 4,1
|
||||
5,string for 5,0
|
||||
6,string for 6,0
|
||||
|
@@ -0,0 +1,4 @@
|
||||
id|info|test_pk
|
||||
10|string for 10|0
|
||||
11|string for 11|0
|
||||
12|string for 12|0
|
||||
@@ -0,0 +1,14 @@
|
||||
import { Database } from "./database.js";
|
||||
import { getConfig } from "./helpers.js";
|
||||
import runWorkbenchTests from "./workbenchTests/index.js";
|
||||
|
||||
async function workbench() {
|
||||
const database = new Database(getConfig());
|
||||
|
||||
await runWorkbenchTests(database);
|
||||
|
||||
database.close();
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
workbench();
|
||||
@@ -0,0 +1,152 @@
|
||||
import {
|
||||
countFields,
|
||||
doltBranchFields,
|
||||
doltCheckoutFields,
|
||||
doltStatusFields,
|
||||
doltCommitFields,
|
||||
} from "../fields.js";
|
||||
import { branchesMatcher } from "./matchers.js";
|
||||
import { dbName } from "../helpers.js";
|
||||
|
||||
export const branchTests = [
|
||||
{
|
||||
q: `SELECT DOLT_BRANCH($1::text, $2::text)`, // TODO: Should work without casts
|
||||
p: ["mybranch", "main"],
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 1,
|
||||
oid: null,
|
||||
rows: [{ dolt_branch: ["0"] }],
|
||||
fields: doltBranchFields,
|
||||
},
|
||||
},
|
||||
{
|
||||
q: `USE '${dbName}/mybranch';`,
|
||||
res: {
|
||||
command: "SET",
|
||||
rowCount: null,
|
||||
oid: null,
|
||||
rows: [],
|
||||
fields: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
q: `create table test (pk int, "value" int, primary key(pk));`,
|
||||
res: {
|
||||
command: "CREATE",
|
||||
rowCount: null,
|
||||
oid: null,
|
||||
rows: [],
|
||||
fields: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
q: `SELECT * FROM dolt.status;`,
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 1,
|
||||
oid: null,
|
||||
rows: [
|
||||
{
|
||||
table_name: "public.test",
|
||||
staged: false,
|
||||
status: "new table",
|
||||
},
|
||||
],
|
||||
fields: doltStatusFields,
|
||||
},
|
||||
},
|
||||
{
|
||||
q: `SELECT DOLT_COMMIT('-Am', $1::text, '--author', $2::text);`,
|
||||
p: ["Create table test", "Dolt <dolt@dolthub.com>"],
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 1,
|
||||
oid: null,
|
||||
rows: [{ dolt_commit: "" }],
|
||||
fields: doltCommitFields,
|
||||
},
|
||||
},
|
||||
{
|
||||
q: `SELECT * FROM dolt.branches LIMIT 200`,
|
||||
res: {
|
||||
rows: [
|
||||
{
|
||||
name: "main",
|
||||
hash: "",
|
||||
latest_committer: "postgres",
|
||||
latest_committer_email: "postgres@127.0.0.1",
|
||||
latest_commit_date: "",
|
||||
latest_commit_message: "CREATE DATABASE",
|
||||
remote: "",
|
||||
branch: "",
|
||||
dirty: 1,
|
||||
},
|
||||
{
|
||||
name: "mybranch",
|
||||
hash: "",
|
||||
latest_committer: "Dolt",
|
||||
latest_committer_email: "dolt@dolthub.com",
|
||||
latest_commit_date: "",
|
||||
latest_commit_message: "Create table test",
|
||||
remote: "",
|
||||
branch: "",
|
||||
dirty: 0,
|
||||
},
|
||||
],
|
||||
},
|
||||
matcher: branchesMatcher,
|
||||
},
|
||||
{
|
||||
q: `SELECT DOLT_CHECKOUT('-b', $1::text)`,
|
||||
p: ["branch-to-delete"],
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 1,
|
||||
oid: null,
|
||||
rows: [{ dolt_checkout: ["0", "Switched to branch 'branch-to-delete'"] }],
|
||||
fields: doltCheckoutFields,
|
||||
},
|
||||
},
|
||||
{
|
||||
q: `SELECT COUNT(*) FROM dolt.branches LIMIT 200`,
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 1,
|
||||
oid: null,
|
||||
rows: [{ count: "3" }],
|
||||
fields: countFields,
|
||||
},
|
||||
},
|
||||
{
|
||||
q: `USE '${dbName}/main';`,
|
||||
res: {
|
||||
command: "SET",
|
||||
rowCount: null,
|
||||
oid: null,
|
||||
rows: [],
|
||||
fields: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
q: `SELECT DOLT_BRANCH('-D', $1::text)`,
|
||||
p: ["branch-to-delete"],
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 1,
|
||||
oid: null,
|
||||
rows: [{ dolt_branch: ["0"] }],
|
||||
fields: doltBranchFields,
|
||||
},
|
||||
},
|
||||
{
|
||||
q: `SELECT COUNT(*) FROM dolt.branches LIMIT 200`,
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 1,
|
||||
oid: null,
|
||||
rows: [{ count: "2" }],
|
||||
fields: countFields,
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,86 @@
|
||||
import { doltCleanFields } from "../fields.js";
|
||||
import { dbName } from "../helpers.js";
|
||||
|
||||
export const databaseTests = [
|
||||
{
|
||||
q: `USE '${dbName}/main';`,
|
||||
res: {
|
||||
command: "SET",
|
||||
rowCount: null,
|
||||
oid: null,
|
||||
rows: [],
|
||||
fields: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
q: `SELECT DOLT_CLEAN('test_table')`,
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 1,
|
||||
oid: null,
|
||||
rows: [{ dolt_clean: ["0"] }],
|
||||
fields: doltCleanFields,
|
||||
},
|
||||
},
|
||||
{
|
||||
q: `SELECT datname FROM pg_database;`,
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 2,
|
||||
oid: null,
|
||||
rows: [{ datname: dbName }, { datname: `${dbName}/main` }],
|
||||
fields: [
|
||||
{
|
||||
name: "datname",
|
||||
tableID: 0,
|
||||
columnID: 0,
|
||||
dataTypeID: 19,
|
||||
dataTypeSize: 64,
|
||||
dataTypeModifier: -1,
|
||||
format: "text",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
q: `CREATE DATABASE "new_db";`,
|
||||
res: {
|
||||
command: "CREATE",
|
||||
rowCount: null,
|
||||
oid: null,
|
||||
rows: [],
|
||||
fields: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
q: `SELECT datname FROM pg_database;`,
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 3,
|
||||
oid: null,
|
||||
rows: [
|
||||
{ datname: "new_db" },
|
||||
{ datname: dbName },
|
||||
{ datname: `${dbName}/main` },
|
||||
],
|
||||
fields: [
|
||||
{
|
||||
name: "datname",
|
||||
tableID: 0,
|
||||
columnID: 0,
|
||||
dataTypeID: 19,
|
||||
dataTypeSize: 64,
|
||||
dataTypeModifier: -1,
|
||||
format: "text",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
q: `SELECT dolt_version();`,
|
||||
res: [{ "dolt_version()": "0.0.0" }],
|
||||
matcher: (data) => {
|
||||
return data.rows[0].dolt_version.length > 0;
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,780 @@
|
||||
import {
|
||||
doltCommitFields,
|
||||
doltDiffStatFields,
|
||||
doltDiffSummaryFields,
|
||||
doltSchemaDiffFields,
|
||||
doltStatusFields,
|
||||
pgTablesFields,
|
||||
} from "../fields.js";
|
||||
import { diffRowsMatcher, patchRowsMatcher } from "./matchers.js";
|
||||
|
||||
export const diffTests = [
|
||||
{
|
||||
q: "UPDATE test SET value=1 WHERE pk=0",
|
||||
res: {
|
||||
command: "UPDATE",
|
||||
rowCount: 1,
|
||||
oid: null,
|
||||
rows: [],
|
||||
fields: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
q: "DROP TABLE test_info",
|
||||
res: {
|
||||
command: "DROP",
|
||||
rowCount: null,
|
||||
oid: null,
|
||||
rows: [],
|
||||
fields: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
q: `CREATE SCHEMA anotherschema;`,
|
||||
res: {
|
||||
command: "CREATE",
|
||||
rowCount: null,
|
||||
oid: null,
|
||||
rows: [],
|
||||
fields: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
q: `create table anotherschema.testanother (pk int, "value" int, primary key(pk));`,
|
||||
res: {
|
||||
command: "CREATE",
|
||||
rowCount: null,
|
||||
oid: null,
|
||||
rows: [],
|
||||
fields: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
q: "INSERT INTO anotherschema.testanother VALUES (1, 2)",
|
||||
res: {
|
||||
command: "INSERT",
|
||||
rowCount: 1,
|
||||
oid: 0,
|
||||
rows: [],
|
||||
fields: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
q: `SELECT * FROM dolt.status ORDER BY table_name;`,
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 5,
|
||||
oid: null,
|
||||
rows: [
|
||||
{ table_name: "anotherschema", staged: false, status: "new schema" },
|
||||
{
|
||||
table_name: "anotherschema.testanother",
|
||||
staged: false,
|
||||
status: "new table",
|
||||
},
|
||||
{
|
||||
table_name: "public.dolt_schemas",
|
||||
staged: false,
|
||||
status: "new table",
|
||||
},
|
||||
{ table_name: "public.test", staged: false, status: "modified" },
|
||||
{ table_name: "public.test_info", staged: false, status: "deleted" },
|
||||
],
|
||||
fields: doltStatusFields,
|
||||
},
|
||||
},
|
||||
{
|
||||
q: "SELECT * FROM dolt_diff_summary('HEAD', 'WORKING') ORDER BY to_table_name;", // TODO: Prepared not working
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 4,
|
||||
oid: null,
|
||||
rows: [
|
||||
{
|
||||
from_table_name: "public.test_info",
|
||||
to_table_name: "",
|
||||
diff_type: "dropped",
|
||||
data_change: 1,
|
||||
schema_change: 1,
|
||||
},
|
||||
{
|
||||
from_table_name: "",
|
||||
to_table_name: "anotherschema.testanother",
|
||||
diff_type: "added",
|
||||
data_change: 1,
|
||||
schema_change: 1,
|
||||
},
|
||||
{
|
||||
from_table_name: "",
|
||||
to_table_name: "public.dolt_schemas",
|
||||
diff_type: "added",
|
||||
data_change: 1,
|
||||
schema_change: 1,
|
||||
},
|
||||
{
|
||||
from_table_name: "public.test",
|
||||
to_table_name: "public.test",
|
||||
diff_type: "modified",
|
||||
data_change: 1,
|
||||
schema_change: 0,
|
||||
},
|
||||
],
|
||||
fields: doltDiffSummaryFields,
|
||||
},
|
||||
},
|
||||
{
|
||||
q: "SELECT * FROM dolt_diff_summary('HEAD', 'WORKING', 'test')",
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 1,
|
||||
oid: null,
|
||||
rows: [
|
||||
{
|
||||
from_table_name: "public.test",
|
||||
to_table_name: "public.test",
|
||||
diff_type: "modified",
|
||||
data_change: 1,
|
||||
schema_change: 0,
|
||||
},
|
||||
],
|
||||
fields: doltDiffSummaryFields,
|
||||
},
|
||||
},
|
||||
{
|
||||
// TODO: What if a table with same name but different schema exists in different schema?
|
||||
q: "SELECT * FROM dolt_diff_summary('HEAD', 'WORKING', 'testanother')",
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 1,
|
||||
oid: null,
|
||||
rows: [
|
||||
{
|
||||
from_table_name: "",
|
||||
to_table_name: "anotherschema.testanother",
|
||||
diff_type: "added",
|
||||
data_change: 1,
|
||||
schema_change: 1,
|
||||
},
|
||||
],
|
||||
fields: doltDiffSummaryFields,
|
||||
},
|
||||
},
|
||||
{
|
||||
q: "SELECT * FROM dolt_diff_stat('HEAD', 'WORKING') ORDER BY table_name",
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 4,
|
||||
oid: null,
|
||||
rows: [
|
||||
{
|
||||
table_name: "anotherschema.testanother",
|
||||
rows_unmodified: "0",
|
||||
rows_added: "1",
|
||||
rows_deleted: "0",
|
||||
rows_modified: "0",
|
||||
cells_added: "2",
|
||||
cells_deleted: "0",
|
||||
cells_modified: "0",
|
||||
old_row_count: "0",
|
||||
new_row_count: "1",
|
||||
old_cell_count: "0",
|
||||
new_cell_count: "2",
|
||||
},
|
||||
{
|
||||
table_name: "public.dolt_schemas",
|
||||
rows_unmodified: "0",
|
||||
rows_added: "1",
|
||||
rows_deleted: "0",
|
||||
rows_modified: "0",
|
||||
cells_added: "5",
|
||||
cells_deleted: "0",
|
||||
cells_modified: "0",
|
||||
old_row_count: "0",
|
||||
new_row_count: "1",
|
||||
old_cell_count: "0",
|
||||
new_cell_count: "5",
|
||||
},
|
||||
{
|
||||
table_name: "public.test",
|
||||
rows_unmodified: "2",
|
||||
rows_added: "0",
|
||||
rows_deleted: "0",
|
||||
rows_modified: "1",
|
||||
cells_added: "0",
|
||||
cells_deleted: "0",
|
||||
cells_modified: "1",
|
||||
old_row_count: "3",
|
||||
new_row_count: "3",
|
||||
old_cell_count: "6",
|
||||
new_cell_count: "6",
|
||||
},
|
||||
{
|
||||
table_name: "public.test_info",
|
||||
rows_unmodified: "0",
|
||||
rows_added: "0",
|
||||
rows_deleted: "1",
|
||||
rows_modified: "0",
|
||||
cells_added: "0",
|
||||
cells_deleted: "3",
|
||||
cells_modified: "0",
|
||||
old_row_count: "1",
|
||||
new_row_count: "0",
|
||||
old_cell_count: "3",
|
||||
new_cell_count: "0",
|
||||
},
|
||||
],
|
||||
fields: doltDiffStatFields,
|
||||
},
|
||||
},
|
||||
{
|
||||
q: "SELECT * FROM dolt_diff_stat('HEAD', 'WORKING', 'test_info')",
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 1,
|
||||
oid: null,
|
||||
rows: [
|
||||
{
|
||||
table_name: "public.test_info",
|
||||
rows_unmodified: "0",
|
||||
rows_added: "0",
|
||||
rows_deleted: "1",
|
||||
rows_modified: "0",
|
||||
cells_added: "0",
|
||||
cells_deleted: "3",
|
||||
cells_modified: "0",
|
||||
old_row_count: "1",
|
||||
new_row_count: "0",
|
||||
old_cell_count: "3",
|
||||
new_cell_count: "0",
|
||||
},
|
||||
],
|
||||
fields: doltDiffStatFields,
|
||||
},
|
||||
},
|
||||
{
|
||||
q: "SELECT * FROM dolt_diff_stat('HEAD', 'WORKING', 'testanother')",
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 1,
|
||||
oid: null,
|
||||
rows: [
|
||||
{
|
||||
table_name: "anotherschema.testanother",
|
||||
rows_unmodified: "0",
|
||||
rows_added: "1",
|
||||
rows_deleted: "0",
|
||||
rows_modified: "0",
|
||||
cells_added: "2",
|
||||
cells_deleted: "0",
|
||||
cells_modified: "0",
|
||||
old_row_count: "0",
|
||||
new_row_count: "1",
|
||||
old_cell_count: "0",
|
||||
new_cell_count: "2",
|
||||
},
|
||||
],
|
||||
fields: doltDiffStatFields,
|
||||
},
|
||||
},
|
||||
{
|
||||
q: "SELECT * FROM DOLT_DIFF('HEAD', 'WORKING', 'test') ORDER BY to_pk ASC, from_pk ASC LIMIT 10 OFFSET 0",
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 1,
|
||||
oid: null,
|
||||
rows: [
|
||||
{
|
||||
to_pk: 0,
|
||||
to_value: 1,
|
||||
to_commit: "WORKING",
|
||||
to_commit_date: "2023-03-09T07:44:47.670Z",
|
||||
from_pk: 0,
|
||||
from_value: 0,
|
||||
from_commit: "HEAD",
|
||||
from_commit_date: "2023-03-09T07:44:47.488Z",
|
||||
diff_type: "modified",
|
||||
},
|
||||
],
|
||||
fields: [],
|
||||
},
|
||||
matcher: diffRowsMatcher,
|
||||
},
|
||||
{
|
||||
q: "SELECT * FROM DOLT_DIFF('HEAD', 'WORKING', 'test_info') ORDER BY to_id ASC, from_id ASC LIMIT 10 OFFSET 0",
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 1,
|
||||
oid: null,
|
||||
rows: [
|
||||
{
|
||||
to_id: null,
|
||||
to_info: null,
|
||||
to_test_pk: null,
|
||||
to_commit: "WORKING",
|
||||
to_commit_date: "2023-03-09T07:53:48.614Z",
|
||||
from_id: 1,
|
||||
from_info: "info about test pk 0",
|
||||
from_test_pk: 0,
|
||||
from_commit: "HEAD",
|
||||
from_commit_date: "2023-03-09T07:53:48.284Z",
|
||||
diff_type: "removed",
|
||||
},
|
||||
],
|
||||
fields: [],
|
||||
},
|
||||
matcher: diffRowsMatcher,
|
||||
},
|
||||
{
|
||||
q: "SELECT * FROM DOLT_DIFF('HEAD', 'WORKING', 'testanother') ORDER BY to_pk ASC, from_pk ASC LIMIT 10 OFFSET 0",
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 1,
|
||||
oid: null,
|
||||
rows: [
|
||||
{
|
||||
to_pk: 1,
|
||||
to_value: 2,
|
||||
to_commit: "WORKING",
|
||||
to_commit_date: "2024-10-03T04:33:43.486Z",
|
||||
from_pk: null,
|
||||
from_value: null,
|
||||
from_commit: "HEAD",
|
||||
from_commit_date: "2024-10-03T04:33:43.430Z",
|
||||
diff_type: "added",
|
||||
},
|
||||
],
|
||||
fields: [],
|
||||
},
|
||||
matcher: diffRowsMatcher,
|
||||
},
|
||||
{
|
||||
q: "SELECT * FROM DOLT_DIFF('HEAD', 'WORKING', 'dolt_schemas') ORDER BY to_name ASC, from_name ASC LIMIT 10 OFFSET 0",
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 1,
|
||||
oid: null,
|
||||
rows: [
|
||||
{
|
||||
to_type: "view",
|
||||
to_name: "myview",
|
||||
to_fragment: "CREATE VIEW myview AS SELECT * FROM test",
|
||||
to_extra: { CreatedAt: 0 },
|
||||
to_sql_mode:
|
||||
"NO_ENGINE_SUBSTITUTION,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES",
|
||||
to_commit: "WORKING",
|
||||
to_commit_date: "2023-03-09T07:56:29.035Z",
|
||||
from_type: null,
|
||||
from_name: null,
|
||||
from_fragment: null,
|
||||
from_extra: null,
|
||||
from_sql_mode: null,
|
||||
from_commit: "HEAD",
|
||||
from_commit_date: "2023-03-09T07:56:28.841Z",
|
||||
diff_type: "added",
|
||||
},
|
||||
],
|
||||
fields: [],
|
||||
},
|
||||
matcher: diffRowsMatcher,
|
||||
},
|
||||
{
|
||||
skip: true, // TODO: Order is not consistent
|
||||
q: "SELECT * FROM DOLT_PATCH('HEAD', 'WORKING') WHERE diff_type = 'schema'",
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 3,
|
||||
oid: null,
|
||||
rows: [
|
||||
{
|
||||
statement_order: "1",
|
||||
from_commit_hash: "",
|
||||
to_commit_hash: "WORKING",
|
||||
table_name: "test_info",
|
||||
diff_type: "schema",
|
||||
statement: "DROP TABLE `test_info`;",
|
||||
},
|
||||
// TODO: Should `CREATE SCHEMA` statement be included here?
|
||||
{
|
||||
statement_order: "2",
|
||||
from_commit_hash: "r6g8g61k89dpgb3cuks70jfnavr6b1q0",
|
||||
to_commit_hash: "WORKING",
|
||||
table_name: "testanother",
|
||||
diff_type: "schema",
|
||||
statement:
|
||||
"CREATE TABLE `testanother` (\n" +
|
||||
" `pk` integer NOT NULL,\n" +
|
||||
" `value` integer,\n" +
|
||||
" PRIMARY KEY (`pk`)\n" +
|
||||
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin;",
|
||||
},
|
||||
{
|
||||
statement_order: "4", // TODO: Why is this 4?
|
||||
from_commit_hash: "",
|
||||
to_commit_hash: "WORKING",
|
||||
table_name: "dolt_schemas",
|
||||
diff_type: "schema",
|
||||
statement:
|
||||
"CREATE TABLE `dolt_schemas` (\n" + // TODO: No backticks
|
||||
" `type` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL,\n" +
|
||||
" `name` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL,\n" +
|
||||
" `fragment` longtext,\n" +
|
||||
" `extra` json,\n" +
|
||||
" `sql_mode` varchar(256) COLLATE utf8mb4_0900_ai_ci,\n" +
|
||||
" PRIMARY KEY (`type`,`name`)\n" +
|
||||
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin;",
|
||||
},
|
||||
],
|
||||
fields: [],
|
||||
},
|
||||
matcher: patchRowsMatcher,
|
||||
},
|
||||
{
|
||||
q: "SELECT * FROM DOLT_PATCH('HEAD', 'WORKING', 'test_info') WHERE diff_type = 'schema'",
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 1,
|
||||
oid: null,
|
||||
rows: [
|
||||
{
|
||||
statement_order: "1",
|
||||
from_commit_hash: "",
|
||||
to_commit_hash: "WORKING",
|
||||
table_name: "public.test_info",
|
||||
diff_type: "schema",
|
||||
statement: "DROP TABLE `test_info`;", // TODO: No backticks
|
||||
},
|
||||
],
|
||||
},
|
||||
matcher: patchRowsMatcher,
|
||||
},
|
||||
{
|
||||
q: "SELECT * FROM DOLT_SCHEMA_DIFF('HEAD', 'WORKING') ORDER BY to_table_name;",
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 3,
|
||||
oid: null,
|
||||
rows: [
|
||||
{
|
||||
from_table_name: "public.test_info",
|
||||
to_table_name: "",
|
||||
from_create_statement:
|
||||
"CREATE TABLE `test_info` (\n" + // TODO: No backticks
|
||||
" `id` integer NOT NULL,\n" +
|
||||
" `info` varchar(255),\n" +
|
||||
" `test_pk` integer,\n" +
|
||||
" PRIMARY KEY (`id`),\n" +
|
||||
" KEY `test_info_test_pk_fkey` (`test_pk`),\n" +
|
||||
" CONSTRAINT `test_info_test_pk_fkey` FOREIGN KEY (`test_pk`) REFERENCES `test` (`pk`) ON DELETE NO ACTION ON UPDATE NO ACTION\n" +
|
||||
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin;",
|
||||
to_create_statement: "",
|
||||
},
|
||||
{
|
||||
from_table_name: "",
|
||||
to_table_name: "anotherschema.testanother",
|
||||
from_create_statement: "",
|
||||
to_create_statement:
|
||||
"CREATE TABLE `testanother` (\n" +
|
||||
" `pk` integer NOT NULL,\n" +
|
||||
" `value` integer,\n" +
|
||||
" PRIMARY KEY (`pk`)\n" +
|
||||
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin;",
|
||||
},
|
||||
{
|
||||
from_table_name: "",
|
||||
to_table_name: "public.dolt_schemas",
|
||||
from_create_statement: "",
|
||||
to_create_statement:
|
||||
"CREATE TABLE `dolt_schemas` (\n" + // TODO: No backticks
|
||||
" `type` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL,\n" +
|
||||
" `name` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL,\n" +
|
||||
" `fragment` longtext,\n" +
|
||||
" `extra` json,\n" +
|
||||
" `sql_mode` varchar(256) COLLATE utf8mb4_0900_ai_ci,\n" +
|
||||
" PRIMARY KEY (`type`,`name`)\n" +
|
||||
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin;",
|
||||
},
|
||||
],
|
||||
fields: doltSchemaDiffFields,
|
||||
},
|
||||
},
|
||||
{
|
||||
q: "SELECT * FROM DOLT_SCHEMA_DIFF('HEAD', 'WORKING', 'test_info')",
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 1,
|
||||
oid: null,
|
||||
rows: [
|
||||
{
|
||||
from_table_name: "public.test_info",
|
||||
to_table_name: "",
|
||||
from_create_statement:
|
||||
"CREATE TABLE `test_info` (\n" + // TODO: No backticks
|
||||
" `id` integer NOT NULL,\n" +
|
||||
" `info` varchar(255),\n" +
|
||||
" `test_pk` integer,\n" +
|
||||
" PRIMARY KEY (`id`),\n" +
|
||||
" KEY `test_info_test_pk_fkey` (`test_pk`),\n" +
|
||||
" CONSTRAINT `test_info_test_pk_fkey` FOREIGN KEY (`test_pk`) REFERENCES `test` (`pk`) ON DELETE NO ACTION ON UPDATE NO ACTION\n" +
|
||||
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin;",
|
||||
to_create_statement: "",
|
||||
},
|
||||
],
|
||||
fields: doltSchemaDiffFields,
|
||||
},
|
||||
},
|
||||
{
|
||||
q: "SELECT * FROM DOLT_SCHEMA_DIFF('HEAD', 'WORKING', 'testanother')",
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 1,
|
||||
oid: null,
|
||||
rows: [
|
||||
{
|
||||
from_table_name: "",
|
||||
to_table_name: "anotherschema.testanother",
|
||||
from_create_statement: "",
|
||||
to_create_statement:
|
||||
"CREATE TABLE `testanother` (\n" +
|
||||
" `pk` integer NOT NULL,\n" +
|
||||
" `value` integer,\n" +
|
||||
" PRIMARY KEY (`pk`)\n" +
|
||||
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin;",
|
||||
},
|
||||
],
|
||||
fields: doltSchemaDiffFields,
|
||||
},
|
||||
},
|
||||
{
|
||||
q: `SELECT DOLT_COMMIT('-A', '-m', $1::text)`,
|
||||
p: ["Make some changes on branch"],
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 1,
|
||||
oid: null,
|
||||
rows: [{ dolt_commit: "" }],
|
||||
fields: doltCommitFields,
|
||||
},
|
||||
},
|
||||
|
||||
// Three dot
|
||||
{
|
||||
q: "SELECT * FROM dolt_diff_summary('main...HEAD') ORDER BY to_table_name",
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 4,
|
||||
oid: null,
|
||||
rows: [
|
||||
{
|
||||
from_table_name: "public.test_info",
|
||||
to_table_name: "",
|
||||
diff_type: "dropped",
|
||||
data_change: 1,
|
||||
schema_change: 1,
|
||||
},
|
||||
{
|
||||
from_table_name: "",
|
||||
to_table_name: "anotherschema.testanother",
|
||||
diff_type: "added",
|
||||
data_change: 1,
|
||||
schema_change: 1,
|
||||
},
|
||||
{
|
||||
from_table_name: "",
|
||||
to_table_name: "public.dolt_schemas",
|
||||
diff_type: "added",
|
||||
data_change: 1,
|
||||
schema_change: 1,
|
||||
},
|
||||
{
|
||||
from_table_name: "public.test",
|
||||
to_table_name: "public.test",
|
||||
diff_type: "modified",
|
||||
data_change: 1,
|
||||
schema_change: 0,
|
||||
},
|
||||
],
|
||||
fields: doltDiffSummaryFields,
|
||||
},
|
||||
},
|
||||
{
|
||||
q: "SELECT * FROM dolt_diff_summary('main...HEAD', 'test')",
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 1,
|
||||
oid: null,
|
||||
rows: [
|
||||
{
|
||||
from_table_name: "public.test",
|
||||
to_table_name: "public.test",
|
||||
diff_type: "modified",
|
||||
data_change: 1,
|
||||
schema_change: 0,
|
||||
},
|
||||
],
|
||||
fields: doltDiffSummaryFields,
|
||||
},
|
||||
},
|
||||
{
|
||||
q: "SELECT * FROM dolt_diff_stat('main...HEAD') ORDER BY table_name;",
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 4,
|
||||
oid: null,
|
||||
rows: [
|
||||
{
|
||||
table_name: "anotherschema.testanother",
|
||||
rows_unmodified: "0",
|
||||
rows_added: "1",
|
||||
rows_deleted: "0",
|
||||
rows_modified: "0",
|
||||
cells_added: "2",
|
||||
cells_deleted: "0",
|
||||
cells_modified: "0",
|
||||
old_row_count: "0",
|
||||
new_row_count: "1",
|
||||
old_cell_count: "0",
|
||||
new_cell_count: "2",
|
||||
},
|
||||
{
|
||||
table_name: "public.dolt_schemas",
|
||||
rows_unmodified: "0",
|
||||
rows_added: "1",
|
||||
rows_deleted: "0",
|
||||
rows_modified: "0",
|
||||
cells_added: "5",
|
||||
cells_deleted: "0",
|
||||
cells_modified: "0",
|
||||
old_row_count: "0",
|
||||
new_row_count: "1",
|
||||
old_cell_count: "0",
|
||||
new_cell_count: "5",
|
||||
},
|
||||
{
|
||||
table_name: "public.test",
|
||||
rows_unmodified: "2",
|
||||
rows_added: "0",
|
||||
rows_deleted: "0",
|
||||
rows_modified: "1",
|
||||
cells_added: "0",
|
||||
cells_deleted: "0",
|
||||
cells_modified: "1",
|
||||
old_row_count: "3",
|
||||
new_row_count: "3",
|
||||
old_cell_count: "6",
|
||||
new_cell_count: "6",
|
||||
},
|
||||
{
|
||||
table_name: "public.test_info",
|
||||
rows_unmodified: "0",
|
||||
rows_added: "0",
|
||||
rows_deleted: "1",
|
||||
rows_modified: "0",
|
||||
cells_added: "0",
|
||||
cells_deleted: "3",
|
||||
cells_modified: "0",
|
||||
old_row_count: "1",
|
||||
new_row_count: "0",
|
||||
old_cell_count: "3",
|
||||
new_cell_count: "0",
|
||||
},
|
||||
],
|
||||
fields: doltDiffStatFields,
|
||||
},
|
||||
},
|
||||
{
|
||||
q: "SELECT * FROM dolt_diff_stat('main...HEAD', 'test_info')",
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 1,
|
||||
oid: null,
|
||||
rows: [
|
||||
{
|
||||
table_name: "public.test_info",
|
||||
rows_unmodified: "0",
|
||||
rows_added: "0",
|
||||
rows_deleted: "1",
|
||||
rows_modified: "0",
|
||||
cells_added: "0",
|
||||
cells_deleted: "3",
|
||||
cells_modified: "0",
|
||||
old_row_count: "1",
|
||||
new_row_count: "0",
|
||||
old_cell_count: "3",
|
||||
new_cell_count: "0",
|
||||
},
|
||||
],
|
||||
fields: doltDiffStatFields,
|
||||
},
|
||||
},
|
||||
{
|
||||
skip: true, // TODO: Order not consistent
|
||||
q: "SELECT * FROM DOLT_PATCH('main...HEAD') WHERE diff_type = 'schema'",
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 4,
|
||||
oid: null,
|
||||
rows: [
|
||||
{
|
||||
statement_order: "1",
|
||||
from_commit_hash: "",
|
||||
to_commit_hash: "",
|
||||
table_name: "public.test_info",
|
||||
diff_type: "schema",
|
||||
statement: "DROP TABLE `test_info`;",
|
||||
},
|
||||
{
|
||||
statement_order: "2",
|
||||
from_commit_hash: "",
|
||||
to_commit_hash: "",
|
||||
table_name: "anotherschema.testanother",
|
||||
diff_type: "schema",
|
||||
statement:
|
||||
"CREATE TABLE `testanother` (\n" +
|
||||
" `pk` integer NOT NULL,\n" +
|
||||
" `value` integer,\n" +
|
||||
" PRIMARY KEY (`pk`)\n" +
|
||||
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin;",
|
||||
},
|
||||
// TODO: `CREATE SCHEMA` here?
|
||||
{
|
||||
statement_order: "4", // TODO: why
|
||||
from_commit_hash: "",
|
||||
to_commit_hash: "",
|
||||
table_name: "public.dolt_schemas",
|
||||
diff_type: "schema",
|
||||
statement:
|
||||
"CREATE TABLE `dolt_schemas` (\n" +
|
||||
" `type` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL,\n" +
|
||||
" `name` varchar(64) COLLATE utf8mb4_0900_ai_ci NOT NULL,\n" +
|
||||
" `fragment` longtext,\n" +
|
||||
" `extra` json,\n" +
|
||||
" `sql_mode` varchar(256) COLLATE utf8mb4_0900_ai_ci,\n" +
|
||||
" PRIMARY KEY (`type`,`name`)\n" +
|
||||
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin;",
|
||||
},
|
||||
],
|
||||
fields: [],
|
||||
},
|
||||
matcher: patchRowsMatcher,
|
||||
},
|
||||
{
|
||||
q: "SELECT * FROM DOLT_PATCH('main...HEAD', 'test_info') WHERE diff_type = 'schema'",
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 1,
|
||||
oid: null,
|
||||
rows: [
|
||||
{
|
||||
statement_order: "1",
|
||||
from_commit_hash: "",
|
||||
to_commit_hash: "",
|
||||
table_name: "public.test_info",
|
||||
diff_type: "schema",
|
||||
statement: "DROP TABLE `test_info`;",
|
||||
},
|
||||
],
|
||||
fields: [],
|
||||
},
|
||||
matcher: patchRowsMatcher,
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,157 @@
|
||||
import {
|
||||
doltCommitFields,
|
||||
doltDocsFields,
|
||||
doltStatusFields,
|
||||
} from "../fields.js";
|
||||
|
||||
const readmeText = `# README
|
||||
## My List
|
||||
|
||||
- Item 1
|
||||
- Item 2
|
||||
`;
|
||||
|
||||
const updatedReadmeText = `${readmeText}-Item 3`;
|
||||
|
||||
export const docsTests = [
|
||||
{
|
||||
q: "select * from dolt.docs",
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 0,
|
||||
oid: null,
|
||||
rows: [],
|
||||
fields: doltDocsFields,
|
||||
},
|
||||
},
|
||||
{
|
||||
q: "INSERT INTO dolt.docs VALUES ($1, $2);",
|
||||
p: ["README.md", readmeText],
|
||||
res: {
|
||||
command: "INSERT",
|
||||
rowCount: 1,
|
||||
oid: 0,
|
||||
rows: [],
|
||||
fields: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
q: `select * from dolt.docs where doc_name=$1`,
|
||||
p: ["README.md"],
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 1,
|
||||
oid: null,
|
||||
rows: [{ doc_name: "README.md", doc_text: readmeText }],
|
||||
fields: doltDocsFields,
|
||||
},
|
||||
},
|
||||
{
|
||||
q: "INSERT INTO dolt.docs VALUES ($1, $2) ON CONFLICT (doc_name) DO UPDATE SET doc_text = $2",
|
||||
p: ["README.md", updatedReadmeText],
|
||||
res: {
|
||||
command: "INSERT",
|
||||
rowCount: 2, // TODO: This should be 1, but there's a bug in the GMS iterators that overcounts
|
||||
oid: 0,
|
||||
rows: [],
|
||||
fields: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
q: `select * from dolt.docs where doc_name=$1`,
|
||||
p: ["README.md"],
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 1,
|
||||
oid: null,
|
||||
rows: [{ doc_name: "README.md", doc_text: updatedReadmeText }],
|
||||
fields: doltDocsFields,
|
||||
},
|
||||
},
|
||||
{
|
||||
q: `SELECT * FROM dolt.status`,
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 1,
|
||||
oid: null,
|
||||
rows: [{ table_name: "dolt.docs", staged: false, status: "new table" }],
|
||||
fields: doltStatusFields,
|
||||
},
|
||||
},
|
||||
{
|
||||
q: `SELECT DOLT_COMMIT('-A', '-m', $1::text)`,
|
||||
p: ["Add dolt.docs table"],
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 1,
|
||||
oid: null,
|
||||
rows: [{ dolt_commit: "" }],
|
||||
fields: doltCommitFields,
|
||||
},
|
||||
},
|
||||
{
|
||||
q: `select * from dolt.docs where doc_name=$1`,
|
||||
p: ["README.md"],
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 1,
|
||||
oid: null,
|
||||
rows: [{ doc_name: "README.md", doc_text: updatedReadmeText }],
|
||||
fields: doltDocsFields,
|
||||
},
|
||||
},
|
||||
{
|
||||
q: "DELETE FROM dolt.docs WHERE doc_name=$1",
|
||||
p: ["README.md"],
|
||||
res: {
|
||||
command: "DELETE",
|
||||
rowCount: 1,
|
||||
oid: null,
|
||||
rows: [],
|
||||
fields: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
q: `select * from dolt.docs where doc_name=$1`,
|
||||
p: ["README.md"],
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 0,
|
||||
oid: null,
|
||||
rows: [],
|
||||
fields: doltDocsFields,
|
||||
},
|
||||
},
|
||||
{
|
||||
q: `SET SEARCH_PATH = 'public,';`,
|
||||
res: {
|
||||
command: "SET",
|
||||
rowCount: null,
|
||||
oid: null,
|
||||
rows: [],
|
||||
fields: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
q: `select * from dolt.docs where doc_name=$1`,
|
||||
p: ["README.md"],
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 0,
|
||||
oid: null,
|
||||
rows: [],
|
||||
fields: doltDocsFields,
|
||||
},
|
||||
},
|
||||
{
|
||||
q: `SELECT DOLT_COMMIT('-A', '-m', $1::text)`,
|
||||
p: ["Remove README"],
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 1,
|
||||
oid: null,
|
||||
rows: [{ dolt_commit: "" }],
|
||||
fields: doltCommitFields,
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,75 @@
|
||||
import fs from "fs";
|
||||
import { pipeline } from "stream/promises";
|
||||
import { from as copyFrom } from "pg-copy-streams";
|
||||
import path from "path";
|
||||
import { branchTests } from "./branches.js";
|
||||
import { databaseTests } from "./databases.js";
|
||||
import { docsTests } from "./docs.js";
|
||||
import { logTests } from "./logs.js";
|
||||
import { assertEqualRows } from "../helpers.js";
|
||||
import { mergeTests } from "./merge.js";
|
||||
import { tableTests } from "./table.js";
|
||||
import { schemaTests } from "./schemas.js";
|
||||
import { tagsTests } from "./tags.js";
|
||||
import { viewsTests } from "./views.js";
|
||||
import { diffTests } from "./diffs.js";
|
||||
|
||||
const args = process.argv.slice(2);
|
||||
const testDataPath = args[3];
|
||||
|
||||
export default async function runWorkbenchTests(database) {
|
||||
await runTests(database, databaseTests, "database");
|
||||
await runTests(database, branchTests, "branches");
|
||||
await runTests(database, schemaTests, "schemas");
|
||||
await runTests(database, logTests, "logs");
|
||||
await runTests(database, mergeTests, "merge");
|
||||
await runTests(database, tableTests, "tables");
|
||||
await runTests(database, docsTests, "docs");
|
||||
await runTests(database, tagsTests, "tags");
|
||||
await runTests(database, viewsTests, "views");
|
||||
await runTests(database, diffTests, "diffs");
|
||||
}
|
||||
|
||||
async function runTests(database, tests, name) {
|
||||
console.log("Running tests for", name);
|
||||
await Promise.all(
|
||||
tests.map(async (test) => {
|
||||
if (test.skip) return;
|
||||
|
||||
if (test.file) {
|
||||
const filePath = path.resolve(testDataPath, test.file);
|
||||
try {
|
||||
// TODO: Is it possible to test the COPY FROM output?
|
||||
const ingestStream = database.client.query(copyFrom(test.q));
|
||||
const sourceStream = fs.createReadStream(filePath);
|
||||
await pipeline([sourceStream, ingestStream]);
|
||||
} catch (err) {
|
||||
console.log("Query errored:", test.q);
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
return database
|
||||
.query(test.q, test.p)
|
||||
.then((data) => {
|
||||
assertEqualRows(test, data);
|
||||
})
|
||||
.catch((err) => {
|
||||
if (test.expectedErr) {
|
||||
if (err.message.includes(test.expectedErr)) {
|
||||
return;
|
||||
} else {
|
||||
console.log("Query error did not match expected:", test.q);
|
||||
}
|
||||
} else {
|
||||
console.log("Query errored:", test.q);
|
||||
}
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
});
|
||||
})
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
import { logsMatcher } from "./matchers.js";
|
||||
import { dbName } from "../helpers.js";
|
||||
|
||||
export const logTests = [
|
||||
{
|
||||
q: `SELECT * FROM DOLT_LOG('main', '--parents') LIMIT 10 OFFSET 0;`, // TODO: Prepared not working
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 1,
|
||||
oid: null,
|
||||
rows: [
|
||||
{
|
||||
commit_hash: "",
|
||||
committer: "postgres",
|
||||
email: "postgres@127.0.0.1",
|
||||
date: "",
|
||||
message: "CREATE DATABASE",
|
||||
parents: ["3orrg69ou1loj2ph21guie3r2lf8bsab"],
|
||||
},
|
||||
{
|
||||
commit_hash: "",
|
||||
committer: "Dolt System Account",
|
||||
email: "doltuser@dolthub.com",
|
||||
date: "",
|
||||
message: "Initialize data repository",
|
||||
parents: [],
|
||||
},
|
||||
],
|
||||
fields: [],
|
||||
},
|
||||
matcher: logsMatcher,
|
||||
},
|
||||
{
|
||||
q: `USE '${dbName}/mybranch';`,
|
||||
res: {
|
||||
command: "SET",
|
||||
rowCount: null,
|
||||
oid: null,
|
||||
rows: [],
|
||||
fields: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
q: `SELECT * FROM dolt.log;`, // TODO: If we decide to implement AS OF, use here instead of USE statement above and below
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 2,
|
||||
oid: null,
|
||||
rows: [
|
||||
{
|
||||
commit_hash: "",
|
||||
committer: "Dolt",
|
||||
email: "dolt@dolthub.com",
|
||||
date: "",
|
||||
message: "Create table test",
|
||||
},
|
||||
{
|
||||
commit_hash: "",
|
||||
committer: "postgres",
|
||||
email: "postgres@127.0.0.1",
|
||||
date: "",
|
||||
message: "CREATE DATABASE",
|
||||
},
|
||||
{
|
||||
commit_hash: "",
|
||||
committer: "Dolt System Account",
|
||||
email: "doltuser@dolthub.com",
|
||||
date: "",
|
||||
message: "Initialize data repository",
|
||||
},
|
||||
],
|
||||
},
|
||||
matcher: logsMatcher,
|
||||
},
|
||||
{
|
||||
q: `USE '${dbName}/main';`,
|
||||
res: {
|
||||
command: "SET",
|
||||
rowCount: null,
|
||||
oid: null,
|
||||
rows: [],
|
||||
fields: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
q: `SELECT * FROM DOLT_LOG('main..mybranch', '--parents')`, // TODO: Prepared not working
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 1,
|
||||
oid: null,
|
||||
rows: [
|
||||
{
|
||||
commit_hash: "",
|
||||
committer: "Dolt",
|
||||
email: "dolt@dolthub.com",
|
||||
date: "",
|
||||
message: "Create table test",
|
||||
parents: [""],
|
||||
},
|
||||
],
|
||||
fields: [],
|
||||
},
|
||||
matcher: logsMatcher,
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,173 @@
|
||||
function matcher(rows, exp, exceptionKeys, getExceptionIsValid) {
|
||||
// Row lengths match
|
||||
if (rows.length !== exp.length) {
|
||||
console.log("row lengths don't match", rows.length, exp.length);
|
||||
return false;
|
||||
}
|
||||
for (let i = 0; i < rows.length; i++) {
|
||||
const rowKeys = Object.keys(rows[i]);
|
||||
const expKeys = Object.keys(exp[i]);
|
||||
// Row key lengths match
|
||||
if (rowKeys.length !== expKeys.length) {
|
||||
console.log(
|
||||
"row key lengths don't match",
|
||||
rowKeys.length,
|
||||
expKeys.length
|
||||
);
|
||||
return false;
|
||||
}
|
||||
// Row key values match
|
||||
for (let j = 0; j < rowKeys.length; j++) {
|
||||
const rowKey = rowKeys[j];
|
||||
// Check if key has an exception function
|
||||
if (exceptionKeys.includes(rowKey)) {
|
||||
const isValid = getExceptionIsValid(rows[i], rowKey, exp[i]);
|
||||
if (!isValid) {
|
||||
console.log("exception was not valid for key:", rowKey);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// Compare cell values
|
||||
const cellVal = JSON.stringify(rows[i][rowKey]);
|
||||
const expCellVal = JSON.stringify(exp[i][rowKey]);
|
||||
if (cellVal !== expCellVal) {
|
||||
console.log("values don't match", cellVal, expCellVal);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function commitHashIsValid(commit) {
|
||||
return commit === "STAGED" || commit === "WORKING" || commit.length === 32;
|
||||
}
|
||||
|
||||
function dateIsValid(date) {
|
||||
return JSON.stringify(date).length > 0;
|
||||
}
|
||||
|
||||
export function branchesMatcher(data, exp) {
|
||||
const exceptionKeys = ["hash", "latest_commit_date"];
|
||||
|
||||
function getExceptionIsValid(row, key) {
|
||||
const val = row[key];
|
||||
switch (key) {
|
||||
case "hash":
|
||||
return commitHashIsValid(val);
|
||||
case "latest_commit_date":
|
||||
return dateIsValid(val);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return matcher(data.rows, exp.rows, exceptionKeys, getExceptionIsValid);
|
||||
}
|
||||
|
||||
export function logsMatcher(data, exp) {
|
||||
const exceptionKeys = ["commit_hash", "date", "parents"];
|
||||
|
||||
function getExceptionIsValid(row, key, expRow) {
|
||||
const val = row[key];
|
||||
switch (key) {
|
||||
case "commit_hash":
|
||||
return commitHashIsValid(val);
|
||||
case "date":
|
||||
return dateIsValid(val);
|
||||
case "parents":
|
||||
const numParents = val.split(", ").filter((v) => !!v.length).length;
|
||||
const expParents = expRow.parents.length;
|
||||
return numParents === expParents;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return matcher(data.rows, exp.rows, exceptionKeys, getExceptionIsValid);
|
||||
}
|
||||
|
||||
export function mergeBaseMatcher(data) {
|
||||
if (data.rows.length !== 1) {
|
||||
return false;
|
||||
}
|
||||
return commitHashIsValid(data.rows[0].dolt_merge_base);
|
||||
}
|
||||
|
||||
export function mergeMatcher(data, exp) {
|
||||
if (data.rows.length !== 1) {
|
||||
console.log("Rows length not 1", data.rows.length);
|
||||
return false;
|
||||
}
|
||||
|
||||
const row = data.rows[0].dolt_merge;
|
||||
const expRow = exp.rows[0].dolt_merge;
|
||||
|
||||
// Check valid commit hash
|
||||
if (!commitHashIsValid(row[0])) {
|
||||
console.log("Invalid commit hash", row[0]);
|
||||
return false;
|
||||
}
|
||||
// Check the rest of the fields
|
||||
for (let i = 1; i < row.length; i++) {
|
||||
if (row[i] !== expRow[i]) {
|
||||
console.log("Values don't match", row[i], expRow[i]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
export function tagsMatcher(data, exp) {
|
||||
const exceptionKeys = ["tag_hash", "date"];
|
||||
|
||||
function getExceptionIsValid(row, key) {
|
||||
const val = row[key];
|
||||
switch (key) {
|
||||
case "tag_hash":
|
||||
return commitHashIsValid(val);
|
||||
case "date":
|
||||
return dateIsValid(val);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return matcher(data.rows, exp.rows, exceptionKeys, getExceptionIsValid);
|
||||
}
|
||||
|
||||
export function diffRowsMatcher(data, exp) {
|
||||
const exceptionKeys = ["to_commit_date", "from_commit_date"];
|
||||
|
||||
function getExceptionIsValid(row, key) {
|
||||
const val = row[key];
|
||||
switch (key) {
|
||||
case "to_commit_date":
|
||||
case "from_commit_date":
|
||||
return dateIsValid(val);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return matcher(data.rows, exp.rows, exceptionKeys, getExceptionIsValid);
|
||||
}
|
||||
|
||||
export function patchRowsMatcher(data, exp) {
|
||||
const exceptionKeys = ["to_commit_hash", "from_commit_hash"];
|
||||
|
||||
function getExceptionIsValid(row, key) {
|
||||
const val = row[key];
|
||||
switch (key) {
|
||||
case "to_commit_hash":
|
||||
case "from_commit_hash":
|
||||
return commitHashIsValid(val);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return matcher(data.rows, exp.rows, exceptionKeys, getExceptionIsValid);
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
import { doltStatusFields } from "../fields.js";
|
||||
import { logsMatcher, mergeBaseMatcher, mergeMatcher } from "./matchers.js";
|
||||
|
||||
export const mergeTests = [
|
||||
{
|
||||
q: `SELECT DOLT_MERGE_BASE($1::text, $2::text);`,
|
||||
p: ["mybranch", "main"],
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 1,
|
||||
oid: null,
|
||||
rows: [{ dolt_merge_base: "" }],
|
||||
fields: [],
|
||||
},
|
||||
matcher: mergeBaseMatcher,
|
||||
},
|
||||
{
|
||||
q: `SELECT * FROM dolt.status`,
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 0,
|
||||
oid: null,
|
||||
rows: [],
|
||||
fields: doltStatusFields,
|
||||
},
|
||||
},
|
||||
{
|
||||
q: `SELECT DOLT_MERGE($1::text, '--no-ff', '-m', $2::text)`,
|
||||
p: ["mybranch", "Merge mybranch into main"],
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 1,
|
||||
oid: null,
|
||||
rows: [
|
||||
{
|
||||
dolt_merge: ["", "0", "0", "merge successful"],
|
||||
},
|
||||
],
|
||||
fields: [],
|
||||
},
|
||||
matcher: mergeMatcher,
|
||||
},
|
||||
{
|
||||
q: `SELECT * FROM DOLT_LOG('main', '--parents') LIMIT 10 OFFSET 0;`, // TODO: Prepared not working
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 4,
|
||||
oid: null,
|
||||
rows: [
|
||||
{
|
||||
commit_hash: "",
|
||||
message: "Merge mybranch into main",
|
||||
committer: "postgres",
|
||||
email: "postgres@127.0.0.1",
|
||||
date: "",
|
||||
parents: ["", ""],
|
||||
},
|
||||
{
|
||||
commit_hash: "",
|
||||
committer: "Dolt",
|
||||
email: "dolt@dolthub.com",
|
||||
date: "",
|
||||
message: "Create table test",
|
||||
parents: [""],
|
||||
},
|
||||
{
|
||||
commit_hash: "",
|
||||
committer: "postgres",
|
||||
email: "postgres@127.0.0.1",
|
||||
date: "",
|
||||
message: "CREATE DATABASE",
|
||||
parents: [""],
|
||||
},
|
||||
{
|
||||
commit_hash: "",
|
||||
committer: "Dolt System Account",
|
||||
email: "doltuser@dolthub.com",
|
||||
date: "",
|
||||
message: "Initialize data repository",
|
||||
parents: [],
|
||||
},
|
||||
],
|
||||
fields: [],
|
||||
},
|
||||
matcher: logsMatcher,
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,197 @@
|
||||
import {
|
||||
doltBranchFields,
|
||||
doltStatusFields,
|
||||
doltCommitFields,
|
||||
schemaNameField,
|
||||
pgTablesFields,
|
||||
} from "../fields.js";
|
||||
import { dbName } from "../helpers.js";
|
||||
|
||||
export const schemaTests = [
|
||||
{
|
||||
q: `SELECT DOLT_BRANCH($1::text, $2::text)`, // TODO: Should work without casts
|
||||
p: ["schemabranch", "main"],
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 1,
|
||||
oid: null,
|
||||
rows: [{ dolt_branch: ["0"] }],
|
||||
fields: doltBranchFields,
|
||||
},
|
||||
},
|
||||
{
|
||||
q: `USE '${dbName}/schemabranch';`,
|
||||
res: {
|
||||
command: "SET",
|
||||
rowCount: null,
|
||||
oid: null,
|
||||
rows: [],
|
||||
fields: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
q: `create table testpub (pk int, "value" int, primary key(pk));`,
|
||||
res: {
|
||||
command: "CREATE",
|
||||
rowCount: null,
|
||||
oid: null,
|
||||
rows: [],
|
||||
fields: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
q: `SELECT DOLT_COMMIT('-Am', $1::text, '--author', $2::text);`,
|
||||
p: ["Create table testpub", "Dolt <dolt@dolthub.com>"],
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 1,
|
||||
oid: null,
|
||||
rows: [{ dolt_commit: "" }],
|
||||
fields: doltCommitFields,
|
||||
},
|
||||
},
|
||||
{
|
||||
q: `CREATE SCHEMA testschema;`,
|
||||
res: {
|
||||
command: "CREATE",
|
||||
rowCount: null,
|
||||
oid: null,
|
||||
rows: [],
|
||||
fields: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
q: `SET SEARCH_PATH = 'testschema';`,
|
||||
res: {
|
||||
command: "SET",
|
||||
rowCount: null,
|
||||
oid: null,
|
||||
rows: [],
|
||||
fields: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
q: `create table test2 (pk int, "value" int, primary key(pk));`,
|
||||
res: {
|
||||
command: "CREATE",
|
||||
rowCount: null,
|
||||
oid: null,
|
||||
rows: [],
|
||||
fields: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
q: `SELECT * FROM dolt.status;`,
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 2,
|
||||
oid: null,
|
||||
rows: [
|
||||
{
|
||||
table_name: "testschema.test2",
|
||||
staged: false,
|
||||
status: "new table",
|
||||
},
|
||||
{ table_name: "testschema", staged: false, status: "new schema" },
|
||||
],
|
||||
fields: doltStatusFields,
|
||||
},
|
||||
},
|
||||
{
|
||||
q: `SELECT schema_name FROM information_schema.schemata WHERE catalog_name = $1;`,
|
||||
p: [`${dbName}/schemabranch`],
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 5,
|
||||
oid: null,
|
||||
rows: [
|
||||
{ schema_name: "dolt" },
|
||||
{ schema_name: "pg_catalog" },
|
||||
{ schema_name: "public" },
|
||||
{ schema_name: "testschema" },
|
||||
{ schema_name: "information_schema" },
|
||||
],
|
||||
fields: [schemaNameField],
|
||||
},
|
||||
},
|
||||
{
|
||||
q: `SELECT DOLT_COMMIT('-Am', $1::text, '--author', $2::text);`,
|
||||
p: ["Create table test2", "Dolt <dolt@dolthub.com>"],
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 1,
|
||||
oid: null,
|
||||
rows: [{ dolt_commit: "" }],
|
||||
fields: doltCommitFields,
|
||||
},
|
||||
},
|
||||
{
|
||||
q: `SELECT schemaname, tablename FROM pg_catalog.pg_tables where schemaname=$1;`,
|
||||
p: ["testschema"],
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 1,
|
||||
oid: null,
|
||||
rows: [{ schemaname: "testschema", tablename: "test2" }],
|
||||
fields: pgTablesFields,
|
||||
},
|
||||
},
|
||||
{
|
||||
q: `SET SEARCH_PATH = 'public';`,
|
||||
res: {
|
||||
command: "SET",
|
||||
rowCount: null,
|
||||
oid: null,
|
||||
rows: [],
|
||||
fields: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
q: `SELECT schemaname, tablename FROM pg_catalog.pg_tables where schemaname=$1;`,
|
||||
p: ["public"],
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 1,
|
||||
oid: null,
|
||||
rows: [{ schemaname: "public", tablename: "testpub" }],
|
||||
fields: pgTablesFields,
|
||||
},
|
||||
},
|
||||
{
|
||||
q: `USE '${dbName}/main';`,
|
||||
res: {
|
||||
command: "SET",
|
||||
rowCount: null,
|
||||
oid: null,
|
||||
rows: [],
|
||||
fields: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
q: `SELECT schema_name FROM information_schema.schemata WHERE catalog_name = $1;`,
|
||||
p: [`${dbName}/main`],
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 4,
|
||||
oid: null,
|
||||
rows: [
|
||||
{ schema_name: "dolt" },
|
||||
{ schema_name: "pg_catalog" },
|
||||
{ schema_name: "public" },
|
||||
{ schema_name: "information_schema" },
|
||||
],
|
||||
fields: [schemaNameField],
|
||||
},
|
||||
},
|
||||
{
|
||||
q: `SELECT schemaname, tablename FROM pg_catalog.pg_tables where schemaname=$1;`,
|
||||
p: ["public"],
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 0,
|
||||
oid: null,
|
||||
rows: [],
|
||||
fields: pgTablesFields,
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,455 @@
|
||||
import { dbName } from "../helpers.js";
|
||||
import {
|
||||
doltAddFields,
|
||||
doltCheckoutFields,
|
||||
doltCommitFields,
|
||||
doltDiffStatFields,
|
||||
doltResetFields,
|
||||
doltStatusFields,
|
||||
infoSchemaKeyColumnUsageFields,
|
||||
} from "../fields.js";
|
||||
|
||||
const testInfoFields = [
|
||||
{
|
||||
name: "id",
|
||||
tableID: 0,
|
||||
columnID: 0,
|
||||
dataTypeID: 23,
|
||||
dataTypeSize: 4,
|
||||
dataTypeModifier: -1,
|
||||
format: "text",
|
||||
},
|
||||
{
|
||||
name: "info",
|
||||
tableID: 0,
|
||||
columnID: 0,
|
||||
dataTypeID: 1043,
|
||||
dataTypeSize: 1020,
|
||||
dataTypeModifier: 259,
|
||||
format: "text",
|
||||
},
|
||||
{
|
||||
name: "test_pk",
|
||||
tableID: 0,
|
||||
columnID: 0,
|
||||
dataTypeID: 23,
|
||||
dataTypeSize: 4,
|
||||
dataTypeModifier: -1,
|
||||
format: "text",
|
||||
},
|
||||
];
|
||||
|
||||
export const tableTests = [
|
||||
{
|
||||
q: "SET search_path TO DEFAULT",
|
||||
res: {
|
||||
command: "SET",
|
||||
rowCount: null,
|
||||
oid: null,
|
||||
rows: [],
|
||||
fields: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
q: "INSERT INTO test VALUES (0, 0), (1, 1), (2,2)",
|
||||
res: {
|
||||
command: "INSERT",
|
||||
rowCount: 3,
|
||||
oid: 0,
|
||||
rows: [],
|
||||
fields: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
q: `CREATE UNIQUE INDEX test_idx ON test (pk, value)`,
|
||||
res: {
|
||||
command: "CREATE",
|
||||
rowCount: null,
|
||||
oid: null,
|
||||
rows: [],
|
||||
fields: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
q: `SELECT ordinal_position, column_name, udt_name as data_type, is_nullable, column_default FROM information_schema.columns WHERE table_catalog=$1 AND table_schema = $2 AND table_name = $3;`,
|
||||
p: [`${dbName}/main`, "public", "test"],
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 2,
|
||||
oid: null,
|
||||
rows: [
|
||||
{
|
||||
ordinal_position: 1,
|
||||
column_name: "pk",
|
||||
data_type: "int4",
|
||||
is_nullable: "NO",
|
||||
column_default: null,
|
||||
},
|
||||
{
|
||||
ordinal_position: 2,
|
||||
column_name: "value",
|
||||
data_type: "int4",
|
||||
is_nullable: "YES",
|
||||
column_default: null,
|
||||
},
|
||||
],
|
||||
fields: [
|
||||
{
|
||||
name: "ordinal_position",
|
||||
tableID: 0,
|
||||
columnID: 0,
|
||||
dataTypeID: 23,
|
||||
dataTypeSize: 4,
|
||||
dataTypeModifier: -1,
|
||||
format: "text",
|
||||
},
|
||||
{
|
||||
name: "column_name",
|
||||
tableID: 0,
|
||||
columnID: 0,
|
||||
dataTypeID: 1043,
|
||||
dataTypeSize: 256,
|
||||
dataTypeModifier: 68,
|
||||
format: "text",
|
||||
},
|
||||
{
|
||||
name: "data_type",
|
||||
tableID: 0,
|
||||
columnID: 0,
|
||||
dataTypeID: 1043,
|
||||
dataTypeSize: 256,
|
||||
dataTypeModifier: 68,
|
||||
format: "text",
|
||||
},
|
||||
{
|
||||
name: "is_nullable",
|
||||
tableID: 0,
|
||||
columnID: 0,
|
||||
dataTypeID: 1043,
|
||||
dataTypeSize: 12,
|
||||
dataTypeModifier: 7,
|
||||
format: "text",
|
||||
},
|
||||
{
|
||||
name: "column_default",
|
||||
tableID: 0,
|
||||
columnID: 0,
|
||||
dataTypeID: 25,
|
||||
dataTypeSize: -1,
|
||||
dataTypeModifier: -1,
|
||||
format: "text",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
q: `SELECT DOLT_COMMIT('-A', '-m', $1::text)`,
|
||||
p: ["Add some rows and a column index"],
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 1,
|
||||
oid: null,
|
||||
rows: [{ dolt_commit: [""] }],
|
||||
fields: doltCommitFields,
|
||||
},
|
||||
},
|
||||
{
|
||||
skip: true, // TODO: ORDER BY is not yet supported
|
||||
q: `SELECT
|
||||
table_name, index_name, comment, non_unique, GROUP_CONCAT(column_name ORDER BY seq_in_index) AS COLUMNS
|
||||
FROM information_schema.statistics
|
||||
WHERE table_catalog=$1 AND table_schema=$2 AND table_name=$3 AND index_name!='PRIMARY'
|
||||
GROUP BY index_name;`,
|
||||
p: [`${dbName}/main`, "public", "test"],
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 1,
|
||||
oid: null,
|
||||
rows: [
|
||||
{
|
||||
TABLE_NAME: "test",
|
||||
INDEX_NAME: "test_idx",
|
||||
COMMENT: "",
|
||||
NON_UNIQUE: 0,
|
||||
COLUMNS: "pk,value",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
q: "CREATE TABLE test_info (id int, info varchar(255), test_pk int, primary key(id), foreign key (test_pk) references test(pk))",
|
||||
res: {
|
||||
command: "CREATE",
|
||||
rowCount: null,
|
||||
oid: null,
|
||||
rows: [],
|
||||
fields: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
q: "INSERT INTO test_info VALUES (1, 'info about test pk 0', 0)",
|
||||
res: {
|
||||
command: "INSERT",
|
||||
rowCount: 1,
|
||||
oid: 0,
|
||||
rows: [],
|
||||
fields: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
q: `SELECT DOLT_COMMIT('-A', '-m', $1::text)`,
|
||||
p: ["Add test_info with foreign key"],
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 1,
|
||||
oid: null,
|
||||
rows: [{ dolt_commit: [""] }],
|
||||
fields: doltCommitFields,
|
||||
},
|
||||
},
|
||||
{
|
||||
q: `SELECT "table_schema", "table_name" FROM "information_schema"."tables" WHERE "table_schema" = $1 AND "table_catalog" = $2;`,
|
||||
p: ["public", `${dbName}/main`],
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 2,
|
||||
oid: null,
|
||||
rows: [
|
||||
{ table_schema: "public", table_name: "test" },
|
||||
{ table_schema: "public", table_name: "test_info" },
|
||||
],
|
||||
fields: [
|
||||
{
|
||||
name: "table_schema",
|
||||
tableID: 0,
|
||||
columnID: 0,
|
||||
dataTypeID: 1043,
|
||||
dataTypeSize: 256,
|
||||
dataTypeModifier: 68,
|
||||
format: "text",
|
||||
},
|
||||
{
|
||||
name: "table_name",
|
||||
tableID: 0,
|
||||
columnID: 0,
|
||||
dataTypeID: 1043,
|
||||
dataTypeSize: 256,
|
||||
dataTypeModifier: 68,
|
||||
format: "text",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
q: `SELECT * FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE table_name=$1 AND table_schema=$2 AND table_catalog=$3 AND referenced_table_schema IS NOT NULL`,
|
||||
p: ["test_info", "public", `${dbName}/main`],
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 1,
|
||||
oid: null,
|
||||
rows: [
|
||||
{
|
||||
CONSTRAINT_CATALOG: `${dbName}/main`,
|
||||
CONSTRAINT_SCHEMA: "public",
|
||||
CONSTRAINT_NAME: "test_info_test_pk_fkey",
|
||||
TABLE_CATALOG: `${dbName}/main`,
|
||||
TABLE_SCHEMA: "public",
|
||||
TABLE_NAME: "test_info",
|
||||
COLUMN_NAME: "test_pk",
|
||||
ORDINAL_POSITION: 1,
|
||||
POSITION_IN_UNIQUE_CONSTRAINT: 1,
|
||||
REFERENCED_TABLE_SCHEMA: `${dbName}/main`,
|
||||
REFERENCED_TABLE_NAME: "test",
|
||||
REFERENCED_COLUMN_NAME: "pk",
|
||||
},
|
||||
],
|
||||
fields: infoSchemaKeyColumnUsageFields,
|
||||
},
|
||||
},
|
||||
{
|
||||
q: `SELECT * FROM "public"."test_info" "public.test_info" ORDER BY id ASC LIMIT 10;`,
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 1,
|
||||
oid: null,
|
||||
rows: [{ id: 1, info: "info about test pk 0", test_pk: 0 }],
|
||||
fields: testInfoFields,
|
||||
},
|
||||
},
|
||||
{
|
||||
q: `USE '${dbName}/main'`,
|
||||
res: {
|
||||
command: "SET",
|
||||
rowCount: null,
|
||||
oid: null,
|
||||
rows: [],
|
||||
fields: [],
|
||||
},
|
||||
},
|
||||
|
||||
// Copy from tests
|
||||
{
|
||||
q: `SELECT * FROM dolt.status`,
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 0,
|
||||
oid: null,
|
||||
rows: [],
|
||||
fields: doltStatusFields,
|
||||
},
|
||||
},
|
||||
{
|
||||
q: `COPY "test_info" FROM STDIN WITH (FORMAT csv, HEADER TRUE);`,
|
||||
file: "insert_test_info.csv",
|
||||
res: { command: "COPY", rowCount: 3, oid: null, rows: [], fields: [] },
|
||||
},
|
||||
{
|
||||
q: "SELECT * FROM test_info",
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 4,
|
||||
oid: null,
|
||||
rows: [
|
||||
{ id: 1, info: "info about test pk 0", test_pk: 0 },
|
||||
{ id: 4, info: "string for 4", test_pk: 1 },
|
||||
{ id: 5, info: "string for 5", test_pk: 0 },
|
||||
{ id: 6, info: "string for 6", test_pk: 0 },
|
||||
],
|
||||
fields: testInfoFields,
|
||||
},
|
||||
},
|
||||
{
|
||||
q: "SELECT * FROM dolt_diff_stat('HEAD', 'WORKING')", // TODO: Prepared not working
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 1,
|
||||
oid: null,
|
||||
rows: [
|
||||
{
|
||||
table_name: "public.test_info",
|
||||
rows_unmodified: "1",
|
||||
rows_added: "3",
|
||||
rows_deleted: "0",
|
||||
rows_modified: "0",
|
||||
cells_added: "9",
|
||||
cells_deleted: "0",
|
||||
cells_modified: "0",
|
||||
old_row_count: "1",
|
||||
new_row_count: "4",
|
||||
old_cell_count: "3",
|
||||
new_cell_count: "12",
|
||||
},
|
||||
],
|
||||
fields: doltDiffStatFields,
|
||||
},
|
||||
},
|
||||
{
|
||||
q: `COPY "test_info" FROM STDIN WITH (FORMAT csv, HEADER TRUE, DELIMITER '|');`,
|
||||
file: "insert_test_info.psv",
|
||||
res: { command: "COPY", rowCount: 3, oid: null, rows: [], fields: [] },
|
||||
},
|
||||
{
|
||||
skip: true,
|
||||
q: "SELECT * FROM dolt_diff_stat('HEAD', 'WORKING')",
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 1,
|
||||
oid: null,
|
||||
rows: [
|
||||
{
|
||||
table_name: "public.test_info",
|
||||
rows_unmodified: 0,
|
||||
rows_added: 3,
|
||||
rows_deleted: 0,
|
||||
rows_modified: 1,
|
||||
cells_added: 9,
|
||||
cells_deleted: 0,
|
||||
cells_modified: 1,
|
||||
old_row_count: 1,
|
||||
new_row_count: 4,
|
||||
old_cell_count: 3,
|
||||
new_cell_count: 12,
|
||||
},
|
||||
],
|
||||
fields: doltDiffStatFields,
|
||||
},
|
||||
},
|
||||
|
||||
// Add and revert load data changes
|
||||
{
|
||||
q: `SELECT * FROM dolt.status`,
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 1,
|
||||
oid: null,
|
||||
rows: [
|
||||
{ table_name: "public.test_info", staged: false, status: "modified" },
|
||||
],
|
||||
fields: doltStatusFields,
|
||||
},
|
||||
},
|
||||
{
|
||||
q: "SELECT DOLT_ADD('.')",
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 1,
|
||||
oid: null,
|
||||
rows: [{ dolt_add: ["0"] }],
|
||||
fields: doltAddFields,
|
||||
},
|
||||
},
|
||||
{
|
||||
q: `SELECT * FROM dolt.status`,
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 1,
|
||||
oid: null,
|
||||
rows: [
|
||||
{ table_name: "public.test_info", staged: true, status: "modified" },
|
||||
],
|
||||
fields: doltStatusFields,
|
||||
},
|
||||
},
|
||||
{
|
||||
q: "SELECT DOLT_RESET('test_info');",
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 1,
|
||||
oid: null,
|
||||
rows: [{ dolt_reset: ["0"] }],
|
||||
fields: doltResetFields,
|
||||
},
|
||||
},
|
||||
{
|
||||
q: `SELECT * FROM dolt.status`,
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 1,
|
||||
oid: null,
|
||||
rows: [
|
||||
{ table_name: "public.test_info", staged: false, status: "modified" },
|
||||
],
|
||||
fields: doltStatusFields,
|
||||
},
|
||||
},
|
||||
{
|
||||
q: "SELECT DOLT_CHECKOUT('test_info')",
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 1,
|
||||
oid: null,
|
||||
rows: [{ dolt_checkout: ["0"] }],
|
||||
fields: doltCheckoutFields,
|
||||
},
|
||||
},
|
||||
{
|
||||
q: `SELECT * FROM dolt.status`,
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 0,
|
||||
oid: null,
|
||||
rows: [],
|
||||
fields: doltStatusFields,
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,121 @@
|
||||
import { doltTagFields, doltTagsFields } from "../fields.js";
|
||||
import { tagsMatcher } from "./matchers.js";
|
||||
|
||||
export const tagsTests = [
|
||||
{
|
||||
q: "SELECT * FROM dolt.tags ORDER BY date DESC",
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 0,
|
||||
oid: null,
|
||||
rows: [],
|
||||
fields: doltTagsFields,
|
||||
},
|
||||
},
|
||||
{
|
||||
q: `SELECT DOLT_TAG($1::text, $2::text);`,
|
||||
p: ["mytag", "main"],
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 1,
|
||||
oid: null,
|
||||
rows: [{ dolt_tag: ["0"] }],
|
||||
fields: doltTagFields,
|
||||
},
|
||||
},
|
||||
{
|
||||
q: "SELECT * FROM dolt.tags ORDER BY date DESC",
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 1,
|
||||
oid: null,
|
||||
rows: [
|
||||
{
|
||||
tag_name: "mytag",
|
||||
message: "",
|
||||
tagger: "Dolt System Account",
|
||||
email: "doltuser@dolthub.com",
|
||||
tag_hash: "",
|
||||
date: "",
|
||||
},
|
||||
],
|
||||
fields: doltTagsFields,
|
||||
},
|
||||
matcher: tagsMatcher,
|
||||
},
|
||||
{
|
||||
q: `SELECT DOLT_TAG('-m', $1::text, $2::text, $3::text)`,
|
||||
p: ["latest release", "mytagnew", "main"],
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 1,
|
||||
oid: null,
|
||||
rows: [{ dolt_tag: ["0"] }],
|
||||
fields: doltTagFields,
|
||||
},
|
||||
},
|
||||
{
|
||||
q: "SELECT * FROM dolt.tags ORDER BY date DESC",
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 2,
|
||||
oid: null,
|
||||
rows: [
|
||||
{
|
||||
tag_name: "mytagnew",
|
||||
message: "latest release",
|
||||
tagger: "Dolt System Account",
|
||||
email: "doltuser@dolthub.com",
|
||||
tag_hash: "",
|
||||
date: "",
|
||||
},
|
||||
{
|
||||
tag_name: "mytag",
|
||||
message: "",
|
||||
tagger: "Dolt System Account",
|
||||
email: "doltuser@dolthub.com",
|
||||
tag_hash: "",
|
||||
date: "",
|
||||
},
|
||||
],
|
||||
fields: doltTagsFields,
|
||||
},
|
||||
matcher: tagsMatcher,
|
||||
},
|
||||
{
|
||||
q: `SELECT DOLT_TAG('-d', $1::text)`,
|
||||
p: ["mytagnew"],
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 1,
|
||||
oid: null,
|
||||
rows: [{ dolt_tag: ["0"] }],
|
||||
fields: doltTagFields,
|
||||
},
|
||||
},
|
||||
{
|
||||
q: "SELECT * FROM dolt.tags ORDER BY date DESC",
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 1,
|
||||
oid: null,
|
||||
rows: [
|
||||
{
|
||||
tag_name: "mytag",
|
||||
message: "",
|
||||
tagger: "Dolt System Account",
|
||||
email: "doltuser@dolthub.com",
|
||||
tag_hash: "",
|
||||
date: "",
|
||||
},
|
||||
],
|
||||
fields: doltTagsFields,
|
||||
},
|
||||
matcher: tagsMatcher,
|
||||
},
|
||||
{
|
||||
q: `SELECT DOLT_COMMIT('-A', '-m', $1::text)`,
|
||||
p: ["Add a tag"],
|
||||
expectedErr: "nothing to commit",
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,196 @@
|
||||
import {
|
||||
doltCheckoutFields,
|
||||
doltSchemasFields,
|
||||
pgTablesFields,
|
||||
} from "../fields.js";
|
||||
|
||||
export const viewsTests = [
|
||||
{
|
||||
q: "SELECT DOLT_CHECKOUT('-b', $1::text);",
|
||||
p: ["more-updates"],
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 1,
|
||||
oid: null,
|
||||
rows: [{ dolt_checkout: ["0", "Switched to branch 'more-updates'"] }],
|
||||
fields: doltCheckoutFields,
|
||||
},
|
||||
},
|
||||
{
|
||||
q: "SELECT * FROM dolt_schemas LIMIT 10 OFFSET 0;",
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 0,
|
||||
oid: null,
|
||||
rows: [],
|
||||
fields: doltSchemasFields,
|
||||
},
|
||||
},
|
||||
{
|
||||
q: "CREATE VIEW myview AS SELECT * FROM test;",
|
||||
res: {
|
||||
command: "CREATE",
|
||||
rowCount: null,
|
||||
oid: null,
|
||||
rows: [],
|
||||
fields: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
q: "SELECT * FROM dolt_schemas LIMIT 10 OFFSET 0",
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 1,
|
||||
oid: null,
|
||||
rows: [
|
||||
{
|
||||
type: "view",
|
||||
name: "myview",
|
||||
fragment: "CREATE VIEW myview AS SELECT * FROM test",
|
||||
extra: { CreatedAt: 0 },
|
||||
sql_mode:
|
||||
"NO_ENGINE_SUBSTITUTION,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES",
|
||||
},
|
||||
],
|
||||
fields: doltSchemasFields,
|
||||
},
|
||||
},
|
||||
{
|
||||
// Excludes views
|
||||
q: "SELECT schemaname, tablename FROM pg_catalog.pg_tables where schemaname=$1;",
|
||||
p: ["public"],
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 2,
|
||||
oid: null,
|
||||
rows: [
|
||||
{
|
||||
schemaname: "public",
|
||||
tablename: "test",
|
||||
},
|
||||
{
|
||||
schemaname: "public",
|
||||
tablename: "test_info",
|
||||
},
|
||||
],
|
||||
fields: pgTablesFields,
|
||||
},
|
||||
},
|
||||
{
|
||||
// Includes views
|
||||
q: "SELECT table_name FROM INFORMATION_SCHEMA.views WHERE table_schema = $1;",
|
||||
p: ["public"],
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 1,
|
||||
oid: null,
|
||||
rows: [{ table_name: "myview" }],
|
||||
fields: [
|
||||
{
|
||||
name: "table_name",
|
||||
tableID: 0,
|
||||
columnID: 0,
|
||||
dataTypeID: 1043,
|
||||
dataTypeSize: 256,
|
||||
dataTypeModifier: 68,
|
||||
format: "text",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
// Includes views
|
||||
q: "SELECT viewname FROM pg_catalog.pg_views WHERE schemaname=$1;",
|
||||
p: ["public"],
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 1,
|
||||
oid: null,
|
||||
rows: [{ viewname: "myview" }],
|
||||
fields: [
|
||||
{
|
||||
name: "viewname",
|
||||
tableID: 0,
|
||||
columnID: 0,
|
||||
dataTypeID: 19,
|
||||
dataTypeSize: 64,
|
||||
dataTypeModifier: -1,
|
||||
format: "text",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
// Includes tables and views
|
||||
q: "SELECT table_schema, table_name, table_type FROM INFORMATION_SCHEMA.tables WHERE table_schema = $1;",
|
||||
p: ["public"],
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 3,
|
||||
oid: null,
|
||||
rows: [
|
||||
{ table_schema: "public", table_name: "myview", table_type: "VIEW" },
|
||||
{
|
||||
table_schema: "public",
|
||||
table_name: "test",
|
||||
table_type: "BASE TABLE",
|
||||
},
|
||||
{
|
||||
table_schema: "public",
|
||||
table_name: "test_info",
|
||||
table_type: "BASE TABLE",
|
||||
},
|
||||
],
|
||||
fields: [
|
||||
{
|
||||
name: "table_schema",
|
||||
tableID: 0,
|
||||
columnID: 0,
|
||||
dataTypeID: 1043,
|
||||
dataTypeSize: 256,
|
||||
dataTypeModifier: 68,
|
||||
format: "text",
|
||||
},
|
||||
{
|
||||
name: "table_name",
|
||||
tableID: 0,
|
||||
columnID: 0,
|
||||
dataTypeID: 1043,
|
||||
dataTypeSize: 256,
|
||||
dataTypeModifier: 68,
|
||||
format: "text",
|
||||
},
|
||||
{
|
||||
name: "table_type",
|
||||
tableID: 0,
|
||||
columnID: 0,
|
||||
dataTypeID: 25,
|
||||
dataTypeSize: -1,
|
||||
dataTypeModifier: -1,
|
||||
format: "text",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
q: `SELECT pg_get_viewdef($1::regclass, true)`,
|
||||
p: ["myview"],
|
||||
res: {
|
||||
command: "SELECT",
|
||||
rowCount: 1,
|
||||
oid: null,
|
||||
rows: [{ pg_get_viewdef: "SELECT * FROM test" }],
|
||||
fields: [
|
||||
{
|
||||
name: "pg_get_viewdef",
|
||||
tableID: 0,
|
||||
columnID: 0,
|
||||
dataTypeID: 25,
|
||||
dataTypeSize: -1,
|
||||
dataTypeModifier: -1,
|
||||
format: "text",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,10 @@
|
||||
LDFLAGS := -lodbc
|
||||
|
||||
all: psqlodbc-test
|
||||
|
||||
psqlodbc-test: psqlodbc-test.c
|
||||
$(CC) -o $@ $^ $(LDFLAGS)
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -f psqlodbc-test
|
||||
@@ -0,0 +1,127 @@
|
||||
#include <sql.h>
|
||||
#include <sqlext.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static void die(SQLSMALLINT type, SQLHANDLE handle, const char *op) {
|
||||
SQLCHAR state[6], msg[256];
|
||||
SQLINTEGER native;
|
||||
SQLSMALLINT len;
|
||||
SQLGetDiagRec(type, handle, 1, state, &native, msg, sizeof(msg), &len);
|
||||
fprintf(stderr, "%s failed: %s (state=%s)\n", op, msg, state);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
#define CHECK_DBC(ret, h, op) do { if ((ret) != SQL_SUCCESS && (ret) != SQL_SUCCESS_WITH_INFO) die(SQL_HANDLE_DBC, (h), (op)); } while (0)
|
||||
#define CHECK_STMT(ret, h, op) do { if ((ret) != SQL_SUCCESS && (ret) != SQL_SUCCESS_WITH_INFO) die(SQL_HANDLE_STMT, (h), (op)); } while (0)
|
||||
|
||||
static void exec_query(SQLHDBC dbc, const char *sql) {
|
||||
SQLHSTMT stmt;
|
||||
SQLAllocHandle(SQL_HANDLE_STMT, dbc, &stmt);
|
||||
SQLRETURN ret = SQLExecDirect(stmt, (SQLCHAR *)sql, SQL_NTS);
|
||||
CHECK_STMT(ret, stmt, sql);
|
||||
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
|
||||
}
|
||||
|
||||
static long long fetch_count(SQLHDBC dbc, const char *sql) {
|
||||
SQLHSTMT stmt;
|
||||
SQLCHAR buf[32];
|
||||
SQLLEN indicator;
|
||||
SQLAllocHandle(SQL_HANDLE_STMT, dbc, &stmt);
|
||||
SQLRETURN ret = SQLExecDirect(stmt, (SQLCHAR *)sql, SQL_NTS);
|
||||
CHECK_STMT(ret, stmt, sql);
|
||||
SQLFetch(stmt);
|
||||
SQLGetData(stmt, 1, SQL_C_CHAR, buf, sizeof(buf), &indicator);
|
||||
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
|
||||
return atoll((char *)buf);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
if (argc < 3) {
|
||||
fprintf(stderr, "Usage: %s <user> <port>\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
const char *user = argv[1];
|
||||
const char *port = argv[2];
|
||||
|
||||
SQLHENV env;
|
||||
SQLHDBC dbc;
|
||||
SQLRETURN ret;
|
||||
|
||||
SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &env);
|
||||
SQLSetEnvAttr(env, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, 0);
|
||||
SQLAllocHandle(SQL_HANDLE_DBC, env, &dbc);
|
||||
|
||||
char connStr[512];
|
||||
snprintf(connStr, sizeof(connStr),
|
||||
"Driver={PostgreSQL Unicode};Server=localhost;Port=%s;Database=postgres;UID=%s;PWD=password;",
|
||||
port, user);
|
||||
|
||||
SQLCHAR outStr[1024];
|
||||
SQLSMALLINT outLen;
|
||||
ret = SQLDriverConnect(dbc, NULL, (SQLCHAR *)connStr, SQL_NTS,
|
||||
outStr, sizeof(outStr), &outLen, SQL_DRIVER_NOPROMPT);
|
||||
CHECK_DBC(ret, dbc, "SQLDriverConnect");
|
||||
|
||||
// SELECT from test_table (set up by bats setup())
|
||||
{
|
||||
SQLHSTMT stmt;
|
||||
SQLINTEGER pk;
|
||||
SQLLEN indicator;
|
||||
SQLAllocHandle(SQL_HANDLE_STMT, dbc, &stmt);
|
||||
ret = SQLExecDirect(stmt, (SQLCHAR *)"SELECT pk FROM test_table LIMIT 1", SQL_NTS);
|
||||
CHECK_STMT(ret, stmt, "SELECT pk FROM test_table LIMIT 1");
|
||||
SQLFetch(stmt);
|
||||
SQLGetData(stmt, 1, SQL_C_SLONG, &pk, sizeof(pk), &indicator);
|
||||
if (pk != 1) {
|
||||
fprintf(stderr, "expected pk=1, got %d\n", pk);
|
||||
return 1;
|
||||
}
|
||||
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
|
||||
}
|
||||
|
||||
exec_query(dbc, "INSERT INTO test_table VALUES (2)");
|
||||
|
||||
long long count = fetch_count(dbc, "SELECT COUNT(*) FROM test_table");
|
||||
if (count != 2) {
|
||||
fprintf(stderr, "expected count=2, got %lld\n", count);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Dolt workflow: create table, insert, commit, branch, insert, commit, merge
|
||||
const char *dolt_queries[] = {
|
||||
"DROP TABLE IF EXISTS test",
|
||||
"CREATE TABLE test (pk int, value int, PRIMARY KEY(pk))",
|
||||
"INSERT INTO test (pk, value) VALUES (0, 0)",
|
||||
"SELECT dolt_add('-A')",
|
||||
"SELECT dolt_commit('-m', 'added table test')",
|
||||
"SELECT dolt_checkout('-b', 'mybranch')",
|
||||
"INSERT INTO test VALUES (1, 1)",
|
||||
"SELECT dolt_commit('-a', '-m', 'updated test')",
|
||||
"SELECT dolt_checkout('main')",
|
||||
"SELECT dolt_merge('mybranch')",
|
||||
NULL
|
||||
};
|
||||
for (int i = 0; dolt_queries[i]; i++)
|
||||
exec_query(dbc, dolt_queries[i]);
|
||||
|
||||
count = fetch_count(dbc, "SELECT COUNT(*) FROM dolt_log");
|
||||
if (count != 4) {
|
||||
fprintf(stderr, "expected 4 dolt_log entries, got %lld\n", count);
|
||||
return 1;
|
||||
}
|
||||
|
||||
count = fetch_count(dbc, "SELECT COUNT(*) FROM test");
|
||||
if (count != 2) {
|
||||
fprintf(stderr, "expected 2 rows in test, got %lld\n", count);
|
||||
return 1;
|
||||
}
|
||||
|
||||
SQLDisconnect(dbc);
|
||||
SQLFreeHandle(SQL_HANDLE_DBC, dbc);
|
||||
SQLFreeHandle(SQL_HANDLE_ENV, env);
|
||||
|
||||
printf("psqlODBC test passed\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
use strict;
|
||||
|
||||
use DBI;
|
||||
|
||||
my $QUERY_RESPONSE = [
|
||||
{ "create table test (pk int, value int, d1 decimal(9, 3), f1 float, c1 char(10), t1 text, primary key(pk))" => '0E0' },
|
||||
{ "insert into test (pk, value, d1, f1, c1, t1) values (0,0,0.0,0.0,'abc','a1')" => 1 },
|
||||
{ "select * from test" => 1 },
|
||||
{"select dolt_add('-A');" => 1 },
|
||||
{"select dolt_commit('-m', 'my commit')" => 1 },
|
||||
{"select dolt_checkout('-b', 'mybranch')" => 1 },
|
||||
{"insert into test (pk, value, d1, f1, c1, t1) values (10,10, 123456.789, 420.42,'example','some text')" => 1 },
|
||||
{"select dolt_commit('-a', '-m', 'my commit2')" => 1 },
|
||||
{"select dolt_checkout('main')" => 1 },
|
||||
{"select dolt_merge('mybranch')" => 1 },
|
||||
{"select COUNT(*) FROM dolt.log" => 1 },
|
||||
];
|
||||
|
||||
my $user = $ARGV[0];
|
||||
my $port = $ARGV[1];
|
||||
my $db = "postgres";
|
||||
|
||||
my $dsn = "DBI:Pg:database=$db;host=127.0.0.1;port=$port";
|
||||
# Connect to the database
|
||||
my $dbh = DBI->connect($dsn, $user, "password", { PrintError => 0, RaiseError => 1 });
|
||||
die "failed to connect to database:DBI->errstr()" unless($dbh);
|
||||
|
||||
foreach my $query_response ( @{$QUERY_RESPONSE} ) {
|
||||
my @query_keys = keys %{$query_response};
|
||||
my $query = $query_keys[0];
|
||||
my $exp_result = $query_response->{$query};
|
||||
|
||||
my $result = $dbh->do($query);
|
||||
if ( $result != $exp_result ) {
|
||||
print "QUERY: $query\n";
|
||||
print "EXPECTED: $exp_result\n";
|
||||
print "RESULT: $result\n";
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
# Define the SQL query
|
||||
my $query = "SELECT * FROM test WHERE pk = 10";
|
||||
|
||||
# Prepare the query
|
||||
my $sth = $dbh->prepare($query);
|
||||
|
||||
# Execute the query
|
||||
$sth->execute();
|
||||
|
||||
my @cols = ("pk", "value", "d1", "f1", "c1", "t1");
|
||||
my @expectedRowResults = ("10", "10", "123456.789", "420.42", "example ", "some text");
|
||||
|
||||
# Fetch and process the results
|
||||
while (my $row = $sth->fetchrow_hashref()) {
|
||||
if ($row) {
|
||||
# Process the row if it's defined
|
||||
# Access individual column values using hash dereferencing
|
||||
for my $i (0 .. $#cols) {
|
||||
my $val = $row->{$cols[$i]};
|
||||
# Comparing the strings using eq operator
|
||||
my $c = $val eq $expectedRowResults[$i];
|
||||
if ( $c == 0 ) {
|
||||
print "Expected: '$expectedRowResults[$i]'\n";
|
||||
print "Actual: '$val'\n";
|
||||
# Disconnect from the database
|
||||
$dbh->disconnect();
|
||||
exit 1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
print("no rows?");
|
||||
last; # Exit the loop when there are no more rows
|
||||
}
|
||||
}
|
||||
|
||||
# Disconnect from the database
|
||||
$dbh->disconnect();
|
||||
|
||||
exit 0;
|
||||
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
$user = $argv[1];
|
||||
$port = $argv[2];
|
||||
$db = 'postgres';
|
||||
|
||||
$conn = new PDO("pgsql:host=localhost;port={$port};dbname={$db}", $user, 'password');
|
||||
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
|
||||
$queries = [
|
||||
"create table test (pk int, value int, d1 decimal(9, 3), f1 float, c1 char(10), t1 text, primary key(pk))" => 0,
|
||||
"insert into test (pk, value, d1, f1, c1, t1) values (0,0,0.0,0.0,'abc','a1')" => 1,
|
||||
"select * from test" => 1,
|
||||
"select dolt_add('-A');" => 1,
|
||||
"select dolt_commit('-m', 'my commit')" => 1,
|
||||
"select dolt_checkout('-b', 'mybranch')" => 1,
|
||||
"insert into test (pk, value, d1, f1, c1, t1) values (1,1, 123456.789, 420.42,'example','some text')" => 1,
|
||||
"select dolt_commit('-a', '-m', 'my commit2')" => 1,
|
||||
"select dolt_checkout('main')" => 1,
|
||||
"select dolt_merge('mybranch')" => 1,
|
||||
"select COUNT(*) FROM dolt.log" => 1
|
||||
];
|
||||
|
||||
foreach ($queries as $query => $expected) {
|
||||
$result = $conn->query($query);
|
||||
if ($result->rowCount() != $expected) {
|
||||
echo "LENGTH: {$result->rowCount()}\n";
|
||||
echo "QUERY: {$query}\n";
|
||||
echo "EXPECTED: {$expected}\n";
|
||||
echo "RESULT: {$result}";
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
$result = $conn->query("SELECT * FROM test WHERE pk = 1");
|
||||
assert(1 == $result->rowCount());
|
||||
while($row = $result->fetch(PDO::FETCH_ASSOC)) {
|
||||
assert(1 == $row['pk']);
|
||||
assert(1 == $row['value']);
|
||||
assert(123456.789 == $row['d1']);
|
||||
assert(420.42 == $row['f1']);
|
||||
assert("example " == $row['c1']);
|
||||
assert("some text" == $row['t1']);
|
||||
}
|
||||
|
||||
exit(0)
|
||||
?>
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
$user = $argv[1];
|
||||
$port = $argv[2];
|
||||
$db = 'postgres';
|
||||
|
||||
$conn = pg_connect("host = localhost port = $port dbname = $db user = $user password = 'password'")
|
||||
or die('Could not connect: ' . pg_result_error());
|
||||
|
||||
$queries = [
|
||||
"create table test (pk int, value int, d1 decimal(9, 3), f1 float, c1 char(10), t1 text, primary key(pk))" => 0,
|
||||
"insert into test (pk, value, d1, f1, c1, t1) values (0,0,0.0,0.0,'abc','a1')" => 0,
|
||||
"select * from test" => 1,
|
||||
"select dolt_add('-A');" => 1,
|
||||
"select dolt_commit('-m', 'my commit')" => 1,
|
||||
"select dolt_checkout('-b', 'mybranch')" => 1,
|
||||
"insert into test (pk, value, d1, f1, c1, t1) values (1,1, 123456.789, 420.42,'example','some text')" => 0,
|
||||
"select dolt_commit('-a', '-m', 'my commit2')" => 1,
|
||||
"select dolt_checkout('main')" => 1,
|
||||
"select dolt_merge('mybranch')" => 1,
|
||||
"select COUNT(*) FROM dolt.log" => 1
|
||||
];
|
||||
|
||||
foreach ($queries as $query => $expected) {
|
||||
$result = pg_query($conn, $query);
|
||||
if (is_bool($result)) {
|
||||
if (!$result) {
|
||||
echo "LENGTH: {pg_num_rows($result)}\n";
|
||||
echo "QUERY: {$query}\n";
|
||||
echo "EXPECTED: {$expected}\n";
|
||||
echo "RESULT: {$result}";
|
||||
exit(1);
|
||||
}
|
||||
} else if (pg_num_rows($result) != $expected) {
|
||||
echo "LENGTH: {pg_num_rows($result)}\n";
|
||||
echo "QUERY: {$query}\n";
|
||||
echo "EXPECTED: {$expected}\n";
|
||||
echo "RESULT: {$result}";
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
$result = pg_query($conn, "SELECT * FROM test WHERE pk = 1");
|
||||
assert(1 == pg_num_rows($result));
|
||||
while($row = pg_fetch_assoc($result)) {
|
||||
assert(1 == $row['pk']);
|
||||
assert(1 == $row['value']);
|
||||
assert(123456.789 == $row['d1']);
|
||||
assert(420.42 == $row['f1']);
|
||||
assert("example " == $row['c1']);
|
||||
assert("some text" == $row['t1']);
|
||||
}
|
||||
|
||||
pg_close($conn);
|
||||
|
||||
exit(0)
|
||||
?>
|
||||
@@ -0,0 +1,4 @@
|
||||
#!/bin/sh
|
||||
|
||||
echo "Running postgres-client-tests:"
|
||||
bats /postgres-client-tests/postgres-client-tests.bats
|
||||
+163
@@ -0,0 +1,163 @@
|
||||
#!/usr/bin/env bats
|
||||
load $BATS_TEST_DIRNAME/helpers.bash
|
||||
|
||||
# PostgreSQL client tests are set up to test Doltgres as a PostgreSQL server and
|
||||
# standard PostgreSQL Clients in a wide array of languages.
|
||||
|
||||
setup() {
|
||||
setup_doltgres_repo
|
||||
|
||||
query_server -c "CREATE TABLE IF NOT EXISTS test_table(pk int)" -t
|
||||
query_server -c "DELETE FROM test_table" -t
|
||||
query_server -c "INSERT INTO test_table VALUES (1)" -t
|
||||
}
|
||||
|
||||
teardown() {
|
||||
cd ..
|
||||
teardown_doltgres_repo
|
||||
}
|
||||
|
||||
@test "postgres-connector-java client" {
|
||||
javac $BATS_TEST_DIRNAME/java/PostgresTest.java
|
||||
java -cp $BATS_TEST_DIRNAME/java:$BATS_TEST_DIRNAME/java/postgresql-42.7.3.jar PostgresTest $USER $PORT
|
||||
}
|
||||
|
||||
@test "r2dbc-postgresql client" {
|
||||
java -jar /build/bin/r2dbc/r2dbc-test.jar $USER $PORT
|
||||
}
|
||||
|
||||
@test "node postgres client" {
|
||||
node $BATS_TEST_DIRNAME/node/index.js $USER $PORT
|
||||
}
|
||||
|
||||
@test "knex node postgres client" {
|
||||
DOLTGRES_VERSION=$( doltgres --version | sed -nre 's/^[^0-9]*(([0-9]+\.)*[0-9]+).*/\1/p' )
|
||||
echo $DOLTGRES_VERSION
|
||||
node $BATS_TEST_DIRNAME/node/knex.js $USER $PORT $DOLTGRES_VERSION
|
||||
}
|
||||
|
||||
@test "node postgres client, workbench stability" {
|
||||
skip "Passes locally, fails on CI, investigating"
|
||||
DOLTGRES_VERSION=$( doltgres --version | sed -nre 's/^[^0-9]*(([0-9]+\.)*[0-9]+).*/\1/p' )
|
||||
echo $DOLTGRES_VERSION
|
||||
node $BATS_TEST_DIRNAME/node/workbench.js $USER $PORT $DOLTGRES_VERSION $BATS_TEST_DIRNAME/node/testdata
|
||||
}
|
||||
|
||||
@test "perl DBI:Pg client" {
|
||||
perl $BATS_TEST_DIRNAME/perl/postgres-test.pl $USER $PORT
|
||||
}
|
||||
|
||||
@test "ruby pg test" {
|
||||
ruby $BATS_TEST_DIRNAME/ruby/pg-test.rb $USER $PORT
|
||||
}
|
||||
|
||||
@test "ruby Sequel client" {
|
||||
ruby $BATS_TEST_DIRNAME/ruby/sequel-test.rb $USER $PORT
|
||||
}
|
||||
|
||||
@test "ruby ActiveRecord client" {
|
||||
ruby $BATS_TEST_DIRNAME/ruby/activerecord-test.rb $USER $PORT
|
||||
}
|
||||
|
||||
@test "php pg_connect client" {
|
||||
cd $BATS_TEST_DIRNAME/php
|
||||
php pg_connect_test.php $USER $PORT
|
||||
}
|
||||
|
||||
@test "php pdo pgsql client" {
|
||||
cd $BATS_TEST_DIRNAME/php
|
||||
php pdo_connector_test.php $USER $PORT
|
||||
}
|
||||
|
||||
@test "c postgres: libpq connector" {
|
||||
(cd $BATS_TEST_DIRNAME/c; make clean; make)
|
||||
$BATS_TEST_DIRNAME/c/postgres-c-connector-test $USER $PORT
|
||||
}
|
||||
|
||||
@test "c++ libpqxx client" {
|
||||
cd $BATS_TEST_DIRNAME/cpp
|
||||
make
|
||||
./libpqxx-test $USER $PORT
|
||||
}
|
||||
|
||||
@test "psqlODBC client" {
|
||||
cd $BATS_TEST_DIRNAME/odbc
|
||||
make
|
||||
./psqlodbc-test $USER $PORT
|
||||
}
|
||||
|
||||
@test "python postgres: psycopg2 client" {
|
||||
cd $BATS_TEST_DIRNAME/python
|
||||
python3 psycopg2_test.py $USER $PORT
|
||||
}
|
||||
|
||||
@test "python postgres: sqlalcchemy client" {
|
||||
cd $BATS_TEST_DIRNAME/python
|
||||
python3 sqlalchemy-test.py $USER $PORT
|
||||
}
|
||||
|
||||
@test "Drizzle ORM smoke test" {
|
||||
# the schema should be empty
|
||||
query_server -c "DROP TABLE IF EXISTS test_table" -t
|
||||
|
||||
cd $BATS_TEST_DIRNAME/drizzle
|
||||
|
||||
# Construct the string and append it to the .env file
|
||||
touch .env
|
||||
echo "DATABASE_URL=postgres://$USER:password@localhost:$PORT/postgres" >> .env
|
||||
|
||||
npm i drizzle-orm pg dotenv
|
||||
npm i -D drizzle-kit tsx @types/pg
|
||||
npx drizzle-kit push
|
||||
|
||||
# we can check if 'components table was created'
|
||||
query_server -c "SELECT * FROM users" -t
|
||||
run query_server -c "SELECT * FROM users" -t
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
# this inserts and updates row and check its updated result
|
||||
npx tsx src/index.ts
|
||||
}
|
||||
|
||||
@test "R RPostgres client" {
|
||||
Rscript $BATS_TEST_DIRNAME/r/rpostgres-test.r $USER $PORT
|
||||
}
|
||||
|
||||
@test "R RPostgreSQL client" {
|
||||
Rscript $BATS_TEST_DIRNAME/r/rpostgresql-test.r $USER $PORT
|
||||
}
|
||||
|
||||
@test "rust sqlx" {
|
||||
/build/bin/rust/sqlx_exists_demo $USER $PORT
|
||||
}
|
||||
|
||||
@test "go pgx client" {
|
||||
/build/bin/go/pgx-test $USER $PORT
|
||||
}
|
||||
|
||||
@test "go lib/pq client" {
|
||||
/build/bin/go/libpq-test $USER $PORT
|
||||
}
|
||||
|
||||
@test "dotnet Npgsql client" {
|
||||
/build/bin/dotnet/npgsql-test $USER $PORT
|
||||
}
|
||||
|
||||
@test "elixir postgrex client" {
|
||||
skip "fails from https://github.com/dolthub/doltgresql/issues/2859"
|
||||
/build/bin/elixir/postgrex-test $USER $PORT
|
||||
}
|
||||
|
||||
@test "swift postgresnio client" {
|
||||
/build/bin/swift/postgresnio-test $USER $PORT
|
||||
}
|
||||
|
||||
@test "libaprutil apr_dbd client" {
|
||||
(cd $BATS_TEST_DIRNAME/c; make)
|
||||
$BATS_TEST_DIRNAME/c/libaprutil-test $USER $PORT
|
||||
}
|
||||
|
||||
@test "libdbi pgsql client" {
|
||||
(cd $BATS_TEST_DIRNAME/c; make)
|
||||
$BATS_TEST_DIRNAME/c/libdbi-test $USER $PORT
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
import sys
|
||||
import traceback
|
||||
import psycopg2
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Query list (kept at top for consistency with other tests)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
TEST_QUERIES = [
|
||||
"DROP TABLE IF EXISTS test",
|
||||
"create table test (pk int, value int, d1 decimal(9, 3), f1 float, c1 char(10), t1 text, primary key(pk))",
|
||||
"select * from test",
|
||||
"insert into test (pk, value, d1, f1, c1, t1) values (0,0,0.0,0.0,'abc','a1')",
|
||||
"select * from test",
|
||||
"select dolt_add('-A');",
|
||||
"select dolt_commit('-m', 'my commit')",
|
||||
"select COUNT(*) FROM dolt.log",
|
||||
"select dolt_checkout('-b', 'mybranch')",
|
||||
"insert into test (pk, value, d1, f1, c1, t1) values (10,10, 123456.789, 420.42,'example','some text')",
|
||||
"select dolt_commit('-a', '-m', 'my commit2')",
|
||||
"select dolt_checkout('main')",
|
||||
"select dolt_merge('mybranch')",
|
||||
"select COUNT(*) FROM dolt.log",
|
||||
]
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def env(name, default=None):
|
||||
return os.getenv(name, default)
|
||||
|
||||
|
||||
def connect(user: str, port: int):
|
||||
conn = psycopg2.connect(
|
||||
host=env("PGHOST", "localhost"),
|
||||
port=port,
|
||||
dbname="postgres",
|
||||
user=user,
|
||||
password=env("PGPASSWORD", "password"),
|
||||
connect_timeout=int(env("PGCONNECT_TIMEOUT", "10")),
|
||||
sslmode=env("PGSSLMODE"),
|
||||
)
|
||||
conn.autocommit = True
|
||||
return conn
|
||||
|
||||
|
||||
def run(cur, q):
|
||||
print(f"SQL> {q}", flush=True)
|
||||
cur.execute(q)
|
||||
if cur.description is not None:
|
||||
cur.fetchall() # drain result set
|
||||
|
||||
# load_test creates a table with |n_rows| and asserts that all rows are correctly returned.
|
||||
def load_test(cur, n_rows=1000):
|
||||
print("\n=== Part 1: Load test ===", flush=True)
|
||||
|
||||
rows = max(1000, int(n_rows))
|
||||
|
||||
run(cur, "DROP TABLE IF EXISTS load_test")
|
||||
run(cur, "CREATE TABLE load_test (id INT PRIMARY KEY, val INT NOT NULL)")
|
||||
|
||||
data = [(i, i * 10) for i in range(rows)]
|
||||
cur.executemany(
|
||||
"INSERT INTO load_test (id, val) VALUES (%s, %s)",
|
||||
data,
|
||||
)
|
||||
|
||||
cur.execute("SELECT COUNT(*) FROM load_test")
|
||||
cnt = cur.fetchone()[0]
|
||||
if cnt != rows:
|
||||
raise AssertionError(f"COUNT(*) mismatch: expected {rows}, got {cnt}")
|
||||
|
||||
cur.execute("SELECT id FROM load_test ORDER BY id")
|
||||
got = cur.fetchall()
|
||||
if len(got) != rows:
|
||||
raise AssertionError(f"fetchall mismatch: expected {rows}, got {len(got)}")
|
||||
|
||||
print(f"Inserted and selected {rows} rows OK.", flush=True)
|
||||
|
||||
|
||||
def compliance_test(cur):
|
||||
print("\n=== Part 2: Test Queries ===", flush=True)
|
||||
for q in TEST_QUERIES:
|
||||
run(cur, q)
|
||||
print("Compliance queries executed OK.", flush=True)
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) != 3:
|
||||
print("Usage: python3 psycopg2_test.py <user> <port>")
|
||||
return 2
|
||||
|
||||
user = sys.argv[1]
|
||||
port = int(sys.argv[2])
|
||||
load_rows = int(env("LOAD_ROWS", "1000"))
|
||||
|
||||
try:
|
||||
with connect(user, port) as conn:
|
||||
with conn.cursor() as cur:
|
||||
load_test(cur, load_rows)
|
||||
compliance_test(cur)
|
||||
|
||||
print("\n✅ All tests passed.", flush=True)
|
||||
return 0
|
||||
|
||||
except Exception as e:
|
||||
print("\n❌ Test failed.", flush=True)
|
||||
print(f"Error: {e}", flush=True)
|
||||
traceback.print_exc()
|
||||
return 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -0,0 +1,67 @@
|
||||
import sqlalchemy
|
||||
import psycopg2
|
||||
|
||||
from sqlalchemy.engine import Engine
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy import text
|
||||
|
||||
import sys
|
||||
|
||||
QUERY_RESPONSE = [
|
||||
{"create table test (pk int, \"value\" int, primary key(pk))": []},
|
||||
{"describe test": [
|
||||
('pk', 'integer', 'NO', 'PRI', None, ''),
|
||||
('value', 'integer', 'YES', '', None, '')
|
||||
]},
|
||||
{"insert into test (pk, value) values (0,0)": ()},
|
||||
{"select * from test": [(0, 0)]},
|
||||
{"select dolt_add('-A');": [(['0'],)]},
|
||||
{"select dolt_commit('-m', 'my commit')": [('',)]},
|
||||
{"select COUNT(*) FROM dolt_log": [(3,)]},
|
||||
{"select dolt_checkout('-b', 'mybranch')": [(['0', "Switched to branch 'mybranch'"],)]},
|
||||
{"insert into test (pk, value) values (1,1)": []},
|
||||
{"select dolt_commit('-a', '-m', 'my commit2')": [('',)]},
|
||||
{"select dolt_checkout('main')": [(['0', "Switched to branch 'main'"],)]},
|
||||
{"select dolt_merge('mybranch')": [('',1,0,)]},
|
||||
{"select COUNT(*) FROM dolt_log": [(4,)]},
|
||||
]
|
||||
|
||||
|
||||
def main():
|
||||
user = sys.argv[1]
|
||||
port = int(sys.argv[2])
|
||||
|
||||
conn_string_base = "postgresql+psycopg2://"
|
||||
|
||||
engine = create_engine(conn_string_base +
|
||||
"{user}:password@127.0.0.1:{port}/postgres".format(user=user,
|
||||
port=port)
|
||||
)
|
||||
|
||||
with engine.connect() as con:
|
||||
for query_response in QUERY_RESPONSE:
|
||||
query = list(query_response.keys())[0]
|
||||
exp_results = query_response[query]
|
||||
|
||||
result_proxy = con.execute(text(query))
|
||||
|
||||
try:
|
||||
results = result_proxy.fetchall()
|
||||
if (results != exp_results) and ("dolt_commit" not in query) and ("dolt_merge" not in query):
|
||||
print("Query:")
|
||||
print(query)
|
||||
print("Expected:")
|
||||
print(exp_results)
|
||||
print("Received:")
|
||||
print(results)
|
||||
sys.exit(1)
|
||||
# You can't call fetchall on an insert
|
||||
# so we'll just ignore the exception
|
||||
except sqlalchemy.exc.ResourceClosedError:
|
||||
pass
|
||||
|
||||
con.close()
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
main()
|
||||
@@ -0,0 +1,79 @@
|
||||
library(RPostgres)
|
||||
library(DBI)
|
||||
|
||||
args = commandArgs(trailingOnly=TRUE)
|
||||
|
||||
user = args[1]
|
||||
port = strtoi(args[2])
|
||||
|
||||
conn = dbConnect(RPostgres::Postgres(),
|
||||
host="localhost",
|
||||
port=port,
|
||||
user=user,
|
||||
password="password",
|
||||
dbname="postgres")
|
||||
|
||||
# check standard queries
|
||||
queries = list(
|
||||
"DROP TABLE IF EXISTS test",
|
||||
"create table test (pk int, value int, primary key(pk))",
|
||||
"select * from test",
|
||||
"insert into test (pk, value) values (0,0)",
|
||||
"select * from test")
|
||||
|
||||
responses = list(
|
||||
NULL,
|
||||
NULL,
|
||||
data.frame(pk = integer(0), value = integer(0), stringsAsFactors = FALSE),
|
||||
NULL,
|
||||
data.frame(pk = c(as.integer(0)), value = c(as.integer(0)), stringsAsFactors = FALSE))
|
||||
|
||||
for (i in 1:length(queries)) {
|
||||
q = queries[[i]]
|
||||
want = responses[[i]]
|
||||
if (!is.null(want)) {
|
||||
got <- dbGetQuery(conn, q)
|
||||
if (length(want) == length(got)) {
|
||||
for (j in 1:length(want)) {
|
||||
if (!identical(want[[j]], got[[j]])) {
|
||||
print(q)
|
||||
print(c("want:", want[[j]], "type: ", typeof(want[[j]])))
|
||||
print(c("got:", got[[j]], "type: ", typeof(got[[j]])))
|
||||
quit("no", 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
invisible(dbExecute(conn, q))
|
||||
}
|
||||
}
|
||||
|
||||
dolt_queries = list(
|
||||
"select dolt_add('-A')",
|
||||
"select dolt_commit('-m', 'my commit')",
|
||||
"select dolt_checkout('-b', 'mybranch')",
|
||||
"insert into test (pk, value) values (1,1)",
|
||||
"select dolt_commit('-a', '-m', 'my commit2')",
|
||||
"select dolt_checkout('main')",
|
||||
"select dolt_merge('mybranch')")
|
||||
|
||||
for (i in 1:length(dolt_queries)) {
|
||||
q = dolt_queries[[i]]
|
||||
if (startsWith(trimws(tolower(q)), "select")) {
|
||||
dbGetQuery(conn, q)
|
||||
} else {
|
||||
invisible(dbExecute(conn, q))
|
||||
}
|
||||
}
|
||||
|
||||
count <- dbGetQuery(conn, "select COUNT(*) as c from dolt.log")
|
||||
want <- data.frame(c = c(4))
|
||||
ret <- all.equal(count, want)
|
||||
if (!isTRUE(ret)) {
|
||||
print("Number of commits is incorrect")
|
||||
print(count)
|
||||
quit("no", 1)
|
||||
}
|
||||
|
||||
dbDisconnect(conn)
|
||||
print("RPostgres test passed")
|
||||
@@ -0,0 +1,80 @@
|
||||
library(RPostgreSQL)
|
||||
library(DBI)
|
||||
|
||||
args = commandArgs(trailingOnly=TRUE)
|
||||
|
||||
user = args[1]
|
||||
port = strtoi(args[2])
|
||||
|
||||
drv = dbDriver("PostgreSQL")
|
||||
conn = dbConnect(drv,
|
||||
host="localhost",
|
||||
port=port,
|
||||
user=user,
|
||||
password="password",
|
||||
dbname="postgres")
|
||||
|
||||
# check standard queries
|
||||
queries = list(
|
||||
"DROP TABLE IF EXISTS test",
|
||||
"create table test (pk int, value int, primary key(pk))",
|
||||
"select * from test",
|
||||
"insert into test (pk, value) values (0,0)",
|
||||
"select * from test")
|
||||
|
||||
responses = list(
|
||||
NULL,
|
||||
NULL,
|
||||
data.frame(pk = integer(0), value = integer(0), stringsAsFactors = FALSE),
|
||||
NULL,
|
||||
data.frame(pk = c(as.integer(0)), value = c(as.integer(0)), stringsAsFactors = FALSE))
|
||||
|
||||
for (i in 1:length(queries)) {
|
||||
q = queries[[i]]
|
||||
want = responses[[i]]
|
||||
if (!is.null(want)) {
|
||||
got <- dbGetQuery(conn, q)
|
||||
if (length(want) == length(got)) {
|
||||
for (j in 1:length(want)) {
|
||||
if (!identical(want[[j]], got[[j]])) {
|
||||
print(q)
|
||||
print(c("want:", want[[j]], "type: ", typeof(want[[j]])))
|
||||
print(c("got:", got[[j]], "type: ", typeof(got[[j]])))
|
||||
quit("no", 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
invisible(dbExecute(conn, q))
|
||||
}
|
||||
}
|
||||
|
||||
dolt_queries = list(
|
||||
"select dolt_add('-A')",
|
||||
"select dolt_commit('-m', 'my commit')",
|
||||
"select dolt_checkout('-b', 'mybranch')",
|
||||
"insert into test (pk, value) values (1,1)",
|
||||
"select dolt_commit('-a', '-m', 'my commit2')",
|
||||
"select dolt_checkout('main')",
|
||||
"select dolt_merge('mybranch')")
|
||||
|
||||
for (i in 1:length(dolt_queries)) {
|
||||
q = dolt_queries[[i]]
|
||||
if (startsWith(trimws(tolower(q)), "select")) {
|
||||
dbGetQuery(conn, q)
|
||||
} else {
|
||||
invisible(dbExecute(conn, q))
|
||||
}
|
||||
}
|
||||
|
||||
count <- dbGetQuery(conn, "select COUNT(*) as c from dolt.log")
|
||||
want <- data.frame(c = c(4L))
|
||||
ret <- all.equal(count, want)
|
||||
if (!isTRUE(ret)) {
|
||||
print("Number of commits is incorrect")
|
||||
print(count)
|
||||
quit("no", 1)
|
||||
}
|
||||
|
||||
dbDisconnect(conn)
|
||||
print("RPostgreSQL test passed")
|
||||
@@ -0,0 +1,53 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>com.dolthub</groupId>
|
||||
<artifactId>r2dbc-test</artifactId>
|
||||
<version>1.0</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.postgresql</groupId>
|
||||
<artifactId>r2dbc-postgresql</artifactId>
|
||||
<version>1.0.7.RELEASE</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-nop</artifactId>
|
||||
<version>2.0.16</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.5.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals><goal>shade</goal></goals>
|
||||
<configuration>
|
||||
<transformers>
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
||||
<mainClass>R2dbcTest</mainClass>
|
||||
</transformer>
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
|
||||
</transformers>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -0,0 +1,85 @@
|
||||
import io.r2dbc.postgresql.PostgresqlConnectionConfiguration;
|
||||
import io.r2dbc.postgresql.PostgresqlConnectionFactory;
|
||||
import io.r2dbc.postgresql.client.SSLMode;
|
||||
import io.r2dbc.spi.Connection;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
public class R2dbcTest {
|
||||
|
||||
public static void main(String[] args) {
|
||||
String user = args[0];
|
||||
int port = Integer.parseInt(args[1]);
|
||||
|
||||
PostgresqlConnectionFactory factory = new PostgresqlConnectionFactory(
|
||||
PostgresqlConnectionConfiguration.builder()
|
||||
.host("localhost")
|
||||
.port(port)
|
||||
.database("postgres")
|
||||
.username(user)
|
||||
.password("password")
|
||||
.sslMode(SSLMode.DISABLE)
|
||||
.build()
|
||||
);
|
||||
|
||||
try {
|
||||
Mono.usingWhen(
|
||||
factory.create(),
|
||||
R2dbcTest::runTests,
|
||||
Connection::close
|
||||
).block();
|
||||
System.out.println("r2dbc test passed");
|
||||
} catch (Exception e) {
|
||||
System.err.println("Error: " + e.getMessage());
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Execute a statement and discard all results (works for both DML and SELECT).
|
||||
static Mono<Void> exec(Connection conn, String sql) {
|
||||
return Flux.from(conn.createStatement(sql).execute())
|
||||
.flatMap(r -> Flux.from(r.map((row, meta) -> 0)))
|
||||
.then();
|
||||
}
|
||||
|
||||
static <T> Mono<T> queryOne(Connection conn, String sql, Class<T> type) {
|
||||
return Mono.from(conn.createStatement(sql).execute())
|
||||
.flatMap(r -> Mono.from(r.map((row, meta) -> row.get(0, type))));
|
||||
}
|
||||
|
||||
static Mono<Void> runTests(Connection conn) {
|
||||
return queryOne(conn, "SELECT pk FROM test_table LIMIT 1", Integer.class)
|
||||
.doOnNext(pk -> {
|
||||
if (pk != 1) throw new RuntimeException("expected pk=1, got " + pk);
|
||||
})
|
||||
.then(exec(conn, "INSERT INTO test_table VALUES (2)"))
|
||||
.then(queryOne(conn, "SELECT COUNT(*) FROM test_table", Long.class))
|
||||
.doOnNext(n -> {
|
||||
if (n.longValue() != 2L)
|
||||
throw new RuntimeException("expected count=2, got " + n);
|
||||
})
|
||||
.thenMany(Flux.fromArray(new String[]{
|
||||
"DROP TABLE IF EXISTS test",
|
||||
"CREATE TABLE test (pk int, value int, PRIMARY KEY(pk))",
|
||||
"INSERT INTO test (pk, value) VALUES (0, 0)",
|
||||
"SELECT dolt_add('-A')",
|
||||
"SELECT dolt_commit('-m', 'added table test')",
|
||||
"SELECT dolt_checkout('-b', 'mybranch')",
|
||||
"INSERT INTO test VALUES (1, 1)",
|
||||
"SELECT dolt_commit('-a', '-m', 'updated test')",
|
||||
"SELECT dolt_checkout('main')",
|
||||
"SELECT dolt_merge('mybranch')",
|
||||
}).concatMap(sql -> exec(conn, sql)))
|
||||
.then(queryOne(conn, "SELECT COUNT(*) FROM dolt_log", Long.class))
|
||||
.doOnNext(n -> {
|
||||
if (n.longValue() != 4L)
|
||||
throw new RuntimeException("expected 4 dolt_log entries, got " + n);
|
||||
})
|
||||
.then(queryOne(conn, "SELECT COUNT(*) FROM test", Long.class))
|
||||
.doOnNext(n -> {
|
||||
if (n.longValue() != 2L)
|
||||
throw new RuntimeException("expected 2 rows in test, got " + n);
|
||||
})
|
||||
.then();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
source "https://rubygems.org"
|
||||
gem 'pg'
|
||||
gem 'test'
|
||||
gem 'sequel'
|
||||
gem 'activerecord'
|
||||
@@ -0,0 +1,52 @@
|
||||
#!/usr/bin/ruby
|
||||
|
||||
require 'active_record'
|
||||
|
||||
user = ARGV[0]
|
||||
port = ARGV[1].to_i
|
||||
|
||||
ActiveRecord::Base.establish_connection(
|
||||
adapter: 'postgresql',
|
||||
host: 'localhost',
|
||||
port: port,
|
||||
database: 'postgres',
|
||||
username: user,
|
||||
password: 'password'
|
||||
)
|
||||
ActiveRecord::Base.logger = nil
|
||||
|
||||
conn = ActiveRecord::Base.connection
|
||||
|
||||
# SELECT pk from test_table (set up by bats setup())
|
||||
pk = conn.execute("SELECT pk FROM test_table LIMIT 1").first["pk"].to_i
|
||||
raise "expected pk=1, got #{pk}" unless pk == 1
|
||||
|
||||
# INSERT
|
||||
conn.execute("INSERT INTO test_table VALUES (2)")
|
||||
|
||||
# COUNT
|
||||
count = conn.execute("SELECT COUNT(*) FROM test_table").first["count"].to_i
|
||||
raise "expected count=2, got #{count}" unless count == 2
|
||||
|
||||
# Dolt workflow
|
||||
[
|
||||
"DROP TABLE IF EXISTS test",
|
||||
"CREATE TABLE test (pk int, value int, PRIMARY KEY(pk))",
|
||||
"INSERT INTO test (pk, value) VALUES (0, 0)",
|
||||
"SELECT dolt_add('-A')",
|
||||
"SELECT dolt_commit('-m', 'added table test')",
|
||||
"SELECT dolt_checkout('-b', 'mybranch')",
|
||||
"INSERT INTO test VALUES (1, 1)",
|
||||
"SELECT dolt_commit('-a', '-m', 'updated test')",
|
||||
"SELECT dolt_checkout('main')",
|
||||
"SELECT dolt_merge('mybranch')",
|
||||
].each { |sql| conn.execute(sql) }
|
||||
|
||||
log_count = conn.execute("SELECT COUNT(*) FROM dolt_log").first["count"].to_i
|
||||
raise "expected 4 dolt_log entries, got #{log_count}" unless log_count == 4
|
||||
|
||||
test_count = conn.execute("SELECT COUNT(*) FROM test").first["count"].to_i
|
||||
raise "expected 2 rows in test, got #{test_count}" unless test_count == 2
|
||||
|
||||
ActiveRecord::Base.connection_pool.disconnect!
|
||||
puts "ActiveRecord test passed"
|
||||
@@ -0,0 +1,49 @@
|
||||
#!/usr/bin/ruby
|
||||
|
||||
require 'pg'
|
||||
require 'test/unit'
|
||||
|
||||
extend Test::Unit::Assertions
|
||||
|
||||
user = ARGV[0]
|
||||
port = ARGV[1]
|
||||
db = "postgres"
|
||||
|
||||
queries = [
|
||||
"create table test (pk int, value int, d1 decimal(9, 3), f1 float, c1 char(10), t1 text, primary key(pk))",
|
||||
"select * from test",
|
||||
"insert into test (pk, value, d1, f1, c1, t1) values (0,0,0.0,0.0,'abc','a1')",
|
||||
"select * from test",
|
||||
"select dolt_add('-A');",
|
||||
"select dolt_commit('-m', 'my commit')",
|
||||
"select COUNT(*) FROM dolt.log",
|
||||
"select dolt_checkout('-b', 'mybranch')",
|
||||
"insert into test (pk, value, d1, f1, c1, t1) values (1,1, 123456.789, 420.42,'example','some text')",
|
||||
"select dolt_commit('-a', '-m', 'my commit2')",
|
||||
"select dolt_checkout('main')",
|
||||
"select dolt_merge('mybranch')",
|
||||
"select COUNT(*) FROM dolt.log",
|
||||
]
|
||||
|
||||
# Smoke test the queries to make sure nothing blows up
|
||||
conn = PG::Connection.new(:host => "localhost", :user => user, :dbname => db, :port => port, :password => "password")
|
||||
queries.each do |query|
|
||||
res = conn.query(query)
|
||||
end
|
||||
|
||||
# Then make sure we can read some data back
|
||||
res = conn.query("SELECT * from test where pk = 1;")
|
||||
rowCount = 0
|
||||
res.each do |row|
|
||||
rowCount += 1
|
||||
assert_equal 1, row["pk"].to_i
|
||||
assert_equal 1, row["value"].to_i
|
||||
assert_equal 123456.789, row["d1"].to_f
|
||||
assert_equal 420.42, row["f1"].to_f
|
||||
assert_equal "example ", row["c1"]
|
||||
assert_equal "some text", row["t1"]
|
||||
end
|
||||
assert_equal 1, rowCount
|
||||
|
||||
conn.close()
|
||||
exit(0)
|
||||
@@ -0,0 +1,49 @@
|
||||
#!/usr/bin/ruby
|
||||
|
||||
require 'sequel'
|
||||
|
||||
user = ARGV[0]
|
||||
port = ARGV[1].to_i
|
||||
|
||||
DB = Sequel.connect(
|
||||
adapter: 'postgres',
|
||||
host: 'localhost',
|
||||
port: port,
|
||||
database: 'postgres',
|
||||
user: user,
|
||||
password: 'password'
|
||||
)
|
||||
|
||||
# SELECT pk from test_table (set up by bats setup())
|
||||
pk = DB["SELECT pk FROM test_table LIMIT 1"].first[:pk]
|
||||
raise "expected pk=1, got #{pk}" unless pk == 1
|
||||
|
||||
# INSERT
|
||||
DB.run("INSERT INTO test_table VALUES (2)")
|
||||
|
||||
# COUNT
|
||||
count = DB["SELECT COUNT(*) FROM test_table"].first[:count]
|
||||
raise "expected count=2, got #{count}" unless count == 2
|
||||
|
||||
# Dolt workflow
|
||||
[
|
||||
"DROP TABLE IF EXISTS test",
|
||||
"CREATE TABLE test (pk int, value int, PRIMARY KEY(pk))",
|
||||
"INSERT INTO test (pk, value) VALUES (0, 0)",
|
||||
"SELECT dolt_add('-A')",
|
||||
"SELECT dolt_commit('-m', 'added table test')",
|
||||
"SELECT dolt_checkout('-b', 'mybranch')",
|
||||
"INSERT INTO test VALUES (1, 1)",
|
||||
"SELECT dolt_commit('-a', '-m', 'updated test')",
|
||||
"SELECT dolt_checkout('main')",
|
||||
"SELECT dolt_merge('mybranch')",
|
||||
].each { |sql| DB.run(sql) }
|
||||
|
||||
log_count = DB["SELECT COUNT(*) FROM dolt_log"].first[:count]
|
||||
raise "expected 4 dolt_log entries, got #{log_count}" unless log_count == 4
|
||||
|
||||
test_count = DB["SELECT COUNT(*) FROM test"].first[:count]
|
||||
raise "expected 2 rows in test, got #{test_count}" unless test_count == 2
|
||||
|
||||
DB.disconnect
|
||||
puts "Sequel test passed"
|
||||
+2276
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,8 @@
|
||||
[package]
|
||||
name = "sqlx_exists_demo"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
|
||||
sqlx = { version = "0.8", features = ["runtime-tokio", "postgres", "tls-native-tls", "uuid", "chrono"] }
|
||||
@@ -0,0 +1,75 @@
|
||||
use sqlx::postgres::PgPoolOptions;
|
||||
use sqlx::types::Uuid;
|
||||
use sqlx::types::chrono::Utc;
|
||||
use sqlx::types::chrono::DateTime;
|
||||
use sqlx::types::chrono::NaiveDate;
|
||||
|
||||
#[derive(sqlx::FromRow)]
|
||||
struct Event {
|
||||
id: sqlx::types::Uuid,
|
||||
created_at: DateTime<Utc>,
|
||||
event_date: Option<NaiveDate>,
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut args = std::env::args();
|
||||
let program = args.next().unwrap_or_else(|| "app".to_string());
|
||||
let user = args.next().ok_or_else(|| {
|
||||
format!("Usage: {program} <USER> <PORT>")
|
||||
})?;
|
||||
let port: u16 = args.next().ok_or_else(|| {
|
||||
format!("Usage: {program} <USER> <PORT>")
|
||||
})?.parse()?;
|
||||
let database_url = format!("postgresql://{user}:password@localhost:{port}/postgres");
|
||||
let pool = PgPoolOptions::new()
|
||||
.max_connections(5)
|
||||
.connect(&database_url)
|
||||
.await?;
|
||||
|
||||
let exists: bool = sqlx::query_scalar("SELECT EXISTS(SELECT 1 FROM pg_catalog.pg_tables WHERE tablename = $1);")
|
||||
.bind("test_table")
|
||||
.fetch_one(&pool)
|
||||
.await?;
|
||||
println!("exists={exists}");
|
||||
|
||||
sqlx::query("DROP TABLE IF EXISTS users, events;")
|
||||
.execute(&pool)
|
||||
.await?;
|
||||
|
||||
sqlx::query("CREATE TABLE users (id uuid default gen_random_uuid(), name text, email text);")
|
||||
.execute(&pool)
|
||||
.await?;
|
||||
|
||||
sqlx::query("INSERT INTO users (name, email) VALUES ($1, $2)")
|
||||
.bind("Alice")
|
||||
.bind("alice@example.com")
|
||||
.execute(&pool)
|
||||
.await?;
|
||||
|
||||
let some_uuid: Uuid = sqlx::query_scalar("SELECT id FROM users WHERE email = $1 LIMIT 1")
|
||||
.bind("alice@example.com")
|
||||
.fetch_one(&pool)
|
||||
.await?;
|
||||
|
||||
sqlx::query("UPDATE users SET name = $1 WHERE id = $2")
|
||||
.bind("Bob")
|
||||
.bind(some_uuid)
|
||||
.execute(&pool)
|
||||
.await?;
|
||||
|
||||
sqlx::query("CREATE TABLE events (id UUID PRIMARY KEY, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), event_date DATE);")
|
||||
.execute(&pool)
|
||||
.await?;
|
||||
|
||||
let some_id: Uuid = sqlx::query_scalar("INSERT INTO events (id, event_date) VALUES (gen_random_uuid(), '2026-02-12') RETURNING id;")
|
||||
.fetch_one(&pool)
|
||||
.await?;
|
||||
|
||||
let __event = sqlx::query_as::<_, Event>("SELECT * FROM events WHERE id = $1")
|
||||
.bind(some_id)
|
||||
.fetch_one(&pool)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
// swift-tools-version: 5.9
|
||||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
name: "postgresnio-test",
|
||||
dependencies: [
|
||||
.package(url: "https://github.com/vapor/postgres-nio.git", from: "1.19.0"),
|
||||
],
|
||||
targets: [
|
||||
.executableTarget(
|
||||
name: "postgresnio-test",
|
||||
dependencies: [
|
||||
.product(name: "PostgresNIO", package: "postgres-nio"),
|
||||
],
|
||||
path: "Sources"
|
||||
),
|
||||
]
|
||||
)
|
||||
@@ -0,0 +1,102 @@
|
||||
import Logging
|
||||
import NIOPosix
|
||||
import PostgresNIO
|
||||
|
||||
struct TestError: Error, CustomStringConvertible {
|
||||
let description: String
|
||||
init(_ msg: String) { description = msg }
|
||||
}
|
||||
|
||||
@main
|
||||
struct PostgresNIOTest {
|
||||
static func main() async {
|
||||
let args = CommandLine.arguments
|
||||
guard args.count >= 3 else {
|
||||
fputs("Usage: postgresnio-test <user> <port>\n", stderr)
|
||||
exit(1)
|
||||
}
|
||||
let user = args[1]
|
||||
let port = Int(args[2])!
|
||||
do {
|
||||
try await runTests(user: user, port: port)
|
||||
print("PostgresNIO test passed")
|
||||
} catch {
|
||||
fputs("Error: \(error)\n", stderr)
|
||||
exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// Execute a statement and drain any result rows.
|
||||
static func exec(_ conn: PostgresConnection, _ sql: String, logger: Logger) async throws {
|
||||
for try await _ in try await conn.query(PostgresQuery(unsafeSQL: sql), logger: logger) {}
|
||||
}
|
||||
|
||||
// Execute a single-row query and return the first row.
|
||||
static func queryRow(_ conn: PostgresConnection, _ sql: String, logger: Logger) async throws -> PostgresRow {
|
||||
for try await row in try await conn.query(PostgresQuery(unsafeSQL: sql), logger: logger) {
|
||||
return row
|
||||
}
|
||||
throw TestError("expected at least one row for: \(sql)")
|
||||
}
|
||||
|
||||
static func runTests(user: String, port: Int) async throws {
|
||||
let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1)
|
||||
defer { try! eventLoopGroup.syncShutdownGracefully() }
|
||||
|
||||
var logger = Logger(label: "postgres-test")
|
||||
logger.logLevel = .critical
|
||||
|
||||
let config = PostgresConnection.Configuration(
|
||||
host: "localhost",
|
||||
port: port,
|
||||
username: user,
|
||||
password: "password",
|
||||
database: "postgres",
|
||||
tls: .disable
|
||||
)
|
||||
let conn = try await PostgresConnection.connect(
|
||||
on: eventLoopGroup.next(),
|
||||
configuration: config,
|
||||
id: 1,
|
||||
logger: logger
|
||||
)
|
||||
defer { try! conn.close().wait() }
|
||||
|
||||
// SELECT pk from test_table (set up by bats setup())
|
||||
let pkRow = try await queryRow(conn, "SELECT pk FROM test_table LIMIT 1", logger: logger)
|
||||
let pk = try pkRow.decode(Int32.self, context: .default)
|
||||
guard pk == 1 else { throw TestError("expected pk=1, got \(pk)") }
|
||||
|
||||
// INSERT
|
||||
try await exec(conn, "INSERT INTO test_table VALUES (2)", logger: logger)
|
||||
|
||||
// COUNT
|
||||
let countRow = try await queryRow(conn, "SELECT COUNT(*) FROM test_table", logger: logger)
|
||||
let count = try countRow.decode(Int64.self, context: .default)
|
||||
guard count == 2 else { throw TestError("expected count=2, got \(count)") }
|
||||
|
||||
// Dolt workflow: create table, insert, commit, branch, insert, commit, merge
|
||||
for q in [
|
||||
"DROP TABLE IF EXISTS test",
|
||||
"CREATE TABLE test (pk int, value int, PRIMARY KEY(pk))",
|
||||
"INSERT INTO test (pk, value) VALUES (0, 0)",
|
||||
"SELECT dolt_add('-A')",
|
||||
"SELECT dolt_commit('-m', 'added table test')",
|
||||
"SELECT dolt_checkout('-b', 'mybranch')",
|
||||
"INSERT INTO test VALUES (1, 1)",
|
||||
"SELECT dolt_commit('-a', '-m', 'updated test')",
|
||||
"SELECT dolt_checkout('main')",
|
||||
"SELECT dolt_merge('mybranch')",
|
||||
] {
|
||||
try await exec(conn, q, logger: logger)
|
||||
}
|
||||
|
||||
let logRow = try await queryRow(conn, "SELECT COUNT(*) FROM dolt_log", logger: logger)
|
||||
let logCount = try logRow.decode(Int64.self, context: .default)
|
||||
guard logCount == 4 else { throw TestError("expected 4 dolt_log entries, got \(logCount)") }
|
||||
|
||||
let testRow = try await queryRow(conn, "SELECT COUNT(*) FROM test", logger: logger)
|
||||
let testCount = try testRow.decode(Int64.self, context: .default)
|
||||
guard testCount == 2 else { throw TestError("expected 2 rows in test, got \(testCount)") }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user