Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package steampipe for openSUSE:Factory checked in at 2026-02-24 15:39:16 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/steampipe (Old) and /work/SRC/openSUSE:Factory/.steampipe.new.1977 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "steampipe" Tue Feb 24 15:39:16 2026 rev:16 rq:1334605 version:2.3.6 Changes: -------- --- /work/SRC/openSUSE:Factory/steampipe/steampipe.changes 2026-02-17 16:53:33.170992878 +0100 +++ /work/SRC/openSUSE:Factory/.steampipe.new.1977/steampipe.changes 2026-02-24 15:39:57.833809430 +0100 @@ -1,0 +2,10 @@ +Mon Feb 23 08:15:40 UTC 2026 - Felix Niederwanger <[email protected]> + +- Update to version 2.3.6: + * v2.3.6 + * Update FDW version to 2.1.5 and SDK to v5.13.2 (#4939) + * Fix date and timestamptz display. closes #4450 + * Add steampipe architecture and development context to CLAUDE.md + * Add release process documentation to CLAUDE.md + +------------------------------------------------------------------- Old: ---- steampipe-2.3.5.obscpio New: ---- steampipe-2.3.6.obscpio ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ steampipe.spec ++++++ --- /var/tmp/diff_new_pack.NSEv7P/_old 2026-02-24 15:39:58.801849490 +0100 +++ /var/tmp/diff_new_pack.NSEv7P/_new 2026-02-24 15:39:58.805849656 +0100 @@ -17,7 +17,7 @@ Name: steampipe -Version: 2.3.5 +Version: 2.3.6 Release: 0 Summary: Query various APIs and services via SQL language License: AGPL-3.0-only @@ -25,7 +25,7 @@ Source: %{name}-%{version}.tar.gz Source1: vendor.tar.gz BuildRequires: golang-packaging -BuildRequires: golang(API) >= 1.22 +BuildRequires: golang(API) >= 1.24 %{go_nostrip} %description ++++++ _service ++++++ --- /var/tmp/diff_new_pack.NSEv7P/_old 2026-02-24 15:39:58.845851312 +0100 +++ /var/tmp/diff_new_pack.NSEv7P/_new 2026-02-24 15:39:58.849851477 +0100 @@ -2,7 +2,7 @@ <service name="obs_scm" mode="manual"> <param name="url">https://github.com/turbot/steampipe.git</param> <param name="scm">git</param> - <param name="revision">v2.3.5</param> + <param name="revision">v2.3.6</param> <param name="versionformat">@PARENT_TAG@</param> <param name="versionrewrite-pattern">v(.*)</param> <param name="changesgenerate">enable</param> ++++++ _servicedata ++++++ --- /var/tmp/diff_new_pack.NSEv7P/_old 2026-02-24 15:39:58.873852470 +0100 +++ /var/tmp/diff_new_pack.NSEv7P/_new 2026-02-24 15:39:58.877852636 +0100 @@ -1,6 +1,6 @@ <servicedata> <service name="tar_scm"> <param name="url">https://github.com/turbot/steampipe.git</param> - <param name="changesrevision">a4b708bcc1939d72853932b78db51ba8d92227ac</param></service></servicedata> + <param name="changesrevision">b713a4cdc6a1799531d0f8192713b6b7f6b3725e</param></service></servicedata> (No newline at EOF) ++++++ steampipe-2.3.5.obscpio -> steampipe-2.3.6.obscpio ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/steampipe-2.3.5/.github/workflows/11-test-acceptance.yaml new/steampipe-2.3.6/.github/workflows/11-test-acceptance.yaml --- old/steampipe-2.3.5/.github/workflows/11-test-acceptance.yaml 2026-02-06 08:59:35.000000000 +0100 +++ new/steampipe-2.3.6/.github/workflows/11-test-acceptance.yaml 2026-02-20 10:26:24.000000000 +0100 @@ -101,6 +101,7 @@ - "blank_aggregators" - "search_path" - "chaos_and_query" + - "date_time_types" - "dynamic_schema" - "dynamic_aggregators" - "cache" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/steampipe-2.3.5/CHANGELOG.md new/steampipe-2.3.6/CHANGELOG.md --- old/steampipe-2.3.5/CHANGELOG.md 2026-02-06 08:59:35.000000000 +0100 +++ new/steampipe-2.3.6/CHANGELOG.md 2026-02-20 10:26:24.000000000 +0100 @@ -1,3 +1,7 @@ +## v2.3.6 [2026-02-20] +_Bug fixes_ +- Fix `date` and `timestamptz` display formatting in query results. ([#4450](https://github.com/turbot/steampipe/issues/4450)) + ## v2.3.5 [2026-02-06] _Bug fixes_ - Fix autocomplete regression where suggestions disappear when typing a table name after `from `. ([#4928](https://github.com/turbot/steampipe/issues/4928)) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/steampipe-2.3.5/CLAUDE.md new/steampipe-2.3.6/CLAUDE.md --- old/steampipe-2.3.5/CLAUDE.md 1970-01-01 01:00:00.000000000 +0100 +++ new/steampipe-2.3.6/CLAUDE.md 2026-02-20 10:26:24.000000000 +0100 @@ -0,0 +1,433 @@ +# Steampipe + +Steampipe is a zero-ETL tool that lets you query cloud APIs using SQL. It embeds PostgreSQL and uses a Foreign Data Wrapper (FDW) to translate SQL queries into API calls via a plugin system. + +## Architecture Overview + +``` +┌──────────────────────────────────────────────────────────────────────┐ +│ User: steampipe query "SELECT * FROM aws_s3_bucket WHERE region='us-east-1'" +└──────────────┬───────────────────────────────────────────────────────┘ + │ + ┌───────▼────────┐ + │ Steampipe CLI │ ← This repo (turbot/steampipe) + │ (Cobra + Go) │ + └───────┬─────────┘ + │ Starts/manages + ┌───────▼──────────────┐ + │ Embedded PostgreSQL │ (v14, port 9193) + │ + FDW Extension │ ← turbot/steampipe-postgres-fdw + └───────┬──────────────┘ + │ gRPC + ┌───────▼──────────────┐ + │ Plugin Process │ Built with turbot/steampipe-plugin-sdk + │ (e.g. steampipe- │ + │ plugin-aws) │ + └───────┬──────────────┘ + │ API calls + ┌───────▼──────────────┐ + │ Cloud API / Service │ + └──────────────────────┘ +``` + +### Query Flow + +1. User executes SQL (interactive REPL or batch mode) +2. Steampipe CLI ensures PostgreSQL + FDW + plugins are running +3. SQL goes to PostgreSQL, which routes foreign table access to the FDW +4. FDW translates the query (columns, WHERE quals, LIMIT, ORDER BY) into a gRPC `ExecuteRequest` +5. Plugin receives the request, calls the appropriate API, streams rows back via gRPC +6. FDW converts rows to PostgreSQL tuples, returns to the query engine +7. PostgreSQL applies any remaining filters/joins/aggregations and returns results + +### Key Design Decisions + +- **Process-per-plugin**: Each plugin is a separate OS process, communicating via gRPC (using HashiCorp go-plugin) +- **Qual pushdown**: WHERE clauses are pushed to plugins so they can filter at the API level (e.g. `region = 'us-east-1'` becomes an API parameter) +- **Limit pushdown**: LIMIT is pushed to plugins when sort order can also be pushed +- **Streaming**: Rows are streamed progressively, not buffered +- **Caching**: Two-level caching (query cache in plugin manager, connection cache per-plugin) + +## Repository Map + +### This Repo: `turbot/steampipe` (CLI) + +The Steampipe CLI manages the database lifecycle, plugin installation, and provides the query interface. + +``` +steampipe/ +├── main.go # Entry point: system checks, then cmd.Execute() +├── cmd/ # Cobra commands +│ ├── root.go # Root command, global flags +│ ├── query.go # `steampipe query` - interactive/batch SQL +│ ├── service.go # `steampipe service` - start/stop/status of DB service +│ ├── plugin.go # `steampipe plugin` - install/update/list/uninstall +│ ├── plugin_manager.go # Plugin manager daemon process +│ ├── login.go # `steampipe login` - Turbot Pipes auth +│ └── completion.go # Shell completion +├── pkg/ +│ ├── db/ +│ │ ├── db_local/ # PostgreSQL process management (start, stop, install, backup) +│ │ ├── db_client/ # Database client (pgx connection pool, query execution, sessions) +│ │ └── db_common/ # Shared DB interfaces and types +│ ├── steampipeconfig/ # HCL config loading (connections, options, connection state) +│ ├── connection/ # Connection refresh, state tracking, config file watcher +│ ├── pluginmanager_service/ # gRPC plugin manager (starts plugins, manages lifecycle) +│ ├── pluginmanager/ # Plugin manager state persistence +│ ├── interactive/ # Interactive REPL (go-prompt, autocomplete, metaqueries) +│ ├── query/ # Query execution (init, batch/interactive, history, results) +│ ├── ociinstaller/ # OCI image installer for DB binaries and FDW +│ ├── introspection/ # Internal metadata tables (steampipe_connection, steampipe_plugin, etc.) +│ ├── constants/ # App constants (ports, schemas, env vars, exit codes) +│ ├── options/ # Config option types (database, general, plugin) +│ ├── initialisation/ # Startup initialization (DB client, services, cloud metadata) +│ ├── export/ # Query result export (snapshots) +│ ├── display/ # Output formatting +│ ├── cmdconfig/ # CLI flag configuration via viper +│ └── ... # error_helpers, statushooks, utils, etc. +├── tests/ +│ ├── acceptance/ # Acceptance test suite +│ ├── dockertesting/ # Docker-based tests +│ └── manual_testing/ # Manual test scripts +└── .ai/ # AI development guides (see below) +``` + +#### Key Internal Flows + +**Service startup** (`steampipe service start` or implicit on `steampipe query`): +1. `db_local.StartServices()` ensures PostgreSQL is installed (via OCI images) +2. Starts PostgreSQL process with the FDW extension loaded +3. Starts plugin manager, loads plugin processes +4. Refreshes all connections (creates/updates foreign table schemas) +5. Creates internal metadata tables (`steampipe_internal` schema) + +**Database client** (`pkg/db/db_client/`): +- Uses `jackc/pgx/v5` connection pool +- Manages per-session search paths (so each query sees the right schemas) +- Executes queries and streams results back + +**Interactive mode** (`pkg/interactive/`): +- Uses a fork of `c-bata/go-prompt` for the REPL +- Provides autocomplete for table names, columns, SQL keywords +- Supports metaqueries (`.inspect`, `.tables`, `.help`, etc.) + +**Plugin management** (`steampipe plugin install aws`): +- Downloads OCI image from registry → extracts to `~/.steampipe/plugins/` +- On next query, plugin manager starts the plugin process +- FDW imports foreign schema (creates foreign tables for each plugin table) + +### Related Repo: `turbot/steampipe-postgres-fdw` (FDW) + +The Foreign Data Wrapper is a PostgreSQL extension written in C + Go. It bridges PostgreSQL and plugins. + +``` +steampipe-postgres-fdw/ +├── fdw/ # C code: PostgreSQL extension callbacks +│ ├── fdw.c # FDW init, handler registration (FdwRoutine) +│ ├── query.c # Query planning: column extraction, sort/limit pushdown +│ └── common.h # Core C structs (ConversionInfo, FdwPlanState, FdwExecState) +├── hub/ # Go code: query engine that talks to plugins +│ ├── hub_base.go # Planning (GetRelSize, GetPathKeys) and scan management +│ ├── hub_remote.go # Remote hub: connection pooling, iterator creation +│ ├── scan_iterator.go # Row streaming from plugin via gRPC +│ └── connection_factory.go # Plugin connection caching +├── fdw.go # Go↔C bridge: exported functions (goFdwBeginForeignScan, etc.) +├── quals.go # PostgreSQL restrictions → protobuf Quals conversion +├── schema.go # Plugin schema → CREATE FOREIGN TABLE SQL +├── helpers.go # C↔Go type conversion (Go values ↔ PostgreSQL Datums) +└── types/ # Go type definitions (Relation, Options, PathKeys) +``` + +#### FDW Lifecycle (per query) + +| Phase | C Callback | Go Function | What Happens | +|-------|-----------|-------------|--------------| +| Planning | `fdwGetForeignRelSize` | `Hub.GetRelSize()` | Estimate row count and width | +| Planning | `fdwGetForeignPaths` | `Hub.GetPathKeys()` | Generate access paths (for join optimization) | +| Planning | `fdwGetForeignPlan` | - | Choose plan, serialize state | +| Execution | `fdwBeginForeignScan` | `Hub.GetIterator()` | Convert quals, create scan iterator | +| Execution | `fdwIterateForeignScan` | `iterator.Next()` | Fetch rows, convert to Datums | +| Cleanup | `fdwEndForeignScan` | `iterator.Close()` | Cleanup, collect scan metadata | + +#### Qual Pushdown + +WHERE clauses are converted from PostgreSQL's internal representation to protobuf `Qual` messages: +- `column = value` → `Qual{FieldName, "=", value}` +- `column IN (a, b)` → `Qual{FieldName, "=", ListValue}` +- `column IS NULL` → `NullTest` qual +- `column LIKE '%pattern%'` → `Qual{FieldName, "~~", value}` +- Boolean expressions (AND/OR) are handled recursively +- Volatile functions and self-references are excluded (left for PostgreSQL to filter) + +### Related Repo: `turbot/steampipe-plugin-sdk` (Plugin SDK) + +The SDK provides the framework for building plugins. Plugin authors only write API-specific code. + +``` +steampipe-plugin-sdk/ +├── plugin/ # Core plugin framework +│ ├── plugin.go # Plugin struct, initialization, execution orchestration +│ ├── table.go # Table definition (columns, List/Get config, hydrate config) +│ ├── column.go # Column definition (name, type, transform, hydrate func) +│ ├── table_fetch.go # Fetch orchestration: Get vs List decision, row building +│ ├── query_data.go # QueryData: quals, key columns, streaming, pagination +│ ├── row_data.go # Row building: parallel hydrate execution, transform application +│ ├── key_column.go # Key column definitions (required/optional/any_of, operators) +│ ├── hydrate_config.go # Hydrate config: dependencies, retry, ignore, concurrency +│ ├── hydrate_error.go # Error wrapping: retry with backoff, error ignoring +│ └── serve.go # Plugin startup: gRPC server registration +├── grpc/ # gRPC server implementation (PluginServer) +│ ├── pluginServer.go # RPC methods: Execute, GetSchema, SetConnectionConfig, etc. +│ └── proto/ # Protobuf definitions (plugin.proto) +├── query_cache/ # Query result caching +├── rate_limiter/ # Token bucket rate limiting with scoped instances +├── connection/ # Per-connection in-memory caching (Ristretto) +├── transform/ # Data transformation functions (FromField, FromGo, NullIfZero, etc.) +└── row_stream/ # Row streaming channel management +``` + +#### Plugin Execution Model + +When a query hits a plugin table: + +1. **Get vs List decision**: If all required key columns have `=` quals → Get call. Otherwise → List call. +2. **List hydrate** runs first, streaming items via `QueryData.StreamListItem()` +3. **Row building** (per item, in parallel): + - Start all hydrate functions (respecting dependency graph) + - Hydrates without dependencies run concurrently + - Each hydrate is wrapped with retry + ignore error logic + - Rate limiters throttle API calls per scope (connection, region, service) +4. **Transform chain** applied per column: `FromField("Name").Transform(toLower).NullIfZero()` +5. **Row streamed** back to FDW via gRPC + +#### Key Types + +``` +Plugin → Top-level struct, holds TableMap, config, caches +Table → Name, Columns, List/Get config, HydrateConfig +Column → Name, Type, Transform, optional Hydrate function +KeyColumn → Column name, operators, required/optional/any_of +HydrateFunc → func(ctx, *QueryData, *HydrateData) (interface{}, error) +QueryData → Quals, key columns, streaming, connection config +TransformCall → Chain of FromXXX → Transform → NullIfZero +``` + +### Related Repo: `turbot/pipe-fittings` (Shared Library) + +Shared infrastructure library used by Steampipe, Flowpipe, and Powerpipe. + +``` +pipe-fittings/ +├── modconfig/ # Mod resources: Mod, HclResource, ModTreeItem interfaces +├── connection/ # Connection types (48+ implementations: AWS, Azure, GCP, GitHub, etc.) +│ └── PipelingConnection # Core interface: Resolve(), Validate(), GetEnv(), CtyValue() +├── parse/ # HCL parsing engine (decoder, body processing, custom types) +├── constants/ # Shared constants across Turbot products +├── utils/ # Plugin utilities, string helpers, file ops +├── credential/ # Credential management +├── schema/ # Resource schema definitions +├── versionmap/ # Dependency version management +├── modinstaller/ # Mod dependency installation +├── ociinstaller/ # OCI image installation +└── backend/ # PostgreSQL connector +``` + +Steampipe imports pipe-fittings as `github.com/turbot/pipe-fittings/v2`. Key usage: +- `modconfig.SteampipeConnection` for connection configuration types +- `constants` for shared database and cloud constants +- `utils` for common helper functions +- `connection` types for Turbot Pipes integration + +## Development Guide + +### Building + +```bash +go build -o steampipe +``` + +### Testing + +```bash +# Unit tests +go test ./... + +# Acceptance tests (local) - sets up a temp install dir, installs chaos plugins, runs all tests +tests/acceptance/run-local.sh + +# Run a single acceptance test file +tests/acceptance/run-local.sh 001.query.bats +``` + +`run-local.sh` creates a temporary `STEAMPIPE_INSTALL_DIR`, runs `steampipe plugin install chaos chaosdynamic`, then delegates to `run.sh`. This isolates tests from your real `~/.steampipe` installation. The `steampipe` binary must already be on your `PATH` (build it first with `go build -o steampipe` and add it or use `go install`). + +### Local Development with Related Repos + +#### Dependency Chain + +``` +pipe-fittings (shared library, no Turbot dependencies) + ↑ +steampipe-plugin-sdk (depends on nothing Turbot-specific) + ↑ +steampipe-postgres-fdw (depends on pipe-fittings + steampipe-plugin-sdk) + ↑ +steampipe (depends on pipe-fittings + steampipe-plugin-sdk) +``` + +Changes flow upward: a change in `pipe-fittings` can affect all three consumers. A change in `steampipe-plugin-sdk` affects `steampipe` and `steampipe-postgres-fdw`. The FDW and CLI are independent of each other. + +#### Using `go.mod` Replace Directives + +Steampipe's `go.mod` has **commented-out replace directives** that point to sibling directories: + +```go +replace ( + github.com/c-bata/go-prompt => github.com/turbot/go-prompt v0.2.6-steampipe.0.0.20221028122246-eb118ec58d50 +// github.com/turbot/pipe-fittings/v2 => ../pipe-fittings +// github.com/turbot/steampipe-plugin-sdk/v5 => ../steampipe-plugin-sdk +) +``` + +**To develop against a local `pipe-fittings` or `steampipe-plugin-sdk`**, uncomment the relevant line(s). This tells Go to use your local checkout instead of the published module version. This is essential when: + +- You need to change `pipe-fittings` or `steampipe-plugin-sdk` alongside `steampipe` +- You're debugging an issue that spans repos (e.g. a config parsing bug in pipe-fittings that manifests in steampipe) +- You want to test unreleased SDK or pipe-fittings changes with the CLI + +**Important**: The `go.mod` expects sibling directories (`../pipe-fittings`, `../steampipe-plugin-sdk`). The local workspace should look like: + +``` +turbot/ +├── steampipe/ # this repo +├── steampipe-postgres-fdw/ # FDW +├── steampipe-plugin-sdk/ # plugin SDK +└── pipe-fittings/ # shared library +``` + +**Remember to re-comment the replace directives before committing** — they should never be checked in uncommented, as CI and other developers won't have the same local paths. The `go-prompt` replace is permanent (it points to Turbot's fork, not a local path). + +The `steampipe-postgres-fdw` repo does **not** have pre-configured replace directives for local development. If you need to develop the FDW against local copies, add them manually: + +```go +// in steampipe-postgres-fdw/go.mod +replace ( + github.com/turbot/pipe-fittings/v2 => ../pipe-fittings + github.com/turbot/steampipe-plugin-sdk/v5 => ../steampipe-plugin-sdk +) +``` + +#### Cross-Repo Change Workflow + +When a change spans multiple repos (e.g. adding a new config field): + +1. Make the change in the lowest dependency first (e.g. `pipe-fittings`) +2. Uncomment the replace directive in the consumer repo (`steampipe`) +3. Build and test locally with the replace active +4. Once working, publish the dependency (merge + tag a release) +5. Update `go.mod` in the consumer to reference the new version: `go get github.com/turbot/pipe-fittings/[email protected]` +6. Re-comment the replace directive +7. Commit and PR the consumer repo + +### Key Directories for Common Tasks + +| Task | Where to Look | +|------|--------------| +| Fix a CLI command | `cmd/` (command definition) + relevant `pkg/` package | +| Fix query execution | `pkg/query/`, `pkg/db/db_client/` | +| Fix interactive mode | `pkg/interactive/` | +| Fix plugin install/management | `pkg/ociinstaller/`, `pkg/pluginmanager_service/` | +| Fix connection handling | `pkg/steampipeconfig/`, `pkg/connection/` | +| Fix DB startup/shutdown | `pkg/db/db_local/` | +| Fix autocomplete | `pkg/interactive/interactive_client_autocomplete.go` | +| Fix service management | `cmd/service.go`, `pkg/db/db_local/` | +| Change internal tables | `pkg/introspection/` | +| Change config parsing | `pkg/steampipeconfig/load_config.go`, pipe-fittings | +| Fix FDW query planning | `steampipe-postgres-fdw/fdw/` (C) + `hub/` (Go) | +| Fix qual pushdown | `steampipe-postgres-fdw/quals.go` | +| Fix type conversion | `steampipe-postgres-fdw/helpers.go` | +| Fix plugin SDK behavior | `steampipe-plugin-sdk/plugin/` | +| Fix hydrate execution | `steampipe-plugin-sdk/plugin/table_fetch.go`, `row_data.go` | +| Fix caching | `steampipe-plugin-sdk/query_cache/` | +| Fix rate limiting | `steampipe-plugin-sdk/rate_limiter/` | + +### Important Constants + +- **Default DB port**: 9193 (`pkg/constants/db.go`) +- **PostgreSQL version**: 14.19.0 +- **FDW version**: 2.1.4 +- **Internal schema**: `steampipe_internal` +- **Install directory**: `~/.steampipe/` +- **Plugin directory**: `~/.steampipe/plugins/` +- **Config directory**: `~/.steampipe/config/` +- **Log directory**: `~/.steampipe/logs/` + +### Branching and Workflow + +- **Base branch**: `develop` for all work +- **Main branch**: `main` (releases merge here) +- **Release branch**: `v2.3.x` (or similar version branch) +- **Bug fixes**: Use the 2-commit pattern (see `.ai/docs/bug-fix-prs.md`) +- **PR titles**: End with `closes #XXXX` for bug fixes +- **Merge-to-develop PRs**: When merging a release or feature branch into `develop`, the PR title must be `Merge branch '<branchname>' into develop` (e.g. `Merge branch 'v2.3.x' into develop`) +- **Small PRs**: One logical change per PR + +### AI Development Guides + +The `.ai/` directory contains detailed guides for AI-assisted development: +- `.ai/docs/bug-fix-prs.md` - Two-commit bug fix pattern (demonstrate bug, then fix) +- `.ai/docs/bug-workflow.md` - Creating GitHub bug issues +- `.ai/docs/test-generation-guide.md` - Writing effective Go tests +- `.ai/docs/parallel-coordination.md` - Coordinating parallel AI agents +- `.ai/templates/` - PR description templates + +## Release Process + +Follow these steps in order to perform a release: + +### 1. Changelog +- Draft a changelog entry in `CHANGELOG.md` matching the style of existing entries. +- Use today's date and the next patch version. + +### 2. Commit +- Commit message for release changelog changes should be the version number, e.g. `v2.3.5`. + +### 3. Release Issue +- Use the `.github/ISSUE_TEMPLATE/release_issue.md` template. +- Title: `Steampipe v<version>`, label: `release`. + +### 4. PRs +1. **Against `develop`**: Title should be `Merge branch '<branchname>' into develop`. +2. **Against `main`**: Title should be `Release Steampipe v<version>`. + - Body format: + ``` + ## Release Issue + [Steampipe v<version>](link-to-release-issue) + + ## Checklist + - [ ] Confirmed that version has been correctly upgraded. + ``` + - Tag the release issue to the PR (add `release` label). + +### 5. steampipe.io Changelog +- Create a changelog PR in the `turbot/steampipe.io` repo. +- Branch off `main`, branch name: `sp-<version without dots>` (e.g. `sp-235`). +- Add a file at `content/changelog/<year>/<YYYYMMDD>-steampipe-cli-v<version-with-dashes>.md`. +- Frontmatter format: + ``` + --- + title: Steampipe CLI v<version> - <short summary> + publishedAt: "<YYYY-MM-DD>T10:00:00" + permalink: steampipe-cli-v<version-with-dashes> + tags: cli + --- + ``` +- Body should match the changelog content from `CHANGELOG.md`. +- PR title: `Steampipe CLI v<version>`, base: `main`. + +### 6. Deploy steampipe.io +- After the steampipe.io changelog PR is merged, trigger the `Deploy steampipe.io` workflow in `turbot/steampipe.io` from `main`. + +### 7. Close Release Issue +- Check off all items in the release issue checklist as steps are completed. +- Close the release issue once all steps are done. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/steampipe-2.3.5/go.mod new/steampipe-2.3.6/go.mod --- old/steampipe-2.3.5/go.mod 2026-02-06 08:59:35.000000000 +0100 +++ new/steampipe-2.3.6/go.mod 2026-02-20 10:26:24.000000000 +0100 @@ -17,7 +17,7 @@ github.com/fatih/color v1.18.0 github.com/fsnotify/fsnotify v1.9.0 github.com/gertd/go-pluralize v0.2.1 - github.com/go-git/go-git/v5 v5.16.2 + github.com/go-git/go-git/v5 v5.16.5 github.com/google/uuid v1.6.0 github.com/hashicorp/go-hclog v1.6.3 github.com/hashicorp/go-plugin v1.7.0 @@ -40,8 +40,8 @@ github.com/spf13/viper v1.20.1 github.com/thediveo/enumflag/v2 v2.0.7 github.com/turbot/go-kit v1.3.0 - github.com/turbot/pipe-fittings/v2 v2.7.2 - github.com/turbot/steampipe-plugin-sdk/v5 v5.13.0 + github.com/turbot/pipe-fittings/v2 v2.7.3 + github.com/turbot/steampipe-plugin-sdk/v5 v5.13.2 github.com/turbot/terraform-components v0.0.0-20250114051614-04b806a9cbed github.com/zclconf/go-cty v1.16.3 // indirect golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 @@ -161,7 +161,7 @@ github.com/tklauser/numcpus v0.10.0 // indirect github.com/tkrajina/go-reflector v0.5.8 // indirect github.com/turbot/pipes-sdk-go v0.12.1 // indirect - github.com/ulikunitz/xz v0.5.14 // indirect + github.com/ulikunitz/xz v0.5.15 // indirect github.com/xlab/treeprint v1.2.0 // indirect github.com/zclconf/go-cty-yaml v1.1.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 // indirect diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/steampipe-2.3.5/go.sum new/steampipe-2.3.6/go.sum --- old/steampipe-2.3.5/go.sum 2026-02-06 08:59:35.000000000 +0100 +++ new/steampipe-2.3.6/go.sum 2026-02-20 10:26:24.000000000 +0100 @@ -828,8 +828,8 @@ github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= -github.com/go-git/go-git/v5 v5.16.2 h1:fT6ZIOjE5iEnkzKyxTHK1W4HGAsPhqEqiSAssSO77hM= -github.com/go-git/go-git/v5 v5.16.2/go.mod h1:4Ge4alE/5gPs30F2H1esi2gPd69R0C39lolkucHBOp8= +github.com/go-git/go-git/v5 v5.16.5 h1:mdkuqblwr57kVfXri5TTH+nMFLNUxIj9Z7F5ykFbw5s= +github.com/go-git/go-git/v5 v5.16.5/go.mod h1:QOMLpNf1qxuSY4StA/ArOdfFR2TrKEjJiye2kel2m+M= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -1258,17 +1258,17 @@ github.com/turbot/go-kit v1.3.0/go.mod h1:piKJMYCF8EYmKf+D2B78Csy7kOHGmnQVOWingtLKWWQ= github.com/turbot/go-prompt v0.2.6-steampipe.0.0.20221028122246-eb118ec58d50 h1:zs87uA6QZsYLk4RRxDOIxt8ro/B2V6HzoMWm05Lo7ao= github.com/turbot/go-prompt v0.2.6-steampipe.0.0.20221028122246-eb118ec58d50/go.mod h1:vFnjEGDIIA/Lib7giyE4E9c50Lvl8j0S+7FVlAwDAVw= -github.com/turbot/pipe-fittings/v2 v2.7.2 h1:fwyzK8tlQJK0SNGLxlZORT7pUI6ghkZwM+wIpf+LMW8= -github.com/turbot/pipe-fittings/v2 v2.7.2/go.mod h1:V619+tgfLaqoEXFDNzA2p24TBZVf4IkDL9FDLQecMnE= +github.com/turbot/pipe-fittings/v2 v2.7.3 h1:DacY/pc8zERJYXszkomJCOi1YDK3e2chJ1HEN6GCzgU= +github.com/turbot/pipe-fittings/v2 v2.7.3/go.mod h1:VYqcgGrYDLsGxn1r4dOkkEh5/KDEgJgUU+nf0SAODY0= github.com/turbot/pipes-sdk-go v0.12.1 h1:mF9Z9Mr6F0uqlWjd1mQn+jqT24GPvWDFDrFTvmkazHc= github.com/turbot/pipes-sdk-go v0.12.1/go.mod h1:iQE0ebN74yqiCRrfv7izxVMRcNlZftPWWDPsMFwejt4= -github.com/turbot/steampipe-plugin-sdk/v5 v5.13.0 h1:6GSmiKsPdMd2X1ULK17Q/8UQvNOpyub4F2a5nmMGkis= -github.com/turbot/steampipe-plugin-sdk/v5 v5.13.0/go.mod h1:C4Ogzsd9ea97e7MJF3g/5k/8S0Ec8/iqAlPmr6zGXHA= +github.com/turbot/steampipe-plugin-sdk/v5 v5.13.2 h1:4SSI20DCC0N3ItU1HGytCaxaekQMKpYuMOySezQ32zQ= +github.com/turbot/steampipe-plugin-sdk/v5 v5.13.2/go.mod h1:qmfaXKt9z+TgUaFoKkKzwZAwYA5h2Mf/3yuoc+P6otY= github.com/turbot/terraform-components v0.0.0-20250114051614-04b806a9cbed h1:1ROP+kYJ0vaJu04qpQO5V2PVrUqG7VZmYXzcyP/yDT0= github.com/turbot/terraform-components v0.0.0-20250114051614-04b806a9cbed/go.mod h1:QJMOFtDVHtXLCJr6luh4oFgk6dtdCImDh7XbIXxnGsc= github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= -github.com/ulikunitz/xz v0.5.14 h1:uv/0Bq533iFdnMHZdRBTOlaNMdb1+ZxXIlHDZHIHcvg= -github.com/ulikunitz/xz v0.5.14/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= +github.com/ulikunitz/xz v0.5.15 h1:9DNdB5s+SgV3bQ2ApL10xRc35ck0DuIX/isZvIk+ubY= +github.com/ulikunitz/xz v0.5.15/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ= github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/steampipe-2.3.5/pkg/constants/db.go new/steampipe-2.3.6/pkg/constants/db.go --- old/steampipe-2.3.5/pkg/constants/db.go 2026-02-06 08:59:35.000000000 +0100 +++ new/steampipe-2.3.6/pkg/constants/db.go 2026-02-20 10:26:24.000000000 +0100 @@ -28,7 +28,7 @@ // constants for installing db and fdw images const ( DatabaseVersion = "14.19.0" - FdwVersion = "2.1.4" + FdwVersion = "2.1.5" // PostgresImageRef is the OCI Image ref for the database binaries PostgresImageRef = "ghcr.io/turbot/steampipe/db:14.19.0" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/steampipe-2.3.5/pkg/db/db_client/db_client_execute.go new/steampipe-2.3.6/pkg/db/db_client/db_client_execute.go --- old/steampipe-2.3.5/pkg/db/db_client/db_client_execute.go 2026-02-06 08:59:35.000000000 +0100 +++ new/steampipe-2.3.6/pkg/db/db_client/db_client_execute.go 2026-02-20 10:26:24.000000000 +0100 @@ -272,8 +272,15 @@ func (c *DbClient) startQuery(ctx context.Context, conn *pgx.Conn, query string, args ...any) (rows pgx.Rows, err error) { doneChan := make(chan bool) go func() { - // start asynchronous query - rows, err = conn.Query(ctx, query, args...) + // Request text format for timestamptz so PostgreSQL returns the value + // formatted in the session timezone, matching psql behavior. + // By default pgx uses binary format which loses session timezone info. + queryArgs := make([]any, 0, len(args)+1) + queryArgs = append(queryArgs, pgx.QueryResultFormatsByOID{ + pgtype.TimestamptzOID: pgx.TextFormatCode, + }) + queryArgs = append(queryArgs, args...) + rows, err = conn.Query(ctx, query, queryArgs...) close(doneChan) }() @@ -351,6 +358,26 @@ elements := utils.Map(arr, func(e interface{}) string { return e.(string) }) result[i] = strings.Join(elements, ",") } + case "_DATE": + if arr, ok := columnValue.([]interface{}); ok { + elements := utils.Map(arr, func(e interface{}) string { + if t, ok := e.(time.Time); ok { + return t.Format("2006-01-02") + } + return fmt.Sprintf("%v", e) + }) + result[i] = strings.Join(elements, ",") + } + case "_TIMESTAMPTZ": + if arr, ok := columnValue.([]interface{}); ok { + elements := utils.Map(arr, func(e interface{}) string { + if t, ok := e.(time.Time); ok { + return t.Format(time.RFC3339) + } + return fmt.Sprintf("%v", e) + }) + result[i] = strings.Join(elements, ",") + } case "INET": if inet, ok := columnValue.(netip.Prefix); ok { result[i] = strings.TrimSuffix(inet.String(), "/32") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/steampipe-2.3.5/pkg/db/db_client/db_client_execute_test.go new/steampipe-2.3.6/pkg/db/db_client/db_client_execute_test.go --- old/steampipe-2.3.5/pkg/db/db_client/db_client_execute_test.go 1970-01-01 01:00:00.000000000 +0100 +++ new/steampipe-2.3.6/pkg/db/db_client/db_client_execute_test.go 2026-02-20 10:26:24.000000000 +0100 @@ -0,0 +1,159 @@ +package db_client + +import ( + "os" + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// TestTimestamptzTextFormatImplemented verifies that the timestamptz wire protocol fix is in place. +// Reference: https://github.com/turbot/steampipe/issues/4450 +// +// This test verifies that startQuery uses QueryResultFormatsByOID to request text format +// for timestamptz columns, ensuring PostgreSQL formats values using the session timezone. +// +// Without this fix, pgx uses binary protocol which loses session timezone info, causing +// timestamptz values to display in the local machine timezone instead of the session timezone. +func TestTimestamptzTextFormatImplemented(t *testing.T) { + // Read the db_client_execute.go file to verify the fix is present + content, err := os.ReadFile("db_client_execute.go") + require.NoError(t, err, "should be able to read db_client_execute.go") + + sourceCode := string(content) + + // Verify QueryResultFormatsByOID is used + assert.Contains(t, sourceCode, "pgx.QueryResultFormatsByOID", + "QueryResultFormatsByOID must be used to specify format for specific column types") + + // Verify TimestamptzOID is referenced + assert.Contains(t, sourceCode, "pgtype.TimestamptzOID", + "TimestamptzOID must be specified to request text format for timestamptz columns") + + // Verify TextFormatCode is used + assert.Contains(t, sourceCode, "pgx.TextFormatCode", + "TextFormatCode must be used to request text format") + + // Verify the fix is in startQuery function + funcStart := strings.Index(sourceCode, "func (c *DbClient) startQuery") + assert.NotEqual(t, -1, funcStart, "startQuery function must exist") + + // Extract just the startQuery function for more precise checking + funcEnd := strings.Index(sourceCode[funcStart:], "\nfunc ") + if funcEnd == -1 { + funcEnd = len(sourceCode) + } else { + funcEnd += funcStart + } + startQueryFunc := sourceCode[funcStart:funcEnd] + + // Verify all three components are in startQuery + assert.Contains(t, startQueryFunc, "QueryResultFormatsByOID", + "QueryResultFormatsByOID must be in startQuery function") + assert.Contains(t, startQueryFunc, "TimestamptzOID", + "TimestamptzOID must be in startQuery function") + assert.Contains(t, startQueryFunc, "TextFormatCode", + "TextFormatCode must be in startQuery function") + + // Verify there's a comment explaining the fix + hasComment := strings.Contains(startQueryFunc, "session timezone") || + strings.Contains(startQueryFunc, "text format for timestamptz") || + strings.Contains(startQueryFunc, "Request text format") + assert.True(t, hasComment, + "Comment should explain why text format is needed for timestamptz") + + // Verify queryArgs are constructed and used + assert.Contains(t, startQueryFunc, "queryArgs", + "queryArgs variable must be used to prepend format specification") + assert.Contains(t, startQueryFunc, "conn.Query(ctx, query, queryArgs...)", + "conn.Query must use queryArgs instead of args directly") +} + +// TestTimestamptzFormatCorrectness verifies the format specification structure +func TestTimestamptzFormatCorrectness(t *testing.T) { + content, err := os.ReadFile("db_client_execute.go") + require.NoError(t, err, "should be able to read db_client_execute.go") + + sourceCode := string(content) + + // Verify the QueryResultFormatsByOID is constructed as the first element + // This is critical - it must be the first argument before actual query parameters + assert.Contains(t, sourceCode, "queryArgs := make([]any, 0, len(args)+1)", + "queryArgs must be allocated with capacity for format spec + args") + + // Verify format spec is appended first + lines := strings.Split(sourceCode, "\n") + var foundMake, foundAppendFormat, foundAppendArgs bool + var makeIdx, appendFormatIdx, appendArgsIdx int + + for i, line := range lines { + if strings.Contains(line, "queryArgs := make([]any, 0, len(args)+1)") { + foundMake = true + makeIdx = i + } + if strings.Contains(line, "queryArgs = append(queryArgs, pgx.QueryResultFormatsByOID{") { + foundAppendFormat = true + appendFormatIdx = i + } + if strings.Contains(line, "queryArgs = append(queryArgs, args...)") { + foundAppendArgs = true + appendArgsIdx = i + } + } + + assert.True(t, foundMake, "queryArgs must be allocated") + assert.True(t, foundAppendFormat, "format spec must be appended to queryArgs") + assert.True(t, foundAppendArgs, "original args must be appended to queryArgs") + + // Verify correct order: make -> append format spec -> append args + if foundMake && foundAppendFormat && foundAppendArgs { + assert.Less(t, makeIdx, appendFormatIdx, + "queryArgs must be allocated before appending format spec") + assert.Less(t, appendFormatIdx, appendArgsIdx, + "format spec must be appended before original args") + } +} + +// TestTimestamptzFormatDoesNotAffectOtherTypes verifies only timestamptz format is changed +func TestTimestamptzFormatDoesNotAffectOtherTypes(t *testing.T) { + content, err := os.ReadFile("db_client_execute.go") + require.NoError(t, err, "should be able to read db_client_execute.go") + + sourceCode := string(content) + + // Find the QueryResultFormatsByOID map construction + funcStart := strings.Index(sourceCode, "func (c *DbClient) startQuery") + require.NotEqual(t, -1, funcStart, "startQuery function must exist") + + funcEnd := strings.Index(sourceCode[funcStart:], "\nfunc ") + if funcEnd == -1 { + funcEnd = len(sourceCode) + } else { + funcEnd += funcStart + } + startQueryFunc := sourceCode[funcStart:funcEnd] + + // Verify ONLY TimestamptzOID is in the map (no other OIDs) + // This ensures we don't accidentally change format for other types + otherOIDs := []string{ + "DateOID", + "TimestampOID", + "TimeOID", + "IntervalOID", + "JSONOID", + "JSONBOID", + } + + for _, oid := range otherOIDs { + assert.NotContains(t, startQueryFunc, "pgtype."+oid, + "Should not change format for "+oid+" - only timestamptz needs text format") + } + + // Verify there's only one entry in QueryResultFormatsByOID + // Count how many times we see "OID:" in the map definition + oidCount := strings.Count(startQueryFunc, "OID:") + assert.Equal(t, 1, oidCount, + "QueryResultFormatsByOID should have exactly one entry (TimestamptzOID)") +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/steampipe-2.3.5/tests/acceptance/test_data/templates/expected_6.json new/steampipe-2.3.6/tests/acceptance/test_data/templates/expected_6.json --- old/steampipe-2.3.5/tests/acceptance/test_data/templates/expected_6.json 2026-02-06 08:59:35.000000000 +0100 +++ new/steampipe-2.3.6/tests/acceptance/test_data/templates/expected_6.json 2026-02-20 10:26:24.000000000 +0100 @@ -115,7 +115,7 @@ "booleancolumn": true, "cidrrange": "10.1.2.3/32", "currency": "$922,337,203,685,477.57", - "date1": "1978-02-05 00:00:00", + "date1": "1978-02-05", "floatcolumn": 4.681642125488754, "integercolumn1": 3278, "integercolumn2": 21445454, @@ -147,7 +147,7 @@ "textcolumn3": "This is a very long text for the PostgreSQL text column", "time1": "08:00:00", "timestamp1": "2016-06-22 19:10:25", - "timestamp2": "2016-06-23T07:40:25+05:30", + "timestamp2": "2016-06-23T02:10:25Z", "uuidcolumn": "6948df80-14bd-4e04-8842-7668d9c001f5", "xmldata": "<book><title>Manual</title><chapter>...</chapter></book>" } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/steampipe-2.3.5/tests/acceptance/test_files/date_time_types.bats new/steampipe-2.3.6/tests/acceptance/test_files/date_time_types.bats --- old/steampipe-2.3.5/tests/acceptance/test_files/date_time_types.bats 1970-01-01 01:00:00.000000000 +0100 +++ new/steampipe-2.3.6/tests/acceptance/test_files/date_time_types.bats 2026-02-20 10:26:24.000000000 +0100 @@ -0,0 +1,159 @@ +load "$LIB_BATS_ASSERT/load.bash" +load "$LIB_BATS_SUPPORT/load.bash" + +# Test DATE, TIMESTAMP, TIMESTAMPTZ display formatting +# Verifies fix for issue #4450 + +@test "DATE displays without time component" { + run steampipe query "select '1984-01-01'::date as date_val" --output json + echo "$output" | jq -e '.rows[0].date_val == "1984-01-01"' + assert_success +} + +@test "DATE with table output" { + run steampipe query "select '2024-02-29'::date as leap_date" + assert_output --partial "2024-02-29" + refute_output --partial "00:00:00" +} + +@test "DATE NULL value" { + run steampipe query "select null::date as null_date" --output json + echo "$output" | jq -e '.rows[0].null_date == null' + assert_success +} + +@test "TIMESTAMPTZ displays with UTC timezone" { + run steampipe query "select '1984-01-01T00:00:00Z'::timestamptz as tstz" --output json + echo "$output" | jq -e '.rows[0].tstz == "1984-01-01T00:00:00Z"' + assert_success +} + +@test "TIMESTAMPTZ with table output" { + run steampipe query "select '2024-01-15T10:30:45Z'::timestamptz as tstz" + assert_output --partial "2024-01-15T10:30:45Z" +} + +@test "TIMESTAMPTZ respects session timezone" { + # Default session timezone is UTC + run steampipe query "show timezone" --output json + echo "$output" | jq -e '.rows[0].TimeZone == "UTC"' + assert_success +} + +@test "TIMESTAMPTZ NULL value" { + run steampipe query "select null::timestamptz as null_tstz" --output json + echo "$output" | jq -e '.rows[0].null_tstz == null' + assert_success +} + +@test "TIMESTAMP displays without timezone" { + run steampipe query "select '1984-01-01 12:30:45'::timestamp as ts" --output json + echo "$output" | jq -e '.rows[0].ts == "1984-01-01 12:30:45"' + assert_success +} + +@test "TIME displays correctly" { + run steampipe query "select '15:30:45'::time as time_val" --output json + echo "$output" | jq -e '.rows[0].time_val == "15:30:45"' + assert_success +} + +@test "INTERVAL displays correctly" { + run steampipe query "select '1 year 2 months 3 days'::interval as interval_val" + assert_output --partial "1 year 2 mons 3 days" +} + +@test "Multiple date/time types together" { + run steampipe query "select '2024-01-15'::date as d, '2024-01-15 10:30:00'::timestamp as ts, '2024-01-15T10:30:00Z'::timestamptz as tstz" --output json + + # Verify DATE has no time component + echo "$output" | jq -e '.rows[0].d == "2024-01-15"' + assert_success + + # Verify TIMESTAMP has time but no timezone + echo "$output" | jq -e '.rows[0].ts == "2024-01-15 10:30:00"' + assert_success + + # Verify TIMESTAMPTZ has timezone + echo "$output" | jq -e '.rows[0].tstz == "2024-01-15T10:30:00Z"' + assert_success +} + +@test "DATE CSV output" { + run steampipe query "select '1984-01-01'::date as date_val" --output csv + assert_output --partial "date_val" + assert_output --partial "1984-01-01" + refute_output --partial "00:00:00" +} + +@test "TIMESTAMPTZ CSV output" { + run steampipe query "select '1984-01-01T00:00:00Z'::timestamptz as tstz" --output csv + assert_output --partial "tstz" + assert_output --partial "1984-01-01T00:00:00Z" +} + +@test "DATE line output" { + run steampipe query "select '1984-01-01'::date as date_val" --output line + assert_output --partial "date_val" + assert_output --partial "1984-01-01" + refute_output --partial "00:00:00" +} + +@test "DATE array" { + run steampipe query "select array['2024-01-01'::date, '2024-12-31'::date] as date_array" --output json + # Array format may vary, just verify it contains the dates without time component + echo "$output" | jq -r '.rows[0].date_array' | grep "2024-01-01" + assert_success + echo "$output" | jq -r '.rows[0].date_array' | grep "2024-12-31" + assert_success + # Verify no time component in array values + refute_output --partial "00:00:00" +} + +@test "TIMESTAMPTZ edge case - leap year" { + run steampipe query "select '2024-02-29T23:59:59Z'::timestamptz as leap_tstz" --output json + echo "$output" | jq -e '.rows[0].leap_tstz == "2024-02-29T23:59:59Z"' + assert_success +} + +@test "TIMESTAMPTZ edge case - year 1" { + run steampipe query "select '0001-01-01T00:00:00Z'::timestamptz as min_tstz" --output json + assert_success +} + +@test "DATE comparison preserves semantics" { + # Verify that DATE values can be compared correctly + run steampipe query "select ('2024-01-15'::date > '2024-01-01'::date) as result" --output json + echo "$output" | jq -e '.rows[0].result == true' + assert_success +} + +@test "now() returns timestamptz in UTC" { + run steampipe query "select now() as now_val" --output json + # Should end with Z or +00:00 or +00 (UTC timezone indicators) + # Extract the value and check it contains UTC timezone marker + now_val=$(echo "$output" | jq -r '.rows[0].now_val') + echo "now() returned: $now_val" + # Check for Z suffix or +00:00 or +00 offset + echo "$now_val" | grep -E '(Z|(\+|-)?00:?00)$' + assert_success +} + +@test "current_date returns date without time" { + run steampipe query "select current_date as today" --output json + # Should not contain time component (no colons for time) + today=$(echo "$output" | jq -r '.rows[0].today') + echo "current_date returned: $today" + # Verify it matches YYYY-MM-DD format without time + echo "$today" | grep -E '^[0-9]{4}-[0-9]{2}-[0-9]{2}$' + assert_success +} + +function teardown_file() { + # list running processes + ps -ef | grep steampipe + + # check if any processes are running + num=$(ps aux | grep steampipe | grep -v bats | grep -v grep | grep -v tests/acceptance | wc -l | tr -d ' ') + assert_equal $num 0 +} ++++++ steampipe.obsinfo ++++++ --- /var/tmp/diff_new_pack.NSEv7P/_old 2026-02-24 15:40:00.161905774 +0100 +++ /var/tmp/diff_new_pack.NSEv7P/_new 2026-02-24 15:40:00.169906105 +0100 @@ -1,5 +1,5 @@ name: steampipe -version: 2.3.5 -mtime: 1770364775 -commit: a4b708bcc1939d72853932b78db51ba8d92227ac +version: 2.3.6 +mtime: 1771579584 +commit: b713a4cdc6a1799531d0f8192713b6b7f6b3725e ++++++ vendor.tar.gz ++++++ /work/SRC/openSUSE:Factory/steampipe/vendor.tar.gz /work/SRC/openSUSE:Factory/.steampipe.new.1977/vendor.tar.gz differ: char 27, line 1
