pgdog.git / summary / log / commit / refs
commit 80c7ec100a8d3e76aed1dde83db860f398ecfcd3
Author: Lev Kokotov <levkk@users.noreply.github.com>
Commit: GitHub <noreply@github.com>
Date: Fri Mar 28 04:43:05 2025 +0000
ruby integration tests (#63)
* ruby integration tests
* run in ci
* bundler
* build in release
.github/workflows/ci.yml | 7 +-
integration/common.sh | 6 +-
integration/python/run.sh | 24 +--
integration/ruby/Gemfile | 4 +
integration/ruby/Gemfile.lock | 226 ++++++++++++++++++++++++++
integration/ruby/pg_spec.rb | 88 ++++++++++
integration/ruby/rspec_helper.rb | 3 +
integration/ruby/run.sh | 18 ++
pgdog/src/frontend/prepared_statements/mod.rs | 3 +-
pgdog/src/net/messages/row_description.rs | 11 +-
10 files changed, 363 insertions(+), 27 deletions(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 371537d1..4ca690cb 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -65,14 +65,19 @@ jobs:
sudo -u postgres createdb $USER
bash integration/setup.sh
sudo apt update && sudo apt install -y python3-virtualenv
+ sudo gem install bundler
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
override: true
- uses: Swatinem/rust-cache@v2
+ with:
+ prefix-key: release
- name: Build PgDog
- run: cargo build
+ run: cargo build --release
- name: Python
run: bash integration/python/run.sh
- name: pgbench
run: bash integration/pgbench/run.sh
+ - name: Ruby
+ run: bash integration/ruby/run.sh
diff --git a/integration/common.sh b/integration/common.sh
index d9bb5151..7eef2094 100644
--- a/integration/common.sh
+++ b/integration/common.sh
@@ -11,14 +11,14 @@ function wait_for_pgdog() {
function run_pgdog() {
pushd ${SCRIPT_DIR}/../../
- cargo build
- target/debug/pgdog --config integration/pgdog.toml --users integration/users.toml > ${SCRIPT_DIR}/log.txt &
+ cargo build --release
+ target/release/pgdog --config integration/pgdog.toml --users integration/users.toml > ${SCRIPT_DIR}/log.txt &
PID=$!
popd
}
function stop_pgdog() {
- kill -TERM ${PID}
+ killall -TERM pgdog
cat ${SCRIPT_DIR}/log.txt
rm ${SCRIPT_DIR}/log.txt
}
diff --git a/integration/python/run.sh b/integration/python/run.sh
index 56a8ce60..01a6ee12 100644
--- a/integration/python/run.sh
+++ b/integration/python/run.sh
@@ -1,29 +1,19 @@
#!/bin/bash
set -e
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
-pushd ${SCRIPT_DIR}/../..
-cargo build
-popd
+source ${SCRIPT_DIR}/../common.sh
+
+run_pgdog
+wait_for_pgdog
pushd ${SCRIPT_DIR}
+
virtualenv venv
source venv/bin/activate
pip install -r requirements.txt
-pushd ${SCRIPT_DIR}/../../
-target/debug/pgdog --config integration/pgdog.toml --users integration/users.toml > ${SCRIPT_DIR}/pytest_log.txt &
-PID=$!
-popd
-
-echo "Waiting for PgDog"
-while ! pg_isready -h 127.0.0.1 -p 6432 -U pgdog -d pgdog > /dev/null; do
- echo "waiting for PgDog" > /dev/null
-done
-echo "Running test suite"
-
pytest
-kill -TERM ${PID}
popd
-cat ${SCRIPT_DIR}/pytest_log.txt
-rm ${SCRIPT_DIR}/pytest_log.txt
+
+stop_pgdog
diff --git a/integration/ruby/Gemfile b/integration/ruby/Gemfile
new file mode 100644
index 00000000..d4960855
--- /dev/null
+++ b/integration/ruby/Gemfile
@@ -0,0 +1,4 @@
+source 'https://rubygems.org'
+gem 'rails'
+gem 'rspec', '~> 3.4'
+gem 'pg'
diff --git a/integration/ruby/Gemfile.lock b/integration/ruby/Gemfile.lock
new file mode 100644
index 00000000..55f3f4e0
--- /dev/null
+++ b/integration/ruby/Gemfile.lock
@@ -0,0 +1,226 @@
+GEM
+ remote: https://rubygems.org/
+ specs:
+ actioncable (8.0.2)
+ actionpack (= 8.0.2)
+ activesupport (= 8.0.2)
+ nio4r (~> 2.0)
+ websocket-driver (>= 0.6.1)
+ zeitwerk (~> 2.6)
+ actionmailbox (8.0.2)
+ actionpack (= 8.0.2)
+ activejob (= 8.0.2)
+ activerecord (= 8.0.2)
+ activestorage (= 8.0.2)
+ activesupport (= 8.0.2)
+ mail (>= 2.8.0)
+ actionmailer (8.0.2)
+ actionpack (= 8.0.2)
+ actionview (= 8.0.2)
+ activejob (= 8.0.2)
+ activesupport (= 8.0.2)
+ mail (>= 2.8.0)
+ rails-dom-testing (~> 2.2)
+ actionpack (8.0.2)
+ actionview (= 8.0.2)
+ activesupport (= 8.0.2)
+ nokogiri (>= 1.8.5)
+ rack (>= 2.2.4)
+ rack-session (>= 1.0.1)
+ rack-test (>= 0.6.3)
+ rails-dom-testing (~> 2.2)
+ rails-html-sanitizer (~> 1.6)
+ useragent (~> 0.16)
+ actiontext (8.0.2)
+ actionpack (= 8.0.2)
+ activerecord (= 8.0.2)
+ activestorage (= 8.0.2)
+ activesupport (= 8.0.2)
+ globalid (>= 0.6.0)
+ nokogiri (>= 1.8.5)
+ actionview (8.0.2)
+ activesupport (= 8.0.2)
+ builder (~> 3.1)
+ erubi (~> 1.11)
+ rails-dom-testing (~> 2.2)
+ rails-html-sanitizer (~> 1.6)
+ activejob (8.0.2)
+ activesupport (= 8.0.2)
+ globalid (>= 0.3.6)
+ activemodel (8.0.2)
+ activesupport (= 8.0.2)
+ activerecord (8.0.2)
+ activemodel (= 8.0.2)
+ activesupport (= 8.0.2)
+ timeout (>= 0.4.0)
+ activestorage (8.0.2)
+ actionpack (= 8.0.2)
+ activejob (= 8.0.2)
+ activerecord (= 8.0.2)
+ activesupport (= 8.0.2)
+ marcel (~> 1.0)
+ activesupport (8.0.2)
+ base64
+ benchmark (>= 0.3)
+ bigdecimal
+ concurrent-ruby (~> 1.0, >= 1.3.1)
+ connection_pool (>= 2.2.5)
+ drb
+ i18n (>= 1.6, < 2)
+ logger (>= 1.4.2)
+ minitest (>= 5.1)
+ securerandom (>= 0.3)
+ tzinfo (~> 2.0, >= 2.0.5)
+ uri (>= 0.13.1)
+ base64 (0.2.0)
+ benchmark (0.4.0)
+ bigdecimal (3.1.9)
+ builder (3.3.0)
+ concurrent-ruby (1.3.5)
+ connection_pool (2.5.0)
+ crass (1.0.6)
+ date (3.4.1)
+ diff-lcs (1.6.1)
+ drb (2.2.1)
+ erubi (1.13.1)
+ globalid (1.2.1)
+ activesupport (>= 6.1)
+ i18n (1.14.7)
+ concurrent-ruby (~> 1.0)
+ io-console (0.8.0)
+ irb (1.15.1)
+ pp (>= 0.6.0)
+ rdoc (>= 4.0.0)
+ reline (>= 0.4.2)
+ logger (1.7.0)
+ loofah (2.24.0)
+ crass (~> 1.0.2)
+ nokogiri (>= 1.12.0)
+ mail (2.8.1)
+ mini_mime (>= 0.1.1)
+ net-imap
+ net-pop
+ net-smtp
+ marcel (1.0.4)
+ mini_mime (1.1.5)
+ minitest (5.25.5)
+ net-imap (0.5.6)
+ date
+ net-protocol
+ net-pop (0.1.2)
+ net-protocol
+ net-protocol (0.2.2)
+ timeout
+ net-smtp (0.5.1)
+ net-protocol
+ nio4r (2.7.4)
+ nokogiri (1.18.6-aarch64-linux-gnu)
+ racc (~> 1.4)
+ nokogiri (1.18.6-aarch64-linux-musl)
+ racc (~> 1.4)
+ nokogiri (1.18.6-arm-linux-gnu)
+ racc (~> 1.4)
+ nokogiri (1.18.6-arm-linux-musl)
+ racc (~> 1.4)
+ nokogiri (1.18.6-arm64-darwin)
+ racc (~> 1.4)
+ nokogiri (1.18.6-x86_64-darwin)
+ racc (~> 1.4)
+ nokogiri (1.18.6-x86_64-linux-gnu)
+ racc (~> 1.4)
+ nokogiri (1.18.6-x86_64-linux-musl)
+ racc (~> 1.4)
+ pg (1.5.9)
+ pp (0.6.2)
+ prettyprint
+ prettyprint (0.2.0)
+ psych (5.2.3)
+ date
+ stringio
+ racc (1.8.1)
+ rack (3.1.12)
+ rack-session (2.1.0)
+ base64 (>= 0.1.0)
+ rack (>= 3.0.0)
+ rack-test (2.2.0)
+ rack (>= 1.3)
+ rackup (2.2.1)
+ rack (>= 3)
+ rails (8.0.2)
+ actioncable (= 8.0.2)
+ actionmailbox (= 8.0.2)
+ actionmailer (= 8.0.2)
+ actionpack (= 8.0.2)
+ actiontext (= 8.0.2)
+ actionview (= 8.0.2)
+ activejob (= 8.0.2)
+ activemodel (= 8.0.2)
+ activerecord (= 8.0.2)
+ activestorage (= 8.0.2)
+ activesupport (= 8.0.2)
+ bundler (>= 1.15.0)
+ railties (= 8.0.2)
+ rails-dom-testing (2.2.0)
+ activesupport (>= 5.0.0)
+ minitest
+ nokogiri (>= 1.6)
+ rails-html-sanitizer (1.6.2)
+ loofah (~> 2.21)
+ nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0)
+ railties (8.0.2)
+ actionpack (= 8.0.2)
+ activesupport (= 8.0.2)
+ irb (~> 1.13)
+ rackup (>= 1.0.0)
+ rake (>= 12.2)
+ thor (~> 1.0, >= 1.2.2)
+ zeitwerk (~> 2.6)
+ rake (13.2.1)
+ rdoc (6.13.0)
+ psych (>= 4.0.0)
+ reline (0.6.0)
+ io-console (~> 0.5)
+ rspec (3.13.0)
+ rspec-core (~> 3.13.0)
+ rspec-expectations (~> 3.13.0)
+ rspec-mocks (~> 3.13.0)
+ rspec-core (3.13.3)
+ rspec-support (~> 3.13.0)
+ rspec-expectations (3.13.3)
+ diff-lcs (>= 1.2.0, < 2.0)
+ rspec-support (~> 3.13.0)
+ rspec-mocks (3.13.2)
+ diff-lcs (>= 1.2.0, < 2.0)
+ rspec-support (~> 3.13.0)
+ rspec-support (3.13.2)
+ securerandom (0.4.1)
+ stringio (3.1.6)
+ thor (1.3.2)
+ timeout (0.4.3)
+ tzinfo (2.0.6)
+ concurrent-ruby (~> 1.0)
+ uri (1.0.3)
+ useragent (0.16.11)
+ websocket-driver (0.7.7)
+ base64
+ websocket-extensions (>= 0.1.0)
+ websocket-extensions (0.1.5)
+ zeitwerk (2.7.2)
+
+PLATFORMS
+ aarch64-linux-gnu
+ aarch64-linux-musl
+ arm-linux-gnu
+ arm-linux-musl
+ arm64-darwin
+ x86_64-darwin
+ x86_64-linux-gnu
+ x86_64-linux-musl
+
+DEPENDENCIES
+ pg
+ rails
+ rspec (~> 3.4)
+
+BUNDLED WITH
+ 2.6.5
diff --git a/integration/ruby/pg_spec.rb b/integration/ruby/pg_spec.rb
new file mode 100644
index 00000000..c3827cff
--- /dev/null
+++ b/integration/ruby/pg_spec.rb
@@ -0,0 +1,88 @@
+require_relative 'rspec_helper'
+
+def connect(dbname = 'pgdog')
+ return PG.connect(dbname: dbname, user: 'pgdog', password: 'pgdog', port: 6432, host: '127.0.0.1')
+end
+
+describe "pg" do
+ it "simple query" do
+ ["pgdog", "pgdog_sharded"].each do |db|
+ conn = connect db
+ res = conn.exec "SELECT 1::bigint AS one"
+ expect(res[0]["one"]).to eq("1")
+ res = conn.exec "SELECT $1 AS one, $2 AS two", [1, 2]
+ expect(res[0]["one"]).to eq("1")
+ expect(res[0]["two"]).to eq("2")
+ end
+ end
+
+ it "prepared statements" do
+ ["pgdog", "pgdog_sharded"].each do |db|
+ conn = connect db
+ 15.times do |i|
+ name = "_pg_#{i}"
+ res = conn.prepare name, "SELECT $1 AS one"
+ res = conn.exec_prepared name, [i]
+ expect(res[0]["one"]).to eq(i.to_s)
+ end
+ 30.times do |i|
+ 15.times do |i|
+ name = "_pg_#{i}"
+ res = conn.exec_prepared name, [i]
+ expect(res[0]["one"]).to eq(i.to_s)
+ end
+ end
+ end
+ end
+
+ it "sharded" do
+ conn = connect "pgdog_sharded"
+ conn.exec "DROP TABLE IF EXISTS sharded"
+ conn.exec "CREATE TABLE sharded (id BIGINT, value TEXT)"
+ conn.prepare "insert", "INSERT INTO sharded (id, value) VALUES ($1, $2) RETURNING *"
+ conn.prepare "select", "SELECT * FROM sharded WHERE id = $1"
+ 15.times do |i|
+ [10, 10_000_000_000].each do |num|
+ id = num + i
+ results = []
+ results << conn.exec("INSERT INTO sharded (id, value) VALUES ($1, $2) RETURNING *", [id, "value_one"])
+ results << conn.exec("SELECT * FROM sharded WHERE id = $1", [id])
+ for result in results
+ expect(result.num_tuples).to eq(1)
+ expect(result[0]["id"]).to eq(id.to_s)
+ expect(result[0]["value"]).to eq("value_one")
+ end
+ conn.exec "TRUNCATE TABLE sharded"
+ results << conn.exec_prepared("insert", [id, "value_one"])
+ results << conn.exec_prepared("select", [id])
+ for result in results
+ expect(result.num_tuples).to eq(1)
+ expect(result[0]["id"]).to eq(id.to_s)
+ expect(result[0]["value"]).to eq("value_one")
+ end
+ end
+ end
+ end
+
+ it "transactions" do
+ ["pgdog", "pgdog_sharded"].each do |db|
+ conn = connect db
+ conn.exec "DROP TABLE IF EXISTS sharded"
+ conn.exec "CREATE TABLE sharded (id BIGINT, value TEXT)"
+ conn.prepare "insert", "INSERT INTO sharded (id, value) VALUES ($1, $2) RETURNING *"
+ conn.prepare "select", "SELECT * FROM sharded WHERE id = $1"
+
+ conn.exec "BEGIN"
+ res = conn.exec_prepared "insert", [1, "test"]
+ conn.exec "COMMIT"
+ expect(res.num_tuples).to eq(1)
+ expect(res[0]["id"]).to eq(1.to_s)
+ conn.exec "BEGIN"
+ res = conn.exec_prepared "select", [1]
+ expect(res.num_tuples).to eq(1)
+ expect(res[0]["id"]).to eq(1.to_s)
+ conn.exec "ROLLBACK"
+ conn.exec "SELECT 1"
+ end
+ end
+end
diff --git a/integration/ruby/rspec_helper.rb b/integration/ruby/rspec_helper.rb
new file mode 100644
index 00000000..aacc4075
--- /dev/null
+++ b/integration/ruby/rspec_helper.rb
@@ -0,0 +1,3 @@
+require 'active_record'
+require 'rspec'
+require 'pg'
diff --git a/integration/ruby/run.sh b/integration/ruby/run.sh
new file mode 100644
index 00000000..c6f6fe27
--- /dev/null
+++ b/integration/ruby/run.sh
@@ -0,0 +1,18 @@
+#!/bin/bash
+set -e
+SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
+source ${SCRIPT_DIR}/../common.sh
+
+run_pgdog
+wait_for_pgdog
+
+pushd ${SCRIPT_DIR}
+
+export GEM_HOME=~/.gem
+mkdir -p ${GEM_HOME}
+bundle install
+bundle exec rspec pg_spec.rb
+
+popd
+
+stop_pgdog
diff --git a/pgdog/src/frontend/prepared_statements/mod.rs b/pgdog/src/frontend/prepared_statements/mod.rs
index b57f225d..acb390eb 100644
--- a/pgdog/src/frontend/prepared_statements/mod.rs
+++ b/pgdog/src/frontend/prepared_statements/mod.rs
@@ -52,8 +52,7 @@ impl PreparedStatements {
/// Register prepared statement with the global cache.
fn insert(&mut self, parse: Parse) -> Parse {
- let mut guard = self.global.lock();
- let (_new, name) = guard.insert(&parse);
+ let (_new, name) = { self.global.lock().insert(&parse) };
self.local.insert(parse.name.clone(), name.clone());
Parse::named(name, parse.query)
diff --git a/pgdog/src/net/messages/row_description.rs b/pgdog/src/net/messages/row_description.rs
index 7c5b6da0..54ca22fb 100644
--- a/pgdog/src/net/messages/row_description.rs
+++ b/pgdog/src/net/messages/row_description.rs
@@ -1,6 +1,7 @@
//! RowDescription (B) message.
use std::ops::Deref;
+use std::sync::Arc;
use crate::net::c_string_buf;
@@ -111,14 +112,14 @@ impl Field {
#[derive(Debug, Clone, PartialEq, Default)]
pub struct RowDescription {
/// Fields.
- pub fields: Vec<Field>,
+ pub fields: Arc<Vec<Field>>,
}
impl RowDescription {
/// Create new row description from fields.
pub fn new(fields: &[Field]) -> Self {
Self {
- fields: fields.to_vec(),
+ fields: Arc::new(fields.to_vec()),
}
}
@@ -180,7 +181,9 @@ impl FromBytes for RowDescription {
})
.collect();
- Ok(Self { fields })
+ Ok(Self {
+ fields: Arc::new(fields),
+ })
}
}
@@ -189,7 +192,7 @@ impl ToBytes for RowDescription {
let mut payload = Payload::named(self.code());
payload.put_i16(self.fields.len() as i16);
- for field in &self.fields {
+ for field in self.fields.iter() {
payload.put_string(&field.name);
payload.put_i32(field.table_oid);
payload.put_i16(field.column);
[parent: b982622603d9]