-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fix import path in SPI module. Add SPI testing extension to test suite to ensure SPI functioning correctly.
- Loading branch information
Showing
11 changed files
with
348 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
# spi_sql - Sample extension using SPI to execute SQL statements. | ||
|
||
This is a sample PostgreSQL extension to test SPI (Server Programming Interface) SQL execution in Zig. The extension provides a number of methods used by the test suite to verify that SPI access if functional. | ||
|
||
## Testing | ||
|
||
The extension uses PostgreSQL regression testing suite, which calls some of the exported functions in the extension itself. | ||
|
||
The extension sets up a sample table with entries that are used by the tests. | ||
|
||
``` | ||
zig build -freference-trace -p $PG_HOME | ||
``` | ||
|
||
Run regression tests: | ||
|
||
``` | ||
zig build -freference-trace -p $PG_HOME pg_regress | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
const std = @import("std"); | ||
|
||
// Load pgzx build support. The build utilities use pg_config to find all dependencies | ||
// and provide functions go create and test extensions. | ||
const PGBuild = @import("pgzx").Build; | ||
|
||
pub fn build(b: *std.Build) void { | ||
// Project meta data | ||
const name = "spi_sql"; | ||
const version = .{ .major = 0, .minor = 1 }; | ||
|
||
const target = b.standardTargetOptions(.{}); | ||
const optimize = b.standardOptimizeOption(.{}); | ||
|
||
// Load the pgzx module and initialize the build utilities | ||
const dep_pgzx = b.dependency("pgzx", .{ .target = target, .optimize = optimize }); | ||
const pgzx = dep_pgzx.module("pgzx"); | ||
var pgbuild = PGBuild.create(b, .{ .target = target, .optimize = optimize }); | ||
|
||
const build_options = b.addOptions(); | ||
build_options.addOption(bool, "testfn", b.option(bool, "testfn", "Register test function") orelse false); | ||
|
||
// Register the dependency with the build system | ||
// and add pgzx as module dependency. | ||
{ | ||
const ext = pgbuild.addInstallExtension(.{ | ||
.name = name, | ||
.version = version, | ||
.root_source_file = .{ | ||
.path = "src/main.zig", | ||
}, | ||
.root_dir = ".", | ||
}); | ||
ext.lib.root_module.addImport("pgzx", pgzx); | ||
ext.lib.root_module.addOptions("build_options", build_options); | ||
|
||
b.getInstallStep().dependOn(&ext.step); | ||
} | ||
|
||
// Configure pg_regress based testing for the current extension. | ||
{ | ||
const extest = pgbuild.addRegress(.{ | ||
.db_user = "postgres", | ||
.db_port = 5432, | ||
.root_dir = ".", | ||
.scripts = &[_][]const u8{ | ||
"spi_sql_test", | ||
}, | ||
}); | ||
|
||
// Make regression tests available to `zig build` | ||
var regress = b.step("pg_regress", "Run regression tests"); | ||
regress.dependOn(&extest.step); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
.{ | ||
.name = "spi_sql", | ||
.version = "0.1.0", | ||
.paths = .{ | ||
"extension", | ||
"src", | ||
}, | ||
.dependencies = .{ | ||
.pgzx = .{ | ||
.path = "./../..", | ||
}, | ||
}, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
CREATE EXTENSION spi_sql; | ||
SELECT spi_sql.query_by_id(0); -- return 'Hello' | ||
query_by_id | ||
------------- | ||
Hello | ||
(1 row) | ||
|
||
SELECT spi_sql.query_by_id(1); -- return 'World' | ||
query_by_id | ||
------------- | ||
World | ||
(1 row) | ||
|
||
SELECT spi_sql.query_by_id(2); -- Fail | ||
ERROR: Unknown id: 2 | ||
SELECT spi_sql.query_by_value('Hello'); -- return 0 | ||
query_by_value | ||
---------------- | ||
0 | ||
(1 row) | ||
|
||
SELECT spi_sql.query_by_value('World'); -- return 1 | ||
query_by_value | ||
---------------- | ||
1 | ||
(1 row) | ||
|
||
SELECT spi_sql.query_by_value('test'); -- FAIL | ||
ERROR: Value 'test' not found | ||
SELECT spi_sql.ins_value(2, 'test'); | ||
ins_value | ||
----------- | ||
2 | ||
(1 row) | ||
|
||
SELECT spi_sql.query_by_id(2); -- return 'test' | ||
query_by_id | ||
------------- | ||
test | ||
(1 row) | ||
|
||
SELECT spi_sql.query_by_value('test'); -- return 2 | ||
query_by_value | ||
---------------- | ||
2 | ||
(1 row) | ||
|
||
SELECT spi_sql.test_iter(); | ||
INFO: id: 0, value: Hello | ||
INFO: id: 1, value: World | ||
INFO: id: 2, value: test | ||
test_iter | ||
----------- | ||
|
||
(1 row) | ||
|
||
SELECT spi_sql.test_rows_of(); | ||
INFO: id: 0, value: Hello | ||
INFO: id: 1, value: World | ||
INFO: id: 2, value: test | ||
test_rows_of | ||
-------------- | ||
|
||
(1 row) | ||
|
||
DROP EXTENSION spi_sql; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
CREATE TABLE tbl ( | ||
id serial not null primary key, | ||
value text | ||
); | ||
|
||
INSERT INTO tbl (id, value) VALUES | ||
(0, 'Hello'), | ||
(1, 'World') | ||
; | ||
|
||
CREATE FUNCTION query_by_id(int4) RETURNS TEXT | ||
AS '$libdir/spi_sql' LANGUAGE C VOLATILE; | ||
|
||
CREATE FUNCTION query_by_value(TEXT) RETURNS INT4 | ||
AS '$libdir/spi_sql' LANGUAGE C VOLATILE; | ||
|
||
CREATE FUNCTION ins_value(INT4, TEXT) RETURNS INT4 | ||
AS '$libdir/spi_sql' LANGUAGE C VOLATILE; | ||
|
||
CREATE FUNCTION test_iter() RETURNS VOID | ||
AS '$libdir/spi_sql' LANGUAGE C VOLATILE; | ||
|
||
CREATE FUNCTION test_rows_of() RETURNS VOID | ||
AS '$libdir/spi_sql' LANGUAGE C VOLATILE; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
comment = 'pgzx: SPI SQL test extension' | ||
default_version = '0.1' | ||
module_pathname = '$libdir/spi_sql' | ||
relocatable = false | ||
superuser = false | ||
schema = 'spi_sql' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
CREATE EXTENSION spi_sql; | ||
|
||
SELECT spi_sql.query_by_id(0); -- return 'Hello' | ||
SELECT spi_sql.query_by_id(1); -- return 'World' | ||
SELECT spi_sql.query_by_id(2); -- Fail | ||
|
||
SELECT spi_sql.query_by_value('Hello'); -- return 0 | ||
SELECT spi_sql.query_by_value('World'); -- return 1 | ||
SELECT spi_sql.query_by_value('test'); -- FAIL | ||
|
||
SELECT spi_sql.ins_value(2, 'test'); | ||
SELECT spi_sql.query_by_id(2); -- return 'test' | ||
SELECT spi_sql.query_by_value('test'); -- return 2 | ||
|
||
SELECT spi_sql.test_iter(); | ||
|
||
SELECT spi_sql.test_rows_of(); | ||
|
||
DROP EXTENSION spi_sql; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
const std = @import("std"); | ||
const pgzx = @import("pgzx"); | ||
const pg = pgzx.c; | ||
|
||
comptime { | ||
pgzx.PG_MODULE_MAGIC(); | ||
|
||
pgzx.PG_FUNCTION_V1("query_by_id", query_by_id); | ||
pgzx.PG_FUNCTION_V1("query_by_value", query_by_value); | ||
pgzx.PG_FUNCTION_V1("ins_value", ins_value); | ||
pgzx.PG_FUNCTION_V1("test_iter", test_iter); | ||
pgzx.PG_FUNCTION_V1("test_rows_of", test_rows_of); | ||
} | ||
|
||
const SCHEMA_NAME = "spi_sql"; | ||
const TABLE_NAME = SCHEMA_NAME ++ ".tbl"; | ||
|
||
fn query_by_id(id: u32) ![]const u8 { | ||
const QUERY = "SELECT value FROM " ++ TABLE_NAME ++ " WHERE id = $1"; | ||
|
||
try pgzx.spi.connect(); | ||
defer pgzx.spi.finish(); | ||
|
||
var rows = try pgzx.spi.query(QUERY, .{ | ||
.limit = 1, | ||
.args = .{ | ||
.types = &[_]pg.Oid{pg.INT4OID}, | ||
.values = &[_]pg.NullableDatum{try pgzx.datum.toNullableDatum(id)}, | ||
}, | ||
}); | ||
defer rows.deinit(); | ||
|
||
if (!rows.next()) { | ||
return pgzx.elog.Error(@src(), "Unknown id: {d}", .{id}); | ||
} | ||
|
||
var value: []const u8 = undefined; | ||
try rows.scan(.{&value}); | ||
return value; | ||
} | ||
|
||
fn query_by_value(value: []const u8) !u32 { | ||
const QUERY = "SELECT id FROM " ++ TABLE_NAME ++ " WHERE value = $1"; | ||
|
||
try pgzx.spi.connect(); | ||
defer pgzx.spi.finish(); | ||
|
||
// Use `RowsOf` to implicitey scan the result without having to declare temporary variables. | ||
|
||
var rows = pgzx.spi.RowsOf(u32).init(try pgzx.spi.query(QUERY, .{ | ||
.limit = 1, | ||
.args = .{ | ||
.types = &[_]pg.Oid{pg.TEXTOID}, | ||
.values = &[_]pg.NullableDatum{try pgzx.datum.toNullableDatum(value)}, | ||
}, | ||
})); | ||
defer rows.deinit(); | ||
|
||
if (try rows.next()) |id| { | ||
return id; | ||
} | ||
return pgzx.elog.Error(@src(), "Value '{s}' not found", .{value}); | ||
} | ||
|
||
fn ins_value(id: u32, value: []const u8) !u32 { | ||
const STMT = "INSERT INTO " ++ TABLE_NAME ++ " (id, value) VALUES ($1, $2) RETURNING id"; | ||
|
||
try pgzx.spi.connect(); | ||
defer pgzx.spi.finish(); | ||
|
||
var rows = pgzx.spi.RowsOf(u32).init(try pgzx.spi.query(STMT, .{ | ||
.args = .{ | ||
.types = &[_]pg.Oid{ | ||
pg.INT4OID, | ||
pg.TEXTOID, | ||
}, | ||
.values = &[_]pg.NullableDatum{ | ||
try pgzx.datum.toNullableDatum(id), | ||
try pgzx.datum.toNullableDatum(value), | ||
}, | ||
}, | ||
})); | ||
defer rows.deinit(); | ||
|
||
if (try rows.next()) |ret_id| { | ||
return ret_id; | ||
} | ||
unreachable; | ||
} | ||
|
||
fn test_iter() !void { | ||
const QUERY = "SELECT id, value FROM " ++ TABLE_NAME; | ||
const Record = struct { | ||
id: u32, | ||
value: []const u8, | ||
}; | ||
|
||
try pgzx.spi.connect(); | ||
defer pgzx.spi.finish(); | ||
|
||
var rows = try pgzx.spi.query(QUERY, .{}); | ||
defer rows.deinit(); | ||
|
||
while (rows.next()) { | ||
var rec: Record = undefined; | ||
|
||
try rows.scan(.{&rec}); | ||
pgzx.elog.Info(@src(), "id: {d}, value: {s}", .{ rec.id, rec.value }); | ||
} | ||
} | ||
|
||
fn test_rows_of() !void { | ||
const QUERY = "SELECT id, value FROM " ++ TABLE_NAME; | ||
const Record = struct { | ||
id: u32, | ||
value: []const u8, | ||
}; | ||
|
||
try pgzx.spi.connect(); | ||
defer pgzx.spi.finish(); | ||
|
||
var rows = pgzx.spi.RowsOf(Record).init(try pgzx.spi.query(QUERY, .{})); | ||
defer rows.deinit(); | ||
|
||
while (try rows.next()) |rec| { | ||
pgzx.elog.Info(@src(), "id: {d}, value: {s}", .{ rec.id, rec.value }); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.