Showing preview only (1,293K chars total). Download the full file or copy to clipboard to get everything.
Repository: go-gorm/gorm
Branch: master
Commit: 4380dd6dd1a5
Files: 184
Total size: 1.2 MB
Directory structure:
gitextract_lfhwtntz/
├── .github/
│ ├── FUNDING.yml
│ ├── dependabot.yml
│ ├── labels.json
│ ├── release-drafter.yml
│ └── workflows/
│ ├── create-release.yml
│ ├── golangci-lint.yml
│ ├── invalid_question.yml
│ ├── labeler.yml
│ ├── missing_playground.yml
│ ├── stale.yml
│ └── tests.yml
├── .gitignore
├── .golangci.yml
├── CODE_OF_CONDUCT.md
├── LICENSE
├── README.md
├── association.go
├── callbacks/
│ ├── associations.go
│ ├── callbacks.go
│ ├── callmethod.go
│ ├── create.go
│ ├── create_test.go
│ ├── delete.go
│ ├── helper.go
│ ├── helper_test.go
│ ├── interfaces.go
│ ├── preload.go
│ ├── query.go
│ ├── raw.go
│ ├── row.go
│ ├── transaction.go
│ └── update.go
├── callbacks.go
├── chainable_api.go
├── clause/
│ ├── association.go
│ ├── benchmarks_test.go
│ ├── clause.go
│ ├── clause_test.go
│ ├── delete.go
│ ├── delete_test.go
│ ├── expression.go
│ ├── expression_test.go
│ ├── from.go
│ ├── from_test.go
│ ├── group_by.go
│ ├── group_by_test.go
│ ├── insert.go
│ ├── insert_test.go
│ ├── joins.go
│ ├── joins_test.go
│ ├── limit.go
│ ├── limit_test.go
│ ├── locking.go
│ ├── locking_test.go
│ ├── on_conflict.go
│ ├── order_by.go
│ ├── order_by_test.go
│ ├── returning.go
│ ├── returning_test.go
│ ├── select.go
│ ├── select_test.go
│ ├── set.go
│ ├── set_test.go
│ ├── update.go
│ ├── update_test.go
│ ├── values.go
│ ├── values_test.go
│ ├── where.go
│ ├── where_test.go
│ └── with.go
├── errors.go
├── finisher_api.go
├── generics.go
├── go.mod
├── go.sum
├── gorm.go
├── interfaces.go
├── internal/
│ ├── lru/
│ │ └── lru.go
│ └── stmt_store/
│ └── stmt_store.go
├── logger/
│ ├── logger.go
│ ├── slog.go
│ ├── slog_test.go
│ ├── sql.go
│ └── sql_test.go
├── migrator/
│ ├── column_type.go
│ ├── index.go
│ ├── migrator.go
│ └── table_type.go
├── migrator.go
├── model.go
├── prepare_stmt.go
├── scan.go
├── schema/
│ ├── callbacks_test.go
│ ├── constraint.go
│ ├── constraint_test.go
│ ├── field.go
│ ├── field_test.go
│ ├── index.go
│ ├── index_test.go
│ ├── interfaces.go
│ ├── model_test.go
│ ├── naming.go
│ ├── naming_test.go
│ ├── pool.go
│ ├── relationship.go
│ ├── relationship_test.go
│ ├── schema.go
│ ├── schema_helper_test.go
│ ├── schema_test.go
│ ├── serializer.go
│ ├── serializer_test.go
│ ├── utils.go
│ └── utils_test.go
├── soft_delete.go
├── statement.go
├── statement_test.go
├── tests/
│ ├── .gitignore
│ ├── README.md
│ ├── association_generics_test.go
│ ├── associations_belongs_to_test.go
│ ├── associations_has_many_test.go
│ ├── associations_has_one_test.go
│ ├── associations_many2many_test.go
│ ├── associations_test.go
│ ├── benchmark_test.go
│ ├── callbacks_test.go
│ ├── chainable_api_test.go
│ ├── compose.yml
│ ├── connection_test.go
│ ├── connpool_test.go
│ ├── count_test.go
│ ├── create_test.go
│ ├── customize_field_test.go
│ ├── default_value_test.go
│ ├── delete_test.go
│ ├── distinct_test.go
│ ├── embedded_struct_test.go
│ ├── error_translator_test.go
│ ├── gaussdb_test.go
│ ├── generics_test.go
│ ├── go.mod
│ ├── gorm_test.go
│ ├── group_by_test.go
│ ├── helper_test.go
│ ├── hooks_test.go
│ ├── joins_table_test.go
│ ├── joins_test.go
│ ├── lru_test.go
│ ├── main_test.go
│ ├── migrate_test.go
│ ├── multi_primary_keys_test.go
│ ├── named_argument_test.go
│ ├── named_polymorphic_test.go
│ ├── non_std_test.go
│ ├── postgres_test.go
│ ├── preload_suits_test.go
│ ├── preload_test.go
│ ├── prepared_stmt_test.go
│ ├── query_test.go
│ ├── scan_test.go
│ ├── scanner_valuer_test.go
│ ├── scopes_test.go
│ ├── serializer_test.go
│ ├── soft_delete_test.go
│ ├── sql_builder_test.go
│ ├── submodel_test.go
│ ├── table_test.go
│ ├── tests_all.sh
│ ├── tests_test.go
│ ├── tracer_test.go
│ ├── transaction_test.go
│ ├── update_belongs_to_test.go
│ ├── update_has_many_test.go
│ ├── update_has_one_test.go
│ ├── update_many2many_test.go
│ ├── update_test.go
│ └── upsert_test.go
└── utils/
├── tests/
│ ├── dummy_dialecter.go
│ ├── models.go
│ └── utils.go
├── utils.go
├── utils_test.go
├── utils_unix_test.go
└── utils_windows_test.go
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms
github: [jinzhu]
patreon: jinzhu
open_collective: gorm
================================================
FILE: .github/dependabot.yml
================================================
---
version: 2
updates:
- package-ecosystem: gomod
directory: /
schedule:
interval: weekly
- package-ecosystem: github-actions
directory: /
schedule:
interval: weekly
- package-ecosystem: gomod
directory: /tests
schedule:
interval: weekly
================================================
FILE: .github/labels.json
================================================
{
"labels": {
"critical": {
"name": "type:critical",
"colour": "#E84137",
"description": "critical questions"
},
"question": {
"name": "type:question",
"colour": "#EDEDED",
"description": "general questions"
},
"feature": {
"name": "type:feature_request",
"colour": "#43952A",
"description": "feature request"
},
"invalid_question": {
"name": "type:invalid question",
"colour": "#CF2E1F",
"description": "invalid question (not related to GORM or described in document or not enough information provided)"
},
"with_playground": {
"name": "type:with reproduction steps",
"colour": "#00ff00",
"description": "with reproduction steps"
},
"without_playground": {
"name": "type:missing reproduction steps",
"colour": "#CF2E1F",
"description": "missing reproduction steps"
},
"has_pr": {
"name": "type:has pull request",
"colour": "#43952A",
"description": "has pull request"
},
"not_tested": {
"name": "type:not tested",
"colour": "#CF2E1F",
"description": "not tested"
},
"tested": {
"name": "type:tested",
"colour": "#00ff00",
"description": "tested"
},
"breaking_change": {
"name": "type:breaking change",
"colour": "#CF2E1F",
"description": "breaking change"
}
},
"issue": {
"with_playground": {
"requires": 1,
"conditions": [
{
"type": "descriptionMatches",
"pattern": "/github.com\/go-gorm\/playground\/pull\/\\d\\d+/s"
}
]
},
"critical": {
"requires": 1,
"conditions": [
{
"type": "descriptionMatches",
"pattern": "/(critical|urgent)/i"
},
{
"type": "titleMatches",
"pattern": "/(critical|urgent)/i"
}
]
},
"question": {
"requires": 1,
"conditions": [
{
"type": "titleMatches",
"pattern": "/question/i"
},
{
"type": "descriptionMatches",
"pattern": "/question/i"
}
]
},
"feature": {
"requires": 1,
"conditions": [
{
"type": "titleMatches",
"pattern": "/feature/i"
},
{
"type": "descriptionMatches",
"pattern": "/Describe the feature/i"
}
]
},
"without_playground": {
"requires": 6,
"conditions": [
{
"type": "descriptionMatches",
"pattern": "/^((?!github.com\/go-gorm\/playground\/pull\/\\d\\d+).)*$/s"
},
{
"type": "titleMatches",
"pattern": "/^((?!question).)*$/s"
},
{
"type": "descriptionMatches",
"pattern": "/^((?!question).)*$/is"
},
{
"type": "descriptionMatches",
"pattern": "/^((?!Describe the feature).)*$/is"
},
{
"type": "titleMatches",
"pattern": "/^((?!critical|urgent).)*$/s"
},
{
"type": "descriptionMatches",
"pattern": "/^((?!critical|urgent).)*$/s"
}
]
}
},
"pr": {
"critical": {
"requires": 1,
"conditions": [
{
"type": "descriptionMatches",
"pattern": "/(critical|urgent)/i"
},
{
"type": "titleMatches",
"pattern": "/(critical|urgent)/i"
}
]
},
"not_tested": {
"requires": 1,
"conditions": [
{
"type": "descriptionMatches",
"pattern": "/\\[\\] Tested/"
}
]
},
"breaking_change": {
"requires": 1,
"conditions": [
{
"type": "descriptionMatches",
"pattern": "/\\[\\] Non breaking API changes/"
}
]
}
}
}
================================================
FILE: .github/release-drafter.yml
================================================
name-template: 'v Release $NEXT_PATCH_VERSION 🌈'
tag-template: 'v$NEXT_PATCH_VERSION'
categories:
- title: '🚀 Features'
labels:
- 'feature'
- 'enhancement'
- title: '🐛 Bug Fixes'
labels:
- 'fix'
- 'bugfix'
- 'bug'
- title: '🧰 Maintenance'
label: 'chore'
change-template: '- $TITLE @$AUTHOR (#$NUMBER)'
change-title-escapes: '\<*_&'
template: |
## Changes
$CHANGES
================================================
FILE: .github/workflows/create-release.yml
================================================
name: Create Release
on:
push:
tags:
- 'v*.*.*'
permissions:
contents: write
pull-requests: read
jobs:
create_release:
name: Create Release
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Generate Release Notes and Publish
id: generate_release_notes
uses: release-drafter/release-drafter@v6
with:
config-name: 'release-drafter.yml'
name: "Release ${{ github.ref_name }}"
tag: ${{ github.ref_name }}
publish: true
prerelease: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
================================================
FILE: .github/workflows/golangci-lint.yml
================================================
name: golangci-lint
on:
push:
branches:
- main
- master
pull_request:
permissions:
contents: read
pull-requests: read
jobs:
golangci:
name: lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: stable
- name: golangci-lint
uses: golangci/golangci-lint-action@v7
with:
version: v2.0
only-new-issues: true
================================================
FILE: .github/workflows/invalid_question.yml
================================================
name: "Close invalid questions issues"
on:
schedule:
- cron: "*/10 * * * *"
permissions:
contents: read
jobs:
stale:
permissions:
issues: write # for actions/stale to close stale issues
pull-requests: write # for actions/stale to close stale PRs
runs-on: ubuntu-latest
env:
ACTIONS_STEP_DEBUG: true
steps:
- name: Close Stale Issues
uses: actions/stale@v8
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
stale-issue-message: "This issue has been marked as invalid question, please give more information by following the `Question` template, if you believe there is a bug of GORM, please create a pull request that could reproduce the issue on [https://github.com/go-gorm/playground](https://github.com/go-gorm/playground), the issue will be closed in 30 days if no further activity occurs. most likely your question already answered https://github.com/go-gorm/gorm/issues or described in the document https://gorm.io ✨ [Search Before Asking](https://stackoverflow.com/help/how-to-ask) ✨"
stale-issue-label: "status:stale"
days-before-stale: 0
days-before-close: 30
remove-stale-when-updated: true
only-labels: "type:invalid question"
================================================
FILE: .github/workflows/labeler.yml
================================================
name: "Issue Labeler"
on:
issues:
types: [opened, edited, reopened]
pull_request:
types: [opened, edited, reopened]
jobs:
triage:
runs-on: ubuntu-latest
name: Label issues and pull requests
steps:
- name: check out
uses: actions/checkout@v4
- name: labeler
uses: jinzhu/super-labeler-action@develop
with:
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
================================================
FILE: .github/workflows/missing_playground.yml
================================================
name: "Close Missing Playground issues"
on:
schedule:
- cron: "*/10 * * * *"
permissions:
contents: read
jobs:
stale:
permissions:
issues: write # for actions/stale to close stale issues
pull-requests: write # for actions/stale to close stale PRs
runs-on: ubuntu-latest
env:
ACTIONS_STEP_DEBUG: true
steps:
- name: Close Stale Issues
uses: actions/stale@v8
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
stale-issue-message: "The issue has been automatically marked as stale as it missing playground pull request link, which is important to help others understand your issue effectively and make sure the issue hasn't been fixed on latest master, checkout [https://github.com/go-gorm/playground](https://github.com/go-gorm/playground) for details. it will be closed in 30 days if no further activity occurs. if you are asking question, please use the `Question` template, most likely your question already answered https://github.com/go-gorm/gorm/issues or described in the document https://gorm.io ✨ [Search Before Asking](https://stackoverflow.com/help/how-to-ask) ✨"
stale-issue-label: "status:stale"
days-before-stale: 0
days-before-close: 30
remove-stale-when-updated: true
only-labels: "type:missing reproduction steps"
================================================
FILE: .github/workflows/stale.yml
================================================
name: "Stale"
on:
schedule:
- cron: "0 2 * * *"
permissions:
contents: read
jobs:
stale:
permissions:
issues: write # for actions/stale to close stale issues
pull-requests: write # for actions/stale to close stale PRs
runs-on: ubuntu-latest
env:
ACTIONS_STEP_DEBUG: true
steps:
- name: Close Stale Issues
uses: actions/stale@v8
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
stale-issue-message: "This issue has been automatically marked as stale because it has been open 360 days with no activity. Remove stale label or comment or this will be closed in 180 days"
days-before-stale: 360
days-before-close: 180
stale-issue-label: "status:stale"
exempt-issue-labels: 'type:feature,type:with reproduction steps,type:has pull request'
stale-pr-label: 'status:stale'
exempt-pr-labels: 'type:feature,type:with reproduction steps,type:has pull request'
================================================
FILE: .github/workflows/tests.yml
================================================
name: tests
on:
push:
branches-ignore:
- 'gh-pages'
pull_request:
branches-ignore:
- 'gh-pages'
permissions:
contents: read
jobs:
# Label of the container job
sqlite:
strategy:
matrix:
go: ['1.24', '1.25']
platform: [ubuntu-latest] # can not run in windows OS
runs-on: ${{ matrix.platform }}
steps:
- name: Set up Go 1.x
uses: actions/setup-go@v4
with:
go-version: ${{ matrix.go }}
- name: Check out code into the Go module directory
uses: actions/checkout@v4
- name: go mod package cache
uses: actions/cache@v4
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ matrix.go }}-${{ hashFiles('tests/go.mod') }}
- name: Tests
run: GITHUB_ACTION=true GORM_DIALECT=sqlite ./tests/tests_all.sh
mysql:
strategy:
matrix:
dbversion: ['mysql:9', 'mysql:8', 'mysql:5.7']
go: ['1.24', '1.25']
platform: [ubuntu-latest]
runs-on: ${{ matrix.platform }}
services:
mysql:
image: ${{ matrix.dbversion }}
env:
MYSQL_DATABASE: gorm
MYSQL_USER: gorm
MYSQL_PASSWORD: gorm
MYSQL_RANDOM_ROOT_PASSWORD: "yes"
ports:
- 9910:3306
options: >-
--health-cmd "mysqladmin ping -ugorm -pgorm"
--health-interval 10s
--health-start-period 10s
--health-timeout 5s
--health-retries 10
steps:
- name: Set up Go 1.x
uses: actions/setup-go@v4
with:
go-version: ${{ matrix.go }}
- name: Check out code into the Go module directory
uses: actions/checkout@v4
- name: go mod package cache
uses: actions/cache@v4
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ matrix.go }}-${{ hashFiles('tests/go.mod') }}
- name: Tests
run: GITHUB_ACTION=true GORM_DIALECT=mysql GORM_DSN="gorm:gorm@tcp(localhost:9910)/gorm?charset=utf8&parseTime=True" ./tests/tests_all.sh
mariadb:
strategy:
matrix:
dbversion: [ 'mariadb:latest' ]
go: ['1.24', '1.25']
platform: [ ubuntu-latest ]
runs-on: ${{ matrix.platform }}
services:
mysql:
image: ${{ matrix.dbversion }}
env:
MYSQL_DATABASE: gorm
MYSQL_USER: gorm
MYSQL_PASSWORD: gorm
MYSQL_RANDOM_ROOT_PASSWORD: "yes"
ports:
- 9910:3306
options: >-
--health-cmd "mariadb-admin ping -ugorm -pgorm"
--health-interval 10s
--health-start-period 10s
--health-timeout 5s
--health-retries 10
steps:
- name: Set up Go 1.x
uses: actions/setup-go@v4
with:
go-version: ${{ matrix.go }}
- name: Check out code into the Go module directory
uses: actions/checkout@v4
- name: go mod package cache
uses: actions/cache@v4
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ matrix.go }}-${{ hashFiles('tests/go.mod') }}
- name: Tests
run: GITHUB_ACTION=true GORM_DIALECT=mysql GORM_DSN="gorm:gorm@tcp(localhost:9910)/gorm?charset=utf8&parseTime=True" ./tests/tests_all.sh
postgres:
strategy:
matrix:
dbversion: ['postgres:latest', 'postgres:15', 'postgres:14', 'postgres:13']
go: ['1.24', '1.25']
platform: [ubuntu-latest] # can not run in macOS and Windows
runs-on: ${{ matrix.platform }}
services:
postgres:
image: ${{ matrix.dbversion }}
env:
POSTGRES_PASSWORD: gorm
POSTGRES_USER: gorm
POSTGRES_DB: gorm
TZ: Asia/Shanghai
ports:
- 9920:5432
# Set health checks to wait until postgres has started
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- name: Set up Go 1.x
uses: actions/setup-go@v4
with:
go-version: ${{ matrix.go }}
- name: Check out code into the Go module directory
uses: actions/checkout@v4
- name: go mod package cache
uses: actions/cache@v4
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ matrix.go }}-${{ hashFiles('tests/go.mod') }}
- name: Tests
run: GITHUB_ACTION=true GORM_DIALECT=postgres GORM_DSN="user=gorm password=gorm dbname=gorm host=localhost port=9920 sslmode=disable TimeZone=Asia/Shanghai" ./tests/tests_all.sh
sqlserver:
strategy:
matrix:
go: ['1.24', '1.25']
platform: [ubuntu-latest] # can not run test in macOS and windows
runs-on: ${{ matrix.platform }}
services:
mssql:
image: mcr.microsoft.com/mssql/server:2022-latest
env:
TZ: Asia/Shanghai
ACCEPT_EULA: Y
MSSQL_SA_PASSWORD: LoremIpsum86
ports:
- 9930:1433
options: >-
--health-cmd="/opt/mssql-tools18/bin/sqlcmd -S localhost -U sa -P ${MSSQL_SA_PASSWORD} -N -C -l 30 -Q \"SELECT 1\" || exit 1"
--health-start-period 10s
--health-interval 10s
--health-timeout 5s
--health-retries 10
steps:
- name: Set up Go 1.x
uses: actions/setup-go@v4
with:
go-version: ${{ matrix.go }}
- name: Check out code into the Go module directory
uses: actions/checkout@v4
- name: go mod package cache
uses: actions/cache@v4
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ matrix.go }}-${{ hashFiles('tests/go.mod') }}
- name: Tests
run: GITHUB_ACTION=true GORM_DIALECT=sqlserver GORM_DSN="sqlserver://sa:LoremIpsum86@localhost:9930?database=master" ./tests/tests_all.sh
tidb:
strategy:
matrix:
dbversion: [ 'v6.5.0' ]
go: ['1.24', '1.25']
platform: [ ubuntu-latest ]
runs-on: ${{ matrix.platform }}
steps:
- name: Setup TiDB
uses: Icemap/tidb-action@main
with:
port: 9940
version: ${{matrix.dbversion}}
- name: Set up Go 1.x
uses: actions/setup-go@v4
with:
go-version: ${{ matrix.go }}
- name: Check out code into the Go module directory
uses: actions/checkout@v4
- name: go mod package cache
uses: actions/cache@v4
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ matrix.go }}-${{ hashFiles('tests/go.mod') }}
- name: Tests
run: GITHUB_ACTION=true GORM_DIALECT=tidb GORM_DSN="root:@tcp(localhost:9940)/test?charset=utf8&parseTime=True&loc=Local" ./tests/tests_all.sh
gaussdb:
strategy:
matrix:
dbversion: ['opengauss/opengauss:7.0.0-RC1.B023']
go: ['1.24', '1.25']
platform: [ubuntu-latest] # can not run in macOS and Windows
runs-on: ${{ matrix.platform }}
services:
gaussdb:
image: ${{ matrix.dbversion }}
env:
# GaussDB has password limitations
GS_PASSWORD: Gaussdb@123
TZ: Asia/Shanghai
ports:
- 9950:5432
steps:
- name: Set up Go 1.x
uses: actions/setup-go@v4
with:
go-version: ${{ matrix.go }}
- name: Check out code into the Go module directory
uses: actions/checkout@v4
- name: Waiting for GaussDB to be ready
run: |
container_name=$(docker ps --filter "ancestor=opengauss/opengauss:7.0.0-RC1.B023" --format "{{.Names}}")
if [ -z "$container_name" ]; then
echo "Error: failed to find a container created from the 'opengauss/opengauss:7.0.0-RC1.B023' image."
exit 1
fi
max_retries=12
retry_count=0
if [ -t 0 ]; then
TTY_FLAG="-t"
else
TTY_FLAG=""
fi
while [ $retry_count -lt $max_retries ]; do
if docker exec -i "${container_name}" bash -c "su - omm -c 'gsql -U omm -c \"select 1;\"'"
then
echo "Creating database gorm..."
sql_file='/tmp/create_database.sql'
echo "CREATE DATABASE gorm DBCOMPATIBILITY 'PG';" > ${sql_file}
docker cp "${sql_file}" "${container_name}":"${sql_file}"
docker exec -i ${TTY_FLAG} "${container_name}" bash -c "su - omm -c 'gsql -U omm -f ${sql_file}'"
echo "Database initialization completed."
break
fi
echo "Waiting for database to be ready... (attempt $((retry_count + 1))/$max_retries)"
sleep 10
((++retry_count))
done
exit 0
- name: go mod package cache
uses: actions/cache@v4
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ matrix.go }}-${{ hashFiles('tests/go.mod') }}
- name: Tests
run: GITHUB_ACTION=true GORM_DIALECT=gaussdb GORM_DSN="user=gaussdb password=Gaussdb@123 dbname=gorm host=localhost port=9950 sslmode=disable TimeZone=Asia/Shanghai" ./tests/tests_all.sh
================================================
FILE: .gitignore
================================================
TODO*
documents
coverage.txt
_book
.idea
vendor
.vscode
================================================
FILE: .golangci.yml
================================================
version: "2"
linters:
default: standard
enable:
- cyclop
- gocritic
- gosec
- ineffassign
- misspell
- prealloc
- unconvert
- unparam
- whitespace
formatters:
enable:
- gofumpt
- goimports
================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to participate in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community includes:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or
advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series
of actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period. This
includes avoiding interactions in community spaces and external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any interaction or public
communication with the community for a specified period. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within
the community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.
================================================
FILE: LICENSE
================================================
The MIT License (MIT)
Copyright (c) 2013-present Jinzhu <wosmvp@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
================================================
FILE: README.md
================================================
# GORM
The fantastic ORM library for Golang, aims to be developer friendly.
[](https://goreportcard.com/report/github.com/go-gorm/gorm)
[](https://github.com/go-gorm/gorm/actions)
[](https://opensource.org/licenses/MIT)
[](https://pkg.go.dev/gorm.io/gorm?tab=doc)
## Overview
* Full-Featured ORM
* Associations (Has One, Has Many, Belongs To, Many To Many, Polymorphism, Single-table inheritance)
* Hooks (Before/After Create/Save/Update/Delete/Find)
* Eager loading with `Preload`, `Joins`
* Transactions, Nested Transactions, Save Point, RollbackTo to Saved Point
* Context, Prepared Statement Mode, DryRun Mode
* Batch Insert, FindInBatches, Find To Map
* SQL Builder, Upsert, Locking, Optimizer/Index/Comment Hints, NamedArg, Search/Update/Create with SQL Expr
* Composite Primary Key
* Auto Migrations
* Logger
* Extendable, flexible plugin API: Database Resolver (Multiple Databases, Read/Write Splitting) / Prometheus…
* Every feature comes with tests
* Developer Friendly
## Getting Started
* GORM Guides [https://gorm.io](https://gorm.io)
* Gen Guides [https://gorm.io/gen/index.html](https://gorm.io/gen/index.html)
## Contributing
[You can help to deliver a better GORM, check out things you can do](https://gorm.io/contribute.html)
## Contributors
[Thank you](https://github.com/go-gorm/gorm/graphs/contributors) for contributing to the GORM framework!
## License
© Jinzhu, 2013~time.Now
Released under the [MIT License](https://github.com/go-gorm/gorm/blob/master/LICENSE)
================================================
FILE: association.go
================================================
package gorm
import (
"fmt"
"reflect"
"strings"
"gorm.io/gorm/clause"
"gorm.io/gorm/schema"
"gorm.io/gorm/utils"
)
// Association Mode contains some helper methods to handle relationship things easily.
type Association struct {
DB *DB
Relationship *schema.Relationship
Unscope bool
Error error
}
func (db *DB) Association(column string) *Association {
association := &Association{DB: db, Unscope: db.Statement.Unscoped}
table := db.Statement.Table
if association.Error = db.Statement.Parse(db.Statement.Model); association.Error == nil {
db.Statement.Table = table
association.Relationship = db.Statement.Schema.Relationships.Relations[column]
if association.Relationship == nil {
association.Error = fmt.Errorf("%w: %s", ErrUnsupportedRelation, column)
}
db.Statement.ReflectValue = reflect.ValueOf(db.Statement.Model)
for db.Statement.ReflectValue.Kind() == reflect.Ptr {
db.Statement.ReflectValue = db.Statement.ReflectValue.Elem()
}
}
return association
}
func (association *Association) Unscoped() *Association {
return &Association{
DB: association.DB,
Relationship: association.Relationship,
Error: association.Error,
Unscope: true,
}
}
func (association *Association) Find(out interface{}, conds ...interface{}) error {
if association.Error == nil {
association.Error = association.buildCondition().Find(out, conds...).Error
}
return association.Error
}
func (association *Association) Append(values ...interface{}) error {
values = expandValues(values)
if association.Error == nil {
switch association.Relationship.Type {
case schema.HasOne, schema.BelongsTo:
if len(values) > 0 {
association.Error = association.Replace(values...)
}
default:
association.saveAssociation( /*clear*/ false, values...)
}
}
return association.Error
}
func (association *Association) Replace(values ...interface{}) error {
values = expandValues(values)
if association.Error == nil {
reflectValue := association.DB.Statement.ReflectValue
rel := association.Relationship
var oldBelongsToExpr clause.Expression
// we have to record the old BelongsTo value
if association.Unscope && rel.Type == schema.BelongsTo {
var foreignFields []*schema.Field
for _, ref := range rel.References {
if !ref.OwnPrimaryKey {
foreignFields = append(foreignFields, ref.ForeignKey)
}
}
if _, fvs := schema.GetIdentityFieldValuesMap(association.DB.Statement.Context, reflectValue, foreignFields); len(fvs) > 0 {
column, values := schema.ToQueryValues(rel.FieldSchema.Table, rel.FieldSchema.PrimaryFieldDBNames, fvs)
oldBelongsToExpr = clause.IN{Column: column, Values: values}
}
}
// save associations
if association.saveAssociation( /*clear*/ true, values...); association.Error != nil {
return association.Error
}
// set old association's foreign key to null
switch rel.Type {
case schema.BelongsTo:
if len(values) == 0 {
updateMap := map[string]interface{}{}
switch reflectValue.Kind() {
case reflect.Slice, reflect.Array:
for i := 0; i < reflectValue.Len(); i++ {
association.Error = rel.Field.Set(association.DB.Statement.Context, reflectValue.Index(i), reflect.Zero(rel.Field.FieldType).Interface())
}
case reflect.Struct:
association.Error = rel.Field.Set(association.DB.Statement.Context, reflectValue, reflect.Zero(rel.Field.FieldType).Interface())
}
for _, ref := range rel.References {
updateMap[ref.ForeignKey.DBName] = nil
}
association.Error = association.DB.UpdateColumns(updateMap).Error
}
if association.Unscope && oldBelongsToExpr != nil {
association.Error = association.DB.Model(nil).Where(oldBelongsToExpr).Delete(reflect.New(rel.FieldSchema.ModelType).Interface()).Error
}
case schema.HasOne, schema.HasMany:
var (
primaryFields []*schema.Field
foreignKeys []string
updateMap = map[string]interface{}{}
relValues = schema.GetRelationsValues(association.DB.Statement.Context, reflectValue, []*schema.Relationship{rel})
modelValue = reflect.New(rel.FieldSchema.ModelType).Interface()
tx = association.DB.Model(modelValue)
)
if _, rvs := schema.GetIdentityFieldValuesMap(association.DB.Statement.Context, relValues, rel.FieldSchema.PrimaryFields); len(rvs) > 0 {
if column, values := schema.ToQueryValues(rel.FieldSchema.Table, rel.FieldSchema.PrimaryFieldDBNames, rvs); len(values) > 0 {
tx.Not(clause.IN{Column: column, Values: values})
}
}
for _, ref := range rel.References {
if ref.OwnPrimaryKey {
primaryFields = append(primaryFields, ref.PrimaryKey)
foreignKeys = append(foreignKeys, ref.ForeignKey.DBName)
updateMap[ref.ForeignKey.DBName] = nil
} else if ref.PrimaryValue != "" {
tx.Where(clause.Eq{Column: ref.ForeignKey.DBName, Value: ref.PrimaryValue})
}
}
if _, pvs := schema.GetIdentityFieldValuesMap(association.DB.Statement.Context, reflectValue, primaryFields); len(pvs) > 0 {
column, values := schema.ToQueryValues(rel.FieldSchema.Table, foreignKeys, pvs)
if association.Unscope {
association.Error = tx.Where(clause.IN{Column: column, Values: values}).Delete(modelValue).Error
} else {
association.Error = tx.Where(clause.IN{Column: column, Values: values}).UpdateColumns(updateMap).Error
}
}
case schema.Many2Many:
var (
primaryFields, relPrimaryFields []*schema.Field
joinPrimaryKeys, joinRelPrimaryKeys []string
modelValue = reflect.New(rel.JoinTable.ModelType).Interface()
tx = association.DB.Model(modelValue)
)
for _, ref := range rel.References {
if ref.PrimaryValue == "" {
if ref.OwnPrimaryKey {
primaryFields = append(primaryFields, ref.PrimaryKey)
joinPrimaryKeys = append(joinPrimaryKeys, ref.ForeignKey.DBName)
} else {
relPrimaryFields = append(relPrimaryFields, ref.PrimaryKey)
joinRelPrimaryKeys = append(joinRelPrimaryKeys, ref.ForeignKey.DBName)
}
} else {
tx.Clauses(clause.Eq{Column: ref.ForeignKey.DBName, Value: ref.PrimaryValue})
}
}
_, pvs := schema.GetIdentityFieldValuesMap(association.DB.Statement.Context, reflectValue, primaryFields)
if column, values := schema.ToQueryValues(rel.JoinTable.Table, joinPrimaryKeys, pvs); len(values) > 0 {
tx.Where(clause.IN{Column: column, Values: values})
} else {
return ErrPrimaryKeyRequired
}
_, rvs := schema.GetIdentityFieldValuesMapFromValues(association.DB.Statement.Context, values, relPrimaryFields)
if relColumn, relValues := schema.ToQueryValues(rel.JoinTable.Table, joinRelPrimaryKeys, rvs); len(relValues) > 0 {
tx.Where(clause.Not(clause.IN{Column: relColumn, Values: relValues}))
}
association.Error = tx.Delete(modelValue).Error
}
}
return association.Error
}
func (association *Association) Delete(values ...interface{}) error {
values = expandValues(values)
if association.Error == nil {
var (
reflectValue = association.DB.Statement.ReflectValue
rel = association.Relationship
primaryFields []*schema.Field
foreignKeys []string
updateAttrs = map[string]interface{}{}
conds []clause.Expression
)
for _, ref := range rel.References {
if ref.PrimaryValue == "" {
primaryFields = append(primaryFields, ref.PrimaryKey)
foreignKeys = append(foreignKeys, ref.ForeignKey.DBName)
updateAttrs[ref.ForeignKey.DBName] = nil
} else {
conds = append(conds, clause.Eq{Column: ref.ForeignKey.DBName, Value: ref.PrimaryValue})
}
}
switch rel.Type {
case schema.BelongsTo:
associationDB := association.DB.Session(&Session{})
tx := associationDB.Model(reflect.New(rel.Schema.ModelType).Interface())
_, pvs := schema.GetIdentityFieldValuesMap(association.DB.Statement.Context, reflectValue, rel.Schema.PrimaryFields)
if pcolumn, pvalues := schema.ToQueryValues(rel.Schema.Table, rel.Schema.PrimaryFieldDBNames, pvs); len(pvalues) > 0 {
conds = append(conds, clause.IN{Column: pcolumn, Values: pvalues})
} else {
return ErrPrimaryKeyRequired
}
_, rvs := schema.GetIdentityFieldValuesMapFromValues(association.DB.Statement.Context, values, primaryFields)
relColumn, relValues := schema.ToQueryValues(rel.Schema.Table, foreignKeys, rvs)
conds = append(conds, clause.IN{Column: relColumn, Values: relValues})
association.Error = tx.Clauses(conds...).UpdateColumns(updateAttrs).Error
if association.Unscope {
var foreignFields []*schema.Field
for _, ref := range rel.References {
if !ref.OwnPrimaryKey {
foreignFields = append(foreignFields, ref.ForeignKey)
}
}
if _, fvs := schema.GetIdentityFieldValuesMap(association.DB.Statement.Context, reflectValue, foreignFields); len(fvs) > 0 {
column, values := schema.ToQueryValues(rel.FieldSchema.Table, rel.FieldSchema.PrimaryFieldDBNames, fvs)
association.Error = associationDB.Model(nil).Where(clause.IN{Column: column, Values: values}).Delete(reflect.New(rel.FieldSchema.ModelType).Interface()).Error
}
}
case schema.HasOne, schema.HasMany:
model := reflect.New(rel.FieldSchema.ModelType).Interface()
tx := association.DB.Model(model)
_, pvs := schema.GetIdentityFieldValuesMap(association.DB.Statement.Context, reflectValue, primaryFields)
if pcolumn, pvalues := schema.ToQueryValues(rel.FieldSchema.Table, foreignKeys, pvs); len(pvalues) > 0 {
conds = append(conds, clause.IN{Column: pcolumn, Values: pvalues})
} else {
return ErrPrimaryKeyRequired
}
_, rvs := schema.GetIdentityFieldValuesMapFromValues(association.DB.Statement.Context, values, rel.FieldSchema.PrimaryFields)
relColumn, relValues := schema.ToQueryValues(rel.FieldSchema.Table, rel.FieldSchema.PrimaryFieldDBNames, rvs)
conds = append(conds, clause.IN{Column: relColumn, Values: relValues})
if association.Unscope {
association.Error = tx.Clauses(conds...).Delete(model).Error
} else {
association.Error = tx.Clauses(conds...).UpdateColumns(updateAttrs).Error
}
case schema.Many2Many:
var (
primaryFields, relPrimaryFields []*schema.Field
joinPrimaryKeys, joinRelPrimaryKeys []string
joinValue = reflect.New(rel.JoinTable.ModelType).Interface()
)
for _, ref := range rel.References {
if ref.PrimaryValue == "" {
if ref.OwnPrimaryKey {
primaryFields = append(primaryFields, ref.PrimaryKey)
joinPrimaryKeys = append(joinPrimaryKeys, ref.ForeignKey.DBName)
} else {
relPrimaryFields = append(relPrimaryFields, ref.PrimaryKey)
joinRelPrimaryKeys = append(joinRelPrimaryKeys, ref.ForeignKey.DBName)
}
} else {
conds = append(conds, clause.Eq{Column: ref.ForeignKey.DBName, Value: ref.PrimaryValue})
}
}
_, pvs := schema.GetIdentityFieldValuesMap(association.DB.Statement.Context, reflectValue, primaryFields)
if pcolumn, pvalues := schema.ToQueryValues(rel.JoinTable.Table, joinPrimaryKeys, pvs); len(pvalues) > 0 {
conds = append(conds, clause.IN{Column: pcolumn, Values: pvalues})
} else {
return ErrPrimaryKeyRequired
}
_, rvs := schema.GetIdentityFieldValuesMapFromValues(association.DB.Statement.Context, values, relPrimaryFields)
relColumn, relValues := schema.ToQueryValues(rel.JoinTable.Table, joinRelPrimaryKeys, rvs)
conds = append(conds, clause.IN{Column: relColumn, Values: relValues})
association.Error = association.DB.Where(clause.Where{Exprs: conds}).Model(nil).Delete(joinValue).Error
}
if association.Error == nil {
// clean up deleted values' foreign key
relValuesMap, _ := schema.GetIdentityFieldValuesMapFromValues(association.DB.Statement.Context, values, rel.FieldSchema.PrimaryFields)
cleanUpDeletedRelations := func(data reflect.Value) {
if _, zero := rel.Field.ValueOf(association.DB.Statement.Context, data); !zero {
fieldValue := reflect.Indirect(rel.Field.ReflectValueOf(association.DB.Statement.Context, data))
primaryValues := make([]interface{}, len(rel.FieldSchema.PrimaryFields))
switch fieldValue.Kind() {
case reflect.Slice, reflect.Array:
validFieldValues := reflect.Zero(rel.Field.IndirectFieldType)
for i := 0; i < fieldValue.Len(); i++ {
for idx, field := range rel.FieldSchema.PrimaryFields {
primaryValues[idx], _ = field.ValueOf(association.DB.Statement.Context, fieldValue.Index(i))
}
if _, ok := relValuesMap[utils.ToStringKey(primaryValues...)]; !ok {
validFieldValues = reflect.Append(validFieldValues, fieldValue.Index(i))
}
}
association.Error = rel.Field.Set(association.DB.Statement.Context, data, validFieldValues.Interface())
case reflect.Struct:
for idx, field := range rel.FieldSchema.PrimaryFields {
primaryValues[idx], _ = field.ValueOf(association.DB.Statement.Context, fieldValue)
}
if _, ok := relValuesMap[utils.ToStringKey(primaryValues...)]; ok {
if association.Error = rel.Field.Set(association.DB.Statement.Context, data, reflect.Zero(rel.FieldSchema.ModelType).Interface()); association.Error != nil {
break
}
if rel.JoinTable == nil {
for _, ref := range rel.References {
if ref.OwnPrimaryKey || ref.PrimaryValue != "" {
association.Error = ref.ForeignKey.Set(association.DB.Statement.Context, fieldValue, reflect.Zero(ref.ForeignKey.FieldType).Interface())
} else {
association.Error = ref.ForeignKey.Set(association.DB.Statement.Context, data, reflect.Zero(ref.ForeignKey.FieldType).Interface())
}
}
}
}
}
}
}
switch reflectValue.Kind() {
case reflect.Slice, reflect.Array:
for i := 0; i < reflectValue.Len(); i++ {
cleanUpDeletedRelations(reflect.Indirect(reflectValue.Index(i)))
}
case reflect.Struct:
cleanUpDeletedRelations(reflectValue)
}
}
}
return association.Error
}
func (association *Association) Clear() error {
return association.Replace()
}
func (association *Association) Count() (count int64) {
if association.Error == nil {
association.Error = association.buildCondition().Count(&count).Error
}
return
}
type assignBack struct {
Source reflect.Value
Index int
Dest reflect.Value
}
func (association *Association) saveAssociation(clear bool, values ...interface{}) {
var (
reflectValue = association.DB.Statement.ReflectValue
assignBacks []assignBack // assign association values back to arguments after save
)
appendToRelations := func(source, rv reflect.Value, clear bool) {
switch association.Relationship.Type {
case schema.HasOne, schema.BelongsTo:
switch rv.Kind() {
case reflect.Slice, reflect.Array:
if rv.Len() > 0 {
association.Error = association.Relationship.Field.Set(association.DB.Statement.Context, source, rv.Index(0).Addr().Interface())
if association.Relationship.Field.FieldType.Kind() == reflect.Struct {
assignBacks = append(assignBacks, assignBack{Source: source, Dest: rv.Index(0)})
}
}
case reflect.Struct:
if !rv.CanAddr() {
association.Error = ErrInvalidValue
return
}
association.Error = association.Relationship.Field.Set(association.DB.Statement.Context, source, rv.Addr().Interface())
if association.Relationship.Field.FieldType.Kind() == reflect.Struct {
assignBacks = append(assignBacks, assignBack{Source: source, Dest: rv})
}
}
case schema.HasMany, schema.Many2Many:
elemType := association.Relationship.Field.IndirectFieldType.Elem()
oldFieldValue := reflect.Indirect(association.Relationship.Field.ReflectValueOf(association.DB.Statement.Context, source))
var fieldValue reflect.Value
if clear {
fieldValue = reflect.MakeSlice(oldFieldValue.Type(), 0, oldFieldValue.Cap())
} else {
fieldValue = reflect.MakeSlice(oldFieldValue.Type(), oldFieldValue.Len(), oldFieldValue.Cap())
reflect.Copy(fieldValue, oldFieldValue)
}
appendToFieldValues := func(ev reflect.Value) {
if ev.Type().AssignableTo(elemType) {
fieldValue = reflect.Append(fieldValue, ev)
} else if ev.Type().Elem().AssignableTo(elemType) {
fieldValue = reflect.Append(fieldValue, ev.Elem())
} else {
association.Error = fmt.Errorf("unsupported data type: %v for relation %s", ev.Type(), association.Relationship.Name)
}
if elemType.Kind() == reflect.Struct {
assignBacks = append(assignBacks, assignBack{Source: source, Dest: ev, Index: fieldValue.Len()})
}
}
processMap := func(mapv reflect.Value) {
child := reflect.New(association.Relationship.FieldSchema.ModelType)
switch association.Relationship.Type {
case schema.HasMany:
for _, ref := range association.Relationship.References {
key := reflect.ValueOf(ref.ForeignKey.DBName)
if ref.OwnPrimaryKey {
v := ref.PrimaryKey.ReflectValueOf(association.DB.Statement.Context, source)
mapv.SetMapIndex(key, v)
} else if ref.PrimaryValue != "" {
mapv.SetMapIndex(key, reflect.ValueOf(ref.PrimaryValue))
}
}
association.Error = association.DB.Session(&Session{
NewDB: true,
}).Model(child.Interface()).Create(mapv.Interface()).Error
case schema.Many2Many:
association.Error = association.DB.Session(&Session{
NewDB: true,
}).Model(child.Interface()).Create(mapv.Interface()).Error
for _, key := range mapv.MapKeys() {
k := strings.ToLower(key.String())
if f, ok := association.Relationship.FieldSchema.FieldsByDBName[k]; ok {
_ = f.Set(association.DB.Statement.Context, child, mapv.MapIndex(key).Interface())
}
}
appendToFieldValues(child)
}
}
switch rv.Kind() {
case reflect.Map:
processMap(rv)
case reflect.Slice, reflect.Array:
for i := 0; i < rv.Len(); i++ {
elem := reflect.Indirect(rv.Index(i))
if elem.Kind() == reflect.Map {
processMap(elem)
continue
}
appendToFieldValues(elem.Addr())
}
case reflect.Struct:
if !rv.CanAddr() {
association.Error = ErrInvalidValue
return
}
appendToFieldValues(rv.Addr())
}
if association.Error == nil {
association.Error = association.Relationship.Field.Set(association.DB.Statement.Context, source, fieldValue.Interface())
}
}
}
selectedSaveColumns := []string{association.Relationship.Name}
omitColumns := []string{}
selectColumns, _ := association.DB.Statement.SelectAndOmitColumns(true, false)
for name, ok := range selectColumns {
columnName := ""
if strings.HasPrefix(name, association.Relationship.Name) {
if columnName = strings.TrimPrefix(name, association.Relationship.Name); columnName == ".*" {
columnName = name
}
} else if strings.HasPrefix(name, clause.Associations) {
columnName = name
}
if columnName != "" {
if ok {
selectedSaveColumns = append(selectedSaveColumns, columnName)
} else {
omitColumns = append(omitColumns, columnName)
}
}
}
for _, ref := range association.Relationship.References {
if !ref.OwnPrimaryKey {
selectedSaveColumns = append(selectedSaveColumns, ref.ForeignKey.Name)
}
}
associationDB := association.DB.Session(&Session{}).Model(nil)
if !association.DB.FullSaveAssociations {
associationDB.Select(selectedSaveColumns)
}
if len(omitColumns) > 0 {
associationDB.Omit(omitColumns...)
}
associationDB = associationDB.Session(&Session{})
switch reflectValue.Kind() {
case reflect.Slice, reflect.Array:
if len(values) != reflectValue.Len() {
// clear old data
if clear && len(values) == 0 {
for i := 0; i < reflectValue.Len(); i++ {
if err := association.Relationship.Field.Set(association.DB.Statement.Context, reflectValue.Index(i), reflect.New(association.Relationship.Field.IndirectFieldType).Interface()); err != nil {
association.Error = err
break
}
if association.Relationship.JoinTable == nil {
for _, ref := range association.Relationship.References {
if !ref.OwnPrimaryKey && ref.PrimaryValue == "" {
if err := ref.ForeignKey.Set(association.DB.Statement.Context, reflectValue.Index(i), reflect.Zero(ref.ForeignKey.FieldType).Interface()); err != nil {
association.Error = err
break
}
}
}
}
}
break
}
association.Error = ErrInvalidValueOfLength
return
}
for i := 0; i < reflectValue.Len(); i++ {
appendToRelations(reflectValue.Index(i), reflect.Indirect(reflect.ValueOf(values[i])), clear)
if association.Error != nil {
return
}
// TODO support save slice data, sql with case?
association.Error = associationDB.Updates(reflectValue.Index(i).Addr().Interface()).Error
}
case reflect.Struct:
// clear old data
if clear && len(values) == 0 {
association.Error = association.Relationship.Field.Set(association.DB.Statement.Context, reflectValue, reflect.New(association.Relationship.Field.IndirectFieldType).Interface())
if association.Relationship.JoinTable == nil && association.Error == nil {
for _, ref := range association.Relationship.References {
if !ref.OwnPrimaryKey && ref.PrimaryValue == "" {
association.Error = ref.ForeignKey.Set(association.DB.Statement.Context, reflectValue, reflect.Zero(ref.ForeignKey.FieldType).Interface())
}
}
}
}
for idx, value := range values {
rv := reflect.Indirect(reflect.ValueOf(value))
appendToRelations(reflectValue, rv, clear && idx == 0)
if association.Error != nil {
return
}
}
if len(values) > 0 {
association.Error = associationDB.Updates(reflectValue.Addr().Interface()).Error
}
}
for _, assignBack := range assignBacks {
fieldValue := reflect.Indirect(association.Relationship.Field.ReflectValueOf(association.DB.Statement.Context, assignBack.Source))
if assignBack.Index > 0 {
reflect.Indirect(assignBack.Dest).Set(fieldValue.Index(assignBack.Index - 1))
} else {
reflect.Indirect(assignBack.Dest).Set(fieldValue)
}
}
}
func (association *Association) buildCondition() *DB {
var (
queryConds = association.Relationship.ToQueryConditions(association.DB.Statement.Context, association.DB.Statement.ReflectValue)
modelValue = reflect.New(association.Relationship.FieldSchema.ModelType).Interface()
tx = association.DB.Model(modelValue)
)
if association.Relationship.JoinTable != nil {
if !tx.Statement.Unscoped && len(association.Relationship.JoinTable.QueryClauses) > 0 {
joinStmt := Statement{DB: tx, Context: tx.Statement.Context, Schema: association.Relationship.JoinTable, Table: association.Relationship.JoinTable.Table, Clauses: map[string]clause.Clause{}}
for _, queryClause := range association.Relationship.JoinTable.QueryClauses {
joinStmt.AddClause(queryClause)
}
joinStmt.Build("WHERE")
if len(joinStmt.SQL.String()) > 0 {
tx.Clauses(clause.Expr{SQL: strings.Replace(joinStmt.SQL.String(), "WHERE ", "", 1), Vars: joinStmt.Vars})
}
}
tx = tx.Session(&Session{QueryFields: true}).Clauses(clause.From{Joins: []clause.Join{{
Table: clause.Table{Name: association.Relationship.JoinTable.Table},
ON: clause.Where{Exprs: queryConds},
}}})
} else {
tx.Clauses(clause.Where{Exprs: queryConds})
}
return tx
}
func expandValues(values ...any) (results []any) {
appendToResult := func(rv reflect.Value) {
// unwrap interface
if rv.IsValid() && rv.Kind() == reflect.Interface {
rv = rv.Elem()
}
if rv.IsValid() && rv.Kind() == reflect.Struct {
p := reflect.New(rv.Type())
p.Elem().Set(rv)
results = append(results, p.Interface())
} else if rv.IsValid() {
results = append(results, rv.Interface())
}
}
// Process each argument; if an argument is a slice/array, expand its elements
for _, value := range values {
rv := reflect.ValueOf(value)
if rv.Kind() == reflect.Slice || rv.Kind() == reflect.Array {
for i := 0; i < rv.Len(); i++ {
appendToResult(rv.Index(i))
}
} else {
appendToResult(rv)
}
}
return
}
================================================
FILE: callbacks/associations.go
================================================
package callbacks
import (
"reflect"
"strings"
"gorm.io/gorm"
"gorm.io/gorm/clause"
"gorm.io/gorm/schema"
"gorm.io/gorm/utils"
)
func SaveBeforeAssociations(create bool) func(db *gorm.DB) {
return func(db *gorm.DB) {
if db.Error == nil && db.Statement.Schema != nil {
selectColumns, restricted := db.Statement.SelectAndOmitColumns(create, !create)
// Save Belongs To associations
for _, rel := range db.Statement.Schema.Relationships.BelongsTo {
if v, ok := selectColumns[rel.Name]; (ok && !v) || (!ok && restricted) {
continue
}
setupReferences := func(obj reflect.Value, elem reflect.Value) {
for _, ref := range rel.References {
if !ref.OwnPrimaryKey {
pv, _ := ref.PrimaryKey.ValueOf(db.Statement.Context, elem)
db.AddError(ref.ForeignKey.Set(db.Statement.Context, obj, pv))
if dest, ok := db.Statement.Dest.(map[string]interface{}); ok {
dest[ref.ForeignKey.DBName] = pv
if _, ok := dest[rel.Name]; ok {
dest[rel.Name] = elem.Interface()
}
}
}
}
}
switch db.Statement.ReflectValue.Kind() {
case reflect.Slice, reflect.Array:
var (
rValLen = db.Statement.ReflectValue.Len()
objs = make([]reflect.Value, 0, rValLen)
fieldType = rel.Field.FieldType
isPtr = fieldType.Kind() == reflect.Ptr
)
if !isPtr {
fieldType = reflect.PointerTo(fieldType)
}
elems := reflect.MakeSlice(reflect.SliceOf(fieldType), 0, 10)
distinctElems := reflect.MakeSlice(reflect.SliceOf(fieldType), 0, 10)
identityMap := map[string]bool{}
for i := 0; i < rValLen; i++ {
obj := db.Statement.ReflectValue.Index(i)
if reflect.Indirect(obj).Kind() != reflect.Struct {
break
}
if _, zero := rel.Field.ValueOf(db.Statement.Context, obj); !zero { // check belongs to relation value
rv := rel.Field.ReflectValueOf(db.Statement.Context, obj) // relation reflect value
if !isPtr {
rv = rv.Addr()
}
objs = append(objs, obj)
elems = reflect.Append(elems, rv)
relPrimaryValues := make([]interface{}, 0, len(rel.FieldSchema.PrimaryFields))
for _, pf := range rel.FieldSchema.PrimaryFields {
if pfv, ok := pf.ValueOf(db.Statement.Context, rv); !ok {
relPrimaryValues = append(relPrimaryValues, pfv)
}
}
cacheKey := utils.ToStringKey(relPrimaryValues...)
if len(relPrimaryValues) != len(rel.FieldSchema.PrimaryFields) || !identityMap[cacheKey] {
if cacheKey != "" { // has primary fields
identityMap[cacheKey] = true
}
distinctElems = reflect.Append(distinctElems, rv)
}
}
}
if elems.Len() > 0 {
if saveAssociations(db, rel, distinctElems, selectColumns, restricted, nil) == nil {
for i := 0; i < elems.Len(); i++ {
setupReferences(objs[i], elems.Index(i))
}
}
}
case reflect.Struct:
if _, zero := rel.Field.ValueOf(db.Statement.Context, db.Statement.ReflectValue); !zero {
rv := rel.Field.ReflectValueOf(db.Statement.Context, db.Statement.ReflectValue) // relation reflect value
if rv.Kind() != reflect.Ptr {
rv = rv.Addr()
}
if saveAssociations(db, rel, rv, selectColumns, restricted, nil) == nil {
setupReferences(db.Statement.ReflectValue, rv)
}
}
}
}
}
}
}
func SaveAfterAssociations(create bool) func(db *gorm.DB) {
return func(db *gorm.DB) {
if db.Error == nil && db.Statement.Schema != nil {
selectColumns, restricted := db.Statement.SelectAndOmitColumns(create, !create)
// Save Has One associations
for _, rel := range db.Statement.Schema.Relationships.HasOne {
if v, ok := selectColumns[rel.Name]; (ok && !v) || (!ok && restricted) {
continue
}
switch db.Statement.ReflectValue.Kind() {
case reflect.Slice, reflect.Array:
var (
fieldType = rel.Field.FieldType
isPtr = fieldType.Kind() == reflect.Ptr
)
if !isPtr {
fieldType = reflect.PointerTo(fieldType)
}
elems := reflect.MakeSlice(reflect.SliceOf(fieldType), 0, 10)
for i := 0; i < db.Statement.ReflectValue.Len(); i++ {
obj := db.Statement.ReflectValue.Index(i)
if reflect.Indirect(obj).Kind() == reflect.Struct {
if _, zero := rel.Field.ValueOf(db.Statement.Context, obj); !zero {
rv := rel.Field.ReflectValueOf(db.Statement.Context, obj)
if rv.Kind() != reflect.Ptr {
rv = rv.Addr()
}
for _, ref := range rel.References {
if ref.OwnPrimaryKey {
fv, _ := ref.PrimaryKey.ValueOf(db.Statement.Context, obj)
db.AddError(ref.ForeignKey.Set(db.Statement.Context, rv, fv))
} else if ref.PrimaryValue != "" {
db.AddError(ref.ForeignKey.Set(db.Statement.Context, rv, ref.PrimaryValue))
}
}
elems = reflect.Append(elems, rv)
}
}
}
if elems.Len() > 0 {
assignmentColumns := make([]string, 0, len(rel.References))
for _, ref := range rel.References {
assignmentColumns = append(assignmentColumns, ref.ForeignKey.DBName)
}
saveAssociations(db, rel, elems, selectColumns, restricted, assignmentColumns)
}
case reflect.Struct:
if _, zero := rel.Field.ValueOf(db.Statement.Context, db.Statement.ReflectValue); !zero {
f := rel.Field.ReflectValueOf(db.Statement.Context, db.Statement.ReflectValue)
if f.Kind() != reflect.Ptr {
f = f.Addr()
}
assignmentColumns := make([]string, 0, len(rel.References))
for _, ref := range rel.References {
if ref.OwnPrimaryKey {
fv, _ := ref.PrimaryKey.ValueOf(db.Statement.Context, db.Statement.ReflectValue)
db.AddError(ref.ForeignKey.Set(db.Statement.Context, f, fv))
} else if ref.PrimaryValue != "" {
db.AddError(ref.ForeignKey.Set(db.Statement.Context, f, ref.PrimaryValue))
}
assignmentColumns = append(assignmentColumns, ref.ForeignKey.DBName)
}
saveAssociations(db, rel, f, selectColumns, restricted, assignmentColumns)
}
}
}
// Save Has Many associations
for _, rel := range db.Statement.Schema.Relationships.HasMany {
if v, ok := selectColumns[rel.Name]; (ok && !v) || (!ok && restricted) {
continue
}
fieldType := rel.Field.IndirectFieldType.Elem()
isPtr := fieldType.Kind() == reflect.Ptr
if !isPtr {
fieldType = reflect.PointerTo(fieldType)
}
elems := reflect.MakeSlice(reflect.SliceOf(fieldType), 0, 10)
identityMap := map[string]bool{}
appendToElems := func(v reflect.Value) {
if _, zero := rel.Field.ValueOf(db.Statement.Context, v); !zero {
f := reflect.Indirect(rel.Field.ReflectValueOf(db.Statement.Context, v))
for i := 0; i < f.Len(); i++ {
elem := f.Index(i)
for _, ref := range rel.References {
if ref.OwnPrimaryKey {
pv, _ := ref.PrimaryKey.ValueOf(db.Statement.Context, v)
db.AddError(ref.ForeignKey.Set(db.Statement.Context, elem, pv))
} else if ref.PrimaryValue != "" {
db.AddError(ref.ForeignKey.Set(db.Statement.Context, elem, ref.PrimaryValue))
}
}
relPrimaryValues := make([]interface{}, 0, len(rel.FieldSchema.PrimaryFields))
for _, pf := range rel.FieldSchema.PrimaryFields {
if pfv, ok := pf.ValueOf(db.Statement.Context, elem); !ok {
relPrimaryValues = append(relPrimaryValues, pfv)
}
}
cacheKey := utils.ToStringKey(relPrimaryValues...)
if len(relPrimaryValues) != len(rel.FieldSchema.PrimaryFields) || !identityMap[cacheKey] {
if cacheKey != "" { // has primary fields
identityMap[cacheKey] = true
}
if isPtr {
elems = reflect.Append(elems, elem)
} else {
elems = reflect.Append(elems, elem.Addr())
}
}
}
}
}
switch db.Statement.ReflectValue.Kind() {
case reflect.Slice, reflect.Array:
for i := 0; i < db.Statement.ReflectValue.Len(); i++ {
obj := db.Statement.ReflectValue.Index(i)
if reflect.Indirect(obj).Kind() == reflect.Struct {
appendToElems(obj)
}
}
case reflect.Struct:
appendToElems(db.Statement.ReflectValue)
}
if elems.Len() > 0 {
assignmentColumns := make([]string, 0, len(rel.References))
for _, ref := range rel.References {
assignmentColumns = append(assignmentColumns, ref.ForeignKey.DBName)
}
saveAssociations(db, rel, elems, selectColumns, restricted, assignmentColumns)
}
}
// Save Many2Many associations
for _, rel := range db.Statement.Schema.Relationships.Many2Many {
if v, ok := selectColumns[rel.Name]; (ok && !v) || (!ok && restricted) {
continue
}
fieldType := rel.Field.IndirectFieldType.Elem()
isPtr := fieldType.Kind() == reflect.Ptr
if !isPtr {
fieldType = reflect.PointerTo(fieldType)
}
elems := reflect.MakeSlice(reflect.SliceOf(fieldType), 0, 10)
distinctElems := reflect.MakeSlice(reflect.SliceOf(fieldType), 0, 10)
joins := reflect.MakeSlice(reflect.SliceOf(reflect.PointerTo(rel.JoinTable.ModelType)), 0, 10)
objs := []reflect.Value{}
appendToJoins := func(obj reflect.Value, elem reflect.Value) {
joinValue := reflect.New(rel.JoinTable.ModelType)
for _, ref := range rel.References {
if ref.OwnPrimaryKey {
fv, _ := ref.PrimaryKey.ValueOf(db.Statement.Context, obj)
db.AddError(ref.ForeignKey.Set(db.Statement.Context, joinValue, fv))
} else if ref.PrimaryValue != "" {
db.AddError(ref.ForeignKey.Set(db.Statement.Context, joinValue, ref.PrimaryValue))
} else {
fv, _ := ref.PrimaryKey.ValueOf(db.Statement.Context, elem)
db.AddError(ref.ForeignKey.Set(db.Statement.Context, joinValue, fv))
}
}
joins = reflect.Append(joins, joinValue)
}
identityMap := map[string]bool{}
appendToElems := func(v reflect.Value) {
if _, zero := rel.Field.ValueOf(db.Statement.Context, v); !zero {
f := reflect.Indirect(rel.Field.ReflectValueOf(db.Statement.Context, v))
for i := 0; i < f.Len(); i++ {
elem := f.Index(i)
if !isPtr {
elem = elem.Addr()
}
objs = append(objs, v)
elems = reflect.Append(elems, elem)
relPrimaryValues := make([]interface{}, 0, len(rel.FieldSchema.PrimaryFields))
for _, pf := range rel.FieldSchema.PrimaryFields {
if pfv, ok := pf.ValueOf(db.Statement.Context, elem); !ok {
relPrimaryValues = append(relPrimaryValues, pfv)
}
}
cacheKey := utils.ToStringKey(relPrimaryValues...)
if len(relPrimaryValues) != len(rel.FieldSchema.PrimaryFields) || !identityMap[cacheKey] {
if cacheKey != "" { // has primary fields
identityMap[cacheKey] = true
}
distinctElems = reflect.Append(distinctElems, elem)
}
}
}
}
switch db.Statement.ReflectValue.Kind() {
case reflect.Slice, reflect.Array:
for i := 0; i < db.Statement.ReflectValue.Len(); i++ {
obj := db.Statement.ReflectValue.Index(i)
if reflect.Indirect(obj).Kind() == reflect.Struct {
appendToElems(obj)
}
}
case reflect.Struct:
appendToElems(db.Statement.ReflectValue)
}
// optimize elems of reflect value length
if elemLen := elems.Len(); elemLen > 0 {
if v, ok := selectColumns[rel.Name+".*"]; !ok || v {
saveAssociations(db, rel, distinctElems, selectColumns, restricted, nil)
}
for i := 0; i < elemLen; i++ {
appendToJoins(objs[i], elems.Index(i))
}
}
if joins.Len() > 0 {
db.AddError(db.Session(&gorm.Session{NewDB: true}).Clauses(clause.OnConflict{DoNothing: true}).Session(&gorm.Session{
SkipHooks: db.Statement.SkipHooks,
DisableNestedTransaction: true,
}).Create(joins.Interface()).Error)
}
}
}
}
}
func onConflictOption(stmt *gorm.Statement, s *schema.Schema, defaultUpdatingColumns []string) (onConflict clause.OnConflict) {
if len(defaultUpdatingColumns) > 0 || stmt.DB.FullSaveAssociations {
onConflict.Columns = make([]clause.Column, 0, len(s.PrimaryFieldDBNames))
for _, dbName := range s.PrimaryFieldDBNames {
onConflict.Columns = append(onConflict.Columns, clause.Column{Name: dbName})
}
onConflict.UpdateAll = stmt.DB.FullSaveAssociations
if !onConflict.UpdateAll {
onConflict.DoUpdates = clause.AssignmentColumns(defaultUpdatingColumns)
}
} else {
onConflict.DoNothing = true
}
return
}
func saveAssociations(db *gorm.DB, rel *schema.Relationship, rValues reflect.Value, selectColumns map[string]bool, restricted bool, defaultUpdatingColumns []string) error {
// stop save association loop
if checkAssociationsSaved(db, rValues) {
return nil
}
var (
selects, omits []string
onConflict = onConflictOption(db.Statement, rel.FieldSchema, defaultUpdatingColumns)
refName = rel.Name + "."
values = rValues.Interface()
)
for name, ok := range selectColumns {
columnName := ""
if strings.HasPrefix(name, refName) {
columnName = strings.TrimPrefix(name, refName)
}
if columnName != "" {
if ok {
selects = append(selects, columnName)
} else {
omits = append(omits, columnName)
}
}
}
tx := db.Session(&gorm.Session{NewDB: true}).Clauses(onConflict).Session(&gorm.Session{
FullSaveAssociations: db.FullSaveAssociations,
SkipHooks: db.Statement.SkipHooks,
DisableNestedTransaction: true,
})
db.Statement.Settings.Range(func(k, v interface{}) bool {
tx.Statement.Settings.Store(k, v)
return true
})
if tx.Statement.FullSaveAssociations {
tx = tx.Set("gorm:update_track_time", true)
}
if len(selects) > 0 {
tx = tx.Select(selects)
} else if restricted && len(omits) == 0 {
tx = tx.Omit(clause.Associations)
}
if len(omits) > 0 {
tx = tx.Omit(omits...)
}
return db.AddError(tx.Create(values).Error)
}
// check association values has been saved
// if values kind is Struct, check it has been saved
// if values kind is Slice/Array, check all items have been saved
var visitMapStoreKey = "gorm:saved_association_map"
func checkAssociationsSaved(db *gorm.DB, values reflect.Value) bool {
if visit, ok := db.Get(visitMapStoreKey); ok {
if v, ok := visit.(*visitMap); ok {
if loadOrStoreVisitMap(v, values) {
return true
}
}
} else {
vistMap := make(visitMap)
loadOrStoreVisitMap(&vistMap, values)
db.Set(visitMapStoreKey, &vistMap)
}
return false
}
================================================
FILE: callbacks/callbacks.go
================================================
package callbacks
import (
"gorm.io/gorm"
)
var (
createClauses = []string{"INSERT", "VALUES", "ON CONFLICT"}
queryClauses = []string{"SELECT", "FROM", "WHERE", "GROUP BY", "ORDER BY", "LIMIT", "FOR"}
updateClauses = []string{"UPDATE", "SET", "WHERE"}
deleteClauses = []string{"DELETE", "FROM", "WHERE"}
)
type Config struct {
LastInsertIDReversed bool
CreateClauses []string
QueryClauses []string
UpdateClauses []string
DeleteClauses []string
}
func RegisterDefaultCallbacks(db *gorm.DB, config *Config) {
enableTransaction := func(db *gorm.DB) bool {
return !db.SkipDefaultTransaction
}
if len(config.CreateClauses) == 0 {
config.CreateClauses = createClauses
}
if len(config.QueryClauses) == 0 {
config.QueryClauses = queryClauses
}
if len(config.DeleteClauses) == 0 {
config.DeleteClauses = deleteClauses
}
if len(config.UpdateClauses) == 0 {
config.UpdateClauses = updateClauses
}
createCallback := db.Callback().Create()
createCallback.Match(enableTransaction).Register("gorm:begin_transaction", BeginTransaction)
createCallback.Register("gorm:before_create", BeforeCreate)
createCallback.Register("gorm:save_before_associations", SaveBeforeAssociations(true))
createCallback.Register("gorm:create", Create(config))
createCallback.Register("gorm:save_after_associations", SaveAfterAssociations(true))
createCallback.Register("gorm:after_create", AfterCreate)
createCallback.Match(enableTransaction).Register("gorm:commit_or_rollback_transaction", CommitOrRollbackTransaction)
createCallback.Clauses = config.CreateClauses
queryCallback := db.Callback().Query()
queryCallback.Register("gorm:query", Query)
queryCallback.Register("gorm:preload", Preload)
queryCallback.Register("gorm:after_query", AfterQuery)
queryCallback.Clauses = config.QueryClauses
deleteCallback := db.Callback().Delete()
deleteCallback.Match(enableTransaction).Register("gorm:begin_transaction", BeginTransaction)
deleteCallback.Register("gorm:before_delete", BeforeDelete)
deleteCallback.Register("gorm:delete_before_associations", DeleteBeforeAssociations)
deleteCallback.Register("gorm:delete", Delete(config))
deleteCallback.Register("gorm:after_delete", AfterDelete)
deleteCallback.Match(enableTransaction).Register("gorm:commit_or_rollback_transaction", CommitOrRollbackTransaction)
deleteCallback.Clauses = config.DeleteClauses
updateCallback := db.Callback().Update()
updateCallback.Match(enableTransaction).Register("gorm:begin_transaction", BeginTransaction)
updateCallback.Register("gorm:setup_reflect_value", SetupUpdateReflectValue)
updateCallback.Register("gorm:before_update", BeforeUpdate)
updateCallback.Register("gorm:save_before_associations", SaveBeforeAssociations(false))
updateCallback.Register("gorm:update", Update(config))
updateCallback.Register("gorm:save_after_associations", SaveAfterAssociations(false))
updateCallback.Register("gorm:after_update", AfterUpdate)
updateCallback.Match(enableTransaction).Register("gorm:commit_or_rollback_transaction", CommitOrRollbackTransaction)
updateCallback.Clauses = config.UpdateClauses
rowCallback := db.Callback().Row()
rowCallback.Register("gorm:row", RowQuery)
rowCallback.Clauses = config.QueryClauses
rawCallback := db.Callback().Raw()
rawCallback.Register("gorm:raw", RawExec)
rawCallback.Clauses = config.QueryClauses
}
================================================
FILE: callbacks/callmethod.go
================================================
package callbacks
import (
"reflect"
"gorm.io/gorm"
)
func callMethod(db *gorm.DB, fc func(value interface{}, tx *gorm.DB) bool) {
tx := db.Session(&gorm.Session{NewDB: true})
if called := fc(db.Statement.ReflectValue.Interface(), tx); !called {
switch db.Statement.ReflectValue.Kind() {
case reflect.Slice, reflect.Array:
db.Statement.CurDestIndex = 0
for i := 0; i < db.Statement.ReflectValue.Len(); i++ {
if value := reflect.Indirect(db.Statement.ReflectValue.Index(i)); value.CanAddr() {
fc(value.Addr().Interface(), tx)
} else {
db.AddError(gorm.ErrInvalidValue)
return
}
db.Statement.CurDestIndex++
}
case reflect.Struct:
if db.Statement.ReflectValue.CanAddr() {
fc(db.Statement.ReflectValue.Addr().Interface(), tx)
} else {
db.AddError(gorm.ErrInvalidValue)
}
}
}
}
================================================
FILE: callbacks/create.go
================================================
package callbacks
import (
"fmt"
"reflect"
"strings"
"gorm.io/gorm"
"gorm.io/gorm/clause"
"gorm.io/gorm/schema"
"gorm.io/gorm/utils"
)
// BeforeCreate before create hooks
func BeforeCreate(db *gorm.DB) {
if db.Error == nil && db.Statement.Schema != nil && !db.Statement.SkipHooks && (db.Statement.Schema.BeforeSave || db.Statement.Schema.BeforeCreate) {
callMethod(db, func(value interface{}, tx *gorm.DB) (called bool) {
if db.Statement.Schema.BeforeSave {
if i, ok := value.(BeforeSaveInterface); ok {
called = true
db.AddError(i.BeforeSave(tx))
}
}
if db.Statement.Schema.BeforeCreate {
if i, ok := value.(BeforeCreateInterface); ok {
called = true
db.AddError(i.BeforeCreate(tx))
}
}
return called
})
}
}
// Create create hook
func Create(config *Config) func(db *gorm.DB) {
supportReturning := utils.Contains(config.CreateClauses, "RETURNING")
return func(db *gorm.DB) {
if db.Error != nil {
return
}
if db.Statement.Schema != nil {
if !db.Statement.Unscoped {
for _, c := range db.Statement.Schema.CreateClauses {
db.Statement.AddClause(c)
}
}
if supportReturning && len(db.Statement.Schema.FieldsWithDefaultDBValue) > 0 {
if _, ok := db.Statement.Clauses["RETURNING"]; !ok {
fromColumns := make([]clause.Column, 0, len(db.Statement.Schema.FieldsWithDefaultDBValue))
for _, field := range db.Statement.Schema.FieldsWithDefaultDBValue {
if field.Readable {
fromColumns = append(fromColumns, clause.Column{Name: field.DBName})
}
}
if len(fromColumns) > 0 {
db.Statement.AddClause(clause.Returning{Columns: fromColumns})
}
}
}
}
if db.Statement.SQL.Len() == 0 {
db.Statement.SQL.Grow(180)
db.Statement.AddClauseIfNotExists(clause.Insert{})
db.Statement.AddClause(ConvertToCreateValues(db.Statement))
db.Statement.Build(db.Statement.BuildClauses...)
}
isDryRun := !db.DryRun && db.Error == nil
if !isDryRun {
return
}
ok, mode := hasReturning(db, supportReturning)
if ok {
if c, ok := db.Statement.Clauses["ON CONFLICT"]; ok {
onConflict, _ := c.Expression.(clause.OnConflict)
if onConflict.DoNothing {
mode |= gorm.ScanOnConflictDoNothing
} else if len(onConflict.DoUpdates) > 0 || onConflict.UpdateAll {
mode |= gorm.ScanUpdate
}
}
rows, err := db.Statement.ConnPool.QueryContext(
db.Statement.Context, db.Statement.SQL.String(), db.Statement.Vars...,
)
if db.AddError(err) == nil {
defer func() {
db.AddError(rows.Close())
}()
gorm.Scan(rows, db, mode)
if db.Statement.Result != nil {
db.Statement.Result.RowsAffected = db.RowsAffected
}
}
return
}
result, err := db.Statement.ConnPool.ExecContext(
db.Statement.Context, db.Statement.SQL.String(), db.Statement.Vars...,
)
if err != nil {
db.AddError(err)
return
}
db.RowsAffected, _ = result.RowsAffected()
if db.Statement.Result != nil {
db.Statement.Result.Result = result
db.Statement.Result.RowsAffected = db.RowsAffected
}
if db.RowsAffected == 0 {
return
}
var (
pkField *schema.Field
pkFieldName = "@id"
)
if db.Statement.Schema != nil {
if db.Statement.Schema.PrioritizedPrimaryField == nil ||
!db.Statement.Schema.PrioritizedPrimaryField.HasDefaultValue ||
!db.Statement.Schema.PrioritizedPrimaryField.Readable {
return
}
pkField = db.Statement.Schema.PrioritizedPrimaryField
pkFieldName = db.Statement.Schema.PrioritizedPrimaryField.DBName
}
insertID, err := result.LastInsertId()
insertOk := err == nil && insertID > 0
if !insertOk {
if !supportReturning {
db.AddError(err)
}
return
}
// append @id column with value for auto-increment primary key
// the @id value is correct, when: 1. without setting auto-increment primary key, 2. database AutoIncrementIncrement = 1
switch values := db.Statement.Dest.(type) {
case map[string]interface{}:
values[pkFieldName] = insertID
case *map[string]interface{}:
(*values)[pkFieldName] = insertID
case []map[string]interface{}, *[]map[string]interface{}:
mapValues, ok := values.([]map[string]interface{})
if !ok {
if v, ok := values.(*[]map[string]interface{}); ok {
if *v != nil {
mapValues = *v
}
}
}
if config.LastInsertIDReversed {
insertID -= int64(len(mapValues)-1) * schema.DefaultAutoIncrementIncrement
}
for _, mapValue := range mapValues {
if mapValue != nil {
mapValue[pkFieldName] = insertID
}
insertID += schema.DefaultAutoIncrementIncrement
}
default:
if pkField == nil {
return
}
switch db.Statement.ReflectValue.Kind() {
case reflect.Slice, reflect.Array:
if config.LastInsertIDReversed {
for i := db.Statement.ReflectValue.Len() - 1; i >= 0; i-- {
rv := db.Statement.ReflectValue.Index(i)
if reflect.Indirect(rv).Kind() != reflect.Struct {
break
}
_, isZero := pkField.ValueOf(db.Statement.Context, rv)
if isZero {
db.AddError(pkField.Set(db.Statement.Context, rv, insertID))
insertID -= pkField.AutoIncrementIncrement
}
}
} else {
for i := 0; i < db.Statement.ReflectValue.Len(); i++ {
rv := db.Statement.ReflectValue.Index(i)
if reflect.Indirect(rv).Kind() != reflect.Struct {
break
}
if _, isZero := pkField.ValueOf(db.Statement.Context, rv); isZero {
db.AddError(pkField.Set(db.Statement.Context, rv, insertID))
insertID += pkField.AutoIncrementIncrement
}
}
}
case reflect.Struct:
_, isZero := pkField.ValueOf(db.Statement.Context, db.Statement.ReflectValue)
if isZero {
db.AddError(pkField.Set(db.Statement.Context, db.Statement.ReflectValue, insertID))
}
}
}
}
}
// AfterCreate after create hooks
func AfterCreate(db *gorm.DB) {
if db.Error == nil && db.Statement.Schema != nil && !db.Statement.SkipHooks && (db.Statement.Schema.AfterSave || db.Statement.Schema.AfterCreate) {
callMethod(db, func(value interface{}, tx *gorm.DB) (called bool) {
if db.Statement.Schema.AfterCreate {
if i, ok := value.(AfterCreateInterface); ok {
called = true
db.AddError(i.AfterCreate(tx))
}
}
if db.Statement.Schema.AfterSave {
if i, ok := value.(AfterSaveInterface); ok {
called = true
db.AddError(i.AfterSave(tx))
}
}
return called
})
}
}
// ConvertToCreateValues convert to create values
func ConvertToCreateValues(stmt *gorm.Statement) (values clause.Values) {
curTime := stmt.DB.NowFunc()
switch value := stmt.Dest.(type) {
case map[string]interface{}:
values = ConvertMapToValuesForCreate(stmt, value)
case *map[string]interface{}:
values = ConvertMapToValuesForCreate(stmt, *value)
case []map[string]interface{}:
values = ConvertSliceOfMapToValuesForCreate(stmt, value)
case *[]map[string]interface{}:
values = ConvertSliceOfMapToValuesForCreate(stmt, *value)
default:
var (
selectColumns, restricted = stmt.SelectAndOmitColumns(true, false)
_, updateTrackTime = stmt.Get("gorm:update_track_time")
isZero bool
)
stmt.Settings.Delete("gorm:update_track_time")
values = clause.Values{Columns: make([]clause.Column, 0, len(stmt.Schema.DBNames))}
for _, db := range stmt.Schema.DBNames {
if field := stmt.Schema.FieldsByDBName[db]; !field.HasDefaultValue || field.DefaultValueInterface != nil {
if v, ok := selectColumns[db]; (ok && v) || (!ok && (!restricted || field.AutoCreateTime > 0 || field.AutoUpdateTime > 0)) {
values.Columns = append(values.Columns, clause.Column{Name: db})
}
}
}
switch stmt.ReflectValue.Kind() {
case reflect.Slice, reflect.Array:
rValLen := stmt.ReflectValue.Len()
if rValLen == 0 {
stmt.AddError(gorm.ErrEmptySlice)
return
}
stmt.SQL.Grow(rValLen * 18)
stmt.Vars = make([]interface{}, 0, rValLen*len(values.Columns))
values.Values = make([][]interface{}, rValLen)
defaultValueFieldsHavingValue := map[*schema.Field][]interface{}{}
for i := 0; i < rValLen; i++ {
rv := reflect.Indirect(stmt.ReflectValue.Index(i))
if !rv.IsValid() {
stmt.AddError(fmt.Errorf("slice data #%v is invalid: %w", i, gorm.ErrInvalidData))
return
}
values.Values[i] = make([]interface{}, len(values.Columns))
for idx, column := range values.Columns {
field := stmt.Schema.FieldsByDBName[column.Name]
if values.Values[i][idx], isZero = field.ValueOf(stmt.Context, rv); isZero {
if field.DefaultValueInterface != nil {
values.Values[i][idx] = field.DefaultValueInterface
stmt.AddError(field.Set(stmt.Context, rv, field.DefaultValueInterface))
} else if field.AutoCreateTime > 0 || field.AutoUpdateTime > 0 {
stmt.AddError(field.Set(stmt.Context, rv, curTime))
values.Values[i][idx], _ = field.ValueOf(stmt.Context, rv)
}
} else if field.AutoUpdateTime > 0 && updateTrackTime {
stmt.AddError(field.Set(stmt.Context, rv, curTime))
values.Values[i][idx], _ = field.ValueOf(stmt.Context, rv)
}
}
for _, field := range stmt.Schema.FieldsWithDefaultDBValue {
if v, ok := selectColumns[field.DBName]; (ok && v) || (!ok && !restricted) {
if rvOfvalue, isZero := field.ValueOf(stmt.Context, rv); !isZero {
if len(defaultValueFieldsHavingValue[field]) == 0 {
defaultValueFieldsHavingValue[field] = make([]interface{}, rValLen)
}
defaultValueFieldsHavingValue[field][i] = rvOfvalue
}
}
}
}
for _, field := range stmt.Schema.FieldsWithDefaultDBValue {
if vs, ok := defaultValueFieldsHavingValue[field]; ok {
values.Columns = append(values.Columns, clause.Column{Name: field.DBName})
for idx := range values.Values {
if vs[idx] == nil {
values.Values[idx] = append(values.Values[idx], stmt.DefaultValueOf(field))
} else {
values.Values[idx] = append(values.Values[idx], vs[idx])
}
}
}
}
case reflect.Struct:
values.Values = [][]interface{}{make([]interface{}, len(values.Columns))}
for idx, column := range values.Columns {
field := stmt.Schema.FieldsByDBName[column.Name]
if values.Values[0][idx], isZero = field.ValueOf(stmt.Context, stmt.ReflectValue); isZero {
if field.DefaultValueInterface != nil {
values.Values[0][idx] = field.DefaultValueInterface
stmt.AddError(field.Set(stmt.Context, stmt.ReflectValue, field.DefaultValueInterface))
} else if field.AutoCreateTime > 0 || field.AutoUpdateTime > 0 {
stmt.AddError(field.Set(stmt.Context, stmt.ReflectValue, curTime))
values.Values[0][idx], _ = field.ValueOf(stmt.Context, stmt.ReflectValue)
}
} else if field.AutoUpdateTime > 0 && updateTrackTime {
stmt.AddError(field.Set(stmt.Context, stmt.ReflectValue, curTime))
values.Values[0][idx], _ = field.ValueOf(stmt.Context, stmt.ReflectValue)
}
}
for _, field := range stmt.Schema.FieldsWithDefaultDBValue {
if v, ok := selectColumns[field.DBName]; (ok && v) || (!ok && !restricted) && field.DefaultValueInterface == nil {
if rvOfvalue, isZero := field.ValueOf(stmt.Context, stmt.ReflectValue); !isZero {
values.Columns = append(values.Columns, clause.Column{Name: field.DBName})
values.Values[0] = append(values.Values[0], rvOfvalue)
}
}
}
default:
stmt.AddError(gorm.ErrInvalidData)
}
}
if c, ok := stmt.Clauses["ON CONFLICT"]; ok {
if onConflict, _ := c.Expression.(clause.OnConflict); onConflict.UpdateAll {
if stmt.Schema != nil && len(values.Columns) >= 1 {
selectColumns, restricted := stmt.SelectAndOmitColumns(true, true)
columns := make([]string, 0, len(values.Columns)-1)
for _, column := range values.Columns {
if field := stmt.Schema.LookUpField(column.Name); field != nil {
if v, ok := selectColumns[field.DBName]; (ok && v) || (!ok && !restricted) {
if !field.PrimaryKey && (!field.HasDefaultValue || field.DefaultValueInterface != nil ||
strings.EqualFold(field.DefaultValue, "NULL")) && field.AutoCreateTime == 0 {
if field.AutoUpdateTime > 0 {
assignment := clause.Assignment{Column: clause.Column{Name: field.DBName}, Value: curTime}
switch field.AutoUpdateTime {
case schema.UnixNanosecond:
assignment.Value = curTime.UnixNano()
case schema.UnixMillisecond:
assignment.Value = curTime.UnixMilli()
case schema.UnixSecond:
assignment.Value = curTime.Unix()
}
onConflict.DoUpdates = append(onConflict.DoUpdates, assignment)
} else {
columns = append(columns, column.Name)
}
}
}
}
}
onConflict.DoUpdates = append(onConflict.DoUpdates, clause.AssignmentColumns(columns)...)
if len(onConflict.DoUpdates) == 0 {
onConflict.DoNothing = true
}
// use primary fields as default OnConflict columns
if len(onConflict.Columns) == 0 {
for _, field := range stmt.Schema.PrimaryFields {
onConflict.Columns = append(onConflict.Columns, clause.Column{Name: field.DBName})
}
}
stmt.AddClause(onConflict)
}
}
}
return values
}
================================================
FILE: callbacks/create_test.go
================================================
package callbacks
import (
"reflect"
"sync"
"testing"
"time"
"gorm.io/gorm"
"gorm.io/gorm/clause"
"gorm.io/gorm/schema"
)
var schemaCache = &sync.Map{}
func TestConvertToCreateValues_DestType_Slice(t *testing.T) {
type user struct {
ID int `gorm:"primaryKey"`
Name string
Email string `gorm:"default:(-)"`
Age int `gorm:"default:(-)"`
}
s, err := schema.Parse(&user{}, schemaCache, schema.NamingStrategy{})
if err != nil {
t.Errorf("parse schema error: %v, is not expected", err)
return
}
dest := []*user{
{
ID: 1,
Name: "alice",
Email: "email",
Age: 18,
},
{
ID: 2,
Name: "bob",
Email: "email",
Age: 19,
},
}
stmt := &gorm.Statement{
DB: &gorm.DB{
Config: &gorm.Config{
NowFunc: func() time.Time { return time.Time{} },
},
Statement: &gorm.Statement{
Settings: sync.Map{},
Schema: s,
},
},
ReflectValue: reflect.ValueOf(dest),
Dest: dest,
}
stmt.Schema = s
values := ConvertToCreateValues(stmt)
expected := clause.Values{
// column has value + defaultValue column has value (which should have a stable order)
Columns: []clause.Column{{Name: "name"}, {Name: "email"}, {Name: "age"}, {Name: "id"}},
Values: [][]interface{}{
{"alice", "email", 18, 1},
{"bob", "email", 19, 2},
},
}
if !reflect.DeepEqual(expected, values) {
t.Errorf("expected: %v got %v", expected, values)
}
}
================================================
FILE: callbacks/delete.go
================================================
package callbacks
import (
"reflect"
"strings"
"gorm.io/gorm"
"gorm.io/gorm/clause"
"gorm.io/gorm/schema"
"gorm.io/gorm/utils"
)
func BeforeDelete(db *gorm.DB) {
if db.Error == nil && db.Statement.Schema != nil && !db.Statement.SkipHooks && db.Statement.Schema.BeforeDelete {
callMethod(db, func(value interface{}, tx *gorm.DB) bool {
if i, ok := value.(BeforeDeleteInterface); ok {
db.AddError(i.BeforeDelete(tx))
return true
}
return false
})
}
}
func DeleteBeforeAssociations(db *gorm.DB) {
if db.Error == nil && db.Statement.Schema != nil {
selectColumns, restricted := db.Statement.SelectAndOmitColumns(true, false)
if !restricted {
return
}
for column, v := range selectColumns {
if !v {
continue
}
rel, ok := db.Statement.Schema.Relationships.Relations[column]
if !ok {
continue
}
switch rel.Type {
case schema.HasOne, schema.HasMany:
queryConds := rel.ToQueryConditions(db.Statement.Context, db.Statement.ReflectValue)
modelValue := reflect.New(rel.FieldSchema.ModelType).Interface()
tx := db.Session(&gorm.Session{NewDB: true}).Model(modelValue)
withoutConditions := false
if db.Statement.Unscoped {
tx = tx.Unscoped()
}
if len(db.Statement.Selects) > 0 {
selects := make([]string, 0, len(db.Statement.Selects))
for _, s := range db.Statement.Selects {
if s == clause.Associations {
selects = append(selects, s)
} else if columnPrefix := column + "."; strings.HasPrefix(s, columnPrefix) {
selects = append(selects, strings.TrimPrefix(s, columnPrefix))
}
}
if len(selects) > 0 {
tx = tx.Select(selects)
}
}
for _, cond := range queryConds {
if c, ok := cond.(clause.IN); ok && len(c.Values) == 0 {
withoutConditions = true
break
}
}
if !withoutConditions && db.AddError(tx.Clauses(clause.Where{Exprs: queryConds}).Delete(modelValue).Error) != nil {
return
}
case schema.Many2Many:
var (
queryConds = make([]clause.Expression, 0, len(rel.References))
foreignFields = make([]*schema.Field, 0, len(rel.References))
relForeignKeys = make([]string, 0, len(rel.References))
modelValue = reflect.New(rel.JoinTable.ModelType).Interface()
table = rel.JoinTable.Table
tx = db.Session(&gorm.Session{NewDB: true}).Model(modelValue).Table(table)
)
for _, ref := range rel.References {
if ref.OwnPrimaryKey {
foreignFields = append(foreignFields, ref.PrimaryKey)
relForeignKeys = append(relForeignKeys, ref.ForeignKey.DBName)
} else if ref.PrimaryValue != "" {
queryConds = append(queryConds, clause.Eq{
Column: clause.Column{Table: rel.JoinTable.Table, Name: ref.ForeignKey.DBName},
Value: ref.PrimaryValue,
})
}
}
_, foreignValues := schema.GetIdentityFieldValuesMap(db.Statement.Context, db.Statement.ReflectValue, foreignFields)
column, values := schema.ToQueryValues(table, relForeignKeys, foreignValues)
queryConds = append(queryConds, clause.IN{Column: column, Values: values})
if db.AddError(tx.Clauses(clause.Where{Exprs: queryConds}).Delete(modelValue).Error) != nil {
return
}
}
}
}
}
func Delete(config *Config) func(db *gorm.DB) {
supportReturning := utils.Contains(config.DeleteClauses, "RETURNING")
return func(db *gorm.DB) {
if db.Error != nil {
return
}
if db.Statement.Schema != nil {
for _, c := range db.Statement.Schema.DeleteClauses {
db.Statement.AddClause(c)
}
}
if db.Statement.SQL.Len() == 0 {
db.Statement.SQL.Grow(100)
db.Statement.AddClauseIfNotExists(clause.Delete{})
if db.Statement.Schema != nil {
_, queryValues := schema.GetIdentityFieldValuesMap(db.Statement.Context, db.Statement.ReflectValue, db.Statement.Schema.PrimaryFields)
column, values := schema.ToQueryValues(db.Statement.Table, db.Statement.Schema.PrimaryFieldDBNames, queryValues)
if len(values) > 0 {
db.Statement.AddClause(clause.Where{Exprs: []clause.Expression{clause.IN{Column: column, Values: values}}})
}
if db.Statement.ReflectValue.CanAddr() && db.Statement.Dest != db.Statement.Model && db.Statement.Model != nil {
_, queryValues = schema.GetIdentityFieldValuesMap(db.Statement.Context, reflect.ValueOf(db.Statement.Model), db.Statement.Schema.PrimaryFields)
column, values = schema.ToQueryValues(db.Statement.Table, db.Statement.Schema.PrimaryFieldDBNames, queryValues)
if len(values) > 0 {
db.Statement.AddClause(clause.Where{Exprs: []clause.Expression{clause.IN{Column: column, Values: values}}})
}
}
}
db.Statement.AddClauseIfNotExists(clause.From{})
db.Statement.Build(db.Statement.BuildClauses...)
}
checkMissingWhereConditions(db)
if !db.DryRun && db.Error == nil {
ok, mode := hasReturning(db, supportReturning)
if !ok {
result, err := db.Statement.ConnPool.ExecContext(db.Statement.Context, db.Statement.SQL.String(), db.Statement.Vars...)
if db.AddError(err) == nil {
db.RowsAffected, _ = result.RowsAffected()
if db.Statement.Result != nil {
db.Statement.Result.Result = result
db.Statement.Result.RowsAffected = db.RowsAffected
}
}
return
}
if rows, err := db.Statement.ConnPool.QueryContext(db.Statement.Context, db.Statement.SQL.String(), db.Statement.Vars...); db.AddError(err) == nil {
gorm.Scan(rows, db, mode)
if db.Statement.Result != nil {
db.Statement.Result.RowsAffected = db.RowsAffected
}
db.AddError(rows.Close())
}
}
}
}
func AfterDelete(db *gorm.DB) {
if db.Error == nil && db.Statement.Schema != nil && !db.Statement.SkipHooks && db.Statement.Schema.AfterDelete {
callMethod(db, func(value interface{}, tx *gorm.DB) bool {
if i, ok := value.(AfterDeleteInterface); ok {
db.AddError(i.AfterDelete(tx))
return true
}
return false
})
}
}
================================================
FILE: callbacks/helper.go
================================================
package callbacks
import (
"reflect"
"sort"
"gorm.io/gorm"
"gorm.io/gorm/clause"
)
// ConvertMapToValuesForCreate convert map to values
func ConvertMapToValuesForCreate(stmt *gorm.Statement, mapValue map[string]interface{}) (values clause.Values) {
values.Columns = make([]clause.Column, 0, len(mapValue))
selectColumns, restricted := stmt.SelectAndOmitColumns(true, false)
keys := make([]string, 0, len(mapValue))
for k := range mapValue {
keys = append(keys, k)
}
sort.Strings(keys)
for _, k := range keys {
value := mapValue[k]
if stmt.Schema != nil {
if field := stmt.Schema.LookUpField(k); field != nil {
k = field.DBName
}
}
if v, ok := selectColumns[k]; (ok && v) || (!ok && !restricted) {
values.Columns = append(values.Columns, clause.Column{Name: k})
if len(values.Values) == 0 {
values.Values = [][]interface{}{{}}
}
values.Values[0] = append(values.Values[0], value)
}
}
return
}
// ConvertSliceOfMapToValuesForCreate convert slice of map to values
func ConvertSliceOfMapToValuesForCreate(stmt *gorm.Statement, mapValues []map[string]interface{}) (values clause.Values) {
columns := make([]string, 0, len(mapValues))
// when the length of mapValues is zero,return directly here
// no need to call stmt.SelectAndOmitColumns method
if len(mapValues) == 0 {
stmt.AddError(gorm.ErrEmptySlice)
return
}
var (
result = make(map[string][]interface{}, len(mapValues))
selectColumns, restricted = stmt.SelectAndOmitColumns(true, false)
)
for idx, mapValue := range mapValues {
for k, v := range mapValue {
if stmt.Schema != nil {
if field := stmt.Schema.LookUpField(k); field != nil {
k = field.DBName
}
}
if _, ok := result[k]; !ok {
if v, ok := selectColumns[k]; (ok && v) || (!ok && !restricted) {
result[k] = make([]interface{}, len(mapValues))
columns = append(columns, k)
} else {
continue
}
}
result[k][idx] = v
}
}
sort.Strings(columns)
values.Values = make([][]interface{}, len(mapValues))
values.Columns = make([]clause.Column, len(columns))
for idx, column := range columns {
values.Columns[idx] = clause.Column{Name: column}
for i, v := range result[column] {
if len(values.Values[i]) == 0 {
values.Values[i] = make([]interface{}, len(columns))
}
values.Values[i][idx] = v
}
}
return
}
func hasReturning(tx *gorm.DB, supportReturning bool) (bool, gorm.ScanMode) {
if supportReturning {
if c, ok := tx.Statement.Clauses["RETURNING"]; ok {
returning, _ := c.Expression.(clause.Returning)
if len(returning.Columns) == 0 || (len(returning.Columns) == 1 && returning.Columns[0].Name == "*") {
return true, 0
}
return true, gorm.ScanUpdate
}
}
return false, 0
}
func checkMissingWhereConditions(db *gorm.DB) {
if !db.AllowGlobalUpdate && db.Error == nil {
where, withCondition := db.Statement.Clauses["WHERE"]
if withCondition {
if _, withSoftDelete := db.Statement.Clauses["soft_delete_enabled"]; withSoftDelete {
whereClause, _ := where.Expression.(clause.Where)
withCondition = len(whereClause.Exprs) > 1
}
}
if !withCondition {
db.AddError(gorm.ErrMissingWhereClause)
}
return
}
}
type visitMap = map[reflect.Value]bool
// Check if circular values, return true if loaded
func loadOrStoreVisitMap(visitMap *visitMap, v reflect.Value) (loaded bool) {
if v.Kind() == reflect.Ptr {
v = v.Elem()
}
switch v.Kind() {
case reflect.Slice, reflect.Array:
loaded = true
for i := 0; i < v.Len(); i++ {
if !loadOrStoreVisitMap(visitMap, v.Index(i)) {
loaded = false
}
}
case reflect.Struct, reflect.Interface:
if v.CanAddr() {
p := v.Addr()
if _, ok := (*visitMap)[p]; ok {
return true
}
(*visitMap)[p] = true
}
}
return
}
================================================
FILE: callbacks/helper_test.go
================================================
package callbacks
import (
"reflect"
"testing"
"gorm.io/gorm"
"gorm.io/gorm/clause"
)
func TestLoadOrStoreVisitMap(t *testing.T) {
var vm visitMap
var loaded bool
type testM struct {
Name string
}
t1 := testM{Name: "t1"}
t2 := testM{Name: "t2"}
t3 := testM{Name: "t3"}
vm = make(visitMap)
if loaded = loadOrStoreVisitMap(&vm, reflect.ValueOf(&t1)); loaded {
t.Fatalf("loaded should be false")
}
if loaded = loadOrStoreVisitMap(&vm, reflect.ValueOf(&t1)); !loaded {
t.Fatalf("loaded should be true")
}
// t1 already exist but t2 not
if loaded = loadOrStoreVisitMap(&vm, reflect.ValueOf([]*testM{&t1, &t2, &t3})); loaded {
t.Fatalf("loaded should be false")
}
if loaded = loadOrStoreVisitMap(&vm, reflect.ValueOf([]*testM{&t2, &t3})); !loaded {
t.Fatalf("loaded should be true")
}
}
func TestConvertMapToValuesForCreate(t *testing.T) {
testCase := []struct {
name string
input map[string]interface{}
expect clause.Values
}{
{
name: "Test convert string value",
input: map[string]interface{}{
"name": "my name",
},
expect: clause.Values{
Columns: []clause.Column{{Name: "name"}},
Values: [][]interface{}{{"my name"}},
},
},
{
name: "Test convert int value",
input: map[string]interface{}{
"age": 18,
},
expect: clause.Values{
Columns: []clause.Column{{Name: "age"}},
Values: [][]interface{}{{18}},
},
},
{
name: "Test convert float value",
input: map[string]interface{}{
"score": 99.5,
},
expect: clause.Values{
Columns: []clause.Column{{Name: "score"}},
Values: [][]interface{}{{99.5}},
},
},
{
name: "Test convert bool value",
input: map[string]interface{}{
"active": true,
},
expect: clause.Values{
Columns: []clause.Column{{Name: "active"}},
Values: [][]interface{}{{true}},
},
},
}
for _, tc := range testCase {
t.Run(tc.name, func(t *testing.T) {
actual := ConvertMapToValuesForCreate(&gorm.Statement{}, tc.input)
if !reflect.DeepEqual(actual, tc.expect) {
t.Errorf("expect %v got %v", tc.expect, actual)
}
})
}
}
func TestConvertSliceOfMapToValuesForCreate(t *testing.T) {
testCase := []struct {
name string
input []map[string]interface{}
expect clause.Values
}{
{
name: "Test convert slice of string value",
input: []map[string]interface{}{
{"name": "my name"},
},
expect: clause.Values{
Columns: []clause.Column{{Name: "name"}},
Values: [][]interface{}{{"my name"}},
},
},
{
name: "Test convert slice of int value",
input: []map[string]interface{}{
{"age": 18},
},
expect: clause.Values{
Columns: []clause.Column{{Name: "age"}},
Values: [][]interface{}{{18}},
},
},
{
name: "Test convert slice of float value",
input: []map[string]interface{}{
{"score": 99.5},
},
expect: clause.Values{
Columns: []clause.Column{{Name: "score"}},
Values: [][]interface{}{{99.5}},
},
},
{
name: "Test convert slice of bool value",
input: []map[string]interface{}{
{"active": true},
},
expect: clause.Values{
Columns: []clause.Column{{Name: "active"}},
Values: [][]interface{}{{true}},
},
},
}
for _, tc := range testCase {
t.Run(tc.name, func(t *testing.T) {
actual := ConvertSliceOfMapToValuesForCreate(&gorm.Statement{}, tc.input)
if !reflect.DeepEqual(actual, tc.expect) {
t.Errorf("expected %v but got %v", tc.expect, actual)
}
})
}
}
================================================
FILE: callbacks/interfaces.go
================================================
package callbacks
import "gorm.io/gorm"
type BeforeCreateInterface interface {
BeforeCreate(*gorm.DB) error
}
type AfterCreateInterface interface {
AfterCreate(*gorm.DB) error
}
type BeforeUpdateInterface interface {
BeforeUpdate(*gorm.DB) error
}
type AfterUpdateInterface interface {
AfterUpdate(*gorm.DB) error
}
type BeforeSaveInterface interface {
BeforeSave(*gorm.DB) error
}
type AfterSaveInterface interface {
AfterSave(*gorm.DB) error
}
type BeforeDeleteInterface interface {
BeforeDelete(*gorm.DB) error
}
type AfterDeleteInterface interface {
AfterDelete(*gorm.DB) error
}
type AfterFindInterface interface {
AfterFind(*gorm.DB) error
}
================================================
FILE: callbacks/preload.go
================================================
package callbacks
import (
"fmt"
"reflect"
"sort"
"strings"
"gorm.io/gorm"
"gorm.io/gorm/clause"
"gorm.io/gorm/schema"
"gorm.io/gorm/utils"
)
// parsePreloadMap extracts nested preloads. e.g.
//
// // schema has a "k0" relation and a "k7.k8" embedded relation
// parsePreloadMap(schema, map[string][]interface{}{
// clause.Associations: {"arg1"},
// "k1": {"arg2"},
// "k2.k3": {"arg3"},
// "k4.k5.k6": {"arg4"},
// })
// // preloadMap is
// map[string]map[string][]interface{}{
// "k0": {},
// "k7": {
// "k8": {},
// },
// "k1": {},
// "k2": {
// "k3": {"arg3"},
// },
// "k4": {
// "k5.k6": {"arg4"},
// },
// }
func parsePreloadMap(s *schema.Schema, preloads map[string][]interface{}) map[string]map[string][]interface{} {
preloadMap := map[string]map[string][]interface{}{}
setPreloadMap := func(name, value string, args []interface{}) {
if _, ok := preloadMap[name]; !ok {
preloadMap[name] = map[string][]interface{}{}
}
if value != "" {
preloadMap[name][value] = args
}
}
for name, args := range preloads {
preloadFields := strings.Split(name, ".")
value := strings.TrimPrefix(strings.TrimPrefix(name, preloadFields[0]), ".")
if preloadFields[0] == clause.Associations {
for _, relation := range s.Relationships.Relations {
if relation.Schema == s {
setPreloadMap(relation.Name, value, args)
}
}
for embedded, embeddedRelations := range s.Relationships.EmbeddedRelations {
for _, value := range embeddedValues(embeddedRelations) {
setPreloadMap(embedded, value, args)
}
}
} else {
setPreloadMap(preloadFields[0], value, args)
}
}
return preloadMap
}
func embeddedValues(embeddedRelations *schema.Relationships) []string {
if embeddedRelations == nil {
return nil
}
names := make([]string, 0, len(embeddedRelations.Relations)+len(embeddedRelations.EmbeddedRelations))
for _, relation := range embeddedRelations.Relations {
// skip first struct name
names = append(names, strings.Join(relation.Field.EmbeddedBindNames[1:], "."))
}
for _, relations := range embeddedRelations.EmbeddedRelations {
names = append(names, embeddedValues(relations)...)
}
return names
}
// preloadEntryPoint enters layer by layer. It will call real preload if it finds the right entry point.
// If the current relationship is embedded or joined, current query will be ignored.
//
//nolint:cyclop
func preloadEntryPoint(db *gorm.DB, joins []string, relationships *schema.Relationships, preloads map[string][]interface{}, associationsConds []interface{}) error {
preloadMap := parsePreloadMap(db.Statement.Schema, preloads)
// avoid random traversal of the map
preloadNames := make([]string, 0, len(preloadMap))
for key := range preloadMap {
preloadNames = append(preloadNames, key)
}
sort.Strings(preloadNames)
isJoined := func(name string) (joined bool, nestedJoins []string) {
for _, join := range joins {
if _, ok := relationships.Relations[join]; ok && name == join {
joined = true
continue
}
join0, join1, cut := strings.Cut(join, ".")
if cut {
if _, ok := relationships.Relations[join0]; ok && name == join0 {
joined = true
nestedJoins = append(nestedJoins, join1)
}
}
}
return joined, nestedJoins
}
for _, name := range preloadNames {
if relations := relationships.EmbeddedRelations[name]; relations != nil {
if err := preloadEntryPoint(db, joins, relations, preloadMap[name], associationsConds); err != nil {
return err
}
} else if rel := relationships.Relations[name]; rel != nil {
if joined, nestedJoins := isJoined(name); joined {
switch rv := db.Statement.ReflectValue; rv.Kind() {
case reflect.Slice, reflect.Array:
if rv.Len() > 0 {
reflectValue := rel.FieldSchema.MakeSlice().Elem()
for i := 0; i < rv.Len(); i++ {
frv := rel.Field.ReflectValueOf(db.Statement.Context, rv.Index(i))
if frv.Kind() != reflect.Ptr {
reflectValue = reflect.Append(reflectValue, frv.Addr())
} else {
if frv.IsNil() {
continue
}
reflectValue = reflect.Append(reflectValue, frv)
}
}
tx := preloadDB(db, reflectValue, reflectValue.Interface())
if err := preloadEntryPoint(tx, nestedJoins, &tx.Statement.Schema.Relationships, preloadMap[name], associationsConds); err != nil {
return err
}
}
case reflect.Struct, reflect.Pointer:
reflectValue := rel.Field.ReflectValueOf(db.Statement.Context, rv)
tx := preloadDB(db, reflectValue, reflectValue.Interface())
if err := preloadEntryPoint(tx, nestedJoins, &tx.Statement.Schema.Relationships, preloadMap[name], associationsConds); err != nil {
return err
}
default:
return gorm.ErrInvalidData
}
} else {
tx := db.Table("").Session(&gorm.Session{Context: db.Statement.Context, SkipHooks: db.Statement.SkipHooks})
tx.Statement.ReflectValue = db.Statement.ReflectValue
tx.Statement.Unscoped = db.Statement.Unscoped
if err := preload(tx, rel, append(preloads[name], associationsConds...), preloadMap[name]); err != nil {
return err
}
}
} else {
return fmt.Errorf("%s: %w for schema %s", name, gorm.ErrUnsupportedRelation, db.Statement.Schema.Name)
}
}
return nil
}
func preloadDB(db *gorm.DB, reflectValue reflect.Value, dest interface{}) *gorm.DB {
tx := db.Session(&gorm.Session{Context: db.Statement.Context, NewDB: true, SkipHooks: db.Statement.SkipHooks, Initialized: true})
db.Statement.Settings.Range(func(k, v interface{}) bool {
tx.Statement.Settings.Store(k, v)
return true
})
if err := tx.Statement.Parse(dest); err != nil {
tx.AddError(err)
return tx
}
tx.Statement.ReflectValue = reflectValue
tx.Statement.Unscoped = db.Statement.Unscoped
return tx
}
func preload(tx *gorm.DB, rel *schema.Relationship, conds []interface{}, preloads map[string][]interface{}) error {
var (
reflectValue = tx.Statement.ReflectValue
relForeignKeys []string
relForeignFields []*schema.Field
foreignFields []*schema.Field
foreignValues [][]interface{}
identityMap = map[string][]reflect.Value{}
inlineConds []interface{}
)
if rel.JoinTable != nil {
var (
joinForeignFields = make([]*schema.Field, 0, len(rel.References))
joinRelForeignFields = make([]*schema.Field, 0, len(rel.References))
joinForeignKeys = make([]string, 0, len(rel.References))
)
for _, ref := range rel.References {
if ref.OwnPrimaryKey {
joinForeignKeys = append(joinForeignKeys, ref.ForeignKey.DBName)
joinForeignFields = append(joinForeignFields, ref.ForeignKey)
foreignFields = append(foreignFields, ref.PrimaryKey)
} else if ref.PrimaryValue != "" {
tx = tx.Where(clause.Eq{Column: ref.ForeignKey.DBName, Value: ref.PrimaryValue})
} else {
joinRelForeignFields = append(joinRelForeignFields, ref.ForeignKey)
relForeignKeys = append(relForeignKeys, ref.PrimaryKey.DBName)
relForeignFields = append(relForeignFields, ref.PrimaryKey)
}
}
joinIdentityMap, joinForeignValues := schema.GetIdentityFieldValuesMap(tx.Statement.Context, reflectValue, foreignFields)
if len(joinForeignValues) == 0 {
return nil
}
joinResults := rel.JoinTable.MakeSlice().Elem()
column, values := schema.ToQueryValues(clause.CurrentTable, joinForeignKeys, joinForeignValues)
if err := tx.Where(clause.IN{Column: column, Values: values}).Find(joinResults.Addr().Interface()).Error; err != nil {
return err
}
// convert join identity map to relation identity map
fieldValues := make([]interface{}, len(joinForeignFields))
joinFieldValues := make([]interface{}, len(joinRelForeignFields))
for i := 0; i < joinResults.Len(); i++ {
joinIndexValue := joinResults.Index(i)
for idx, field := range joinForeignFields {
fieldValues[idx], _ = field.ValueOf(tx.Statement.Context, joinIndexValue)
}
for idx, field := range joinRelForeignFields {
joinFieldValues[idx], _ = field.ValueOf(tx.Statement.Context, joinIndexValue)
}
if results, ok := joinIdentityMap[utils.ToStringKey(fieldValues...)]; ok {
joinKey := utils.ToStringKey(joinFieldValues...)
identityMap[joinKey] = append(identityMap[joinKey], results...)
}
}
_, foreignValues = schema.GetIdentityFieldValuesMap(tx.Statement.Context, joinResults, joinRelForeignFields)
} else {
for _, ref := range rel.References {
if ref.OwnPrimaryKey {
relForeignKeys = append(relForeignKeys, ref.ForeignKey.DBName)
relForeignFields = append(relForeignFields, ref.ForeignKey)
foreignFields = append(foreignFields, ref.PrimaryKey)
} else if ref.PrimaryValue != "" {
tx = tx.Where(clause.Eq{Column: ref.ForeignKey.DBName, Value: ref.PrimaryValue})
} else {
relForeignKeys = append(relForeignKeys, ref.PrimaryKey.DBName)
relForeignFields = append(relForeignFields, ref.PrimaryKey)
foreignFields = append(foreignFields, ref.ForeignKey)
}
}
identityMap, foreignValues = schema.GetIdentityFieldValuesMap(tx.Statement.Context, reflectValue, foreignFields)
if len(foreignValues) == 0 {
return nil
}
}
// nested preload
for p, pvs := range preloads {
tx = tx.Preload(p, pvs...)
}
reflectResults := rel.FieldSchema.MakeSlice().Elem()
column, values := schema.ToQueryValues(clause.CurrentTable, relForeignKeys, foreignValues)
if len(values) != 0 {
tx = tx.Model(reflectResults.Addr().Interface()).Where(clause.IN{Column: column, Values: values})
for _, cond := range conds {
if fc, ok := cond.(func(*gorm.DB) *gorm.DB); ok {
tx = fc(tx)
} else {
inlineConds = append(inlineConds, cond)
}
}
if len(inlineConds) > 0 {
tx = tx.Where(inlineConds[0], inlineConds[1:]...)
}
if err := tx.Find(reflectResults.Addr().Interface()).Error; err != nil {
return err
}
}
fieldValues := make([]interface{}, len(relForeignFields))
// clean up old values before preloading
switch reflectValue.Kind() {
case reflect.Struct:
switch rel.Type {
case schema.HasMany, schema.Many2Many:
tx.AddError(rel.Field.Set(tx.Statement.Context, reflectValue, reflect.MakeSlice(rel.Field.IndirectFieldType, 0, 10).Interface()))
default:
tx.AddError(rel.Field.Set(tx.Statement.Context, reflectValue, reflect.New(rel.Field.FieldType).Interface()))
}
case reflect.Slice, reflect.Array:
for i := 0; i < reflectValue.Len(); i++ {
switch rel.Type {
case schema.HasMany, schema.Many2Many:
tx.AddError(rel.Field.Set(tx.Statement.Context, reflectValue.Index(i), reflect.MakeSlice(rel.Field.IndirectFieldType, 0, 10).Interface()))
default:
tx.AddError(rel.Field.Set(tx.Statement.Context, reflectValue.Index(i), reflect.New(rel.Field.FieldType).Interface()))
}
}
}
for i := 0; i < reflectResults.Len(); i++ {
elem := reflectResults.Index(i)
for idx, field := range relForeignFields {
fieldValues[idx], _ = field.ValueOf(tx.Statement.Context, elem)
}
datas, ok := identityMap[utils.ToStringKey(fieldValues...)]
if !ok {
return fmt.Errorf("failed to assign association %#v, make sure foreign fields exists", elem.Interface())
}
for _, data := range datas {
reflectFieldValue := rel.Field.ReflectValueOf(tx.Statement.Context, data)
if reflectFieldValue.Kind() == reflect.Ptr && reflectFieldValue.IsNil() {
reflectFieldValue.Set(reflect.New(rel.Field.FieldType.Elem()))
}
reflectFieldValue = reflect.Indirect(reflectFieldValue)
switch reflectFieldValue.Kind() {
case reflect.Struct:
tx.AddError(rel.Field.Set(tx.Statement.Context, data, elem.Interface()))
case reflect.Slice, reflect.Array:
if reflectFieldValue.Type().Elem().Kind() == reflect.Ptr {
tx.AddError(rel.Field.Set(tx.Statement.Context, data, reflect.Append(reflectFieldValue, elem).Interface()))
} else {
tx.AddError(rel.Field.Set(tx.Statement.Context, data, reflect.Append(reflectFieldValue, elem.Elem()).Interface()))
}
}
}
}
return tx.Error
}
================================================
FILE: callbacks/query.go
================================================
package callbacks
import (
"fmt"
"reflect"
"strings"
"gorm.io/gorm"
"gorm.io/gorm/clause"
"gorm.io/gorm/schema"
"gorm.io/gorm/utils"
)
func Query(db *gorm.DB) {
if db.Error == nil {
BuildQuerySQL(db)
if !db.DryRun && db.Error == nil {
rows, err := db.Statement.ConnPool.QueryContext(db.Statement.Context, db.Statement.SQL.String(), db.Statement.Vars...)
if err != nil {
db.AddError(err)
return
}
defer func() {
db.AddError(rows.Close())
}()
gorm.Scan(rows, db, 0)
if db.Statement.Result != nil {
db.Statement.Result.RowsAffected = db.RowsAffected
}
}
}
}
func BuildQuerySQL(db *gorm.DB) {
if db.Statement.Schema != nil {
for _, c := range db.Statement.Schema.QueryClauses {
db.Statement.AddClause(c)
}
}
if db.Statement.SQL.Len() == 0 {
db.Statement.SQL.Grow(100)
clauseSelect := clause.Select{Distinct: db.Statement.Distinct}
if db.Statement.ReflectValue.Kind() == reflect.Struct && db.Statement.ReflectValue.Type() == db.Statement.Schema.ModelType {
var conds []clause.Expression
for _, primaryField := range db.Statement.Schema.PrimaryFields {
if v, isZero := primaryField.ValueOf(db.Statement.Context, db.Statement.ReflectValue); !isZero {
conds = append(conds, clause.Eq{Column: clause.Column{Table: db.Statement.Table, Name: primaryField.DBName}, Value: v})
}
}
if len(conds) > 0 {
db.Statement.AddClause(clause.Where{Exprs: conds})
}
}
if len(db.Statement.Selects) > 0 {
clauseSelect.Columns = make([]clause.Column, len(db.Statement.Selects))
for idx, name := range db.Statement.Selects {
if db.Statement.Schema == nil {
clauseSelect.Columns[idx] = clause.Column{Name: name, Raw: true}
} else if f := db.Statement.Schema.LookUpField(name); f != nil {
clauseSelect.Columns[idx] = clause.Column{Name: f.DBName}
} else {
clauseSelect.Columns[idx] = clause.Column{Name: name, Raw: true}
}
}
} else if db.Statement.Schema != nil && len(db.Statement.Omits) > 0 {
selectColumns, _ := db.Statement.SelectAndOmitColumns(false, false)
clauseSelect.Columns = make([]clause.Column, 0, len(db.Statement.Schema.DBNames))
for _, dbName := range db.Statement.Schema.DBNames {
if v, ok := selectColumns[dbName]; (ok && v) || !ok {
clauseSelect.Columns = append(clauseSelect.Columns, clause.Column{Table: db.Statement.Table, Name: dbName})
}
}
} else if db.Statement.Schema != nil && db.Statement.ReflectValue.IsValid() {
queryFields := db.QueryFields
if !queryFields {
switch db.Statement.ReflectValue.Kind() {
case reflect.Struct:
queryFields = db.Statement.ReflectValue.Type() != db.Statement.Schema.ModelType
case reflect.Slice:
queryFields = db.Statement.ReflectValue.Type().Elem() != db.Statement.Schema.ModelType
}
}
if queryFields {
stmt := gorm.Statement{DB: db}
// smaller struct
if err := stmt.Parse(db.Statement.Dest); err == nil && (db.QueryFields || stmt.Schema.ModelType != db.Statement.Schema.ModelType) {
clauseSelect.Columns = make([]clause.Column, len(stmt.Schema.DBNames))
for idx, dbName := range stmt.Schema.DBNames {
clauseSelect.Columns[idx] = clause.Column{Table: db.Statement.Table, Name: dbName}
}
}
}
}
// inline joins
fromClause := clause.From{}
if v, ok := db.Statement.Clauses["FROM"].Expression.(clause.From); ok {
fromClause = v
}
if len(db.Statement.Joins) != 0 || len(fromClause.Joins) != 0 {
if len(db.Statement.Selects) == 0 && len(db.Statement.Omits) == 0 && db.Statement.Schema != nil {
clauseSelect.Columns = make([]clause.Column, len(db.Statement.Schema.DBNames))
for idx, dbName := range db.Statement.Schema.DBNames {
clauseSelect.Columns[idx] = clause.Column{Table: db.Statement.Table, Name: dbName}
}
}
specifiedRelationsName := map[string]string{clause.CurrentTable: clause.CurrentTable}
for _, join := range db.Statement.Joins {
if db.Statement.Schema != nil {
var isRelations bool // is relations or raw sql
var relations []*schema.Relationship
relation, ok := db.Statement.Schema.Relationships.Relations[join.Name]
if ok {
isRelations = true
relations = append(relations, relation)
} else {
// handle nested join like "Manager.Company"
nestedJoinNames := strings.Split(join.Name, ".")
if len(nestedJoinNames) > 1 {
isNestedJoin := true
guessNestedRelations := make([]*schema.Relationship, 0, len(nestedJoinNames))
currentRelations := db.Statement.Schema.Relationships.Relations
for _, relname := range nestedJoinNames {
// incomplete match, only treated as raw sql
if relation, ok = currentRelations[relname]; ok {
guessNestedRelations = append(guessNestedRelations, relation)
currentRelations = relation.FieldSchema.Relationships.Relations
} else {
isNestedJoin = false
break
}
}
if isNestedJoin {
isRelations = true
relations = guessNestedRelations
}
}
}
if isRelations {
genJoinClause := func(joinType clause.JoinType, tableAliasName string, parentTableName string, relation *schema.Relationship) clause.Join {
columnStmt := gorm.Statement{
Table: tableAliasName, DB: db, Schema: relation.FieldSchema,
Selects: join.Selects, Omits: join.Omits,
}
selectColumns, restricted := columnStmt.SelectAndOmitColumns(false, false)
for _, s := range relation.FieldSchema.DBNames {
if v, ok := selectColumns[s]; (ok && v) || (!ok && !restricted) {
clauseSelect.Columns = append(clauseSelect.Columns, clause.Column{
Table: tableAliasName,
Name: s,
Alias: utils.NestedRelationName(tableAliasName, s),
})
}
}
if join.Expression != nil {
return clause.Join{
Type: join.JoinType,
Expression: join.Expression,
}
}
exprs := make([]clause.Expression, len(relation.References))
for idx, ref := range relation.References {
if ref.OwnPrimaryKey {
exprs[idx] = clause.Eq{
Column: clause.Column{Table: parentTableName, Name: ref.PrimaryKey.DBName},
Value: clause.Column{Table: tableAliasName, Name: ref.ForeignKey.DBName},
}
} else {
if ref.PrimaryValue == "" {
exprs[idx] = clause.Eq{
Column: clause.Column{Table: parentTableName, Name: ref.ForeignKey.DBName},
Value: clause.Column{Table: tableAliasName, Name: ref.PrimaryKey.DBName},
}
} else {
exprs[idx] = clause.Eq{
Column: clause.Column{Table: tableAliasName, Name: ref.ForeignKey.DBName},
Value: ref.PrimaryValue,
}
}
}
}
{
onStmt := gorm.Statement{Table: tableAliasName, DB: db, Clauses: map[string]clause.Clause{}}
for _, c := range relation.FieldSchema.QueryClauses {
onStmt.AddClause(c)
}
if join.On != nil {
onStmt.AddClause(join.On)
}
if cs, ok := onStmt.Clauses["WHERE"]; ok {
if where, ok := cs.Expression.(clause.Where); ok {
where.Build(&onStmt)
if onSQL := onStmt.SQL.String(); onSQL != "" {
vars := onStmt.Vars
for idx, v := range vars {
bindvar := strings.Builder{}
onStmt.Vars = vars[0 : idx+1]
db.Dialector.BindVarTo(&bindvar, &onStmt, v)
onSQL = strings.Replace(onSQL, bindvar.String(), "?", 1)
}
exprs = append(exprs, clause.Expr{SQL: onSQL, Vars: vars})
}
}
}
}
return clause.Join{
Type: joinType,
Table: clause.Table{Name: relation.FieldSchema.Table, Alias: tableAliasName},
ON: clause.Where{Exprs: exprs},
}
}
parentTableName := clause.CurrentTable
for idx, rel := range relations {
// joins table alias like "Manager, Company, Manager__Company"
curAliasName := rel.Name
if parentTableName != clause.CurrentTable {
curAliasName = utils.NestedRelationName(parentTableName, curAliasName)
}
if _, ok := specifiedRelationsName[curAliasName]; !ok {
aliasName := curAliasName
if idx == len(relations)-1 && join.Alias != "" {
aliasName = join.Alias
}
fromClause.Joins = append(fromClause.Joins, genJoinClause(join.JoinType, aliasName, specifiedRelationsName[parentTableName], rel))
specifiedRelationsName[curAliasName] = aliasName
}
parentTableName = curAliasName
}
} else {
fromClause.Joins = append(fromClause.Joins, clause.Join{
Expression: clause.NamedExpr{SQL: join.Name, Vars: join.Conds},
})
}
} else {
fromClause.Joins = append(fromClause.Joins, clause.Join{
Expression: clause.NamedExpr{SQL: join.Name, Vars: join.Conds},
})
}
}
db.Statement.AddClause(fromClause)
} else {
db.Statement.AddClauseIfNotExists(clause.From{})
}
db.Statement.AddClauseIfNotExists(clauseSelect)
db.Statement.Build(db.Statement.BuildClauses...)
}
}
func Preload(db *gorm.DB) {
if db.Error == nil && len(db.Statement.Preloads) > 0 {
if db.Statement.Schema == nil {
db.AddError(fmt.Errorf("%w when using preload", gorm.ErrModelValueRequired))
return
}
joins := make([]string, 0, len(db.Statement.Joins))
for _, join := range db.Statement.Joins {
joins = append(joins, join.Name)
}
tx := preloadDB(db, db.Statement.ReflectValue, db.Statement.Dest)
if tx.Error != nil {
return
}
db.AddError(preloadEntryPoint(tx, joins, &tx.Statement.Schema.Relationships, db.Statement.Preloads, db.Statement.Preloads[clause.Associations]))
}
}
func AfterQuery(db *gorm.DB) {
// clear the joins after query because preload need it
if v, ok := db.Statement.Clauses["FROM"].Expression.(clause.From); ok {
fromClause := db.Statement.Clauses["FROM"]
fromClause.Expression = clause.From{Tables: v.Tables, Joins: utils.RTrimSlice(v.Joins, len(db.Statement.Joins))} // keep the original From Joins
db.Statement.Clauses["FROM"] = fromClause
}
if db.Error == nil && db.Statement.Schema != nil && !db.Statement.SkipHooks && db.Statement.Schema.AfterFind && db.RowsAffected > 0 {
callMethod(db, func(value interface{}, tx *gorm.DB) bool {
if i, ok := value.(AfterFindInterface); ok {
db.AddError(i.AfterFind(tx))
return true
}
return false
})
}
}
================================================
FILE: callbacks/raw.go
================================================
package callbacks
import (
"gorm.io/gorm"
)
func RawExec(db *gorm.DB) {
if db.Error == nil && !db.DryRun {
result, err := db.Statement.ConnPool.ExecContext(db.Statement.Context, db.Statement.SQL.String(), db.Statement.Vars...)
if err != nil {
db.AddError(err)
return
}
db.RowsAffected, _ = result.RowsAffected()
if db.Statement.Result != nil {
db.Statement.Result.Result = result
db.Statement.Result.RowsAffected = db.RowsAffected
}
}
}
================================================
FILE: callbacks/row.go
================================================
package callbacks
import (
"gorm.io/gorm"
)
func RowQuery(db *gorm.DB) {
if db.Error == nil {
BuildQuerySQL(db)
if db.DryRun || db.Error != nil {
return
}
if isRows, ok := db.Get("rows"); ok && isRows.(bool) {
db.Statement.Settings.Delete("rows")
db.Statement.Dest, db.Error = db.Statement.ConnPool.QueryContext(db.Statement.Context, db.Statement.SQL.String(), db.Statement.Vars...)
} else {
db.Statement.Dest = db.Statement.ConnPool.QueryRowContext(db.Statement.Context, db.Statement.SQL.String(), db.Statement.Vars...)
}
db.RowsAffected = -1
}
}
================================================
FILE: callbacks/transaction.go
================================================
package callbacks
import (
"gorm.io/gorm"
)
func BeginTransaction(db *gorm.DB) {
if !db.Config.SkipDefaultTransaction && db.Error == nil {
if tx := db.Begin(); tx.Error == nil {
db.Statement.ConnPool = tx.Statement.ConnPool
db.InstanceSet("gorm:started_transaction", true)
} else if tx.Error == gorm.ErrInvalidTransaction {
tx.Error = nil
} else {
db.Error = tx.Error
}
}
}
func CommitOrRollbackTransaction(db *gorm.DB) {
if !db.Config.SkipDefaultTransaction {
if _, ok := db.InstanceGet("gorm:started_transaction"); ok {
if db.Error != nil {
db.Rollback()
} else {
db.Commit()
}
db.Statement.ConnPool = db.ConnPool
}
}
}
================================================
FILE: callbacks/update.go
================================================
package callbacks
import (
"reflect"
"sort"
"gorm.io/gorm"
"gorm.io/gorm/clause"
"gorm.io/gorm/schema"
"gorm.io/gorm/utils"
)
func SetupUpdateReflectValue(db *gorm.DB) {
if db.Error == nil && db.Statement.Schema != nil {
if !db.Statement.ReflectValue.CanAddr() || db.Statement.Model != db.Statement.Dest {
db.Statement.ReflectValue = reflect.ValueOf(db.Statement.Model)
for db.Statement.ReflectValue.Kind() == reflect.Ptr {
db.Statement.ReflectValue = db.Statement.ReflectValue.Elem()
}
if dest, ok := db.Statement.Dest.(map[string]interface{}); ok {
for _, rel := range db.Statement.Schema.Relationships.BelongsTo {
if _, ok := dest[rel.Name]; ok {
db.AddError(rel.Field.Set(db.Statement.Context, db.Statement.ReflectValue, dest[rel.Name]))
}
}
}
}
}
}
// BeforeUpdate before update hooks
func BeforeUpdate(db *gorm.DB) {
if db.Error == nil && db.Statement.Schema != nil && !db.Statement.SkipHooks && (db.Statement.Schema.BeforeSave || db.Statement.Schema.BeforeUpdate) {
callMethod(db, func(value interface{}, tx *gorm.DB) (called bool) {
if db.Statement.Schema.BeforeSave {
if i, ok := value.(BeforeSaveInterface); ok {
called = true
db.AddError(i.BeforeSave(tx))
}
}
if db.Statement.Schema.BeforeUpdate {
if i, ok := value.(BeforeUpdateInterface); ok {
called = true
db.AddError(i.BeforeUpdate(tx))
}
}
return called
})
}
}
// Update update hook
func Update(config *Config) func(db *gorm.DB) {
supportReturning := utils.Contains(config.UpdateClauses, "RETURNING")
return func(db *gorm.DB) {
if db.Error != nil {
return
}
if db.Statement.Schema != nil {
for _, c := range db.Statement.Schema.UpdateClauses {
db.Statement.AddClause(c)
}
}
if db.Statement.SQL.Len() == 0 {
db.Statement.SQL.Grow(180)
db.Statement.AddClauseIfNotExists(clause.Update{})
if _, ok := db.Statement.Clauses["SET"]; !ok {
if set := ConvertToAssignments(db.Statement); len(set) != 0 {
defer delete(db.Statement.Clauses, "SET")
db.Statement.AddClause(set)
} else {
return
}
}
db.Statement.Build(db.Statement.BuildClauses...)
}
checkMissingWhereConditions(db)
if !db.DryRun && db.Error == nil {
if ok, mode := hasReturning(db, supportReturning); ok {
if rows, err := db.Statement.ConnPool.QueryContext(db.Statement.Context, db.Statement.SQL.String(), db.Statement.Vars...); db.AddError(err) == nil {
dest := db.Statement.Dest
db.Statement.Dest = db.Statement.ReflectValue.Addr().Interface()
gorm.Scan(rows, db, mode)
db.Statement.Dest = dest
db.AddError(rows.Close())
if db.Statement.Result != nil {
db.Statement.Result.RowsAffected = db.RowsAffected
}
}
} else {
result, err := db.Statement.ConnPool.ExecContext(db.Statement.Context, db.Statement.SQL.String(), db.Statement.Vars...)
if db.AddError(err) == nil {
db.RowsAffected, _ = result.RowsAffected()
}
if db.Statement.Result != nil {
db.Statement.Result.Result = result
db.Statement.Result.RowsAffected = db.RowsAffected
}
}
}
}
}
// AfterUpdate after update hooks
func AfterUpdate(db *gorm.DB) {
if db.Error == nil && db.Statement.Schema != nil && !db.Statement.SkipHooks && (db.Statement.Schema.AfterSave || db.Statement.Schema.AfterUpdate) {
callMethod(db, func(value interface{}, tx *gorm.DB) (called bool) {
if db.Statement.Schema.AfterUpdate {
if i, ok := value.(AfterUpdateInterface); ok {
called = true
db.AddError(i.AfterUpdate(tx))
}
}
if db.Statement.Schema.AfterSave {
if i, ok := value.(AfterSaveInterface); ok {
called = true
db.AddError(i.AfterSave(tx))
}
}
return called
})
}
}
// ConvertToAssignments convert to update assignments
func ConvertToAssignments(stmt *gorm.Statement) (set clause.Set) {
var (
selectColumns, restricted = stmt.SelectAndOmitColumns(false, true)
assignValue func(field *schema.Field, value interface{})
)
switch stmt.ReflectValue.Kind() {
case reflect.Slice, reflect.Array:
assignValue = func(field *schema.Field, value interface{}) {
for i := 0; i < stmt.ReflectValue.Len(); i++ {
if stmt.ReflectValue.CanAddr() {
field.Set(stmt.Context, stmt.ReflectValue.Index(i), value)
}
}
}
case reflect.Struct:
assignValue = func(field *schema.Field, value interface{}) {
if stmt.ReflectValue.CanAddr() {
field.Set(stmt.Context, stmt.ReflectValue, value)
}
}
default:
assignValue = func(field *schema.Field, value interface{}) {
}
}
updatingValue := reflect.ValueOf(stmt.Dest)
for updatingValue.Kind() == reflect.Ptr {
updatingValue = updatingValue.Elem()
}
if !updatingValue.CanAddr() || stmt.Dest != stmt.Model {
switch stmt.ReflectValue.Kind() {
case reflect.Slice, reflect.Array:
if size := stmt.ReflectValue.Len(); size > 0 {
var isZero bool
for i := 0; i < size; i++ {
for _, field := range stmt.Schema.PrimaryFields {
_, isZero = field.ValueOf(stmt.Context, stmt.ReflectValue.Index(i))
if !isZero {
break
}
}
}
if !isZero {
_, primaryValues := schema.GetIdentityFieldValuesMap(stmt.Context, stmt.ReflectValue, stmt.Schema.PrimaryFields)
column, values := schema.ToQueryValues("", stmt.Schema.PrimaryFieldDBNames, primaryValues)
stmt.AddClause(clause.Where{Exprs: []clause.Expression{clause.IN{Column: column, Values: values}}})
}
}
case reflect.Struct:
for _, field := range stmt.Schema.PrimaryFields {
if value, isZero := field.ValueOf(stmt.Context, stmt.ReflectValue); !isZero {
stmt.AddClause(clause.Where{Exprs: []clause.Expression{clause.Eq{Column: field.DBName, Value: value}}})
}
}
}
}
switch value := updatingValue.Interface().(type) {
case map[string]interface{}:
set = make([]clause.Assignment, 0, len(value))
keys := make([]string, 0, len(value))
for k := range value {
keys = append(keys, k)
}
sort.Strings(keys)
for _, k := range keys {
kv := value[k]
if _, ok := kv.(*gorm.DB); ok {
kv = []interface{}{kv}
}
if stmt.Schema != nil {
if field := stmt.Schema.LookUpField(k); field != nil {
if field.DBName != "" {
if v, ok := selectColumns[field.DBName]; (ok && v) || (!ok && !restricted) {
set = append(set, clause.Assignment{Column: clause.Column{Name: field.DBName}, Value: kv})
assignValue(field, value[k])
}
} else if v, ok := selectColumns[field.Name]; (ok && v) || (!ok && !restricted) {
assignValue(field, value[k])
}
continue
}
}
if v, ok := selectColumns[k]; (ok && v) || (!ok && !restricted) {
set = append(set, clause.Assignment{Column: clause.Column{Name: k}, Value: kv})
}
}
if !stmt.SkipHooks && stmt.Schema != nil {
for _, dbName := range stmt.Schema.DBNames {
field := stmt.Schema.LookUpField(dbName)
if field.AutoUpdateTime > 0 && value[field.Name] == nil && value[field.DBName] == nil {
if v, ok := selectColumns[field.DBName]; (ok && v) || !ok {
now := stmt.DB.NowFunc()
assignValue(field, now)
if field.AutoUpdateTime == schema.UnixNanosecond {
set = append(set, clause.Assignment{Column: clause.Column{Name: field.DBName}, Value: now.UnixNano()})
} else if field.AutoUpdateTime == schema.UnixMillisecond {
set = append(set, clause.Assignment{Column: clause.Column{Name: field.DBName}, Value: now.UnixMilli()})
} else if field.AutoUpdateTime == schema.UnixSecond {
set = append(set, clause.Assignment{Column: clause.Column{Name: field.DBName}, Value: now.Unix()})
} else {
set = append(set, clause.Assignment{Column: clause.Column{Name: field.DBName}, Value: now})
}
}
}
}
}
default:
updatingSchema := stmt.Schema
var isDiffSchema bool
if !updatingValue.CanAddr() || stmt.Dest != stmt.Model {
// different schema
updatingStmt := &gorm.Statement{DB: stmt.DB}
if err := updatingStmt.Parse(stmt.Dest); err == nil {
updatingSchema = updatingStmt.Schema
isDiffSchema = true
}
}
switch updatingValue.Kind() {
case reflect.Struct:
set = make([]clause.Assignment, 0, len(stmt.Schema.FieldsByDBName))
for _, dbName := range stmt.Schema.DBNames {
if field := updatingSchema.LookUpField(dbName); field != nil {
if !field.PrimaryKey || !updatingValue.CanAddr() || stmt.Dest != stmt.Model {
if v, ok := selectColumns[field.DBName]; (ok && v) || (!ok && (!restricted || (!stmt.SkipHooks && field.AutoUpdateTime > 0))) {
value, isZero := field.ValueOf(stmt.Context, updatingValue)
if !stmt.SkipHooks && field.AutoUpdateTime > 0 {
if field.AutoUpdateTime == schema.UnixNanosecond {
value = stmt.DB.NowFunc().UnixNano()
} else if field.AutoUpdateTime == schema.UnixMillisecond {
value = stmt.DB.NowFunc().UnixMilli()
} else if field.AutoUpdateTime == schema.UnixSecond {
value = stmt.DB.NowFunc().Unix()
} else {
value = stmt.DB.NowFunc()
}
isZero = false
}
if (ok || !isZero) && field.Updatable {
set = append(set, clause.Assignment{Column: clause.Column{Name: field.DBName}, Value: value})
assignField := field
if isDiffSchema {
if originField := stmt.Schema.LookUpField(dbName); originField != nil {
assignField = originField
}
}
assignValue(assignField, value)
}
}
} else {
if value, isZero := field.ValueOf(stmt.Context, updatingValue); !isZero {
stmt.AddClause(clause.Where{Exprs: []clause.Expression{clause.Eq{Column: field.DBName, Value: value}}})
}
}
}
}
default:
stmt.AddError(gorm.ErrInvalidData)
}
}
return
}
================================================
FILE: callbacks.go
================================================
package gorm
import (
"context"
"errors"
"fmt"
"reflect"
"sort"
"time"
"gorm.io/gorm/schema"
"gorm.io/gorm/utils"
)
func initializeCallbacks(db *DB) *callbacks {
return &callbacks{
processors: map[string]*processor{
"create": {db: db},
"query": {db: db},
"update": {db: db},
"delete": {db: db},
"row": {db: db},
"raw": {db: db},
},
}
}
// callbacks gorm callbacks manager
type callbacks struct {
processors map[string]*processor
}
type processor struct {
db *DB
Clauses []string
fns []func(*DB)
callbacks []*callback
}
type callback struct {
name string
before string
after string
remove bool
replace bool
match func(*DB) bool
handler func(*DB)
processor *processor
}
func (cs *callbacks) Create() *processor {
return cs.processors["create"]
}
func (cs *callbacks) Query() *processor {
return cs.processors["query"]
}
func (cs *callbacks) Update() *processor {
return cs.processors["update"]
}
func (cs *callbacks) Delete() *processor {
return cs.processors["delete"]
}
func (cs *callbacks) Row() *processor {
return cs.processors["row"]
}
func (cs *callbacks) Raw() *processor {
return cs.processors["raw"]
}
func (p *processor) Execute(db *DB) *DB {
// call scopes
for len(db.Statement.scopes) > 0 {
db = db.executeScopes()
}
var (
curTime = time.Now()
stmt = db.Statement
resetBuildClauses bool
)
if len(stmt.BuildClauses) == 0 {
stmt.BuildClauses = p.Clauses
resetBuildClauses = true
}
if optimizer, ok := stmt.Dest.(StatementModifier); ok {
optimizer.ModifyStatement(stmt)
}
if db.DefaultContextTimeout > 0 {
if _, ok := stmt.Context.Deadline(); !ok {
stmt.Context, _ = context.WithTimeout(stmt.Context, db.DefaultContextTimeout)
}
}
// assign model values
if stmt.Model == nil {
stmt.Model = stmt.Dest
} else if stmt.Dest == nil {
stmt.Dest = stmt.Model
}
// parse model values
if stmt.Model != nil {
if err := stmt.Parse(stmt.Model); err != nil && (!errors.Is(err, schema.ErrUnsupportedDataType) || (stmt.Table == "" && stmt.TableExpr == nil && stmt.SQL.Len() == 0)) {
if errors.Is(err, schema.ErrUnsupportedDataType) && stmt.Table == "" && stmt.TableExpr == nil {
db.AddError(fmt.Errorf("%w: Table not set, please set it like: db.Model(&user) or db.Table(\"users\")", err))
} else {
db.AddError(err)
}
}
}
// assign stmt.ReflectValue
if stmt.Dest != nil {
stmt.ReflectValue = reflect.ValueOf(stmt.Dest)
for stmt.ReflectValue.Kind() == reflect.Ptr {
if stmt.ReflectValue.IsNil() && stmt.ReflectValue.CanAddr() {
stmt.ReflectValue.Set(reflect.New(stmt.ReflectValue.Type().Elem()))
}
stmt.ReflectValue = stmt.ReflectValue.Elem()
}
if !stmt.ReflectValue.IsValid() {
db.AddError(ErrInvalidValue)
}
}
for _, f := range p.fns {
f(db)
}
if stmt.SQL.Len() > 0 {
db.Logger.Trace(stmt.Context, curTime, func() (string, int64) {
sql, vars := stmt.SQL.String(), stmt.Vars
if filter, ok := db.Logger.(ParamsFilter); ok {
sql, vars = filter.ParamsFilter(stmt.Context, stmt.SQL.String(), stmt.Vars...)
}
return db.Dialector.Explain(sql, vars...), db.RowsAffected
}, db.Error)
}
if !stmt.DB.DryRun {
stmt.SQL.Reset()
stmt.Vars = nil
}
if resetBuildClauses {
stmt.BuildClauses = nil
}
return db
}
func (p *processor) Get(name string) func(*DB) {
for i := len(p.callbacks) - 1; i >= 0; i-- {
if v := p.callbacks[i]; v.name == name && !v.remove {
return v.handler
}
}
return nil
}
func (p *processor) Before(name string) *callback {
return &callback{before: name, processor: p}
}
func (p *processor) After(name string) *callback {
return &callback{after: name, processor: p}
}
func (p *processor) Match(fc func(*DB) bool) *callback {
return &callback{match: fc, processor: p}
}
func (p *processor) Register(name string, fn func(*DB)) error {
return (&callback{processor: p}).Register(name, fn)
}
func (p *processor) Remove(name string) error {
return (&callback{processor: p}).Remove(name)
}
func (p *processor) Replace(name string, fn func(*DB)) error {
return (&callback{processor: p}).Replace(name, fn)
}
func (p *processor) compile() (err error) {
var callbacks []*callback
removedMap := map[string]bool{}
for _, callback := range p.callbacks {
if callback.match == nil || callback.match(p.db) {
callbacks = append(callbacks, callback)
}
if callback.remove {
removedMap[callback.name] = true
}
}
if len(removedMap) > 0 {
callbacks = removeCallbacks(callbacks, removedMap)
}
p.callbacks = callbacks
if p.fns, err = sortCallbacks(p.callbacks); err != nil {
p.db.Logger.Error(context.Background(), "Got error when compile callbacks, got %v", err)
}
return
}
func (c *callback) Before(name string) *callback {
c.before = name
return c
}
func (c *callback) After(name string) *callback {
c.after = name
return c
}
func (c *callback) Register(name string, fn func(*DB)) error {
c.name = name
c.handler = fn
c.processor.callbacks = append(c.processor.callbacks, c)
return c.processor.compile()
}
func (c *callback) Remove(name string) error {
c.processor.db.Logger.Warn(context.Background(), "removing callback `%s` from %s\n", name, utils.FileWithLineNum())
c.name = name
c.remove = true
c.processor.callbacks = append(c.processor.callbacks, c)
return c.processor.compile()
}
func (c *callback) Replace(name string, fn func(*DB)) error {
c.processor.db.Logger.Info(context.Background(), "replacing callback `%s` from %s\n", name, utils.FileWithLineNum())
c.name = name
c.handler = fn
c.replace = true
c.processor.callbacks = append(c.processor.callbacks, c)
return c.processor.compile()
}
// getRIndex get right index from string slice
func getRIndex(strs []string, str string) int {
for i := len(strs) - 1; i >= 0; i-- {
if strs[i] == str {
return i
}
}
return -1
}
func sortCallbacks(cs []*callback) (fns []func(*DB), err error) {
var (
names, sorted []string
sortCallback func(*callback) error
)
sort.SliceStable(cs, func(i, j int) bool {
if cs[j].before == "*" && cs[i].before != "*" {
return true
}
if cs[j].after == "*" && cs[i].after != "*" {
return true
}
return false
})
for _, c := range cs {
// show warning message the callback name already exists
if idx := getRIndex(names, c.name); idx > -1 && !c.replace && !c.remove && !cs[idx].remove {
c.processor.db.Logger.Warn(context.Background(), "duplicated callback `%s` from %s\n", c.name, utils.FileWithLineNum())
}
names = append(names, c.name)
}
sortCallback = func(c *callback) error {
if c.before != "" { // if defined before callback
if c.before == "*" && len(sorted) > 0 {
if curIdx := getRIndex(sorted, c.name); curIdx == -1 {
sorted = append([]string{c.name}, sorted...)
}
} else if sortedIdx := getRIndex(sorted, c.before); sortedIdx != -1 {
if curIdx := getRIndex(sorted, c.name); curIdx == -1 {
// if before callback already sorted, append current callback just after it
sorted = append(sorted[:sortedIdx], append([]string{c.name}, sorted[sortedIdx:]...)...)
} else if curIdx > sortedIdx {
return fmt.Errorf("conflicting callback %s with before %s", c.name, c.before)
}
} else if idx := getRIndex(names, c.before); idx != -1 {
// if before callback exists
cs[idx].after = c.name
}
}
if c.after != "" { // if defined after callback
if c.after == "*" && len(sorted) > 0 {
if curIdx := getRIndex(sorted, c.name); curIdx == -1 {
sorted = append(sorted, c.name)
}
} else if sortedIdx := getRIndex(sorted, c.after); sortedIdx != -1 {
if curIdx := getRIndex(sorted, c.name); curIdx == -1 {
// if after callback sorted, append current callback to last
sorted = append(sorted, c.name)
} else if curIdx < sortedIdx {
return fmt.Errorf("conflicting callback %s with before %s", c.name, c.after)
}
} else if idx := getRIndex(names, c.after); idx != -1 {
// if after callback exists but haven't sorted
// set after callback's before callback to current callback
after := cs[idx]
if after.before == "" {
after.before = c.name
}
if err := sortCallback(after); err != nil {
return err
}
if err := sortCallback(c); err != nil {
return err
}
}
}
// if current callback haven't been sorted, append it to last
if getRIndex(sorted, c.name) == -1 {
sorted = append(sorted, c.name)
}
return nil
}
for _, c := range cs {
if err = sortCallback(c); err != nil {
return
}
}
for _, name := range sorted {
if idx := getRIndex(names, name); !cs[idx].remove {
fns = append(fns, cs[idx].handler)
}
}
return
}
func removeCallbacks(cs []*callback, nameMap map[string]bool) []*callback {
callbacks := make([]*callback, 0, len(cs))
for _, callback := range cs {
if nameMap[callback.name] {
continue
}
callbacks = append(callbacks, callback)
}
return callbacks
}
================================================
FILE: chainable_api.go
================================================
package gorm
import (
"fmt"
"regexp"
"strings"
"gorm.io/gorm/clause"
"gorm.io/gorm/utils"
)
// Model specify the model you would like to run db operations
//
// // update all users's name to `hello`
// db.Model(&User{}).Update("name", "hello")
// // if user's primary key is non-blank, will use it as condition, then will only update that user's name to `hello`
// db.Model(&user).Update("name", "hello")
func (db *DB) Model(value interface{}) (tx *DB) {
tx = db.getInstance()
tx.Statement.Model = value
return
}
// Clauses Add clauses
//
// This supports both standard clauses (clause.OrderBy, clause.Limit, clause.Where) and more
// advanced techniques like specifying lock strength and optimizer hints. See the
// [docs] for more depth.
//
// // add a simple limit clause
// db.Clauses(clause.Limit{Limit: 1}).Find(&User{})
// // tell the optimizer to use the `idx_user_name` index
// db.Clauses(hints.UseIndex("idx_user_name")).Find(&User{})
// // specify the lock strength to UPDATE
// db.Clauses(clause.Locking{Strength: "UPDATE"}).Find(&users)
//
// [docs]: https://gorm.io/docs/sql_builder.html#Clauses
func (db *DB) Clauses(conds ...clause.Expression) (tx *DB) {
tx = db.getInstance()
var whereConds []interface{}
for _, cond := range conds {
if c, ok := cond.(clause.Interface); ok {
tx.Statement.AddClause(c)
} else if optimizer, ok := cond.(StatementModifier); ok {
optimizer.ModifyStatement(tx.Statement)
} else {
whereConds = append(whereConds, cond)
}
}
if len(whereConds) > 0 {
tx.Statement.AddClause(clause.Where{Exprs: tx.Statement.BuildCondition(whereConds[0], whereConds[1:]...)})
}
return
}
var tableRegexp = regexp.MustCompile(`(?i)(?:.+? AS (\w+)\s*(?:$|,)|^\w+\s+(\w+)$)`)
// Table specify the table you would like to run db operations
//
// // Get a user
// db.Table("users").Take(&result)
func (db *DB) Table(name string, args ...interface{}) (tx *DB) {
tx = db.getInstance()
if strings.Contains(name, " ") || strings.Contains(name, "`") || len(args) > 0 {
tx.Statement.TableExpr = &clause.Expr{SQL: name, Vars: args}
if results := tableRegexp.FindStringSubmatch(name); len(results) == 3 {
if results[1] != "" {
tx.Statement.Table = results[1]
} else {
tx.Statement.Table = results[2]
}
}
} else if tables := strings.Split(name, "."); len(tables) == 2 {
tx.Statement.TableExpr = &clause.Expr{SQL: tx.Statement.Quote(name)}
tx.Statement.Table = tables[1]
} else if name != "" {
tx.Statement.TableExpr = &clause.Expr{SQL: tx.Statement.Quote(name)}
tx.Statement.Table = name
} else {
tx.Statement.TableExpr = nil
tx.Statement.Table = ""
}
return
}
// Distinct specify distinct fields that you want querying
//
// // Select distinct names of users
// db.Distinct("name").Find(&results)
// // Select distinct name/age pairs from users
// db.Distinct("name", "age").Find(&results)
func (db *DB) Distinct(args ...interface{}) (tx *DB) {
tx = db.getInstance()
tx.Statement.Distinct = true
if len(args) > 0 {
tx = tx.Select(args[0], args[1:]...)
}
return
}
// Select specify fields that you want when querying, creating, updating
//
// Use Select when you only want a subset of the fields. By default, GORM will select all fields.
// Select accepts both string arguments and arrays.
//
// // Select name and age of user using multiple arguments
// db.Select("name", "age").Find(&users)
// // Select name and age of user using an array
// db.Select([]string{"name", "age"}).Find(&users)
func (db *DB) Select(query interface{}, args ...interface{}) (tx *DB) {
tx = db.getInstance()
switch v := query.(type) {
case []string:
tx.Statement.Selects = v
for _, arg := range args {
switch arg := arg.(type) {
case string:
tx.Statement.Selects = append(tx.Statement.Selects, arg)
case []string:
tx.Statement.Selects = append(tx.Statement.Selects, arg...)
default:
tx.AddError(fmt.Errorf("unsupported select args %v %v", query, args))
return
}
}
if clause, ok := tx.Statement.Clauses["SELECT"]; ok {
clause.Expression = nil
tx.Statement.Clauses["SELECT"] = clause
}
case string:
if strings.Count(v, "?") >= len(args) && len(args) > 0 {
tx.Statement.AddClause(clause.Select{
Distinct: db.Statement.Distinct,
Expression: clause.Expr{SQL: v, Vars: args},
})
} else if strings.Count(v, "@") > 0 && len(args) > 0 {
tx.Statement.AddClause(clause.Select{
Distinct: db.Statement.Distinct,
Expression: clause.NamedExpr{SQL: v, Vars: args},
})
} else {
tx.Statement.Selects = []string{v}
for _, arg := range args {
switch arg := arg.(type) {
case string:
tx.Statement.Selects = append(tx.Statement.Selects, arg)
case []string:
tx.Statement.Selects = append(tx.Statement.Selects, arg...)
default:
tx.Statement.AddClause(clause.Select{
Distinct: db.Statement.Distinct,
Expression: clause.Expr{SQL: v, Vars: args},
})
return
}
}
if clause, ok := tx.Statement.Clauses["SELECT"]; ok {
clause.Expression = nil
tx.Statement.Clauses["SELECT"] = clause
}
}
default:
tx.AddError(fmt.Errorf("unsupported select args %v %v", query, args))
}
return
}
// Omit specify fields that you want to ignore when creating, updating and querying
func (db *DB) Omit(columns ...string) (tx *DB) {
tx = db.getInstance()
if len(columns) == 1 && strings.ContainsRune(columns[0], ',') {
tx.Statement.Omits = strings.FieldsFunc(columns[0], utils.IsInvalidDBNameChar)
} else {
tx.Statement.Omits = columns
}
return
}
// MapColumns modify the column names in the query results to facilitate align to the corresponding structural fields
func (db *DB) MapColumns(m map[string]string) (tx *DB) {
tx = db.getInstance()
tx.Statement.ColumnMapping = m
return
}
// Where add conditions
//
// See the [docs] for details on the various formats that where clauses can take. By default, where clauses chain with AND.
//
// // Find the first user with name jinzhu
// db.Where("name = ?", "jinzhu").First(&user)
// // Find the first user with name jinzhu and age 20
// db.Where(&User{Name: "jinzhu", Age: 20}).First(&user)
// // Find the first user with name jinzhu and age not equal to 20
// db.Where("name = ?", "jinzhu").Where("age <> ?", "20").First(&user)
//
// [docs]: https://gorm.io/docs/query.html#Conditions
func (db *DB) Where(query interface{}, args ...interface{}) (tx *DB) {
tx = db.getInstance()
if conds := tx.Statement.BuildCondition(query, args...); len(conds) > 0 {
tx.Statement.AddClause(clause.Where{Exprs: conds})
}
return
}
// Not add NOT conditions
//
// Not works similarly to where, and has the same syntax.
//
// // Find the first user with name not equal to jinzhu
// db.Not("name = ?", "jinzhu").First(&user)
func (db *DB) Not(query interface{}, args ...interface{}) (tx *DB) {
tx = db.getInstance()
if conds := tx.Statement.BuildCondition(query, args...); len(conds) > 0 {
tx.Statement.AddClause(clause.Where{Exprs: []clause.Expression{clause.Not(conds...)}})
}
return
}
// Or add OR conditions
//
// Or is used to chain together queries with an OR.
//
// // Find the first user with name equal to jinzhu or john
// db.Where("name = ?", "jinzhu").Or("name = ?", "john").First(&user)
func (db *DB) Or(query interface{}, args ...interface{}) (tx *DB) {
tx = db.getInstance()
if conds := tx.Statement.BuildCondition(query, args...); len(conds) > 0 {
tx.Statement.AddClause(clause.Where{Exprs: []clause.Expression{clause.Or(clause.And(conds...))}})
}
return
}
// Joins specify Joins conditions
//
// db.Joins("Account").Find(&user)
// db.Joins("JOIN emails ON emails.user_id = users.id AND emails.email = ?", "jinzhu@example.org").Find(&user)
// db.Joins("Account", DB.Select("id").Where("user_id = users.id AND name = ?", "someName").Model(&Account{}))
func (db *DB) Joins(query string, args ...interface{}) (tx *DB) {
return joins(db, clause.LeftJoin, query, args...)
}
// InnerJoins specify inner joins conditions
// db.InnerJoins("Account").Find(&user)
func (db *DB) InnerJoins(query string, args ...interface{}) (tx *DB) {
return joins(db, clause.InnerJoin, query, args...)
}
func joins(db *DB, joinType clause.JoinType, query string, args ...interface{}) (tx *DB) {
tx = db.getInstance()
if len(args) == 1 {
if db, ok := args[0].(*DB); ok {
j := join{
Name: query, Conds: args, Selects: db.Statement.Selects,
Omits: db.Statement.Omits, JoinType: joinType,
}
if where, ok := db.Statement.Clauses["WHERE"].Expression.(clause.Where); ok {
j.On = &where
}
tx.Statement.Joins = append(tx.Statement.Joins, j)
return
}
}
tx.Statement.Joins = append(tx.Statement.Joins, join{Name: query, Conds: args, JoinType: joinType})
return
}
// Group specify the group method on the find
//
// // Select the sum age of users with given names
// db.Model(&User{}).Select("name, sum(age) as total").Group("name").Find(&results)
func (db *DB) Group(name string) (tx *DB) {
tx = db.getInstance()
fields := strings.FieldsFunc(name, utils.IsInvalidDBNameChar)
tx.Statement.AddClause(clause.GroupBy{
Columns: []clause.Column{{Name: name, Raw: len(fields) != 1}},
})
return
}
// Having specify HAVING conditions for GROUP BY
//
// // Select the sum age of users with name jinzhu
// db.Model(&User{}).Select("name, sum(age) as total").Group("name").Having("name = ?", "jinzhu").Find(&result)
func (db *DB) Having(query interface{}, args ...interface{}) (tx *DB) {
tx = db.getInstance()
tx.Statement.AddClause(clause.GroupBy{
Having: tx.Statement.BuildCondition(query, args...),
})
return
}
// Order specify order when retrieving records from database
//
// db.Order("name DESC")
// db.Order(clause.OrderByColumn{Column: clause.Column{Name: "name"}, Desc: true})
// db.Order(clause.OrderBy{Columns: []clause.OrderByColumn{
// {Column: clause.Column{Name: "name"}, Desc: true},
// {Column: clause.Column{Name: "age"}, Desc: true},
// }})
func (db *DB) Order(value interface{}) (tx *DB) {
tx = db.getInstance()
switch v := value.(type) {
case clause.OrderBy:
tx.Statement.AddClause(v)
case clause.OrderByColumn:
tx.Statement.AddClause(clause.OrderBy{
Columns: []clause.OrderByColumn{v},
})
case string:
if v != "" {
tx.Statement.AddClause(clause.OrderBy{
Columns: []clause.OrderByColumn{{
Column: clause.Column{Name: v, Raw: true},
}},
})
}
}
return
}
// Limit specify the number of records to be retrieved
//
// Limit conditions can be cancelled by using `Limit(-1)`.
//
// // retrieve 3 users
// db.Limit(3).Find(&users)
// // retrieve 3 users into users1, and all users into users2
// db.Limit(3).Find(&users1).Limit(-1).Find(&users2)
func (db *DB) Limit(limit int) (tx *DB) {
tx = db.getInstance()
tx.Statement.AddClause(clause.Limit{Limit: &limit})
return
}
// Offset specify the number of records to skip before starting to return the records
//
// Offset conditions can be cancelled by using `Offset(-1)`.
//
// // select the third user
// db.Offset(2).First(&user)
// // select the first user by cancelling an earlier chained offset
// db.Offset(5).Offset(-1).First(&user)
func (db *DB) Offset(offset int) (tx *DB) {
tx = db.getInstance()
tx.Statement.AddClause(clause.Limit{Offset: offset})
return
}
// Scopes pass current database connection to arguments `func(DB) DB`, which could be used to add conditions dynamically
//
// func AmountGreaterThan1000(db *gorm.DB) *gorm.DB {
// return db.Where("amount > ?", 1000)
// }
//
// func OrderStatus(status []string) func (db *gorm.DB) *gorm.DB {
// return func (db *gorm.DB) *gorm.DB {
// return db.Scopes(AmountGreaterThan1000).Where("status in (?)", status)
// }
// }
//
// db.Scopes(AmountGreaterThan1000, OrderStatus([]string{"paid", "shipped"})).Find(&orders)
func (db *DB) Scopes(funcs ...func(*DB) *DB) (tx *DB) {
tx = db.getInstance()
tx.Statement.scopes = append(tx.Statement.scopes, funcs...)
return tx
}
func (db *DB) executeScopes() (tx *DB) {
scopes := db.Statement.scopes
db.Statement.scopes = nil
for _, scope := range scopes {
db = scope(db)
}
return db
}
// Preload preload associations with given conditions
//
// // get all users, and preload all non-cancelled orders
// db.Preload("Orders", "state NOT IN (?)", "cancelled").Find(&users)
func (db *DB) Preload(query string, args ...interface{}) (tx *DB) {
tx = db.getInstance()
if tx.Statement.Preloads == nil {
tx.Statement.Preloads = map[string][]interface{}{}
}
tx.Statement.Preloads[query] = args
return
}
// Attrs provide attributes used in [FirstOrCreate] or [FirstOrInit]
//
// Attrs only adds attributes if the record is not found.
//
// // assign an email if the record is not found
// db.Where(User{Name: "non_existing"}).Attrs(User{Email: "fake@fake.org"}).FirstOrInit(&user)
// // user -> User{Name: "non_existing", Email: "fake@fake.org"}
//
// // assign an email if the record is not found, otherwise ignore provided email
// db.Where(User{Name: "jinzhu"}).Attrs(User{Email: "fake@fake.org"}).FirstOrInit(&user)
// // user -> User{Name: "jinzhu", Age: 20}
//
// [FirstOrCreate]: https://gorm.io/docs/advanced_query.html#FirstOrCreate
// [FirstOrInit]: https://gorm.io/docs/advanced_query.html#FirstOrInit
func (db *DB) Attrs(attrs ...interface{}) (tx *DB) {
tx = db.getInstance()
tx.Statement.attrs = attrs
return
}
// Assign provide attributes used in [FirstOrCreate] or [FirstOrInit]
//
// Assign adds attributes even if the record is found. If using FirstOrCreate, this means that
// records will be updated even if they are found.
//
// // assign an email regardless of if the record is not found
// db.Where(User{Name: "non_existing"}).Assign(User{Email: "fake@fake.org"}).FirstOrInit(&user)
// // user -> User{Name: "non_existing", Email: "fake@fake.org"}
//
// // assign email regardless of if record is found
// db.Where(User{Name: "jinzhu"}).Assign(User{Email: "fake@fake.org"}).FirstOrInit(&user)
// // user -> User{Name: "jinzhu", Age: 20, Email: "fake@fake.org"}
//
// [FirstOrCreate]: https://gorm.io/docs/advanced_query.html#FirstOrCreate
// [FirstOrInit]: https://gorm.io/docs/advanced_query.html#FirstOrInit
func (db *DB) Assign(attrs ...interface{}) (tx *DB) {
tx = db.getInstance()
tx.Statement.assigns = attrs
return
}
// Unscoped disables the global scope of soft deletion in a query.
// By default, GORM uses soft deletion, marking records as "deleted"
// by setting a timestamp on a specific field (e.g., `deleted_at`).
// Unscoped allows queries to include records marked as deleted,
// overriding the soft deletion behavior.
// Example:
//
// var users []User
// db.Unscoped().Find(&users)
// // Retrieves all users, including deleted ones.
func (db *DB) Unscoped() (tx *DB) {
tx = db.getInstance()
tx.Statement.Unscoped = true
return
}
func (db *DB) Raw(sql string, values ...interface{}) (tx *DB) {
tx = db.getInstance()
tx.Statement.SQL = strings.Builder{}
if strings.Contains(sql, "@") {
clause.NamedExpr{SQL: sql, Vars: values}.Build(tx.Statement)
} else {
clause.Expr{SQL: sql, Vars: values}.Build(tx.Statement)
}
return
}
================================================
FILE: clause/association.go
================================================
package clause
// AssociationOpType represents association operation types
type AssociationOpType int
const (
OpUnlink AssociationOpType = iota // Unlink association
OpDelete // Delete association records
OpUpdate // Update association records
OpCreate // Create association records with assignments
)
// Association represents an association operation
type Association struct {
Association string // Association name
Type AssociationOpType // Operation type
Conditions []Expression // Filter conditions
Set []Assignment // Assignment operations (for Update and Create)
Values []interface{} // Values for Create operation
}
// AssociationAssigner is an interface for association operation providers
type AssociationAssigner interface {
AssociationAssignments() []Association
}
// Assignments implements the Assigner interface so that AssociationOperation can be used as a Set method parameter
func (ao Association) Assignments() []Assignment {
return []Assignment{}
}
// AssociationAssignments implements the AssociationAssigner interface
func (ao Association) AssociationAssignments() []Association {
return []Association{ao}
}
================================================
FILE: clause/benchmarks_test.go
================================================
package clause_test
import (
"sync"
"testing"
"gorm.io/gorm"
"gorm.io/gorm/clause"
"gorm.io/gorm/schema"
"gorm.io/gorm/utils/tests"
)
func BenchmarkSelect(b *testing.B) {
user, _ := schema.Parse(&tests.User{}, &sync.Map{}, db.NamingStrategy)
for i := 0; i < b.N; i++ {
stmt := gorm.Statement{DB: db, Table: user.Table, Schema: user, Clauses: map[string]clause.Clause{}}
clauses := []clause.Interface{clause.Select{}, clause.From{}, clause.Where{Exprs: []clause.Expression{clause.Eq{Column: clause.PrimaryColumn, Value: "1"}, clause.Gt{Column: "age", Value: 18}, clause.Or(clause.Neq{Column: "name", Value: "jinzhu"})}}}
for _, clause := range clauses {
stmt.AddClause(clause)
}
stmt.Build("SELECT", "FROM", "WHERE")
_ = stmt.SQL.String()
}
}
func BenchmarkComplexSelect(b *testing.B) {
user, _ := schema.Parse(&tests.User{}, &sync.Map{}, db.NamingStrategy)
limit10 := 10
for i := 0; i < b.N; i++ {
stmt := gorm.Statement{DB: db, Table: user.Table, Schema: user, Clauses: map[string]clause.Clause{}}
clauses := []clause.Interface{
clause.Select{},
clause.From{},
clause.Where{Exprs: []clause.Expression{
clause.Eq{Column: clause.PrimaryColumn, Value: "1"},
clause.Gt{Column: "age", Value: 18},
clause.Or(clause.Neq{Column: "name", Value: "jinzhu"}),
}},
clause.Where{Exprs: []clause.Expression{
clause.Or(clause.Gt{Column: "score", Value: 100}, clause.Like{Column: "name", Value: "%linus%"}),
}},
clause.GroupBy{Columns: []clause.Column{{Name: "role"}}, Having: []clause.Expression{clause.Eq{"role", "admin"}}},
clause.Limit{Limit: &limit10, Offset: 20},
clause.OrderBy{Columns: []clause.OrderByColumn{{Column: clause.PrimaryColumn, Desc: true}}},
}
for _, clause := range clauses {
stmt.AddClause(clause)
}
stmt.Build("SELECT", "FROM", "WHERE", "GROUP BY", "LIMIT", "ORDER BY")
_ = stmt.SQL.String()
}
}
================================================
FILE: clause/clause.go
================================================
package clause
// Interface clause interface
type Interface interface {
Name() string
Build(Builder)
MergeClause(*Clause)
}
// ClauseBuilder clause builder, allows to customize how to build clause
type ClauseBuilder func(Clause, Builder)
type Writer interface {
WriteByte(byte) error
WriteString(string) (int, error)
}
// Builder builder interface
type Builder interface {
Writer
WriteQuoted(field interface{})
AddVar(Writer, ...interface{})
AddError(error) error
}
// Clause
type Clause struct {
Name string // WHERE
BeforeExpression Expression
AfterNameExpression Expression
AfterExpression Expression
Expression Expression
Builder ClauseBuilder
}
// Build build clause
func (c Clause) Build(builder Builder) {
if c.Builder != nil {
c.Builder(c, builder)
} else if c.Expression != nil {
if c.BeforeExpression != nil {
c.BeforeExpression.Build(builder)
builder.WriteByte(' ')
}
if c.Name != "" {
builder.WriteString(c.Name)
builder.WriteByte(' ')
}
if c.AfterNameExpression != nil {
c.AfterNameExpression.Build(builder)
builder.WriteByte(' ')
}
c.Expression.Build(builder)
if c.AfterExpression != nil {
builder.WriteByte(' ')
c.AfterExpression.Build(builder)
}
}
}
const (
PrimaryKey string = "~~~py~~~" // primary key
CurrentTable string = "~~~ct~~~" // current table
Associations string = "~~~as~~~" // associations
)
var (
currentTable = Table{Name: CurrentTable}
PrimaryColumn = Column{Table: CurrentTable, Name: PrimaryKey}
)
// Column quote with name
type Column struct {
Table string
Name string
Alias string
Raw bool
}
// Table quote with name
type Table struct {
Name string
Alias string
Raw bool
}
================================================
FILE: clause/clause_test.go
================================================
package clause_test
import (
"reflect"
"strings"
"sync"
"testing"
"gorm.io/gorm"
"gorm.io/gorm/clause"
"gorm.io/gorm/schema"
"gorm.io/gorm/utils/tests"
)
var db, _ = gorm.Open(tests.DummyDialector{}, nil)
func checkBuildClauses(t *testing.T, clauses []clause.Interface, result string, vars []interface{}) {
var (
buildNames []string
buildNamesMap = map[string]bool{}
user, _ = schema.Parse(&tests.User{}, &sync.Map{}, db.NamingStrategy)
stmt = gorm.Statement{DB: db, Table: user.Table, Schema: user, Clauses: map[string]clause.Clause{}}
)
for _, c := range clauses {
if _, ok := buildNamesMap[c.Name()]; !ok {
buildNames = append(buildNames, c.Name())
buildNamesMap[c.Name()] = true
}
stmt.AddClause(c)
}
stmt.Build(buildNames...)
if strings.TrimSpace(stmt.SQL.String()) != result {
t.Errorf("SQL expects %v got %v", result, stmt.SQL.String())
}
if !reflect.DeepEqual(stmt.Vars, vars) {
t.Errorf("Vars expects %+v got %v", stmt.Vars, vars)
}
}
================================================
FILE: clause/delete.go
================================================
package clause
type Delete struct {
Modifier string
}
func (d Delete) Name() string {
return "DELETE"
}
func (d Delete) Build(builder Builder) {
builder.WriteString("DELETE")
if d.Modifier != "" {
builder.WriteByte(' ')
builder.WriteString(d.Modifier)
}
}
func (d Delete) MergeClause(clause *Clause) {
clause.Name = ""
clause.Expression = d
}
================================================
FILE: clause/delete_test.go
================================================
package clause_test
import (
"fmt"
"testing"
"gorm.io/gorm/clause"
)
func TestDelete(t *testing.T) {
results := []struct {
Clauses []clause.Interface
Result string
Vars []interface{}
}{
{
[]clause.Interface{clause.Delete{}, clause.From{}},
"DELETE FROM `users`", nil,
},
{
[]clause.Interface{clause.Delete{Modifier: "LOW_PRIORITY"}, clause.From{}},
"DELETE LOW_PRIORITY FROM `users`", nil,
},
}
for idx, result := range results {
t.Run(fmt.Sprintf("case #%v", idx), func(t *testing.T) {
checkBuildClauses(t, result.Clauses, result.Result, result.Vars)
})
}
}
================================================
FILE: clause/expression.go
================================================
package clause
import (
"database/sql"
"database/sql/driver"
"go/ast"
"reflect"
)
// Expression expression interface
type Expression interface {
Build(builder Builder)
}
// NegationExpressionBuilder negation expression builder
type NegationExpressionBuilder interface {
NegationBuild(builder Builder)
}
// Expr raw expression
type Expr struct {
SQL string
Vars []interface{}
WithoutParentheses bool
}
// Build build raw expression
func (expr Expr) Build(builder Builder) {
var (
afterParenthesis bool
idx int
)
for _, v := range []byte(expr.SQL) {
if v == '?' && len(expr.Vars) > idx {
if afterParenthesis || expr.WithoutParentheses {
processValue(builder, expr.Vars[idx])
} else {
builder.AddVar(builder, expr.Vars[idx])
}
idx++
} else {
if v == '(' {
afterParenthesis = true
} else {
afterParenthesis = false
}
builder.WriteByte(v)
}
}
if idx < len(expr.Vars) {
for _, v := range expr.Vars[idx:] {
builder.AddVar(builder, sql.NamedArg{Value: v})
}
}
}
// NamedExpr raw expression for named expr
type NamedExpr struct {
SQL string
Vars []interface{}
}
// Build build raw expression
func (expr NamedExpr) Build(builder Builder) {
var (
idx int
inName bool
afterParenthesis bool
namedMap = make(map[string]interface{}, len(expr.Vars))
)
for _, v := range expr.Vars {
switch value := v.(type) {
case sql.NamedArg:
namedMap[value.Name] = value.Value
case map[string]interface{}:
for k, v := range value {
namedMap[k] = v
}
default:
var appendFieldsToMap func(reflect.Value)
appendFieldsToMap = func(reflectValue reflect.Value) {
reflectValue = reflect.Indirect(reflectValue)
switch reflectValue.Kind() {
case reflect.Struct:
modelType := reflectValue.Type()
for i := 0; i < modelType.NumField(); i++ {
if fieldStruct := modelType.Field(i); ast.IsExported(fieldStruct.Name) {
namedMap[fieldStruct.Name] = reflectValue.Field(i).Interface()
if fieldStruct.Anonymous {
appendFieldsToMap(reflectValue.Field(i))
}
}
}
}
}
appendFieldsToMap(reflect.ValueOf(value))
}
}
name := make([]byte, 0, 10)
for _, v := range []byte(expr.SQL) {
if v == '@' && !inName {
inName = true
name = name[:0]
} else if v == ' ' || v == ',' || v == ')' || v == '"' || v == '\'' || v == '`' || v == '\r' || v == '\n' || v == ';' {
if inName {
if nv, ok := namedMap[string(name)]; ok {
if afterParenthesis {
processValue(builder, nv)
} else {
builder.AddVar(builder, nv)
}
} else {
builder.WriteByte('@')
builder.WriteString(string(name))
}
inName = false
}
afterParenthesis = false
builder.WriteByte(v)
} else if v == '?' && len(expr.Vars) > idx {
if afterParenthesis {
processValue(builder, expr.Vars[idx])
} else {
builder.AddVar(builder, expr.Vars[idx])
}
idx++
} else if inName {
name = append(name, v)
} else {
if v == '(' {
afterParenthesis = true
} else {
afterParenthesis = false
}
builder.WriteByte(v)
}
}
if inName {
if nv, ok := namedMap[string(name)]; ok {
builder.AddVar(builder, nv)
} else {
builder.WriteByte('@')
builder.WriteString(string(name))
}
}
}
// processValue handles different value types appropriately for SQL parameter binding
// It checks for driver.Valuer first, then handles slices/arrays, and finally adds single values
func processValue(builder Builder, value interface{}) {
if _, ok := value.(driver.Valuer); ok {
builder.AddVar(builder, value)
return
}
switch rv := reflect.ValueOf(value); rv.Kind() {
case reflect.Slice, reflect.Array:
if rv.Len() == 0 {
builder.AddVar(builder, nil)
} else {
for i := 0; i < rv.Len(); i++ {
if i > 0 {
builder.WriteByte(',')
}
builder.AddVar(builder, rv.Index(i).Interface())
}
}
default:
builder.AddVar(builder, value)
}
}
// IN Whether a value is within a set of values
type IN struct {
Column interface{}
Values []interface{}
}
func (in IN) Build(builder Builder) {
builder.WriteQuoted(in.Column)
switch len(in.Values) {
case 0:
builder.WriteString(" IN (NULL)")
case 1:
if _, ok := in.Values[0].([]interface{}); !ok {
builder.WriteString(" = ")
builder.AddVar(builder, in.Values[0])
break
}
fallthrough
default:
builder.WriteString(" IN (")
builder.AddVar(builder, in.Values...)
builder.WriteByte(')')
}
}
func (in IN) NegationBuild(builder Builder) {
builder.WriteQuoted(in.Column)
switch len(in.Values) {
case 0:
builder.WriteString(" IS NOT NULL")
case 1:
if _, ok := in.Values[0].([]interface{}); !ok {
builder.WriteString(" <> ")
builder.AddVar(builder, in.Values[0])
break
}
fallthrough
default:
builder.WriteString(" NOT IN (")
builder.AddVar(builder, in.Values...)
builder.WriteByte(')')
}
}
// Eq equal to for where
type Eq struct {
Column interface{}
Value interface{}
}
func (eq Eq) Build(builder Builder) {
builder.WriteQuoted(eq.Column)
switch eq.Value.(type) {
case []string, []int, []int32, []int64, []uint, []uint32, []uint64, []interface{}:
rv := reflect.ValueOf(eq.Value)
if rv.Len() == 0 {
builder.WriteString(" IN (NULL)")
} else {
builder.WriteString(" IN (")
for i := 0; i < rv.Len(); i++ {
if i > 0 {
builder.WriteByte(',')
}
builder.AddVar(builder, rv.Index(i).Interface())
}
builder.WriteByte(')')
}
default:
if eqNil(eq.Value) {
builder.WriteString(" IS NULL")
} else {
builder.WriteString(" = ")
builder.AddVar(builder, eq.Value)
}
}
}
func (eq Eq) NegationBuild(builder Builder) {
Neq(eq).Build(builder)
}
// Neq not equal to for where
type Neq Eq
func (neq Neq) Build(builder Builder) {
builder.WriteQuoted(neq.Column)
switch neq.Value.(type) {
case []string, []int, []int32, []int64, []uint, []uint32, []uint64, []interface{}:
builder.WriteString(" NOT IN (")
rv := reflect.ValueOf(neq.Value)
for i := 0; i < rv.Len(); i++ {
if i > 0 {
builder.WriteByte(',')
}
builder.AddVar(builder, rv.Index(i).Interface())
}
builder.WriteByte(')')
default:
if eqNil(neq.Value) {
builder.WriteString(" IS NOT NULL")
} else {
builder.WriteString(" <> ")
builder.AddVar(builder, neq.Value)
}
}
}
func (neq Neq) NegationBuild(builder Builder) {
Eq(neq).Build(builder)
}
// Gt greater than for where
type Gt Eq
func (gt Gt) Build(builder Builder) {
builder.WriteQuoted(gt.Column)
builder.WriteString(" > ")
builder.AddVar(builder, gt.Value)
}
func (gt Gt) NegationBuild(builder Builder) {
Lte(gt).Build(builder)
}
// Gte greater than or equal to for where
type Gte Eq
func (gte Gte) Build(builder Builder) {
builder.WriteQuoted(gte.Column)
builder.WriteString(" >= ")
builder.AddVar(builder, gte.Value)
}
func (gte Gte) NegationBuild(builder Builder) {
Lt(gte).Build(builder)
}
// Lt less than for where
type Lt Eq
func (lt Lt) Build(builder Builder) {
builder.WriteQuoted(lt.Column)
builder.WriteString(" < ")
builder.AddVar(builder, lt.Value)
}
func (lt Lt) NegationBuild(builder Builder) {
Gte(lt).Build(builder)
}
// Lte less than or equal to for where
type Lte Eq
func (lte Lte) Build(builder Builder) {
builder.WriteQuoted(lte.Column)
builder.WriteString(" <= ")
builder.AddVar(builder, lte.Value)
}
func (lte Lte) NegationBuild(builder Builder) {
Gt(lte).Build(builder)
}
// Like whether string matches regular expression
type Like Eq
func (like Like) Build(builder Builder) {
builder.WriteQuoted(like.Column)
builder.WriteString(" LIKE ")
builder.AddVar(builder, like.Value)
}
func (like Like) NegationBuild(builder Builder) {
builder.WriteQuoted(like.Column)
builder.WriteString(" NOT LIKE ")
builder.AddVar(builder, like.Value)
}
func eqNil(value interface{}) bool {
if valuer, ok := value.(driver.Valuer); ok && !eqNilReflect(valuer) {
value, _ = valuer.Value()
}
return value == nil || eqNilReflect(value)
}
func eqNilReflect(value interface{}) bool {
reflectValue := reflect.ValueOf(value)
return reflectValue.Kind() == reflect.Ptr && reflectValue.IsNil()
}
================================================
FILE: clause/expression_test.go
================================================
package clause_test
import (
"database/sql"
"fmt"
"reflect"
"sync"
"testing"
"gorm.io/gorm"
"gorm.io/gorm/clause"
"gorm.io/gorm/schema"
"gorm.io/gorm/utils/tests"
)
func TestExpr(t *testing.T) {
results := []struct {
SQL string
Result string
Vars []interface{}
}{{
SQL: "create table ? (? ?, ? ?)",
Vars: []interface{}{clause.Table{Name: "users"}, clause.Column{Name: "id"}, clause.Expr{SQL: "int"}, clause.Column{Name: "name"}, clause.Expr{SQL: "text"}},
Result: "create table `users` (`id` int, `name` text)",
}}
for idx, result := range results {
t.Run(fmt.Sprintf("case #%v", idx), func(t *testing.T) {
user, _ := schema.Parse(&tests.User{}, &sync.Map{}, db.NamingStrategy)
stmt := &gorm.Statement{DB: db, Table: user.Table, Schema: user, Clauses: map[string]clause.Clause{}}
clause.Expr{SQL: result.SQL, Vars: result.Vars}.Build(stmt)
if stmt.SQL.String() != result.Result {
t.Errorf("generated SQL is not equal, expects %v, but got %v", result.Result, stmt.SQL.String())
}
})
}
}
func TestNamedExpr(t *testing.T) {
type Base struct {
Name2 string
}
type NamedArgument struct {
Name1 string
Base
}
results := []struct {
SQL string
Result string
Vars []interface{}
ExpectedVars []interface{}
}{{
SQL: "create table ? (? ?, ? ?)",
Vars: []interface{}{clause.Table{Name: "users"}, clause.Column{Name: "id"}, clause.Expr{SQL: "int"}, clause.Column{Name: "name"}, clause.Expr{SQL: "text"}},
Result: "create table `users` (`id` int, `name` text)",
}, {
SQL: "name1 = @name AND name2 = @name",
Vars: []interface{}{sql.Named("name", "jinzhu")},
Result: "name1 = ? AND name2 = ?",
ExpectedVars: []interface{}{"jinzhu", "jinzhu"},
}, {
SQL: "name1 = @name AND name2 = @@name",
Vars: []interface{}{map[string]interface{}{"name": "jinzhu"}},
Result: "name1 = ? AND name2 = @@name",
ExpectedVars: []interface{}{"jinzhu"},
}, {
SQL: "name1 = @name1 AND name2 = @name2 AND name3 = @name1",
Vars: []interface{}{sql.Named("name1", "jinzhu"), sql.Named("name2", "jinzhu2")},
Result: "name1 = ? AND name2 = ? AND name3 = ?",
ExpectedVars: []interface{}{"jinzhu", "jinzhu2", "jinzhu"},
}, {
SQL: "name1 = @name1 AND name2 = @name2 AND name3 = @name1",
Vars: []interface{}{map[string]interface{}{"name1": "jinzhu", "name2": "jinzhu2"}},
Result: "name1 = ? AND name2 = ? AND name3 = ?",
ExpectedVars: []interface{}{"jinzhu", "jinzhu2", "jinzhu"},
}, {
SQL: "@@test AND name1 = @name1 AND name2 = @name2 AND name3 = @name1 @notexist",
Vars: []interface{}{sql.Named("name1", "jinzhu"), sql.Named("name2", "jinzhu2")},
Result: "@@test AND name1 = ? AND name2 = ? AND name3 = ? @notexist",
ExpectedVars: []interface{}{"jinzhu", "jinzhu2", "jinzhu"},
}, {
SQL: "@@test AND name1 = @Name1 AND name2 = @Name2 AND name3 = @Name1 @notexist",
Vars: []interface{}{NamedArgument{Name1: "jinzhu", Base: Base{Name2: "jinzhu2"}}},
Result: "@@test AND name1 = ? AND name2 = ? AND name3 = ? @notexist",
ExpectedVars: []interface{}{"jinzhu", "jinzhu2", "jinzhu"},
}, {
SQL: "name in (@names)",
Vars: []interface{}{map[string]interface{}{"names": []interface{}{"jinzhu", "jinzhu2"}}},
Result: "name in (?,?)",
ExpectedVars: []interface{}{"jinzhu", "jinzhu2"},
}, {
SQL: "name in (@names)",
Vars: []interface{}{map[string]interface{}{"names": "jinzhu"}},
Result: "name in (?)",
ExpectedVars: []interface{}{"jinzhu"},
}, {
SQL: "create table ? (? ?, ? ?)",
Vars: []interface{}{},
Result: "create table ? (? ?, ? ?)",
}, {
SQL: "name1 = @name AND name2 = @name;",
Vars: []interface{}{sql.Named("name", "jinzhu")},
Result: "name1 = ? AND name2 = ?;",
ExpectedVars: []interface{}{"jinzhu", "jinzhu"},
}, {
SQL: "name1 = @name1\r\n AND name2 = @name2",
Vars: []interface{}{map[string]interface{}{"name1": "jinzhu", "name2": "jinzhu"}},
Result: "name1 = ?\r\n AND name2 = ?",
ExpectedVars: []interface{}{"jinzhu", "jinzhu"},
}, {
SQL: "name1 = @name1\r AND name2 = @name2",
Vars: []interface{}{map[string]interface{}{"name1": "jinzhu", "name2": "jinzhu"}},
Result: "name1 = ?\r AND name2 = ?",
ExpectedVars: []interface{}{"jinzhu", "jinzhu"},
}, {
SQL: "?",
Vars: []interface{}{clause.Column{Table: "table", Name: "col"}},
Result: "`table`.`col`",
}, {
SQL: "?",
Vars: []interface{}{clause.Column{Table: "table", Name: "col", Raw: true}},
Result: "table.col",
}, {
SQL: "?",
Vars: []interface{}{clause.Column{Table: "table", Name: clause.PrimaryKey, Raw: true}},
Result: "table.id",
}, {
SQL: "?",
Vars: []interface{}{clause.Column{Table: "table", Name: "col", Alias: "alias"}},
Result: "`table`.`col` AS `alias`",
}, {
SQL: "?",
Vars: []interface{}{clause.Column{Table: "table", Name: "col", Alias: "alias", Raw: true}},
Result: "table.col AS alias",
}, {
SQL: "?",
Vars: []interface{}{clause.Table{Name: "table", Alias: "alias"}},
Result: "`table` `alias`",
}, {
SQL: "?",
Vars: []interface{}{clause.Table{Name: "table", Alias: "alias", Raw: true}},
Result: "table alias",
}}
for idx, result := range results {
t.Run(fmt.Sprintf("case #%v", idx), func(t *testing.T) {
user, _ := schema.Parse(&tests.User{}, &sync.Map{}, db.NamingStrategy)
stmt := &gorm.Statement{DB: db, Table: user.Table, Schema: user, Clauses: map[string]clause.Clause{}}
clause.NamedExpr{SQL: result.SQL, Vars: result.Vars}.Build(stmt)
if stmt.SQL.String() != result.Result {
t.Errorf("generated SQL is not equal, expects %v, but got %v", result.Result, stmt.SQL.String())
}
if !reflect.DeepEqual(result.ExpectedVars, stmt.Vars) {
t.Errorf("generated vars is not equal, expects %v, but got %v", result.ExpectedVars, stmt.Vars)
}
})
}
}
func TestExpression(t *testing.T) {
column := "column-name"
results := []struct {
Expressions []clause.Expression
ExpectedVars []interface{}
Result string
}{{
Expressions: []clause.Expression{
clause.Eq{Column: column, Value: "column-value"},
},
ExpectedVars: []interface{}{"column-value"},
Result: "`column-name` = ?",
}, {
Expressions: []clause.Expression{
clause.Eq{Column: column, Value: nil},
clause.Eq{Column: column, Value: (*string)(nil)},
clause.Eq{Column: column, Value: (*int)(nil)},
clause.Eq{Column: column, Value: (*bool)(nil)},
clause.Eq{Column: column, Value: (interface{})(nil)},
clause.Eq{Column: column, Value: sql.NullString{String: "", Valid: false}},
},
Result: "`column-name` IS NULL",
}, {
Expressions: []clause.Expression{
clause.Neq{Column: column, Value: "column-value"},
},
ExpectedVars: []interface{}{"column-value"},
Result: "`column-name` <> ?",
}, {
Expressions: []clause.Expression{
clause.Neq{Column: column, Value: nil},
clause.Neq{Column: column, Value: (*string)(nil)},
clause.Neq{Column: column, Value: (*int)(nil)},
clause.Neq{Column: column, Value: (*bool)(nil)},
clause.Neq{Column: column, Value: (interface{})(nil)},
},
Result: "`column-name` IS NOT NULL",
}, {
Expressions: []clause.Expression{
clause.Eq{Column: column, Value: []string{"a", "b"}},
},
ExpectedVars: []interface{}{"a", "b"},
Result: "`column-name` IN (?,?)",
}, {
Expressions: []clause.Expression{
clause.Neq{Column: column, Value: []string{"a", "b"}},
},
ExpectedVars: []interface{}{"a", "b"},
Result: "`column-name` NOT IN (?,?)",
}, {
Expressions: []clause.Expression{
clause.Eq{Column: column, Value: []string{}},
},
Result: "`column-name` IN (NULL)",
}, {
Expressions: []clause.Expression{
clause.Eq{Column: clause.Expr{SQL: "SUM(?)", Vars: []interface{}{clause.Column{Name: "id"}}}, Value: 100},
},
ExpectedVars: []interface{}{100},
Result: "SUM(`id`) = ?",
}, {
Expressions: []clause.Expression{
clause.Gte{Column: clause.Expr{SQL: "SUM(?)", Vars: []interface{}{clause.Column{Table: "users", Name: "id"}}}, Value: 100},
},
ExpectedVars: []interface{}{100},
Result: "SUM(`users`.`id`) >= ?",
}}
for idx, result := range results {
for idy, expression := range result.Expressions {
t.Run(fmt.Sprintf("case #%v.%v", idx, idy), func(t *testing.T) {
user, _ := schema.Parse(&tests.User{}, &sync.Map{}, db.NamingStrategy)
stmt := &gorm.Statement{DB: db, Table: user.Table, Schema: user, Clauses: map[string]clause.Clause{}}
expression.Build(stmt)
if stmt.SQL.String() != result.Result {
t.Errorf("generated SQL is not equal, expects %v, but got %v", result.Result, stmt.SQL.String())
}
if !reflect.DeepEqual(result.ExpectedVars, stmt.Vars) {
t.Errorf("generated vars is not equal, expects %v, but got %v", result.ExpectedVars, stmt.Vars)
}
})
}
}
}
================================================
FILE: clause/from.go
================================================
package clause
// From from clause
type From struct {
Tables []Table
Joins []Join
}
// Name from clause name
func (from From) Name() string {
return "FROM"
}
// Build build from clause
func (from From) Build(builder Builder) {
if len(from.Tables) > 0 {
for idx, table := range from.Tables {
if idx > 0 {
builder.WriteByte(',')
}
builder.WriteQuoted(table)
}
} else {
builder.WriteQuoted(currentTable)
}
for _, join := range from.Joins {
builder.WriteByte(' ')
join.Build(builder)
}
}
// MergeClause merge from clause
func (from From) MergeClause(clause *Clause) {
clause.Expression = from
}
================================================
FILE: clause/from_test.go
================================================
package clause_test
import (
"fmt"
"testing"
"gorm.io/gorm/clause"
)
func TestFrom(t *testing.T) {
results := []struct {
Clauses []clause.Interface
Result string
Vars []interface{}
}{
{
[]clause.Interface{clause.Select{}, clause.From{}},
"SELECT * FROM `users`", nil,
},
{
[]clause.Interface{
clause.Select{}, clause.From{
Tables: []clause.Table{{Name: "users"}},
Joins: []clause.Join{
{
Type: clause.InnerJoin,
Table: clause.Table{Name: "articles"},
ON: clause.Where{
[]clause.Expression{clause.Eq{clause.Column{Table: "articles", Name: "id"}, clause.PrimaryColumn}},
},
},
},
},
},
"SELECT * FROM `users` INNER JOIN `articles` ON `articles`.`id` = `users`.`id`", nil,
},
{
[]clause.Interface{
clause.Select{}, clause.From{
Tables: []clause.Table{{Name: "users"}},
Joins: []clause.Join{
{
Type: clause.RightJoin,
Table: clause.Table{Name: "profiles"},
ON: clause.Where{
[]clause.Expression{clause.Eq{clause.Column{Table: "profiles", Name: "email"}, clause.Column{Table: clause.CurrentTable, Name: "email"}}},
},
},
},
}, clause.From{
Joins: []clause.Join{
{
Type: clause.InnerJoin,
Table: clause.Table{Name: "articles"},
ON: clause.Where{
[]clause.Expression{clause.Eq{clause.Column{Table: "articles", Name: "id"}, clause.PrimaryColumn}},
},
}, {
Type: clause.LeftJoin,
Table: clause.Table{Name: "companies"},
Using: []string{"company_name"},
},
},
},
},
"SELECT * FROM `users` INNER JOIN `articles` ON `articles`.`id` = `users`.`id` LEFT JOIN `companies` USING (`company_name`)", nil,
},
}
for idx, result := range results {
t.Run(fmt.Sprintf("case #%v", idx), func(t *testing.T) {
checkBuildClauses(t, result.Clauses, result.Result, result.Vars)
})
}
}
================================================
FILE: clause/group_by.go
================================================
package clause
// GroupBy group by clause
type GroupBy struct {
Columns []Column
Having []Expression
}
// Name from clause name
func (groupBy GroupBy) Name() string {
return "GROUP BY"
}
// Build build group by clause
func (groupBy GroupBy) Build(builder Builder) {
for idx, column := range groupBy.Columns {
if idx > 0 {
builder.WriteByte(',')
}
builder.WriteQuoted(column)
}
if len(groupBy.Having) > 0 {
builder.WriteString(" HAVING ")
Where{Exprs: groupBy.Having}.Build(builder)
}
}
// MergeClause merge group by clause
func (groupBy GroupBy) MergeClause(clause *Clause) {
if v, ok := clause.Expression.(GroupBy); ok {
copiedColumns := make([]Column, len(v.Columns))
copy(copiedColumns, v.Columns)
groupBy.Columns = append(copiedColumns, groupBy.Columns...)
copiedHaving := make([]Expression, len(v.Having))
copy(copiedHaving, v.Having)
groupBy.Having = append(copiedHaving, groupBy.Having...)
}
clause.Expression = groupBy
if len(groupBy.Columns) == 0 {
clause.Name = ""
} else {
clause.Name = groupBy.Name()
}
}
================================================
FILE: clause/group_by_test.go
================================================
package clause_test
import (
"fmt"
"testing"
"gorm.io/gorm/clause"
)
func TestGroupBy(t *testing.T) {
results := []struct {
Clauses []clause.Interface
Result string
Vars []interface{}
}{
{
[]clause.Interface{clause.Select{}, clause.From{}, clause.GroupBy{
Columns: []clause.Column{{Name: "role"}},
Having: []clause.Expression{clause.Eq{"role", "admin"}},
}},
"SELECT * FROM `users` GROUP BY `role` HAVING `role` = ?",
[]interface{}{"admin"},
},
{
[]clause.Interface{clause.Select{}, clause.From{}, clause.GroupBy{
Columns: []clause.Column{{Name: "role"}},
Having: []clause.Expression{clause.Eq{"role", "admin"}},
}, clause.GroupBy{
Columns: []clause.Column{{Name: "gender"}},
Having: []clause.Expression{clause.Neq{"gender", "U"}},
}},
"SELECT * FROM `users` GROUP BY `role`,`gender` HAVING `role` = ? AND `gender` <> ?",
[]interface{}{"admin", "U"},
},
}
for idx, result := range results {
t.Run(fmt.Sprintf("case #%v", idx), func(t *testing.T) {
checkBuildClauses(t, result.Clauses, result.Result, result.Vars)
})
}
}
================================================
FILE: clause/insert.go
================================================
package clause
type Insert struct {
Table Table
Modifier string
}
// Name insert clause name
func (insert Insert) Name() string {
return "INSERT"
}
// Build build insert clause
func (insert Insert) Build(builder Builder) {
if insert.Modifier != "" {
builder.WriteString(insert.Modifier)
builder.WriteByte(' ')
}
builder.WriteString("INTO ")
if insert.Table.Name == "" {
builder.WriteQuoted(currentTable)
} else {
builder.WriteQuoted(insert.Table)
}
}
// MergeClause merge insert clause
func (insert Insert) MergeClause(clause *Clause) {
if v, ok := clause.Expression.(Insert); ok {
if insert.Modifier == "" {
insert.Modifier = v.Modifier
}
if insert.Table.Name == "" {
insert.Table = v.Table
}
}
clause.Expression = insert
}
================================================
FILE: clause/insert_test.go
================================================
package clause_test
import (
"fmt"
"testing"
"gorm.io/gorm/clause"
)
func TestInsert(t *testing.T) {
results := []struct {
Clauses []clause.Interface
Result string
Vars []interface{}
}{
{
[]clause.Interface{clause.Insert{}},
"INSERT INTO `users`", nil,
},
{
[]clause.Interface{clause.Insert{Modifier: "LOW_PRIORITY"}},
"INSERT LOW_PRIORITY INTO `users`", nil,
},
{
[]clause.Interface{clause.Insert{Table: clause.Table{Name: "products"}, Modifier: "LOW_PRIORITY"}},
"INSERT LOW_PRIORITY INTO `products`", nil,
},
}
for idx, result := range results {
t.Run(fmt.Sprintf("case #%v", idx), func(t *testing.T) {
checkBuildClauses(t, result.Clauses, result.Result, result.Vars)
})
}
}
================================================
FILE: clause/joins.go
================================================
package clause
import "gorm.io/gorm/utils"
type JoinType string
const (
CrossJoin JoinType = "CROSS"
InnerJoin JoinType = "INNER"
LeftJoin JoinType = "LEFT"
RightJoin JoinType = "RIGHT"
)
type JoinTarget struct {
Type JoinType
Association string
Subquery Expression
Table string
}
func Has(name string) JoinTarget {
return JoinTarget{Type: InnerJoin, Association: name}
}
func (jt JoinType) Association(name string) JoinTarget {
return JoinTarget{Type: jt, Association: name}
}
func (jt JoinType) AssociationFrom(name string, subquery Expression) JoinTarget {
return JoinTarget{Type: jt, Association: name, Subquery: subquery}
}
func (jt JoinTarget) As(name string) JoinTarget {
jt.Table = name
return jt
}
// Join clause for from
type Join struct {
Type JoinType
Table Table
ON Where
Using []string
Expression Expression
}
func JoinTable(names ...string) Table {
return Table{
Name: utils.JoinNestedRelationNames(names),
}
}
func (join Join) Build(builder Builder) {
if join.Expression != nil {
join.Expression.Build(builder)
} else {
if join.Type != "" {
builder.WriteString(string(join.Type))
builder.WriteByte(' ')
}
builder.WriteString("JOIN ")
builder.WriteQuoted(join.Table)
if len(join.ON.Exprs) > 0 {
builder.WriteString(" ON ")
join.ON.Build(builder)
} else if len(join.Using) > 0 {
builder.WriteString(" USING (")
for idx, c := range join.Using {
if idx > 0 {
builder.WriteByte(',')
}
builder.WriteQuoted(c)
}
builder.WriteByte(')')
}
}
}
================================================
FILE: clause/joins_test.go
================================================
package clause_test
import (
"sync"
"testing"
"gorm.io/gorm"
"gorm.io/gorm/clause"
"gorm.io/gorm/schema"
"gorm.io/gorm/utils/tests"
)
func TestJoin(t *testing.T) {
results := []struct {
name string
join clause.Join
sql string
}{
{
name: "LEFT JOIN",
join: clause.Join{
Type: clause.LeftJoin,
Table: clause.Table{Name: "user"},
ON: clause.Where{
Exprs: []clause.Expression{clause.Eq{clause.Column{Table: "user_info", Name: "user_id"}, clause.PrimaryColumn}},
},
},
sql: "LEFT JOIN `user` ON `user_info`.`user_id` = `users`.`id`",
},
{
name: "RIGHT JOIN",
join: clause.Join{
Type: clause.RightJoin,
Table: clause.Table{Name: "user"},
ON: clause.Where{
Exprs: []clause.Expression{clause.Eq{clause.Column{Table: "user_info", Name: "user_id"}, clause.PrimaryColumn}},
},
},
sql: "RIGHT JOIN `user` ON `user_info`.`user_id` = `users`.`id`",
},
{
name: "INNER JOIN",
join: clause.Join{
Type: clause.InnerJoin,
Table: clause.Table{Name: "user"},
ON: clause.Where{
Exprs: []clause.Expression{clause.Eq{clause.Column{Table: "user_info", Name: "user_id"}, clause.PrimaryColumn}},
},
},
sql: "INNER JOIN `user` ON `user_info`.`user_id` = `users`.`id`",
},
{
name: "CROSS JOIN",
join: clause.Join{
Type: clause.CrossJoin,
Table: clause.Table{Name: "user"},
ON: clause.Where{
Exprs: []clause.Expression{clause.Eq{clause.Column{Table: "user_info", Name: "user_id"}, clause.PrimaryColumn}},
},
},
sql: "CROSS JOIN `user` ON `user_info`.`user_id` = `users`.`id`",
},
{
name: "USING",
join: clause.Join{
Type: clause.InnerJoin,
Table: clause.Table{Name: "user"},
Using: []string{"id"},
},
sql: "INNER JOIN `user` USING (`id`)",
},
{
name: "Expression",
join: clause.Join{
// Invalid
Type: clause.LeftJoin,
Table: clause.Table{Name: "user"},
ON: clause.Where{
Exprs: []clause.Expression{clause.Eq{clause.Column{Table: "user_info", Name: "user_id"}, clause.PrimaryColumn}},
},
// Valid
Expression: clause.Join{
Type: clause.InnerJoin,
Table: clause.Table{Name: "user"},
Using: []string{"id"},
},
},
sql: "INNER JOIN `user` USING (`id`)",
},
}
for _, result := range results {
t.Run(result.name, func(t *testing.T) {
user, _ := schema.Parse(&tests.User{}, &sync.Map{}, db.NamingStrategy)
stmt := &gorm.Statement{DB: db, Table: user.Table, Schema: user, Clauses: map[string]clause.Clause{}}
result.join.Build(stmt)
if result.sql != stmt.SQL.String() {
t.Errorf("want: %s, got: %s", result.sql, stmt.SQL.String())
}
})
}
}
================================================
FILE: clause/limit.go
================================================
package clause
// Limit limit clause
type Limit struct {
Limit *int
Offset int
}
// Name where clause name
func (limit Limit) Name() string {
return "LIMIT"
}
// Build build where clause
func (limit Limit) Build(builder Builder) {
if limit.Limit != nil && *limit.Limit >= 0 {
builder.WriteString("LIMIT ")
builder.AddVar(builder, *limit.Limit)
}
if limit.Offset > 0 {
if limit.Limit != nil && *limit.Limit >= 0 {
builder.WriteByte(' ')
}
builder.WriteString("OFFSET ")
builder.AddVar(builder, limit.Offset)
}
}
// MergeClause merge order by clauses
func (limit Limit) MergeClause(clause *Clause) {
clause.Name = ""
if v, ok := clause.Expression.(Limit); ok {
if (limit.Limit == nil || *limit.Limit == 0) && v.Limit != nil {
limit.Limit = v.Limit
}
if limit.Offset == 0 && v.Offset > 0 {
limit.Offset = v.Offset
} else if limit.Offset < 0 {
limit.Offset = 0
}
}
clause.Expression = limit
}
================================================
FILE: clause/limit_test.go
================================================
package clause_test
import (
"fmt"
"testing"
"gorm.io/gorm/clause"
)
fun
gitextract_lfhwtntz/
├── .github/
│ ├── FUNDING.yml
│ ├── dependabot.yml
│ ├── labels.json
│ ├── release-drafter.yml
│ └── workflows/
│ ├── create-release.yml
│ ├── golangci-lint.yml
│ ├── invalid_question.yml
│ ├── labeler.yml
│ ├── missing_playground.yml
│ ├── stale.yml
│ └── tests.yml
├── .gitignore
├── .golangci.yml
├── CODE_OF_CONDUCT.md
├── LICENSE
├── README.md
├── association.go
├── callbacks/
│ ├── associations.go
│ ├── callbacks.go
│ ├── callmethod.go
│ ├── create.go
│ ├── create_test.go
│ ├── delete.go
│ ├── helper.go
│ ├── helper_test.go
│ ├── interfaces.go
│ ├── preload.go
│ ├── query.go
│ ├── raw.go
│ ├── row.go
│ ├── transaction.go
│ └── update.go
├── callbacks.go
├── chainable_api.go
├── clause/
│ ├── association.go
│ ├── benchmarks_test.go
│ ├── clause.go
│ ├── clause_test.go
│ ├── delete.go
│ ├── delete_test.go
│ ├── expression.go
│ ├── expression_test.go
│ ├── from.go
│ ├── from_test.go
│ ├── group_by.go
│ ├── group_by_test.go
│ ├── insert.go
│ ├── insert_test.go
│ ├── joins.go
│ ├── joins_test.go
│ ├── limit.go
│ ├── limit_test.go
│ ├── locking.go
│ ├── locking_test.go
│ ├── on_conflict.go
│ ├── order_by.go
│ ├── order_by_test.go
│ ├── returning.go
│ ├── returning_test.go
│ ├── select.go
│ ├── select_test.go
│ ├── set.go
│ ├── set_test.go
│ ├── update.go
│ ├── update_test.go
│ ├── values.go
│ ├── values_test.go
│ ├── where.go
│ ├── where_test.go
│ └── with.go
├── errors.go
├── finisher_api.go
├── generics.go
├── go.mod
├── go.sum
├── gorm.go
├── interfaces.go
├── internal/
│ ├── lru/
│ │ └── lru.go
│ └── stmt_store/
│ └── stmt_store.go
├── logger/
│ ├── logger.go
│ ├── slog.go
│ ├── slog_test.go
│ ├── sql.go
│ └── sql_test.go
├── migrator/
│ ├── column_type.go
│ ├── index.go
│ ├── migrator.go
│ └── table_type.go
├── migrator.go
├── model.go
├── prepare_stmt.go
├── scan.go
├── schema/
│ ├── callbacks_test.go
│ ├── constraint.go
│ ├── constraint_test.go
│ ├── field.go
│ ├── field_test.go
│ ├── index.go
│ ├── index_test.go
│ ├── interfaces.go
│ ├── model_test.go
│ ├── naming.go
│ ├── naming_test.go
│ ├── pool.go
│ ├── relationship.go
│ ├── relationship_test.go
│ ├── schema.go
│ ├── schema_helper_test.go
│ ├── schema_test.go
│ ├── serializer.go
│ ├── serializer_test.go
│ ├── utils.go
│ └── utils_test.go
├── soft_delete.go
├── statement.go
├── statement_test.go
├── tests/
│ ├── .gitignore
│ ├── README.md
│ ├── association_generics_test.go
│ ├── associations_belongs_to_test.go
│ ├── associations_has_many_test.go
│ ├── associations_has_one_test.go
│ ├── associations_many2many_test.go
│ ├── associations_test.go
│ ├── benchmark_test.go
│ ├── callbacks_test.go
│ ├── chainable_api_test.go
│ ├── compose.yml
│ ├── connection_test.go
│ ├── connpool_test.go
│ ├── count_test.go
│ ├── create_test.go
│ ├── customize_field_test.go
│ ├── default_value_test.go
│ ├── delete_test.go
│ ├── distinct_test.go
│ ├── embedded_struct_test.go
│ ├── error_translator_test.go
│ ├── gaussdb_test.go
│ ├── generics_test.go
│ ├── go.mod
│ ├── gorm_test.go
│ ├── group_by_test.go
│ ├── helper_test.go
│ ├── hooks_test.go
│ ├── joins_table_test.go
│ ├── joins_test.go
│ ├── lru_test.go
│ ├── main_test.go
│ ├── migrate_test.go
│ ├── multi_primary_keys_test.go
│ ├── named_argument_test.go
│ ├── named_polymorphic_test.go
│ ├── non_std_test.go
│ ├── postgres_test.go
│ ├── preload_suits_test.go
│ ├── preload_test.go
│ ├── prepared_stmt_test.go
│ ├── query_test.go
│ ├── scan_test.go
│ ├── scanner_valuer_test.go
│ ├── scopes_test.go
│ ├── serializer_test.go
│ ├── soft_delete_test.go
│ ├── sql_builder_test.go
│ ├── submodel_test.go
│ ├── table_test.go
│ ├── tests_all.sh
│ ├── tests_test.go
│ ├── tracer_test.go
│ ├── transaction_test.go
│ ├── update_belongs_to_test.go
│ ├── update_has_many_test.go
│ ├── update_has_one_test.go
│ ├── update_many2many_test.go
│ ├── update_test.go
│ └── upsert_test.go
└── utils/
├── tests/
│ ├── dummy_dialecter.go
│ ├── models.go
│ └── utils.go
├── utils.go
├── utils_test.go
├── utils_unix_test.go
└── utils_windows_test.go
SYMBOL INDEX (1607 symbols across 159 files)
FILE: association.go
type Association (line 14) | type Association struct
method Unscoped (line 42) | func (association *Association) Unscoped() *Association {
method Find (line 51) | func (association *Association) Find(out interface{}, conds ...interfa...
method Append (line 58) | func (association *Association) Append(values ...interface{}) error {
method Replace (line 75) | func (association *Association) Replace(values ...interface{}) error {
method Delete (line 199) | func (association *Association) Delete(values ...interface{}) error {
method Clear (line 367) | func (association *Association) Clear() error {
method Count (line 371) | func (association *Association) Count() (count int64) {
method saveAssociation (line 384) | func (association *Association) saveAssociation(clear bool, values ......
method buildCondition (line 608) | func (association *Association) buildCondition() *DB {
method Association (line 21) | func (db *DB) Association(column string) *Association {
type assignBack (line 378) | type assignBack struct
function expandValues (line 638) | func expandValues(values ...any) (results []any) {
FILE: callbacks.go
function initializeCallbacks (line 15) | func initializeCallbacks(db *DB) *callbacks {
type callbacks (line 29) | type callbacks struct
method Create (line 51) | func (cs *callbacks) Create() *processor {
method Query (line 55) | func (cs *callbacks) Query() *processor {
method Update (line 59) | func (cs *callbacks) Update() *processor {
method Delete (line 63) | func (cs *callbacks) Delete() *processor {
method Row (line 67) | func (cs *callbacks) Row() *processor {
method Raw (line 71) | func (cs *callbacks) Raw() *processor {
type processor (line 33) | type processor struct
method Execute (line 75) | func (p *processor) Execute(db *DB) *DB {
method Get (line 161) | func (p *processor) Get(name string) func(*DB) {
method Before (line 170) | func (p *processor) Before(name string) *callback {
method After (line 174) | func (p *processor) After(name string) *callback {
method Match (line 178) | func (p *processor) Match(fc func(*DB) bool) *callback {
method Register (line 182) | func (p *processor) Register(name string, fn func(*DB)) error {
method Remove (line 186) | func (p *processor) Remove(name string) error {
method Replace (line 190) | func (p *processor) Replace(name string, fn func(*DB)) error {
method compile (line 194) | func (p *processor) compile() (err error) {
type callback (line 40) | type callback struct
method Before (line 217) | func (c *callback) Before(name string) *callback {
method After (line 222) | func (c *callback) After(name string) *callback {
method Register (line 227) | func (c *callback) Register(name string, fn func(*DB)) error {
method Remove (line 234) | func (c *callback) Remove(name string) error {
method Replace (line 242) | func (c *callback) Replace(name string, fn func(*DB)) error {
function getRIndex (line 252) | func getRIndex(strs []string, str string) int {
function sortCallbacks (line 261) | func sortCallbacks(cs []*callback) (fns []func(*DB), err error) {
function removeCallbacks (line 357) | func removeCallbacks(cs []*callback, nameMap map[string]bool) []*callback {
FILE: callbacks/associations.go
function SaveBeforeAssociations (line 13) | func SaveBeforeAssociations(create bool) func(db *gorm.DB) {
function SaveAfterAssociations (line 110) | func SaveAfterAssociations(create bool) func(db *gorm.DB) {
function onConflictOption (line 360) | func onConflictOption(stmt *gorm.Statement, s *schema.Schema, defaultUpd...
function saveAssociations (line 378) | func saveAssociations(db *gorm.DB, rel *schema.Relationship, rValues ref...
function checkAssociationsSaved (line 439) | func checkAssociationsSaved(db *gorm.DB, values reflect.Value) bool {
FILE: callbacks/callbacks.go
type Config (line 14) | type Config struct
function RegisterDefaultCallbacks (line 22) | func RegisterDefaultCallbacks(db *gorm.DB, config *Config) {
FILE: callbacks/callmethod.go
function callMethod (line 9) | func callMethod(db *gorm.DB, fc func(value interface{}, tx *gorm.DB) boo...
FILE: callbacks/create.go
function BeforeCreate (line 15) | func BeforeCreate(db *gorm.DB) {
function Create (line 37) | func Create(config *Config) func(db *gorm.DB) {
function AfterCreate (line 223) | func AfterCreate(db *gorm.DB) {
function ConvertToCreateValues (line 245) | func ConvertToCreateValues(stmt *gorm.Statement) (values clause.Values) {
FILE: callbacks/create_test.go
function TestConvertToCreateValues_DestType_Slice (line 16) | func TestConvertToCreateValues_DestType_Slice(t *testing.T) {
FILE: callbacks/delete.go
function BeforeDelete (line 13) | func BeforeDelete(db *gorm.DB) {
function DeleteBeforeAssociations (line 26) | func DeleteBeforeAssociations(db *gorm.DB) {
function Delete (line 113) | func Delete(config *Config) func(db *gorm.DB) {
function AfterDelete (line 185) | func AfterDelete(db *gorm.DB) {
FILE: callbacks/helper.go
function ConvertMapToValuesForCreate (line 12) | func ConvertMapToValuesForCreate(stmt *gorm.Statement, mapValue map[stri...
function ConvertSliceOfMapToValuesForCreate (line 43) | func ConvertSliceOfMapToValuesForCreate(stmt *gorm.Statement, mapValues ...
function hasReturning (line 96) | func hasReturning(tx *gorm.DB, supportReturning bool) (bool, gorm.ScanMo...
function checkMissingWhereConditions (line 109) | func checkMissingWhereConditions(db *gorm.DB) {
function loadOrStoreVisitMap (line 128) | func loadOrStoreVisitMap(visitMap *visitMap, v reflect.Value) (loaded bo...
FILE: callbacks/helper_test.go
function TestLoadOrStoreVisitMap (line 11) | func TestLoadOrStoreVisitMap(t *testing.T) {
function TestConvertMapToValuesForCreate (line 41) | func TestConvertMapToValuesForCreate(t *testing.T) {
function TestConvertSliceOfMapToValuesForCreate (line 99) | func TestConvertSliceOfMapToValuesForCreate(t *testing.T) {
FILE: callbacks/interfaces.go
type BeforeCreateInterface (line 5) | type BeforeCreateInterface interface
type AfterCreateInterface (line 9) | type AfterCreateInterface interface
type BeforeUpdateInterface (line 13) | type BeforeUpdateInterface interface
type AfterUpdateInterface (line 17) | type AfterUpdateInterface interface
type BeforeSaveInterface (line 21) | type BeforeSaveInterface interface
type AfterSaveInterface (line 25) | type AfterSaveInterface interface
type BeforeDeleteInterface (line 29) | type BeforeDeleteInterface interface
type AfterDeleteInterface (line 33) | type AfterDeleteInterface interface
type AfterFindInterface (line 37) | type AfterFindInterface interface
FILE: callbacks/preload.go
function parsePreloadMap (line 38) | func parsePreloadMap(s *schema.Schema, preloads map[string][]interface{}...
function embeddedValues (line 71) | func embeddedValues(embeddedRelations *schema.Relationships) []string {
function preloadEntryPoint (line 90) | func preloadEntryPoint(db *gorm.DB, joins []string, relationships *schem...
function preloadDB (line 169) | func preloadDB(db *gorm.DB, reflectValue reflect.Value, dest interface{}...
function preload (line 185) | func preload(tx *gorm.DB, rel *schema.Relationship, conds []interface{},...
FILE: callbacks/query.go
function Query (line 14) | func Query(db *gorm.DB) {
function BuildQuerySQL (line 36) | func BuildQuerySQL(db *gorm.DB) {
function Preload (line 277) | func Preload(db *gorm.DB) {
function AfterQuery (line 298) | func AfterQuery(db *gorm.DB) {
FILE: callbacks/raw.go
function RawExec (line 7) | func RawExec(db *gorm.DB) {
FILE: callbacks/row.go
function RowQuery (line 7) | func RowQuery(db *gorm.DB) {
FILE: callbacks/transaction.go
function BeginTransaction (line 7) | func BeginTransaction(db *gorm.DB) {
function CommitOrRollbackTransaction (line 20) | func CommitOrRollbackTransaction(db *gorm.DB) {
FILE: callbacks/update.go
function SetupUpdateReflectValue (line 13) | func SetupUpdateReflectValue(db *gorm.DB) {
function BeforeUpdate (line 33) | func BeforeUpdate(db *gorm.DB) {
function Update (line 56) | func Update(config *Config) func(db *gorm.DB) {
function AfterUpdate (line 117) | func AfterUpdate(db *gorm.DB) {
function ConvertToAssignments (line 140) | func ConvertToAssignments(stmt *gorm.Statement) (set clause.Set) {
FILE: chainable_api.go
method Model (line 18) | func (db *DB) Model(value interface{}) (tx *DB) {
method Clauses (line 38) | func (db *DB) Clauses(conds ...clause.Expression) (tx *DB) {
method Table (line 64) | func (db *DB) Table(name string, args ...interface{}) (tx *DB) {
method Distinct (line 94) | func (db *DB) Distinct(args ...interface{}) (tx *DB) {
method Select (line 112) | func (db *DB) Select(query interface{}, args ...interface{}) (tx *DB) {
method Omit (line 177) | func (db *DB) Omit(columns ...string) (tx *DB) {
method MapColumns (line 189) | func (db *DB) MapColumns(m map[string]string) (tx *DB) {
method Where (line 207) | func (db *DB) Where(query interface{}, args ...interface{}) (tx *DB) {
method Not (line 221) | func (db *DB) Not(query interface{}, args ...interface{}) (tx *DB) {
method Or (line 235) | func (db *DB) Or(query interface{}, args ...interface{}) (tx *DB) {
method Joins (line 248) | func (db *DB) Joins(query string, args ...interface{}) (tx *DB) {
method InnerJoins (line 254) | func (db *DB) InnerJoins(query string, args ...interface{}) (tx *DB) {
function joins (line 258) | func joins(db *DB, joinType clause.JoinType, query string, args ...inter...
method Group (line 283) | func (db *DB) Group(name string) (tx *DB) {
method Having (line 297) | func (db *DB) Having(query interface{}, args ...interface{}) (tx *DB) {
method Order (line 313) | func (db *DB) Order(value interface{}) (tx *DB) {
method Limit (line 343) | func (db *DB) Limit(limit int) (tx *DB) {
method Offset (line 357) | func (db *DB) Offset(offset int) (tx *DB) {
method Scopes (line 376) | func (db *DB) Scopes(funcs ...func(*DB) *DB) (tx *DB) {
method executeScopes (line 382) | func (db *DB) executeScopes() (tx *DB) {
method Preload (line 395) | func (db *DB) Preload(query string, args ...interface{}) (tx *DB) {
method Attrs (line 418) | func (db *DB) Attrs(attrs ...interface{}) (tx *DB) {
method Assign (line 439) | func (db *DB) Assign(attrs ...interface{}) (tx *DB) {
method Unscoped (line 455) | func (db *DB) Unscoped() (tx *DB) {
method Raw (line 461) | func (db *DB) Raw(sql string, values ...interface{}) (tx *DB) {
FILE: clause/association.go
type AssociationOpType (line 4) | type AssociationOpType
constant OpUnlink (line 7) | OpUnlink AssociationOpType = iota
constant OpDelete (line 8) | OpDelete
constant OpUpdate (line 9) | OpUpdate
constant OpCreate (line 10) | OpCreate
type Association (line 14) | type Association struct
method Assignments (line 28) | func (ao Association) Assignments() []Assignment {
method AssociationAssignments (line 33) | func (ao Association) AssociationAssignments() []Association {
type AssociationAssigner (line 23) | type AssociationAssigner interface
FILE: clause/benchmarks_test.go
function BenchmarkSelect (line 13) | func BenchmarkSelect(b *testing.B) {
function BenchmarkComplexSelect (line 29) | func BenchmarkComplexSelect(b *testing.B) {
FILE: clause/clause.go
type Interface (line 4) | type Interface interface
type ClauseBuilder (line 11) | type ClauseBuilder
type Writer (line 13) | type Writer interface
type Builder (line 19) | type Builder interface
type Clause (line 27) | type Clause struct
method Build (line 37) | func (c Clause) Build(builder Builder) {
constant PrimaryKey (line 66) | PrimaryKey string = "~~~py~~~"
constant CurrentTable (line 67) | CurrentTable string = "~~~ct~~~"
constant Associations (line 68) | Associations string = "~~~as~~~"
type Column (line 77) | type Column struct
type Table (line 85) | type Table struct
FILE: clause/clause_test.go
function checkBuildClauses (line 17) | func checkBuildClauses(t *testing.T, clauses []clause.Interface, result ...
FILE: clause/delete.go
type Delete (line 3) | type Delete struct
method Name (line 7) | func (d Delete) Name() string {
method Build (line 11) | func (d Delete) Build(builder Builder) {
method MergeClause (line 20) | func (d Delete) MergeClause(clause *Clause) {
FILE: clause/delete_test.go
function TestDelete (line 10) | func TestDelete(t *testing.T) {
FILE: clause/expression.go
type Expression (line 11) | type Expression interface
type NegationExpressionBuilder (line 16) | type NegationExpressionBuilder interface
type Expr (line 21) | type Expr struct
method Build (line 28) | func (expr Expr) Build(builder Builder) {
type NamedExpr (line 61) | type NamedExpr struct
method Build (line 67) | func (expr NamedExpr) Build(builder Builder) {
function processValue (line 161) | func processValue(builder Builder, value interface{}) {
type IN (line 185) | type IN struct
method Build (line 190) | func (in IN) Build(builder Builder) {
method NegationBuild (line 211) | func (in IN) NegationBuild(builder Builder) {
type Eq (line 232) | type Eq struct
method Build (line 237) | func (eq Eq) Build(builder Builder) {
method NegationBuild (line 265) | func (eq Eq) NegationBuild(builder Builder) {
type Neq (line 270) | type Neq
method Build (line 272) | func (neq Neq) Build(builder Builder) {
method NegationBuild (line 296) | func (neq Neq) NegationBuild(builder Builder) {
type Gt (line 301) | type Gt
method Build (line 303) | func (gt Gt) Build(builder Builder) {
method NegationBuild (line 309) | func (gt Gt) NegationBuild(builder Builder) {
type Gte (line 314) | type Gte
method Build (line 316) | func (gte Gte) Build(builder Builder) {
method NegationBuild (line 322) | func (gte Gte) NegationBuild(builder Builder) {
type Lt (line 327) | type Lt
method Build (line 329) | func (lt Lt) Build(builder Builder) {
method NegationBuild (line 335) | func (lt Lt) NegationBuild(builder Builder) {
type Lte (line 340) | type Lte
method Build (line 342) | func (lte Lte) Build(builder Builder) {
method NegationBuild (line 348) | func (lte Lte) NegationBuild(builder Builder) {
type Like (line 353) | type Like
method Build (line 355) | func (like Like) Build(builder Builder) {
method NegationBuild (line 361) | func (like Like) NegationBuild(builder Builder) {
function eqNil (line 367) | func eqNil(value interface{}) bool {
function eqNilReflect (line 375) | func eqNilReflect(value interface{}) bool {
FILE: clause/expression_test.go
function TestExpr (line 16) | func TestExpr(t *testing.T) {
function TestNamedExpr (line 39) | func TestNamedExpr(t *testing.T) {
function TestExpression (line 163) | func TestExpression(t *testing.T) {
FILE: clause/from.go
type From (line 4) | type From struct
method Name (line 10) | func (from From) Name() string {
method Build (line 15) | func (from From) Build(builder Builder) {
method MergeClause (line 35) | func (from From) MergeClause(clause *Clause) {
FILE: clause/from_test.go
function TestFrom (line 10) | func TestFrom(t *testing.T) {
FILE: clause/group_by.go
type GroupBy (line 4) | type GroupBy struct
method Name (line 10) | func (groupBy GroupBy) Name() string {
method Build (line 15) | func (groupBy GroupBy) Build(builder Builder) {
method MergeClause (line 31) | func (groupBy GroupBy) MergeClause(clause *Clause) {
FILE: clause/group_by_test.go
function TestGroupBy (line 10) | func TestGroupBy(t *testing.T) {
FILE: clause/insert.go
type Insert (line 3) | type Insert struct
method Name (line 9) | func (insert Insert) Name() string {
method Build (line 14) | func (insert Insert) Build(builder Builder) {
method MergeClause (line 29) | func (insert Insert) MergeClause(clause *Clause) {
FILE: clause/insert_test.go
function TestInsert (line 10) | func TestInsert(t *testing.T) {
FILE: clause/joins.go
type JoinType (line 5) | type JoinType
method Association (line 25) | func (jt JoinType) Association(name string) JoinTarget {
method AssociationFrom (line 29) | func (jt JoinType) AssociationFrom(name string, subquery Expression) J...
constant CrossJoin (line 8) | CrossJoin JoinType = "CROSS"
constant InnerJoin (line 9) | InnerJoin JoinType = "INNER"
constant LeftJoin (line 10) | LeftJoin JoinType = "LEFT"
constant RightJoin (line 11) | RightJoin JoinType = "RIGHT"
type JoinTarget (line 14) | type JoinTarget struct
method As (line 33) | func (jt JoinTarget) As(name string) JoinTarget {
function Has (line 21) | func Has(name string) JoinTarget {
type Join (line 39) | type Join struct
method Build (line 53) | func (join Join) Build(builder Builder) {
function JoinTable (line 47) | func JoinTable(names ...string) Table {
FILE: clause/joins_test.go
function TestJoin (line 13) | func TestJoin(t *testing.T) {
FILE: clause/limit.go
type Limit (line 4) | type Limit struct
method Name (line 10) | func (limit Limit) Name() string {
method Build (line 15) | func (limit Limit) Build(builder Builder) {
method MergeClause (line 30) | func (limit Limit) MergeClause(clause *Clause) {
FILE: clause/limit_test.go
function TestLimit (line 10) | func TestLimit(t *testing.T) {
FILE: clause/locking.go
constant LockingStrengthUpdate (line 4) | LockingStrengthUpdate = "UPDATE"
constant LockingStrengthShare (line 5) | LockingStrengthShare = "SHARE"
constant LockingOptionsSkipLocked (line 6) | LockingOptionsSkipLocked = "SKIP LOCKED"
constant LockingOptionsNoWait (line 7) | LockingOptionsNoWait = "NOWAIT"
type Locking (line 10) | type Locking struct
method Name (line 17) | func (locking Locking) Name() string {
method Build (line 22) | func (locking Locking) Build(builder Builder) {
method MergeClause (line 36) | func (locking Locking) MergeClause(clause *Clause) {
FILE: clause/locking_test.go
function TestLocking (line 10) | func TestLocking(t *testing.T) {
FILE: clause/on_conflict.go
type OnConflict (line 3) | type OnConflict struct
method Name (line 13) | func (OnConflict) Name() string {
method Build (line 18) | func (onConflict OnConflict) Build(builder Builder) {
method MergeClause (line 57) | func (onConflict OnConflict) MergeClause(clause *Clause) {
FILE: clause/order_by.go
type OrderByColumn (line 3) | type OrderByColumn struct
type OrderBy (line 9) | type OrderBy struct
method Name (line 15) | func (orderBy OrderBy) Name() string {
method Build (line 20) | func (orderBy OrderBy) Build(builder Builder) {
method MergeClause (line 38) | func (orderBy OrderBy) MergeClause(clause *Clause) {
FILE: clause/order_by_test.go
function TestOrderBy (line 10) | func TestOrderBy(t *testing.T) {
FILE: clause/returning.go
type Returning (line 3) | type Returning struct
method Name (line 8) | func (returning Returning) Name() string {
method Build (line 13) | func (returning Returning) Build(builder Builder) {
method MergeClause (line 28) | func (returning Returning) MergeClause(clause *Clause) {
FILE: clause/returning_test.go
function TestReturning (line 10) | func TestReturning(t *testing.T) {
FILE: clause/select.go
type Select (line 4) | type Select struct
method Name (line 10) | func (s Select) Name() string {
method Build (line 14) | func (s Select) Build(builder Builder) {
method MergeClause (line 31) | func (s Select) MergeClause(clause *Clause) {
type CommaExpression (line 48) | type CommaExpression struct
method Build (line 52) | func (comma CommaExpression) Build(builder Builder) {
FILE: clause/select_test.go
function TestSelect (line 10) | func TestSelect(t *testing.T) {
FILE: clause/set.go
type Set (line 5) | type Set
method Name (line 17) | func (set Set) Name() string {
method Build (line 21) | func (set Set) Build(builder Builder) {
method MergeClause (line 39) | func (set Set) MergeClause(clause *Clause) {
method Assignments (line 46) | func (set Set) Assignments() []Assignment { return []Assignment(set) }
type Assignment (line 7) | type Assignment struct
method Assignments (line 71) | func (a Assignment) Assignments() []Assignment { return []Assignment{a} }
type Assigner (line 13) | type Assigner interface
function Assignments (line 48) | func Assignments(values map[string]interface{}) Set {
function AssignmentColumns (line 62) | func AssignmentColumns(values []string) Set {
FILE: clause/set_test.go
function TestSet (line 18) | func TestSet(t *testing.T) {
function TestAssignments (line 50) | func TestAssignments(t *testing.T) {
FILE: clause/update.go
type Update (line 3) | type Update struct
method Name (line 9) | func (update Update) Name() string {
method Build (line 14) | func (update Update) Build(builder Builder) {
method MergeClause (line 28) | func (update Update) MergeClause(clause *Clause) {
FILE: clause/update_test.go
function TestUpdate (line 10) | func TestUpdate(t *testing.T) {
FILE: clause/values.go
type Values (line 3) | type Values struct
method Name (line 9) | func (Values) Name() string {
method Build (line 14) | func (values Values) Build(builder Builder) {
method MergeClause (line 42) | func (values Values) MergeClause(clause *Clause) {
FILE: clause/values_test.go
function TestValues (line 10) | func TestValues(t *testing.T) {
FILE: clause/where.go
constant AndWithSpace (line 8) | AndWithSpace = " AND "
constant OrWithSpace (line 9) | OrWithSpace = " OR "
type Where (line 13) | type Where struct
method Name (line 18) | func (where Where) Name() string {
method Build (line 23) | func (where Where) Build(builder Builder) {
method MergeClause (line 92) | func (where Where) MergeClause(clause *Clause) {
function buildExprs (line 43) | func buildExprs(exprs []Expression, builder Builder, joinCond string) {
function And (line 103) | func And(exprs ...Expression) Expression {
type AndConditions (line 117) | type AndConditions struct
method Build (line 121) | func (and AndConditions) Build(builder Builder) {
function Or (line 131) | func Or(exprs ...Expression) Expression {
type OrConditions (line 138) | type OrConditions struct
method Build (line 142) | func (or OrConditions) Build(builder Builder) {
function Not (line 152) | func Not(exprs ...Expression) Expression {
type NotConditions (line 164) | type NotConditions struct
method Build (line 168) | func (not NotConditions) Build(builder Builder) {
FILE: clause/where_test.go
function TestWhere (line 10) | func TestWhere(t *testing.T) {
FILE: clause/with.go
type With (line 3) | type With struct
FILE: finisher_api.go
method Create (line 19) | func (db *DB) Create(value interface{}) (tx *DB) {
method CreateInBatches (line 30) | func (db *DB) CreateInBatches(value interface{}, batchSize int) (tx *DB) {
method Save (line 75) | func (db *DB) Save(value interface{}) (tx *DB) {
method First (line 120) | func (db *DB) First(dest interface{}, conds ...interface{}) (tx *DB) {
method Take (line 135) | func (db *DB) Take(dest interface{}, conds ...interface{}) (tx *DB) {
method Last (line 148) | func (db *DB) Last(dest interface{}, conds ...interface{}) (tx *DB) {
method Find (line 164) | func (db *DB) Find(dest interface{}, conds ...interface{}) (tx *DB) {
method FindInBatches (line 176) | func (db *DB) FindInBatches(dest interface{}, batchSize int, fc func(tx ...
method assignInterfacesToValue (line 248) | func (db *DB) assignInterfacesToValue(values ...interface{}) {
method FirstOrInit (line 309) | func (db *DB) FirstOrInit(dest interface{}, conds ...interface{}) (tx *D...
method FirstOrCreate (line 348) | func (db *DB) FirstOrCreate(dest interface{}, conds ...interface{}) (tx ...
method Update (line 403) | func (db *DB) Update(column string, value interface{}) (tx *DB) {
method Updates (line 410) | func (db *DB) Updates(values interface{}) (tx *DB) {
method UpdateColumn (line 416) | func (db *DB) UpdateColumn(column string, value interface{}) (tx *DB) {
method UpdateColumns (line 423) | func (db *DB) UpdateColumns(values interface{}) (tx *DB) {
method Delete (line 433) | func (db *DB) Delete(value interface{}, conds ...interface{}) (tx *DB) {
method Count (line 444) | func (db *DB) Count(count *int64) (tx *DB) {
method Row (line 506) | func (db *DB) Row() *sql.Row {
method Rows (line 516) | func (db *DB) Rows() (*sql.Rows, error) {
method Scan (line 527) | func (db *DB) Scan(dest interface{}) (tx *DB) {
method Pluck (line 556) | func (db *DB) Pluck(column string, dest interface{}) (tx *DB) {
method ScanRows (line 577) | func (db *DB) ScanRows(rows *sql.Rows, dest interface{}) error {
method Connection (line 598) | func (db *DB) Connection(fc func(tx *DB) error) (err error) {
method Transaction (line 622) | func (db *DB) Transaction(fc func(tx *DB) error, opts ...*sql.TxOptions)...
method Begin (line 665) | func (db *DB) Begin(opts ...*sql.TxOptions) *DB {
method Commit (line 701) | func (db *DB) Commit() *DB {
method Rollback (line 711) | func (db *DB) Rollback() *DB {
method SavePoint (line 722) | func (db *DB) SavePoint(name string) *DB {
method RollbackTo (line 745) | func (db *DB) RollbackTo(name string) *DB {
method Exec (line 769) | func (db *DB) Exec(sql string, values ...interface{}) (tx *DB) {
FILE: generics.go
type result (line 17) | type result struct
method ModifyStatement (line 22) | func (info *result) ModifyStatement(stmt *Statement) {
method Build (line 27) | func (result) Build(clause.Builder) {
function WithResult (line 30) | func WithResult() *result {
type Interface (line 34) | type Interface interface
type CreateInterface (line 40) | type CreateInterface interface
type ChainInterface (line 71) | type ChainInterface interface
type SetUpdateOnlyInterface (line 100) | type SetUpdateOnlyInterface interface
type SetCreateOrUpdateInterface (line 105) | type SetCreateOrUpdateInterface interface
type ExecInterface (line 110) | type ExecInterface interface
type JoinBuilder (line 121) | type JoinBuilder interface
type PreloadBuilder (line 129) | type PreloadBuilder interface
type op (line 141) | type op
function G (line 143) | func G[T any](db *DB, opts ...clause.Expression) Interface[T] {
type g (line 163) | type g struct
method apply (line 169) | func (g *g[T]) apply(ctx context.Context) *DB {
method Raw (line 181) | func (c *g[T]) Raw(sql string, values ...interface{}) ExecInterface[T] {
method Exec (line 191) | func (c *g[T]) Exec(ctx context.Context, sql string, values ...interface...
type createG (line 196) | type createG struct
method Table (line 200) | func (c createG[T]) Table(name string, args ...interface{}) CreateInterf...
method Select (line 206) | func (c createG[T]) Select(query string, args ...interface{}) CreateInte...
method Omit (line 212) | func (c createG[T]) Omit(columns ...string) CreateInterface[T] {
method Set (line 218) | func (c createG[T]) Set(assignments ...clause.Assigner) SetCreateOrUpdat...
method Create (line 222) | func (c createG[T]) Create(ctx context.Context, r *T) error {
method CreateInBatches (line 226) | func (c createG[T]) CreateInBatches(ctx context.Context, r *[]T, batchSi...
type chainG (line 230) | type chainG struct
method getInstance (line 234) | func (c chainG[T]) getInstance() *DB {
method with (line 239) | func (c chainG[T]) with(v op) chainG[T] {
method Table (line 248) | func (c chainG[T]) Table(name string, args ...interface{}) ChainInterfac...
method Scopes (line 254) | func (c chainG[T]) Scopes(scopes ...func(db *Statement)) ChainInterface[...
method Where (line 263) | func (c chainG[T]) Where(query interface{}, args ...interface{}) ChainIn...
method Not (line 269) | func (c chainG[T]) Not(query interface{}, args ...interface{}) ChainInte...
method Or (line 275) | func (c chainG[T]) Or(query interface{}, args ...interface{}) ChainInter...
method Limit (line 281) | func (c chainG[T]) Limit(offset int) ChainInterface[T] {
method Offset (line 287) | func (c chainG[T]) Offset(offset int) ChainInterface[T] {
type joinBuilder (line 293) | type joinBuilder struct
method Where (line 297) | func (q *joinBuilder) Where(query interface{}, args ...interface{}) Jo...
method Or (line 302) | func (q *joinBuilder) Or(query interface{}, args ...interface{}) JoinB...
method Not (line 307) | func (q *joinBuilder) Not(query interface{}, args ...interface{}) Join...
method Select (line 312) | func (q *joinBuilder) Select(columns ...string) JoinBuilder {
method Omit (line 317) | func (q *joinBuilder) Omit(columns ...string) JoinBuilder {
type preloadBuilder (line 322) | type preloadBuilder struct
method Where (line 327) | func (q *preloadBuilder) Where(query interface{}, args ...interface{})...
method Or (line 332) | func (q *preloadBuilder) Or(query interface{}, args ...interface{}) Pr...
method Not (line 337) | func (q *preloadBuilder) Not(query interface{}, args ...interface{}) P...
method Select (line 342) | func (q *preloadBuilder) Select(columns ...string) PreloadBuilder {
method Omit (line 347) | func (q *preloadBuilder) Omit(columns ...string) PreloadBuilder {
method Limit (line 352) | func (q *preloadBuilder) Limit(limit int) PreloadBuilder {
method Offset (line 357) | func (q *preloadBuilder) Offset(offset int) PreloadBuilder {
method Order (line 362) | func (q *preloadBuilder) Order(value interface{}) PreloadBuilder {
method LimitPerRecord (line 367) | func (q *preloadBuilder) LimitPerRecord(num int) PreloadBuilder {
method Joins (line 372) | func (c chainG[T]) Joins(jt clause.JoinTarget, on func(db JoinBuilder, j...
method Select (line 431) | func (c chainG[T]) Select(query string, args ...interface{}) ChainInterf...
method Omit (line 437) | func (c chainG[T]) Omit(columns ...string) ChainInterface[T] {
method MapColumns (line 443) | func (c chainG[T]) MapColumns(m map[string]string) ChainInterface[T] {
method Set (line 449) | func (c chainG[T]) Set(assignments ...clause.Assigner) SetUpdateOnlyInte...
method Distinct (line 453) | func (c chainG[T]) Distinct(args ...interface{}) ChainInterface[T] {
method Group (line 459) | func (c chainG[T]) Group(name string) ChainInterface[T] {
method Having (line 465) | func (c chainG[T]) Having(query interface{}, args ...interface{}) ChainI...
method Order (line 471) | func (c chainG[T]) Order(value interface{}) ChainInterface[T] {
method Preload (line 477) | func (c chainG[T]) Preload(association string, query func(db PreloadBuil...
method Delete (line 560) | func (c chainG[T]) Delete(ctx context.Context) (rowsAffected int, err er...
method Update (line 566) | func (c chainG[T]) Update(ctx context.Context, name string, value any) (...
method Updates (line 572) | func (c chainG[T]) Updates(ctx context.Context, t T) (rowsAffected int, ...
method Count (line 577) | func (c chainG[T]) Count(ctx context.Context, column string) (result int...
method Build (line 583) | func (c chainG[T]) Build(builder clause.Builder) {
type execG (line 620) | type execG struct
method First (line 624) | func (g execG[T]) First(ctx context.Context) (T, error) {
method Scan (line 630) | func (g execG[T]) Scan(ctx context.Context, result interface{}) error {
method Last (line 636) | func (g execG[T]) Last(ctx context.Context) (T, error) {
method Take (line 642) | func (g execG[T]) Take(ctx context.Context) (T, error) {
method Find (line 648) | func (g execG[T]) Find(ctx context.Context) ([]T, error) {
method FindInBatches (line 654) | func (g execG[T]) FindInBatches(ctx context.Context, batchSize int, fc f...
method Row (line 661) | func (g execG[T]) Row(ctx context.Context) *sql.Row {
method Rows (line 666) | func (g execG[T]) Rows(ctx context.Context) (*sql.Rows, error) {
method processSet (line 671) | func (c chainG[T]) processSet(items ...clause.Assigner) setCreateOrUpdat...
type setCreateOrUpdateG (line 695) | type setCreateOrUpdateG struct
method Update (line 701) | func (s setCreateOrUpdateG[T]) Update(ctx context.Context) (rowsAffected...
method Create (line 719) | func (s setCreateOrUpdateG[T]) Create(ctx context.Context) error {
method executeAssociationOperation (line 741) | func (s setCreateOrUpdateG[T]) executeAssociationOperation(ctx context.C...
method handleAssociationCreate (line 755) | func (s setCreateOrUpdateG[T]) handleAssociationCreate(ctx context.Conte...
method handleAssociationForOwners (line 772) | func (s setCreateOrUpdateG[T]) handleAssociationForOwners(base *DB, ctx ...
method handleAssociation (line 791) | func (s setCreateOrUpdateG[T]) handleAssociation(ctx context.Context, ba...
FILE: gorm.go
constant preparedStmtDBKey (line 18) | preparedStmtDBKey = "preparedStmt"
type Config (line 21) | type Config struct
method Apply (line 78) | func (c *Config) Apply(config *Config) error {
method AfterInitialize (line 86) | func (c *Config) AfterInitialize(db *DB) error {
type Option (line 98) | type Option interface
type DB (line 104) | type DB struct
method Session (line 255) | func (db *DB) Session(config *Session) *DB {
method WithContext (line 357) | func (db *DB) WithContext(ctx context.Context) *DB {
method Debug (line 362) | func (db *DB) Debug() (tx *DB) {
method Set (line 370) | func (db *DB) Set(key string, value interface{}) *DB {
method Get (line 377) | func (db *DB) Get(key string) (interface{}, bool) {
method InstanceSet (line 382) | func (db *DB) InstanceSet(key string, value interface{}) *DB {
method InstanceGet (line 389) | func (db *DB) InstanceGet(key string) (interface{}, bool) {
method Callback (line 394) | func (db *DB) Callback() *callbacks {
method AddError (line 399) | func (db *DB) AddError(err error) error {
method DB (line 417) | func (db *DB) DB() (*sql.DB, error) {
method getInstance (line 439) | func (db *DB) getInstance() *DB {
method SetupJoinTable (line 474) | func (db *DB) SetupJoinTable(model interface{}, field string, joinTabl...
method Use (line 525) | func (db *DB) Use(plugin Plugin) error {
method ToSQL (line 545) | func (db *DB) ToSQL(queryFn func(tx *DB) *DB) string {
type Session (line 113) | type Session struct
function Open (line 132) | func Open(dialector Dialector, opts ...Option) (db *DB, err error) {
function Expr (line 469) | func Expr(expr string, args ...interface{}) clause.Expr {
FILE: interfaces.go
type Dialector (line 12) | type Dialector interface
type Plugin (line 24) | type Plugin interface
type ParamsFilter (line 29) | type ParamsFilter interface
type ConnPool (line 34) | type ConnPool interface
type SavePointerDialectorInterface (line 42) | type SavePointerDialectorInterface interface
type TxBeginner (line 48) | type TxBeginner interface
type ConnPoolBeginner (line 53) | type ConnPoolBeginner interface
type TxCommitter (line 58) | type TxCommitter interface
type Tx (line 64) | type Tx interface
type Valuer (line 71) | type Valuer interface
type GetDBConnector (line 76) | type GetDBConnector interface
type Rows (line 81) | type Rows interface
type ErrorTranslator (line 90) | type ErrorTranslator interface
FILE: internal/lru/lru.go
type EvictCallback (line 11) | type EvictCallback
type LRU (line 14) | type LRU struct
type bucket (line 32) | type bucket struct
constant noEvictionTTL (line 38) | noEvictionTTL = time.Hour * 24 * 365 * 10
constant numBuckets (line 42) | numBuckets = 100
function NewLRU (line 51) | func NewLRU[K comparable, V any](size int, onEvict EvictCallback[K, V], ...
method Purge (line 97) | func (c *LRU[K, V]) Purge() {
method Add (line 117) | func (c *LRU[K, V]) Add(key K, value V) (evicted bool) {
method Get (line 146) | func (c *LRU[K, V]) Get(key K) (value V, ok bool) {
method Contains (line 163) | func (c *LRU[K, V]) Contains(key K) (ok bool) {
method Peek (line 172) | func (c *LRU[K, V]) Peek(key K) (value V, ok bool) {
method Remove (line 188) | func (c *LRU[K, V]) Remove(key K) bool {
method RemoveOldest (line 199) | func (c *LRU[K, V]) RemoveOldest() (key K, value V, ok bool) {
method GetOldest (line 210) | func (c *LRU[K, V]) GetOldest() (key K, value V, ok bool) {
method KeyValues (line 219) | func (c *LRU[K, V]) KeyValues() map[K]V {
method Keys (line 236) | func (c *LRU[K, V]) Keys() []K {
method Values (line 252) | func (c *LRU[K, V]) Values() []V {
method Len (line 267) | func (c *LRU[K, V]) Len() int {
method Resize (line 274) | func (c *LRU[K, V]) Resize(size int) (evicted int) {
method removeOldest (line 305) | func (c *LRU[K, V]) removeOldest() {
method removeElement (line 312) | func (c *LRU[K, V]) removeElement(e *Entry[K, V]) {
method deleteExpired (line 323) | func (c *LRU[K, V]) deleteExpired() {
method addToBucket (line 341) | func (c *LRU[K, V]) addToBucket(e *Entry[K, V]) {
method removeFromBucket (line 351) | func (c *LRU[K, V]) removeFromBucket(e *Entry[K, V]) {
method Cap (line 356) | func (c *LRU[K, V]) Cap() int {
type Entry (line 361) | type Entry struct
method PrevEntry (line 386) | func (e *Entry[K, V]) PrevEntry() *Entry[K, V] {
type LruList (line 395) | type LruList struct
method Init (line 401) | func (l *LruList[K, V]) Init() *LruList[K, V] {
function NewList (line 409) | func NewList[K comparable, V any]() *LruList[K, V] { return new(LruList[...
method Length (line 413) | func (l *LruList[K, V]) Length() int { return l.len }
method Back (line 416) | func (l *LruList[K, V]) Back() *Entry[K, V] {
method lazyInit (line 424) | func (l *LruList[K, V]) lazyInit() {
method insert (line 431) | func (l *LruList[K, V]) insert(e, at *Entry[K, V]) *Entry[K, V] {
method insertValue (line 442) | func (l *LruList[K, V]) insertValue(k K, v V, expiresAt time.Time, at *E...
method Remove (line 447) | func (l *LruList[K, V]) Remove(e *Entry[K, V]) V {
method move (line 459) | func (l *LruList[K, V]) move(e, at *Entry[K, V]) {
method PushFront (line 473) | func (l *LruList[K, V]) PushFront(k K, v V) *Entry[K, V] {
method PushFrontExpirable (line 479) | func (l *LruList[K, V]) PushFrontExpirable(k K, v V, expiresAt time.Time...
method MoveToFront (line 487) | func (l *LruList[K, V]) MoveToFront(e *Entry[K, V]) {
FILE: internal/stmt_store/stmt_store.go
type Stmt (line 13) | type Stmt struct
method Error (line 20) | func (stmt *Stmt) Error() error {
method Close (line 24) | func (stmt *Stmt) Close() error {
type Store (line 36) | type Store interface
constant defaultMaxSize (line 77) | defaultMaxSize = math.MaxInt
constant defaultTTL (line 80) | defaultTTL = time.Hour * 24
function New (line 98) | func New(size int, ttl time.Duration) Store {
type lruStore (line 115) | type lruStore struct
method Keys (line 119) | func (s *lruStore) Keys() []string {
method Get (line 123) | func (s *lruStore) Get(key string) (*Stmt, bool) {
method Set (line 131) | func (s *lruStore) Set(key string, value *Stmt) {
method Delete (line 135) | func (s *lruStore) Delete(key string) {
method New (line 157) | func (s *lruStore) New(ctx context.Context, key string, isTransaction ...
type ConnPool (line 139) | type ConnPool interface
FILE: logger/logger.go
constant Reset (line 20) | Reset = "\033[0m"
constant Red (line 21) | Red = "\033[31m"
constant Green (line 22) | Green = "\033[32m"
constant Yellow (line 23) | Yellow = "\033[33m"
constant Blue (line 24) | Blue = "\033[34m"
constant Magenta (line 25) | Magenta = "\033[35m"
constant Cyan (line 26) | Cyan = "\033[36m"
constant White (line 27) | White = "\033[37m"
constant BlueBold (line 28) | BlueBold = "\033[34;1m"
constant MagentaBold (line 29) | MagentaBold = "\033[35;1m"
constant RedBold (line 30) | RedBold = "\033[31;1m"
constant YellowBold (line 31) | YellowBold = "\033[33;1m"
type LogLevel (line 35) | type LogLevel
constant Silent (line 39) | Silent LogLevel = iota + 1
constant Error (line 41) | Error
constant Warn (line 43) | Warn
constant Info (line 45) | Info
type Writer (line 49) | type Writer interface
type Config (line 54) | type Config struct
type Interface (line 63) | type Interface interface
function New (line 91) | func New(writer Writer, config Config) Interface {
type logger (line 122) | type logger struct
method LogMode (line 130) | func (l *logger) LogMode(level LogLevel) Interface {
method Info (line 137) | func (l *logger) Info(ctx context.Context, msg string, data ...interfa...
method Warn (line 144) | func (l *logger) Warn(ctx context.Context, msg string, data ...interfa...
method Error (line 151) | func (l *logger) Error(ctx context.Context, msg string, data ...interf...
method Trace (line 160) | func (l *logger) Trace(ctx context.Context, begin time.Time, fc func()...
method ParamsFilter (line 193) | func (l *logger) ParamsFilter(ctx context.Context, sql string, params ...
type traceRecorder (line 200) | type traceRecorder struct
method New (line 209) | func (l *traceRecorder) New() *traceRecorder {
method Trace (line 214) | func (l *traceRecorder) Trace(ctx context.Context, begin time.Time, fc...
method ParamsFilter (line 220) | func (l *traceRecorder) ParamsFilter(ctx context.Context, sql string, ...
FILE: logger/slog.go
type slogLogger (line 15) | type slogLogger struct
method LogMode (line 34) | func (l *slogLogger) LogMode(level LogLevel) Interface {
method Info (line 40) | func (l *slogLogger) Info(ctx context.Context, msg string, data ...int...
method Warn (line 46) | func (l *slogLogger) Warn(ctx context.Context, msg string, data ...int...
method Error (line 52) | func (l *slogLogger) Error(ctx context.Context, msg string, data ...in...
method Trace (line 58) | func (l *slogLogger) Trace(ctx context.Context, begin time.Time, fc fu...
method log (line 96) | func (l *slogLogger) log(ctx context.Context, level slog.Level, msg st...
method ParamsFilter (line 111) | func (l *slogLogger) ParamsFilter(ctx context.Context, sql string, par...
function NewSlogLogger (line 24) | func NewSlogLogger(logger *slog.Logger, config Config) Interface {
FILE: logger/slog_test.go
function TestSlogLogger (line 14) | func TestSlogLogger(t *testing.T) {
FILE: logger/sql.go
constant tmFmtWithMS (line 17) | tmFmtWithMS = "2006-01-02 15:04:05.999"
constant tmFmtZero (line 18) | tmFmtZero = "0000-00-00 00:00:00"
constant nullStr (line 19) | nullStr = "NULL"
function isPrintable (line 22) | func isPrintable(s string) bool {
function isNumeric (line 37) | func isNumeric(k reflect.Kind) bool {
function ExplainSQL (line 51) | func ExplainSQL(sql string, numericPlaceholder *regexp.Regexp, escaper s...
FILE: logger/sql_test.go
type JSON (line 15) | type JSON
method Value (line 17) | func (j JSON) Value() (driver.Value, error) {
type ExampleStruct (line 24) | type ExampleStruct struct
method Value (line 29) | func (s ExampleStruct) Value() (driver.Value, error) {
function format (line 33) | func format(v []byte, escaper string) string {
function TestExplainSQL (line 37) | func TestExplainSQL(t *testing.T) {
FILE: migrator.go
method Migrator (line 11) | func (db *DB) Migrator() Migrator {
method AutoMigrate (line 23) | func (db *DB) AutoMigrate(dst ...interface{}) error {
type ViewOption (line 28) | type ViewOption struct
type ColumnType (line 35) | type ColumnType interface
type Index (line 50) | type Index interface
type TableType (line 60) | type TableType interface
type Migrator (line 68) | type Migrator interface
FILE: migrator/column_type.go
type ColumnType (line 9) | type ColumnType struct
method Name (line 27) | func (ct ColumnType) Name() string {
method DatabaseTypeName (line 40) | func (ct ColumnType) DatabaseTypeName() string {
method ColumnType (line 48) | func (ct ColumnType) ColumnType() (columnType string, ok bool) {
method PrimaryKey (line 53) | func (ct ColumnType) PrimaryKey() (isPrimaryKey bool, ok bool) {
method AutoIncrement (line 58) | func (ct ColumnType) AutoIncrement() (isAutoIncrement bool, ok bool) {
method Length (line 63) | func (ct ColumnType) Length() (length int64, ok bool) {
method DecimalSize (line 71) | func (ct ColumnType) DecimalSize() (precision int64, scale int64, ok b...
method Nullable (line 79) | func (ct ColumnType) Nullable() (nullable bool, ok bool) {
method Unique (line 87) | func (ct ColumnType) Unique() (unique bool, ok bool) {
method ScanType (line 92) | func (ct ColumnType) ScanType() reflect.Type {
method Comment (line 100) | func (ct ColumnType) Comment() (value string, ok bool) {
method DefaultValue (line 105) | func (ct ColumnType) DefaultValue() (value string, ok bool) {
FILE: migrator/index.go
type Index (line 6) | type Index struct
method Table (line 16) | func (idx Index) Table() string {
method Name (line 21) | func (idx Index) Name() string {
method Columns (line 26) | func (idx Index) Columns() []string {
method PrimaryKey (line 31) | func (idx Index) PrimaryKey() (isPrimaryKey bool, ok bool) {
method Unique (line 36) | func (idx Index) Unique() (unique bool, ok bool) {
method Option (line 41) | func (idx Index) Option() string {
FILE: migrator/migrator.go
type Migrator (line 34) | type Migrator struct
method RunWithValue (line 61) | func (m Migrator) RunWithValue(value interface{}, fc func(*gorm.Statem...
method DataTypeOf (line 78) | func (m Migrator) DataTypeOf(field *schema.Field) string {
method FullDataTypeOf (line 90) | func (m Migrator) FullDataTypeOf(field *schema.Field) (expr clause.Exp...
method GetQueryAndExecTx (line 110) | func (m Migrator) GetQueryAndExecTx() (queryTx, execTx *gorm.DB) {
method AutoMigrate (line 121) | func (m Migrator) AutoMigrate(values ...interface{}) error {
method GetTables (line 208) | func (m Migrator) GetTables() (tableList []string, err error) {
method CreateTable (line 215) | func (m Migrator) CreateTable(values ...interface{}) error {
method DropTable (line 319) | func (m Migrator) DropTable(values ...interface{}) error {
method HasTable (line 333) | func (m Migrator) HasTable(value interface{}) bool {
method RenameTable (line 345) | func (m Migrator) RenameTable(oldName, newName interface{}) error {
method AddColumn (line 373) | func (m Migrator) AddColumn(value interface{}, name string) error {
method DropColumn (line 396) | func (m Migrator) DropColumn(value interface{}, name string) error {
method AlterColumn (line 411) | func (m Migrator) AlterColumn(value interface{}, field string) error {
method HasColumn (line 428) | func (m Migrator) HasColumn(value interface{}, field string) bool {
method RenameColumn (line 449) | func (m Migrator) RenameColumn(value interface{}, oldName, newName str...
method MigrateColumn (line 469) | func (m Migrator) MigrateColumn(value interface{}, field *schema.Field...
method MigrateColumnUnique (line 594) | func (m Migrator) MigrateColumnUnique(value interface{}, field *schema...
method ColumnTypes (line 615) | func (m Migrator) ColumnTypes(value interface{}) ([]gorm.ColumnType, e...
method CreateView (line 655) | func (m Migrator) CreateView(name string, option gorm.ViewOption) error {
method DropView (line 679) | func (m Migrator) DropView(name string) error {
method GuessConstraintAndTable (line 686) | func (m Migrator) GuessConstraintAndTable(stmt *gorm.Statement, name s...
method GuessConstraintInterfaceAndTable (line 700) | func (m Migrator) GuessConstraintInterfaceAndTable(stmt *gorm.Statemen...
method CreateConstraint (line 757) | func (m Migrator) CreateConstraint(value interface{}, name string) err...
method DropConstraint (line 773) | func (m Migrator) DropConstraint(value interface{}, name string) error {
method HasConstraint (line 784) | func (m Migrator) HasConstraint(value interface{}, name string) bool {
method BuildIndexOptions (line 803) | func (m Migrator) BuildIndexOptions(opts []schema.IndexOption, stmt *g...
method CreateIndex (line 830) | func (m Migrator) CreateIndex(value interface{}, name string) error {
method DropIndex (line 865) | func (m Migrator) DropIndex(value interface{}, name string) error {
method HasIndex (line 878) | func (m Migrator) HasIndex(value interface{}, name string) bool {
method RenameIndex (line 898) | func (m Migrator) RenameIndex(value interface{}, oldName, newName stri...
method CurrentDatabase (line 908) | func (m Migrator) CurrentDatabase() (name string) {
method ReorderModels (line 914) | func (m Migrator) ReorderModels(values []interface{}, autoAdd bool) (r...
method CurrentTable (line 1018) | func (m Migrator) CurrentTable(stmt *gorm.Statement) interface{} {
method GetIndexes (line 1026) | func (m Migrator) GetIndexes(dst interface{}) ([]gorm.Index, error) {
method GetTypeAliases (line 1031) | func (m Migrator) GetTypeAliases(databaseTypeName string) []string {
method TableType (line 1036) | func (m Migrator) TableType(dst interface{}) (gorm.TableType, error) {
type Config (line 39) | type Config struct
type printSQLLogger (line 45) | type printSQLLogger struct
method Trace (line 49) | func (l *printSQLLogger) Trace(ctx context.Context, begin time.Time, f...
type GormDataTypeInterface (line 56) | type GormDataTypeInterface interface
type BuildIndexOptionsInterface (line 825) | type BuildIndexOptionsInterface interface
FILE: migrator/table_type.go
type TableType (line 8) | type TableType struct
method Schema (line 16) | func (ct TableType) Schema() string {
method Name (line 21) | func (ct TableType) Name() string {
method Type (line 26) | func (ct TableType) Type() string {
method Comment (line 31) | func (ct TableType) Comment() (comment string, ok bool) {
FILE: model.go
type Model (line 11) | type Model struct
FILE: prepare_stmt.go
type PreparedStmtDB (line 15) | type PreparedStmtDB struct
method GetDBConn (line 39) | func (db *PreparedStmtDB) GetDBConn() (*sql.DB, error) {
method Close (line 52) | func (db *PreparedStmtDB) Close() {
method Reset (line 62) | func (db *PreparedStmtDB) Reset() {
method prepare (line 66) | func (db *PreparedStmtDB) prepare(ctx context.Context, conn ConnPool, ...
method BeginTx (line 88) | func (db *PreparedStmtDB) BeginTx(ctx context.Context, opt *sql.TxOpti...
method ExecContext (line 109) | func (db *PreparedStmtDB) ExecContext(ctx context.Context, query strin...
method QueryContext (line 120) | func (db *PreparedStmtDB) QueryContext(ctx context.Context, query stri...
method QueryRowContext (line 131) | func (db *PreparedStmtDB) QueryRowContext(ctx context.Context, query s...
method Ping (line 139) | func (db *PreparedStmtDB) Ping() error {
function NewPreparedStmtDB (line 30) | func NewPreparedStmtDB(connPool ConnPool, maxSize int, ttl time.Duration...
type PreparedStmtTX (line 147) | type PreparedStmtTX struct
method GetDBConn (line 152) | func (db *PreparedStmtTX) GetDBConn() (*sql.DB, error) {
method Commit (line 156) | func (tx *PreparedStmtTX) Commit() error {
method Rollback (line 163) | func (tx *PreparedStmtTX) Rollback() error {
method ExecContext (line 170) | func (tx *PreparedStmtTX) ExecContext(ctx context.Context, query strin...
method QueryContext (line 181) | func (tx *PreparedStmtTX) QueryContext(ctx context.Context, query stri...
method QueryRowContext (line 192) | func (tx *PreparedStmtTX) QueryRowContext(ctx context.Context, query s...
method Ping (line 200) | func (tx *PreparedStmtTX) Ping() error {
FILE: scan.go
function prepareValues (line 15) | func prepareValues(values []interface{}, db *DB, columnTypes []*sql.Colu...
function scanIntoMap (line 39) | func scanIntoMap(mapValue map[string]interface{}, values []interface{}, ...
method scanIntoStruct (line 54) | func (db *DB) scanIntoStruct(rows Rows, reflectValue reflect.Value, valu...
type ScanMode (line 116) | type ScanMode
constant ScanInitialized (line 120) | ScanInitialized ScanMode = 1 << 0
constant ScanUpdate (line 121) | ScanUpdate ScanMode = 1 << 1
constant ScanOnConflictDoNothing (line 122) | ScanOnConflictDoNothing ScanMode = 1 << 2
function Scan (line 126) | func Scan(rows Rows, db *DB, mode ScanMode) {
FILE: schema/callbacks_test.go
type UserWithCallback (line 12) | type UserWithCallback struct
method BeforeSave (line 14) | func (UserWithCallback) BeforeSave(*gorm.DB) error {
method AfterCreate (line 18) | func (UserWithCallback) AfterCreate(*gorm.DB) error {
function TestCallback (line 22) | func TestCallback(t *testing.T) {
FILE: schema/constraint.go
type CheckConstraint (line 13) | type CheckConstraint struct
method GetName (line 19) | func (chk *CheckConstraint) GetName() string { return chk.Name }
method Build (line 21) | func (chk *CheckConstraint) Build() (sql string, vars []interface{}) {
method ParseCheckConstraints (line 26) | func (schema *Schema) ParseCheckConstraints() map[string]CheckConstraint {
type UniqueConstraint (line 45) | type UniqueConstraint struct
method GetName (line 50) | func (uni *UniqueConstraint) GetName() string { return uni.Name }
method Build (line 52) | func (uni *UniqueConstraint) Build() (sql string, vars []interface{}) {
method ParseUniqueConstraints (line 57) | func (schema *Schema) ParseUniqueConstraints() map[string]UniqueConstrai...
FILE: schema/constraint_test.go
type UserCheck (line 12) | type UserCheck struct
function TestParseCheck (line 18) | func TestParseCheck(t *testing.T) {
function TestParseUniqueConstraints (line 58) | func TestParseUniqueConstraints(t *testing.T) {
FILE: schema/field.go
type DataType (line 28) | type DataType
type TimeType (line 30) | type TimeType
constant UnixTime (line 35) | UnixTime TimeType = 1
constant UnixSecond (line 36) | UnixSecond TimeType = 2
constant UnixMillisecond (line 37) | UnixMillisecond TimeType = 3
constant UnixNanosecond (line 38) | UnixNanosecond TimeType = 4
constant Bool (line 43) | Bool DataType = "bool"
constant Int (line 44) | Int DataType = "int"
constant Uint (line 45) | Uint DataType = "uint"
constant Float (line 46) | Float DataType = "float"
constant String (line 47) | String DataType = "string"
constant Time (line 48) | Time DataType = "time"
constant Bytes (line 49) | Bytes DataType = "bytes"
constant DefaultAutoIncrementIncrement (line 52) | DefaultAutoIncrementIncrement int64 = 1
type Field (line 55) | type Field struct
method BindName (line 101) | func (field *Field) BindName() string {
method setupValuerAndSetter (line 451) | func (field *Field) setupValuerAndSetter(modelType reflect.Type) {
method setupNewValuePool (line 992) | func (field *Field) setupNewValuePool() {
method ParseField (line 106) | func (schema *Schema) ParseField(fieldStruct reflect.StructField) *Field {
FILE: schema/field_test.go
function TestFieldValuerAndSetter (line 16) | func TestFieldValuerAndSetter(t *testing.T) {
function TestPointerFieldValuerAndSetter (line 92) | func TestPointerFieldValuerAndSetter(t *testing.T) {
function TestAdvancedDataTypeValuerAndSetter (line 162) | func TestAdvancedDataTypeValuerAndSetter(t *testing.T) {
type UserWithPermissionControl (line 230) | type UserWithPermissionControl struct
function TestParseFieldWithPermission (line 242) | func TestParseFieldWithPermission(t *testing.T) {
type ID (line 266) | type ID
type INT (line 267) | type INT
type INT8 (line 268) | type INT8
type INT16 (line 269) | type INT16
type INT32 (line 270) | type INT32
type INT64 (line 271) | type INT64
type UINT (line 272) | type UINT
type UINT8 (line 273) | type UINT8
type UINT16 (line 274) | type UINT16
type UINT32 (line 275) | type UINT32
type UINT64 (line 276) | type UINT64
type FLOAT32 (line 277) | type FLOAT32
type FLOAT64 (line 278) | type FLOAT64
type BOOL (line 279) | type BOOL
type STRING (line 280) | type STRING
type TIME (line 281) | type TIME
type BYTES (line 282) | type BYTES
type TypeAlias (line 284) | type TypeAlias struct
function TestTypeAliasField (line 305) | func TestTypeAliasField(t *testing.T) {
FILE: schema/index.go
type Index (line 10) | type Index struct
type IndexOption (line 20) | type IndexOption struct
method ParseIndexes (line 30) | func (schema *Schema) ParseIndexes() []*Index {
method LookIndex (line 80) | func (schema *Schema) LookIndex(name string) *Index {
function parseFieldIndexes (line 99) | func parseFieldIndexes(field *Field) (indexes []Index, err error) {
FILE: schema/index_test.go
type UserIndex (line 11) | type UserIndex struct
type CompIdxLevel1C (line 40) | type CompIdxLevel1C struct
type CompIdxLevel1B (line 45) | type CompIdxLevel1B struct
type CompIdxLevel2C (line 49) | type CompIdxLevel2C struct
type CompIdxLevel2B (line 54) | type CompIdxLevel2B struct
function TestParseIndex (line 58) | func TestParseIndex(t *testing.T) {
function TestParseIndexWithUniqueIndexAndUnique (line 174) | func TestParseIndexWithUniqueIndexAndUnique(t *testing.T) {
function CheckIndices (line 254) | func CheckIndices(t *testing.T, expected, actual []*schema.Index) {
FILE: schema/interfaces.go
type ConstraintInterface (line 8) | type ConstraintInterface interface
type GormDataTypeInterface (line 14) | type GormDataTypeInterface interface
type FieldNewValuePool (line 19) | type FieldNewValuePool interface
type CreateClausesInterface (line 25) | type CreateClausesInterface interface
type QueryClausesInterface (line 30) | type QueryClausesInterface interface
type UpdateClausesInterface (line 35) | type UpdateClausesInterface interface
type DeleteClausesInterface (line 40) | type DeleteClausesInterface interface
FILE: schema/model_test.go
type User (line 11) | type User struct
type mytime (line 30) | type mytime
type myint (line 31) | type myint
type AdvancedDataTypeUser (line 35) | type AdvancedDataTypeUser struct
type BaseModel (line 45) | type BaseModel struct
type VersionModel (line 54) | type VersionModel struct
type VersionUser (line 59) | type VersionUser struct
FILE: schema/naming.go
type Namer (line 16) | type Namer interface
type Replacer (line 28) | type Replacer interface
type NamingStrategy (line 35) | type NamingStrategy struct
method TableName (line 44) | func (ns NamingStrategy) TableName(str string) string {
method SchemaName (line 52) | func (ns NamingStrategy) SchemaName(table string) string {
method ColumnName (line 62) | func (ns NamingStrategy) ColumnName(table, column string) string {
method JoinTableName (line 67) | func (ns NamingStrategy) JoinTableName(str string) string {
method RelationshipFKName (line 79) | func (ns NamingStrategy) RelationshipFKName(rel Relationship) string {
method CheckerName (line 84) | func (ns NamingStrategy) CheckerName(table, column string) string {
method IndexName (line 89) | func (ns NamingStrategy) IndexName(table, column string) string {
method UniqueName (line 94) | func (ns NamingStrategy) UniqueName(table, column string) string {
method formatName (line 98) | func (ns NamingStrategy) formatName(prefix, table, name string) string {
method toDBName (line 131) | func (ns NamingStrategy) toDBName(name string) string {
method toSchemaName (line 190) | func (ns NamingStrategy) toSchemaName(name string) string {
function init (line 123) | func init() {
FILE: schema/naming_test.go
function TestToDBName (line 8) | func TestToDBName(t *testing.T) {
function TestNamingStrategy (line 58) | func TestNamingStrategy(t *testing.T) {
type CustomReplacer (line 96) | type CustomReplacer struct
method Replace (line 100) | func (r CustomReplacer) Replace(name string) string {
function TestCustomReplacer (line 104) | func TestCustomReplacer(t *testing.T) {
function TestCustomReplacerWithNoLowerCase (line 148) | func TestCustomReplacerWithNoLowerCase(t *testing.T) {
function TestFormatNameWithStringLongerThan63Characters (line 192) | func TestFormatNameWithStringLongerThan63Characters(t *testing.T) {
function TestFormatNameWithStringLongerThan64Characters (line 201) | func TestFormatNameWithStringLongerThan64Characters(t *testing.T) {
function TestReplaceEmptyTableName (line 210) | func TestReplaceEmptyTableName(t *testing.T) {
FILE: schema/relationship.go
type RelationshipType (line 18) | type RelationshipType
constant HasOne (line 21) | HasOne RelationshipType = "has_one"
constant HasMany (line 22) | HasMany RelationshipType = "has_many"
constant BelongsTo (line 23) | BelongsTo RelationshipType = "belongs_to"
constant Many2Many (line 24) | Many2Many RelationshipType = "many_to_many"
constant has (line 25) | has RelationshipType = "has"
type Relationships (line 28) | type Relationships struct
type Relationship (line 40) | type Relationship struct
method ParseConstraint (line 661) | func (rel *Relationship) ParseConstraint() *Constraint {
method ToQueryConditions (line 728) | func (rel *Relationship) ToQueryConditions(ctx context.Context, reflec...
type Polymorphic (line 52) | type Polymorphic struct
type Reference (line 58) | type Reference struct
method parseRelation (line 65) | func (schema *Schema) parseRelation(field *Field) *Relationship {
function hasPolymorphicRelation (line 136) | func hasPolymorphicRelation(tagSettings map[string]string) bool {
method setRelation (line 147) | func (schema *Schema) setRelation(relation *Relationship) {
method buildPolymorphicRelation (line 195) | func (schema *Schema) buildPolymorphicRelation(relation *Relationship, f...
method buildMany2ManyRelation (line 271) | func (schema *Schema) buildMany2ManyRelation(relation *Relationship, fie...
type guessLevel (line 446) | type guessLevel
constant guessGuess (line 449) | guessGuess guessLevel = iota
constant guessBelongs (line 450) | guessBelongs
constant guessEmbeddedBelongs (line 451) | guessEmbeddedBelongs
constant guessHas (line 452) | guessHas
constant guessEmbeddedHas (line 453) | guessEmbeddedHas
method guessRelation (line 456) | func (schema *Schema) guessRelation(relation *Relationship, field *Field...
type Constraint (line 625) | type Constraint struct
method GetName (line 636) | func (constraint *Constraint) GetName() string { return constraint.Name }
method Build (line 638) | func (constraint *Constraint) Build() (sql string, vars []interface{}) {
function copyableDataType (line 775) | func copyableDataType(str DataType) bool {
FILE: schema/relationship_test.go
function checkStructRelation (line 13) | func checkStructRelation(t *testing.T, data interface{}, relations ...Re...
function TestBelongsToOverrideForeignKey (line 23) | func TestBelongsToOverrideForeignKey(t *testing.T) {
function TestBelongsToOverrideReferences (line 41) | func TestBelongsToOverrideReferences(t *testing.T) {
function TestBelongsToWithOnlyReferences (line 60) | func TestBelongsToWithOnlyReferences(t *testing.T) {
function TestBelongsToWithOnlyReferences2 (line 79) | func TestBelongsToWithOnlyReferences2(t *testing.T) {
function TestSelfReferentialBelongsTo (line 98) | func TestSelfReferentialBelongsTo(t *testing.T) {
function TestSelfReferentialBelongsToOverrideReferences (line 112) | func TestSelfReferentialBelongsToOverrideReferences(t *testing.T) {
function TestBelongsToWithMixin (line 126) | func TestBelongsToWithMixin(t *testing.T) {
function TestHasOneOverrideForeignKey (line 149) | func TestHasOneOverrideForeignKey(t *testing.T) {
function TestHasOneOverrideReferences (line 167) | func TestHasOneOverrideReferences(t *testing.T) {
function TestHasOneOverrideReferences2 (line 186) | func TestHasOneOverrideReferences2(t *testing.T) {
function TestHasOneWithOnlyReferences (line 204) | func TestHasOneWithOnlyReferences(t *testing.T) {
function TestHasOneWithOnlyReferences2 (line 223) | func TestHasOneWithOnlyReferences2(t *testing.T) {
function TestHasManyOverrideForeignKey (line 242) | func TestHasManyOverrideForeignKey(t *testing.T) {
function TestHasManyOverrideReferences (line 260) | func TestHasManyOverrideReferences(t *testing.T) {
function TestMany2ManyOverrideForeignKeyAndReferences (line 279) | func TestMany2ManyOverrideForeignKeyAndReferences(t *testing.T) {
function TestMany2ManyOverrideForeignKey (line 310) | func TestMany2ManyOverrideForeignKey(t *testing.T) {
function TestMany2ManySharedForeignKey (line 333) | func TestMany2ManySharedForeignKey(t *testing.T) {
function TestMany2ManyOverrideJoinForeignKey (line 360) | func TestMany2ManyOverrideJoinForeignKey(t *testing.T) {
function TestBuildReadonlyMany2ManyRelation (line 383) | func TestBuildReadonlyMany2ManyRelation(t *testing.T) {
function TestMany2ManyWithMultiPrimaryKeys (line 406) | func TestMany2ManyWithMultiPrimaryKeys(t *testing.T) {
function TestMultipleMany2Many (line 454) | func TestMultipleMany2Many(t *testing.T) {
function TestSelfReferentialMany2Many (line 485) | func TestSelfReferentialMany2Many(t *testing.T) {
type CreatedByModel (line 510) | type CreatedByModel struct
type CreatedUser (line 515) | type CreatedUser struct
function TestEmbeddedRelation (line 520) | func TestEmbeddedRelation(t *testing.T) {
function TestEmbeddedHas (line 546) | func TestEmbeddedHas(t *testing.T) {
function TestPolymorphic (line 605) | func TestPolymorphic(t *testing.T) {
function TestEmbeddedBelongsTo (line 792) | func TestEmbeddedBelongsTo(t *testing.T) {
function TestVariableRelation (line 860) | func TestVariableRelation(t *testing.T) {
function TestSameForeignKey (line 880) | func TestSameForeignKey(t *testing.T) {
function TestBelongsToSameForeignKey (line 904) | func TestBelongsToSameForeignKey(t *testing.T) {
function TestHasOneWithSameForeignKey (line 928) | func TestHasOneWithSameForeignKey(t *testing.T) {
function TestHasManySameForeignKey (line 947) | func TestHasManySameForeignKey(t *testing.T) {
type Author (line 966) | type Author struct
type Book (line 970) | type Book struct
method TableName (line 976) | func (Book) TableName() string {
function TestParseConstraintNameWithSchemaQualifiedLongTableName (line 980) | func TestParseConstraintNameWithSchemaQualifiedLongTableName(t *testing....
type InfoRelation (line 1002) | type InfoRelation struct
type Info1 (line 1009) | type Info1 struct
type Info2 (line 1016) | type Info2 struct
function TestDataRace (line 1023) | func TestDataRace(t *testing.T) {
FILE: schema/schema.go
type callbackType (line 17) | type callbackType
constant callbackTypeBeforeCreate (line 20) | callbackTypeBeforeCreate callbackType = "BeforeCreate"
constant callbackTypeBeforeUpdate (line 21) | callbackTypeBeforeUpdate callbackType = "BeforeUpdate"
constant callbackTypeAfterCreate (line 22) | callbackTypeAfterCreate callbackType = "AfterCreate"
constant callbackTypeAfterUpdate (line 23) | callbackTypeAfterUpdate callbackType = "AfterUpdate"
constant callbackTypeBeforeSave (line 24) | callbackTypeBeforeSave callbackType = "BeforeSave"
constant callbackTypeAfterSave (line 25) | callbackTypeAfterSave callbackType = "AfterSave"
constant callbackTypeBeforeDelete (line 26) | callbackTypeBeforeDelete callbackType = "BeforeDelete"
constant callbackTypeAfterDelete (line 27) | callbackTypeAfterDelete callbackType = "AfterDelete"
constant callbackTypeAfterFind (line 28) | callbackTypeAfterFind callbackType = "AfterFind"
type Schema (line 34) | type Schema struct
method String (line 63) | func (schema *Schema) String() string {
method MakeSlice (line 70) | func (schema *Schema) MakeSlice() reflect.Value {
method LookUpField (line 78) | func (schema *Schema) LookUpField(name string) *Field {
method LookUpFieldByBindName (line 106) | func (schema *Schema) LookUpFieldByBindName(bindNames []string, name s...
type Tabler (line 116) | type Tabler interface
type TablerWithNamer (line 120) | type TablerWithNamer interface
function Parse (line 133) | func Parse(dest interface{}, cacheStore *sync.Map, namer Namer) (*Schema...
function ParseWithSpecialTableName (line 138) | func ParseWithSpecialTableName(dest interface{}, cacheStore *sync.Map, n...
function getOrParse (line 392) | func getOrParse(dest interface{}, cacheStore *sync.Map, namer Namer) (*S...
FILE: schema/schema_helper_test.go
function checkSchema (line 14) | func checkSchema(t *testing.T, s *schema.Schema, v *schema.Schema, prima...
function checkSchemaField (line 39) | func checkSchemaField(t *testing.T, s *schema.Schema, f *schema.Field, f...
type Relation (line 93) | type Relation struct
type Polymorphic (line 103) | type Polymorphic struct
type JoinTable (line 109) | type JoinTable struct
type Reference (line 115) | type Reference struct
function checkSchemaRelation (line 124) | func checkSchemaRelation(t *testing.T, s *schema.Schema, relation Relati...
type EmbeddedRelations (line 204) | type EmbeddedRelations struct
function checkEmbeddedRelations (line 209) | func checkEmbeddedRelations(t *testing.T, actual map[string]*schema.Rela...
function checkField (line 235) | func checkField(t *testing.T, s *schema.Schema, value reflect.Value, val...
FILE: schema/schema_test.go
function TestParseSchema (line 13) | func TestParseSchema(t *testing.T) {
function TestParseSchemaWithMap (line 22) | func TestParseSchemaWithMap(t *testing.T) {
function TestParseSchemaWithPointerFields (line 38) | func TestParseSchemaWithPointerFields(t *testing.T) {
function checkUserSchema (line 47) | func checkUserSchema(t *testing.T, user *schema.Schema) {
function TestParseSchemaWithAdvancedDataType (line 135) | func TestParseSchemaWithAdvancedDataType(t *testing.T) {
type CustomizeTable (line 164) | type CustomizeTable struct
method TableName (line 166) | func (CustomizeTable) TableName() string {
function TestCustomizeTableName (line 170) | func TestCustomizeTableName(t *testing.T) {
function TestNestedModel (line 181) | func TestNestedModel(t *testing.T) {
function TestEmbeddedStruct (line 202) | func TestEmbeddedStruct(t *testing.T) {
type CustomizedNamingStrategy (line 245) | type CustomizedNamingStrategy struct
method ColumnName (line 249) | func (ns CustomizedNamingStrategy) ColumnName(table, column string) st...
function TestEmbeddedStructForCustomizedNamingStrategy (line 270) | func TestEmbeddedStructForCustomizedNamingStrategy(t *testing.T) {
function TestCompositePrimaryKeyWithAutoIncrement (line 313) | func TestCompositePrimaryKeyWithAutoIncrement(t *testing.T) {
FILE: schema/serializer.go
function RegisterSerializer (line 21) | func RegisterSerializer(name string, serializer SerializerInterface) {
function GetSerializer (line 26) | func GetSerializer(name string) (serializer SerializerInterface, ok bool) {
function init (line 34) | func init() {
type serializer (line 41) | type serializer struct
method Scan (line 52) | func (s *serializer) Scan(value interface{}) error {
method Value (line 58) | func (s serializer) Value() (driver.Value, error) {
type SerializerInterface (line 63) | type SerializerInterface interface
type SerializerValuerInterface (line 69) | type SerializerValuerInterface interface
type JSONSerializer (line 74) | type JSONSerializer struct
method Scan (line 77) | func (JSONSerializer) Scan(ctx context.Context, field *Field, dst refl...
method Value (line 104) | func (JSONSerializer) Value(ctx context.Context, field *Field, dst ref...
type UnixSecondSerializer (line 116) | type UnixSecondSerializer struct
method Scan (line 119) | func (UnixSecondSerializer) Scan(ctx context.Context, field *Field, ds...
method Value (line 129) | func (UnixSecondSerializer) Value(ctx context.Context, field *Field, d...
type GobSerializer (line 161) | type GobSerializer struct
method Scan (line 164) | func (GobSerializer) Scan(ctx context.Context, field *Field, dst refle...
method Value (line 185) | func (GobSerializer) Value(ctx context.Context, field *Field, dst refl...
FILE: schema/serializer_test.go
function TestUnixSecondSerializer_Value (line 11) | func TestUnixSecondSerializer_Value(t *testing.T) {
FILE: schema/utils.go
function ParseTagSetting (line 16) | func ParseTagSetting(str string, sep string) map[string]string {
function toColumns (line 45) | func toColumns(val string) (results []string) {
function removeSettingFromTag (line 54) | func removeSettingFromTag(tag reflect.StructTag, names ...string) reflec...
function appendSettingFromTag (line 61) | func appendSettingFromTag(tag reflect.StructTag, value string) reflect.S...
function GetRelationsValues (line 70) | func GetRelationsValues(ctx context.Context, reflectValue reflect.Value,...
function GetIdentityFieldValuesMap (line 108) | func GetIdentityFieldValuesMap(ctx context.Context, reflectValue reflect...
function GetIdentityFieldValuesMapFromValues (line 182) | func GetIdentityFieldValuesMapFromValues(ctx context.Context, values []i...
function ToQueryValues (line 197) | func ToQueryValues(table string, foreignKeys []string, foreignValues [][...
type embeddedNamer (line 219) | type embeddedNamer struct
FILE: schema/utils_test.go
function TestRemoveSettingFromTag (line 8) | func TestRemoveSettingFromTag(t *testing.T) {
function TestParseTagSettingWithDoubleQuoteEscape (line 26) | func TestParseTagSettingWithDoubleQuoteEscape(t *testing.T) {
FILE: soft_delete.go
type DeletedAt (line 14) | type DeletedAt
method Scan (line 17) | func (n *DeletedAt) Scan(value interface{}) error {
method Value (line 22) | func (n DeletedAt) Value() (driver.Value, error) {
method MarshalJSON (line 29) | func (n DeletedAt) MarshalJSON() ([]byte, error) {
method UnmarshalJSON (line 36) | func (n *DeletedAt) UnmarshalJSON(b []byte) error {
method QueryClauses (line 48) | func (DeletedAt) QueryClauses(f *schema.Field) []clause.Interface {
method UpdateClauses (line 98) | func (DeletedAt) UpdateClauses(f *schema.Field) []clause.Interface {
method DeleteClauses (line 123) | func (DeletedAt) DeleteClauses(f *schema.Field) []clause.Interface {
function parseZeroValueTag (line 52) | func parseZeroValueTag(f *schema.Field) sql.NullString {
type SoftDeleteQueryClause (line 61) | type SoftDeleteQueryClause struct
method Name (line 66) | func (sd SoftDeleteQueryClause) Name() string {
method Build (line 70) | func (sd SoftDeleteQueryClause) Build(clause.Builder) {
method MergeClause (line 73) | func (sd SoftDeleteQueryClause) MergeClause(*clause.Clause) {
method ModifyStatement (line 76) | func (sd SoftDeleteQueryClause) ModifyStatement(stmt *Statement) {
type SoftDeleteUpdateClause (line 102) | type SoftDeleteUpdateClause struct
method Name (line 107) | func (sd SoftDeleteUpdateClause) Name() string {
method Build (line 111) | func (sd SoftDeleteUpdateClause) Build(clause.Builder) {
method MergeClause (line 114) | func (sd SoftDeleteUpdateClause) MergeClause(*clause.Clause) {
method ModifyStatement (line 117) | func (sd SoftDeleteUpdateClause) ModifyStatement(stmt *Statement) {
type SoftDeleteDeleteClause (line 127) | type SoftDeleteDeleteClause struct
method Name (line 132) | func (sd SoftDeleteDeleteClause) Name() string {
method Build (line 136) | func (sd SoftDeleteDeleteClause) Build(clause.Builder) {
method MergeClause (line 139) | func (sd SoftDeleteDeleteClause) MergeClause(*clause.Clause) {
method ModifyStatement (line 142) | func (sd SoftDeleteDeleteClause) ModifyStatement(stmt *Statement) {
FILE: statement.go
type Statement (line 22) | type Statement struct
method WriteString (line 70) | func (stmt *Statement) WriteString(str string) (int, error) {
method WriteByte (line 75) | func (stmt *Statement) WriteByte(c byte) error {
method WriteQuoted (line 80) | func (stmt *Statement) WriteQuoted(value interface{}) {
method QuoteTo (line 85) | func (stmt *Statement) QuoteTo(writer clause.Writer, field interface{}) {
method Quote (line 168) | func (stmt *Statement) Quote(field interface{}) string {
method AddVar (line 175) | func (stmt *Statement) AddVar(writer clause.Writer, vars ...interface{...
method AddClause (line 272) | func (stmt *Statement) AddClause(v clause.Interface) {
method AddClauseIfNotExists (line 285) | func (stmt *Statement) AddClauseIfNotExists(v clause.Interface) {
method BuildCondition (line 292) | func (stmt *Statement) BuildCondition(query interface{}, args ...inter...
method Build (line 493) | func (stmt *Statement) Build(clauses ...string) {
method Parse (line 512) | func (stmt *Statement) Parse(value interface{}) (err error) {
method ParseWithSpecialTableName (line 516) | func (stmt *Statement) ParseWithSpecialTableName(value interface{}, sp...
method clone (line 529) | func (stmt *Statement) clone() *Statement {
method SetColumn (line 587) | func (stmt *Statement) SetColumn(name string, value interface{}, fromC...
method Changed (line 643) | func (stmt *Statement) Changed(fields ...string) bool {
method SelectAndOmitColumns (line 715) | func (stmt *Statement) SelectAndOmitColumns(requireCreate, requireUpda...
type join (line 53) | type join struct
type StatementModifier (line 65) | type StatementModifier interface
FILE: statement_test.go
function TestWhereCloneCorruption (line 11) | func TestWhereCloneCorruption(t *testing.T) {
function TestNilCondition (line 38) | func TestNilCondition(t *testing.T) {
function TestNameMatcher (line 45) | func TestNameMatcher(t *testing.T) {
FILE: tests/association_generics_test.go
function TestClauseAssociationSetCreateWithOpCreate (line 15) | func TestClauseAssociationSetCreateWithOpCreate(t *testing.T) {
function TestClauseAssociationSetUpdateWithOpCreate (line 56) | func TestClauseAssociationSetUpdateWithOpCreate(t *testing.T) {
function TestClauseAssociationSetCreateWithMultipleAssociations (line 112) | func TestClauseAssociationSetCreateWithMultipleAssociations(t *testing.T) {
function TestClauseAssociationSetUpdateWithMultipleAssociations (line 162) | func TestClauseAssociationSetUpdateWithMultipleAssociations(t *testing.T) {
function TestClauseAssociationSetUpdateWithOpUnlink (line 219) | func TestClauseAssociationSetUpdateWithOpUnlink(t *testing.T) {
function TestClauseAssociationSetUpdateWithOpCreateValues (line 281) | func TestClauseAssociationSetUpdateWithOpCreateValues(t *testing.T) {
function TestClauseAssociationSetCreateWithManyToMany (line 325) | func TestClauseAssociationSetCreateWithManyToMany(t *testing.T) {
function TestClauseAssociationSetCreateWithBelongsTo (line 371) | func TestClauseAssociationSetCreateWithBelongsTo(t *testing.T) {
function TestClauseAssociationSetUpdateBelongsToCreateValues (line 406) | func TestClauseAssociationSetUpdateBelongsToCreateValues(t *testing.T) {
function TestClauseAssociationSetUpdateMixedFieldAndAssociation (line 431) | func TestClauseAssociationSetUpdateMixedFieldAndAssociation(t *testing.T) {
function TestClauseAssociationSetUpdateHasOneUnlink (line 463) | func TestClauseAssociationSetUpdateHasOneUnlink(t *testing.T) {
function TestClauseAssociationSetUpdateManyToManyCreateWithSet (line 488) | func TestClauseAssociationSetUpdateManyToManyCreateWithSet(t *testing.T) {
function TestClauseAssociationSetUpdateManyToManyClear (line 509) | func TestClauseAssociationSetUpdateManyToManyClear(t *testing.T) {
function TestClauseAssociationSetUpdatePolymorphicTools (line 534) | func TestClauseAssociationSetUpdatePolymorphicTools(t *testing.T) {
function TestClauseAssociationSetUpdateInvalidAssociation (line 559) | func TestClauseAssociationSetUpdateInvalidAssociation(t *testing.T) {
function TestClauseAssociationSetUpdateNoOwnerMatch (line 573) | func TestClauseAssociationSetUpdateNoOwnerMatch(t *testing.T) {
function TestClauseAssociationSetUpdateAndDelete (line 584) | func TestClauseAssociationSetUpdateAndDelete(t *testing.T) {
function TestClauseAssociationSetUpdateAndDeleteHasOne (line 615) | func TestClauseAssociationSetUpdateAndDeleteHasOne(t *testing.T) {
function TestAssociationMany2ManyAppendMap_GenericFile (line 644) | func TestAssociationMany2ManyAppendMap_GenericFile(t *testing.T) {
function TestClauseAssociationSetUpdateAndDeleteBelongsTo (line 668) | func TestClauseAssociationSetUpdateAndDeleteBelongsTo(t *testing.T) {
function TestClauseAssociationSetUpdateAndDeleteMany2Many (line 717) | func TestClauseAssociationSetUpdateAndDeleteMany2Many(t *testing.T) {
function TestClauseAssociationSetUpdateAndDeleteManyOwnersHasMany (line 760) | func TestClauseAssociationSetUpdateAndDeleteManyOwnersHasMany(t *testing...
function TestClauseAssociationSetUpdateAndDeleteManyOwnersBelongsTo (line 803) | func TestClauseAssociationSetUpdateAndDeleteManyOwnersBelongsTo(t *testi...
function TestClauseAssociationSetUpdateAndDeleteManyOwnersMany2Many (line 869) | func TestClauseAssociationSetUpdateAndDeleteManyOwnersMany2Many(t *testi...
function TestClauseAssociationSetUpdateHasOneCreateValues (line 913) | func TestClauseAssociationSetUpdateHasOneCreateValues(t *testing.T) {
function TestClauseAssociationSetUpdateHasManyClear (line 945) | func TestClauseAssociationSetUpdateHasManyClear(t *testing.T) {
function TestClauseAssociationSetUpdateManyToManyUnlink (line 966) | func TestClauseAssociationSetUpdateManyToManyUnlink(t *testing.T) {
function TestClauseAssociationSetUpdatePolymorphicCreate (line 999) | func TestClauseAssociationSetUpdatePolymorphicCreate(t *testing.T) {
function TestClauseAssociationSetUpdateMultipleOwners (line 1021) | func TestClauseAssociationSetUpdateMultipleOwners(t *testing.T) {
FILE: tests/associations_belongs_to_test.go
function TestBelongsToAssociation (line 10) | func TestBelongsToAssociation(t *testing.T) {
function TestBelongsToAssociationForSlice (line 146) | func TestBelongsToAssociationForSlice(t *testing.T) {
function TestBelongsToDefaultValue (line 230) | func TestBelongsToDefaultValue(t *testing.T) {
function TestBelongsToAssociationUnscoped (line 255) | func TestBelongsToAssociationUnscoped(t *testing.T) {
FILE: tests/associations_has_many_test.go
function TestHasManyAssociation (line 10) | func TestHasManyAssociation(t *testing.T) {
function TestSingleTableHasManyAssociation (line 122) | func TestSingleTableHasManyAssociation(t *testing.T) {
function TestHasManyAssociationForSlice (line 217) | func TestHasManyAssociationForSlice(t *testing.T) {
function TestSingleTableHasManyAssociationForSlice (line 271) | func TestSingleTableHasManyAssociationForSlice(t *testing.T) {
function TestPolymorphicHasManyAssociation (line 327) | func TestPolymorphicHasManyAssociation(t *testing.T) {
function TestPolymorphicHasManyAssociationForSlice (line 422) | func TestPolymorphicHasManyAssociationForSlice(t *testing.T) {
function TestHasManyAssociationUnscoped (line 485) | func TestHasManyAssociationUnscoped(t *testing.T) {
function TestHasManyAssociationReplaceWithStructValue (line 558) | func TestHasManyAssociationReplaceWithStructValue(t *testing.T) {
FILE: tests/associations_has_one_test.go
function TestHasOneAssociation (line 9) | func TestHasOneAssociation(t *testing.T) {
function TestHasOneAssociationWithSelect (line 86) | func TestHasOneAssociationWithSelect(t *testing.T) {
function TestHasOneAssociationForSlice (line 100) | func TestHasOneAssociationForSlice(t *testing.T) {
function TestPolymorphicHasOneAssociation (line 141) | func TestPolymorphicHasOneAssociation(t *testing.T) {
function TestPolymorphicHasOneAssociationForSlice (line 218) | func TestPolymorphicHasOneAssociationForSlice(t *testing.T) {
function TestHasOneAssociationReplaceWithNonValidValue (line 259) | func TestHasOneAssociationReplaceWithNonValidValue(t *testing.T) {
FILE: tests/associations_many2many_test.go
function TestMany2ManyAssociation (line 13) | func TestMany2ManyAssociation(t *testing.T) {
function TestMany2ManyOmitAssociations (line 100) | func TestMany2ManyOmitAssociations(t *testing.T) {
function TestMany2ManyAssociationForSlice (line 129) | func TestMany2ManyAssociationForSlice(t *testing.T) {
function TestSingleTableMany2ManyAssociation (line 199) | func TestSingleTableMany2ManyAssociation(t *testing.T) {
function TestSingleTableMany2ManyAssociationForSlice (line 280) | func TestSingleTableMany2ManyAssociationForSlice(t *testing.T) {
function TestDuplicateMany2ManyAssociation (line 334) | func TestDuplicateMany2ManyAssociation(t *testing.T) {
function TestConcurrentMany2ManyAssociation (line 360) | func TestConcurrentMany2ManyAssociation(t *testing.T) {
function TestMany2ManyDuplicateBelongsToAssociation (line 397) | func TestMany2ManyDuplicateBelongsToAssociation(t *testing.T) {
FILE: tests/associations_test.go
function AssertAssociationCount (line 12) | func AssertAssociationCount(t *testing.T, data interface{}, name string,...
function TestInvalidAssociation (line 31) | func TestInvalidAssociation(t *testing.T) {
function TestAssociationNotNullClear (line 38) | func TestAssociationNotNullClear(t *testing.T) {
function TestForeignKeyConstraints (line 73) | func TestForeignKeyConstraints(t *testing.T) {
function TestForeignKeyConstraintsBelongsTo (line 130) | func TestForeignKeyConstraintsBelongsTo(t *testing.T) {
function TestFullSaveAssociations (line 186) | func TestFullSaveAssociations(t *testing.T) {
function TestSaveBelongsCircularReference (line 230) | func TestSaveBelongsCircularReference(t *testing.T) {
function TestSaveHasManyCircularReference (line 251) | func TestSaveHasManyCircularReference(t *testing.T) {
function TestAssociationError (line 271) | func TestAssociationError(t *testing.T) {
type myType (line 295) | type myType
method QueryClauses (line 301) | func (myType) QueryClauses(f *schema.Field) []clause.Interface {
type emptyQueryClause (line 296) | type emptyQueryClause struct
method Name (line 305) | func (sd emptyQueryClause) Name() string {
method Build (line 309) | func (sd emptyQueryClause) Build(clause.Builder) {
method MergeClause (line 312) | func (sd emptyQueryClause) MergeClause(*clause.Clause) {
method ModifyStatement (line 315) | func (sd emptyQueryClause) ModifyStatement(stmt *gorm.Statement) {
function TestAssociationEmptyQueryClause (line 319) | func TestAssociationEmptyQueryClause(t *testing.T) {
type AssociationEmptyUser (line 356) | type AssociationEmptyUser struct
type AssociationEmptyPet (line 362) | type AssociationEmptyPet struct
function TestAssociationEmptyPrimaryKey (line 367) | func TestAssociationEmptyPrimaryKey(t *testing.T) {
function TestAssociationMany2ManyAppendMap (line 399) | func TestAssociationMany2ManyAppendMap(t *testing.T) {
function TestAssociationMany2ManyReplaceMap (line 438) | func TestAssociationMany2ManyReplaceMap(t *testing.T) {
FILE: tests/benchmark_test.go
function BenchmarkCreate (line 10) | func BenchmarkCreate(b *testing.B) {
function BenchmarkFind (line 19) | func BenchmarkFind(b *testing.B) {
function BenchmarkScan (line 28) | func BenchmarkScan(b *testing.B) {
function BenchmarkScanSlice (line 39) | func BenchmarkScanSlice(b *testing.B) {
function BenchmarkScanSlicePointer (line 53) | func BenchmarkScanSlicePointer(b *testing.B) {
function BenchmarkUpdate (line 67) | func BenchmarkUpdate(b *testing.B) {
function BenchmarkDelete (line 76) | func BenchmarkDelete(b *testing.B) {
FILE: tests/callbacks_test.go
function assertCallbacks (line 13) | func assertCallbacks(v interface{}, fnames []string) (result bool, msg s...
function getFuncName (line 26) | func getFuncName(fc interface{}) string {
function c1 (line 36) | func c1(*gorm.DB) {}
function c2 (line 37) | func c2(*gorm.DB) {}
function c3 (line 38) | func c3(*gorm.DB) {}
function c4 (line 39) | func c4(*gorm.DB) {}
function c5 (line 40) | func c5(*gorm.DB) {}
function c6 (line 41) | func c6(*gorm.DB) {}
function TestCallbacks (line 43) | func TestCallbacks(t *testing.T) {
function TestPluginCallbacks (line 176) | func TestPluginCallbacks(t *testing.T) {
function TestCallbacksGet (line 210) | func TestCallbacksGet(t *testing.T) {
function TestCallbacksRemove (line 225) | func TestCallbacksRemove(t *testing.T) {
FILE: tests/chainable_api_test.go
type testDialector (line 13) | type testDialector struct
method Name (line 15) | func (d testDialector) Name() string ...
method Initialize (line 16) | func (d testDialector) Initialize(*gorm.DB) error ...
method Migrator (line 17) | func (d testDialector) Migrator(db *gorm.DB) gorm.Migrator ...
method DataTypeOf (line 18) | func (d testDialector) DataTypeOf(*schema.Field) string ...
method DefaultValueOf (line 19) | func (d testDialector) DefaultValueOf(*schema.Field) clause.Expression...
method BindVarTo (line 20) | func (d testDialector) BindVarTo(writer clause.Writer, stmt *gorm.Stat...
method QuoteTo (line 24) | func (d testDialector) QuoteTo(writer clause.Writer, s string) ...
method Explain (line 25) | func (d testDialector) Explain(sql string, vars ...interface{}) string...
function newTestDB (line 28) | func newTestDB() *gorm.DB {
function TestChainableAPI (line 43) | func TestChainableAPI(t *testing.T) {
FILE: tests/connection_test.go
function TestWithSingleConnection (line 10) | func TestWithSingleConnection(t *testing.T) {
function getSetSQL (line 38) | func getSetSQL(driverName string) (string, string) {
FILE: tests/connpool_test.go
type wrapperTx (line 15) | type wrapperTx struct
method PrepareContext (line 20) | func (c *wrapperTx) PrepareContext(ctx context.Context, query string) ...
method ExecContext (line 25) | func (c *wrapperTx) ExecContext(ctx context.Context, query string, arg...
method QueryContext (line 30) | func (c *wrapperTx) QueryContext(ctx context.Context, query string, ar...
method QueryRowContext (line 35) | func (c *wrapperTx) QueryRowContext(ctx context.Context, query string,...
type wrapperConnPool (line 40) | type wrapperConnPool struct
method Ping (line 46) | func (c *wrapperConnPool) Ping() error {
method BeginTx (line 57) | func (c *wrapperConnPool) BeginTx(ctx context.Context, opts *sql.TxOpt...
method PrepareContext (line 65) | func (c *wrapperConnPool) PrepareContext(ctx context.Context, query st...
method ExecContext (line 70) | func (c *wrapperConnPool) ExecContext(ctx context.Context, query strin...
method QueryContext (line 75) | func (c *wrapperConnPool) QueryContext(ctx context.Context, query stri...
method QueryRowContext (line 80) | func (c *wrapperConnPool) QueryRowContext(ctx context.Context, query s...
function TestConnPoolWrapper (line 85) | func TestConnPoolWrapper(t *testing.T) {
FILE: tests/count_test.go
function TestCountWithGroup (line 13) | func TestCountWithGroup(t *testing.T) {
function TestCount (line 39) | func TestCount(t *testing.T) {
FILE: tests/create_test.go
function TestCreate (line 16) | func TestCreate(t *testing.T) {
function TestCreateInBatches (line 62) | func TestCreateInBatches(t *testing.T) {
function TestCreateInBatchesWithDefaultSize (line 91) | func TestCreateInBatchesWithDefaultSize(t *testing.T) {
function TestCreateFromMap (line 120) | func TestCreateFromMap(t *testing.T) {
function TestCreateWithAssociations (line 159) | func TestCreateWithAssociations(t *testing.T) {
function TestBulkCreateWithAssociations (line 182) | func TestBulkCreateWithAssociations(t *testing.T) {
function TestBulkCreatePtrDataWithAssociations (line 213) | func TestBulkCreatePtrDataWithAssociations(t *testing.T) {
function TestPolymorphicHasOne (line 242) | func TestPolymorphicHasOne(t *testing.T) {
function TestCreateEmptyStruct (line 353) | func TestCreateEmptyStruct(t *testing.T) {
function TestCreateEmptySlice (line 368) | func TestCreateEmptySlice(t *testing.T) {
function TestCreateInvalidSlice (line 380) | func TestCreateInvalidSlice(t *testing.T) {
function TestCreateWithExistingTimestamp (line 392) | func TestCreateWithExistingTimestamp(t *testing.T) {
function TestCreateWithNowFuncOverride (line 409) | func TestCreateWithNowFuncOverride(t *testing.T) {
function TestCreateWithNoGORMPrimaryKey (line 431) | func TestCreateWithNoGORMPrimaryKey(t *testing.T) {
function TestSelectWithCreate (line 449) | func TestSelectWithCreate(t *testing.T) {
function TestOmitWithCreate (line 465) | func TestOmitWithCreate(t *testing.T) {
function TestFirstOrCreateNotExistsTable (line 497) | func TestFirstOrCreateNotExistsTable(t *testing.T) {
function TestFirstOrCreateWithPrimaryKey (line 504) | func TestFirstOrCreateWithPrimaryKey(t *testing.T) {
function TestCreateFromSubQuery (line 523) | func TestCreateFromSubQuery(t *testing.T) {
function TestCreateNilPointer (line 546) | func TestCreateNilPointer(t *testing.T) {
function TestFirstOrCreateRowsAffected (line 555) | func TestFirstOrCreateRowsAffected(t *testing.T) {
function TestCreateWithAutoIncrementCompositeKey (line 569) | func TestCreateWithAutoIncrementCompositeKey(t *testing.T) {
function TestCreateOnConflictWithDefaultNull (line 601) | func TestCreateOnConflictWithDefaultNull(t *testing.T) {
function TestCreateFromMapWithoutPK (line 637) | func TestCreateFromMapWithoutPK(t *testing.T) {
function TestCreateFromMapWithTable (line 732) | func TestCreateFromMapWithTable(t *testing.T) {
FILE: tests/customize_field_test.go
function TestCustomizeColumn (line 11) | func TestCustomizeColumn(t *testing.T) {
function TestCustomColumnAndIgnoredFieldClash (line 46) | func TestCustomColumnAndIgnoredFieldClash(t *testing.T) {
function TestCustomizeField (line 61) | func TestCustomizeField(t *testing.T) {
FILE: tests/default_value_test.go
function TestDefaultValue (line 10) | func TestDefaultValue(t *testing.T) {
FILE: tests/delete_test.go
function TestDelete (line 12) | func TestDelete(t *testing.T) {
function TestDeleteWithTable (line 61) | func TestDeleteWithTable(t *testing.T) {
function TestInlineCondDelete (line 104) | func TestInlineCondDelete(t *testing.T) {
function TestBlockGlobalDelete (line 122) | func TestBlockGlobalDelete(t *testing.T) {
function TestDeleteWithAssociations (line 132) | func TestDeleteWithAssociations(t *testing.T) {
function TestDeleteAssociationsWithUnscoped (line 156) | func TestDeleteAssociationsWithUnscoped(t *testing.T) {
function TestDeleteSliceWithAssociations (line 180) | func TestDeleteSliceWithAssociations(t *testing.T) {
function TestSoftDeleteReturning (line 210) | func TestSoftDeleteReturning(t *testing.T) {
function TestDeleteReturning (line 235) | func TestDeleteReturning(t *testing.T) {
FILE: tests/distinct_test.go
function TestDistinct (line 11) | func TestDistinct(t *testing.T) {
FILE: tests/embedded_struct_test.go
function TestEmbeddedStruct (line 15) | func TestEmbeddedStruct(t *testing.T) {
function TestEmbeddedPointerTypeStruct (line 101) | func TestEmbeddedPointerTypeStruct(t *testing.T) {
type Content (line 188) | type Content struct
method Value (line 192) | func (c Content) Value() (driver.Value, error) {
method Scan (line 198) | func (c *Content) Scan(src interface{}) error {
function TestEmbeddedScanValuer (line 220) | func TestEmbeddedScanValuer(t *testing.T) {
function TestEmbeddedRelations (line 238) | func TestEmbeddedRelations(t *testing.T) {
function TestEmbeddedTagSetting (line 260) | func TestEmbeddedTagSetting(t *testing.T) {
FILE: tests/error_translator_test.go
function TestDialectorWithErrorTranslatorSupport (line 11) | func TestDialectorWithErrorTranslatorSupport(t *testing.T) {
function TestSupportedDialectorWithErrDuplicatedKey (line 31) | func TestSupportedDialectorWithErrDuplicatedKey(t *testing.T) {
function TestSupportedDialectorWithErrForeignKeyViolated (line 64) | func TestSupportedDialectorWithErrForeignKeyViolated(t *testing.T) {
FILE: tests/gaussdb_test.go
function TestGaussDBReturningIDWhichHasStringType (line 14) | func TestGaussDBReturningIDWhichHasStringType(t *testing.T) {
function TestGaussDB (line 66) | func TestGaussDB(t *testing.T) {
function TestGaussDBMany2ManyWithDefaultValueUUID (line 160) | func TestGaussDBMany2ManyWithDefaultValueUUID(t *testing.T) {
function TestGaussDBOnConstraint (line 186) | func TestGaussDBOnConstraint(t *testing.T) {
function TestGaussDBAlterColumnDataType (line 237) | func TestGaussDBAlterColumnDataType(t *testing.T) {
FILE: tests/generics_test.go
function TestGenericsCreate (line 22) | func TestGenericsCreate(t *testing.T) {
function TestGenericsCreateInBatches (line 104) | func TestGenericsCreateInBatches(t *testing.T) {
function TestGenericsExecAndUpdate (line 146) | func TestGenericsExecAndUpdate(t *testing.T) {
function TestGenericsRow (line 192) | func TestGenericsRow(t *testing.T) {
function TestGenericsDelete (line 272) | func TestGenericsDelete(t *testing.T) {
function TestGenericsFindInBatches (line 294) | func TestGenericsFindInBatches(t *testing.T) {
function TestGenericsScopes (line 326) | func TestGenericsScopes(t *testing.T) {
function TestGenericsJoins (line 362) | func TestGenericsJoins(t *testing.T) {
function TestGenericsNestedJoins (line 461) | func TestGenericsNestedJoins(t *testing.T) {
function TestGenericsPreloads (line 537) | func TestGenericsPreloads(t *testing.T) {
function TestGenericsNestedPreloads (line 658) | func TestGenericsNestedPreloads(t *testing.T) {
function TestGenericsDistinct (line 709) | func TestGenericsDistinct(t *testing.T) {
function TestGenericsSetCreate (line 741) | func TestGenericsSetCreate(t *testing.T) {
function TestGenericsSetUpdate (line 764) | func TestGenericsSetUpdate(t *testing.T) {
function TestGenericsGroupHaving (line 799) | func TestGenericsGroupHaving(t *testing.T) {
function TestGenericsSubQuery (line 823) | func TestGenericsSubQuery(t *testing.T) {
function TestGenericsUpsert (line 855) | func TestGenericsUpsert(t *testing.T) {
function TestGenericsWithResult (line 892) | func TestGenericsWithResult(t *testing.T) {
function TestGenericsReuse (line 907) | func TestGenericsReuse(t *testing.T) {
function TestGenericsWithTransaction (line 946) | func TestGenericsWithTransaction(t *testing.T) {
function TestGenericsToSQL (line 977) | func TestGenericsToSQL(t *testing.T) {
function TestGenericsScanUUID (line 989) | func TestGenericsScanUUID(t *testing.T) {
function TestGenericsCount (line 1011) | func TestGenericsCount(t *testing.T) {
function TestGenericsUpdate (line 1021) | func TestGenericsUpdate(t *testing.T) {
function TestGenericsUpdates (line 1031) | func TestGenericsUpdates(t *testing.T) {
function TestGenericsDeleteAPI (line 1041) | func TestGenericsDeleteAPI(t *testing.T) {
function TestGenericsAssociation (line 1051) | func TestGenericsAssociation(t *testing.T) {
function TestGenericsAssociationSlice (line 1101) | func TestGenericsAssociationSlice(t *testing.T) {
FILE: tests/gorm_test.go
function TestOpen (line 11) | func TestOpen(t *testing.T) {
function TestReturningWithNullToZeroValues (line 19) | func TestReturningWithNullToZeroValues(t *testing.T) {
FILE: tests/group_by_test.go
function TestGroupBy (line 9) | func TestGroupBy(t *testing.T) {
FILE: tests/helper_test.go
type Config (line 16) | type Config struct
function GetUser (line 29) | func GetUser(name string, config Config) *User {
function CheckPetUnscoped (line 84) | func CheckPetUnscoped(t *testing.T, pet Pet, expect Pet) {
function CheckPet (line 88) | func CheckPet(t *testing.T, pet Pet, expect Pet) {
function doCheckPet (line 92) | func doCheckPet(t *testing.T, pet Pet, expect Pet, unscoped bool) {
function CheckUserUnscoped (line 112) | func CheckUserUnscoped(t *testing.T, user User, expect User) {
function CheckUser (line 116) | func CheckUser(t *testing.T, user User, expect User) {
function doCheckUser (line 120) | func doCheckUser(t *testing.T, user User, expect User, unscoped bool) {
function tidbSkip (line 270) | func tidbSkip(t *testing.T, reason string) {
function isTiDB (line 276) | func isTiDB() bool {
function isMysql (line 280) | func isMysql() bool {
function isSqlite (line 284) | func isSqlite() bool {
function db (line 288) | func db(unscoped bool) *gorm.DB {
FILE: tests/hooks_test.go
type Product (line 15) | type Product struct
method BeforeCreate (line 31) | func (s *Product) BeforeCreate(tx *gorm.DB) (err error) {
method BeforeUpdate (line 39) | func (s *Product) BeforeUpdate(tx *gorm.DB) (err error) {
method BeforeSave (line 47) | func (s *Product) BeforeSave(tx *gorm.DB) (err error) {
method AfterFind (line 55) | func (s *Product) AfterFind(tx *gorm.DB) (err error) {
method AfterCreate (line 60) | func (s *Product) AfterCreate(tx *gorm.DB) (err error) {
method AfterUpdate (line 64) | func (s *Product) AfterUpdate(tx *gorm.DB) (err error) {
method AfterSave (line 69) | func (s *Product) AfterSave(tx *gorm.DB) (err error) {
method BeforeDelete (line 77) | func (s *Product) BeforeDelete(tx *gorm.DB) (err error) {
method AfterDelete (line 85) | func (s *Product) AfterDelete(tx *gorm.DB) (err error) {
method GetCallTimes (line 93) | func (s *Product) GetCallTimes() []int64 {
function TestRunCallbacks (line 97) | func TestRunCallbacks(t *testing.T) {
function TestCallbacksWithErrors (line 149) | func TestCallbacksWithErrors(t *testing.T) {
type Product2 (line 215) | type Product2 struct
method BeforeCreate (line 223) | func (s Product2) BeforeCreate(tx *gorm.DB) (err error) {
method BeforeUpdate (line 238) | func (s *Product2) BeforeUpdate(tx *gorm.DB) (err error) {
function TestUseDBInHooks (line 243) | func TestUseDBInHooks(t *testing.T) {
type Product3 (line 300) | type Product3 struct
method BeforeCreate (line 308) | func (s Product3) BeforeCreate(tx *gorm.DB) (err error) {
method BeforeUpdate (line 313) | func (s Product3) BeforeUpdate(tx *gorm.DB) (err error) {
function TestSetColumn (line 325) | func TestSetColumn(t *testing.T) {
function TestHooksForSlice (line 409) | func TestHooksForSlice(t *testing.T) {
type Product4 (line 460) | type Product4 struct
type ProductItem (line 469) | type ProductItem struct
method BeforeCreate (line 476) | func (pi ProductItem) BeforeCreate(*gorm.DB) error {
method AfterFind (line 483) | func (pi *ProductItem) AfterFind(*gorm.DB) error {
function TestFailedToSaveAssociationShouldRollback (line 488) | func TestFailedToSaveAssociationShouldRollback(t *testing.T) {
type Product5 (line 520) | type Product5 struct
method BeforeUpdate (line 527) | func (p *Product5) BeforeUpdate(*gorm.DB) error {
function TestUpdateCallbacks (line 532) | func TestUpdateCallbacks(t *testing.T) {
type Product6 (line 572) | type Product6 struct
method BeforeDelete (line 583) | func (p *Product6) BeforeDelete(tx *gorm.DB) error {
type ProductItem2 (line 578) | type ProductItem2 struct
function TestPropagateUnscoped (line 590) | func TestPropagateUnscoped(t *testing.T) {
FILE: tests/joins_table_test.go
type Person (line 11) | type Person struct
type Address (line 18) | type Address struct
type PersonAddress (line 23) | type PersonAddress struct
function TestOverrideJoinTable (line 30) | func TestOverrideJoinTable(t *testing.T) {
FILE: tests/joins_test.go
function TestJoins (line 14) | func TestJoins(t *testing.T) {
function TestJoinsForSlice (line 27) | func TestJoinsForSlice(t *testing.T) {
function TestJoinConds (line 61) | func TestJoinConds(t *testing.T) {
function TestJoinOn (line 115) | func TestJoinOn(t *testing.T) {
function TestJoinsWithSelect (line 136) | func TestJoinsWithSelect(t *testing.T) {
function TestJoinWithOmit (line 163) | func TestJoinWithOmit(t *testing.T) {
function TestJoinCount (line 179) | func TestJoinCount(t *testing.T) {
function TestJoinWithSoftDeleted (line 208) | func TestJoinWithSoftDeleted(t *testing.T) {
function TestInnerJoins (line 237) | func TestInnerJoins(t *testing.T) {
function TestJoinWithSameColumnName (line 259) | func TestJoinWithSameColumnName(t *testing.T) {
function TestJoinArgsWithDB (line 290) | func TestJoinArgsWithDB(t *testing.T) {
function TestNestedJoins (line 333) | func TestNestedJoins(t *testing.T) {
function TestJoinsPreload_Issue7013 (line 408) | func TestJoinsPreload_Issue7013(t *testing.T) {
function TestJoinsPreload_Issue7013_RelationEmpty (line 428) | func TestJoinsPreload_Issue7013_RelationEmpty(t *testing.T) {
function TestJoinsPreload_Issue7013_NoEntries (line 467) | func TestJoinsPreload_Issue7013_NoEntries(t *testing.T) {
FILE: tests/lru_test.go
function TestLRU_Add_ExistingKey_UpdatesValueAndExpiresAt (line 16) | func TestLRU_Add_ExistingKey_UpdatesValueAndExpiresAt(t *testing.T) {
function TestLRU_Add_NewKey_AddsEntry (line 26) | func TestLRU_Add_NewKey_AddsEntry(t *testing.T) {
function TestLRU_Add_ExceedsSize_RemovesOldest (line 35) | func TestLRU_Add_ExceedsSize_RemovesOldest(t *testing.T) {
function TestLRU_Add_UnlimitedSize_NoEviction (line 46) | func TestLRU_Add_UnlimitedSize_NoEviction(t *testing.T) {
function TestLRU_Add_Eviction (line 57) | func TestLRU_Add_Eviction(t *testing.T) {
function BenchmarkLRU_Rand_NoExpire (line 69) | func BenchmarkLRU_Rand_NoExpire(b *testing.B) {
function BenchmarkLRU_Freq_NoExpire (line 94) | func BenchmarkLRU_Freq_NoExpire(b *testing.B) {
function BenchmarkLRU_Rand_WithExpire (line 122) | func BenchmarkLRU_Rand_WithExpire(b *testing.B) {
function BenchmarkLRU_Freq_WithExpire (line 147) | func BenchmarkLRU_Freq_WithExpire(b *testing.B) {
function TestLRUNoPurge (line 175) | func TestLRUNoPurge(t *testing.T) {
function TestLRUEdgeCases (line 222) | func TestLRUEdgeCases(t *testing.T) {
function TestLRU_Values (line 243) | func TestLRU_Values(t *testing.T) {
function TestLRUWithPurge (line 263) | func TestLRUWithPurge(t *testing.T) {
function TestLRUWithPurgeEnforcedBySize (line 328) | func TestLRUWithPurgeEnforcedBySize(t *testing.T) {
function TestLRUConcurrency (line 351) | func TestLRUConcurrency(t *testing.T) {
function TestLRUInvalidateAndEvict (line 367) | func TestLRUInvalidateAndEvict(t *testing.T) {
function TestLoadingExpired (line 398) | func TestLoadingExpired(t *testing.T) {
function TestLRURemoveOldest (line 454) | func TestLRURemoveOldest(t *testing.T) {
function getRand (line 524) | func getRand(tb testing.TB) int64 {
FILE: tests/main_test.go
function TestExceptionsWithInvalidSql (line 9) | func TestExceptionsWithInvalidSql(t *testing.T) {
function TestSetAndGet (line 43) | func TestSetAndGet(t *testing.T) {
FILE: tests/migrate_test.go
function TestMigrate (line 26) | func TestMigrate(t *testing.T) {
function TestAutoMigrateInt8PGAndGaussDB (line 86) | func TestAutoMigrateInt8PGAndGaussDB(t *testing.T) {
function TestAutoMigrateSelfReferential (line 124) | func TestAutoMigrateSelfReferential(t *testing.T) {
function TestAutoMigrateNullable (line 143) | func TestAutoMigrateNullable(t *testing.T) {
function TestSmartMigrateColumn (line 185) | func TestSmartMigrateColumn(t *testing.T) {
function TestSmartMigrateColumnGaussDB (line 272) | func TestSmartMigrateColumnGaussDB(t *testing.T) {
function TestMigrateWithColumnComment (line 359) | func TestMigrateWithColumnComment(t *testing.T) {
function TestMigrateWithIndexComment (line 374) | func TestMigrateWithIndexComment(t *testing.T) {
function TestMigrateWithUniqueIndex (line 393) | func TestMigrateWithUniqueIndex(t *testing.T) {
function TestMigrateTable (line 423) | func TestMigrateTable(t *testing.T) {
function TestMigrateWithQuotedIndex (line 456) | func TestMigrateWithQuotedIndex(t *testing.T) {
function TestMigrateIndexes (line 475) | func TestMigrateIndexes(t *testing.T) {
function TestTiDBMigrateColumns (line 525) | func TestTiDBMigrateColumns(t *testing.T) {
function TestMigrateColumns (line 664) | func TestMigrateColumns(t *testing.T) {
function TestMigrateConstraint (line 804) | func TestMigrateConstraint(t *testing.T) {
type DynamicUser (line 830) | type DynamicUser struct
function TestMigrateIndexesWithDynamicTableName (line 838) | func TestMigrateIndexesWithDynamicTableName(t *testing.T) {
function TestMigrateColumnOrder (line 871) | func TestMigrateColumnOrder(t *testing.T) {
function TestMigrateSerialColumn (line 940) | func TestMigrateSerialColumn(t *testing.T) {
function TestMigrateWithSpecialName (line 1002) | func TestMigrateWithSpecialName(t *testing.T) {
function TestMigrateAutoIncrement (line 1023) | func TestMigrateAutoIncrement(t *testing.T) {
function TestPrimarykeyID (line 1065) | func TestPrimarykeyID(t *testing.T) {
function TestPrimarykeyIDGaussDB (line 1100) | func TestPrimarykeyIDGaussDB(t *testing.T) {
function TestCurrentTimestamp (line 1136) | func TestCurrentTimestamp(t *testing.T) {
function TestUniqueColumn (line 1163) | func TestUniqueColumn(t *testing.T) {
function findColumnType (line 1283) | func findColumnType(dest interface{}, columnName string) (
function TestInvalidCachedPlanSimpleProtocol (line 1301) | func TestInvalidCachedPlanSimpleProtocol(t *testing.T) {
function TestInvalidCachedPlanSimpleProtocolGaussDB (line 1337) | func TestInvalidCachedPlanSimpleProtocolGaussDB(t *testing.T) {
function TestDifferentTypeWithoutDeclaredLength (line 1373) | func TestDifferentTypeWithoutDeclaredLength(t *testing.T) {
function TestMigrateArrayTypeModel (line 1412) | func TestMigrateArrayTypeModel(t *testing.T) {
type mockMigrator (line 1448) | type mockMigrator struct
method AlterColumn (line 1452) | func (mm mockMigrator) AlterColumn(dst interface{}, field string) error {
function TestMigrateDonotAlterColumn (line 1460) | func TestMigrateDonotAlterColumn(t *testing.T) {
function TestMigrateSameEmbeddedFieldName (line 1492) | func TestMigrateSameEmbeddedFieldName(t *testing.T) {
function TestMigrateWithDefaultValue (line 1530) | func TestMigrateWithDefaultValue(t *testing.T) {
function TestMigrateMySQLWithCustomizedTypes (line 1605) | func TestMigrateMySQLWithCustomizedTypes(t *testing.T) {
function TestMigrateIgnoreRelations (line 1637) | func TestMigrateIgnoreRelations(t *testing.T) {
function TestMigrateView (line 1701) | func TestMigrateView(t *testing.T) {
function TestMigrateExistingBoolColumnPGAndGaussDB (line 1735) | func TestMigrateExistingBoolColumnPGAndGaussDB(t *testing.T) {
function TestTableType (line 1794) | func TestTableType(t *testing.T) {
function TestMigrateWithUniqueIndexAndUnique (line 1840) | func TestMigrateWithUniqueIndexAndUnique(t *testing.T) {
function testAutoMigrateDecimal (line 2055) | func testAutoMigrateDecimal(t *testing.T, model1, model2 any) []string {
function decimalColumnsTest (line 2090) | func decimalColumnsTest[T, T2 any](t *testing.T, expectedSql []string) {
function TestAutoMigrateDecimal (line 2111) | func TestAutoMigrateDecimal(t *testing.T) {
FILE: tests/multi_primary_keys_test.go
type Blog (line 12) | type Blog struct
type Tag (line 22) | type Tag struct
function compareTags (line 29) | func compareTags(tags []Tag, contents []string) bool {
function TestManyToManyWithMultiPrimaryKeys (line 39) | func TestManyToManyWithMultiPrimaryKeys(t *testing.T) {
function TestManyToManyWithCustomizedForeignKeys (line 137) | func TestManyToManyWithCustomizedForeignKeys(t *testing.T) {
function TestManyToManyWithCustomizedForeignKeys2 (line 265) | func TestManyToManyWithCustomizedForeignKeys2(t *testing.T) {
function TestCompositePrimaryKeysAssociations (line 422) | func TestCompositePrimaryKeysAssociations(t *testing.T) {
FILE: tests/named_argument_test.go
function TestNamedArg (line 12) | func TestNamedArg(t *testing.T) {
FILE: tests/named_polymorphic_test.go
type Hamster (line 9) | type Hamster struct
function TestNamedPolymorphic (line 16) | func TestNamedPolymorphic(t *testing.T) {
FILE: tests/non_std_test.go
type Animal (line 8) | type Animal struct
function TestNonStdPrimaryKeyAndDefaultValues (line 18) | func TestNonStdPrimaryKeyAndDefaultValues(t *testing.T) {
FILE: tests/postgres_test.go
function TestPostgresReturningIDWhichHasStringType (line 14) | func TestPostgresReturningIDWhichHasStringType(t *testing.T) {
function TestPostgres (line 64) | func TestPostgres(t *testing.T) {
type Post (line 156) | type Post struct
type Category (line 162) | type Category struct
function TestMany2ManyWithDefaultValueUUID (line 168) | func TestMany2ManyWithDefaultValueUUID(t *testing.T) {
function TestPostgresOnConstraint (line 193) | func TestPostgresOnConstraint(t *testing.T) {
type CompanyNew (line 243) | type CompanyNew struct
function TestAlterColumnDataType (line 248) | func TestAlterColumnDataType(t *testing.T) {
FILE: tests/preload_suits_test.go
function toJSONString (line 14) | func toJSONString(v interface{}) []byte {
function TestNestedPreload1 (line 19) | func TestNestedPreload1(t *testing.T) {
function TestNestedPreload2 (line 61) | func TestNestedPreload2(t *testing.T) {
function TestNestedPreload3 (line 113) | func TestNestedPreload3(t *testing.T) {
function TestNestedPreload4 (line 156) | func TestNestedPreload4(t *testing.T) {
function TestNestedPreload5 (line 202) | func TestNestedPreload5(t *testing.T) {
function TestNestedPreload6 (line 245) | func TestNestedPreload6(t *testing.T) {
function TestNestedPreload7 (line 317) | func TestNestedPreload7(t *testing.T) {
function TestNestedPreload8 (line 371) | func TestNestedPreload8(t *testing.T) {
function TestNestedPreload9 (line 428) | func TestNestedPreload9(t *testing.T) {
type LevelA1 (line 522) | type LevelA1 struct
type LevelA2 (line 527) | type LevelA2 struct
type LevelA3 (line 533) | type LevelA3 struct
function TestNestedPreload10 (line 542) | func TestNestedPreload10(t *testing.T) {
type LevelB1 (line 584) | type LevelB1 struct
type LevelB2 (line 590) | type LevelB2 struct
type LevelB3 (line 595) | type LevelB3 struct
function TestNestedPreload11 (line 603) | func TestNestedPreload11(t *testing.T) {
type LevelC1 (line 635) | type LevelC1 struct
type LevelC2 (line 641) | type LevelC2 struct
type LevelC3 (line 647) | type LevelC3 struct
function TestNestedPreload12 (line 654) | func TestNestedPreload12(t *testing.T) {
function TestManyToManyPreloadWithMultiPrimaryKeys (line 694) | func TestManyToManyPreloadWithMultiPrimaryKeys(t *testing.T) {
function TestManyToManyPreloadForNestedPointer (line 788) | func TestManyToManyPreloadForNestedPointer(t *testing.T) {
function TestNestedManyToManyPreload (line 889) | func TestNestedManyToManyPreload(t *testing.T) {
function TestNestedManyToManyPreload2 (line 950) | func TestNestedManyToManyPreload2(t *testing.T) {
function TestNestedManyToManyPreload3 (line 1005) | func TestNestedManyToManyPreload3(t *testing.T) {
function TestNestedManyToManyPreload3ForStruct (line 1077) | func TestNestedManyToManyPreload3ForStruct(t *testing.T) {
function TestNestedManyToManyPreload4 (line 1150) | func TestNestedManyToManyPreload4(t *testing.T) {
function TestManyToManyPreloadForPointer (line 1204) | func TestManyToManyPreloadForPointer(t *testing.T) {
function TestNilPointerSlice (line 1286) | func TestNilPointerSlice(t *testing.T) {
function TestNilPointerSlice2 (line 1350) | func TestNilPointerSlice2(t *testing.T) {
function TestPrefixedPreloadDuplication (line 1393) | func TestPrefixedPreloadDuplication(t *testing.T) {
function TestPreloadManyToManyCallbacks (line 1478) | func TestPreloadManyToManyCallbacks(t *testing.T) {
FILE: tests/preload_test.go
function TestPreloadWithAssociations (line 18) | func TestPreloadWithAssociations(t *testing.T) {
function TestNestedPreload (line 55) | func TestNestedPreload(t *testing.T) {
function TestNestedPreloadForSlice (line 79) | func TestNestedPreloadForSlice(t *testing.T) {
function TestPreloadWithConds (line 109) | func TestPreloadWithConds(t *testing.T) {
function TestNestedPreloadWithConds (line 167) | func TestNestedPreloadWithConds(t *testing.T) {
function TestPreloadEmptyData (line 217) | func TestPreloadEmptyData(t *testing.T) {
function TestPreloadGoroutine (line 239) | func TestPreloadGoroutine(t *testing.T) {
function TestPreloadWithDiffModel (line 257) | func TestPreloadWithDiffModel(t *testing.T) {
function TestNestedPreloadWithUnscoped (line 275) | func TestNestedPreloadWithUnscoped(t *testing.T) {
function TestNestedPreloadWithNestedJoin (line 312) | func TestNestedPreloadWithNestedJoin(t *testing.T) {
function TestMergeNestedPreloadWithNestedJoin (line 389) | func TestMergeNestedPreloadWithNestedJoin(t *testing.T) {
function TestNestedPreloadWithPointerJoin (line 443) | func TestNestedPreloadWithPointerJoin(t *testing.T) {
function TestEmbedPreload (line 495) | func TestEmbedPreload(t *testing.T) {
FILE: tests/prepared_stmt_test.go
function TestPreparedStmt (line 14) | func TestPreparedStmt(t *testing.T) {
function TestPreparedStmtFromTransaction (line 56) | func TestPreparedStmtFromTransaction(t *testing.T) {
function TestPreparedStmtLruFromTransaction (line 94) | func TestPreparedStmtLruFromTransaction(t *testing.T) {
function TestPreparedStmtDeadlock (line 153) | func TestPreparedStmtDeadlock(t *testing.T) {
function TestPreparedStmtInTransaction (line 188) | func TestPreparedStmtInTransaction(t *testing.T) {
function TestPreparedStmtClose (line 204) | func TestPreparedStmtClose(t *testing.T) {
function isUsingClosedConnError (line 230) | func isUsingClosedConnError(err error) bool {
function TestPreparedStmtConcurrentClose (line 237) | func TestPreparedStmtConcurrentClose(t *testing.T) {
FILE: tests/query_test.go
function TestFind (line 20) | func TestFind(t *testing.T) {
function TestQueryWithAssociation (line 250) | func TestQueryWithAssociation(t *testing.T) {
function TestFindInBatches (line 268) | func TestFindInBatches(t *testing.T) {
function TestFindInBatchesWithOffsetLimit (line 320) | func TestFindInBatchesWithOffsetLimit(t *testing.T) {
function TestFindInBatchesWithError (line 382) | func TestFindInBatchesWithError(t *testing.T) {
function TestFillSmallerStruct (line 421) | func TestFillSmallerStruct(t *testing.T) {
function TestFillSmallerStructWithAllFields (line 477) | func TestFillSmallerStructWithAllFields(t *testing.T) {
function TestNot (line 510) | func TestNot(t *testing.T) {
function TestNotWithAllFields (line 569) | func TestNotWithAllFields(t *testing.T) {
function TestOr (line 616) | func TestOr(t *testing.T) {
function TestOrWithAllFields (line 666) | func TestOrWithAllFields(t *testing.T) {
type Int64 (line 687) | type Int64
method Value (line 689) | func (v Int64) Value() (driver.Value, error) {
method Scan (line 693) | func (f *Int64) Scan(v interface{}) error {
function TestPluck (line 699) | func TestPluck(t *testing.T) {
function TestSelect (line 777) | func TestSelect(t *testing.T) {
function TestOmit (line 839) | func TestOmit(t *testing.T) {
function TestOmitWithAllFields (line 854) | func TestOmitWithAllFields(t *testing.T) {
function TestMapColumns (line 878) | func TestMapColumns(t *testing.T) {
function TestPluckWithSelect (line 900) | func TestPluckWithSelect(t *testing.T) {
function TestSelectWithVariables (line 919) | func TestSelectWithVariables(t *testing.T) {
function TestSelectWithArrayInput (line 934) | func TestSelectWithArrayInput(t *testing.T) {
function TestCustomizedTypePrimaryKey (line 945) | func TestCustomizedTypePrimaryKey(t *testing.T) {
function TestStringPrimaryKeyForNumericValueStartingWithZero (line 979) | func TestStringPrimaryKeyForNumericValueStartingWithZero(t *testing.T) {
function TestSearchWithEmptyChain (line 999) | func TestSearchWithEmptyChain(t *testing.T) {
function TestOrder (line 1019) | func TestOrder(t *testing.T) {
function TestOrderWithAllFields (line 1052) | func TestOrderWithAllFields(t *testing.T) {
function TestLimit (line 1077) | func TestLimit(t *testing.T) {
function TestOffset (line 1097) | func TestOffset(t *testing.T) {
function TestSearchWithMap (line 1116) | func TestSearchWithMap(t *testing.T) {
function TestSearchWithStruct (line 1163) | func TestSearchWithStruct(t *testing.T) {
function TestSubQuery (line 1187) | func TestSubQuery(t *testing.T) {
function TestSubQueryWithRaw (line 1213) | func TestSubQueryWithRaw(t *testing.T) {
function TestSubQueryWithHaving (line 1262) | func TestSubQueryWithHaving(t *testing.T) {
function TestScanNullValue (line 1280) | func TestScanNullValue(t *testing.T) {
function TestQueryWithTableAndConditions (line 1312) | func TestQueryWithTableAndConditions(t *testing.T) {
function TestQueryWithTableAndConditionsAndAllFields (line 1320) | func TestQueryWithTableAndConditionsAndAllFields(t *testing.T) {
type DoubleInt64 (line 1330) | type DoubleInt64 struct
method Scan (line 1334) | func (t *DoubleInt64) Scan(val interface{}) error {
function TestQueryScannerWithSingleColumn (line 1345) | func TestQueryScannerWithSingleColumn(t *testing.T) {
function TestQueryResetNullValue (line 1366) | func TestQueryResetNullValue(t *testing.T) {
function TestQueryError (line 1443) | func TestQueryError(t *testing.T) {
function TestQueryScanToArray (line 1457) | func TestQueryScanToArray(t *testing.T) {
FILE: tests/scan_test.go
type PersonAddressInfo (line 14) | type PersonAddressInfo struct
function TestScan (line 19) | func TestScan(t *testing.T) {
function TestScanRows (line 122) | func TestScanRows(t *testing.T) {
function TestScanRowsNullValuesScanToFieldDefault (line 166) | func TestScanRowsNullValuesScanToFieldDefault(t *testing.T) {
function TestScanToEmbedded (line 264) | func TestScanToEmbedded(t *testing.T) {
FILE: tests/scanner_valuer_test.go
function TestScannerValuer (line 21) | func TestScannerValuer(t *testing.T) {
function TestScannerValuerWithFirstOrCreate (line 68) | func TestScannerValuerWithFirstOrCreate(t *testing.T) {
function TestInvalidValuer (line 111) | func TestInvalidValuer(t *testing.T) {
type ScannerValuerStruct (line 143) | type ScannerValuerStruct struct
type EncryptedData (line 165) | type EncryptedData
method Scan (line 167) | func (data *EncryptedData) Scan(value interface{}) error {
method Value (line 183) | func (data EncryptedData) Value() (driver.Value, error) {
type Num (line 193) | type Num
method Scan (line 195) | func (i *Num) Scan(src interface{}) error {
type StringsSlice (line 208) | type StringsSlice
method Value (line 210) | func (l StringsSlice) Value() (driver.Value, error) {
method Scan (line 215) | func (l *StringsSlice) Scan(input interface{}) error {
type ExampleStruct (line 226) | type ExampleStruct struct
method GormDataType (line 231) | func (ExampleStruct) GormDataType() string {
method Value (line 235) | func (s ExampleStruct) Value() (driver.Value, error) {
method Scan (line 244) | func (s *ExampleStruct) Scan(src interface{}) error {
type StructsSlice (line 255) | type StructsSlice
method Value (line 257) | func (l StructsSlice) Value() (driver.Value, error) {
method Scan (line 262) | func (l *StructsSlice) Scan(input interface{}) error {
type Role (line 273) | type Role struct
method Scan (line 277) | func (role *Role) Scan(value interface{}) error {
method Value (line 286) | func (role Role) Value() (driver.Value, error) {
method IsAdmin (line 290) | func (role Role) IsAdmin() bool {
type EmptyTime (line 294) | type EmptyTime struct
method Scan (line 298) | func (t *EmptyTime) Scan(v interface{}) error {
method Value (line 305) | func (t EmptyTime) Value() (driver.Value, error) {
type NullString (line 309) | type NullString struct
type Point (line 313) | type Point struct
method GormDataType (line 317) | func (point Point) GormDataType() string {
method GormValue (line 321) | func (point Point) GormValue(ctx context.Context, db *gorm.DB) clause....
function TestGORMValuer (line 328) | func TestGORMValuer(t *testing.T) {
FILE: tests/scopes_test.go
function NameIn1And2 (line 11) | func NameIn1And2(d *gorm.DB) *gorm.DB {
function NameIn2And3 (line 15) | func NameIn2And3(d *gorm.DB) *gorm.DB {
function NameIn (line 19) | func NameIn(names []string) func(d *gorm.DB) *gorm.DB {
function TestScopes (line 25) | func TestScopes(t *testing.T) {
function TestComplexScopes (line 76) | func TestComplexScopes(t *testing.T) {
FILE: tests/serializer_test.go
type SerializerStruct (line 17) | type SerializerStruct struct
type SerializerPostgresStruct (line 31) | type SerializerPostgresStruct struct
method TableName (line 45) | func (*SerializerPostgresStruct) TableName() string { return "serializ...
function adaptorSerializerModel (line 47) | func adaptorSerializerModel(s *SerializerStruct) interface{} {
type Roles (line 55) | type Roles
type Job (line 57) | type Job struct
type EncryptedString (line 64) | type EncryptedString
method Scan (line 66) | func (es *EncryptedString) Scan(ctx context.Context, field *schema.Fie...
method Value (line 78) | func (es EncryptedString) Value(ctx context.Context, field *schema.Fie...
type CustomSerializer (line 82) | type CustomSerializer struct
method Scan (line 90) | func (c *CustomSerializer) Scan(ctx context.Context, field *schema.Fie...
method Value (line 102) | func (c *CustomSerializer) Value(ctx context.Context, field *schema.Fi...
function NewCustomSerializer (line 86) | func NewCustomSerializer(prefix string) *CustomSerializer {
function TestSerializer (line 106) | func TestSerializer(t *testing.T) {
function TestSerializerZeroValue (line 152) | func TestSerializerZeroValue(t *testing.T) {
function TestSerializerAssignFirstOrCreate (line 181) | func TestSerializerAssignFirstOrCreate(t *testing.T) {
function TestSerializerWithAnyType (line 232) | func TestSerializerWithAnyType(t *testing.T) {
FILE: tests/soft_delete_test.go
function TestSoftDelete (line 15) | func TestSoftDelete(t *testing.T) {
function TestDeletedAtUnMarshal (line 82) | func TestDeletedAtUnMarshal(t *testing.T) {
function TestDeletedAtOneOr (line 93) | func TestDeletedAtOneOr(t *testing.T) {
function TestSoftDeleteZeroValue (line 103) | func TestSoftDeleteZeroValue(t *testing.T) {
FILE: tests/sql_builder_test.go
function TestRow (line 14) | func TestRow(t *testing.T) {
function TestRows (line 48) | func TestRows(t *testing.T) {
function TestRaw (line 72) | func TestRaw(t *testing.T) {
function TestRowsWithGroup (line 113) | func TestRowsWithGroup(t *testing.T) {
function TestQueryRaw (line 142) | func TestQueryRaw(t *testing.T) {
function TestDryRun (line 155) | func TestDryRun(t *testing.T) {
type ageInt (line 171) | type ageInt
method String (line 173) | func (ageInt) String() string {
type ageBool (line 177) | type ageBool
method String (line 179) | func (ageBool) String() string {
type ageUint64 (line 183) | type ageUint64
method String (line 185) | func (ageUint64) String() string {
type ageFloat (line 189) | type ageFloat
method String (line 191) | func (ageFloat) String() string {
function TestExplainSQL (line 195) | func TestExplainSQL(t *testing.T) {
function TestGroupConditions (line 224) | func TestGroupConditions(t *testing.T) {
function TestCombineStringConditions (line 263) | func TestCombineStringConditions(t *testing.T) {
function TestFromWithJoins (line 316) | func TestFromWithJoins(t *testing.T) {
function TestToSQL (line 360) | func TestToSQL(t *testing.T) {
function assertEqualSQL (line 459) | func assertEqualSQL(t *testing.T, expected string, actually string) {
function replaceQuoteInSQL (line 484) | func replaceQuoteInSQL(sql string) string {
FILE: tests/submodel_test.go
type Man (line 9) | type Man struct
method BeforeUpdate (line 17) | func (m *Man) BeforeUpdate(tx *gorm.DB) (err error) {
function TestSubModel (line 24) | func TestSubModel(t *testing.T) {
FILE: tests/table_test.go
type UserWithTable (line 16) | type UserWithTable struct
method TableName (line 21) | func (UserWithTable) TableName() string {
function TestTable (line 25) | func TestTable(t *testing.T) {
function TestTableWithAllFields (line 97) | func TestTableWithAllFields(t *testing.T) {
type UserWithTableNamer (line 154) | type UserWithTableNamer struct
method TableName (line 159) | func (UserWithTableNamer) TableName(namer schema.Namer) string {
function TestTableWithNamer (line 163) | func TestTableWithNamer(t *testing.T) {
function TestPostgresTableWithIdentifierLength (line 179) | func TestPostgresTableWithIdentifierLength(t *testing.T) {
function TestGaussDBTableWithIdentifierLength (line 255) | func TestGaussDBTableWithIdentifierLength(t *testing.T) {
type mockUniqueNamingStrategy (line 331) | type mockUniqueNamingStrategy struct
method UniqueName (line 336) | func (a mockUniqueNamingStrategy) UniqueName(table, column string) str...
FILE: tests/tests_test.go
function init (line 30) | func init() {
function OpenTestConnection (line 52) | func OpenTestConnection(cfg *gorm.Config) (db *gorm.DB, err error) {
function RunMigrations (line 120) | func RunMigrations() {
FILE: tests/tracer_test.go
type Tracer (line 10) | type Tracer struct
method LogMode (line 15) | func (S Tracer) LogMode(level logger.LogLevel) logger.Interface {
method Info (line 19) | func (S Tracer) Info(ctx context.Context, s string, i ...interface{}) {
method Warn (line 23) | func (S Tracer) Warn(ctx context.Context, s string, i ...interface{}) {
method Error (line 27) | func (S Tracer) Error(ctx context.Context, s string, i ...interface{}) {
method Trace (line 31) | func (S Tracer) Trace(ctx context.Context, begin time.Time, fc func() ...
FILE: tests/transaction_test.go
function TestTransaction (line 13) | func TestTransaction(t *testing.T) {
function TestCancelTransaction (line 76) | func TestCancelTransaction(t *testing.T) {
function TestTransactionWithBlock (line 95) | func TestTransactionWithBlock(t *testing.T) {
function TestTransactionRaiseErrorOnRollbackAfterCommit (line 165) | func TestTransactionRaiseErrorOnRollbackAfterCommit(t *testing.T) {
function TestTransactionWithSavePoint (line 181) | func TestTransactionWithSavePoint(t *testing.T) {
function TestNestedTransactionWithBlock (line 238) | func TestNestedTransactionWithBlock(t *testing.T) {
function TestDeeplyNestedTransactionWithBlockAndWrappedCallback (line 301) | func TestDeeplyNestedTransactionWithBlockAndWrappedCallback(t *testing.T) {
function TestDisabledNestedTransaction (line 369) | func TestDisabledNestedTransaction(t *testing.T) {
function TestTransactionOnClosedConn (line 432) | func TestTransactionOnClosedConn(t *testing.T) {
function TestTransactionWithHooks (line 453) | func TestTransactionWithHooks(t *testing.T) {
function TestTransactionWithDefaultTimeout (line 481) | func TestTransactionWithDefaultTimeout(t *testing.T) {
FILE: tests/update_belongs_to_test.go
function TestUpdateBelongsTo (line 10) | func TestUpdateBelongsTo(t *testing.T) {
FILE: tests/update_has_many_test.go
function TestUpdateHasManyAssociations (line 10) | func TestUpdateHasManyAssociations(t *testing.T) {
FILE: tests/update_has_one_test.go
function TestUpdateHasOne (line 12) | func TestUpdateHasOne(t *testing.T) {
FILE: tests/update_many2many_test.go
function TestUpdateMany2ManyAssociations (line 10) | func TestUpdateMany2ManyAssociations(t *testing.T) {
FILE: tests/update_test.go
function TestUpdate (line 17) | func TestUpdate(t *testing.T) {
function TestUpdates (line 135) | func TestUpdates(t *testing.T) {
function TestUpdateColumn (line 188) | func TestUpdateColumn(t *testing.T) {
function TestBlockGlobalUpdate (line 244) | func TestBlockGlobalUpdate(t *testing.T) {
function TestSelectWithUpdate (line 254) | func TestSelectWithUpdate(t *testing.T) {
function TestSelectWithUpdateWithMap (line 318) | func TestSelectWithUpdateWithMap(t *testing.T) {
function TestWithUpdateWithInvalidMap (line 366) | func TestWithUpdateWithInvalidMap(t *testing.T) {
function TestOmitWithUpdate (line 375) | func TestOmitWithUpdate(t *testing.T) {
function TestOmitWithUpdateWithMap (line 425) | func TestOmitWithUpdateWithMap(t *testing.T) {
function TestSelectWithUpdateColumn (line 477) | func TestSelectWithUpdateColumn(t *testing.T) {
function TestOmitWithUpdateColumn (line 502) | func TestOmitWithUpdateColumn(t *testing.T) {
function TestUpdateColumnsSkipsAssociations (line 520) | func TestUpdateColumnsSkipsAssociations(t *testing.T) {
function TestUpdatesWithBlankValues (line 547) | func TestUpdatesWithBlankValues(t *testing.T) {
function TestUpdatesTableWithIgnoredValues (line 563) | func TestUpdatesTableWithIgnoredValues(t *testing.T) {
function TestUpdateFromSubQuery (line 589) | func TestUpdateFromSubQuery(t *testing.T) {
function TestIdempotentSave (line 617) | func TestIdempotentSave(t *testing.T) {
function TestSave (line 636) | func TestSave(t *testing.T) {
function TestSaveWithPrimaryValue (line 726) | func TestSaveWithPrimaryValue(t *testing.T) {
function TestUpdateReturning (line 769) | func TestUpdateReturning(t *testing.T) {
function TestUpdateWithDiffSchema (line 800) | func TestUpdateWithDiffSchema(t *testing.T) {
type TokenOwner (line 813) | type TokenOwner struct
method BeforeSave (line 819) | func (t *TokenOwner) BeforeSave(tx *gorm.DB) error {
type Token (line 824) | type Token struct
method BeforeSave (line 829) | func (t *Token) BeforeSave(tx *gorm.DB) error {
function TestSaveWithHooks (line 834) | func TestSaveWithHooks(t *testing.T) {
function TestUpdateFrom (line 887) | func TestUpdateFrom(t *testing.T) {
FILE: tests/upsert_test.go
function TestUpsert (line 13) | func TestUpsert(t *testing.T) {
function TestUpsertSlice (line 91) | func TestUpsertSlice(t *testing.T) {
function TestUpsertSliceWithReturning (line 138) | func TestUpsertSliceWithReturning(t *testing.T) {
function TestUpsertWithSave (line 185) | func TestUpsertWithSave(t *testing.T) {
function TestFindOrInitialize (line 247) | func TestFindOrInitialize(t *testing.T) {
function TestFindOrCreate (line 294) | func TestFindOrCreate(t *testing.T) {
function TestUpdateWithMissWhere (line 360) | func TestUpdateWithMissWhere(t *testing.T) {
FILE: utils/tests/dummy_dialecter.go
type DummyDialector (line 11) | type DummyDialector struct
method Name (line 15) | func (DummyDialector) Name() string {
method Initialize (line 19) | func (DummyDialector) Initialize(db *gorm.DB) error {
method DefaultValueOf (line 30) | func (DummyDialector) DefaultValueOf(field *schema.Field) clause.Expre...
method Migrator (line 34) | func (DummyDialector) Migrator(*gorm.DB) gorm.Migrator {
method BindVarTo (line 38) | func (DummyDialector) BindVarTo(writer clause.Writer, stmt *gorm.State...
method QuoteTo (line 42) | func (DummyDialector) QuoteTo(writer clause.Writer, str string) {
method Explain (line 90) | func (DummyDialector) Explain(sql string, vars ...interface{}) string {
method DataTypeOf (line 94) | func (DummyDialector) DataTypeOf(*schema.Field) string {
method Translate (line 98) | func (d DummyDialector) Translate(err error) error {
FILE: utils/tests/models.go
type User (line 15) | type User struct
type Account (line 35) | type Account struct
type Pet (line 41) | type Pet struct
type Toy (line 48) | type Toy struct
type Tools (line 55) | type Tools struct
type Company (line 62) | type Company struct
type Language (line 67) | type Language struct
type Coupon (line 72) | type Coupon struct
type CouponProduct (line 79) | type CouponProduct struct
type Order (line 85) | type Order struct
type Parent (line 92) | type Parent struct
type Child (line 99) | type Child struct
FILE: utils/tests/utils.go
function AssertObjEqual (line 14) | func AssertObjEqual(t *testing.T, r, e interface{}, names ...string) {
function AssertEqual (line 30) | func AssertEqual(t *testing.T, got, expect interface{}) {
function Now (line 125) | func Now() *time.Time {
FILE: utils/utils.go
function init (line 16) | func init() {
function sourceDir (line 22) | func sourceDir(file string) string {
function CallerFrame (line 38) | func CallerFrame() runtime.Frame {
function FileWithLineNum (line 56) | func FileWithLineNum() string {
function IsInvalidDBNameChar (line 65) | func IsInvalidDBNameChar(c rune) bool {
function CheckTruth (line 70) | func CheckTruth(vals ...string) bool {
function ToStringKey (line 79) | func ToStringKey(values ...interface{}) string {
function Contains (line 106) | func Contains(elems []string, elem string) bool {
function AssertEqual (line 115) | func AssertEqual(x, y interface{}) bool {
function ToString (line 139) | func ToString(value interface{}) string {
constant nestedRelationSplit (line 167) | nestedRelationSplit = "__"
function NestedRelationName (line 170) | func NestedRelationName(prefix, name string) string {
function SplitNestedRelationName (line 175) | func SplitNestedRelationName(name string) []string {
function JoinNestedRelationNames (line 180) | func JoinNestedRelationNames(relationNames []string) string {
function RTrimSlice (line 185) | func RTrimSlice[T any](v []T, trimLen int) []T {
FILE: utils/utils_test.go
function TestIsInvalidDBNameChar (line 13) | func TestIsInvalidDBNameChar(t *testing.T) {
function TestCheckTruth (line 21) | func TestCheckTruth(t *testing.T) {
function TestToStringKey (line 44) | func TestToStringKey(t *testing.T) {
function TestContains (line 63) | func TestContains(t *testing.T) {
type ModifyAt (line 82) | type ModifyAt
method Value (line 85) | func (n ModifyAt) Value() (driver.Value, error) {
function TestAssertEqual (line 92) | func TestAssertEqual(t *testing.T) {
function TestToString (line 114) | func TestToString(t *testing.T) {
function TestRTrimSlice (line 142) | func TestRTrimSlice(t *testing.T) {
FILE: utils/utils_unix_test.go
function TestSourceDir (line 10) | func TestSourceDir(t *testing.T) {
FILE: utils/utils_windows_test.go
function TestSourceDir (line 7) | func TestSourceDir(t *testing.T) {
Condensed preview — 184 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,405K chars).
[
{
"path": ".github/FUNDING.yml",
"chars": 102,
"preview": "# These are supported funding model platforms\n\ngithub: [jinzhu]\npatreon: jinzhu\nopen_collective: gorm\n"
},
{
"path": ".github/dependabot.yml",
"chars": 287,
"preview": "---\nversion: 2\nupdates:\n - package-ecosystem: gomod\n directory: /\n schedule:\n interval: weekly\n - package-e"
},
{
"path": ".github/labels.json",
"chars": 3916,
"preview": "{\n \"labels\": {\n \"critical\": {\n \"name\": \"type:critical\",\n \"colour\": \"#E84137\",\n \"description\": \"critic"
},
{
"path": ".github/release-drafter.yml",
"chars": 416,
"preview": "name-template: 'v Release $NEXT_PATCH_VERSION 🌈'\ntag-template: 'v$NEXT_PATCH_VERSION'\ncategories:\n - title: '🚀 Features"
},
{
"path": ".github/workflows/create-release.yml",
"chars": 663,
"preview": "name: Create Release\n\non:\n push:\n tags:\n - 'v*.*.*'\n\npermissions:\n contents: write\n pull-requests: read\n\njobs"
},
{
"path": ".github/workflows/golangci-lint.yml",
"chars": 471,
"preview": "name: golangci-lint\non:\n push:\n branches:\n - main\n - master\n pull_request:\n\npermissions:\n contents: read"
},
{
"path": ".github/workflows/invalid_question.yml",
"chars": 1251,
"preview": "name: \"Close invalid questions issues\"\non:\n schedule:\n - cron: \"*/10 * * * *\"\n\npermissions:\n contents: read\n\njobs:\n "
},
{
"path": ".github/workflows/labeler.yml",
"chars": 423,
"preview": "name: \"Issue Labeler\"\non:\n issues:\n types: [opened, edited, reopened]\n pull_request:\n types: [opened, edited, re"
},
{
"path": ".github/workflows/missing_playground.yml",
"chars": 1343,
"preview": "name: \"Close Missing Playground issues\"\non:\n schedule:\n - cron: \"*/10 * * * *\"\n\npermissions:\n contents: read\n\njobs:\n "
},
{
"path": ".github/workflows/stale.yml",
"chars": 972,
"preview": "name: \"Stale\"\non:\n schedule:\n - cron: \"0 2 * * *\"\n\npermissions:\n contents: read\n\njobs:\n stale:\n permissions:\n "
},
{
"path": ".github/workflows/tests.yml",
"chars": 9131,
"preview": "name: tests\n\non:\n push:\n branches-ignore:\n - 'gh-pages'\n pull_request:\n branches-ignore:\n - 'gh-pages'"
},
{
"path": ".gitignore",
"chars": 56,
"preview": "TODO*\ndocuments\ncoverage.txt\n_book\n.idea\nvendor\n.vscode\n"
},
{
"path": ".golangci.yml",
"chars": 241,
"preview": "version: \"2\"\n\nlinters:\n default: standard\n enable:\n - cyclop\n - gocritic\n - gosec\n - ineffassign\n - mis"
},
{
"path": "CODE_OF_CONDUCT.md",
"chars": 5164,
"preview": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nWe as members, contributors, and leaders pledge to participate in"
},
{
"path": "LICENSE",
"chars": 1101,
"preview": "The MIT License (MIT)\n\nCopyright (c) 2013-present Jinzhu <wosmvp@gmail.com>\n\nPermission is hereby granted, free of char"
},
{
"path": "README.md",
"chars": 1841,
"preview": "# GORM\n\nThe fantastic ORM library for Golang, aims to be developer friendly.\n\n[\n\nvar (\n\tcreateClauses = []string{\"INSERT\", \"VALUES\", \"ON CONFLICT\"}\n\tquery"
},
{
"path": "callbacks/callmethod.go",
"chars": 846,
"preview": "package callbacks\n\nimport (\n\t\"reflect\"\n\n\t\"gorm.io/gorm\"\n)\n\nfunc callMethod(db *gorm.DB, fc func(value interface{}, tx *g"
},
{
"path": "callbacks/create.go",
"chars": 13307,
"preview": "package callbacks\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n\t\"strings\"\n\n\t\"gorm.io/gorm\"\n\t\"gorm.io/gorm/clause\"\n\t\"gorm.io/gorm/schema\"\n"
},
{
"path": "callbacks/create_test.go",
"chars": 1428,
"preview": "package callbacks\n\nimport (\n\t\"reflect\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t\"gorm.io/gorm\"\n\t\"gorm.io/gorm/clause\"\n\t\"gorm.io/gorm"
},
{
"path": "callbacks/delete.go",
"chars": 5984,
"preview": "package callbacks\n\nimport (\n\t\"reflect\"\n\t\"strings\"\n\n\t\"gorm.io/gorm\"\n\t\"gorm.io/gorm/clause\"\n\t\"gorm.io/gorm/schema\"\n\t\"gorm."
},
{
"path": "callbacks/helper.go",
"chars": 3816,
"preview": "package callbacks\n\nimport (\n\t\"reflect\"\n\t\"sort\"\n\n\t\"gorm.io/gorm\"\n\t\"gorm.io/gorm/clause\"\n)\n\n// ConvertMapToValuesForCreate"
},
{
"path": "callbacks/helper_test.go",
"chars": 3490,
"preview": "package callbacks\n\nimport (\n\t\"reflect\"\n\t\"testing\"\n\n\t\"gorm.io/gorm\"\n\t\"gorm.io/gorm/clause\"\n)\n\nfunc TestLoadOrStoreVisitMa"
},
{
"path": "callbacks/interfaces.go",
"chars": 667,
"preview": "package callbacks\n\nimport \"gorm.io/gorm\"\n\ntype BeforeCreateInterface interface {\n\tBeforeCreate(*gorm.DB) error\n}\n\ntype A"
},
{
"path": "callbacks/preload.go",
"chars": 12012,
"preview": "package callbacks\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n\t\"sort\"\n\t\"strings\"\n\n\t\"gorm.io/gorm\"\n\t\"gorm.io/gorm/clause\"\n\t\"gorm.io/gorm/"
},
{
"path": "callbacks/query.go",
"chars": 10637,
"preview": "package callbacks\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n\t\"strings\"\n\n\t\"gorm.io/gorm\"\n\t\"gorm.io/gorm/clause\"\n\t\"gorm.io/gorm/schema\"\n"
},
{
"path": "callbacks/raw.go",
"chars": 468,
"preview": "package callbacks\n\nimport (\n\t\"gorm.io/gorm\"\n)\n\nfunc RawExec(db *gorm.DB) {\n\tif db.Error == nil && !db.DryRun {\n\t\tresult,"
},
{
"path": "callbacks/row.go",
"chars": 581,
"preview": "package callbacks\n\nimport (\n\t\"gorm.io/gorm\"\n)\n\nfunc RowQuery(db *gorm.DB) {\n\tif db.Error == nil {\n\t\tBuildQuerySQL(db)\n\t\t"
},
{
"path": "callbacks/transaction.go",
"chars": 675,
"preview": "package callbacks\n\nimport (\n\t\"gorm.io/gorm\"\n)\n\nfunc BeginTransaction(db *gorm.DB) {\n\tif !db.Config.SkipDefaultTransactio"
},
{
"path": "callbacks/update.go",
"chars": 9856,
"preview": "package callbacks\n\nimport (\n\t\"reflect\"\n\t\"sort\"\n\n\t\"gorm.io/gorm\"\n\t\"gorm.io/gorm/clause\"\n\t\"gorm.io/gorm/schema\"\n\t\"gorm.io/"
},
{
"path": "callbacks.go",
"chars": 9013,
"preview": "package gorm\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"sort\"\n\t\"time\"\n\n\t\"gorm.io/gorm/schema\"\n\t\"gorm.io/gorm/uti"
},
{
"path": "chainable_api.go",
"chars": 15133,
"preview": "package gorm\n\nimport (\n\t\"fmt\"\n\t\"regexp\"\n\t\"strings\"\n\n\t\"gorm.io/gorm/clause\"\n\t\"gorm.io/gorm/utils\"\n)\n\n// Model specify the"
},
{
"path": "clause/association.go",
"chars": 1268,
"preview": "package clause\n\n// AssociationOpType represents association operation types\ntype AssociationOpType int\n\nconst (\n\tOpUnlin"
},
{
"path": "clause/benchmarks_test.go",
"chars": 1902,
"preview": "package clause_test\n\nimport (\n\t\"sync\"\n\t\"testing\"\n\n\t\"gorm.io/gorm\"\n\t\"gorm.io/gorm/clause\"\n\t\"gorm.io/gorm/schema\"\n\t\"gorm.i"
},
{
"path": "clause/clause.go",
"chars": 1749,
"preview": "package clause\n\n// Interface clause interface\ntype Interface interface {\n\tName() string\n\tBuild(Builder)\n\tMergeClause(*Cl"
},
{
"path": "clause/clause_test.go",
"chars": 1012,
"preview": "package clause_test\n\nimport (\n\t\"reflect\"\n\t\"strings\"\n\t\"sync\"\n\t\"testing\"\n\n\t\"gorm.io/gorm\"\n\t\"gorm.io/gorm/clause\"\n\t\"gorm.io"
},
{
"path": "clause/delete.go",
"chars": 359,
"preview": "package clause\n\ntype Delete struct {\n\tModifier string\n}\n\nfunc (d Delete) Name() string {\n\treturn \"DELETE\"\n}\n\nfunc (d Del"
},
{
"path": "clause/delete_test.go",
"chars": 608,
"preview": "package clause_test\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\n\t\"gorm.io/gorm/clause\"\n)\n\nfunc TestDelete(t *testing.T) {\n\tresults := ["
},
{
"path": "clause/expression.go",
"chars": 8231,
"preview": "package clause\n\nimport (\n\t\"database/sql\"\n\t\"database/sql/driver\"\n\t\"go/ast\"\n\t\"reflect\"\n)\n\n// Expression expression interfa"
},
{
"path": "clause/expression_test.go",
"chars": 9063,
"preview": "package clause_test\n\nimport (\n\t\"database/sql\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"sync\"\n\t\"testing\"\n\n\t\"gorm.io/gorm\"\n\t\"gorm.io/gorm/claus"
},
{
"path": "clause/from.go",
"chars": 630,
"preview": "package clause\n\n// From from clause\ntype From struct {\n\tTables []Table\n\tJoins []Join\n}\n\n// Name from clause name\nfunc ("
},
{
"path": "clause/from_test.go",
"chars": 1956,
"preview": "package clause_test\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\n\t\"gorm.io/gorm/clause\"\n)\n\nfunc TestFrom(t *testing.T) {\n\tresults := []s"
},
{
"path": "clause/group_by.go",
"chars": 1068,
"preview": "package clause\n\n// GroupBy group by clause\ntype GroupBy struct {\n\tColumns []Column\n\tHaving []Expression\n}\n\n// Name from"
},
{
"path": "clause/group_by_test.go",
"chars": 1111,
"preview": "package clause_test\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\n\t\"gorm.io/gorm/clause\"\n)\n\nfunc TestGroupBy(t *testing.T) {\n\tresults := "
},
{
"path": "clause/insert.go",
"chars": 767,
"preview": "package clause\n\ntype Insert struct {\n\tTable Table\n\tModifier string\n}\n\n// Name insert clause name\nfunc (insert Insert)"
},
{
"path": "clause/insert_test.go",
"chars": 737,
"preview": "package clause_test\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\n\t\"gorm.io/gorm/clause\"\n)\n\nfunc TestInsert(t *testing.T) {\n\tresults := ["
},
{
"path": "clause/joins.go",
"chars": 1586,
"preview": "package clause\n\nimport \"gorm.io/gorm/utils\"\n\ntype JoinType string\n\nconst (\n\tCrossJoin JoinType = \"CROSS\"\n\tInnerJoin Join"
},
{
"path": "clause/joins_test.go",
"chars": 2695,
"preview": "package clause_test\n\nimport (\n\t\"sync\"\n\t\"testing\"\n\n\t\"gorm.io/gorm\"\n\t\"gorm.io/gorm/clause\"\n\t\"gorm.io/gorm/schema\"\n\t\"gorm.i"
},
{
"path": "clause/limit.go",
"chars": 942,
"preview": "package clause\n\n// Limit limit clause\ntype Limit struct {\n\tLimit *int\n\tOffset int\n}\n\n// Name where clause name\nfunc (li"
},
{
"path": "clause/limit_test.go",
"chars": 2396,
"preview": "package clause_test\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\n\t\"gorm.io/gorm/clause\"\n)\n\nfunc TestLimit(t *testing.T) {\n\tlimit0 := 0\n\t"
},
{
"path": "clause/locking.go",
"chars": 773,
"preview": "package clause\n\nconst (\n\tLockingStrengthUpdate = \"UPDATE\"\n\tLockingStrengthShare = \"SHARE\"\n\tLockingOptionsSkipLock"
},
{
"path": "clause/locking_test.go",
"chars": 1194,
"preview": "package clause_test\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\n\t\"gorm.io/gorm/clause\"\n)\n\nfunc TestLocking(t *testing.T) {\n\tresults := "
},
{
"path": "clause/on_conflict.go",
"chars": 1298,
"preview": "package clause\n\ntype OnConflict struct {\n\tColumns []Column\n\tWhere Where\n\tTargetWhere Where\n\tOnConstraint st"
},
{
"path": "clause/order_by.go",
"chars": 1114,
"preview": "package clause\n\ntype OrderByColumn struct {\n\tColumn Column\n\tDesc bool\n\tReorder bool\n}\n\ntype OrderBy struct {\n\tColumn"
},
{
"path": "clause/order_by_test.go",
"chars": 1590,
"preview": "package clause_test\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\n\t\"gorm.io/gorm/clause\"\n)\n\nfunc TestOrderBy(t *testing.T) {\n\tresults := "
},
{
"path": "clause/returning.go",
"chars": 777,
"preview": "package clause\n\ntype Returning struct {\n\tColumns []Column\n}\n\n// Name where clause name\nfunc (returning Returning) Name()"
},
{
"path": "clause/returning_test.go",
"chars": 1389,
"preview": "package clause_test\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\n\t\"gorm.io/gorm/clause\"\n)\n\nfunc TestReturning(t *testing.T) {\n\tresults :"
},
{
"path": "clause/select.go",
"chars": 1096,
"preview": "package clause\n\n// Select select attrs when querying, updating, creating\ntype Select struct {\n\tDistinct bool\n\tColumns "
},
{
"path": "clause/select_test.go",
"chars": 1721,
"preview": "package clause_test\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\n\t\"gorm.io/gorm/clause\"\n)\n\nfunc TestSelect(t *testing.T) {\n\tresults := ["
},
{
"path": "clause/set.go",
"chars": 1742,
"preview": "package clause\n\nimport \"sort\"\n\ntype Set []Assignment\n\ntype Assignment struct {\n\tColumn Column\n\tValue interface{}\n}\n\n// "
},
{
"path": "clause/set_test.go",
"chars": 1547,
"preview": "package clause_test\n\nimport (\n\t\"fmt\"\n\t\"sort\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"gorm.io/gorm/clause\"\n)\n\n// Compile-time assertions"
},
{
"path": "clause/update.go",
"chars": 737,
"preview": "package clause\n\ntype Update struct {\n\tModifier string\n\tTable Table\n}\n\n// Name update clause name\nfunc (update Update)"
},
{
"path": "clause/update_test.go",
"chars": 722,
"preview": "package clause_test\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\n\t\"gorm.io/gorm/clause\"\n)\n\nfunc TestUpdate(t *testing.T) {\n\tresults := ["
},
{
"path": "clause/values.go",
"chars": 849,
"preview": "package clause\n\ntype Values struct {\n\tColumns []Column\n\tValues [][]interface{}\n}\n\n// Name from clause name\nfunc (Values"
},
{
"path": "clause/values_test.go",
"chars": 691,
"preview": "package clause_test\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\n\t\"gorm.io/gorm/clause\"\n)\n\nfunc TestValues(t *testing.T) {\n\tresults := ["
},
{
"path": "clause/where.go",
"chars": 5236,
"preview": "package clause\n\nimport (\n\t\"strings\"\n)\n\nconst (\n\tAndWithSpace = \" AND \"\n\tOrWithSpace = \" OR \"\n)\n\n// Where where clause\nt"
},
{
"path": "clause/where_test.go",
"chars": 6328,
"preview": "package clause_test\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\n\t\"gorm.io/gorm/clause\"\n)\n\nfunc TestWhere(t *testing.T) {\n\tresults := []"
},
{
"path": "clause/with.go",
"chars": 35,
"preview": "package clause\n\ntype With struct{}\n"
},
{
"path": "errors.go",
"chars": 2562,
"preview": "package gorm\n\nimport (\n\t\"errors\"\n\n\t\"gorm.io/gorm/logger\"\n)\n\nvar (\n\t// ErrRecordNotFound record not found error\n\tErrRecor"
},
{
"path": "finisher_api.go",
"chars": 23461,
"preview": "package gorm\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"errors\"\n\t\"fmt\"\n\t\"hash/maphash\"\n\t\"reflect\"\n\t\"strings\"\n\n\t\"gorm.io/gorm"
},
{
"path": "generics.go",
"chars": 26550,
"preview": "package gorm\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"errors\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"sort\"\n\t\"strings\"\n\n\t\"gorm.io/gorm/clause\""
},
{
"path": "go.mod",
"chars": 241,
"preview": "module gorm.io/gorm\n\ngo 1.18\n\nrequire (\n\tgithub.com/jinzhu/inflection v1.0.0\n\tgithub.com/jinzhu/now v1.1.5\n\tgolang.org/x"
},
{
"path": "go.sum",
"chars": 829,
"preview": "github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=\ngithub.com/jinzhu/inflection v1.0.0/"
},
{
"path": "gorm.go",
"chars": 13177,
"preview": "package gorm\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"sort\"\n\t\"sync\"\n\t\"time\"\n\n\t\"gorm.io/gorm/clause\"\n\t\"go"
},
{
"path": "interfaces.go",
"chars": 2216,
"preview": "package gorm\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\n\t\"gorm.io/gorm/clause\"\n\t\"gorm.io/gorm/schema\"\n)\n\n// Dialector GORM da"
},
{
"path": "internal/lru/lru.go",
"chars": 12824,
"preview": "package lru\n\n// golang -lru\n// https://github.com/hashicorp/golang-lru\nimport (\n\t\"sync\"\n\t\"time\"\n)\n\n// EvictCallback is u"
},
{
"path": "internal/stmt_store/stmt_store.go",
"chars": 6154,
"preview": "package stmt_store\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"math\"\n\t\"sync\"\n\t\"time\"\n\n\t\"gorm.io/gorm/internal/lru\"\n)\n\ntype St"
},
{
"path": "logger/logger.go",
"chars": 6407,
"preview": "package logger\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"log\"\n\t\"os\"\n\t\"time\"\n\n\t\"gorm.io/gorm/utils\"\n)\n\n// ErrRecordNo"
},
{
"path": "logger/slog.go",
"chars": 2927,
"preview": "//go:build go1.21\n\npackage logger\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"log/slog\"\n\t\"time\"\n\n\t\"gorm.io/gorm/utils\"\n)\n\nty"
},
{
"path": "logger/slog_test.go",
"chars": 782,
"preview": "//go:build go1.21\n\npackage logger\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"log/slog\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestSl"
},
{
"path": "logger/sql.go",
"chars": 5146,
"preview": "package logger\n\nimport (\n\t\"database/sql/driver\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"regexp\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\t\"unicode\"\n\n\t\""
},
{
"path": "logger/sql_test.go",
"chars": 8565,
"preview": "package logger_test\n\nimport (\n\t\"database/sql/driver\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"regexp\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.c"
},
{
"path": "migrator/column_type.go",
"chars": 3374,
"preview": "package migrator\n\nimport (\n\t\"database/sql\"\n\t\"reflect\"\n)\n\n// ColumnType column type implements ColumnType interface\ntype "
},
{
"path": "migrator/index.go",
"chars": 1023,
"preview": "package migrator\n\nimport \"database/sql\"\n\n// Index implements gorm.Index interface\ntype Index struct {\n\tTableName s"
},
{
"path": "migrator/migrator.go",
"chars": 30371,
"preview": "package migrator\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"errors\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"regexp\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time"
},
{
"path": "migrator/table_type.go",
"chars": 688,
"preview": "package migrator\n\nimport (\n\t\"database/sql\"\n)\n\n// TableType table type implements TableType interface\ntype TableType stru"
},
{
"path": "migrator.go",
"chars": 3205,
"preview": "package gorm\n\nimport (\n\t\"reflect\"\n\n\t\"gorm.io/gorm/clause\"\n\t\"gorm.io/gorm/schema\"\n)\n\n// Migrator returns migrator\nfunc (d"
},
{
"path": "model.go",
"chars": 396,
"preview": "package gorm\n\nimport \"time\"\n\n// Model a basic GoLang struct which includes the following fields: ID, CreatedAt, UpdatedA"
},
{
"path": "prepare_stmt.go",
"chars": 5826,
"preview": "package gorm\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"database/sql/driver\"\n\t\"errors\"\n\t\"reflect\"\n\t\"sync\"\n\t\"time\"\n\n\t\"gorm.io"
},
{
"path": "scan.go",
"chars": 10650,
"preview": "package gorm\n\nimport (\n\t\"database/sql\"\n\t\"database/sql/driver\"\n\t\"reflect\"\n\t\"strings\"\n\t\"time\"\n\n\t\"gorm.io/gorm/schema\"\n\t\"go"
},
{
"path": "schema/callbacks_test.go",
"chars": 939,
"preview": "package schema_test\n\nimport (\n\t\"reflect\"\n\t\"sync\"\n\t\"testing\"\n\n\t\"gorm.io/gorm\"\n\t\"gorm.io/gorm/schema\"\n)\n\ntype UserWithCall"
},
{
"path": "schema/constraint.go",
"chars": 1984,
"preview": "package schema\n\nimport (\n\t\"regexp\"\n\t\"strings\"\n\n\t\"gorm.io/gorm/clause\"\n)\n\n// reg match english letters and midline\nvar re"
},
{
"path": "schema/constraint_test.go",
"chars": 2245,
"preview": "package schema_test\n\nimport (\n\t\"reflect\"\n\t\"sync\"\n\t\"testing\"\n\n\t\"gorm.io/gorm/schema\"\n\t\"gorm.io/gorm/utils/tests\"\n)\n\ntype "
},
{
"path": "schema/field.go",
"chars": 32991,
"preview": "package schema\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"database/sql/driver\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"strconv\"\n\t\"strings\"\n\t\"syn"
},
{
"path": "schema/field_test.go",
"chars": 13023,
"preview": "package schema_test\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"reflect\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t\"gorm.io/gorm\"\n\t\"gorm.i"
},
{
"path": "schema/index.go",
"chars": 3863,
"preview": "package schema\n\nimport (\n\t\"fmt\"\n\t\"sort\"\n\t\"strconv\"\n\t\"strings\"\n)\n\ntype Index struct {\n\tName string\n\tClass string // "
},
{
"path": "schema/index_test.go",
"chars": 8140,
"preview": "package schema_test\n\nimport (\n\t\"sync\"\n\t\"testing\"\n\n\t\"gorm.io/gorm/schema\"\n\t\"gorm.io/gorm/utils/tests\"\n)\n\ntype UserIndex s"
},
{
"path": "schema/interfaces.go",
"chars": 980,
"preview": "package schema\n\nimport (\n\t\"gorm.io/gorm/clause\"\n)\n\n// ConstraintInterface database constraint interface\ntype ConstraintI"
},
{
"path": "schema/model_test.go",
"chars": 1176,
"preview": "package schema_test\n\nimport (\n\t\"database/sql\"\n\t\"time\"\n\n\t\"gorm.io/gorm\"\n\t\"gorm.io/gorm/utils/tests\"\n)\n\ntype User struct {"
},
{
"path": "schema/naming.go",
"chars": 5465,
"preview": "package schema\n\nimport (\n\t\"crypto/sha1\"\n\t\"encoding/hex\"\n\t\"regexp\"\n\t\"strings\"\n\t\"unicode/utf8\"\n\n\t\"github.com/jinzhu/inflec"
},
{
"path": "schema/naming_test.go",
"chars": 7150,
"preview": "package schema\n\nimport (\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc TestToDBName(t *testing.T) {\n\tmaps := map[string]string{\n\t\t\"\": "
},
{
"path": "schema/pool.go",
"chars": 345,
"preview": "package schema\n\nimport (\n\t\"reflect\"\n\t\"sync\"\n)\n\n// sync pools\nvar (\n\tnormalPool sync.Map\n\tpoolInitializer = func(ref"
},
{
"path": "schema/relationship.go",
"chars": 23677,
"preview": "package schema\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"strings\"\n\t\"sync\"\n\n\t\"github.com/jinzhu/inflection\"\n\t\"golang.org/x"
},
{
"path": "schema/relationship_test.go",
"chars": 27136,
"preview": "package schema_test\n\nimport (\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t\"gorm.io/gorm\"\n\t\"gorm.io/gorm/schema\"\n\t\"gorm.io/gorm/utils/te"
},
{
"path": "schema/schema.go",
"chars": 13455,
"preview": "package schema\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"go/ast\"\n\t\"path\"\n\t\"reflect\"\n\t\"strings\"\n\t\"sync\"\n\n\t\"gorm.io/gorm/cla"
},
{
"path": "schema/schema_helper_test.go",
"chars": 7705,
"preview": "package schema_test\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"gorm.io/gorm/schema\"\n\t\"gorm.io/gorm/"
},
{
"path": "schema/schema_test.go",
"chars": 13610,
"preview": "package schema_test\n\nimport (\n\t\"strings\"\n\t\"sync\"\n\t\"testing\"\n\n\t\"gorm.io/gorm\"\n\t\"gorm.io/gorm/schema\"\n\t\"gorm.io/gorm/utils"
},
{
"path": "schema/serializer.go",
"chars": 5190,
"preview": "package schema\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"database/sql\"\n\t\"database/sql/driver\"\n\t\"encoding/gob\"\n\t\"encoding/json\"\n\t\"f"
},
{
"path": "schema/serializer_test.go",
"chars": 4866,
"preview": "package schema\n\nimport (\n\t\"context\"\n\t\"math\"\n\t\"reflect\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestUnixSecondSerializer_Value(t *test"
},
{
"path": "schema/utils.go",
"chars": 6053,
"preview": "package schema\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"regexp\"\n\t\"strings\"\n\n\t\"gorm.io/gorm/clause\"\n\t\"gorm.io/gorm/utils\""
},
{
"path": "schema/utils_test.go",
"chars": 1880,
"preview": "package schema\n\nimport (\n\t\"reflect\"\n\t\"testing\"\n)\n\nfunc TestRemoveSettingFromTag(t *testing.T) {\n\ttags := map[string]stri"
},
{
"path": "soft_delete.go",
"chars": 4633,
"preview": "package gorm\n\nimport (\n\t\"database/sql\"\n\t\"database/sql/driver\"\n\t\"encoding/json\"\n\t\"reflect\"\n\n\t\"github.com/jinzhu/now\"\n\t\"go"
},
{
"path": "statement.go",
"chars": 21384,
"preview": "package gorm\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"database/sql/driver\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"regexp\"\n\t\"sort\"\n\t\"strconv\"\n"
},
{
"path": "statement_test.go",
"chars": 1930,
"preview": "package gorm\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n\t\"testing\"\n\n\t\"gorm.io/gorm/clause\"\n)\n\nfunc TestWhereCloneCorruption(t *testing."
},
{
"path": "tests/.gitignore",
"chars": 7,
"preview": "go.sum\n"
},
{
"path": "tests/README.md",
"chars": 110,
"preview": "# Test Guide\n\n```bash\ncd tests\n# prepare test databases\ndocker-compose up\n\n# run all tests\n./tests_all.sh\n```\n"
},
{
"path": "tests/association_generics_test.go",
"chars": 38787,
"preview": "package tests_test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"gorm.io/gorm\"\n\t\"gorm.io/gorm/clause\"\n\t. \"gorm.io/gorm/utils/tests\""
},
{
"path": "tests/associations_belongs_to_test.go",
"chars": 9533,
"preview": "package tests_test\n\nimport (\n\t\"testing\"\n\n\t\"gorm.io/gorm\"\n\t. \"gorm.io/gorm/utils/tests\"\n)\n\nfunc TestBelongsToAssociation("
},
{
"path": "tests/associations_has_many_test.go",
"chars": 16650,
"preview": "package tests_test\n\nimport (\n\t\"testing\"\n\n\t\"gorm.io/gorm\"\n\t. \"gorm.io/gorm/utils/tests\"\n)\n\nfunc TestHasManyAssociation(t "
},
{
"path": "tests/associations_has_one_test.go",
"chars": 7304,
"preview": "package tests_test\n\nimport (\n\t\"testing\"\n\n\t. \"gorm.io/gorm/utils/tests\"\n)\n\nfunc TestHasOneAssociation(t *testing.T) {\n\tus"
},
{
"path": "tests/associations_many2many_test.go",
"chars": 13547,
"preview": "package tests_test\n\nimport (\n\t\"fmt\"\n\t\"sync\"\n\t\"testing\"\n\n\t\"gorm.io/gorm\"\n\t\"gorm.io/gorm/clause\"\n\t. \"gorm.io/gorm/utils/te"
},
{
"path": "tests/associations_test.go",
"chars": 13579,
"preview": "package tests_test\n\nimport (\n\t\"testing\"\n\n\t\"gorm.io/gorm\"\n\t\"gorm.io/gorm/clause\"\n\t\"gorm.io/gorm/schema\"\n\t. \"gorm.io/gorm/"
},
{
"path": "tests/benchmark_test.go",
"chars": 1523,
"preview": "package tests_test\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\n\t. \"gorm.io/gorm/utils/tests\"\n)\n\nfunc BenchmarkCreate(b *testing.B) {\n\tu"
},
{
"path": "tests/callbacks_test.go",
"chars": 7403,
"preview": "package tests_test\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n\t\"runtime\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"gorm.io/gorm\"\n)\n\nfunc assertCallbacks"
},
{
"path": "tests/chainable_api_test.go",
"chars": 3957,
"preview": "package tests\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"gorm.io/gorm\"\n\t\"gorm.io/gorm/clause\"\n\t\"gorm.io/gorm/schema\"\n)\n\n// testD"
},
{
"path": "tests/compose.yml",
"chars": 1924,
"preview": "services:\n mysql:\n image: 'mysql:latest'\n ports:\n - \"127.0.0.1:9910:3306\"\n environment:\n - MYSQL_DAT"
},
{
"path": "tests/connection_test.go",
"chars": 943,
"preview": "package tests_test\n\nimport (\n\t\"testing\"\n\n\t\"gorm.io/driver/mysql\"\n\t\"gorm.io/gorm\"\n)\n\nfunc TestWithSingleConnection(t *tes"
},
{
"path": "tests/connpool_test.go",
"chars": 5678,
"preview": "package tests_test\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"os\"\n\t\"reflect\"\n\t\"testing\"\n\n\t\"gorm.io/driver/mysql\"\n\t\"gorm.io/g"
},
{
"path": "tests/count_test.go",
"chars": 6978,
"preview": "package tests_test\n\nimport (\n\t\"regexp\"\n\t\"sort\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"gorm.io/gorm\"\n\t. \"gorm.io/gorm/utils/tests\"\n)\n\nf"
},
{
"path": "tests/create_test.go",
"chars": 27468,
"preview": "package tests_test\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"regexp\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/jinzhu/now\"\n\t\"gorm.io/gorm\"\n\t\"g"
},
{
"path": "tests/customize_field_test.go",
"chars": 7049,
"preview": "package tests_test\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t\"gorm.io/gorm\"\n\t. \"gorm.io/gorm/utils/tests\"\n)\n\nfunc TestCustomizeColu"
},
{
"path": "tests/default_value_test.go",
"chars": 2396,
"preview": "package tests_test\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t\"gorm.io/gorm\"\n)\n\nfunc TestDefaultValue(t *testing.T) {\n\ttype Harumph "
},
{
"path": "tests/delete_test.go",
"chars": 9696,
"preview": "package tests_test\n\nimport (\n\t\"errors\"\n\t\"testing\"\n\n\t\"gorm.io/gorm\"\n\t\"gorm.io/gorm/clause\"\n\t. \"gorm.io/gorm/utils/tests\"\n"
},
{
"path": "tests/distinct_test.go",
"chars": 2581,
"preview": "package tests_test\n\nimport (\n\t\"regexp\"\n\t\"testing\"\n\n\t\"gorm.io/gorm\"\n\t. \"gorm.io/gorm/utils/tests\"\n)\n\nfunc TestDistinct(t "
},
{
"path": "tests/embedded_struct_test.go",
"chars": 7495,
"preview": "package tests_test\n\nimport (\n\t\"database/sql/driver\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"reflect\"\n\t\"testing\"\n\t\"time\"\n\n\t\"gorm.io/"
},
{
"path": "tests/error_translator_test.go",
"chars": 3170,
"preview": "package tests_test\n\nimport (\n\t\"errors\"\n\t\"testing\"\n\n\t\"gorm.io/gorm\"\n\t\"gorm.io/gorm/utils/tests\"\n)\n\nfunc TestDialectorWith"
},
{
"path": "tests/gaussdb_test.go",
"chars": 6788,
"preview": "package tests_test\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/google/uuid\"\n\t\"github.com/lib/pq\"\n\t\"gorm.io/gorm\"\n\t\"gorm.i"
},
{
"path": "tests/generics_test.go",
"chars": 35781,
"preview": "package tests_test\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"regexp\"\n\t\"sort\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"te"
},
{
"path": "tests/go.mod",
"chars": 1345,
"preview": "module gorm.io/gorm/tests\n\ngo 1.24.0\n\nrequire (\n\tgithub.com/google/uuid v1.6.0\n\tgithub.com/jinzhu/now v1.1.5\n\tgithub.com"
},
{
"path": "tests/gorm_test.go",
"chars": 3362,
"preview": "package tests_test\n\nimport (\n\t\"testing\"\n\n\t\"gorm.io/driver/mysql\"\n\n\t\"gorm.io/gorm\"\n)\n\nfunc TestOpen(t *testing.T) {\n\tdsn "
},
{
"path": "tests/group_by_test.go",
"chars": 3374,
"preview": "package tests_test\n\nimport (\n\t\"testing\"\n\n\t. \"gorm.io/gorm/utils/tests\"\n)\n\nfunc TestGroupBy(t *testing.T) {\n\tusers := []U"
},
{
"path": "tests/helper_test.go",
"chars": 8178,
"preview": "package tests_test\n\nimport (\n\t\"os\"\n\t\"sort\"\n\t\"strconv\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"gorm.io/gorm\"\n\n\t. \"gorm.io/gorm/u"
},
{
"path": "tests/hooks_test.go",
"chars": 17095,
"preview": "package tests_test\n\nimport (\n\t\"errors\"\n\t\"log\"\n\t\"os\"\n\t\"reflect\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"gorm.io/gorm\"\n\t. \"gorm.io/gorm/u"
},
{
"path": "tests/joins_table_test.go",
"chars": 3537,
"preview": "package tests_test\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t\"gorm.io/gorm\"\n\t\"gorm.io/gorm/clause\"\n)\n\ntype Person struct {\n\tID "
},
{
"path": "tests/joins_test.go",
"chars": 15342,
"preview": "package tests_test\n\nimport (\n\t\"fmt\"\n\t\"regexp\"\n\t\"sort\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"gorm.io/gorm\"\n"
},
{
"path": "tests/lru_test.go",
"chars": 10630,
"preview": "package tests_test\n\nimport (\n\t\"crypto/rand\"\n\t\"fmt\"\n\t\"math\"\n\t\"math/big\"\n\t\"reflect\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t\"gorm.io/"
},
{
"path": "tests/main_test.go",
"chars": 1399,
"preview": "package tests_test\n\nimport (\n\t\"testing\"\n\n\t. \"gorm.io/gorm/utils/tests\"\n)\n\nfunc TestExceptionsWithInvalidSql(t *testing.T"
},
{
"path": "tests/migrate_test.go",
"chars": 66779,
"preview": "package tests_test\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"fmt\"\n\t\"math/rand\"\n\t\"reflect\"\n\t\"strconv\"\n\t\"strings\"\n\t\"testing\"\n"
},
{
"path": "tests/multi_primary_keys_test.go",
"chars": 13444,
"preview": "package tests_test\n\nimport (\n\t\"reflect\"\n\t\"sort\"\n\t\"testing\"\n\n\t\"gorm.io/gorm\"\n\t. \"gorm.io/gorm/utils/tests\"\n)\n\ntype Blog s"
},
{
"path": "tests/named_argument_test.go",
"chars": 2789,
"preview": "package tests_test\n\nimport (\n\t\"database/sql\"\n\t\"errors\"\n\t\"testing\"\n\n\t\"gorm.io/gorm\"\n\t. \"gorm.io/gorm/utils/tests\"\n)\n\nfunc"
},
{
"path": "tests/named_polymorphic_test.go",
"chars": 4305,
"preview": "package tests_test\n\nimport (\n\t\"testing\"\n\n\t. \"gorm.io/gorm/utils/tests\"\n)\n\ntype Hamster struct {\n\tId int\n\tName "
},
{
"path": "tests/non_std_test.go",
"chars": 1970,
"preview": "package tests_test\n\nimport (\n\t\"testing\"\n\t\"time\"\n)\n\ntype Animal struct {\n\tCounter uint64 `gorm:\"primary_key:yes\"`\n\tNam"
},
{
"path": "tests/postgres_test.go",
"chars": 6576,
"preview": "package tests_test\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/google/uuid\"\n\t\"github.com/lib/pq\"\n\t\"gorm.io/gorm\"\n\t\"gorm.i"
},
{
"path": "tests/preload_suits_test.go",
"chars": 31113,
"preview": "package tests_test\n\nimport (\n\t\"database/sql\"\n\t\"encoding/json\"\n\t\"reflect\"\n\t\"sort\"\n\t\"sync/atomic\"\n\t\"testing\"\n\n\t\"gorm.io/go"
},
{
"path": "tests/preload_test.go",
"chars": 16306,
"preview": "package tests_test\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"regexp\"\n\t\"sort\"\n\t\"strconv\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t\"gorm"
},
{
"path": "tests/prepared_stmt_test.go",
"chars": 8216,
"preview": "package tests_test\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t\"gorm.io/gorm\"\n\t. \"gorm.io/gorm/utils/tes"
},
{
"path": "tests/query_test.go",
"chars": 52228,
"preview": "package tests_test\n\nimport (\n\t\"database/sql\"\n\t\"database/sql/driver\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"regexp\"\n\t\"sort\"\n\t\"strconv\"\n\t\"str"
},
{
"path": "tests/scan_test.go",
"chars": 11115,
"preview": "package tests_test\n\nimport (\n\t\"reflect\"\n\t\"sort\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"gorm.io/gorm\"\n\t. \"gorm.io/gorm/utils/te"
},
{
"path": "tests/scanner_valuer_test.go",
"chars": 10830,
"preview": "package tests_test\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"database/sql/driver\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"refle"
},
{
"path": "tests/scopes_test.go",
"chars": 3429,
"preview": "package tests_test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"gorm.io/gorm\"\n\t. \"gorm.io/gorm/utils/tests\"\n)\n\nfunc NameIn1And2(d "
},
{
"path": "tests/serializer_test.go",
"chars": 9538,
"preview": "package tests_test\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"gorm.io/gorm\"\n\t\"gorm"
},
{
"path": "tests/soft_delete_test.go",
"chars": 5799,
"preview": "package tests_test\n\nimport (\n\t\"database/sql\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"regexp\"\n\t\"testing\"\n\n\t\"github.com/jinzhu/now\"\n\t"
},
{
"path": "tests/sql_builder_test.go",
"chars": 17071,
"preview": "package tests_test\n\nimport (\n\t\"regexp\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"gorm.io/gorm\"\n\t\"gorm.io/gorm/clause\"\n\t. \"gorm.io"
},
{
"path": "tests/submodel_test.go",
"chars": 947,
"preview": "package tests_test\n\nimport (\n\t\"testing\"\n\n\t\"gorm.io/gorm\"\n)\n\ntype Man struct {\n\tID int\n\tAge int\n\tName string\n\tDe"
},
{
"path": "tests/table_test.go",
"chars": 12979,
"preview": "package tests_test\n\nimport (\n\t\"regexp\"\n\t\"sync\"\n\t\"testing\"\n\n\t\"gorm.io/driver/gaussdb\"\n\t\"gorm.io/driver/postgres\"\n\t\"gorm.i"
},
{
"path": "tests/tests_all.sh",
"chars": 1604,
"preview": "#!/bin/bash -e\n\ndialects=(\"sqlite\" \"mysql\" \"postgres\" \"gaussdb\" \"sqlserver\" \"tidb\")\n\nif [[ $(pwd) == *\"gorm/tests\"* ]]; "
},
{
"path": "tests/tests_test.go",
"chars": 3828,
"preview": "//go:debug x509negativeserial=1\npackage tests_test\n\nimport (\n\t\"log\"\n\t\"math/rand\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"time\"\n\n\t\"gorm."
},
{
"path": "tests/tracer_test.go",
"chars": 830,
"preview": "package tests_test\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\t\"gorm.io/gorm/logger\"\n)\n\ntype Tracer struct {\n\tLogger logger.Interface"
},
{
"path": "tests/transaction_test.go",
"chars": 13650,
"preview": "package tests_test\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"testing\"\n\t\"time\"\n\n\t\"gorm.io/gorm\"\n\t. \"gorm.io/gorm/utils/tests\"\n)\n\nf"
},
{
"path": "tests/update_belongs_to_test.go",
"chars": 1672,
"preview": "package tests_test\n\nimport (\n\t\"testing\"\n\n\t\"gorm.io/gorm\"\n\t. \"gorm.io/gorm/utils/tests\"\n)\n\nfunc TestUpdateBelongsTo(t *te"
},
{
"path": "tests/update_has_many_test.go",
"chars": 2028,
"preview": "package tests_test\n\nimport (\n\t\"testing\"\n\n\t\"gorm.io/gorm\"\n\t. \"gorm.io/gorm/utils/tests\"\n)\n\nfunc TestUpdateHasManyAssociat"
},
{
"path": "tests/update_has_one_test.go",
"chars": 3677,
"preview": "package tests_test\n\nimport (\n\t\"database/sql\"\n\t\"testing\"\n\t\"time\"\n\n\t\"gorm.io/gorm\"\n\t. \"gorm.io/gorm/utils/tests\"\n)\n\nfunc T"
},
{
"path": "tests/update_many2many_test.go",
"chars": 1377,
"preview": "package tests_test\n\nimport (\n\t\"testing\"\n\n\t\"gorm.io/gorm\"\n\t. \"gorm.io/gorm/utils/tests\"\n)\n\nfunc TestUpdateMany2ManyAssoci"
},
{
"path": "tests/update_test.go",
"chars": 31136,
"preview": "package tests_test\n\nimport (\n\t\"errors\"\n\t\"regexp\"\n\t\"sort\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"gorm.io/gorm\"\n\t\"gorm.io/gorm/c"
},
{
"path": "tests/upsert_test.go",
"chars": 13413,
"preview": "package tests_test\n\nimport (\n\t\"regexp\"\n\t\"testing\"\n\t\"time\"\n\n\t\"gorm.io/gorm\"\n\t\"gorm.io/gorm/clause\"\n\t. \"gorm.io/gorm/utils"
},
{
"path": "utils/tests/dummy_dialecter.go",
"chars": 2231,
"preview": "package tests\n\nimport (\n\t\"gorm.io/gorm\"\n\t\"gorm.io/gorm/callbacks\"\n\t\"gorm.io/gorm/clause\"\n\t\"gorm.io/gorm/logger\"\n\t\"gorm.i"
},
{
"path": "utils/tests/models.go",
"chars": 2187,
"preview": "package tests\n\nimport (\n\t\"database/sql\"\n\t\"time\"\n\n\t\"gorm.io/gorm\"\n)\n\n// User has one `Account` (has one), many `Pets` (ha"
},
{
"path": "utils/tests/utils.go",
"chars": 3963,
"preview": "package tests\n\nimport (\n\t\"database/sql/driver\"\n\t\"fmt\"\n\t\"go/ast\"\n\t\"reflect\"\n\t\"testing\"\n\t\"time\"\n\n\t\"gorm.io/gorm/utils\"\n)\n\n"
},
{
"path": "utils/utils.go",
"chars": 4635,
"preview": "package utils\n\nimport (\n\t\"database/sql/driver\"\n\t\"fmt\"\n\t\"path/filepath\"\n\t\"reflect\"\n\t\"runtime\"\n\t\"strconv\"\n\t\"strings\"\n\t\"uni"
},
{
"path": "utils/utils_test.go",
"chars": 5021,
"preview": "package utils\n\nimport (\n\t\"database/sql\"\n\t\"database/sql/driver\"\n\t\"errors\"\n\t\"math\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc T"
},
{
"path": "utils/utils_unix_test.go",
"chars": 731,
"preview": "//go:build unix\n// +build unix\n\npackage utils\n\nimport (\n\t\"testing\"\n)\n\nfunc TestSourceDir(t *testing.T) {\n\tcases := []str"
},
{
"path": "utils/utils_windows_test.go",
"chars": 715,
"preview": "package utils\n\nimport (\n\t\"testing\"\n)\n\nfunc TestSourceDir(t *testing.T) {\n\tcases := []struct {\n\t\tfile string\n\t\twant strin"
}
]
About this extraction
This page contains the full source code of the go-gorm/gorm GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 184 files (1.2 MB), approximately 367.6k tokens, and a symbol index with 1607 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.