Compare commits

...

12 Commits

Author SHA1 Message Date
69e3362d0d feat: new version v2.9.0 (#4204) 2025-03-17 15:52:26 +08:00
9a61a6970f fix(database/gdb): fix transaction propagation feature (#4199) 2025-03-17 14:50:07 +08:00
abf77fac50 fix(cmd/gf): invalid binary suffix after installing binary using custom renamed file name that has suffix with . character (#4207) 2025-03-17 13:48:54 +08:00
07696fc779 feat(net/ghttp): add GetMetaTag function to retrieve metadata value for HandlerItem (#4206) 2025-03-17 09:21:00 +08:00
bc1e1019c5 refract(util/gconv): change Converter interface definition for more convenient usage (#4202) 2025-03-14 18:23:07 +08:00
bb696bb281 refract(net/ghttp): move Request.GetMetaTag to HandlerItemParsed.GetMetaTag (#4191) 2025-03-12 21:55:35 +08:00
029f324c5c feat: version v2.9.0-beta (#4189) 2025-03-09 22:31:20 +08:00
f8331bad6e feat(net/ghttp): add Request.GetMetaTag to retrieve specific meta tag value (#4185) 2025-03-09 11:17:41 +08:00
bcda48bf82 fix(net/ghttp):check parameter existence to determine using default or front-end value. (#4182) 2025-03-08 20:56:27 +08:00
a8a055f122 fix(util/gvalid): lots of memory consumed when required validation on big binary field (#4097) 2025-03-07 13:52:12 +08:00
dfe088f5cd refactor(util/gconv): add Converter feature for more flexable and extensible type converting (#4107) 2025-03-06 23:04:26 +08:00
f4074cd815 fix(net/gclient): remove default discovery for gclient when Discovery feature enabled (#4174) 2025-03-03 16:43:21 +08:00
154 changed files with 7847 additions and 5307 deletions

View File

@ -1,16 +1,16 @@
**Please ensure you adhere to every item in this list.**
+ The PR title is formatted as follows: `<type>[optional scope]: <description>` For example, `fix(os/gtime): fix time zone issue`
+ `<type>` is mandatory and can be one of `fix`, `feat`, `build`, `ci`, `docs`, `style`, `refactor`, `perf`, `test`, `chore`
+ fix: Used when a bug has been fixed.
+ feat: Used when a new feature has been added.
+ build: Used for modifications to the project build system, such as changes to dependencies, external interfaces, or upgrading Node version.
+ ci: Used for modifications to continuous integration processes, such as changes to Travis, Jenkins workflow configurations.
+ docs: Used for modifications to documentation, such as changes to README files, API documentation, etc.
+ style: Used for changes to code style, such as adjustments to indentation, spaces, blank lines, etc.
+ refactor: Used for code refactoring, such as changes to code structure, variable names, function names, without altering functionality.
+ perf: Used for performance optimization, such as improving code performance, reducing memory usage, etc.
+ test: Used for modifications to test cases, such as adding, deleting, or modifying test cases for code.
+ chore: Used for modifications to non-business-related code, such as changes to build processes or tool configurations.
+ `fix`: Used when a bug has been fixed.
+ `feat`: Used when a new feature has been added.
+ `build`: Used for modifications to the project build system, such as changes to dependencies, external interfaces, or upgrading Node version.
+ `ci`: Used for modifications to continuous integration processes, such as changes to Travis, Jenkins workflow configurations.
+ `docs`: Used for modifications to documentation, such as changes to README files, API documentation, etc.
+ `style`: Used for changes to code style, such as adjustments to indentation, spaces, blank lines, etc.
+ `refactor`: Used for code refactoring, such as changes to code structure, variable names, function names, without altering functionality.
+ `perf`: Used for performance optimization, such as improving code performance, reducing memory usage, etc.
+ `test`: Used for modifications to test cases, such as adding, deleting, or modifying test cases for code.
+ `chore`: Used for modifications to non-business-related code, such as changes to build processes or tool configurations.
+ After `<type>`, specify the affected package name or scope in parentheses, for example, `(os/gtime)`.
+ The part after the colon uses the verb tense + phrase that completes the blank in
+ Lowercase verb after the colon

View File

@ -239,15 +239,15 @@ jobs:
export PATH="$PATH:$(go env GOPATH)/bin"
- name: Before Script
run: bash .github/workflows/before_script.sh
run: bash .github/workflows/scripts/before_script.sh
- name: Build & Test
if: ${{ (github.event_name == 'push' && github.ref != 'refs/heads/master') || github.event_name == 'pull_request' }}
run: bash .github/workflows/ci-main.sh
run: bash .github/workflows/scripts/ci-main.sh
- name: Build & Test & Coverage
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }}
run: bash .github/workflows/ci-main.sh coverage
run: bash .github/workflows/scripts/ci-main.sh coverage
- name: Stop Redis Cluster Containers
run: docker compose -f ".github/workflows/redis/docker-compose.yml" down

View File

@ -64,9 +64,9 @@ jobs:
cache-dependency-path: '**/go.sum'
- name: Before Script
run: bash .github/workflows/before_script.sh
run: bash .github/workflows/scripts/before_script.sh
- name: Build & Test
run: bash .github/workflows/ci-sub.sh
run: bash .github/workflows/scripts/ci-sub.sh

View File

@ -1,38 +0,0 @@
name: Deploy to GitHub Pages
on:
push:
branches:
- 'doc-build'
schedule:
- cron: '0 15 * * *'
jobs:
deploy:
name: Deploy to GitHub Pages
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: doc-build
- uses: actions/setup-node@v4
with:
node-version: 18
cache: npm
- name: Set Up Golang Environment
uses: actions/setup-go@v5
with:
go-version: 1.23.4
cache: false
- name: download goframe docs
run: ./download.sh
- name: Install dependencies
run: npm ci
- name: Build website
run: npm run build
- name: Deploy to GitHub Pages
uses: peaceiris/actions-gh-pages@v4
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./build
cname: pages.goframe.org

View File

@ -23,6 +23,6 @@ jobs:
with:
actions: 'check-inactive'
inactive-label: 'inactive'
inactive-day: 7
inactive-day: 30
issue-state: open
exclude-labels: 'bug,planned,$exclude-empty'

View File

@ -3,7 +3,10 @@
coverage=$1
# update code of submodules
make subup
git clone https://github.com/gogf/examples
# update go.mod in examples directory to replace github.com/gogf/gf packages with local directory
bash .github/workflows/scripts/replace_examples_gomod.sh
# find all path that contains go.mod.
for file in `find . -name go.mod`; do
@ -22,14 +25,14 @@ for file in `find . -name go.mod`; do
fi
# Check if it's a contrib directory or examples directory
if [[ $dirpath =~ "/contrib/" ]] || [ "examples" = $(basename $dirpath) ]; then
if [[ $dirpath =~ "/contrib/" ]] || [[ $dirpath =~ "/examples/" ]]; then
# Check if go version meets the requirement
if ! go version | grep -qE "go${LATEST_GO_VERSION}"; then
echo "ignore path $dirpath as go version is not ${LATEST_GO_VERSION}: $(go version)"
continue 1
fi
# If it's examples directory, only build without tests
if [ "examples" = $(basename $dirpath) ]; then
if [[ $dirpath =~ "/examples/" ]]; then
echo "the examples directory only needs to be built, not unit tests and coverage tests."
cd $dirpath
go mod tidy

View File

@ -0,0 +1,81 @@
#!/usr/bin/env bash
# Get the absolute path to the repository root
repo_root=$(pwd)
workdir=$repo_root/examples
echo "Prepare to process go.mod files in the ${workdir} directory"
# Check if examples directory exists
if [ ! -d "${workdir}" ]; then
echo "Error: examples directory not found at ${workdir}"
exit 1
fi
# Check if find command is available
if ! command -v find &> /dev/null; then
echo "Error: find command not found!"
exit 1
fi
for file in `find ${workdir} -name go.mod`; do
goModPath=$(dirname $file)
echo ""
echo "Processing dir: $goModPath"
# Calculate relative path to root
# First get the relative path from go.mod to repo root
relativePath=""
current="$goModPath"
while [ "$current" != "$repo_root" ]; do
relativePath="../$relativePath"
current=$(dirname "$current")
done
relativePath=${relativePath%/} # Remove trailing slash
echo "Relative path to root: $relativePath"
# Get all github.com/gogf/gf dependencies
# Use awk to get package names without version numbers
dependencies=$(awk '/^[[:space:]]*github\.com\/gogf\/gf\// {print $1}' "$file" | sort -u)
if [ -n "$dependencies" ]; then
echo "Found GoFrame dependencies:"
echo "$dependencies"
echo "Adding replace directives..."
# Create temporary file
temp_file="${file}.tmp"
# Remove existing replace directives and copy to temp file
sed '/^replace.*github\.com\/gogf\/gf.*/d' "$file" > "$temp_file"
# Add new replace block
echo "" >> "$temp_file"
echo "replace (" >> "$temp_file"
while IFS= read -r dep; do
# Skip empty lines
[ -z "$dep" ] && continue
# Calculate the relative path for the replacement
if [[ "$dep" == "github.com/gogf/gf/v2" ]]; then
replacement="$relativePath"
else
# Extract the path after v2 and remove trailing version
subpath=$(echo "$dep" | sed -E 's/github\.com\/gogf\/gf\/(contrib\/[^/]+\/[^/]+)\/v2.*/\1/')
replacement="$relativePath/$subpath"
fi
echo " $dep => $replacement/" >> "$temp_file"
done <<< "$dependencies"
echo ")" >> "$temp_file"
# Replace original file with temporary file
mv "$temp_file" "$file"
echo "Replace directives added to $file"
else
echo "No GoFrame dependencies found in $file"
fi
done
echo "\nAll go.mod files have been processed successfully."

1
.gitignore vendored
View File

@ -23,3 +23,4 @@ go.work.sum
node_modules
.docusaurus
output
.example/

33
.make_tidy.sh Executable file
View File

@ -0,0 +1,33 @@
#!/usr/bin/env bash
workdir=.
echo "Prepare to tidy all go.mod files in the ${workdir} directory"
# check find command support or not
output=$(find "${workdir}" -name go.mod 2>&1)
if [[ $? -ne 0 ]]; then
echo "Error: please use bash or zsh to run!"
exit 1
fi
for file in `find ${workdir} -name go.mod`; do
goModPath=$(dirname $file)
echo ""
echo "processing dir: $goModPath"
if [[ $goModPath =~ "/testdata/" ]]; then
echo "ignore testdata path $goModPath"
continue 1
fi
if [[ $goModPath =~ "/examples/" ]]; then
echo "ignore examples path $goModPath"
continue 1
fi
cd $goModPath
go mod tidy
# Remove toolchain line if exists
sed -i '' '/^toolchain/d' go.mod
cd - > /dev/null
done

View File

@ -17,7 +17,7 @@ fi
workdir=.
newVersion=$2
echo "Prepare to replace the GF library version numbers in all go.mod files in the ${workdir} directory with ${newVersion}"
echo "Prepare to replace the GoFrame library version numbers in all go.mod files in the ${workdir} directory with ${newVersion}"
# check find command support or not
output=$(find "${workdir}" -name go.mod 2>&1)
@ -49,6 +49,11 @@ for file in `find ${workdir} -name go.mod`; do
continue 1
fi
if [[ $goModPath =~ "/examples/" ]]; then
echo "ignore examples path $goModPath"
continue 1
fi
cd $goModPath
if [ $goModPath = "./cmd/gf" ]; then
mv go.work go.work.version.bak
@ -59,15 +64,18 @@ for file in `find ${workdir} -name go.mod`; do
go mod edit -replace github.com/gogf/gf/contrib/drivers/oracle/v2=../../contrib/drivers/oracle
go mod edit -replace github.com/gogf/gf/contrib/drivers/pgsql/v2=../../contrib/drivers/pgsql
go mod edit -replace github.com/gogf/gf/contrib/drivers/sqlite/v2=../../contrib/drivers/sqlite
# else
# cd -
# continue 1
fi
go mod tidy
# Upgrading only GF related libraries, sometimes even if a version number is specified, it may not be possible to successfully upgrade. Please confirm before submitting the code
# Remove toolchain line if exists
sed -i '' '/^toolchain/d' go.mod
# Upgrading only GoFrame related libraries, sometimes even if a version number is specified,
# it may not be possible to successfully upgrade. Please confirm before submitting the code
go list -f "{{if and (not .Indirect) (not .Main)}}{{.Path}}@${newVersion}{{end}}" -m all | grep "^github.com/gogf/gf"
go list -f "{{if and (not .Indirect) (not .Main)}}{{.Path}}@${newVersion}{{end}}" -m all | grep "^github.com/gogf/gf" | xargs -L1 go get -v
go mod tidy
# Remove toolchain line if exists
sed -i '' '/^toolchain/d' go.mod
if [ $goModPath = "./cmd/gf" ]; then
go mod edit -dropreplace github.com/gogf/gf/v2
go mod edit -dropreplace github.com/gogf/gf/contrib/drivers/clickhouse/v2

View File

@ -3,17 +3,7 @@ SHELL := /bin/bash
# execute "go mod tidy" on all folders that have go.mod file
.PHONY: tidy
tidy:
$(eval files=$(shell find . -name go.mod))
@set -e; \
for file in ${files}; do \
goModPath=$$(dirname $$file); \
if ! echo $$goModPath | grep -q "testdata"; then \
echo "handle: $$goModPath"; \
cd $$goModPath; \
go mod tidy; \
cd -; \
fi \
done
./.make_tidy.sh
# execute "golangci-lint" to check code style
.PHONY: lint
@ -25,7 +15,7 @@ lint:
version:
@set -e; \
newVersion=$(to); \
./.set_version.sh ./ $$newVersion; \
./.make_version.sh ./ $$newVersion; \
echo "make version to=$(to) done"
@ -33,10 +23,9 @@ version:
.PHONY: subup
subup:
@set -e; \
cd examples; \
echo "Updating submodules..."; \
git pull origin; \
cd ..;
git submodule init;\
git submodule update;
# update and commit submodules
.PHONY: subsync

View File

@ -36,7 +36,7 @@ A powerful framework for faster, easier, and more efficient project development.
💖 [Thanks to all the contributors who made GoFrame possible](https://github.com/gogf/gf/graphs/contributors) 💖
<a href="https://github.com/gogf/gf/graphs/contributors">
<img src="https://goframe.org/img/contributors.svg?version=v2.8.3" alt="goframe contributors"/>
<img src="https://goframe.org/img/contributors.svg?version=v2.9.0" alt="goframe contributors"/>
</a>
# License

View File

@ -1,15 +1,15 @@
module github.com/gogf/gf/cmd/gf/v2
go 1.20
go 1.22
require (
github.com/gogf/gf/contrib/drivers/clickhouse/v2 v2.8.3
github.com/gogf/gf/contrib/drivers/mssql/v2 v2.8.3
github.com/gogf/gf/contrib/drivers/mysql/v2 v2.8.3
github.com/gogf/gf/contrib/drivers/oracle/v2 v2.8.3
github.com/gogf/gf/contrib/drivers/pgsql/v2 v2.8.3
github.com/gogf/gf/contrib/drivers/sqlite/v2 v2.8.3
github.com/gogf/gf/v2 v2.8.3
github.com/gogf/gf/contrib/drivers/clickhouse/v2 v2.9.0
github.com/gogf/gf/contrib/drivers/mssql/v2 v2.9.0
github.com/gogf/gf/contrib/drivers/mysql/v2 v2.9.0
github.com/gogf/gf/contrib/drivers/oracle/v2 v2.9.0
github.com/gogf/gf/contrib/drivers/pgsql/v2 v2.9.0
github.com/gogf/gf/contrib/drivers/sqlite/v2 v2.9.0
github.com/gogf/gf/v2 v2.9.0
github.com/gogf/selfupdate v0.0.0-20231215043001-5c48c528462f
github.com/olekukonko/tablewriter v0.0.5
github.com/schollz/progressbar/v3 v3.15.0
@ -48,10 +48,10 @@ require (
github.com/rivo/uniseg v0.4.7 // indirect
github.com/shopspring/decimal v1.3.1 // indirect
github.com/sijms/go-ora/v2 v2.7.10 // indirect
go.opentelemetry.io/otel v1.24.0 // indirect
go.opentelemetry.io/otel/metric v1.24.0 // indirect
go.opentelemetry.io/otel/sdk v1.24.0 // indirect
go.opentelemetry.io/otel/trace v1.24.0 // indirect
go.opentelemetry.io/otel v1.32.0 // indirect
go.opentelemetry.io/otel/metric v1.32.0 // indirect
go.opentelemetry.io/otel/sdk v1.32.0 // indirect
go.opentelemetry.io/otel/trace v1.32.0 // indirect
golang.org/x/crypto v0.31.0 // indirect
golang.org/x/net v0.33.0 // indirect
golang.org/x/sync v0.10.0 // indirect

View File

@ -1,11 +1,17 @@
aead.dev/minisign v0.2.0 h1:kAWrq/hBRu4AARY6AlciO83xhNnW9UaC8YipS2uhLPk=
aead.dev/minisign v0.2.0/go.mod h1:zdq6LdSd9TbuSxchxwhpA9zEb9YXcVGoE8JakuiGaIQ=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.1 h1:lGlwhPtrX6EVml1hO0ivjkUxsSyl4dsiw9qcA1k/3IQ=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.1/go.mod h1:RKUqNu35KJYcVG/fqTRqmuXJZYNhYkBrnC/hX7yGbTA=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.1 h1:sO0/P7g68FrryJzljemN+6GTssUXdANk6aJ7T1ZxnsQ=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.1/go.mod h1:h8hyGFDsU5HMivxiS2iYFZsgDbU9OnnJ163x5UGVKYo=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.1 h1:6oNBlSdi1QqM1PNW7FPA6xOGA5UNsXnkaYZz9vdPGhA=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.1/go.mod h1:s4kgfzA0covAXNicZHDMN58jExvcng2mC/DepXiF1EI=
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.0.1 h1:MyVTgWR8qd/Jw1Le0NZebGBUCLbtak3bJ3z1OlqZBpw=
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.0.1/go.mod h1:GpPjLhVR9dnUoJMyHWSPy71xY9/lcmpzIPZXmF0FCVY=
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.0.0 h1:D3occbWoio4EBLkbkevetNMAVX197GkzbUMtqjGWn80=
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.0.0/go.mod h1:bTSOgj05NGRuHHhQwAdPnYr9TOdNmKlZTgGLL6nyAdI=
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.1 h1:DzHpqpoJVaCgOUdVHxE8QB52S6NiVdDQvGlny1qvPqA=
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.1/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/ClickHouse/clickhouse-go v1.5.4/go.mod h1:EaI/sW7Azgz9UATzd5ZdZHRUhHgv5+JMS9NSr2smCJI=
@ -40,24 +46,11 @@ github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiU
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
github.com/gogf/gf/contrib/drivers/clickhouse/v2 v2.8.3 h1:b/AQMTxiHKPHsidEdk471AC5pkfoK88a5cPmKnzE53U=
github.com/gogf/gf/contrib/drivers/clickhouse/v2 v2.8.3/go.mod h1:qYrF+x5urXLhce3pMcUAyccIsw3Oec0htynoDE4Boi4=
github.com/gogf/gf/contrib/drivers/mssql/v2 v2.8.3 h1:F7Gt1y6YsYOIvgrUlRK07H29BL77dEgLPXilTqqVC80=
github.com/gogf/gf/contrib/drivers/mssql/v2 v2.8.3/go.mod h1:K5prIMZwHANSZrqZbfm6PoEIMfLtd0PwR7u+hZD9HFs=
github.com/gogf/gf/contrib/drivers/mysql/v2 v2.8.3 h1:RtoBg5HWACFrgIrFkpzH94kxSd5EWefNAq5k6olNY6c=
github.com/gogf/gf/contrib/drivers/mysql/v2 v2.8.3/go.mod h1:elZjckHRCejwml5Kdx2zfhOUDiAV3r5i4BgXcKAeH00=
github.com/gogf/gf/contrib/drivers/oracle/v2 v2.8.3 h1:10/RCoWmvQ6PSm+leoS6CsKijH4dB38HOXLgP5+aScQ=
github.com/gogf/gf/contrib/drivers/oracle/v2 v2.8.3/go.mod h1:XSaHf3/vTlzj/zioUbzKmaffPuoKvPV639fT91caheM=
github.com/gogf/gf/contrib/drivers/pgsql/v2 v2.8.3 h1:DvpoiVac1cwGVDTqC6wzFbDb+gXNzcceRgZUIcuTmaI=
github.com/gogf/gf/contrib/drivers/pgsql/v2 v2.8.3/go.mod h1:zugvYVb6c/X9rJ8Gb6b5WkMe+bFz2BsxQ5OLf4RSZos=
github.com/gogf/gf/contrib/drivers/sqlite/v2 v2.8.3 h1:3pdibfm4UOiTGGh6UD8jfMyGZBGH9ikrrIMU8i/XANA=
github.com/gogf/gf/contrib/drivers/sqlite/v2 v2.8.3/go.mod h1:G5rfcFkBhtmZT4+CU7A3fJH3sNmP4WRIaJ+4JFeVE08=
github.com/gogf/gf/v2 v2.8.3 h1:h9Px3lqJnnH6It0AqHRz4/1hx0JmvaSf1IvUir5x1rA=
github.com/gogf/gf/v2 v2.8.3/go.mod h1:n++xPYGUUMadw6IygLEgGZqc6y6DRLrJKg5kqCrPLWY=
github.com/gogf/selfupdate v0.0.0-20231215043001-5c48c528462f h1:7xfXR/BhG3JDqO1s45n65Oyx9t4E/UqDOXep6jXdLCM=
github.com/gogf/selfupdate v0.0.0-20231215043001-5c48c528462f/go.mod h1:HnYoio6S7VaFJdryKcD/r9HgX+4QzYfr00XiXUo/xz0=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-jwt/jwt/v5 v5.2.0 h1:d/ix8ftRUorsN+5eMIlF4T6J8CAt9rch3My2winC1Jw=
github.com/golang-jwt/jwt/v5 v5.2.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA=
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A=
@ -66,7 +59,9 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
@ -80,7 +75,12 @@ github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhB
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
@ -109,6 +109,7 @@ github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi
github.com/pierrec/lz4/v4 v4.1.14 h1:+fL8AQEZtz/ijeNnpduH0bROTu0O3NZAlPjQxGn8LwE=
github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
@ -117,6 +118,8 @@ github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qq
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
github.com/schollz/progressbar/v3 v3.15.0 h1:cNZmcNiVyea6oofBTg80ZhVXxf3wG/JoAhqCCwopkQo=
github.com/schollz/progressbar/v3 v3.15.0/go.mod h1:ncBdc++eweU0dQoeZJ3loXoAc+bjaallHRIm8pVVeQM=
github.com/shirou/gopsutil v2.19.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
@ -131,22 +134,23 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/tklauser/go-sysconf v0.3.10/go.mod h1:C8XykCvCb+Gn0oNCWPIlcb0RuglQTYaQ2hGm7jmxEFk=
github.com/tklauser/numcpus v0.4.0/go.mod h1:1+UI3pD8NW14VMwdgJNJ1ESk2UnwhAnz5hMwiKKqXCQ=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
go.opentelemetry.io/otel v1.7.0/go.mod h1:5BdUoMIz5WEs0vt0CUEMtSSaTSHBBVwrhnz7+nrD5xk=
go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo=
go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo=
go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI=
go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco=
go.opentelemetry.io/otel/sdk v1.24.0 h1:YMPPDNymmQN3ZgczicBY3B6sf9n62Dlj9pWD3ucgoDw=
go.opentelemetry.io/otel/sdk v1.24.0/go.mod h1:KVrIYw6tEubO9E96HQpcmpTKDVn9gdv35HoYiQWGDFg=
go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U=
go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg=
go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZktzNa0M=
go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8=
go.opentelemetry.io/otel/sdk v1.32.0 h1:RNxepc9vK59A8XsgZQouW8ue8Gkb4jpWtJm9ge5lEG4=
go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU=
go.opentelemetry.io/otel/trace v1.7.0/go.mod h1:fzLSB9nqR2eXzxPXb2JW9IKE+ScyXA48yyE4TNvoHqU=
go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI=
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
go.opentelemetry.io/otel/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM=
go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
@ -202,8 +206,9 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

View File

@ -1,4 +1,4 @@
go 1.20
go 1.22
use (
./

View File

@ -36,7 +36,7 @@ type (
Link string `name:"link" short:"l" brief:"{CGenDaoBriefLink}"`
Tables string `name:"tables" short:"t" brief:"{CGenDaoBriefTables}"`
TablesEx string `name:"tablesEx" short:"x" brief:"{CGenDaoBriefTablesEx}"`
ShardingPattern []string `name:"ShardingPattern" short:"sp" brief:"{CGenDaoBriefShardingPattern}"`
ShardingPattern []string `name:"shardingPattern" short:"sp" brief:"{CGenDaoBriefShardingPattern}"`
Group string `name:"group" short:"g" brief:"{CGenDaoBriefGroup}" d:"default"`
Prefix string `name:"prefix" short:"f" brief:"{CGenDaoBriefPrefix}"`
RemovePrefix string `name:"removePrefix" short:"r" brief:"{CGenDaoBriefRemovePrefix}"`

View File

@ -1,12 +1,12 @@
module github.com/gogf/gf/cmd/gf/cmd/gf/testdata/vardump/v2
go 1.18
go 1.22
require github.com/gogf/gf/v2 v2.8.2
require (
go.opentelemetry.io/otel v1.24.0 // indirect
go.opentelemetry.io/otel/trace v1.24.0 // indirect
go.opentelemetry.io/otel v1.32.0 // indirect
go.opentelemetry.io/otel/trace v1.32.0 // indirect
)
replace github.com/gogf/gf/v2 => ../../../../../../../

View File

@ -19,10 +19,12 @@ github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo=
go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo=
go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg=
go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI=
go.opentelemetry.io/otel/sdk v1.24.0 h1:YMPPDNymmQN3ZgczicBY3B6sf9n62Dlj9pWD3ucgoDw=
go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI=
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8=
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=

View File

@ -162,8 +162,14 @@ func (s serviceInstall) getGoPathBin() string {
func (s serviceInstall) getAvailablePaths() []serviceInstallAvailablePath {
var (
folderPaths []serviceInstallAvailablePath
binaryFileName = "gf" + gfile.Ext(gfile.SelfPath())
binaryFileName = "gf"
)
// Windows binary file name suffix.
if runtime.GOOS == "windows" {
binaryFileName += ".exe"
}
// $GOPATH/bin
if goPathBin := s.getGoPathBin(); goPathBin != "" {
folderPaths = s.checkAndAppendToAvailablePath(

View File

@ -8,14 +8,8 @@
package gvar
import (
"time"
"github.com/gogf/gf/v2/container/gtype"
"github.com/gogf/gf/v2/internal/deepcopy"
"github.com/gogf/gf/v2/internal/json"
"github.com/gogf/gf/v2/os/gtime"
"github.com/gogf/gf/v2/util/gconv"
"github.com/gogf/gf/v2/util/gutil"
)
// Var is an universal variable type implementer.
@ -39,144 +33,8 @@ func New(value interface{}, safe ...bool) *Var {
}
}
// Copy does a deep copy of current Var and returns a pointer to this Var.
func (v *Var) Copy() *Var {
return New(gutil.Copy(v.Val()), v.safe)
}
// Clone does a shallow copy of current Var and returns a pointer to this Var.
func (v *Var) Clone() *Var {
return New(v.Val(), v.safe)
}
// Set sets `value` to `v`, and returns the old value.
func (v *Var) Set(value interface{}) (old interface{}) {
if v.safe {
if t, ok := v.value.(*gtype.Interface); ok {
old = t.Set(value)
return
}
}
old = v.value
v.value = value
return
}
// Val returns the current value of `v`.
func (v *Var) Val() interface{} {
if v == nil {
return nil
}
if v.safe {
if t, ok := v.value.(*gtype.Interface); ok {
return t.Val()
}
}
return v.value
}
// Interface is alias of Val.
func (v *Var) Interface() interface{} {
return v.Val()
}
// Bytes converts and returns `v` as []byte.
func (v *Var) Bytes() []byte {
return gconv.Bytes(v.Val())
}
// String converts and returns `v` as string.
func (v *Var) String() string {
return gconv.String(v.Val())
}
// Bool converts and returns `v` as bool.
func (v *Var) Bool() bool {
return gconv.Bool(v.Val())
}
// Int converts and returns `v` as int.
func (v *Var) Int() int {
return gconv.Int(v.Val())
}
// Int8 converts and returns `v` as int8.
func (v *Var) Int8() int8 {
return gconv.Int8(v.Val())
}
// Int16 converts and returns `v` as int16.
func (v *Var) Int16() int16 {
return gconv.Int16(v.Val())
}
// Int32 converts and returns `v` as int32.
func (v *Var) Int32() int32 {
return gconv.Int32(v.Val())
}
// Int64 converts and returns `v` as int64.
func (v *Var) Int64() int64 {
return gconv.Int64(v.Val())
}
// Uint converts and returns `v` as uint.
func (v *Var) Uint() uint {
return gconv.Uint(v.Val())
}
// Uint8 converts and returns `v` as uint8.
func (v *Var) Uint8() uint8 {
return gconv.Uint8(v.Val())
}
// Uint16 converts and returns `v` as uint16.
func (v *Var) Uint16() uint16 {
return gconv.Uint16(v.Val())
}
// Uint32 converts and returns `v` as uint32.
func (v *Var) Uint32() uint32 {
return gconv.Uint32(v.Val())
}
// Uint64 converts and returns `v` as uint64.
func (v *Var) Uint64() uint64 {
return gconv.Uint64(v.Val())
}
// Float32 converts and returns `v` as float32.
func (v *Var) Float32() float32 {
return gconv.Float32(v.Val())
}
// Float64 converts and returns `v` as float64.
func (v *Var) Float64() float64 {
return gconv.Float64(v.Val())
}
// Time converts and returns `v` as time.Time.
// The parameter `format` specifies the format of the time string using gtime,
// eg: Y-m-d H:i:s.
func (v *Var) Time(format ...string) time.Time {
return gconv.Time(v.Val(), format...)
}
// Duration converts and returns `v` as time.Duration.
// If value of `v` is string, then it uses time.ParseDuration for conversion.
func (v *Var) Duration() time.Duration {
return gconv.Duration(v.Val())
}
// GTime converts and returns `v` as *gtime.Time.
// The parameter `format` specifies the format of the time string using gtime,
// eg: Y-m-d H:i:s.
func (v *Var) GTime(format ...string) *gtime.Time {
return gconv.GTime(v.Val(), format...)
}
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
func (v Var) MarshalJSON() ([]byte, error) {
func (v *Var) MarshalJSON() ([]byte, error) {
return json.Marshal(v.Val())
}
@ -195,11 +53,3 @@ func (v *Var) UnmarshalValue(value interface{}) error {
v.Set(value)
return nil
}
// DeepCopy implements interface for deep copy of current type.
func (v *Var) DeepCopy() interface{} {
if v == nil {
return nil
}
return New(deepcopy.Copy(v.Val()), v.safe)
}

View File

@ -0,0 +1,105 @@
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package gvar
import (
"github.com/gogf/gf/v2/container/gtype"
"github.com/gogf/gf/v2/util/gconv"
)
// Val returns the current value of `v`.
func (v *Var) Val() any {
if v == nil {
return nil
}
if v.safe {
if t, ok := v.value.(*gtype.Interface); ok {
return t.Val()
}
}
return v.value
}
// Interface is alias of Val.
func (v *Var) Interface() any {
return v.Val()
}
// Bytes converts and returns `v` as []byte.
func (v *Var) Bytes() []byte {
return gconv.Bytes(v.Val())
}
// String converts and returns `v` as string.
func (v *Var) String() string {
return gconv.String(v.Val())
}
// Bool converts and returns `v` as bool.
func (v *Var) Bool() bool {
return gconv.Bool(v.Val())
}
// Int converts and returns `v` as int.
func (v *Var) Int() int {
return gconv.Int(v.Val())
}
// Int8 converts and returns `v` as int8.
func (v *Var) Int8() int8 {
return gconv.Int8(v.Val())
}
// Int16 converts and returns `v` as int16.
func (v *Var) Int16() int16 {
return gconv.Int16(v.Val())
}
// Int32 converts and returns `v` as int32.
func (v *Var) Int32() int32 {
return gconv.Int32(v.Val())
}
// Int64 converts and returns `v` as int64.
func (v *Var) Int64() int64 {
return gconv.Int64(v.Val())
}
// Uint converts and returns `v` as uint.
func (v *Var) Uint() uint {
return gconv.Uint(v.Val())
}
// Uint8 converts and returns `v` as uint8.
func (v *Var) Uint8() uint8 {
return gconv.Uint8(v.Val())
}
// Uint16 converts and returns `v` as uint16.
func (v *Var) Uint16() uint16 {
return gconv.Uint16(v.Val())
}
// Uint32 converts and returns `v` as uint32.
func (v *Var) Uint32() uint32 {
return gconv.Uint32(v.Val())
}
// Uint64 converts and returns `v` as uint64.
func (v *Var) Uint64() uint64 {
return gconv.Uint64(v.Val())
}
// Float32 converts and returns `v` as float32.
func (v *Var) Float32() float32 {
return gconv.Float32(v.Val())
}
// Float64 converts and returns `v` as float64.
func (v *Var) Float64() float64 {
return gconv.Float64(v.Val())
}

View File

@ -0,0 +1,30 @@
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package gvar
import (
"github.com/gogf/gf/v2/internal/deepcopy"
"github.com/gogf/gf/v2/util/gutil"
)
// Copy does a deep copy of current Var and returns a pointer to this Var.
func (v *Var) Copy() *Var {
return New(gutil.Copy(v.Val()), v.safe)
}
// Clone does a shallow copy of current Var and returns a pointer to this Var.
func (v *Var) Clone() *Var {
return New(v.Val(), v.safe)
}
// DeepCopy implements interface for deep copy of current type.
func (v *Var) DeepCopy() interface{} {
if v == nil {
return nil
}
return New(deepcopy.Copy(v.Val()), v.safe)
}

View File

@ -10,8 +10,7 @@ import (
"github.com/gogf/gf/v2/util/gconv"
)
// Scan automatically checks the type of `pointer` and converts `params` to `pointer`. It supports `pointer`
// with type of `*map/*[]map/*[]*map/*struct/**struct/*[]struct/*[]*struct` for converting.
// Scan automatically checks the type of `pointer` and converts value of Var to `pointer`.
//
// See gconv.Scan.
func (v *Var) Scan(pointer interface{}, mapping ...map[string]string) error {

View File

@ -0,0 +1,24 @@
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package gvar
import (
"github.com/gogf/gf/v2/container/gtype"
)
// Set sets `value` to `v`, and returns the old value.
func (v *Var) Set(value interface{}) (old interface{}) {
if v.safe {
if t, ok := v.value.(*gtype.Interface); ok {
old = t.Set(value)
return
}
}
old = v.value
v.value = value
return
}

View File

@ -0,0 +1,34 @@
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package gvar
import (
"time"
"github.com/gogf/gf/v2/os/gtime"
"github.com/gogf/gf/v2/util/gconv"
)
// Time converts and returns `v` as time.Time.
// The parameter `format` specifies the format of the time string using gtime,
// eg: Y-m-d H:i:s.
func (v *Var) Time(format ...string) time.Time {
return gconv.Time(v.Val(), format...)
}
// Duration converts and returns `v` as time.Duration.
// If value of `v` is string, then it uses time.ParseDuration for conversion.
func (v *Var) Duration() time.Duration {
return gconv.Duration(v.Val())
}
// GTime converts and returns `v` as *gtime.Time.
// The parameter `format` specifies the format of the time string using gtime,
// eg: Y-m-d H:i:s.
func (v *Var) GTime(format ...string) *gtime.Time {
return gconv.GTime(v.Val(), format...)
}

View File

@ -2,11 +2,9 @@ module github.com/gogf/gf/contrib/config/apollo/v2
go 1.22
toolchain go1.22.0
require (
github.com/apolloconfig/agollo/v4 v4.3.1
github.com/gogf/gf/v2 v2.8.3
github.com/gogf/gf/v2 v2.9.0
)
require (

View File

@ -2,10 +2,8 @@ module github.com/gogf/gf/contrib/config/consul/v2
go 1.22
toolchain go1.22.0
require (
github.com/gogf/gf/v2 v2.8.3
github.com/gogf/gf/v2 v2.9.0
github.com/hashicorp/consul/api v1.24.0
github.com/hashicorp/go-cleanhttp v0.5.2
)

View File

@ -2,10 +2,8 @@ module github.com/gogf/gf/contrib/config/kubecm/v2
go 1.22
toolchain go1.22.0
require (
github.com/gogf/gf/v2 v2.8.3
github.com/gogf/gf/v2 v2.9.0
k8s.io/api v0.27.4
k8s.io/apimachinery v0.27.4
k8s.io/client-go v0.27.4

View File

@ -2,10 +2,8 @@ module github.com/gogf/gf/contrib/config/nacos/v2
go 1.22
toolchain go1.22.0
require (
github.com/gogf/gf/v2 v2.8.3
github.com/gogf/gf/v2 v2.9.0
github.com/nacos-group/nacos-sdk-go/v2 v2.2.5
)

View File

@ -2,10 +2,8 @@ module github.com/gogf/gf/contrib/config/polaris/v2
go 1.22
toolchain go1.22.0
require (
github.com/gogf/gf/v2 v2.8.3
github.com/gogf/gf/v2 v2.9.0
github.com/polarismesh/polaris-go v1.5.8
)

View File

@ -2,11 +2,9 @@ module github.com/gogf/gf/contrib/drivers/clickhouse/v2
go 1.22
toolchain go1.22.0
require (
github.com/ClickHouse/clickhouse-go/v2 v2.0.15
github.com/gogf/gf/v2 v2.8.3
github.com/gogf/gf/v2 v2.9.0
github.com/google/uuid v1.6.0
github.com/shopspring/decimal v1.3.1
)

View File

@ -2,13 +2,11 @@ module github.com/gogf/gf/contrib/drivers/dm/v2
go 1.22
toolchain go1.22.0
replace github.com/gogf/gf/v2 => ../../../
require (
gitee.com/chunanyong/dm v1.8.12
github.com/gogf/gf/v2 v2.8.3
github.com/gogf/gf/v2 v2.9.0
)
require (

View File

@ -2,10 +2,8 @@ module github.com/gogf/gf/contrib/drivers/mssql/v2
go 1.22
toolchain go1.22.0
require (
github.com/gogf/gf/v2 v2.8.3
github.com/gogf/gf/v2 v2.9.0
github.com/microsoft/go-mssqldb v1.7.1
)

View File

@ -2,11 +2,9 @@ module github.com/gogf/gf/contrib/drivers/mysql/v2
go 1.22
toolchain go1.22.0
require (
github.com/go-sql-driver/mysql v1.7.1
github.com/gogf/gf/v2 v2.8.3
github.com/gogf/gf/v2 v2.9.0
)
require (

View File

@ -17,7 +17,6 @@ import (
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
"github.com/gogf/gf/v2/test/gtest"
"github.com/gogf/gf/v2/text/gstr"
"github.com/gogf/gf/v2/util/gconv"
)
@ -481,117 +480,3 @@ func Test_Scan_AutoFilteringByStructAttributes(t *testing.T) {
t.Assert(users[0].Id, 1)
})
}
func Test_Scan_JsonAttributes(t *testing.T) {
type GiftImage struct {
Uid string `json:"uid"`
Url string `json:"url"`
Status string `json:"status"`
Name string `json:"name"`
}
type GiftComment struct {
Name string `json:"name"`
Field string `json:"field"`
Required bool `json:"required"`
}
type Prop struct {
Name string `json:"name"`
Values []string `json:"values"`
}
type Sku struct {
GiftId int64 `json:"gift_id"`
Name string `json:"name"`
ScorePrice int `json:"score_price"`
MarketPrice int `json:"market_price"`
CostPrice int `json:"cost_price"`
Stock int `json:"stock"`
}
type Covers struct {
List []GiftImage `json:"list"`
}
type GiftEntity struct {
Id int64 `json:"id"`
StoreId int64 `json:"store_id"`
GiftType int `json:"gift_type"`
GiftName string `json:"gift_name"`
Description string `json:"description"`
Covers Covers `json:"covers"`
Cover string `json:"cover"`
GiftCategoryId []int64 `json:"gift_category_id"`
HasProps bool `json:"has_props"`
OutSn string `json:"out_sn"`
IsLimitSell bool `json:"is_limit_sell"`
LimitSellType int `json:"limit_sell_type"`
LimitSellCycle string `json:"limit_sell_cycle"`
LimitSellCycleCount int `json:"limit_sell_cycle_count"`
LimitSellCustom bool `json:"limit_sell_custom"` // 只允许特定会员兑换
LimitCustomerTags []int64 `json:"limit_customer_tags"` // 允许兑换的成员
ScorePrice int `json:"score_price"`
MarketPrice float64 `json:"market_price"`
CostPrice int `json:"cost_price"`
Stock int `json:"stock"`
Props []Prop `json:"props"`
Skus []Sku `json:"skus"`
ExpressType []string `json:"express_type"`
Comments []GiftComment `json:"comments"`
Content string `json:"content"`
AtLeastRechargeCount int `json:"at_least_recharge_count"`
Status int `json:"status"`
}
type User struct {
Id int
Passport string
}
table := "jfy_gift"
array := gstr.SplitAndTrim(gtest.DataContent(`issue1380.sql`), ";")
for _, v := range array {
if _, err := db.Exec(ctx, v); err != nil {
gtest.Error(err)
}
}
defer dropTable(table)
gtest.C(t, func(t *gtest.T) {
var (
entity = new(GiftEntity)
err = db.Model(table).Where("id", 17).Scan(entity)
)
t.AssertNil(err)
t.Assert(len(entity.Skus), 2)
t.Assert(entity.Skus[0].Name, "red")
t.Assert(entity.Skus[0].Stock, 10)
t.Assert(entity.Skus[0].GiftId, 1)
t.Assert(entity.Skus[0].CostPrice, 80)
t.Assert(entity.Skus[0].ScorePrice, 188)
t.Assert(entity.Skus[0].MarketPrice, 388)
t.Assert(entity.Skus[1].Name, "blue")
t.Assert(entity.Skus[1].Stock, 100)
t.Assert(entity.Skus[1].GiftId, 2)
t.Assert(entity.Skus[1].CostPrice, 81)
t.Assert(entity.Skus[1].ScorePrice, 200)
t.Assert(entity.Skus[1].MarketPrice, 288)
t.Assert(entity.Id, 17)
t.Assert(entity.StoreId, 100004)
t.Assert(entity.GiftType, 1)
t.Assert(entity.GiftName, "GIFT")
t.Assert(entity.Description, "支持个性定制的父亲节老师长辈的专属礼物")
t.Assert(len(entity.Covers.List), 3)
t.Assert(entity.OutSn, "259402")
t.Assert(entity.LimitCustomerTags, "[]")
t.Assert(entity.ScorePrice, 10)
t.Assert(len(entity.Props), 1)
t.Assert(len(entity.Comments), 2)
t.Assert(entity.Status, 99)
t.Assert(entity.Content, `<p>礼品详情</p>`)
})
}

View File

@ -8,6 +8,7 @@ package mysql_test
import (
"context"
"encoding/json"
"fmt"
"sync"
"testing"
@ -24,6 +25,121 @@ import (
"github.com/gogf/gf/v2/util/guid"
)
// https://github.com/gogf/gf/issues/1380
func Test_Issue1380(t *testing.T) {
type GiftImage struct {
Uid string `json:"uid"`
Url string `json:"url"`
Status string `json:"status"`
Name string `json:"name"`
}
type GiftComment struct {
Name string `json:"name"`
Field string `json:"field"`
Required bool `json:"required"`
}
type Prop struct {
Name string `json:"name"`
Values []string `json:"values"`
}
type Sku struct {
GiftId int64 `json:"gift_id"`
Name string `json:"name"`
ScorePrice int `json:"score_price"`
MarketPrice int `json:"market_price"`
CostPrice int `json:"cost_price"`
Stock int `json:"stock"`
}
type Covers struct {
List []GiftImage `json:"list"`
}
type GiftEntity struct {
Id int64 `json:"id"`
StoreId int64 `json:"store_id"`
GiftType int `json:"gift_type"`
GiftName string `json:"gift_name"`
Description string `json:"description"`
Covers Covers `json:"covers"`
Cover string `json:"cover"`
GiftCategoryId []int64 `json:"gift_category_id"`
HasProps bool `json:"has_props"`
OutSn string `json:"out_sn"`
IsLimitSell bool `json:"is_limit_sell"`
LimitSellType int `json:"limit_sell_type"`
LimitSellCycle string `json:"limit_sell_cycle"`
LimitSellCycleCount int `json:"limit_sell_cycle_count"`
LimitSellCustom bool `json:"limit_sell_custom"` // 只允许特定会员兑换
LimitCustomerTags []int64 `json:"limit_customer_tags"` // 允许兑换的成员
ScorePrice int `json:"score_price"`
MarketPrice float64 `json:"market_price"`
CostPrice int `json:"cost_price"`
Stock int `json:"stock"`
Props []Prop `json:"props"`
Skus []Sku `json:"skus"`
ExpressType []string `json:"express_type"`
Comments []GiftComment `json:"comments"`
Content string `json:"content"`
AtLeastRechargeCount int `json:"at_least_recharge_count"`
Status int `json:"status"`
}
type User struct {
Id int
Passport string
}
table := "jfy_gift"
array := gstr.SplitAndTrim(gtest.DataContent(`issues`, `1380.sql`), ";")
for _, v := range array {
if _, err := db.Exec(ctx, v); err != nil {
gtest.Error(err)
}
}
defer dropTable(table)
gtest.C(t, func(t *gtest.T) {
var (
entity = new(GiftEntity)
err = db.Model(table).Where("id", 17).Scan(entity)
)
t.AssertNil(err)
t.Assert(len(entity.Skus), 2)
t.Assert(entity.Skus[0].Name, "red")
t.Assert(entity.Skus[0].Stock, 10)
t.Assert(entity.Skus[0].GiftId, 1)
t.Assert(entity.Skus[0].CostPrice, 80)
t.Assert(entity.Skus[0].ScorePrice, 188)
t.Assert(entity.Skus[0].MarketPrice, 388)
t.Assert(entity.Skus[1].Name, "blue")
t.Assert(entity.Skus[1].Stock, 100)
t.Assert(entity.Skus[1].GiftId, 2)
t.Assert(entity.Skus[1].CostPrice, 81)
t.Assert(entity.Skus[1].ScorePrice, 200)
t.Assert(entity.Skus[1].MarketPrice, 288)
t.Assert(entity.Id, 17)
t.Assert(entity.StoreId, 100004)
t.Assert(entity.GiftType, 1)
t.Assert(entity.GiftName, "GIFT")
t.Assert(entity.Description, "支持个性定制的父亲节老师长辈的专属礼物")
t.Assert(len(entity.Covers.List), 3)
t.Assert(entity.OutSn, "259402")
t.Assert(entity.LimitCustomerTags, "[]")
t.Assert(entity.ScorePrice, 10)
t.Assert(len(entity.Props), 1)
t.Assert(len(entity.Comments), 2)
t.Assert(entity.Status, 99)
t.Assert(entity.Content, `<p>礼品详情</p>`)
})
}
// https://github.com/gogf/gf/issues/1934
func Test_Issue1934(t *testing.T) {
table := createInitTable()
@ -170,7 +286,7 @@ func Test_Issue1401(t *testing.T) {
table1 = "parcels"
table2 = "parcel_items"
)
array := gstr.SplitAndTrim(gtest.DataContent(`issue1401.sql`), ";")
array := gstr.SplitAndTrim(gtest.DataContent(`issues`, `1401.sql`), ";")
for _, v := range array {
if _, err := db.Exec(ctx, v); err != nil {
gtest.Error(err)
@ -212,7 +328,7 @@ func Test_Issue1412(t *testing.T) {
table1 = "parcels"
table2 = "items"
)
array := gstr.SplitAndTrim(gtest.DataContent(`issue1412.sql`), ";")
array := gstr.SplitAndTrim(gtest.DataContent(`issues`, `1412.sql`), ";")
for _, v := range array {
if _, err := db.Exec(ctx, v); err != nil {
gtest.Error(err)
@ -474,7 +590,7 @@ func Test_Issue2012(t *testing.T) {
// https://github.com/gogf/gf/issues/2105
func Test_Issue2105(t *testing.T) {
table := "issue2105"
array := gstr.SplitAndTrim(gtest.DataContent(`issue2105.sql`), ";")
array := gstr.SplitAndTrim(gtest.DataContent(`issues`, `2105.sql`), ";")
for _, v := range array {
if _, err := db.Exec(ctx, v); err != nil {
gtest.Error(err)
@ -772,7 +888,7 @@ func Test_Issue2561(t *testing.T) {
// https://github.com/gogf/gf/issues/2439
func Test_Issue2439(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := gstr.SplitAndTrim(gtest.DataContent(`issue2439.sql`), ";")
array := gstr.SplitAndTrim(gtest.DataContent(`issues`, `2439.sql`), ";")
for _, v := range array {
if _, err := db.Exec(ctx, v); err != nil {
gtest.Error(err)
@ -868,7 +984,7 @@ func Test_Issue2907(t *testing.T) {
// https://github.com/gogf/gf/issues/3086
func Test_Issue3086(t *testing.T) {
table := "issue3086_user"
array := gstr.SplitAndTrim(gtest.DataContent(`issue3086.sql`), ";")
array := gstr.SplitAndTrim(gtest.DataContent(`issues`, `3086.sql`), ";")
for _, v := range array {
if _, err := db.Exec(ctx, v); err != nil {
gtest.Error(err)
@ -1013,7 +1129,7 @@ func Test_Issue3204(t *testing.T) {
// https://github.com/gogf/gf/issues/3218
func Test_Issue3218(t *testing.T) {
table := "issue3218_sys_config"
array := gstr.SplitAndTrim(gtest.DataContent(`issue3218.sql`), ";")
array := gstr.SplitAndTrim(gtest.DataContent(`issues`, `3218.sql`), ";")
for _, v := range array {
if _, err := db.Exec(ctx, v); err != nil {
gtest.Error(err)
@ -1136,7 +1252,7 @@ func Test_Issue2552_ClearTableFields(t *testing.T) {
// https://github.com/gogf/gf/issues/2643
func Test_Issue2643(t *testing.T) {
table := "issue2643"
array := gstr.SplitAndTrim(gtest.DataContent(`issue2643.sql`), ";")
array := gstr.SplitAndTrim(gtest.DataContent(`issues`, `2643.sql`), ";")
for _, v := range array {
if _, err := db.Exec(ctx, v); err != nil {
gtest.Error(err)
@ -1221,7 +1337,7 @@ func Test_Issue3649(t *testing.T) {
// https://github.com/gogf/gf/issues/3754
func Test_Issue3754(t *testing.T) {
table := "issue3754"
array := gstr.SplitAndTrim(gtest.DataContent(`issue3754.sql`), ";")
array := gstr.SplitAndTrim(gtest.DataContent(`issues`, `3754.sql`), ";")
for _, v := range array {
if _, err := db.Exec(ctx, v); err != nil {
gtest.Error(err)
@ -1283,7 +1399,7 @@ func Test_Issue3754(t *testing.T) {
// https://github.com/gogf/gf/issues/3626
func Test_Issue3626(t *testing.T) {
table := "issue3626"
array := gstr.SplitAndTrim(gtest.DataContent(`issue3626.sql`), ";")
array := gstr.SplitAndTrim(gtest.DataContent(`issues`, `3626.sql`), ";")
defer dropTable(table)
for _, v := range array {
if _, err := db.Exec(ctx, v); err != nil {
@ -1413,7 +1529,7 @@ func Test_Issue3968(t *testing.T) {
// https://github.com/gogf/gf/issues/3915
func Test_Issue3915(t *testing.T) {
table := "issue3915"
array := gstr.SplitAndTrim(gtest.DataContent(`issue3915.sql`), ";")
array := gstr.SplitAndTrim(gtest.DataContent(`issues`, `3915.sql`), ";")
for _, v := range array {
if _, err := db.Exec(ctx, v); err != nil {
gtest.Error(err)
@ -1500,7 +1616,7 @@ func Test_Issue2119(t *testing.T) {
defer dropTable(tables[0])
defer dropTable(tables[1])
_ = tables
array := gstr.SplitAndTrim(gtest.DataContent(`issue2119.sql`), ";")
array := gstr.SplitAndTrim(gtest.DataContent(`issues`, `2119.sql`), ";")
for _, v := range array {
_, err := db.Exec(ctx, v)
t.AssertNil(err)
@ -1561,7 +1677,7 @@ func Test_Issue2119(t *testing.T) {
func Test_Issue4034(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
table := "issue4034"
array := gstr.SplitAndTrim(gtest.DataContent(`issue4034.sql`), ";")
array := gstr.SplitAndTrim(gtest.DataContent(`issues`, `4034.sql`), ";")
for _, v := range array {
_, err := db.Exec(ctx, v)
t.AssertNil(err)
@ -1590,3 +1706,162 @@ func issue4034SaveAppDevice(ctx context.Context, table string, tx gdb.TX) error
}).Save()
return err
}
// https://github.com/gogf/gf/issues/4086
func Test_Issue4086(t *testing.T) {
table := "issue4086"
defer dropTable(table)
array := gstr.SplitAndTrim(gtest.DataContent(`issues`, `4086.sql`), ";")
for _, v := range array {
_, err := db.Exec(ctx, v)
gtest.AssertNil(err)
}
gtest.C(t, func(t *gtest.T) {
type ProxyParam struct {
ProxyId int64 `json:"proxyId" orm:"proxy_id"`
RecommendIds []int64 `json:"recommendIds" orm:"recommend_ids"`
Photos []int64 `json:"photos" orm:"photos"`
}
var proxyParamList []*ProxyParam
err := db.Model(table).Ctx(ctx).Scan(&proxyParamList)
t.AssertNil(err)
t.Assert(len(proxyParamList), 2)
t.Assert(proxyParamList, []*ProxyParam{
{
ProxyId: 1,
RecommendIds: []int64{584, 585},
Photos: nil,
},
{
ProxyId: 2,
RecommendIds: []int64{},
Photos: nil,
},
})
})
gtest.C(t, func(t *gtest.T) {
type ProxyParam struct {
ProxyId int64 `json:"proxyId" orm:"proxy_id"`
RecommendIds []int64 `json:"recommendIds" orm:"recommend_ids"`
Photos []float32 `json:"photos" orm:"photos"`
}
var proxyParamList []*ProxyParam
err := db.Model(table).Ctx(ctx).Scan(&proxyParamList)
t.AssertNil(err)
t.Assert(len(proxyParamList), 2)
t.Assert(proxyParamList, []*ProxyParam{
{
ProxyId: 1,
RecommendIds: []int64{584, 585},
Photos: nil,
},
{
ProxyId: 2,
RecommendIds: []int64{},
Photos: nil,
},
})
})
gtest.C(t, func(t *gtest.T) {
type ProxyParam struct {
ProxyId int64 `json:"proxyId" orm:"proxy_id"`
RecommendIds []int64 `json:"recommendIds" orm:"recommend_ids"`
Photos []string `json:"photos" orm:"photos"`
}
var proxyParamList []*ProxyParam
err := db.Model(table).Ctx(ctx).Scan(&proxyParamList)
t.AssertNil(err)
t.Assert(len(proxyParamList), 2)
t.Assert(proxyParamList, []*ProxyParam{
{
ProxyId: 1,
RecommendIds: []int64{584, 585},
Photos: nil,
},
{
ProxyId: 2,
RecommendIds: []int64{},
Photos: nil,
},
})
})
gtest.C(t, func(t *gtest.T) {
type ProxyParam struct {
ProxyId int64 `json:"proxyId" orm:"proxy_id"`
RecommendIds []int64 `json:"recommendIds" orm:"recommend_ids"`
Photos []any `json:"photos" orm:"photos"`
}
var proxyParamList []*ProxyParam
err := db.Model(table).Ctx(ctx).Scan(&proxyParamList)
t.AssertNil(err)
t.Assert(len(proxyParamList), 2)
t.Assert(proxyParamList, []*ProxyParam{
{
ProxyId: 1,
RecommendIds: []int64{584, 585},
Photos: nil,
},
{
ProxyId: 2,
RecommendIds: []int64{},
Photos: nil,
},
})
})
gtest.C(t, func(t *gtest.T) {
type ProxyParam struct {
ProxyId int64 `json:"proxyId" orm:"proxy_id"`
RecommendIds []int64 `json:"recommendIds" orm:"recommend_ids"`
Photos string `json:"photos" orm:"photos"`
}
var proxyParamList []*ProxyParam
err := db.Model(table).Ctx(ctx).Scan(&proxyParamList)
t.AssertNil(err)
t.Assert(len(proxyParamList), 2)
t.Assert(proxyParamList, []*ProxyParam{
{
ProxyId: 1,
RecommendIds: []int64{584, 585},
Photos: "null",
},
{
ProxyId: 2,
RecommendIds: []int64{},
Photos: "",
},
})
})
gtest.C(t, func(t *gtest.T) {
type ProxyParam struct {
ProxyId int64 `json:"proxyId" orm:"proxy_id"`
RecommendIds string `json:"recommendIds" orm:"recommend_ids"`
Photos json.RawMessage `json:"photos" orm:"photos"`
}
var proxyParamList []*ProxyParam
err := db.Model(table).Ctx(ctx).Scan(&proxyParamList)
t.AssertNil(err)
t.Assert(len(proxyParamList), 2)
t.Assert(proxyParamList, []*ProxyParam{
{
ProxyId: 1,
RecommendIds: "[584, 585]",
Photos: json.RawMessage("null"),
},
{
ProxyId: 2,
RecommendIds: "[]",
Photos: json.RawMessage("null"),
},
})
})
}

View File

@ -682,6 +682,7 @@ func Test_Model_Array(t *testing.T) {
t.Assert(all.Array("id"), g.Slice{1, 2, 3})
t.Assert(all.Array("nickname"), g.Slice{"name_1", "name_2", "name_3"})
})
gtest.C(t, func(t *gtest.T) {
array, err := db.Model(table).Fields("nickname").Where("id", g.Slice{1, 2, 3}).Array()
t.AssertNil(err)
@ -757,6 +758,7 @@ func Test_Model_Value_WithCache(t *testing.T) {
t.AssertNil(err)
t.Assert(value.Int(), 0)
})
gtest.C(t, func(t *gtest.T) {
result, err := db.Model(table).Data(g.MapStrAny{
"id": 1,
@ -1437,6 +1439,7 @@ func Test_Model_Option_Map(t *testing.T) {
t.AssertNE(one["nickname"].String(), "1")
t.Assert(one["passport"].String(), "1")
})
gtest.C(t, func(t *gtest.T) {
table := createTable()
defer dropTable(table)

View File

@ -1286,8 +1286,7 @@ func Test_Transaction_Propagation(t *testing.T) {
Propagation: gdb.PropagationNotSupported,
}, func(ctx context.Context, tx2 gdb.TX) error {
// Should execute without transaction
t.Assert(tx2, nil)
_, err := db.Insert(ctx, table, g.Map{
_, err = db.Insert(ctx, table, g.Map{
"id": 9,
"passport": "non_tx_record",
})
@ -1346,8 +1345,6 @@ func Test_Transaction_Propagation(t *testing.T) {
err := db.TransactionWithOptions(ctx, gdb.TxOptions{
Propagation: gdb.PropagationNever,
}, func(ctx context.Context, tx gdb.TX) error {
// Should execute without transaction
t.Assert(tx, nil)
_, err := db.Insert(ctx, table, g.Map{
"id": 11,
"passport": "never",
@ -1369,6 +1366,51 @@ func Test_Transaction_Propagation(t *testing.T) {
})
}
func Test_Transaction_Propagation_PropagationSupports(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
table := createTable()
defer dropTable(table)
// scenario1: when in a transaction, use PropagationSupports to execute a transaction
err := db.Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
// insert in outer tx.
_, err := tx.Insert(table, g.Map{
"id": 1,
})
if err != nil {
return err
}
err = tx.TransactionWithOptions(ctx, gdb.TxOptions{
Propagation: gdb.PropagationSupports,
}, func(ctx context.Context, tx2 gdb.TX) error {
_, err = tx2.Insert(table, g.Map{
"id": 2,
})
return gerror.New("error")
})
return err
})
t.AssertNE(err, nil)
// scenario2: when not in a transaction, do not use transaction but direct db link.
err = db.TransactionWithOptions(ctx, gdb.TxOptions{
Propagation: gdb.PropagationSupports,
}, func(ctx context.Context, tx gdb.TX) error {
_, err = tx.Insert(table, g.Map{
"id": 3,
})
return err
})
t.AssertNil(err)
// 查询结果
result, err := db.Model(table).OrderAsc("id").All()
t.AssertNil(err)
t.Assert(len(result), 1)
t.Assert(result[0]["id"], 3)
})
}
func Test_Transaction_Propagation_Complex(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
table1 := createTable()
@ -1389,7 +1431,7 @@ func Test_Transaction_Propagation_Complex(t *testing.T) {
err = tx1.TransactionWithOptions(ctx, gdb.TxOptions{
Propagation: gdb.PropagationNested,
}, func(ctx context.Context, tx2 gdb.TX) error {
_, err := tx2.Insert(table1, g.Map{
_, err = tx2.Insert(table1, g.Map{
"id": 2,
"passport": "nested1",
})
@ -1427,10 +1469,7 @@ func Test_Transaction_Propagation_Complex(t *testing.T) {
err = tx1.TransactionWithOptions(ctx, gdb.TxOptions{
Propagation: gdb.PropagationNotSupported,
}, func(ctx context.Context, tx2 gdb.TX) error {
// Should execute without transaction
t.Assert(tx2, nil)
_, err := db.Insert(ctx, table2, g.Map{
_, err = db.Insert(ctx, table2, g.Map{
"id": 5,
"passport": "not_supported",
})
@ -1489,9 +1528,6 @@ func Test_Transaction_Propagation_Complex(t *testing.T) {
err = tx1.TransactionWithOptions(ctx, gdb.TxOptions{
Propagation: gdb.PropagationNotSupported,
}, func(ctx context.Context, tx2 gdb.TX) error {
// Should execute without transaction
t.Assert(tx2, nil)
// Start a new independent transaction
return db.Transaction(ctx, func(ctx context.Context, tx3 gdb.TX) error {
_, err := tx3.Insert(table, g.Map{

View File

@ -0,0 +1,10 @@
DROP TABLE IF EXISTS `issue4086`;
CREATE TABLE `issue4086` (
`proxy_id` bigint NOT NULL,
`recommend_ids` json DEFAULT NULL,
`photos` json DEFAULT NULL,
PRIMARY KEY (`proxy_id`)
) ENGINE=InnoDB;
INSERT INTO `issue4086` (`proxy_id`, `recommend_ids`, `photos`) VALUES (1, '[584, 585]', 'null');
INSERT INTO `issue4086` (`proxy_id`, `recommend_ids`, `photos`) VALUES (2, '[]', NULL);

View File

@ -2,10 +2,8 @@ module github.com/gogf/gf/contrib/drivers/oracle/v2
go 1.22
toolchain go1.22.0
require (
github.com/gogf/gf/v2 v2.8.3
github.com/gogf/gf/v2 v2.9.0
github.com/sijms/go-ora/v2 v2.7.10
)

View File

@ -2,10 +2,8 @@ module github.com/gogf/gf/contrib/drivers/pgsql/v2
go 1.22
toolchain go1.22.0
require (
github.com/gogf/gf/v2 v2.8.3
github.com/gogf/gf/v2 v2.9.0
github.com/lib/pq v1.10.9
)

View File

@ -124,7 +124,7 @@ func (d *Driver) ConvertValueForLocal(ctx context.Context, fieldType string, fie
// String slice.
case "_varchar", "_text":
var result pq.StringArray
var result = make(pq.StringArray, 0)
if err := result.Scan(fieldValue); err != nil {
return nil, err
}

View File

@ -2,11 +2,9 @@ module github.com/gogf/gf/contrib/drivers/sqlite/v2
go 1.22
toolchain go1.22.0
require (
github.com/glebarez/go-sqlite v1.21.2
github.com/gogf/gf/v2 v2.8.3
github.com/gogf/gf/v2 v2.9.0
)
require (

View File

@ -2,10 +2,8 @@ module github.com/gogf/gf/contrib/drivers/sqlitecgo/v2
go 1.22
toolchain go1.22.0
require (
github.com/gogf/gf/v2 v2.8.3
github.com/gogf/gf/v2 v2.9.0
github.com/mattn/go-sqlite3 v1.14.17
)

View File

@ -3,7 +3,7 @@ module github.com/gogf/gf/contrib/metric/otelmetric/v2
go 1.22
require (
github.com/gogf/gf/v2 v2.8.3
github.com/gogf/gf/v2 v2.9.0
github.com/prometheus/client_golang v1.19.1
go.opentelemetry.io/contrib/instrumentation/runtime v0.49.0
go.opentelemetry.io/otel v1.32.0

View File

@ -2,10 +2,8 @@ module github.com/gogf/gf/contrib/nosql/redis/v2
go 1.22
toolchain go1.22.0
require (
github.com/gogf/gf/v2 v2.8.3
github.com/gogf/gf/v2 v2.9.0
github.com/redis/go-redis/v9 v9.7.0
go.opentelemetry.io/otel v1.32.0
go.opentelemetry.io/otel/trace v1.32.0

View File

@ -2,10 +2,8 @@ module github.com/gogf/gf/contrib/registry/consul/v2
go 1.22
toolchain go1.22.0
require (
github.com/gogf/gf/v2 v2.8.3
github.com/gogf/gf/v2 v2.9.0
github.com/hashicorp/consul/api v1.26.1
)

View File

@ -3,7 +3,7 @@ module github.com/gogf/gf/contrib/registry/etcd/v2
go 1.22
require (
github.com/gogf/gf/v2 v2.8.3
github.com/gogf/gf/v2 v2.9.0
go.etcd.io/etcd/client/v3 v3.5.17
google.golang.org/grpc v1.59.0
)

View File

@ -45,6 +45,7 @@ func Test_HTTP_Registry(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
client := g.Client()
client.SetDiscovery(gsvc.GetRegistry())
client.SetPrefix(fmt.Sprintf("http://%s", svcName))
// GET
t.Assert(client.GetContent(ctx, "/http-registry"), svcName)
@ -71,6 +72,7 @@ func Test_HTTP_Discovery_Disable(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
client := g.Client()
client.SetDiscovery(gsvc.GetRegistry())
client.SetPrefix(fmt.Sprintf("http://%s", svcName))
result, err := client.Get(ctx, "/http-registry")
defer result.Close()

View File

@ -2,9 +2,7 @@ module github.com/gogf/gf/contrib/registry/file/v2
go 1.22
toolchain go1.22.0
require github.com/gogf/gf/v2 v2.8.3
require github.com/gogf/gf/v2 v2.9.0
require (
github.com/BurntSushi/toml v1.4.0 // indirect

View File

@ -2,10 +2,8 @@ module github.com/gogf/gf/contrib/registry/nacos/v2
go 1.22
toolchain go1.22.0
require (
github.com/gogf/gf/v2 v2.8.3
github.com/gogf/gf/v2 v2.9.0
github.com/nacos-group/nacos-sdk-go/v2 v2.2.7
)

View File

@ -2,10 +2,8 @@ module github.com/gogf/gf/contrib/registry/polaris/v2
go 1.22
toolchain go1.22.0
require (
github.com/gogf/gf/v2 v2.8.3
github.com/gogf/gf/v2 v2.9.0
github.com/polarismesh/polaris-go v1.5.8
)

View File

@ -2,11 +2,9 @@ module github.com/gogf/gf/contrib/registry/zookeeper/v2
go 1.22
toolchain go1.22.0
require (
github.com/go-zookeeper/zk v1.0.3
github.com/gogf/gf/v2 v2.8.3
github.com/gogf/gf/v2 v2.9.0
golang.org/x/sync v0.10.0
)

View File

@ -2,11 +2,9 @@ module github.com/gogf/gf/contrib/rpc/grpcx/v2
go 1.22
toolchain go1.22.0
require (
github.com/gogf/gf/contrib/registry/file/v2 v2.8.3
github.com/gogf/gf/v2 v2.8.3
github.com/gogf/gf/contrib/registry/file/v2 v2.9.0
github.com/gogf/gf/v2 v2.9.0
go.opentelemetry.io/otel v1.32.0
go.opentelemetry.io/otel/trace v1.32.0
google.golang.org/grpc v1.64.1

View File

@ -2,9 +2,7 @@ module github.com/gogf/gf/contrib/sdk/httpclient/v2
go 1.22
toolchain go1.22.0
require github.com/gogf/gf/v2 v2.8.3
require github.com/gogf/gf/v2 v2.9.0
require (
github.com/BurntSushi/toml v1.4.0 // indirect

View File

@ -2,10 +2,8 @@ module github.com/gogf/gf/contrib/trace/otlpgrpc/v2
go 1.22
toolchain go1.22.0
require (
github.com/gogf/gf/v2 v2.8.3
github.com/gogf/gf/v2 v2.9.0
go.opentelemetry.io/otel v1.32.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.24.0

View File

@ -2,10 +2,8 @@ module github.com/gogf/gf/contrib/trace/otlphttp/v2
go 1.22
toolchain go1.22.0
require (
github.com/gogf/gf/v2 v2.8.3
github.com/gogf/gf/v2 v2.9.0
go.opentelemetry.io/otel v1.32.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.24.0

View File

@ -516,8 +516,9 @@ type Core struct {
links *gmap.Map // links caches all created links by node.
logger glog.ILogger // Logger for logging functionality.
config *ConfigNode // Current config node.
localTypeMap *gmap.StrAnyMap // Local type map for database field type conversion.
dynamicConfig dynamicConfig // Dynamic configurations, which can be changed in runtime.
innerMemCache *gcache.Cache
innerMemCache *gcache.Cache // Internal memory cache for storing temporary data.
}
type dynamicConfig struct {
@ -768,6 +769,8 @@ const (
SqlTypeStmtQueryRowContext SqlType = "DB.Statement.QueryRowContext"
)
// LocalType is a type that defines the local storage type of a field value.
// It is used to specify how the field value should be processed locally.
type LocalType string
const (
@ -925,6 +928,7 @@ func newDBByConfigNode(node *ConfigNode, group string) (db DB, err error) {
links: gmap.New(true),
logger: glog.New(),
config: node,
localTypeMap: gmap.NewStrAnyMap(true),
innerMemCache: gcache.New(),
dynamicConfig: dynamicConfig{
MaxIdleConnCount: node.MaxIdleConnCount,

View File

@ -0,0 +1,82 @@
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package gdb
import (
"reflect"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/internal/json"
"github.com/gogf/gf/v2/util/gconv"
)
// iVal is used for type assert api for Val().
type iVal interface {
Val() any
}
var (
// converter is the internal type converter for gdb.
converter = gconv.NewConverter()
)
func init() {
converter.RegisterAnyConverterFunc(
sliceTypeConverterFunc,
reflect.TypeOf([]string{}),
reflect.TypeOf([]float32{}),
reflect.TypeOf([]float64{}),
reflect.TypeOf([]int{}),
reflect.TypeOf([]int32{}),
reflect.TypeOf([]int64{}),
reflect.TypeOf([]uint{}),
reflect.TypeOf([]uint32{}),
reflect.TypeOf([]uint64{}),
)
}
// GetConverter returns the internal type converter for gdb.
func GetConverter() gconv.Converter {
return converter
}
func sliceTypeConverterFunc(from any, to reflect.Value) (err error) {
v, ok := from.(iVal)
if !ok {
return nil
}
fromVal := v.Val()
switch x := fromVal.(type) {
case []byte:
dst := to.Addr().Interface()
err = json.Unmarshal(x, dst)
case string:
dst := to.Addr().Interface()
err = json.Unmarshal([]byte(x), dst)
default:
fromType := reflect.TypeOf(fromVal)
switch fromType.Kind() {
case reflect.Slice:
convertOption := gconv.ConvertOption{
SliceOption: gconv.SliceOption{ContinueOnError: true},
MapOption: gconv.MapOption{ContinueOnError: true},
StructOption: gconv.StructOption{ContinueOnError: true},
}
dv, err := converter.ConvertWithTypeName(fromVal, to.Type().String(), convertOption)
if err != nil {
return err
}
to.Set(reflect.ValueOf(dv))
default:
err = gerror.Newf(
`unsupported type converting from type "%T" to type "%T"`,
fromVal, to,
)
}
}
return err
}

View File

@ -223,6 +223,8 @@ func (c *Core) GetScan(ctx context.Context, pointer interface{}, sql string, arg
case reflect.Struct:
return c.db.GetCore().doGetStruct(ctx, pointer, sql, args...)
default:
}
return gerror.NewCodef(
gcode.CodeInvalidParameter,
@ -735,6 +737,7 @@ func (c *Core) HasTable(name string) (bool, error) {
return false, nil
}
// GetInnerMemCache retrieves and returns the inner memory cache object.
func (c *Core) GetInnerMemCache() *gcache.Cache {
return c.innerMemCache
}

View File

@ -223,7 +223,9 @@ Default:
}
// CheckLocalTypeForField checks and returns corresponding type for given db type.
func (c *Core) CheckLocalTypeForField(ctx context.Context, fieldType string, fieldValue interface{}) (LocalType, error) {
// The `fieldType` is retrieved from ColumnTypes of db driver, example:
// UNSIGNED INT
func (c *Core) CheckLocalTypeForField(ctx context.Context, fieldType string, _ interface{}) (LocalType, error) {
var (
typeName string
typePattern string
@ -233,7 +235,12 @@ func (c *Core) CheckLocalTypeForField(ctx context.Context, fieldType string, fie
typeName = gstr.Trim(match[1])
typePattern = gstr.Trim(match[2])
} else {
typeName = gstr.Split(fieldType, " ")[0]
var array = gstr.SplitAndTrim(fieldType, " ")
if len(array) > 1 && gstr.Equal(array[0], "unsigned") {
typeName = array[1]
} else if len(array) > 0 {
typeName = array[0]
}
}
typeName = strings.ToLower(typeName)
@ -291,11 +298,6 @@ func (c *Core) CheckLocalTypeForField(ctx context.Context, fieldType string, fie
if typePattern == "1" {
return LocalTypeBool, nil
}
s := gconv.String(fieldValue)
// mssql is true|false string.
if strings.EqualFold(s, "true") || strings.EqualFold(s, "false") {
return LocalTypeBool, nil
}
if gstr.ContainsI(fieldType, "unsigned") {
return LocalTypeUint64Bytes, nil
}

View File

@ -19,9 +19,15 @@ import (
type Propagation string
const (
// PropagationNested starts a nested transaction if already in a transaction,
// or behaves like PropagationRequired if not in a transaction.
//
// It is the default behavior.
PropagationNested Propagation = "NESTED"
// PropagationRequired starts a new transaction if not in a transaction,
// or uses the existing transaction if already in a transaction.
PropagationRequired Propagation = "" // REQUIRED
PropagationRequired Propagation = "REQUIRED"
// PropagationSupports executes within the existing transaction if present,
// otherwise executes without transaction.
@ -30,10 +36,6 @@ const (
// PropagationRequiresNew starts a new transaction, and suspends the current transaction if one exists.
PropagationRequiresNew Propagation = "REQUIRES_NEW"
// PropagationNested starts a nested transaction if already in a transaction,
// or behaves like PropagationRequired if not in a transaction.
PropagationNested Propagation = "NESTED"
// PropagationNotSupported executes non-transactional, suspends any existing transaction.
PropagationNotSupported Propagation = "NOT_SUPPORTED"
@ -66,7 +68,8 @@ var transactionIdGenerator = gtype.NewUint64()
// DefaultTxOptions returns the default transaction options.
func DefaultTxOptions() TxOptions {
return TxOptions{
Propagation: PropagationRequired,
// Note the default propagation type is PropagationNested not PropagationRequired.
Propagation: PropagationNested,
}
}
@ -138,11 +141,14 @@ func (c *Core) TransactionWithOptions(
switch opts.Propagation {
case PropagationRequired:
if currentTx != nil {
return currentTx.Transaction(ctx, f)
return f(ctx, currentTx)
}
return c.createNewTransaction(ctx, opts, f)
case PropagationSupports:
if currentTx == nil {
currentTx = c.newEmptyTX()
}
return f(ctx, currentTx)
case PropagationMandatory:
@ -160,7 +166,7 @@ func (c *Core) TransactionWithOptions(
case PropagationNotSupported:
ctx = WithoutTX(ctx, group)
return f(ctx, nil)
return f(ctx, c.newEmptyTX())
case PropagationNever:
if currentTx != nil {
@ -169,22 +175,12 @@ func (c *Core) TransactionWithOptions(
"transaction propagation NEVER cannot run within an existing transaction",
)
}
return f(ctx, nil)
ctx = WithoutTX(ctx, group)
return f(ctx, c.newEmptyTX())
case PropagationNested:
if currentTx != nil {
// Create savepoint for nested transaction
if err = currentTx.Begin(); err != nil {
return err
}
defer func() {
if err != nil {
if rbErr := currentTx.Rollback(); rbErr != nil {
err = gerror.Wrap(err, rbErr.Error())
}
}
}()
return f(ctx, currentTx)
return currentTx.Transaction(ctx, f)
}
return c.createNewTransaction(ctx, opts, f)
@ -280,6 +276,10 @@ func TXFromCtx(ctx context.Context, group string) TX {
if tx.IsClosed() {
return nil
}
// no underlying sql tx.
if tx.GetSqlTX() == nil {
return nil
}
tx = tx.Ctx(ctx)
return tx
}

View File

@ -46,6 +46,12 @@ type TXCore struct {
cancelFunc context.CancelFunc
}
func (c *Core) newEmptyTX() TX {
return &TXCore{
db: c.db,
}
}
// transactionKeyForNestedPoint forms and returns the transaction key at current save point.
func (tx *TXCore) transactionKeyForNestedPoint() string {
return tx.db.GetCore().QuoteWord(
@ -427,5 +433,5 @@ func (tx *TXCore) IsOnMaster() bool {
// IsTransaction implements interface function Link.IsTransaction.
func (tx *TXCore) IsTransaction() bool {
return true
return tx != nil
}

View File

@ -103,7 +103,7 @@ func (c *Core) DoExec(ctx context.Context, link Link, sql string, args ...interf
return nil, err
}
} else if !link.IsTransaction() {
// If current link is not transaction link, it checks and retrieves transaction from context.
// If current link is not transaction link, it tries retrieving transaction object from context.
if tx := TXFromCtx(ctx, c.db.GetGroup()); tx != nil {
link = &txLink{tx.GetSqlTX()}
}
@ -478,8 +478,11 @@ func (c *Core) RowsToResult(ctx context.Context, rows *sql.Rows) (Result, error)
// which will cause struct converting issue.
record[columnTypes[i].Name()] = nil
} else {
var convertedValue interface{}
if convertedValue, err = c.columnValueToLocalValue(ctx, value, columnTypes[i]); err != nil {
var (
convertedValue interface{}
columnType = columnTypes[i]
)
if convertedValue, err = c.columnValueToLocalValue(ctx, value, columnType); err != nil {
return nil, err
}
record[columnTypes[i].Name()] = gvar.New(convertedValue)
@ -498,7 +501,9 @@ func (c *Core) OrderRandomFunction() string {
return "RAND()"
}
func (c *Core) columnValueToLocalValue(ctx context.Context, value interface{}, columnType *sql.ColumnType) (interface{}, error) {
func (c *Core) columnValueToLocalValue(
ctx context.Context, value interface{}, columnType *sql.ColumnType,
) (interface{}, error) {
var scanType = columnType.ScanType()
if scanType != nil {
// Common basic builtin types.

View File

@ -247,7 +247,9 @@ func (m *Model) doMappingAndFilterForInsertOrUpdateDataMap(data Map, allowOmitEm
// The parameter `master` specifies whether using the master node if master-slave configured.
func (m *Model) getLink(master bool) Link {
if m.tx != nil {
return &txLink{m.tx.GetSqlTX()}
if sqlTx := m.tx.GetSqlTX(); sqlTx != nil {
return &txLink{sqlTx}
}
}
linkType := m.linkType
if linkType == 0 {

View File

@ -53,7 +53,10 @@ func (r Record) Struct(pointer interface{}) error {
}
return nil
}
return gconv.StructTag(r, pointer, OrmTagForStruct)
return converter.Struct(r, pointer, gconv.StructOption{
PriorityTag: OrmTagForStruct,
ContinueOnError: true,
})
}
// IsEmpty checks and returns whether `r` is empty.

View File

@ -200,5 +200,15 @@ func (r Result) Structs(pointer interface{}) (err error) {
}
return nil
}
return gconv.StructsTag(r, pointer, OrmTagForStruct)
var (
sliceOption = gconv.SliceOption{ContinueOnError: true}
structOption = gconv.StructOption{
PriorityTag: OrmTagForStruct,
ContinueOnError: true,
}
)
return converter.Structs(r, pointer, gconv.StructsOption{
SliceOption: sliceOption,
StructOption: structOption,
})
}

View File

@ -14,6 +14,15 @@ import (
"github.com/gogf/gf/v2/text/gregex"
)
func Test_GetConverter(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
c := GetConverter()
s, err := c.String(1)
t.AssertNil(err)
t.AssertEQ(s, "1")
})
}
func Test_HookSelect_Regex(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var (

View File

@ -72,7 +72,7 @@ func New() *Client {
header: make(map[string]string),
cookies: make(map[string]string),
builder: gsel.GetBuilder(),
discovery: gsvc.GetRegistry(),
discovery: nil,
}
c.header[httpHeaderUserAgent] = defaultClientAgent
// It enables OpenTelemetry for client in default.

View File

@ -16,7 +16,6 @@ import (
"github.com/gogf/gf/v2/internal/intlog"
"github.com/gogf/gf/v2/net/gsel"
"github.com/gogf/gf/v2/net/gsvc"
"github.com/gogf/gf/v2/text/gstr"
)
type discoveryNode struct {
@ -39,7 +38,7 @@ var clientSelectorMap = gmap.New(true)
// internalMiddlewareDiscovery is a client middleware that enables service discovery feature for client.
func internalMiddlewareDiscovery(c *Client, r *http.Request) (response *Response, err error) {
if c.discovery == nil && !isServiceName(r.URL.Host) {
if c.discovery == nil {
return c.Next(r)
}
var (
@ -107,11 +106,3 @@ func updateSelectorNodesByService(ctx context.Context, selector gsel.Selector, s
}
return selector.Update(ctx, nodes)
}
// isServiceName checks and returns whether given input parameter is service name or not.
// It checks by whether the parameter is address by containing port delimiter character ':'.
//
// It does not contain any port number if using service discovery.
func isServiceName(serviceNameOrAddress string) bool {
return !gstr.Contains(serviceNameOrAddress, gsvc.EndpointHostPortDelimiter)
}

View File

@ -64,11 +64,11 @@ type (
Handler *HandlerItem // The handler.
Server string // Server name.
Address string // Listening address.
Domain string // Bound domain.
Domain string // Bound domain, eg: example.com
Type HandlerType // Route handler type.
Middleware string // Bound middleware.
Method string // Handler method name.
Route string // Route URI.
Method string // Handler method name, eg: get, post.
Route string // Route URI, eg: /api/v1/user/{id}.
Priority int // Just for reference.
IsServiceHandler bool // Is a service handler.
}

View File

@ -40,7 +40,7 @@ type Request struct {
// =================================================================================================================
handlers []*HandlerItemParsed // All matched handlers containing handler, hook and middleware for this request.
serveHandler *HandlerItemParsed // Real handler serving for this request, not hook or middleware.
serveHandler *HandlerItemParsed // Real business handler serving for this request, not hook or middleware handler.
handlerResponse interface{} // Handler response object for Request/Response handler.
hasHookHandler bool // A bool marking whether there's hook handler in the handlers for performance purpose.
hasServeHandler bool // A bool marking whether there's serving handler in the handlers for performance purpose.
@ -278,13 +278,3 @@ func (r *Request) ReloadParam() {
r.parsedQuery = false
r.bodyContent = nil
}
// GetHandlerResponse retrieves and returns the handler response object and its error.
func (r *Request) GetHandlerResponse() interface{} {
return r.handlerResponse
}
// GetServeHandler retrieves and returns the user defined handler used to serve this request.
func (r *Request) GetServeHandler() *HandlerItemParsed {
return r.serveHandler
}

View File

@ -107,7 +107,6 @@ func (r *Request) doParse(pointer interface{}, requestType int) error {
return err
}
}
// TODO: https://github.com/gogf/gf/pull/2450
// Validation.
if err = gvalid.New().
Bail().

View File

@ -0,0 +1,17 @@
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package ghttp
// GetHandlerResponse retrieves and returns the handler response object and its error.
func (r *Request) GetHandlerResponse() interface{} {
return r.handlerResponse
}
// GetServeHandler retrieves and returns the user defined handler used to serve this request.
func (r *Request) GetServeHandler() *HandlerItemParsed {
return r.serveHandler
}

View File

@ -8,7 +8,6 @@ package ghttp
import (
"github.com/gogf/gf/v2/container/gvar"
"github.com/gogf/gf/v2/internal/empty"
"github.com/gogf/gf/v2/net/goai"
"github.com/gogf/gf/v2/os/gstructs"
"github.com/gogf/gf/v2/util/gconv"
@ -178,15 +177,17 @@ func (r *Request) doGetRequestStruct(pointer interface{}, mapping ...map[string]
if data == nil {
data = map[string]interface{}{}
}
// Default struct values.
if err = r.mergeDefaultStructValue(data, pointer); err != nil {
return data, nil
}
// `in` Tag Struct values.
if err = r.mergeInTagStructValue(data); err != nil {
return data, nil
}
// Default struct values.
if err = r.mergeDefaultStructValue(data, pointer); err != nil {
return data, nil
}
return data, gconv.Struct(data, pointer, mapping...)
}
@ -194,20 +195,9 @@ func (r *Request) doGetRequestStruct(pointer interface{}, mapping ...map[string]
func (r *Request) mergeDefaultStructValue(data map[string]interface{}, pointer interface{}) error {
fields := r.serveHandler.Handler.Info.ReqStructFields
if len(fields) > 0 {
var (
foundKey string
foundValue interface{}
)
for _, field := range fields {
if tagValue := field.TagDefault(); tagValue != "" {
foundKey, foundValue = gutil.MapPossibleItemByKey(data, field.Name())
if foundKey == "" {
data[field.Name()] = tagValue
} else {
if empty.IsEmpty(foundValue) {
data[foundKey] = tagValue
}
}
mergeTagValueWithFoundKey(data, false, field.Name(), field.Name(), tagValue)
}
}
return nil
@ -219,19 +209,8 @@ func (r *Request) mergeDefaultStructValue(data map[string]interface{}, pointer i
return err
}
if len(tagFields) > 0 {
var (
foundKey string
foundValue interface{}
)
for _, field := range tagFields {
foundKey, foundValue = gutil.MapPossibleItemByKey(data, field.Name())
if foundKey == "" {
data[field.Name()] = field.TagValue
} else {
if empty.IsEmpty(foundValue) {
data[foundKey] = field.TagValue
}
}
mergeTagValueWithFoundKey(data, false, field.Name(), field.Name(), field.TagValue)
}
}
@ -261,34 +240,29 @@ func (r *Request) mergeInTagStructValue(data map[string]interface{}) error {
for _, field := range fields {
if tagValue := field.TagIn(); tagValue != "" {
findKey := field.TagPriorityName()
switch tagValue {
case goai.ParameterInHeader:
foundHeaderKey, foundHeaderValue := gutil.MapPossibleItemByKey(headerMap, field.TagPriorityName())
if foundHeaderKey != "" {
foundKey, foundValue = gutil.MapPossibleItemByKey(data, foundHeaderKey)
if foundKey == "" {
data[field.Name()] = foundHeaderValue
} else {
if empty.IsEmpty(foundValue) {
data[foundKey] = foundHeaderValue
}
}
}
foundKey, foundValue = gutil.MapPossibleItemByKey(headerMap, findKey)
case goai.ParameterInCookie:
foundCookieKey, foundCookieValue := gutil.MapPossibleItemByKey(cookieMap, field.TagPriorityName())
if foundCookieKey != "" {
foundKey, foundValue = gutil.MapPossibleItemByKey(data, foundCookieKey)
if foundKey == "" {
data[field.Name()] = foundCookieValue
} else {
if empty.IsEmpty(foundValue) {
data[foundKey] = foundCookieValue
}
}
}
foundKey, foundValue = gutil.MapPossibleItemByKey(cookieMap, findKey)
}
if foundKey != "" {
mergeTagValueWithFoundKey(data, true, foundKey, field.Name(), foundValue)
}
}
}
}
return nil
}
// mergeTagValueWithFoundKey merges the request parameters when the key does not exist in the map or overwritten is true or the value is nil.
func mergeTagValueWithFoundKey(data map[string]interface{}, overwritten bool, findKey string, fieldName string, tagValue interface{}) {
if foundKey, foundValue := gutil.MapPossibleItemByKey(data, findKey); foundKey == "" {
data[fieldName] = tagValue
} else {
if overwritten || foundValue == nil {
data[foundKey] = tagValue
}
}
}

View File

@ -18,6 +18,7 @@ import (
"github.com/gogf/gf/v2/internal/intlog"
"github.com/gogf/gf/v2/internal/json"
"github.com/gogf/gf/v2/text/gregex"
"github.com/gogf/gf/v2/util/gmeta"
)
// handlerCacheItem is an item just for internal router searching cache.
@ -252,40 +253,71 @@ func (s *Server) searchHandlers(method, path, domain string) (parsedItems []*Han
}
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
func (item HandlerItem) MarshalJSON() ([]byte, error) {
switch item.Type {
func (h *HandlerItem) MarshalJSON() ([]byte, error) {
switch h.Type {
case HandlerTypeHook:
return json.Marshal(
fmt.Sprintf(
`%s %s:%s (%s)`,
item.Router.Uri,
item.Router.Domain,
item.Router.Method,
item.HookName,
h.Router.Uri,
h.Router.Domain,
h.Router.Method,
h.HookName,
),
)
case HandlerTypeMiddleware:
return json.Marshal(
fmt.Sprintf(
`%s %s:%s (MIDDLEWARE)`,
item.Router.Uri,
item.Router.Domain,
item.Router.Method,
h.Router.Uri,
h.Router.Domain,
h.Router.Method,
),
)
default:
return json.Marshal(
fmt.Sprintf(
`%s %s:%s`,
item.Router.Uri,
item.Router.Domain,
item.Router.Method,
h.Router.Uri,
h.Router.Domain,
h.Router.Method,
),
)
}
}
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
func (item HandlerItemParsed) MarshalJSON() ([]byte, error) {
return json.Marshal(item.Handler)
func (h *HandlerItemParsed) MarshalJSON() ([]byte, error) {
return json.Marshal(h.Handler)
}
// GetMetaTag retrieves and returns the metadata value associated with the given key from the request struct.
// The meta value is from struct tags from g.Meta/gmeta.Meta type.
func (h *HandlerItem) GetMetaTag(key string) string {
if h == nil {
return ""
}
metaValue := gmeta.Get(h.Info.Type.In(1), key)
if metaValue != nil {
return metaValue.String()
}
return ""
}
// GetMetaTag retrieves and returns the metadata value associated with the given key from the request struct.
// The meta value is from struct tags from g.Meta/gmeta.Meta type.
// For example:
//
// type GetMetaTagReq struct {
// g.Meta `path:"/test" method:"post" summary:"meta_tag" tags:"meta"`
// // ...
// }
//
// r.GetServeHandler().GetMetaTag("summary") // returns "meta_tag"
// r.GetServeHandler().GetMetaTag("method") // returns "post"
func (h *HandlerItemParsed) GetMetaTag(key string) string {
if h == nil || h.Handler == nil {
return ""
}
return h.Handler.GetMetaTag(key)
}

View File

@ -30,15 +30,15 @@ func Test_ConfigFromMap(t *testing.T) {
"readTimeout": "60s",
"indexFiles": g.Slice{"index.php", "main.php"},
"errorLogEnabled": true,
"cookieMaxAge": "1y",
"cookieMaxAge": "1d",
"cookieSameSite": "lax",
"cookieSecure": true,
"cookieHttpOnly": true,
}
config, err := ghttp.ConfigFromMap(m)
t.AssertNil(err)
d1, _ := time.ParseDuration(gconv.String(m["readTimeout"]))
d2, _ := time.ParseDuration(gconv.String(m["cookieMaxAge"]))
d1, _ := gtime.ParseDuration(gconv.String(m["readTimeout"]))
d2, _ := gtime.ParseDuration(gconv.String(m["cookieMaxAge"]))
t.Assert(config.Address, m["address"])
t.Assert(config.ReadTimeout, d1)
t.Assert(config.CookieMaxAge, d2)

View File

@ -1,3 +1,9 @@
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package ghttp_test
import (
@ -13,34 +19,34 @@ import (
"github.com/gogf/gf/v2/util/guid"
)
type UserReq struct {
// UserTagInReq struct tag "in" supports: header, cookie
type UserTagInReq struct {
g.Meta `path:"/user" tags:"User" method:"post" summary:"user api" title:"api title"`
Id int `v:"required" d:"1"`
Name string `v:"required" in:"cookie"`
Age string `v:"required" in:"header"`
// header,query,cookie,form
}
type UserRes struct {
type UserTagInRes struct {
g.Meta `mime:"text/html" example:"string"`
}
var (
User = cUser{}
UserTagIn = cUserTagIn{}
)
type cUser struct{}
type cUserTagIn struct{}
func (c *cUser) User(ctx context.Context, req *UserReq) (res *UserRes, err error) {
func (c *cUserTagIn) User(ctx context.Context, req *UserTagInReq) (res *UserTagInRes, err error) {
g.RequestFromCtx(ctx).Response.WriteJson(req)
return
}
func Test_Params_Tag(t *testing.T) {
func Test_ParamsTagIn(t *testing.T) {
s := g.Server(guid.S())
s.Group("/", func(group *ghttp.RouterGroup) {
group.Middleware(ghttp.MiddlewareHandlerResponse)
group.Bind(User)
group.Bind(UserTagIn)
})
s.SetDumpRouterMap(false)
s.Start()
@ -56,17 +62,101 @@ func Test_Params_Tag(t *testing.T) {
client.SetHeader("age", "18")
t.Assert(client.PostContent(ctx, "/user"), `{"Id":1,"Name":"john","Age":"18"}`)
t.Assert(client.PostContent(ctx, "/user", "name=&age=&id="), `{"Id":1,"Name":"john","Age":"18"}`)
t.Assert(client.PostContent(ctx, "/user", "name=&age="), `{"Id":1,"Name":"john","Age":"18"}`)
})
}
func Benchmark_ParamTag(b *testing.B) {
type UserTagDefaultReq struct {
g.Meta `path:"/user-default" method:"post,get" summary:"user default tag api"`
Id int `v:"required" d:"1"`
Name string `d:"john"`
Age int `d:"18"`
Score float64 `d:"99.9"`
IsVip bool `d:"true"`
NickName string `p:"nickname" d:"nickname-default"`
EmptyStr string `d:""`
Email string
Address string
}
type UserTagDefaultRes struct {
g.Meta `mime:"application/json" example:"string"`
}
var (
UserTagDefault = cUserTagDefault{}
)
type cUserTagDefault struct{}
func (c *cUserTagDefault) User(ctx context.Context, req *UserTagDefaultReq) (res *UserTagDefaultRes, err error) {
g.RequestFromCtx(ctx).Response.WriteJson(req)
return
}
func Test_ParamsTagDefault(t *testing.T) {
s := g.Server(guid.S())
s.Group("/", func(group *ghttp.RouterGroup) {
group.Middleware(ghttp.MiddlewareHandlerResponse)
group.Bind(UserTagDefault)
})
s.SetDumpRouterMap(false)
s.Start()
defer s.Shutdown()
time.Sleep(100 * time.Millisecond)
gtest.C(t, func(t *gtest.T) {
prefix := fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort())
client := g.Client()
client.SetPrefix(prefix)
// Test with no parameters, should use all default values
resp := client.GetContent(ctx, "/user-default")
t.Assert(resp, `{"Id":1,"Name":"john","Age":18,"Score":99.9,"IsVip":true,"NickName":"nickname-default","EmptyStr":"","Email":"","Address":""}`)
// Test with partial parameters (query method), should use partial default values
resp = client.GetContent(ctx, "/user-default?id=100&name=smith")
t.Assert(resp, `{"Id":100,"Name":"smith","Age":18,"Score":99.9,"IsVip":true,"NickName":"nickname-default","EmptyStr":"","Email":"","Address":""}`)
// Test with partial parameters (query method), should use partial default values
resp = client.GetContent(ctx, "/user-default?id=100&name=smith&age")
t.Assert(resp, `{"Id":100,"Name":"smith","Age":18,"Score":99.9,"IsVip":true,"NickName":"nickname-default","EmptyStr":"","Email":"","Address":""}`)
// Test providing partial parameters via POST form
resp = client.PostContent(ctx, "/user-default", "id=200&age=30&nickname=jack")
t.Assert(resp, `{"Id":200,"Name":"john","Age":30,"Score":99.9,"IsVip":true,"NickName":"jack","EmptyStr":"","Email":"","Address":""}`)
// Test providing partial parameters via POST JSON
resp = client.ContentJson().PostContent(ctx, "/user-default", g.Map{
"id": 300,
"name": "bob",
"score": 88.8,
"address": "beijing",
})
t.Assert(resp, `{"Id":300,"Name":"bob","Age":18,"Score":88.8,"IsVip":true,"NickName":"nickname-default","EmptyStr":"","Email":"","Address":"beijing"}`)
// Test providing JSON content via GET request
resp = client.ContentJson().PostContent(ctx, "/user-default", `{"id":500,"isVip":false}`)
t.Assert(resp, `{"Id":500,"Name":"john","Age":18,"Score":99.9,"IsVip":false,"NickName":"nickname-default","EmptyStr":"","Email":"","Address":""}`)
// Test providing empty values, should use default values
resp = client.PostContent(ctx, "/user-default", "id=400&name=&age=")
t.Assert(resp, `{"Id":400,"Name":"","Age":0,"Score":99.9,"IsVip":true,"NickName":"nickname-default","EmptyStr":"","Email":"","Address":""}`)
// Test providing JSON content via GET request
resp = client.ContentJson().GetContent(ctx, "/user-default", `{"id":500,"isVip":false}`)
t.Assert(resp, `{"Id":500,"Name":"john","Age":18,"Score":99.9,"IsVip":false,"NickName":"nickname-default","EmptyStr":"","Email":"","Address":""}`)
})
}
func Benchmark_ParamTagIn(b *testing.B) {
b.StopTimer()
s := g.Server(guid.S())
s.Group("/", func(group *ghttp.RouterGroup) {
group.Middleware(ghttp.MiddlewareHandlerResponse)
group.Bind(User)
group.Bind(UserTagIn)
})
s.SetDumpRouterMap(false)
s.SetAccessLogEnabled(false)

View File

@ -8,6 +8,7 @@ package ghttp_test
import (
"bytes"
"context"
"fmt"
"io"
"testing"
@ -861,3 +862,44 @@ func Test_Params_GetRequestMapStrVar(t *testing.T) {
t.Assert(client.GetContent(ctx, "/GetRequestMapStrVar", "id=1"), 1)
})
}
type GetMetaTagReq struct {
g.Meta `path:"/test" method:"post" summary:"meta_tag" tags:"meta"`
Name string
}
type GetMetaTagRes struct{}
type GetMetaTagSt struct{}
func (r GetMetaTagSt) PostTest(ctx context.Context, req *GetMetaTagReq) (*GetMetaTagRes, error) {
return &GetMetaTagRes{}, nil
}
func TestRequest_GetServeHandler_GetMetaTag(t *testing.T) {
s := g.Server(guid.S())
s.Use(func(r *ghttp.Request) {
r.Response.Writef(
"summary:%s,method:%s",
r.GetServeHandler().GetMetaTag("summary"),
r.GetServeHandler().GetMetaTag("method"),
)
})
s.Group("/", func(grp *ghttp.RouterGroup) {
grp.Bind(GetMetaTagSt{})
})
s.SetDumpRouterMap(false)
s.Start()
defer s.Shutdown()
time.Sleep(1000 * time.Millisecond)
s.SetDumpRouterMap(false)
s.Start()
defer s.Shutdown()
time.Sleep(100 * time.Millisecond)
gtest.C(t, func(t *gtest.T) {
client := g.Client()
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort()))
t.Assert(client.PostContent(ctx, "/test", "name=john"), "summary:meta_tag,method:post")
})
}

View File

@ -21,7 +21,7 @@ import (
"github.com/gogf/gf/v2/util/guid"
)
func Test_Router_Handler_Strict_WithObject(t *testing.T) {
func Test_Router_Handler_Standard_WithObject(t *testing.T) {
type TestReq struct {
Age int
Name string
@ -137,7 +137,7 @@ func (ControllerForHandlerWithObjectAndMeta2) Test4(ctx context.Context, req *Te
}, nil
}
func Test_Router_Handler_Strict_WithObjectAndMeta(t *testing.T) {
func Test_Router_Handler_Standard_WithObjectAndMeta(t *testing.T) {
s := g.Server(guid.S())
s.Use(ghttp.MiddlewareHandlerResponse)
s.Group("/", func(group *ghttp.RouterGroup) {
@ -159,7 +159,7 @@ func Test_Router_Handler_Strict_WithObjectAndMeta(t *testing.T) {
})
}
func Test_Router_Handler_Strict_Group_Bind(t *testing.T) {
func Test_Router_Handler_Standard_Group_Bind(t *testing.T) {
s := g.Server(guid.S())
s.Use(ghttp.MiddlewareHandlerResponse)
s.Group("/api/v1", func(group *ghttp.RouterGroup) {
@ -300,7 +300,7 @@ func Test_Custom_Slice_Type_Attribute(t *testing.T) {
})
}
func Test_Router_Handler_Strict_WithGeneric(t *testing.T) {
func Test_Router_Handler_Standard_WithGeneric(t *testing.T) {
type TestReq struct {
Age int
}
@ -397,7 +397,7 @@ func (c *ParameterCaseSensitiveController) Path(
return &ParameterCaseSensitiveControllerPathRes{Path: req.Path}, nil
}
func Test_Router_Handler_Strict_ParameterCaseSensitive(t *testing.T) {
func Test_Router_Handler_Standard_ParameterCaseSensitive(t *testing.T) {
s := g.Server(guid.S())
s.Use(ghttp.MiddlewareHandlerResponse)
s.Group("/", func(group *ghttp.RouterGroup) {
@ -505,7 +505,6 @@ func (t *testNullStringIssue3465) Test(ctx context.Context, req *testNullStringI
// https://github.com/gogf/gf/issues/3465
func Test_NullString_Issue3465(t *testing.T) {
s := g.Server(guid.S())
s.Use(ghttp.MiddlewareHandlerResponse)
s.Group("/", func(group *ghttp.RouterGroup) {
@ -535,3 +534,40 @@ func Test_NullString_Issue3465(t *testing.T) {
})
}
type testHandlerItemGetMetaTagReq struct {
g.Meta `path:"/test" method:"get" sm:"hello" tags:"示例"`
}
type testHandlerItemGetMetaTagRes struct{}
type testHandlerItemGetMetaTag struct {
}
func (t *testHandlerItemGetMetaTag) Test(ctx context.Context, req *testHandlerItemGetMetaTagReq) (res *testHandlerItemGetMetaTagRes, err error) {
return nil, nil
}
func TestHandlerItem_GetMetaTag(t *testing.T) {
s := g.Server(guid.S())
s.Use(ghttp.MiddlewareHandlerResponse)
s.Group("/", func(group *ghttp.RouterGroup) {
group.Bind(new(testHandlerItemGetMetaTag))
})
s.SetDumpRouterMap(false)
s.Start()
defer s.Shutdown()
time.Sleep(100 * time.Millisecond)
gtest.C(t, func(t *gtest.T) {
routes := s.GetRoutes()
for _, route := range routes {
if !route.IsServiceHandler {
continue
}
t.Assert(route.Handler.GetMetaTag("path"), "/test")
t.Assert(route.Handler.GetMetaTag("method"), "get")
t.Assert(route.Handler.GetMetaTag("sm"), "hello")
t.Assert(route.Handler.GetMetaTag("tags"), "示例")
}
})
}

View File

@ -678,3 +678,51 @@ func Test_Issue4047(t *testing.T) {
t.Assert(s.Logger(), nil)
})
}
// Issue4093Req
type Issue4093Req struct {
g.Meta `path:"/test" method:"post"`
Page int `json:"page" example:"10" d:"1" v:"min:1#页码最小值不能低于1" dc:"当前页码"`
PerPage int `json:"pageSize" example:"1" d:"10" v:"min:1|max:200#每页数量最小值不能低于1|最大值不能大于200" dc:"每页数量"`
Pagination bool `json:"pagination" d:"true" dc:"是否需要进行分页"`
Name string `json:"name" d:"john"`
Number int `json:"number" d:"1"`
}
type Issue4093Res struct {
g.Meta `mime:"text/html" example:"string"`
}
var (
Issue4093 = cIssue4093{}
)
type cIssue4093 struct{}
func (c *cIssue4093) User(ctx context.Context, req *Issue4093Req) (res *Issue4093Res, err error) {
g.RequestFromCtx(ctx).Response.WriteJson(req)
return
}
// https://github.com/gogf/gf/issues/4093
func Test_Issue4093(t *testing.T) {
s := g.Server(guid.S())
s.Group("/", func(group *ghttp.RouterGroup) {
group.Middleware(ghttp.MiddlewareHandlerResponse)
group.Bind(Issue4093)
})
s.SetDumpRouterMap(false)
s.Start()
defer s.Shutdown()
time.Sleep(100 * time.Millisecond)
gtest.C(t, func(t *gtest.T) {
prefix := fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort())
client := g.Client().ContentJson()
client.SetPrefix(prefix)
t.Assert(client.PostContent(ctx, "/test", `{"pagination":false,"name":"","number":0}`), `{"page":1,"pageSize":10,"pagination":false,"name":"","number":0}`)
t.Assert(client.PostContent(ctx, "/test"), `{"page":1,"pageSize":10,"pagination":true,"name":"john","number":1}`)
})
}

View File

@ -36,7 +36,7 @@ type Field struct {
type FieldsInput struct {
// Pointer should be type of struct/*struct.
// TODO this attribute name is not suitable, which would make confuse.
Pointer interface{}
Pointer any
// RecursiveOption specifies the way retrieving the fields recursively if the attribute
// is an embedded struct. It is RecursiveOptionNone in default.
@ -47,7 +47,7 @@ type FieldsInput struct {
type FieldMapInput struct {
// Pointer should be type of struct/*struct.
// TODO this attribute name is not suitable, which would make confuse.
Pointer interface{}
Pointer any
// PriorityTagArray specifies the priority tag array for retrieving from high to low.
// If it's given `nil`, it returns map[name]Field, of which the `name` is attribute name.
@ -123,6 +123,7 @@ func Fields(in FieldsInput) ([]Field, error) {
}
}
continue
default:
}
}
continue
@ -194,6 +195,7 @@ func FieldMap(in FieldMapInput) (map[string]Field, error) {
mapField[k] = tempV
}
}
default:
}
} else {
mapField[field.Name()] = tempField
@ -205,7 +207,19 @@ func FieldMap(in FieldMapInput) (map[string]Field, error) {
// StructType retrieves and returns the struct Type of specified struct/*struct.
// The parameter `object` should be either type of struct/*struct/[]struct/[]*struct.
func StructType(object interface{}) (*Type, error) {
func StructType(object any) (*Type, error) {
// if already reflect.Type
if reflectType, ok := object.(reflect.Type); ok {
for reflectType.Kind() == reflect.Ptr {
reflectType = reflectType.Elem()
}
if reflectType.Kind() == reflect.Struct {
return &Type{
Type: reflectType,
}, nil
}
}
var (
reflectValue reflect.Value
reflectKind reflect.Kind

View File

@ -10,36 +10,161 @@
package gconv
import (
"reflect"
"time"
"github.com/gogf/gf/v2/os/gtime"
"github.com/gogf/gf/v2/util/gconv/internal/converter"
"github.com/gogf/gf/v2/util/gconv/internal/localinterface"
"github.com/gogf/gf/v2/util/gconv/internal/structcache"
)
var (
// Empty strings.
emptyStringMap = map[string]struct{}{
"": {},
"0": {},
"no": {},
"off": {},
"false": {},
}
// Converter is the manager for type converting.
type Converter interface {
ConverterForConvert
ConverterForRegister
ConverterForInt
ConverterForUint
ConverterForTime
ConverterForFloat
ConverterForMap
ConverterForSlice
ConverterForStruct
ConverterForBasic
}
// ConverterForBasic is the basic converting interface.
type ConverterForBasic interface {
Scan(srcValue, dstPointer any, option ...ScanOption) (err error)
String(any any) (string, error)
Bool(any any) (bool, error)
Rune(any any) (rune, error)
}
// ConverterForTime is the converting interface for time.
type ConverterForTime interface {
Time(v any, format ...string) (time.Time, error)
Duration(v any) (time.Duration, error)
GTime(v any, format ...string) (*gtime.Time, error)
}
// ConverterForInt is the converting interface for integer.
type ConverterForInt interface {
Int(v any) (int, error)
Int8(v any) (int8, error)
Int16(v any) (int16, error)
Int32(v any) (int32, error)
Int64(v any) (int64, error)
}
// ConverterForUint is the converting interface for unsigned integer.
type ConverterForUint interface {
Uint(v any) (uint, error)
Uint8(v any) (uint8, error)
Uint16(v any) (uint16, error)
Uint32(v any) (uint32, error)
Uint64(v any) (uint64, error)
}
// ConverterForFloat is the converting interface for float.
type ConverterForFloat interface {
Float32(v any) (float32, error)
Float64(v any) (float64, error)
}
// ConverterForMap is the converting interface for map.
type ConverterForMap interface {
Map(v any, option ...MapOption) (map[string]any, error)
MapStrStr(v any, option ...MapOption) (map[string]string, error)
}
// ConverterForSlice is the converting interface for slice.
type ConverterForSlice interface {
Bytes(v any) ([]byte, error)
Runes(v any) ([]rune, error)
SliceAny(v any, option ...SliceOption) ([]any, error)
SliceFloat32(v any, option ...SliceOption) ([]float32, error)
SliceFloat64(v any, option ...SliceOption) ([]float64, error)
SliceInt(v any, option ...SliceOption) ([]int, error)
SliceInt32(v any, option ...SliceOption) ([]int32, error)
SliceInt64(v any, option ...SliceOption) ([]int64, error)
SliceUint(v any, option ...SliceOption) ([]uint, error)
SliceUint32(v any, option ...SliceOption) ([]uint32, error)
SliceUint64(v any, option ...SliceOption) ([]uint64, error)
SliceStr(v any, option ...SliceOption) ([]string, error)
SliceMap(v any, option ...SliceMapOption) ([]map[string]any, error)
}
// ConverterForStruct is the converting interface for struct.
type ConverterForStruct interface {
Struct(params, pointer any, option ...StructOption) (err error)
Structs(params, pointer any, option ...StructsOption) (err error)
}
// ConverterForConvert is the converting interface for custom converting.
type ConverterForConvert interface {
ConvertWithRefer(fromValue, referValue any, option ...ConvertOption) (any, error)
ConvertWithTypeName(fromValue any, toTypeName string, option ...ConvertOption) (any, error)
}
// ConverterForRegister is the converting interface for custom converter registration.
type ConverterForRegister interface {
RegisterTypeConverterFunc(f any) error
RegisterAnyConverterFunc(f AnyConvertFunc, types ...reflect.Type)
}
type (
// AnyConvertFunc is the function type for converting any to specified type.
AnyConvertFunc = structcache.AnyConvertFunc
// MapOption specifies the option for map converting.
MapOption = converter.MapOption
// SliceOption is the option for Slice type converting.
SliceOption = converter.SliceOption
// SliceMapOption is the option for SliceMap function.
SliceMapOption = converter.SliceMapOption
// ScanOption is the option for the Scan function.
ScanOption = converter.ScanOption
// StructOption is the option for Struct converting.
StructOption = converter.StructOption
// StructsOption is the option for Structs function.
StructsOption = converter.StructsOption
// ConvertOption is the option for converting.
ConvertOption = converter.ConvertOption
)
// IUnmarshalValue is the interface for custom defined types customizing value assignment.
// Note that only pointer can implement interface IUnmarshalValue.
type IUnmarshalValue = localinterface.IUnmarshalValue
func init() {
// register common converters for internal usage.
structcache.RegisterCommonConverter(structcache.CommonConverter{
Int64: Int64,
Uint64: Uint64,
String: String,
Float32: Float32,
Float64: Float64,
Time: Time,
GTime: GTime,
Bytes: Bytes,
Bool: Bool,
})
var (
// defaultConverter is the default management object converting.
defaultConverter = converter.NewConverter()
)
// NewConverter creates and returns management object for type converting.
func NewConverter() Converter {
return converter.NewConverter()
}
// RegisterConverter registers custom converter.
// Deprecated: use RegisterTypeConverterFunc instead for clear
func RegisterConverter(fn any) (err error) {
return RegisterTypeConverterFunc(fn)
}
// RegisterTypeConverterFunc registers custom converter.
func RegisterTypeConverterFunc(fn any) (err error) {
return defaultConverter.RegisterTypeConverterFunc(fn)
}
// RegisterAnyConverterFunc registers custom type converting function for specified type.
func RegisterAnyConverterFunc(f AnyConvertFunc, types ...reflect.Type) {
defaultConverter.RegisterAnyConverterFunc(f, types...)
}

View File

@ -6,331 +6,40 @@
package gconv
import (
"fmt"
"math"
"reflect"
"strconv"
"strings"
"time"
"github.com/gogf/gf/v2/encoding/gbinary"
"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/internal/json"
"github.com/gogf/gf/v2/internal/reflection"
"github.com/gogf/gf/v2/os/gtime"
"github.com/gogf/gf/v2/util/gconv/internal/localinterface"
)
// Byte converts `any` to byte.
func Byte(any any) byte {
v, _ := doByte(any)
v, _ := defaultConverter.Uint8(any)
return v
}
func doByte(any any) (byte, error) {
if v, ok := any.(byte); ok {
return v, nil
}
return doUint8(any)
}
// Bytes converts `any` to []byte.
func Bytes(any any) []byte {
v, _ := doBytes(any)
v, _ := defaultConverter.Bytes(any)
return v
}
func doBytes(any any) ([]byte, error) {
if any == nil {
return nil, nil
}
switch value := any.(type) {
case string:
return []byte(value), nil
case []byte:
return value, nil
default:
if f, ok := value.(localinterface.IBytes); ok {
return f.Bytes(), nil
}
originValueAndKind := reflection.OriginValueAndKind(any)
switch originValueAndKind.OriginKind {
case reflect.Map:
bytes, err := json.Marshal(any)
if err != nil {
return nil, err
}
return bytes, nil
case reflect.Array, reflect.Slice:
var (
ok = true
bytes = make([]byte, originValueAndKind.OriginValue.Len())
)
for i := range bytes {
int32Value, err := doInt32(originValueAndKind.OriginValue.Index(i).Interface())
if err != nil {
return nil, err
}
if int32Value < 0 || int32Value > math.MaxUint8 {
ok = false
break
}
bytes[i] = byte(int32Value)
}
if ok {
return bytes, nil
}
default:
}
return gbinary.Encode(any), nil
}
}
// Rune converts `any` to rune.
func Rune(any any) rune {
v, _ := doRune(any)
v, _ := defaultConverter.Rune(any)
return v
}
func doRune(any any) (rune, error) {
if v, ok := any.(rune); ok {
return v, nil
}
v, err := doInt32(any)
if err != nil {
return 0, err
}
return v, nil
}
// Runes converts `any` to []rune.
func Runes(any any) []rune {
v, _ := doRunes(any)
v, _ := defaultConverter.Runes(any)
return v
}
func doRunes(any any) ([]rune, error) {
if v, ok := any.([]rune); ok {
return v, nil
}
s, err := doString(any)
if err != nil {
return nil, err
}
return []rune(s), nil
}
// String converts `any` to string.
// It's most commonly used converting function.
func String(any any) string {
v, _ := doString(any)
v, _ := defaultConverter.String(any)
return v
}
func doString(any any) (string, error) {
if any == nil {
return "", nil
}
switch value := any.(type) {
case int:
return strconv.Itoa(value), nil
case int8:
return strconv.Itoa(int(value)), nil
case int16:
return strconv.Itoa(int(value)), nil
case int32:
return strconv.Itoa(int(value)), nil
case int64:
return strconv.FormatInt(value, 10), nil
case uint:
return strconv.FormatUint(uint64(value), 10), nil
case uint8:
return strconv.FormatUint(uint64(value), 10), nil
case uint16:
return strconv.FormatUint(uint64(value), 10), nil
case uint32:
return strconv.FormatUint(uint64(value), 10), nil
case uint64:
return strconv.FormatUint(value, 10), nil
case float32:
return strconv.FormatFloat(float64(value), 'f', -1, 32), nil
case float64:
return strconv.FormatFloat(value, 'f', -1, 64), nil
case bool:
return strconv.FormatBool(value), nil
case string:
return value, nil
case []byte:
return string(value), nil
case complex64, complex128:
return fmt.Sprintf("%v", value), nil
case time.Time:
if value.IsZero() {
return "", nil
}
return value.String(), nil
case *time.Time:
if value == nil {
return "", nil
}
return value.String(), nil
case gtime.Time:
if value.IsZero() {
return "", nil
}
return value.String(), nil
case *gtime.Time:
if value == nil {
return "", nil
}
return value.String(), nil
default:
if f, ok := value.(localinterface.IString); ok {
// If the variable implements the String() interface,
// then use that interface to perform the conversion
return f.String(), nil
}
if f, ok := value.(localinterface.IError); ok {
// If the variable implements the Error() interface,
// then use that interface to perform the conversion
return f.Error(), nil
}
// Reflect checks.
var (
rv = reflect.ValueOf(value)
kind = rv.Kind()
)
switch kind {
case
reflect.Chan,
reflect.Map,
reflect.Slice,
reflect.Func,
reflect.Interface,
reflect.UnsafePointer:
if rv.IsNil() {
return "", nil
}
case reflect.String:
return rv.String(), nil
case reflect.Ptr:
if rv.IsNil() {
return "", nil
}
return doString(rv.Elem().Interface())
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return strconv.FormatInt(rv.Int(), 10), nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return strconv.FormatUint(rv.Uint(), 10), nil
case reflect.Uintptr:
return strconv.FormatUint(rv.Uint(), 10), nil
case reflect.Float32, reflect.Float64:
return strconv.FormatFloat(rv.Float(), 'f', -1, 64), nil
case reflect.Bool:
return strconv.FormatBool(rv.Bool()), nil
default:
}
// Finally, we use json.Marshal to convert.
jsonContent, err := json.Marshal(value)
if err != nil {
return fmt.Sprint(value), gerror.WrapCodef(
gcode.CodeInvalidParameter, err, "error marshaling value to JSON for: %v", value,
)
}
return string(jsonContent), nil
}
}
// Bool converts `any` to bool.
// It returns false if `any` is: false, "", 0, "false", "off", "no", empty slice/map.
func Bool(any any) bool {
v, _ := doBool(any)
v, _ := defaultConverter.Bool(any)
return v
}
func doBool(any any) (bool, error) {
if any == nil {
return false, nil
}
switch value := any.(type) {
case bool:
return value, nil
case []byte:
if _, ok := emptyStringMap[strings.ToLower(string(value))]; ok {
return false, nil
}
return true, nil
case string:
if _, ok := emptyStringMap[strings.ToLower(value)]; ok {
return false, nil
}
return true, nil
default:
if f, ok := value.(localinterface.IBool); ok {
return f.Bool(), nil
}
rv := reflect.ValueOf(any)
switch rv.Kind() {
case reflect.Ptr:
if rv.IsNil() {
return false, nil
}
if rv.Type().Elem().Kind() == reflect.Bool {
return rv.Elem().Bool(), nil
}
return doBool(rv.Elem().Interface())
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return rv.Int() != 0, nil
case reflect.Uintptr, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return rv.Uint() != 0, nil
case reflect.Float32, reflect.Float64:
return rv.Float() != 0, nil
case reflect.Bool:
return rv.Bool(), nil
// TODO(MapArraySliceStruct) It might panic here for these types.
case reflect.Map, reflect.Array:
fallthrough
case reflect.Slice:
return rv.Len() != 0, nil
case reflect.Struct:
return true, nil
default:
s, err := doString(any)
if err != nil {
return false, err
}
if _, ok := emptyStringMap[strings.ToLower(s)]; ok {
return false, nil
}
return true, nil
}
}
}
// checkJsonAndUnmarshalUseNumber checks if given `any` is JSON formatted string value and does converting using `json.UnmarshalUseNumber`.
func checkJsonAndUnmarshalUseNumber(any any, target any) bool {
switch r := any.(type) {
case []byte:
if json.Valid(r) {
if err := json.UnmarshalUseNumber(r, &target); err != nil {
return false
}
return true
}
case string:
anyAsBytes := []byte(r)
if json.Valid(anyAsBytes) {
if err := json.UnmarshalUseNumber(anyAsBytes, &target); err != nil {
return false
}
return true
}
}
return false
}

Some files were not shown because too many files have changed in this diff Show More