mirror of
https://github.com/oven-sh/bun
synced 2026-02-03 07:28:53 +00:00
Compare commits
7 Commits
don/fix/no
...
ciro/fix-p
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
190a5030ae | ||
|
|
55e74b1dab | ||
|
|
2bf14621cf | ||
|
|
2c3755646d | ||
|
|
80e658c953 | ||
|
|
31e57102eb | ||
|
|
39d9e87d00 |
@@ -240,15 +240,23 @@ pub const PostgresSQLQuery = struct {
|
||||
}
|
||||
|
||||
pub const Status = enum(u8) {
|
||||
/// The query was just enqueued, statement status can be checked for more details
|
||||
pending,
|
||||
written,
|
||||
running,
|
||||
/// The query is being bound to the statement
|
||||
binding,
|
||||
/// The query is running
|
||||
running,
|
||||
/// The query was successful
|
||||
success,
|
||||
/// The query failed
|
||||
fail,
|
||||
|
||||
pub fn isRunning(this: Status) bool {
|
||||
return this == .running or this == .binding;
|
||||
return @intFromEnum(this) > @intFromEnum(Status.pending) and @intFromEnum(this) < @intFromEnum(Status.success);
|
||||
}
|
||||
|
||||
pub fn isDone(this: Status) bool {
|
||||
return this == .success or this == .fail;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -606,57 +614,60 @@ pub const PostgresSQLQuery = struct {
|
||||
};
|
||||
|
||||
const has_params = signature.fields.len > 0;
|
||||
var did_write = false;
|
||||
|
||||
var reset_timeout = false;
|
||||
enqueue: {
|
||||
if (entry.found_existing) {
|
||||
this.statement = entry.value_ptr.*;
|
||||
this.statement.?.ref();
|
||||
signature.deinit();
|
||||
|
||||
if (has_params and this.statement.?.status == .parsing) {
|
||||
|
||||
// if it has params, we need to wait for ParamDescription to be received before we can write the data
|
||||
} else {
|
||||
if (@intFromEnum(this.statement.?.status) == @intFromEnum(PostgresSQLStatement.Status.prepared) and !connection.isCurrentRunning()) {
|
||||
this.flags.binary = this.statement.?.fields.len > 0;
|
||||
log("bindAndExecute", .{});
|
||||
// bindAndExecute will bind + execute, it will change to running after binding is complete
|
||||
this.status = .binding;
|
||||
PostgresRequest.bindAndExecute(globalObject, this.statement.?, binding_value, columns_value, PostgresSQLConnection.Writer, writer) catch |err| {
|
||||
if (!globalObject.hasException())
|
||||
return globalObject.throwError(err, "failed to bind and execute query");
|
||||
return error.JSError;
|
||||
};
|
||||
did_write = true;
|
||||
reset_timeout = true;
|
||||
}
|
||||
|
||||
break :enqueue;
|
||||
}
|
||||
|
||||
// If it does not have params, we can write and execute immediately in one go
|
||||
if (!has_params) {
|
||||
log("prepareAndQueryWithSignature", .{});
|
||||
const can_execute = !connection.isCurrentRunning();
|
||||
|
||||
PostgresRequest.prepareAndQueryWithSignature(globalObject, query_str.slice(), binding_value, PostgresSQLConnection.Writer, writer, &signature) catch |err| {
|
||||
signature.deinit();
|
||||
if (!globalObject.hasException())
|
||||
return globalObject.throwError(err, "failed to prepare and query");
|
||||
return error.JSError;
|
||||
};
|
||||
did_write = true;
|
||||
} else {
|
||||
log("writeQuery", .{});
|
||||
|
||||
PostgresRequest.writeQuery(query_str.slice(), signature.prepared_statement_name, signature.fields, PostgresSQLConnection.Writer, writer) catch |err| {
|
||||
signature.deinit();
|
||||
if (!globalObject.hasException())
|
||||
return globalObject.throwError(err, "failed to write query");
|
||||
return error.JSError;
|
||||
};
|
||||
writer.write(&protocol.Sync) catch |err| {
|
||||
signature.deinit();
|
||||
if (!globalObject.hasException())
|
||||
return globalObject.throwError(err, "failed to flush");
|
||||
return error.JSError;
|
||||
};
|
||||
if (can_execute) {
|
||||
// If it does not have params, we can write and execute immediately in one go
|
||||
if (!has_params) {
|
||||
log("prepareAndQueryWithSignature", .{});
|
||||
// prepareAndQueryWithSignature will write + bind + execute, it will change to running after binding is complete
|
||||
this.status = .binding;
|
||||
PostgresRequest.prepareAndQueryWithSignature(globalObject, query_str.slice(), binding_value, PostgresSQLConnection.Writer, writer, &signature) catch |err| {
|
||||
signature.deinit();
|
||||
if (!globalObject.hasException())
|
||||
return globalObject.throwError(err, "failed to prepare and query");
|
||||
return error.JSError;
|
||||
};
|
||||
reset_timeout = true;
|
||||
} else {
|
||||
log("writeQuery", .{});
|
||||
PostgresRequest.writeQuery(query_str.slice(), signature.prepared_statement_name, signature.fields, PostgresSQLConnection.Writer, writer) catch |err| {
|
||||
signature.deinit();
|
||||
if (!globalObject.hasException())
|
||||
return globalObject.throwError(err, "failed to write query");
|
||||
return error.JSError;
|
||||
};
|
||||
writer.write(&protocol.Sync) catch |err| {
|
||||
signature.deinit();
|
||||
if (!globalObject.hasException())
|
||||
return globalObject.throwError(err, "failed to flush");
|
||||
return error.JSError;
|
||||
};
|
||||
reset_timeout = true;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
@@ -664,7 +675,7 @@ pub const PostgresSQLQuery = struct {
|
||||
return globalObject.throwError(err, "failed to allocate statement");
|
||||
};
|
||||
connection.prepared_statement_id += 1;
|
||||
stmt.* = .{ .signature = signature, .ref_count = 2, .status = PostgresSQLStatement.Status.parsing };
|
||||
stmt.* = .{ .signature = signature, .ref_count = 2, .status = if (can_execute) .parsing else .pending };
|
||||
this.statement = stmt;
|
||||
entry.value_ptr.* = stmt;
|
||||
}
|
||||
@@ -672,12 +683,11 @@ pub const PostgresSQLQuery = struct {
|
||||
|
||||
connection.requests.writeItem(this) catch {};
|
||||
this.ref();
|
||||
this.status = if (did_write) .binding else .pending;
|
||||
PostgresSQLQuery.targetSetCached(this_value, globalObject, query);
|
||||
|
||||
if (connection.is_ready_for_query)
|
||||
connection.flushDataAndResetTimeout()
|
||||
else if (did_write)
|
||||
else if (reset_timeout)
|
||||
connection.resetConnectionTimeout();
|
||||
|
||||
return .undefined;
|
||||
@@ -1508,37 +1518,37 @@ pub const PostgresSQLConnection = struct {
|
||||
|
||||
pub fn onHandshake(this: *PostgresSQLConnection, success: i32, ssl_error: uws.us_bun_verify_error_t) void {
|
||||
debug("onHandshake: {d} {d}", .{ success, ssl_error.error_no });
|
||||
const handshake_success = if (success == 1) true else false;
|
||||
if (handshake_success) {
|
||||
if (this.tls_config.reject_unauthorized != 0) {
|
||||
// only reject the connection if reject_unauthorized == true
|
||||
switch (this.ssl_mode) {
|
||||
// https://github.com/porsager/postgres/blob/6ec85a432b17661ccacbdf7f765c651e88969d36/src/connection.js#L272-L279
|
||||
|
||||
if (this.tls_config.reject_unauthorized == 0) {
|
||||
return;
|
||||
}
|
||||
.verify_ca, .verify_full => {
|
||||
if (ssl_error.error_no != 0) {
|
||||
this.failWithJSValue(ssl_error.toJS(this.globalObject));
|
||||
return;
|
||||
}
|
||||
|
||||
const do_tls_verification = switch (this.ssl_mode) {
|
||||
// https://github.com/porsager/postgres/blob/6ec85a432b17661ccacbdf7f765c651e88969d36/src/connection.js#L272-L279
|
||||
.verify_ca, .verify_full => true,
|
||||
else => false,
|
||||
};
|
||||
|
||||
if (!do_tls_verification) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (success != 1) {
|
||||
this.failWithJSValue(ssl_error.toJS(this.globalObject));
|
||||
return;
|
||||
}
|
||||
|
||||
if (ssl_error.error_no != 0) {
|
||||
this.failWithJSValue(ssl_error.toJS(this.globalObject));
|
||||
return;
|
||||
}
|
||||
|
||||
const ssl_ptr = @as(*BoringSSL.SSL, @ptrCast(this.socket.getNativeHandle()));
|
||||
if (BoringSSL.SSL_get_servername(ssl_ptr, 0)) |servername| {
|
||||
const hostname = servername[0..bun.len(servername)];
|
||||
if (!BoringSSL.checkServerIdentity(ssl_ptr, hostname)) {
|
||||
this.failWithJSValue(ssl_error.toJS(this.globalObject));
|
||||
const ssl_ptr = @as(*BoringSSL.SSL, @ptrCast(this.socket.getNativeHandle()));
|
||||
if (BoringSSL.SSL_get_servername(ssl_ptr, 0)) |servername| {
|
||||
const hostname = servername[0..bun.len(servername)];
|
||||
if (!BoringSSL.checkServerIdentity(ssl_ptr, hostname)) {
|
||||
this.failWithJSValue(ssl_error.toJS(this.globalObject));
|
||||
}
|
||||
}
|
||||
this.failWithJSValue(ssl_error.toJS(this.globalObject));
|
||||
},
|
||||
else => {
|
||||
return;
|
||||
},
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// if we are here is because server rejected us, and the error_no is the cause of this
|
||||
// no matter if reject_unauthorized is false because we are disconnected by the server
|
||||
this.failWithJSValue(ssl_error.toJS(this.globalObject));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1707,8 +1717,10 @@ pub const PostgresSQLConnection = struct {
|
||||
return .zero;
|
||||
}
|
||||
|
||||
if (tls_config.reject_unauthorized != 0)
|
||||
tls_config.request_cert = 1;
|
||||
// we always request the cert so we can verify it and also we manually abort the connection if the hostname doesn't match
|
||||
const original_reject_unauthorized = tls_config.reject_unauthorized;
|
||||
tls_config.reject_unauthorized = 0;
|
||||
tls_config.request_cert = 1;
|
||||
|
||||
// We create it right here so we can throw errors early.
|
||||
const context_options = tls_config.asUSockets();
|
||||
@@ -1720,6 +1732,8 @@ pub const PostgresSQLConnection = struct {
|
||||
return globalObject.throwValue(err.toJS(globalObject));
|
||||
}
|
||||
};
|
||||
// restore the original reject_unauthorized
|
||||
tls_config.reject_unauthorized = original_reject_unauthorized;
|
||||
|
||||
if (err != .none) {
|
||||
tls_config.deinit();
|
||||
@@ -1969,6 +1983,15 @@ pub const PostgresSQLConnection = struct {
|
||||
return this.requests.peekItem(0);
|
||||
}
|
||||
|
||||
fn isCurrentRunning(this: *PostgresSQLConnection) bool {
|
||||
if (this.current()) |query| {
|
||||
if (query.statement) |stmt| {
|
||||
return query.status.isRunning() or @intFromEnum(stmt.status) > @intFromEnum(PostgresSQLStatement.Status.pending);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
pub const Writer = struct {
|
||||
connection: *PostgresSQLConnection,
|
||||
|
||||
@@ -2453,55 +2476,94 @@ pub const PostgresSQLConnection = struct {
|
||||
fn advance(this: *PostgresSQLConnection) !bool {
|
||||
defer this.updateRef();
|
||||
var any = false;
|
||||
|
||||
defer if (any) this.resetConnectionTimeout();
|
||||
while (this.requests.readableLength() > 0) {
|
||||
var req: *PostgresSQLQuery = this.requests.peekItem(0);
|
||||
switch (req.status) {
|
||||
.pending => {
|
||||
const stmt = req.statement orelse return error.ExpectedStatement;
|
||||
if (stmt.status == .failed) {
|
||||
req.onError(stmt.error_response, this.globalObject);
|
||||
this.requests.discard(1);
|
||||
any = true;
|
||||
} else {
|
||||
break;
|
||||
|
||||
switch (stmt.status) {
|
||||
.failed => {
|
||||
req.onError(stmt.error_response, this.globalObject);
|
||||
this.requests.discard(1);
|
||||
any = true;
|
||||
continue;
|
||||
},
|
||||
.prepared => {
|
||||
const binding_value = PostgresSQLQuery.bindingGetCached(req.thisValue) orelse .zero;
|
||||
const columns_value = PostgresSQLQuery.columnsGetCached(req.thisValue) orelse .zero;
|
||||
req.flags.binary = stmt.fields.len > 0;
|
||||
|
||||
PostgresRequest.bindAndExecute(this.globalObject, stmt, binding_value, columns_value, PostgresSQLConnection.Writer, this.writer()) catch |err| {
|
||||
req.onWriteFail(err, this.globalObject, this.getQueriesArray());
|
||||
req.deref();
|
||||
this.requests.discard(1);
|
||||
continue;
|
||||
};
|
||||
req.status = .binding;
|
||||
any = true;
|
||||
return any;
|
||||
},
|
||||
.pending => {
|
||||
// statement is pending, lets write/parse it
|
||||
var query_str = req.query.toUTF8(bun.default_allocator);
|
||||
defer query_str.deinit();
|
||||
stmt.status = .parsing;
|
||||
const has_params = stmt.signature.fields.len > 0;
|
||||
// If it does not have params, we can write and execute immediately in one go
|
||||
if (!has_params) {
|
||||
// prepareAndQueryWithSignature will write + bind + execute, it will change to running after binding is complete
|
||||
req.status = .binding;
|
||||
|
||||
const binding_value = PostgresSQLQuery.bindingGetCached(req.thisValue) orelse .zero;
|
||||
|
||||
PostgresRequest.prepareAndQueryWithSignature(this.globalObject, query_str.slice(), binding_value, PostgresSQLConnection.Writer, this.writer(), &stmt.signature) catch |err| {
|
||||
req.onWriteFail(err, this.globalObject, this.getQueriesArray());
|
||||
req.deref();
|
||||
this.requests.discard(1);
|
||||
continue;
|
||||
};
|
||||
any = true;
|
||||
return any;
|
||||
}
|
||||
const connection_writer = this.writer();
|
||||
// write query and wait for it to be prepared
|
||||
PostgresRequest.writeQuery(query_str.slice(), stmt.signature.prepared_statement_name, stmt.signature.fields, PostgresSQLConnection.Writer, connection_writer) catch |err| {
|
||||
req.onWriteFail(err, this.globalObject, this.getQueriesArray());
|
||||
req.deref();
|
||||
this.requests.discard(1);
|
||||
continue;
|
||||
};
|
||||
connection_writer.write(&protocol.Sync) catch |err| {
|
||||
req.onWriteFail(err, this.globalObject, this.getQueriesArray());
|
||||
req.deref();
|
||||
this.requests.discard(1);
|
||||
continue;
|
||||
};
|
||||
any = true;
|
||||
return any;
|
||||
},
|
||||
.parsing => {
|
||||
// we are still parsing, lets wait for it to be prepared or failed
|
||||
return any;
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
.running, .binding => {
|
||||
// if we are binding it will switch to running immediately
|
||||
// if we are running, we need to wait for it to be success or fail
|
||||
return any;
|
||||
},
|
||||
.success, .fail => {
|
||||
this.requests.discard(1);
|
||||
req.deref();
|
||||
this.requests.discard(1);
|
||||
any = true;
|
||||
continue;
|
||||
},
|
||||
else => break,
|
||||
}
|
||||
}
|
||||
|
||||
while (this.requests.readableLength() > 0) {
|
||||
var req: *PostgresSQLQuery = this.requests.peekItem(0);
|
||||
const stmt = req.statement orelse return error.ExpectedStatement;
|
||||
|
||||
switch (stmt.status) {
|
||||
.prepared => {
|
||||
if (req.status == .pending and stmt.status == .prepared) {
|
||||
const binding_value = PostgresSQLQuery.bindingGetCached(req.thisValue) orelse .zero;
|
||||
const columns_value = PostgresSQLQuery.columnsGetCached(req.thisValue) orelse .zero;
|
||||
PostgresRequest.bindAndExecute(this.globalObject, stmt, binding_value, columns_value, PostgresSQLConnection.Writer, this.writer()) catch |err| {
|
||||
req.onWriteFail(err, this.globalObject, this.getQueriesArray());
|
||||
req.deref();
|
||||
this.requests.discard(1);
|
||||
continue;
|
||||
};
|
||||
req.status = .binding;
|
||||
req.flags.binary = stmt.fields.len > 0;
|
||||
any = true;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
},
|
||||
else => break,
|
||||
}
|
||||
}
|
||||
|
||||
return any;
|
||||
}
|
||||
|
||||
@@ -2973,12 +3035,13 @@ pub const PostgresSQLStatement = struct {
|
||||
fields: []protocol.FieldDescription = &[_]protocol.FieldDescription{},
|
||||
parameters: []const int4 = &[_]int4{},
|
||||
signature: Signature,
|
||||
status: Status = Status.parsing,
|
||||
status: Status = Status.pending,
|
||||
error_response: protocol.ErrorResponse = .{},
|
||||
needs_duplicate_check: bool = true,
|
||||
fields_flags: PostgresSQLConnection.DataCell.Flags = .{},
|
||||
|
||||
pub const Status = enum {
|
||||
pending,
|
||||
parsing,
|
||||
prepared,
|
||||
failed,
|
||||
|
||||
82
test/js/sql/docker-tls/Dockerfile
Normal file
82
test/js/sql/docker-tls/Dockerfile
Normal file
@@ -0,0 +1,82 @@
|
||||
# Dockerfile
|
||||
FROM postgres:15
|
||||
|
||||
# Create directory for SSL certificates
|
||||
RUN mkdir -p /etc/postgresql/ssl
|
||||
|
||||
# Copy existing certificates
|
||||
COPY server.key server.crt /etc/postgresql/ssl/
|
||||
RUN chmod 600 /etc/postgresql/ssl/server.key && \
|
||||
chown postgres:postgres /etc/postgresql/ssl/server.key /etc/postgresql/ssl/server.crt
|
||||
|
||||
# Create initialization script
|
||||
RUN echo '#!/bin/bash\n\
|
||||
set -e\n\
|
||||
\n\
|
||||
# Wait for PostgreSQL to start\n\
|
||||
until pg_isready; do\n\
|
||||
echo "Waiting for PostgreSQL to start..."\n\
|
||||
sleep 1\n\
|
||||
done\n\
|
||||
\n\
|
||||
dropdb --if-exists bun_sql_test\n\
|
||||
\n\
|
||||
# Drop and recreate users with different auth methods\n\
|
||||
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL\n\
|
||||
DROP USER IF EXISTS bun_sql_test;\n\
|
||||
CREATE USER bun_sql_test;\n\
|
||||
\n\
|
||||
ALTER SYSTEM SET password_encryption = '"'"'md5'"'"';\n\
|
||||
SELECT pg_reload_conf();\n\
|
||||
DROP USER IF EXISTS bun_sql_test_md5;\n\
|
||||
CREATE USER bun_sql_test_md5 WITH PASSWORD '"'"'bun_sql_test_md5'"'"';\n\
|
||||
\n\
|
||||
ALTER SYSTEM SET password_encryption = '"'"'scram-sha-256'"'"';\n\
|
||||
SELECT pg_reload_conf();\n\
|
||||
DROP USER IF EXISTS bun_sql_test_scram;\n\
|
||||
CREATE USER bun_sql_test_scram WITH PASSWORD '"'"'bun_sql_test_scram'"'"';\n\
|
||||
EOSQL\n\
|
||||
\n\
|
||||
# Create database and set permissions\n\
|
||||
createdb bun_sql_test\n\
|
||||
\n\
|
||||
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL\n\
|
||||
GRANT ALL ON DATABASE bun_sql_test TO bun_sql_test;\n\
|
||||
ALTER DATABASE bun_sql_test OWNER TO bun_sql_test;\n\
|
||||
EOSQL\n\
|
||||
' > /docker-entrypoint-initdb.d/init-users-db.sh
|
||||
|
||||
# Make the script executable
|
||||
RUN chmod +x /docker-entrypoint-initdb.d/init-users-db.sh
|
||||
|
||||
# Create pg_hba.conf with SSL requirements
|
||||
RUN mkdir -p /etc/postgresql && touch /etc/postgresql/pg_hba.conf && \
|
||||
echo "local all postgres trust" >> /etc/postgresql/pg_hba.conf && \
|
||||
echo "local all bun_sql_test trust" >> /etc/postgresql/pg_hba.conf && \
|
||||
echo "local all bun_sql_test_md5 md5" >> /etc/postgresql/pg_hba.conf && \
|
||||
echo "local all bun_sql_test_scram scram-sha-256" >> /etc/postgresql/pg_hba.conf && \
|
||||
echo "hostssl all postgres 127.0.0.1/32 trust" >> /etc/postgresql/pg_hba.conf && \
|
||||
echo "hostssl all bun_sql_test 127.0.0.1/32 trust" >> /etc/postgresql/pg_hba.conf && \
|
||||
echo "hostssl all bun_sql_test_md5 127.0.0.1/32 md5" >> /etc/postgresql/pg_hba.conf && \
|
||||
echo "hostssl all bun_sql_test_scram 127.0.0.1/32 scram-sha-256" >> /etc/postgresql/pg_hba.conf && \
|
||||
echo "hostssl all postgres ::1/128 trust" >> /etc/postgresql/pg_hba.conf && \
|
||||
echo "hostssl all bun_sql_test ::1/128 trust" >> /etc/postgresql/pg_hba.conf && \
|
||||
echo "hostssl all bun_sql_test_md5 ::1/128 md5" >> /etc/postgresql/pg_hba.conf && \
|
||||
echo "hostssl all bun_sql_test_scram ::1/128 scram-sha-256" >> /etc/postgresql/pg_hba.conf && \
|
||||
echo "local replication all trust" >> /etc/postgresql/pg_hba.conf && \
|
||||
echo "hostssl replication all 127.0.0.1/32 trust" >> /etc/postgresql/pg_hba.conf && \
|
||||
echo "hostssl replication all ::1/128 trust" >> /etc/postgresql/pg_hba.conf
|
||||
|
||||
# Configure PostgreSQL for SSL
|
||||
RUN mkdir -p /docker-entrypoint-initdb.d && \
|
||||
echo "ALTER SYSTEM SET max_prepared_transactions = '100';" > /docker-entrypoint-initdb.d/configure-postgres.sql && \
|
||||
echo "ALTER SYSTEM SET ssl = 'on';" >> /docker-entrypoint-initdb.d/configure-postgres.sql && \
|
||||
echo "ALTER SYSTEM SET ssl_cert_file = '/etc/postgresql/ssl/server.crt';" >> /docker-entrypoint-initdb.d/configure-postgres.sql && \
|
||||
echo "ALTER SYSTEM SET ssl_key_file = '/etc/postgresql/ssl/server.key';" >> /docker-entrypoint-initdb.d/configure-postgres.sql
|
||||
|
||||
# Set environment variables
|
||||
ENV POSTGRES_HOST_AUTH_METHOD=trust
|
||||
ENV POSTGRES_USER=postgres
|
||||
|
||||
# Expose PostgreSQL port
|
||||
EXPOSE 5432
|
||||
98
test/js/sql/docker-tls/pg_hba.conf
Normal file
98
test/js/sql/docker-tls/pg_hba.conf
Normal file
@@ -0,0 +1,98 @@
|
||||
# PostgreSQL Client Authentication Configuration File
|
||||
# ===================================================
|
||||
#
|
||||
# Refer to the "Client Authentication" section in the PostgreSQL
|
||||
# documentation for a complete description of this file. A short
|
||||
# synopsis follows.
|
||||
#
|
||||
# This file controls: which hosts are allowed to connect, how clients
|
||||
# are authenticated, which PostgreSQL user names they can use, which
|
||||
# databases they can access. Records take one of these forms:
|
||||
#
|
||||
# local DATABASE USER METHOD [OPTIONS]
|
||||
# host DATABASE USER ADDRESS METHOD [OPTIONS]
|
||||
# hostssl DATABASE USER ADDRESS METHOD [OPTIONS]
|
||||
# hostnossl DATABASE USER ADDRESS METHOD [OPTIONS]
|
||||
# hostgssenc DATABASE USER ADDRESS METHOD [OPTIONS]
|
||||
# hostnogssenc DATABASE USER ADDRESS METHOD [OPTIONS]
|
||||
#
|
||||
# (The uppercase items must be replaced by actual values.)
|
||||
#
|
||||
# The first field is the connection type:
|
||||
# - "local" is a Unix-domain socket
|
||||
# - "host" is a TCP/IP socket (encrypted or not)
|
||||
# - "hostssl" is a TCP/IP socket that is SSL-encrypted
|
||||
# - "hostnossl" is a TCP/IP socket that is not SSL-encrypted
|
||||
# - "hostgssenc" is a TCP/IP socket that is GSSAPI-encrypted
|
||||
# - "hostnogssenc" is a TCP/IP socket that is not GSSAPI-encrypted
|
||||
#
|
||||
# DATABASE can be "all", "sameuser", "samerole", "replication", a
|
||||
# database name, or a comma-separated list thereof. The "all"
|
||||
# keyword does not match "replication". Access to replication
|
||||
# must be enabled in a separate record (see example below).
|
||||
#
|
||||
# USER can be "all", a user name, a group name prefixed with "+", or a
|
||||
# comma-separated list thereof. In both the DATABASE and USER fields
|
||||
# you can also write a file name prefixed with "@" to include names
|
||||
# from a separate file.
|
||||
#
|
||||
# ADDRESS specifies the set of hosts the record matches. It can be a
|
||||
# host name, or it is made up of an IP address and a CIDR mask that is
|
||||
# an integer (between 0 and 32 (IPv4) or 128 (IPv6) inclusive) that
|
||||
# specifies the number of significant bits in the mask. A host name
|
||||
# that starts with a dot (.) matches a suffix of the actual host name.
|
||||
# Alternatively, you can write an IP address and netmask in separate
|
||||
# columns to specify the set of hosts. Instead of a CIDR-address, you
|
||||
# can write "samehost" to match any of the server's own IP addresses,
|
||||
# or "samenet" to match any address in any subnet that the server is
|
||||
# directly connected to.
|
||||
#
|
||||
# METHOD can be "trust", "reject", "md5", "password", "scram-sha-256",
|
||||
# "gss", "sspi", "ident", "peer", "pam", "ldap", "radius" or "cert".
|
||||
# Note that "password" sends passwords in clear text; "md5" or
|
||||
# "scram-sha-256" are preferred since they send encrypted passwords.
|
||||
#
|
||||
# OPTIONS are a set of options for the authentication in the format
|
||||
# NAME=VALUE. The available options depend on the different
|
||||
# authentication methods -- refer to the "Client Authentication"
|
||||
# section in the documentation for a list of which options are
|
||||
# available for which authentication methods.
|
||||
#
|
||||
# Database and user names containing spaces, commas, quotes and other
|
||||
# special characters must be quoted. Quoting one of the keywords
|
||||
# "all", "sameuser", "samerole" or "replication" makes the name lose
|
||||
# its special character, and just match a database or username with
|
||||
# that name.
|
||||
#
|
||||
# This file is read on server startup and when the server receives a
|
||||
# SIGHUP signal. If you edit the file on a running system, you have to
|
||||
# SIGHUP the server for the changes to take effect, run "pg_ctl reload",
|
||||
# or execute "SELECT pg_reload_conf()".
|
||||
#
|
||||
# Put your actual configuration here
|
||||
# ----------------------------------
|
||||
#
|
||||
# If you want to allow non-local connections, you need to add more
|
||||
# "host" records. In that case you will also need to make PostgreSQL
|
||||
# listen on a non-local interface via the listen_addresses
|
||||
# configuration parameter, or via the -i or -h command line switches.
|
||||
|
||||
# CAUTION: Configuring the system for local "trust" authentication
|
||||
# allows any local user to connect as any PostgreSQL user, including
|
||||
# the database superuser. If you do not trust all your local users,
|
||||
# use another authentication method.
|
||||
|
||||
|
||||
# TYPE DATABASE USER ADDRESS METHOD
|
||||
|
||||
# "local" is for Unix domain socket connections only
|
||||
local all all trust
|
||||
# IPv4 local connections:
|
||||
host all all 127.0.0.1/32 trust
|
||||
# IPv6 local connections:
|
||||
host all all ::1/128 trust
|
||||
# Allow replication connections from localhost, by a user with the
|
||||
# replication privilege.
|
||||
local replication all trust
|
||||
host replication all 127.0.0.1/32 trust
|
||||
host replication all ::1/128 trust
|
||||
33
test/js/sql/docker-tls/server.crt
Normal file
33
test/js/sql/docker-tls/server.crt
Normal file
@@ -0,0 +1,33 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFxjCCA66gAwIBAgIUDfpkxHY/sHFNJv/Zn6XgYDg+Y98wDQYJKoZIhvcNAQEL
|
||||
BQAwYjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMRYwFAYDVQQHDA1TYW4gRnJh
|
||||
bmNpc2NvMQwwCgYDVQQKDANCdW4xDDAKBgNVBAsMA0J1bjESMBAGA1UEAwwJbG9j
|
||||
YWxob3N0MB4XDTI1MDEyMzAxMjA1OFoXDTM1MDEyMTAxMjA1OFowYjELMAkGA1UE
|
||||
BhMCVVMxCzAJBgNVBAgMAkNBMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2NvMQwwCgYD
|
||||
VQQKDANCdW4xDDAKBgNVBAsMA0J1bjESMBAGA1UEAwwJbG9jYWxob3N0MIICIjAN
|
||||
BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAycLMJ6rxyy8uxoOmYeOH1VQNmSXD
|
||||
KgQhRvkbd+CtOHUke8sW5WrZiV6aVHYCd7P+Phbyt1SXdvy0ZPiS+umfVrSt6QWV
|
||||
s6H8Aw1gcDX7aoaCoqFpx6/PZpbnZ4HSTqZTdwbrwaJTCS9zRornVaB0yyhQ1VOL
|
||||
XNqQxN74Fa3mh02Q2gaacEIRwAmGM/Lfbu3zKzaHtoJcH+IIRZ2nk05WzBOjmtQR
|
||||
CDI7nAHFr69MFH+lUF7gYrl2FF1gIl2xxAFA3x7CeTPVqfM4qhzICdeacjN48jrM
|
||||
1V+gZKp3JpDxOygUDJkR4tufpHHllKreDnw0SJxCzWEj9V1PhaTyN4+hFAmWj1ia
|
||||
90ZlQQcMVceIEwFeW2goRKCh690y3PYqZBeHaOKi48Uyvd3betnv8NaCofbJ95oM
|
||||
l+744nWpIcMTVi12Aszps8uAWONbO+1eyrjSnx/Bl8ZcnXrRB2S7C2PJnBIBzQIG
|
||||
a75i7St7L5qW2In+y6a4F2qe2zRNTWuGssnhmWEt4ZKIfv1Mfqr+q67xl8VWii7k
|
||||
7DT+1lv8wF9vJiieJuL9gYkmtFcj+XgbYW1auEtyKL/Liz/Dny54PoJ3bQeOqo20
|
||||
VgkcPfXwxUj6CpRJ8l2xi2Jfmt75EQFuTvGo3zNUmQYbqLRocfkYjxL3kVjzAggX
|
||||
OqXfPxw5ngA0yIcCAwEAAaN0MHIwHQYDVR0OBBYEFGk2RthCDB9NGIJKHa9gP9s6
|
||||
bgr8MB8GA1UdIwQYMBaAFGk2RthCDB9NGIJKHa9gP9s6bgr8MA8GA1UdEwEB/wQF
|
||||
MAMBAf8wHwYDVR0RBBgwFoIJbG9jYWxob3N0ggkxMjcuMC4wLjEwDQYJKoZIhvcN
|
||||
AQELBQADggIBACDkcgDj9w6tY9q/LkGFBT2gWRQnb/3AaXWFv0cWMO7iFGdaUesP
|
||||
dT7KuOweIZAz5f7PToOWwUN5Y5W774OzY8Fy6WIfo+fUzut3vO5M3FSTqM4Yrm/d
|
||||
Vapfoa0fNMwKrnO5RyKZjUqeLUtwownFY67qCbg5xdlImb1GXtBplnJKZN50cQqL
|
||||
08aZWUPEwpzGqPMNZWFufA9A/bx6SY8n3JJVnpvXq5P4ndK5Slq129QUcbCk89r9
|
||||
6Iog+1dTTifIaHIJ5suKbgSTBoRSs8J/xgnqcaBrwpLkpvg21QvlRjvxGxwQ5ybR
|
||||
2Z5KCWa+QzpLYlYV0OfPKsKQRQ5TuCYd6y9n8zQtjzjINuZysw/YMvlSKuiR53Wk
|
||||
2vjjuL91ICtV0Ye6Mj7GzPBdmBdthyLRCTKn5TVWFPBm/pAANus8v3mCgiFBPl/Y
|
||||
G4cC1yaXKGiD9jvQOSkZTNP0kvtOLVI75cHiGap13XF8MeOsv4AhnUgDp7Ow3XPG
|
||||
AJhs37tweYTsW8sAQinLpFM63xU9xZgutKggopftRzvQe5flfKhxV0D91WZgcjyE
|
||||
vHmM8/DpU4/udEPFrqYb9NcYsCEdwVuFT1TC5ZuOqFfQZUuCco3sUvBFAqYqfxoq
|
||||
LCjHe/xxbnhU7PBRHgoo7oKGldlvIqkIB9pTlIolXL0XaOMoqoGAmWKC
|
||||
-----END CERTIFICATE-----
|
||||
52
test/js/sql/docker-tls/server.key
Normal file
52
test/js/sql/docker-tls/server.key
Normal file
@@ -0,0 +1,52 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
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
|
||||
AWDbBPL8WN2F2/Q0QBxC2BECKSVxhg==
|
||||
-----END PRIVATE KEY-----
|
||||
125
test/js/sql/local-sql.test.ts
Normal file
125
test/js/sql/local-sql.test.ts
Normal file
@@ -0,0 +1,125 @@
|
||||
import { sql, SQL } from "bun";
|
||||
const postgres = (...args) => new sql(...args);
|
||||
import { expect, test, mock, beforeAll, afterAll } from "bun:test";
|
||||
import { $ } from "bun";
|
||||
import { bunExe, isCI, withoutAggressiveGC, isLinux } from "harness";
|
||||
import path from "path";
|
||||
|
||||
import { exec, execSync } from "child_process";
|
||||
import { promisify } from "util";
|
||||
|
||||
const execAsync = promisify(exec);
|
||||
import net from "net";
|
||||
const dockerCLI = Bun.which("docker") as string;
|
||||
|
||||
async function findRandomPort() {
|
||||
return new Promise((resolve, reject) => {
|
||||
// Create a server to listen on a random port
|
||||
const server = net.createServer();
|
||||
server.listen(0, () => {
|
||||
const port = server.address().port;
|
||||
server.close(() => resolve(port));
|
||||
});
|
||||
server.on("error", reject);
|
||||
});
|
||||
}
|
||||
async function waitForPostgres(port) {
|
||||
for (let i = 0; i < 3; i++) {
|
||||
try {
|
||||
const sql = new SQL(`postgres://bun_sql_test@localhost:${port}/bun_sql_test`, {
|
||||
idle_timeout: 20,
|
||||
max_lifetime: 60 * 30,
|
||||
tls: {
|
||||
ca: Bun.file(path.join(import.meta.dir, "docker-tls", "server.crt")),
|
||||
},
|
||||
});
|
||||
|
||||
await sql`SELECT 1`;
|
||||
await sql.end();
|
||||
console.log("PostgreSQL is ready!");
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.log(`Waiting for PostgreSQL... (${i + 1}/3)`);
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
}
|
||||
}
|
||||
throw new Error("PostgreSQL failed to start");
|
||||
}
|
||||
|
||||
async function startContainer(): Promise<{ port: number; containerName: string }> {
|
||||
try {
|
||||
// Build the Docker image
|
||||
console.log("Building Docker image...");
|
||||
const dockerfilePath = path.join(import.meta.dir, "docker-tls", "Dockerfile");
|
||||
await execAsync(`${dockerCLI} build --pull --rm -f "${dockerfilePath}" -t custom-postgres-tls .`, {
|
||||
cwd: path.join(import.meta.dir, "docker-tls"),
|
||||
});
|
||||
const port = await findRandomPort();
|
||||
const containerName = `postgres-test-${port}`;
|
||||
// Check if container exists and remove it
|
||||
try {
|
||||
await execAsync(`${dockerCLI} rm -f ${containerName}`);
|
||||
} catch (error) {
|
||||
// Container might not exist, ignore error
|
||||
}
|
||||
|
||||
// Start the container
|
||||
await execAsync(`${dockerCLI} run -d --name ${containerName} -p ${port}:5432 custom-postgres-tls`);
|
||||
|
||||
// Wait for PostgreSQL to be ready
|
||||
await waitForPostgres(port);
|
||||
return {
|
||||
port,
|
||||
containerName,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Error:", error);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
function isDockerEnabled(): boolean {
|
||||
if (!dockerCLI) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: investigate why its not starting on Linux arm64
|
||||
if (isLinux && process.arch === "arm64") {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
const info = execSync(`${dockerCLI} info`, { stdio: ["ignore", "pipe", "inherit"] });
|
||||
return info.toString().indexOf("Server Version:") !== -1;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (isDockerEnabled()) {
|
||||
const container: { port: number; containerName: string } = await startContainer();
|
||||
afterAll(async () => {
|
||||
try {
|
||||
await execAsync(`${dockerCLI} stop -t 0 ${container.containerName}`);
|
||||
await execAsync(`${dockerCLI} rm -f ${container.containerName}`);
|
||||
} catch (error) {}
|
||||
});
|
||||
|
||||
process.env.DATABASE_URL = `postgres://bun_sql_test@localhost:${container.port}/bun_sql_test`;
|
||||
|
||||
test.only("Connects with no options", async () => {
|
||||
// we need at least the usename and port
|
||||
await using sql = postgres({
|
||||
max: 1,
|
||||
idle_timeout: 1,
|
||||
connection_timeout: 1,
|
||||
// sslmode: "verify-full",
|
||||
tls: {
|
||||
ca: Bun.file(path.join(import.meta.dir, "docker-tls", "server.crt")),
|
||||
},
|
||||
});
|
||||
|
||||
const result = (await sql`select 1 as x`)[0].x;
|
||||
sql.close();
|
||||
expect(result).toBe(1);
|
||||
});
|
||||
}
|
||||
@@ -61,9 +61,7 @@ async function startContainer(): Promise<{ port: number; containerName: string }
|
||||
}
|
||||
|
||||
// Start the container
|
||||
await execAsync(
|
||||
`${dockerCLI} run -d --name ${containerName} -p ${port}:5432 custom-postgres`,
|
||||
);
|
||||
await execAsync(`${dockerCLI} run -d --name ${containerName} -p ${port}:5432 custom-postgres`);
|
||||
|
||||
// Wait for PostgreSQL to be ready
|
||||
await waitForPostgres(port);
|
||||
@@ -491,6 +489,38 @@ if (isDockerEnabled()) {
|
||||
return expect(error.code).toBe("ERR_POSTGRES_UNSAFE_TRANSACTION");
|
||||
});
|
||||
|
||||
test("should be able to execute different queries in the same connection #16774", async () => {
|
||||
const sql = postgres({ ...options, max: 1, fetch_types: false });
|
||||
const random_table_name = `test_user_${Math.random().toString(36).substring(2, 15)}`;
|
||||
await sql`CREATE TEMPORARY TABLE IF NOT EXISTS ${sql(random_table_name)} (id int, name text)`;
|
||||
|
||||
const promises: Array<Promise<any>> = [];
|
||||
// POPULATE TABLE
|
||||
for (let i = 0; i < 1_000; i++) {
|
||||
promises.push(sql`insert into ${sql(random_table_name)} values (${i}, ${`test${i}`})`.execute());
|
||||
}
|
||||
await Promise.all(promises);
|
||||
|
||||
// QUERY TABLE using execute() to force executing the query immediately
|
||||
{
|
||||
for (let i = 0; i < 1_000; i++) {
|
||||
// mix different parameters
|
||||
switch (i % 3) {
|
||||
case 0:
|
||||
promises.push(sql`select "id", "name" from ${sql(random_table_name)} where "id" = ${i}`.execute());
|
||||
break;
|
||||
case 1:
|
||||
promises.push(sql`select "id" from ${sql(random_table_name)} where "id" = ${i}`.execute());
|
||||
break;
|
||||
case 2:
|
||||
promises.push(sql`select 1, "id", "name" from ${sql(random_table_name)} where "id" = ${i}`.execute());
|
||||
break;
|
||||
}
|
||||
}
|
||||
await Promise.all(promises);
|
||||
}
|
||||
});
|
||||
|
||||
test("Transaction throws", async () => {
|
||||
await sql`create table if not exists test (a int)`;
|
||||
try {
|
||||
|
||||
@@ -167,7 +167,7 @@ if (TLS_POSTGRES_DATABASE_URL) {
|
||||
await using sql = new SQL({ ...options, debug: true, idle_timeout: 1, fetch_types: false, max: 10 });
|
||||
expect(
|
||||
await sql
|
||||
.begin(sql => [sql`select wat`, sql`select current_setting('bun_sql.test') as x, ${1} as a`])
|
||||
.begin(sql => [sql`select wat`.execute(), sql`select current_setting('bun_sql.test') as x, ${1} as a`])
|
||||
.catch(e => e.errno),
|
||||
).toBe("42703");
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user