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]