Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 78 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
name: CI

on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
test:
runs-on: ${{ matrix.os }}
container: ${{ matrix.container }}
name: ${{ matrix.label }}
defaults:
run:
shell: bash
# Mirror alexzhangs/xsh's shell matrix: bash 3.2 / 4.4 / 5.x + zsh 5.x.
# Utilities run under zsh's ksh emulation (provided by xsh >= 0.7.0).
strategy:
fail-fast: false
matrix:
include:
- { os: ubuntu-latest, container: '', shell_path: /bin/bash, label: bash-5.x-linux }
- { os: macos-latest, container: '', shell_path: /bin/bash, label: bash-3.2-macos }
- { os: macos-latest, container: '', shell_path: brew, label: bash-5.x-macos }
- { os: ubuntu-latest, container: rockylinux:8, shell_path: /bin/bash, label: bash-4.4-linux }
- { os: macos-latest, container: '', shell_path: /bin/zsh, label: zsh-5.x-macos }
- { os: ubuntu-latest, container: '', shell_path: /usr/bin/zsh, label: zsh-5.x-linux }

steps:
- name: Pre-install git (rockylinux container)
if: matrix.container != ''
run: dnf install -y git

- name: Install dependencies (rockylinux container)
if: matrix.container != ''
run: |
dnf install -y --allowerasing coreutils
dnf install -y gawk sed file findutils diffutils procps-ng which tar gzip python3
command -v python >/dev/null 2>&1 || ln -sf "$(command -v python3)" /usr/local/bin/python
git config --global --add safe.directory '*'

- name: Install zsh (Linux)
if: matrix.container == '' && runner.os == 'Linux' && contains(matrix.shell_path, 'zsh')
run: sudo apt-get update && sudo apt-get install -y zsh

- name: Install Homebrew bash (macOS bash 5.x)
if: matrix.shell_path == 'brew'
run: |
brew install bash
echo "SHELL_PATH=$(brew --prefix)/bin/bash" >> "$GITHUB_ENV"

- name: Set shell path
if: matrix.shell_path != 'brew'
run: echo "SHELL_PATH=${{ matrix.shell_path }}" >> "$GITHUB_ENV"

- name: Install xsh
run: |
git clone --depth=50 --branch=master https://github.com/alexzhangs/xsh.git /tmp/xsh
bash /tmp/xsh/install.sh

- name: Load dependency library xsh-lib/core
run: |
source ~/.xshrc
xsh load xsh-lib/core

- name: Load library from current branch
run: |
source ~/.xshrc
# Fork PRs fall back to the default branch (the source branch isn't on
# the main repo).
xsh load -b "${{ github.head_ref || github.ref_name }}" xsh-lib/xsql || xsh load xsh-lib/xsql

- name: Run tests (${{ matrix.label }})
# test.sh self-sources ~/.xshrc, so running it under $SHELL_PATH makes
# the utilities execute under that shell (bash, or zsh's ksh emulation).
run: |
"$SHELL_PATH" ~/.xsh/repo/xsh-lib/xsql/test.sh
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +0,0 @@
.idea/
24 changes: 20 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
[![GitHub tag](https://img.shields.io/github/v/tag/xsh-lib/xsql?sort=date)](https://github.com/xsh-lib/xsql/tags)
[![GitHub](https://img.shields.io/github/license/xsh-lib/xsql.svg?style=flat-square)](https://github.com/xsh-lib/xsql/)
[![GitHub last commit](https://img.shields.io/github/last-commit/xsh-lib/xsql.svg?style=flat-square)](https://github.com/xsh-lib/xsql/commits/main)

[![CI](https://github.com/xsh-lib/xsql/actions/workflows/ci.yml/badge.svg)](https://github.com/xsh-lib/xsql/actions/workflows/ci.yml)
[![CodeFactor](https://www.codefactor.io/repository/github/xsh-lib/xsql/badge)](https://www.codefactor.io/repository/github/xsh-lib/xsql)
[![GitHub issues](https://img.shields.io/github/issues/xsh-lib/xsql.svg?style=flat-square)](https://github.com/xsh-lib/xsql/issues)
[![GitHub pull requests](https://img.shields.io/github/issues-pr/xsh-lib/xsql.svg?style=flat-square)](https://github.com/xsh-lib/xsql/pulls)

# xsh-lib/xsql

xsh Library - xsh SQL, a pseudo SQL interpreter for Bash..
Expand All @@ -6,11 +15,18 @@ About xsh and its libraries, check out [xsh document](https://github.com/alexzha

## Requirements

1. bash
`xsh-lib/xsql` is tested in CI ([GitHub Actions](https://github.com/xsh-lib/xsql/actions/workflows/ci.yml)) on every push and pull request, across the following shell/OS combinations:

| Shell | Version | OS | Tested |
|-------|---------|-----------------------|:------:|
| bash | 3.2 | macOS | ✅ |
| bash | 4.4 | Linux (rockylinux:8) | ✅ |
| bash | 5.x | Linux (ubuntu-latest) | ✅ |
| bash | 5.x | macOS (Homebrew) | ✅ |
| zsh | 5.x | Linux (ubuntu-latest) | ✅ |
| zsh | 5.x | macOS | ✅ |

Tested with bash:
* 4.3.48 on Linux
* 3.2.57 on macOS
zsh utilities run under xsh's ksh emulation and require **xsh ≥ 0.7.0**.

## Dependency

Expand Down
5 changes: 4 additions & 1 deletion functions/query.sh
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,10 @@ function query () {
printf "%s" "$XSQL_QUERY_OFS"
fi
declare varname="XSQL_QUERY_FIELDS_${qf_name}_ROWS[$row_index]"
printf "%s" "${!varname}"
# `${!varname}` array-element indirection is bash-only; `eval` does
# it portably (the name is built from controlled prefixes + parsed
# field names / numeric indices)
eval "printf '%s' \"\${${varname}}\""
i=$((i + 1))
done
echo
Expand Down
8 changes: 6 additions & 2 deletions functions/query/parser.sh
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,12 @@ function parser () {
*)
case $clause in
'SELECT')
# parse the field list into array
IFS=$', ' read -r -a fields <<< "$1"
# parse the comma/space-separated field list into the
# array. `read -a` is bash-only (zsh's read has no -a);
# field names contain no spaces, so replacing commas
# with spaces and word-splitting is portable.
# shellcheck disable=SC2206
fields=( ${1//,/ } )
XSQL_QUERY_SELECTED_FIELDS+=( "${fields[@]}" )
;;
'FROM')
Expand Down
79 changes: 79 additions & 0 deletions test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
#!/bin/bash
#
# Tests for xsh-lib/xsql. Plain assertions, no external test framework.
#
# Usage:
# xsh load xsh-lib/core xsh-lib/xsql # one-time
# bash test.sh # or: zsh test.sh
#

# Make the `xsh` function available when run as a child process. Under bash it
# is inherited as an exported function (no-op here); zsh cannot export functions,
# so a child `zsh test.sh` sources ~/.xshrc to define xsh as a real zsh function.
if ! type xsh 2>/dev/null | grep -q 'function'; then
# shellcheck source=/dev/null
. ~/.xshrc
fi

# No `set -e`: query ends in a getopts/return path that can yield a non-zero
# status on success, which zsh's ERR_EXIT would trip inside `$()`. Tally
# failures explicitly instead. Array assertions use `[*]` (joins regardless of
# index base) so they're identical under bash (0-indexed) and zsh (1-indexed).
__fails=0
assert_eq () { # <desc> <expected> <actual>
if [ "$2" = "$3" ]; then printf 'ok - %s\n' "$1"
else printf 'FAIL - %s: expected [%s], got [%s]\n' "$1" "$2" "$3" >&2; __fails=$((__fails + 1)); fi
}
assert_rc_nonzero () { # <desc> <cmd...>
if "${@:2}" >/dev/null 2>&1; then
printf 'FAIL - %s: expected non-zero exit\n' "$1" >&2; __fails=$((__fails + 1))
else printf 'ok - %s\n' "$1"; fi
}

xsh log info "xsh list xsql/"
xsh list 'xsql/*' >/dev/null

xsh log info "import-smoke: all xsql function utilities"
while read -r __lpue; do
[ -z "$__lpue" ] && continue
xsh import "$__lpue" || { printf 'FAIL - import %s\n' "$__lpue" >&2; __fails=$((__fails + 1)); }
done < <(xsh list 'xsql/*' | awk '$1 == "[functions]" {print $2}')
printf 'ok - import-smoke\n'

# ----------------------------------------------------------------------------
# xsql/query/parser — parses a SQL expression into XSQL_QUERY_* globals
# (exercises the comma/space field-list split ported off bash-only `read -a`)
# ----------------------------------------------------------------------------
xsh log info "xsql/query/parser"
xsh xsql/query/parser select f1,f2 from A where f1 = x
assert_eq "parser TABLE" "A" "${XSQL_QUERY_TABLE}"
assert_eq "parser FIELDS" "f1 f2" "${XSQL_QUERY_SELECTED_FIELDS[*]}"
assert_eq "parser WHERE" "f1 = x" "${XSQL_QUERY_WHERE[*]}"
assert_rc_nonzero "parser errors without a FROM table" xsh xsql/query/parser select f1

# ----------------------------------------------------------------------------
# xsql/query — query a text-file "table" (exercises the ${!varname} array
# indirection ported to portable eval)
# ----------------------------------------------------------------------------
xsh log info "xsql/query"
__tbl=$(mktemp "${TMPDIR:-/tmp}/xsh-xsql-test.XXXXXXXX")
printf 'id name age\n1 alice 30\n2 bob 25\n' > "$__tbl"

assert_eq "query select+where (-O ,)" "name,age
bob,25" "$(xsh xsql/query -H -O , select name,age from "$__tbl" where id = 2 2>/dev/null || true)"

assert_eq "query select all rows (-O ,)" "alice
bob" "$(xsh xsql/query -O , select name from "$__tbl" 2>/dev/null || true)"

assert_eq "query multi-field row order" "bob,25" \
"$(xsh xsql/query -O , select name,age from "$__tbl" where name = bob 2>/dev/null || true)"
rm -f "$__tbl"

echo
if [ "$__fails" -eq 0 ]; then
xsh log info "xsql tests: all passed"
exit 0
else
xsh log error "xsql tests: ${__fails} failure(s)"
exit 1
fi