[
  {
    "path": ".github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n\ngithub: [jinzhu]\npatreon: jinzhu\nopen_collective: gorm\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "---\nversion: 2\nupdates:\n  - package-ecosystem: gomod\n    directory: /\n    schedule:\n      interval: weekly\n  - package-ecosystem: github-actions\n    directory: /\n    schedule:\n      interval: weekly\n  - package-ecosystem: gomod\n    directory: /tests\n    schedule:\n      interval: weekly\n"
  },
  {
    "path": ".github/labels.json",
    "content": "{\n  \"labels\": {\n    \"critical\": {\n      \"name\": \"type:critical\",\n      \"colour\": \"#E84137\",\n      \"description\": \"critical questions\"\n    },\n    \"question\": {\n      \"name\": \"type:question\",\n      \"colour\": \"#EDEDED\",\n      \"description\": \"general questions\"\n    },\n    \"feature\": {\n      \"name\": \"type:feature_request\",\n      \"colour\": \"#43952A\",\n      \"description\": \"feature request\"\n    },\n    \"invalid_question\": {\n      \"name\": \"type:invalid question\",\n      \"colour\": \"#CF2E1F\",\n      \"description\": \"invalid question (not related to GORM or described in document or not enough information provided)\"\n    },\n    \"with_playground\": {\n      \"name\": \"type:with reproduction steps\",\n      \"colour\": \"#00ff00\",\n      \"description\": \"with reproduction steps\"\n    },\n    \"without_playground\": {\n      \"name\": \"type:missing reproduction steps\",\n      \"colour\": \"#CF2E1F\",\n      \"description\": \"missing reproduction steps\"\n    },\n    \"has_pr\": {\n      \"name\": \"type:has pull request\",\n      \"colour\": \"#43952A\",\n      \"description\": \"has pull request\"\n    },\n    \"not_tested\": {\n      \"name\": \"type:not tested\",\n      \"colour\": \"#CF2E1F\",\n      \"description\": \"not tested\"\n    },\n    \"tested\": {\n      \"name\": \"type:tested\",\n      \"colour\": \"#00ff00\",\n      \"description\": \"tested\"\n    },\n    \"breaking_change\": {\n      \"name\": \"type:breaking change\",\n      \"colour\": \"#CF2E1F\",\n      \"description\": \"breaking change\"\n    }\n  },\n  \"issue\": {\n    \"with_playground\": {\n      \"requires\": 1,\n      \"conditions\": [\n        {\n          \"type\": \"descriptionMatches\",\n          \"pattern\": \"/github.com\\/go-gorm\\/playground\\/pull\\/\\\\d\\\\d+/s\"\n        }\n      ]\n    },\n    \"critical\": {\n      \"requires\": 1,\n      \"conditions\": [\n        {\n          \"type\": \"descriptionMatches\",\n          \"pattern\": \"/(critical|urgent)/i\"\n        },\n        {\n          \"type\": \"titleMatches\",\n          \"pattern\": \"/(critical|urgent)/i\"\n        }\n      ]\n    },\n    \"question\": {\n      \"requires\": 1,\n      \"conditions\": [\n        {\n          \"type\": \"titleMatches\",\n          \"pattern\": \"/question/i\"\n        },\n        {\n          \"type\": \"descriptionMatches\",\n          \"pattern\": \"/question/i\"\n        }\n      ]\n    },\n    \"feature\": {\n      \"requires\": 1,\n      \"conditions\": [\n        {\n          \"type\": \"titleMatches\",\n          \"pattern\": \"/feature/i\"\n        },\n        {\n          \"type\": \"descriptionMatches\",\n          \"pattern\": \"/Describe the feature/i\"\n        }\n      ]\n    },\n    \"without_playground\": {\n      \"requires\": 6,\n      \"conditions\": [\n        {\n          \"type\": \"descriptionMatches\",\n          \"pattern\": \"/^((?!github.com\\/go-gorm\\/playground\\/pull\\/\\\\d\\\\d+).)*$/s\"\n        },\n        {\n          \"type\": \"titleMatches\",\n          \"pattern\": \"/^((?!question).)*$/s\"\n        },\n        {\n          \"type\": \"descriptionMatches\",\n          \"pattern\": \"/^((?!question).)*$/is\"\n        },\n        {\n          \"type\": \"descriptionMatches\",\n          \"pattern\": \"/^((?!Describe the feature).)*$/is\"\n        },\n        {\n          \"type\": \"titleMatches\",\n          \"pattern\": \"/^((?!critical|urgent).)*$/s\"\n        },\n        {\n          \"type\": \"descriptionMatches\",\n          \"pattern\": \"/^((?!critical|urgent).)*$/s\"\n        }\n      ]\n    }\n  },\n  \"pr\": {\n    \"critical\": {\n      \"requires\": 1,\n      \"conditions\": [\n        {\n          \"type\": \"descriptionMatches\",\n          \"pattern\": \"/(critical|urgent)/i\"\n        },\n        {\n          \"type\": \"titleMatches\",\n          \"pattern\": \"/(critical|urgent)/i\"\n        }\n      ]\n    },\n    \"not_tested\": {\n      \"requires\": 1,\n      \"conditions\": [\n        {\n          \"type\": \"descriptionMatches\",\n          \"pattern\": \"/\\\\[\\\\] Tested/\"\n        }\n      ]\n    },\n    \"breaking_change\": {\n      \"requires\": 1,\n      \"conditions\": [\n        {\n          \"type\": \"descriptionMatches\",\n          \"pattern\": \"/\\\\[\\\\] Non breaking API changes/\"\n        }\n      ]\n    }\n  }\n}\n"
  },
  {
    "path": ".github/release-drafter.yml",
    "content": "name-template: 'v Release $NEXT_PATCH_VERSION 🌈'\ntag-template: 'v$NEXT_PATCH_VERSION'\ncategories:\n  - title: '🚀 Features'\n    labels:\n      - 'feature'\n      - 'enhancement'\n  - title: '🐛 Bug Fixes'\n    labels:\n      - 'fix'\n      - 'bugfix'\n      - 'bug'\n  - title: '🧰 Maintenance'\n    label: 'chore'\nchange-template: '- $TITLE @$AUTHOR (#$NUMBER)'\nchange-title-escapes: '\\<*_&'\ntemplate: |\n  ## Changes\n\n  $CHANGES"
  },
  {
    "path": ".github/workflows/create-release.yml",
    "content": "name: Create Release\n\non:\n  push:\n    tags:\n      - 'v*.*.*'\n\npermissions:\n  contents: write\n  pull-requests: read\n\njobs:\n  create_release:\n    name: Create Release\n    runs-on: ubuntu-latest\n\n    steps:\n      - name: Checkout code\n        uses: actions/checkout@v4\n\n      - name: Generate Release Notes and Publish\n        id: generate_release_notes\n        uses: release-drafter/release-drafter@v6\n        with:\n          config-name: 'release-drafter.yml'\n          name: \"Release ${{ github.ref_name }}\"\n          tag: ${{ github.ref_name }}\n          publish: true\n          prerelease: false\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n"
  },
  {
    "path": ".github/workflows/golangci-lint.yml",
    "content": "name: golangci-lint\non:\n  push:\n    branches:\n      - main\n      - master\n  pull_request:\n\npermissions:\n  contents: read\n  pull-requests: read\n\njobs:\n  golangci:\n    name: lint\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n      - uses: actions/setup-go@v5\n        with:\n          go-version: stable\n      - name: golangci-lint\n        uses: golangci/golangci-lint-action@v7\n        with:\n          version: v2.0\n          only-new-issues: true\n"
  },
  {
    "path": ".github/workflows/invalid_question.yml",
    "content": "name: \"Close invalid questions issues\"\non:\n  schedule:\n  - cron: \"*/10 * * * *\"\n\npermissions:\n  contents: read\n\njobs:\n  stale:\n    permissions:\n      issues: write  # for actions/stale to close stale issues\n      pull-requests: write  # for actions/stale to close stale PRs\n    runs-on: ubuntu-latest\n    env:\n      ACTIONS_STEP_DEBUG: true\n    steps:\n    - name: Close Stale Issues\n      uses: actions/stale@v8\n      with:\n        repo-token: ${{ secrets.GITHUB_TOKEN }}\n        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) ✨\"\n        stale-issue-label: \"status:stale\"\n        days-before-stale: 0\n        days-before-close: 30\n        remove-stale-when-updated: true\n        only-labels: \"type:invalid question\"\n\n"
  },
  {
    "path": ".github/workflows/labeler.yml",
    "content": "name: \"Issue Labeler\"\non:\n  issues:\n    types: [opened, edited, reopened]\n  pull_request:\n    types: [opened, edited, reopened]\n\njobs:\n  triage:\n    runs-on: ubuntu-latest\n    name: Label issues and pull requests\n    steps:\n      - name: check out\n        uses: actions/checkout@v4\n\n      - name: labeler\n        uses: jinzhu/super-labeler-action@develop\n        with:\n          GITHUB_TOKEN: \"${{ secrets.GITHUB_TOKEN }}\"\n"
  },
  {
    "path": ".github/workflows/missing_playground.yml",
    "content": "name: \"Close Missing Playground issues\"\non:\n  schedule:\n  - cron: \"*/10 * * * *\"\n\npermissions:\n  contents: read\n\njobs:\n  stale:\n    permissions:\n      issues: write  # for actions/stale to close stale issues\n      pull-requests: write  # for actions/stale to close stale PRs\n    runs-on: ubuntu-latest\n    env:\n      ACTIONS_STEP_DEBUG: true\n    steps:\n    - name: Close Stale Issues\n      uses: actions/stale@v8\n      with:\n        repo-token: ${{ secrets.GITHUB_TOKEN }}\n        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) ✨\"\n        stale-issue-label: \"status:stale\"\n        days-before-stale: 0\n        days-before-close: 30\n        remove-stale-when-updated: true\n        only-labels: \"type:missing reproduction steps\"\n"
  },
  {
    "path": ".github/workflows/stale.yml",
    "content": "name: \"Stale\"\non:\n  schedule:\n  - cron: \"0 2 * * *\"\n\npermissions:\n  contents: read\n\njobs:\n  stale:\n    permissions:\n      issues: write  # for actions/stale to close stale issues\n      pull-requests: write  # for actions/stale to close stale PRs\n    runs-on: ubuntu-latest\n    env:\n      ACTIONS_STEP_DEBUG: true\n    steps:\n    - name: Close Stale Issues\n      uses: actions/stale@v8\n      with:\n        repo-token: ${{ secrets.GITHUB_TOKEN }}\n        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\"\n        days-before-stale: 360\n        days-before-close: 180\n        stale-issue-label: \"status:stale\"\n        exempt-issue-labels: 'type:feature,type:with reproduction steps,type:has pull request'\n        stale-pr-label: 'status:stale'\n        exempt-pr-labels: 'type:feature,type:with reproduction steps,type:has pull request'\n"
  },
  {
    "path": ".github/workflows/tests.yml",
    "content": "name: tests\n\non:\n  push:\n    branches-ignore:\n      - 'gh-pages'\n  pull_request:\n    branches-ignore:\n      - 'gh-pages'\n\npermissions:\n  contents: read\n\njobs:\n  # Label of the container job\n  sqlite:\n    strategy:\n      matrix:\n        go: ['1.24', '1.25']\n        platform: [ubuntu-latest] # can not run in windows OS\n    runs-on: ${{ matrix.platform }}\n\n    steps:\n    - name: Set up Go 1.x\n      uses: actions/setup-go@v4\n      with:\n        go-version: ${{ matrix.go }}\n\n    - name: Check out code into the Go module directory\n      uses: actions/checkout@v4\n\n    - name: go mod package cache\n      uses: actions/cache@v4\n      with:\n        path: ~/go/pkg/mod\n        key: ${{ runner.os }}-go-${{ matrix.go }}-${{ hashFiles('tests/go.mod') }}\n\n    - name: Tests\n      run: GITHUB_ACTION=true GORM_DIALECT=sqlite ./tests/tests_all.sh\n\n  mysql:\n    strategy:\n      matrix:\n        dbversion: ['mysql:9', 'mysql:8', 'mysql:5.7']\n        go: ['1.24', '1.25']\n        platform: [ubuntu-latest]\n    runs-on: ${{ matrix.platform }}\n\n    services:\n      mysql:\n        image: ${{ matrix.dbversion }}\n        env:\n          MYSQL_DATABASE: gorm\n          MYSQL_USER: gorm\n          MYSQL_PASSWORD: gorm\n          MYSQL_RANDOM_ROOT_PASSWORD: \"yes\"\n        ports:\n          - 9910:3306\n        options: >-\n          --health-cmd \"mysqladmin ping -ugorm -pgorm\"\n          --health-interval 10s\n          --health-start-period 10s\n          --health-timeout 5s\n          --health-retries 10\n\n    steps:\n    - name: Set up Go 1.x\n      uses: actions/setup-go@v4\n      with:\n        go-version: ${{ matrix.go }}\n\n    - name: Check out code into the Go module directory\n      uses: actions/checkout@v4\n\n    - name: go mod package cache\n      uses: actions/cache@v4\n      with:\n        path: ~/go/pkg/mod\n        key: ${{ runner.os }}-go-${{ matrix.go }}-${{ hashFiles('tests/go.mod') }}\n\n    - name: Tests\n      run: GITHUB_ACTION=true GORM_DIALECT=mysql GORM_DSN=\"gorm:gorm@tcp(localhost:9910)/gorm?charset=utf8&parseTime=True\" ./tests/tests_all.sh\n\n  mariadb:\n    strategy:\n      matrix:\n        dbversion: [ 'mariadb:latest' ]\n        go: ['1.24', '1.25']\n        platform: [ ubuntu-latest ]\n    runs-on: ${{ matrix.platform }}\n\n    services:\n      mysql:\n        image: ${{ matrix.dbversion }}\n        env:\n          MYSQL_DATABASE: gorm\n          MYSQL_USER: gorm\n          MYSQL_PASSWORD: gorm\n          MYSQL_RANDOM_ROOT_PASSWORD: \"yes\"\n        ports:\n          - 9910:3306\n        options: >-\n          --health-cmd \"mariadb-admin ping -ugorm -pgorm\"\n          --health-interval 10s\n          --health-start-period 10s\n          --health-timeout 5s\n          --health-retries 10\n\n    steps:\n      - name: Set up Go 1.x\n        uses: actions/setup-go@v4\n        with:\n          go-version: ${{ matrix.go }}\n\n      - name: Check out code into the Go module directory\n        uses: actions/checkout@v4\n\n      - name: go mod package cache\n        uses: actions/cache@v4\n        with:\n          path: ~/go/pkg/mod\n          key: ${{ runner.os }}-go-${{ matrix.go }}-${{ hashFiles('tests/go.mod') }}\n\n      - name: Tests\n        run: GITHUB_ACTION=true GORM_DIALECT=mysql GORM_DSN=\"gorm:gorm@tcp(localhost:9910)/gorm?charset=utf8&parseTime=True\" ./tests/tests_all.sh\n\n  postgres:\n    strategy:\n      matrix:\n        dbversion: ['postgres:latest', 'postgres:15', 'postgres:14', 'postgres:13']\n        go: ['1.24', '1.25']\n        platform: [ubuntu-latest] # can not run in macOS and Windows\n    runs-on: ${{ matrix.platform }}\n\n    services:\n      postgres:\n        image: ${{ matrix.dbversion }}\n        env:\n          POSTGRES_PASSWORD: gorm\n          POSTGRES_USER: gorm\n          POSTGRES_DB: gorm\n          TZ: Asia/Shanghai\n        ports:\n          - 9920:5432\n        # Set health checks to wait until postgres has started\n        options: >-\n          --health-cmd pg_isready\n          --health-interval 10s\n          --health-timeout 5s\n          --health-retries 5\n\n    steps:\n    - name: Set up Go 1.x\n      uses: actions/setup-go@v4\n      with:\n        go-version: ${{ matrix.go }}\n\n    - name: Check out code into the Go module directory\n      uses: actions/checkout@v4\n\n    - name: go mod package cache\n      uses: actions/cache@v4\n      with:\n        path: ~/go/pkg/mod\n        key: ${{ runner.os }}-go-${{ matrix.go }}-${{ hashFiles('tests/go.mod') }}\n\n    - name: Tests\n      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\n\n  sqlserver:\n    strategy:\n      matrix:\n        go: ['1.24', '1.25']\n        platform: [ubuntu-latest] # can not run test in macOS and windows\n    runs-on: ${{ matrix.platform }}\n\n    services:\n      mssql:\n        image: mcr.microsoft.com/mssql/server:2022-latest\n        env:\n          TZ: Asia/Shanghai\n          ACCEPT_EULA: Y\n          MSSQL_SA_PASSWORD: LoremIpsum86\n        ports:\n          - 9930:1433\n        options: >-\n          --health-cmd=\"/opt/mssql-tools18/bin/sqlcmd -S localhost -U sa -P ${MSSQL_SA_PASSWORD} -N -C -l 30 -Q \\\"SELECT 1\\\" || exit 1\"\n          --health-start-period 10s\n          --health-interval 10s\n          --health-timeout 5s\n          --health-retries 10\n\n    steps:\n    - name: Set up Go 1.x\n      uses: actions/setup-go@v4\n      with:\n        go-version: ${{ matrix.go }}\n\n    - name: Check out code into the Go module directory\n      uses: actions/checkout@v4\n\n    - name: go mod package cache\n      uses: actions/cache@v4\n      with:\n        path: ~/go/pkg/mod\n        key: ${{ runner.os }}-go-${{ matrix.go }}-${{ hashFiles('tests/go.mod') }}\n\n    - name: Tests\n      run: GITHUB_ACTION=true GORM_DIALECT=sqlserver GORM_DSN=\"sqlserver://sa:LoremIpsum86@localhost:9930?database=master\" ./tests/tests_all.sh\n\n  tidb:\n    strategy:\n      matrix:\n        dbversion: [ 'v6.5.0' ]\n        go: ['1.24', '1.25']\n        platform: [ ubuntu-latest ]\n    runs-on: ${{ matrix.platform }}\n\n    steps:\n      - name: Setup TiDB\n        uses: Icemap/tidb-action@main\n        with:\n          port: 9940\n          version: ${{matrix.dbversion}}\n\n      - name: Set up Go 1.x\n        uses: actions/setup-go@v4\n        with:\n          go-version: ${{ matrix.go }}\n\n      - name: Check out code into the Go module directory\n        uses: actions/checkout@v4\n\n\n      - name: go mod package cache\n        uses: actions/cache@v4\n        with:\n          path: ~/go/pkg/mod\n          key: ${{ runner.os }}-go-${{ matrix.go }}-${{ hashFiles('tests/go.mod') }}\n\n      - name: Tests\n        run: GITHUB_ACTION=true GORM_DIALECT=tidb GORM_DSN=\"root:@tcp(localhost:9940)/test?charset=utf8&parseTime=True&loc=Local\" ./tests/tests_all.sh\n\n  gaussdb:\n    strategy:\n      matrix:\n        dbversion: ['opengauss/opengauss:7.0.0-RC1.B023']\n        go: ['1.24', '1.25']\n        platform: [ubuntu-latest] # can not run in macOS and Windows\n    runs-on: ${{ matrix.platform }}\n\n    services:\n      gaussdb:\n        image: ${{ matrix.dbversion }}\n        env:\n          # GaussDB has password limitations\n          GS_PASSWORD: Gaussdb@123\n          TZ: Asia/Shanghai\n        ports:\n          - 9950:5432\n\n    steps:\n      - name: Set up Go 1.x\n        uses: actions/setup-go@v4\n        with:\n          go-version: ${{ matrix.go }}\n\n      - name: Check out code into the Go module directory\n        uses: actions/checkout@v4\n\n      - name: Waiting for GaussDB to be ready\n        run: |\n          container_name=$(docker ps --filter \"ancestor=opengauss/opengauss:7.0.0-RC1.B023\" --format \"{{.Names}}\")\n          if [ -z \"$container_name\" ]; then\n            echo \"Error: failed to find a container created from the 'opengauss/opengauss:7.0.0-RC1.B023' image.\"\n            exit 1\n          fi\n          max_retries=12\n          retry_count=0\n          if [ -t 0 ]; then\n            TTY_FLAG=\"-t\"\n          else\n            TTY_FLAG=\"\"\n          fi\n          while [ $retry_count -lt $max_retries ]; do\n            if docker exec -i \"${container_name}\" bash -c \"su - omm -c 'gsql -U omm -c \\\"select 1;\\\"'\" \n            then\n              echo \"Creating database gorm...\"\n              sql_file='/tmp/create_database.sql'\n              echo \"CREATE DATABASE gorm DBCOMPATIBILITY 'PG';\" > ${sql_file}\n              docker cp \"${sql_file}\" \"${container_name}\":\"${sql_file}\"\n              docker exec -i ${TTY_FLAG} \"${container_name}\" bash -c \"su - omm -c 'gsql -U omm -f ${sql_file}'\"\n              echo \"Database initialization completed.\"\n              break\n            fi\n\n            echo \"Waiting for database to be ready... (attempt $((retry_count + 1))/$max_retries)\"\n            sleep 10\n            ((++retry_count))\n          done\n          exit 0\n\n      - name: go mod package cache\n        uses: actions/cache@v4\n        with:\n          path: ~/go/pkg/mod\n          key: ${{ runner.os }}-go-${{ matrix.go }}-${{ hashFiles('tests/go.mod') }}\n\n      - name: Tests\n        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\n"
  },
  {
    "path": ".gitignore",
    "content": "TODO*\ndocuments\ncoverage.txt\n_book\n.idea\nvendor\n.vscode\n"
  },
  {
    "path": ".golangci.yml",
    "content": "version: \"2\"\n\nlinters:\n  default: standard\n  enable:\n    - cyclop\n    - gocritic\n    - gosec\n    - ineffassign\n    - misspell\n    - prealloc\n    - unconvert\n    - unparam\n    - whitespace\n\nformatters:\n  enable:\n    - gofumpt\n    - goimports\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nWe as members, contributors, and leaders pledge to participate in our\ncommunity a harassment-free experience for everyone, regardless of age, body\nsize, visible or invisible disability, ethnicity, sex characteristics, gender\nidentity and expression, level of experience, education, socio-economic status,\nnationality, personal appearance, race, religion, or sexual identity\nand orientation.\n\nWe pledge to act and interact in ways that contribute to an open, welcoming,\ndiverse, inclusive, and healthy community.\n\n## Our Standards\n\nExamples of behavior that contributes to a positive environment for our\ncommunity includes:\n\n* Demonstrating empathy and kindness toward other people\n* Being respectful of differing opinions, viewpoints, and experiences\n* Giving and gracefully accepting constructive feedback\n* Accepting responsibility and apologizing to those affected by our mistakes,\n  and learning from the experience\n* Focusing on what is best not just for us as individuals, but for the\n  overall community\n\nExamples of unacceptable behavior include:\n\n* The use of sexualized language or imagery, and sexual attention or\n  advances of any kind\n* Trolling, insulting or derogatory comments, and personal or political attacks\n* Public or private harassment\n* Publishing others' private information, such as a physical or email\n  address, without their explicit permission\n* Other conduct which could reasonably be considered inappropriate in a\n  professional setting\n\n## Enforcement Responsibilities\n\nCommunity leaders are responsible for clarifying and enforcing our standards of\nacceptable behavior and will take appropriate and fair corrective action in\nresponse to any behavior that they deem inappropriate, threatening, offensive,\nor harmful.\n\nCommunity leaders have the right and responsibility to remove, edit, or reject\ncomments, commits, code, wiki edits, issues, and other contributions that are\nnot aligned to this Code of Conduct, and will communicate reasons for moderation\ndecisions when appropriate.\n\n## Scope\n\nThis Code of Conduct applies within all community spaces and also applies when\nan individual is officially representing the community in public spaces.\nExamples of representing our community include using an official e-mail address,\nposting via an official social media account, or acting as an appointed\nrepresentative at an online or offline event.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be\nreported to the community leaders responsible for enforcement at\n.\nAll complaints will be reviewed and investigated promptly and fairly.\n\nAll community leaders are obligated to respect the privacy and security of the\nreporter of any incident.\n\n## Enforcement Guidelines\n\nCommunity leaders will follow these Community Impact Guidelines in determining\nthe consequences for any action they deem in violation of this Code of Conduct:\n\n### 1. Correction\n\n**Community Impact**: Use of inappropriate language or other behavior deemed\nunprofessional or unwelcome in the community.\n\n**Consequence**: A private, written warning from community leaders, providing\nclarity around the nature of the violation and an explanation of why the\nbehavior was inappropriate. A public apology may be requested.\n\n### 2. Warning\n\n**Community Impact**: A violation through a single incident or series\nof actions.\n\n**Consequence**: A warning with consequences for continued behavior. No\ninteraction with the people involved, including unsolicited interaction with\nthose enforcing the Code of Conduct, for a specified period. This\nincludes avoiding interactions in community spaces and external channels\nlike social media. Violating these terms may lead to a temporary or\npermanent ban.\n\n### 3. Temporary Ban\n\n**Community Impact**: A serious violation of community standards, including\nsustained inappropriate behavior.\n\n**Consequence**: A temporary ban from any interaction or public\ncommunication with the community for a specified period. No public or\nprivate interaction with the people involved, including unsolicited interaction\nwith those enforcing the Code of Conduct, is allowed during this period.\nViolating these terms may lead to a permanent ban.\n\n### 4. Permanent Ban\n\n**Community Impact**: Demonstrating a pattern of violation of community\nstandards, including sustained inappropriate behavior,  harassment of an\nindividual, or aggression toward or disparagement of classes of individuals.\n\n**Consequence**: A permanent ban from any sort of public interaction within\nthe community.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage],\nversion 2.0, available at\nhttps://www.contributor-covenant.org/version/2/0/code_of_conduct.html.\n\nCommunity Impact Guidelines were inspired by [Mozilla's code of conduct\nenforcement ladder](https://github.com/mozilla/diversity).\n\n[homepage]: https://www.contributor-covenant.org\n\nFor answers to common questions about this code of conduct, see the FAQ at\nhttps://www.contributor-covenant.org/faq. Translations are available at\nhttps://www.contributor-covenant.org/translations.\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2013-present  Jinzhu <wosmvp@gmail.com>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# GORM\n\nThe fantastic ORM library for Golang, aims to be developer friendly.\n\n[![go report card](https://goreportcard.com/badge/github.com/go-gorm/gorm \"go report card\")](https://goreportcard.com/report/github.com/go-gorm/gorm)\n[![test status](https://github.com/go-gorm/gorm/actions/workflows/tests.yml/badge.svg)](https://github.com/go-gorm/gorm/actions)\n[![MIT license](https://img.shields.io/badge/license-MIT-brightgreen.svg)](https://opensource.org/licenses/MIT)\n[![Go.Dev reference](https://img.shields.io/badge/go.dev-reference-blue?logo=go&logoColor=white)](https://pkg.go.dev/gorm.io/gorm?tab=doc)\n\n## Overview\n\n* Full-Featured ORM\n* Associations (Has One, Has Many, Belongs To, Many To Many, Polymorphism, Single-table inheritance)\n* Hooks (Before/After Create/Save/Update/Delete/Find)\n* Eager loading with `Preload`, `Joins`\n* Transactions, Nested Transactions, Save Point, RollbackTo to Saved Point\n* Context, Prepared Statement Mode, DryRun Mode\n* Batch Insert, FindInBatches, Find To Map\n* SQL Builder, Upsert, Locking, Optimizer/Index/Comment Hints, NamedArg, Search/Update/Create with SQL Expr\n* Composite Primary Key\n* Auto Migrations\n* Logger\n* Extendable, flexible plugin API: Database Resolver (Multiple Databases, Read/Write Splitting) / Prometheus…\n* Every feature comes with tests\n* Developer Friendly\n\n## Getting Started\n\n* GORM Guides [https://gorm.io](https://gorm.io)\n* Gen Guides [https://gorm.io/gen/index.html](https://gorm.io/gen/index.html)\n\n## Contributing\n\n[You can help to deliver a better GORM, check out things you can do](https://gorm.io/contribute.html)\n\n## Contributors\n\n[Thank you](https://github.com/go-gorm/gorm/graphs/contributors) for contributing to the GORM framework!\n\n## License\n\n© Jinzhu, 2013~time.Now\n\nReleased under the [MIT License](https://github.com/go-gorm/gorm/blob/master/LICENSE)\n"
  },
  {
    "path": "association.go",
    "content": "package gorm\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n\t\"strings\"\n\n\t\"gorm.io/gorm/clause\"\n\t\"gorm.io/gorm/schema\"\n\t\"gorm.io/gorm/utils\"\n)\n\n// Association Mode contains some helper methods to handle relationship things easily.\ntype Association struct {\n\tDB           *DB\n\tRelationship *schema.Relationship\n\tUnscope      bool\n\tError        error\n}\n\nfunc (db *DB) Association(column string) *Association {\n\tassociation := &Association{DB: db, Unscope: db.Statement.Unscoped}\n\ttable := db.Statement.Table\n\n\tif association.Error = db.Statement.Parse(db.Statement.Model); association.Error == nil {\n\t\tdb.Statement.Table = table\n\t\tassociation.Relationship = db.Statement.Schema.Relationships.Relations[column]\n\n\t\tif association.Relationship == nil {\n\t\t\tassociation.Error = fmt.Errorf(\"%w: %s\", ErrUnsupportedRelation, column)\n\t\t}\n\n\t\tdb.Statement.ReflectValue = reflect.ValueOf(db.Statement.Model)\n\t\tfor db.Statement.ReflectValue.Kind() == reflect.Ptr {\n\t\t\tdb.Statement.ReflectValue = db.Statement.ReflectValue.Elem()\n\t\t}\n\t}\n\n\treturn association\n}\n\nfunc (association *Association) Unscoped() *Association {\n\treturn &Association{\n\t\tDB:           association.DB,\n\t\tRelationship: association.Relationship,\n\t\tError:        association.Error,\n\t\tUnscope:      true,\n\t}\n}\n\nfunc (association *Association) Find(out interface{}, conds ...interface{}) error {\n\tif association.Error == nil {\n\t\tassociation.Error = association.buildCondition().Find(out, conds...).Error\n\t}\n\treturn association.Error\n}\n\nfunc (association *Association) Append(values ...interface{}) error {\n\tvalues = expandValues(values)\n\n\tif association.Error == nil {\n\t\tswitch association.Relationship.Type {\n\t\tcase schema.HasOne, schema.BelongsTo:\n\t\t\tif len(values) > 0 {\n\t\t\t\tassociation.Error = association.Replace(values...)\n\t\t\t}\n\t\tdefault:\n\t\t\tassociation.saveAssociation( /*clear*/ false, values...)\n\t\t}\n\t}\n\n\treturn association.Error\n}\n\nfunc (association *Association) Replace(values ...interface{}) error {\n\tvalues = expandValues(values)\n\n\tif association.Error == nil {\n\t\treflectValue := association.DB.Statement.ReflectValue\n\t\trel := association.Relationship\n\n\t\tvar oldBelongsToExpr clause.Expression\n\t\t// we have to record the old BelongsTo value\n\t\tif association.Unscope && rel.Type == schema.BelongsTo {\n\t\t\tvar foreignFields []*schema.Field\n\t\t\tfor _, ref := range rel.References {\n\t\t\t\tif !ref.OwnPrimaryKey {\n\t\t\t\t\tforeignFields = append(foreignFields, ref.ForeignKey)\n\t\t\t\t}\n\t\t\t}\n\t\t\tif _, fvs := schema.GetIdentityFieldValuesMap(association.DB.Statement.Context, reflectValue, foreignFields); len(fvs) > 0 {\n\t\t\t\tcolumn, values := schema.ToQueryValues(rel.FieldSchema.Table, rel.FieldSchema.PrimaryFieldDBNames, fvs)\n\t\t\t\toldBelongsToExpr = clause.IN{Column: column, Values: values}\n\t\t\t}\n\t\t}\n\n\t\t// save associations\n\t\tif association.saveAssociation( /*clear*/ true, values...); association.Error != nil {\n\t\t\treturn association.Error\n\t\t}\n\n\t\t// set old association's foreign key to null\n\t\tswitch rel.Type {\n\t\tcase schema.BelongsTo:\n\t\t\tif len(values) == 0 {\n\t\t\t\tupdateMap := map[string]interface{}{}\n\t\t\t\tswitch reflectValue.Kind() {\n\t\t\t\tcase reflect.Slice, reflect.Array:\n\t\t\t\t\tfor i := 0; i < reflectValue.Len(); i++ {\n\t\t\t\t\t\tassociation.Error = rel.Field.Set(association.DB.Statement.Context, reflectValue.Index(i), reflect.Zero(rel.Field.FieldType).Interface())\n\t\t\t\t\t}\n\t\t\t\tcase reflect.Struct:\n\t\t\t\t\tassociation.Error = rel.Field.Set(association.DB.Statement.Context, reflectValue, reflect.Zero(rel.Field.FieldType).Interface())\n\t\t\t\t}\n\n\t\t\t\tfor _, ref := range rel.References {\n\t\t\t\t\tupdateMap[ref.ForeignKey.DBName] = nil\n\t\t\t\t}\n\n\t\t\t\tassociation.Error = association.DB.UpdateColumns(updateMap).Error\n\t\t\t}\n\t\t\tif association.Unscope && oldBelongsToExpr != nil {\n\t\t\t\tassociation.Error = association.DB.Model(nil).Where(oldBelongsToExpr).Delete(reflect.New(rel.FieldSchema.ModelType).Interface()).Error\n\t\t\t}\n\t\tcase schema.HasOne, schema.HasMany:\n\t\t\tvar (\n\t\t\t\tprimaryFields []*schema.Field\n\t\t\t\tforeignKeys   []string\n\t\t\t\tupdateMap     = map[string]interface{}{}\n\t\t\t\trelValues     = schema.GetRelationsValues(association.DB.Statement.Context, reflectValue, []*schema.Relationship{rel})\n\t\t\t\tmodelValue    = reflect.New(rel.FieldSchema.ModelType).Interface()\n\t\t\t\ttx            = association.DB.Model(modelValue)\n\t\t\t)\n\n\t\t\tif _, rvs := schema.GetIdentityFieldValuesMap(association.DB.Statement.Context, relValues, rel.FieldSchema.PrimaryFields); len(rvs) > 0 {\n\t\t\t\tif column, values := schema.ToQueryValues(rel.FieldSchema.Table, rel.FieldSchema.PrimaryFieldDBNames, rvs); len(values) > 0 {\n\t\t\t\t\ttx.Not(clause.IN{Column: column, Values: values})\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfor _, ref := range rel.References {\n\t\t\t\tif ref.OwnPrimaryKey {\n\t\t\t\t\tprimaryFields = append(primaryFields, ref.PrimaryKey)\n\t\t\t\t\tforeignKeys = append(foreignKeys, ref.ForeignKey.DBName)\n\t\t\t\t\tupdateMap[ref.ForeignKey.DBName] = nil\n\t\t\t\t} else if ref.PrimaryValue != \"\" {\n\t\t\t\t\ttx.Where(clause.Eq{Column: ref.ForeignKey.DBName, Value: ref.PrimaryValue})\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif _, pvs := schema.GetIdentityFieldValuesMap(association.DB.Statement.Context, reflectValue, primaryFields); len(pvs) > 0 {\n\t\t\t\tcolumn, values := schema.ToQueryValues(rel.FieldSchema.Table, foreignKeys, pvs)\n\t\t\t\tif association.Unscope {\n\t\t\t\t\tassociation.Error = tx.Where(clause.IN{Column: column, Values: values}).Delete(modelValue).Error\n\t\t\t\t} else {\n\t\t\t\t\tassociation.Error = tx.Where(clause.IN{Column: column, Values: values}).UpdateColumns(updateMap).Error\n\t\t\t\t}\n\t\t\t}\n\t\tcase schema.Many2Many:\n\t\t\tvar (\n\t\t\t\tprimaryFields, relPrimaryFields     []*schema.Field\n\t\t\t\tjoinPrimaryKeys, joinRelPrimaryKeys []string\n\t\t\t\tmodelValue                          = reflect.New(rel.JoinTable.ModelType).Interface()\n\t\t\t\ttx                                  = association.DB.Model(modelValue)\n\t\t\t)\n\n\t\t\tfor _, ref := range rel.References {\n\t\t\t\tif ref.PrimaryValue == \"\" {\n\t\t\t\t\tif ref.OwnPrimaryKey {\n\t\t\t\t\t\tprimaryFields = append(primaryFields, ref.PrimaryKey)\n\t\t\t\t\t\tjoinPrimaryKeys = append(joinPrimaryKeys, ref.ForeignKey.DBName)\n\t\t\t\t\t} else {\n\t\t\t\t\t\trelPrimaryFields = append(relPrimaryFields, ref.PrimaryKey)\n\t\t\t\t\t\tjoinRelPrimaryKeys = append(joinRelPrimaryKeys, ref.ForeignKey.DBName)\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\ttx.Clauses(clause.Eq{Column: ref.ForeignKey.DBName, Value: ref.PrimaryValue})\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t_, pvs := schema.GetIdentityFieldValuesMap(association.DB.Statement.Context, reflectValue, primaryFields)\n\t\t\tif column, values := schema.ToQueryValues(rel.JoinTable.Table, joinPrimaryKeys, pvs); len(values) > 0 {\n\t\t\t\ttx.Where(clause.IN{Column: column, Values: values})\n\t\t\t} else {\n\t\t\t\treturn ErrPrimaryKeyRequired\n\t\t\t}\n\n\t\t\t_, rvs := schema.GetIdentityFieldValuesMapFromValues(association.DB.Statement.Context, values, relPrimaryFields)\n\t\t\tif relColumn, relValues := schema.ToQueryValues(rel.JoinTable.Table, joinRelPrimaryKeys, rvs); len(relValues) > 0 {\n\t\t\t\ttx.Where(clause.Not(clause.IN{Column: relColumn, Values: relValues}))\n\t\t\t}\n\n\t\t\tassociation.Error = tx.Delete(modelValue).Error\n\t\t}\n\t}\n\treturn association.Error\n}\n\nfunc (association *Association) Delete(values ...interface{}) error {\n\tvalues = expandValues(values)\n\n\tif association.Error == nil {\n\t\tvar (\n\t\t\treflectValue  = association.DB.Statement.ReflectValue\n\t\t\trel           = association.Relationship\n\t\t\tprimaryFields []*schema.Field\n\t\t\tforeignKeys   []string\n\t\t\tupdateAttrs   = map[string]interface{}{}\n\t\t\tconds         []clause.Expression\n\t\t)\n\n\t\tfor _, ref := range rel.References {\n\t\t\tif ref.PrimaryValue == \"\" {\n\t\t\t\tprimaryFields = append(primaryFields, ref.PrimaryKey)\n\t\t\t\tforeignKeys = append(foreignKeys, ref.ForeignKey.DBName)\n\t\t\t\tupdateAttrs[ref.ForeignKey.DBName] = nil\n\t\t\t} else {\n\t\t\t\tconds = append(conds, clause.Eq{Column: ref.ForeignKey.DBName, Value: ref.PrimaryValue})\n\t\t\t}\n\t\t}\n\n\t\tswitch rel.Type {\n\t\tcase schema.BelongsTo:\n\t\t\tassociationDB := association.DB.Session(&Session{})\n\t\t\ttx := associationDB.Model(reflect.New(rel.Schema.ModelType).Interface())\n\n\t\t\t_, pvs := schema.GetIdentityFieldValuesMap(association.DB.Statement.Context, reflectValue, rel.Schema.PrimaryFields)\n\t\t\tif pcolumn, pvalues := schema.ToQueryValues(rel.Schema.Table, rel.Schema.PrimaryFieldDBNames, pvs); len(pvalues) > 0 {\n\t\t\t\tconds = append(conds, clause.IN{Column: pcolumn, Values: pvalues})\n\t\t\t} else {\n\t\t\t\treturn ErrPrimaryKeyRequired\n\t\t\t}\n\n\t\t\t_, rvs := schema.GetIdentityFieldValuesMapFromValues(association.DB.Statement.Context, values, primaryFields)\n\t\t\trelColumn, relValues := schema.ToQueryValues(rel.Schema.Table, foreignKeys, rvs)\n\t\t\tconds = append(conds, clause.IN{Column: relColumn, Values: relValues})\n\n\t\t\tassociation.Error = tx.Clauses(conds...).UpdateColumns(updateAttrs).Error\n\t\t\tif association.Unscope {\n\t\t\t\tvar foreignFields []*schema.Field\n\t\t\t\tfor _, ref := range rel.References {\n\t\t\t\t\tif !ref.OwnPrimaryKey {\n\t\t\t\t\t\tforeignFields = append(foreignFields, ref.ForeignKey)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif _, fvs := schema.GetIdentityFieldValuesMap(association.DB.Statement.Context, reflectValue, foreignFields); len(fvs) > 0 {\n\t\t\t\t\tcolumn, values := schema.ToQueryValues(rel.FieldSchema.Table, rel.FieldSchema.PrimaryFieldDBNames, fvs)\n\t\t\t\t\tassociation.Error = associationDB.Model(nil).Where(clause.IN{Column: column, Values: values}).Delete(reflect.New(rel.FieldSchema.ModelType).Interface()).Error\n\t\t\t\t}\n\t\t\t}\n\t\tcase schema.HasOne, schema.HasMany:\n\t\t\tmodel := reflect.New(rel.FieldSchema.ModelType).Interface()\n\t\t\ttx := association.DB.Model(model)\n\n\t\t\t_, pvs := schema.GetIdentityFieldValuesMap(association.DB.Statement.Context, reflectValue, primaryFields)\n\t\t\tif pcolumn, pvalues := schema.ToQueryValues(rel.FieldSchema.Table, foreignKeys, pvs); len(pvalues) > 0 {\n\t\t\t\tconds = append(conds, clause.IN{Column: pcolumn, Values: pvalues})\n\t\t\t} else {\n\t\t\t\treturn ErrPrimaryKeyRequired\n\t\t\t}\n\n\t\t\t_, rvs := schema.GetIdentityFieldValuesMapFromValues(association.DB.Statement.Context, values, rel.FieldSchema.PrimaryFields)\n\t\t\trelColumn, relValues := schema.ToQueryValues(rel.FieldSchema.Table, rel.FieldSchema.PrimaryFieldDBNames, rvs)\n\t\t\tconds = append(conds, clause.IN{Column: relColumn, Values: relValues})\n\n\t\t\tif association.Unscope {\n\t\t\t\tassociation.Error = tx.Clauses(conds...).Delete(model).Error\n\t\t\t} else {\n\t\t\t\tassociation.Error = tx.Clauses(conds...).UpdateColumns(updateAttrs).Error\n\t\t\t}\n\t\tcase schema.Many2Many:\n\t\t\tvar (\n\t\t\t\tprimaryFields, relPrimaryFields     []*schema.Field\n\t\t\t\tjoinPrimaryKeys, joinRelPrimaryKeys []string\n\t\t\t\tjoinValue                           = reflect.New(rel.JoinTable.ModelType).Interface()\n\t\t\t)\n\n\t\t\tfor _, ref := range rel.References {\n\t\t\t\tif ref.PrimaryValue == \"\" {\n\t\t\t\t\tif ref.OwnPrimaryKey {\n\t\t\t\t\t\tprimaryFields = append(primaryFields, ref.PrimaryKey)\n\t\t\t\t\t\tjoinPrimaryKeys = append(joinPrimaryKeys, ref.ForeignKey.DBName)\n\t\t\t\t\t} else {\n\t\t\t\t\t\trelPrimaryFields = append(relPrimaryFields, ref.PrimaryKey)\n\t\t\t\t\t\tjoinRelPrimaryKeys = append(joinRelPrimaryKeys, ref.ForeignKey.DBName)\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tconds = append(conds, clause.Eq{Column: ref.ForeignKey.DBName, Value: ref.PrimaryValue})\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t_, pvs := schema.GetIdentityFieldValuesMap(association.DB.Statement.Context, reflectValue, primaryFields)\n\t\t\tif pcolumn, pvalues := schema.ToQueryValues(rel.JoinTable.Table, joinPrimaryKeys, pvs); len(pvalues) > 0 {\n\t\t\t\tconds = append(conds, clause.IN{Column: pcolumn, Values: pvalues})\n\t\t\t} else {\n\t\t\t\treturn ErrPrimaryKeyRequired\n\t\t\t}\n\n\t\t\t_, rvs := schema.GetIdentityFieldValuesMapFromValues(association.DB.Statement.Context, values, relPrimaryFields)\n\t\t\trelColumn, relValues := schema.ToQueryValues(rel.JoinTable.Table, joinRelPrimaryKeys, rvs)\n\t\t\tconds = append(conds, clause.IN{Column: relColumn, Values: relValues})\n\n\t\t\tassociation.Error = association.DB.Where(clause.Where{Exprs: conds}).Model(nil).Delete(joinValue).Error\n\t\t}\n\n\t\tif association.Error == nil {\n\t\t\t// clean up deleted values' foreign key\n\t\t\trelValuesMap, _ := schema.GetIdentityFieldValuesMapFromValues(association.DB.Statement.Context, values, rel.FieldSchema.PrimaryFields)\n\n\t\t\tcleanUpDeletedRelations := func(data reflect.Value) {\n\t\t\t\tif _, zero := rel.Field.ValueOf(association.DB.Statement.Context, data); !zero {\n\t\t\t\t\tfieldValue := reflect.Indirect(rel.Field.ReflectValueOf(association.DB.Statement.Context, data))\n\t\t\t\t\tprimaryValues := make([]interface{}, len(rel.FieldSchema.PrimaryFields))\n\n\t\t\t\t\tswitch fieldValue.Kind() {\n\t\t\t\t\tcase reflect.Slice, reflect.Array:\n\t\t\t\t\t\tvalidFieldValues := reflect.Zero(rel.Field.IndirectFieldType)\n\t\t\t\t\t\tfor i := 0; i < fieldValue.Len(); i++ {\n\t\t\t\t\t\t\tfor idx, field := range rel.FieldSchema.PrimaryFields {\n\t\t\t\t\t\t\t\tprimaryValues[idx], _ = field.ValueOf(association.DB.Statement.Context, fieldValue.Index(i))\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif _, ok := relValuesMap[utils.ToStringKey(primaryValues...)]; !ok {\n\t\t\t\t\t\t\t\tvalidFieldValues = reflect.Append(validFieldValues, fieldValue.Index(i))\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tassociation.Error = rel.Field.Set(association.DB.Statement.Context, data, validFieldValues.Interface())\n\t\t\t\t\tcase reflect.Struct:\n\t\t\t\t\t\tfor idx, field := range rel.FieldSchema.PrimaryFields {\n\t\t\t\t\t\t\tprimaryValues[idx], _ = field.ValueOf(association.DB.Statement.Context, fieldValue)\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif _, ok := relValuesMap[utils.ToStringKey(primaryValues...)]; ok {\n\t\t\t\t\t\t\tif association.Error = rel.Field.Set(association.DB.Statement.Context, data, reflect.Zero(rel.FieldSchema.ModelType).Interface()); association.Error != nil {\n\t\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif rel.JoinTable == nil {\n\t\t\t\t\t\t\t\tfor _, ref := range rel.References {\n\t\t\t\t\t\t\t\t\tif ref.OwnPrimaryKey || ref.PrimaryValue != \"\" {\n\t\t\t\t\t\t\t\t\t\tassociation.Error = ref.ForeignKey.Set(association.DB.Statement.Context, fieldValue, reflect.Zero(ref.ForeignKey.FieldType).Interface())\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tassociation.Error = ref.ForeignKey.Set(association.DB.Statement.Context, data, reflect.Zero(ref.ForeignKey.FieldType).Interface())\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tswitch reflectValue.Kind() {\n\t\t\tcase reflect.Slice, reflect.Array:\n\t\t\t\tfor i := 0; i < reflectValue.Len(); i++ {\n\t\t\t\t\tcleanUpDeletedRelations(reflect.Indirect(reflectValue.Index(i)))\n\t\t\t\t}\n\t\t\tcase reflect.Struct:\n\t\t\t\tcleanUpDeletedRelations(reflectValue)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn association.Error\n}\n\nfunc (association *Association) Clear() error {\n\treturn association.Replace()\n}\n\nfunc (association *Association) Count() (count int64) {\n\tif association.Error == nil {\n\t\tassociation.Error = association.buildCondition().Count(&count).Error\n\t}\n\treturn\n}\n\ntype assignBack struct {\n\tSource reflect.Value\n\tIndex  int\n\tDest   reflect.Value\n}\n\nfunc (association *Association) saveAssociation(clear bool, values ...interface{}) {\n\tvar (\n\t\treflectValue = association.DB.Statement.ReflectValue\n\t\tassignBacks  []assignBack // assign association values back to arguments after save\n\t)\n\n\tappendToRelations := func(source, rv reflect.Value, clear bool) {\n\t\tswitch association.Relationship.Type {\n\t\tcase schema.HasOne, schema.BelongsTo:\n\t\t\tswitch rv.Kind() {\n\t\t\tcase reflect.Slice, reflect.Array:\n\t\t\t\tif rv.Len() > 0 {\n\t\t\t\t\tassociation.Error = association.Relationship.Field.Set(association.DB.Statement.Context, source, rv.Index(0).Addr().Interface())\n\n\t\t\t\t\tif association.Relationship.Field.FieldType.Kind() == reflect.Struct {\n\t\t\t\t\t\tassignBacks = append(assignBacks, assignBack{Source: source, Dest: rv.Index(0)})\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\tcase reflect.Struct:\n\t\t\t\tif !rv.CanAddr() {\n\t\t\t\t\tassociation.Error = ErrInvalidValue\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tassociation.Error = association.Relationship.Field.Set(association.DB.Statement.Context, source, rv.Addr().Interface())\n\n\t\t\t\tif association.Relationship.Field.FieldType.Kind() == reflect.Struct {\n\t\t\t\t\tassignBacks = append(assignBacks, assignBack{Source: source, Dest: rv})\n\t\t\t\t}\n\t\t\t}\n\t\tcase schema.HasMany, schema.Many2Many:\n\t\t\telemType := association.Relationship.Field.IndirectFieldType.Elem()\n\t\t\toldFieldValue := reflect.Indirect(association.Relationship.Field.ReflectValueOf(association.DB.Statement.Context, source))\n\t\t\tvar fieldValue reflect.Value\n\t\t\tif clear {\n\t\t\t\tfieldValue = reflect.MakeSlice(oldFieldValue.Type(), 0, oldFieldValue.Cap())\n\t\t\t} else {\n\t\t\t\tfieldValue = reflect.MakeSlice(oldFieldValue.Type(), oldFieldValue.Len(), oldFieldValue.Cap())\n\t\t\t\treflect.Copy(fieldValue, oldFieldValue)\n\t\t\t}\n\n\t\t\tappendToFieldValues := func(ev reflect.Value) {\n\t\t\t\tif ev.Type().AssignableTo(elemType) {\n\t\t\t\t\tfieldValue = reflect.Append(fieldValue, ev)\n\t\t\t\t} else if ev.Type().Elem().AssignableTo(elemType) {\n\t\t\t\t\tfieldValue = reflect.Append(fieldValue, ev.Elem())\n\t\t\t\t} else {\n\t\t\t\t\tassociation.Error = fmt.Errorf(\"unsupported data type: %v for relation %s\", ev.Type(), association.Relationship.Name)\n\t\t\t\t}\n\n\t\t\t\tif elemType.Kind() == reflect.Struct {\n\t\t\t\t\tassignBacks = append(assignBacks, assignBack{Source: source, Dest: ev, Index: fieldValue.Len()})\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tprocessMap := func(mapv reflect.Value) {\n\t\t\t\tchild := reflect.New(association.Relationship.FieldSchema.ModelType)\n\n\t\t\t\tswitch association.Relationship.Type {\n\t\t\t\tcase schema.HasMany:\n\t\t\t\t\tfor _, ref := range association.Relationship.References {\n\t\t\t\t\t\tkey := reflect.ValueOf(ref.ForeignKey.DBName)\n\t\t\t\t\t\tif ref.OwnPrimaryKey {\n\t\t\t\t\t\t\tv := ref.PrimaryKey.ReflectValueOf(association.DB.Statement.Context, source)\n\t\t\t\t\t\t\tmapv.SetMapIndex(key, v)\n\t\t\t\t\t\t} else if ref.PrimaryValue != \"\" {\n\t\t\t\t\t\t\tmapv.SetMapIndex(key, reflect.ValueOf(ref.PrimaryValue))\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tassociation.Error = association.DB.Session(&Session{\n\t\t\t\t\t\tNewDB: true,\n\t\t\t\t\t}).Model(child.Interface()).Create(mapv.Interface()).Error\n\t\t\t\tcase schema.Many2Many:\n\t\t\t\t\tassociation.Error = association.DB.Session(&Session{\n\t\t\t\t\t\tNewDB: true,\n\t\t\t\t\t}).Model(child.Interface()).Create(mapv.Interface()).Error\n\n\t\t\t\t\tfor _, key := range mapv.MapKeys() {\n\t\t\t\t\t\tk := strings.ToLower(key.String())\n\t\t\t\t\t\tif f, ok := association.Relationship.FieldSchema.FieldsByDBName[k]; ok {\n\t\t\t\t\t\t\t_ = f.Set(association.DB.Statement.Context, child, mapv.MapIndex(key).Interface())\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tappendToFieldValues(child)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tswitch rv.Kind() {\n\t\t\tcase reflect.Map:\n\t\t\t\tprocessMap(rv)\n\t\t\tcase reflect.Slice, reflect.Array:\n\t\t\t\tfor i := 0; i < rv.Len(); i++ {\n\t\t\t\t\telem := reflect.Indirect(rv.Index(i))\n\t\t\t\t\tif elem.Kind() == reflect.Map {\n\t\t\t\t\t\tprocessMap(elem)\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tappendToFieldValues(elem.Addr())\n\t\t\t\t}\n\t\t\tcase reflect.Struct:\n\t\t\t\tif !rv.CanAddr() {\n\t\t\t\t\tassociation.Error = ErrInvalidValue\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tappendToFieldValues(rv.Addr())\n\t\t\t}\n\n\t\t\tif association.Error == nil {\n\t\t\t\tassociation.Error = association.Relationship.Field.Set(association.DB.Statement.Context, source, fieldValue.Interface())\n\t\t\t}\n\t\t}\n\t}\n\n\tselectedSaveColumns := []string{association.Relationship.Name}\n\tomitColumns := []string{}\n\tselectColumns, _ := association.DB.Statement.SelectAndOmitColumns(true, false)\n\tfor name, ok := range selectColumns {\n\t\tcolumnName := \"\"\n\t\tif strings.HasPrefix(name, association.Relationship.Name) {\n\t\t\tif columnName = strings.TrimPrefix(name, association.Relationship.Name); columnName == \".*\" {\n\t\t\t\tcolumnName = name\n\t\t\t}\n\t\t} else if strings.HasPrefix(name, clause.Associations) {\n\t\t\tcolumnName = name\n\t\t}\n\n\t\tif columnName != \"\" {\n\t\t\tif ok {\n\t\t\t\tselectedSaveColumns = append(selectedSaveColumns, columnName)\n\t\t\t} else {\n\t\t\t\tomitColumns = append(omitColumns, columnName)\n\t\t\t}\n\t\t}\n\t}\n\n\tfor _, ref := range association.Relationship.References {\n\t\tif !ref.OwnPrimaryKey {\n\t\t\tselectedSaveColumns = append(selectedSaveColumns, ref.ForeignKey.Name)\n\t\t}\n\t}\n\n\tassociationDB := association.DB.Session(&Session{}).Model(nil)\n\tif !association.DB.FullSaveAssociations {\n\t\tassociationDB.Select(selectedSaveColumns)\n\t}\n\tif len(omitColumns) > 0 {\n\t\tassociationDB.Omit(omitColumns...)\n\t}\n\tassociationDB = associationDB.Session(&Session{})\n\n\tswitch reflectValue.Kind() {\n\tcase reflect.Slice, reflect.Array:\n\t\tif len(values) != reflectValue.Len() {\n\t\t\t// clear old data\n\t\t\tif clear && len(values) == 0 {\n\t\t\t\tfor i := 0; i < reflectValue.Len(); i++ {\n\t\t\t\t\tif err := association.Relationship.Field.Set(association.DB.Statement.Context, reflectValue.Index(i), reflect.New(association.Relationship.Field.IndirectFieldType).Interface()); err != nil {\n\t\t\t\t\t\tassociation.Error = err\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\n\t\t\t\t\tif association.Relationship.JoinTable == nil {\n\t\t\t\t\t\tfor _, ref := range association.Relationship.References {\n\t\t\t\t\t\t\tif !ref.OwnPrimaryKey && ref.PrimaryValue == \"\" {\n\t\t\t\t\t\t\t\tif err := ref.ForeignKey.Set(association.DB.Statement.Context, reflectValue.Index(i), reflect.Zero(ref.ForeignKey.FieldType).Interface()); err != nil {\n\t\t\t\t\t\t\t\t\tassociation.Error = err\n\t\t\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tassociation.Error = ErrInvalidValueOfLength\n\t\t\treturn\n\t\t}\n\n\t\tfor i := 0; i < reflectValue.Len(); i++ {\n\t\t\tappendToRelations(reflectValue.Index(i), reflect.Indirect(reflect.ValueOf(values[i])), clear)\n\t\t\tif association.Error != nil {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// TODO support save slice data, sql with case?\n\t\t\tassociation.Error = associationDB.Updates(reflectValue.Index(i).Addr().Interface()).Error\n\t\t}\n\tcase reflect.Struct:\n\t\t// clear old data\n\t\tif clear && len(values) == 0 {\n\t\t\tassociation.Error = association.Relationship.Field.Set(association.DB.Statement.Context, reflectValue, reflect.New(association.Relationship.Field.IndirectFieldType).Interface())\n\n\t\t\tif association.Relationship.JoinTable == nil && association.Error == nil {\n\t\t\t\tfor _, ref := range association.Relationship.References {\n\t\t\t\t\tif !ref.OwnPrimaryKey && ref.PrimaryValue == \"\" {\n\t\t\t\t\t\tassociation.Error = ref.ForeignKey.Set(association.DB.Statement.Context, reflectValue, reflect.Zero(ref.ForeignKey.FieldType).Interface())\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tfor idx, value := range values {\n\t\t\trv := reflect.Indirect(reflect.ValueOf(value))\n\t\t\tappendToRelations(reflectValue, rv, clear && idx == 0)\n\t\t\tif association.Error != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\tif len(values) > 0 {\n\t\t\tassociation.Error = associationDB.Updates(reflectValue.Addr().Interface()).Error\n\t\t}\n\t}\n\n\tfor _, assignBack := range assignBacks {\n\t\tfieldValue := reflect.Indirect(association.Relationship.Field.ReflectValueOf(association.DB.Statement.Context, assignBack.Source))\n\t\tif assignBack.Index > 0 {\n\t\t\treflect.Indirect(assignBack.Dest).Set(fieldValue.Index(assignBack.Index - 1))\n\t\t} else {\n\t\t\treflect.Indirect(assignBack.Dest).Set(fieldValue)\n\t\t}\n\t}\n}\n\nfunc (association *Association) buildCondition() *DB {\n\tvar (\n\t\tqueryConds = association.Relationship.ToQueryConditions(association.DB.Statement.Context, association.DB.Statement.ReflectValue)\n\t\tmodelValue = reflect.New(association.Relationship.FieldSchema.ModelType).Interface()\n\t\ttx         = association.DB.Model(modelValue)\n\t)\n\n\tif association.Relationship.JoinTable != nil {\n\t\tif !tx.Statement.Unscoped && len(association.Relationship.JoinTable.QueryClauses) > 0 {\n\t\t\tjoinStmt := Statement{DB: tx, Context: tx.Statement.Context, Schema: association.Relationship.JoinTable, Table: association.Relationship.JoinTable.Table, Clauses: map[string]clause.Clause{}}\n\t\t\tfor _, queryClause := range association.Relationship.JoinTable.QueryClauses {\n\t\t\t\tjoinStmt.AddClause(queryClause)\n\t\t\t}\n\t\t\tjoinStmt.Build(\"WHERE\")\n\t\t\tif len(joinStmt.SQL.String()) > 0 {\n\t\t\t\ttx.Clauses(clause.Expr{SQL: strings.Replace(joinStmt.SQL.String(), \"WHERE \", \"\", 1), Vars: joinStmt.Vars})\n\t\t\t}\n\t\t}\n\n\t\ttx = tx.Session(&Session{QueryFields: true}).Clauses(clause.From{Joins: []clause.Join{{\n\t\t\tTable: clause.Table{Name: association.Relationship.JoinTable.Table},\n\t\t\tON:    clause.Where{Exprs: queryConds},\n\t\t}}})\n\t} else {\n\t\ttx.Clauses(clause.Where{Exprs: queryConds})\n\t}\n\n\treturn tx\n}\n\nfunc expandValues(values ...any) (results []any) {\n\tappendToResult := func(rv reflect.Value) {\n\t\t// unwrap interface\n\t\tif rv.IsValid() && rv.Kind() == reflect.Interface {\n\t\t\trv = rv.Elem()\n\t\t}\n\t\tif rv.IsValid() && rv.Kind() == reflect.Struct {\n\t\t\tp := reflect.New(rv.Type())\n\t\t\tp.Elem().Set(rv)\n\t\t\tresults = append(results, p.Interface())\n\t\t} else if rv.IsValid() {\n\t\t\tresults = append(results, rv.Interface())\n\t\t}\n\t}\n\n\t// Process each argument; if an argument is a slice/array, expand its elements\n\tfor _, value := range values {\n\t\trv := reflect.ValueOf(value)\n\t\tif rv.Kind() == reflect.Slice || rv.Kind() == reflect.Array {\n\t\t\tfor i := 0; i < rv.Len(); i++ {\n\t\t\t\tappendToResult(rv.Index(i))\n\t\t\t}\n\t\t} else {\n\t\t\tappendToResult(rv)\n\t\t}\n\t}\n\treturn\n}\n"
  },
  {
    "path": "callbacks/associations.go",
    "content": "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.io/gorm/utils\"\n)\n\nfunc SaveBeforeAssociations(create bool) func(db *gorm.DB) {\n\treturn func(db *gorm.DB) {\n\t\tif db.Error == nil && db.Statement.Schema != nil {\n\t\t\tselectColumns, restricted := db.Statement.SelectAndOmitColumns(create, !create)\n\n\t\t\t// Save Belongs To associations\n\t\t\tfor _, rel := range db.Statement.Schema.Relationships.BelongsTo {\n\t\t\t\tif v, ok := selectColumns[rel.Name]; (ok && !v) || (!ok && restricted) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tsetupReferences := func(obj reflect.Value, elem reflect.Value) {\n\t\t\t\t\tfor _, ref := range rel.References {\n\t\t\t\t\t\tif !ref.OwnPrimaryKey {\n\t\t\t\t\t\t\tpv, _ := ref.PrimaryKey.ValueOf(db.Statement.Context, elem)\n\t\t\t\t\t\t\tdb.AddError(ref.ForeignKey.Set(db.Statement.Context, obj, pv))\n\n\t\t\t\t\t\t\tif dest, ok := db.Statement.Dest.(map[string]interface{}); ok {\n\t\t\t\t\t\t\t\tdest[ref.ForeignKey.DBName] = pv\n\t\t\t\t\t\t\t\tif _, ok := dest[rel.Name]; ok {\n\t\t\t\t\t\t\t\t\tdest[rel.Name] = elem.Interface()\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tswitch db.Statement.ReflectValue.Kind() {\n\t\t\t\tcase reflect.Slice, reflect.Array:\n\t\t\t\t\tvar (\n\t\t\t\t\t\trValLen   = db.Statement.ReflectValue.Len()\n\t\t\t\t\t\tobjs      = make([]reflect.Value, 0, rValLen)\n\t\t\t\t\t\tfieldType = rel.Field.FieldType\n\t\t\t\t\t\tisPtr     = fieldType.Kind() == reflect.Ptr\n\t\t\t\t\t)\n\n\t\t\t\t\tif !isPtr {\n\t\t\t\t\t\tfieldType = reflect.PointerTo(fieldType)\n\t\t\t\t\t}\n\n\t\t\t\t\telems := reflect.MakeSlice(reflect.SliceOf(fieldType), 0, 10)\n\t\t\t\t\tdistinctElems := reflect.MakeSlice(reflect.SliceOf(fieldType), 0, 10)\n\t\t\t\t\tidentityMap := map[string]bool{}\n\t\t\t\t\tfor i := 0; i < rValLen; i++ {\n\t\t\t\t\t\tobj := db.Statement.ReflectValue.Index(i)\n\t\t\t\t\t\tif reflect.Indirect(obj).Kind() != reflect.Struct {\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif _, zero := rel.Field.ValueOf(db.Statement.Context, obj); !zero { // check belongs to relation value\n\t\t\t\t\t\t\trv := rel.Field.ReflectValueOf(db.Statement.Context, obj) // relation reflect value\n\t\t\t\t\t\t\tif !isPtr {\n\t\t\t\t\t\t\t\trv = rv.Addr()\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tobjs = append(objs, obj)\n\t\t\t\t\t\t\telems = reflect.Append(elems, rv)\n\n\t\t\t\t\t\t\trelPrimaryValues := make([]interface{}, 0, len(rel.FieldSchema.PrimaryFields))\n\t\t\t\t\t\t\tfor _, pf := range rel.FieldSchema.PrimaryFields {\n\t\t\t\t\t\t\t\tif pfv, ok := pf.ValueOf(db.Statement.Context, rv); !ok {\n\t\t\t\t\t\t\t\t\trelPrimaryValues = append(relPrimaryValues, pfv)\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcacheKey := utils.ToStringKey(relPrimaryValues...)\n\t\t\t\t\t\t\tif len(relPrimaryValues) != len(rel.FieldSchema.PrimaryFields) || !identityMap[cacheKey] {\n\t\t\t\t\t\t\t\tif cacheKey != \"\" { // has primary fields\n\t\t\t\t\t\t\t\t\tidentityMap[cacheKey] = true\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tdistinctElems = reflect.Append(distinctElems, rv)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif elems.Len() > 0 {\n\t\t\t\t\t\tif saveAssociations(db, rel, distinctElems, selectColumns, restricted, nil) == nil {\n\t\t\t\t\t\t\tfor i := 0; i < elems.Len(); i++ {\n\t\t\t\t\t\t\t\tsetupReferences(objs[i], elems.Index(i))\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\tcase reflect.Struct:\n\t\t\t\t\tif _, zero := rel.Field.ValueOf(db.Statement.Context, db.Statement.ReflectValue); !zero {\n\t\t\t\t\t\trv := rel.Field.ReflectValueOf(db.Statement.Context, db.Statement.ReflectValue) // relation reflect value\n\t\t\t\t\t\tif rv.Kind() != reflect.Ptr {\n\t\t\t\t\t\t\trv = rv.Addr()\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif saveAssociations(db, rel, rv, selectColumns, restricted, nil) == nil {\n\t\t\t\t\t\t\tsetupReferences(db.Statement.ReflectValue, rv)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc SaveAfterAssociations(create bool) func(db *gorm.DB) {\n\treturn func(db *gorm.DB) {\n\t\tif db.Error == nil && db.Statement.Schema != nil {\n\t\t\tselectColumns, restricted := db.Statement.SelectAndOmitColumns(create, !create)\n\n\t\t\t// Save Has One associations\n\t\t\tfor _, rel := range db.Statement.Schema.Relationships.HasOne {\n\t\t\t\tif v, ok := selectColumns[rel.Name]; (ok && !v) || (!ok && restricted) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tswitch db.Statement.ReflectValue.Kind() {\n\t\t\t\tcase reflect.Slice, reflect.Array:\n\t\t\t\t\tvar (\n\t\t\t\t\t\tfieldType = rel.Field.FieldType\n\t\t\t\t\t\tisPtr     = fieldType.Kind() == reflect.Ptr\n\t\t\t\t\t)\n\n\t\t\t\t\tif !isPtr {\n\t\t\t\t\t\tfieldType = reflect.PointerTo(fieldType)\n\t\t\t\t\t}\n\n\t\t\t\t\telems := reflect.MakeSlice(reflect.SliceOf(fieldType), 0, 10)\n\n\t\t\t\t\tfor i := 0; i < db.Statement.ReflectValue.Len(); i++ {\n\t\t\t\t\t\tobj := db.Statement.ReflectValue.Index(i)\n\n\t\t\t\t\t\tif reflect.Indirect(obj).Kind() == reflect.Struct {\n\t\t\t\t\t\t\tif _, zero := rel.Field.ValueOf(db.Statement.Context, obj); !zero {\n\t\t\t\t\t\t\t\trv := rel.Field.ReflectValueOf(db.Statement.Context, obj)\n\t\t\t\t\t\t\t\tif rv.Kind() != reflect.Ptr {\n\t\t\t\t\t\t\t\t\trv = rv.Addr()\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tfor _, ref := range rel.References {\n\t\t\t\t\t\t\t\t\tif ref.OwnPrimaryKey {\n\t\t\t\t\t\t\t\t\t\tfv, _ := ref.PrimaryKey.ValueOf(db.Statement.Context, obj)\n\t\t\t\t\t\t\t\t\t\tdb.AddError(ref.ForeignKey.Set(db.Statement.Context, rv, fv))\n\t\t\t\t\t\t\t\t\t} else if ref.PrimaryValue != \"\" {\n\t\t\t\t\t\t\t\t\t\tdb.AddError(ref.ForeignKey.Set(db.Statement.Context, rv, ref.PrimaryValue))\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\telems = reflect.Append(elems, rv)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif elems.Len() > 0 {\n\t\t\t\t\t\tassignmentColumns := make([]string, 0, len(rel.References))\n\t\t\t\t\t\tfor _, ref := range rel.References {\n\t\t\t\t\t\t\tassignmentColumns = append(assignmentColumns, ref.ForeignKey.DBName)\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tsaveAssociations(db, rel, elems, selectColumns, restricted, assignmentColumns)\n\t\t\t\t\t}\n\t\t\t\tcase reflect.Struct:\n\t\t\t\t\tif _, zero := rel.Field.ValueOf(db.Statement.Context, db.Statement.ReflectValue); !zero {\n\t\t\t\t\t\tf := rel.Field.ReflectValueOf(db.Statement.Context, db.Statement.ReflectValue)\n\t\t\t\t\t\tif f.Kind() != reflect.Ptr {\n\t\t\t\t\t\t\tf = f.Addr()\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tassignmentColumns := make([]string, 0, len(rel.References))\n\t\t\t\t\t\tfor _, ref := range rel.References {\n\t\t\t\t\t\t\tif ref.OwnPrimaryKey {\n\t\t\t\t\t\t\t\tfv, _ := ref.PrimaryKey.ValueOf(db.Statement.Context, db.Statement.ReflectValue)\n\t\t\t\t\t\t\t\tdb.AddError(ref.ForeignKey.Set(db.Statement.Context, f, fv))\n\t\t\t\t\t\t\t} else if ref.PrimaryValue != \"\" {\n\t\t\t\t\t\t\t\tdb.AddError(ref.ForeignKey.Set(db.Statement.Context, f, ref.PrimaryValue))\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tassignmentColumns = append(assignmentColumns, ref.ForeignKey.DBName)\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tsaveAssociations(db, rel, f, selectColumns, restricted, assignmentColumns)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Save Has Many associations\n\t\t\tfor _, rel := range db.Statement.Schema.Relationships.HasMany {\n\t\t\t\tif v, ok := selectColumns[rel.Name]; (ok && !v) || (!ok && restricted) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tfieldType := rel.Field.IndirectFieldType.Elem()\n\t\t\t\tisPtr := fieldType.Kind() == reflect.Ptr\n\t\t\t\tif !isPtr {\n\t\t\t\t\tfieldType = reflect.PointerTo(fieldType)\n\t\t\t\t}\n\t\t\t\telems := reflect.MakeSlice(reflect.SliceOf(fieldType), 0, 10)\n\t\t\t\tidentityMap := map[string]bool{}\n\t\t\t\tappendToElems := func(v reflect.Value) {\n\t\t\t\t\tif _, zero := rel.Field.ValueOf(db.Statement.Context, v); !zero {\n\t\t\t\t\t\tf := reflect.Indirect(rel.Field.ReflectValueOf(db.Statement.Context, v))\n\n\t\t\t\t\t\tfor i := 0; i < f.Len(); i++ {\n\t\t\t\t\t\t\telem := f.Index(i)\n\t\t\t\t\t\t\tfor _, ref := range rel.References {\n\t\t\t\t\t\t\t\tif ref.OwnPrimaryKey {\n\t\t\t\t\t\t\t\t\tpv, _ := ref.PrimaryKey.ValueOf(db.Statement.Context, v)\n\t\t\t\t\t\t\t\t\tdb.AddError(ref.ForeignKey.Set(db.Statement.Context, elem, pv))\n\t\t\t\t\t\t\t\t} else if ref.PrimaryValue != \"\" {\n\t\t\t\t\t\t\t\t\tdb.AddError(ref.ForeignKey.Set(db.Statement.Context, elem, ref.PrimaryValue))\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\trelPrimaryValues := make([]interface{}, 0, len(rel.FieldSchema.PrimaryFields))\n\t\t\t\t\t\t\tfor _, pf := range rel.FieldSchema.PrimaryFields {\n\t\t\t\t\t\t\t\tif pfv, ok := pf.ValueOf(db.Statement.Context, elem); !ok {\n\t\t\t\t\t\t\t\t\trelPrimaryValues = append(relPrimaryValues, pfv)\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tcacheKey := utils.ToStringKey(relPrimaryValues...)\n\t\t\t\t\t\t\tif len(relPrimaryValues) != len(rel.FieldSchema.PrimaryFields) || !identityMap[cacheKey] {\n\t\t\t\t\t\t\t\tif cacheKey != \"\" { // has primary fields\n\t\t\t\t\t\t\t\t\tidentityMap[cacheKey] = true\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tif isPtr {\n\t\t\t\t\t\t\t\t\telems = reflect.Append(elems, elem)\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\telems = reflect.Append(elems, elem.Addr())\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tswitch db.Statement.ReflectValue.Kind() {\n\t\t\t\tcase reflect.Slice, reflect.Array:\n\t\t\t\t\tfor i := 0; i < db.Statement.ReflectValue.Len(); i++ {\n\t\t\t\t\t\tobj := db.Statement.ReflectValue.Index(i)\n\t\t\t\t\t\tif reflect.Indirect(obj).Kind() == reflect.Struct {\n\t\t\t\t\t\t\tappendToElems(obj)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\tcase reflect.Struct:\n\t\t\t\t\tappendToElems(db.Statement.ReflectValue)\n\t\t\t\t}\n\n\t\t\t\tif elems.Len() > 0 {\n\t\t\t\t\tassignmentColumns := make([]string, 0, len(rel.References))\n\t\t\t\t\tfor _, ref := range rel.References {\n\t\t\t\t\t\tassignmentColumns = append(assignmentColumns, ref.ForeignKey.DBName)\n\t\t\t\t\t}\n\n\t\t\t\t\tsaveAssociations(db, rel, elems, selectColumns, restricted, assignmentColumns)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Save Many2Many associations\n\t\t\tfor _, rel := range db.Statement.Schema.Relationships.Many2Many {\n\t\t\t\tif v, ok := selectColumns[rel.Name]; (ok && !v) || (!ok && restricted) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tfieldType := rel.Field.IndirectFieldType.Elem()\n\t\t\t\tisPtr := fieldType.Kind() == reflect.Ptr\n\t\t\t\tif !isPtr {\n\t\t\t\t\tfieldType = reflect.PointerTo(fieldType)\n\t\t\t\t}\n\t\t\t\telems := reflect.MakeSlice(reflect.SliceOf(fieldType), 0, 10)\n\t\t\t\tdistinctElems := reflect.MakeSlice(reflect.SliceOf(fieldType), 0, 10)\n\t\t\t\tjoins := reflect.MakeSlice(reflect.SliceOf(reflect.PointerTo(rel.JoinTable.ModelType)), 0, 10)\n\t\t\t\tobjs := []reflect.Value{}\n\n\t\t\t\tappendToJoins := func(obj reflect.Value, elem reflect.Value) {\n\t\t\t\t\tjoinValue := reflect.New(rel.JoinTable.ModelType)\n\t\t\t\t\tfor _, ref := range rel.References {\n\t\t\t\t\t\tif ref.OwnPrimaryKey {\n\t\t\t\t\t\t\tfv, _ := ref.PrimaryKey.ValueOf(db.Statement.Context, obj)\n\t\t\t\t\t\t\tdb.AddError(ref.ForeignKey.Set(db.Statement.Context, joinValue, fv))\n\t\t\t\t\t\t} else if ref.PrimaryValue != \"\" {\n\t\t\t\t\t\t\tdb.AddError(ref.ForeignKey.Set(db.Statement.Context, joinValue, ref.PrimaryValue))\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tfv, _ := ref.PrimaryKey.ValueOf(db.Statement.Context, elem)\n\t\t\t\t\t\t\tdb.AddError(ref.ForeignKey.Set(db.Statement.Context, joinValue, fv))\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tjoins = reflect.Append(joins, joinValue)\n\t\t\t\t}\n\n\t\t\t\tidentityMap := map[string]bool{}\n\t\t\t\tappendToElems := func(v reflect.Value) {\n\t\t\t\t\tif _, zero := rel.Field.ValueOf(db.Statement.Context, v); !zero {\n\t\t\t\t\t\tf := reflect.Indirect(rel.Field.ReflectValueOf(db.Statement.Context, v))\n\t\t\t\t\t\tfor i := 0; i < f.Len(); i++ {\n\t\t\t\t\t\t\telem := f.Index(i)\n\t\t\t\t\t\t\tif !isPtr {\n\t\t\t\t\t\t\t\telem = elem.Addr()\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tobjs = append(objs, v)\n\t\t\t\t\t\t\telems = reflect.Append(elems, elem)\n\n\t\t\t\t\t\t\trelPrimaryValues := make([]interface{}, 0, len(rel.FieldSchema.PrimaryFields))\n\t\t\t\t\t\t\tfor _, pf := range rel.FieldSchema.PrimaryFields {\n\t\t\t\t\t\t\t\tif pfv, ok := pf.ValueOf(db.Statement.Context, elem); !ok {\n\t\t\t\t\t\t\t\t\trelPrimaryValues = append(relPrimaryValues, pfv)\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tcacheKey := utils.ToStringKey(relPrimaryValues...)\n\t\t\t\t\t\t\tif len(relPrimaryValues) != len(rel.FieldSchema.PrimaryFields) || !identityMap[cacheKey] {\n\t\t\t\t\t\t\t\tif cacheKey != \"\" { // has primary fields\n\t\t\t\t\t\t\t\t\tidentityMap[cacheKey] = true\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tdistinctElems = reflect.Append(distinctElems, elem)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tswitch db.Statement.ReflectValue.Kind() {\n\t\t\t\tcase reflect.Slice, reflect.Array:\n\t\t\t\t\tfor i := 0; i < db.Statement.ReflectValue.Len(); i++ {\n\t\t\t\t\t\tobj := db.Statement.ReflectValue.Index(i)\n\t\t\t\t\t\tif reflect.Indirect(obj).Kind() == reflect.Struct {\n\t\t\t\t\t\t\tappendToElems(obj)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\tcase reflect.Struct:\n\t\t\t\t\tappendToElems(db.Statement.ReflectValue)\n\t\t\t\t}\n\n\t\t\t\t// optimize elems of reflect value length\n\t\t\t\tif elemLen := elems.Len(); elemLen > 0 {\n\t\t\t\t\tif v, ok := selectColumns[rel.Name+\".*\"]; !ok || v {\n\t\t\t\t\t\tsaveAssociations(db, rel, distinctElems, selectColumns, restricted, nil)\n\t\t\t\t\t}\n\n\t\t\t\t\tfor i := 0; i < elemLen; i++ {\n\t\t\t\t\t\tappendToJoins(objs[i], elems.Index(i))\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif joins.Len() > 0 {\n\t\t\t\t\tdb.AddError(db.Session(&gorm.Session{NewDB: true}).Clauses(clause.OnConflict{DoNothing: true}).Session(&gorm.Session{\n\t\t\t\t\t\tSkipHooks:                db.Statement.SkipHooks,\n\t\t\t\t\t\tDisableNestedTransaction: true,\n\t\t\t\t\t}).Create(joins.Interface()).Error)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc onConflictOption(stmt *gorm.Statement, s *schema.Schema, defaultUpdatingColumns []string) (onConflict clause.OnConflict) {\n\tif len(defaultUpdatingColumns) > 0 || stmt.DB.FullSaveAssociations {\n\t\tonConflict.Columns = make([]clause.Column, 0, len(s.PrimaryFieldDBNames))\n\t\tfor _, dbName := range s.PrimaryFieldDBNames {\n\t\t\tonConflict.Columns = append(onConflict.Columns, clause.Column{Name: dbName})\n\t\t}\n\n\t\tonConflict.UpdateAll = stmt.DB.FullSaveAssociations\n\t\tif !onConflict.UpdateAll {\n\t\t\tonConflict.DoUpdates = clause.AssignmentColumns(defaultUpdatingColumns)\n\t\t}\n\t} else {\n\t\tonConflict.DoNothing = true\n\t}\n\n\treturn\n}\n\nfunc saveAssociations(db *gorm.DB, rel *schema.Relationship, rValues reflect.Value, selectColumns map[string]bool, restricted bool, defaultUpdatingColumns []string) error {\n\t// stop save association loop\n\tif checkAssociationsSaved(db, rValues) {\n\t\treturn nil\n\t}\n\n\tvar (\n\t\tselects, omits []string\n\t\tonConflict     = onConflictOption(db.Statement, rel.FieldSchema, defaultUpdatingColumns)\n\t\trefName        = rel.Name + \".\"\n\t\tvalues         = rValues.Interface()\n\t)\n\n\tfor name, ok := range selectColumns {\n\t\tcolumnName := \"\"\n\t\tif strings.HasPrefix(name, refName) {\n\t\t\tcolumnName = strings.TrimPrefix(name, refName)\n\t\t}\n\n\t\tif columnName != \"\" {\n\t\t\tif ok {\n\t\t\t\tselects = append(selects, columnName)\n\t\t\t} else {\n\t\t\t\tomits = append(omits, columnName)\n\t\t\t}\n\t\t}\n\t}\n\n\ttx := db.Session(&gorm.Session{NewDB: true}).Clauses(onConflict).Session(&gorm.Session{\n\t\tFullSaveAssociations:     db.FullSaveAssociations,\n\t\tSkipHooks:                db.Statement.SkipHooks,\n\t\tDisableNestedTransaction: true,\n\t})\n\n\tdb.Statement.Settings.Range(func(k, v interface{}) bool {\n\t\ttx.Statement.Settings.Store(k, v)\n\t\treturn true\n\t})\n\n\tif tx.Statement.FullSaveAssociations {\n\t\ttx = tx.Set(\"gorm:update_track_time\", true)\n\t}\n\n\tif len(selects) > 0 {\n\t\ttx = tx.Select(selects)\n\t} else if restricted && len(omits) == 0 {\n\t\ttx = tx.Omit(clause.Associations)\n\t}\n\n\tif len(omits) > 0 {\n\t\ttx = tx.Omit(omits...)\n\t}\n\n\treturn db.AddError(tx.Create(values).Error)\n}\n\n// check association values has been saved\n// if values kind is Struct, check it has been saved\n// if values kind is Slice/Array, check all items have been saved\nvar visitMapStoreKey = \"gorm:saved_association_map\"\n\nfunc checkAssociationsSaved(db *gorm.DB, values reflect.Value) bool {\n\tif visit, ok := db.Get(visitMapStoreKey); ok {\n\t\tif v, ok := visit.(*visitMap); ok {\n\t\t\tif loadOrStoreVisitMap(v, values) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\t} else {\n\t\tvistMap := make(visitMap)\n\t\tloadOrStoreVisitMap(&vistMap, values)\n\t\tdb.Set(visitMapStoreKey, &vistMap)\n\t}\n\n\treturn false\n}\n"
  },
  {
    "path": "callbacks/callbacks.go",
    "content": "package callbacks\n\nimport (\n\t\"gorm.io/gorm\"\n)\n\nvar (\n\tcreateClauses = []string{\"INSERT\", \"VALUES\", \"ON CONFLICT\"}\n\tqueryClauses  = []string{\"SELECT\", \"FROM\", \"WHERE\", \"GROUP BY\", \"ORDER BY\", \"LIMIT\", \"FOR\"}\n\tupdateClauses = []string{\"UPDATE\", \"SET\", \"WHERE\"}\n\tdeleteClauses = []string{\"DELETE\", \"FROM\", \"WHERE\"}\n)\n\ntype Config struct {\n\tLastInsertIDReversed bool\n\tCreateClauses        []string\n\tQueryClauses         []string\n\tUpdateClauses        []string\n\tDeleteClauses        []string\n}\n\nfunc RegisterDefaultCallbacks(db *gorm.DB, config *Config) {\n\tenableTransaction := func(db *gorm.DB) bool {\n\t\treturn !db.SkipDefaultTransaction\n\t}\n\n\tif len(config.CreateClauses) == 0 {\n\t\tconfig.CreateClauses = createClauses\n\t}\n\tif len(config.QueryClauses) == 0 {\n\t\tconfig.QueryClauses = queryClauses\n\t}\n\tif len(config.DeleteClauses) == 0 {\n\t\tconfig.DeleteClauses = deleteClauses\n\t}\n\tif len(config.UpdateClauses) == 0 {\n\t\tconfig.UpdateClauses = updateClauses\n\t}\n\n\tcreateCallback := db.Callback().Create()\n\tcreateCallback.Match(enableTransaction).Register(\"gorm:begin_transaction\", BeginTransaction)\n\tcreateCallback.Register(\"gorm:before_create\", BeforeCreate)\n\tcreateCallback.Register(\"gorm:save_before_associations\", SaveBeforeAssociations(true))\n\tcreateCallback.Register(\"gorm:create\", Create(config))\n\tcreateCallback.Register(\"gorm:save_after_associations\", SaveAfterAssociations(true))\n\tcreateCallback.Register(\"gorm:after_create\", AfterCreate)\n\tcreateCallback.Match(enableTransaction).Register(\"gorm:commit_or_rollback_transaction\", CommitOrRollbackTransaction)\n\tcreateCallback.Clauses = config.CreateClauses\n\n\tqueryCallback := db.Callback().Query()\n\tqueryCallback.Register(\"gorm:query\", Query)\n\tqueryCallback.Register(\"gorm:preload\", Preload)\n\tqueryCallback.Register(\"gorm:after_query\", AfterQuery)\n\tqueryCallback.Clauses = config.QueryClauses\n\n\tdeleteCallback := db.Callback().Delete()\n\tdeleteCallback.Match(enableTransaction).Register(\"gorm:begin_transaction\", BeginTransaction)\n\tdeleteCallback.Register(\"gorm:before_delete\", BeforeDelete)\n\tdeleteCallback.Register(\"gorm:delete_before_associations\", DeleteBeforeAssociations)\n\tdeleteCallback.Register(\"gorm:delete\", Delete(config))\n\tdeleteCallback.Register(\"gorm:after_delete\", AfterDelete)\n\tdeleteCallback.Match(enableTransaction).Register(\"gorm:commit_or_rollback_transaction\", CommitOrRollbackTransaction)\n\tdeleteCallback.Clauses = config.DeleteClauses\n\n\tupdateCallback := db.Callback().Update()\n\tupdateCallback.Match(enableTransaction).Register(\"gorm:begin_transaction\", BeginTransaction)\n\tupdateCallback.Register(\"gorm:setup_reflect_value\", SetupUpdateReflectValue)\n\tupdateCallback.Register(\"gorm:before_update\", BeforeUpdate)\n\tupdateCallback.Register(\"gorm:save_before_associations\", SaveBeforeAssociations(false))\n\tupdateCallback.Register(\"gorm:update\", Update(config))\n\tupdateCallback.Register(\"gorm:save_after_associations\", SaveAfterAssociations(false))\n\tupdateCallback.Register(\"gorm:after_update\", AfterUpdate)\n\tupdateCallback.Match(enableTransaction).Register(\"gorm:commit_or_rollback_transaction\", CommitOrRollbackTransaction)\n\tupdateCallback.Clauses = config.UpdateClauses\n\n\trowCallback := db.Callback().Row()\n\trowCallback.Register(\"gorm:row\", RowQuery)\n\trowCallback.Clauses = config.QueryClauses\n\n\trawCallback := db.Callback().Raw()\n\trawCallback.Register(\"gorm:raw\", RawExec)\n\trawCallback.Clauses = config.QueryClauses\n}\n"
  },
  {
    "path": "callbacks/callmethod.go",
    "content": "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 *gorm.DB) bool) {\n\ttx := db.Session(&gorm.Session{NewDB: true})\n\tif called := fc(db.Statement.ReflectValue.Interface(), tx); !called {\n\t\tswitch db.Statement.ReflectValue.Kind() {\n\t\tcase reflect.Slice, reflect.Array:\n\t\t\tdb.Statement.CurDestIndex = 0\n\t\t\tfor i := 0; i < db.Statement.ReflectValue.Len(); i++ {\n\t\t\t\tif value := reflect.Indirect(db.Statement.ReflectValue.Index(i)); value.CanAddr() {\n\t\t\t\t\tfc(value.Addr().Interface(), tx)\n\t\t\t\t} else {\n\t\t\t\t\tdb.AddError(gorm.ErrInvalidValue)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tdb.Statement.CurDestIndex++\n\t\t\t}\n\t\tcase reflect.Struct:\n\t\t\tif db.Statement.ReflectValue.CanAddr() {\n\t\t\t\tfc(db.Statement.ReflectValue.Addr().Interface(), tx)\n\t\t\t} else {\n\t\t\t\tdb.AddError(gorm.ErrInvalidValue)\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "callbacks/create.go",
    "content": "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\t\"gorm.io/gorm/utils\"\n)\n\n// BeforeCreate before create hooks\nfunc BeforeCreate(db *gorm.DB) {\n\tif db.Error == nil && db.Statement.Schema != nil && !db.Statement.SkipHooks && (db.Statement.Schema.BeforeSave || db.Statement.Schema.BeforeCreate) {\n\t\tcallMethod(db, func(value interface{}, tx *gorm.DB) (called bool) {\n\t\t\tif db.Statement.Schema.BeforeSave {\n\t\t\t\tif i, ok := value.(BeforeSaveInterface); ok {\n\t\t\t\t\tcalled = true\n\t\t\t\t\tdb.AddError(i.BeforeSave(tx))\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif db.Statement.Schema.BeforeCreate {\n\t\t\t\tif i, ok := value.(BeforeCreateInterface); ok {\n\t\t\t\t\tcalled = true\n\t\t\t\t\tdb.AddError(i.BeforeCreate(tx))\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn called\n\t\t})\n\t}\n}\n\n// Create create hook\nfunc Create(config *Config) func(db *gorm.DB) {\n\tsupportReturning := utils.Contains(config.CreateClauses, \"RETURNING\")\n\n\treturn func(db *gorm.DB) {\n\t\tif db.Error != nil {\n\t\t\treturn\n\t\t}\n\n\t\tif db.Statement.Schema != nil {\n\t\t\tif !db.Statement.Unscoped {\n\t\t\t\tfor _, c := range db.Statement.Schema.CreateClauses {\n\t\t\t\t\tdb.Statement.AddClause(c)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif supportReturning && len(db.Statement.Schema.FieldsWithDefaultDBValue) > 0 {\n\t\t\t\tif _, ok := db.Statement.Clauses[\"RETURNING\"]; !ok {\n\t\t\t\t\tfromColumns := make([]clause.Column, 0, len(db.Statement.Schema.FieldsWithDefaultDBValue))\n\t\t\t\t\tfor _, field := range db.Statement.Schema.FieldsWithDefaultDBValue {\n\t\t\t\t\t\tif field.Readable {\n\t\t\t\t\t\t\tfromColumns = append(fromColumns, clause.Column{Name: field.DBName})\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif len(fromColumns) > 0 {\n\t\t\t\t\t\tdb.Statement.AddClause(clause.Returning{Columns: fromColumns})\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif db.Statement.SQL.Len() == 0 {\n\t\t\tdb.Statement.SQL.Grow(180)\n\t\t\tdb.Statement.AddClauseIfNotExists(clause.Insert{})\n\t\t\tdb.Statement.AddClause(ConvertToCreateValues(db.Statement))\n\n\t\t\tdb.Statement.Build(db.Statement.BuildClauses...)\n\t\t}\n\n\t\tisDryRun := !db.DryRun && db.Error == nil\n\t\tif !isDryRun {\n\t\t\treturn\n\t\t}\n\n\t\tok, mode := hasReturning(db, supportReturning)\n\t\tif ok {\n\t\t\tif c, ok := db.Statement.Clauses[\"ON CONFLICT\"]; ok {\n\t\t\t\tonConflict, _ := c.Expression.(clause.OnConflict)\n\t\t\t\tif onConflict.DoNothing {\n\t\t\t\t\tmode |= gorm.ScanOnConflictDoNothing\n\t\t\t\t} else if len(onConflict.DoUpdates) > 0 || onConflict.UpdateAll {\n\t\t\t\t\tmode |= gorm.ScanUpdate\n\t\t\t\t}\n\t\t\t}\n\n\t\t\trows, err := db.Statement.ConnPool.QueryContext(\n\t\t\t\tdb.Statement.Context, db.Statement.SQL.String(), db.Statement.Vars...,\n\t\t\t)\n\t\t\tif db.AddError(err) == nil {\n\t\t\t\tdefer func() {\n\t\t\t\t\tdb.AddError(rows.Close())\n\t\t\t\t}()\n\t\t\t\tgorm.Scan(rows, db, mode)\n\n\t\t\t\tif db.Statement.Result != nil {\n\t\t\t\t\tdb.Statement.Result.RowsAffected = db.RowsAffected\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn\n\t\t}\n\n\t\tresult, err := db.Statement.ConnPool.ExecContext(\n\t\t\tdb.Statement.Context, db.Statement.SQL.String(), db.Statement.Vars...,\n\t\t)\n\t\tif err != nil {\n\t\t\tdb.AddError(err)\n\t\t\treturn\n\t\t}\n\n\t\tdb.RowsAffected, _ = result.RowsAffected()\n\n\t\tif db.Statement.Result != nil {\n\t\t\tdb.Statement.Result.Result = result\n\t\t\tdb.Statement.Result.RowsAffected = db.RowsAffected\n\t\t}\n\n\t\tif db.RowsAffected == 0 {\n\t\t\treturn\n\t\t}\n\n\t\tvar (\n\t\t\tpkField     *schema.Field\n\t\t\tpkFieldName = \"@id\"\n\t\t)\n\n\t\tif db.Statement.Schema != nil {\n\t\t\tif db.Statement.Schema.PrioritizedPrimaryField == nil ||\n\t\t\t\t!db.Statement.Schema.PrioritizedPrimaryField.HasDefaultValue ||\n\t\t\t\t!db.Statement.Schema.PrioritizedPrimaryField.Readable {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tpkField = db.Statement.Schema.PrioritizedPrimaryField\n\t\t\tpkFieldName = db.Statement.Schema.PrioritizedPrimaryField.DBName\n\t\t}\n\n\t\tinsertID, err := result.LastInsertId()\n\t\tinsertOk := err == nil && insertID > 0\n\n\t\tif !insertOk {\n\t\t\tif !supportReturning {\n\t\t\t\tdb.AddError(err)\n\t\t\t}\n\t\t\treturn\n\t\t}\n\n\t\t// append @id column with value for auto-increment primary key\n\t\t// the @id value is correct, when: 1. without setting auto-increment primary key, 2. database AutoIncrementIncrement = 1\n\t\tswitch values := db.Statement.Dest.(type) {\n\t\tcase map[string]interface{}:\n\t\t\tvalues[pkFieldName] = insertID\n\t\tcase *map[string]interface{}:\n\t\t\t(*values)[pkFieldName] = insertID\n\t\tcase []map[string]interface{}, *[]map[string]interface{}:\n\t\t\tmapValues, ok := values.([]map[string]interface{})\n\t\t\tif !ok {\n\t\t\t\tif v, ok := values.(*[]map[string]interface{}); ok {\n\t\t\t\t\tif *v != nil {\n\t\t\t\t\t\tmapValues = *v\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif config.LastInsertIDReversed {\n\t\t\t\tinsertID -= int64(len(mapValues)-1) * schema.DefaultAutoIncrementIncrement\n\t\t\t}\n\n\t\t\tfor _, mapValue := range mapValues {\n\t\t\t\tif mapValue != nil {\n\t\t\t\t\tmapValue[pkFieldName] = insertID\n\t\t\t\t}\n\t\t\t\tinsertID += schema.DefaultAutoIncrementIncrement\n\t\t\t}\n\t\tdefault:\n\t\t\tif pkField == nil {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tswitch db.Statement.ReflectValue.Kind() {\n\t\t\tcase reflect.Slice, reflect.Array:\n\t\t\t\tif config.LastInsertIDReversed {\n\t\t\t\t\tfor i := db.Statement.ReflectValue.Len() - 1; i >= 0; i-- {\n\t\t\t\t\t\trv := db.Statement.ReflectValue.Index(i)\n\t\t\t\t\t\tif reflect.Indirect(rv).Kind() != reflect.Struct {\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t_, isZero := pkField.ValueOf(db.Statement.Context, rv)\n\t\t\t\t\t\tif isZero {\n\t\t\t\t\t\t\tdb.AddError(pkField.Set(db.Statement.Context, rv, insertID))\n\t\t\t\t\t\t\tinsertID -= pkField.AutoIncrementIncrement\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tfor i := 0; i < db.Statement.ReflectValue.Len(); i++ {\n\t\t\t\t\t\trv := db.Statement.ReflectValue.Index(i)\n\t\t\t\t\t\tif reflect.Indirect(rv).Kind() != reflect.Struct {\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif _, isZero := pkField.ValueOf(db.Statement.Context, rv); isZero {\n\t\t\t\t\t\t\tdb.AddError(pkField.Set(db.Statement.Context, rv, insertID))\n\t\t\t\t\t\t\tinsertID += pkField.AutoIncrementIncrement\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\tcase reflect.Struct:\n\t\t\t\t_, isZero := pkField.ValueOf(db.Statement.Context, db.Statement.ReflectValue)\n\t\t\t\tif isZero {\n\t\t\t\t\tdb.AddError(pkField.Set(db.Statement.Context, db.Statement.ReflectValue, insertID))\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n// AfterCreate after create hooks\nfunc AfterCreate(db *gorm.DB) {\n\tif db.Error == nil && db.Statement.Schema != nil && !db.Statement.SkipHooks && (db.Statement.Schema.AfterSave || db.Statement.Schema.AfterCreate) {\n\t\tcallMethod(db, func(value interface{}, tx *gorm.DB) (called bool) {\n\t\t\tif db.Statement.Schema.AfterCreate {\n\t\t\t\tif i, ok := value.(AfterCreateInterface); ok {\n\t\t\t\t\tcalled = true\n\t\t\t\t\tdb.AddError(i.AfterCreate(tx))\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif db.Statement.Schema.AfterSave {\n\t\t\t\tif i, ok := value.(AfterSaveInterface); ok {\n\t\t\t\t\tcalled = true\n\t\t\t\t\tdb.AddError(i.AfterSave(tx))\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn called\n\t\t})\n\t}\n}\n\n// ConvertToCreateValues convert to create values\nfunc ConvertToCreateValues(stmt *gorm.Statement) (values clause.Values) {\n\tcurTime := stmt.DB.NowFunc()\n\n\tswitch value := stmt.Dest.(type) {\n\tcase map[string]interface{}:\n\t\tvalues = ConvertMapToValuesForCreate(stmt, value)\n\tcase *map[string]interface{}:\n\t\tvalues = ConvertMapToValuesForCreate(stmt, *value)\n\tcase []map[string]interface{}:\n\t\tvalues = ConvertSliceOfMapToValuesForCreate(stmt, value)\n\tcase *[]map[string]interface{}:\n\t\tvalues = ConvertSliceOfMapToValuesForCreate(stmt, *value)\n\tdefault:\n\t\tvar (\n\t\t\tselectColumns, restricted = stmt.SelectAndOmitColumns(true, false)\n\t\t\t_, updateTrackTime        = stmt.Get(\"gorm:update_track_time\")\n\t\t\tisZero                    bool\n\t\t)\n\t\tstmt.Settings.Delete(\"gorm:update_track_time\")\n\n\t\tvalues = clause.Values{Columns: make([]clause.Column, 0, len(stmt.Schema.DBNames))}\n\n\t\tfor _, db := range stmt.Schema.DBNames {\n\t\t\tif field := stmt.Schema.FieldsByDBName[db]; !field.HasDefaultValue || field.DefaultValueInterface != nil {\n\t\t\t\tif v, ok := selectColumns[db]; (ok && v) || (!ok && (!restricted || field.AutoCreateTime > 0 || field.AutoUpdateTime > 0)) {\n\t\t\t\t\tvalues.Columns = append(values.Columns, clause.Column{Name: db})\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tswitch stmt.ReflectValue.Kind() {\n\t\tcase reflect.Slice, reflect.Array:\n\t\t\trValLen := stmt.ReflectValue.Len()\n\t\t\tif rValLen == 0 {\n\t\t\t\tstmt.AddError(gorm.ErrEmptySlice)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tstmt.SQL.Grow(rValLen * 18)\n\t\t\tstmt.Vars = make([]interface{}, 0, rValLen*len(values.Columns))\n\t\t\tvalues.Values = make([][]interface{}, rValLen)\n\n\t\t\tdefaultValueFieldsHavingValue := map[*schema.Field][]interface{}{}\n\t\t\tfor i := 0; i < rValLen; i++ {\n\t\t\t\trv := reflect.Indirect(stmt.ReflectValue.Index(i))\n\t\t\t\tif !rv.IsValid() {\n\t\t\t\t\tstmt.AddError(fmt.Errorf(\"slice data #%v is invalid: %w\", i, gorm.ErrInvalidData))\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tvalues.Values[i] = make([]interface{}, len(values.Columns))\n\t\t\t\tfor idx, column := range values.Columns {\n\t\t\t\t\tfield := stmt.Schema.FieldsByDBName[column.Name]\n\t\t\t\t\tif values.Values[i][idx], isZero = field.ValueOf(stmt.Context, rv); isZero {\n\t\t\t\t\t\tif field.DefaultValueInterface != nil {\n\t\t\t\t\t\t\tvalues.Values[i][idx] = field.DefaultValueInterface\n\t\t\t\t\t\t\tstmt.AddError(field.Set(stmt.Context, rv, field.DefaultValueInterface))\n\t\t\t\t\t\t} else if field.AutoCreateTime > 0 || field.AutoUpdateTime > 0 {\n\t\t\t\t\t\t\tstmt.AddError(field.Set(stmt.Context, rv, curTime))\n\t\t\t\t\t\t\tvalues.Values[i][idx], _ = field.ValueOf(stmt.Context, rv)\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if field.AutoUpdateTime > 0 && updateTrackTime {\n\t\t\t\t\t\tstmt.AddError(field.Set(stmt.Context, rv, curTime))\n\t\t\t\t\t\tvalues.Values[i][idx], _ = field.ValueOf(stmt.Context, rv)\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tfor _, field := range stmt.Schema.FieldsWithDefaultDBValue {\n\t\t\t\t\tif v, ok := selectColumns[field.DBName]; (ok && v) || (!ok && !restricted) {\n\t\t\t\t\t\tif rvOfvalue, isZero := field.ValueOf(stmt.Context, rv); !isZero {\n\t\t\t\t\t\t\tif len(defaultValueFieldsHavingValue[field]) == 0 {\n\t\t\t\t\t\t\t\tdefaultValueFieldsHavingValue[field] = make([]interface{}, rValLen)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tdefaultValueFieldsHavingValue[field][i] = rvOfvalue\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfor _, field := range stmt.Schema.FieldsWithDefaultDBValue {\n\t\t\t\tif vs, ok := defaultValueFieldsHavingValue[field]; ok {\n\t\t\t\t\tvalues.Columns = append(values.Columns, clause.Column{Name: field.DBName})\n\t\t\t\t\tfor idx := range values.Values {\n\t\t\t\t\t\tif vs[idx] == nil {\n\t\t\t\t\t\t\tvalues.Values[idx] = append(values.Values[idx], stmt.DefaultValueOf(field))\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tvalues.Values[idx] = append(values.Values[idx], vs[idx])\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\tcase reflect.Struct:\n\t\t\tvalues.Values = [][]interface{}{make([]interface{}, len(values.Columns))}\n\t\t\tfor idx, column := range values.Columns {\n\t\t\t\tfield := stmt.Schema.FieldsByDBName[column.Name]\n\t\t\t\tif values.Values[0][idx], isZero = field.ValueOf(stmt.Context, stmt.ReflectValue); isZero {\n\t\t\t\t\tif field.DefaultValueInterface != nil {\n\t\t\t\t\t\tvalues.Values[0][idx] = field.DefaultValueInterface\n\t\t\t\t\t\tstmt.AddError(field.Set(stmt.Context, stmt.ReflectValue, field.DefaultValueInterface))\n\t\t\t\t\t} else if field.AutoCreateTime > 0 || field.AutoUpdateTime > 0 {\n\t\t\t\t\t\tstmt.AddError(field.Set(stmt.Context, stmt.ReflectValue, curTime))\n\t\t\t\t\t\tvalues.Values[0][idx], _ = field.ValueOf(stmt.Context, stmt.ReflectValue)\n\t\t\t\t\t}\n\t\t\t\t} else if field.AutoUpdateTime > 0 && updateTrackTime {\n\t\t\t\t\tstmt.AddError(field.Set(stmt.Context, stmt.ReflectValue, curTime))\n\t\t\t\t\tvalues.Values[0][idx], _ = field.ValueOf(stmt.Context, stmt.ReflectValue)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfor _, field := range stmt.Schema.FieldsWithDefaultDBValue {\n\t\t\t\tif v, ok := selectColumns[field.DBName]; (ok && v) || (!ok && !restricted) && field.DefaultValueInterface == nil {\n\t\t\t\t\tif rvOfvalue, isZero := field.ValueOf(stmt.Context, stmt.ReflectValue); !isZero {\n\t\t\t\t\t\tvalues.Columns = append(values.Columns, clause.Column{Name: field.DBName})\n\t\t\t\t\t\tvalues.Values[0] = append(values.Values[0], rvOfvalue)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\tdefault:\n\t\t\tstmt.AddError(gorm.ErrInvalidData)\n\t\t}\n\t}\n\n\tif c, ok := stmt.Clauses[\"ON CONFLICT\"]; ok {\n\t\tif onConflict, _ := c.Expression.(clause.OnConflict); onConflict.UpdateAll {\n\t\t\tif stmt.Schema != nil && len(values.Columns) >= 1 {\n\t\t\t\tselectColumns, restricted := stmt.SelectAndOmitColumns(true, true)\n\n\t\t\t\tcolumns := make([]string, 0, len(values.Columns)-1)\n\t\t\t\tfor _, column := range values.Columns {\n\t\t\t\t\tif field := stmt.Schema.LookUpField(column.Name); field != nil {\n\t\t\t\t\t\tif v, ok := selectColumns[field.DBName]; (ok && v) || (!ok && !restricted) {\n\t\t\t\t\t\t\tif !field.PrimaryKey && (!field.HasDefaultValue || field.DefaultValueInterface != nil ||\n\t\t\t\t\t\t\t\tstrings.EqualFold(field.DefaultValue, \"NULL\")) && field.AutoCreateTime == 0 {\n\t\t\t\t\t\t\t\tif field.AutoUpdateTime > 0 {\n\t\t\t\t\t\t\t\t\tassignment := clause.Assignment{Column: clause.Column{Name: field.DBName}, Value: curTime}\n\t\t\t\t\t\t\t\t\tswitch field.AutoUpdateTime {\n\t\t\t\t\t\t\t\t\tcase schema.UnixNanosecond:\n\t\t\t\t\t\t\t\t\t\tassignment.Value = curTime.UnixNano()\n\t\t\t\t\t\t\t\t\tcase schema.UnixMillisecond:\n\t\t\t\t\t\t\t\t\t\tassignment.Value = curTime.UnixMilli()\n\t\t\t\t\t\t\t\t\tcase schema.UnixSecond:\n\t\t\t\t\t\t\t\t\t\tassignment.Value = curTime.Unix()\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\tonConflict.DoUpdates = append(onConflict.DoUpdates, assignment)\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tcolumns = append(columns, column.Name)\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tonConflict.DoUpdates = append(onConflict.DoUpdates, clause.AssignmentColumns(columns)...)\n\t\t\t\tif len(onConflict.DoUpdates) == 0 {\n\t\t\t\t\tonConflict.DoNothing = true\n\t\t\t\t}\n\n\t\t\t\t// use primary fields as default OnConflict columns\n\t\t\t\tif len(onConflict.Columns) == 0 {\n\t\t\t\t\tfor _, field := range stmt.Schema.PrimaryFields {\n\t\t\t\t\t\tonConflict.Columns = append(onConflict.Columns, clause.Column{Name: field.DBName})\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tstmt.AddClause(onConflict)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn values\n}\n"
  },
  {
    "path": "callbacks/create_test.go",
    "content": "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/schema\"\n)\n\nvar schemaCache = &sync.Map{}\n\nfunc TestConvertToCreateValues_DestType_Slice(t *testing.T) {\n\ttype user struct {\n\t\tID    int `gorm:\"primaryKey\"`\n\t\tName  string\n\t\tEmail string `gorm:\"default:(-)\"`\n\t\tAge   int    `gorm:\"default:(-)\"`\n\t}\n\n\ts, err := schema.Parse(&user{}, schemaCache, schema.NamingStrategy{})\n\tif err != nil {\n\t\tt.Errorf(\"parse schema error: %v, is not expected\", err)\n\t\treturn\n\t}\n\tdest := []*user{\n\t\t{\n\t\t\tID:    1,\n\t\t\tName:  \"alice\",\n\t\t\tEmail: \"email\",\n\t\t\tAge:   18,\n\t\t},\n\t\t{\n\t\t\tID:    2,\n\t\t\tName:  \"bob\",\n\t\t\tEmail: \"email\",\n\t\t\tAge:   19,\n\t\t},\n\t}\n\tstmt := &gorm.Statement{\n\t\tDB: &gorm.DB{\n\t\t\tConfig: &gorm.Config{\n\t\t\t\tNowFunc: func() time.Time { return time.Time{} },\n\t\t\t},\n\t\t\tStatement: &gorm.Statement{\n\t\t\t\tSettings: sync.Map{},\n\t\t\t\tSchema:   s,\n\t\t\t},\n\t\t},\n\t\tReflectValue: reflect.ValueOf(dest),\n\t\tDest:         dest,\n\t}\n\n\tstmt.Schema = s\n\n\tvalues := ConvertToCreateValues(stmt)\n\texpected := clause.Values{\n\t\t// column has value + defaultValue column has value (which should have a stable order)\n\t\tColumns: []clause.Column{{Name: \"name\"}, {Name: \"email\"}, {Name: \"age\"}, {Name: \"id\"}},\n\t\tValues: [][]interface{}{\n\t\t\t{\"alice\", \"email\", 18, 1},\n\t\t\t{\"bob\", \"email\", 19, 2},\n\t\t},\n\t}\n\tif !reflect.DeepEqual(expected, values) {\n\t\tt.Errorf(\"expected: %v got %v\", expected, values)\n\t}\n}\n"
  },
  {
    "path": "callbacks/delete.go",
    "content": "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.io/gorm/utils\"\n)\n\nfunc BeforeDelete(db *gorm.DB) {\n\tif db.Error == nil && db.Statement.Schema != nil && !db.Statement.SkipHooks && db.Statement.Schema.BeforeDelete {\n\t\tcallMethod(db, func(value interface{}, tx *gorm.DB) bool {\n\t\t\tif i, ok := value.(BeforeDeleteInterface); ok {\n\t\t\t\tdb.AddError(i.BeforeDelete(tx))\n\t\t\t\treturn true\n\t\t\t}\n\n\t\t\treturn false\n\t\t})\n\t}\n}\n\nfunc DeleteBeforeAssociations(db *gorm.DB) {\n\tif db.Error == nil && db.Statement.Schema != nil {\n\t\tselectColumns, restricted := db.Statement.SelectAndOmitColumns(true, false)\n\t\tif !restricted {\n\t\t\treturn\n\t\t}\n\n\t\tfor column, v := range selectColumns {\n\t\t\tif !v {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\trel, ok := db.Statement.Schema.Relationships.Relations[column]\n\t\t\tif !ok {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tswitch rel.Type {\n\t\t\tcase schema.HasOne, schema.HasMany:\n\t\t\t\tqueryConds := rel.ToQueryConditions(db.Statement.Context, db.Statement.ReflectValue)\n\t\t\t\tmodelValue := reflect.New(rel.FieldSchema.ModelType).Interface()\n\t\t\t\ttx := db.Session(&gorm.Session{NewDB: true}).Model(modelValue)\n\t\t\t\twithoutConditions := false\n\t\t\t\tif db.Statement.Unscoped {\n\t\t\t\t\ttx = tx.Unscoped()\n\t\t\t\t}\n\n\t\t\t\tif len(db.Statement.Selects) > 0 {\n\t\t\t\t\tselects := make([]string, 0, len(db.Statement.Selects))\n\t\t\t\t\tfor _, s := range db.Statement.Selects {\n\t\t\t\t\t\tif s == clause.Associations {\n\t\t\t\t\t\t\tselects = append(selects, s)\n\t\t\t\t\t\t} else if columnPrefix := column + \".\"; strings.HasPrefix(s, columnPrefix) {\n\t\t\t\t\t\t\tselects = append(selects, strings.TrimPrefix(s, columnPrefix))\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif len(selects) > 0 {\n\t\t\t\t\t\ttx = tx.Select(selects)\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tfor _, cond := range queryConds {\n\t\t\t\t\tif c, ok := cond.(clause.IN); ok && len(c.Values) == 0 {\n\t\t\t\t\t\twithoutConditions = true\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif !withoutConditions && db.AddError(tx.Clauses(clause.Where{Exprs: queryConds}).Delete(modelValue).Error) != nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\tcase schema.Many2Many:\n\t\t\t\tvar (\n\t\t\t\t\tqueryConds     = make([]clause.Expression, 0, len(rel.References))\n\t\t\t\t\tforeignFields  = make([]*schema.Field, 0, len(rel.References))\n\t\t\t\t\trelForeignKeys = make([]string, 0, len(rel.References))\n\t\t\t\t\tmodelValue     = reflect.New(rel.JoinTable.ModelType).Interface()\n\t\t\t\t\ttable          = rel.JoinTable.Table\n\t\t\t\t\ttx             = db.Session(&gorm.Session{NewDB: true}).Model(modelValue).Table(table)\n\t\t\t\t)\n\n\t\t\t\tfor _, ref := range rel.References {\n\t\t\t\t\tif ref.OwnPrimaryKey {\n\t\t\t\t\t\tforeignFields = append(foreignFields, ref.PrimaryKey)\n\t\t\t\t\t\trelForeignKeys = append(relForeignKeys, ref.ForeignKey.DBName)\n\t\t\t\t\t} else if ref.PrimaryValue != \"\" {\n\t\t\t\t\t\tqueryConds = append(queryConds, clause.Eq{\n\t\t\t\t\t\t\tColumn: clause.Column{Table: rel.JoinTable.Table, Name: ref.ForeignKey.DBName},\n\t\t\t\t\t\t\tValue:  ref.PrimaryValue,\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t_, foreignValues := schema.GetIdentityFieldValuesMap(db.Statement.Context, db.Statement.ReflectValue, foreignFields)\n\t\t\t\tcolumn, values := schema.ToQueryValues(table, relForeignKeys, foreignValues)\n\t\t\t\tqueryConds = append(queryConds, clause.IN{Column: column, Values: values})\n\n\t\t\t\tif db.AddError(tx.Clauses(clause.Where{Exprs: queryConds}).Delete(modelValue).Error) != nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t}\n}\n\nfunc Delete(config *Config) func(db *gorm.DB) {\n\tsupportReturning := utils.Contains(config.DeleteClauses, \"RETURNING\")\n\n\treturn func(db *gorm.DB) {\n\t\tif db.Error != nil {\n\t\t\treturn\n\t\t}\n\n\t\tif db.Statement.Schema != nil {\n\t\t\tfor _, c := range db.Statement.Schema.DeleteClauses {\n\t\t\t\tdb.Statement.AddClause(c)\n\t\t\t}\n\t\t}\n\n\t\tif db.Statement.SQL.Len() == 0 {\n\t\t\tdb.Statement.SQL.Grow(100)\n\t\t\tdb.Statement.AddClauseIfNotExists(clause.Delete{})\n\n\t\t\tif db.Statement.Schema != nil {\n\t\t\t\t_, queryValues := schema.GetIdentityFieldValuesMap(db.Statement.Context, db.Statement.ReflectValue, db.Statement.Schema.PrimaryFields)\n\t\t\t\tcolumn, values := schema.ToQueryValues(db.Statement.Table, db.Statement.Schema.PrimaryFieldDBNames, queryValues)\n\n\t\t\t\tif len(values) > 0 {\n\t\t\t\t\tdb.Statement.AddClause(clause.Where{Exprs: []clause.Expression{clause.IN{Column: column, Values: values}}})\n\t\t\t\t}\n\n\t\t\t\tif db.Statement.ReflectValue.CanAddr() && db.Statement.Dest != db.Statement.Model && db.Statement.Model != nil {\n\t\t\t\t\t_, queryValues = schema.GetIdentityFieldValuesMap(db.Statement.Context, reflect.ValueOf(db.Statement.Model), db.Statement.Schema.PrimaryFields)\n\t\t\t\t\tcolumn, values = schema.ToQueryValues(db.Statement.Table, db.Statement.Schema.PrimaryFieldDBNames, queryValues)\n\n\t\t\t\t\tif len(values) > 0 {\n\t\t\t\t\t\tdb.Statement.AddClause(clause.Where{Exprs: []clause.Expression{clause.IN{Column: column, Values: values}}})\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tdb.Statement.AddClauseIfNotExists(clause.From{})\n\n\t\t\tdb.Statement.Build(db.Statement.BuildClauses...)\n\t\t}\n\n\t\tcheckMissingWhereConditions(db)\n\n\t\tif !db.DryRun && db.Error == nil {\n\t\t\tok, mode := hasReturning(db, supportReturning)\n\t\t\tif !ok {\n\t\t\t\tresult, err := db.Statement.ConnPool.ExecContext(db.Statement.Context, db.Statement.SQL.String(), db.Statement.Vars...)\n\n\t\t\t\tif db.AddError(err) == nil {\n\t\t\t\t\tdb.RowsAffected, _ = result.RowsAffected()\n\n\t\t\t\t\tif db.Statement.Result != nil {\n\t\t\t\t\t\tdb.Statement.Result.Result = result\n\t\t\t\t\t\tdb.Statement.Result.RowsAffected = db.RowsAffected\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif rows, err := db.Statement.ConnPool.QueryContext(db.Statement.Context, db.Statement.SQL.String(), db.Statement.Vars...); db.AddError(err) == nil {\n\t\t\t\tgorm.Scan(rows, db, mode)\n\n\t\t\t\tif db.Statement.Result != nil {\n\t\t\t\t\tdb.Statement.Result.RowsAffected = db.RowsAffected\n\t\t\t\t}\n\t\t\t\tdb.AddError(rows.Close())\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc AfterDelete(db *gorm.DB) {\n\tif db.Error == nil && db.Statement.Schema != nil && !db.Statement.SkipHooks && db.Statement.Schema.AfterDelete {\n\t\tcallMethod(db, func(value interface{}, tx *gorm.DB) bool {\n\t\t\tif i, ok := value.(AfterDeleteInterface); ok {\n\t\t\t\tdb.AddError(i.AfterDelete(tx))\n\t\t\t\treturn true\n\t\t\t}\n\t\t\treturn false\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "callbacks/helper.go",
    "content": "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 convert map to values\nfunc ConvertMapToValuesForCreate(stmt *gorm.Statement, mapValue map[string]interface{}) (values clause.Values) {\n\tvalues.Columns = make([]clause.Column, 0, len(mapValue))\n\tselectColumns, restricted := stmt.SelectAndOmitColumns(true, false)\n\n\tkeys := make([]string, 0, len(mapValue))\n\tfor k := range mapValue {\n\t\tkeys = append(keys, k)\n\t}\n\tsort.Strings(keys)\n\n\tfor _, k := range keys {\n\t\tvalue := mapValue[k]\n\t\tif stmt.Schema != nil {\n\t\t\tif field := stmt.Schema.LookUpField(k); field != nil {\n\t\t\t\tk = field.DBName\n\t\t\t}\n\t\t}\n\n\t\tif v, ok := selectColumns[k]; (ok && v) || (!ok && !restricted) {\n\t\t\tvalues.Columns = append(values.Columns, clause.Column{Name: k})\n\t\t\tif len(values.Values) == 0 {\n\t\t\t\tvalues.Values = [][]interface{}{{}}\n\t\t\t}\n\n\t\t\tvalues.Values[0] = append(values.Values[0], value)\n\t\t}\n\t}\n\treturn\n}\n\n// ConvertSliceOfMapToValuesForCreate convert slice of map to values\nfunc ConvertSliceOfMapToValuesForCreate(stmt *gorm.Statement, mapValues []map[string]interface{}) (values clause.Values) {\n\tcolumns := make([]string, 0, len(mapValues))\n\n\t// when the length of mapValues is zero,return directly here\n\t// no need to call stmt.SelectAndOmitColumns method\n\tif len(mapValues) == 0 {\n\t\tstmt.AddError(gorm.ErrEmptySlice)\n\t\treturn\n\t}\n\n\tvar (\n\t\tresult                    = make(map[string][]interface{}, len(mapValues))\n\t\tselectColumns, restricted = stmt.SelectAndOmitColumns(true, false)\n\t)\n\n\tfor idx, mapValue := range mapValues {\n\t\tfor k, v := range mapValue {\n\t\t\tif stmt.Schema != nil {\n\t\t\t\tif field := stmt.Schema.LookUpField(k); field != nil {\n\t\t\t\t\tk = field.DBName\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif _, ok := result[k]; !ok {\n\t\t\t\tif v, ok := selectColumns[k]; (ok && v) || (!ok && !restricted) {\n\t\t\t\t\tresult[k] = make([]interface{}, len(mapValues))\n\t\t\t\t\tcolumns = append(columns, k)\n\t\t\t\t} else {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tresult[k][idx] = v\n\t\t}\n\t}\n\n\tsort.Strings(columns)\n\tvalues.Values = make([][]interface{}, len(mapValues))\n\tvalues.Columns = make([]clause.Column, len(columns))\n\tfor idx, column := range columns {\n\t\tvalues.Columns[idx] = clause.Column{Name: column}\n\n\t\tfor i, v := range result[column] {\n\t\t\tif len(values.Values[i]) == 0 {\n\t\t\t\tvalues.Values[i] = make([]interface{}, len(columns))\n\t\t\t}\n\n\t\t\tvalues.Values[i][idx] = v\n\t\t}\n\t}\n\treturn\n}\n\nfunc hasReturning(tx *gorm.DB, supportReturning bool) (bool, gorm.ScanMode) {\n\tif supportReturning {\n\t\tif c, ok := tx.Statement.Clauses[\"RETURNING\"]; ok {\n\t\t\treturning, _ := c.Expression.(clause.Returning)\n\t\t\tif len(returning.Columns) == 0 || (len(returning.Columns) == 1 && returning.Columns[0].Name == \"*\") {\n\t\t\t\treturn true, 0\n\t\t\t}\n\t\t\treturn true, gorm.ScanUpdate\n\t\t}\n\t}\n\treturn false, 0\n}\n\nfunc checkMissingWhereConditions(db *gorm.DB) {\n\tif !db.AllowGlobalUpdate && db.Error == nil {\n\t\twhere, withCondition := db.Statement.Clauses[\"WHERE\"]\n\t\tif withCondition {\n\t\t\tif _, withSoftDelete := db.Statement.Clauses[\"soft_delete_enabled\"]; withSoftDelete {\n\t\t\t\twhereClause, _ := where.Expression.(clause.Where)\n\t\t\t\twithCondition = len(whereClause.Exprs) > 1\n\t\t\t}\n\t\t}\n\t\tif !withCondition {\n\t\t\tdb.AddError(gorm.ErrMissingWhereClause)\n\t\t}\n\t\treturn\n\t}\n}\n\ntype visitMap = map[reflect.Value]bool\n\n// Check if circular values, return true if loaded\nfunc loadOrStoreVisitMap(visitMap *visitMap, v reflect.Value) (loaded bool) {\n\tif v.Kind() == reflect.Ptr {\n\t\tv = v.Elem()\n\t}\n\n\tswitch v.Kind() {\n\tcase reflect.Slice, reflect.Array:\n\t\tloaded = true\n\t\tfor i := 0; i < v.Len(); i++ {\n\t\t\tif !loadOrStoreVisitMap(visitMap, v.Index(i)) {\n\t\t\t\tloaded = false\n\t\t\t}\n\t\t}\n\tcase reflect.Struct, reflect.Interface:\n\t\tif v.CanAddr() {\n\t\t\tp := v.Addr()\n\t\t\tif _, ok := (*visitMap)[p]; ok {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\t(*visitMap)[p] = true\n\t\t}\n\t}\n\n\treturn\n}\n"
  },
  {
    "path": "callbacks/helper_test.go",
    "content": "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 TestLoadOrStoreVisitMap(t *testing.T) {\n\tvar vm visitMap\n\tvar loaded bool\n\ttype testM struct {\n\t\tName string\n\t}\n\n\tt1 := testM{Name: \"t1\"}\n\tt2 := testM{Name: \"t2\"}\n\tt3 := testM{Name: \"t3\"}\n\n\tvm = make(visitMap)\n\tif loaded = loadOrStoreVisitMap(&vm, reflect.ValueOf(&t1)); loaded {\n\t\tt.Fatalf(\"loaded should be false\")\n\t}\n\n\tif loaded = loadOrStoreVisitMap(&vm, reflect.ValueOf(&t1)); !loaded {\n\t\tt.Fatalf(\"loaded should be true\")\n\t}\n\n\t// t1 already exist but t2 not\n\tif loaded = loadOrStoreVisitMap(&vm, reflect.ValueOf([]*testM{&t1, &t2, &t3})); loaded {\n\t\tt.Fatalf(\"loaded should be false\")\n\t}\n\n\tif loaded = loadOrStoreVisitMap(&vm, reflect.ValueOf([]*testM{&t2, &t3})); !loaded {\n\t\tt.Fatalf(\"loaded should be true\")\n\t}\n}\n\nfunc TestConvertMapToValuesForCreate(t *testing.T) {\n\ttestCase := []struct {\n\t\tname   string\n\t\tinput  map[string]interface{}\n\t\texpect clause.Values\n\t}{\n\t\t{\n\t\t\tname: \"Test convert string value\",\n\t\t\tinput: map[string]interface{}{\n\t\t\t\t\"name\": \"my name\",\n\t\t\t},\n\t\t\texpect: clause.Values{\n\t\t\t\tColumns: []clause.Column{{Name: \"name\"}},\n\t\t\t\tValues:  [][]interface{}{{\"my name\"}},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"Test convert int value\",\n\t\t\tinput: map[string]interface{}{\n\t\t\t\t\"age\": 18,\n\t\t\t},\n\t\t\texpect: clause.Values{\n\t\t\t\tColumns: []clause.Column{{Name: \"age\"}},\n\t\t\t\tValues:  [][]interface{}{{18}},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"Test convert float value\",\n\t\t\tinput: map[string]interface{}{\n\t\t\t\t\"score\": 99.5,\n\t\t\t},\n\t\t\texpect: clause.Values{\n\t\t\t\tColumns: []clause.Column{{Name: \"score\"}},\n\t\t\t\tValues:  [][]interface{}{{99.5}},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"Test convert bool value\",\n\t\t\tinput: map[string]interface{}{\n\t\t\t\t\"active\": true,\n\t\t\t},\n\t\t\texpect: clause.Values{\n\t\t\t\tColumns: []clause.Column{{Name: \"active\"}},\n\t\t\t\tValues:  [][]interface{}{{true}},\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, tc := range testCase {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tactual := ConvertMapToValuesForCreate(&gorm.Statement{}, tc.input)\n\t\t\tif !reflect.DeepEqual(actual, tc.expect) {\n\t\t\t\tt.Errorf(\"expect %v got %v\", tc.expect, actual)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestConvertSliceOfMapToValuesForCreate(t *testing.T) {\n\ttestCase := []struct {\n\t\tname   string\n\t\tinput  []map[string]interface{}\n\t\texpect clause.Values\n\t}{\n\t\t{\n\t\t\tname: \"Test convert slice of string value\",\n\t\t\tinput: []map[string]interface{}{\n\t\t\t\t{\"name\": \"my name\"},\n\t\t\t},\n\t\t\texpect: clause.Values{\n\t\t\t\tColumns: []clause.Column{{Name: \"name\"}},\n\t\t\t\tValues:  [][]interface{}{{\"my name\"}},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"Test convert slice of int value\",\n\t\t\tinput: []map[string]interface{}{\n\t\t\t\t{\"age\": 18},\n\t\t\t},\n\t\t\texpect: clause.Values{\n\t\t\t\tColumns: []clause.Column{{Name: \"age\"}},\n\t\t\t\tValues:  [][]interface{}{{18}},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"Test convert slice of float value\",\n\t\t\tinput: []map[string]interface{}{\n\t\t\t\t{\"score\": 99.5},\n\t\t\t},\n\t\t\texpect: clause.Values{\n\t\t\t\tColumns: []clause.Column{{Name: \"score\"}},\n\t\t\t\tValues:  [][]interface{}{{99.5}},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"Test convert slice of bool value\",\n\t\t\tinput: []map[string]interface{}{\n\t\t\t\t{\"active\": true},\n\t\t\t},\n\t\t\texpect: clause.Values{\n\t\t\t\tColumns: []clause.Column{{Name: \"active\"}},\n\t\t\t\tValues:  [][]interface{}{{true}},\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, tc := range testCase {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tactual := ConvertSliceOfMapToValuesForCreate(&gorm.Statement{}, tc.input)\n\n\t\t\tif !reflect.DeepEqual(actual, tc.expect) {\n\t\t\t\tt.Errorf(\"expected %v but got %v\", tc.expect, actual)\n\t\t\t}\n\t\t})\n\t}\n\n}\n"
  },
  {
    "path": "callbacks/interfaces.go",
    "content": "package callbacks\n\nimport \"gorm.io/gorm\"\n\ntype BeforeCreateInterface interface {\n\tBeforeCreate(*gorm.DB) error\n}\n\ntype AfterCreateInterface interface {\n\tAfterCreate(*gorm.DB) error\n}\n\ntype BeforeUpdateInterface interface {\n\tBeforeUpdate(*gorm.DB) error\n}\n\ntype AfterUpdateInterface interface {\n\tAfterUpdate(*gorm.DB) error\n}\n\ntype BeforeSaveInterface interface {\n\tBeforeSave(*gorm.DB) error\n}\n\ntype AfterSaveInterface interface {\n\tAfterSave(*gorm.DB) error\n}\n\ntype BeforeDeleteInterface interface {\n\tBeforeDelete(*gorm.DB) error\n}\n\ntype AfterDeleteInterface interface {\n\tAfterDelete(*gorm.DB) error\n}\n\ntype AfterFindInterface interface {\n\tAfterFind(*gorm.DB) error\n}\n"
  },
  {
    "path": "callbacks/preload.go",
    "content": "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/schema\"\n\t\"gorm.io/gorm/utils\"\n)\n\n// parsePreloadMap extracts nested preloads. e.g.\n//\n//\t// schema has a \"k0\" relation and a \"k7.k8\" embedded relation\n//\tparsePreloadMap(schema, map[string][]interface{}{\n//\t\tclause.Associations: {\"arg1\"},\n//\t\t\"k1\":                {\"arg2\"},\n//\t\t\"k2.k3\":             {\"arg3\"},\n//\t\t\"k4.k5.k6\":          {\"arg4\"},\n//\t})\n//\t// preloadMap is\n//\tmap[string]map[string][]interface{}{\n//\t\t\"k0\": {},\n//\t\t\"k7\": {\n//\t\t\t\"k8\": {},\n//\t\t},\n//\t\t\"k1\": {},\n//\t\t\"k2\": {\n//\t\t\t\"k3\": {\"arg3\"},\n//\t\t},\n//\t\t\"k4\": {\n//\t\t\t\"k5.k6\": {\"arg4\"},\n//\t\t},\n//\t}\nfunc parsePreloadMap(s *schema.Schema, preloads map[string][]interface{}) map[string]map[string][]interface{} {\n\tpreloadMap := map[string]map[string][]interface{}{}\n\tsetPreloadMap := func(name, value string, args []interface{}) {\n\t\tif _, ok := preloadMap[name]; !ok {\n\t\t\tpreloadMap[name] = map[string][]interface{}{}\n\t\t}\n\t\tif value != \"\" {\n\t\t\tpreloadMap[name][value] = args\n\t\t}\n\t}\n\n\tfor name, args := range preloads {\n\t\tpreloadFields := strings.Split(name, \".\")\n\t\tvalue := strings.TrimPrefix(strings.TrimPrefix(name, preloadFields[0]), \".\")\n\t\tif preloadFields[0] == clause.Associations {\n\t\t\tfor _, relation := range s.Relationships.Relations {\n\t\t\t\tif relation.Schema == s {\n\t\t\t\t\tsetPreloadMap(relation.Name, value, args)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfor embedded, embeddedRelations := range s.Relationships.EmbeddedRelations {\n\t\t\t\tfor _, value := range embeddedValues(embeddedRelations) {\n\t\t\t\t\tsetPreloadMap(embedded, value, args)\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tsetPreloadMap(preloadFields[0], value, args)\n\t\t}\n\t}\n\treturn preloadMap\n}\n\nfunc embeddedValues(embeddedRelations *schema.Relationships) []string {\n\tif embeddedRelations == nil {\n\t\treturn nil\n\t}\n\tnames := make([]string, 0, len(embeddedRelations.Relations)+len(embeddedRelations.EmbeddedRelations))\n\tfor _, relation := range embeddedRelations.Relations {\n\t\t// skip first struct name\n\t\tnames = append(names, strings.Join(relation.Field.EmbeddedBindNames[1:], \".\"))\n\t}\n\tfor _, relations := range embeddedRelations.EmbeddedRelations {\n\t\tnames = append(names, embeddedValues(relations)...)\n\t}\n\treturn names\n}\n\n// preloadEntryPoint enters layer by layer. It will call real preload if it finds the right entry point.\n// If the current relationship is embedded or joined, current query will be ignored.\n//\n//nolint:cyclop\nfunc preloadEntryPoint(db *gorm.DB, joins []string, relationships *schema.Relationships, preloads map[string][]interface{}, associationsConds []interface{}) error {\n\tpreloadMap := parsePreloadMap(db.Statement.Schema, preloads)\n\n\t// avoid random traversal of the map\n\tpreloadNames := make([]string, 0, len(preloadMap))\n\tfor key := range preloadMap {\n\t\tpreloadNames = append(preloadNames, key)\n\t}\n\tsort.Strings(preloadNames)\n\n\tisJoined := func(name string) (joined bool, nestedJoins []string) {\n\t\tfor _, join := range joins {\n\t\t\tif _, ok := relationships.Relations[join]; ok && name == join {\n\t\t\t\tjoined = true\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tjoin0, join1, cut := strings.Cut(join, \".\")\n\t\t\tif cut {\n\t\t\t\tif _, ok := relationships.Relations[join0]; ok && name == join0 {\n\t\t\t\t\tjoined = true\n\t\t\t\t\tnestedJoins = append(nestedJoins, join1)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn joined, nestedJoins\n\t}\n\n\tfor _, name := range preloadNames {\n\t\tif relations := relationships.EmbeddedRelations[name]; relations != nil {\n\t\t\tif err := preloadEntryPoint(db, joins, relations, preloadMap[name], associationsConds); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t} else if rel := relationships.Relations[name]; rel != nil {\n\t\t\tif joined, nestedJoins := isJoined(name); joined {\n\t\t\t\tswitch rv := db.Statement.ReflectValue; rv.Kind() {\n\t\t\t\tcase reflect.Slice, reflect.Array:\n\t\t\t\t\tif rv.Len() > 0 {\n\t\t\t\t\t\treflectValue := rel.FieldSchema.MakeSlice().Elem()\n\t\t\t\t\t\tfor i := 0; i < rv.Len(); i++ {\n\t\t\t\t\t\t\tfrv := rel.Field.ReflectValueOf(db.Statement.Context, rv.Index(i))\n\t\t\t\t\t\t\tif frv.Kind() != reflect.Ptr {\n\t\t\t\t\t\t\t\treflectValue = reflect.Append(reflectValue, frv.Addr())\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tif frv.IsNil() {\n\t\t\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\treflectValue = reflect.Append(reflectValue, frv)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\ttx := preloadDB(db, reflectValue, reflectValue.Interface())\n\t\t\t\t\t\tif err := preloadEntryPoint(tx, nestedJoins, &tx.Statement.Schema.Relationships, preloadMap[name], associationsConds); err != nil {\n\t\t\t\t\t\t\treturn err\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\tcase reflect.Struct, reflect.Pointer:\n\t\t\t\t\treflectValue := rel.Field.ReflectValueOf(db.Statement.Context, rv)\n\t\t\t\t\ttx := preloadDB(db, reflectValue, reflectValue.Interface())\n\t\t\t\t\tif err := preloadEntryPoint(tx, nestedJoins, &tx.Statement.Schema.Relationships, preloadMap[name], associationsConds); err != nil {\n\t\t\t\t\t\treturn err\n\t\t\t\t\t}\n\t\t\t\tdefault:\n\t\t\t\t\treturn gorm.ErrInvalidData\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\ttx := db.Table(\"\").Session(&gorm.Session{Context: db.Statement.Context, SkipHooks: db.Statement.SkipHooks})\n\t\t\t\ttx.Statement.ReflectValue = db.Statement.ReflectValue\n\t\t\t\ttx.Statement.Unscoped = db.Statement.Unscoped\n\t\t\t\tif err := preload(tx, rel, append(preloads[name], associationsConds...), preloadMap[name]); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\treturn fmt.Errorf(\"%s: %w for schema %s\", name, gorm.ErrUnsupportedRelation, db.Statement.Schema.Name)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc preloadDB(db *gorm.DB, reflectValue reflect.Value, dest interface{}) *gorm.DB {\n\ttx := db.Session(&gorm.Session{Context: db.Statement.Context, NewDB: true, SkipHooks: db.Statement.SkipHooks, Initialized: true})\n\tdb.Statement.Settings.Range(func(k, v interface{}) bool {\n\t\ttx.Statement.Settings.Store(k, v)\n\t\treturn true\n\t})\n\n\tif err := tx.Statement.Parse(dest); err != nil {\n\t\ttx.AddError(err)\n\t\treturn tx\n\t}\n\ttx.Statement.ReflectValue = reflectValue\n\ttx.Statement.Unscoped = db.Statement.Unscoped\n\treturn tx\n}\n\nfunc preload(tx *gorm.DB, rel *schema.Relationship, conds []interface{}, preloads map[string][]interface{}) error {\n\tvar (\n\t\treflectValue     = tx.Statement.ReflectValue\n\t\trelForeignKeys   []string\n\t\trelForeignFields []*schema.Field\n\t\tforeignFields    []*schema.Field\n\t\tforeignValues    [][]interface{}\n\t\tidentityMap      = map[string][]reflect.Value{}\n\t\tinlineConds      []interface{}\n\t)\n\n\tif rel.JoinTable != nil {\n\t\tvar (\n\t\t\tjoinForeignFields    = make([]*schema.Field, 0, len(rel.References))\n\t\t\tjoinRelForeignFields = make([]*schema.Field, 0, len(rel.References))\n\t\t\tjoinForeignKeys      = make([]string, 0, len(rel.References))\n\t\t)\n\n\t\tfor _, ref := range rel.References {\n\t\t\tif ref.OwnPrimaryKey {\n\t\t\t\tjoinForeignKeys = append(joinForeignKeys, ref.ForeignKey.DBName)\n\t\t\t\tjoinForeignFields = append(joinForeignFields, ref.ForeignKey)\n\t\t\t\tforeignFields = append(foreignFields, ref.PrimaryKey)\n\t\t\t} else if ref.PrimaryValue != \"\" {\n\t\t\t\ttx = tx.Where(clause.Eq{Column: ref.ForeignKey.DBName, Value: ref.PrimaryValue})\n\t\t\t} else {\n\t\t\t\tjoinRelForeignFields = append(joinRelForeignFields, ref.ForeignKey)\n\t\t\t\trelForeignKeys = append(relForeignKeys, ref.PrimaryKey.DBName)\n\t\t\t\trelForeignFields = append(relForeignFields, ref.PrimaryKey)\n\t\t\t}\n\t\t}\n\n\t\tjoinIdentityMap, joinForeignValues := schema.GetIdentityFieldValuesMap(tx.Statement.Context, reflectValue, foreignFields)\n\t\tif len(joinForeignValues) == 0 {\n\t\t\treturn nil\n\t\t}\n\n\t\tjoinResults := rel.JoinTable.MakeSlice().Elem()\n\t\tcolumn, values := schema.ToQueryValues(clause.CurrentTable, joinForeignKeys, joinForeignValues)\n\t\tif err := tx.Where(clause.IN{Column: column, Values: values}).Find(joinResults.Addr().Interface()).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t// convert join identity map to relation identity map\n\t\tfieldValues := make([]interface{}, len(joinForeignFields))\n\t\tjoinFieldValues := make([]interface{}, len(joinRelForeignFields))\n\t\tfor i := 0; i < joinResults.Len(); i++ {\n\t\t\tjoinIndexValue := joinResults.Index(i)\n\t\t\tfor idx, field := range joinForeignFields {\n\t\t\t\tfieldValues[idx], _ = field.ValueOf(tx.Statement.Context, joinIndexValue)\n\t\t\t}\n\n\t\t\tfor idx, field := range joinRelForeignFields {\n\t\t\t\tjoinFieldValues[idx], _ = field.ValueOf(tx.Statement.Context, joinIndexValue)\n\t\t\t}\n\n\t\t\tif results, ok := joinIdentityMap[utils.ToStringKey(fieldValues...)]; ok {\n\t\t\t\tjoinKey := utils.ToStringKey(joinFieldValues...)\n\t\t\t\tidentityMap[joinKey] = append(identityMap[joinKey], results...)\n\t\t\t}\n\t\t}\n\n\t\t_, foreignValues = schema.GetIdentityFieldValuesMap(tx.Statement.Context, joinResults, joinRelForeignFields)\n\t} else {\n\t\tfor _, ref := range rel.References {\n\t\t\tif ref.OwnPrimaryKey {\n\t\t\t\trelForeignKeys = append(relForeignKeys, ref.ForeignKey.DBName)\n\t\t\t\trelForeignFields = append(relForeignFields, ref.ForeignKey)\n\t\t\t\tforeignFields = append(foreignFields, ref.PrimaryKey)\n\t\t\t} else if ref.PrimaryValue != \"\" {\n\t\t\t\ttx = tx.Where(clause.Eq{Column: ref.ForeignKey.DBName, Value: ref.PrimaryValue})\n\t\t\t} else {\n\t\t\t\trelForeignKeys = append(relForeignKeys, ref.PrimaryKey.DBName)\n\t\t\t\trelForeignFields = append(relForeignFields, ref.PrimaryKey)\n\t\t\t\tforeignFields = append(foreignFields, ref.ForeignKey)\n\t\t\t}\n\t\t}\n\n\t\tidentityMap, foreignValues = schema.GetIdentityFieldValuesMap(tx.Statement.Context, reflectValue, foreignFields)\n\t\tif len(foreignValues) == 0 {\n\t\t\treturn nil\n\t\t}\n\t}\n\n\t// nested preload\n\tfor p, pvs := range preloads {\n\t\ttx = tx.Preload(p, pvs...)\n\t}\n\n\treflectResults := rel.FieldSchema.MakeSlice().Elem()\n\tcolumn, values := schema.ToQueryValues(clause.CurrentTable, relForeignKeys, foreignValues)\n\n\tif len(values) != 0 {\n\t\ttx = tx.Model(reflectResults.Addr().Interface()).Where(clause.IN{Column: column, Values: values})\n\n\t\tfor _, cond := range conds {\n\t\t\tif fc, ok := cond.(func(*gorm.DB) *gorm.DB); ok {\n\t\t\t\ttx = fc(tx)\n\t\t\t} else {\n\t\t\t\tinlineConds = append(inlineConds, cond)\n\t\t\t}\n\t\t}\n\n\t\tif len(inlineConds) > 0 {\n\t\t\ttx = tx.Where(inlineConds[0], inlineConds[1:]...)\n\t\t}\n\n\t\tif err := tx.Find(reflectResults.Addr().Interface()).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tfieldValues := make([]interface{}, len(relForeignFields))\n\n\t// clean up old values before preloading\n\tswitch reflectValue.Kind() {\n\tcase reflect.Struct:\n\t\tswitch rel.Type {\n\t\tcase schema.HasMany, schema.Many2Many:\n\t\t\ttx.AddError(rel.Field.Set(tx.Statement.Context, reflectValue, reflect.MakeSlice(rel.Field.IndirectFieldType, 0, 10).Interface()))\n\t\tdefault:\n\t\t\ttx.AddError(rel.Field.Set(tx.Statement.Context, reflectValue, reflect.New(rel.Field.FieldType).Interface()))\n\t\t}\n\tcase reflect.Slice, reflect.Array:\n\t\tfor i := 0; i < reflectValue.Len(); i++ {\n\t\t\tswitch rel.Type {\n\t\t\tcase schema.HasMany, schema.Many2Many:\n\t\t\t\ttx.AddError(rel.Field.Set(tx.Statement.Context, reflectValue.Index(i), reflect.MakeSlice(rel.Field.IndirectFieldType, 0, 10).Interface()))\n\t\t\tdefault:\n\t\t\t\ttx.AddError(rel.Field.Set(tx.Statement.Context, reflectValue.Index(i), reflect.New(rel.Field.FieldType).Interface()))\n\t\t\t}\n\t\t}\n\t}\n\n\tfor i := 0; i < reflectResults.Len(); i++ {\n\t\telem := reflectResults.Index(i)\n\t\tfor idx, field := range relForeignFields {\n\t\t\tfieldValues[idx], _ = field.ValueOf(tx.Statement.Context, elem)\n\t\t}\n\n\t\tdatas, ok := identityMap[utils.ToStringKey(fieldValues...)]\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"failed to assign association %#v, make sure foreign fields exists\", elem.Interface())\n\t\t}\n\n\t\tfor _, data := range datas {\n\t\t\treflectFieldValue := rel.Field.ReflectValueOf(tx.Statement.Context, data)\n\t\t\tif reflectFieldValue.Kind() == reflect.Ptr && reflectFieldValue.IsNil() {\n\t\t\t\treflectFieldValue.Set(reflect.New(rel.Field.FieldType.Elem()))\n\t\t\t}\n\n\t\t\treflectFieldValue = reflect.Indirect(reflectFieldValue)\n\t\t\tswitch reflectFieldValue.Kind() {\n\t\t\tcase reflect.Struct:\n\t\t\t\ttx.AddError(rel.Field.Set(tx.Statement.Context, data, elem.Interface()))\n\t\t\tcase reflect.Slice, reflect.Array:\n\t\t\t\tif reflectFieldValue.Type().Elem().Kind() == reflect.Ptr {\n\t\t\t\t\ttx.AddError(rel.Field.Set(tx.Statement.Context, data, reflect.Append(reflectFieldValue, elem).Interface()))\n\t\t\t\t} else {\n\t\t\t\t\ttx.AddError(rel.Field.Set(tx.Statement.Context, data, reflect.Append(reflectFieldValue, elem.Elem()).Interface()))\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn tx.Error\n}\n"
  },
  {
    "path": "callbacks/query.go",
    "content": "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\t\"gorm.io/gorm/utils\"\n)\n\nfunc Query(db *gorm.DB) {\n\tif db.Error == nil {\n\t\tBuildQuerySQL(db)\n\n\t\tif !db.DryRun && db.Error == nil {\n\t\t\trows, err := db.Statement.ConnPool.QueryContext(db.Statement.Context, db.Statement.SQL.String(), db.Statement.Vars...)\n\t\t\tif err != nil {\n\t\t\t\tdb.AddError(err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tdefer func() {\n\t\t\t\tdb.AddError(rows.Close())\n\t\t\t}()\n\t\t\tgorm.Scan(rows, db, 0)\n\n\t\t\tif db.Statement.Result != nil {\n\t\t\t\tdb.Statement.Result.RowsAffected = db.RowsAffected\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc BuildQuerySQL(db *gorm.DB) {\n\tif db.Statement.Schema != nil {\n\t\tfor _, c := range db.Statement.Schema.QueryClauses {\n\t\t\tdb.Statement.AddClause(c)\n\t\t}\n\t}\n\n\tif db.Statement.SQL.Len() == 0 {\n\t\tdb.Statement.SQL.Grow(100)\n\t\tclauseSelect := clause.Select{Distinct: db.Statement.Distinct}\n\n\t\tif db.Statement.ReflectValue.Kind() == reflect.Struct && db.Statement.ReflectValue.Type() == db.Statement.Schema.ModelType {\n\t\t\tvar conds []clause.Expression\n\t\t\tfor _, primaryField := range db.Statement.Schema.PrimaryFields {\n\t\t\t\tif v, isZero := primaryField.ValueOf(db.Statement.Context, db.Statement.ReflectValue); !isZero {\n\t\t\t\t\tconds = append(conds, clause.Eq{Column: clause.Column{Table: db.Statement.Table, Name: primaryField.DBName}, Value: v})\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif len(conds) > 0 {\n\t\t\t\tdb.Statement.AddClause(clause.Where{Exprs: conds})\n\t\t\t}\n\t\t}\n\n\t\tif len(db.Statement.Selects) > 0 {\n\t\t\tclauseSelect.Columns = make([]clause.Column, len(db.Statement.Selects))\n\t\t\tfor idx, name := range db.Statement.Selects {\n\t\t\t\tif db.Statement.Schema == nil {\n\t\t\t\t\tclauseSelect.Columns[idx] = clause.Column{Name: name, Raw: true}\n\t\t\t\t} else if f := db.Statement.Schema.LookUpField(name); f != nil {\n\t\t\t\t\tclauseSelect.Columns[idx] = clause.Column{Name: f.DBName}\n\t\t\t\t} else {\n\t\t\t\t\tclauseSelect.Columns[idx] = clause.Column{Name: name, Raw: true}\n\t\t\t\t}\n\t\t\t}\n\t\t} else if db.Statement.Schema != nil && len(db.Statement.Omits) > 0 {\n\t\t\tselectColumns, _ := db.Statement.SelectAndOmitColumns(false, false)\n\t\t\tclauseSelect.Columns = make([]clause.Column, 0, len(db.Statement.Schema.DBNames))\n\t\t\tfor _, dbName := range db.Statement.Schema.DBNames {\n\t\t\t\tif v, ok := selectColumns[dbName]; (ok && v) || !ok {\n\t\t\t\t\tclauseSelect.Columns = append(clauseSelect.Columns, clause.Column{Table: db.Statement.Table, Name: dbName})\n\t\t\t\t}\n\t\t\t}\n\t\t} else if db.Statement.Schema != nil && db.Statement.ReflectValue.IsValid() {\n\t\t\tqueryFields := db.QueryFields\n\t\t\tif !queryFields {\n\t\t\t\tswitch db.Statement.ReflectValue.Kind() {\n\t\t\t\tcase reflect.Struct:\n\t\t\t\t\tqueryFields = db.Statement.ReflectValue.Type() != db.Statement.Schema.ModelType\n\t\t\t\tcase reflect.Slice:\n\t\t\t\t\tqueryFields = db.Statement.ReflectValue.Type().Elem() != db.Statement.Schema.ModelType\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif queryFields {\n\t\t\t\tstmt := gorm.Statement{DB: db}\n\t\t\t\t// smaller struct\n\t\t\t\tif err := stmt.Parse(db.Statement.Dest); err == nil && (db.QueryFields || stmt.Schema.ModelType != db.Statement.Schema.ModelType) {\n\t\t\t\t\tclauseSelect.Columns = make([]clause.Column, len(stmt.Schema.DBNames))\n\n\t\t\t\t\tfor idx, dbName := range stmt.Schema.DBNames {\n\t\t\t\t\t\tclauseSelect.Columns[idx] = clause.Column{Table: db.Statement.Table, Name: dbName}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// inline joins\n\t\tfromClause := clause.From{}\n\t\tif v, ok := db.Statement.Clauses[\"FROM\"].Expression.(clause.From); ok {\n\t\t\tfromClause = v\n\t\t}\n\n\t\tif len(db.Statement.Joins) != 0 || len(fromClause.Joins) != 0 {\n\t\t\tif len(db.Statement.Selects) == 0 && len(db.Statement.Omits) == 0 && db.Statement.Schema != nil {\n\t\t\t\tclauseSelect.Columns = make([]clause.Column, len(db.Statement.Schema.DBNames))\n\t\t\t\tfor idx, dbName := range db.Statement.Schema.DBNames {\n\t\t\t\t\tclauseSelect.Columns[idx] = clause.Column{Table: db.Statement.Table, Name: dbName}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tspecifiedRelationsName := map[string]string{clause.CurrentTable: clause.CurrentTable}\n\t\t\tfor _, join := range db.Statement.Joins {\n\t\t\t\tif db.Statement.Schema != nil {\n\t\t\t\t\tvar isRelations bool // is relations or raw sql\n\t\t\t\t\tvar relations []*schema.Relationship\n\t\t\t\t\trelation, ok := db.Statement.Schema.Relationships.Relations[join.Name]\n\t\t\t\t\tif ok {\n\t\t\t\t\t\tisRelations = true\n\t\t\t\t\t\trelations = append(relations, relation)\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// handle nested join like \"Manager.Company\"\n\t\t\t\t\t\tnestedJoinNames := strings.Split(join.Name, \".\")\n\t\t\t\t\t\tif len(nestedJoinNames) > 1 {\n\t\t\t\t\t\t\tisNestedJoin := true\n\t\t\t\t\t\t\tguessNestedRelations := make([]*schema.Relationship, 0, len(nestedJoinNames))\n\t\t\t\t\t\t\tcurrentRelations := db.Statement.Schema.Relationships.Relations\n\t\t\t\t\t\t\tfor _, relname := range nestedJoinNames {\n\t\t\t\t\t\t\t\t// incomplete match, only treated as raw sql\n\t\t\t\t\t\t\t\tif relation, ok = currentRelations[relname]; ok {\n\t\t\t\t\t\t\t\t\tguessNestedRelations = append(guessNestedRelations, relation)\n\t\t\t\t\t\t\t\t\tcurrentRelations = relation.FieldSchema.Relationships.Relations\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tisNestedJoin = false\n\t\t\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif isNestedJoin {\n\t\t\t\t\t\t\t\tisRelations = true\n\t\t\t\t\t\t\t\trelations = guessNestedRelations\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif isRelations {\n\t\t\t\t\t\tgenJoinClause := func(joinType clause.JoinType, tableAliasName string, parentTableName string, relation *schema.Relationship) clause.Join {\n\t\t\t\t\t\t\tcolumnStmt := gorm.Statement{\n\t\t\t\t\t\t\t\tTable: tableAliasName, DB: db, Schema: relation.FieldSchema,\n\t\t\t\t\t\t\t\tSelects: join.Selects, Omits: join.Omits,\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tselectColumns, restricted := columnStmt.SelectAndOmitColumns(false, false)\n\t\t\t\t\t\t\tfor _, s := range relation.FieldSchema.DBNames {\n\t\t\t\t\t\t\t\tif v, ok := selectColumns[s]; (ok && v) || (!ok && !restricted) {\n\t\t\t\t\t\t\t\t\tclauseSelect.Columns = append(clauseSelect.Columns, clause.Column{\n\t\t\t\t\t\t\t\t\t\tTable: tableAliasName,\n\t\t\t\t\t\t\t\t\t\tName:  s,\n\t\t\t\t\t\t\t\t\t\tAlias: utils.NestedRelationName(tableAliasName, s),\n\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif join.Expression != nil {\n\t\t\t\t\t\t\t\treturn clause.Join{\n\t\t\t\t\t\t\t\t\tType:       join.JoinType,\n\t\t\t\t\t\t\t\t\tExpression: join.Expression,\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\texprs := make([]clause.Expression, len(relation.References))\n\t\t\t\t\t\t\tfor idx, ref := range relation.References {\n\t\t\t\t\t\t\t\tif ref.OwnPrimaryKey {\n\t\t\t\t\t\t\t\t\texprs[idx] = clause.Eq{\n\t\t\t\t\t\t\t\t\t\tColumn: clause.Column{Table: parentTableName, Name: ref.PrimaryKey.DBName},\n\t\t\t\t\t\t\t\t\t\tValue:  clause.Column{Table: tableAliasName, Name: ref.ForeignKey.DBName},\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tif ref.PrimaryValue == \"\" {\n\t\t\t\t\t\t\t\t\t\texprs[idx] = clause.Eq{\n\t\t\t\t\t\t\t\t\t\t\tColumn: clause.Column{Table: parentTableName, Name: ref.ForeignKey.DBName},\n\t\t\t\t\t\t\t\t\t\t\tValue:  clause.Column{Table: tableAliasName, Name: ref.PrimaryKey.DBName},\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\texprs[idx] = clause.Eq{\n\t\t\t\t\t\t\t\t\t\t\tColumn: clause.Column{Table: tableAliasName, Name: ref.ForeignKey.DBName},\n\t\t\t\t\t\t\t\t\t\t\tValue:  ref.PrimaryValue,\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tonStmt := gorm.Statement{Table: tableAliasName, DB: db, Clauses: map[string]clause.Clause{}}\n\t\t\t\t\t\t\t\tfor _, c := range relation.FieldSchema.QueryClauses {\n\t\t\t\t\t\t\t\t\tonStmt.AddClause(c)\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tif join.On != nil {\n\t\t\t\t\t\t\t\t\tonStmt.AddClause(join.On)\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tif cs, ok := onStmt.Clauses[\"WHERE\"]; ok {\n\t\t\t\t\t\t\t\t\tif where, ok := cs.Expression.(clause.Where); ok {\n\t\t\t\t\t\t\t\t\t\twhere.Build(&onStmt)\n\n\t\t\t\t\t\t\t\t\t\tif onSQL := onStmt.SQL.String(); onSQL != \"\" {\n\t\t\t\t\t\t\t\t\t\t\tvars := onStmt.Vars\n\t\t\t\t\t\t\t\t\t\t\tfor idx, v := range vars {\n\t\t\t\t\t\t\t\t\t\t\t\tbindvar := strings.Builder{}\n\t\t\t\t\t\t\t\t\t\t\t\tonStmt.Vars = vars[0 : idx+1]\n\t\t\t\t\t\t\t\t\t\t\t\tdb.Dialector.BindVarTo(&bindvar, &onStmt, v)\n\t\t\t\t\t\t\t\t\t\t\t\tonSQL = strings.Replace(onSQL, bindvar.String(), \"?\", 1)\n\t\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\t\texprs = append(exprs, clause.Expr{SQL: onSQL, Vars: vars})\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn clause.Join{\n\t\t\t\t\t\t\t\tType:  joinType,\n\t\t\t\t\t\t\t\tTable: clause.Table{Name: relation.FieldSchema.Table, Alias: tableAliasName},\n\t\t\t\t\t\t\t\tON:    clause.Where{Exprs: exprs},\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tparentTableName := clause.CurrentTable\n\t\t\t\t\t\tfor idx, rel := range relations {\n\t\t\t\t\t\t\t// joins table alias like \"Manager, Company, Manager__Company\"\n\t\t\t\t\t\t\tcurAliasName := rel.Name\n\t\t\t\t\t\t\tif parentTableName != clause.CurrentTable {\n\t\t\t\t\t\t\t\tcurAliasName = utils.NestedRelationName(parentTableName, curAliasName)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif _, ok := specifiedRelationsName[curAliasName]; !ok {\n\t\t\t\t\t\t\t\taliasName := curAliasName\n\t\t\t\t\t\t\t\tif idx == len(relations)-1 && join.Alias != \"\" {\n\t\t\t\t\t\t\t\t\taliasName = join.Alias\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tfromClause.Joins = append(fromClause.Joins, genJoinClause(join.JoinType, aliasName, specifiedRelationsName[parentTableName], rel))\n\t\t\t\t\t\t\t\tspecifiedRelationsName[curAliasName] = aliasName\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tparentTableName = curAliasName\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tfromClause.Joins = append(fromClause.Joins, clause.Join{\n\t\t\t\t\t\t\tExpression: clause.NamedExpr{SQL: join.Name, Vars: join.Conds},\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tfromClause.Joins = append(fromClause.Joins, clause.Join{\n\t\t\t\t\t\tExpression: clause.NamedExpr{SQL: join.Name, Vars: join.Conds},\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tdb.Statement.AddClause(fromClause)\n\t\t} else {\n\t\t\tdb.Statement.AddClauseIfNotExists(clause.From{})\n\t\t}\n\n\t\tdb.Statement.AddClauseIfNotExists(clauseSelect)\n\n\t\tdb.Statement.Build(db.Statement.BuildClauses...)\n\t}\n}\n\nfunc Preload(db *gorm.DB) {\n\tif db.Error == nil && len(db.Statement.Preloads) > 0 {\n\t\tif db.Statement.Schema == nil {\n\t\t\tdb.AddError(fmt.Errorf(\"%w when using preload\", gorm.ErrModelValueRequired))\n\t\t\treturn\n\t\t}\n\n\t\tjoins := make([]string, 0, len(db.Statement.Joins))\n\t\tfor _, join := range db.Statement.Joins {\n\t\t\tjoins = append(joins, join.Name)\n\t\t}\n\n\t\ttx := preloadDB(db, db.Statement.ReflectValue, db.Statement.Dest)\n\t\tif tx.Error != nil {\n\t\t\treturn\n\t\t}\n\n\t\tdb.AddError(preloadEntryPoint(tx, joins, &tx.Statement.Schema.Relationships, db.Statement.Preloads, db.Statement.Preloads[clause.Associations]))\n\t}\n}\n\nfunc AfterQuery(db *gorm.DB) {\n\t// clear the joins after query because preload need it\n\tif v, ok := db.Statement.Clauses[\"FROM\"].Expression.(clause.From); ok {\n\t\tfromClause := db.Statement.Clauses[\"FROM\"]\n\t\tfromClause.Expression = clause.From{Tables: v.Tables, Joins: utils.RTrimSlice(v.Joins, len(db.Statement.Joins))} // keep the original From Joins\n\t\tdb.Statement.Clauses[\"FROM\"] = fromClause\n\t}\n\tif db.Error == nil && db.Statement.Schema != nil && !db.Statement.SkipHooks && db.Statement.Schema.AfterFind && db.RowsAffected > 0 {\n\t\tcallMethod(db, func(value interface{}, tx *gorm.DB) bool {\n\t\t\tif i, ok := value.(AfterFindInterface); ok {\n\t\t\t\tdb.AddError(i.AfterFind(tx))\n\t\t\t\treturn true\n\t\t\t}\n\t\t\treturn false\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "callbacks/raw.go",
    "content": "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, err := db.Statement.ConnPool.ExecContext(db.Statement.Context, db.Statement.SQL.String(), db.Statement.Vars...)\n\t\tif err != nil {\n\t\t\tdb.AddError(err)\n\t\t\treturn\n\t\t}\n\n\t\tdb.RowsAffected, _ = result.RowsAffected()\n\n\t\tif db.Statement.Result != nil {\n\t\t\tdb.Statement.Result.Result = result\n\t\t\tdb.Statement.Result.RowsAffected = db.RowsAffected\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "callbacks/row.go",
    "content": "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\tif db.DryRun || db.Error != nil {\n\t\t\treturn\n\t\t}\n\n\t\tif isRows, ok := db.Get(\"rows\"); ok && isRows.(bool) {\n\t\t\tdb.Statement.Settings.Delete(\"rows\")\n\t\t\tdb.Statement.Dest, db.Error = db.Statement.ConnPool.QueryContext(db.Statement.Context, db.Statement.SQL.String(), db.Statement.Vars...)\n\t\t} else {\n\t\t\tdb.Statement.Dest = db.Statement.ConnPool.QueryRowContext(db.Statement.Context, db.Statement.SQL.String(), db.Statement.Vars...)\n\t\t}\n\n\t\tdb.RowsAffected = -1\n\t}\n}\n"
  },
  {
    "path": "callbacks/transaction.go",
    "content": "package callbacks\n\nimport (\n\t\"gorm.io/gorm\"\n)\n\nfunc BeginTransaction(db *gorm.DB) {\n\tif !db.Config.SkipDefaultTransaction && db.Error == nil {\n\t\tif tx := db.Begin(); tx.Error == nil {\n\t\t\tdb.Statement.ConnPool = tx.Statement.ConnPool\n\t\t\tdb.InstanceSet(\"gorm:started_transaction\", true)\n\t\t} else if tx.Error == gorm.ErrInvalidTransaction {\n\t\t\ttx.Error = nil\n\t\t} else {\n\t\t\tdb.Error = tx.Error\n\t\t}\n\t}\n}\n\nfunc CommitOrRollbackTransaction(db *gorm.DB) {\n\tif !db.Config.SkipDefaultTransaction {\n\t\tif _, ok := db.InstanceGet(\"gorm:started_transaction\"); ok {\n\t\t\tif db.Error != nil {\n\t\t\t\tdb.Rollback()\n\t\t\t} else {\n\t\t\t\tdb.Commit()\n\t\t\t}\n\n\t\t\tdb.Statement.ConnPool = db.ConnPool\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "callbacks/update.go",
    "content": "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/gorm/utils\"\n)\n\nfunc SetupUpdateReflectValue(db *gorm.DB) {\n\tif db.Error == nil && db.Statement.Schema != nil {\n\t\tif !db.Statement.ReflectValue.CanAddr() || db.Statement.Model != db.Statement.Dest {\n\t\t\tdb.Statement.ReflectValue = reflect.ValueOf(db.Statement.Model)\n\t\t\tfor db.Statement.ReflectValue.Kind() == reflect.Ptr {\n\t\t\t\tdb.Statement.ReflectValue = db.Statement.ReflectValue.Elem()\n\t\t\t}\n\n\t\t\tif dest, ok := db.Statement.Dest.(map[string]interface{}); ok {\n\t\t\t\tfor _, rel := range db.Statement.Schema.Relationships.BelongsTo {\n\t\t\t\t\tif _, ok := dest[rel.Name]; ok {\n\t\t\t\t\t\tdb.AddError(rel.Field.Set(db.Statement.Context, db.Statement.ReflectValue, dest[rel.Name]))\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n// BeforeUpdate before update hooks\nfunc BeforeUpdate(db *gorm.DB) {\n\tif db.Error == nil && db.Statement.Schema != nil && !db.Statement.SkipHooks && (db.Statement.Schema.BeforeSave || db.Statement.Schema.BeforeUpdate) {\n\t\tcallMethod(db, func(value interface{}, tx *gorm.DB) (called bool) {\n\t\t\tif db.Statement.Schema.BeforeSave {\n\t\t\t\tif i, ok := value.(BeforeSaveInterface); ok {\n\t\t\t\t\tcalled = true\n\t\t\t\t\tdb.AddError(i.BeforeSave(tx))\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif db.Statement.Schema.BeforeUpdate {\n\t\t\t\tif i, ok := value.(BeforeUpdateInterface); ok {\n\t\t\t\t\tcalled = true\n\t\t\t\t\tdb.AddError(i.BeforeUpdate(tx))\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn called\n\t\t})\n\t}\n}\n\n// Update update hook\nfunc Update(config *Config) func(db *gorm.DB) {\n\tsupportReturning := utils.Contains(config.UpdateClauses, \"RETURNING\")\n\n\treturn func(db *gorm.DB) {\n\t\tif db.Error != nil {\n\t\t\treturn\n\t\t}\n\n\t\tif db.Statement.Schema != nil {\n\t\t\tfor _, c := range db.Statement.Schema.UpdateClauses {\n\t\t\t\tdb.Statement.AddClause(c)\n\t\t\t}\n\t\t}\n\n\t\tif db.Statement.SQL.Len() == 0 {\n\t\t\tdb.Statement.SQL.Grow(180)\n\t\t\tdb.Statement.AddClauseIfNotExists(clause.Update{})\n\t\t\tif _, ok := db.Statement.Clauses[\"SET\"]; !ok {\n\t\t\t\tif set := ConvertToAssignments(db.Statement); len(set) != 0 {\n\t\t\t\t\tdefer delete(db.Statement.Clauses, \"SET\")\n\t\t\t\t\tdb.Statement.AddClause(set)\n\t\t\t\t} else {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tdb.Statement.Build(db.Statement.BuildClauses...)\n\t\t}\n\n\t\tcheckMissingWhereConditions(db)\n\n\t\tif !db.DryRun && db.Error == nil {\n\t\t\tif ok, mode := hasReturning(db, supportReturning); ok {\n\t\t\t\tif rows, err := db.Statement.ConnPool.QueryContext(db.Statement.Context, db.Statement.SQL.String(), db.Statement.Vars...); db.AddError(err) == nil {\n\t\t\t\t\tdest := db.Statement.Dest\n\t\t\t\t\tdb.Statement.Dest = db.Statement.ReflectValue.Addr().Interface()\n\t\t\t\t\tgorm.Scan(rows, db, mode)\n\t\t\t\t\tdb.Statement.Dest = dest\n\t\t\t\t\tdb.AddError(rows.Close())\n\n\t\t\t\t\tif db.Statement.Result != nil {\n\t\t\t\t\t\tdb.Statement.Result.RowsAffected = db.RowsAffected\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tresult, err := db.Statement.ConnPool.ExecContext(db.Statement.Context, db.Statement.SQL.String(), db.Statement.Vars...)\n\n\t\t\t\tif db.AddError(err) == nil {\n\t\t\t\t\tdb.RowsAffected, _ = result.RowsAffected()\n\t\t\t\t}\n\n\t\t\t\tif db.Statement.Result != nil {\n\t\t\t\t\tdb.Statement.Result.Result = result\n\t\t\t\t\tdb.Statement.Result.RowsAffected = db.RowsAffected\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n// AfterUpdate after update hooks\nfunc AfterUpdate(db *gorm.DB) {\n\tif db.Error == nil && db.Statement.Schema != nil && !db.Statement.SkipHooks && (db.Statement.Schema.AfterSave || db.Statement.Schema.AfterUpdate) {\n\t\tcallMethod(db, func(value interface{}, tx *gorm.DB) (called bool) {\n\t\t\tif db.Statement.Schema.AfterUpdate {\n\t\t\t\tif i, ok := value.(AfterUpdateInterface); ok {\n\t\t\t\t\tcalled = true\n\t\t\t\t\tdb.AddError(i.AfterUpdate(tx))\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif db.Statement.Schema.AfterSave {\n\t\t\t\tif i, ok := value.(AfterSaveInterface); ok {\n\t\t\t\t\tcalled = true\n\t\t\t\t\tdb.AddError(i.AfterSave(tx))\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn called\n\t\t})\n\t}\n}\n\n// ConvertToAssignments convert to update assignments\nfunc ConvertToAssignments(stmt *gorm.Statement) (set clause.Set) {\n\tvar (\n\t\tselectColumns, restricted = stmt.SelectAndOmitColumns(false, true)\n\t\tassignValue               func(field *schema.Field, value interface{})\n\t)\n\n\tswitch stmt.ReflectValue.Kind() {\n\tcase reflect.Slice, reflect.Array:\n\t\tassignValue = func(field *schema.Field, value interface{}) {\n\t\t\tfor i := 0; i < stmt.ReflectValue.Len(); i++ {\n\t\t\t\tif stmt.ReflectValue.CanAddr() {\n\t\t\t\t\tfield.Set(stmt.Context, stmt.ReflectValue.Index(i), value)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\tcase reflect.Struct:\n\t\tassignValue = func(field *schema.Field, value interface{}) {\n\t\t\tif stmt.ReflectValue.CanAddr() {\n\t\t\t\tfield.Set(stmt.Context, stmt.ReflectValue, value)\n\t\t\t}\n\t\t}\n\tdefault:\n\t\tassignValue = func(field *schema.Field, value interface{}) {\n\t\t}\n\t}\n\n\tupdatingValue := reflect.ValueOf(stmt.Dest)\n\tfor updatingValue.Kind() == reflect.Ptr {\n\t\tupdatingValue = updatingValue.Elem()\n\t}\n\n\tif !updatingValue.CanAddr() || stmt.Dest != stmt.Model {\n\t\tswitch stmt.ReflectValue.Kind() {\n\t\tcase reflect.Slice, reflect.Array:\n\t\t\tif size := stmt.ReflectValue.Len(); size > 0 {\n\t\t\t\tvar isZero bool\n\t\t\t\tfor i := 0; i < size; i++ {\n\t\t\t\t\tfor _, field := range stmt.Schema.PrimaryFields {\n\t\t\t\t\t\t_, isZero = field.ValueOf(stmt.Context, stmt.ReflectValue.Index(i))\n\t\t\t\t\t\tif !isZero {\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif !isZero {\n\t\t\t\t\t_, primaryValues := schema.GetIdentityFieldValuesMap(stmt.Context, stmt.ReflectValue, stmt.Schema.PrimaryFields)\n\t\t\t\t\tcolumn, values := schema.ToQueryValues(\"\", stmt.Schema.PrimaryFieldDBNames, primaryValues)\n\t\t\t\t\tstmt.AddClause(clause.Where{Exprs: []clause.Expression{clause.IN{Column: column, Values: values}}})\n\t\t\t\t}\n\t\t\t}\n\t\tcase reflect.Struct:\n\t\t\tfor _, field := range stmt.Schema.PrimaryFields {\n\t\t\t\tif value, isZero := field.ValueOf(stmt.Context, stmt.ReflectValue); !isZero {\n\t\t\t\t\tstmt.AddClause(clause.Where{Exprs: []clause.Expression{clause.Eq{Column: field.DBName, Value: value}}})\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tswitch value := updatingValue.Interface().(type) {\n\tcase map[string]interface{}:\n\t\tset = make([]clause.Assignment, 0, len(value))\n\n\t\tkeys := make([]string, 0, len(value))\n\t\tfor k := range value {\n\t\t\tkeys = append(keys, k)\n\t\t}\n\t\tsort.Strings(keys)\n\n\t\tfor _, k := range keys {\n\t\t\tkv := value[k]\n\t\t\tif _, ok := kv.(*gorm.DB); ok {\n\t\t\t\tkv = []interface{}{kv}\n\t\t\t}\n\n\t\t\tif stmt.Schema != nil {\n\t\t\t\tif field := stmt.Schema.LookUpField(k); field != nil {\n\t\t\t\t\tif field.DBName != \"\" {\n\t\t\t\t\t\tif v, ok := selectColumns[field.DBName]; (ok && v) || (!ok && !restricted) {\n\t\t\t\t\t\t\tset = append(set, clause.Assignment{Column: clause.Column{Name: field.DBName}, Value: kv})\n\t\t\t\t\t\t\tassignValue(field, value[k])\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if v, ok := selectColumns[field.Name]; (ok && v) || (!ok && !restricted) {\n\t\t\t\t\t\tassignValue(field, value[k])\n\t\t\t\t\t}\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif v, ok := selectColumns[k]; (ok && v) || (!ok && !restricted) {\n\t\t\t\tset = append(set, clause.Assignment{Column: clause.Column{Name: k}, Value: kv})\n\t\t\t}\n\t\t}\n\n\t\tif !stmt.SkipHooks && stmt.Schema != nil {\n\t\t\tfor _, dbName := range stmt.Schema.DBNames {\n\t\t\t\tfield := stmt.Schema.LookUpField(dbName)\n\t\t\t\tif field.AutoUpdateTime > 0 && value[field.Name] == nil && value[field.DBName] == nil {\n\t\t\t\t\tif v, ok := selectColumns[field.DBName]; (ok && v) || !ok {\n\t\t\t\t\t\tnow := stmt.DB.NowFunc()\n\t\t\t\t\t\tassignValue(field, now)\n\n\t\t\t\t\t\tif field.AutoUpdateTime == schema.UnixNanosecond {\n\t\t\t\t\t\t\tset = append(set, clause.Assignment{Column: clause.Column{Name: field.DBName}, Value: now.UnixNano()})\n\t\t\t\t\t\t} else if field.AutoUpdateTime == schema.UnixMillisecond {\n\t\t\t\t\t\t\tset = append(set, clause.Assignment{Column: clause.Column{Name: field.DBName}, Value: now.UnixMilli()})\n\t\t\t\t\t\t} else if field.AutoUpdateTime == schema.UnixSecond {\n\t\t\t\t\t\t\tset = append(set, clause.Assignment{Column: clause.Column{Name: field.DBName}, Value: now.Unix()})\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tset = append(set, clause.Assignment{Column: clause.Column{Name: field.DBName}, Value: now})\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\tdefault:\n\t\tupdatingSchema := stmt.Schema\n\t\tvar isDiffSchema bool\n\t\tif !updatingValue.CanAddr() || stmt.Dest != stmt.Model {\n\t\t\t// different schema\n\t\t\tupdatingStmt := &gorm.Statement{DB: stmt.DB}\n\t\t\tif err := updatingStmt.Parse(stmt.Dest); err == nil {\n\t\t\t\tupdatingSchema = updatingStmt.Schema\n\t\t\t\tisDiffSchema = true\n\t\t\t}\n\t\t}\n\n\t\tswitch updatingValue.Kind() {\n\t\tcase reflect.Struct:\n\t\t\tset = make([]clause.Assignment, 0, len(stmt.Schema.FieldsByDBName))\n\t\t\tfor _, dbName := range stmt.Schema.DBNames {\n\t\t\t\tif field := updatingSchema.LookUpField(dbName); field != nil {\n\t\t\t\t\tif !field.PrimaryKey || !updatingValue.CanAddr() || stmt.Dest != stmt.Model {\n\t\t\t\t\t\tif v, ok := selectColumns[field.DBName]; (ok && v) || (!ok && (!restricted || (!stmt.SkipHooks && field.AutoUpdateTime > 0))) {\n\t\t\t\t\t\t\tvalue, isZero := field.ValueOf(stmt.Context, updatingValue)\n\t\t\t\t\t\t\tif !stmt.SkipHooks && field.AutoUpdateTime > 0 {\n\t\t\t\t\t\t\t\tif field.AutoUpdateTime == schema.UnixNanosecond {\n\t\t\t\t\t\t\t\t\tvalue = stmt.DB.NowFunc().UnixNano()\n\t\t\t\t\t\t\t\t} else if field.AutoUpdateTime == schema.UnixMillisecond {\n\t\t\t\t\t\t\t\t\tvalue = stmt.DB.NowFunc().UnixMilli()\n\t\t\t\t\t\t\t\t} else if field.AutoUpdateTime == schema.UnixSecond {\n\t\t\t\t\t\t\t\t\tvalue = stmt.DB.NowFunc().Unix()\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tvalue = stmt.DB.NowFunc()\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tisZero = false\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (ok || !isZero) && field.Updatable {\n\t\t\t\t\t\t\t\tset = append(set, clause.Assignment{Column: clause.Column{Name: field.DBName}, Value: value})\n\t\t\t\t\t\t\t\tassignField := field\n\t\t\t\t\t\t\t\tif isDiffSchema {\n\t\t\t\t\t\t\t\t\tif originField := stmt.Schema.LookUpField(dbName); originField != nil {\n\t\t\t\t\t\t\t\t\t\tassignField = originField\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tassignValue(assignField, value)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif value, isZero := field.ValueOf(stmt.Context, updatingValue); !isZero {\n\t\t\t\t\t\t\tstmt.AddClause(clause.Where{Exprs: []clause.Expression{clause.Eq{Column: field.DBName, Value: value}}})\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\tdefault:\n\t\t\tstmt.AddError(gorm.ErrInvalidData)\n\t\t}\n\t}\n\n\treturn\n}\n"
  },
  {
    "path": "callbacks.go",
    "content": "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/utils\"\n)\n\nfunc initializeCallbacks(db *DB) *callbacks {\n\treturn &callbacks{\n\t\tprocessors: map[string]*processor{\n\t\t\t\"create\": {db: db},\n\t\t\t\"query\":  {db: db},\n\t\t\t\"update\": {db: db},\n\t\t\t\"delete\": {db: db},\n\t\t\t\"row\":    {db: db},\n\t\t\t\"raw\":    {db: db},\n\t\t},\n\t}\n}\n\n// callbacks gorm callbacks manager\ntype callbacks struct {\n\tprocessors map[string]*processor\n}\n\ntype processor struct {\n\tdb        *DB\n\tClauses   []string\n\tfns       []func(*DB)\n\tcallbacks []*callback\n}\n\ntype callback struct {\n\tname      string\n\tbefore    string\n\tafter     string\n\tremove    bool\n\treplace   bool\n\tmatch     func(*DB) bool\n\thandler   func(*DB)\n\tprocessor *processor\n}\n\nfunc (cs *callbacks) Create() *processor {\n\treturn cs.processors[\"create\"]\n}\n\nfunc (cs *callbacks) Query() *processor {\n\treturn cs.processors[\"query\"]\n}\n\nfunc (cs *callbacks) Update() *processor {\n\treturn cs.processors[\"update\"]\n}\n\nfunc (cs *callbacks) Delete() *processor {\n\treturn cs.processors[\"delete\"]\n}\n\nfunc (cs *callbacks) Row() *processor {\n\treturn cs.processors[\"row\"]\n}\n\nfunc (cs *callbacks) Raw() *processor {\n\treturn cs.processors[\"raw\"]\n}\n\nfunc (p *processor) Execute(db *DB) *DB {\n\t// call scopes\n\tfor len(db.Statement.scopes) > 0 {\n\t\tdb = db.executeScopes()\n\t}\n\n\tvar (\n\t\tcurTime           = time.Now()\n\t\tstmt              = db.Statement\n\t\tresetBuildClauses bool\n\t)\n\n\tif len(stmt.BuildClauses) == 0 {\n\t\tstmt.BuildClauses = p.Clauses\n\t\tresetBuildClauses = true\n\t}\n\n\tif optimizer, ok := stmt.Dest.(StatementModifier); ok {\n\t\toptimizer.ModifyStatement(stmt)\n\t}\n\n\tif db.DefaultContextTimeout > 0 {\n\t\tif _, ok := stmt.Context.Deadline(); !ok {\n\t\t\tstmt.Context, _ = context.WithTimeout(stmt.Context, db.DefaultContextTimeout)\n\t\t}\n\t}\n\n\t// assign model values\n\tif stmt.Model == nil {\n\t\tstmt.Model = stmt.Dest\n\t} else if stmt.Dest == nil {\n\t\tstmt.Dest = stmt.Model\n\t}\n\n\t// parse model values\n\tif stmt.Model != nil {\n\t\tif err := stmt.Parse(stmt.Model); err != nil && (!errors.Is(err, schema.ErrUnsupportedDataType) || (stmt.Table == \"\" && stmt.TableExpr == nil && stmt.SQL.Len() == 0)) {\n\t\t\tif errors.Is(err, schema.ErrUnsupportedDataType) && stmt.Table == \"\" && stmt.TableExpr == nil {\n\t\t\t\tdb.AddError(fmt.Errorf(\"%w: Table not set, please set it like: db.Model(&user) or db.Table(\\\"users\\\")\", err))\n\t\t\t} else {\n\t\t\t\tdb.AddError(err)\n\t\t\t}\n\t\t}\n\t}\n\n\t// assign stmt.ReflectValue\n\tif stmt.Dest != nil {\n\t\tstmt.ReflectValue = reflect.ValueOf(stmt.Dest)\n\t\tfor stmt.ReflectValue.Kind() == reflect.Ptr {\n\t\t\tif stmt.ReflectValue.IsNil() && stmt.ReflectValue.CanAddr() {\n\t\t\t\tstmt.ReflectValue.Set(reflect.New(stmt.ReflectValue.Type().Elem()))\n\t\t\t}\n\n\t\t\tstmt.ReflectValue = stmt.ReflectValue.Elem()\n\t\t}\n\t\tif !stmt.ReflectValue.IsValid() {\n\t\t\tdb.AddError(ErrInvalidValue)\n\t\t}\n\t}\n\n\tfor _, f := range p.fns {\n\t\tf(db)\n\t}\n\n\tif stmt.SQL.Len() > 0 {\n\t\tdb.Logger.Trace(stmt.Context, curTime, func() (string, int64) {\n\t\t\tsql, vars := stmt.SQL.String(), stmt.Vars\n\t\t\tif filter, ok := db.Logger.(ParamsFilter); ok {\n\t\t\t\tsql, vars = filter.ParamsFilter(stmt.Context, stmt.SQL.String(), stmt.Vars...)\n\t\t\t}\n\t\t\treturn db.Dialector.Explain(sql, vars...), db.RowsAffected\n\t\t}, db.Error)\n\t}\n\n\tif !stmt.DB.DryRun {\n\t\tstmt.SQL.Reset()\n\t\tstmt.Vars = nil\n\t}\n\n\tif resetBuildClauses {\n\t\tstmt.BuildClauses = nil\n\t}\n\n\treturn db\n}\n\nfunc (p *processor) Get(name string) func(*DB) {\n\tfor i := len(p.callbacks) - 1; i >= 0; i-- {\n\t\tif v := p.callbacks[i]; v.name == name && !v.remove {\n\t\t\treturn v.handler\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (p *processor) Before(name string) *callback {\n\treturn &callback{before: name, processor: p}\n}\n\nfunc (p *processor) After(name string) *callback {\n\treturn &callback{after: name, processor: p}\n}\n\nfunc (p *processor) Match(fc func(*DB) bool) *callback {\n\treturn &callback{match: fc, processor: p}\n}\n\nfunc (p *processor) Register(name string, fn func(*DB)) error {\n\treturn (&callback{processor: p}).Register(name, fn)\n}\n\nfunc (p *processor) Remove(name string) error {\n\treturn (&callback{processor: p}).Remove(name)\n}\n\nfunc (p *processor) Replace(name string, fn func(*DB)) error {\n\treturn (&callback{processor: p}).Replace(name, fn)\n}\n\nfunc (p *processor) compile() (err error) {\n\tvar callbacks []*callback\n\tremovedMap := map[string]bool{}\n\tfor _, callback := range p.callbacks {\n\t\tif callback.match == nil || callback.match(p.db) {\n\t\t\tcallbacks = append(callbacks, callback)\n\t\t}\n\t\tif callback.remove {\n\t\t\tremovedMap[callback.name] = true\n\t\t}\n\t}\n\n\tif len(removedMap) > 0 {\n\t\tcallbacks = removeCallbacks(callbacks, removedMap)\n\t}\n\tp.callbacks = callbacks\n\n\tif p.fns, err = sortCallbacks(p.callbacks); err != nil {\n\t\tp.db.Logger.Error(context.Background(), \"Got error when compile callbacks, got %v\", err)\n\t}\n\treturn\n}\n\nfunc (c *callback) Before(name string) *callback {\n\tc.before = name\n\treturn c\n}\n\nfunc (c *callback) After(name string) *callback {\n\tc.after = name\n\treturn c\n}\n\nfunc (c *callback) Register(name string, fn func(*DB)) error {\n\tc.name = name\n\tc.handler = fn\n\tc.processor.callbacks = append(c.processor.callbacks, c)\n\treturn c.processor.compile()\n}\n\nfunc (c *callback) Remove(name string) error {\n\tc.processor.db.Logger.Warn(context.Background(), \"removing callback `%s` from %s\\n\", name, utils.FileWithLineNum())\n\tc.name = name\n\tc.remove = true\n\tc.processor.callbacks = append(c.processor.callbacks, c)\n\treturn c.processor.compile()\n}\n\nfunc (c *callback) Replace(name string, fn func(*DB)) error {\n\tc.processor.db.Logger.Info(context.Background(), \"replacing callback `%s` from %s\\n\", name, utils.FileWithLineNum())\n\tc.name = name\n\tc.handler = fn\n\tc.replace = true\n\tc.processor.callbacks = append(c.processor.callbacks, c)\n\treturn c.processor.compile()\n}\n\n// getRIndex get right index from string slice\nfunc getRIndex(strs []string, str string) int {\n\tfor i := len(strs) - 1; i >= 0; i-- {\n\t\tif strs[i] == str {\n\t\t\treturn i\n\t\t}\n\t}\n\treturn -1\n}\n\nfunc sortCallbacks(cs []*callback) (fns []func(*DB), err error) {\n\tvar (\n\t\tnames, sorted []string\n\t\tsortCallback  func(*callback) error\n\t)\n\tsort.SliceStable(cs, func(i, j int) bool {\n\t\tif cs[j].before == \"*\" && cs[i].before != \"*\" {\n\t\t\treturn true\n\t\t}\n\t\tif cs[j].after == \"*\" && cs[i].after != \"*\" {\n\t\t\treturn true\n\t\t}\n\t\treturn false\n\t})\n\n\tfor _, c := range cs {\n\t\t// show warning message the callback name already exists\n\t\tif idx := getRIndex(names, c.name); idx > -1 && !c.replace && !c.remove && !cs[idx].remove {\n\t\t\tc.processor.db.Logger.Warn(context.Background(), \"duplicated callback `%s` from %s\\n\", c.name, utils.FileWithLineNum())\n\t\t}\n\t\tnames = append(names, c.name)\n\t}\n\n\tsortCallback = func(c *callback) error {\n\t\tif c.before != \"\" { // if defined before callback\n\t\t\tif c.before == \"*\" && len(sorted) > 0 {\n\t\t\t\tif curIdx := getRIndex(sorted, c.name); curIdx == -1 {\n\t\t\t\t\tsorted = append([]string{c.name}, sorted...)\n\t\t\t\t}\n\t\t\t} else if sortedIdx := getRIndex(sorted, c.before); sortedIdx != -1 {\n\t\t\t\tif curIdx := getRIndex(sorted, c.name); curIdx == -1 {\n\t\t\t\t\t// if before callback already sorted, append current callback just after it\n\t\t\t\t\tsorted = append(sorted[:sortedIdx], append([]string{c.name}, sorted[sortedIdx:]...)...)\n\t\t\t\t} else if curIdx > sortedIdx {\n\t\t\t\t\treturn fmt.Errorf(\"conflicting callback %s with before %s\", c.name, c.before)\n\t\t\t\t}\n\t\t\t} else if idx := getRIndex(names, c.before); idx != -1 {\n\t\t\t\t// if before callback exists\n\t\t\t\tcs[idx].after = c.name\n\t\t\t}\n\t\t}\n\n\t\tif c.after != \"\" { // if defined after callback\n\t\t\tif c.after == \"*\" && len(sorted) > 0 {\n\t\t\t\tif curIdx := getRIndex(sorted, c.name); curIdx == -1 {\n\t\t\t\t\tsorted = append(sorted, c.name)\n\t\t\t\t}\n\t\t\t} else if sortedIdx := getRIndex(sorted, c.after); sortedIdx != -1 {\n\t\t\t\tif curIdx := getRIndex(sorted, c.name); curIdx == -1 {\n\t\t\t\t\t// if after callback sorted, append current callback to last\n\t\t\t\t\tsorted = append(sorted, c.name)\n\t\t\t\t} else if curIdx < sortedIdx {\n\t\t\t\t\treturn fmt.Errorf(\"conflicting callback %s with before %s\", c.name, c.after)\n\t\t\t\t}\n\t\t\t} else if idx := getRIndex(names, c.after); idx != -1 {\n\t\t\t\t// if after callback exists but haven't sorted\n\t\t\t\t// set after callback's before callback to current callback\n\t\t\t\tafter := cs[idx]\n\n\t\t\t\tif after.before == \"\" {\n\t\t\t\t\tafter.before = c.name\n\t\t\t\t}\n\n\t\t\t\tif err := sortCallback(after); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\n\t\t\t\tif err := sortCallback(c); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// if current callback haven't been sorted, append it to last\n\t\tif getRIndex(sorted, c.name) == -1 {\n\t\t\tsorted = append(sorted, c.name)\n\t\t}\n\n\t\treturn nil\n\t}\n\n\tfor _, c := range cs {\n\t\tif err = sortCallback(c); err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\n\tfor _, name := range sorted {\n\t\tif idx := getRIndex(names, name); !cs[idx].remove {\n\t\t\tfns = append(fns, cs[idx].handler)\n\t\t}\n\t}\n\n\treturn\n}\n\nfunc removeCallbacks(cs []*callback, nameMap map[string]bool) []*callback {\n\tcallbacks := make([]*callback, 0, len(cs))\n\tfor _, callback := range cs {\n\t\tif nameMap[callback.name] {\n\t\t\tcontinue\n\t\t}\n\t\tcallbacks = append(callbacks, callback)\n\t}\n\treturn callbacks\n}\n"
  },
  {
    "path": "chainable_api.go",
    "content": "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 model you would like to run db operations\n//\n//\t// update all users's name to `hello`\n//\tdb.Model(&User{}).Update(\"name\", \"hello\")\n//\t// if user's primary key is non-blank, will use it as condition, then will only update that user's name to `hello`\n//\tdb.Model(&user).Update(\"name\", \"hello\")\nfunc (db *DB) Model(value interface{}) (tx *DB) {\n\ttx = db.getInstance()\n\ttx.Statement.Model = value\n\treturn\n}\n\n// Clauses Add clauses\n//\n// This supports both standard clauses (clause.OrderBy, clause.Limit, clause.Where) and more\n// advanced techniques like specifying lock strength and optimizer hints. See the\n// [docs] for more depth.\n//\n//\t// add a simple limit clause\n//\tdb.Clauses(clause.Limit{Limit: 1}).Find(&User{})\n//\t// tell the optimizer to use the `idx_user_name` index\n//\tdb.Clauses(hints.UseIndex(\"idx_user_name\")).Find(&User{})\n//\t// specify the lock strength to UPDATE\n//\tdb.Clauses(clause.Locking{Strength: \"UPDATE\"}).Find(&users)\n//\n// [docs]: https://gorm.io/docs/sql_builder.html#Clauses\nfunc (db *DB) Clauses(conds ...clause.Expression) (tx *DB) {\n\ttx = db.getInstance()\n\tvar whereConds []interface{}\n\n\tfor _, cond := range conds {\n\t\tif c, ok := cond.(clause.Interface); ok {\n\t\t\ttx.Statement.AddClause(c)\n\t\t} else if optimizer, ok := cond.(StatementModifier); ok {\n\t\t\toptimizer.ModifyStatement(tx.Statement)\n\t\t} else {\n\t\t\twhereConds = append(whereConds, cond)\n\t\t}\n\t}\n\n\tif len(whereConds) > 0 {\n\t\ttx.Statement.AddClause(clause.Where{Exprs: tx.Statement.BuildCondition(whereConds[0], whereConds[1:]...)})\n\t}\n\treturn\n}\n\nvar tableRegexp = regexp.MustCompile(`(?i)(?:.+? AS (\\w+)\\s*(?:$|,)|^\\w+\\s+(\\w+)$)`)\n\n// Table specify the table you would like to run db operations\n//\n//\t// Get a user\n//\tdb.Table(\"users\").Take(&result)\nfunc (db *DB) Table(name string, args ...interface{}) (tx *DB) {\n\ttx = db.getInstance()\n\tif strings.Contains(name, \" \") || strings.Contains(name, \"`\") || len(args) > 0 {\n\t\ttx.Statement.TableExpr = &clause.Expr{SQL: name, Vars: args}\n\t\tif results := tableRegexp.FindStringSubmatch(name); len(results) == 3 {\n\t\t\tif results[1] != \"\" {\n\t\t\t\ttx.Statement.Table = results[1]\n\t\t\t} else {\n\t\t\t\ttx.Statement.Table = results[2]\n\t\t\t}\n\t\t}\n\t} else if tables := strings.Split(name, \".\"); len(tables) == 2 {\n\t\ttx.Statement.TableExpr = &clause.Expr{SQL: tx.Statement.Quote(name)}\n\t\ttx.Statement.Table = tables[1]\n\t} else if name != \"\" {\n\t\ttx.Statement.TableExpr = &clause.Expr{SQL: tx.Statement.Quote(name)}\n\t\ttx.Statement.Table = name\n\t} else {\n\t\ttx.Statement.TableExpr = nil\n\t\ttx.Statement.Table = \"\"\n\t}\n\treturn\n}\n\n// Distinct specify distinct fields that you want querying\n//\n//\t// Select distinct names of users\n//\tdb.Distinct(\"name\").Find(&results)\n//\t// Select distinct name/age pairs from users\n//\tdb.Distinct(\"name\", \"age\").Find(&results)\nfunc (db *DB) Distinct(args ...interface{}) (tx *DB) {\n\ttx = db.getInstance()\n\ttx.Statement.Distinct = true\n\tif len(args) > 0 {\n\t\ttx = tx.Select(args[0], args[1:]...)\n\t}\n\treturn\n}\n\n// Select specify fields that you want when querying, creating, updating\n//\n// Use Select when you only want a subset of the fields. By default, GORM will select all fields.\n// Select accepts both string arguments and arrays.\n//\n//\t// Select name and age of user using multiple arguments\n//\tdb.Select(\"name\", \"age\").Find(&users)\n//\t// Select name and age of user using an array\n//\tdb.Select([]string{\"name\", \"age\"}).Find(&users)\nfunc (db *DB) Select(query interface{}, args ...interface{}) (tx *DB) {\n\ttx = db.getInstance()\n\n\tswitch v := query.(type) {\n\tcase []string:\n\t\ttx.Statement.Selects = v\n\n\t\tfor _, arg := range args {\n\t\t\tswitch arg := arg.(type) {\n\t\t\tcase string:\n\t\t\t\ttx.Statement.Selects = append(tx.Statement.Selects, arg)\n\t\t\tcase []string:\n\t\t\t\ttx.Statement.Selects = append(tx.Statement.Selects, arg...)\n\t\t\tdefault:\n\t\t\t\ttx.AddError(fmt.Errorf(\"unsupported select args %v %v\", query, args))\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\tif clause, ok := tx.Statement.Clauses[\"SELECT\"]; ok {\n\t\t\tclause.Expression = nil\n\t\t\ttx.Statement.Clauses[\"SELECT\"] = clause\n\t\t}\n\tcase string:\n\t\tif strings.Count(v, \"?\") >= len(args) && len(args) > 0 {\n\t\t\ttx.Statement.AddClause(clause.Select{\n\t\t\t\tDistinct:   db.Statement.Distinct,\n\t\t\t\tExpression: clause.Expr{SQL: v, Vars: args},\n\t\t\t})\n\t\t} else if strings.Count(v, \"@\") > 0 && len(args) > 0 {\n\t\t\ttx.Statement.AddClause(clause.Select{\n\t\t\t\tDistinct:   db.Statement.Distinct,\n\t\t\t\tExpression: clause.NamedExpr{SQL: v, Vars: args},\n\t\t\t})\n\t\t} else {\n\t\t\ttx.Statement.Selects = []string{v}\n\n\t\t\tfor _, arg := range args {\n\t\t\t\tswitch arg := arg.(type) {\n\t\t\t\tcase string:\n\t\t\t\t\ttx.Statement.Selects = append(tx.Statement.Selects, arg)\n\t\t\t\tcase []string:\n\t\t\t\t\ttx.Statement.Selects = append(tx.Statement.Selects, arg...)\n\t\t\t\tdefault:\n\t\t\t\t\ttx.Statement.AddClause(clause.Select{\n\t\t\t\t\t\tDistinct:   db.Statement.Distinct,\n\t\t\t\t\t\tExpression: clause.Expr{SQL: v, Vars: args},\n\t\t\t\t\t})\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif clause, ok := tx.Statement.Clauses[\"SELECT\"]; ok {\n\t\t\t\tclause.Expression = nil\n\t\t\t\ttx.Statement.Clauses[\"SELECT\"] = clause\n\t\t\t}\n\t\t}\n\tdefault:\n\t\ttx.AddError(fmt.Errorf(\"unsupported select args %v %v\", query, args))\n\t}\n\n\treturn\n}\n\n// Omit specify fields that you want to ignore when creating, updating and querying\nfunc (db *DB) Omit(columns ...string) (tx *DB) {\n\ttx = db.getInstance()\n\n\tif len(columns) == 1 && strings.ContainsRune(columns[0], ',') {\n\t\ttx.Statement.Omits = strings.FieldsFunc(columns[0], utils.IsInvalidDBNameChar)\n\t} else {\n\t\ttx.Statement.Omits = columns\n\t}\n\treturn\n}\n\n// MapColumns modify the column names in the query results to facilitate align to the corresponding structural fields\nfunc (db *DB) MapColumns(m map[string]string) (tx *DB) {\n\ttx = db.getInstance()\n\ttx.Statement.ColumnMapping = m\n\treturn\n}\n\n// Where add conditions\n//\n// See the [docs] for details on the various formats that where clauses can take. By default, where clauses chain with AND.\n//\n//\t// Find the first user with name jinzhu\n//\tdb.Where(\"name = ?\", \"jinzhu\").First(&user)\n//\t// Find the first user with name jinzhu and age 20\n//\tdb.Where(&User{Name: \"jinzhu\", Age: 20}).First(&user)\n//\t// Find the first user with name jinzhu and age not equal to 20\n//\tdb.Where(\"name = ?\", \"jinzhu\").Where(\"age <> ?\", \"20\").First(&user)\n//\n// [docs]: https://gorm.io/docs/query.html#Conditions\nfunc (db *DB) Where(query interface{}, args ...interface{}) (tx *DB) {\n\ttx = db.getInstance()\n\tif conds := tx.Statement.BuildCondition(query, args...); len(conds) > 0 {\n\t\ttx.Statement.AddClause(clause.Where{Exprs: conds})\n\t}\n\treturn\n}\n\n// Not add NOT conditions\n//\n// Not works similarly to where, and has the same syntax.\n//\n//\t// Find the first user with name not equal to jinzhu\n//\tdb.Not(\"name = ?\", \"jinzhu\").First(&user)\nfunc (db *DB) Not(query interface{}, args ...interface{}) (tx *DB) {\n\ttx = db.getInstance()\n\tif conds := tx.Statement.BuildCondition(query, args...); len(conds) > 0 {\n\t\ttx.Statement.AddClause(clause.Where{Exprs: []clause.Expression{clause.Not(conds...)}})\n\t}\n\treturn\n}\n\n// Or add OR conditions\n//\n// Or is used to chain together queries with an OR.\n//\n//\t// Find the first user with name equal to jinzhu or john\n//\tdb.Where(\"name = ?\", \"jinzhu\").Or(\"name = ?\", \"john\").First(&user)\nfunc (db *DB) Or(query interface{}, args ...interface{}) (tx *DB) {\n\ttx = db.getInstance()\n\tif conds := tx.Statement.BuildCondition(query, args...); len(conds) > 0 {\n\t\ttx.Statement.AddClause(clause.Where{Exprs: []clause.Expression{clause.Or(clause.And(conds...))}})\n\t}\n\treturn\n}\n\n// Joins specify Joins conditions\n//\n//\tdb.Joins(\"Account\").Find(&user)\n//\tdb.Joins(\"JOIN emails ON emails.user_id = users.id AND emails.email = ?\", \"jinzhu@example.org\").Find(&user)\n//\tdb.Joins(\"Account\", DB.Select(\"id\").Where(\"user_id = users.id AND name = ?\", \"someName\").Model(&Account{}))\nfunc (db *DB) Joins(query string, args ...interface{}) (tx *DB) {\n\treturn joins(db, clause.LeftJoin, query, args...)\n}\n\n// InnerJoins specify inner joins conditions\n// db.InnerJoins(\"Account\").Find(&user)\nfunc (db *DB) InnerJoins(query string, args ...interface{}) (tx *DB) {\n\treturn joins(db, clause.InnerJoin, query, args...)\n}\n\nfunc joins(db *DB, joinType clause.JoinType, query string, args ...interface{}) (tx *DB) {\n\ttx = db.getInstance()\n\n\tif len(args) == 1 {\n\t\tif db, ok := args[0].(*DB); ok {\n\t\t\tj := join{\n\t\t\t\tName: query, Conds: args, Selects: db.Statement.Selects,\n\t\t\t\tOmits: db.Statement.Omits, JoinType: joinType,\n\t\t\t}\n\t\t\tif where, ok := db.Statement.Clauses[\"WHERE\"].Expression.(clause.Where); ok {\n\t\t\t\tj.On = &where\n\t\t\t}\n\t\t\ttx.Statement.Joins = append(tx.Statement.Joins, j)\n\t\t\treturn\n\t\t}\n\t}\n\n\ttx.Statement.Joins = append(tx.Statement.Joins, join{Name: query, Conds: args, JoinType: joinType})\n\treturn\n}\n\n// Group specify the group method on the find\n//\n//\t// Select the sum age of users with given names\n//\tdb.Model(&User{}).Select(\"name, sum(age) as total\").Group(\"name\").Find(&results)\nfunc (db *DB) Group(name string) (tx *DB) {\n\ttx = db.getInstance()\n\n\tfields := strings.FieldsFunc(name, utils.IsInvalidDBNameChar)\n\ttx.Statement.AddClause(clause.GroupBy{\n\t\tColumns: []clause.Column{{Name: name, Raw: len(fields) != 1}},\n\t})\n\treturn\n}\n\n// Having specify HAVING conditions for GROUP BY\n//\n//\t// Select the sum age of users with name jinzhu\n//\tdb.Model(&User{}).Select(\"name, sum(age) as total\").Group(\"name\").Having(\"name = ?\", \"jinzhu\").Find(&result)\nfunc (db *DB) Having(query interface{}, args ...interface{}) (tx *DB) {\n\ttx = db.getInstance()\n\ttx.Statement.AddClause(clause.GroupBy{\n\t\tHaving: tx.Statement.BuildCondition(query, args...),\n\t})\n\treturn\n}\n\n// Order specify order when retrieving records from database\n//\n//\tdb.Order(\"name DESC\")\n//\tdb.Order(clause.OrderByColumn{Column: clause.Column{Name: \"name\"}, Desc: true})\n//\tdb.Order(clause.OrderBy{Columns: []clause.OrderByColumn{\n//\t\t{Column: clause.Column{Name: \"name\"}, Desc: true},\n//\t\t{Column: clause.Column{Name: \"age\"}, Desc: true},\n//\t}})\nfunc (db *DB) Order(value interface{}) (tx *DB) {\n\ttx = db.getInstance()\n\n\tswitch v := value.(type) {\n\tcase clause.OrderBy:\n\t\ttx.Statement.AddClause(v)\n\tcase clause.OrderByColumn:\n\t\ttx.Statement.AddClause(clause.OrderBy{\n\t\t\tColumns: []clause.OrderByColumn{v},\n\t\t})\n\tcase string:\n\t\tif v != \"\" {\n\t\t\ttx.Statement.AddClause(clause.OrderBy{\n\t\t\t\tColumns: []clause.OrderByColumn{{\n\t\t\t\t\tColumn: clause.Column{Name: v, Raw: true},\n\t\t\t\t}},\n\t\t\t})\n\t\t}\n\t}\n\treturn\n}\n\n// Limit specify the number of records to be retrieved\n//\n// Limit conditions can be cancelled by using `Limit(-1)`.\n//\n//\t// retrieve 3 users\n//\tdb.Limit(3).Find(&users)\n//\t// retrieve 3 users into users1, and all users into users2\n//\tdb.Limit(3).Find(&users1).Limit(-1).Find(&users2)\nfunc (db *DB) Limit(limit int) (tx *DB) {\n\ttx = db.getInstance()\n\ttx.Statement.AddClause(clause.Limit{Limit: &limit})\n\treturn\n}\n\n// Offset specify the number of records to skip before starting to return the records\n//\n// Offset conditions can be cancelled by using `Offset(-1)`.\n//\n//\t// select the third user\n//\tdb.Offset(2).First(&user)\n//\t// select the first user by cancelling an earlier chained offset\n//\tdb.Offset(5).Offset(-1).First(&user)\nfunc (db *DB) Offset(offset int) (tx *DB) {\n\ttx = db.getInstance()\n\ttx.Statement.AddClause(clause.Limit{Offset: offset})\n\treturn\n}\n\n// Scopes pass current database connection to arguments `func(DB) DB`, which could be used to add conditions dynamically\n//\n//\tfunc AmountGreaterThan1000(db *gorm.DB) *gorm.DB {\n//\t    return db.Where(\"amount > ?\", 1000)\n//\t}\n//\n//\tfunc OrderStatus(status []string) func (db *gorm.DB) *gorm.DB {\n//\t    return func (db *gorm.DB) *gorm.DB {\n//\t        return db.Scopes(AmountGreaterThan1000).Where(\"status in (?)\", status)\n//\t    }\n//\t}\n//\n//\tdb.Scopes(AmountGreaterThan1000, OrderStatus([]string{\"paid\", \"shipped\"})).Find(&orders)\nfunc (db *DB) Scopes(funcs ...func(*DB) *DB) (tx *DB) {\n\ttx = db.getInstance()\n\ttx.Statement.scopes = append(tx.Statement.scopes, funcs...)\n\treturn tx\n}\n\nfunc (db *DB) executeScopes() (tx *DB) {\n\tscopes := db.Statement.scopes\n\tdb.Statement.scopes = nil\n\tfor _, scope := range scopes {\n\t\tdb = scope(db)\n\t}\n\treturn db\n}\n\n// Preload preload associations with given conditions\n//\n//\t// get all users, and preload all non-cancelled orders\n//\tdb.Preload(\"Orders\", \"state NOT IN (?)\", \"cancelled\").Find(&users)\nfunc (db *DB) Preload(query string, args ...interface{}) (tx *DB) {\n\ttx = db.getInstance()\n\tif tx.Statement.Preloads == nil {\n\t\ttx.Statement.Preloads = map[string][]interface{}{}\n\t}\n\ttx.Statement.Preloads[query] = args\n\treturn\n}\n\n// Attrs provide attributes used in [FirstOrCreate] or [FirstOrInit]\n//\n// Attrs only adds attributes if the record is not found.\n//\n//\t// assign an email if the record is not found\n//\tdb.Where(User{Name: \"non_existing\"}).Attrs(User{Email: \"fake@fake.org\"}).FirstOrInit(&user)\n//\t// user -> User{Name: \"non_existing\", Email: \"fake@fake.org\"}\n//\n//\t// assign an email if the record is not found, otherwise ignore provided email\n//\tdb.Where(User{Name: \"jinzhu\"}).Attrs(User{Email: \"fake@fake.org\"}).FirstOrInit(&user)\n//\t// user -> User{Name: \"jinzhu\", Age: 20}\n//\n// [FirstOrCreate]: https://gorm.io/docs/advanced_query.html#FirstOrCreate\n// [FirstOrInit]: https://gorm.io/docs/advanced_query.html#FirstOrInit\nfunc (db *DB) Attrs(attrs ...interface{}) (tx *DB) {\n\ttx = db.getInstance()\n\ttx.Statement.attrs = attrs\n\treturn\n}\n\n// Assign provide attributes used in [FirstOrCreate] or [FirstOrInit]\n//\n// Assign adds attributes even if the record is found. If using FirstOrCreate, this means that\n// records will be updated even if they are found.\n//\n//\t// assign an email regardless of if the record is not found\n//\tdb.Where(User{Name: \"non_existing\"}).Assign(User{Email: \"fake@fake.org\"}).FirstOrInit(&user)\n//\t// user -> User{Name: \"non_existing\", Email: \"fake@fake.org\"}\n//\n//\t// assign email regardless of if record is found\n//\tdb.Where(User{Name: \"jinzhu\"}).Assign(User{Email: \"fake@fake.org\"}).FirstOrInit(&user)\n//\t// user -> User{Name: \"jinzhu\", Age: 20, Email: \"fake@fake.org\"}\n//\n// [FirstOrCreate]: https://gorm.io/docs/advanced_query.html#FirstOrCreate\n// [FirstOrInit]: https://gorm.io/docs/advanced_query.html#FirstOrInit\nfunc (db *DB) Assign(attrs ...interface{}) (tx *DB) {\n\ttx = db.getInstance()\n\ttx.Statement.assigns = attrs\n\treturn\n}\n\n// Unscoped disables the global scope of soft deletion in a query.\n// By default, GORM uses soft deletion, marking records as \"deleted\"\n// by setting a timestamp on a specific field (e.g., `deleted_at`).\n// Unscoped allows queries to include records marked as deleted,\n// overriding the soft deletion behavior.\n// Example:\n//\n//\tvar users []User\n//\tdb.Unscoped().Find(&users)\n//\t// Retrieves all users, including deleted ones.\nfunc (db *DB) Unscoped() (tx *DB) {\n\ttx = db.getInstance()\n\ttx.Statement.Unscoped = true\n\treturn\n}\n\nfunc (db *DB) Raw(sql string, values ...interface{}) (tx *DB) {\n\ttx = db.getInstance()\n\ttx.Statement.SQL = strings.Builder{}\n\n\tif strings.Contains(sql, \"@\") {\n\t\tclause.NamedExpr{SQL: sql, Vars: values}.Build(tx.Statement)\n\t} else {\n\t\tclause.Expr{SQL: sql, Vars: values}.Build(tx.Statement)\n\t}\n\treturn\n}\n"
  },
  {
    "path": "clause/association.go",
    "content": "package clause\n\n// AssociationOpType represents association operation types\ntype AssociationOpType int\n\nconst (\n\tOpUnlink AssociationOpType = iota // Unlink association\n\tOpDelete                          // Delete association records\n\tOpUpdate                          // Update association records\n\tOpCreate                          // Create association records with assignments\n)\n\n// Association represents an association operation\ntype Association struct {\n\tAssociation string            // Association name\n\tType        AssociationOpType // Operation type\n\tConditions  []Expression      // Filter conditions\n\tSet         []Assignment      // Assignment operations (for Update and Create)\n\tValues      []interface{}     // Values for Create operation\n}\n\n// AssociationAssigner is an interface for association operation providers\ntype AssociationAssigner interface {\n\tAssociationAssignments() []Association\n}\n\n// Assignments implements the Assigner interface so that AssociationOperation can be used as a Set method parameter\nfunc (ao Association) Assignments() []Assignment {\n\treturn []Assignment{}\n}\n\n// AssociationAssignments implements the AssociationAssigner interface\nfunc (ao Association) AssociationAssignments() []Association {\n\treturn []Association{ao}\n}\n"
  },
  {
    "path": "clause/benchmarks_test.go",
    "content": "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.io/gorm/utils/tests\"\n)\n\nfunc BenchmarkSelect(b *testing.B) {\n\tuser, _ := schema.Parse(&tests.User{}, &sync.Map{}, db.NamingStrategy)\n\n\tfor i := 0; i < b.N; i++ {\n\t\tstmt := gorm.Statement{DB: db, Table: user.Table, Schema: user, Clauses: map[string]clause.Clause{}}\n\t\tclauses := []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\"})}}}\n\n\t\tfor _, clause := range clauses {\n\t\t\tstmt.AddClause(clause)\n\t\t}\n\n\t\tstmt.Build(\"SELECT\", \"FROM\", \"WHERE\")\n\t\t_ = stmt.SQL.String()\n\t}\n}\n\nfunc BenchmarkComplexSelect(b *testing.B) {\n\tuser, _ := schema.Parse(&tests.User{}, &sync.Map{}, db.NamingStrategy)\n\n\tlimit10 := 10\n\tfor i := 0; i < b.N; i++ {\n\t\tstmt := gorm.Statement{DB: db, Table: user.Table, Schema: user, Clauses: map[string]clause.Clause{}}\n\t\tclauses := []clause.Interface{\n\t\t\tclause.Select{},\n\t\t\tclause.From{},\n\t\t\tclause.Where{Exprs: []clause.Expression{\n\t\t\t\tclause.Eq{Column: clause.PrimaryColumn, Value: \"1\"},\n\t\t\t\tclause.Gt{Column: \"age\", Value: 18},\n\t\t\t\tclause.Or(clause.Neq{Column: \"name\", Value: \"jinzhu\"}),\n\t\t\t}},\n\t\t\tclause.Where{Exprs: []clause.Expression{\n\t\t\t\tclause.Or(clause.Gt{Column: \"score\", Value: 100}, clause.Like{Column: \"name\", Value: \"%linus%\"}),\n\t\t\t}},\n\t\t\tclause.GroupBy{Columns: []clause.Column{{Name: \"role\"}}, Having: []clause.Expression{clause.Eq{\"role\", \"admin\"}}},\n\t\t\tclause.Limit{Limit: &limit10, Offset: 20},\n\t\t\tclause.OrderBy{Columns: []clause.OrderByColumn{{Column: clause.PrimaryColumn, Desc: true}}},\n\t\t}\n\n\t\tfor _, clause := range clauses {\n\t\t\tstmt.AddClause(clause)\n\t\t}\n\n\t\tstmt.Build(\"SELECT\", \"FROM\", \"WHERE\", \"GROUP BY\", \"LIMIT\", \"ORDER BY\")\n\t\t_ = stmt.SQL.String()\n\t}\n}\n"
  },
  {
    "path": "clause/clause.go",
    "content": "package clause\n\n// Interface clause interface\ntype Interface interface {\n\tName() string\n\tBuild(Builder)\n\tMergeClause(*Clause)\n}\n\n// ClauseBuilder clause builder, allows to customize how to build clause\ntype ClauseBuilder func(Clause, Builder)\n\ntype Writer interface {\n\tWriteByte(byte) error\n\tWriteString(string) (int, error)\n}\n\n// Builder builder interface\ntype Builder interface {\n\tWriter\n\tWriteQuoted(field interface{})\n\tAddVar(Writer, ...interface{})\n\tAddError(error) error\n}\n\n// Clause\ntype Clause struct {\n\tName                string // WHERE\n\tBeforeExpression    Expression\n\tAfterNameExpression Expression\n\tAfterExpression     Expression\n\tExpression          Expression\n\tBuilder             ClauseBuilder\n}\n\n// Build build clause\nfunc (c Clause) Build(builder Builder) {\n\tif c.Builder != nil {\n\t\tc.Builder(c, builder)\n\t} else if c.Expression != nil {\n\t\tif c.BeforeExpression != nil {\n\t\t\tc.BeforeExpression.Build(builder)\n\t\t\tbuilder.WriteByte(' ')\n\t\t}\n\n\t\tif c.Name != \"\" {\n\t\t\tbuilder.WriteString(c.Name)\n\t\t\tbuilder.WriteByte(' ')\n\t\t}\n\n\t\tif c.AfterNameExpression != nil {\n\t\t\tc.AfterNameExpression.Build(builder)\n\t\t\tbuilder.WriteByte(' ')\n\t\t}\n\n\t\tc.Expression.Build(builder)\n\n\t\tif c.AfterExpression != nil {\n\t\t\tbuilder.WriteByte(' ')\n\t\t\tc.AfterExpression.Build(builder)\n\t\t}\n\t}\n}\n\nconst (\n\tPrimaryKey   string = \"~~~py~~~\" // primary key\n\tCurrentTable string = \"~~~ct~~~\" // current table\n\tAssociations string = \"~~~as~~~\" // associations\n)\n\nvar (\n\tcurrentTable  = Table{Name: CurrentTable}\n\tPrimaryColumn = Column{Table: CurrentTable, Name: PrimaryKey}\n)\n\n// Column quote with name\ntype Column struct {\n\tTable string\n\tName  string\n\tAlias string\n\tRaw   bool\n}\n\n// Table quote with name\ntype Table struct {\n\tName  string\n\tAlias string\n\tRaw   bool\n}\n"
  },
  {
    "path": "clause/clause_test.go",
    "content": "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/gorm/schema\"\n\t\"gorm.io/gorm/utils/tests\"\n)\n\nvar db, _ = gorm.Open(tests.DummyDialector{}, nil)\n\nfunc checkBuildClauses(t *testing.T, clauses []clause.Interface, result string, vars []interface{}) {\n\tvar (\n\t\tbuildNames    []string\n\t\tbuildNamesMap = map[string]bool{}\n\t\tuser, _       = schema.Parse(&tests.User{}, &sync.Map{}, db.NamingStrategy)\n\t\tstmt          = gorm.Statement{DB: db, Table: user.Table, Schema: user, Clauses: map[string]clause.Clause{}}\n\t)\n\n\tfor _, c := range clauses {\n\t\tif _, ok := buildNamesMap[c.Name()]; !ok {\n\t\t\tbuildNames = append(buildNames, c.Name())\n\t\t\tbuildNamesMap[c.Name()] = true\n\t\t}\n\n\t\tstmt.AddClause(c)\n\t}\n\n\tstmt.Build(buildNames...)\n\n\tif strings.TrimSpace(stmt.SQL.String()) != result {\n\t\tt.Errorf(\"SQL expects %v got %v\", result, stmt.SQL.String())\n\t}\n\n\tif !reflect.DeepEqual(stmt.Vars, vars) {\n\t\tt.Errorf(\"Vars expects %+v got %v\", stmt.Vars, vars)\n\t}\n}\n"
  },
  {
    "path": "clause/delete.go",
    "content": "package clause\n\ntype Delete struct {\n\tModifier string\n}\n\nfunc (d Delete) Name() string {\n\treturn \"DELETE\"\n}\n\nfunc (d Delete) Build(builder Builder) {\n\tbuilder.WriteString(\"DELETE\")\n\n\tif d.Modifier != \"\" {\n\t\tbuilder.WriteByte(' ')\n\t\tbuilder.WriteString(d.Modifier)\n\t}\n}\n\nfunc (d Delete) MergeClause(clause *Clause) {\n\tclause.Name = \"\"\n\tclause.Expression = d\n}\n"
  },
  {
    "path": "clause/delete_test.go",
    "content": "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 := []struct {\n\t\tClauses []clause.Interface\n\t\tResult  string\n\t\tVars    []interface{}\n\t}{\n\t\t{\n\t\t\t[]clause.Interface{clause.Delete{}, clause.From{}},\n\t\t\t\"DELETE FROM `users`\", nil,\n\t\t},\n\t\t{\n\t\t\t[]clause.Interface{clause.Delete{Modifier: \"LOW_PRIORITY\"}, clause.From{}},\n\t\t\t\"DELETE LOW_PRIORITY FROM `users`\", nil,\n\t\t},\n\t}\n\n\tfor idx, result := range results {\n\t\tt.Run(fmt.Sprintf(\"case #%v\", idx), func(t *testing.T) {\n\t\t\tcheckBuildClauses(t, result.Clauses, result.Result, result.Vars)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "clause/expression.go",
    "content": "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 interface\ntype Expression interface {\n\tBuild(builder Builder)\n}\n\n// NegationExpressionBuilder negation expression builder\ntype NegationExpressionBuilder interface {\n\tNegationBuild(builder Builder)\n}\n\n// Expr raw expression\ntype Expr struct {\n\tSQL                string\n\tVars               []interface{}\n\tWithoutParentheses bool\n}\n\n// Build build raw expression\nfunc (expr Expr) Build(builder Builder) {\n\tvar (\n\t\tafterParenthesis bool\n\t\tidx              int\n\t)\n\n\tfor _, v := range []byte(expr.SQL) {\n\t\tif v == '?' && len(expr.Vars) > idx {\n\t\t\tif afterParenthesis || expr.WithoutParentheses {\n\t\t\t\tprocessValue(builder, expr.Vars[idx])\n\t\t\t} else {\n\t\t\t\tbuilder.AddVar(builder, expr.Vars[idx])\n\t\t\t}\n\n\t\t\tidx++\n\t\t} else {\n\t\t\tif v == '(' {\n\t\t\t\tafterParenthesis = true\n\t\t\t} else {\n\t\t\t\tafterParenthesis = false\n\t\t\t}\n\t\t\tbuilder.WriteByte(v)\n\t\t}\n\t}\n\n\tif idx < len(expr.Vars) {\n\t\tfor _, v := range expr.Vars[idx:] {\n\t\t\tbuilder.AddVar(builder, sql.NamedArg{Value: v})\n\t\t}\n\t}\n}\n\n// NamedExpr raw expression for named expr\ntype NamedExpr struct {\n\tSQL  string\n\tVars []interface{}\n}\n\n// Build build raw expression\nfunc (expr NamedExpr) Build(builder Builder) {\n\tvar (\n\t\tidx              int\n\t\tinName           bool\n\t\tafterParenthesis bool\n\t\tnamedMap         = make(map[string]interface{}, len(expr.Vars))\n\t)\n\n\tfor _, v := range expr.Vars {\n\t\tswitch value := v.(type) {\n\t\tcase sql.NamedArg:\n\t\t\tnamedMap[value.Name] = value.Value\n\t\tcase map[string]interface{}:\n\t\t\tfor k, v := range value {\n\t\t\t\tnamedMap[k] = v\n\t\t\t}\n\t\tdefault:\n\t\t\tvar appendFieldsToMap func(reflect.Value)\n\t\t\tappendFieldsToMap = func(reflectValue reflect.Value) {\n\t\t\t\treflectValue = reflect.Indirect(reflectValue)\n\t\t\t\tswitch reflectValue.Kind() {\n\t\t\t\tcase reflect.Struct:\n\t\t\t\t\tmodelType := reflectValue.Type()\n\t\t\t\t\tfor i := 0; i < modelType.NumField(); i++ {\n\t\t\t\t\t\tif fieldStruct := modelType.Field(i); ast.IsExported(fieldStruct.Name) {\n\t\t\t\t\t\t\tnamedMap[fieldStruct.Name] = reflectValue.Field(i).Interface()\n\n\t\t\t\t\t\t\tif fieldStruct.Anonymous {\n\t\t\t\t\t\t\t\tappendFieldsToMap(reflectValue.Field(i))\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tappendFieldsToMap(reflect.ValueOf(value))\n\t\t}\n\t}\n\n\tname := make([]byte, 0, 10)\n\n\tfor _, v := range []byte(expr.SQL) {\n\t\tif v == '@' && !inName {\n\t\t\tinName = true\n\t\t\tname = name[:0]\n\t\t} else if v == ' ' || v == ',' || v == ')' || v == '\"' || v == '\\'' || v == '`' || v == '\\r' || v == '\\n' || v == ';' {\n\t\t\tif inName {\n\t\t\t\tif nv, ok := namedMap[string(name)]; ok {\n\t\t\t\t\tif afterParenthesis {\n\t\t\t\t\t\tprocessValue(builder, nv)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tbuilder.AddVar(builder, nv)\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tbuilder.WriteByte('@')\n\t\t\t\t\tbuilder.WriteString(string(name))\n\t\t\t\t}\n\t\t\t\tinName = false\n\t\t\t}\n\n\t\t\tafterParenthesis = false\n\t\t\tbuilder.WriteByte(v)\n\t\t} else if v == '?' && len(expr.Vars) > idx {\n\t\t\tif afterParenthesis {\n\t\t\t\tprocessValue(builder, expr.Vars[idx])\n\t\t\t} else {\n\t\t\t\tbuilder.AddVar(builder, expr.Vars[idx])\n\t\t\t}\n\n\t\t\tidx++\n\t\t} else if inName {\n\t\t\tname = append(name, v)\n\t\t} else {\n\t\t\tif v == '(' {\n\t\t\t\tafterParenthesis = true\n\t\t\t} else {\n\t\t\t\tafterParenthesis = false\n\t\t\t}\n\t\t\tbuilder.WriteByte(v)\n\t\t}\n\t}\n\n\tif inName {\n\t\tif nv, ok := namedMap[string(name)]; ok {\n\t\t\tbuilder.AddVar(builder, nv)\n\t\t} else {\n\t\t\tbuilder.WriteByte('@')\n\t\t\tbuilder.WriteString(string(name))\n\t\t}\n\t}\n}\n\n// processValue handles different value types appropriately for SQL parameter binding\n// It checks for driver.Valuer first, then handles slices/arrays, and finally adds single values\nfunc processValue(builder Builder, value interface{}) {\n\tif _, ok := value.(driver.Valuer); ok {\n\t\tbuilder.AddVar(builder, value)\n\t\treturn\n\t}\n\n\tswitch rv := reflect.ValueOf(value); rv.Kind() {\n\tcase reflect.Slice, reflect.Array:\n\t\tif rv.Len() == 0 {\n\t\t\tbuilder.AddVar(builder, nil)\n\t\t} else {\n\t\t\tfor i := 0; i < rv.Len(); i++ {\n\t\t\t\tif i > 0 {\n\t\t\t\t\tbuilder.WriteByte(',')\n\t\t\t\t}\n\t\t\t\tbuilder.AddVar(builder, rv.Index(i).Interface())\n\t\t\t}\n\t\t}\n\tdefault:\n\t\tbuilder.AddVar(builder, value)\n\t}\n}\n\n// IN Whether a value is within a set of values\ntype IN struct {\n\tColumn interface{}\n\tValues []interface{}\n}\n\nfunc (in IN) Build(builder Builder) {\n\tbuilder.WriteQuoted(in.Column)\n\n\tswitch len(in.Values) {\n\tcase 0:\n\t\tbuilder.WriteString(\" IN (NULL)\")\n\tcase 1:\n\t\tif _, ok := in.Values[0].([]interface{}); !ok {\n\t\t\tbuilder.WriteString(\" = \")\n\t\t\tbuilder.AddVar(builder, in.Values[0])\n\t\t\tbreak\n\t\t}\n\n\t\tfallthrough\n\tdefault:\n\t\tbuilder.WriteString(\" IN (\")\n\t\tbuilder.AddVar(builder, in.Values...)\n\t\tbuilder.WriteByte(')')\n\t}\n}\n\nfunc (in IN) NegationBuild(builder Builder) {\n\tbuilder.WriteQuoted(in.Column)\n\tswitch len(in.Values) {\n\tcase 0:\n\t\tbuilder.WriteString(\" IS NOT NULL\")\n\tcase 1:\n\t\tif _, ok := in.Values[0].([]interface{}); !ok {\n\t\t\tbuilder.WriteString(\" <> \")\n\t\t\tbuilder.AddVar(builder, in.Values[0])\n\t\t\tbreak\n\t\t}\n\n\t\tfallthrough\n\tdefault:\n\t\tbuilder.WriteString(\" NOT IN (\")\n\t\tbuilder.AddVar(builder, in.Values...)\n\t\tbuilder.WriteByte(')')\n\t}\n}\n\n// Eq equal to for where\ntype Eq struct {\n\tColumn interface{}\n\tValue  interface{}\n}\n\nfunc (eq Eq) Build(builder Builder) {\n\tbuilder.WriteQuoted(eq.Column)\n\n\tswitch eq.Value.(type) {\n\tcase []string, []int, []int32, []int64, []uint, []uint32, []uint64, []interface{}:\n\t\trv := reflect.ValueOf(eq.Value)\n\t\tif rv.Len() == 0 {\n\t\t\tbuilder.WriteString(\" IN (NULL)\")\n\t\t} else {\n\t\t\tbuilder.WriteString(\" IN (\")\n\t\t\tfor i := 0; i < rv.Len(); i++ {\n\t\t\t\tif i > 0 {\n\t\t\t\t\tbuilder.WriteByte(',')\n\t\t\t\t}\n\t\t\t\tbuilder.AddVar(builder, rv.Index(i).Interface())\n\t\t\t}\n\t\t\tbuilder.WriteByte(')')\n\t\t}\n\tdefault:\n\t\tif eqNil(eq.Value) {\n\t\t\tbuilder.WriteString(\" IS NULL\")\n\t\t} else {\n\t\t\tbuilder.WriteString(\" = \")\n\t\t\tbuilder.AddVar(builder, eq.Value)\n\t\t}\n\t}\n}\n\nfunc (eq Eq) NegationBuild(builder Builder) {\n\tNeq(eq).Build(builder)\n}\n\n// Neq not equal to for where\ntype Neq Eq\n\nfunc (neq Neq) Build(builder Builder) {\n\tbuilder.WriteQuoted(neq.Column)\n\n\tswitch neq.Value.(type) {\n\tcase []string, []int, []int32, []int64, []uint, []uint32, []uint64, []interface{}:\n\t\tbuilder.WriteString(\" NOT IN (\")\n\t\trv := reflect.ValueOf(neq.Value)\n\t\tfor i := 0; i < rv.Len(); i++ {\n\t\t\tif i > 0 {\n\t\t\t\tbuilder.WriteByte(',')\n\t\t\t}\n\t\t\tbuilder.AddVar(builder, rv.Index(i).Interface())\n\t\t}\n\t\tbuilder.WriteByte(')')\n\tdefault:\n\t\tif eqNil(neq.Value) {\n\t\t\tbuilder.WriteString(\" IS NOT NULL\")\n\t\t} else {\n\t\t\tbuilder.WriteString(\" <> \")\n\t\t\tbuilder.AddVar(builder, neq.Value)\n\t\t}\n\t}\n}\n\nfunc (neq Neq) NegationBuild(builder Builder) {\n\tEq(neq).Build(builder)\n}\n\n// Gt greater than for where\ntype Gt Eq\n\nfunc (gt Gt) Build(builder Builder) {\n\tbuilder.WriteQuoted(gt.Column)\n\tbuilder.WriteString(\" > \")\n\tbuilder.AddVar(builder, gt.Value)\n}\n\nfunc (gt Gt) NegationBuild(builder Builder) {\n\tLte(gt).Build(builder)\n}\n\n// Gte greater than or equal to for where\ntype Gte Eq\n\nfunc (gte Gte) Build(builder Builder) {\n\tbuilder.WriteQuoted(gte.Column)\n\tbuilder.WriteString(\" >= \")\n\tbuilder.AddVar(builder, gte.Value)\n}\n\nfunc (gte Gte) NegationBuild(builder Builder) {\n\tLt(gte).Build(builder)\n}\n\n// Lt less than for where\ntype Lt Eq\n\nfunc (lt Lt) Build(builder Builder) {\n\tbuilder.WriteQuoted(lt.Column)\n\tbuilder.WriteString(\" < \")\n\tbuilder.AddVar(builder, lt.Value)\n}\n\nfunc (lt Lt) NegationBuild(builder Builder) {\n\tGte(lt).Build(builder)\n}\n\n// Lte less than or equal to for where\ntype Lte Eq\n\nfunc (lte Lte) Build(builder Builder) {\n\tbuilder.WriteQuoted(lte.Column)\n\tbuilder.WriteString(\" <= \")\n\tbuilder.AddVar(builder, lte.Value)\n}\n\nfunc (lte Lte) NegationBuild(builder Builder) {\n\tGt(lte).Build(builder)\n}\n\n// Like whether string matches regular expression\ntype Like Eq\n\nfunc (like Like) Build(builder Builder) {\n\tbuilder.WriteQuoted(like.Column)\n\tbuilder.WriteString(\" LIKE \")\n\tbuilder.AddVar(builder, like.Value)\n}\n\nfunc (like Like) NegationBuild(builder Builder) {\n\tbuilder.WriteQuoted(like.Column)\n\tbuilder.WriteString(\" NOT LIKE \")\n\tbuilder.AddVar(builder, like.Value)\n}\n\nfunc eqNil(value interface{}) bool {\n\tif valuer, ok := value.(driver.Valuer); ok && !eqNilReflect(valuer) {\n\t\tvalue, _ = valuer.Value()\n\t}\n\n\treturn value == nil || eqNilReflect(value)\n}\n\nfunc eqNilReflect(value interface{}) bool {\n\treflectValue := reflect.ValueOf(value)\n\treturn reflectValue.Kind() == reflect.Ptr && reflectValue.IsNil()\n}\n"
  },
  {
    "path": "clause/expression_test.go",
    "content": "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/clause\"\n\t\"gorm.io/gorm/schema\"\n\t\"gorm.io/gorm/utils/tests\"\n)\n\nfunc TestExpr(t *testing.T) {\n\tresults := []struct {\n\t\tSQL    string\n\t\tResult string\n\t\tVars   []interface{}\n\t}{{\n\t\tSQL:    \"create table ? (? ?, ? ?)\",\n\t\tVars:   []interface{}{clause.Table{Name: \"users\"}, clause.Column{Name: \"id\"}, clause.Expr{SQL: \"int\"}, clause.Column{Name: \"name\"}, clause.Expr{SQL: \"text\"}},\n\t\tResult: \"create table `users` (`id` int, `name` text)\",\n\t}}\n\n\tfor idx, result := range results {\n\t\tt.Run(fmt.Sprintf(\"case #%v\", idx), func(t *testing.T) {\n\t\t\tuser, _ := schema.Parse(&tests.User{}, &sync.Map{}, db.NamingStrategy)\n\t\t\tstmt := &gorm.Statement{DB: db, Table: user.Table, Schema: user, Clauses: map[string]clause.Clause{}}\n\t\t\tclause.Expr{SQL: result.SQL, Vars: result.Vars}.Build(stmt)\n\t\t\tif stmt.SQL.String() != result.Result {\n\t\t\t\tt.Errorf(\"generated SQL is not equal, expects %v, but got %v\", result.Result, stmt.SQL.String())\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestNamedExpr(t *testing.T) {\n\ttype Base struct {\n\t\tName2 string\n\t}\n\n\ttype NamedArgument struct {\n\t\tName1 string\n\t\tBase\n\t}\n\n\tresults := []struct {\n\t\tSQL          string\n\t\tResult       string\n\t\tVars         []interface{}\n\t\tExpectedVars []interface{}\n\t}{{\n\t\tSQL:    \"create table ? (? ?, ? ?)\",\n\t\tVars:   []interface{}{clause.Table{Name: \"users\"}, clause.Column{Name: \"id\"}, clause.Expr{SQL: \"int\"}, clause.Column{Name: \"name\"}, clause.Expr{SQL: \"text\"}},\n\t\tResult: \"create table `users` (`id` int, `name` text)\",\n\t}, {\n\t\tSQL:          \"name1 = @name AND name2 = @name\",\n\t\tVars:         []interface{}{sql.Named(\"name\", \"jinzhu\")},\n\t\tResult:       \"name1 = ? AND name2 = ?\",\n\t\tExpectedVars: []interface{}{\"jinzhu\", \"jinzhu\"},\n\t}, {\n\t\tSQL:          \"name1 = @name AND name2 = @@name\",\n\t\tVars:         []interface{}{map[string]interface{}{\"name\": \"jinzhu\"}},\n\t\tResult:       \"name1 = ? AND name2 = @@name\",\n\t\tExpectedVars: []interface{}{\"jinzhu\"},\n\t}, {\n\t\tSQL:          \"name1 = @name1 AND name2 = @name2 AND name3 = @name1\",\n\t\tVars:         []interface{}{sql.Named(\"name1\", \"jinzhu\"), sql.Named(\"name2\", \"jinzhu2\")},\n\t\tResult:       \"name1 = ? AND name2 = ? AND name3 = ?\",\n\t\tExpectedVars: []interface{}{\"jinzhu\", \"jinzhu2\", \"jinzhu\"},\n\t}, {\n\t\tSQL:          \"name1 = @name1 AND name2 = @name2 AND name3 = @name1\",\n\t\tVars:         []interface{}{map[string]interface{}{\"name1\": \"jinzhu\", \"name2\": \"jinzhu2\"}},\n\t\tResult:       \"name1 = ? AND name2 = ? AND name3 = ?\",\n\t\tExpectedVars: []interface{}{\"jinzhu\", \"jinzhu2\", \"jinzhu\"},\n\t}, {\n\t\tSQL:          \"@@test AND name1 = @name1 AND name2 = @name2 AND name3 = @name1 @notexist\",\n\t\tVars:         []interface{}{sql.Named(\"name1\", \"jinzhu\"), sql.Named(\"name2\", \"jinzhu2\")},\n\t\tResult:       \"@@test AND name1 = ? AND name2 = ? AND name3 = ? @notexist\",\n\t\tExpectedVars: []interface{}{\"jinzhu\", \"jinzhu2\", \"jinzhu\"},\n\t}, {\n\t\tSQL:          \"@@test AND name1 = @Name1 AND name2 = @Name2 AND name3 = @Name1 @notexist\",\n\t\tVars:         []interface{}{NamedArgument{Name1: \"jinzhu\", Base: Base{Name2: \"jinzhu2\"}}},\n\t\tResult:       \"@@test AND name1 = ? AND name2 = ? AND name3 = ? @notexist\",\n\t\tExpectedVars: []interface{}{\"jinzhu\", \"jinzhu2\", \"jinzhu\"},\n\t}, {\n\t\tSQL:          \"name in (@names)\",\n\t\tVars:         []interface{}{map[string]interface{}{\"names\": []interface{}{\"jinzhu\", \"jinzhu2\"}}},\n\t\tResult:       \"name in (?,?)\",\n\t\tExpectedVars: []interface{}{\"jinzhu\", \"jinzhu2\"},\n\t}, {\n\t\tSQL:          \"name in (@names)\",\n\t\tVars:         []interface{}{map[string]interface{}{\"names\": \"jinzhu\"}},\n\t\tResult:       \"name in (?)\",\n\t\tExpectedVars: []interface{}{\"jinzhu\"},\n\t}, {\n\t\tSQL:    \"create table ? (? ?, ? ?)\",\n\t\tVars:   []interface{}{},\n\t\tResult: \"create table ? (? ?, ? ?)\",\n\t}, {\n\t\tSQL:          \"name1 = @name AND name2 = @name;\",\n\t\tVars:         []interface{}{sql.Named(\"name\", \"jinzhu\")},\n\t\tResult:       \"name1 = ? AND name2 = ?;\",\n\t\tExpectedVars: []interface{}{\"jinzhu\", \"jinzhu\"},\n\t}, {\n\t\tSQL:          \"name1 = @name1\\r\\n AND name2 = @name2\",\n\t\tVars:         []interface{}{map[string]interface{}{\"name1\": \"jinzhu\", \"name2\": \"jinzhu\"}},\n\t\tResult:       \"name1 = ?\\r\\n AND name2 = ?\",\n\t\tExpectedVars: []interface{}{\"jinzhu\", \"jinzhu\"},\n\t}, {\n\t\tSQL:          \"name1 = @name1\\r AND name2 = @name2\",\n\t\tVars:         []interface{}{map[string]interface{}{\"name1\": \"jinzhu\", \"name2\": \"jinzhu\"}},\n\t\tResult:       \"name1 = ?\\r AND name2 = ?\",\n\t\tExpectedVars: []interface{}{\"jinzhu\", \"jinzhu\"},\n\t}, {\n\t\tSQL:    \"?\",\n\t\tVars:   []interface{}{clause.Column{Table: \"table\", Name: \"col\"}},\n\t\tResult: \"`table`.`col`\",\n\t}, {\n\t\tSQL:    \"?\",\n\t\tVars:   []interface{}{clause.Column{Table: \"table\", Name: \"col\", Raw: true}},\n\t\tResult: \"table.col\",\n\t}, {\n\t\tSQL:    \"?\",\n\t\tVars:   []interface{}{clause.Column{Table: \"table\", Name: clause.PrimaryKey, Raw: true}},\n\t\tResult: \"table.id\",\n\t}, {\n\t\tSQL:    \"?\",\n\t\tVars:   []interface{}{clause.Column{Table: \"table\", Name: \"col\", Alias: \"alias\"}},\n\t\tResult: \"`table`.`col` AS `alias`\",\n\t}, {\n\t\tSQL:    \"?\",\n\t\tVars:   []interface{}{clause.Column{Table: \"table\", Name: \"col\", Alias: \"alias\", Raw: true}},\n\t\tResult: \"table.col AS alias\",\n\t}, {\n\t\tSQL:    \"?\",\n\t\tVars:   []interface{}{clause.Table{Name: \"table\", Alias: \"alias\"}},\n\t\tResult: \"`table` `alias`\",\n\t}, {\n\t\tSQL:    \"?\",\n\t\tVars:   []interface{}{clause.Table{Name: \"table\", Alias: \"alias\", Raw: true}},\n\t\tResult: \"table alias\",\n\t}}\n\n\tfor idx, result := range results {\n\t\tt.Run(fmt.Sprintf(\"case #%v\", idx), func(t *testing.T) {\n\t\t\tuser, _ := schema.Parse(&tests.User{}, &sync.Map{}, db.NamingStrategy)\n\t\t\tstmt := &gorm.Statement{DB: db, Table: user.Table, Schema: user, Clauses: map[string]clause.Clause{}}\n\t\t\tclause.NamedExpr{SQL: result.SQL, Vars: result.Vars}.Build(stmt)\n\t\t\tif stmt.SQL.String() != result.Result {\n\t\t\t\tt.Errorf(\"generated SQL is not equal, expects %v, but got %v\", result.Result, stmt.SQL.String())\n\t\t\t}\n\n\t\t\tif !reflect.DeepEqual(result.ExpectedVars, stmt.Vars) {\n\t\t\t\tt.Errorf(\"generated vars is not equal, expects %v, but got %v\", result.ExpectedVars, stmt.Vars)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestExpression(t *testing.T) {\n\tcolumn := \"column-name\"\n\tresults := []struct {\n\t\tExpressions  []clause.Expression\n\t\tExpectedVars []interface{}\n\t\tResult       string\n\t}{{\n\t\tExpressions: []clause.Expression{\n\t\t\tclause.Eq{Column: column, Value: \"column-value\"},\n\t\t},\n\t\tExpectedVars: []interface{}{\"column-value\"},\n\t\tResult:       \"`column-name` = ?\",\n\t}, {\n\t\tExpressions: []clause.Expression{\n\t\t\tclause.Eq{Column: column, Value: nil},\n\t\t\tclause.Eq{Column: column, Value: (*string)(nil)},\n\t\t\tclause.Eq{Column: column, Value: (*int)(nil)},\n\t\t\tclause.Eq{Column: column, Value: (*bool)(nil)},\n\t\t\tclause.Eq{Column: column, Value: (interface{})(nil)},\n\t\t\tclause.Eq{Column: column, Value: sql.NullString{String: \"\", Valid: false}},\n\t\t},\n\t\tResult: \"`column-name` IS NULL\",\n\t}, {\n\t\tExpressions: []clause.Expression{\n\t\t\tclause.Neq{Column: column, Value: \"column-value\"},\n\t\t},\n\t\tExpectedVars: []interface{}{\"column-value\"},\n\t\tResult:       \"`column-name` <> ?\",\n\t}, {\n\t\tExpressions: []clause.Expression{\n\t\t\tclause.Neq{Column: column, Value: nil},\n\t\t\tclause.Neq{Column: column, Value: (*string)(nil)},\n\t\t\tclause.Neq{Column: column, Value: (*int)(nil)},\n\t\t\tclause.Neq{Column: column, Value: (*bool)(nil)},\n\t\t\tclause.Neq{Column: column, Value: (interface{})(nil)},\n\t\t},\n\t\tResult: \"`column-name` IS NOT NULL\",\n\t}, {\n\t\tExpressions: []clause.Expression{\n\t\t\tclause.Eq{Column: column, Value: []string{\"a\", \"b\"}},\n\t\t},\n\t\tExpectedVars: []interface{}{\"a\", \"b\"},\n\t\tResult:       \"`column-name` IN (?,?)\",\n\t}, {\n\t\tExpressions: []clause.Expression{\n\t\t\tclause.Neq{Column: column, Value: []string{\"a\", \"b\"}},\n\t\t},\n\t\tExpectedVars: []interface{}{\"a\", \"b\"},\n\t\tResult:       \"`column-name` NOT IN (?,?)\",\n\t}, {\n\t\tExpressions: []clause.Expression{\n\t\t\tclause.Eq{Column: column, Value: []string{}},\n\t\t},\n\t\tResult: \"`column-name` IN (NULL)\",\n\t}, {\n\t\tExpressions: []clause.Expression{\n\t\t\tclause.Eq{Column: clause.Expr{SQL: \"SUM(?)\", Vars: []interface{}{clause.Column{Name: \"id\"}}}, Value: 100},\n\t\t},\n\t\tExpectedVars: []interface{}{100},\n\t\tResult:       \"SUM(`id`) = ?\",\n\t}, {\n\t\tExpressions: []clause.Expression{\n\t\t\tclause.Gte{Column: clause.Expr{SQL: \"SUM(?)\", Vars: []interface{}{clause.Column{Table: \"users\", Name: \"id\"}}}, Value: 100},\n\t\t},\n\t\tExpectedVars: []interface{}{100},\n\t\tResult:       \"SUM(`users`.`id`) >= ?\",\n\t}}\n\n\tfor idx, result := range results {\n\t\tfor idy, expression := range result.Expressions {\n\t\t\tt.Run(fmt.Sprintf(\"case #%v.%v\", idx, idy), func(t *testing.T) {\n\t\t\t\tuser, _ := schema.Parse(&tests.User{}, &sync.Map{}, db.NamingStrategy)\n\t\t\t\tstmt := &gorm.Statement{DB: db, Table: user.Table, Schema: user, Clauses: map[string]clause.Clause{}}\n\t\t\t\texpression.Build(stmt)\n\t\t\t\tif stmt.SQL.String() != result.Result {\n\t\t\t\t\tt.Errorf(\"generated SQL is not equal, expects %v, but got %v\", result.Result, stmt.SQL.String())\n\t\t\t\t}\n\n\t\t\t\tif !reflect.DeepEqual(result.ExpectedVars, stmt.Vars) {\n\t\t\t\t\tt.Errorf(\"generated vars is not equal, expects %v, but got %v\", result.ExpectedVars, stmt.Vars)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "clause/from.go",
    "content": "package clause\n\n// From from clause\ntype From struct {\n\tTables []Table\n\tJoins  []Join\n}\n\n// Name from clause name\nfunc (from From) Name() string {\n\treturn \"FROM\"\n}\n\n// Build build from clause\nfunc (from From) Build(builder Builder) {\n\tif len(from.Tables) > 0 {\n\t\tfor idx, table := range from.Tables {\n\t\t\tif idx > 0 {\n\t\t\t\tbuilder.WriteByte(',')\n\t\t\t}\n\n\t\t\tbuilder.WriteQuoted(table)\n\t\t}\n\t} else {\n\t\tbuilder.WriteQuoted(currentTable)\n\t}\n\n\tfor _, join := range from.Joins {\n\t\tbuilder.WriteByte(' ')\n\t\tjoin.Build(builder)\n\t}\n}\n\n// MergeClause merge from clause\nfunc (from From) MergeClause(clause *Clause) {\n\tclause.Expression = from\n}\n"
  },
  {
    "path": "clause/from_test.go",
    "content": "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 := []struct {\n\t\tClauses []clause.Interface\n\t\tResult  string\n\t\tVars    []interface{}\n\t}{\n\t\t{\n\t\t\t[]clause.Interface{clause.Select{}, clause.From{}},\n\t\t\t\"SELECT * FROM `users`\", nil,\n\t\t},\n\t\t{\n\t\t\t[]clause.Interface{\n\t\t\t\tclause.Select{}, clause.From{\n\t\t\t\t\tTables: []clause.Table{{Name: \"users\"}},\n\t\t\t\t\tJoins: []clause.Join{\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tType:  clause.InnerJoin,\n\t\t\t\t\t\t\tTable: clause.Table{Name: \"articles\"},\n\t\t\t\t\t\t\tON: clause.Where{\n\t\t\t\t\t\t\t\t[]clause.Expression{clause.Eq{clause.Column{Table: \"articles\", Name: \"id\"}, clause.PrimaryColumn}},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\t\"SELECT * FROM `users` INNER JOIN `articles` ON `articles`.`id` = `users`.`id`\", nil,\n\t\t},\n\t\t{\n\t\t\t[]clause.Interface{\n\t\t\t\tclause.Select{}, clause.From{\n\t\t\t\t\tTables: []clause.Table{{Name: \"users\"}},\n\t\t\t\t\tJoins: []clause.Join{\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tType:  clause.RightJoin,\n\t\t\t\t\t\t\tTable: clause.Table{Name: \"profiles\"},\n\t\t\t\t\t\t\tON: clause.Where{\n\t\t\t\t\t\t\t\t[]clause.Expression{clause.Eq{clause.Column{Table: \"profiles\", Name: \"email\"}, clause.Column{Table: clause.CurrentTable, Name: \"email\"}}},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t}, clause.From{\n\t\t\t\t\tJoins: []clause.Join{\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tType:  clause.InnerJoin,\n\t\t\t\t\t\t\tTable: clause.Table{Name: \"articles\"},\n\t\t\t\t\t\t\tON: clause.Where{\n\t\t\t\t\t\t\t\t[]clause.Expression{clause.Eq{clause.Column{Table: \"articles\", Name: \"id\"}, clause.PrimaryColumn}},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}, {\n\t\t\t\t\t\t\tType:  clause.LeftJoin,\n\t\t\t\t\t\t\tTable: clause.Table{Name: \"companies\"},\n\t\t\t\t\t\t\tUsing: []string{\"company_name\"},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\t\"SELECT * FROM `users` INNER JOIN `articles` ON `articles`.`id` = `users`.`id` LEFT JOIN `companies` USING (`company_name`)\", nil,\n\t\t},\n\t}\n\n\tfor idx, result := range results {\n\t\tt.Run(fmt.Sprintf(\"case #%v\", idx), func(t *testing.T) {\n\t\t\tcheckBuildClauses(t, result.Clauses, result.Result, result.Vars)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "clause/group_by.go",
    "content": "package clause\n\n// GroupBy group by clause\ntype GroupBy struct {\n\tColumns []Column\n\tHaving  []Expression\n}\n\n// Name from clause name\nfunc (groupBy GroupBy) Name() string {\n\treturn \"GROUP BY\"\n}\n\n// Build build group by clause\nfunc (groupBy GroupBy) Build(builder Builder) {\n\tfor idx, column := range groupBy.Columns {\n\t\tif idx > 0 {\n\t\t\tbuilder.WriteByte(',')\n\t\t}\n\n\t\tbuilder.WriteQuoted(column)\n\t}\n\n\tif len(groupBy.Having) > 0 {\n\t\tbuilder.WriteString(\" HAVING \")\n\t\tWhere{Exprs: groupBy.Having}.Build(builder)\n\t}\n}\n\n// MergeClause merge group by clause\nfunc (groupBy GroupBy) MergeClause(clause *Clause) {\n\tif v, ok := clause.Expression.(GroupBy); ok {\n\t\tcopiedColumns := make([]Column, len(v.Columns))\n\t\tcopy(copiedColumns, v.Columns)\n\t\tgroupBy.Columns = append(copiedColumns, groupBy.Columns...)\n\n\t\tcopiedHaving := make([]Expression, len(v.Having))\n\t\tcopy(copiedHaving, v.Having)\n\t\tgroupBy.Having = append(copiedHaving, groupBy.Having...)\n\t}\n\tclause.Expression = groupBy\n\n\tif len(groupBy.Columns) == 0 {\n\t\tclause.Name = \"\"\n\t} else {\n\t\tclause.Name = groupBy.Name()\n\t}\n}\n"
  },
  {
    "path": "clause/group_by_test.go",
    "content": "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 := []struct {\n\t\tClauses []clause.Interface\n\t\tResult  string\n\t\tVars    []interface{}\n\t}{\n\t\t{\n\t\t\t[]clause.Interface{clause.Select{}, clause.From{}, clause.GroupBy{\n\t\t\t\tColumns: []clause.Column{{Name: \"role\"}},\n\t\t\t\tHaving:  []clause.Expression{clause.Eq{\"role\", \"admin\"}},\n\t\t\t}},\n\t\t\t\"SELECT * FROM `users` GROUP BY `role` HAVING `role` = ?\",\n\t\t\t[]interface{}{\"admin\"},\n\t\t},\n\t\t{\n\t\t\t[]clause.Interface{clause.Select{}, clause.From{}, clause.GroupBy{\n\t\t\t\tColumns: []clause.Column{{Name: \"role\"}},\n\t\t\t\tHaving:  []clause.Expression{clause.Eq{\"role\", \"admin\"}},\n\t\t\t}, clause.GroupBy{\n\t\t\t\tColumns: []clause.Column{{Name: \"gender\"}},\n\t\t\t\tHaving:  []clause.Expression{clause.Neq{\"gender\", \"U\"}},\n\t\t\t}},\n\t\t\t\"SELECT * FROM `users` GROUP BY `role`,`gender` HAVING `role` = ? AND `gender` <> ?\",\n\t\t\t[]interface{}{\"admin\", \"U\"},\n\t\t},\n\t}\n\n\tfor idx, result := range results {\n\t\tt.Run(fmt.Sprintf(\"case #%v\", idx), func(t *testing.T) {\n\t\t\tcheckBuildClauses(t, result.Clauses, result.Result, result.Vars)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "clause/insert.go",
    "content": "package clause\n\ntype Insert struct {\n\tTable    Table\n\tModifier string\n}\n\n// Name insert clause name\nfunc (insert Insert) Name() string {\n\treturn \"INSERT\"\n}\n\n// Build build insert clause\nfunc (insert Insert) Build(builder Builder) {\n\tif insert.Modifier != \"\" {\n\t\tbuilder.WriteString(insert.Modifier)\n\t\tbuilder.WriteByte(' ')\n\t}\n\n\tbuilder.WriteString(\"INTO \")\n\tif insert.Table.Name == \"\" {\n\t\tbuilder.WriteQuoted(currentTable)\n\t} else {\n\t\tbuilder.WriteQuoted(insert.Table)\n\t}\n}\n\n// MergeClause merge insert clause\nfunc (insert Insert) MergeClause(clause *Clause) {\n\tif v, ok := clause.Expression.(Insert); ok {\n\t\tif insert.Modifier == \"\" {\n\t\t\tinsert.Modifier = v.Modifier\n\t\t}\n\t\tif insert.Table.Name == \"\" {\n\t\t\tinsert.Table = v.Table\n\t\t}\n\t}\n\tclause.Expression = insert\n}\n"
  },
  {
    "path": "clause/insert_test.go",
    "content": "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 := []struct {\n\t\tClauses []clause.Interface\n\t\tResult  string\n\t\tVars    []interface{}\n\t}{\n\t\t{\n\t\t\t[]clause.Interface{clause.Insert{}},\n\t\t\t\"INSERT INTO `users`\", nil,\n\t\t},\n\t\t{\n\t\t\t[]clause.Interface{clause.Insert{Modifier: \"LOW_PRIORITY\"}},\n\t\t\t\"INSERT LOW_PRIORITY INTO `users`\", nil,\n\t\t},\n\t\t{\n\t\t\t[]clause.Interface{clause.Insert{Table: clause.Table{Name: \"products\"}, Modifier: \"LOW_PRIORITY\"}},\n\t\t\t\"INSERT LOW_PRIORITY INTO `products`\", nil,\n\t\t},\n\t}\n\n\tfor idx, result := range results {\n\t\tt.Run(fmt.Sprintf(\"case #%v\", idx), func(t *testing.T) {\n\t\t\tcheckBuildClauses(t, result.Clauses, result.Result, result.Vars)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "clause/joins.go",
    "content": "package clause\n\nimport \"gorm.io/gorm/utils\"\n\ntype JoinType string\n\nconst (\n\tCrossJoin JoinType = \"CROSS\"\n\tInnerJoin JoinType = \"INNER\"\n\tLeftJoin  JoinType = \"LEFT\"\n\tRightJoin JoinType = \"RIGHT\"\n)\n\ntype JoinTarget struct {\n\tType        JoinType\n\tAssociation string\n\tSubquery    Expression\n\tTable       string\n}\n\nfunc Has(name string) JoinTarget {\n\treturn JoinTarget{Type: InnerJoin, Association: name}\n}\n\nfunc (jt JoinType) Association(name string) JoinTarget {\n\treturn JoinTarget{Type: jt, Association: name}\n}\n\nfunc (jt JoinType) AssociationFrom(name string, subquery Expression) JoinTarget {\n\treturn JoinTarget{Type: jt, Association: name, Subquery: subquery}\n}\n\nfunc (jt JoinTarget) As(name string) JoinTarget {\n\tjt.Table = name\n\treturn jt\n}\n\n// Join clause for from\ntype Join struct {\n\tType       JoinType\n\tTable      Table\n\tON         Where\n\tUsing      []string\n\tExpression Expression\n}\n\nfunc JoinTable(names ...string) Table {\n\treturn Table{\n\t\tName: utils.JoinNestedRelationNames(names),\n\t}\n}\n\nfunc (join Join) Build(builder Builder) {\n\tif join.Expression != nil {\n\t\tjoin.Expression.Build(builder)\n\t} else {\n\t\tif join.Type != \"\" {\n\t\t\tbuilder.WriteString(string(join.Type))\n\t\t\tbuilder.WriteByte(' ')\n\t\t}\n\n\t\tbuilder.WriteString(\"JOIN \")\n\t\tbuilder.WriteQuoted(join.Table)\n\n\t\tif len(join.ON.Exprs) > 0 {\n\t\t\tbuilder.WriteString(\" ON \")\n\t\t\tjoin.ON.Build(builder)\n\t\t} else if len(join.Using) > 0 {\n\t\t\tbuilder.WriteString(\" USING (\")\n\t\t\tfor idx, c := range join.Using {\n\t\t\t\tif idx > 0 {\n\t\t\t\t\tbuilder.WriteByte(',')\n\t\t\t\t}\n\t\t\t\tbuilder.WriteQuoted(c)\n\t\t\t}\n\t\t\tbuilder.WriteByte(')')\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "clause/joins_test.go",
    "content": "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.io/gorm/utils/tests\"\n)\n\nfunc TestJoin(t *testing.T) {\n\tresults := []struct {\n\t\tname string\n\t\tjoin clause.Join\n\t\tsql  string\n\t}{\n\t\t{\n\t\t\tname: \"LEFT JOIN\",\n\t\t\tjoin: clause.Join{\n\t\t\t\tType:  clause.LeftJoin,\n\t\t\t\tTable: clause.Table{Name: \"user\"},\n\t\t\t\tON: clause.Where{\n\t\t\t\t\tExprs: []clause.Expression{clause.Eq{clause.Column{Table: \"user_info\", Name: \"user_id\"}, clause.PrimaryColumn}},\n\t\t\t\t},\n\t\t\t},\n\t\t\tsql: \"LEFT JOIN `user` ON `user_info`.`user_id` = `users`.`id`\",\n\t\t},\n\t\t{\n\t\t\tname: \"RIGHT JOIN\",\n\t\t\tjoin: clause.Join{\n\t\t\t\tType:  clause.RightJoin,\n\t\t\t\tTable: clause.Table{Name: \"user\"},\n\t\t\t\tON: clause.Where{\n\t\t\t\t\tExprs: []clause.Expression{clause.Eq{clause.Column{Table: \"user_info\", Name: \"user_id\"}, clause.PrimaryColumn}},\n\t\t\t\t},\n\t\t\t},\n\t\t\tsql: \"RIGHT JOIN `user` ON `user_info`.`user_id` = `users`.`id`\",\n\t\t},\n\t\t{\n\t\t\tname: \"INNER JOIN\",\n\t\t\tjoin: clause.Join{\n\t\t\t\tType:  clause.InnerJoin,\n\t\t\t\tTable: clause.Table{Name: \"user\"},\n\t\t\t\tON: clause.Where{\n\t\t\t\t\tExprs: []clause.Expression{clause.Eq{clause.Column{Table: \"user_info\", Name: \"user_id\"}, clause.PrimaryColumn}},\n\t\t\t\t},\n\t\t\t},\n\t\t\tsql: \"INNER JOIN `user` ON `user_info`.`user_id` = `users`.`id`\",\n\t\t},\n\t\t{\n\t\t\tname: \"CROSS JOIN\",\n\t\t\tjoin: clause.Join{\n\t\t\t\tType:  clause.CrossJoin,\n\t\t\t\tTable: clause.Table{Name: \"user\"},\n\t\t\t\tON: clause.Where{\n\t\t\t\t\tExprs: []clause.Expression{clause.Eq{clause.Column{Table: \"user_info\", Name: \"user_id\"}, clause.PrimaryColumn}},\n\t\t\t\t},\n\t\t\t},\n\t\t\tsql: \"CROSS JOIN `user` ON `user_info`.`user_id` = `users`.`id`\",\n\t\t},\n\t\t{\n\t\t\tname: \"USING\",\n\t\t\tjoin: clause.Join{\n\t\t\t\tType:  clause.InnerJoin,\n\t\t\t\tTable: clause.Table{Name: \"user\"},\n\t\t\t\tUsing: []string{\"id\"},\n\t\t\t},\n\t\t\tsql: \"INNER JOIN `user` USING (`id`)\",\n\t\t},\n\t\t{\n\t\t\tname: \"Expression\",\n\t\t\tjoin: clause.Join{\n\t\t\t\t// Invalid\n\t\t\t\tType:  clause.LeftJoin,\n\t\t\t\tTable: clause.Table{Name: \"user\"},\n\t\t\t\tON: clause.Where{\n\t\t\t\t\tExprs: []clause.Expression{clause.Eq{clause.Column{Table: \"user_info\", Name: \"user_id\"}, clause.PrimaryColumn}},\n\t\t\t\t},\n\t\t\t\t// Valid\n\t\t\t\tExpression: clause.Join{\n\t\t\t\t\tType:  clause.InnerJoin,\n\t\t\t\t\tTable: clause.Table{Name: \"user\"},\n\t\t\t\t\tUsing: []string{\"id\"},\n\t\t\t\t},\n\t\t\t},\n\t\t\tsql: \"INNER JOIN `user` USING (`id`)\",\n\t\t},\n\t}\n\tfor _, result := range results {\n\t\tt.Run(result.name, func(t *testing.T) {\n\t\t\tuser, _ := schema.Parse(&tests.User{}, &sync.Map{}, db.NamingStrategy)\n\t\t\tstmt := &gorm.Statement{DB: db, Table: user.Table, Schema: user, Clauses: map[string]clause.Clause{}}\n\t\t\tresult.join.Build(stmt)\n\t\t\tif result.sql != stmt.SQL.String() {\n\t\t\t\tt.Errorf(\"want: %s, got: %s\", result.sql, stmt.SQL.String())\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "clause/limit.go",
    "content": "package clause\n\n// Limit limit clause\ntype Limit struct {\n\tLimit  *int\n\tOffset int\n}\n\n// Name where clause name\nfunc (limit Limit) Name() string {\n\treturn \"LIMIT\"\n}\n\n// Build build where clause\nfunc (limit Limit) Build(builder Builder) {\n\tif limit.Limit != nil && *limit.Limit >= 0 {\n\t\tbuilder.WriteString(\"LIMIT \")\n\t\tbuilder.AddVar(builder, *limit.Limit)\n\t}\n\tif limit.Offset > 0 {\n\t\tif limit.Limit != nil && *limit.Limit >= 0 {\n\t\t\tbuilder.WriteByte(' ')\n\t\t}\n\t\tbuilder.WriteString(\"OFFSET \")\n\t\tbuilder.AddVar(builder, limit.Offset)\n\t}\n}\n\n// MergeClause merge order by clauses\nfunc (limit Limit) MergeClause(clause *Clause) {\n\tclause.Name = \"\"\n\n\tif v, ok := clause.Expression.(Limit); ok {\n\t\tif (limit.Limit == nil || *limit.Limit == 0) && v.Limit != nil {\n\t\t\tlimit.Limit = v.Limit\n\t\t}\n\n\t\tif limit.Offset == 0 && v.Offset > 0 {\n\t\t\tlimit.Offset = v.Offset\n\t\t} else if limit.Offset < 0 {\n\t\t\tlimit.Offset = 0\n\t\t}\n\t}\n\n\tclause.Expression = limit\n}\n"
  },
  {
    "path": "clause/limit_test.go",
    "content": "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\tlimit10 := 10\n\tlimit50 := 50\n\tlimitNeg10 := -10\n\tresults := []struct {\n\t\tClauses []clause.Interface\n\t\tResult  string\n\t\tVars    []interface{}\n\t}{\n\t\t{\n\t\t\t[]clause.Interface{clause.Select{}, clause.From{}, clause.Limit{\n\t\t\t\tLimit:  &limit10,\n\t\t\t\tOffset: 20,\n\t\t\t}},\n\t\t\t\"SELECT * FROM `users` LIMIT ? OFFSET ?\",\n\t\t\t[]interface{}{limit10, 20},\n\t\t},\n\t\t{\n\t\t\t[]clause.Interface{clause.Select{}, clause.From{}, clause.Limit{Limit: &limit0}},\n\t\t\t\"SELECT * FROM `users` LIMIT ?\",\n\t\t\t[]interface{}{limit0},\n\t\t},\n\t\t{\n\t\t\t[]clause.Interface{clause.Select{}, clause.From{}, clause.Limit{Limit: &limit0}, clause.Limit{Offset: 0}},\n\t\t\t\"SELECT * FROM `users` LIMIT ?\",\n\t\t\t[]interface{}{limit0},\n\t\t},\n\t\t{\n\t\t\t[]clause.Interface{clause.Select{}, clause.From{}, clause.Limit{Offset: 20}},\n\t\t\t\"SELECT * FROM `users` OFFSET ?\",\n\t\t\t[]interface{}{20},\n\t\t},\n\t\t{\n\t\t\t[]clause.Interface{clause.Select{}, clause.From{}, clause.Limit{Offset: 20}, clause.Limit{Offset: 30}},\n\t\t\t\"SELECT * FROM `users` OFFSET ?\",\n\t\t\t[]interface{}{30},\n\t\t},\n\t\t{\n\t\t\t[]clause.Interface{clause.Select{}, clause.From{}, clause.Limit{Offset: 20}, clause.Limit{Limit: &limit10}},\n\t\t\t\"SELECT * FROM `users` LIMIT ? OFFSET ?\",\n\t\t\t[]interface{}{limit10, 20},\n\t\t},\n\t\t{\n\t\t\t[]clause.Interface{clause.Select{}, clause.From{}, clause.Limit{Limit: &limit10, Offset: 20}, clause.Limit{Offset: 30}},\n\t\t\t\"SELECT * FROM `users` LIMIT ? OFFSET ?\",\n\t\t\t[]interface{}{limit10, 30},\n\t\t},\n\t\t{\n\t\t\t[]clause.Interface{clause.Select{}, clause.From{}, clause.Limit{Limit: &limit10, Offset: 20}, clause.Limit{Offset: 30}, clause.Limit{Offset: -10}},\n\t\t\t\"SELECT * FROM `users` LIMIT ?\",\n\t\t\t[]interface{}{limit10},\n\t\t},\n\t\t{\n\t\t\t[]clause.Interface{clause.Select{}, clause.From{}, clause.Limit{Limit: &limit10, Offset: 20}, clause.Limit{Offset: 30}, clause.Limit{Limit: &limitNeg10}},\n\t\t\t\"SELECT * FROM `users` OFFSET ?\",\n\t\t\t[]interface{}{30},\n\t\t},\n\t\t{\n\t\t\t[]clause.Interface{clause.Select{}, clause.From{}, clause.Limit{Limit: &limit10, Offset: 20}, clause.Limit{Offset: 30}, clause.Limit{Limit: &limit50}},\n\t\t\t\"SELECT * FROM `users` LIMIT ? OFFSET ?\",\n\t\t\t[]interface{}{limit50, 30},\n\t\t},\n\t}\n\n\tfor idx, result := range results {\n\t\tt.Run(fmt.Sprintf(\"case #%v\", idx), func(t *testing.T) {\n\t\t\tcheckBuildClauses(t, result.Clauses, result.Result, result.Vars)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "clause/locking.go",
    "content": "package clause\n\nconst (\n\tLockingStrengthUpdate    = \"UPDATE\"\n\tLockingStrengthShare     = \"SHARE\"\n\tLockingOptionsSkipLocked = \"SKIP LOCKED\"\n\tLockingOptionsNoWait     = \"NOWAIT\"\n)\n\ntype Locking struct {\n\tStrength string\n\tTable    Table\n\tOptions  string\n}\n\n// Name where clause name\nfunc (locking Locking) Name() string {\n\treturn \"FOR\"\n}\n\n// Build build where clause\nfunc (locking Locking) Build(builder Builder) {\n\tbuilder.WriteString(locking.Strength)\n\tif locking.Table.Name != \"\" {\n\t\tbuilder.WriteString(\" OF \")\n\t\tbuilder.WriteQuoted(locking.Table)\n\t}\n\n\tif locking.Options != \"\" {\n\t\tbuilder.WriteByte(' ')\n\t\tbuilder.WriteString(locking.Options)\n\t}\n}\n\n// MergeClause merge order by clauses\nfunc (locking Locking) MergeClause(clause *Clause) {\n\tclause.Expression = locking\n}\n"
  },
  {
    "path": "clause/locking_test.go",
    "content": "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 := []struct {\n\t\tClauses []clause.Interface\n\t\tResult  string\n\t\tVars    []interface{}\n\t}{\n\t\t{\n\t\t\t[]clause.Interface{clause.Select{}, clause.From{}, clause.Locking{Strength: clause.LockingStrengthUpdate}},\n\t\t\t\"SELECT * FROM `users` FOR UPDATE\", nil,\n\t\t},\n\t\t{\n\t\t\t[]clause.Interface{clause.Select{}, clause.From{}, clause.Locking{Strength: clause.LockingStrengthShare, Table: clause.Table{Name: clause.CurrentTable}}},\n\t\t\t\"SELECT * FROM `users` FOR SHARE OF `users`\", nil,\n\t\t},\n\t\t{\n\t\t\t[]clause.Interface{clause.Select{}, clause.From{}, clause.Locking{Strength: clause.LockingStrengthUpdate, Options: clause.LockingOptionsNoWait}},\n\t\t\t\"SELECT * FROM `users` FOR UPDATE NOWAIT\", nil,\n\t\t},\n\t\t{\n\t\t\t[]clause.Interface{clause.Select{}, clause.From{}, clause.Locking{Strength: clause.LockingStrengthUpdate, Options: clause.LockingOptionsSkipLocked}},\n\t\t\t\"SELECT * FROM `users` FOR UPDATE SKIP LOCKED\", nil,\n\t\t},\n\t}\n\n\tfor idx, result := range results {\n\t\tt.Run(fmt.Sprintf(\"case #%v\", idx), func(t *testing.T) {\n\t\t\tcheckBuildClauses(t, result.Clauses, result.Result, result.Vars)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "clause/on_conflict.go",
    "content": "package clause\n\ntype OnConflict struct {\n\tColumns      []Column\n\tWhere        Where\n\tTargetWhere  Where\n\tOnConstraint string\n\tDoNothing    bool\n\tDoUpdates    Set\n\tUpdateAll    bool\n}\n\nfunc (OnConflict) Name() string {\n\treturn \"ON CONFLICT\"\n}\n\n// Build build onConflict clause\nfunc (onConflict OnConflict) Build(builder Builder) {\n\tif onConflict.OnConstraint != \"\" {\n\t\tbuilder.WriteString(\"ON CONSTRAINT \")\n\t\tbuilder.WriteString(onConflict.OnConstraint)\n\t\tbuilder.WriteByte(' ')\n\t} else {\n\t\tif len(onConflict.Columns) > 0 {\n\t\t\tbuilder.WriteByte('(')\n\t\t\tfor idx, column := range onConflict.Columns {\n\t\t\t\tif idx > 0 {\n\t\t\t\t\tbuilder.WriteByte(',')\n\t\t\t\t}\n\t\t\t\tbuilder.WriteQuoted(column)\n\t\t\t}\n\t\t\tbuilder.WriteString(`) `)\n\t\t}\n\n\t\tif len(onConflict.TargetWhere.Exprs) > 0 {\n\t\t\tbuilder.WriteString(\" WHERE \")\n\t\t\tonConflict.TargetWhere.Build(builder)\n\t\t\tbuilder.WriteByte(' ')\n\t\t}\n\t}\n\n\tif onConflict.DoNothing {\n\t\tbuilder.WriteString(\"DO NOTHING\")\n\t} else {\n\t\tbuilder.WriteString(\"DO UPDATE SET \")\n\t\tonConflict.DoUpdates.Build(builder)\n\t}\n\n\tif len(onConflict.Where.Exprs) > 0 {\n\t\tbuilder.WriteString(\" WHERE \")\n\t\tonConflict.Where.Build(builder)\n\t\tbuilder.WriteByte(' ')\n\t}\n}\n\n// MergeClause merge onConflict clauses\nfunc (onConflict OnConflict) MergeClause(clause *Clause) {\n\tclause.Expression = onConflict\n}\n"
  },
  {
    "path": "clause/order_by.go",
    "content": "package clause\n\ntype OrderByColumn struct {\n\tColumn  Column\n\tDesc    bool\n\tReorder bool\n}\n\ntype OrderBy struct {\n\tColumns    []OrderByColumn\n\tExpression Expression\n}\n\n// Name where clause name\nfunc (orderBy OrderBy) Name() string {\n\treturn \"ORDER BY\"\n}\n\n// Build build where clause\nfunc (orderBy OrderBy) Build(builder Builder) {\n\tif orderBy.Expression != nil {\n\t\torderBy.Expression.Build(builder)\n\t} else {\n\t\tfor idx, column := range orderBy.Columns {\n\t\t\tif idx > 0 {\n\t\t\t\tbuilder.WriteByte(',')\n\t\t\t}\n\n\t\t\tbuilder.WriteQuoted(column.Column)\n\t\t\tif column.Desc {\n\t\t\t\tbuilder.WriteString(\" DESC\")\n\t\t\t}\n\t\t}\n\t}\n}\n\n// MergeClause merge order by clauses\nfunc (orderBy OrderBy) MergeClause(clause *Clause) {\n\tif v, ok := clause.Expression.(OrderBy); ok {\n\t\tfor i := len(orderBy.Columns) - 1; i >= 0; i-- {\n\t\t\tif orderBy.Columns[i].Reorder {\n\t\t\t\torderBy.Columns = orderBy.Columns[i:]\n\t\t\t\tclause.Expression = orderBy\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\tcopiedColumns := make([]OrderByColumn, len(v.Columns))\n\t\tcopy(copiedColumns, v.Columns)\n\t\torderBy.Columns = append(copiedColumns, orderBy.Columns...)\n\t}\n\n\tclause.Expression = orderBy\n}\n"
  },
  {
    "path": "clause/order_by_test.go",
    "content": "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 := []struct {\n\t\tClauses []clause.Interface\n\t\tResult  string\n\t\tVars    []interface{}\n\t}{\n\t\t{\n\t\t\t[]clause.Interface{clause.Select{}, clause.From{}, clause.OrderBy{\n\t\t\t\tColumns: []clause.OrderByColumn{{Column: clause.PrimaryColumn, Desc: true}},\n\t\t\t}},\n\t\t\t\"SELECT * FROM `users` ORDER BY `users`.`id` DESC\", nil,\n\t\t},\n\t\t{\n\t\t\t[]clause.Interface{\n\t\t\t\tclause.Select{}, clause.From{}, clause.OrderBy{\n\t\t\t\t\tColumns: []clause.OrderByColumn{{Column: clause.PrimaryColumn, Desc: true}},\n\t\t\t\t}, clause.OrderBy{\n\t\t\t\t\tColumns: []clause.OrderByColumn{{Column: clause.Column{Name: \"name\"}}},\n\t\t\t\t},\n\t\t\t},\n\t\t\t\"SELECT * FROM `users` ORDER BY `users`.`id` DESC,`name`\", nil,\n\t\t},\n\t\t{\n\t\t\t[]clause.Interface{\n\t\t\t\tclause.Select{}, clause.From{}, clause.OrderBy{\n\t\t\t\t\tColumns: []clause.OrderByColumn{{Column: clause.PrimaryColumn, Desc: true}},\n\t\t\t\t}, clause.OrderBy{\n\t\t\t\t\tColumns: []clause.OrderByColumn{{Column: clause.Column{Name: \"name\"}, Reorder: true}},\n\t\t\t\t},\n\t\t\t},\n\t\t\t\"SELECT * FROM `users` ORDER BY `name`\", nil,\n\t\t},\n\t\t{\n\t\t\t[]clause.Interface{\n\t\t\t\tclause.Select{}, clause.From{}, clause.OrderBy{\n\t\t\t\t\tExpression: clause.Expr{SQL: \"FIELD(id, ?)\", Vars: []interface{}{[]int{1, 2, 3}}, WithoutParentheses: true},\n\t\t\t\t},\n\t\t\t},\n\t\t\t\"SELECT * FROM `users` ORDER BY FIELD(id, ?,?,?)\",\n\t\t\t[]interface{}{1, 2, 3},\n\t\t},\n\t}\n\n\tfor idx, result := range results {\n\t\tt.Run(fmt.Sprintf(\"case #%v\", idx), func(t *testing.T) {\n\t\t\tcheckBuildClauses(t, result.Clauses, result.Result, result.Vars)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "clause/returning.go",
    "content": "package clause\n\ntype Returning struct {\n\tColumns []Column\n}\n\n// Name where clause name\nfunc (returning Returning) Name() string {\n\treturn \"RETURNING\"\n}\n\n// Build build where clause\nfunc (returning Returning) Build(builder Builder) {\n\tif len(returning.Columns) > 0 {\n\t\tfor idx, column := range returning.Columns {\n\t\t\tif idx > 0 {\n\t\t\t\tbuilder.WriteByte(',')\n\t\t\t}\n\n\t\t\tbuilder.WriteQuoted(column)\n\t\t}\n\t} else {\n\t\tbuilder.WriteByte('*')\n\t}\n}\n\n// MergeClause merge order by clauses\nfunc (returning Returning) MergeClause(clause *Clause) {\n\tif v, ok := clause.Expression.(Returning); ok && len(returning.Columns) > 0 {\n\t\tif v.Columns != nil {\n\t\t\treturning.Columns = append(v.Columns, returning.Columns...)\n\t\t} else {\n\t\t\treturning.Columns = nil\n\t\t}\n\t}\n\tclause.Expression = returning\n}\n"
  },
  {
    "path": "clause/returning_test.go",
    "content": "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 := []struct {\n\t\tClauses []clause.Interface\n\t\tResult  string\n\t\tVars    []interface{}\n\t}{\n\t\t{\n\t\t\t[]clause.Interface{clause.Select{}, clause.From{}, clause.Returning{\n\t\t\t\t[]clause.Column{clause.PrimaryColumn},\n\t\t\t}},\n\t\t\t\"SELECT * FROM `users` RETURNING `users`.`id`\", nil,\n\t\t}, {\n\t\t\t[]clause.Interface{clause.Select{}, clause.From{}, clause.Returning{\n\t\t\t\t[]clause.Column{clause.PrimaryColumn},\n\t\t\t}, clause.Returning{\n\t\t\t\t[]clause.Column{{Name: \"name\"}, {Name: \"age\"}},\n\t\t\t}},\n\t\t\t\"SELECT * FROM `users` RETURNING `users`.`id`,`name`,`age`\", nil,\n\t\t},\n\t\t{\n\t\t\t[]clause.Interface{clause.Select{}, clause.From{}, clause.Returning{\n\t\t\t\t[]clause.Column{clause.PrimaryColumn},\n\t\t\t}, clause.Returning{}, clause.Returning{\n\t\t\t\t[]clause.Column{{Name: \"name\"}, {Name: \"age\"}},\n\t\t\t}},\n\t\t\t\"SELECT * FROM `users` RETURNING *\", nil,\n\t\t},\n\t\t{\n\t\t\t[]clause.Interface{clause.Select{}, clause.From{}, clause.Returning{\n\t\t\t\t[]clause.Column{clause.PrimaryColumn},\n\t\t\t}, clause.Returning{\n\t\t\t\t[]clause.Column{{Name: \"name\"}, {Name: \"age\"}},\n\t\t\t}, clause.Returning{}},\n\t\t\t\"SELECT * FROM `users` RETURNING *\", nil,\n\t\t},\n\t}\n\n\tfor idx, result := range results {\n\t\tt.Run(fmt.Sprintf(\"case #%v\", idx), func(t *testing.T) {\n\t\t\tcheckBuildClauses(t, result.Clauses, result.Result, result.Vars)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "clause/select.go",
    "content": "package clause\n\n// Select select attrs when querying, updating, creating\ntype Select struct {\n\tDistinct   bool\n\tColumns    []Column\n\tExpression Expression\n}\n\nfunc (s Select) Name() string {\n\treturn \"SELECT\"\n}\n\nfunc (s Select) Build(builder Builder) {\n\tif len(s.Columns) > 0 {\n\t\tif s.Distinct {\n\t\t\tbuilder.WriteString(\"DISTINCT \")\n\t\t}\n\n\t\tfor idx, column := range s.Columns {\n\t\t\tif idx > 0 {\n\t\t\t\tbuilder.WriteByte(',')\n\t\t\t}\n\t\t\tbuilder.WriteQuoted(column)\n\t\t}\n\t} else {\n\t\tbuilder.WriteByte('*')\n\t}\n}\n\nfunc (s Select) MergeClause(clause *Clause) {\n\tif s.Expression != nil {\n\t\tif s.Distinct {\n\t\t\tif expr, ok := s.Expression.(Expr); ok {\n\t\t\t\texpr.SQL = \"DISTINCT \" + expr.SQL\n\t\t\t\tclause.Expression = expr\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\tclause.Expression = s.Expression\n\t} else {\n\t\tclause.Expression = s\n\t}\n}\n\n// CommaExpression represents a group of expressions separated by commas.\ntype CommaExpression struct {\n\tExprs []Expression\n}\n\nfunc (comma CommaExpression) Build(builder Builder) {\n\tfor idx, expr := range comma.Exprs {\n\t\tif idx > 0 {\n\t\t\t_, _ = builder.WriteString(\", \")\n\t\t}\n\t\texpr.Build(builder)\n\t}\n}\n"
  },
  {
    "path": "clause/select_test.go",
    "content": "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 := []struct {\n\t\tClauses []clause.Interface\n\t\tResult  string\n\t\tVars    []interface{}\n\t}{\n\t\t{\n\t\t\t[]clause.Interface{clause.Select{}, clause.From{}},\n\t\t\t\"SELECT * FROM `users`\", nil,\n\t\t},\n\t\t{\n\t\t\t[]clause.Interface{clause.Select{\n\t\t\t\tColumns: []clause.Column{clause.PrimaryColumn},\n\t\t\t}, clause.From{}},\n\t\t\t\"SELECT `users`.`id` FROM `users`\", nil,\n\t\t},\n\t\t{\n\t\t\t[]clause.Interface{clause.Select{\n\t\t\t\tColumns: []clause.Column{clause.PrimaryColumn},\n\t\t\t}, clause.Select{\n\t\t\t\tColumns: []clause.Column{{Name: \"name\"}},\n\t\t\t}, clause.From{}},\n\t\t\t\"SELECT `name` FROM `users`\", nil,\n\t\t},\n\t\t{\n\t\t\t[]clause.Interface{clause.Select{\n\t\t\t\tExpression: clause.CommaExpression{\n\t\t\t\t\tExprs: []clause.Expression{\n\t\t\t\t\t\tclause.NamedExpr{\"?\", []interface{}{clause.Column{Name: \"id\"}}},\n\t\t\t\t\t\tclause.NamedExpr{\"?\", []interface{}{clause.Column{Name: \"name\"}}},\n\t\t\t\t\t\tclause.NamedExpr{\"LENGTH(?)\", []interface{}{clause.Column{Name: \"mobile\"}}},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t}, clause.From{}},\n\t\t\t\"SELECT `id`, `name`, LENGTH(`mobile`) FROM `users`\", nil,\n\t\t},\n\t\t{\n\t\t\t[]clause.Interface{clause.Select{\n\t\t\t\tExpression: clause.CommaExpression{\n\t\t\t\t\tExprs: []clause.Expression{\n\t\t\t\t\t\tclause.Expr{\n\t\t\t\t\t\t\tSQL: \"? as name\",\n\t\t\t\t\t\t\tVars: []interface{}{\n\t\t\t\t\t\t\t\tclause.Eq{\n\t\t\t\t\t\t\t\t\tColumn: clause.Column{Name: \"age\"},\n\t\t\t\t\t\t\t\t\tValue:  18,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t}, clause.From{}},\n\t\t\t\"SELECT `age` = ? as name FROM `users`\",\n\t\t\t[]interface{}{18},\n\t\t},\n\t}\n\n\tfor idx, result := range results {\n\t\tt.Run(fmt.Sprintf(\"case #%v\", idx), func(t *testing.T) {\n\t\t\tcheckBuildClauses(t, result.Clauses, result.Result, result.Vars)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "clause/set.go",
    "content": "package clause\n\nimport \"sort\"\n\ntype Set []Assignment\n\ntype Assignment struct {\n\tColumn Column\n\tValue  interface{}\n}\n\n// Assigner assignments provider interface\ntype Assigner interface {\n\tAssignments() []Assignment\n}\n\nfunc (set Set) Name() string {\n\treturn \"SET\"\n}\n\nfunc (set Set) Build(builder Builder) {\n\tif len(set) > 0 {\n\t\tfor idx, assignment := range set {\n\t\t\tif idx > 0 {\n\t\t\t\tbuilder.WriteByte(',')\n\t\t\t}\n\t\t\tbuilder.WriteQuoted(assignment.Column)\n\t\t\tbuilder.WriteByte('=')\n\t\t\tbuilder.AddVar(builder, assignment.Value)\n\t\t}\n\t} else {\n\t\tbuilder.WriteQuoted(Column{Name: PrimaryKey})\n\t\tbuilder.WriteByte('=')\n\t\tbuilder.WriteQuoted(Column{Name: PrimaryKey})\n\t}\n}\n\n// MergeClause merge assignments clauses\nfunc (set Set) MergeClause(clause *Clause) {\n\tcopiedAssignments := make([]Assignment, len(set))\n\tcopy(copiedAssignments, set)\n\tclause.Expression = Set(copiedAssignments)\n}\n\n// Assignments implements Assigner for Set.\nfunc (set Set) Assignments() []Assignment { return []Assignment(set) }\n\nfunc Assignments(values map[string]interface{}) Set {\n\tkeys := make([]string, 0, len(values))\n\tfor key := range values {\n\t\tkeys = append(keys, key)\n\t}\n\tsort.Strings(keys)\n\n\tassignments := make([]Assignment, len(keys))\n\tfor idx, key := range keys {\n\t\tassignments[idx] = Assignment{Column: Column{Name: key}, Value: values[key]}\n\t}\n\treturn assignments\n}\n\nfunc AssignmentColumns(values []string) Set {\n\tassignments := make([]Assignment, len(values))\n\tfor idx, value := range values {\n\t\tassignments[idx] = Assignment{Column: Column{Name: value}, Value: Column{Table: \"excluded\", Name: value}}\n\t}\n\treturn assignments\n}\n\n// Assignments implements Assigner for a single Assignment.\nfunc (a Assignment) Assignments() []Assignment { return []Assignment{a} }\n"
  },
  {
    "path": "clause/set_test.go",
    "content": "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 that types implement clause.Assigner\nvar (\n\t_ clause.Assigner = clause.Assignment{}\n\t_ clause.Assigner = clause.Set{}\n)\n\nfunc TestSet(t *testing.T) {\n\tresults := []struct {\n\t\tClauses []clause.Interface\n\t\tResult  string\n\t\tVars    []interface{}\n\t}{\n\t\t{\n\t\t\t[]clause.Interface{\n\t\t\t\tclause.Update{},\n\t\t\t\tclause.Set([]clause.Assignment{{clause.PrimaryColumn, 1}}),\n\t\t\t},\n\t\t\t\"UPDATE `users` SET `users`.`id`=?\",\n\t\t\t[]interface{}{1},\n\t\t},\n\t\t{\n\t\t\t[]clause.Interface{\n\t\t\t\tclause.Update{},\n\t\t\t\tclause.Set([]clause.Assignment{{clause.PrimaryColumn, 1}}),\n\t\t\t\tclause.Set([]clause.Assignment{{clause.Column{Name: \"name\"}, \"jinzhu\"}}),\n\t\t\t},\n\t\t\t\"UPDATE `users` SET `name`=?\",\n\t\t\t[]interface{}{\"jinzhu\"},\n\t\t},\n\t}\n\n\tfor idx, result := range results {\n\t\tt.Run(fmt.Sprintf(\"case #%v\", idx), func(t *testing.T) {\n\t\t\tcheckBuildClauses(t, result.Clauses, result.Result, result.Vars)\n\t\t})\n\t}\n}\n\nfunc TestAssignments(t *testing.T) {\n\tset := clause.Assignments(map[string]interface{}{\n\t\t\"name\": \"jinzhu\",\n\t\t\"age\":  18,\n\t})\n\n\tassignments := []clause.Assignment(set)\n\n\tsort.Slice(assignments, func(i, j int) bool {\n\t\treturn strings.Compare(assignments[i].Column.Name, assignments[j].Column.Name) > 0\n\t})\n\n\tif len(assignments) != 2 || assignments[0].Column.Name != \"name\" || assignments[0].Value.(string) != \"jinzhu\" || assignments[1].Column.Name != \"age\" || assignments[1].Value.(int) != 18 {\n\t\tt.Errorf(\"invalid assignments, got %v\", assignments)\n\t}\n}\n"
  },
  {
    "path": "clause/update.go",
    "content": "package clause\n\ntype Update struct {\n\tModifier string\n\tTable    Table\n}\n\n// Name update clause name\nfunc (update Update) Name() string {\n\treturn \"UPDATE\"\n}\n\n// Build build update clause\nfunc (update Update) Build(builder Builder) {\n\tif update.Modifier != \"\" {\n\t\tbuilder.WriteString(update.Modifier)\n\t\tbuilder.WriteByte(' ')\n\t}\n\n\tif update.Table.Name == \"\" {\n\t\tbuilder.WriteQuoted(currentTable)\n\t} else {\n\t\tbuilder.WriteQuoted(update.Table)\n\t}\n}\n\n// MergeClause merge update clause\nfunc (update Update) MergeClause(clause *Clause) {\n\tif v, ok := clause.Expression.(Update); ok {\n\t\tif update.Modifier == \"\" {\n\t\t\tupdate.Modifier = v.Modifier\n\t\t}\n\t\tif update.Table.Name == \"\" {\n\t\t\tupdate.Table = v.Table\n\t\t}\n\t}\n\tclause.Expression = update\n}\n"
  },
  {
    "path": "clause/update_test.go",
    "content": "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 := []struct {\n\t\tClauses []clause.Interface\n\t\tResult  string\n\t\tVars    []interface{}\n\t}{\n\t\t{\n\t\t\t[]clause.Interface{clause.Update{}},\n\t\t\t\"UPDATE `users`\", nil,\n\t\t},\n\t\t{\n\t\t\t[]clause.Interface{clause.Update{Modifier: \"LOW_PRIORITY\"}},\n\t\t\t\"UPDATE LOW_PRIORITY `users`\", nil,\n\t\t},\n\t\t{\n\t\t\t[]clause.Interface{clause.Update{Table: clause.Table{Name: \"products\"}, Modifier: \"LOW_PRIORITY\"}},\n\t\t\t\"UPDATE LOW_PRIORITY `products`\", nil,\n\t\t},\n\t}\n\n\tfor idx, result := range results {\n\t\tt.Run(fmt.Sprintf(\"case #%v\", idx), func(t *testing.T) {\n\t\t\tcheckBuildClauses(t, result.Clauses, result.Result, result.Vars)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "clause/values.go",
    "content": "package clause\n\ntype Values struct {\n\tColumns []Column\n\tValues  [][]interface{}\n}\n\n// Name from clause name\nfunc (Values) Name() string {\n\treturn \"VALUES\"\n}\n\n// Build build from clause\nfunc (values Values) Build(builder Builder) {\n\tif len(values.Columns) > 0 {\n\t\tbuilder.WriteByte('(')\n\t\tfor idx, column := range values.Columns {\n\t\t\tif idx > 0 {\n\t\t\t\tbuilder.WriteByte(',')\n\t\t\t}\n\t\t\tbuilder.WriteQuoted(column)\n\t\t}\n\t\tbuilder.WriteByte(')')\n\n\t\tbuilder.WriteString(\" VALUES \")\n\n\t\tfor idx, value := range values.Values {\n\t\t\tif idx > 0 {\n\t\t\t\tbuilder.WriteByte(',')\n\t\t\t}\n\n\t\t\tbuilder.WriteByte('(')\n\t\t\tbuilder.AddVar(builder, value...)\n\t\t\tbuilder.WriteByte(')')\n\t\t}\n\t} else {\n\t\tbuilder.WriteString(\"DEFAULT VALUES\")\n\t}\n}\n\n// MergeClause merge values clauses\nfunc (values Values) MergeClause(clause *Clause) {\n\tclause.Name = \"\"\n\tclause.Expression = values\n}\n"
  },
  {
    "path": "clause/values_test.go",
    "content": "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 := []struct {\n\t\tClauses []clause.Interface\n\t\tResult  string\n\t\tVars    []interface{}\n\t}{\n\t\t{\n\t\t\t[]clause.Interface{\n\t\t\t\tclause.Insert{},\n\t\t\t\tclause.Values{\n\t\t\t\t\tColumns: []clause.Column{{Name: \"name\"}, {Name: \"age\"}},\n\t\t\t\t\tValues:  [][]interface{}{{\"jinzhu\", 18}, {\"josh\", 1}},\n\t\t\t\t},\n\t\t\t},\n\t\t\t\"INSERT INTO `users` (`name`,`age`) VALUES (?,?),(?,?)\",\n\t\t\t[]interface{}{\"jinzhu\", 18, \"josh\", 1},\n\t\t},\n\t}\n\n\tfor idx, result := range results {\n\t\tt.Run(fmt.Sprintf(\"case #%v\", idx), func(t *testing.T) {\n\t\t\tcheckBuildClauses(t, result.Clauses, result.Result, result.Vars)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "clause/where.go",
    "content": "package clause\n\nimport (\n\t\"strings\"\n)\n\nconst (\n\tAndWithSpace = \" AND \"\n\tOrWithSpace  = \" OR \"\n)\n\n// Where where clause\ntype Where struct {\n\tExprs []Expression\n}\n\n// Name where clause name\nfunc (where Where) Name() string {\n\treturn \"WHERE\"\n}\n\n// Build build where clause\nfunc (where Where) Build(builder Builder) {\n\tif len(where.Exprs) == 1 {\n\t\tif andCondition, ok := where.Exprs[0].(AndConditions); ok {\n\t\t\twhere.Exprs = andCondition.Exprs\n\t\t}\n\t}\n\n\t// Switch position if the first query expression is a single Or condition\n\tfor idx, expr := range where.Exprs {\n\t\tif v, ok := expr.(OrConditions); !ok || len(v.Exprs) > 1 {\n\t\t\tif idx != 0 {\n\t\t\t\twhere.Exprs[0], where.Exprs[idx] = where.Exprs[idx], where.Exprs[0]\n\t\t\t}\n\t\t\tbreak\n\t\t}\n\t}\n\n\tbuildExprs(where.Exprs, builder, AndWithSpace)\n}\n\nfunc buildExprs(exprs []Expression, builder Builder, joinCond string) {\n\twrapInParentheses := false\n\n\tfor idx, expr := range exprs {\n\t\tif idx > 0 {\n\t\t\tif v, ok := expr.(OrConditions); ok && len(v.Exprs) == 1 {\n\t\t\t\tbuilder.WriteString(OrWithSpace)\n\t\t\t} else {\n\t\t\t\tbuilder.WriteString(joinCond)\n\t\t\t}\n\t\t}\n\n\t\tif len(exprs) > 1 {\n\t\t\tswitch v := expr.(type) {\n\t\t\tcase OrConditions:\n\t\t\t\tif len(v.Exprs) == 1 {\n\t\t\t\t\tif e, ok := v.Exprs[0].(Expr); ok {\n\t\t\t\t\t\tsql := strings.ToUpper(e.SQL)\n\t\t\t\t\t\twrapInParentheses = strings.Contains(sql, AndWithSpace) || strings.Contains(sql, OrWithSpace)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\tcase AndConditions:\n\t\t\t\tif len(v.Exprs) == 1 {\n\t\t\t\t\tif e, ok := v.Exprs[0].(Expr); ok {\n\t\t\t\t\t\tsql := strings.ToUpper(e.SQL)\n\t\t\t\t\t\twrapInParentheses = strings.Contains(sql, AndWithSpace) || strings.Contains(sql, OrWithSpace)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\tcase Expr:\n\t\t\t\tsql := strings.ToUpper(v.SQL)\n\t\t\t\twrapInParentheses = strings.Contains(sql, AndWithSpace) || strings.Contains(sql, OrWithSpace)\n\t\t\tcase NamedExpr:\n\t\t\t\tsql := strings.ToUpper(v.SQL)\n\t\t\t\twrapInParentheses = strings.Contains(sql, AndWithSpace) || strings.Contains(sql, OrWithSpace)\n\t\t\t}\n\t\t}\n\n\t\tif wrapInParentheses {\n\t\t\tbuilder.WriteByte('(')\n\t\t\texpr.Build(builder)\n\t\t\tbuilder.WriteByte(')')\n\t\t\twrapInParentheses = false\n\t\t} else {\n\t\t\texpr.Build(builder)\n\t\t}\n\t}\n}\n\n// MergeClause merge where clauses\nfunc (where Where) MergeClause(clause *Clause) {\n\tif w, ok := clause.Expression.(Where); ok {\n\t\texprs := make([]Expression, len(w.Exprs)+len(where.Exprs))\n\t\tcopy(exprs, w.Exprs)\n\t\tcopy(exprs[len(w.Exprs):], where.Exprs)\n\t\twhere.Exprs = exprs\n\t}\n\n\tclause.Expression = where\n}\n\nfunc And(exprs ...Expression) Expression {\n\tif len(exprs) == 0 {\n\t\treturn nil\n\t}\n\n\tif len(exprs) == 1 {\n\t\tif _, ok := exprs[0].(OrConditions); !ok {\n\t\t\treturn exprs[0]\n\t\t}\n\t}\n\n\treturn AndConditions{Exprs: exprs}\n}\n\ntype AndConditions struct {\n\tExprs []Expression\n}\n\nfunc (and AndConditions) Build(builder Builder) {\n\tif len(and.Exprs) > 1 {\n\t\tbuilder.WriteByte('(')\n\t\tbuildExprs(and.Exprs, builder, AndWithSpace)\n\t\tbuilder.WriteByte(')')\n\t} else {\n\t\tbuildExprs(and.Exprs, builder, AndWithSpace)\n\t}\n}\n\nfunc Or(exprs ...Expression) Expression {\n\tif len(exprs) == 0 {\n\t\treturn nil\n\t}\n\treturn OrConditions{Exprs: exprs}\n}\n\ntype OrConditions struct {\n\tExprs []Expression\n}\n\nfunc (or OrConditions) Build(builder Builder) {\n\tif len(or.Exprs) > 1 {\n\t\tbuilder.WriteByte('(')\n\t\tbuildExprs(or.Exprs, builder, OrWithSpace)\n\t\tbuilder.WriteByte(')')\n\t} else {\n\t\tbuildExprs(or.Exprs, builder, OrWithSpace)\n\t}\n}\n\nfunc Not(exprs ...Expression) Expression {\n\tif len(exprs) == 0 {\n\t\treturn nil\n\t}\n\tif len(exprs) == 1 {\n\t\tif andCondition, ok := exprs[0].(AndConditions); ok {\n\t\t\texprs = andCondition.Exprs\n\t\t}\n\t}\n\treturn NotConditions{Exprs: exprs}\n}\n\ntype NotConditions struct {\n\tExprs []Expression\n}\n\nfunc (not NotConditions) Build(builder Builder) {\n\tanyNegationBuilder := false\n\tfor _, c := range not.Exprs {\n\t\tif _, ok := c.(NegationExpressionBuilder); ok {\n\t\t\tanyNegationBuilder = true\n\t\t\tbreak\n\t\t}\n\t}\n\n\tif anyNegationBuilder {\n\t\tif len(not.Exprs) > 1 {\n\t\t\tbuilder.WriteByte('(')\n\t\t}\n\n\t\tfor idx, c := range not.Exprs {\n\t\t\tif idx > 0 {\n\t\t\t\tbuilder.WriteString(AndWithSpace)\n\t\t\t}\n\n\t\t\tif negationBuilder, ok := c.(NegationExpressionBuilder); ok {\n\t\t\t\tnegationBuilder.NegationBuild(builder)\n\t\t\t} else {\n\t\t\t\tbuilder.WriteString(\"NOT \")\n\t\t\t\te, wrapInParentheses := c.(Expr)\n\t\t\t\tif wrapInParentheses {\n\t\t\t\t\tsql := strings.ToUpper(e.SQL)\n\t\t\t\t\tif wrapInParentheses = strings.Contains(sql, AndWithSpace) || strings.Contains(sql, OrWithSpace); wrapInParentheses {\n\t\t\t\t\t\tbuilder.WriteByte('(')\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tc.Build(builder)\n\n\t\t\t\tif wrapInParentheses {\n\t\t\t\t\tbuilder.WriteByte(')')\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif len(not.Exprs) > 1 {\n\t\t\tbuilder.WriteByte(')')\n\t\t}\n\t} else {\n\t\tbuilder.WriteString(\"NOT \")\n\t\tif len(not.Exprs) > 1 {\n\t\t\tbuilder.WriteByte('(')\n\t\t}\n\n\t\tfor idx, c := range not.Exprs {\n\t\t\tif idx > 0 {\n\t\t\t\tswitch c.(type) {\n\t\t\t\tcase OrConditions:\n\t\t\t\t\tbuilder.WriteString(OrWithSpace)\n\t\t\t\tdefault:\n\t\t\t\t\tbuilder.WriteString(AndWithSpace)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\te, wrapInParentheses := c.(Expr)\n\t\t\tif wrapInParentheses {\n\t\t\t\tsql := strings.ToUpper(e.SQL)\n\t\t\t\tif wrapInParentheses = strings.Contains(sql, AndWithSpace) || strings.Contains(sql, OrWithSpace); wrapInParentheses {\n\t\t\t\t\tbuilder.WriteByte('(')\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tc.Build(builder)\n\n\t\t\tif wrapInParentheses {\n\t\t\t\tbuilder.WriteByte(')')\n\t\t\t}\n\t\t}\n\n\t\tif len(not.Exprs) > 1 {\n\t\t\tbuilder.WriteByte(')')\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "clause/where_test.go",
    "content": "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 := []struct {\n\t\tClauses []clause.Interface\n\t\tResult  string\n\t\tVars    []interface{}\n\t}{\n\t\t{\n\t\t\t[]clause.Interface{clause.Select{}, clause.From{}, clause.Where{\n\t\t\t\tExprs: []clause.Expression{clause.Eq{Column: clause.PrimaryColumn, Value: \"1\"}, clause.Gt{Column: \"age\", Value: 18}, clause.Or(clause.Neq{Column: \"name\", Value: \"jinzhu\"})},\n\t\t\t}},\n\t\t\t\"SELECT * FROM `users` WHERE `users`.`id` = ? AND `age` > ? OR `name` <> ?\",\n\t\t\t[]interface{}{\"1\", 18, \"jinzhu\"},\n\t\t},\n\t\t{\n\t\t\t[]clause.Interface{clause.Select{}, clause.From{}, clause.Where{\n\t\t\t\tExprs: []clause.Expression{clause.Or(clause.Neq{Column: \"name\", Value: \"jinzhu\"}), clause.Eq{Column: clause.PrimaryColumn, Value: \"1\"}, clause.Gt{Column: \"age\", Value: 18}},\n\t\t\t}},\n\t\t\t\"SELECT * FROM `users` WHERE `users`.`id` = ? OR `name` <> ? AND `age` > ?\",\n\t\t\t[]interface{}{\"1\", \"jinzhu\", 18},\n\t\t},\n\t\t{\n\t\t\t[]clause.Interface{clause.Select{}, clause.From{}, clause.Where{\n\t\t\t\tExprs: []clause.Expression{clause.Or(clause.Neq{Column: \"name\", Value: \"jinzhu\"}), clause.Eq{Column: clause.PrimaryColumn, Value: \"1\"}, clause.Gt{Column: \"age\", Value: 18}},\n\t\t\t}},\n\t\t\t\"SELECT * FROM `users` WHERE `users`.`id` = ? OR `name` <> ? AND `age` > ?\",\n\t\t\t[]interface{}{\"1\", \"jinzhu\", 18},\n\t\t},\n\t\t{\n\t\t\t[]clause.Interface{clause.Select{}, clause.From{}, clause.Where{\n\t\t\t\tExprs: []clause.Expression{clause.Or(clause.Eq{Column: clause.PrimaryColumn, Value: \"1\"}), clause.Or(clause.Neq{Column: \"name\", Value: \"jinzhu\"})},\n\t\t\t}},\n\t\t\t\"SELECT * FROM `users` WHERE `users`.`id` = ? OR `name` <> ?\",\n\t\t\t[]interface{}{\"1\", \"jinzhu\"},\n\t\t},\n\t\t{\n\t\t\t[]clause.Interface{clause.Select{}, clause.From{}, clause.Where{\n\t\t\t\tExprs: []clause.Expression{clause.Eq{Column: clause.PrimaryColumn, Value: \"1\"}, clause.Gt{Column: \"age\", Value: 18}, clause.Or(clause.Neq{Column: \"name\", Value: \"jinzhu\"})},\n\t\t\t}, clause.Where{\n\t\t\t\tExprs: []clause.Expression{clause.Or(clause.Gt{Column: \"score\", Value: 100}, clause.Like{Column: \"name\", Value: \"%linus%\"})},\n\t\t\t}},\n\t\t\t\"SELECT * FROM `users` WHERE `users`.`id` = ? AND `age` > ? OR `name` <> ? AND (`score` > ? OR `name` LIKE ?)\",\n\t\t\t[]interface{}{\"1\", 18, \"jinzhu\", 100, \"%linus%\"},\n\t\t},\n\t\t{\n\t\t\t[]clause.Interface{clause.Select{}, clause.From{}, clause.Where{\n\t\t\t\tExprs: []clause.Expression{clause.Not(clause.Eq{Column: clause.PrimaryColumn, Value: \"1\"}, clause.Gt{Column: \"age\", Value: 18}), clause.Or(clause.Neq{Column: \"name\", Value: \"jinzhu\"})},\n\t\t\t}, clause.Where{\n\t\t\t\tExprs: []clause.Expression{clause.Or(clause.Not(clause.Gt{Column: \"score\", Value: 100}), clause.Like{Column: \"name\", Value: \"%linus%\"})},\n\t\t\t}},\n\t\t\t\"SELECT * FROM `users` WHERE (`users`.`id` <> ? AND `age` <= ?) OR `name` <> ? AND (`score` <= ? OR `name` LIKE ?)\",\n\t\t\t[]interface{}{\"1\", 18, \"jinzhu\", 100, \"%linus%\"},\n\t\t},\n\t\t{\n\t\t\t[]clause.Interface{clause.Select{}, clause.From{}, clause.Where{\n\t\t\t\tExprs: []clause.Expression{clause.And(clause.Eq{Column: \"age\", Value: 18}, clause.Or(clause.Neq{Column: \"name\", Value: \"jinzhu\"}))},\n\t\t\t}},\n\t\t\t\"SELECT * FROM `users` WHERE `age` = ? OR `name` <> ?\",\n\t\t\t[]interface{}{18, \"jinzhu\"},\n\t\t},\n\t\t{\n\t\t\t[]clause.Interface{clause.Select{}, clause.From{}, clause.Where{\n\t\t\t\tExprs: []clause.Expression{clause.Not(clause.Eq{Column: clause.PrimaryColumn, Value: \"1\"}, clause.Gt{Column: \"age\", Value: 18}), clause.And(clause.Expr{SQL: \"`score` <= ?\", Vars: []interface{}{100}, WithoutParentheses: false})},\n\t\t\t}},\n\t\t\t\"SELECT * FROM `users` WHERE (`users`.`id` <> ? AND `age` <= ?) AND `score` <= ?\",\n\t\t\t[]interface{}{\"1\", 18, 100},\n\t\t},\n\t\t{\n\t\t\t[]clause.Interface{clause.Select{}, clause.From{}, clause.Where{\n\t\t\t\tExprs: []clause.Expression{clause.Not(clause.Eq{Column: clause.PrimaryColumn, Value: \"1\"}, clause.Gt{Column: \"age\", Value: 18}), clause.Expr{SQL: \"`score` <= ?\", Vars: []interface{}{100}, WithoutParentheses: false}},\n\t\t\t}},\n\t\t\t\"SELECT * FROM `users` WHERE (`users`.`id` <> ? AND `age` <= ?) AND `score` <= ?\",\n\t\t\t[]interface{}{\"1\", 18, 100},\n\t\t},\n\t\t{\n\t\t\t[]clause.Interface{clause.Select{}, clause.From{}, clause.Where{\n\t\t\t\tExprs: []clause.Expression{clause.Not(clause.Eq{Column: clause.PrimaryColumn, Value: \"1\"}, clause.Gt{Column: \"age\", Value: 18}), clause.Or(clause.Expr{SQL: \"`score` <= ?\", Vars: []interface{}{100}, WithoutParentheses: false})},\n\t\t\t}},\n\t\t\t\"SELECT * FROM `users` WHERE (`users`.`id` <> ? AND `age` <= ?) OR `score` <= ?\",\n\t\t\t[]interface{}{\"1\", 18, 100},\n\t\t},\n\t\t{\n\t\t\t[]clause.Interface{clause.Select{}, clause.From{}, clause.Where{\n\t\t\t\tExprs: []clause.Expression{\n\t\t\t\t\tclause.And(clause.Not(clause.Eq{Column: clause.PrimaryColumn, Value: \"1\"}),\n\t\t\t\t\t\tclause.And(clause.Expr{SQL: \"`score` <= ?\", Vars: []interface{}{100}, WithoutParentheses: false})),\n\t\t\t\t},\n\t\t\t}},\n\t\t\t\"SELECT * FROM `users` WHERE `users`.`id` <> ? AND `score` <= ?\",\n\t\t\t[]interface{}{\"1\", 100},\n\t\t},\n\t\t{\n\t\t\t[]clause.Interface{clause.Select{}, clause.From{}, clause.Where{\n\t\t\t\tExprs: []clause.Expression{clause.Not(clause.Eq{Column: clause.PrimaryColumn, Value: \"1\"},\n\t\t\t\t\tclause.And(clause.Expr{SQL: \"`score` <= ?\", Vars: []interface{}{100}, WithoutParentheses: false}))},\n\t\t\t}},\n\t\t\t\"SELECT * FROM `users` WHERE (`users`.`id` <> ? AND NOT `score` <= ?)\",\n\t\t\t[]interface{}{\"1\", 100},\n\t\t},\n\t\t{\n\t\t\t[]clause.Interface{clause.Select{}, clause.From{}, clause.Where{\n\t\t\t\tExprs: []clause.Expression{clause.Not(clause.Expr{SQL: \"`score` <= ?\", Vars: []interface{}{100}},\n\t\t\t\t\tclause.Expr{SQL: \"`age` <= ?\", Vars: []interface{}{60}})},\n\t\t\t}},\n\t\t\t\"SELECT * FROM `users` WHERE NOT (`score` <= ? AND `age` <= ?)\",\n\t\t\t[]interface{}{100, 60},\n\t\t},\n\t\t{\n\t\t\t[]clause.Interface{clause.Select{}, clause.From{}, clause.Where{\n\t\t\t\tExprs: []clause.Expression{\n\t\t\t\t\tclause.Not(clause.AndConditions{\n\t\t\t\t\t\tExprs: []clause.Expression{\n\t\t\t\t\t\t\tclause.Eq{Column: clause.PrimaryColumn, Value: \"1\"},\n\t\t\t\t\t\t\tclause.Gt{Column: \"age\", Value: 18},\n\t\t\t\t\t\t}}, clause.OrConditions{\n\t\t\t\t\t\tExprs: []clause.Expression{\n\t\t\t\t\t\t\tclause.Lt{Column: \"score\", Value: 100},\n\t\t\t\t\t\t},\n\t\t\t\t\t}),\n\t\t\t\t}}},\n\t\t\t\"SELECT * FROM `users` WHERE NOT ((`users`.`id` = ? AND `age` > ?) OR `score` < ?)\",\n\t\t\t[]interface{}{\"1\", 18, 100},\n\t\t},\n\t}\n\n\tfor idx, result := range results {\n\t\tt.Run(fmt.Sprintf(\"case #%v\", idx), func(t *testing.T) {\n\t\t\tcheckBuildClauses(t, result.Clauses, result.Result, result.Vars)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "clause/with.go",
    "content": "package clause\n\ntype With struct{}\n"
  },
  {
    "path": "errors.go",
    "content": "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\tErrRecordNotFound = logger.ErrRecordNotFound\n\t// ErrInvalidTransaction invalid transaction when you are trying to `Commit` or `Rollback`\n\tErrInvalidTransaction = errors.New(\"invalid transaction\")\n\t// ErrNotImplemented not implemented\n\tErrNotImplemented = errors.New(\"not implemented\")\n\t// ErrMissingWhereClause missing where clause\n\tErrMissingWhereClause = errors.New(\"WHERE conditions required\")\n\t// ErrUnsupportedRelation unsupported relations\n\tErrUnsupportedRelation = errors.New(\"unsupported relations\")\n\t// ErrPrimaryKeyRequired primary keys required\n\tErrPrimaryKeyRequired = errors.New(\"primary key required\")\n\t// ErrModelValueRequired model value required\n\tErrModelValueRequired = errors.New(\"model value required\")\n\t// ErrModelAccessibleFieldsRequired model accessible fields required\n\tErrModelAccessibleFieldsRequired = errors.New(\"model accessible fields required\")\n\t// ErrSubQueryRequired sub query required\n\tErrSubQueryRequired = errors.New(\"sub query required\")\n\t// ErrInvalidData unsupported data\n\tErrInvalidData = errors.New(\"unsupported data\")\n\t// ErrUnsupportedDriver unsupported driver\n\tErrUnsupportedDriver = errors.New(\"unsupported driver\")\n\t// ErrRegistered registered\n\tErrRegistered = errors.New(\"registered\")\n\t// ErrInvalidField invalid field\n\tErrInvalidField = errors.New(\"invalid field\")\n\t// ErrEmptySlice empty slice found\n\tErrEmptySlice = errors.New(\"empty slice found\")\n\t// ErrDryRunModeUnsupported dry run mode unsupported\n\tErrDryRunModeUnsupported = errors.New(\"dry run mode unsupported\")\n\t// ErrInvalidDB invalid db\n\tErrInvalidDB = errors.New(\"invalid db\")\n\t// ErrInvalidValue invalid value\n\tErrInvalidValue = errors.New(\"invalid value, should be pointer to struct or slice\")\n\t// ErrInvalidValueOfLength invalid values do not match length\n\tErrInvalidValueOfLength = errors.New(\"invalid association values, length doesn't match\")\n\t// ErrPreloadNotAllowed preload is not allowed when count is used\n\tErrPreloadNotAllowed = errors.New(\"preload is not allowed when count is used\")\n\t// ErrDuplicatedKey occurs when there is a unique key constraint violation\n\tErrDuplicatedKey = errors.New(\"duplicated key not allowed\")\n\t// ErrForeignKeyViolated occurs when there is a foreign key constraint violation\n\tErrForeignKeyViolated = errors.New(\"violates foreign key constraint\")\n\t// ErrCheckConstraintViolated occurs when there is a check constraint violation\n\tErrCheckConstraintViolated = errors.New(\"violates check constraint\")\n)\n"
  },
  {
    "path": "finisher_api.go",
    "content": "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/clause\"\n\t\"gorm.io/gorm/logger\"\n\t\"gorm.io/gorm/schema\"\n\t\"gorm.io/gorm/utils\"\n)\n\n// Create inserts value, returning the inserted data's primary key in value's id\nfunc (db *DB) Create(value interface{}) (tx *DB) {\n\tif db.CreateBatchSize > 0 {\n\t\treturn db.CreateInBatches(value, db.CreateBatchSize)\n\t}\n\n\ttx = db.getInstance()\n\ttx.Statement.Dest = value\n\treturn tx.callbacks.Create().Execute(tx)\n}\n\n// CreateInBatches inserts value in batches of batchSize\nfunc (db *DB) CreateInBatches(value interface{}, batchSize int) (tx *DB) {\n\treflectValue := reflect.Indirect(reflect.ValueOf(value))\n\n\tswitch reflectValue.Kind() {\n\tcase reflect.Slice, reflect.Array:\n\t\tvar rowsAffected int64\n\t\ttx = db.getInstance()\n\n\t\t// the reflection length judgment of the optimized value\n\t\treflectLen := reflectValue.Len()\n\n\t\tcallFc := func(tx *DB) error {\n\t\t\tfor i := 0; i < reflectLen; i += batchSize {\n\t\t\t\tends := i + batchSize\n\t\t\t\tif ends > reflectLen {\n\t\t\t\t\tends = reflectLen\n\t\t\t\t}\n\n\t\t\t\tsubtx := tx.getInstance()\n\t\t\t\tsubtx.Statement.Dest = reflectValue.Slice(i, ends).Interface()\n\t\t\t\tsubtx.callbacks.Create().Execute(subtx)\n\t\t\t\tif subtx.Error != nil {\n\t\t\t\t\treturn subtx.Error\n\t\t\t\t}\n\t\t\t\trowsAffected += subtx.RowsAffected\n\t\t\t}\n\t\t\treturn nil\n\t\t}\n\n\t\tif tx.SkipDefaultTransaction || reflectLen <= batchSize {\n\t\t\ttx.AddError(callFc(tx.Session(&Session{})))\n\t\t} else {\n\t\t\ttx.AddError(tx.Transaction(callFc))\n\t\t}\n\n\t\ttx.RowsAffected = rowsAffected\n\tdefault:\n\t\ttx = db.getInstance()\n\t\ttx.Statement.Dest = value\n\t\ttx = tx.callbacks.Create().Execute(tx)\n\t}\n\treturn\n}\n\n// Save updates value in database. If value doesn't contain a matching primary key, value is inserted.\nfunc (db *DB) Save(value interface{}) (tx *DB) {\n\ttx = db.getInstance()\n\ttx.Statement.Dest = value\n\n\treflectValue := reflect.Indirect(reflect.ValueOf(value))\n\tfor reflectValue.Kind() == reflect.Ptr || reflectValue.Kind() == reflect.Interface {\n\t\treflectValue = reflect.Indirect(reflectValue)\n\t}\n\n\tswitch reflectValue.Kind() {\n\tcase reflect.Slice, reflect.Array:\n\t\tif _, ok := tx.Statement.Clauses[\"ON CONFLICT\"]; !ok {\n\t\t\ttx = tx.Clauses(clause.OnConflict{UpdateAll: true})\n\t\t}\n\t\ttx = tx.callbacks.Create().Execute(tx.Set(\"gorm:update_track_time\", true))\n\tcase reflect.Struct:\n\t\tif err := tx.Statement.Parse(value); err == nil && tx.Statement.Schema != nil {\n\t\t\tfor _, pf := range tx.Statement.Schema.PrimaryFields {\n\t\t\t\tif _, isZero := pf.ValueOf(tx.Statement.Context, reflectValue); isZero {\n\t\t\t\t\treturn tx.callbacks.Create().Execute(tx)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tfallthrough\n\tdefault:\n\t\tselectedUpdate := len(tx.Statement.Selects) != 0\n\t\t// when updating, use all fields including those zero-value fields\n\t\tif !selectedUpdate {\n\t\t\ttx.Statement.Selects = append(tx.Statement.Selects, \"*\")\n\t\t}\n\n\t\tupdateTx := tx.callbacks.Update().Execute(tx.Session(&Session{Initialized: true}))\n\n\t\tif updateTx.Error == nil && updateTx.RowsAffected == 0 && !updateTx.DryRun && !selectedUpdate {\n\t\t\treturn tx.Session(&Session{SkipHooks: true}).Clauses(clause.OnConflict{UpdateAll: true}).Create(value)\n\t\t}\n\n\t\treturn updateTx\n\t}\n\n\treturn\n}\n\n// First finds the first record ordered by primary key, matching given conditions conds\nfunc (db *DB) First(dest interface{}, conds ...interface{}) (tx *DB) {\n\ttx = db.Limit(1).Order(clause.OrderByColumn{\n\t\tColumn: clause.Column{Table: clause.CurrentTable, Name: clause.PrimaryKey},\n\t})\n\tif len(conds) > 0 {\n\t\tif exprs := tx.Statement.BuildCondition(conds[0], conds[1:]...); len(exprs) > 0 {\n\t\t\ttx.Statement.AddClause(clause.Where{Exprs: exprs})\n\t\t}\n\t}\n\ttx.Statement.RaiseErrorOnNotFound = true\n\ttx.Statement.Dest = dest\n\treturn tx.callbacks.Query().Execute(tx)\n}\n\n// Take finds the first record returned by the database in no specified order, matching given conditions conds\nfunc (db *DB) Take(dest interface{}, conds ...interface{}) (tx *DB) {\n\ttx = db.Limit(1)\n\tif len(conds) > 0 {\n\t\tif exprs := tx.Statement.BuildCondition(conds[0], conds[1:]...); len(exprs) > 0 {\n\t\t\ttx.Statement.AddClause(clause.Where{Exprs: exprs})\n\t\t}\n\t}\n\ttx.Statement.RaiseErrorOnNotFound = true\n\ttx.Statement.Dest = dest\n\treturn tx.callbacks.Query().Execute(tx)\n}\n\n// Last finds the last record ordered by primary key, matching given conditions conds\nfunc (db *DB) Last(dest interface{}, conds ...interface{}) (tx *DB) {\n\ttx = db.Limit(1).Order(clause.OrderByColumn{\n\t\tColumn: clause.Column{Table: clause.CurrentTable, Name: clause.PrimaryKey},\n\t\tDesc:   true,\n\t})\n\tif len(conds) > 0 {\n\t\tif exprs := tx.Statement.BuildCondition(conds[0], conds[1:]...); len(exprs) > 0 {\n\t\t\ttx.Statement.AddClause(clause.Where{Exprs: exprs})\n\t\t}\n\t}\n\ttx.Statement.RaiseErrorOnNotFound = true\n\ttx.Statement.Dest = dest\n\treturn tx.callbacks.Query().Execute(tx)\n}\n\n// Find finds all records matching given conditions conds\nfunc (db *DB) Find(dest interface{}, conds ...interface{}) (tx *DB) {\n\ttx = db.getInstance()\n\tif len(conds) > 0 {\n\t\tif exprs := tx.Statement.BuildCondition(conds[0], conds[1:]...); len(exprs) > 0 {\n\t\t\ttx.Statement.AddClause(clause.Where{Exprs: exprs})\n\t\t}\n\t}\n\ttx.Statement.Dest = dest\n\treturn tx.callbacks.Query().Execute(tx)\n}\n\n// FindInBatches finds all records in batches of batchSize\nfunc (db *DB) FindInBatches(dest interface{}, batchSize int, fc func(tx *DB, batch int) error) *DB {\n\tvar (\n\t\ttx = db.Order(clause.OrderByColumn{\n\t\t\tColumn: clause.Column{Table: clause.CurrentTable, Name: clause.PrimaryKey},\n\t\t}).Session(&Session{})\n\t\tqueryDB      = tx\n\t\trowsAffected int64\n\t\tbatch        int\n\t)\n\n\t// user specified offset or limit\n\tvar totalSize int\n\tif c, ok := tx.Statement.Clauses[\"LIMIT\"]; ok {\n\t\tif limit, ok := c.Expression.(clause.Limit); ok {\n\t\t\tif limit.Limit != nil {\n\t\t\t\ttotalSize = *limit.Limit\n\t\t\t}\n\n\t\t\tif totalSize > 0 && batchSize > totalSize {\n\t\t\t\tbatchSize = totalSize\n\t\t\t}\n\n\t\t\t// reset to offset to 0 in next batch\n\t\t\ttx = tx.Offset(-1).Session(&Session{})\n\t\t}\n\t}\n\n\tfor {\n\t\tresult := queryDB.Limit(batchSize).Find(dest)\n\t\trowsAffected += result.RowsAffected\n\t\tbatch++\n\n\t\tif result.Error == nil && result.RowsAffected != 0 {\n\t\t\tfcTx := result.Session(&Session{NewDB: true})\n\t\t\tfcTx.RowsAffected = result.RowsAffected\n\t\t\ttx.AddError(fc(fcTx, batch))\n\t\t} else if result.Error != nil {\n\t\t\ttx.AddError(result.Error)\n\t\t}\n\n\t\tif tx.Error != nil || int(result.RowsAffected) < batchSize {\n\t\t\tbreak\n\t\t}\n\n\t\tif totalSize > 0 {\n\t\t\tif totalSize <= int(rowsAffected) {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tif totalSize/batchSize == batch {\n\t\t\t\tbatchSize = totalSize % batchSize\n\t\t\t}\n\t\t}\n\n\t\t// Optimize for-break\n\t\tresultsValue := reflect.Indirect(reflect.ValueOf(dest))\n\t\tif result.Statement.Schema.PrioritizedPrimaryField == nil {\n\t\t\ttx.AddError(ErrPrimaryKeyRequired)\n\t\t\tbreak\n\t\t}\n\n\t\tprimaryValue, zero := result.Statement.Schema.PrioritizedPrimaryField.ValueOf(tx.Statement.Context, resultsValue.Index(resultsValue.Len()-1))\n\t\tif zero {\n\t\t\ttx.AddError(ErrPrimaryKeyRequired)\n\t\t\tbreak\n\t\t}\n\t\tqueryDB = tx.Clauses(clause.Gt{Column: clause.Column{Table: clause.CurrentTable, Name: clause.PrimaryKey}, Value: primaryValue})\n\t}\n\n\ttx.RowsAffected = rowsAffected\n\treturn tx\n}\n\nfunc (db *DB) assignInterfacesToValue(values ...interface{}) {\n\tfor _, value := range values {\n\t\tswitch v := value.(type) {\n\t\tcase []clause.Expression:\n\t\t\tfor _, expr := range v {\n\t\t\t\tif eq, ok := expr.(clause.Eq); ok {\n\t\t\t\t\tswitch column := eq.Column.(type) {\n\t\t\t\t\tcase string:\n\t\t\t\t\t\tif field := db.Statement.Schema.LookUpField(column); field != nil {\n\t\t\t\t\t\t\tdb.AddError(field.Set(db.Statement.Context, db.Statement.ReflectValue, eq.Value))\n\t\t\t\t\t\t}\n\t\t\t\t\tcase clause.Column:\n\t\t\t\t\t\tif field := db.Statement.Schema.LookUpField(column.Name); field != nil {\n\t\t\t\t\t\t\tdb.AddError(field.Set(db.Statement.Context, db.Statement.ReflectValue, eq.Value))\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else if andCond, ok := expr.(clause.AndConditions); ok {\n\t\t\t\t\tdb.assignInterfacesToValue(andCond.Exprs)\n\t\t\t\t}\n\t\t\t}\n\t\tcase clause.Expression, map[string]string, map[interface{}]interface{}, map[string]interface{}:\n\t\t\tif exprs := db.Statement.BuildCondition(value); len(exprs) > 0 {\n\t\t\t\tdb.assignInterfacesToValue(exprs)\n\t\t\t}\n\t\tdefault:\n\t\t\tif s, err := schema.Parse(value, db.cacheStore, db.NamingStrategy); err == nil {\n\t\t\t\treflectValue := reflect.Indirect(reflect.ValueOf(value))\n\t\t\t\tswitch reflectValue.Kind() {\n\t\t\t\tcase reflect.Struct:\n\t\t\t\t\tfor _, f := range s.Fields {\n\t\t\t\t\t\tif f.Readable {\n\t\t\t\t\t\t\tif v, isZero := f.ValueOf(db.Statement.Context, reflectValue); !isZero {\n\t\t\t\t\t\t\t\tif field := db.Statement.Schema.LookUpField(f.Name); field != nil {\n\t\t\t\t\t\t\t\t\tdb.AddError(field.Set(db.Statement.Context, db.Statement.ReflectValue, v))\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else if len(values) > 0 {\n\t\t\t\tif exprs := db.Statement.BuildCondition(values[0], values[1:]...); len(exprs) > 0 {\n\t\t\t\t\tdb.assignInterfacesToValue(exprs)\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n}\n\n// FirstOrInit finds the first matching record, otherwise if not found initializes a new instance with given conds.\n// Each conds must be a struct or map.\n//\n// FirstOrInit never modifies the database. It is often used with Assign and Attrs.\n//\n//\t// assign an email if the record is not found\n//\tdb.Where(User{Name: \"non_existing\"}).Attrs(User{Email: \"fake@fake.org\"}).FirstOrInit(&user)\n//\t// user -> User{Name: \"non_existing\", Email: \"fake@fake.org\"}\n//\n//\t// assign email regardless of if record is found\n//\tdb.Where(User{Name: \"jinzhu\"}).Assign(User{Email: \"fake@fake.org\"}).FirstOrInit(&user)\n//\t// user -> User{Name: \"jinzhu\", Age: 20, Email: \"fake@fake.org\"}\nfunc (db *DB) FirstOrInit(dest interface{}, conds ...interface{}) (tx *DB) {\n\tqueryTx := db.Limit(1).Order(clause.OrderByColumn{\n\t\tColumn: clause.Column{Table: clause.CurrentTable, Name: clause.PrimaryKey},\n\t})\n\n\tif tx = queryTx.Find(dest, conds...); tx.RowsAffected == 0 {\n\t\tif c, ok := tx.Statement.Clauses[\"WHERE\"]; ok {\n\t\t\tif where, ok := c.Expression.(clause.Where); ok {\n\t\t\t\ttx.assignInterfacesToValue(where.Exprs)\n\t\t\t}\n\t\t}\n\n\t\t// initialize with attrs, conds\n\t\tif len(tx.Statement.attrs) > 0 {\n\t\t\ttx.assignInterfacesToValue(tx.Statement.attrs...)\n\t\t}\n\t}\n\n\t// initialize with attrs, conds\n\tif len(tx.Statement.assigns) > 0 {\n\t\ttx.assignInterfacesToValue(tx.Statement.assigns...)\n\t}\n\treturn\n}\n\n// FirstOrCreate finds the first matching record, otherwise if not found creates a new instance with given conds.\n// Each conds must be a struct or map.\n//\n// Using FirstOrCreate in conjunction with Assign will result in an update to the database even if the record exists.\n//\n//\t// assign an email if the record is not found\n//\tresult := db.Where(User{Name: \"non_existing\"}).Attrs(User{Email: \"fake@fake.org\"}).FirstOrCreate(&user)\n//\t// user -> User{Name: \"non_existing\", Email: \"fake@fake.org\"}\n//\t// result.RowsAffected -> 1\n//\n//\t// assign email regardless of if record is found\n//\tresult := db.Where(User{Name: \"jinzhu\"}).Assign(User{Email: \"fake@fake.org\"}).FirstOrCreate(&user)\n//\t// user -> User{Name: \"jinzhu\", Age: 20, Email: \"fake@fake.org\"}\n//\t// result.RowsAffected -> 1\nfunc (db *DB) FirstOrCreate(dest interface{}, conds ...interface{}) (tx *DB) {\n\ttx = db.getInstance()\n\tqueryTx := db.Session(&Session{}).Limit(1).Order(clause.OrderByColumn{\n\t\tColumn: clause.Column{Table: clause.CurrentTable, Name: clause.PrimaryKey},\n\t})\n\n\tresult := queryTx.Find(dest, conds...)\n\tif result.Error != nil {\n\t\ttx.Error = result.Error\n\t\treturn tx\n\t}\n\n\tif result.RowsAffected == 0 {\n\t\tif c, ok := result.Statement.Clauses[\"WHERE\"]; ok {\n\t\t\tif where, ok := c.Expression.(clause.Where); ok {\n\t\t\t\tresult.assignInterfacesToValue(where.Exprs)\n\t\t\t}\n\t\t}\n\n\t\t// initialize with attrs, conds\n\t\tif len(db.Statement.attrs) > 0 {\n\t\t\tresult.assignInterfacesToValue(db.Statement.attrs...)\n\t\t}\n\n\t\t// initialize with attrs, conds\n\t\tif len(db.Statement.assigns) > 0 {\n\t\t\tresult.assignInterfacesToValue(db.Statement.assigns...)\n\t\t}\n\n\t\treturn tx.Create(dest)\n\t} else if len(db.Statement.assigns) > 0 {\n\t\texprs := tx.Statement.BuildCondition(db.Statement.assigns[0], db.Statement.assigns[1:]...)\n\t\tassigns := map[string]interface{}{}\n\t\tfor i := 0; i < len(exprs); i++ {\n\t\t\texpr := exprs[i]\n\n\t\t\tif eq, ok := expr.(clause.AndConditions); ok {\n\t\t\t\texprs = append(exprs, eq.Exprs...)\n\t\t\t} else if eq, ok := expr.(clause.Eq); ok {\n\t\t\t\tswitch column := eq.Column.(type) {\n\t\t\t\tcase string:\n\t\t\t\t\tassigns[column] = eq.Value\n\t\t\t\tcase clause.Column:\n\t\t\t\t\tassigns[column.Name] = eq.Value\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn tx.Model(dest).Updates(assigns)\n\t}\n\n\treturn tx\n}\n\n// Update updates column with value using callbacks. Reference: https://gorm.io/docs/update.html#Update-Changed-Fields\nfunc (db *DB) Update(column string, value interface{}) (tx *DB) {\n\ttx = db.getInstance()\n\ttx.Statement.Dest = map[string]interface{}{column: value}\n\treturn tx.callbacks.Update().Execute(tx)\n}\n\n// Updates updates attributes using callbacks. values must be a struct or map. Reference: https://gorm.io/docs/update.html#Update-Changed-Fields\nfunc (db *DB) Updates(values interface{}) (tx *DB) {\n\ttx = db.getInstance()\n\ttx.Statement.Dest = values\n\treturn tx.callbacks.Update().Execute(tx)\n}\n\nfunc (db *DB) UpdateColumn(column string, value interface{}) (tx *DB) {\n\ttx = db.getInstance()\n\ttx.Statement.Dest = map[string]interface{}{column: value}\n\ttx.Statement.SkipHooks = true\n\treturn tx.callbacks.Update().Execute(tx)\n}\n\nfunc (db *DB) UpdateColumns(values interface{}) (tx *DB) {\n\ttx = db.getInstance()\n\ttx.Statement.Dest = values\n\ttx.Statement.SkipHooks = true\n\treturn tx.callbacks.Update().Execute(tx)\n}\n\n// Delete deletes value matching given conditions. If value contains primary key it is included in the conditions. If\n// value includes a deleted_at field, then Delete performs a soft delete instead by setting deleted_at with the current\n// time if null.\nfunc (db *DB) Delete(value interface{}, conds ...interface{}) (tx *DB) {\n\ttx = db.getInstance()\n\tif len(conds) > 0 {\n\t\tif exprs := tx.Statement.BuildCondition(conds[0], conds[1:]...); len(exprs) > 0 {\n\t\t\ttx.Statement.AddClause(clause.Where{Exprs: exprs})\n\t\t}\n\t}\n\ttx.Statement.Dest = value\n\treturn tx.callbacks.Delete().Execute(tx)\n}\n\nfunc (db *DB) Count(count *int64) (tx *DB) {\n\ttx = db.getInstance()\n\tif tx.Statement.Model == nil {\n\t\ttx.Statement.Model = tx.Statement.Dest\n\t\tdefer func() {\n\t\t\ttx.Statement.Model = nil\n\t\t}()\n\t}\n\n\tif selectClause, ok := db.Statement.Clauses[\"SELECT\"]; ok {\n\t\tdefer func() {\n\t\t\ttx.Statement.Clauses[\"SELECT\"] = selectClause\n\t\t}()\n\t} else {\n\t\tdefer delete(tx.Statement.Clauses, \"SELECT\")\n\t}\n\n\tif len(tx.Statement.Selects) == 0 {\n\t\ttx.Statement.AddClause(clause.Select{Expression: clause.Expr{SQL: \"count(*)\"}})\n\t} else if !strings.HasPrefix(strings.TrimSpace(strings.ToLower(tx.Statement.Selects[0])), \"count(\") {\n\t\texpr := clause.Expr{SQL: \"count(*)\"}\n\n\t\tif len(tx.Statement.Selects) == 1 {\n\t\t\tdbName := tx.Statement.Selects[0]\n\t\t\tfields := strings.FieldsFunc(dbName, utils.IsInvalidDBNameChar)\n\t\t\tif len(fields) == 1 || (len(fields) == 3 && (strings.ToUpper(fields[1]) == \"AS\" || fields[1] == \".\")) {\n\t\t\t\tif tx.Statement.Parse(tx.Statement.Model) == nil {\n\t\t\t\t\tif f := tx.Statement.Schema.LookUpField(dbName); f != nil {\n\t\t\t\t\t\tdbName = f.DBName\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif tx.Statement.Distinct {\n\t\t\t\t\texpr = clause.Expr{SQL: \"COUNT(DISTINCT(?))\", Vars: []interface{}{clause.Column{Name: dbName}}}\n\t\t\t\t} else if dbName != \"*\" {\n\t\t\t\t\texpr = clause.Expr{SQL: \"COUNT(?)\", Vars: []interface{}{clause.Column{Name: dbName}}}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\ttx.Statement.AddClause(clause.Select{Expression: expr})\n\t}\n\n\tif orderByClause, ok := db.Statement.Clauses[\"ORDER BY\"]; ok {\n\t\tif _, ok := db.Statement.Clauses[\"GROUP BY\"]; !ok {\n\t\t\tdelete(tx.Statement.Clauses, \"ORDER BY\")\n\t\t\tdefer func() {\n\t\t\t\ttx.Statement.Clauses[\"ORDER BY\"] = orderByClause\n\t\t\t}()\n\t\t}\n\t}\n\n\ttx.Statement.Dest = count\n\ttx = tx.callbacks.Query().Execute(tx)\n\n\tif _, ok := db.Statement.Clauses[\"GROUP BY\"]; ok || tx.RowsAffected != 1 {\n\t\t*count = tx.RowsAffected\n\t}\n\n\treturn\n}\n\nfunc (db *DB) Row() *sql.Row {\n\ttx := db.getInstance().Set(\"rows\", false)\n\ttx = tx.callbacks.Row().Execute(tx)\n\trow, ok := tx.Statement.Dest.(*sql.Row)\n\tif !ok && tx.DryRun {\n\t\tdb.Logger.Error(tx.Statement.Context, ErrDryRunModeUnsupported.Error())\n\t}\n\treturn row\n}\n\nfunc (db *DB) Rows() (*sql.Rows, error) {\n\ttx := db.getInstance().Set(\"rows\", true)\n\ttx = tx.callbacks.Row().Execute(tx)\n\trows, ok := tx.Statement.Dest.(*sql.Rows)\n\tif !ok && tx.DryRun && tx.Error == nil {\n\t\ttx.Error = ErrDryRunModeUnsupported\n\t}\n\treturn rows, tx.Error\n}\n\n// Scan scans selected value to the struct dest\nfunc (db *DB) Scan(dest interface{}) (tx *DB) {\n\tconfig := *db.Config\n\tcurrentLogger, newLogger := config.Logger, logger.Recorder.New()\n\tconfig.Logger = newLogger\n\n\ttx = db.getInstance()\n\ttx.Config = &config\n\n\tif rows, err := tx.Rows(); err == nil {\n\t\tif rows.Next() {\n\t\t\ttx.ScanRows(rows, dest)\n\t\t} else {\n\t\t\ttx.RowsAffected = 0\n\t\t\ttx.AddError(rows.Err())\n\t\t}\n\t\ttx.AddError(rows.Close())\n\t}\n\n\tcurrentLogger.Trace(tx.Statement.Context, newLogger.BeginAt, func() (string, int64) {\n\t\treturn newLogger.SQL, tx.RowsAffected\n\t}, tx.Error)\n\ttx.Logger = currentLogger\n\treturn\n}\n\n// Pluck queries a single column from a model, returning in the slice dest. E.g.:\n//\n//\tvar ages []int64\n//\tdb.Model(&users).Pluck(\"age\", &ages)\nfunc (db *DB) Pluck(column string, dest interface{}) (tx *DB) {\n\ttx = db.getInstance()\n\tif tx.Statement.Model != nil {\n\t\tif tx.Statement.Parse(tx.Statement.Model) == nil {\n\t\t\tif f := tx.Statement.Schema.LookUpField(column); f != nil {\n\t\t\t\tcolumn = f.DBName\n\t\t\t}\n\t\t}\n\t}\n\n\tif len(tx.Statement.Selects) != 1 {\n\t\tfields := strings.FieldsFunc(column, utils.IsInvalidDBNameChar)\n\t\ttx.Statement.AddClauseIfNotExists(clause.Select{\n\t\t\tDistinct: tx.Statement.Distinct,\n\t\t\tColumns:  []clause.Column{{Name: column, Raw: len(fields) != 1}},\n\t\t})\n\t}\n\ttx.Statement.Dest = dest\n\treturn tx.callbacks.Query().Execute(tx)\n}\n\nfunc (db *DB) ScanRows(rows *sql.Rows, dest interface{}) error {\n\ttx := db.getInstance()\n\tif err := tx.Statement.Parse(dest); !errors.Is(err, schema.ErrUnsupportedDataType) {\n\t\ttx.AddError(err)\n\t}\n\ttx.Statement.Dest = dest\n\ttx.Statement.ReflectValue = reflect.ValueOf(dest)\n\tfor tx.Statement.ReflectValue.Kind() == reflect.Ptr {\n\t\telem := tx.Statement.ReflectValue.Elem()\n\t\tif !elem.IsValid() {\n\t\t\telem = reflect.New(tx.Statement.ReflectValue.Type().Elem())\n\t\t\ttx.Statement.ReflectValue.Set(elem)\n\t\t}\n\t\ttx.Statement.ReflectValue = elem\n\t}\n\tScan(rows, tx, ScanInitialized)\n\treturn tx.Error\n}\n\n// Connection uses a db connection to execute an arbitrary number of commands in fc. When finished, the connection is\n// returned to the connection pool.\nfunc (db *DB) Connection(fc func(tx *DB) error) (err error) {\n\tif db.Error != nil {\n\t\treturn db.Error\n\t}\n\n\ttx := db.getInstance()\n\tsqlDB, err := tx.DB()\n\tif err != nil {\n\t\treturn\n\t}\n\n\tconn, err := sqlDB.Conn(tx.Statement.Context)\n\tif err != nil {\n\t\treturn\n\t}\n\n\tdefer conn.Close()\n\ttx.Statement.ConnPool = conn\n\treturn fc(tx)\n}\n\n// Transaction start a transaction as a block, return error will rollback, otherwise to commit. Transaction executes an\n// arbitrary number of commands in fc within a transaction. On success the changes are committed; if an error occurs\n// they are rolled back.\nfunc (db *DB) Transaction(fc func(tx *DB) error, opts ...*sql.TxOptions) (err error) {\n\tpanicked := true\n\n\tif committer, ok := db.Statement.ConnPool.(TxCommitter); ok && committer != nil {\n\t\t// nested transaction\n\t\tif !db.DisableNestedTransaction {\n\t\t\tspID := new(maphash.Hash).Sum64()\n\t\t\terr = db.SavePoint(fmt.Sprintf(\"sp%d\", spID)).Error\n\t\t\tif err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tdefer func() {\n\t\t\t\t// Make sure to rollback when panic, Block error or Commit error\n\t\t\t\tif panicked || err != nil {\n\t\t\t\t\tdb.RollbackTo(fmt.Sprintf(\"sp%d\", spID))\n\t\t\t\t}\n\t\t\t}()\n\t\t}\n\t\terr = fc(db.Session(&Session{NewDB: db.clone == 1}))\n\t} else {\n\t\ttx := db.Begin(opts...)\n\t\tif tx.Error != nil {\n\t\t\treturn tx.Error\n\t\t}\n\n\t\tdefer func() {\n\t\t\t// Make sure to rollback when panic, Block error or Commit error\n\t\t\tif panicked || err != nil {\n\t\t\t\ttx.Rollback()\n\t\t\t}\n\t\t}()\n\n\t\tif err = fc(tx); err == nil {\n\t\t\tpanicked = false\n\t\t\treturn tx.Commit().Error\n\t\t}\n\t}\n\n\tpanicked = false\n\treturn\n}\n\n// Begin begins a transaction with any transaction options opts\nfunc (db *DB) Begin(opts ...*sql.TxOptions) *DB {\n\tvar (\n\t\t// clone statement\n\t\ttx  = db.getInstance().Session(&Session{Context: db.Statement.Context, NewDB: db.clone == 1})\n\t\topt *sql.TxOptions\n\t\terr error\n\t)\n\n\tif len(opts) > 0 {\n\t\topt = opts[0]\n\t}\n\n\tctx := tx.Statement.Context\n\tif db.DefaultTransactionTimeout > 0 {\n\t\tif _, ok := ctx.Deadline(); !ok {\n\t\t\tctx, _ = context.WithTimeout(ctx, db.DefaultTransactionTimeout)\n\t\t}\n\t}\n\n\tswitch beginner := tx.Statement.ConnPool.(type) {\n\tcase TxBeginner:\n\t\ttx.Statement.ConnPool, err = beginner.BeginTx(ctx, opt)\n\tcase ConnPoolBeginner:\n\t\ttx.Statement.ConnPool, err = beginner.BeginTx(ctx, opt)\n\tdefault:\n\t\terr = ErrInvalidTransaction\n\t}\n\n\tif err != nil {\n\t\ttx.AddError(err)\n\t}\n\n\treturn tx\n}\n\n// Commit commits the changes in a transaction\nfunc (db *DB) Commit() *DB {\n\tif committer, ok := db.Statement.ConnPool.(TxCommitter); ok && committer != nil && !reflect.ValueOf(committer).IsNil() {\n\t\tdb.AddError(committer.Commit())\n\t} else {\n\t\tdb.AddError(ErrInvalidTransaction)\n\t}\n\treturn db\n}\n\n// Rollback rollbacks the changes in a transaction\nfunc (db *DB) Rollback() *DB {\n\tif committer, ok := db.Statement.ConnPool.(TxCommitter); ok && committer != nil {\n\t\tif !reflect.ValueOf(committer).IsNil() {\n\t\t\tdb.AddError(committer.Rollback())\n\t\t}\n\t} else {\n\t\tdb.AddError(ErrInvalidTransaction)\n\t}\n\treturn db\n}\n\nfunc (db *DB) SavePoint(name string) *DB {\n\tif savePointer, ok := db.Dialector.(SavePointerDialectorInterface); ok {\n\t\t// close prepared statement, because SavePoint not support prepared statement.\n\t\t// e.g. mysql8.0 doc: https://dev.mysql.com/doc/refman/8.0/en/sql-prepared-statements.html\n\t\tvar (\n\t\t\tpreparedStmtTx   *PreparedStmtTX\n\t\t\tisPreparedStmtTx bool\n\t\t)\n\t\t// close prepared statement, because SavePoint not support prepared statement.\n\t\tif preparedStmtTx, isPreparedStmtTx = db.Statement.ConnPool.(*PreparedStmtTX); isPreparedStmtTx {\n\t\t\tdb.Statement.ConnPool = preparedStmtTx.Tx\n\t\t}\n\t\tdb.AddError(savePointer.SavePoint(db, name))\n\t\t// restore prepared statement\n\t\tif isPreparedStmtTx {\n\t\t\tdb.Statement.ConnPool = preparedStmtTx\n\t\t}\n\t} else {\n\t\tdb.AddError(ErrUnsupportedDriver)\n\t}\n\treturn db\n}\n\nfunc (db *DB) RollbackTo(name string) *DB {\n\tif savePointer, ok := db.Dialector.(SavePointerDialectorInterface); ok {\n\t\t// close prepared statement, because RollbackTo not support prepared statement.\n\t\t// e.g. mysql8.0 doc: https://dev.mysql.com/doc/refman/8.0/en/sql-prepared-statements.html\n\t\tvar (\n\t\t\tpreparedStmtTx   *PreparedStmtTX\n\t\t\tisPreparedStmtTx bool\n\t\t)\n\t\t// close prepared statement, because SavePoint not support prepared statement.\n\t\tif preparedStmtTx, isPreparedStmtTx = db.Statement.ConnPool.(*PreparedStmtTX); isPreparedStmtTx {\n\t\t\tdb.Statement.ConnPool = preparedStmtTx.Tx\n\t\t}\n\t\tdb.AddError(savePointer.RollbackTo(db, name))\n\t\t// restore prepared statement\n\t\tif isPreparedStmtTx {\n\t\t\tdb.Statement.ConnPool = preparedStmtTx\n\t\t}\n\t} else {\n\t\tdb.AddError(ErrUnsupportedDriver)\n\t}\n\treturn db\n}\n\n// Exec executes raw sql\nfunc (db *DB) Exec(sql string, values ...interface{}) (tx *DB) {\n\ttx = db.getInstance()\n\ttx.Statement.SQL = strings.Builder{}\n\n\tif strings.Contains(sql, \"@\") {\n\t\tclause.NamedExpr{SQL: sql, Vars: values}.Build(tx.Statement)\n\t} else {\n\t\tclause.Expr{SQL: sql, Vars: values}.Build(tx.Statement)\n\t}\n\n\treturn tx.callbacks.Raw().Execute(tx)\n}\n"
  },
  {
    "path": "generics.go",
    "content": "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\"\n\t\"gorm.io/gorm/logger\"\n\t\"gorm.io/gorm/schema\"\n)\n\ntype result struct {\n\tResult       sql.Result\n\tRowsAffected int64\n}\n\nfunc (info *result) ModifyStatement(stmt *Statement) {\n\tstmt.Result = info\n}\n\n// Build implements clause.Expression interface\nfunc (result) Build(clause.Builder) {\n}\n\nfunc WithResult() *result {\n\treturn &result{}\n}\n\ntype Interface[T any] interface {\n\tRaw(sql string, values ...interface{}) ExecInterface[T]\n\tExec(ctx context.Context, sql string, values ...interface{}) error\n\tCreateInterface[T]\n}\n\ntype CreateInterface[T any] interface {\n\tExecInterface[T]\n\t// chain methods available at start; Select/Omit keep CreateInterface to allow Create chaining\n\tScopes(scopes ...func(db *Statement)) ChainInterface[T]\n\tWhere(query interface{}, args ...interface{}) ChainInterface[T]\n\tNot(query interface{}, args ...interface{}) ChainInterface[T]\n\tOr(query interface{}, args ...interface{}) ChainInterface[T]\n\tLimit(offset int) ChainInterface[T]\n\tOffset(offset int) ChainInterface[T]\n\tJoins(query clause.JoinTarget, on func(db JoinBuilder, joinTable clause.Table, curTable clause.Table) error) ChainInterface[T]\n\tPreload(association string, query func(db PreloadBuilder) error) ChainInterface[T]\n\tSelect(query string, args ...interface{}) CreateInterface[T]\n\tOmit(columns ...string) CreateInterface[T]\n\tMapColumns(m map[string]string) ChainInterface[T]\n\tDistinct(args ...interface{}) ChainInterface[T]\n\tGroup(name string) ChainInterface[T]\n\tHaving(query interface{}, args ...interface{}) ChainInterface[T]\n\tOrder(value interface{}) ChainInterface[T]\n\tBuild(builder clause.Builder)\n\n\tDelete(ctx context.Context) (rowsAffected int, err error)\n\tUpdate(ctx context.Context, name string, value any) (rowsAffected int, err error)\n\tUpdates(ctx context.Context, t T) (rowsAffected int, err error)\n\tCount(ctx context.Context, column string) (result int64, err error)\n\n\tTable(name string, args ...interface{}) CreateInterface[T]\n\tCreate(ctx context.Context, r *T) error\n\tCreateInBatches(ctx context.Context, r *[]T, batchSize int) error\n\tSet(assignments ...clause.Assigner) SetCreateOrUpdateInterface[T]\n}\n\ntype ChainInterface[T any] interface {\n\tExecInterface[T]\n\tScopes(scopes ...func(db *Statement)) ChainInterface[T]\n\tWhere(query interface{}, args ...interface{}) ChainInterface[T]\n\tNot(query interface{}, args ...interface{}) ChainInterface[T]\n\tOr(query interface{}, args ...interface{}) ChainInterface[T]\n\tLimit(offset int) ChainInterface[T]\n\tOffset(offset int) ChainInterface[T]\n\tJoins(query clause.JoinTarget, on func(db JoinBuilder, joinTable clause.Table, curTable clause.Table) error) ChainInterface[T]\n\tPreload(association string, query func(db PreloadBuilder) error) ChainInterface[T]\n\tSelect(query string, args ...interface{}) ChainInterface[T]\n\tOmit(columns ...string) ChainInterface[T]\n\tMapColumns(m map[string]string) ChainInterface[T]\n\tDistinct(args ...interface{}) ChainInterface[T]\n\tGroup(name string) ChainInterface[T]\n\tHaving(query interface{}, args ...interface{}) ChainInterface[T]\n\tOrder(value interface{}) ChainInterface[T]\n\tSet(assignments ...clause.Assigner) SetUpdateOnlyInterface[T]\n\n\tBuild(builder clause.Builder)\n\n\tTable(name string, args ...interface{}) ChainInterface[T]\n\tDelete(ctx context.Context) (rowsAffected int, err error)\n\tUpdate(ctx context.Context, name string, value any) (rowsAffected int, err error)\n\tUpdates(ctx context.Context, t T) (rowsAffected int, err error)\n\tCount(ctx context.Context, column string) (result int64, err error)\n}\n\n// SetUpdateOnlyInterface is returned by Set after chaining; only Update is allowed\ntype SetUpdateOnlyInterface[T any] interface {\n\tUpdate(ctx context.Context) (rowsAffected int, err error)\n}\n\n// SetCreateOrUpdateInterface is returned by Set at start; Create or Update are allowed\ntype SetCreateOrUpdateInterface[T any] interface {\n\tCreate(ctx context.Context) error\n\tUpdate(ctx context.Context) (rowsAffected int, err error)\n}\n\ntype ExecInterface[T any] interface {\n\tScan(ctx context.Context, r interface{}) error\n\tFirst(context.Context) (T, error)\n\tLast(ctx context.Context) (T, error)\n\tTake(context.Context) (T, error)\n\tFind(ctx context.Context) ([]T, error)\n\tFindInBatches(ctx context.Context, batchSize int, fc func(data []T, batch int) error) error\n\tRow(ctx context.Context) *sql.Row\n\tRows(ctx context.Context) (*sql.Rows, error)\n}\n\ntype JoinBuilder interface {\n\tSelect(...string) JoinBuilder\n\tOmit(...string) JoinBuilder\n\tWhere(query interface{}, args ...interface{}) JoinBuilder\n\tNot(query interface{}, args ...interface{}) JoinBuilder\n\tOr(query interface{}, args ...interface{}) JoinBuilder\n}\n\ntype PreloadBuilder interface {\n\tSelect(...string) PreloadBuilder\n\tOmit(...string) PreloadBuilder\n\tWhere(query interface{}, args ...interface{}) PreloadBuilder\n\tNot(query interface{}, args ...interface{}) PreloadBuilder\n\tOr(query interface{}, args ...interface{}) PreloadBuilder\n\tLimit(offset int) PreloadBuilder\n\tOffset(offset int) PreloadBuilder\n\tOrder(value interface{}) PreloadBuilder\n\tLimitPerRecord(num int) PreloadBuilder\n}\n\ntype op func(*DB) *DB\n\nfunc G[T any](db *DB, opts ...clause.Expression) Interface[T] {\n\tv := &g[T]{\n\t\tdb:  db,\n\t\tops: make([]op, 0, 5),\n\t}\n\n\tif len(opts) > 0 {\n\t\tv.ops = append(v.ops, func(db *DB) *DB {\n\t\t\treturn db.Clauses(opts...)\n\t\t})\n\t}\n\n\tv.createG = &createG[T]{\n\t\tchainG: chainG[T]{\n\t\t\texecG: execG[T]{g: v},\n\t\t},\n\t}\n\treturn v\n}\n\ntype g[T any] struct {\n\t*createG[T]\n\tdb  *DB\n\tops []op\n}\n\nfunc (g *g[T]) apply(ctx context.Context) *DB {\n\tdb := g.db\n\tif !db.DryRun {\n\t\tdb = db.Session(&Session{NewDB: true, Context: ctx}).getInstance()\n\t}\n\n\tfor _, op := range g.ops {\n\t\tdb = op(db)\n\t}\n\treturn db\n}\n\nfunc (c *g[T]) Raw(sql string, values ...interface{}) ExecInterface[T] {\n\treturn execG[T]{g: &g[T]{\n\t\tdb: c.db,\n\t\tops: append(c.ops, func(db *DB) *DB {\n\t\t\tvar r T\n\t\t\treturn db.Model(r).Raw(sql, values...)\n\t\t}),\n\t}}\n}\n\nfunc (c *g[T]) Exec(ctx context.Context, sql string, values ...interface{}) error {\n\tvar r T\n\treturn c.apply(ctx).Model(r).Exec(sql, values...).Error\n}\n\ntype createG[T any] struct {\n\tchainG[T]\n}\n\nfunc (c createG[T]) Table(name string, args ...interface{}) CreateInterface[T] {\n\treturn createG[T]{c.with(func(db *DB) *DB {\n\t\treturn db.Table(name, args...)\n\t})}\n}\n\nfunc (c createG[T]) Select(query string, args ...interface{}) CreateInterface[T] {\n\treturn createG[T]{c.with(func(db *DB) *DB {\n\t\treturn db.Select(query, args...)\n\t})}\n}\n\nfunc (c createG[T]) Omit(columns ...string) CreateInterface[T] {\n\treturn createG[T]{c.with(func(db *DB) *DB {\n\t\treturn db.Omit(columns...)\n\t})}\n}\n\nfunc (c createG[T]) Set(assignments ...clause.Assigner) SetCreateOrUpdateInterface[T] {\n\treturn c.processSet(assignments...)\n}\n\nfunc (c createG[T]) Create(ctx context.Context, r *T) error {\n\treturn c.g.apply(ctx).Create(r).Error\n}\n\nfunc (c createG[T]) CreateInBatches(ctx context.Context, r *[]T, batchSize int) error {\n\treturn c.g.apply(ctx).CreateInBatches(r, batchSize).Error\n}\n\ntype chainG[T any] struct {\n\texecG[T]\n}\n\nfunc (c chainG[T]) getInstance() *DB {\n\tvar r T\n\treturn c.g.apply(context.Background()).Model(r).getInstance()\n}\n\nfunc (c chainG[T]) with(v op) chainG[T] {\n\treturn chainG[T]{\n\t\texecG: execG[T]{g: &g[T]{\n\t\t\tdb:  c.g.db,\n\t\t\tops: append(append([]op(nil), c.g.ops...), v),\n\t\t}},\n\t}\n}\n\nfunc (c chainG[T]) Table(name string, args ...interface{}) ChainInterface[T] {\n\treturn c.with(func(db *DB) *DB {\n\t\treturn db.Table(name, args...)\n\t})\n}\n\nfunc (c chainG[T]) Scopes(scopes ...func(db *Statement)) ChainInterface[T] {\n\treturn c.with(func(db *DB) *DB {\n\t\tfor _, fc := range scopes {\n\t\t\tfc(db.Statement)\n\t\t}\n\t\treturn db\n\t})\n}\n\nfunc (c chainG[T]) Where(query interface{}, args ...interface{}) ChainInterface[T] {\n\treturn c.with(func(db *DB) *DB {\n\t\treturn db.Where(query, args...)\n\t})\n}\n\nfunc (c chainG[T]) Not(query interface{}, args ...interface{}) ChainInterface[T] {\n\treturn c.with(func(db *DB) *DB {\n\t\treturn db.Not(query, args...)\n\t})\n}\n\nfunc (c chainG[T]) Or(query interface{}, args ...interface{}) ChainInterface[T] {\n\treturn c.with(func(db *DB) *DB {\n\t\treturn db.Or(query, args...)\n\t})\n}\n\nfunc (c chainG[T]) Limit(offset int) ChainInterface[T] {\n\treturn c.with(func(db *DB) *DB {\n\t\treturn db.Limit(offset)\n\t})\n}\n\nfunc (c chainG[T]) Offset(offset int) ChainInterface[T] {\n\treturn c.with(func(db *DB) *DB {\n\t\treturn db.Offset(offset)\n\t})\n}\n\ntype joinBuilder struct {\n\tdb *DB\n}\n\nfunc (q *joinBuilder) Where(query interface{}, args ...interface{}) JoinBuilder {\n\tq.db.Where(query, args...)\n\treturn q\n}\n\nfunc (q *joinBuilder) Or(query interface{}, args ...interface{}) JoinBuilder {\n\tq.db.Where(query, args...)\n\treturn q\n}\n\nfunc (q *joinBuilder) Not(query interface{}, args ...interface{}) JoinBuilder {\n\tq.db.Where(query, args...)\n\treturn q\n}\n\nfunc (q *joinBuilder) Select(columns ...string) JoinBuilder {\n\tq.db.Select(columns)\n\treturn q\n}\n\nfunc (q *joinBuilder) Omit(columns ...string) JoinBuilder {\n\tq.db.Omit(columns...)\n\treturn q\n}\n\ntype preloadBuilder struct {\n\tlimitPerRecord int\n\tdb             *DB\n}\n\nfunc (q *preloadBuilder) Where(query interface{}, args ...interface{}) PreloadBuilder {\n\tq.db.Where(query, args...)\n\treturn q\n}\n\nfunc (q *preloadBuilder) Or(query interface{}, args ...interface{}) PreloadBuilder {\n\tq.db.Where(query, args...)\n\treturn q\n}\n\nfunc (q *preloadBuilder) Not(query interface{}, args ...interface{}) PreloadBuilder {\n\tq.db.Where(query, args...)\n\treturn q\n}\n\nfunc (q *preloadBuilder) Select(columns ...string) PreloadBuilder {\n\tq.db.Select(columns)\n\treturn q\n}\n\nfunc (q *preloadBuilder) Omit(columns ...string) PreloadBuilder {\n\tq.db.Omit(columns...)\n\treturn q\n}\n\nfunc (q *preloadBuilder) Limit(limit int) PreloadBuilder {\n\tq.db.Limit(limit)\n\treturn q\n}\n\nfunc (q *preloadBuilder) Offset(offset int) PreloadBuilder {\n\tq.db.Offset(offset)\n\treturn q\n}\n\nfunc (q *preloadBuilder) Order(value interface{}) PreloadBuilder {\n\tq.db.Order(value)\n\treturn q\n}\n\nfunc (q *preloadBuilder) LimitPerRecord(num int) PreloadBuilder {\n\tq.limitPerRecord = num\n\treturn q\n}\n\nfunc (c chainG[T]) Joins(jt clause.JoinTarget, on func(db JoinBuilder, joinTable clause.Table, curTable clause.Table) error) ChainInterface[T] {\n\treturn c.with(func(db *DB) *DB {\n\t\tif jt.Table == \"\" {\n\t\t\tjt.Table = clause.JoinTable(strings.Split(jt.Association, \".\")...).Name\n\t\t}\n\n\t\tq := joinBuilder{db: db.Session(&Session{NewDB: true, Initialized: true}).Table(jt.Table)}\n\t\tif on != nil {\n\t\t\tif err := on(&q, clause.Table{Name: jt.Table}, clause.Table{Name: clause.CurrentTable}); err != nil {\n\t\t\t\tdb.AddError(err)\n\t\t\t}\n\t\t}\n\n\t\tj := join{\n\t\t\tName:     jt.Association,\n\t\t\tAlias:    jt.Table,\n\t\t\tSelects:  q.db.Statement.Selects,\n\t\t\tOmits:    q.db.Statement.Omits,\n\t\t\tJoinType: jt.Type,\n\t\t}\n\n\t\tif where, ok := q.db.Statement.Clauses[\"WHERE\"].Expression.(clause.Where); ok {\n\t\t\tj.On = &where\n\t\t}\n\n\t\tif jt.Subquery != nil {\n\t\t\tjoinType := j.JoinType\n\t\t\tif joinType == \"\" {\n\t\t\t\tjoinType = clause.LeftJoin\n\t\t\t}\n\n\t\t\tif db, ok := jt.Subquery.(interface{ getInstance() *DB }); ok {\n\t\t\t\tstmt := db.getInstance().Statement\n\t\t\t\tif len(j.Selects) == 0 {\n\t\t\t\t\tj.Selects = stmt.Selects\n\t\t\t\t}\n\t\t\t\tif len(j.Omits) == 0 {\n\t\t\t\t\tj.Omits = stmt.Omits\n\t\t\t\t}\n\t\t\t}\n\n\t\t\texpr := clause.NamedExpr{SQL: fmt.Sprintf(\"%s JOIN (?) AS ?\", joinType), Vars: []interface{}{jt.Subquery, clause.Table{Name: j.Alias}}}\n\n\t\t\tif j.On != nil {\n\t\t\t\texpr.SQL += \" ON ?\"\n\t\t\t\texpr.Vars = append(expr.Vars, clause.AndConditions{Exprs: j.On.Exprs})\n\t\t\t}\n\n\t\t\tj.Expression = expr\n\t\t}\n\n\t\tdb.Statement.Joins = append(db.Statement.Joins, j)\n\t\tsort.Slice(db.Statement.Joins, func(i, j int) bool {\n\t\t\treturn db.Statement.Joins[i].Name < db.Statement.Joins[j].Name\n\t\t})\n\t\treturn db\n\t})\n}\n\nfunc (c chainG[T]) Select(query string, args ...interface{}) ChainInterface[T] {\n\treturn c.with(func(db *DB) *DB {\n\t\treturn db.Select(query, args...)\n\t})\n}\n\nfunc (c chainG[T]) Omit(columns ...string) ChainInterface[T] {\n\treturn c.with(func(db *DB) *DB {\n\t\treturn db.Omit(columns...)\n\t})\n}\n\nfunc (c chainG[T]) MapColumns(m map[string]string) ChainInterface[T] {\n\treturn c.with(func(db *DB) *DB {\n\t\treturn db.MapColumns(m)\n\t})\n}\n\nfunc (c chainG[T]) Set(assignments ...clause.Assigner) SetUpdateOnlyInterface[T] {\n\treturn c.processSet(assignments...)\n}\n\nfunc (c chainG[T]) Distinct(args ...interface{}) ChainInterface[T] {\n\treturn c.with(func(db *DB) *DB {\n\t\treturn db.Distinct(args...)\n\t})\n}\n\nfunc (c chainG[T]) Group(name string) ChainInterface[T] {\n\treturn c.with(func(db *DB) *DB {\n\t\treturn db.Group(name)\n\t})\n}\n\nfunc (c chainG[T]) Having(query interface{}, args ...interface{}) ChainInterface[T] {\n\treturn c.with(func(db *DB) *DB {\n\t\treturn db.Having(query, args...)\n\t})\n}\n\nfunc (c chainG[T]) Order(value interface{}) ChainInterface[T] {\n\treturn c.with(func(db *DB) *DB {\n\t\treturn db.Order(value)\n\t})\n}\n\nfunc (c chainG[T]) Preload(association string, query func(db PreloadBuilder) error) ChainInterface[T] {\n\treturn c.with(func(db *DB) *DB {\n\t\treturn db.Preload(association, func(tx *DB) *DB {\n\t\t\tq := preloadBuilder{db: tx.getInstance()}\n\t\t\tif query != nil {\n\t\t\t\tif err := query(&q); err != nil {\n\t\t\t\t\tdb.AddError(err)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\trelation, ok := db.Statement.Schema.Relationships.Relations[association]\n\t\t\tif !ok {\n\t\t\t\tif preloadFields := strings.Split(association, \".\"); len(preloadFields) > 1 {\n\t\t\t\t\trelationships := &db.Statement.Schema.Relationships\n\t\t\t\t\tfor _, field := range preloadFields {\n\t\t\t\t\t\tvar ok bool\n\t\t\t\t\t\trelation, ok = relationships.Relations[field]\n\t\t\t\t\t\tif ok {\n\t\t\t\t\t\t\trelationships = &relation.FieldSchema.Relationships\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tdb.AddError(fmt.Errorf(\"relation %s not found\", association))\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tdb.AddError(fmt.Errorf(\"relation %s not found\", association))\n\t\t\t\t\treturn nil\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif q.limitPerRecord > 0 {\n\t\t\t\tif relation.JoinTable != nil {\n\t\t\t\t\ttx.AddError(fmt.Errorf(\"many2many relation %s don't support LimitPerRecord\", association))\n\t\t\t\t\treturn tx\n\t\t\t\t}\n\n\t\t\t\trefColumns := []clause.Column{}\n\t\t\t\tfor _, rel := range relation.References {\n\t\t\t\t\tif rel.OwnPrimaryKey {\n\t\t\t\t\t\trefColumns = append(refColumns, clause.Column{Name: rel.ForeignKey.DBName})\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif len(refColumns) != 0 {\n\t\t\t\t\tselectExpr := clause.CommaExpression{}\n\t\t\t\t\tfor _, column := range q.db.Statement.Selects {\n\t\t\t\t\t\tselectExpr.Exprs = append(selectExpr.Exprs, clause.Expr{SQL: \"?\", Vars: []interface{}{clause.Column{Name: column}}})\n\t\t\t\t\t}\n\n\t\t\t\t\tif len(selectExpr.Exprs) == 0 {\n\t\t\t\t\t\tselectExpr.Exprs = []clause.Expression{clause.Expr{SQL: \"*\", Vars: []interface{}{}}}\n\t\t\t\t\t}\n\n\t\t\t\t\tpartitionBy := clause.CommaExpression{}\n\t\t\t\t\tfor _, column := range refColumns {\n\t\t\t\t\t\tpartitionBy.Exprs = append(partitionBy.Exprs, clause.Expr{SQL: \"?\", Vars: []interface{}{clause.Column{Name: column.Name}}})\n\t\t\t\t\t}\n\n\t\t\t\t\trnnColumn := clause.Column{Name: \"gorm_preload_rnn\"}\n\t\t\t\t\tsql := \"ROW_NUMBER() OVER (PARTITION BY ? ?)\"\n\t\t\t\t\tvars := []interface{}{partitionBy}\n\t\t\t\t\tif orderBy, ok := q.db.Statement.Clauses[\"ORDER BY\"]; ok {\n\t\t\t\t\t\tvars = append(vars, orderBy)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tvars = append(vars, clause.Clause{Name: \"ORDER BY\", Expression: clause.OrderBy{\n\t\t\t\t\t\t\tColumns: []clause.OrderByColumn{{Column: clause.PrimaryColumn, Desc: true}},\n\t\t\t\t\t\t}})\n\t\t\t\t\t}\n\t\t\t\t\tvars = append(vars, rnnColumn)\n\n\t\t\t\t\tselectExpr.Exprs = append(selectExpr.Exprs, clause.Expr{SQL: sql + \" AS ?\", Vars: vars})\n\n\t\t\t\t\tq.db.Clauses(clause.Select{Expression: selectExpr})\n\n\t\t\t\t\treturn q.db.Session(&Session{NewDB: true}).Unscoped().Table(\"(?) t\", q.db).Where(\"? <= ?\", rnnColumn, q.limitPerRecord)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn q.db\n\t\t})\n\t})\n}\n\nfunc (c chainG[T]) Delete(ctx context.Context) (rowsAffected int, err error) {\n\tr := new(T)\n\tres := c.g.apply(ctx).Delete(r)\n\treturn int(res.RowsAffected), res.Error\n}\n\nfunc (c chainG[T]) Update(ctx context.Context, name string, value any) (rowsAffected int, err error) {\n\tvar r T\n\tres := c.g.apply(ctx).Model(r).Update(name, value)\n\treturn int(res.RowsAffected), res.Error\n}\n\nfunc (c chainG[T]) Updates(ctx context.Context, t T) (rowsAffected int, err error) {\n\tres := c.g.apply(ctx).Updates(t)\n\treturn int(res.RowsAffected), res.Error\n}\n\nfunc (c chainG[T]) Count(ctx context.Context, column string) (result int64, err error) {\n\tvar r T\n\terr = c.g.apply(ctx).Model(r).Select(column).Count(&result).Error\n\treturn\n}\n\nfunc (c chainG[T]) Build(builder clause.Builder) {\n\tsubdb := c.getInstance()\n\tsubdb.Logger = logger.Discard\n\tsubdb.DryRun = true\n\n\tif stmt, ok := builder.(*Statement); ok {\n\t\tif subdb.Statement.SQL.Len() > 0 {\n\t\t\tvar (\n\t\t\t\tvars = subdb.Statement.Vars\n\t\t\t\tsql  = subdb.Statement.SQL.String()\n\t\t\t)\n\n\t\t\tsubdb.Statement.Vars = make([]interface{}, 0, len(vars))\n\t\t\tfor _, vv := range vars {\n\t\t\t\tsubdb.Statement.Vars = append(subdb.Statement.Vars, vv)\n\t\t\t\tbindvar := strings.Builder{}\n\t\t\t\tsubdb.BindVarTo(&bindvar, subdb.Statement, vv)\n\t\t\t\tsql = strings.Replace(sql, bindvar.String(), \"?\", 1)\n\t\t\t}\n\n\t\t\tsubdb.Statement.SQL.Reset()\n\t\t\tsubdb.Statement.Vars = stmt.Vars\n\t\t\tif strings.Contains(sql, \"@\") {\n\t\t\t\tclause.NamedExpr{SQL: sql, Vars: vars}.Build(subdb.Statement)\n\t\t\t} else {\n\t\t\t\tclause.Expr{SQL: sql, Vars: vars}.Build(subdb.Statement)\n\t\t\t}\n\t\t} else {\n\t\t\tsubdb.Statement.Vars = append(stmt.Vars, subdb.Statement.Vars...)\n\t\t\tsubdb.callbacks.Query().Execute(subdb)\n\t\t}\n\n\t\tbuilder.WriteString(subdb.Statement.SQL.String())\n\t\tstmt.Vars = subdb.Statement.Vars\n\t}\n}\n\ntype execG[T any] struct {\n\tg *g[T]\n}\n\nfunc (g execG[T]) First(ctx context.Context) (T, error) {\n\tvar r T\n\terr := g.g.apply(ctx).First(&r).Error\n\treturn r, err\n}\n\nfunc (g execG[T]) Scan(ctx context.Context, result interface{}) error {\n\tvar r T\n\terr := g.g.apply(ctx).Model(r).Find(result).Error\n\treturn err\n}\n\nfunc (g execG[T]) Last(ctx context.Context) (T, error) {\n\tvar r T\n\terr := g.g.apply(ctx).Last(&r).Error\n\treturn r, err\n}\n\nfunc (g execG[T]) Take(ctx context.Context) (T, error) {\n\tvar r T\n\terr := g.g.apply(ctx).Take(&r).Error\n\treturn r, err\n}\n\nfunc (g execG[T]) Find(ctx context.Context) ([]T, error) {\n\tvar r []T\n\terr := g.g.apply(ctx).Find(&r).Error\n\treturn r, err\n}\n\nfunc (g execG[T]) FindInBatches(ctx context.Context, batchSize int, fc func(data []T, batch int) error) error {\n\tvar data []T\n\treturn g.g.apply(ctx).FindInBatches(&data, batchSize, func(tx *DB, batch int) error {\n\t\treturn fc(data, batch)\n\t}).Error\n}\n\nfunc (g execG[T]) Row(ctx context.Context) *sql.Row {\n\tvar r T\n\treturn g.g.apply(ctx).Model(r).Row()\n}\n\nfunc (g execG[T]) Rows(ctx context.Context) (*sql.Rows, error) {\n\tvar r T\n\treturn g.g.apply(ctx).Model(r).Rows()\n}\n\nfunc (c chainG[T]) processSet(items ...clause.Assigner) setCreateOrUpdateG[T] {\n\tvar (\n\t\tassigns  []clause.Assignment\n\t\tassocOps []clause.Association\n\t)\n\n\tfor _, item := range items {\n\t\t// Check if it's an AssociationAssigner\n\t\tif assocAssigner, ok := item.(clause.AssociationAssigner); ok {\n\t\t\tassocOps = append(assocOps, assocAssigner.AssociationAssignments()...)\n\t\t} else {\n\t\t\tassigns = append(assigns, item.Assignments()...)\n\t\t}\n\t}\n\n\treturn setCreateOrUpdateG[T]{\n\t\tc:        c,\n\t\tassigns:  assigns,\n\t\tassocOps: assocOps,\n\t}\n}\n\n// setCreateOrUpdateG[T] is a struct that holds operations to be executed in a batch.\n// It supports regular assignments and association operations.\ntype setCreateOrUpdateG[T any] struct {\n\tc        chainG[T]\n\tassigns  []clause.Assignment\n\tassocOps []clause.Association\n}\n\nfunc (s setCreateOrUpdateG[T]) Update(ctx context.Context) (rowsAffected int, err error) {\n\t// Execute association operations\n\tfor _, assocOp := range s.assocOps {\n\t\tif err := s.executeAssociationOperation(ctx, assocOp); err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t}\n\n\t// Execute assignment operations\n\tif len(s.assigns) > 0 {\n\t\tvar r T\n\t\tres := s.c.g.apply(ctx).Model(r).Clauses(clause.Set(s.assigns)).Updates(map[string]interface{}{})\n\t\treturn int(res.RowsAffected), res.Error\n\t}\n\n\treturn 0, nil\n}\n\nfunc (s setCreateOrUpdateG[T]) Create(ctx context.Context) error {\n\t// Execute association operations\n\tfor _, assocOp := range s.assocOps {\n\t\tif err := s.executeAssociationOperation(ctx, assocOp); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\t// Execute assignment operations\n\tif len(s.assigns) > 0 {\n\t\tdata := make(map[string]interface{}, len(s.assigns))\n\t\tfor _, a := range s.assigns {\n\t\t\tdata[a.Column.Name] = a.Value\n\t\t}\n\t\tvar r T\n\t\treturn s.c.g.apply(ctx).Model(r).Create(data).Error\n\t}\n\n\treturn nil\n}\n\n// executeAssociationOperation executes an association operation\nfunc (s setCreateOrUpdateG[T]) executeAssociationOperation(ctx context.Context, op clause.Association) error {\n\tvar r T\n\tbase := s.c.g.apply(ctx).Model(r)\n\n\tswitch op.Type {\n\tcase clause.OpCreate:\n\t\treturn s.handleAssociationCreate(ctx, base, op)\n\tcase clause.OpUnlink, clause.OpDelete, clause.OpUpdate:\n\t\treturn s.handleAssociation(ctx, base, op)\n\tdefault:\n\t\treturn fmt.Errorf(\"unknown association operation type: %v\", op.Type)\n\t}\n}\n\nfunc (s setCreateOrUpdateG[T]) handleAssociationCreate(ctx context.Context, base *DB, op clause.Association) error {\n\tif len(op.Set) > 0 {\n\t\treturn s.handleAssociationForOwners(base, ctx, func(owner T, assoc *Association) error {\n\t\t\tdata := make(map[string]interface{}, len(op.Set))\n\t\t\tfor _, a := range op.Set {\n\t\t\t\tdata[a.Column.Name] = a.Value\n\t\t\t}\n\t\t\treturn assoc.Append(data)\n\t\t}, op.Association)\n\t}\n\n\treturn s.handleAssociationForOwners(base, ctx, func(owner T, assoc *Association) error {\n\t\treturn assoc.Append(op.Values...)\n\t}, op.Association)\n}\n\n// handleAssociationForOwners is a helper function that handles associations for all owners\nfunc (s setCreateOrUpdateG[T]) handleAssociationForOwners(base *DB, ctx context.Context, handler func(owner T, association *Association) error, associationName string) error {\n\tvar owners []T\n\tif err := base.Find(&owners).Error; err != nil {\n\t\treturn err\n\t}\n\n\tfor _, owner := range owners {\n\t\tassoc := base.Session(&Session{NewDB: true, Context: ctx}).Model(&owner).Association(associationName)\n\t\tif assoc.Error != nil {\n\t\t\treturn assoc.Error\n\t\t}\n\n\t\tif err := handler(owner, assoc); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (s setCreateOrUpdateG[T]) handleAssociation(ctx context.Context, base *DB, op clause.Association) error {\n\tassoc := base.Association(op.Association)\n\tif assoc.Error != nil {\n\t\treturn assoc.Error\n\t}\n\n\tvar (\n\t\trel            = assoc.Relationship\n\t\tassocModel     = reflect.New(rel.FieldSchema.ModelType).Interface()\n\t\tfkNil          = map[string]any{}\n\t\tsetMap         = make(map[string]any, len(op.Set))\n\t\townerPKNames   []string\n\t\townerFKNames   []string\n\t\tprimaryColumns []any\n\t\tforeignColumns []any\n\t)\n\n\tfor _, a := range op.Set {\n\t\tsetMap[a.Column.Name] = a.Value\n\t}\n\n\tfor _, ref := range rel.References {\n\t\tfkNil[ref.ForeignKey.DBName] = nil\n\n\t\tif ref.OwnPrimaryKey && ref.PrimaryKey != nil {\n\t\t\townerPKNames = append(ownerPKNames, ref.PrimaryKey.DBName)\n\t\t\tprimaryColumns = append(primaryColumns, clause.Column{Name: ref.PrimaryKey.DBName})\n\t\t\tforeignColumns = append(foreignColumns, clause.Column{Name: ref.ForeignKey.DBName})\n\t\t} else if !ref.OwnPrimaryKey && ref.PrimaryKey != nil {\n\t\t\townerFKNames = append(ownerFKNames, ref.ForeignKey.DBName)\n\t\t\tprimaryColumns = append(primaryColumns, clause.Column{Name: ref.PrimaryKey.DBName})\n\t\t}\n\t}\n\n\tassocDB := s.c.g.db.Session(&Session{NewDB: true, Context: ctx}).Model(assocModel).Where(op.Conditions)\n\n\tswitch rel.Type {\n\tcase schema.HasOne, schema.HasMany:\n\t\tassocDB = assocDB.Where(\"? IN (?)\", foreignColumns, base.Select(ownerPKNames))\n\t\tswitch op.Type {\n\t\tcase clause.OpUnlink:\n\t\t\treturn assocDB.Updates(fkNil).Error\n\t\tcase clause.OpDelete:\n\t\t\treturn assocDB.Delete(assocModel).Error\n\t\tcase clause.OpUpdate:\n\t\t\treturn assocDB.Updates(setMap).Error\n\t\t}\n\tcase schema.BelongsTo:\n\t\tswitch op.Type {\n\t\tcase clause.OpDelete:\n\t\t\treturn base.Transaction(func(tx *DB) error {\n\t\t\t\tassocDB.Statement.ConnPool = tx.Statement.ConnPool\n\t\t\t\tbase.Statement.ConnPool = tx.Statement.ConnPool\n\n\t\t\t\tif err := assocDB.Where(\"? IN (?)\", primaryColumns, base.Select(ownerFKNames)).Delete(assocModel).Error; err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\treturn base.Updates(fkNil).Error\n\t\t\t})\n\t\tcase clause.OpUnlink:\n\t\t\treturn base.Updates(fkNil).Error\n\t\tcase clause.OpUpdate:\n\t\t\treturn assocDB.Where(\"? IN (?)\", primaryColumns, base.Select(ownerFKNames)).Updates(setMap).Error\n\t\t}\n\tcase schema.Many2Many:\n\t\tjoinModel := reflect.New(rel.JoinTable.ModelType).Interface()\n\t\tjoinDB := base.Session(&Session{NewDB: true, Context: ctx}).Model(joinModel)\n\n\t\t// EXISTS owners: owners.pk = join.owner_fk for all owner refs\n\t\townersExists := base.Session(&Session{NewDB: true, Context: ctx}).Table(rel.Schema.Table).Select(\"1\")\n\t\tfor _, ref := range rel.References {\n\t\t\tif ref.OwnPrimaryKey && ref.PrimaryKey != nil {\n\t\t\t\townersExists = ownersExists.Where(clause.Eq{\n\t\t\t\t\tColumn: clause.Column{Table: rel.Schema.Table, Name: ref.PrimaryKey.DBName},\n\t\t\t\t\tValue:  clause.Column{Table: rel.JoinTable.Table, Name: ref.ForeignKey.DBName},\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\n\t\t// EXISTS related: related.pk = join.rel_fk for all related refs, plus optional conditions\n\t\trelatedExists := base.Session(&Session{NewDB: true, Context: ctx}).Table(rel.FieldSchema.Table).Select(\"1\")\n\t\tfor _, ref := range rel.References {\n\t\t\tif !ref.OwnPrimaryKey && ref.PrimaryKey != nil {\n\t\t\t\trelatedExists = relatedExists.Where(clause.Eq{\n\t\t\t\t\tColumn: clause.Column{Table: rel.FieldSchema.Table, Name: ref.PrimaryKey.DBName},\n\t\t\t\t\tValue:  clause.Column{Table: rel.JoinTable.Table, Name: ref.ForeignKey.DBName},\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t\trelatedExists = relatedExists.Where(op.Conditions)\n\n\t\tswitch op.Type {\n\t\tcase clause.OpUnlink, clause.OpDelete:\n\t\t\tjoinDB = joinDB.Where(\"EXISTS (?)\", ownersExists)\n\t\t\tif len(op.Conditions) > 0 {\n\t\t\t\tjoinDB = joinDB.Where(\"EXISTS (?)\", relatedExists)\n\t\t\t}\n\t\t\treturn joinDB.Delete(nil).Error\n\t\tcase clause.OpUpdate:\n\t\t\t// Update related table rows that have join rows matching owners\n\t\t\trelatedDB := base.Session(&Session{NewDB: true, Context: ctx}).Table(rel.FieldSchema.Table).Where(op.Conditions)\n\n\t\t\t// correlated join subquery: join.rel_fk = related.pk AND EXISTS owners\n\t\t\tjoinSub := base.Session(&Session{NewDB: true, Context: ctx}).Table(rel.JoinTable.Table).Select(\"1\")\n\t\t\tfor _, ref := range rel.References {\n\t\t\t\tif !ref.OwnPrimaryKey && ref.PrimaryKey != nil {\n\t\t\t\t\tjoinSub = joinSub.Where(clause.Eq{\n\t\t\t\t\t\tColumn: clause.Column{Table: rel.JoinTable.Table, Name: ref.ForeignKey.DBName},\n\t\t\t\t\t\tValue:  clause.Column{Table: rel.FieldSchema.Table, Name: ref.PrimaryKey.DBName},\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\t\t\tjoinSub = joinSub.Where(\"EXISTS (?)\", ownersExists)\n\t\t\treturn relatedDB.Where(\"EXISTS (?)\", joinSub).Updates(setMap).Error\n\t\t}\n\t}\n\treturn errors.New(\"unsupported relationship\")\n}\n"
  },
  {
    "path": "go.mod",
    "content": "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/text v0.20.0\n)\n\nrequire (\n\tgithub.com/mattn/go-sqlite3 v1.14.22 // indirect\n\tgorm.io/driver/sqlite v1.6.0 // indirect\n)\n"
  },
  {
    "path": "go.sum",
    "content": "github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=\ngithub.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=\ngithub.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=\ngithub.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=\ngithub.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=\ngithub.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=\ngolang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug=\ngolang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=\ngorm.io/driver/sqlite v1.6.0 h1:WHRRrIiulaPiPFmDcod6prc4l2VGVWHz80KspNsxSfQ=\ngorm.io/driver/sqlite v1.6.0/go.mod h1:AO9V1qIQddBESngQUKWL9yoH93HIeA1X6V633rBwyT8=\n"
  },
  {
    "path": "gorm.go",
    "content": "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\"gorm.io/gorm/logger\"\n\t\"gorm.io/gorm/schema\"\n)\n\n// for Config.cacheStore store PreparedStmtDB key\nconst preparedStmtDBKey = \"preparedStmt\"\n\n// Config GORM config\ntype Config struct {\n\t// GORM perform single create, update, delete operations in transactions by default to ensure database data integrity\n\t// You can disable it by setting `SkipDefaultTransaction` to true\n\tSkipDefaultTransaction    bool\n\tDefaultTransactionTimeout time.Duration\n\tDefaultContextTimeout     time.Duration\n\n\t// NamingStrategy tables, columns naming strategy\n\tNamingStrategy schema.Namer\n\t// FullSaveAssociations full save associations\n\tFullSaveAssociations bool\n\t// Logger\n\tLogger logger.Interface\n\t// NowFunc the function to be used when creating a new timestamp\n\tNowFunc func() time.Time\n\t// DryRun generate sql without execute\n\tDryRun bool\n\t// PrepareStmt executes the given query in cached statement\n\tPrepareStmt bool\n\t// PrepareStmt cache support LRU expired,\n\t// default maxsize=int64 Max value and ttl=1h\n\tPrepareStmtMaxSize int\n\tPrepareStmtTTL     time.Duration\n\n\t// DisableAutomaticPing\n\tDisableAutomaticPing bool\n\t// DisableForeignKeyConstraintWhenMigrating\n\tDisableForeignKeyConstraintWhenMigrating bool\n\t// IgnoreRelationshipsWhenMigrating\n\tIgnoreRelationshipsWhenMigrating bool\n\t// DisableNestedTransaction disable nested transaction\n\tDisableNestedTransaction bool\n\t// AllowGlobalUpdate allow global update\n\tAllowGlobalUpdate bool\n\t// QueryFields executes the SQL query with all fields of the table\n\tQueryFields bool\n\t// CreateBatchSize default create batch size\n\tCreateBatchSize int\n\t// TranslateError enabling error translation\n\tTranslateError bool\n\t// PropagateUnscoped propagate Unscoped to every other nested statement\n\tPropagateUnscoped bool\n\n\t// ClauseBuilders clause builder\n\tClauseBuilders map[string]clause.ClauseBuilder\n\t// ConnPool db conn pool\n\tConnPool ConnPool\n\t// Dialector database dialector\n\tDialector\n\t// Plugins registered plugins\n\tPlugins map[string]Plugin\n\n\tcallbacks  *callbacks\n\tcacheStore *sync.Map\n}\n\n// Apply update config to new config\nfunc (c *Config) Apply(config *Config) error {\n\tif config != c {\n\t\t*config = *c\n\t}\n\treturn nil\n}\n\n// AfterInitialize initialize plugins after db connected\nfunc (c *Config) AfterInitialize(db *DB) error {\n\tif db != nil {\n\t\tfor _, plugin := range c.Plugins {\n\t\t\tif err := plugin.Initialize(db); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\n// Option gorm option interface\ntype Option interface {\n\tApply(*Config) error\n\tAfterInitialize(*DB) error\n}\n\n// DB GORM DB definition\ntype DB struct {\n\t*Config\n\tError        error\n\tRowsAffected int64\n\tStatement    *Statement\n\tclone        int\n}\n\n// Session session config when create session with Session() method\ntype Session struct {\n\tDryRun                   bool\n\tPrepareStmt              bool\n\tNewDB                    bool\n\tInitialized              bool\n\tSkipHooks                bool\n\tSkipDefaultTransaction   bool\n\tDisableNestedTransaction bool\n\tAllowGlobalUpdate        bool\n\tFullSaveAssociations     bool\n\tPropagateUnscoped        bool\n\tQueryFields              bool\n\tContext                  context.Context\n\tLogger                   logger.Interface\n\tNowFunc                  func() time.Time\n\tCreateBatchSize          int\n}\n\n// Open initialize db session based on dialector\nfunc Open(dialector Dialector, opts ...Option) (db *DB, err error) {\n\tconfig := &Config{}\n\n\tsort.Slice(opts, func(i, j int) bool {\n\t\t_, isConfig := opts[i].(*Config)\n\t\t_, isConfig2 := opts[j].(*Config)\n\t\treturn isConfig && !isConfig2\n\t})\n\n\tif len(opts) > 0 {\n\t\tif c, ok := opts[0].(*Config); ok {\n\t\t\tconfig = c\n\t\t} else {\n\t\t\topts = append([]Option{config}, opts...)\n\t\t}\n\t}\n\n\tvar skipAfterInitialize bool\n\tfor _, opt := range opts {\n\t\tif opt != nil {\n\t\t\tif applyErr := opt.Apply(config); applyErr != nil {\n\t\t\t\treturn nil, applyErr\n\t\t\t}\n\t\t\tdefer func(opt Option) {\n\t\t\t\tif skipAfterInitialize {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tif errr := opt.AfterInitialize(db); errr != nil {\n\t\t\t\t\terr = errr\n\t\t\t\t}\n\t\t\t}(opt)\n\t\t}\n\t}\n\n\tif d, ok := dialector.(interface{ Apply(*Config) error }); ok {\n\t\tif err = d.Apply(config); err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\n\tif config.NamingStrategy == nil {\n\t\tconfig.NamingStrategy = schema.NamingStrategy{IdentifierMaxLength: 64} // Default Identifier length is 64\n\t}\n\n\tif config.Logger == nil {\n\t\tconfig.Logger = logger.Default\n\t}\n\n\tif config.NowFunc == nil {\n\t\tconfig.NowFunc = func() time.Time { return time.Now().Local() }\n\t}\n\n\tif dialector != nil {\n\t\tconfig.Dialector = dialector\n\t}\n\n\tif config.Plugins == nil {\n\t\tconfig.Plugins = map[string]Plugin{}\n\t}\n\n\tif config.cacheStore == nil {\n\t\tconfig.cacheStore = &sync.Map{}\n\t}\n\n\tdb = &DB{Config: config, clone: 1}\n\n\tdb.callbacks = initializeCallbacks(db)\n\n\tif config.ClauseBuilders == nil {\n\t\tconfig.ClauseBuilders = map[string]clause.ClauseBuilder{}\n\t}\n\n\tif config.Dialector != nil {\n\t\terr = config.Dialector.Initialize(db)\n\t\tif err != nil {\n\t\t\tif db, _ := db.DB(); db != nil {\n\t\t\t\t_ = db.Close()\n\t\t\t}\n\n\t\t\t// DB is not initialized, so we skip AfterInitialize\n\t\t\tskipAfterInitialize = true\n\t\t\treturn\n\t\t}\n\n\t\tif config.TranslateError {\n\t\t\tif _, ok := db.Dialector.(ErrorTranslator); !ok {\n\t\t\t\tconfig.Logger.Warn(context.Background(), \"The TranslateError option is enabled, but the Dialector %s does not implement ErrorTranslator.\", db.Dialector.Name())\n\t\t\t}\n\t\t}\n\t}\n\n\tif config.PrepareStmt {\n\t\tpreparedStmt := NewPreparedStmtDB(db.ConnPool, config.PrepareStmtMaxSize, config.PrepareStmtTTL)\n\t\tdb.cacheStore.Store(preparedStmtDBKey, preparedStmt)\n\t\tdb.ConnPool = preparedStmt\n\t}\n\n\tdb.Statement = &Statement{\n\t\tDB:       db,\n\t\tConnPool: db.ConnPool,\n\t\tContext:  context.Background(),\n\t\tClauses:  map[string]clause.Clause{},\n\t}\n\n\tif err == nil && !config.DisableAutomaticPing {\n\t\tif pinger, ok := db.ConnPool.(interface{ Ping() error }); ok {\n\t\t\terr = pinger.Ping()\n\t\t\tif err != nil {\n\t\t\t\tif db, _ := db.DB(); db != nil {\n\t\t\t\t\t_ = db.Close()\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tif err != nil {\n\t\tconfig.Logger.Error(context.Background(), \"failed to initialize database, got error %v\", err)\n\t}\n\n\treturn\n}\n\n// Session create new db session\nfunc (db *DB) Session(config *Session) *DB {\n\tvar (\n\t\ttxConfig = *db.Config\n\t\ttx       = &DB{\n\t\t\tConfig:    &txConfig,\n\t\t\tStatement: db.Statement,\n\t\t\tError:     db.Error,\n\t\t\tclone:     1,\n\t\t}\n\t)\n\tif config.CreateBatchSize > 0 {\n\t\ttx.Config.CreateBatchSize = config.CreateBatchSize\n\t}\n\n\tif config.SkipDefaultTransaction {\n\t\ttx.Config.SkipDefaultTransaction = true\n\t}\n\n\tif config.AllowGlobalUpdate {\n\t\ttxConfig.AllowGlobalUpdate = true\n\t}\n\n\tif config.FullSaveAssociations {\n\t\ttxConfig.FullSaveAssociations = true\n\t}\n\n\tif config.PropagateUnscoped {\n\t\ttxConfig.PropagateUnscoped = true\n\t}\n\n\tif config.Context != nil || config.PrepareStmt || config.SkipHooks {\n\t\ttx.Statement = tx.Statement.clone()\n\t\ttx.Statement.DB = tx\n\t}\n\n\tif config.Context != nil {\n\t\ttx.Statement.Context = config.Context\n\t}\n\n\tif config.PrepareStmt {\n\t\tvar preparedStmt *PreparedStmtDB\n\n\t\tif v, ok := db.cacheStore.Load(preparedStmtDBKey); ok {\n\t\t\tpreparedStmt = v.(*PreparedStmtDB)\n\t\t} else {\n\t\t\tpreparedStmt = NewPreparedStmtDB(db.ConnPool, db.PrepareStmtMaxSize, db.PrepareStmtTTL)\n\t\t\tdb.cacheStore.Store(preparedStmtDBKey, preparedStmt)\n\t\t}\n\n\t\tswitch t := tx.Statement.ConnPool.(type) {\n\t\tcase Tx:\n\t\t\ttx.Statement.ConnPool = &PreparedStmtTX{\n\t\t\t\tTx:             t,\n\t\t\t\tPreparedStmtDB: preparedStmt,\n\t\t\t}\n\t\tdefault:\n\t\t\ttx.Statement.ConnPool = &PreparedStmtDB{\n\t\t\t\tConnPool: db.Config.ConnPool,\n\t\t\t\tMux:      preparedStmt.Mux,\n\t\t\t\tStmts:    preparedStmt.Stmts,\n\t\t\t}\n\t\t}\n\t\ttxConfig.ConnPool = tx.Statement.ConnPool\n\t\ttxConfig.PrepareStmt = true\n\t}\n\n\tif config.SkipHooks {\n\t\ttx.Statement.SkipHooks = true\n\t}\n\n\tif config.DisableNestedTransaction {\n\t\ttxConfig.DisableNestedTransaction = true\n\t}\n\n\tif !config.NewDB {\n\t\ttx.clone = 2\n\t}\n\n\tif config.DryRun {\n\t\ttx.Config.DryRun = true\n\t}\n\n\tif config.QueryFields {\n\t\ttx.Config.QueryFields = true\n\t}\n\n\tif config.Logger != nil {\n\t\ttx.Config.Logger = config.Logger\n\t}\n\n\tif config.NowFunc != nil {\n\t\ttx.Config.NowFunc = config.NowFunc\n\t}\n\n\tif config.Initialized {\n\t\ttx = tx.getInstance()\n\t}\n\n\treturn tx\n}\n\n// WithContext change current instance db's context to ctx\nfunc (db *DB) WithContext(ctx context.Context) *DB {\n\treturn db.Session(&Session{Context: ctx})\n}\n\n// Debug start debug mode\nfunc (db *DB) Debug() (tx *DB) {\n\ttx = db.getInstance()\n\treturn tx.Session(&Session{\n\t\tLogger: db.Logger.LogMode(logger.Info),\n\t})\n}\n\n// Set store value with key into current db instance's context\nfunc (db *DB) Set(key string, value interface{}) *DB {\n\ttx := db.getInstance()\n\ttx.Statement.Settings.Store(key, value)\n\treturn tx\n}\n\n// Get get value with key from current db instance's context\nfunc (db *DB) Get(key string) (interface{}, bool) {\n\treturn db.Statement.Settings.Load(key)\n}\n\n// InstanceSet store value with key into current db instance's context\nfunc (db *DB) InstanceSet(key string, value interface{}) *DB {\n\ttx := db.getInstance()\n\ttx.Statement.Settings.Store(fmt.Sprintf(\"%p\", tx.Statement)+key, value)\n\treturn tx\n}\n\n// InstanceGet get value with key from current db instance's context\nfunc (db *DB) InstanceGet(key string) (interface{}, bool) {\n\treturn db.Statement.Settings.Load(fmt.Sprintf(\"%p\", db.Statement) + key)\n}\n\n// Callback returns callback manager\nfunc (db *DB) Callback() *callbacks {\n\treturn db.callbacks\n}\n\n// AddError add error to db\nfunc (db *DB) AddError(err error) error {\n\tif err != nil {\n\t\tif db.Config.TranslateError {\n\t\t\tif errTranslator, ok := db.Dialector.(ErrorTranslator); ok {\n\t\t\t\terr = errTranslator.Translate(err)\n\t\t\t}\n\t\t}\n\n\t\tif db.Error == nil {\n\t\t\tdb.Error = err\n\t\t} else {\n\t\t\tdb.Error = fmt.Errorf(\"%v; %w\", db.Error, err)\n\t\t}\n\t}\n\treturn db.Error\n}\n\n// DB returns `*sql.DB`\nfunc (db *DB) DB() (*sql.DB, error) {\n\tconnPool := db.ConnPool\n\tif db.Statement != nil && db.Statement.ConnPool != nil {\n\t\tconnPool = db.Statement.ConnPool\n\t}\n\tif tx, ok := connPool.(*sql.Tx); ok && tx != nil {\n\t\treturn (*sql.DB)(reflect.ValueOf(tx).Elem().FieldByName(\"db\").UnsafePointer()), nil\n\t}\n\n\tif dbConnector, ok := connPool.(GetDBConnector); ok && dbConnector != nil {\n\t\tif sqldb, err := dbConnector.GetDBConn(); sqldb != nil || err != nil {\n\t\t\treturn sqldb, err\n\t\t}\n\t}\n\n\tif sqldb, ok := connPool.(*sql.DB); ok && sqldb != nil {\n\t\treturn sqldb, nil\n\t}\n\n\treturn nil, ErrInvalidDB\n}\n\nfunc (db *DB) getInstance() *DB {\n\tif db.clone > 0 {\n\t\ttx := &DB{Config: db.Config, Error: db.Error}\n\n\t\tif db.clone == 1 {\n\t\t\t// clone with new statement\n\t\t\ttx.Statement = &Statement{\n\t\t\t\tDB:        tx,\n\t\t\t\tConnPool:  db.Statement.ConnPool,\n\t\t\t\tContext:   db.Statement.Context,\n\t\t\t\tClauses:   map[string]clause.Clause{},\n\t\t\t\tVars:      make([]interface{}, 0, 8),\n\t\t\t\tSkipHooks: db.Statement.SkipHooks,\n\t\t\t}\n\t\t\tif db.Config.PropagateUnscoped {\n\t\t\t\ttx.Statement.Unscoped = db.Statement.Unscoped\n\t\t\t}\n\t\t} else {\n\t\t\t// with clone statement\n\t\t\ttx.Statement = db.Statement.clone()\n\t\t\ttx.Statement.DB = tx\n\t\t}\n\n\t\treturn tx\n\t}\n\n\treturn db\n}\n\n// Expr returns clause.Expr, which can be used to pass SQL expression as params\nfunc Expr(expr string, args ...interface{}) clause.Expr {\n\treturn clause.Expr{SQL: expr, Vars: args}\n}\n\n// SetupJoinTable setup join table schema\nfunc (db *DB) SetupJoinTable(model interface{}, field string, joinTable interface{}) error {\n\tvar (\n\t\ttx                      = db.getInstance()\n\t\tstmt                    = tx.Statement\n\t\tmodelSchema, joinSchema *schema.Schema\n\t)\n\n\terr := stmt.Parse(model)\n\tif err != nil {\n\t\treturn err\n\t}\n\tmodelSchema = stmt.Schema\n\n\terr = stmt.Parse(joinTable)\n\tif err != nil {\n\t\treturn err\n\t}\n\tjoinSchema = stmt.Schema\n\n\trelation, ok := modelSchema.Relationships.Relations[field]\n\tisRelation := ok && relation.JoinTable != nil\n\tif !isRelation {\n\t\treturn fmt.Errorf(\"failed to find relation: %s\", field)\n\t}\n\n\tfor _, ref := range relation.References {\n\t\tf := joinSchema.LookUpField(ref.ForeignKey.DBName)\n\t\tif f == nil {\n\t\t\treturn fmt.Errorf(\"missing field %s for join table\", ref.ForeignKey.DBName)\n\t\t}\n\n\t\tf.DataType = ref.ForeignKey.DataType\n\t\tf.GORMDataType = ref.ForeignKey.GORMDataType\n\t\tif f.Size == 0 {\n\t\t\tf.Size = ref.ForeignKey.Size\n\t\t}\n\t\tref.ForeignKey = f\n\t}\n\n\tfor name, rel := range relation.JoinTable.Relationships.Relations {\n\t\tif _, ok := joinSchema.Relationships.Relations[name]; !ok {\n\t\t\trel.Schema = joinSchema\n\t\t\tjoinSchema.Relationships.Relations[name] = rel\n\t\t}\n\t}\n\trelation.JoinTable = joinSchema\n\n\treturn nil\n}\n\n// Use use plugin\nfunc (db *DB) Use(plugin Plugin) error {\n\tname := plugin.Name()\n\tif _, ok := db.Plugins[name]; ok {\n\t\treturn ErrRegistered\n\t}\n\tif err := plugin.Initialize(db); err != nil {\n\t\treturn err\n\t}\n\tdb.Plugins[name] = plugin\n\treturn nil\n}\n\n// ToSQL for generate SQL string.\n//\n//\tdb.ToSQL(func(tx *gorm.DB) *gorm.DB {\n//\t\t\treturn tx.Model(&User{}).Where(&User{Name: \"foo\", Age: 20})\n//\t\t\t\t.Limit(10).Offset(5)\n//\t\t\t\t.Order(\"name ASC\")\n//\t\t\t\t.First(&User{})\n//\t})\nfunc (db *DB) ToSQL(queryFn func(tx *DB) *DB) string {\n\ttx := queryFn(db.Session(&Session{DryRun: true, SkipDefaultTransaction: true}).getInstance())\n\tstmt := tx.Statement\n\n\treturn db.Dialector.Explain(stmt.SQL.String(), stmt.Vars...)\n}\n"
  },
  {
    "path": "interfaces.go",
    "content": "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 database dialector\ntype Dialector interface {\n\tName() string\n\tInitialize(*DB) error\n\tMigrator(db *DB) Migrator\n\tDataTypeOf(*schema.Field) string\n\tDefaultValueOf(*schema.Field) clause.Expression\n\tBindVarTo(writer clause.Writer, stmt *Statement, v interface{})\n\tQuoteTo(clause.Writer, string)\n\tExplain(sql string, vars ...interface{}) string\n}\n\n// Plugin GORM plugin interface\ntype Plugin interface {\n\tName() string\n\tInitialize(*DB) error\n}\n\ntype ParamsFilter interface {\n\tParamsFilter(ctx context.Context, sql string, params ...interface{}) (string, []interface{})\n}\n\n// ConnPool db conns pool interface\ntype ConnPool interface {\n\tPrepareContext(ctx context.Context, query string) (*sql.Stmt, error)\n\tExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error)\n\tQueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error)\n\tQueryRowContext(ctx context.Context, query string, args ...interface{}) *sql.Row\n}\n\n// SavePointerDialectorInterface save pointer interface\ntype SavePointerDialectorInterface interface {\n\tSavePoint(tx *DB, name string) error\n\tRollbackTo(tx *DB, name string) error\n}\n\n// TxBeginner tx beginner\ntype TxBeginner interface {\n\tBeginTx(ctx context.Context, opts *sql.TxOptions) (*sql.Tx, error)\n}\n\n// ConnPoolBeginner conn pool beginner\ntype ConnPoolBeginner interface {\n\tBeginTx(ctx context.Context, opts *sql.TxOptions) (ConnPool, error)\n}\n\n// TxCommitter tx committer\ntype TxCommitter interface {\n\tCommit() error\n\tRollback() error\n}\n\n// Tx sql.Tx interface\ntype Tx interface {\n\tConnPool\n\tTxCommitter\n\tStmtContext(ctx context.Context, stmt *sql.Stmt) *sql.Stmt\n}\n\n// Valuer gorm valuer interface\ntype Valuer interface {\n\tGormValue(context.Context, *DB) clause.Expr\n}\n\n// GetDBConnector SQL db connector\ntype GetDBConnector interface {\n\tGetDBConn() (*sql.DB, error)\n}\n\n// Rows rows interface\ntype Rows interface {\n\tColumns() ([]string, error)\n\tColumnTypes() ([]*sql.ColumnType, error)\n\tNext() bool\n\tScan(dest ...interface{}) error\n\tErr() error\n\tClose() error\n}\n\ntype ErrorTranslator interface {\n\tTranslate(err error) error\n}\n"
  },
  {
    "path": "internal/lru/lru.go",
    "content": "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 used to get a callback when a cache entry is evicted\ntype EvictCallback[K comparable, V any] func(key K, value V)\n\n// LRU implements a thread-safe LRU with expirable entries.\ntype LRU[K comparable, V any] struct {\n\tsize      int\n\tevictList *LruList[K, V]\n\titems     map[K]*Entry[K, V]\n\tonEvict   EvictCallback[K, V]\n\n\t// expirable options\n\tmu   sync.RWMutex\n\tttl  time.Duration\n\tdone chan struct{}\n\n\t// buckets for expiration\n\tbuckets []bucket[K, V]\n\t// uint8 because it's number between 0 and numBuckets\n\tnextCleanupBucket uint8\n}\n\n// bucket is a container for holding entries to be expired\ntype bucket[K comparable, V any] struct {\n\tentries     map[K]*Entry[K, V]\n\tnewestEntry time.Time\n}\n\n// noEvictionTTL - very long ttl to prevent eviction\nconst noEvictionTTL = time.Hour * 24 * 365 * 10\n\n// because of uint8 usage for nextCleanupBucket, should not exceed 256.\n// casting it as uint8 explicitly requires type conversions in multiple places\nconst numBuckets = 100\n\n// NewLRU returns a new thread-safe cache with expirable entries.\n//\n// Size parameter set to 0 makes cache of unlimited size, e.g. turns LRU mechanism off.\n//\n// Providing 0 TTL turns expiring off.\n//\n// Delete expired entries every 1/100th of ttl value. Goroutine which deletes expired entries runs indefinitely.\nfunc NewLRU[K comparable, V any](size int, onEvict EvictCallback[K, V], ttl time.Duration) *LRU[K, V] {\n\tif size < 0 {\n\t\tsize = 0\n\t}\n\tif ttl <= 0 {\n\t\tttl = noEvictionTTL\n\t}\n\n\tres := LRU[K, V]{\n\t\tttl:       ttl,\n\t\tsize:      size,\n\t\tevictList: NewList[K, V](),\n\t\titems:     make(map[K]*Entry[K, V]),\n\t\tonEvict:   onEvict,\n\t\tdone:      make(chan struct{}),\n\t}\n\n\t// initialize the buckets\n\tres.buckets = make([]bucket[K, V], numBuckets)\n\tfor i := 0; i < numBuckets; i++ {\n\t\tres.buckets[i] = bucket[K, V]{entries: make(map[K]*Entry[K, V])}\n\t}\n\n\t// enable deleteExpired() running in separate goroutine for cache with non-zero TTL\n\t//\n\t// Important: done channel is never closed, so deleteExpired() goroutine will never exit,\n\t// it's decided to add functionality to close it in the version later than v2.\n\tif res.ttl != noEvictionTTL {\n\t\tgo func(done <-chan struct{}) {\n\t\t\tticker := time.NewTicker(res.ttl / numBuckets)\n\t\t\tdefer ticker.Stop()\n\t\t\tfor {\n\t\t\t\tselect {\n\t\t\t\tcase <-done:\n\t\t\t\t\treturn\n\t\t\t\tcase <-ticker.C:\n\t\t\t\t\tres.deleteExpired()\n\t\t\t\t}\n\t\t\t}\n\t\t}(res.done)\n\t}\n\treturn &res\n}\n\n// Purge clears the cache completely.\n// onEvict is called for each evicted key.\nfunc (c *LRU[K, V]) Purge() {\n\tc.mu.Lock()\n\tdefer c.mu.Unlock()\n\tfor k, v := range c.items {\n\t\tif c.onEvict != nil {\n\t\t\tc.onEvict(k, v.Value)\n\t\t}\n\t\tdelete(c.items, k)\n\t}\n\tfor _, b := range c.buckets {\n\t\tfor _, ent := range b.entries {\n\t\t\tdelete(b.entries, ent.Key)\n\t\t}\n\t}\n\tc.evictList.Init()\n}\n\n// Add adds a value to the cache. Returns true if an eviction occurred.\n// Returns false if there was no eviction: the item was already in the cache,\n// or the size was not exceeded.\nfunc (c *LRU[K, V]) Add(key K, value V) (evicted bool) {\n\tc.mu.Lock()\n\tdefer c.mu.Unlock()\n\tnow := time.Now()\n\n\t// Check for existing item\n\tif ent, ok := c.items[key]; ok {\n\t\tc.evictList.MoveToFront(ent)\n\t\tc.removeFromBucket(ent) // remove the entry from its current bucket as expiresAt is renewed\n\t\tent.Value = value\n\t\tent.ExpiresAt = now.Add(c.ttl)\n\t\tc.addToBucket(ent)\n\t\treturn false\n\t}\n\n\t// Add new item\n\tent := c.evictList.PushFrontExpirable(key, value, now.Add(c.ttl))\n\tc.items[key] = ent\n\tc.addToBucket(ent) // adds the entry to the appropriate bucket and sets entry.expireBucket\n\n\tevict := c.size > 0 && c.evictList.Length() > c.size\n\t// Verify size not exceeded\n\tif evict {\n\t\tc.removeOldest()\n\t}\n\treturn evict\n}\n\n// Get looks up a key's value from the cache.\nfunc (c *LRU[K, V]) Get(key K) (value V, ok bool) {\n\tc.mu.Lock()\n\tdefer c.mu.Unlock()\n\tvar ent *Entry[K, V]\n\tif ent, ok = c.items[key]; ok {\n\t\t// Expired item check\n\t\tif time.Now().After(ent.ExpiresAt) {\n\t\t\treturn value, false\n\t\t}\n\t\tc.evictList.MoveToFront(ent)\n\t\treturn ent.Value, true\n\t}\n\treturn\n}\n\n// Contains checks if a key is in the cache, without updating the recent-ness\n// or deleting it for being stale.\nfunc (c *LRU[K, V]) Contains(key K) (ok bool) {\n\tc.mu.RLock()\n\tdefer c.mu.RUnlock()\n\t_, ok = c.items[key]\n\treturn ok\n}\n\n// Peek returns the key value (or undefined if not found) without updating\n// the \"recently used\"-ness of the key.\nfunc (c *LRU[K, V]) Peek(key K) (value V, ok bool) {\n\tc.mu.RLock()\n\tdefer c.mu.RUnlock()\n\tvar ent *Entry[K, V]\n\tif ent, ok = c.items[key]; ok {\n\t\t// Expired item check\n\t\tif time.Now().After(ent.ExpiresAt) {\n\t\t\treturn value, false\n\t\t}\n\t\treturn ent.Value, true\n\t}\n\treturn\n}\n\n// Remove removes the provided key from the cache, returning if the\n// key was contained.\nfunc (c *LRU[K, V]) Remove(key K) bool {\n\tc.mu.Lock()\n\tdefer c.mu.Unlock()\n\tif ent, ok := c.items[key]; ok {\n\t\tc.removeElement(ent)\n\t\treturn true\n\t}\n\treturn false\n}\n\n// RemoveOldest removes the oldest item from the cache.\nfunc (c *LRU[K, V]) RemoveOldest() (key K, value V, ok bool) {\n\tc.mu.Lock()\n\tdefer c.mu.Unlock()\n\tif ent := c.evictList.Back(); ent != nil {\n\t\tc.removeElement(ent)\n\t\treturn ent.Key, ent.Value, true\n\t}\n\treturn\n}\n\n// GetOldest returns the oldest entry\nfunc (c *LRU[K, V]) GetOldest() (key K, value V, ok bool) {\n\tc.mu.RLock()\n\tdefer c.mu.RUnlock()\n\tif ent := c.evictList.Back(); ent != nil {\n\t\treturn ent.Key, ent.Value, true\n\t}\n\treturn\n}\n\nfunc (c *LRU[K, V]) KeyValues() map[K]V {\n\tc.mu.RLock()\n\tdefer c.mu.RUnlock()\n\tmaps := make(map[K]V)\n\tnow := time.Now()\n\tfor ent := c.evictList.Back(); ent != nil; ent = ent.PrevEntry() {\n\t\tif now.After(ent.ExpiresAt) {\n\t\t\tcontinue\n\t\t}\n\t\tmaps[ent.Key] = ent.Value\n\t\t// keys = append(keys, ent.Key)\n\t}\n\treturn maps\n}\n\n// Keys returns a slice of the keys in the cache, from oldest to newest.\n// Expired entries are filtered out.\nfunc (c *LRU[K, V]) Keys() []K {\n\tc.mu.RLock()\n\tdefer c.mu.RUnlock()\n\tkeys := make([]K, 0, len(c.items))\n\tnow := time.Now()\n\tfor ent := c.evictList.Back(); ent != nil; ent = ent.PrevEntry() {\n\t\tif now.After(ent.ExpiresAt) {\n\t\t\tcontinue\n\t\t}\n\t\tkeys = append(keys, ent.Key)\n\t}\n\treturn keys\n}\n\n// Values returns a slice of the values in the cache, from oldest to newest.\n// Expired entries are filtered out.\nfunc (c *LRU[K, V]) Values() []V {\n\tc.mu.RLock()\n\tdefer c.mu.RUnlock()\n\tvalues := make([]V, 0, len(c.items))\n\tnow := time.Now()\n\tfor ent := c.evictList.Back(); ent != nil; ent = ent.PrevEntry() {\n\t\tif now.After(ent.ExpiresAt) {\n\t\t\tcontinue\n\t\t}\n\t\tvalues = append(values, ent.Value)\n\t}\n\treturn values\n}\n\n// Len returns the number of items in the cache.\nfunc (c *LRU[K, V]) Len() int {\n\tc.mu.RLock()\n\tdefer c.mu.RUnlock()\n\treturn c.evictList.Length()\n}\n\n// Resize changes the cache size. Size of 0 means unlimited.\nfunc (c *LRU[K, V]) Resize(size int) (evicted int) {\n\tc.mu.Lock()\n\tdefer c.mu.Unlock()\n\tif size <= 0 {\n\t\tc.size = 0\n\t\treturn 0\n\t}\n\tdiff := c.evictList.Length() - size\n\tif diff < 0 {\n\t\tdiff = 0\n\t}\n\tfor i := 0; i < diff; i++ {\n\t\tc.removeOldest()\n\t}\n\tc.size = size\n\treturn diff\n}\n\n// Close destroys cleanup goroutine. To clean up the cache, run Purge() before Close().\n// func (c *LRU[K, V]) Close() {\n//\tc.mu.Lock()\n//\tdefer c.mu.Unlock()\n//\tselect {\n//\tcase <-c.done:\n//\t\treturn\n//\tdefault:\n//\t}\n//\tclose(c.done)\n// }\n\n// removeOldest removes the oldest item from the cache. Has to be called with lock!\nfunc (c *LRU[K, V]) removeOldest() {\n\tif ent := c.evictList.Back(); ent != nil {\n\t\tc.removeElement(ent)\n\t}\n}\n\n// removeElement is used to remove a given list element from the cache. Has to be called with lock!\nfunc (c *LRU[K, V]) removeElement(e *Entry[K, V]) {\n\tc.evictList.Remove(e)\n\tdelete(c.items, e.Key)\n\tc.removeFromBucket(e)\n\tif c.onEvict != nil {\n\t\tc.onEvict(e.Key, e.Value)\n\t}\n}\n\n// deleteExpired deletes expired records from the oldest bucket, waiting for the newest entry\n// in it to expire first.\nfunc (c *LRU[K, V]) deleteExpired() {\n\tc.mu.Lock()\n\tbucketIdx := c.nextCleanupBucket\n\ttimeToExpire := time.Until(c.buckets[bucketIdx].newestEntry)\n\t// wait for newest entry to expire before cleanup without holding lock\n\tif timeToExpire > 0 {\n\t\tc.mu.Unlock()\n\t\ttime.Sleep(timeToExpire)\n\t\tc.mu.Lock()\n\t}\n\tfor _, ent := range c.buckets[bucketIdx].entries {\n\t\tc.removeElement(ent)\n\t}\n\tc.nextCleanupBucket = (c.nextCleanupBucket + 1) % numBuckets\n\tc.mu.Unlock()\n}\n\n// addToBucket adds entry to expire bucket so that it will be cleaned up when the time comes. Has to be called with lock!\nfunc (c *LRU[K, V]) addToBucket(e *Entry[K, V]) {\n\tbucketID := (numBuckets + c.nextCleanupBucket - 1) % numBuckets\n\te.ExpireBucket = bucketID\n\tc.buckets[bucketID].entries[e.Key] = e\n\tif c.buckets[bucketID].newestEntry.Before(e.ExpiresAt) {\n\t\tc.buckets[bucketID].newestEntry = e.ExpiresAt\n\t}\n}\n\n// removeFromBucket removes the entry from its corresponding bucket. Has to be called with lock!\nfunc (c *LRU[K, V]) removeFromBucket(e *Entry[K, V]) {\n\tdelete(c.buckets[e.ExpireBucket].entries, e.Key)\n}\n\n// Cap returns the capacity of the cache\nfunc (c *LRU[K, V]) Cap() int {\n\treturn c.size\n}\n\n// Entry is an LRU Entry\ntype Entry[K comparable, V any] struct {\n\t// Next and previous pointers in the doubly-linked list of elements.\n\t// To simplify the implementation, internally a list l is implemented\n\t// as a ring, such that &l.root is both the next element of the last\n\t// list element (l.Back()) and the previous element of the first list\n\t// element (l.Front()).\n\tnext, prev *Entry[K, V]\n\n\t// The list to which this element belongs.\n\tlist *LruList[K, V]\n\n\t// The LRU Key of this element.\n\tKey K\n\n\t// The Value stored with this element.\n\tValue V\n\n\t// The time this element would be cleaned up, optional\n\tExpiresAt time.Time\n\n\t// The expiry bucket item was put in, optional\n\tExpireBucket uint8\n}\n\n// PrevEntry returns the previous list element or nil.\nfunc (e *Entry[K, V]) PrevEntry() *Entry[K, V] {\n\tif p := e.prev; e.list != nil && p != &e.list.root {\n\t\treturn p\n\t}\n\treturn nil\n}\n\n// LruList represents a doubly linked list.\n// The zero Value for LruList is an empty list ready to use.\ntype LruList[K comparable, V any] struct {\n\troot Entry[K, V] // sentinel list element, only &root, root.prev, and root.next are used\n\tlen  int         // current list Length excluding (this) sentinel element\n}\n\n// Init initializes or clears list l.\nfunc (l *LruList[K, V]) Init() *LruList[K, V] {\n\tl.root.next = &l.root\n\tl.root.prev = &l.root\n\tl.len = 0\n\treturn l\n}\n\n// NewList returns an initialized list.\nfunc NewList[K comparable, V any]() *LruList[K, V] { return new(LruList[K, V]).Init() }\n\n// Length returns the number of elements of list l.\n// The complexity is O(1).\nfunc (l *LruList[K, V]) Length() int { return l.len }\n\n// Back returns the last element of list l or nil if the list is empty.\nfunc (l *LruList[K, V]) Back() *Entry[K, V] {\n\tif l.len == 0 {\n\t\treturn nil\n\t}\n\treturn l.root.prev\n}\n\n// lazyInit lazily initializes a zero List Value.\nfunc (l *LruList[K, V]) lazyInit() {\n\tif l.root.next == nil {\n\t\tl.Init()\n\t}\n}\n\n// insert inserts e after at, increments l.len, and returns e.\nfunc (l *LruList[K, V]) insert(e, at *Entry[K, V]) *Entry[K, V] {\n\te.prev = at\n\te.next = at.next\n\te.prev.next = e\n\te.next.prev = e\n\te.list = l\n\tl.len++\n\treturn e\n}\n\n// insertValue is a convenience wrapper for insert(&Entry{Value: v, ExpiresAt: ExpiresAt}, at).\nfunc (l *LruList[K, V]) insertValue(k K, v V, expiresAt time.Time, at *Entry[K, V]) *Entry[K, V] {\n\treturn l.insert(&Entry[K, V]{Value: v, Key: k, ExpiresAt: expiresAt}, at)\n}\n\n// Remove removes e from its list, decrements l.len\nfunc (l *LruList[K, V]) Remove(e *Entry[K, V]) V {\n\te.prev.next = e.next\n\te.next.prev = e.prev\n\te.next = nil // avoid memory leaks\n\te.prev = nil // avoid memory leaks\n\te.list = nil\n\tl.len--\n\n\treturn e.Value\n}\n\n// move moves e to next to at.\nfunc (l *LruList[K, V]) move(e, at *Entry[K, V]) {\n\tif e == at {\n\t\treturn\n\t}\n\te.prev.next = e.next\n\te.next.prev = e.prev\n\n\te.prev = at\n\te.next = at.next\n\te.prev.next = e\n\te.next.prev = e\n}\n\n// PushFront inserts a new element e with value v at the front of list l and returns e.\nfunc (l *LruList[K, V]) PushFront(k K, v V) *Entry[K, V] {\n\tl.lazyInit()\n\treturn l.insertValue(k, v, time.Time{}, &l.root)\n}\n\n// PushFrontExpirable inserts a new expirable element e with Value v at the front of list l and returns e.\nfunc (l *LruList[K, V]) PushFrontExpirable(k K, v V, expiresAt time.Time) *Entry[K, V] {\n\tl.lazyInit()\n\treturn l.insertValue(k, v, expiresAt, &l.root)\n}\n\n// MoveToFront moves element e to the front of list l.\n// If e is not an element of l, the list is not modified.\n// The element must not be nil.\nfunc (l *LruList[K, V]) MoveToFront(e *Entry[K, V]) {\n\tif e.list != l || l.root.next == e {\n\t\treturn\n\t}\n\t// see comment in List.Remove about initialization of l\n\tl.move(e, &l.root)\n}\n"
  },
  {
    "path": "internal/stmt_store/stmt_store.go",
    "content": "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 Stmt struct {\n\t*sql.Stmt\n\tTransaction bool\n\tprepared    chan struct{}\n\tprepareErr  error\n}\n\nfunc (stmt *Stmt) Error() error {\n\treturn stmt.prepareErr\n}\n\nfunc (stmt *Stmt) Close() error {\n\t<-stmt.prepared\n\n\tif stmt.Stmt != nil {\n\t\treturn stmt.Stmt.Close()\n\t}\n\treturn nil\n}\n\n// Store defines an interface for managing the caching operations of SQL statements (Stmt).\n// This interface provides methods for creating new statements, retrieving all cache keys,\n// getting cached statements, setting cached statements, and deleting cached statements.\ntype Store interface {\n\t// New creates a new Stmt object and caches it.\n\t// Parameters:\n\t//   ctx: The context for the request, which can carry deadlines, cancellation signals, etc.\n\t//   key: The key representing the SQL query, used for caching and preparing the statement.\n\t//   isTransaction: Indicates whether this operation is part of a transaction, which may affect the caching strategy.\n\t//   connPool: A connection pool that provides database connections.\n\t//   locker: A synchronization lock that is unlocked after initialization to avoid deadlocks.\n\t// Returns:\n\t//   *Stmt: A newly created statement object for executing SQL operations.\n\t//   error: An error if the statement preparation fails.\n\tNew(ctx context.Context, key string, isTransaction bool, connPool ConnPool, locker sync.Locker) (*Stmt, error)\n\n\t// Keys returns a slice of all cache keys in the store.\n\tKeys() []string\n\n\t// Get retrieves a Stmt object from the store based on the given key.\n\t// Parameters:\n\t//   key: The key used to look up the Stmt object.\n\t// Returns:\n\t//   *Stmt: The found Stmt object, or nil if not found.\n\t//   bool: Indicates whether the corresponding Stmt object was successfully found.\n\tGet(key string) (*Stmt, bool)\n\n\t// Set stores the given Stmt object in the store and associates it with the specified key.\n\t// Parameters:\n\t//   key: The key used to associate the Stmt object.\n\t//   value: The Stmt object to be stored.\n\tSet(key string, value *Stmt)\n\n\t// Delete removes the Stmt object corresponding to the specified key from the store.\n\t// Parameters:\n\t//   key: The key associated with the Stmt object to be deleted.\n\tDelete(key string)\n}\n\n// defaultMaxSize defines the default maximum capacity of the cache.\n// Its value is the maximum value of the int64 type, which means that when the cache size is not specified,\n// the cache can theoretically store as many elements as possible.\n// (1 << 63) - 1 is the maximum value that an int64 type can represent.\nconst (\n\tdefaultMaxSize = math.MaxInt\n\t// defaultTTL defines the default time-to-live (TTL) for each cache entry.\n\t// When the TTL for cache entries is not specified, each cache entry will expire after 24 hours.\n\tdefaultTTL = time.Hour * 24\n)\n\n// New creates and returns a new Store instance.\n//\n// Parameters:\n//   - size: The maximum capacity of the cache. If the provided size is less than or equal to 0,\n//     it defaults to defaultMaxSize.\n//   - ttl: The time-to-live duration for each cache entry. If the provided ttl is less than or equal to 0,\n//     it defaults to defaultTTL.\n//\n// This function defines an onEvicted callback that is invoked when a cache entry is evicted.\n// The callback ensures that if the evicted value (v) is not nil, its Close method is called asynchronously\n// to release associated resources.\n//\n// Returns:\n//   - A Store instance implemented by lruStore, which internally uses an LRU cache with the specified size,\n//     eviction callback, and TTL.\nfunc New(size int, ttl time.Duration) Store {\n\tif size <= 0 {\n\t\tsize = defaultMaxSize\n\t}\n\n\tif ttl <= 0 {\n\t\tttl = defaultTTL\n\t}\n\n\tonEvicted := func(k string, v *Stmt) {\n\t\tif v != nil {\n\t\t\tgo v.Close()\n\t\t}\n\t}\n\treturn &lruStore{lru: lru.NewLRU[string, *Stmt](size, onEvicted, ttl)}\n}\n\ntype lruStore struct {\n\tlru *lru.LRU[string, *Stmt]\n}\n\nfunc (s *lruStore) Keys() []string {\n\treturn s.lru.Keys()\n}\n\nfunc (s *lruStore) Get(key string) (*Stmt, bool) {\n\tstmt, ok := s.lru.Get(key)\n\tif ok && stmt != nil {\n\t\t<-stmt.prepared\n\t}\n\treturn stmt, ok\n}\n\nfunc (s *lruStore) Set(key string, value *Stmt) {\n\ts.lru.Add(key, value)\n}\n\nfunc (s *lruStore) Delete(key string) {\n\ts.lru.Remove(key)\n}\n\ntype ConnPool interface {\n\tPrepareContext(ctx context.Context, query string) (*sql.Stmt, error)\n}\n\n// New creates a new Stmt object for executing SQL queries.\n// It caches the Stmt object for future use and handles preparation and error states.\n// Parameters:\n//\n//\tctx: Context for the request, used to carry deadlines, cancellation signals, etc.\n//\tkey: The key representing the SQL query, used for caching and preparing the statement.\n//\tisTransaction: Indicates whether this operation is part of a transaction, affecting cache strategy.\n//\tconn: A connection pool that provides database connections.\n//\tlocker: A synchronization lock that is unlocked after initialization to avoid deadlocks.\n//\n// Returns:\n//\n//\t*Stmt: A newly created statement object for executing SQL operations.\n//\terror: An error if the statement preparation fails.\nfunc (s *lruStore) New(ctx context.Context, key string, isTransaction bool, conn ConnPool, locker sync.Locker) (_ *Stmt, err error) {\n\t// Create a Stmt object and set its Transaction property.\n\t// The prepared channel is used to synchronize the statement preparation state.\n\tcacheStmt := &Stmt{\n\t\tTransaction: isTransaction,\n\t\tprepared:    make(chan struct{}),\n\t}\n\t// Cache the Stmt object with the associated key.\n\ts.Set(key, cacheStmt)\n\t// Unlock after completing initialization to prevent deadlocks.\n\tlocker.Unlock()\n\n\t// Ensure the prepared channel is closed after the function execution completes.\n\tdefer close(cacheStmt.prepared)\n\n\t// Prepare the SQL statement using the provided connection.\n\tcacheStmt.Stmt, err = conn.PrepareContext(ctx, key)\n\tif err != nil {\n\t\t// If statement preparation fails, record the error and remove the invalid Stmt object from the cache.\n\t\tcacheStmt.prepareErr = err\n\t\ts.Delete(key)\n\t\treturn &Stmt{}, err\n\t}\n\n\t// Return the successfully prepared Stmt object.\n\treturn cacheStmt, nil\n}\n"
  },
  {
    "path": "logger/logger.go",
    "content": "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// ErrRecordNotFound record not found error\nvar ErrRecordNotFound = errors.New(\"record not found\")\n\n// Colors\nconst (\n\tReset       = \"\\033[0m\"\n\tRed         = \"\\033[31m\"\n\tGreen       = \"\\033[32m\"\n\tYellow      = \"\\033[33m\"\n\tBlue        = \"\\033[34m\"\n\tMagenta     = \"\\033[35m\"\n\tCyan        = \"\\033[36m\"\n\tWhite       = \"\\033[37m\"\n\tBlueBold    = \"\\033[34;1m\"\n\tMagentaBold = \"\\033[35;1m\"\n\tRedBold     = \"\\033[31;1m\"\n\tYellowBold  = \"\\033[33;1m\"\n)\n\n// LogLevel log level\ntype LogLevel int\n\nconst (\n\t// Silent silent log level\n\tSilent LogLevel = iota + 1\n\t// Error error log level\n\tError\n\t// Warn warn log level\n\tWarn\n\t// Info info log level\n\tInfo\n)\n\n// Writer log writer interface\ntype Writer interface {\n\tPrintf(string, ...interface{})\n}\n\n// Config logger config\ntype Config struct {\n\tSlowThreshold             time.Duration\n\tColorful                  bool\n\tIgnoreRecordNotFoundError bool\n\tParameterizedQueries      bool\n\tLogLevel                  LogLevel\n}\n\n// Interface logger interface\ntype Interface interface {\n\tLogMode(LogLevel) Interface\n\tInfo(context.Context, string, ...interface{})\n\tWarn(context.Context, string, ...interface{})\n\tError(context.Context, string, ...interface{})\n\tTrace(ctx context.Context, begin time.Time, fc func() (sql string, rowsAffected int64), err error)\n}\n\nvar (\n\t// Discard logger will print any log to io.Discard\n\tDiscard = New(log.New(io.Discard, \"\", log.LstdFlags), Config{})\n\t// Default Default logger\n\tDefault = New(log.New(os.Stdout, \"\\r\\n\", log.LstdFlags), Config{\n\t\tSlowThreshold:             200 * time.Millisecond,\n\t\tLogLevel:                  Warn,\n\t\tIgnoreRecordNotFoundError: false,\n\t\tColorful:                  true,\n\t})\n\t// Recorder logger records running SQL into a recorder instance\n\tRecorder = traceRecorder{Interface: Default, BeginAt: time.Now()}\n\n\t// RecorderParamsFilter defaults to no-op, allows to be run-over by a different implementation\n\tRecorderParamsFilter = func(ctx context.Context, sql string, params ...interface{}) (string, []interface{}) {\n\t\treturn sql, params\n\t}\n)\n\n// New initialize logger\nfunc New(writer Writer, config Config) Interface {\n\tvar (\n\t\tinfoStr      = \"%s\\n[info] \"\n\t\twarnStr      = \"%s\\n[warn] \"\n\t\terrStr       = \"%s\\n[error] \"\n\t\ttraceStr     = \"%s\\n[%.3fms] [rows:%v] %s\"\n\t\ttraceWarnStr = \"%s %s\\n[%.3fms] [rows:%v] %s\"\n\t\ttraceErrStr  = \"%s %s\\n[%.3fms] [rows:%v] %s\"\n\t)\n\n\tif config.Colorful {\n\t\tinfoStr = Green + \"%s\\n\" + Reset + Green + \"[info] \" + Reset\n\t\twarnStr = BlueBold + \"%s\\n\" + Reset + Magenta + \"[warn] \" + Reset\n\t\terrStr = Magenta + \"%s\\n\" + Reset + Red + \"[error] \" + Reset\n\t\ttraceStr = Green + \"%s\\n\" + Reset + Yellow + \"[%.3fms] \" + BlueBold + \"[rows:%v]\" + Reset + \" %s\"\n\t\ttraceWarnStr = Green + \"%s \" + Yellow + \"%s\\n\" + Reset + RedBold + \"[%.3fms] \" + Yellow + \"[rows:%v]\" + Magenta + \" %s\" + Reset\n\t\ttraceErrStr = RedBold + \"%s \" + MagentaBold + \"%s\\n\" + Reset + Yellow + \"[%.3fms] \" + BlueBold + \"[rows:%v]\" + Reset + \" %s\"\n\t}\n\n\treturn &logger{\n\t\tWriter:       writer,\n\t\tConfig:       config,\n\t\tinfoStr:      infoStr,\n\t\twarnStr:      warnStr,\n\t\terrStr:       errStr,\n\t\ttraceStr:     traceStr,\n\t\ttraceWarnStr: traceWarnStr,\n\t\ttraceErrStr:  traceErrStr,\n\t}\n}\n\ntype logger struct {\n\tWriter\n\tConfig\n\tinfoStr, warnStr, errStr            string\n\ttraceStr, traceErrStr, traceWarnStr string\n}\n\n// LogMode log mode\nfunc (l *logger) LogMode(level LogLevel) Interface {\n\tnewlogger := *l\n\tnewlogger.LogLevel = level\n\treturn &newlogger\n}\n\n// Info print info\nfunc (l *logger) Info(ctx context.Context, msg string, data ...interface{}) {\n\tif l.LogLevel >= Info {\n\t\tl.Printf(l.infoStr+msg, append([]interface{}{utils.FileWithLineNum()}, data...)...)\n\t}\n}\n\n// Warn print warn messages\nfunc (l *logger) Warn(ctx context.Context, msg string, data ...interface{}) {\n\tif l.LogLevel >= Warn {\n\t\tl.Printf(l.warnStr+msg, append([]interface{}{utils.FileWithLineNum()}, data...)...)\n\t}\n}\n\n// Error print error messages\nfunc (l *logger) Error(ctx context.Context, msg string, data ...interface{}) {\n\tif l.LogLevel >= Error {\n\t\tl.Printf(l.errStr+msg, append([]interface{}{utils.FileWithLineNum()}, data...)...)\n\t}\n}\n\n// Trace print sql message\n//\n//nolint:cyclop\nfunc (l *logger) Trace(ctx context.Context, begin time.Time, fc func() (string, int64), err error) {\n\tif l.LogLevel <= Silent {\n\t\treturn\n\t}\n\n\telapsed := time.Since(begin)\n\tswitch {\n\tcase err != nil && l.LogLevel >= Error && (!errors.Is(err, ErrRecordNotFound) || !l.IgnoreRecordNotFoundError):\n\t\tsql, rows := fc()\n\t\tif rows == -1 {\n\t\t\tl.Printf(l.traceErrStr, utils.FileWithLineNum(), err, float64(elapsed.Nanoseconds())/1e6, \"-\", sql)\n\t\t} else {\n\t\t\tl.Printf(l.traceErrStr, utils.FileWithLineNum(), err, float64(elapsed.Nanoseconds())/1e6, rows, sql)\n\t\t}\n\tcase elapsed > l.SlowThreshold && l.SlowThreshold != 0 && l.LogLevel >= Warn:\n\t\tsql, rows := fc()\n\t\tslowLog := fmt.Sprintf(\"SLOW SQL >= %v\", l.SlowThreshold)\n\t\tif rows == -1 {\n\t\t\tl.Printf(l.traceWarnStr, utils.FileWithLineNum(), slowLog, float64(elapsed.Nanoseconds())/1e6, \"-\", sql)\n\t\t} else {\n\t\t\tl.Printf(l.traceWarnStr, utils.FileWithLineNum(), slowLog, float64(elapsed.Nanoseconds())/1e6, rows, sql)\n\t\t}\n\tcase l.LogLevel == Info:\n\t\tsql, rows := fc()\n\t\tif rows == -1 {\n\t\t\tl.Printf(l.traceStr, utils.FileWithLineNum(), float64(elapsed.Nanoseconds())/1e6, \"-\", sql)\n\t\t} else {\n\t\t\tl.Printf(l.traceStr, utils.FileWithLineNum(), float64(elapsed.Nanoseconds())/1e6, rows, sql)\n\t\t}\n\t}\n}\n\n// ParamsFilter filter params\nfunc (l *logger) ParamsFilter(ctx context.Context, sql string, params ...interface{}) (string, []interface{}) {\n\tif l.Config.ParameterizedQueries {\n\t\treturn sql, nil\n\t}\n\treturn sql, params\n}\n\ntype traceRecorder struct {\n\tInterface\n\tBeginAt      time.Time\n\tSQL          string\n\tRowsAffected int64\n\tErr          error\n}\n\n// New trace recorder\nfunc (l *traceRecorder) New() *traceRecorder {\n\treturn &traceRecorder{Interface: l.Interface, BeginAt: time.Now()}\n}\n\n// Trace implement logger interface\nfunc (l *traceRecorder) Trace(ctx context.Context, begin time.Time, fc func() (string, int64), err error) {\n\tl.BeginAt = begin\n\tl.SQL, l.RowsAffected = fc()\n\tl.Err = err\n}\n\nfunc (l *traceRecorder) ParamsFilter(ctx context.Context, sql string, params ...interface{}) (string, []interface{}) {\n\tif RecorderParamsFilter == nil {\n\t\treturn sql, params\n\t}\n\treturn RecorderParamsFilter(ctx, sql, params...)\n}\n"
  },
  {
    "path": "logger/slog.go",
    "content": "//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\ntype slogLogger struct {\n\tLogger                    *slog.Logger\n\tLogLevel                  LogLevel\n\tSlowThreshold             time.Duration\n\tParameterized             bool\n\tColorful                  bool // Ignored in slog\n\tIgnoreRecordNotFoundError bool\n}\n\nfunc NewSlogLogger(logger *slog.Logger, config Config) Interface {\n\treturn &slogLogger{\n\t\tLogger:                    logger,\n\t\tLogLevel:                  config.LogLevel,\n\t\tSlowThreshold:             config.SlowThreshold,\n\t\tParameterized:             config.ParameterizedQueries,\n\t\tIgnoreRecordNotFoundError: config.IgnoreRecordNotFoundError,\n\t}\n}\n\nfunc (l *slogLogger) LogMode(level LogLevel) Interface {\n\tnewLogger := *l\n\tnewLogger.LogLevel = level\n\treturn &newLogger\n}\n\nfunc (l *slogLogger) Info(ctx context.Context, msg string, data ...interface{}) {\n\tif l.LogLevel >= Info {\n\t\tl.log(ctx, slog.LevelInfo, msg, slog.Any(\"data\", data))\n\t}\n}\n\nfunc (l *slogLogger) Warn(ctx context.Context, msg string, data ...interface{}) {\n\tif l.LogLevel >= Warn {\n\t\tl.log(ctx, slog.LevelWarn, msg, slog.Any(\"data\", data))\n\t}\n}\n\nfunc (l *slogLogger) Error(ctx context.Context, msg string, data ...interface{}) {\n\tif l.LogLevel >= Error {\n\t\tl.log(ctx, slog.LevelError, msg, slog.Any(\"data\", data))\n\t}\n}\n\nfunc (l *slogLogger) Trace(ctx context.Context, begin time.Time, fc func() (sql string, rowsAffected int64), err error) {\n\tif l.LogLevel <= Silent {\n\t\treturn\n\t}\n\n\telapsed := time.Since(begin)\n\tsql, rows := fc()\n\tfields := []slog.Attr{\n\t\tslog.String(\"duration\", fmt.Sprintf(\"%.3fms\", float64(elapsed.Nanoseconds())/1e6)),\n\t\tslog.String(\"sql\", sql),\n\t}\n\n\tif rows != -1 {\n\t\tfields = append(fields, slog.Int64(\"rows\", rows))\n\t}\n\n\tswitch {\n\tcase err != nil && (!l.IgnoreRecordNotFoundError || !errors.Is(err, ErrRecordNotFound)):\n\t\tfields = append(fields, slog.String(\"error\", err.Error()))\n\t\tl.log(ctx, slog.LevelError, \"SQL executed\", slog.Attr{\n\t\t\tKey:   \"trace\",\n\t\t\tValue: slog.GroupValue(fields...),\n\t\t})\n\n\tcase l.SlowThreshold != 0 && elapsed > l.SlowThreshold:\n\t\tl.log(ctx, slog.LevelWarn, \"SQL executed\", slog.Attr{\n\t\t\tKey:   \"trace\",\n\t\t\tValue: slog.GroupValue(fields...),\n\t\t})\n\n\tcase l.LogLevel >= Info:\n\t\tl.log(ctx, slog.LevelInfo, \"SQL executed\", slog.Attr{\n\t\t\tKey:   \"trace\",\n\t\t\tValue: slog.GroupValue(fields...),\n\t\t})\n\t}\n}\n\nfunc (l *slogLogger) log(ctx context.Context, level slog.Level, msg string, args ...any) {\n\tif ctx == nil {\n\t\tctx = context.Background()\n\t}\n\n\tif !l.Logger.Enabled(ctx, level) {\n\t\treturn\n\t}\n\n\tr := slog.NewRecord(time.Now(), level, msg, utils.CallerFrame().PC)\n\tr.Add(args...)\n\t_ = l.Logger.Handler().Handle(ctx, r)\n}\n\n// ParamsFilter filter params\nfunc (l *slogLogger) ParamsFilter(ctx context.Context, sql string, params ...interface{}) (string, []interface{}) {\n\tif l.Parameterized {\n\t\treturn sql, nil\n\t}\n\treturn sql, params\n}\n"
  },
  {
    "path": "logger/slog_test.go",
    "content": "//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 TestSlogLogger(t *testing.T) {\n\tbuf := &bytes.Buffer{}\n\thandler := slog.NewTextHandler(buf, &slog.HandlerOptions{AddSource: true})\n\tlogger := NewSlogLogger(slog.New(handler), Config{LogLevel: Info})\n\n\tlogger.Trace(context.Background(), time.Now(), func() (string, int64) {\n\t\treturn \"select count(*) from users\", 0\n\t}, nil)\n\n\tif strings.Contains(buf.String(), \"gorm/logger/slog.go\") {\n\t\tt.Error(\"Found internal slog.go reference in caller frame. Expected only test file references.\")\n\t}\n\n\tif !strings.Contains(buf.String(), \"gorm/logger/slog_test.go\") {\n\t\tt.Error(\"Missing expected test file reference. 'gorm/logger/slog_test.go' should appear in caller frames.\")\n\t}\n}\n"
  },
  {
    "path": "logger/sql.go",
    "content": "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\"gorm.io/gorm/utils\"\n)\n\nconst (\n\ttmFmtWithMS = \"2006-01-02 15:04:05.999\"\n\ttmFmtZero   = \"0000-00-00 00:00:00\"\n\tnullStr     = \"NULL\"\n)\n\nfunc isPrintable(s string) bool {\n\tfor _, r := range s {\n\t\tif !unicode.IsPrint(r) {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\n// A list of Go types that should be converted to SQL primitives\nvar convertibleTypes = []reflect.Type{reflect.TypeOf(time.Time{}), reflect.TypeOf(false), reflect.TypeOf([]byte{})}\n\n// RegEx matches only numeric values\nvar numericPlaceholderRe = regexp.MustCompile(`\\$\\d+\\$`)\n\nfunc isNumeric(k reflect.Kind) bool {\n\tswitch k {\n\tcase reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:\n\t\treturn true\n\tcase reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:\n\t\treturn true\n\tcase reflect.Float32, reflect.Float64:\n\t\treturn true\n\tdefault:\n\t\treturn false\n\t}\n}\n\n// ExplainSQL generate SQL string with given parameters, the generated SQL is expected to be used in logger, execute it might introduce a SQL injection vulnerability\nfunc ExplainSQL(sql string, numericPlaceholder *regexp.Regexp, escaper string, avars ...interface{}) string {\n\tvar (\n\t\tconvertParams func(interface{}, int)\n\t\tvars          = make([]string, len(avars))\n\t)\n\n\tconvertParams = func(v interface{}, idx int) {\n\t\tswitch v := v.(type) {\n\t\tcase bool:\n\t\t\tvars[idx] = strconv.FormatBool(v)\n\t\tcase time.Time:\n\t\t\tif v.IsZero() {\n\t\t\t\tvars[idx] = escaper + tmFmtZero + escaper\n\t\t\t} else {\n\t\t\t\tvars[idx] = escaper + v.Format(tmFmtWithMS) + escaper\n\t\t\t}\n\t\tcase *time.Time:\n\t\t\tif v != nil {\n\t\t\t\tif v.IsZero() {\n\t\t\t\t\tvars[idx] = escaper + tmFmtZero + escaper\n\t\t\t\t} else {\n\t\t\t\t\tvars[idx] = escaper + v.Format(tmFmtWithMS) + escaper\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tvars[idx] = nullStr\n\t\t\t}\n\t\tcase driver.Valuer:\n\t\t\treflectValue := reflect.ValueOf(v)\n\t\t\tif v != nil && reflectValue.IsValid() && ((reflectValue.Kind() == reflect.Ptr && !reflectValue.IsNil()) || reflectValue.Kind() != reflect.Ptr) {\n\t\t\t\tr, _ := v.Value()\n\t\t\t\tconvertParams(r, idx)\n\t\t\t} else {\n\t\t\t\tvars[idx] = nullStr\n\t\t\t}\n\t\tcase fmt.Stringer:\n\t\t\treflectValue := reflect.ValueOf(v)\n\t\t\tswitch reflectValue.Kind() {\n\t\t\tcase reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:\n\t\t\t\tvars[idx] = fmt.Sprintf(\"%d\", reflectValue.Interface())\n\t\t\tcase reflect.Float32, reflect.Float64:\n\t\t\t\tvars[idx] = fmt.Sprintf(\"%.6f\", reflectValue.Interface())\n\t\t\tcase reflect.Bool:\n\t\t\t\tvars[idx] = fmt.Sprintf(\"%t\", reflectValue.Interface())\n\t\t\tcase reflect.String:\n\t\t\t\tvars[idx] = escaper + strings.ReplaceAll(fmt.Sprintf(\"%v\", v), escaper, escaper+escaper) + escaper\n\t\t\tdefault:\n\t\t\t\tif v != nil && reflectValue.IsValid() && ((reflectValue.Kind() == reflect.Ptr && !reflectValue.IsNil()) || reflectValue.Kind() != reflect.Ptr) {\n\t\t\t\t\tvars[idx] = escaper + strings.ReplaceAll(fmt.Sprintf(\"%v\", v), escaper, escaper+escaper) + escaper\n\t\t\t\t} else {\n\t\t\t\t\tvars[idx] = nullStr\n\t\t\t\t}\n\t\t\t}\n\t\tcase []byte:\n\t\t\tif s := string(v); isPrintable(s) {\n\t\t\t\tvars[idx] = escaper + strings.ReplaceAll(s, escaper, escaper+escaper) + escaper\n\t\t\t} else {\n\t\t\t\tvars[idx] = escaper + \"<binary>\" + escaper\n\t\t\t}\n\t\tcase int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:\n\t\t\tvars[idx] = utils.ToString(v)\n\t\tcase float32:\n\t\t\tvars[idx] = strconv.FormatFloat(float64(v), 'f', -1, 32)\n\t\tcase float64:\n\t\t\tvars[idx] = strconv.FormatFloat(v, 'f', -1, 64)\n\t\tcase string:\n\t\t\tvars[idx] = escaper + strings.ReplaceAll(v, escaper, escaper+escaper) + escaper\n\t\tdefault:\n\t\t\trv := reflect.ValueOf(v)\n\t\t\tif v == nil || !rv.IsValid() || rv.Kind() == reflect.Ptr && rv.IsNil() {\n\t\t\t\tvars[idx] = nullStr\n\t\t\t} else if valuer, ok := v.(driver.Valuer); ok {\n\t\t\t\tv, _ = valuer.Value()\n\t\t\t\tconvertParams(v, idx)\n\t\t\t} else if rv.Kind() == reflect.Ptr && !rv.IsZero() {\n\t\t\t\tconvertParams(reflect.Indirect(rv).Interface(), idx)\n\t\t\t} else if isNumeric(rv.Kind()) {\n\t\t\t\tif rv.CanInt() || rv.CanUint() {\n\t\t\t\t\tvars[idx] = fmt.Sprintf(\"%d\", rv.Interface())\n\t\t\t\t} else {\n\t\t\t\t\tvars[idx] = fmt.Sprintf(\"%.6f\", rv.Interface())\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfor _, t := range convertibleTypes {\n\t\t\t\t\tif rv.Type().ConvertibleTo(t) {\n\t\t\t\t\t\tconvertParams(rv.Convert(t).Interface(), idx)\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tvars[idx] = escaper + strings.ReplaceAll(fmt.Sprint(v), escaper, escaper+escaper) + escaper\n\t\t\t}\n\t\t}\n\t}\n\n\tfor idx, v := range avars {\n\t\tconvertParams(v, idx)\n\t}\n\n\tif numericPlaceholder == nil {\n\t\tvar idx int\n\t\tvar newSQL strings.Builder\n\n\t\tfor _, v := range []byte(sql) {\n\t\t\tif v == '?' {\n\t\t\t\tif len(vars) > idx {\n\t\t\t\t\tnewSQL.WriteString(vars[idx])\n\t\t\t\t\tidx++\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t}\n\t\t\tnewSQL.WriteByte(v)\n\t\t}\n\n\t\tsql = newSQL.String()\n\t} else {\n\t\tsql = numericPlaceholder.ReplaceAllString(sql, \"$$$1$$\")\n\n\t\tsql = numericPlaceholderRe.ReplaceAllStringFunc(sql, func(v string) string {\n\t\t\tnum := v[1 : len(v)-1]\n\t\t\tn, _ := strconv.Atoi(num)\n\n\t\t\t// position var start from 1 ($1, $2)\n\t\t\tn -= 1\n\t\t\tif n >= 0 && n <= len(vars)-1 {\n\t\t\t\treturn vars[n]\n\t\t\t}\n\t\t\treturn v\n\t\t})\n\t}\n\n\treturn sql\n}\n"
  },
  {
    "path": "logger/sql_test.go",
    "content": "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.com/jinzhu/now\"\n\t\"gorm.io/gorm/logger\"\n)\n\ntype JSON json.RawMessage\n\nfunc (j JSON) Value() (driver.Value, error) {\n\tif len(j) == 0 {\n\t\treturn nil, nil\n\t}\n\treturn json.RawMessage(j).MarshalJSON()\n}\n\ntype ExampleStruct struct {\n\tName string\n\tVal  string\n}\n\nfunc (s ExampleStruct) Value() (driver.Value, error) {\n\treturn json.Marshal(s)\n}\n\nfunc format(v []byte, escaper string) string {\n\treturn escaper + strings.ReplaceAll(string(v), escaper, escaper+escaper) + escaper\n}\n\nfunc TestExplainSQL(t *testing.T) {\n\ttype role string\n\ttype password []byte\n\ttype intType int\n\ttype floatType float64\n\tvar (\n\t\ttt                 = now.MustParse(\"2020-02-23 11:10:10\")\n\t\tmyrole             = role(\"admin\")\n\t\tpwd                = password(\"pass\")\n\t\tjsVal              = []byte(`{\"Name\":\"test\",\"Val\":\"test\"}`)\n\t\tjs                 = JSON(jsVal)\n\t\tesVal              = []byte(`{\"Name\":\"test\",\"Val\":\"test\"}`)\n\t\tes                 = ExampleStruct{Name: \"test\", Val: \"test\"}\n\t\tintVal   intType   = 1\n\t\tfloatVal floatType = 1.23\n\t)\n\n\tresults := []struct {\n\t\tSQL           string\n\t\tNumericRegexp *regexp.Regexp\n\t\tVars          []interface{}\n\t\tResult        string\n\t}{\n\t\t{\n\t\t\tSQL:           \"create table users (name, age, height, actived, bytes, create_at, update_at, deleted_at, email, role, pass) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\",\n\t\t\tNumericRegexp: nil,\n\t\t\tVars:          []interface{}{\"jinzhu\", 1, 999.99, true, []byte(\"12345\"), tt, &tt, nil, \"w@g.\\\"com\", myrole, pwd},\n\t\t\tResult:        `create table users (name, age, height, actived, bytes, create_at, update_at, deleted_at, email, role, pass) values (\"jinzhu\", 1, 999.99, true, \"12345\", \"2020-02-23 11:10:10\", \"2020-02-23 11:10:10\", NULL, \"w@g.\"\"com\", \"admin\", \"pass\")`,\n\t\t},\n\t\t{\n\t\t\tSQL:           \"create table users (name, age, height, actived, bytes, create_at, update_at, deleted_at, email, role, pass) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\",\n\t\t\tNumericRegexp: nil,\n\t\t\tVars:          []interface{}{\"jinzhu?\", 1, 999.99, true, []byte(\"12345\"), tt, &tt, nil, \"w@g.\\\"com\", myrole, pwd},\n\t\t\tResult:        `create table users (name, age, height, actived, bytes, create_at, update_at, deleted_at, email, role, pass) values (\"jinzhu?\", 1, 999.99, true, \"12345\", \"2020-02-23 11:10:10\", \"2020-02-23 11:10:10\", NULL, \"w@g.\"\"com\", \"admin\", \"pass\")`,\n\t\t},\n\t\t{\n\t\t\tSQL:           \"create table users (name, age, height, actived, bytes, create_at, update_at, deleted_at, email, role, pass) values (@p1, @p2, @p3, @p4, @p5, @p6, @p7, @p8, @p9, @p10, @p11)\",\n\t\t\tNumericRegexp: regexp.MustCompile(`@p(\\d+)`),\n\t\t\tVars:          []interface{}{\"jinzhu\", 1, 999.99, true, []byte(\"12345\"), tt, &tt, nil, \"w@g.com\", myrole, pwd},\n\t\t\tResult:        `create table users (name, age, height, actived, bytes, create_at, update_at, deleted_at, email, role, pass) values (\"jinzhu\", 1, 999.99, true, \"12345\", \"2020-02-23 11:10:10\", \"2020-02-23 11:10:10\", NULL, \"w@g.com\", \"admin\", \"pass\")`,\n\t\t},\n\t\t{\n\t\t\tSQL:           \"create table users (name, age, height, actived, bytes, create_at, update_at, deleted_at, email, role, pass) values ($3, $4, $1, $2, $7, $8, $5, $6, $9, $10, $11)\",\n\t\t\tNumericRegexp: regexp.MustCompile(`\\$(\\d+)`),\n\t\t\tVars:          []interface{}{999.99, true, \"jinzhu\", 1, &tt, nil, []byte(\"12345\"), tt, \"w@g.com\", myrole, pwd},\n\t\t\tResult:        `create table users (name, age, height, actived, bytes, create_at, update_at, deleted_at, email, role, pass) values (\"jinzhu\", 1, 999.99, true, \"12345\", \"2020-02-23 11:10:10\", \"2020-02-23 11:10:10\", NULL, \"w@g.com\", \"admin\", \"pass\")`,\n\t\t},\n\t\t{\n\t\t\tSQL:           \"create table users (name, age, height, actived, bytes, create_at, update_at, deleted_at, email, role, pass) values (@p1, @p11, @p2, @p3, @p4, @p5, @p6, @p7, @p8, @p9, @p10)\",\n\t\t\tNumericRegexp: regexp.MustCompile(`@p(\\d+)`),\n\t\t\tVars:          []interface{}{\"jinzhu\", 999.99, true, []byte(\"12345\"), tt, &tt, nil, \"w@g.com\", myrole, pwd, 1},\n\t\t\tResult:        `create table users (name, age, height, actived, bytes, create_at, update_at, deleted_at, email, role, pass) values (\"jinzhu\", 1, 999.99, true, \"12345\", \"2020-02-23 11:10:10\", \"2020-02-23 11:10:10\", NULL, \"w@g.com\", \"admin\", \"pass\")`,\n\t\t},\n\t\t{\n\t\t\tSQL:           \"create table users (name, age, height, actived, bytes, create_at, update_at, deleted_at, email, role, pass, json_struct, example_struct) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\",\n\t\t\tNumericRegexp: nil,\n\t\t\tVars:          []interface{}{\"jinzhu\", 1, 999.99, true, []byte(\"12345\"), tt, &tt, nil, \"w@g.\\\"com\", myrole, pwd, js, es},\n\t\t\tResult:        fmt.Sprintf(`create table users (name, age, height, actived, bytes, create_at, update_at, deleted_at, email, role, pass, json_struct, example_struct) values (\"jinzhu\", 1, 999.99, true, \"12345\", \"2020-02-23 11:10:10\", \"2020-02-23 11:10:10\", NULL, \"w@g.\"\"com\", \"admin\", \"pass\", %v, %v)`, format(jsVal, `\"`), format(esVal, `\"`)),\n\t\t},\n\t\t{\n\t\t\tSQL:           \"create table users (name, age, height, actived, bytes, create_at, update_at, deleted_at, email, role, pass, json_struct, example_struct) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\",\n\t\t\tNumericRegexp: nil,\n\t\t\tVars:          []interface{}{\"jinzhu\", 1, 999.99, true, []byte(\"12345\"), tt, &tt, nil, \"w@g.\\\"com\", myrole, pwd, &js, &es},\n\t\t\tResult:        fmt.Sprintf(`create table users (name, age, height, actived, bytes, create_at, update_at, deleted_at, email, role, pass, json_struct, example_struct) values (\"jinzhu\", 1, 999.99, true, \"12345\", \"2020-02-23 11:10:10\", \"2020-02-23 11:10:10\", NULL, \"w@g.\"\"com\", \"admin\", \"pass\", %v, %v)`, format(jsVal, `\"`), format(esVal, `\"`)),\n\t\t},\n\t\t{\n\t\t\tSQL:           \"create table users (name, age, height, actived, bytes, create_at, update_at, deleted_at, email, role, pass, json_struct, example_struct) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\",\n\t\t\tNumericRegexp: nil,\n\t\t\tVars:          []interface{}{\"jinzhu\", 1, 0.1753607109, true, []byte(\"12345\"), tt, &tt, nil, \"w@g.\\\"com\", myrole, pwd, &js, &es},\n\t\t\tResult:        fmt.Sprintf(`create table users (name, age, height, actived, bytes, create_at, update_at, deleted_at, email, role, pass, json_struct, example_struct) values (\"jinzhu\", 1, 0.1753607109, true, \"12345\", \"2020-02-23 11:10:10\", \"2020-02-23 11:10:10\", NULL, \"w@g.\"\"com\", \"admin\", \"pass\", %v, %v)`, format(jsVal, `\"`), format(esVal, `\"`)),\n\t\t},\n\t\t{\n\t\t\tSQL:           \"create table users (name, age, height, actived, bytes, create_at, update_at, deleted_at, email, role, pass, json_struct, example_struct) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\",\n\t\t\tNumericRegexp: nil,\n\t\t\tVars:          []interface{}{\"jinzhu\", 1, float32(999.99), true, []byte(\"12345\"), tt, &tt, nil, \"w@g.\\\"com\", myrole, pwd, &js, &es},\n\t\t\tResult:        fmt.Sprintf(`create table users (name, age, height, actived, bytes, create_at, update_at, deleted_at, email, role, pass, json_struct, example_struct) values (\"jinzhu\", 1, 999.99, true, \"12345\", \"2020-02-23 11:10:10\", \"2020-02-23 11:10:10\", NULL, \"w@g.\"\"com\", \"admin\", \"pass\", %v, %v)`, format(jsVal, `\"`), format(esVal, `\"`)),\n\t\t},\n\t\t{\n\t\t\tSQL:           \"create table users (name, age, height, actived, bytes, create_at, update_at, deleted_at, email, role, pass, int_val) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\",\n\t\t\tNumericRegexp: nil,\n\t\t\tVars:          []interface{}{\"jinzhu?\", 1, 999.99, true, []byte(\"12345\"), tt, &tt, nil, \"w@g.\\\"com\", myrole, pwd, intVal},\n\t\t\tResult:        `create table users (name, age, height, actived, bytes, create_at, update_at, deleted_at, email, role, pass, int_val) values (\"jinzhu?\", 1, 999.99, true, \"12345\", \"2020-02-23 11:10:10\", \"2020-02-23 11:10:10\", NULL, \"w@g.\"\"com\", \"admin\", \"pass\", 1)`,\n\t\t},\n\t\t{\n\t\t\tSQL:           \"create table users (name, age, height, actived, bytes, create_at, update_at, deleted_at, email, role, pass, float_val) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\",\n\t\t\tNumericRegexp: nil,\n\t\t\tVars:          []interface{}{\"jinzhu?\", 1, 999.99, true, []byte(\"12345\"), tt, &tt, nil, \"w@g.\\\"com\", myrole, pwd, floatVal},\n\t\t\tResult:        `create table users (name, age, height, actived, bytes, create_at, update_at, deleted_at, email, role, pass, float_val) values (\"jinzhu?\", 1, 999.99, true, \"12345\", \"2020-02-23 11:10:10\", \"2020-02-23 11:10:10\", NULL, \"w@g.\"\"com\", \"admin\", \"pass\", 1.230000)`,\n\t\t},\n\t}\n\n\tfor idx, r := range results {\n\t\tif result := logger.ExplainSQL(r.SQL, r.NumericRegexp, `\"`, r.Vars...); result != r.Result {\n\t\t\tt.Errorf(\"Explain SQL #%v expects %v, but got %v\", idx, r.Result, result)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "migrator/column_type.go",
    "content": "package migrator\n\nimport (\n\t\"database/sql\"\n\t\"reflect\"\n)\n\n// ColumnType column type implements ColumnType interface\ntype ColumnType struct {\n\tSQLColumnType      *sql.ColumnType\n\tNameValue          sql.NullString\n\tDataTypeValue      sql.NullString\n\tColumnTypeValue    sql.NullString\n\tPrimaryKeyValue    sql.NullBool\n\tUniqueValue        sql.NullBool\n\tAutoIncrementValue sql.NullBool\n\tLengthValue        sql.NullInt64\n\tDecimalSizeValue   sql.NullInt64\n\tScaleValue         sql.NullInt64\n\tNullableValue      sql.NullBool\n\tScanTypeValue      reflect.Type\n\tCommentValue       sql.NullString\n\tDefaultValueValue  sql.NullString\n}\n\n// Name returns the name or alias of the column.\nfunc (ct ColumnType) Name() string {\n\tif ct.NameValue.Valid {\n\t\treturn ct.NameValue.String\n\t}\n\treturn ct.SQLColumnType.Name()\n}\n\n// DatabaseTypeName returns the database system name of the column type. If an empty\n// string is returned, then the driver type name is not supported.\n// Consult your driver documentation for a list of driver data types. Length specifiers\n// are not included.\n// Common type names include \"VARCHAR\", \"TEXT\", \"NVARCHAR\", \"DECIMAL\", \"BOOL\",\n// \"INT\", and \"BIGINT\".\nfunc (ct ColumnType) DatabaseTypeName() string {\n\tif ct.DataTypeValue.Valid {\n\t\treturn ct.DataTypeValue.String\n\t}\n\treturn ct.SQLColumnType.DatabaseTypeName()\n}\n\n// ColumnType returns the database type of the column. like `varchar(16)`\nfunc (ct ColumnType) ColumnType() (columnType string, ok bool) {\n\treturn ct.ColumnTypeValue.String, ct.ColumnTypeValue.Valid\n}\n\n// PrimaryKey returns the column is primary key or not.\nfunc (ct ColumnType) PrimaryKey() (isPrimaryKey bool, ok bool) {\n\treturn ct.PrimaryKeyValue.Bool, ct.PrimaryKeyValue.Valid\n}\n\n// AutoIncrement returns the column is auto increment or not.\nfunc (ct ColumnType) AutoIncrement() (isAutoIncrement bool, ok bool) {\n\treturn ct.AutoIncrementValue.Bool, ct.AutoIncrementValue.Valid\n}\n\n// Length returns the column type length for variable length column types\nfunc (ct ColumnType) Length() (length int64, ok bool) {\n\tif ct.LengthValue.Valid {\n\t\treturn ct.LengthValue.Int64, true\n\t}\n\treturn ct.SQLColumnType.Length()\n}\n\n// DecimalSize returns the scale and precision of a decimal type.\nfunc (ct ColumnType) DecimalSize() (precision int64, scale int64, ok bool) {\n\tif ct.DecimalSizeValue.Valid {\n\t\treturn ct.DecimalSizeValue.Int64, ct.ScaleValue.Int64, true\n\t}\n\treturn ct.SQLColumnType.DecimalSize()\n}\n\n// Nullable reports whether the column may be null.\nfunc (ct ColumnType) Nullable() (nullable bool, ok bool) {\n\tif ct.NullableValue.Valid {\n\t\treturn ct.NullableValue.Bool, true\n\t}\n\treturn ct.SQLColumnType.Nullable()\n}\n\n// Unique reports whether the column may be unique.\nfunc (ct ColumnType) Unique() (unique bool, ok bool) {\n\treturn ct.UniqueValue.Bool, ct.UniqueValue.Valid\n}\n\n// ScanType returns a Go type suitable for scanning into using Rows.Scan.\nfunc (ct ColumnType) ScanType() reflect.Type {\n\tif ct.ScanTypeValue != nil {\n\t\treturn ct.ScanTypeValue\n\t}\n\treturn ct.SQLColumnType.ScanType()\n}\n\n// Comment returns the comment of current column.\nfunc (ct ColumnType) Comment() (value string, ok bool) {\n\treturn ct.CommentValue.String, ct.CommentValue.Valid\n}\n\n// DefaultValue returns the default value of current column.\nfunc (ct ColumnType) DefaultValue() (value string, ok bool) {\n\treturn ct.DefaultValueValue.String, ct.DefaultValueValue.Valid\n}\n"
  },
  {
    "path": "migrator/index.go",
    "content": "package migrator\n\nimport \"database/sql\"\n\n// Index implements gorm.Index interface\ntype Index struct {\n\tTableName       string\n\tNameValue       string\n\tColumnList      []string\n\tPrimaryKeyValue sql.NullBool\n\tUniqueValue     sql.NullBool\n\tOptionValue     string\n}\n\n// Table return the table name of the index.\nfunc (idx Index) Table() string {\n\treturn idx.TableName\n}\n\n// Name return the name of the index.\nfunc (idx Index) Name() string {\n\treturn idx.NameValue\n}\n\n// Columns return the columns of the index\nfunc (idx Index) Columns() []string {\n\treturn idx.ColumnList\n}\n\n// PrimaryKey returns the index is primary key or not.\nfunc (idx Index) PrimaryKey() (isPrimaryKey bool, ok bool) {\n\treturn idx.PrimaryKeyValue.Bool, idx.PrimaryKeyValue.Valid\n}\n\n// Unique returns whether the index is unique or not.\nfunc (idx Index) Unique() (unique bool, ok bool) {\n\treturn idx.UniqueValue.Bool, idx.UniqueValue.Valid\n}\n\n// Option return the optional attribute of the index\nfunc (idx Index) Option() string {\n\treturn idx.OptionValue\n}\n"
  },
  {
    "path": "migrator/migrator.go",
    "content": "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\"\n\n\t\"gorm.io/gorm\"\n\t\"gorm.io/gorm/clause\"\n\t\"gorm.io/gorm/logger\"\n\t\"gorm.io/gorm/schema\"\n)\n\n// This regular expression seeks to find a sequence of digits (\\d+) among zero or more non-digit characters (\\D*),\n// with a possible trailing non-digit character (\\D?).\n\n// For example, values that can pass this regular expression are:\n// - \"123\"\n// - \"abc456\"\n// -\"%$#@789\"\nvar regFullDataType = regexp.MustCompile(`\\D*(\\d+)\\D?`)\n\n// TODO:? Create const vars for raw sql queries ?\n\nvar _ gorm.Migrator = (*Migrator)(nil)\n\n// Migrator m struct\ntype Migrator struct {\n\tConfig\n}\n\n// Config schema config\ntype Config struct {\n\tCreateIndexAfterCreateTable bool\n\tDB                          *gorm.DB\n\tgorm.Dialector\n}\n\ntype printSQLLogger struct {\n\tlogger.Interface\n}\n\nfunc (l *printSQLLogger) Trace(ctx context.Context, begin time.Time, fc func() (sql string, rowsAffected int64), err error) {\n\tsql, _ := fc()\n\tfmt.Println(sql + \";\")\n\tl.Interface.Trace(ctx, begin, fc, err)\n}\n\n// GormDataTypeInterface gorm data type interface\ntype GormDataTypeInterface interface {\n\tGormDBDataType(*gorm.DB, *schema.Field) string\n}\n\n// RunWithValue run migration with statement value\nfunc (m Migrator) RunWithValue(value interface{}, fc func(*gorm.Statement) error) error {\n\tstmt := &gorm.Statement{DB: m.DB}\n\tif m.DB.Statement != nil {\n\t\tstmt.Table = m.DB.Statement.Table\n\t\tstmt.TableExpr = m.DB.Statement.TableExpr\n\t}\n\n\tif table, ok := value.(string); ok {\n\t\tstmt.Table = table\n\t} else if err := stmt.ParseWithSpecialTableName(value, stmt.Table); err != nil {\n\t\treturn err\n\t}\n\n\treturn fc(stmt)\n}\n\n// DataTypeOf return field's db data type\nfunc (m Migrator) DataTypeOf(field *schema.Field) string {\n\tfieldValue := reflect.New(field.IndirectFieldType)\n\tif dataTyper, ok := fieldValue.Interface().(GormDataTypeInterface); ok {\n\t\tif dataType := dataTyper.GormDBDataType(m.DB, field); dataType != \"\" {\n\t\t\treturn dataType\n\t\t}\n\t}\n\n\treturn m.Dialector.DataTypeOf(field)\n}\n\n// FullDataTypeOf returns field's db full data type\nfunc (m Migrator) FullDataTypeOf(field *schema.Field) (expr clause.Expr) {\n\texpr.SQL = m.DataTypeOf(field)\n\n\tif field.NotNull {\n\t\texpr.SQL += \" NOT NULL\"\n\t}\n\n\tif field.HasDefaultValue && (field.DefaultValueInterface != nil || field.DefaultValue != \"\") {\n\t\tif field.DefaultValueInterface != nil {\n\t\t\tdefaultStmt := &gorm.Statement{Vars: []interface{}{field.DefaultValueInterface}}\n\t\t\tm.Dialector.BindVarTo(defaultStmt, defaultStmt, field.DefaultValueInterface)\n\t\t\texpr.SQL += \" DEFAULT \" + m.Dialector.Explain(defaultStmt.SQL.String(), field.DefaultValueInterface)\n\t\t} else if field.DefaultValue != \"(-)\" {\n\t\t\texpr.SQL += \" DEFAULT \" + field.DefaultValue\n\t\t}\n\t}\n\n\treturn\n}\n\nfunc (m Migrator) GetQueryAndExecTx() (queryTx, execTx *gorm.DB) {\n\tqueryTx = m.DB.Session(&gorm.Session{})\n\texecTx = queryTx\n\tif m.DB.DryRun {\n\t\tqueryTx.DryRun = false\n\t\texecTx = m.DB.Session(&gorm.Session{Logger: &printSQLLogger{Interface: m.DB.Logger}})\n\t}\n\treturn queryTx, execTx\n}\n\n// AutoMigrate auto migrate values\nfunc (m Migrator) AutoMigrate(values ...interface{}) error {\n\tfor _, value := range m.ReorderModels(values, true) {\n\t\tqueryTx, execTx := m.GetQueryAndExecTx()\n\t\tif !queryTx.Migrator().HasTable(value) {\n\t\t\tif err := execTx.Migrator().CreateTable(value); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t} else {\n\t\t\tif err := m.RunWithValue(value, func(stmt *gorm.Statement) error {\n\n\t\t\t\tif stmt.Schema == nil {\n\t\t\t\t\treturn errors.New(\"failed to get schema\")\n\t\t\t\t}\n\n\t\t\t\tcolumnTypes, err := queryTx.Migrator().ColumnTypes(value)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tvar (\n\t\t\t\t\tparseIndexes          = stmt.Schema.ParseIndexes()\n\t\t\t\t\tparseCheckConstraints = stmt.Schema.ParseCheckConstraints()\n\t\t\t\t)\n\t\t\t\tfor _, dbName := range stmt.Schema.DBNames {\n\t\t\t\t\tvar foundColumn gorm.ColumnType\n\n\t\t\t\t\tfor _, columnType := range columnTypes {\n\t\t\t\t\t\tif columnType.Name() == dbName {\n\t\t\t\t\t\t\tfoundColumn = columnType\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif foundColumn == nil {\n\t\t\t\t\t\t// not found, add column\n\t\t\t\t\t\tif err = execTx.Migrator().AddColumn(value, dbName); err != nil {\n\t\t\t\t\t\t\treturn err\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// found, smartly migrate\n\t\t\t\t\t\tfield := stmt.Schema.FieldsByDBName[dbName]\n\t\t\t\t\t\tif err = execTx.Migrator().MigrateColumn(value, field, foundColumn); err != nil {\n\t\t\t\t\t\t\treturn err\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif !m.DB.DisableForeignKeyConstraintWhenMigrating && !m.DB.IgnoreRelationshipsWhenMigrating {\n\t\t\t\t\tfor _, rel := range stmt.Schema.Relationships.Relations {\n\t\t\t\t\t\tif rel.Field.IgnoreMigration {\n\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif constraint := rel.ParseConstraint(); constraint != nil &&\n\t\t\t\t\t\t\tconstraint.Schema == stmt.Schema && !queryTx.Migrator().HasConstraint(value, constraint.Name) {\n\t\t\t\t\t\t\tif err := execTx.Migrator().CreateConstraint(value, constraint.Name); err != nil {\n\t\t\t\t\t\t\t\treturn err\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tfor _, chk := range parseCheckConstraints {\n\t\t\t\t\tif !queryTx.Migrator().HasConstraint(value, chk.Name) {\n\t\t\t\t\t\tif err := execTx.Migrator().CreateConstraint(value, chk.Name); err != nil {\n\t\t\t\t\t\t\treturn err\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tfor _, idx := range parseIndexes {\n\t\t\t\t\tif !queryTx.Migrator().HasIndex(value, idx.Name) {\n\t\t\t\t\t\tif err := execTx.Migrator().CreateIndex(value, idx.Name); err != nil {\n\t\t\t\t\t\t\treturn err\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn nil\n\t\t\t}); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// GetTables returns tables\nfunc (m Migrator) GetTables() (tableList []string, err error) {\n\terr = m.DB.Raw(\"SELECT TABLE_NAME FROM information_schema.tables where TABLE_SCHEMA=?\", m.CurrentDatabase()).\n\t\tScan(&tableList).Error\n\treturn\n}\n\n// CreateTable create table in database for values\nfunc (m Migrator) CreateTable(values ...interface{}) error {\n\tfor _, value := range m.ReorderModels(values, false) {\n\t\ttx := m.DB.Session(&gorm.Session{})\n\t\tif err := m.RunWithValue(value, func(stmt *gorm.Statement) (err error) {\n\n\t\t\tif stmt.Schema == nil {\n\t\t\t\treturn errors.New(\"failed to get schema\")\n\t\t\t}\n\n\t\t\tvar (\n\t\t\t\tcreateTableSQL          = \"CREATE TABLE ? (\"\n\t\t\t\tvalues                  = []interface{}{m.CurrentTable(stmt)}\n\t\t\t\thasPrimaryKeyInDataType bool\n\t\t\t)\n\n\t\t\tfor _, dbName := range stmt.Schema.DBNames {\n\t\t\t\tfield := stmt.Schema.FieldsByDBName[dbName]\n\t\t\t\tif !field.IgnoreMigration {\n\t\t\t\t\tcreateTableSQL += \"? ?\"\n\t\t\t\t\thasPrimaryKeyInDataType = hasPrimaryKeyInDataType || strings.Contains(strings.ToUpper(m.DataTypeOf(field)), \"PRIMARY KEY\")\n\t\t\t\t\tvalues = append(values, clause.Column{Name: dbName}, m.DB.Migrator().FullDataTypeOf(field))\n\t\t\t\t\tcreateTableSQL += \",\"\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif !hasPrimaryKeyInDataType && len(stmt.Schema.PrimaryFields) > 0 {\n\t\t\t\tcreateTableSQL += \"PRIMARY KEY ?,\"\n\t\t\t\tprimaryKeys := make([]interface{}, 0, len(stmt.Schema.PrimaryFields))\n\t\t\t\tfor _, field := range stmt.Schema.PrimaryFields {\n\t\t\t\t\tprimaryKeys = append(primaryKeys, clause.Column{Name: field.DBName})\n\t\t\t\t}\n\n\t\t\t\tvalues = append(values, primaryKeys)\n\t\t\t}\n\n\t\t\tfor _, idx := range stmt.Schema.ParseIndexes() {\n\t\t\t\tif m.CreateIndexAfterCreateTable {\n\t\t\t\t\tdefer func(value interface{}, name string) {\n\t\t\t\t\t\tif err == nil {\n\t\t\t\t\t\t\terr = tx.Migrator().CreateIndex(value, name)\n\t\t\t\t\t\t}\n\t\t\t\t\t}(value, idx.Name)\n\t\t\t\t} else {\n\t\t\t\t\tif idx.Class != \"\" {\n\t\t\t\t\t\tcreateTableSQL += idx.Class + \" \"\n\t\t\t\t\t}\n\t\t\t\t\tcreateTableSQL += \"INDEX ? ?\"\n\n\t\t\t\t\tif idx.Comment != \"\" {\n\t\t\t\t\t\tcreateTableSQL += fmt.Sprintf(\" COMMENT '%s'\", idx.Comment)\n\t\t\t\t\t}\n\n\t\t\t\t\tif idx.Option != \"\" {\n\t\t\t\t\t\tcreateTableSQL += \" \" + idx.Option\n\t\t\t\t\t}\n\n\t\t\t\t\tcreateTableSQL += \",\"\n\t\t\t\t\tvalues = append(values, clause.Column{Name: idx.Name}, tx.Migrator().(BuildIndexOptionsInterface).BuildIndexOptions(idx.Fields, stmt))\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif !m.DB.DisableForeignKeyConstraintWhenMigrating && !m.DB.IgnoreRelationshipsWhenMigrating {\n\t\t\t\tfor _, rel := range stmt.Schema.Relationships.Relations {\n\t\t\t\t\tif rel.Field.IgnoreMigration {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tif constraint := rel.ParseConstraint(); constraint != nil {\n\t\t\t\t\t\tif constraint.Schema == stmt.Schema {\n\t\t\t\t\t\t\tsql, vars := constraint.Build()\n\t\t\t\t\t\t\tcreateTableSQL += sql + \",\"\n\t\t\t\t\t\t\tvalues = append(values, vars...)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfor _, uni := range stmt.Schema.ParseUniqueConstraints() {\n\t\t\t\tcreateTableSQL += \"CONSTRAINT ? UNIQUE (?),\"\n\t\t\t\tvalues = append(values, clause.Column{Name: uni.Name}, clause.Expr{SQL: stmt.Quote(uni.Field.DBName)})\n\t\t\t}\n\n\t\t\tfor _, chk := range stmt.Schema.ParseCheckConstraints() {\n\t\t\t\tcreateTableSQL += \"CONSTRAINT ? CHECK (?),\"\n\t\t\t\tvalues = append(values, clause.Column{Name: chk.Name}, clause.Expr{SQL: chk.Constraint})\n\t\t\t}\n\n\t\t\tcreateTableSQL = strings.TrimSuffix(createTableSQL, \",\")\n\n\t\t\tcreateTableSQL += \")\"\n\n\t\t\tif tableOption, ok := m.DB.Get(\"gorm:table_options\"); ok {\n\t\t\t\tcreateTableSQL += fmt.Sprint(tableOption)\n\t\t\t}\n\n\t\t\terr = tx.Exec(createTableSQL, values...).Error\n\t\t\treturn err\n\t\t}); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\n// DropTable drop table for values\nfunc (m Migrator) DropTable(values ...interface{}) error {\n\tvalues = m.ReorderModels(values, false)\n\tfor i := len(values) - 1; i >= 0; i-- {\n\t\ttx := m.DB.Session(&gorm.Session{})\n\t\tif err := m.RunWithValue(values[i], func(stmt *gorm.Statement) error {\n\t\t\treturn tx.Exec(\"DROP TABLE IF EXISTS ?\", m.CurrentTable(stmt)).Error\n\t\t}); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\n// HasTable returns table exists or not for value, value could be a struct or string\nfunc (m Migrator) HasTable(value interface{}) bool {\n\tvar count int64\n\n\tm.RunWithValue(value, func(stmt *gorm.Statement) error {\n\t\tcurrentDatabase := m.DB.Migrator().CurrentDatabase()\n\t\treturn m.DB.Raw(\"SELECT count(*) FROM information_schema.tables WHERE table_schema = ? AND table_name = ? AND table_type = ?\", currentDatabase, stmt.Table, \"BASE TABLE\").Row().Scan(&count)\n\t})\n\n\treturn count > 0\n}\n\n// RenameTable rename table from oldName to newName\nfunc (m Migrator) RenameTable(oldName, newName interface{}) error {\n\tvar oldTable, newTable interface{}\n\tif v, ok := oldName.(string); ok {\n\t\toldTable = clause.Table{Name: v}\n\t} else {\n\t\tstmt := &gorm.Statement{DB: m.DB}\n\t\tif err := stmt.Parse(oldName); err == nil {\n\t\t\toldTable = m.CurrentTable(stmt)\n\t\t} else {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tif v, ok := newName.(string); ok {\n\t\tnewTable = clause.Table{Name: v}\n\t} else {\n\t\tstmt := &gorm.Statement{DB: m.DB}\n\t\tif err := stmt.Parse(newName); err == nil {\n\t\t\tnewTable = m.CurrentTable(stmt)\n\t\t} else {\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn m.DB.Exec(\"ALTER TABLE ? RENAME TO ?\", oldTable, newTable).Error\n}\n\n// AddColumn create `name` column for value\nfunc (m Migrator) AddColumn(value interface{}, name string) error {\n\treturn m.RunWithValue(value, func(stmt *gorm.Statement) error {\n\t\t// avoid using the same name field\n\t\tif stmt.Schema == nil {\n\t\t\treturn errors.New(\"failed to get schema\")\n\t\t}\n\t\tf := stmt.Schema.LookUpField(name)\n\t\tif f == nil {\n\t\t\treturn fmt.Errorf(\"failed to look up field with name: %s\", name)\n\t\t}\n\n\t\tif !f.IgnoreMigration {\n\t\t\treturn m.DB.Exec(\n\t\t\t\t\"ALTER TABLE ? ADD ? ?\",\n\t\t\t\tm.CurrentTable(stmt), clause.Column{Name: f.DBName}, m.DB.Migrator().FullDataTypeOf(f),\n\t\t\t).Error\n\t\t}\n\n\t\treturn nil\n\t})\n}\n\n// DropColumn drop value's `name` column\nfunc (m Migrator) DropColumn(value interface{}, name string) error {\n\treturn m.RunWithValue(value, func(stmt *gorm.Statement) error {\n\t\tif stmt.Schema != nil {\n\t\t\tif field := stmt.Schema.LookUpField(name); field != nil {\n\t\t\t\tname = field.DBName\n\t\t\t}\n\t\t}\n\n\t\treturn m.DB.Exec(\n\t\t\t\"ALTER TABLE ? DROP COLUMN ?\", m.CurrentTable(stmt), clause.Column{Name: name},\n\t\t).Error\n\t})\n}\n\n// AlterColumn alter value's `field` column' type based on schema definition\nfunc (m Migrator) AlterColumn(value interface{}, field string) error {\n\treturn m.RunWithValue(value, func(stmt *gorm.Statement) error {\n\t\tif stmt.Schema != nil {\n\t\t\tif field := stmt.Schema.LookUpField(field); field != nil {\n\t\t\t\tfileType := m.FullDataTypeOf(field)\n\t\t\t\treturn m.DB.Exec(\n\t\t\t\t\t\"ALTER TABLE ? ALTER COLUMN ? TYPE ?\",\n\t\t\t\t\tm.CurrentTable(stmt), clause.Column{Name: field.DBName}, fileType,\n\t\t\t\t).Error\n\n\t\t\t}\n\t\t}\n\t\treturn fmt.Errorf(\"failed to look up field with name: %s\", field)\n\t})\n}\n\n// HasColumn check has column `field` for value or not\nfunc (m Migrator) HasColumn(value interface{}, field string) bool {\n\tvar count int64\n\tm.RunWithValue(value, func(stmt *gorm.Statement) error {\n\t\tcurrentDatabase := m.DB.Migrator().CurrentDatabase()\n\t\tname := field\n\t\tif stmt.Schema != nil {\n\t\t\tif field := stmt.Schema.LookUpField(field); field != nil {\n\t\t\t\tname = field.DBName\n\t\t\t}\n\t\t}\n\n\t\treturn m.DB.Raw(\n\t\t\t\"SELECT count(*) FROM INFORMATION_SCHEMA.columns WHERE table_schema = ? AND table_name = ? AND column_name = ?\",\n\t\t\tcurrentDatabase, stmt.Table, name,\n\t\t).Row().Scan(&count)\n\t})\n\n\treturn count > 0\n}\n\n// RenameColumn rename value's field name from oldName to newName\nfunc (m Migrator) RenameColumn(value interface{}, oldName, newName string) error {\n\treturn m.RunWithValue(value, func(stmt *gorm.Statement) error {\n\t\tif stmt.Schema != nil {\n\t\t\tif field := stmt.Schema.LookUpField(oldName); field != nil {\n\t\t\t\toldName = field.DBName\n\t\t\t}\n\n\t\t\tif field := stmt.Schema.LookUpField(newName); field != nil {\n\t\t\t\tnewName = field.DBName\n\t\t\t}\n\t\t}\n\n\t\treturn m.DB.Exec(\n\t\t\t\"ALTER TABLE ? RENAME COLUMN ? TO ?\",\n\t\t\tm.CurrentTable(stmt), clause.Column{Name: oldName}, clause.Column{Name: newName},\n\t\t).Error\n\t})\n}\n\n// MigrateColumn migrate column\nfunc (m Migrator) MigrateColumn(value interface{}, field *schema.Field, columnType gorm.ColumnType) error {\n\tif field.IgnoreMigration {\n\t\treturn nil\n\t}\n\n\t// found, smart migrate\n\tfullDataType := strings.TrimSpace(strings.ToLower(m.DB.Migrator().FullDataTypeOf(field).SQL))\n\trealDataType := strings.ToLower(columnType.DatabaseTypeName())\n\tvar (\n\t\talterColumn bool\n\t\tisSameType  = fullDataType == realDataType\n\t)\n\n\tif !field.PrimaryKey {\n\t\t// check type\n\t\tif !strings.HasPrefix(fullDataType, realDataType) {\n\t\t\t// check type aliases\n\t\t\taliases := m.DB.Migrator().GetTypeAliases(realDataType)\n\t\t\tfor _, alias := range aliases {\n\t\t\t\tif strings.HasPrefix(fullDataType, alias) {\n\t\t\t\t\tisSameType = true\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif !isSameType {\n\t\t\t\talterColumn = true\n\t\t\t}\n\t\t}\n\t}\n\n\tif !isSameType {\n\t\t// check size\n\t\tif length, ok := columnType.Length(); length != int64(field.Size) {\n\t\t\tif length > 0 && field.Size > 0 {\n\t\t\t\talterColumn = true\n\t\t\t} else {\n\t\t\t\t// has size in data type and not equal\n\t\t\t\t// Since the following code is frequently called in the for loop, reg optimization is needed here\n\t\t\t\tmatches2 := regFullDataType.FindAllStringSubmatch(fullDataType, -1)\n\t\t\t\tif !field.PrimaryKey &&\n\t\t\t\t\t(len(matches2) == 1 && matches2[0][1] != fmt.Sprint(length) && ok) {\n\t\t\t\t\talterColumn = true\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// check precision\n\tif realDataType == \"decimal\" || realDataType == \"numeric\" &&\n\t\tregexp.MustCompile(realDataType+`\\(.*\\)`).FindString(fullDataType) != \"\" { // if realDataType has no precision,ignore\n\t\tprecision, scale, ok := columnType.DecimalSize()\n\t\tif ok {\n\t\t\tif !strings.HasPrefix(fullDataType, fmt.Sprintf(\"%s(%d,%d)\", realDataType, precision, scale)) &&\n\t\t\t\t!strings.HasPrefix(fullDataType, fmt.Sprintf(\"%s(%d)\", realDataType, precision)) {\n\t\t\t\talterColumn = true\n\t\t\t}\n\t\t}\n\t} else {\n\t\tif precision, _, ok := columnType.DecimalSize(); ok && int64(field.Precision) != precision {\n\t\t\tif regexp.MustCompile(fmt.Sprintf(\"[^0-9]%d[^0-9]\", field.Precision)).MatchString(m.DataTypeOf(field)) {\n\t\t\t\talterColumn = true\n\t\t\t}\n\t\t}\n\t}\n\n\t// check nullable\n\tif nullable, ok := columnType.Nullable(); ok && nullable == field.NotNull {\n\t\t// not primary key & current database is non-nullable(to be nullable)\n\t\tif !field.PrimaryKey && !nullable {\n\t\t\talterColumn = true\n\t\t}\n\t}\n\n\t// check default value\n\tif !field.PrimaryKey {\n\t\tcurrentDefaultNotNull := field.HasDefaultValue && (field.DefaultValueInterface != nil || !strings.EqualFold(field.DefaultValue, \"NULL\"))\n\t\tdv, dvNotNull := columnType.DefaultValue()\n\t\tif dvNotNull && !currentDefaultNotNull {\n\t\t\t// default value -> null\n\t\t\talterColumn = true\n\t\t} else if !dvNotNull && currentDefaultNotNull {\n\t\t\t// null -> default value\n\t\t\talterColumn = true\n\t\t} else if currentDefaultNotNull || dvNotNull {\n\t\t\tswitch field.GORMDataType {\n\t\t\tcase schema.Time:\n\t\t\t\tif !strings.EqualFold(strings.TrimSuffix(dv, \"()\"), strings.TrimSuffix(field.DefaultValue, \"()\")) {\n\t\t\t\t\talterColumn = true\n\t\t\t\t}\n\t\t\tcase schema.Bool:\n\t\t\t\tv1, _ := strconv.ParseBool(dv)\n\t\t\t\tv2, _ := strconv.ParseBool(field.DefaultValue)\n\t\t\t\talterColumn = v1 != v2\n\t\t\tcase schema.String:\n\t\t\t\tif dv != field.DefaultValue && dv != strings.Trim(field.DefaultValue, \"'\\\"\") {\n\t\t\t\t\talterColumn = true\n\t\t\t\t}\n\t\t\tdefault:\n\t\t\t\talterColumn = dv != field.DefaultValue\n\t\t\t}\n\t\t}\n\t}\n\n\t// check comment\n\tif comment, ok := columnType.Comment(); ok && comment != field.Comment {\n\t\t// not primary key\n\t\tif !field.PrimaryKey {\n\t\t\talterColumn = true\n\t\t}\n\t}\n\n\tif alterColumn {\n\t\tif err := m.DB.Migrator().AlterColumn(value, field.DBName); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tif err := m.DB.Migrator().MigrateColumnUnique(value, field, columnType); err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc (m Migrator) MigrateColumnUnique(value interface{}, field *schema.Field, columnType gorm.ColumnType) error {\n\tunique, ok := columnType.Unique()\n\tif !ok || field.PrimaryKey {\n\t\treturn nil // skip primary key\n\t}\n\t// By default, ColumnType's Unique is not affected by UniqueIndex, so we don't care about UniqueIndex.\n\treturn m.RunWithValue(value, func(stmt *gorm.Statement) error {\n\t\t// We're currently only receiving boolean values on `Unique` tag,\n\t\t// so the UniqueConstraint name is fixed\n\t\tconstraint := m.DB.NamingStrategy.UniqueName(stmt.Table, field.DBName)\n\t\tif unique && !field.Unique {\n\t\t\treturn m.DB.Migrator().DropConstraint(value, constraint)\n\t\t}\n\t\tif !unique && field.Unique {\n\t\t\treturn m.DB.Migrator().CreateConstraint(value, constraint)\n\t\t}\n\t\treturn nil\n\t})\n}\n\n// ColumnTypes return columnTypes []gorm.ColumnType and execErr error\nfunc (m Migrator) ColumnTypes(value interface{}) ([]gorm.ColumnType, error) {\n\tcolumnTypes := make([]gorm.ColumnType, 0)\n\texecErr := m.RunWithValue(value, func(stmt *gorm.Statement) (err error) {\n\t\trows, err := m.DB.Session(&gorm.Session{}).Table(stmt.Table).Limit(1).Rows()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tdefer func() {\n\t\t\terr = rows.Close()\n\t\t}()\n\n\t\tvar rawColumnTypes []*sql.ColumnType\n\t\trawColumnTypes, err = rows.ColumnTypes()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tfor _, c := range rawColumnTypes {\n\t\t\tcolumnTypes = append(columnTypes, ColumnType{SQLColumnType: c})\n\t\t}\n\n\t\treturn\n\t})\n\n\treturn columnTypes, execErr\n}\n\n// CreateView create view from Query in gorm.ViewOption.\n// Query in gorm.ViewOption is a [subquery]\n//\n//\t// CREATE VIEW `user_view` AS SELECT * FROM `users` WHERE age > 20\n//\tq := DB.Model(&User{}).Where(\"age > ?\", 20)\n//\tDB.Debug().Migrator().CreateView(\"user_view\", gorm.ViewOption{Query: q})\n//\n//\t// CREATE OR REPLACE VIEW `users_view` AS SELECT * FROM `users` WITH CHECK OPTION\n//\tq := DB.Model(&User{})\n//\tDB.Debug().Migrator().CreateView(\"user_view\", gorm.ViewOption{Query: q, Replace: true, CheckOption: \"WITH CHECK OPTION\"})\n//\n// [subquery]: https://gorm.io/docs/advanced_query.html#SubQuery\nfunc (m Migrator) CreateView(name string, option gorm.ViewOption) error {\n\tif option.Query == nil {\n\t\treturn gorm.ErrSubQueryRequired\n\t}\n\n\tsql := new(strings.Builder)\n\tsql.WriteString(\"CREATE \")\n\tif option.Replace {\n\t\tsql.WriteString(\"OR REPLACE \")\n\t}\n\tsql.WriteString(\"VIEW \")\n\tm.QuoteTo(sql, name)\n\tsql.WriteString(\" AS \")\n\n\tm.DB.Statement.AddVar(sql, option.Query)\n\n\tif option.CheckOption != \"\" {\n\t\tsql.WriteString(\" \")\n\t\tsql.WriteString(option.CheckOption)\n\t}\n\treturn m.DB.Exec(m.Explain(sql.String(), m.DB.Statement.Vars...)).Error\n}\n\n// DropView drop view\nfunc (m Migrator) DropView(name string) error {\n\treturn m.DB.Exec(\"DROP VIEW IF EXISTS ?\", clause.Table{Name: name}).Error\n}\n\n// GuessConstraintAndTable guess statement's constraint and it's table based on name\n//\n// Deprecated: use GuessConstraintInterfaceAndTable instead.\nfunc (m Migrator) GuessConstraintAndTable(stmt *gorm.Statement, name string) (*schema.Constraint, *schema.CheckConstraint, string) {\n\tconstraint, table := m.GuessConstraintInterfaceAndTable(stmt, name)\n\tswitch c := constraint.(type) {\n\tcase *schema.Constraint:\n\t\treturn c, nil, table\n\tcase *schema.CheckConstraint:\n\t\treturn nil, c, table\n\tdefault:\n\t\treturn nil, nil, table\n\t}\n}\n\n// GuessConstraintInterfaceAndTable guess statement's constraint and it's table based on name\n// nolint:cyclop\nfunc (m Migrator) GuessConstraintInterfaceAndTable(stmt *gorm.Statement, name string) (_ schema.ConstraintInterface, table string) {\n\tif stmt.Schema == nil {\n\t\treturn nil, stmt.Table\n\t}\n\n\tcheckConstraints := stmt.Schema.ParseCheckConstraints()\n\tif chk, ok := checkConstraints[name]; ok {\n\t\treturn &chk, stmt.Table\n\t}\n\n\tuniqueConstraints := stmt.Schema.ParseUniqueConstraints()\n\tif uni, ok := uniqueConstraints[name]; ok {\n\t\treturn &uni, stmt.Table\n\t}\n\n\tgetTable := func(rel *schema.Relationship) string {\n\t\tswitch rel.Type {\n\t\tcase schema.HasOne, schema.HasMany:\n\t\t\treturn rel.FieldSchema.Table\n\t\tcase schema.Many2Many:\n\t\t\treturn rel.JoinTable.Table\n\t\t}\n\t\treturn stmt.Table\n\t}\n\n\tfor _, rel := range stmt.Schema.Relationships.Relations {\n\t\tif constraint := rel.ParseConstraint(); constraint != nil && constraint.Name == name {\n\t\t\treturn constraint, getTable(rel)\n\t\t}\n\t}\n\n\tif field := stmt.Schema.LookUpField(name); field != nil {\n\t\tfor k := range checkConstraints {\n\t\t\tif checkConstraints[k].Field == field {\n\t\t\t\tv := checkConstraints[k]\n\t\t\t\treturn &v, stmt.Table\n\t\t\t}\n\t\t}\n\n\t\tfor k := range uniqueConstraints {\n\t\t\tif uniqueConstraints[k].Field == field {\n\t\t\t\tv := uniqueConstraints[k]\n\t\t\t\treturn &v, stmt.Table\n\t\t\t}\n\t\t}\n\n\t\tfor _, rel := range stmt.Schema.Relationships.Relations {\n\t\t\tif constraint := rel.ParseConstraint(); constraint != nil && rel.Field == field {\n\t\t\t\treturn constraint, getTable(rel)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn nil, stmt.Schema.Table\n}\n\n// CreateConstraint create constraint\nfunc (m Migrator) CreateConstraint(value interface{}, name string) error {\n\treturn m.RunWithValue(value, func(stmt *gorm.Statement) error {\n\t\tconstraint, table := m.GuessConstraintInterfaceAndTable(stmt, name)\n\t\tif constraint != nil {\n\t\t\tvars := []interface{}{clause.Table{Name: table}}\n\t\t\tif stmt.TableExpr != nil {\n\t\t\t\tvars[0] = stmt.TableExpr\n\t\t\t}\n\t\t\tsql, values := constraint.Build()\n\t\t\treturn m.DB.Exec(\"ALTER TABLE ? ADD \"+sql, append(vars, values...)...).Error\n\t\t}\n\t\treturn nil\n\t})\n}\n\n// DropConstraint drop constraint\nfunc (m Migrator) DropConstraint(value interface{}, name string) error {\n\treturn m.RunWithValue(value, func(stmt *gorm.Statement) error {\n\t\tconstraint, table := m.GuessConstraintInterfaceAndTable(stmt, name)\n\t\tif constraint != nil {\n\t\t\tname = constraint.GetName()\n\t\t}\n\t\treturn m.DB.Exec(\"ALTER TABLE ? DROP CONSTRAINT ?\", clause.Table{Name: table}, clause.Column{Name: name}).Error\n\t})\n}\n\n// HasConstraint check has constraint or not\nfunc (m Migrator) HasConstraint(value interface{}, name string) bool {\n\tvar count int64\n\tm.RunWithValue(value, func(stmt *gorm.Statement) error {\n\t\tcurrentDatabase := m.DB.Migrator().CurrentDatabase()\n\t\tconstraint, table := m.GuessConstraintInterfaceAndTable(stmt, name)\n\t\tif constraint != nil {\n\t\t\tname = constraint.GetName()\n\t\t}\n\n\t\treturn m.DB.Raw(\n\t\t\t\"SELECT count(*) FROM INFORMATION_SCHEMA.table_constraints WHERE constraint_schema = ? AND table_name = ? AND constraint_name = ?\",\n\t\t\tcurrentDatabase, table, name,\n\t\t).Row().Scan(&count)\n\t})\n\n\treturn count > 0\n}\n\n// BuildIndexOptions build index options\nfunc (m Migrator) BuildIndexOptions(opts []schema.IndexOption, stmt *gorm.Statement) (results []interface{}) {\n\tfor _, opt := range opts {\n\t\tstr := stmt.Quote(opt.DBName)\n\t\tif opt.Expression != \"\" {\n\t\t\tstr = opt.Expression\n\t\t} else if opt.Length > 0 {\n\t\t\tstr += fmt.Sprintf(\"(%d)\", opt.Length)\n\t\t}\n\n\t\tif opt.Collate != \"\" {\n\t\t\tstr += \" COLLATE \" + opt.Collate\n\t\t}\n\n\t\tif opt.Sort != \"\" {\n\t\t\tstr += \" \" + opt.Sort\n\t\t}\n\t\tresults = append(results, clause.Expr{SQL: str})\n\t}\n\treturn\n}\n\n// BuildIndexOptionsInterface build index options interface\ntype BuildIndexOptionsInterface interface {\n\tBuildIndexOptions([]schema.IndexOption, *gorm.Statement) []interface{}\n}\n\n// CreateIndex create index `name`\nfunc (m Migrator) CreateIndex(value interface{}, name string) error {\n\treturn m.RunWithValue(value, func(stmt *gorm.Statement) error {\n\t\tif stmt.Schema == nil {\n\t\t\treturn errors.New(\"failed to get schema\")\n\t\t}\n\t\tif idx := stmt.Schema.LookIndex(name); idx != nil {\n\t\t\topts := m.DB.Migrator().(BuildIndexOptionsInterface).BuildIndexOptions(idx.Fields, stmt)\n\t\t\tvalues := []interface{}{clause.Column{Name: idx.Name}, m.CurrentTable(stmt), opts}\n\n\t\t\tcreateIndexSQL := \"CREATE \"\n\t\t\tif idx.Class != \"\" {\n\t\t\t\tcreateIndexSQL += idx.Class + \" \"\n\t\t\t}\n\t\t\tcreateIndexSQL += \"INDEX ? ON ??\"\n\n\t\t\tif idx.Type != \"\" {\n\t\t\t\tcreateIndexSQL += \" USING \" + idx.Type\n\t\t\t}\n\n\t\t\tif idx.Comment != \"\" {\n\t\t\t\tcreateIndexSQL += fmt.Sprintf(\" COMMENT '%s'\", idx.Comment)\n\t\t\t}\n\n\t\t\tif idx.Option != \"\" {\n\t\t\t\tcreateIndexSQL += \" \" + idx.Option\n\t\t\t}\n\n\t\t\treturn m.DB.Exec(createIndexSQL, values...).Error\n\t\t}\n\n\t\treturn fmt.Errorf(\"failed to create index with name %s\", name)\n\t})\n}\n\n// DropIndex drop index `name`\nfunc (m Migrator) DropIndex(value interface{}, name string) error {\n\treturn m.RunWithValue(value, func(stmt *gorm.Statement) error {\n\t\tif stmt.Schema != nil {\n\t\t\tif idx := stmt.Schema.LookIndex(name); idx != nil {\n\t\t\t\tname = idx.Name\n\t\t\t}\n\t\t}\n\n\t\treturn m.DB.Exec(\"DROP INDEX ? ON ?\", clause.Column{Name: name}, m.CurrentTable(stmt)).Error\n\t})\n}\n\n// HasIndex check has index `name` or not\nfunc (m Migrator) HasIndex(value interface{}, name string) bool {\n\tvar count int64\n\tm.RunWithValue(value, func(stmt *gorm.Statement) error {\n\t\tcurrentDatabase := m.DB.Migrator().CurrentDatabase()\n\t\tif stmt.Schema != nil {\n\t\t\tif idx := stmt.Schema.LookIndex(name); idx != nil {\n\t\t\t\tname = idx.Name\n\t\t\t}\n\t\t}\n\n\t\treturn m.DB.Raw(\n\t\t\t\"SELECT count(*) FROM information_schema.statistics WHERE table_schema = ? AND table_name = ? AND index_name = ?\",\n\t\t\tcurrentDatabase, stmt.Table, name,\n\t\t).Row().Scan(&count)\n\t})\n\n\treturn count > 0\n}\n\n// RenameIndex rename index from oldName to newName\nfunc (m Migrator) RenameIndex(value interface{}, oldName, newName string) error {\n\treturn m.RunWithValue(value, func(stmt *gorm.Statement) error {\n\t\treturn m.DB.Exec(\n\t\t\t\"ALTER TABLE ? RENAME INDEX ? TO ?\",\n\t\t\tm.CurrentTable(stmt), clause.Column{Name: oldName}, clause.Column{Name: newName},\n\t\t).Error\n\t})\n}\n\n// CurrentDatabase returns current database name\nfunc (m Migrator) CurrentDatabase() (name string) {\n\tm.DB.Raw(\"SELECT DATABASE()\").Row().Scan(&name)\n\treturn\n}\n\n// ReorderModels reorder models according to constraint dependencies\nfunc (m Migrator) ReorderModels(values []interface{}, autoAdd bool) (results []interface{}) {\n\ttype Dependency struct {\n\t\t*gorm.Statement\n\t\tDepends []*schema.Schema\n\t}\n\n\tvar (\n\t\tmodelNames, orderedModelNames []string\n\t\torderedModelNamesMap          = map[string]bool{}\n\t\tparsedSchemas                 = map[*schema.Schema]bool{}\n\t\tvaluesMap                     = map[string]Dependency{}\n\t\tinsertIntoOrderedList         func(name string)\n\t\tparseDependence               func(value interface{}, addToList bool)\n\t)\n\n\tparseDependence = func(value interface{}, addToList bool) {\n\t\tdep := Dependency{\n\t\t\tStatement: &gorm.Statement{DB: m.DB, Dest: value},\n\t\t}\n\t\tbeDependedOn := map[*schema.Schema]bool{}\n\t\t// support for special table name\n\t\tif err := dep.ParseWithSpecialTableName(value, m.DB.Statement.Table); err != nil {\n\t\t\tm.DB.Logger.Error(context.Background(), \"failed to parse value %#v, got error %v\", value, err)\n\t\t}\n\t\tif _, ok := parsedSchemas[dep.Statement.Schema]; ok {\n\t\t\treturn\n\t\t}\n\t\tparsedSchemas[dep.Statement.Schema] = true\n\n\t\tif !m.DB.IgnoreRelationshipsWhenMigrating {\n\t\t\tfor _, rel := range dep.Schema.Relationships.Relations {\n\t\t\t\tif rel.Field.IgnoreMigration {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif c := rel.ParseConstraint(); c != nil && c.Schema == dep.Statement.Schema && c.Schema != c.ReferenceSchema {\n\t\t\t\t\tdep.Depends = append(dep.Depends, c.ReferenceSchema)\n\t\t\t\t}\n\n\t\t\t\tif rel.Type == schema.HasOne || rel.Type == schema.HasMany {\n\t\t\t\t\tbeDependedOn[rel.FieldSchema] = true\n\t\t\t\t}\n\n\t\t\t\tif rel.JoinTable != nil {\n\t\t\t\t\t// append join value\n\t\t\t\t\tdefer func(rel *schema.Relationship, joinValue interface{}) {\n\t\t\t\t\t\tif !beDependedOn[rel.FieldSchema] {\n\t\t\t\t\t\t\tdep.Depends = append(dep.Depends, rel.FieldSchema)\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tfieldValue := reflect.New(rel.FieldSchema.ModelType).Interface()\n\t\t\t\t\t\t\tparseDependence(fieldValue, autoAdd)\n\t\t\t\t\t\t}\n\t\t\t\t\t\tparseDependence(joinValue, autoAdd)\n\t\t\t\t\t}(rel, reflect.New(rel.JoinTable.ModelType).Interface())\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tvaluesMap[dep.Schema.Table] = dep\n\n\t\tif addToList {\n\t\t\tmodelNames = append(modelNames, dep.Schema.Table)\n\t\t}\n\t}\n\n\tinsertIntoOrderedList = func(name string) {\n\t\tif _, ok := orderedModelNamesMap[name]; ok {\n\t\t\treturn // avoid loop\n\t\t}\n\t\torderedModelNamesMap[name] = true\n\n\t\tif autoAdd {\n\t\t\tdep := valuesMap[name]\n\t\t\tfor _, d := range dep.Depends {\n\t\t\t\tif _, ok := valuesMap[d.Table]; ok {\n\t\t\t\t\tinsertIntoOrderedList(d.Table)\n\t\t\t\t} else {\n\t\t\t\t\tparseDependence(reflect.New(d.ModelType).Interface(), autoAdd)\n\t\t\t\t\tinsertIntoOrderedList(d.Table)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\torderedModelNames = append(orderedModelNames, name)\n\t}\n\n\tfor _, value := range values {\n\t\tif v, ok := value.(string); ok {\n\t\t\tresults = append(results, v)\n\t\t} else {\n\t\t\tparseDependence(value, true)\n\t\t}\n\t}\n\n\tfor _, name := range modelNames {\n\t\tinsertIntoOrderedList(name)\n\t}\n\n\tfor _, name := range orderedModelNames {\n\t\tresults = append(results, valuesMap[name].Statement.Dest)\n\t}\n\treturn\n}\n\n// CurrentTable returns current statement's table expression\nfunc (m Migrator) CurrentTable(stmt *gorm.Statement) interface{} {\n\tif stmt.TableExpr != nil {\n\t\treturn *stmt.TableExpr\n\t}\n\treturn clause.Table{Name: stmt.Table}\n}\n\n// GetIndexes return Indexes []gorm.Index and execErr error\nfunc (m Migrator) GetIndexes(dst interface{}) ([]gorm.Index, error) {\n\treturn nil, errors.New(\"not support\")\n}\n\n// GetTypeAliases return database type aliases\nfunc (m Migrator) GetTypeAliases(databaseTypeName string) []string {\n\treturn nil\n}\n\n// TableType return tableType gorm.TableType and execErr error\nfunc (m Migrator) TableType(dst interface{}) (gorm.TableType, error) {\n\treturn nil, errors.New(\"not support\")\n}\n"
  },
  {
    "path": "migrator/table_type.go",
    "content": "package migrator\n\nimport (\n\t\"database/sql\"\n)\n\n// TableType table type implements TableType interface\ntype TableType struct {\n\tSchemaValue  string\n\tNameValue    string\n\tTypeValue    string\n\tCommentValue sql.NullString\n}\n\n// Schema returns the schema of the table.\nfunc (ct TableType) Schema() string {\n\treturn ct.SchemaValue\n}\n\n// Name returns the name of the table.\nfunc (ct TableType) Name() string {\n\treturn ct.NameValue\n}\n\n// Type returns the type of the table.\nfunc (ct TableType) Type() string {\n\treturn ct.TypeValue\n}\n\n// Comment returns the comment of current table.\nfunc (ct TableType) Comment() (comment string, ok bool) {\n\treturn ct.CommentValue.String, ct.CommentValue.Valid\n}\n"
  },
  {
    "path": "migrator.go",
    "content": "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 (db *DB) Migrator() Migrator {\n\ttx := db.getInstance()\n\n\t// apply scopes to migrator\n\tfor len(tx.Statement.scopes) > 0 {\n\t\ttx = tx.executeScopes()\n\t}\n\n\treturn tx.Dialector.Migrator(tx.Session(&Session{}))\n}\n\n// AutoMigrate run auto migration for given models\nfunc (db *DB) AutoMigrate(dst ...interface{}) error {\n\treturn db.Migrator().AutoMigrate(dst...)\n}\n\n// ViewOption view option\ntype ViewOption struct {\n\tReplace     bool   // If true, exec `CREATE`. If false, exec `CREATE OR REPLACE`\n\tCheckOption string // optional. e.g. `WITH [ CASCADED | LOCAL ] CHECK OPTION`\n\tQuery       *DB    // required subquery.\n}\n\n// ColumnType column type interface\ntype ColumnType interface {\n\tName() string\n\tDatabaseTypeName() string                 // varchar\n\tColumnType() (columnType string, ok bool) // varchar(64)\n\tPrimaryKey() (isPrimaryKey bool, ok bool)\n\tAutoIncrement() (isAutoIncrement bool, ok bool)\n\tLength() (length int64, ok bool)\n\tDecimalSize() (precision int64, scale int64, ok bool)\n\tNullable() (nullable bool, ok bool)\n\tUnique() (unique bool, ok bool)\n\tScanType() reflect.Type\n\tComment() (value string, ok bool)\n\tDefaultValue() (value string, ok bool)\n}\n\ntype Index interface {\n\tTable() string\n\tName() string\n\tColumns() []string\n\tPrimaryKey() (isPrimaryKey bool, ok bool)\n\tUnique() (unique bool, ok bool)\n\tOption() string\n}\n\n// TableType table type interface\ntype TableType interface {\n\tSchema() string\n\tName() string\n\tType() string\n\tComment() (comment string, ok bool)\n}\n\n// Migrator migrator interface\ntype Migrator interface {\n\t// AutoMigrate\n\tAutoMigrate(dst ...interface{}) error\n\n\t// Database\n\tCurrentDatabase() string\n\tFullDataTypeOf(*schema.Field) clause.Expr\n\tGetTypeAliases(databaseTypeName string) []string\n\n\t// Tables\n\tCreateTable(dst ...interface{}) error\n\tDropTable(dst ...interface{}) error\n\tHasTable(dst interface{}) bool\n\tRenameTable(oldName, newName interface{}) error\n\tGetTables() (tableList []string, err error)\n\tTableType(dst interface{}) (TableType, error)\n\n\t// Columns\n\tAddColumn(dst interface{}, field string) error\n\tDropColumn(dst interface{}, field string) error\n\tAlterColumn(dst interface{}, field string) error\n\tMigrateColumn(dst interface{}, field *schema.Field, columnType ColumnType) error\n\t// MigrateColumnUnique migrate column's UNIQUE constraint, it's part of MigrateColumn.\n\tMigrateColumnUnique(dst interface{}, field *schema.Field, columnType ColumnType) error\n\tHasColumn(dst interface{}, field string) bool\n\tRenameColumn(dst interface{}, oldName, field string) error\n\tColumnTypes(dst interface{}) ([]ColumnType, error)\n\n\t// Views\n\tCreateView(name string, option ViewOption) error\n\tDropView(name string) error\n\n\t// Constraints\n\tCreateConstraint(dst interface{}, name string) error\n\tDropConstraint(dst interface{}, name string) error\n\tHasConstraint(dst interface{}, name string) bool\n\n\t// Indexes\n\tCreateIndex(dst interface{}, name string) error\n\tDropIndex(dst interface{}, name string) error\n\tHasIndex(dst interface{}, name string) bool\n\tRenameIndex(dst interface{}, oldName, newName string) error\n\tGetIndexes(dst interface{}) ([]Index, error)\n}\n"
  },
  {
    "path": "model.go",
    "content": "package gorm\n\nimport \"time\"\n\n// Model a basic GoLang struct which includes the following fields: ID, CreatedAt, UpdatedAt, DeletedAt\n// It may be embedded into your model or you may build your own model without it\n//\n//\ttype User struct {\n//\t  gorm.Model\n//\t}\ntype Model struct {\n\tID        uint `gorm:\"primarykey\"`\n\tCreatedAt time.Time\n\tUpdatedAt time.Time\n\tDeletedAt DeletedAt `gorm:\"index\"`\n}\n"
  },
  {
    "path": "prepare_stmt.go",
    "content": "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/gorm/internal/stmt_store\"\n)\n\ntype PreparedStmtDB struct {\n\tStmts stmt_store.Store\n\tMux   *sync.RWMutex\n\tConnPool\n}\n\n// NewPreparedStmtDB creates and initializes a new instance of PreparedStmtDB.\n//\n// Parameters:\n// - connPool: A connection pool that implements the ConnPool interface, used for managing database connections.\n// - maxSize: The maximum number of prepared statements that can be stored in the statement store.\n// - ttl: The time-to-live duration for each prepared statement in the store. Statements older than this duration will be automatically removed.\n//\n// Returns:\n// - A pointer to a PreparedStmtDB instance, which manages prepared statements using the provided connection pool and configuration.\nfunc NewPreparedStmtDB(connPool ConnPool, maxSize int, ttl time.Duration) *PreparedStmtDB {\n\treturn &PreparedStmtDB{\n\t\tConnPool: connPool,                     // Assigns the provided connection pool to manage database connections.\n\t\tStmts:    stmt_store.New(maxSize, ttl), // Initializes a new statement store with the specified maximum size and TTL.\n\t\tMux:      &sync.RWMutex{},              // Sets up a read-write mutex for synchronizing access to the statement store.\n\t}\n}\n\n// GetDBConn returns the underlying *sql.DB connection\nfunc (db *PreparedStmtDB) GetDBConn() (*sql.DB, error) {\n\tif sqldb, ok := db.ConnPool.(*sql.DB); ok {\n\t\treturn sqldb, nil\n\t}\n\n\tif dbConnector, ok := db.ConnPool.(GetDBConnector); ok && dbConnector != nil {\n\t\treturn dbConnector.GetDBConn()\n\t}\n\n\treturn nil, ErrInvalidDB\n}\n\n// Close closes all prepared statements in the store\nfunc (db *PreparedStmtDB) Close() {\n\tdb.Mux.Lock()\n\tdefer db.Mux.Unlock()\n\n\tfor _, key := range db.Stmts.Keys() {\n\t\tdb.Stmts.Delete(key)\n\t}\n}\n\n// Reset Deprecated use Close instead\nfunc (db *PreparedStmtDB) Reset() {\n\tdb.Close()\n}\n\nfunc (db *PreparedStmtDB) prepare(ctx context.Context, conn ConnPool, isTransaction bool, query string) (_ *stmt_store.Stmt, err error) {\n\tdb.Mux.RLock()\n\tif db.Stmts != nil {\n\t\tif stmt, ok := db.Stmts.Get(query); ok && (!stmt.Transaction || isTransaction) {\n\t\t\tdb.Mux.RUnlock()\n\t\t\treturn stmt, stmt.Error()\n\t\t}\n\t}\n\tdb.Mux.RUnlock()\n\n\t// retry\n\tdb.Mux.Lock()\n\tif db.Stmts != nil {\n\t\tif stmt, ok := db.Stmts.Get(query); ok && (!stmt.Transaction || isTransaction) {\n\t\t\tdb.Mux.Unlock()\n\t\t\treturn stmt, stmt.Error()\n\t\t}\n\t}\n\n\treturn db.Stmts.New(ctx, query, isTransaction, conn, db.Mux)\n}\n\nfunc (db *PreparedStmtDB) BeginTx(ctx context.Context, opt *sql.TxOptions) (ConnPool, error) {\n\tif beginner, ok := db.ConnPool.(TxBeginner); ok {\n\t\ttx, err := beginner.BeginTx(ctx, opt)\n\t\treturn &PreparedStmtTX{PreparedStmtDB: db, Tx: tx}, err\n\t}\n\n\tbeginner, ok := db.ConnPool.(ConnPoolBeginner)\n\tif !ok {\n\t\treturn nil, ErrInvalidTransaction\n\t}\n\n\tconnPool, err := beginner.BeginTx(ctx, opt)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif tx, ok := connPool.(Tx); ok {\n\t\treturn &PreparedStmtTX{PreparedStmtDB: db, Tx: tx}, nil\n\t}\n\treturn nil, ErrInvalidTransaction\n}\n\nfunc (db *PreparedStmtDB) ExecContext(ctx context.Context, query string, args ...interface{}) (result sql.Result, err error) {\n\tstmt, err := db.prepare(ctx, db.ConnPool, false, query)\n\tif err == nil {\n\t\tresult, err = stmt.ExecContext(ctx, args...)\n\t\tif errors.Is(err, driver.ErrBadConn) {\n\t\t\tdb.Stmts.Delete(query)\n\t\t}\n\t}\n\treturn result, err\n}\n\nfunc (db *PreparedStmtDB) QueryContext(ctx context.Context, query string, args ...interface{}) (rows *sql.Rows, err error) {\n\tstmt, err := db.prepare(ctx, db.ConnPool, false, query)\n\tif err == nil {\n\t\trows, err = stmt.QueryContext(ctx, args...)\n\t\tif errors.Is(err, driver.ErrBadConn) {\n\t\t\tdb.Stmts.Delete(query)\n\t\t}\n\t}\n\treturn rows, err\n}\n\nfunc (db *PreparedStmtDB) QueryRowContext(ctx context.Context, query string, args ...interface{}) *sql.Row {\n\tstmt, err := db.prepare(ctx, db.ConnPool, false, query)\n\tif err == nil {\n\t\treturn stmt.QueryRowContext(ctx, args...)\n\t}\n\treturn &sql.Row{}\n}\n\nfunc (db *PreparedStmtDB) Ping() error {\n\tconn, err := db.GetDBConn()\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn conn.Ping()\n}\n\ntype PreparedStmtTX struct {\n\tTx\n\tPreparedStmtDB *PreparedStmtDB\n}\n\nfunc (db *PreparedStmtTX) GetDBConn() (*sql.DB, error) {\n\treturn db.PreparedStmtDB.GetDBConn()\n}\n\nfunc (tx *PreparedStmtTX) Commit() error {\n\tif tx.Tx != nil && !reflect.ValueOf(tx.Tx).IsNil() {\n\t\treturn tx.Tx.Commit()\n\t}\n\treturn ErrInvalidTransaction\n}\n\nfunc (tx *PreparedStmtTX) Rollback() error {\n\tif tx.Tx != nil && !reflect.ValueOf(tx.Tx).IsNil() {\n\t\treturn tx.Tx.Rollback()\n\t}\n\treturn ErrInvalidTransaction\n}\n\nfunc (tx *PreparedStmtTX) ExecContext(ctx context.Context, query string, args ...interface{}) (result sql.Result, err error) {\n\tstmt, err := tx.PreparedStmtDB.prepare(ctx, tx.Tx, true, query)\n\tif err == nil {\n\t\tresult, err = tx.Tx.StmtContext(ctx, stmt.Stmt).ExecContext(ctx, args...)\n\t\tif errors.Is(err, driver.ErrBadConn) {\n\t\t\ttx.PreparedStmtDB.Stmts.Delete(query)\n\t\t}\n\t}\n\treturn result, err\n}\n\nfunc (tx *PreparedStmtTX) QueryContext(ctx context.Context, query string, args ...interface{}) (rows *sql.Rows, err error) {\n\tstmt, err := tx.PreparedStmtDB.prepare(ctx, tx.Tx, true, query)\n\tif err == nil {\n\t\trows, err = tx.Tx.StmtContext(ctx, stmt.Stmt).QueryContext(ctx, args...)\n\t\tif errors.Is(err, driver.ErrBadConn) {\n\t\t\ttx.PreparedStmtDB.Stmts.Delete(query)\n\t\t}\n\t}\n\treturn rows, err\n}\n\nfunc (tx *PreparedStmtTX) QueryRowContext(ctx context.Context, query string, args ...interface{}) *sql.Row {\n\tstmt, err := tx.PreparedStmtDB.prepare(ctx, tx.Tx, true, query)\n\tif err == nil {\n\t\treturn tx.Tx.StmtContext(ctx, stmt.Stmt).QueryRowContext(ctx, args...)\n\t}\n\treturn &sql.Row{}\n}\n\nfunc (tx *PreparedStmtTX) Ping() error {\n\tconn, err := tx.GetDBConn()\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn conn.Ping()\n}\n"
  },
  {
    "path": "scan.go",
    "content": "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\"gorm.io/gorm/utils\"\n)\n\n// prepareValues prepare values slice\nfunc prepareValues(values []interface{}, db *DB, columnTypes []*sql.ColumnType, columns []string) {\n\tif db.Statement.Schema != nil {\n\t\tfor idx, name := range columns {\n\t\t\tif field := db.Statement.Schema.LookUpField(name); field != nil {\n\t\t\t\tvalues[idx] = reflect.New(reflect.PointerTo(field.FieldType)).Interface()\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tvalues[idx] = new(interface{})\n\t\t}\n\t} else if len(columnTypes) > 0 {\n\t\tfor idx, columnType := range columnTypes {\n\t\t\tif columnType.ScanType() != nil {\n\t\t\t\tvalues[idx] = reflect.New(reflect.PointerTo(columnType.ScanType())).Interface()\n\t\t\t} else {\n\t\t\t\tvalues[idx] = new(interface{})\n\t\t\t}\n\t\t}\n\t} else {\n\t\tfor idx := range columns {\n\t\t\tvalues[idx] = new(interface{})\n\t\t}\n\t}\n}\n\nfunc scanIntoMap(mapValue map[string]interface{}, values []interface{}, columns []string) {\n\tfor idx, column := range columns {\n\t\tif reflectValue := reflect.Indirect(reflect.Indirect(reflect.ValueOf(values[idx]))); reflectValue.IsValid() {\n\t\t\tmapValue[column] = reflectValue.Interface()\n\t\t\tif valuer, ok := mapValue[column].(driver.Valuer); ok {\n\t\t\t\tmapValue[column], _ = valuer.Value()\n\t\t\t} else if b, ok := mapValue[column].(sql.RawBytes); ok {\n\t\t\t\tmapValue[column] = string(b)\n\t\t\t}\n\t\t} else {\n\t\t\tmapValue[column] = nil\n\t\t}\n\t}\n}\n\nfunc (db *DB) scanIntoStruct(rows Rows, reflectValue reflect.Value, values []interface{}, fields []*schema.Field, joinFields [][]*schema.Field) {\n\tfor idx, field := range fields {\n\t\tif field != nil {\n\t\t\tvalues[idx] = field.NewValuePool.Get()\n\t\t} else if len(fields) == 1 {\n\t\t\tif reflectValue.CanAddr() {\n\t\t\t\tvalues[idx] = reflectValue.Addr().Interface()\n\t\t\t} else {\n\t\t\t\tvalues[idx] = reflectValue.Interface()\n\t\t\t}\n\t\t}\n\t}\n\n\tdb.RowsAffected++\n\tdb.AddError(rows.Scan(values...))\n\tjoinedNestedSchemaMap := make(map[string]interface{})\n\tfor idx, field := range fields {\n\t\tif field == nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tif len(joinFields) == 0 || len(joinFields[idx]) == 0 {\n\t\t\tdb.AddError(field.Set(db.Statement.Context, reflectValue, values[idx]))\n\t\t} else { // joinFields count is larger than 2 when using join\n\t\t\tvar isNilPtrValue bool\n\t\t\tvar relValue reflect.Value\n\t\t\t// does not contain raw dbname\n\t\t\tnestedJoinSchemas := joinFields[idx][:len(joinFields[idx])-1]\n\t\t\t// current reflect value\n\t\t\tcurrentReflectValue := reflectValue\n\t\t\tfullRels := make([]string, 0, len(nestedJoinSchemas))\n\t\t\tfor _, joinSchema := range nestedJoinSchemas {\n\t\t\t\tfullRels = append(fullRels, joinSchema.Name)\n\t\t\t\trelValue = joinSchema.ReflectValueOf(db.Statement.Context, currentReflectValue)\n\t\t\t\tif relValue.Kind() == reflect.Ptr {\n\t\t\t\t\tfullRelsName := utils.JoinNestedRelationNames(fullRels)\n\t\t\t\t\t// same nested structure\n\t\t\t\t\tif _, ok := joinedNestedSchemaMap[fullRelsName]; !ok {\n\t\t\t\t\t\tif value := reflect.ValueOf(values[idx]).Elem(); value.Kind() == reflect.Ptr && value.IsNil() {\n\t\t\t\t\t\t\tisNilPtrValue = true\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\trelValue.Set(reflect.New(relValue.Type().Elem()))\n\t\t\t\t\t\tjoinedNestedSchemaMap[fullRelsName] = nil\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcurrentReflectValue = relValue\n\t\t\t}\n\n\t\t\tif !isNilPtrValue { // ignore if value is nil\n\t\t\t\tf := joinFields[idx][len(joinFields[idx])-1]\n\t\t\t\tdb.AddError(f.Set(db.Statement.Context, relValue, values[idx]))\n\t\t\t}\n\t\t}\n\n\t\t// release data to pool\n\t\tfield.NewValuePool.Put(values[idx])\n\t}\n}\n\n// ScanMode scan data mode\ntype ScanMode uint8\n\n// scan modes\nconst (\n\tScanInitialized         ScanMode = 1 << 0 // 1\n\tScanUpdate              ScanMode = 1 << 1 // 2\n\tScanOnConflictDoNothing ScanMode = 1 << 2 // 4\n)\n\n// Scan scan rows into db statement\nfunc Scan(rows Rows, db *DB, mode ScanMode) {\n\tvar (\n\t\tcolumns, _          = rows.Columns()\n\t\tvalues              = make([]interface{}, len(columns))\n\t\tinitialized         = mode&ScanInitialized != 0\n\t\tupdate              = mode&ScanUpdate != 0\n\t\tonConflictDonothing = mode&ScanOnConflictDoNothing != 0\n\t)\n\n\tif len(db.Statement.ColumnMapping) > 0 {\n\t\tfor i, column := range columns {\n\t\t\tv, ok := db.Statement.ColumnMapping[column]\n\t\t\tif ok {\n\t\t\t\tcolumns[i] = v\n\t\t\t}\n\t\t}\n\t}\n\n\tdb.RowsAffected = 0\n\n\tswitch dest := db.Statement.Dest.(type) {\n\tcase map[string]interface{}, *map[string]interface{}:\n\t\tif initialized || rows.Next() {\n\t\t\tcolumnTypes, _ := rows.ColumnTypes()\n\t\t\tprepareValues(values, db, columnTypes, columns)\n\n\t\t\tdb.RowsAffected++\n\t\t\tdb.AddError(rows.Scan(values...))\n\n\t\t\tmapValue, ok := dest.(map[string]interface{})\n\t\t\tif !ok {\n\t\t\t\tif v, ok := dest.(*map[string]interface{}); ok {\n\t\t\t\t\tif *v == nil {\n\t\t\t\t\t\t*v = map[string]interface{}{}\n\t\t\t\t\t}\n\t\t\t\t\tmapValue = *v\n\t\t\t\t}\n\t\t\t}\n\t\t\tscanIntoMap(mapValue, values, columns)\n\t\t}\n\tcase *[]map[string]interface{}:\n\t\tcolumnTypes, _ := rows.ColumnTypes()\n\t\tfor initialized || rows.Next() {\n\t\t\tprepareValues(values, db, columnTypes, columns)\n\n\t\t\tinitialized = false\n\t\t\tdb.RowsAffected++\n\t\t\tdb.AddError(rows.Scan(values...))\n\n\t\t\tmapValue := map[string]interface{}{}\n\t\t\tscanIntoMap(mapValue, values, columns)\n\t\t\t*dest = append(*dest, mapValue)\n\t\t}\n\tcase *int, *int8, *int16, *int32, *int64,\n\t\t*uint, *uint8, *uint16, *uint32, *uint64, *uintptr,\n\t\t*float32, *float64,\n\t\t*bool, *string, *time.Time,\n\t\t*sql.NullInt32, *sql.NullInt64, *sql.NullFloat64,\n\t\t*sql.NullBool, *sql.NullString, *sql.NullTime:\n\t\tfor initialized || rows.Next() {\n\t\t\tinitialized = false\n\t\t\tdb.RowsAffected++\n\t\t\tdb.AddError(rows.Scan(dest))\n\t\t}\n\tdefault:\n\t\tvar (\n\t\t\tfields       = make([]*schema.Field, len(columns))\n\t\t\tjoinFields   [][]*schema.Field\n\t\t\tsch          = db.Statement.Schema\n\t\t\treflectValue = db.Statement.ReflectValue\n\t\t)\n\n\t\tif reflectValue.Kind() == reflect.Interface {\n\t\t\treflectValue = reflectValue.Elem()\n\t\t}\n\n\t\treflectValueType := reflectValue.Type()\n\t\tswitch reflectValueType.Kind() {\n\t\tcase reflect.Array, reflect.Slice:\n\t\t\treflectValueType = reflectValueType.Elem()\n\t\t}\n\t\tisPtr := reflectValueType.Kind() == reflect.Ptr\n\t\tif isPtr {\n\t\t\treflectValueType = reflectValueType.Elem()\n\t\t}\n\n\t\tif sch != nil {\n\t\t\tif reflectValueType != sch.ModelType && reflectValueType.Kind() == reflect.Struct {\n\t\t\t\tsch, _ = schema.Parse(db.Statement.Dest, db.cacheStore, db.NamingStrategy)\n\t\t\t}\n\n\t\t\tif len(columns) == 1 {\n\t\t\t\t// Is Pluck\n\t\t\t\tif _, ok := reflect.New(reflectValueType).Interface().(sql.Scanner); (reflectValueType != sch.ModelType && ok) || // is scanner\n\t\t\t\t\treflectValueType.Kind() != reflect.Struct || // is not struct\n\t\t\t\t\tsch.ModelType.ConvertibleTo(schema.TimeReflectType) { // is time\n\t\t\t\t\tsch = nil\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Not Pluck\n\t\t\tif sch != nil {\n\t\t\t\tmatchedFieldCount := make(map[string]int, len(columns))\n\t\t\t\tfor idx, column := range columns {\n\t\t\t\t\tif field := sch.LookUpField(column); field != nil && field.Readable {\n\t\t\t\t\t\tfields[idx] = field\n\t\t\t\t\t\tif count, ok := matchedFieldCount[column]; ok {\n\t\t\t\t\t\t\t// handle duplicate fields\n\t\t\t\t\t\t\tfor _, selectField := range sch.Fields {\n\t\t\t\t\t\t\t\tif selectField.DBName == column && selectField.Readable {\n\t\t\t\t\t\t\t\t\tif count == 0 {\n\t\t\t\t\t\t\t\t\t\tmatchedFieldCount[column]++\n\t\t\t\t\t\t\t\t\t\tfields[idx] = selectField\n\t\t\t\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tcount--\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tmatchedFieldCount[column] = 1\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if names := utils.SplitNestedRelationName(column); len(names) > 1 { // has nested relation\n\t\t\t\t\t\taliasName := utils.JoinNestedRelationNames(names[0 : len(names)-1])\n\t\t\t\t\t\tfor _, join := range db.Statement.Joins {\n\t\t\t\t\t\t\tif join.Alias == aliasName {\n\t\t\t\t\t\t\t\tnames = append(strings.Split(join.Name, \".\"), names[len(names)-1])\n\t\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif rel, ok := sch.Relationships.Relations[names[0]]; ok {\n\t\t\t\t\t\t\tsubNameCount := len(names)\n\t\t\t\t\t\t\t// nested relation fields\n\t\t\t\t\t\t\trelFields := make([]*schema.Field, 0, subNameCount-1)\n\t\t\t\t\t\t\trelFields = append(relFields, rel.Field)\n\t\t\t\t\t\t\tfor _, name := range names[1 : subNameCount-1] {\n\t\t\t\t\t\t\t\trel = rel.FieldSchema.Relationships.Relations[name]\n\t\t\t\t\t\t\t\trelFields = append(relFields, rel.Field)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t// latest name is raw dbname\n\t\t\t\t\t\t\tdbName := names[subNameCount-1]\n\t\t\t\t\t\t\tif field := rel.FieldSchema.LookUpField(dbName); field != nil && field.Readable {\n\t\t\t\t\t\t\t\tfields[idx] = field\n\n\t\t\t\t\t\t\t\tif len(joinFields) == 0 {\n\t\t\t\t\t\t\t\t\tjoinFields = make([][]*schema.Field, len(columns))\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\trelFields = append(relFields, field)\n\t\t\t\t\t\t\t\tjoinFields[idx] = relFields\n\t\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tvar val interface{}\n\t\t\t\t\t\tvalues[idx] = &val\n\t\t\t\t\t} else {\n\t\t\t\t\t\tvar val interface{}\n\t\t\t\t\t\tvalues[idx] = &val\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tswitch reflectValue.Kind() {\n\t\tcase reflect.Slice, reflect.Array:\n\t\t\tvar (\n\t\t\t\telem        reflect.Value\n\t\t\t\tisArrayKind = reflectValue.Kind() == reflect.Array\n\t\t\t)\n\n\t\t\tif !update || reflectValue.Len() == 0 {\n\t\t\t\tupdate = false\n\t\t\t\tif isArrayKind {\n\t\t\t\t\tdb.Statement.ReflectValue.Set(reflect.Zero(reflectValue.Type()))\n\t\t\t\t} else {\n\t\t\t\t\t// if the slice cap is externally initialized, the externally initialized slice is directly used here\n\t\t\t\t\tif reflectValue.Cap() == 0 {\n\t\t\t\t\t\tdb.Statement.ReflectValue.Set(reflect.MakeSlice(reflectValue.Type(), 0, 20))\n\t\t\t\t\t} else {\n\t\t\t\t\t\treflectValue.SetLen(0)\n\t\t\t\t\t\tdb.Statement.ReflectValue.Set(reflectValue)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfor initialized || rows.Next() {\n\t\t\tBEGIN:\n\t\t\t\tinitialized = false\n\n\t\t\t\tif update {\n\t\t\t\t\tif int(db.RowsAffected) >= reflectValue.Len() {\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\telem = reflectValue.Index(int(db.RowsAffected))\n\t\t\t\t\tif onConflictDonothing {\n\t\t\t\t\t\tfor _, field := range fields {\n\t\t\t\t\t\t\tif _, ok := field.ValueOf(db.Statement.Context, elem); !ok {\n\t\t\t\t\t\t\t\tdb.RowsAffected++\n\t\t\t\t\t\t\t\tgoto BEGIN\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\telem = reflect.New(reflectValueType)\n\t\t\t\t}\n\n\t\t\t\tdb.scanIntoStruct(rows, elem, values, fields, joinFields)\n\n\t\t\t\tif !update {\n\t\t\t\t\tif !isPtr {\n\t\t\t\t\t\telem = elem.Elem()\n\t\t\t\t\t}\n\t\t\t\t\tif isArrayKind {\n\t\t\t\t\t\tif reflectValue.Len() >= int(db.RowsAffected) {\n\t\t\t\t\t\t\treflectValue.Index(int(db.RowsAffected - 1)).Set(elem)\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\treflectValue = reflect.Append(reflectValue, elem)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif !update {\n\t\t\t\tdb.Statement.ReflectValue.Set(reflectValue)\n\t\t\t}\n\t\tcase reflect.Struct, reflect.Ptr:\n\t\t\tif initialized || rows.Next() {\n\t\t\t\tif mode == ScanInitialized && reflectValue.Kind() == reflect.Struct {\n\t\t\t\t\tdb.Statement.ReflectValue.Set(reflect.Zero(reflectValue.Type()))\n\t\t\t\t}\n\t\t\t\tdb.scanIntoStruct(rows, reflectValue, values, fields, joinFields)\n\t\t\t}\n\t\tdefault:\n\t\t\tdb.AddError(rows.Scan(dest))\n\t\t}\n\t}\n\n\tif err := rows.Err(); err != nil && err != db.Error {\n\t\tdb.AddError(err)\n\t}\n\n\tif db.RowsAffected == 0 && db.Statement.RaiseErrorOnNotFound && db.Error == nil {\n\t\tdb.AddError(ErrRecordNotFound)\n\t}\n}\n"
  },
  {
    "path": "schema/callbacks_test.go",
    "content": "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 UserWithCallback struct{}\n\nfunc (UserWithCallback) BeforeSave(*gorm.DB) error {\n\treturn nil\n}\n\nfunc (UserWithCallback) AfterCreate(*gorm.DB) error {\n\treturn nil\n}\n\nfunc TestCallback(t *testing.T) {\n\tuser, err := schema.Parse(&UserWithCallback{}, &sync.Map{}, schema.NamingStrategy{})\n\tif err != nil {\n\t\tt.Fatalf(\"failed to parse user with callback, got error %v\", err)\n\t}\n\n\tfor _, str := range []string{\"BeforeSave\", \"AfterCreate\"} {\n\t\tif !reflect.Indirect(reflect.ValueOf(user)).FieldByName(str).Interface().(bool) {\n\t\t\tt.Errorf(\"%v should be true\", str)\n\t\t}\n\t}\n\n\tfor _, str := range []string{\"BeforeCreate\", \"BeforeUpdate\", \"AfterUpdate\", \"AfterSave\", \"BeforeDelete\", \"AfterDelete\", \"AfterFind\"} {\n\t\tif reflect.Indirect(reflect.ValueOf(user)).FieldByName(str).Interface().(bool) {\n\t\t\tt.Errorf(\"%v should be false\", str)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "schema/constraint.go",
    "content": "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 regEnLetterAndMidline = regexp.MustCompile(`^[\\w-]+$`)\n\ntype CheckConstraint struct {\n\tName       string\n\tConstraint string // length(phone) >= 10\n\t*Field\n}\n\nfunc (chk *CheckConstraint) GetName() string { return chk.Name }\n\nfunc (chk *CheckConstraint) Build() (sql string, vars []interface{}) {\n\treturn \"CONSTRAINT ? CHECK (?)\", []interface{}{clause.Column{Name: chk.Name}, clause.Expr{SQL: chk.Constraint}}\n}\n\n// ParseCheckConstraints parse schema check constraints\nfunc (schema *Schema) ParseCheckConstraints() map[string]CheckConstraint {\n\tchecks := map[string]CheckConstraint{}\n\tfor _, field := range schema.FieldsByDBName {\n\t\tif chk := field.TagSettings[\"CHECK\"]; chk != \"\" {\n\t\t\tnames := strings.Split(chk, \",\")\n\t\t\tif len(names) > 1 && regEnLetterAndMidline.MatchString(names[0]) {\n\t\t\t\tchecks[names[0]] = CheckConstraint{Name: names[0], Constraint: strings.Join(names[1:], \",\"), Field: field}\n\t\t\t} else {\n\t\t\t\tif names[0] == \"\" {\n\t\t\t\t\tchk = strings.Join(names[1:], \",\")\n\t\t\t\t}\n\t\t\t\tname := schema.namer.CheckerName(schema.Table, field.DBName)\n\t\t\t\tchecks[name] = CheckConstraint{Name: name, Constraint: chk, Field: field}\n\t\t\t}\n\t\t}\n\t}\n\treturn checks\n}\n\ntype UniqueConstraint struct {\n\tName  string\n\tField *Field\n}\n\nfunc (uni *UniqueConstraint) GetName() string { return uni.Name }\n\nfunc (uni *UniqueConstraint) Build() (sql string, vars []interface{}) {\n\treturn \"CONSTRAINT ? UNIQUE (?)\", []interface{}{clause.Column{Name: uni.Name}, clause.Column{Name: uni.Field.DBName}}\n}\n\n// ParseUniqueConstraints parse schema unique constraints\nfunc (schema *Schema) ParseUniqueConstraints() map[string]UniqueConstraint {\n\tuniques := make(map[string]UniqueConstraint)\n\tfor _, field := range schema.Fields {\n\t\tif field.Unique {\n\t\t\tname := schema.namer.UniqueName(schema.Table, field.DBName)\n\t\t\tuniques[name] = UniqueConstraint{Name: name, Field: field}\n\t\t}\n\t}\n\treturn uniques\n}\n"
  },
  {
    "path": "schema/constraint_test.go",
    "content": "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 UserCheck struct {\n\tName  string `gorm:\"check:name_checker,name <> 'jinzhu'\"`\n\tName2 string `gorm:\"check:name <> 'jinzhu'\"`\n\tName3 string `gorm:\"check:,name <> 'jinzhu'\"`\n}\n\nfunc TestParseCheck(t *testing.T) {\n\tuser, err := schema.Parse(&UserCheck{}, &sync.Map{}, schema.NamingStrategy{})\n\tif err != nil {\n\t\tt.Fatalf(\"failed to parse user check, got error %v\", err)\n\t}\n\n\tresults := map[string]schema.CheckConstraint{\n\t\t\"name_checker\": {\n\t\t\tName:       \"name_checker\",\n\t\t\tConstraint: \"name <> 'jinzhu'\",\n\t\t},\n\t\t\"chk_user_checks_name2\": {\n\t\t\tName:       \"chk_user_checks_name2\",\n\t\t\tConstraint: \"name <> 'jinzhu'\",\n\t\t},\n\t\t\"chk_user_checks_name3\": {\n\t\t\tName:       \"chk_user_checks_name3\",\n\t\t\tConstraint: \"name <> 'jinzhu'\",\n\t\t},\n\t}\n\n\tchecks := user.ParseCheckConstraints()\n\n\tfor k, result := range results {\n\t\tv, ok := checks[k]\n\t\tif !ok {\n\t\t\tt.Errorf(\"Failed to found check %v from parsed checks %+v\", k, checks)\n\t\t}\n\n\t\tfor _, name := range []string{\"Name\", \"Constraint\"} {\n\t\t\tif reflect.ValueOf(result).FieldByName(name).Interface() != reflect.ValueOf(v).FieldByName(name).Interface() {\n\t\t\t\tt.Errorf(\n\t\t\t\t\t\"check %v %v should equal, expects %v, got %v\",\n\t\t\t\t\tk, name, reflect.ValueOf(result).FieldByName(name).Interface(), reflect.ValueOf(v).FieldByName(name).Interface(),\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc TestParseUniqueConstraints(t *testing.T) {\n\ttype UserUnique struct {\n\t\tName1 string `gorm:\"unique\"`\n\t\tName2 string `gorm:\"uniqueIndex\"`\n\t}\n\n\tuser, err := schema.Parse(&UserUnique{}, &sync.Map{}, schema.NamingStrategy{})\n\tif err != nil {\n\t\tt.Fatalf(\"failed to parse user unique, got error %v\", err)\n\t}\n\tconstraints := user.ParseUniqueConstraints()\n\n\tresults := map[string]schema.UniqueConstraint{\n\t\t\"uni_user_uniques_name1\": {\n\t\t\tName:  \"uni_user_uniques_name1\",\n\t\t\tField: &schema.Field{Name: \"Name1\", Unique: true},\n\t\t},\n\t}\n\tfor k, result := range results {\n\t\tv, ok := constraints[k]\n\t\tif !ok {\n\t\t\tt.Errorf(\"Failed to found unique constraint %v from parsed constraints %+v\", k, constraints)\n\t\t}\n\t\ttests.AssertObjEqual(t, result, v, \"Name\")\n\t\ttests.AssertObjEqual(t, result.Field, v.Field, \"Name\", \"Unique\", \"UniqueIndex\")\n\t}\n}\n"
  },
  {
    "path": "schema/field.go",
    "content": "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\"sync\"\n\t\"time\"\n\n\t\"github.com/jinzhu/now\"\n\t\"gorm.io/gorm/clause\"\n\t\"gorm.io/gorm/utils\"\n)\n\n// special types' reflect type\nvar (\n\tTimeReflectType    = reflect.TypeOf(time.Time{})\n\tTimePtrReflectType = reflect.TypeOf(&time.Time{})\n\tByteReflectType    = reflect.TypeOf(uint8(0))\n)\n\ntype (\n\t// DataType GORM data type\n\tDataType string\n\t// TimeType GORM time type\n\tTimeType int64\n)\n\n// GORM time types\nconst (\n\tUnixTime        TimeType = 1\n\tUnixSecond      TimeType = 2\n\tUnixMillisecond TimeType = 3\n\tUnixNanosecond  TimeType = 4\n)\n\n// GORM fields types\nconst (\n\tBool   DataType = \"bool\"\n\tInt    DataType = \"int\"\n\tUint   DataType = \"uint\"\n\tFloat  DataType = \"float\"\n\tString DataType = \"string\"\n\tTime   DataType = \"time\"\n\tBytes  DataType = \"bytes\"\n)\n\nconst DefaultAutoIncrementIncrement int64 = 1\n\n// Field is the representation of model schema's field\ntype Field struct {\n\tName                   string\n\tDBName                 string\n\tBindNames              []string\n\tEmbeddedBindNames      []string\n\tDataType               DataType\n\tGORMDataType           DataType\n\tPrimaryKey             bool\n\tAutoIncrement          bool\n\tAutoIncrementIncrement int64\n\tCreatable              bool\n\tUpdatable              bool\n\tReadable               bool\n\tAutoCreateTime         TimeType\n\tAutoUpdateTime         TimeType\n\tHasDefaultValue        bool\n\tDefaultValue           string\n\tDefaultValueInterface  interface{}\n\tNotNull                bool\n\tUnique                 bool\n\tComment                string\n\tSize                   int\n\tPrecision              int\n\tScale                  int\n\tIgnoreMigration        bool\n\tFieldType              reflect.Type\n\tIndirectFieldType      reflect.Type\n\tStructField            reflect.StructField\n\tTag                    reflect.StructTag\n\tTagSettings            map[string]string\n\tSchema                 *Schema\n\tEmbeddedSchema         *Schema\n\tOwnerSchema            *Schema\n\tReflectValueOf         func(context.Context, reflect.Value) reflect.Value\n\tValueOf                func(context.Context, reflect.Value) (value interface{}, zero bool)\n\tSet                    func(context.Context, reflect.Value, interface{}) error\n\tSerializer             SerializerInterface\n\tNewValuePool           FieldNewValuePool\n\n\t// In some db (e.g. MySQL), Unique and UniqueIndex are indistinguishable.\n\t// When a column has a (not Mul) UniqueIndex, Migrator always reports its gorm.ColumnType is Unique.\n\t// It causes field unnecessarily migration.\n\t// Therefore, we need to record the UniqueIndex on this column (exclude Mul UniqueIndex) for MigrateColumnUnique.\n\tUniqueIndex string\n}\n\nfunc (field *Field) BindName() string {\n\treturn strings.Join(field.BindNames, \".\")\n}\n\n// ParseField parses reflect.StructField to Field\nfunc (schema *Schema) ParseField(fieldStruct reflect.StructField) *Field {\n\tvar (\n\t\terr        error\n\t\ttagSetting = ParseTagSetting(fieldStruct.Tag.Get(\"gorm\"), \";\")\n\t)\n\n\tfield := &Field{\n\t\tName:                   fieldStruct.Name,\n\t\tDBName:                 tagSetting[\"COLUMN\"],\n\t\tBindNames:              []string{fieldStruct.Name},\n\t\tEmbeddedBindNames:      []string{fieldStruct.Name},\n\t\tFieldType:              fieldStruct.Type,\n\t\tIndirectFieldType:      fieldStruct.Type,\n\t\tStructField:            fieldStruct,\n\t\tTag:                    fieldStruct.Tag,\n\t\tTagSettings:            tagSetting,\n\t\tSchema:                 schema,\n\t\tCreatable:              true,\n\t\tUpdatable:              true,\n\t\tReadable:               true,\n\t\tPrimaryKey:             utils.CheckTruth(tagSetting[\"PRIMARYKEY\"], tagSetting[\"PRIMARY_KEY\"]),\n\t\tAutoIncrement:          utils.CheckTruth(tagSetting[\"AUTOINCREMENT\"]),\n\t\tHasDefaultValue:        utils.CheckTruth(tagSetting[\"AUTOINCREMENT\"]),\n\t\tNotNull:                utils.CheckTruth(tagSetting[\"NOT NULL\"], tagSetting[\"NOTNULL\"]),\n\t\tUnique:                 utils.CheckTruth(tagSetting[\"UNIQUE\"]),\n\t\tComment:                tagSetting[\"COMMENT\"],\n\t\tAutoIncrementIncrement: DefaultAutoIncrementIncrement,\n\t}\n\n\tfor field.IndirectFieldType.Kind() == reflect.Ptr {\n\t\tfield.IndirectFieldType = field.IndirectFieldType.Elem()\n\t}\n\n\tfieldValue := reflect.New(field.IndirectFieldType)\n\t// if field is valuer, used its value or first field as data type\n\tvaluer, isValuer := fieldValue.Interface().(driver.Valuer)\n\tif isValuer {\n\t\tif _, ok := fieldValue.Interface().(GormDataTypeInterface); !ok {\n\t\t\tif v, err := valuer.Value(); reflect.ValueOf(v).IsValid() && err == nil {\n\t\t\t\tfieldValue = reflect.ValueOf(v)\n\t\t\t}\n\n\t\t\t// Use the field struct's first field type as data type, e.g: use `string` for sql.NullString\n\t\t\tvar getRealFieldValue func(reflect.Value)\n\t\t\tgetRealFieldValue = func(v reflect.Value) {\n\t\t\t\tvar (\n\t\t\t\t\trv     = reflect.Indirect(v)\n\t\t\t\t\trvType = rv.Type()\n\t\t\t\t)\n\n\t\t\t\tif rv.Kind() == reflect.Struct && !rvType.ConvertibleTo(TimeReflectType) {\n\t\t\t\t\tfor i := 0; i < rvType.NumField(); i++ {\n\t\t\t\t\t\tfor key, value := range ParseTagSetting(rvType.Field(i).Tag.Get(\"gorm\"), \";\") {\n\t\t\t\t\t\t\tif _, ok := field.TagSettings[key]; !ok {\n\t\t\t\t\t\t\t\tfield.TagSettings[key] = value\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tfor i := 0; i < rvType.NumField(); i++ {\n\t\t\t\t\t\tnewFieldType := rvType.Field(i).Type\n\t\t\t\t\t\tfor newFieldType.Kind() == reflect.Ptr {\n\t\t\t\t\t\t\tnewFieldType = newFieldType.Elem()\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tfieldValue = reflect.New(newFieldType)\n\t\t\t\t\t\tif rvType != reflect.Indirect(fieldValue).Type() {\n\t\t\t\t\t\t\tgetRealFieldValue(fieldValue)\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif fieldValue.IsValid() {\n\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tgetRealFieldValue(fieldValue)\n\t\t}\n\t}\n\n\tif v, isSerializer := fieldValue.Interface().(SerializerInterface); isSerializer {\n\t\tfield.DataType = String\n\t\tfield.Serializer = v\n\t} else {\n\t\tserializerName := field.TagSettings[\"JSON\"]\n\t\tif serializerName == \"\" {\n\t\t\tserializerName = field.TagSettings[\"SERIALIZER\"]\n\t\t}\n\t\tif serializerName != \"\" {\n\t\t\tif serializer, ok := GetSerializer(serializerName); ok {\n\t\t\t\t// Set default data type to string for serializer\n\t\t\t\tfield.DataType = String\n\t\t\t\tfield.Serializer = serializer\n\t\t\t} else {\n\t\t\t\tschema.err = fmt.Errorf(\"invalid serializer type %v\", serializerName)\n\t\t\t}\n\t\t}\n\t}\n\n\tif num, ok := field.TagSettings[\"AUTOINCREMENTINCREMENT\"]; ok {\n\t\tfield.AutoIncrementIncrement, _ = strconv.ParseInt(num, 10, 64)\n\t}\n\n\tif v, ok := field.TagSettings[\"DEFAULT\"]; ok {\n\t\tfield.HasDefaultValue = true\n\t\tfield.DefaultValue = v\n\t}\n\n\tif num, ok := field.TagSettings[\"SIZE\"]; ok {\n\t\tif field.Size, err = strconv.Atoi(num); err != nil {\n\t\t\tfield.Size = -1\n\t\t}\n\t}\n\n\tif p, ok := field.TagSettings[\"PRECISION\"]; ok {\n\t\tfield.Precision, _ = strconv.Atoi(p)\n\t}\n\n\tif s, ok := field.TagSettings[\"SCALE\"]; ok {\n\t\tfield.Scale, _ = strconv.Atoi(s)\n\t}\n\n\t// default value is function or null or blank (primary keys)\n\tfield.DefaultValue = strings.TrimSpace(field.DefaultValue)\n\tskipParseDefaultValue := strings.Contains(field.DefaultValue, \"(\") &&\n\t\tstrings.Contains(field.DefaultValue, \")\") || strings.ToLower(field.DefaultValue) == \"null\" || field.DefaultValue == \"\"\n\tswitch reflect.Indirect(fieldValue).Kind() {\n\tcase reflect.Bool:\n\t\tfield.DataType = Bool\n\t\tif field.HasDefaultValue && !skipParseDefaultValue {\n\t\t\tif field.DefaultValueInterface, err = strconv.ParseBool(field.DefaultValue); err != nil {\n\t\t\t\tschema.err = fmt.Errorf(\"failed to parse %s as default value for bool, got error: %v\", field.DefaultValue, err)\n\t\t\t}\n\t\t}\n\tcase reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:\n\t\tfield.DataType = Int\n\t\tif field.HasDefaultValue && !skipParseDefaultValue {\n\t\t\tif field.DefaultValueInterface, err = strconv.ParseInt(field.DefaultValue, 0, 64); err != nil {\n\t\t\t\tschema.err = fmt.Errorf(\"failed to parse %s as default value for int, got error: %v\", field.DefaultValue, err)\n\t\t\t}\n\t\t}\n\tcase reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:\n\t\tfield.DataType = Uint\n\t\tif field.HasDefaultValue && !skipParseDefaultValue {\n\t\t\tif field.DefaultValueInterface, err = strconv.ParseUint(field.DefaultValue, 0, 64); err != nil {\n\t\t\t\tschema.err = fmt.Errorf(\"failed to parse %s as default value for uint, got error: %v\", field.DefaultValue, err)\n\t\t\t}\n\t\t}\n\tcase reflect.Float32, reflect.Float64:\n\t\tfield.DataType = Float\n\t\tif field.HasDefaultValue && !skipParseDefaultValue {\n\t\t\tif field.DefaultValueInterface, err = strconv.ParseFloat(field.DefaultValue, 64); err != nil {\n\t\t\t\tschema.err = fmt.Errorf(\"failed to parse %s as default value for float, got error: %v\", field.DefaultValue, err)\n\t\t\t}\n\t\t}\n\tcase reflect.String:\n\t\tfield.DataType = String\n\t\tif field.HasDefaultValue && !skipParseDefaultValue {\n\t\t\tfield.DefaultValue = strings.Trim(field.DefaultValue, \"'\")\n\t\t\tfield.DefaultValue = strings.Trim(field.DefaultValue, `\"`)\n\t\t\tfield.DefaultValueInterface = field.DefaultValue\n\t\t}\n\tcase reflect.Struct:\n\t\tif _, ok := fieldValue.Interface().(*time.Time); ok {\n\t\t\tfield.DataType = Time\n\t\t} else if fieldValue.Type().ConvertibleTo(TimeReflectType) {\n\t\t\tfield.DataType = Time\n\t\t} else if fieldValue.Type().ConvertibleTo(TimePtrReflectType) {\n\t\t\tfield.DataType = Time\n\t\t}\n\t\tif field.HasDefaultValue && !skipParseDefaultValue && field.DataType == Time {\n\t\t\tif t, err := now.Parse(field.DefaultValue); err == nil {\n\t\t\t\tfield.DefaultValueInterface = t\n\t\t\t}\n\t\t}\n\tcase reflect.Array, reflect.Slice:\n\t\tif reflect.Indirect(fieldValue).Type().Elem() == ByteReflectType && field.DataType == \"\" {\n\t\t\tfield.DataType = Bytes\n\t\t}\n\t}\n\n\tif dataTyper, ok := fieldValue.Interface().(GormDataTypeInterface); ok {\n\t\tfield.DataType = DataType(dataTyper.GormDataType())\n\t}\n\n\tif v, ok := field.TagSettings[\"AUTOCREATETIME\"]; (ok && utils.CheckTruth(v)) || (!ok && field.Name == \"CreatedAt\" && (field.DataType == Time || field.DataType == Int || field.DataType == Uint)) {\n\t\tif field.DataType == Time {\n\t\t\tfield.AutoCreateTime = UnixTime\n\t\t} else if strings.ToUpper(v) == \"NANO\" {\n\t\t\tfield.AutoCreateTime = UnixNanosecond\n\t\t} else if strings.ToUpper(v) == \"MILLI\" {\n\t\t\tfield.AutoCreateTime = UnixMillisecond\n\t\t} else {\n\t\t\tfield.AutoCreateTime = UnixSecond\n\t\t}\n\t}\n\n\tif v, ok := field.TagSettings[\"AUTOUPDATETIME\"]; (ok && utils.CheckTruth(v)) || (!ok && field.Name == \"UpdatedAt\" && (field.DataType == Time || field.DataType == Int || field.DataType == Uint)) {\n\t\tif field.DataType == Time {\n\t\t\tfield.AutoUpdateTime = UnixTime\n\t\t} else if strings.ToUpper(v) == \"NANO\" {\n\t\t\tfield.AutoUpdateTime = UnixNanosecond\n\t\t} else if strings.ToUpper(v) == \"MILLI\" {\n\t\t\tfield.AutoUpdateTime = UnixMillisecond\n\t\t} else {\n\t\t\tfield.AutoUpdateTime = UnixSecond\n\t\t}\n\t}\n\n\tif field.GORMDataType == \"\" {\n\t\tfield.GORMDataType = field.DataType\n\t}\n\n\tif val, ok := field.TagSettings[\"TYPE\"]; ok {\n\t\tlowerVal := DataType(strings.ToLower(val))\n\t\tswitch lowerVal {\n\t\tcase Bool, Int, Uint, Float, String, Time, Bytes:\n\t\t\tfield.DataType = lowerVal\n\t\tdefault:\n\t\t\tfield.DataType = DataType(val)\n\t\t}\n\t}\n\n\tif field.Size == 0 {\n\t\tswitch reflect.Indirect(fieldValue).Kind() {\n\t\tcase reflect.Int, reflect.Int64, reflect.Uint, reflect.Uint64, reflect.Float64:\n\t\t\tfield.Size = 64\n\t\tcase reflect.Int8, reflect.Uint8:\n\t\t\tfield.Size = 8\n\t\tcase reflect.Int16, reflect.Uint16:\n\t\t\tfield.Size = 16\n\t\tcase reflect.Int32, reflect.Uint32, reflect.Float32:\n\t\t\tfield.Size = 32\n\t\t}\n\t}\n\n\t// setup permission\n\tif val, ok := field.TagSettings[\"-\"]; ok {\n\t\tval = strings.ToLower(strings.TrimSpace(val))\n\t\tswitch val {\n\t\tcase \"-\":\n\t\t\tfield.Creatable = false\n\t\t\tfield.Updatable = false\n\t\t\tfield.Readable = false\n\t\t\tfield.DataType = \"\"\n\t\tcase \"all\":\n\t\t\tfield.Creatable = false\n\t\t\tfield.Updatable = false\n\t\t\tfield.Readable = false\n\t\t\tfield.DataType = \"\"\n\t\t\tfield.IgnoreMigration = true\n\t\tcase \"migration\":\n\t\t\tfield.IgnoreMigration = true\n\t\t}\n\t}\n\n\tif v, ok := field.TagSettings[\"->\"]; ok {\n\t\tfield.Creatable = false\n\t\tfield.Updatable = false\n\t\tif strings.ToLower(v) == \"false\" {\n\t\t\tfield.Readable = false\n\t\t} else {\n\t\t\tfield.Readable = true\n\t\t}\n\t}\n\n\tif v, ok := field.TagSettings[\"<-\"]; ok {\n\t\tfield.Creatable = true\n\t\tfield.Updatable = true\n\n\t\tif v != \"<-\" {\n\t\t\tif !strings.Contains(v, \"create\") {\n\t\t\t\tfield.Creatable = false\n\t\t\t}\n\n\t\t\tif !strings.Contains(v, \"update\") {\n\t\t\t\tfield.Updatable = false\n\t\t\t}\n\t\t}\n\t}\n\n\t// Normal anonymous field or having `EMBEDDED` tag\n\tif _, ok := field.TagSettings[\"EMBEDDED\"]; ok || (field.GORMDataType != Time && field.GORMDataType != Bytes && !isValuer &&\n\t\tfieldStruct.Anonymous && (field.Creatable || field.Updatable || field.Readable)) {\n\t\tkind := reflect.Indirect(fieldValue).Kind()\n\t\tswitch kind {\n\t\tcase reflect.Struct:\n\t\t\tvar err error\n\t\t\tfield.Creatable = false\n\t\t\tfield.Updatable = false\n\t\t\tfield.Readable = false\n\n\t\t\tcacheStore := &sync.Map{}\n\t\t\tcacheStore.Store(embeddedCacheKey, true)\n\t\t\tif field.EmbeddedSchema, err = getOrParse(fieldValue.Interface(), cacheStore, embeddedNamer{Table: schema.Table, Namer: schema.namer}); err != nil {\n\t\t\t\tschema.err = err\n\t\t\t}\n\n\t\t\tfor _, ef := range field.EmbeddedSchema.Fields {\n\t\t\t\tef.Schema = schema\n\t\t\t\tef.OwnerSchema = field.EmbeddedSchema\n\t\t\t\tef.BindNames = append([]string{fieldStruct.Name}, ef.BindNames...)\n\t\t\t\tif _, ok := field.TagSettings[\"EMBEDDED\"]; ok || !fieldStruct.Anonymous {\n\t\t\t\t\tef.EmbeddedBindNames = append([]string{fieldStruct.Name}, ef.EmbeddedBindNames...)\n\t\t\t\t}\n\t\t\t\t// index is negative means is pointer\n\t\t\t\tif field.FieldType.Kind() == reflect.Struct {\n\t\t\t\t\tef.StructField.Index = append([]int{fieldStruct.Index[0]}, ef.StructField.Index...)\n\t\t\t\t} else {\n\t\t\t\t\tef.StructField.Index = append([]int{-fieldStruct.Index[0] - 1}, ef.StructField.Index...)\n\t\t\t\t}\n\n\t\t\t\tif prefix, ok := field.TagSettings[\"EMBEDDEDPREFIX\"]; ok && ef.DBName != \"\" {\n\t\t\t\t\tef.DBName = prefix + ef.DBName\n\t\t\t\t}\n\n\t\t\t\tif ef.PrimaryKey {\n\t\t\t\t\tif !utils.CheckTruth(ef.TagSettings[\"PRIMARYKEY\"], ef.TagSettings[\"PRIMARY_KEY\"]) {\n\t\t\t\t\t\tef.PrimaryKey = false\n\n\t\t\t\t\t\tif val, ok := ef.TagSettings[\"AUTOINCREMENT\"]; !ok || !utils.CheckTruth(val) {\n\t\t\t\t\t\t\tef.AutoIncrement = false\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif !ef.AutoIncrement && ef.DefaultValue == \"\" {\n\t\t\t\t\t\t\tef.HasDefaultValue = false\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tfor k, v := range field.TagSettings {\n\t\t\t\t\tef.TagSettings[k] = v\n\t\t\t\t}\n\t\t\t}\n\t\tcase reflect.Invalid, reflect.Uintptr, reflect.Array, reflect.Chan, reflect.Func, reflect.Interface,\n\t\t\treflect.Map, reflect.Ptr, reflect.Slice, reflect.UnsafePointer, reflect.Complex64, reflect.Complex128:\n\t\t\tschema.err = fmt.Errorf(\"invalid embedded struct for %s's field %s, should be struct, but got %v\", field.Schema.Name, field.Name, field.FieldType)\n\t\t}\n\t}\n\n\treturn field\n}\n\n// create valuer, setter when parse struct\nfunc (field *Field) setupValuerAndSetter(modelType reflect.Type) {\n\t// Setup NewValuePool\n\tfield.setupNewValuePool()\n\n\t// ValueOf returns field's value and if it is zero\n\tfieldIndex := field.StructField.Index[0]\n\tswitch {\n\tcase len(field.StructField.Index) == 1 && fieldIndex >= 0:\n\t\tfield.ValueOf = func(ctx context.Context, v reflect.Value) (interface{}, bool) {\n\t\t\tv = reflect.Indirect(v)\n\t\t\tfieldValue := v.Field(fieldIndex)\n\t\t\treturn fieldValue.Interface(), fieldValue.IsZero()\n\t\t}\n\tdefault:\n\t\tfield.ValueOf = func(ctx context.Context, v reflect.Value) (interface{}, bool) {\n\t\t\tv = reflect.Indirect(v)\n\t\t\tfor _, fieldIdx := range field.StructField.Index {\n\t\t\t\tif fieldIdx >= 0 {\n\t\t\t\t\tv = v.Field(fieldIdx)\n\t\t\t\t} else {\n\t\t\t\t\tv = v.Field(-fieldIdx - 1)\n\n\t\t\t\t\tif !v.IsNil() {\n\t\t\t\t\t\tv = v.Elem()\n\t\t\t\t\t} else {\n\t\t\t\t\t\treturn nil, true\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfv, zero := v.Interface(), v.IsZero()\n\t\t\treturn fv, zero\n\t\t}\n\t}\n\n\tif field.Serializer != nil {\n\t\toldValuerOf := field.ValueOf\n\t\tfield.ValueOf = func(ctx context.Context, v reflect.Value) (interface{}, bool) {\n\t\t\tvalue, zero := oldValuerOf(ctx, v)\n\n\t\t\ts, ok := value.(SerializerValuerInterface)\n\t\t\tif !ok {\n\t\t\t\ts = field.Serializer\n\t\t\t}\n\n\t\t\treturn &serializer{\n\t\t\t\tField:           field,\n\t\t\t\tSerializeValuer: s,\n\t\t\t\tDestination:     v,\n\t\t\t\tContext:         ctx,\n\t\t\t\tfieldValue:      value,\n\t\t\t}, zero\n\t\t}\n\t}\n\n\t// ReflectValueOf returns field's reflect value\n\tswitch {\n\tcase len(field.StructField.Index) == 1 && fieldIndex >= 0:\n\t\tfield.ReflectValueOf = func(ctx context.Context, v reflect.Value) reflect.Value {\n\t\t\tv = reflect.Indirect(v)\n\t\t\treturn v.Field(fieldIndex)\n\t\t}\n\tdefault:\n\t\tfield.ReflectValueOf = func(ctx context.Context, v reflect.Value) reflect.Value {\n\t\t\tv = reflect.Indirect(v)\n\t\t\tfor idx, fieldIdx := range field.StructField.Index {\n\t\t\t\tif fieldIdx >= 0 {\n\t\t\t\t\tv = v.Field(fieldIdx)\n\t\t\t\t} else {\n\t\t\t\t\tv = v.Field(-fieldIdx - 1)\n\n\t\t\t\t\tif v.IsNil() {\n\t\t\t\t\t\tv.Set(reflect.New(v.Type().Elem()))\n\t\t\t\t\t}\n\n\t\t\t\t\tif idx < len(field.StructField.Index)-1 {\n\t\t\t\t\t\tv = v.Elem()\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn v\n\t\t}\n\t}\n\n\tfallbackSetter := func(ctx context.Context, value reflect.Value, v interface{}, setter func(context.Context, reflect.Value, interface{}) error) (err error) {\n\t\tif v == nil {\n\t\t\tfield.ReflectValueOf(ctx, value).Set(reflect.New(field.FieldType).Elem())\n\t\t} else {\n\t\t\treflectV := reflect.ValueOf(v)\n\t\t\t// Optimal value type acquisition for v\n\t\t\treflectValType := reflectV.Type()\n\n\t\t\tif reflectValType.AssignableTo(field.FieldType) {\n\t\t\t\tif reflectV.Kind() == reflect.Ptr && reflectV.Elem().Kind() == reflect.Ptr {\n\t\t\t\t\treflectV = reflect.Indirect(reflectV)\n\t\t\t\t}\n\t\t\t\tfield.ReflectValueOf(ctx, value).Set(reflectV)\n\t\t\t\treturn\n\t\t\t} else if reflectValType.ConvertibleTo(field.FieldType) {\n\t\t\t\tfield.ReflectValueOf(ctx, value).Set(reflectV.Convert(field.FieldType))\n\t\t\t\treturn\n\t\t\t} else if field.FieldType.Kind() == reflect.Ptr {\n\t\t\t\tfieldValue := field.ReflectValueOf(ctx, value)\n\t\t\t\tfieldType := field.FieldType.Elem()\n\n\t\t\t\tif reflectValType.AssignableTo(fieldType) {\n\t\t\t\t\tif !fieldValue.IsValid() {\n\t\t\t\t\t\tfieldValue = reflect.New(fieldType)\n\t\t\t\t\t} else if fieldValue.IsNil() {\n\t\t\t\t\t\tfieldValue.Set(reflect.New(fieldType))\n\t\t\t\t\t}\n\t\t\t\t\tfieldValue.Elem().Set(reflectV)\n\t\t\t\t\treturn\n\t\t\t\t} else if reflectValType.ConvertibleTo(fieldType) {\n\t\t\t\t\tif fieldValue.IsNil() {\n\t\t\t\t\t\tfieldValue.Set(reflect.New(fieldType))\n\t\t\t\t\t}\n\n\t\t\t\t\tfieldValue.Elem().Set(reflectV.Convert(fieldType))\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif reflectV.Kind() == reflect.Ptr {\n\t\t\t\tif reflectV.IsNil() {\n\t\t\t\t\tfield.ReflectValueOf(ctx, value).Set(reflect.New(field.FieldType).Elem())\n\t\t\t\t} else if reflectV.Type().Elem().AssignableTo(field.FieldType) {\n\t\t\t\t\tfield.ReflectValueOf(ctx, value).Set(reflectV.Elem())\n\t\t\t\t\treturn\n\t\t\t\t} else {\n\t\t\t\t\terr = setter(ctx, value, reflectV.Elem().Interface())\n\t\t\t\t}\n\t\t\t} else if valuer, ok := v.(driver.Valuer); ok {\n\t\t\t\tif v, err = valuer.Value(); err == nil {\n\t\t\t\t\terr = setter(ctx, value, v)\n\t\t\t\t}\n\t\t\t} else if _, ok := v.(clause.Expr); !ok {\n\t\t\t\treturn fmt.Errorf(\"failed to set value %#v to field %s\", v, field.Name)\n\t\t\t}\n\t\t}\n\n\t\treturn\n\t}\n\n\t// Set\n\tswitch field.FieldType.Kind() {\n\tcase reflect.Bool:\n\t\tfield.Set = func(ctx context.Context, value reflect.Value, v interface{}) error {\n\t\t\tswitch data := v.(type) {\n\t\t\tcase **bool:\n\t\t\t\tif data != nil && *data != nil {\n\t\t\t\t\tfield.ReflectValueOf(ctx, value).SetBool(**data)\n\t\t\t\t}\n\t\t\tcase bool:\n\t\t\t\tfield.ReflectValueOf(ctx, value).SetBool(data)\n\t\t\tcase int64:\n\t\t\t\tfield.ReflectValueOf(ctx, value).SetBool(data > 0)\n\t\t\tcase string:\n\t\t\t\tb, _ := strconv.ParseBool(data)\n\t\t\t\tfield.ReflectValueOf(ctx, value).SetBool(b)\n\t\t\tdefault:\n\t\t\t\treturn fallbackSetter(ctx, value, v, field.Set)\n\t\t\t}\n\t\t\treturn nil\n\t\t}\n\tcase reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:\n\t\tfield.Set = func(ctx context.Context, value reflect.Value, v interface{}) (err error) {\n\t\t\tswitch data := v.(type) {\n\t\t\tcase **int64:\n\t\t\t\tif data != nil && *data != nil {\n\t\t\t\t\tfield.ReflectValueOf(ctx, value).SetInt(**data)\n\t\t\t\t}\n\t\t\tcase **int:\n\t\t\t\tif data != nil && *data != nil {\n\t\t\t\t\tfield.ReflectValueOf(ctx, value).SetInt(int64(**data))\n\t\t\t\t}\n\t\t\tcase **int8:\n\t\t\t\tif data != nil && *data != nil {\n\t\t\t\t\tfield.ReflectValueOf(ctx, value).SetInt(int64(**data))\n\t\t\t\t}\n\t\t\tcase **int16:\n\t\t\t\tif data != nil && *data != nil {\n\t\t\t\t\tfield.ReflectValueOf(ctx, value).SetInt(int64(**data))\n\t\t\t\t}\n\t\t\tcase **int32:\n\t\t\t\tif data != nil && *data != nil {\n\t\t\t\t\tfield.ReflectValueOf(ctx, value).SetInt(int64(**data))\n\t\t\t\t}\n\t\t\tcase int64:\n\t\t\t\tfield.ReflectValueOf(ctx, value).SetInt(data)\n\t\t\tcase int:\n\t\t\t\tfield.ReflectValueOf(ctx, value).SetInt(int64(data))\n\t\t\tcase int8:\n\t\t\t\tfield.ReflectValueOf(ctx, value).SetInt(int64(data))\n\t\t\tcase int16:\n\t\t\t\tfield.ReflectValueOf(ctx, value).SetInt(int64(data))\n\t\t\tcase int32:\n\t\t\t\tfield.ReflectValueOf(ctx, value).SetInt(int64(data))\n\t\t\tcase uint:\n\t\t\t\tfield.ReflectValueOf(ctx, value).SetInt(int64(data))\n\t\t\tcase uint8:\n\t\t\t\tfield.ReflectValueOf(ctx, value).SetInt(int64(data))\n\t\t\tcase uint16:\n\t\t\t\tfield.ReflectValueOf(ctx, value).SetInt(int64(data))\n\t\t\tcase uint32:\n\t\t\t\tfield.ReflectValueOf(ctx, value).SetInt(int64(data))\n\t\t\tcase uint64:\n\t\t\t\tfield.ReflectValueOf(ctx, value).SetInt(int64(data))\n\t\t\tcase float32:\n\t\t\t\tfield.ReflectValueOf(ctx, value).SetInt(int64(data))\n\t\t\tcase float64:\n\t\t\t\tfield.ReflectValueOf(ctx, value).SetInt(int64(data))\n\t\t\tcase []byte:\n\t\t\t\treturn field.Set(ctx, value, string(data))\n\t\t\tcase string:\n\t\t\t\tif i, err := strconv.ParseInt(data, 0, 64); err == nil {\n\t\t\t\t\tfield.ReflectValueOf(ctx, value).SetInt(i)\n\t\t\t\t} else {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\tcase time.Time:\n\t\t\t\tif field.AutoCreateTime == UnixNanosecond || field.AutoUpdateTime == UnixNanosecond {\n\t\t\t\t\tfield.ReflectValueOf(ctx, value).SetInt(data.UnixNano())\n\t\t\t\t} else if field.AutoCreateTime == UnixMillisecond || field.AutoUpdateTime == UnixMillisecond {\n\t\t\t\t\tfield.ReflectValueOf(ctx, value).SetInt(data.UnixMilli())\n\t\t\t\t} else {\n\t\t\t\t\tfield.ReflectValueOf(ctx, value).SetInt(data.Unix())\n\t\t\t\t}\n\t\t\tcase *time.Time:\n\t\t\t\tif data != nil {\n\t\t\t\t\tif field.AutoCreateTime == UnixNanosecond || field.AutoUpdateTime == UnixNanosecond {\n\t\t\t\t\t\tfield.ReflectValueOf(ctx, value).SetInt(data.UnixNano())\n\t\t\t\t\t} else if field.AutoCreateTime == UnixMillisecond || field.AutoUpdateTime == UnixMillisecond {\n\t\t\t\t\t\tfield.ReflectValueOf(ctx, value).SetInt(data.UnixMilli())\n\t\t\t\t\t} else {\n\t\t\t\t\t\tfield.ReflectValueOf(ctx, value).SetInt(data.Unix())\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tfield.ReflectValueOf(ctx, value).SetInt(0)\n\t\t\t\t}\n\t\t\tdefault:\n\t\t\t\treturn fallbackSetter(ctx, value, v, field.Set)\n\t\t\t}\n\t\t\treturn err\n\t\t}\n\tcase reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:\n\t\tfield.Set = func(ctx context.Context, value reflect.Value, v interface{}) (err error) {\n\t\t\tswitch data := v.(type) {\n\t\t\tcase **uint64:\n\t\t\t\tif data != nil && *data != nil {\n\t\t\t\t\tfield.ReflectValueOf(ctx, value).SetUint(**data)\n\t\t\t\t}\n\t\t\tcase **uint:\n\t\t\t\tif data != nil && *data != nil {\n\t\t\t\t\tfield.ReflectValueOf(ctx, value).SetUint(uint64(**data))\n\t\t\t\t}\n\t\t\tcase **uint8:\n\t\t\t\tif data != nil && *data != nil {\n\t\t\t\t\tfield.ReflectValueOf(ctx, value).SetUint(uint64(**data))\n\t\t\t\t}\n\t\t\tcase **uint16:\n\t\t\t\tif data != nil && *data != nil {\n\t\t\t\t\tfield.ReflectValueOf(ctx, value).SetUint(uint64(**data))\n\t\t\t\t}\n\t\t\tcase **uint32:\n\t\t\t\tif data != nil && *data != nil {\n\t\t\t\t\tfield.ReflectValueOf(ctx, value).SetUint(uint64(**data))\n\t\t\t\t}\n\t\t\tcase uint64:\n\t\t\t\tfield.ReflectValueOf(ctx, value).SetUint(data)\n\t\t\tcase uint:\n\t\t\t\tfield.ReflectValueOf(ctx, value).SetUint(uint64(data))\n\t\t\tcase uint8:\n\t\t\t\tfield.ReflectValueOf(ctx, value).SetUint(uint64(data))\n\t\t\tcase uint16:\n\t\t\t\tfield.ReflectValueOf(ctx, value).SetUint(uint64(data))\n\t\t\tcase uint32:\n\t\t\t\tfield.ReflectValueOf(ctx, value).SetUint(uint64(data))\n\t\t\tcase int64:\n\t\t\t\tfield.ReflectValueOf(ctx, value).SetUint(uint64(data))\n\t\t\tcase int:\n\t\t\t\tfield.ReflectValueOf(ctx, value).SetUint(uint64(data))\n\t\t\tcase int8:\n\t\t\t\tfield.ReflectValueOf(ctx, value).SetUint(uint64(data))\n\t\t\tcase int16:\n\t\t\t\tfield.ReflectValueOf(ctx, value).SetUint(uint64(data))\n\t\t\tcase int32:\n\t\t\t\tfield.ReflectValueOf(ctx, value).SetUint(uint64(data))\n\t\t\tcase float32:\n\t\t\t\tfield.ReflectValueOf(ctx, value).SetUint(uint64(data))\n\t\t\tcase float64:\n\t\t\t\tfield.ReflectValueOf(ctx, value).SetUint(uint64(data))\n\t\t\tcase []byte:\n\t\t\t\treturn field.Set(ctx, value, string(data))\n\t\t\tcase time.Time:\n\t\t\t\tif field.AutoCreateTime == UnixNanosecond || field.AutoUpdateTime == UnixNanosecond {\n\t\t\t\t\tfield.ReflectValueOf(ctx, value).SetUint(uint64(data.UnixNano()))\n\t\t\t\t} else if field.AutoCreateTime == UnixMillisecond || field.AutoUpdateTime == UnixMillisecond {\n\t\t\t\t\tfield.ReflectValueOf(ctx, value).SetUint(uint64(data.UnixMilli()))\n\t\t\t\t} else {\n\t\t\t\t\tfield.ReflectValueOf(ctx, value).SetUint(uint64(data.Unix()))\n\t\t\t\t}\n\t\t\tcase string:\n\t\t\t\tif i, err := strconv.ParseUint(data, 0, 64); err == nil {\n\t\t\t\t\tfield.ReflectValueOf(ctx, value).SetUint(i)\n\t\t\t\t} else {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\tdefault:\n\t\t\t\treturn fallbackSetter(ctx, value, v, field.Set)\n\t\t\t}\n\t\t\treturn err\n\t\t}\n\tcase reflect.Float32, reflect.Float64:\n\t\tfield.Set = func(ctx context.Context, value reflect.Value, v interface{}) (err error) {\n\t\t\tswitch data := v.(type) {\n\t\t\tcase **float64:\n\t\t\t\tif data != nil && *data != nil {\n\t\t\t\t\tfield.ReflectValueOf(ctx, value).SetFloat(**data)\n\t\t\t\t}\n\t\t\tcase **float32:\n\t\t\t\tif data != nil && *data != nil {\n\t\t\t\t\tfield.ReflectValueOf(ctx, value).SetFloat(float64(**data))\n\t\t\t\t}\n\t\t\tcase float64:\n\t\t\t\tfield.ReflectValueOf(ctx, value).SetFloat(data)\n\t\t\tcase float32:\n\t\t\t\tfield.ReflectValueOf(ctx, value).SetFloat(float64(data))\n\t\t\tcase int64:\n\t\t\t\tfield.ReflectValueOf(ctx, value).SetFloat(float64(data))\n\t\t\tcase int:\n\t\t\t\tfield.ReflectValueOf(ctx, value).SetFloat(float64(data))\n\t\t\tcase int8:\n\t\t\t\tfield.ReflectValueOf(ctx, value).SetFloat(float64(data))\n\t\t\tcase int16:\n\t\t\t\tfield.ReflectValueOf(ctx, value).SetFloat(float64(data))\n\t\t\tcase int32:\n\t\t\t\tfield.ReflectValueOf(ctx, value).SetFloat(float64(data))\n\t\t\tcase uint:\n\t\t\t\tfield.ReflectValueOf(ctx, value).SetFloat(float64(data))\n\t\t\tcase uint8:\n\t\t\t\tfield.ReflectValueOf(ctx, value).SetFloat(float64(data))\n\t\t\tcase uint16:\n\t\t\t\tfield.ReflectValueOf(ctx, value).SetFloat(float64(data))\n\t\t\tcase uint32:\n\t\t\t\tfield.ReflectValueOf(ctx, value).SetFloat(float64(data))\n\t\t\tcase uint64:\n\t\t\t\tfield.ReflectValueOf(ctx, value).SetFloat(float64(data))\n\t\t\tcase []byte:\n\t\t\t\treturn field.Set(ctx, value, string(data))\n\t\t\tcase string:\n\t\t\t\tif i, err := strconv.ParseFloat(data, 64); err == nil {\n\t\t\t\t\tfield.ReflectValueOf(ctx, value).SetFloat(i)\n\t\t\t\t} else {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\tdefault:\n\t\t\t\treturn fallbackSetter(ctx, value, v, field.Set)\n\t\t\t}\n\t\t\treturn err\n\t\t}\n\tcase reflect.String:\n\t\tfield.Set = func(ctx context.Context, value reflect.Value, v interface{}) (err error) {\n\t\t\tswitch data := v.(type) {\n\t\t\tcase **string:\n\t\t\t\tif data != nil && *data != nil {\n\t\t\t\t\tfield.ReflectValueOf(ctx, value).SetString(**data)\n\t\t\t\t}\n\t\t\tcase string:\n\t\t\t\tfield.ReflectValueOf(ctx, value).SetString(data)\n\t\t\tcase []byte:\n\t\t\t\tfield.ReflectValueOf(ctx, value).SetString(string(data))\n\t\t\tcase int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:\n\t\t\t\tfield.ReflectValueOf(ctx, value).SetString(utils.ToString(data))\n\t\t\tcase float64, float32:\n\t\t\t\tfield.ReflectValueOf(ctx, value).SetString(fmt.Sprintf(\"%.\"+strconv.Itoa(field.Precision)+\"f\", data))\n\t\t\tdefault:\n\t\t\t\treturn fallbackSetter(ctx, value, v, field.Set)\n\t\t\t}\n\t\t\treturn err\n\t\t}\n\tdefault:\n\t\tfieldValue := reflect.New(field.FieldType)\n\t\tswitch fieldValue.Elem().Interface().(type) {\n\t\tcase time.Time:\n\t\t\tfield.Set = func(ctx context.Context, value reflect.Value, v interface{}) error {\n\t\t\t\tswitch data := v.(type) {\n\t\t\t\tcase **time.Time:\n\t\t\t\t\tif data != nil && *data != nil {\n\t\t\t\t\t\tfield.Set(ctx, value, *data)\n\t\t\t\t\t}\n\t\t\t\tcase time.Time:\n\t\t\t\t\tfield.ReflectValueOf(ctx, value).Set(reflect.ValueOf(v))\n\t\t\t\tcase *time.Time:\n\t\t\t\t\tif data != nil {\n\t\t\t\t\t\tfield.ReflectValueOf(ctx, value).Set(reflect.ValueOf(data).Elem())\n\t\t\t\t\t} else {\n\t\t\t\t\t\tfield.ReflectValueOf(ctx, value).Set(reflect.ValueOf(time.Time{}))\n\t\t\t\t\t}\n\t\t\t\tcase string:\n\t\t\t\t\tif t, err := now.Parse(data); err == nil {\n\t\t\t\t\t\tfield.ReflectValueOf(ctx, value).Set(reflect.ValueOf(t))\n\t\t\t\t\t} else {\n\t\t\t\t\t\treturn fmt.Errorf(\"failed to set string %v to time.Time field %s, failed to parse it as time, got error %v\", v, field.Name, err)\n\t\t\t\t\t}\n\t\t\t\tdefault:\n\t\t\t\t\treturn fallbackSetter(ctx, value, v, field.Set)\n\t\t\t\t}\n\t\t\t\treturn nil\n\t\t\t}\n\t\tcase *time.Time:\n\t\t\tfield.Set = func(ctx context.Context, value reflect.Value, v interface{}) error {\n\t\t\t\tswitch data := v.(type) {\n\t\t\t\tcase **time.Time:\n\t\t\t\t\tif data != nil && *data != nil {\n\t\t\t\t\t\tfield.ReflectValueOf(ctx, value).Set(reflect.ValueOf(*data))\n\t\t\t\t\t}\n\t\t\t\tcase time.Time:\n\t\t\t\t\tfieldValue := field.ReflectValueOf(ctx, value)\n\t\t\t\t\tif fieldValue.IsNil() {\n\t\t\t\t\t\tfieldValue.Set(reflect.New(field.FieldType.Elem()))\n\t\t\t\t\t}\n\t\t\t\t\tfieldValue.Elem().Set(reflect.ValueOf(v))\n\t\t\t\tcase *time.Time:\n\t\t\t\t\tfield.ReflectValueOf(ctx, value).Set(reflect.ValueOf(v))\n\t\t\t\tcase string:\n\t\t\t\t\tif t, err := now.Parse(data); err == nil {\n\t\t\t\t\t\tfieldValue := field.ReflectValueOf(ctx, value)\n\t\t\t\t\t\tif fieldValue.IsNil() {\n\t\t\t\t\t\t\tif v == \"\" {\n\t\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tfieldValue.Set(reflect.New(field.FieldType.Elem()))\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfieldValue.Elem().Set(reflect.ValueOf(t))\n\t\t\t\t\t} else {\n\t\t\t\t\t\treturn fmt.Errorf(\"failed to set string %v to time.Time field %s, failed to parse it as time, got error %v\", v, field.Name, err)\n\t\t\t\t\t}\n\t\t\t\tdefault:\n\t\t\t\t\treturn fallbackSetter(ctx, value, v, field.Set)\n\t\t\t\t}\n\t\t\t\treturn nil\n\t\t\t}\n\t\tdefault:\n\t\t\tif _, ok := fieldValue.Elem().Interface().(sql.Scanner); ok {\n\t\t\t\t// pointer scanner\n\t\t\t\tfield.Set = func(ctx context.Context, value reflect.Value, v interface{}) (err error) {\n\t\t\t\t\treflectV := reflect.ValueOf(v)\n\t\t\t\t\tif !reflectV.IsValid() {\n\t\t\t\t\t\tfield.ReflectValueOf(ctx, value).Set(reflect.New(field.FieldType).Elem())\n\t\t\t\t\t} else if reflectV.Kind() == reflect.Ptr && reflectV.IsNil() {\n\t\t\t\t\t\treturn\n\t\t\t\t\t} else if reflectV.Type().AssignableTo(field.FieldType) {\n\t\t\t\t\t\tfield.ReflectValueOf(ctx, value).Set(reflectV)\n\t\t\t\t\t} else if reflectV.Kind() == reflect.Ptr {\n\t\t\t\t\t\treturn field.Set(ctx, value, reflectV.Elem().Interface())\n\t\t\t\t\t} else {\n\t\t\t\t\t\tfieldValue := field.ReflectValueOf(ctx, value)\n\t\t\t\t\t\tif fieldValue.IsNil() {\n\t\t\t\t\t\t\tfieldValue.Set(reflect.New(field.FieldType.Elem()))\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif valuer, ok := v.(driver.Valuer); ok {\n\t\t\t\t\t\t\tv, _ = valuer.Value()\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\terr = fieldValue.Interface().(sql.Scanner).Scan(v)\n\t\t\t\t\t}\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t} else if _, ok := fieldValue.Interface().(sql.Scanner); ok {\n\t\t\t\t// struct scanner\n\t\t\t\tfield.Set = func(ctx context.Context, value reflect.Value, v interface{}) (err error) {\n\t\t\t\t\treflectV := reflect.ValueOf(v)\n\t\t\t\t\tif !reflectV.IsValid() {\n\t\t\t\t\t\tfield.ReflectValueOf(ctx, value).Set(reflect.New(field.FieldType).Elem())\n\t\t\t\t\t} else if reflectV.Kind() == reflect.Ptr && reflectV.IsNil() {\n\t\t\t\t\t\treturn\n\t\t\t\t\t} else if reflectV.Type().AssignableTo(field.FieldType) {\n\t\t\t\t\t\tfield.ReflectValueOf(ctx, value).Set(reflectV)\n\t\t\t\t\t} else if reflectV.Kind() == reflect.Ptr {\n\t\t\t\t\t\treturn field.Set(ctx, value, reflectV.Elem().Interface())\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif valuer, ok := v.(driver.Valuer); ok {\n\t\t\t\t\t\t\tv, _ = valuer.Value()\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\terr = field.ReflectValueOf(ctx, value).Addr().Interface().(sql.Scanner).Scan(v)\n\t\t\t\t\t}\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfield.Set = func(ctx context.Context, value reflect.Value, v interface{}) (err error) {\n\t\t\t\t\treturn fallbackSetter(ctx, value, v, field.Set)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tif field.Serializer != nil {\n\t\tvar (\n\t\t\toldFieldSetter = field.Set\n\t\t\tsameElemType   bool\n\t\t\tsameType       = field.FieldType == reflect.ValueOf(field.Serializer).Type()\n\t\t)\n\n\t\tif reflect.ValueOf(field.Serializer).Kind() == reflect.Ptr {\n\t\t\tsameElemType = field.FieldType == reflect.ValueOf(field.Serializer).Type().Elem()\n\t\t}\n\n\t\tserializerValue := reflect.Indirect(reflect.ValueOf(field.Serializer))\n\t\tserializerType := serializerValue.Type()\n\t\tfield.Set = func(ctx context.Context, value reflect.Value, v interface{}) (err error) {\n\t\t\tif s, ok := v.(*serializer); ok {\n\t\t\t\tif s.fieldValue == nil && s.Serializer == nil {\n\t\t\t\t\trv := field.ReflectValueOf(ctx, value)\n\t\t\t\t\tif rv.IsValid() && rv.CanSet() {\n\t\t\t\t\t\trv.Set(reflect.Zero(field.FieldType))\n\t\t\t\t\t}\n\t\t\t\t\treturn nil\n\t\t\t\t}\n\t\t\t\tif s.fieldValue != nil {\n\t\t\t\t\terr = oldFieldSetter(ctx, value, s.fieldValue)\n\t\t\t\t} else if err = s.Serializer.Scan(ctx, field, value, s.value); err == nil {\n\t\t\t\t\tif sameElemType {\n\t\t\t\t\t\tfield.ReflectValueOf(ctx, value).Set(reflect.ValueOf(s.Serializer).Elem())\n\t\t\t\t\t} else if sameType {\n\t\t\t\t\t\tfield.ReflectValueOf(ctx, value).Set(reflect.ValueOf(s.Serializer))\n\t\t\t\t\t}\n\t\t\t\t\tsi := reflect.New(serializerType)\n\t\t\t\t\tsi.Elem().Set(serializerValue)\n\t\t\t\t\ts.Serializer = si.Interface().(SerializerInterface)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\terr = oldFieldSetter(ctx, value, v)\n\t\t\t}\n\t\t\treturn\n\t\t}\n\t}\n}\n\nfunc (field *Field) setupNewValuePool() {\n\tif field.Serializer != nil {\n\t\tserializerValue := reflect.Indirect(reflect.ValueOf(field.Serializer))\n\t\tserializerType := serializerValue.Type()\n\t\tfield.NewValuePool = &sync.Pool{\n\t\t\tNew: func() interface{} {\n\t\t\t\tsi := reflect.New(serializerType)\n\t\t\t\tsi.Elem().Set(serializerValue)\n\t\t\t\treturn &serializer{\n\t\t\t\t\tField:      field,\n\t\t\t\t\tSerializer: si.Interface().(SerializerInterface),\n\t\t\t\t}\n\t\t\t},\n\t\t}\n\t}\n\n\tif field.NewValuePool == nil {\n\t\tfield.NewValuePool = poolInitializer(reflect.PointerTo(field.IndirectFieldType))\n\t}\n}\n"
  },
  {
    "path": "schema/field_test.go",
    "content": "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.io/gorm/schema\"\n\t\"gorm.io/gorm/utils/tests\"\n)\n\nfunc TestFieldValuerAndSetter(t *testing.T) {\n\tvar (\n\t\tuserSchema, _ = schema.Parse(&tests.User{}, &sync.Map{}, schema.NamingStrategy{})\n\t\tuser          = tests.User{\n\t\t\tModel: gorm.Model{\n\t\t\t\tID:        10,\n\t\t\t\tCreatedAt: time.Now(),\n\t\t\t\tUpdatedAt: time.Now(),\n\t\t\t\tDeletedAt: gorm.DeletedAt{Time: time.Now(), Valid: true},\n\t\t\t},\n\t\t\tName:     \"valuer_and_setter\",\n\t\t\tAge:      18,\n\t\t\tBirthday: tests.Now(),\n\t\t\tActive:   true,\n\t\t}\n\t\treflectValue = reflect.ValueOf(&user)\n\t)\n\n\t// test valuer\n\tvalues := map[string]interface{}{\n\t\t\"name\":       user.Name,\n\t\t\"id\":         user.ID,\n\t\t\"created_at\": user.CreatedAt,\n\t\t\"updated_at\": user.UpdatedAt,\n\t\t\"deleted_at\": user.DeletedAt,\n\t\t\"age\":        user.Age,\n\t\t\"birthday\":   user.Birthday,\n\t\t\"active\":     true,\n\t}\n\tcheckField(t, userSchema, reflectValue, values)\n\n\tvar f *bool\n\t// test setter\n\tnewValues := map[string]interface{}{\n\t\t\"name\":       \"valuer_and_setter_2\",\n\t\t\"id\":         2,\n\t\t\"created_at\": time.Now(),\n\t\t\"updated_at\": nil,\n\t\t\"deleted_at\": time.Now(),\n\t\t\"age\":        20,\n\t\t\"birthday\":   time.Now(),\n\t\t\"active\":     f,\n\t}\n\n\tfor k, v := range newValues {\n\t\tif err := userSchema.FieldsByDBName[k].Set(context.Background(), reflectValue, v); err != nil {\n\t\t\tt.Errorf(\"no error should happen when assign value to field %v, but got %v\", k, err)\n\t\t}\n\t}\n\tnewValues[\"updated_at\"] = time.Time{}\n\tnewValues[\"active\"] = false\n\tcheckField(t, userSchema, reflectValue, newValues)\n\n\t// test valuer and other type\n\tage := myint(10)\n\tvar nilTime *time.Time\n\tnewValues2 := map[string]interface{}{\n\t\t\"name\":       sql.NullString{String: \"valuer_and_setter_3\", Valid: true},\n\t\t\"id\":         &sql.NullInt64{Int64: 3, Valid: true},\n\t\t\"created_at\": tests.Now(),\n\t\t\"updated_at\": nilTime,\n\t\t\"deleted_at\": time.Now(),\n\t\t\"age\":        &age,\n\t\t\"birthday\":   mytime(time.Now()),\n\t\t\"active\":     mybool(true),\n\t}\n\n\tfor k, v := range newValues2 {\n\t\tif err := userSchema.FieldsByDBName[k].Set(context.Background(), reflectValue, v); err != nil {\n\t\t\tt.Errorf(\"no error should happen when assign value to field %v, but got %v\", k, err)\n\t\t}\n\t}\n\tnewValues2[\"updated_at\"] = time.Time{}\n\tcheckField(t, userSchema, reflectValue, newValues2)\n}\n\nfunc TestPointerFieldValuerAndSetter(t *testing.T) {\n\tvar (\n\t\tuserSchema, _      = schema.Parse(&User{}, &sync.Map{}, schema.NamingStrategy{})\n\t\tname               = \"pointer_field_valuer_and_setter\"\n\t\tage           uint = 18\n\t\tactive             = true\n\t\tuser               = User{\n\t\t\tModel: &gorm.Model{\n\t\t\t\tID:        10,\n\t\t\t\tCreatedAt: time.Now(),\n\t\t\t\tDeletedAt: gorm.DeletedAt{Time: time.Now(), Valid: true},\n\t\t\t},\n\t\t\tName:     &name,\n\t\t\tAge:      &age,\n\t\t\tBirthday: tests.Now(),\n\t\t\tActive:   &active,\n\t\t}\n\t\treflectValue = reflect.ValueOf(&user)\n\t)\n\n\t// test valuer\n\tvalues := map[string]interface{}{\n\t\t\"name\":       user.Name,\n\t\t\"id\":         user.ID,\n\t\t\"created_at\": user.CreatedAt,\n\t\t\"deleted_at\": user.DeletedAt,\n\t\t\"age\":        user.Age,\n\t\t\"birthday\":   user.Birthday,\n\t\t\"active\":     true,\n\t}\n\tcheckField(t, userSchema, reflectValue, values)\n\n\t// test setter\n\tnewValues := map[string]interface{}{\n\t\t\"name\":       \"valuer_and_setter_2\",\n\t\t\"id\":         2,\n\t\t\"created_at\": time.Now(),\n\t\t\"deleted_at\": time.Now(),\n\t\t\"age\":        20,\n\t\t\"birthday\":   time.Now(),\n\t\t\"active\":     false,\n\t}\n\n\tfor k, v := range newValues {\n\t\tif err := userSchema.FieldsByDBName[k].Set(context.Background(), reflectValue, v); err != nil {\n\t\t\tt.Errorf(\"no error should happen when assign value to field %v, but got %v\", k, err)\n\t\t}\n\t}\n\tcheckField(t, userSchema, reflectValue, newValues)\n\n\t// test valuer and other type\n\tage2 := myint(10)\n\tnewValues2 := map[string]interface{}{\n\t\t\"name\":       sql.NullString{String: \"valuer_and_setter_3\", Valid: true},\n\t\t\"id\":         &sql.NullInt64{Int64: 3, Valid: true},\n\t\t\"created_at\": tests.Now(),\n\t\t\"deleted_at\": time.Now(),\n\t\t\"age\":        &age2,\n\t\t\"birthday\":   mytime(time.Now()),\n\t\t\"active\":     mybool(true),\n\t}\n\n\tfor k, v := range newValues2 {\n\t\tif err := userSchema.FieldsByDBName[k].Set(context.Background(), reflectValue, v); err != nil {\n\t\t\tt.Errorf(\"no error should happen when assign value to field %v, but got %v\", k, err)\n\t\t}\n\t}\n\tcheckField(t, userSchema, reflectValue, newValues2)\n}\n\nfunc TestAdvancedDataTypeValuerAndSetter(t *testing.T) {\n\tvar (\n\t\tuserSchema, _ = schema.Parse(&AdvancedDataTypeUser{}, &sync.Map{}, schema.NamingStrategy{})\n\t\tname          = \"advanced_data_type_valuer_and_setter\"\n\t\tdeletedAt     = mytime(time.Now())\n\t\tisAdmin       = mybool(false)\n\t\tuser          = AdvancedDataTypeUser{\n\t\t\tID:           sql.NullInt64{Int64: 10, Valid: true},\n\t\t\tName:         &sql.NullString{String: name, Valid: true},\n\t\t\tBirthday:     sql.NullTime{Time: time.Now(), Valid: true},\n\t\t\tRegisteredAt: mytime(time.Now()),\n\t\t\tDeletedAt:    &deletedAt,\n\t\t\tActive:       mybool(true),\n\t\t\tAdmin:        &isAdmin,\n\t\t}\n\t\treflectValue = reflect.ValueOf(&user)\n\t)\n\n\t// test valuer\n\tvalues := map[string]interface{}{\n\t\t\"id\":            user.ID,\n\t\t\"name\":          user.Name,\n\t\t\"birthday\":      user.Birthday,\n\t\t\"registered_at\": user.RegisteredAt,\n\t\t\"deleted_at\":    user.DeletedAt,\n\t\t\"active\":        user.Active,\n\t\t\"admin\":         user.Admin,\n\t}\n\tcheckField(t, userSchema, reflectValue, values)\n\n\t// test setter\n\tnewDeletedAt := mytime(time.Now())\n\tnewIsAdmin := mybool(true)\n\tnewValues := map[string]interface{}{\n\t\t\"id\":            sql.NullInt64{Int64: 1, Valid: true},\n\t\t\"name\":          &sql.NullString{String: name + \"rename\", Valid: true},\n\t\t\"birthday\":      time.Now(),\n\t\t\"registered_at\": mytime(time.Now()),\n\t\t\"deleted_at\":    &newDeletedAt,\n\t\t\"active\":        mybool(false),\n\t\t\"admin\":         &newIsAdmin,\n\t}\n\n\tfor k, v := range newValues {\n\t\tif err := userSchema.FieldsByDBName[k].Set(context.Background(), reflectValue, v); err != nil {\n\t\t\tt.Errorf(\"no error should happen when assign value to field %v, but got %v\", k, err)\n\t\t}\n\t}\n\tcheckField(t, userSchema, reflectValue, newValues)\n\n\tnewValues2 := map[string]interface{}{\n\t\t\"id\":            5,\n\t\t\"name\":          name + \"rename2\",\n\t\t\"birthday\":      time.Now(),\n\t\t\"registered_at\": time.Now(),\n\t\t\"deleted_at\":    time.Now(),\n\t\t\"active\":        true,\n\t\t\"admin\":         false,\n\t}\n\n\tfor k, v := range newValues2 {\n\t\tif err := userSchema.FieldsByDBName[k].Set(context.Background(), reflectValue, v); err != nil {\n\t\t\tt.Errorf(\"no error should happen when assign value to field %v, but got %v\", k, err)\n\t\t}\n\t}\n\tcheckField(t, userSchema, reflectValue, newValues2)\n}\n\ntype UserWithPermissionControl struct {\n\tID    uint\n\tName  string `gorm:\"-\"`\n\tName2 string `gorm:\"->\"`\n\tName3 string `gorm:\"<-\"`\n\tName4 string `gorm:\"<-:create\"`\n\tName5 string `gorm:\"<-:update\"`\n\tName6 string `gorm:\"<-:create,update\"`\n\tName7 string `gorm:\"->:false;<-:create,update\"`\n\tName8 string `gorm:\"->;-:migration\"`\n}\n\nfunc TestParseFieldWithPermission(t *testing.T) {\n\tuser, err := schema.Parse(&UserWithPermissionControl{}, &sync.Map{}, schema.NamingStrategy{})\n\tif err != nil {\n\t\tt.Fatalf(\"Failed to parse user with permission, got error %v\", err)\n\t}\n\n\tfields := []*schema.Field{\n\t\t{Name: \"ID\", DBName: \"id\", BindNames: []string{\"ID\"}, DataType: schema.Uint, PrimaryKey: true, Size: 64, Creatable: true, Updatable: true, Readable: true, HasDefaultValue: true, AutoIncrement: true},\n\t\t{Name: \"Name\", DBName: \"\", BindNames: []string{\"Name\"}, DataType: \"\", Tag: `gorm:\"-\"`, Creatable: false, Updatable: false, Readable: false},\n\t\t{Name: \"Name2\", DBName: \"name2\", BindNames: []string{\"Name2\"}, DataType: schema.String, Tag: `gorm:\"->\"`, Creatable: false, Updatable: false, Readable: true},\n\t\t{Name: \"Name3\", DBName: \"name3\", BindNames: []string{\"Name3\"}, DataType: schema.String, Tag: `gorm:\"<-\"`, Creatable: true, Updatable: true, Readable: true},\n\t\t{Name: \"Name4\", DBName: \"name4\", BindNames: []string{\"Name4\"}, DataType: schema.String, Tag: `gorm:\"<-:create\"`, Creatable: true, Updatable: false, Readable: true},\n\t\t{Name: \"Name5\", DBName: \"name5\", BindNames: []string{\"Name5\"}, DataType: schema.String, Tag: `gorm:\"<-:update\"`, Creatable: false, Updatable: true, Readable: true},\n\t\t{Name: \"Name6\", DBName: \"name6\", BindNames: []string{\"Name6\"}, DataType: schema.String, Tag: `gorm:\"<-:create,update\"`, Creatable: true, Updatable: true, Readable: true},\n\t\t{Name: \"Name7\", DBName: \"name7\", BindNames: []string{\"Name7\"}, DataType: schema.String, Tag: `gorm:\"->:false;<-:create,update\"`, Creatable: true, Updatable: true, Readable: false},\n\t\t{Name: \"Name8\", DBName: \"name8\", BindNames: []string{\"Name8\"}, DataType: schema.String, Tag: `gorm:\"->;-:migration\"`, Creatable: false, Updatable: false, Readable: true, IgnoreMigration: true},\n\t}\n\n\tfor _, f := range fields {\n\t\tcheckSchemaField(t, user, f, func(f *schema.Field) {})\n\t}\n}\n\ntype (\n\tID      int64\n\tINT     int\n\tINT8    int8\n\tINT16   int16\n\tINT32   int32\n\tINT64   int64\n\tUINT    uint\n\tUINT8   uint8\n\tUINT16  uint16\n\tUINT32  uint32\n\tUINT64  uint64\n\tFLOAT32 float32\n\tFLOAT64 float64\n\tBOOL    bool\n\tSTRING  string\n\tTIME    time.Time\n\tBYTES   []byte\n\n\tTypeAlias struct {\n\t\tID\n\t\tINT     `gorm:\"column:fint\"`\n\t\tINT8    `gorm:\"column:fint8\"`\n\t\tINT16   `gorm:\"column:fint16\"`\n\t\tINT32   `gorm:\"column:fint32\"`\n\t\tINT64   `gorm:\"column:fint64\"`\n\t\tUINT    `gorm:\"column:fuint\"`\n\t\tUINT8   `gorm:\"column:fuint8\"`\n\t\tUINT16  `gorm:\"column:fuint16\"`\n\t\tUINT32  `gorm:\"column:fuint32\"`\n\t\tUINT64  `gorm:\"column:fuint64\"`\n\t\tFLOAT32 `gorm:\"column:ffloat32\"`\n\t\tFLOAT64 `gorm:\"column:ffloat64\"`\n\t\tBOOL    `gorm:\"column:fbool\"`\n\t\tSTRING  `gorm:\"column:fstring\"`\n\t\tTIME    `gorm:\"column:ftime\"`\n\t\tBYTES   `gorm:\"column:fbytes\"`\n\t}\n)\n\nfunc TestTypeAliasField(t *testing.T) {\n\talias, err := schema.Parse(&TypeAlias{}, &sync.Map{}, schema.NamingStrategy{})\n\tif err != nil {\n\t\tt.Fatalf(\"Failed to parse TypeAlias with permission, got error %v\", err)\n\t}\n\n\tfields := []*schema.Field{\n\t\t{Name: \"ID\", DBName: \"id\", BindNames: []string{\"ID\"}, DataType: schema.Int, Creatable: true, Updatable: true, Readable: true, Size: 64, PrimaryKey: true, HasDefaultValue: true, AutoIncrement: true},\n\t\t{Name: \"INT\", DBName: \"fint\", BindNames: []string{\"INT\"}, DataType: schema.Int, Creatable: true, Updatable: true, Readable: true, Size: 64, Tag: `gorm:\"column:fint\"`},\n\t\t{Name: \"INT8\", DBName: \"fint8\", BindNames: []string{\"INT8\"}, DataType: schema.Int, Creatable: true, Updatable: true, Readable: true, Size: 8, Tag: `gorm:\"column:fint8\"`},\n\t\t{Name: \"INT16\", DBName: \"fint16\", BindNames: []string{\"INT16\"}, DataType: schema.Int, Creatable: true, Updatable: true, Readable: true, Size: 16, Tag: `gorm:\"column:fint16\"`},\n\t\t{Name: \"INT32\", DBName: \"fint32\", BindNames: []string{\"INT32\"}, DataType: schema.Int, Creatable: true, Updatable: true, Readable: true, Size: 32, Tag: `gorm:\"column:fint32\"`},\n\t\t{Name: \"INT64\", DBName: \"fint64\", BindNames: []string{\"INT64\"}, DataType: schema.Int, Creatable: true, Updatable: true, Readable: true, Size: 64, Tag: `gorm:\"column:fint64\"`},\n\t\t{Name: \"UINT\", DBName: \"fuint\", BindNames: []string{\"UINT\"}, DataType: schema.Uint, Creatable: true, Updatable: true, Readable: true, Size: 64, Tag: `gorm:\"column:fuint\"`},\n\t\t{Name: \"UINT8\", DBName: \"fuint8\", BindNames: []string{\"UINT8\"}, DataType: schema.Uint, Creatable: true, Updatable: true, Readable: true, Size: 8, Tag: `gorm:\"column:fuint8\"`},\n\t\t{Name: \"UINT16\", DBName: \"fuint16\", BindNames: []string{\"UINT16\"}, DataType: schema.Uint, Creatable: true, Updatable: true, Readable: true, Size: 16, Tag: `gorm:\"column:fuint16\"`},\n\t\t{Name: \"UINT32\", DBName: \"fuint32\", BindNames: []string{\"UINT32\"}, DataType: schema.Uint, Creatable: true, Updatable: true, Readable: true, Size: 32, Tag: `gorm:\"column:fuint32\"`},\n\t\t{Name: \"UINT64\", DBName: \"fuint64\", BindNames: []string{\"UINT64\"}, DataType: schema.Uint, Creatable: true, Updatable: true, Readable: true, Size: 64, Tag: `gorm:\"column:fuint64\"`},\n\t\t{Name: \"FLOAT32\", DBName: \"ffloat32\", BindNames: []string{\"FLOAT32\"}, DataType: schema.Float, Creatable: true, Updatable: true, Readable: true, Size: 32, Tag: `gorm:\"column:ffloat32\"`},\n\t\t{Name: \"FLOAT64\", DBName: \"ffloat64\", BindNames: []string{\"FLOAT64\"}, DataType: schema.Float, Creatable: true, Updatable: true, Readable: true, Size: 64, Tag: `gorm:\"column:ffloat64\"`},\n\t\t{Name: \"BOOL\", DBName: \"fbool\", BindNames: []string{\"BOOL\"}, DataType: schema.Bool, Creatable: true, Updatable: true, Readable: true, Tag: `gorm:\"column:fbool\"`},\n\t\t{Name: \"STRING\", DBName: \"fstring\", BindNames: []string{\"STRING\"}, DataType: schema.String, Creatable: true, Updatable: true, Readable: true, Tag: `gorm:\"column:fstring\"`},\n\t\t{Name: \"TIME\", DBName: \"ftime\", BindNames: []string{\"TIME\"}, DataType: schema.Time, Creatable: true, Updatable: true, Readable: true, Tag: `gorm:\"column:ftime\"`},\n\t\t{Name: \"BYTES\", DBName: \"fbytes\", BindNames: []string{\"BYTES\"}, DataType: schema.Bytes, Creatable: true, Updatable: true, Readable: true, Tag: `gorm:\"column:fbytes\"`},\n\t}\n\n\tfor _, f := range fields {\n\t\tcheckSchemaField(t, alias, f, func(f *schema.Field) {})\n\t}\n}\n"
  },
  {
    "path": "schema/index.go",
    "content": "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 // UNIQUE | FULLTEXT | SPATIAL\n\tType    string // btree, hash, gist, spgist, gin, and brin\n\tWhere   string\n\tComment string\n\tOption  string        // WITH PARSER parser_name\n\tFields  []IndexOption // Note: IndexOption's Field maybe the same\n}\n\ntype IndexOption struct {\n\t*Field\n\tExpression string\n\tSort       string // DESC, ASC\n\tCollate    string\n\tLength     int\n\tPriority   int\n}\n\n// ParseIndexes parse schema indexes\nfunc (schema *Schema) ParseIndexes() []*Index {\n\tindexesByName := map[string]*Index{}\n\tindexes := []*Index{}\n\n\tfor _, field := range schema.Fields {\n\t\tif field.TagSettings[\"INDEX\"] != \"\" || field.TagSettings[\"UNIQUEINDEX\"] != \"\" {\n\t\t\tfieldIndexes, err := parseFieldIndexes(field)\n\t\t\tif err != nil {\n\t\t\t\tschema.err = err\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tfor _, index := range fieldIndexes {\n\t\t\t\tidx := indexesByName[index.Name]\n\t\t\t\tif idx == nil {\n\t\t\t\t\tidx = &Index{Name: index.Name}\n\t\t\t\t\tindexesByName[index.Name] = idx\n\t\t\t\t\tindexes = append(indexes, idx)\n\t\t\t\t}\n\t\t\t\tidx.Name = index.Name\n\t\t\t\tif idx.Class == \"\" {\n\t\t\t\t\tidx.Class = index.Class\n\t\t\t\t}\n\t\t\t\tif idx.Type == \"\" {\n\t\t\t\t\tidx.Type = index.Type\n\t\t\t\t}\n\t\t\t\tif idx.Where == \"\" {\n\t\t\t\t\tidx.Where = index.Where\n\t\t\t\t}\n\t\t\t\tif idx.Comment == \"\" {\n\t\t\t\t\tidx.Comment = index.Comment\n\t\t\t\t}\n\t\t\t\tif idx.Option == \"\" {\n\t\t\t\t\tidx.Option = index.Option\n\t\t\t\t}\n\n\t\t\t\tidx.Fields = append(idx.Fields, index.Fields...)\n\t\t\t\tsort.Slice(idx.Fields, func(i, j int) bool {\n\t\t\t\t\treturn idx.Fields[i].Priority < idx.Fields[j].Priority\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t}\n\tfor _, index := range indexes {\n\t\tif index.Class == \"UNIQUE\" && len(index.Fields) == 1 {\n\t\t\tindex.Fields[0].Field.UniqueIndex = index.Name\n\t\t}\n\t}\n\treturn indexes\n}\n\nfunc (schema *Schema) LookIndex(name string) *Index {\n\tif schema != nil {\n\t\tindexes := schema.ParseIndexes()\n\t\tfor _, index := range indexes {\n\t\t\tif index.Name == name {\n\t\t\t\treturn index\n\t\t\t}\n\n\t\t\tfor _, field := range index.Fields {\n\t\t\t\tif field.Name == name {\n\t\t\t\t\treturn index\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc parseFieldIndexes(field *Field) (indexes []Index, err error) {\n\tfor _, value := range strings.Split(field.Tag.Get(\"gorm\"), \";\") {\n\t\tif value != \"\" {\n\t\t\tv := strings.Split(value, \":\")\n\t\t\tk := strings.TrimSpace(strings.ToUpper(v[0]))\n\t\t\tif k == \"INDEX\" || k == \"UNIQUEINDEX\" {\n\t\t\t\tvar (\n\t\t\t\t\tname       string\n\t\t\t\t\ttag        = strings.Join(v[1:], \":\")\n\t\t\t\t\tidx        = strings.IndexByte(tag, ',')\n\t\t\t\t\ttagSetting = strings.Join(strings.Split(tag, \",\")[1:], \",\")\n\t\t\t\t\tsettings   = ParseTagSetting(tagSetting, \",\")\n\t\t\t\t\tlength, _  = strconv.Atoi(settings[\"LENGTH\"])\n\t\t\t\t)\n\n\t\t\t\tif idx == -1 {\n\t\t\t\t\tidx = len(tag)\n\t\t\t\t}\n\n\t\t\t\tname = tag[0:idx]\n\t\t\t\tif name == \"\" {\n\t\t\t\t\tsubName := field.Name\n\t\t\t\t\tconst key = \"COMPOSITE\"\n\t\t\t\t\tif composite, found := settings[key]; found {\n\t\t\t\t\t\tif len(composite) == 0 || composite == key {\n\t\t\t\t\t\t\terr = fmt.Errorf(\n\t\t\t\t\t\t\t\t\"the composite tag of %s.%s cannot be empty\",\n\t\t\t\t\t\t\t\tfield.Schema.Name,\n\t\t\t\t\t\t\t\tfield.Name)\n\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t}\n\t\t\t\t\t\tsubName = composite\n\t\t\t\t\t}\n\t\t\t\t\tname = field.Schema.namer.IndexName(\n\t\t\t\t\t\tfield.Schema.Table, subName)\n\t\t\t\t}\n\n\t\t\t\tif (k == \"UNIQUEINDEX\") || settings[\"UNIQUE\"] != \"\" {\n\t\t\t\t\tsettings[\"CLASS\"] = \"UNIQUE\"\n\t\t\t\t}\n\n\t\t\t\tpriority, err := strconv.Atoi(settings[\"PRIORITY\"])\n\t\t\t\tif err != nil {\n\t\t\t\t\tpriority = 10\n\t\t\t\t}\n\n\t\t\t\tindexes = append(indexes, Index{\n\t\t\t\t\tName:    name,\n\t\t\t\t\tClass:   settings[\"CLASS\"],\n\t\t\t\t\tType:    settings[\"TYPE\"],\n\t\t\t\t\tWhere:   settings[\"WHERE\"],\n\t\t\t\t\tComment: settings[\"COMMENT\"],\n\t\t\t\t\tOption:  settings[\"OPTION\"],\n\t\t\t\t\tFields: []IndexOption{{\n\t\t\t\t\t\tField:      field,\n\t\t\t\t\t\tExpression: settings[\"EXPRESSION\"],\n\t\t\t\t\t\tSort:       settings[\"SORT\"],\n\t\t\t\t\t\tCollate:    settings[\"COLLATE\"],\n\t\t\t\t\t\tLength:     length,\n\t\t\t\t\t\tPriority:   priority,\n\t\t\t\t\t}},\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t}\n\n\terr = nil\n\treturn\n}\n"
  },
  {
    "path": "schema/index_test.go",
    "content": "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 struct {\n\tName         string `gorm:\"index\"`\n\tName2        string `gorm:\"index:idx_name,unique\"`\n\tName3        string `gorm:\"index:,sort:desc,collate:utf8,type:btree,length:10,where:name3 != 'jinzhu'\"`\n\tName4        string `gorm:\"uniqueIndex\"`\n\tName5        int64  `gorm:\"index:,class:FULLTEXT,comment:hello \\\\, world,where:age > 10\"`\n\tName6        int64  `gorm:\"index:profile,comment:hello \\\\, world,where:age > 10\"`\n\tAge          int64  `gorm:\"index:profile,expression:ABS(age),option:WITH PARSER parser_name\"`\n\tOID          int64  `gorm:\"index:idx_id;index:idx_oid,unique\"`\n\tMemberNumber string `gorm:\"index:idx_id,priority:1\"`\n\tName7        string `gorm:\"index:type\"`\n\tName8        string `gorm:\"index:,length:10;index:,collate:utf8\"`\n\n\tCompName1 string `gorm:\"index:,unique,composite:idx_compname_1,option:NULLS NOT DISTINCT;not null\"`\n\tCompName2 string `gorm:\"index:,composite:idx_compname_1\"`\n\n\t// Composite Index: Flattened structure.\n\tData0A string `gorm:\"index:,composite:comp_id0\"`\n\tData0B string `gorm:\"index:,composite:comp_id0\"`\n\n\t// Composite Index: Nested structure.\n\tData1A string `gorm:\"index:,composite:comp_id1\"`\n\tCompIdxLevel1C\n\n\t// Composite Index: Unique and priority.\n\tData2A string `gorm:\"index:,unique,composite:comp_id2,priority:2\"`\n\tCompIdxLevel2C\n}\n\ntype CompIdxLevel1C struct {\n\tCompIdxLevel1B\n\tData1C string `gorm:\"index:,composite:comp_id1\"`\n}\n\ntype CompIdxLevel1B struct {\n\tData1B string `gorm:\"index:,composite:comp_id1\"`\n}\n\ntype CompIdxLevel2C struct {\n\tCompIdxLevel2B\n\tData2C string `gorm:\"index:,unique,composite:comp_id2,priority:1\"`\n}\n\ntype CompIdxLevel2B struct {\n\tData2B string `gorm:\"index:,unique,composite:comp_id2,priority:3\"`\n}\n\nfunc TestParseIndex(t *testing.T) {\n\tuser, err := schema.Parse(&UserIndex{}, &sync.Map{}, schema.NamingStrategy{})\n\tif err != nil {\n\t\tt.Fatalf(\"failed to parse user index, got error %v\", err)\n\t}\n\n\tresults := []*schema.Index{\n\t\t{\n\t\t\tName:   \"idx_user_indices_name\",\n\t\t\tFields: []schema.IndexOption{{Field: &schema.Field{Name: \"Name\"}}},\n\t\t},\n\t\t{\n\t\t\tName:   \"idx_name\",\n\t\t\tClass:  \"UNIQUE\",\n\t\t\tFields: []schema.IndexOption{{Field: &schema.Field{Name: \"Name2\", UniqueIndex: \"idx_name\"}}},\n\t\t},\n\t\t{\n\t\t\tName:  \"idx_user_indices_name3\",\n\t\t\tType:  \"btree\",\n\t\t\tWhere: \"name3 != 'jinzhu'\",\n\t\t\tFields: []schema.IndexOption{{\n\t\t\t\tField:   &schema.Field{Name: \"Name3\"},\n\t\t\t\tSort:    \"desc\",\n\t\t\t\tCollate: \"utf8\",\n\t\t\t\tLength:  10,\n\t\t\t}},\n\t\t},\n\t\t{\n\t\t\tName:   \"idx_user_indices_name4\",\n\t\t\tClass:  \"UNIQUE\",\n\t\t\tFields: []schema.IndexOption{{Field: &schema.Field{Name: \"Name4\", UniqueIndex: \"idx_user_indices_name4\"}}},\n\t\t},\n\t\t{\n\t\t\tName:    \"idx_user_indices_name5\",\n\t\t\tClass:   \"FULLTEXT\",\n\t\t\tComment: \"hello , world\",\n\t\t\tWhere:   \"age > 10\",\n\t\t\tFields:  []schema.IndexOption{{Field: &schema.Field{Name: \"Name5\"}}},\n\t\t},\n\t\t{\n\t\t\tName:    \"profile\",\n\t\t\tComment: \"hello , world\",\n\t\t\tWhere:   \"age > 10\",\n\t\t\tOption:  \"WITH PARSER parser_name\",\n\t\t\tFields: []schema.IndexOption{{Field: &schema.Field{Name: \"Name6\"}}, {\n\t\t\t\tField:      &schema.Field{Name: \"Age\"},\n\t\t\t\tExpression: \"ABS(age)\",\n\t\t\t}},\n\t\t},\n\t\t{\n\t\t\tName:   \"idx_id\",\n\t\t\tFields: []schema.IndexOption{{Field: &schema.Field{Name: \"MemberNumber\"}}, {Field: &schema.Field{Name: \"OID\", UniqueIndex: \"idx_oid\"}}},\n\t\t},\n\t\t{\n\t\t\tName:   \"idx_oid\",\n\t\t\tClass:  \"UNIQUE\",\n\t\t\tFields: []schema.IndexOption{{Field: &schema.Field{Name: \"OID\", UniqueIndex: \"idx_oid\"}}},\n\t\t},\n\t\t{\n\t\t\tName:   \"type\",\n\t\t\tType:   \"\",\n\t\t\tFields: []schema.IndexOption{{Field: &schema.Field{Name: \"Name7\"}}},\n\t\t},\n\t\t{\n\t\t\tName: \"idx_user_indices_name8\",\n\t\t\tType: \"\",\n\t\t\tFields: []schema.IndexOption{\n\t\t\t\t{Field: &schema.Field{Name: \"Name8\"}, Length: 10},\n\t\t\t\t// Note: Duplicate Columns\n\t\t\t\t{Field: &schema.Field{Name: \"Name8\"}, Collate: \"utf8\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tClass:  \"UNIQUE\",\n\t\t\tName:   \"idx_user_indices_idx_compname_1\",\n\t\t\tOption: \"NULLS NOT DISTINCT\",\n\t\t\tFields: []schema.IndexOption{\n\t\t\t\t{Field: &schema.Field{Name: \"CompName1\", NotNull: true}},\n\t\t\t\t{Field: &schema.Field{Name: \"CompName2\"}},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tName: \"idx_user_indices_comp_id0\",\n\t\t\tType: \"\",\n\t\t\tFields: []schema.IndexOption{{\n\t\t\t\tField: &schema.Field{Name: \"Data0A\"},\n\t\t\t}, {\n\t\t\t\tField: &schema.Field{Name: \"Data0B\"},\n\t\t\t}},\n\t\t},\n\t\t{\n\t\t\tName: \"idx_user_indices_comp_id1\",\n\t\t\tFields: []schema.IndexOption{{\n\t\t\t\tField: &schema.Field{Name: \"Data1A\"},\n\t\t\t}, {\n\t\t\t\tField: &schema.Field{Name: \"Data1B\"},\n\t\t\t}, {\n\t\t\t\tField: &schema.Field{Name: \"Data1C\"},\n\t\t\t}},\n\t\t},\n\t\t{\n\t\t\tName:  \"idx_user_indices_comp_id2\",\n\t\t\tClass: \"UNIQUE\",\n\t\t\tFields: []schema.IndexOption{{\n\t\t\t\tField: &schema.Field{Name: \"Data2C\"},\n\t\t\t}, {\n\t\t\t\tField: &schema.Field{Name: \"Data2A\"},\n\t\t\t}, {\n\t\t\t\tField: &schema.Field{Name: \"Data2B\"},\n\t\t\t}},\n\t\t},\n\t}\n\n\tCheckIndices(t, results, user.ParseIndexes())\n}\n\nfunc TestParseIndexWithUniqueIndexAndUnique(t *testing.T) {\n\ttype IndexTest struct {\n\t\tFieldA string `gorm:\"unique;index\"` // unique and index\n\t\tFieldB string `gorm:\"unique\"`       // unique\n\n\t\tFieldC string `gorm:\"index:,unique\"`     // uniqueIndex\n\t\tFieldD string `gorm:\"uniqueIndex;index\"` // uniqueIndex and index\n\n\t\tFieldE1 string `gorm:\"uniqueIndex:uniq_field_e1_e2\"` // mul uniqueIndex\n\t\tFieldE2 string `gorm:\"uniqueIndex:uniq_field_e1_e2\"`\n\n\t\tFieldF1 string `gorm:\"uniqueIndex:uniq_field_f1_f2;index\"` // mul uniqueIndex and index\n\t\tFieldF2 string `gorm:\"uniqueIndex:uniq_field_f1_f2;\"`\n\n\t\tFieldG string `gorm:\"unique;uniqueIndex\"` // unique and uniqueIndex\n\n\t\tFieldH1 string `gorm:\"unique;uniqueIndex:uniq_field_h1_h2\"` // unique and mul uniqueIndex\n\t\tFieldH2 string `gorm:\"uniqueIndex:uniq_field_h1_h2\"`        // unique and mul uniqueIndex\n\t}\n\tindexSchema, err := schema.Parse(&IndexTest{}, &sync.Map{}, schema.NamingStrategy{})\n\tif err != nil {\n\t\tt.Fatalf(\"failed to parse user index, got error %v\", err)\n\t}\n\tindices := indexSchema.ParseIndexes()\n\texpectedIndices := []*schema.Index{\n\t\t{\n\t\t\tName:   \"idx_index_tests_field_a\",\n\t\t\tFields: []schema.IndexOption{{Field: &schema.Field{Name: \"FieldA\", Unique: true}}},\n\t\t},\n\t\t{\n\t\t\tName:   \"idx_index_tests_field_c\",\n\t\t\tClass:  \"UNIQUE\",\n\t\t\tFields: []schema.IndexOption{{Field: &schema.Field{Name: \"FieldC\", UniqueIndex: \"idx_index_tests_field_c\"}}},\n\t\t},\n\t\t{\n\t\t\tName:  \"idx_index_tests_field_d\",\n\t\t\tClass: \"UNIQUE\",\n\t\t\tFields: []schema.IndexOption{\n\t\t\t\t{Field: &schema.Field{Name: \"FieldD\"}},\n\t\t\t\t// Note: Duplicate Columns\n\t\t\t\t{Field: &schema.Field{Name: \"FieldD\"}},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tName:  \"uniq_field_e1_e2\",\n\t\t\tClass: \"UNIQUE\",\n\t\t\tFields: []schema.IndexOption{\n\t\t\t\t{Field: &schema.Field{Name: \"FieldE1\"}},\n\t\t\t\t{Field: &schema.Field{Name: \"FieldE2\"}},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tName:  \"uniq_field_f1_f2\",\n\t\t\tClass: \"UNIQUE\",\n\t\t\tFields: []schema.IndexOption{\n\t\t\t\t{Field: &schema.Field{Name: \"FieldF1\"}},\n\t\t\t\t{Field: &schema.Field{Name: \"FieldF2\"}},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tName:   \"idx_index_tests_field_f1\",\n\t\t\tFields: []schema.IndexOption{{Field: &schema.Field{Name: \"FieldF1\"}}},\n\t\t},\n\t\t{\n\t\t\tName:   \"idx_index_tests_field_g\",\n\t\t\tClass:  \"UNIQUE\",\n\t\t\tFields: []schema.IndexOption{{Field: &schema.Field{Name: \"FieldG\", Unique: true, UniqueIndex: \"idx_index_tests_field_g\"}}},\n\t\t},\n\t\t{\n\t\t\tName:  \"uniq_field_h1_h2\",\n\t\t\tClass: \"UNIQUE\",\n\t\t\tFields: []schema.IndexOption{\n\t\t\t\t{Field: &schema.Field{Name: \"FieldH1\", Unique: true}},\n\t\t\t\t{Field: &schema.Field{Name: \"FieldH2\"}},\n\t\t\t},\n\t\t},\n\t}\n\tCheckIndices(t, expectedIndices, indices)\n}\n\nfunc CheckIndices(t *testing.T, expected, actual []*schema.Index) {\n\tif len(expected) != len(actual) {\n\t\tt.Errorf(\"expected %d indices, but got %d\", len(expected), len(actual))\n\t\treturn\n\t}\n\n\tfor i, ei := range expected {\n\t\tt.Run(ei.Name, func(t *testing.T) {\n\t\t\tai := actual[i]\n\t\t\ttests.AssertObjEqual(t, ai, ei, \"Name\", \"Class\", \"Type\", \"Where\", \"Comment\", \"Option\")\n\n\t\t\tif len(ei.Fields) != len(ai.Fields) {\n\t\t\t\tt.Errorf(\"expected index %q field length is %d but actual %d\", ei.Name, len(ei.Fields), len(ai.Fields))\n\t\t\t\treturn\n\t\t\t}\n\t\t\tfor i, ef := range ei.Fields {\n\t\t\t\taf := ai.Fields[i]\n\t\t\t\ttests.AssertObjEqual(t, af, ef, \"Name\", \"Unique\", \"UniqueIndex\", \"Expression\", \"Sort\", \"Collate\", \"Length\", \"NotNull\")\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "schema/interfaces.go",
    "content": "package schema\n\nimport (\n\t\"gorm.io/gorm/clause\"\n)\n\n// ConstraintInterface database constraint interface\ntype ConstraintInterface interface {\n\tGetName() string\n\tBuild() (sql string, vars []interface{})\n}\n\n// GormDataTypeInterface gorm data type interface\ntype GormDataTypeInterface interface {\n\tGormDataType() string\n}\n\n// FieldNewValuePool field new scan value pool\ntype FieldNewValuePool interface {\n\tGet() interface{}\n\tPut(interface{})\n}\n\n// CreateClausesInterface create clauses interface\ntype CreateClausesInterface interface {\n\tCreateClauses(*Field) []clause.Interface\n}\n\n// QueryClausesInterface query clauses interface\ntype QueryClausesInterface interface {\n\tQueryClauses(*Field) []clause.Interface\n}\n\n// UpdateClausesInterface update clauses interface\ntype UpdateClausesInterface interface {\n\tUpdateClauses(*Field) []clause.Interface\n}\n\n// DeleteClausesInterface delete clauses interface\ntype DeleteClausesInterface interface {\n\tDeleteClauses(*Field) []clause.Interface\n}\n"
  },
  {
    "path": "schema/model_test.go",
    "content": "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 {\n\t*gorm.Model\n\tName      *string\n\tAge       *uint\n\tBirthday  *time.Time\n\tAccount   *tests.Account\n\tPets      []*tests.Pet\n\tToys      []*tests.Toy `gorm:\"polymorphic:Owner\"`\n\tCompanyID *int\n\tCompany   *tests.Company\n\tManagerID *uint\n\tManager   *User\n\tTeam      []*User           `gorm:\"foreignkey:ManagerID\"`\n\tLanguages []*tests.Language `gorm:\"many2many:UserSpeak\"`\n\tFriends   []*User           `gorm:\"many2many:user_friends\"`\n\tActive    *bool\n}\n\ntype (\n\tmytime time.Time\n\tmyint  int\n\tmybool = bool\n)\n\ntype AdvancedDataTypeUser struct {\n\tID           sql.NullInt64\n\tName         *sql.NullString\n\tBirthday     sql.NullTime\n\tRegisteredAt mytime\n\tDeletedAt    *mytime\n\tActive       mybool\n\tAdmin        *mybool\n}\n\ntype BaseModel struct {\n\tID        uint\n\tCreatedAt time.Time\n\tCreatedBy *int\n\tCreated   *VersionUser `gorm:\"foreignKey:CreatedBy\"`\n\tUpdatedAt time.Time\n\tDeletedAt gorm.DeletedAt `gorm:\"index\"`\n}\n\ntype VersionModel struct {\n\tBaseModel\n\tVersion int\n}\n\ntype VersionUser struct {\n\tVersionModel\n\tName     string\n\tAge      uint\n\tBirthday *time.Time\n}\n"
  },
  {
    "path": "schema/naming.go",
    "content": "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/inflection\"\n\t\"golang.org/x/text/cases\"\n\t\"golang.org/x/text/language\"\n)\n\n// Namer namer interface\ntype Namer interface {\n\tTableName(table string) string\n\tSchemaName(table string) string\n\tColumnName(table, column string) string\n\tJoinTableName(joinTable string) string\n\tRelationshipFKName(Relationship) string\n\tCheckerName(table, column string) string\n\tIndexName(table, column string) string\n\tUniqueName(table, column string) string\n}\n\n// Replacer replacer interface like strings.Replacer\ntype Replacer interface {\n\tReplace(name string) string\n}\n\nvar _ Namer = (*NamingStrategy)(nil)\n\n// NamingStrategy tables, columns naming strategy\ntype NamingStrategy struct {\n\tTablePrefix         string\n\tSingularTable       bool\n\tNameReplacer        Replacer\n\tNoLowerCase         bool\n\tIdentifierMaxLength int\n}\n\n// TableName convert string to table name\nfunc (ns NamingStrategy) TableName(str string) string {\n\tif ns.SingularTable {\n\t\treturn ns.TablePrefix + ns.toDBName(str)\n\t}\n\treturn ns.TablePrefix + inflection.Plural(ns.toDBName(str))\n}\n\n// SchemaName generate schema name from table name, don't guarantee it is the reverse value of TableName\nfunc (ns NamingStrategy) SchemaName(table string) string {\n\ttable = strings.TrimPrefix(table, ns.TablePrefix)\n\n\tif ns.SingularTable {\n\t\treturn ns.toSchemaName(table)\n\t}\n\treturn ns.toSchemaName(inflection.Singular(table))\n}\n\n// ColumnName convert string to column name\nfunc (ns NamingStrategy) ColumnName(table, column string) string {\n\treturn ns.toDBName(column)\n}\n\n// JoinTableName convert string to join table name\nfunc (ns NamingStrategy) JoinTableName(str string) string {\n\tif !ns.NoLowerCase && strings.ToLower(str) == str {\n\t\treturn ns.TablePrefix + str\n\t}\n\n\tif ns.SingularTable {\n\t\treturn ns.TablePrefix + ns.toDBName(str)\n\t}\n\treturn ns.TablePrefix + inflection.Plural(ns.toDBName(str))\n}\n\n// RelationshipFKName generate fk name for relation\nfunc (ns NamingStrategy) RelationshipFKName(rel Relationship) string {\n\treturn ns.formatName(\"fk\", rel.Schema.Table, ns.toDBName(rel.Name))\n}\n\n// CheckerName generate checker name\nfunc (ns NamingStrategy) CheckerName(table, column string) string {\n\treturn ns.formatName(\"chk\", table, column)\n}\n\n// IndexName generate index name\nfunc (ns NamingStrategy) IndexName(table, column string) string {\n\treturn ns.formatName(\"idx\", table, ns.toDBName(column))\n}\n\n// UniqueName generate unique constraint name\nfunc (ns NamingStrategy) UniqueName(table, column string) string {\n\treturn ns.formatName(\"uni\", table, ns.toDBName(column))\n}\n\nfunc (ns NamingStrategy) formatName(prefix, table, name string) string {\n\tformattedName := strings.ReplaceAll(strings.Join([]string{\n\t\tprefix, table, name,\n\t}, \"_\"), \".\", \"_\")\n\n\tif ns.IdentifierMaxLength == 0 {\n\t\tns.IdentifierMaxLength = 64\n\t}\n\n\tif utf8.RuneCountInString(formattedName) > ns.IdentifierMaxLength {\n\t\th := sha1.New()\n\t\th.Write([]byte(formattedName))\n\t\tbs := h.Sum(nil)\n\n\t\tformattedName = formattedName[0:ns.IdentifierMaxLength-8] + hex.EncodeToString(bs)[:8]\n\t}\n\treturn formattedName\n}\n\nvar (\n\t// https://github.com/golang/lint/blob/master/lint.go#L770\n\tcommonInitialisms         = []string{\"API\", \"ASCII\", \"CPU\", \"CSS\", \"DNS\", \"EOF\", \"GUID\", \"HTML\", \"HTTP\", \"HTTPS\", \"ID\", \"IP\", \"JSON\", \"LHS\", \"QPS\", \"RAM\", \"RHS\", \"RPC\", \"SLA\", \"SMTP\", \"SSH\", \"TLS\", \"TTL\", \"UID\", \"UI\", \"UUID\", \"URI\", \"URL\", \"UTF8\", \"VM\", \"XML\", \"XSRF\", \"XSS\"}\n\tcommonInitialismsReplacer *strings.Replacer\n)\n\nfunc init() {\n\tcommonInitialismsForReplacer := make([]string, 0, len(commonInitialisms))\n\tfor _, initialism := range commonInitialisms {\n\t\tcommonInitialismsForReplacer = append(commonInitialismsForReplacer, initialism, cases.Title(language.Und).String(initialism))\n\t}\n\tcommonInitialismsReplacer = strings.NewReplacer(commonInitialismsForReplacer...)\n}\n\nfunc (ns NamingStrategy) toDBName(name string) string {\n\tif name == \"\" {\n\t\treturn \"\"\n\t}\n\n\tif ns.NameReplacer != nil {\n\t\ttmpName := ns.NameReplacer.Replace(name)\n\n\t\tif tmpName == \"\" {\n\t\t\treturn name\n\t\t}\n\n\t\tname = tmpName\n\t}\n\n\tif ns.NoLowerCase {\n\t\treturn name\n\t}\n\n\tvar (\n\t\tvalue                          = commonInitialismsReplacer.Replace(name)\n\t\tbuf                            strings.Builder\n\t\tlastCase, nextCase, nextNumber bool // upper case == true\n\t\tcurCase                        = value[0] <= 'Z' && value[0] >= 'A'\n\t)\n\n\tfor i, v := range value[:len(value)-1] {\n\t\tnextCase = value[i+1] <= 'Z' && value[i+1] >= 'A'\n\t\tnextNumber = value[i+1] >= '0' && value[i+1] <= '9'\n\n\t\tif curCase {\n\t\t\tif lastCase && (nextCase || nextNumber) {\n\t\t\t\tbuf.WriteRune(v + 32)\n\t\t\t} else {\n\t\t\t\tif i > 0 && value[i-1] != '_' && value[i+1] != '_' {\n\t\t\t\t\tbuf.WriteByte('_')\n\t\t\t\t}\n\t\t\t\tbuf.WriteRune(v + 32)\n\t\t\t}\n\t\t} else {\n\t\t\tbuf.WriteRune(v)\n\t\t}\n\n\t\tlastCase = curCase\n\t\tcurCase = nextCase\n\t}\n\n\tif curCase {\n\t\tif !lastCase && len(value) > 1 {\n\t\t\tbuf.WriteByte('_')\n\t\t}\n\t\tbuf.WriteByte(value[len(value)-1] + 32)\n\t} else {\n\t\tbuf.WriteByte(value[len(value)-1])\n\t}\n\tret := buf.String()\n\treturn ret\n}\n\nfunc (ns NamingStrategy) toSchemaName(name string) string {\n\tresult := strings.ReplaceAll(cases.Title(language.Und, cases.NoLower).String(strings.ReplaceAll(name, \"_\", \" \")), \" \", \"\")\n\tfor _, initialism := range commonInitialisms {\n\t\tresult = regexp.MustCompile(cases.Title(language.Und, cases.NoLower).String(strings.ToLower(initialism))+\"([A-Z]|$|_)\").ReplaceAllString(result, initialism+\"$1\")\n\t}\n\treturn result\n}\n"
  },
  {
    "path": "schema/naming_test.go",
    "content": "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\"\":                          \"\",\n\t\t\"x\":                         \"x\",\n\t\t\"X\":                         \"x\",\n\t\t\"userRestrictions\":          \"user_restrictions\",\n\t\t\"ThisIsATest\":               \"this_is_a_test\",\n\t\t\"PFAndESI\":                  \"pf_and_esi\",\n\t\t\"AbcAndJkl\":                 \"abc_and_jkl\",\n\t\t\"EmployeeID\":                \"employee_id\",\n\t\t\"SKU_ID\":                    \"sku_id\",\n\t\t\"FieldX\":                    \"field_x\",\n\t\t\"HTTPAndSMTP\":               \"http_and_smtp\",\n\t\t\"HTTPServerHandlerForURLID\": \"http_server_handler_for_url_id\",\n\t\t\"UUID\":                      \"uuid\",\n\t\t\"HTTPURL\":                   \"http_url\",\n\t\t\"HTTP_URL\":                  \"http_url\",\n\t\t\"SHA256Hash\":                \"sha256_hash\",\n\t\t\"SHA256HASH\":                \"sha256_hash\",\n\t\t\"ThisIsActuallyATestSoWeMayBeAbleToUseThisCodeInGormPackageAlsoIdCanBeUsedAtTheEndAsID\": \"this_is_actually_a_test_so_we_may_be_able_to_use_this_code_in_gorm_package_also_id_can_be_used_at_the_end_as_id\",\n\t}\n\n\tns := NamingStrategy{}\n\tfor key, value := range maps {\n\t\tif ns.toDBName(key) != value {\n\t\t\tt.Errorf(\"%v toName should equal %v, but got %v\", key, value, ns.toDBName(key))\n\t\t}\n\t}\n\n\tmaps = map[string]string{\n\t\t\"x\":                              \"X\",\n\t\t\"user_restrictions\":              \"UserRestriction\",\n\t\t\"this_is_a_test\":                 \"ThisIsATest\",\n\t\t\"abc_and_jkl\":                    \"AbcAndJkl\",\n\t\t\"employee_id\":                    \"EmployeeID\",\n\t\t\"field_x\":                        \"FieldX\",\n\t\t\"http_and_smtp\":                  \"HTTPAndSMTP\",\n\t\t\"http_server_handler_for_url_id\": \"HTTPServerHandlerForURLID\",\n\t\t\"uuid\":                           \"UUID\",\n\t\t\"http_url\":                       \"HTTPURL\",\n\t\t\"sha256_hash\":                    \"Sha256Hash\",\n\t\t\"this_is_actually_a_test_so_we_may_be_able_to_use_this_code_in_gorm_package_also_id_can_be_used_at_the_end_as_id\": \"ThisIsActuallyATestSoWeMayBeAbleToUseThisCodeInGormPackageAlsoIDCanBeUsedAtTheEndAsID\",\n\t}\n\tfor key, value := range maps {\n\t\tif ns.SchemaName(key) != value {\n\t\t\tt.Errorf(\"%v schema name should equal %v, but got %v\", key, value, ns.SchemaName(key))\n\t\t}\n\t}\n}\n\nfunc TestNamingStrategy(t *testing.T) {\n\tns := NamingStrategy{\n\t\tTablePrefix:   \"public.\",\n\t\tSingularTable: true,\n\t\tNameReplacer:  strings.NewReplacer(\"CID\", \"Cid\"),\n\t}\n\tidxName := ns.IndexName(\"public.table\", \"name\")\n\n\tif idxName != \"idx_public_table_name\" {\n\t\tt.Errorf(\"invalid index name generated, got %v\", idxName)\n\t}\n\n\tchkName := ns.CheckerName(\"public.table\", \"name\")\n\tif chkName != \"chk_public_table_name\" {\n\t\tt.Errorf(\"invalid checker name generated, got %v\", chkName)\n\t}\n\n\tjoinTable := ns.JoinTableName(\"user_languages\")\n\tif joinTable != \"public.user_languages\" {\n\t\tt.Errorf(\"invalid join table generated, got %v\", joinTable)\n\t}\n\n\tjoinTable2 := ns.JoinTableName(\"UserLanguage\")\n\tif joinTable2 != \"public.user_language\" {\n\t\tt.Errorf(\"invalid join table generated, got %v\", joinTable2)\n\t}\n\n\ttableName := ns.TableName(\"Company\")\n\tif tableName != \"public.company\" {\n\t\tt.Errorf(\"invalid table name generated, got %v\", tableName)\n\t}\n\n\tcolumdName := ns.ColumnName(\"\", \"NameCID\")\n\tif columdName != \"name_cid\" {\n\t\tt.Errorf(\"invalid column name generated, got %v\", columdName)\n\t}\n}\n\ntype CustomReplacer struct {\n\tf func(string) string\n}\n\nfunc (r CustomReplacer) Replace(name string) string {\n\treturn r.f(name)\n}\n\nfunc TestCustomReplacer(t *testing.T) {\n\tns := NamingStrategy{\n\t\tTablePrefix:   \"public.\",\n\t\tSingularTable: true,\n\t\tNameReplacer: CustomReplacer{\n\t\t\tfunc(name string) string {\n\t\t\t\treplaced := \"REPLACED_\" + strings.ToUpper(name)\n\t\t\t\treturn strings.NewReplacer(\"CID\", \"_Cid\").Replace(replaced)\n\t\t\t},\n\t\t},\n\t\tNoLowerCase: false,\n\t}\n\n\tidxName := ns.IndexName(\"public.table\", \"name\")\n\tif idxName != \"idx_public_table_replaced_name\" {\n\t\tt.Errorf(\"invalid index name generated, got %v\", idxName)\n\t}\n\n\tchkName := ns.CheckerName(\"public.table\", \"name\")\n\tif chkName != \"chk_public_table_name\" {\n\t\tt.Errorf(\"invalid checker name generated, got %v\", chkName)\n\t}\n\n\tjoinTable := ns.JoinTableName(\"user_languages\")\n\tif joinTable != \"public.user_languages\" { // Seems like a bug in NamingStrategy to skip the Replacer when the name is lowercase here.\n\t\tt.Errorf(\"invalid join table generated, got %v\", joinTable)\n\t}\n\n\tjoinTable2 := ns.JoinTableName(\"UserLanguage\")\n\tif joinTable2 != \"public.replaced_userlanguage\" {\n\t\tt.Errorf(\"invalid join table generated, got %v\", joinTable2)\n\t}\n\n\ttableName := ns.TableName(\"Company\")\n\tif tableName != \"public.replaced_company\" {\n\t\tt.Errorf(\"invalid table name generated, got %v\", tableName)\n\t}\n\n\tcolumdName := ns.ColumnName(\"\", \"NameCID\")\n\tif columdName != \"replaced_name_cid\" {\n\t\tt.Errorf(\"invalid column name generated, got %v\", columdName)\n\t}\n}\n\nfunc TestCustomReplacerWithNoLowerCase(t *testing.T) {\n\tns := NamingStrategy{\n\t\tTablePrefix:   \"public.\",\n\t\tSingularTable: true,\n\t\tNameReplacer: CustomReplacer{\n\t\t\tfunc(name string) string {\n\t\t\t\treplaced := \"REPLACED_\" + strings.ToUpper(name)\n\t\t\t\treturn strings.NewReplacer(\"CID\", \"_Cid\").Replace(replaced)\n\t\t\t},\n\t\t},\n\t\tNoLowerCase: true,\n\t}\n\n\tidxName := ns.IndexName(\"public.table\", \"name\")\n\tif idxName != \"idx_public_table_REPLACED_NAME\" {\n\t\tt.Errorf(\"invalid index name generated, got %v\", idxName)\n\t}\n\n\tchkName := ns.CheckerName(\"public.table\", \"name\")\n\tif chkName != \"chk_public_table_name\" {\n\t\tt.Errorf(\"invalid checker name generated, got %v\", chkName)\n\t}\n\n\tjoinTable := ns.JoinTableName(\"user_languages\")\n\tif joinTable != \"public.REPLACED_USER_LANGUAGES\" {\n\t\tt.Errorf(\"invalid join table generated, got %v\", joinTable)\n\t}\n\n\tjoinTable2 := ns.JoinTableName(\"UserLanguage\")\n\tif joinTable2 != \"public.REPLACED_USERLANGUAGE\" {\n\t\tt.Errorf(\"invalid join table generated, got %v\", joinTable2)\n\t}\n\n\ttableName := ns.TableName(\"Company\")\n\tif tableName != \"public.REPLACED_COMPANY\" {\n\t\tt.Errorf(\"invalid table name generated, got %v\", tableName)\n\t}\n\n\tcolumdName := ns.ColumnName(\"\", \"NameCID\")\n\tif columdName != \"REPLACED_NAME_Cid\" {\n\t\tt.Errorf(\"invalid column name generated, got %v\", columdName)\n\t}\n}\n\nfunc TestFormatNameWithStringLongerThan63Characters(t *testing.T) {\n\tns := NamingStrategy{IdentifierMaxLength: 63}\n\n\tformattedName := ns.formatName(\"prefix\", \"table\", \"thisIsAVeryVeryVeryVeryVeryVeryVeryVeryVeryLongString\")\n\tif formattedName != \"prefix_table_thisIsAVeryVeryVeryVeryVeryVeryVeryVeryVer180f2c67\" {\n\t\tt.Errorf(\"invalid formatted name generated, got %v\", formattedName)\n\t}\n}\n\nfunc TestFormatNameWithStringLongerThan64Characters(t *testing.T) {\n\tns := NamingStrategy{IdentifierMaxLength: 64}\n\n\tformattedName := ns.formatName(\"prefix\", \"table\", \"thisIsAVeryVeryVeryVeryVeryVeryVeryVeryVeryLongString\")\n\tif formattedName != \"prefix_table_thisIsAVeryVeryVeryVeryVeryVeryVeryVeryVery180f2c67\" {\n\t\tt.Errorf(\"invalid formatted name generated, got %v\", formattedName)\n\t}\n}\n\nfunc TestReplaceEmptyTableName(t *testing.T) {\n\tns := NamingStrategy{\n\t\tSingularTable: true,\n\t\tNameReplacer:  strings.NewReplacer(\"Model\", \"\"),\n\t}\n\ttableName := ns.TableName(\"Model\")\n\tif tableName != \"Model\" {\n\t\tt.Errorf(\"invalid table name generated, got %v\", tableName)\n\t}\n}\n"
  },
  {
    "path": "schema/pool.go",
    "content": "package schema\n\nimport (\n\t\"reflect\"\n\t\"sync\"\n)\n\n// sync pools\nvar (\n\tnormalPool      sync.Map\n\tpoolInitializer = func(reflectType reflect.Type) FieldNewValuePool {\n\t\tv, _ := normalPool.LoadOrStore(reflectType, &sync.Pool{\n\t\t\tNew: func() interface{} {\n\t\t\t\treturn reflect.New(reflectType).Interface()\n\t\t\t},\n\t\t})\n\t\treturn v.(FieldNewValuePool)\n\t}\n)\n"
  },
  {
    "path": "schema/relationship.go",
    "content": "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/text/cases\"\n\t\"golang.org/x/text/language\"\n\n\t\"gorm.io/gorm/clause\"\n)\n\n// RelationshipType relationship type\ntype RelationshipType string\n\nconst (\n\tHasOne    RelationshipType = \"has_one\"      // HasOneRel has one relationship\n\tHasMany   RelationshipType = \"has_many\"     // HasManyRel has many relationship\n\tBelongsTo RelationshipType = \"belongs_to\"   // BelongsToRel belongs to relationship\n\tMany2Many RelationshipType = \"many_to_many\" // Many2ManyRel many to many relationship\n\thas       RelationshipType = \"has\"\n)\n\ntype Relationships struct {\n\tHasOne    []*Relationship\n\tBelongsTo []*Relationship\n\tHasMany   []*Relationship\n\tMany2Many []*Relationship\n\tRelations map[string]*Relationship\n\n\tEmbeddedRelations map[string]*Relationships\n\n\tMux sync.RWMutex\n}\n\ntype Relationship struct {\n\tName                     string\n\tType                     RelationshipType\n\tField                    *Field\n\tPolymorphic              *Polymorphic\n\tReferences               []*Reference\n\tSchema                   *Schema\n\tFieldSchema              *Schema\n\tJoinTable                *Schema\n\tforeignKeys, primaryKeys []string\n}\n\ntype Polymorphic struct {\n\tPolymorphicID   *Field\n\tPolymorphicType *Field\n\tValue           string\n}\n\ntype Reference struct {\n\tPrimaryKey    *Field\n\tPrimaryValue  string\n\tForeignKey    *Field\n\tOwnPrimaryKey bool\n}\n\nfunc (schema *Schema) parseRelation(field *Field) *Relationship {\n\tvar (\n\t\terr        error\n\t\tfieldValue = reflect.New(field.IndirectFieldType).Interface()\n\t\trelation   = &Relationship{\n\t\t\tName:        field.Name,\n\t\t\tField:       field,\n\t\t\tSchema:      schema,\n\t\t\tforeignKeys: toColumns(field.TagSettings[\"FOREIGNKEY\"]),\n\t\t\tprimaryKeys: toColumns(field.TagSettings[\"REFERENCES\"]),\n\t\t}\n\t)\n\n\tif relation.FieldSchema, err = getOrParse(fieldValue, schema.cacheStore, schema.namer); err != nil {\n\t\tschema.err = fmt.Errorf(\"failed to parse field: %s, error: %w\", field.Name, err)\n\t\treturn nil\n\t}\n\n\tif hasPolymorphicRelation(field.TagSettings) {\n\t\tschema.buildPolymorphicRelation(relation, field)\n\t} else if many2many := field.TagSettings[\"MANY2MANY\"]; many2many != \"\" {\n\t\tschema.buildMany2ManyRelation(relation, field, many2many)\n\t} else if belongsTo := field.TagSettings[\"BELONGSTO\"]; belongsTo != \"\" {\n\t\tschema.guessRelation(relation, field, guessBelongs)\n\t} else {\n\t\tswitch field.IndirectFieldType.Kind() {\n\t\tcase reflect.Struct:\n\t\t\tschema.guessRelation(relation, field, guessGuess)\n\t\tcase reflect.Slice:\n\t\t\tschema.guessRelation(relation, field, guessHas)\n\t\tdefault:\n\t\t\tschema.err = fmt.Errorf(\"unsupported data type %v for %v on field %s\", relation.FieldSchema, schema,\n\t\t\t\tfield.Name)\n\t\t}\n\t}\n\n\tif relation.Type == has {\n\t\tif relation.FieldSchema != relation.Schema && relation.Polymorphic == nil && field.OwnerSchema == nil {\n\t\t\trelation.FieldSchema.Relationships.Mux.Lock()\n\t\t\trelation.FieldSchema.Relationships.Relations[\"_\"+relation.Schema.Name+\"_\"+relation.Name] = relation\n\t\t\trelation.FieldSchema.Relationships.Mux.Unlock()\n\t\t}\n\n\t\tswitch field.IndirectFieldType.Kind() {\n\t\tcase reflect.Struct:\n\t\t\trelation.Type = HasOne\n\t\tcase reflect.Slice:\n\t\t\trelation.Type = HasMany\n\t\t}\n\t}\n\n\tif schema.err == nil {\n\t\tschema.setRelation(relation)\n\t\tswitch relation.Type {\n\t\tcase HasOne:\n\t\t\tschema.Relationships.HasOne = append(schema.Relationships.HasOne, relation)\n\t\tcase HasMany:\n\t\t\tschema.Relationships.HasMany = append(schema.Relationships.HasMany, relation)\n\t\tcase BelongsTo:\n\t\t\tschema.Relationships.BelongsTo = append(schema.Relationships.BelongsTo, relation)\n\t\tcase Many2Many:\n\t\t\tschema.Relationships.Many2Many = append(schema.Relationships.Many2Many, relation)\n\t\t}\n\t}\n\n\treturn relation\n}\n\n// hasPolymorphicRelation check if has polymorphic relation\n// 1. `POLYMORPHIC` tag\n// 2. `POLYMORPHICTYPE` and `POLYMORPHICID` tag\nfunc hasPolymorphicRelation(tagSettings map[string]string) bool {\n\tif _, ok := tagSettings[\"POLYMORPHIC\"]; ok {\n\t\treturn true\n\t}\n\n\t_, hasType := tagSettings[\"POLYMORPHICTYPE\"]\n\t_, hasId := tagSettings[\"POLYMORPHICID\"]\n\n\treturn hasType && hasId\n}\n\nfunc (schema *Schema) setRelation(relation *Relationship) {\n\tschema.Relationships.Mux.Lock()\n\tdefer schema.Relationships.Mux.Unlock()\n\n\t// set non-embedded relation\n\tif rel := schema.Relationships.Relations[relation.Name]; rel != nil {\n\t\tif len(rel.Field.BindNames) > 1 {\n\t\t\tschema.Relationships.Relations[relation.Name] = relation\n\t\t}\n\t} else {\n\t\tschema.Relationships.Relations[relation.Name] = relation\n\t}\n\n\t// set embedded relation\n\tif len(relation.Field.EmbeddedBindNames) <= 1 {\n\t\treturn\n\t}\n\trelationships := &schema.Relationships\n\tfor i, name := range relation.Field.EmbeddedBindNames {\n\t\tif i < len(relation.Field.EmbeddedBindNames)-1 {\n\t\t\tif relationships.EmbeddedRelations == nil {\n\t\t\t\trelationships.EmbeddedRelations = map[string]*Relationships{}\n\t\t\t}\n\t\t\tif r := relationships.EmbeddedRelations[name]; r == nil {\n\t\t\t\trelationships.EmbeddedRelations[name] = &Relationships{}\n\t\t\t}\n\t\t\trelationships = relationships.EmbeddedRelations[name]\n\t\t} else {\n\t\t\tif relationships.Relations == nil {\n\t\t\t\trelationships.Relations = map[string]*Relationship{}\n\t\t\t}\n\t\t\trelationships.Relations[relation.Name] = relation\n\t\t}\n\t}\n}\n\n// User has many Toys, its `Polymorphic` is `Owner`, Pet has one Toy, its `Polymorphic` is `Owner`\n//\n//\ttype User struct {\n//\t  Toys []Toy `gorm:\"polymorphic:Owner;\"`\n//\t}\n//\ttype Pet struct {\n//\t  Toy Toy `gorm:\"polymorphic:Owner;\"`\n//\t}\n//\ttype Toy struct {\n//\t  OwnerID   int\n//\t  OwnerType string\n//\t}\nfunc (schema *Schema) buildPolymorphicRelation(relation *Relationship, field *Field) {\n\tpolymorphic := field.TagSettings[\"POLYMORPHIC\"]\n\n\trelation.Polymorphic = &Polymorphic{\n\t\tValue: schema.Table,\n\t}\n\n\tvar (\n\t\ttypeName = polymorphic + \"Type\"\n\t\ttypeId   = polymorphic + \"ID\"\n\t)\n\n\tif value, ok := field.TagSettings[\"POLYMORPHICTYPE\"]; ok {\n\t\ttypeName = strings.TrimSpace(value)\n\t}\n\n\tif value, ok := field.TagSettings[\"POLYMORPHICID\"]; ok {\n\t\ttypeId = strings.TrimSpace(value)\n\t}\n\n\trelation.Polymorphic.PolymorphicType = relation.FieldSchema.FieldsByName[typeName]\n\trelation.Polymorphic.PolymorphicID = relation.FieldSchema.FieldsByName[typeId]\n\n\tif value, ok := field.TagSettings[\"POLYMORPHICVALUE\"]; ok {\n\t\trelation.Polymorphic.Value = strings.TrimSpace(value)\n\t}\n\n\tif relation.Polymorphic.PolymorphicType == nil {\n\t\tschema.err = fmt.Errorf(\"invalid polymorphic type %v for %v on field %s, missing field %s\",\n\t\t\trelation.FieldSchema, schema, field.Name, polymorphic+\"Type\")\n\t}\n\n\tif relation.Polymorphic.PolymorphicID == nil {\n\t\tschema.err = fmt.Errorf(\"invalid polymorphic type %v for %v on field %s, missing field %s\",\n\t\t\trelation.FieldSchema, schema, field.Name, polymorphic+\"ID\")\n\t}\n\n\tif schema.err == nil {\n\t\trelation.References = append(relation.References, &Reference{\n\t\t\tPrimaryValue: relation.Polymorphic.Value,\n\t\t\tForeignKey:   relation.Polymorphic.PolymorphicType,\n\t\t})\n\n\t\tprimaryKeyField := schema.PrioritizedPrimaryField\n\t\tif len(relation.foreignKeys) > 0 {\n\t\t\tif primaryKeyField = schema.LookUpField(relation.foreignKeys[0]); primaryKeyField == nil || len(relation.foreignKeys) > 1 {\n\t\t\t\tschema.err = fmt.Errorf(\"invalid polymorphic foreign keys %+v for %v on field %s\", relation.foreignKeys,\n\t\t\t\t\tschema, field.Name)\n\t\t\t}\n\t\t}\n\n\t\tif primaryKeyField == nil {\n\t\t\tschema.err = fmt.Errorf(\"invalid polymorphic type %v for %v on field %s, missing primaryKey field\",\n\t\t\t\trelation.FieldSchema, schema, field.Name)\n\t\t\treturn\n\t\t}\n\n\t\t// use same data type for foreign keys\n\t\tif copyableDataType(primaryKeyField.DataType) {\n\t\t\trelation.Polymorphic.PolymorphicID.DataType = primaryKeyField.DataType\n\t\t}\n\t\trelation.Polymorphic.PolymorphicID.GORMDataType = primaryKeyField.GORMDataType\n\t\tif relation.Polymorphic.PolymorphicID.Size == 0 {\n\t\t\trelation.Polymorphic.PolymorphicID.Size = primaryKeyField.Size\n\t\t}\n\n\t\trelation.References = append(relation.References, &Reference{\n\t\t\tPrimaryKey:    primaryKeyField,\n\t\t\tForeignKey:    relation.Polymorphic.PolymorphicID,\n\t\t\tOwnPrimaryKey: true,\n\t\t})\n\t}\n\n\trelation.Type = has\n}\n\nfunc (schema *Schema) buildMany2ManyRelation(relation *Relationship, field *Field, many2many string) {\n\trelation.Type = Many2Many\n\n\tvar (\n\t\terr             error\n\t\tjoinTableFields []reflect.StructField\n\t\tfieldsMap       = map[string]*Field{}\n\t\townFieldsMap    = map[string]*Field{} // fix self join many2many\n\t\treferFieldsMap  = map[string]*Field{}\n\t\tjoinForeignKeys = toColumns(field.TagSettings[\"JOINFOREIGNKEY\"])\n\t\tjoinReferences  = toColumns(field.TagSettings[\"JOINREFERENCES\"])\n\t)\n\n\townForeignFields := schema.PrimaryFields\n\trefForeignFields := relation.FieldSchema.PrimaryFields\n\n\tif len(relation.foreignKeys) > 0 {\n\t\townForeignFields = []*Field{}\n\t\tfor _, foreignKey := range relation.foreignKeys {\n\t\t\tif field := schema.LookUpField(foreignKey); field != nil {\n\t\t\t\townForeignFields = append(ownForeignFields, field)\n\t\t\t} else {\n\t\t\t\tschema.err = fmt.Errorf(\"invalid foreign key: %s\", foreignKey)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n\n\tif len(relation.primaryKeys) > 0 {\n\t\trefForeignFields = []*Field{}\n\t\tfor _, foreignKey := range relation.primaryKeys {\n\t\t\tif field := relation.FieldSchema.LookUpField(foreignKey); field != nil {\n\t\t\t\trefForeignFields = append(refForeignFields, field)\n\t\t\t} else {\n\t\t\t\tschema.err = fmt.Errorf(\"invalid foreign key: %s\", foreignKey)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n\n\tfor idx, ownField := range ownForeignFields {\n\t\tjoinFieldName := cases.Title(language.Und, cases.NoLower).String(schema.Name) + ownField.Name\n\t\tif len(joinForeignKeys) > idx {\n\t\t\tjoinFieldName = cases.Title(language.Und, cases.NoLower).String(joinForeignKeys[idx])\n\t\t}\n\n\t\townFieldsMap[joinFieldName] = ownField\n\t\tfieldsMap[joinFieldName] = ownField\n\t\tjoinTableFields = append(joinTableFields, reflect.StructField{\n\t\t\tName:    joinFieldName,\n\t\t\tPkgPath: ownField.StructField.PkgPath,\n\t\t\tType:    ownField.StructField.Type,\n\t\t\tTag: removeSettingFromTag(appendSettingFromTag(ownField.StructField.Tag, \"primaryKey\"),\n\t\t\t\t\"column\", \"autoincrement\", \"index\", \"unique\", \"uniqueindex\"),\n\t\t})\n\t}\n\n\tfor idx, relField := range refForeignFields {\n\t\tjoinFieldName := cases.Title(language.Und, cases.NoLower).String(relation.FieldSchema.Name) + relField.Name\n\n\t\tif _, ok := ownFieldsMap[joinFieldName]; ok {\n\t\t\tif field.Name != relation.FieldSchema.Name {\n\t\t\t\tjoinFieldName = inflection.Singular(field.Name) + relField.Name\n\t\t\t} else {\n\t\t\t\tjoinFieldName += \"Reference\"\n\t\t\t}\n\t\t}\n\n\t\tif len(joinReferences) > idx {\n\t\t\tjoinFieldName = cases.Title(language.Und, cases.NoLower).String(joinReferences[idx])\n\t\t}\n\n\t\treferFieldsMap[joinFieldName] = relField\n\n\t\tif _, ok := fieldsMap[joinFieldName]; !ok {\n\t\t\tfieldsMap[joinFieldName] = relField\n\t\t\tjoinTableFields = append(joinTableFields, reflect.StructField{\n\t\t\t\tName:    joinFieldName,\n\t\t\t\tPkgPath: relField.StructField.PkgPath,\n\t\t\t\tType:    relField.StructField.Type,\n\t\t\t\tTag: removeSettingFromTag(appendSettingFromTag(relField.StructField.Tag, \"primaryKey\"),\n\t\t\t\t\t\"column\", \"autoincrement\", \"index\", \"unique\", \"uniqueindex\"),\n\t\t\t})\n\t\t}\n\t}\n\n\tjoinTableFields = append(joinTableFields, reflect.StructField{\n\t\tName: cases.Title(language.Und, cases.NoLower).String(schema.Name) + field.Name,\n\t\tType: schema.ModelType,\n\t\tTag:  `gorm:\"-\"`,\n\t})\n\n\tif relation.JoinTable, err = Parse(reflect.New(reflect.StructOf(joinTableFields)).Interface(), schema.cacheStore,\n\t\tschema.namer); err != nil {\n\t\tschema.err = err\n\t}\n\trelation.JoinTable.Name = many2many\n\trelation.JoinTable.Table = schema.namer.JoinTableName(many2many)\n\trelation.JoinTable.PrimaryFields = make([]*Field, 0, len(relation.JoinTable.Fields))\n\n\trelName := relation.Schema.Name\n\trelRefName := relation.FieldSchema.Name\n\tif relName == relRefName {\n\t\trelRefName = relation.Field.Name\n\t}\n\n\tif _, ok := relation.JoinTable.Relationships.Relations[relName]; !ok {\n\t\trelation.JoinTable.Relationships.Relations[relName] = &Relationship{\n\t\t\tName:        relName,\n\t\t\tType:        BelongsTo,\n\t\t\tSchema:      relation.JoinTable,\n\t\t\tFieldSchema: relation.Schema,\n\t\t}\n\t} else {\n\t\trelation.JoinTable.Relationships.Relations[relName].References = []*Reference{}\n\t}\n\n\tif _, ok := relation.JoinTable.Relationships.Relations[relRefName]; !ok {\n\t\trelation.JoinTable.Relationships.Relations[relRefName] = &Relationship{\n\t\t\tName:        relRefName,\n\t\t\tType:        BelongsTo,\n\t\t\tSchema:      relation.JoinTable,\n\t\t\tFieldSchema: relation.FieldSchema,\n\t\t}\n\t} else {\n\t\trelation.JoinTable.Relationships.Relations[relRefName].References = []*Reference{}\n\t}\n\n\t// build references\n\tfor _, f := range relation.JoinTable.Fields {\n\t\tif f.Creatable || f.Readable || f.Updatable {\n\t\t\t// use same data type for foreign keys\n\t\t\tif copyableDataType(fieldsMap[f.Name].DataType) {\n\t\t\t\tf.DataType = fieldsMap[f.Name].DataType\n\t\t\t}\n\t\t\tf.GORMDataType = fieldsMap[f.Name].GORMDataType\n\t\t\tif f.Size == 0 {\n\t\t\t\tf.Size = fieldsMap[f.Name].Size\n\t\t\t}\n\t\t\trelation.JoinTable.PrimaryFields = append(relation.JoinTable.PrimaryFields, f)\n\n\t\t\tif of, ok := ownFieldsMap[f.Name]; ok {\n\t\t\t\tjoinRel := relation.JoinTable.Relationships.Relations[relName]\n\t\t\t\tjoinRel.Field = relation.Field\n\t\t\t\tjoinRel.References = append(joinRel.References, &Reference{\n\t\t\t\t\tPrimaryKey: of,\n\t\t\t\t\tForeignKey: f,\n\t\t\t\t})\n\n\t\t\t\trelation.References = append(relation.References, &Reference{\n\t\t\t\t\tPrimaryKey:    of,\n\t\t\t\t\tForeignKey:    f,\n\t\t\t\t\tOwnPrimaryKey: true,\n\t\t\t\t})\n\t\t\t}\n\n\t\t\tif rf, ok := referFieldsMap[f.Name]; ok {\n\t\t\t\tjoinRefRel := relation.JoinTable.Relationships.Relations[relRefName]\n\t\t\t\tif joinRefRel.Field == nil {\n\t\t\t\t\tjoinRefRel.Field = relation.Field\n\t\t\t\t}\n\t\t\t\tjoinRefRel.References = append(joinRefRel.References, &Reference{\n\t\t\t\t\tPrimaryKey: rf,\n\t\t\t\t\tForeignKey: f,\n\t\t\t\t})\n\n\t\t\t\trelation.References = append(relation.References, &Reference{\n\t\t\t\t\tPrimaryKey: rf,\n\t\t\t\t\tForeignKey: f,\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t}\n}\n\ntype guessLevel int\n\nconst (\n\tguessGuess guessLevel = iota\n\tguessBelongs\n\tguessEmbeddedBelongs\n\tguessHas\n\tguessEmbeddedHas\n)\n\nfunc (schema *Schema) guessRelation(relation *Relationship, field *Field, cgl guessLevel) {\n\tvar (\n\t\tprimaryFields, foreignFields []*Field\n\t\tprimarySchema, foreignSchema = schema, relation.FieldSchema\n\t\tgl                           = cgl\n\t)\n\n\tif gl == guessGuess {\n\t\tif field.Schema == relation.FieldSchema {\n\t\t\tgl = guessBelongs\n\t\t} else {\n\t\t\tgl = guessHas\n\t\t}\n\t}\n\n\treguessOrErr := func() {\n\t\tswitch cgl {\n\t\tcase guessGuess:\n\t\t\tschema.guessRelation(relation, field, guessBelongs)\n\t\tcase guessBelongs:\n\t\t\tschema.guessRelation(relation, field, guessEmbeddedBelongs)\n\t\tcase guessEmbeddedBelongs:\n\t\t\tschema.guessRelation(relation, field, guessHas)\n\t\tcase guessHas:\n\t\t\tschema.guessRelation(relation, field, guessEmbeddedHas)\n\t\t// case guessEmbeddedHas:\n\t\tdefault:\n\t\t\tschema.err = fmt.Errorf(\"invalid field found for struct %v's field %s: define a valid foreign key for relations or implement the Valuer/Scanner interface\",\n\t\t\t\tschema, field.Name)\n\t\t}\n\t}\n\n\tswitch gl {\n\tcase guessBelongs:\n\t\tprimarySchema, foreignSchema = relation.FieldSchema, schema\n\tcase guessEmbeddedBelongs:\n\t\tif field.OwnerSchema == nil {\n\t\t\treguessOrErr()\n\t\t\treturn\n\t\t}\n\t\tprimarySchema, foreignSchema = relation.FieldSchema, field.OwnerSchema\n\tcase guessHas:\n\tcase guessEmbeddedHas:\n\t\tif field.OwnerSchema == nil {\n\t\t\treguessOrErr()\n\t\t\treturn\n\t\t}\n\t\tprimarySchema, foreignSchema = field.OwnerSchema, relation.FieldSchema\n\t}\n\n\tif len(relation.foreignKeys) > 0 {\n\t\tfor _, foreignKey := range relation.foreignKeys {\n\t\t\tf := foreignSchema.LookUpField(foreignKey)\n\t\t\tif f == nil {\n\t\t\t\treguessOrErr()\n\t\t\t\treturn\n\t\t\t}\n\t\t\tforeignFields = append(foreignFields, f)\n\t\t}\n\t} else {\n\t\tprimarySchemaName := primarySchema.Name\n\t\tif primarySchemaName == \"\" {\n\t\t\tprimarySchemaName = relation.FieldSchema.Name\n\t\t}\n\n\t\tif len(relation.primaryKeys) > 0 {\n\t\t\tfor _, primaryKey := range relation.primaryKeys {\n\t\t\t\tif f := primarySchema.LookUpField(primaryKey); f != nil {\n\t\t\t\t\tprimaryFields = append(primaryFields, f)\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tprimaryFields = primarySchema.PrimaryFields\n\t\t}\n\n\tprimaryFieldLoop:\n\t\tfor _, primaryField := range primaryFields {\n\t\t\tlookUpName := primarySchemaName + primaryField.Name\n\t\t\tif gl == guessBelongs {\n\t\t\t\tlookUpName = field.Name + primaryField.Name\n\t\t\t}\n\n\t\t\tlookUpNames := []string{lookUpName}\n\t\t\tif len(primaryFields) == 1 {\n\t\t\t\tlookUpNames = append(lookUpNames, strings.TrimSuffix(lookUpName, primaryField.Name)+\"ID\",\n\t\t\t\t\tstrings.TrimSuffix(lookUpName, primaryField.Name)+\"Id\", schema.namer.ColumnName(foreignSchema.Table,\n\t\t\t\t\t\tstrings.TrimSuffix(lookUpName, primaryField.Name)+\"ID\"))\n\t\t\t}\n\n\t\t\tfor _, name := range lookUpNames {\n\t\t\t\tif f := foreignSchema.LookUpFieldByBindName(field.BindNames, name); f != nil {\n\t\t\t\t\tforeignFields = append(foreignFields, f)\n\t\t\t\t\tprimaryFields = append(primaryFields, primaryField)\n\t\t\t\t\tcontinue primaryFieldLoop\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor _, name := range lookUpNames {\n\t\t\t\tif f := foreignSchema.LookUpField(name); f != nil {\n\t\t\t\t\tforeignFields = append(foreignFields, f)\n\t\t\t\t\tprimaryFields = append(primaryFields, primaryField)\n\t\t\t\t\tcontinue primaryFieldLoop\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tswitch {\n\tcase len(foreignFields) == 0:\n\t\treguessOrErr()\n\t\treturn\n\tcase len(relation.primaryKeys) > 0:\n\t\tfor idx, primaryKey := range relation.primaryKeys {\n\t\t\tif f := primarySchema.LookUpField(primaryKey); f != nil {\n\t\t\t\tif len(primaryFields) < idx+1 {\n\t\t\t\t\tprimaryFields = append(primaryFields, f)\n\t\t\t\t} else if f != primaryFields[idx] {\n\t\t\t\t\treguessOrErr()\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\treguessOrErr()\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\tcase len(primaryFields) == 0:\n\t\tif len(foreignFields) == 1 && primarySchema.PrioritizedPrimaryField != nil {\n\t\t\tprimaryFields = append(primaryFields, primarySchema.PrioritizedPrimaryField)\n\t\t} else if len(primarySchema.PrimaryFields) == len(foreignFields) {\n\t\t\tprimaryFields = append(primaryFields, primarySchema.PrimaryFields...)\n\t\t} else {\n\t\t\treguessOrErr()\n\t\t\treturn\n\t\t}\n\t}\n\n\t// build references\n\tfor idx, foreignField := range foreignFields {\n\t\t// use same data type for foreign keys\n\t\tschema.Relationships.Mux.Lock()\n\t\tif schema != foreignField.Schema {\n\t\t\tforeignField.Schema.Relationships.Mux.Lock()\n\t\t}\n\t\tif copyableDataType(primaryFields[idx].DataType) {\n\t\t\tforeignField.DataType = primaryFields[idx].DataType\n\t\t}\n\t\tforeignField.GORMDataType = primaryFields[idx].GORMDataType\n\t\tif foreignField.Size == 0 {\n\t\t\tforeignField.Size = primaryFields[idx].Size\n\t\t}\n\t\tschema.Relationships.Mux.Unlock()\n\t\tif schema != foreignField.Schema {\n\t\t\tforeignField.Schema.Relationships.Mux.Unlock()\n\t\t}\n\n\t\trelation.References = append(relation.References, &Reference{\n\t\t\tPrimaryKey:    primaryFields[idx],\n\t\t\tForeignKey:    foreignField,\n\t\t\tOwnPrimaryKey: (schema == primarySchema && gl == guessHas) || (field.OwnerSchema == primarySchema && gl == guessEmbeddedHas),\n\t\t})\n\t}\n\n\tif gl == guessHas || gl == guessEmbeddedHas {\n\t\trelation.Type = has\n\t} else {\n\t\trelation.Type = BelongsTo\n\t}\n}\n\n// Constraint is ForeignKey Constraint\ntype Constraint struct {\n\tName            string\n\tField           *Field\n\tSchema          *Schema\n\tForeignKeys     []*Field\n\tReferenceSchema *Schema\n\tReferences      []*Field\n\tOnDelete        string\n\tOnUpdate        string\n}\n\nfunc (constraint *Constraint) GetName() string { return constraint.Name }\n\nfunc (constraint *Constraint) Build() (sql string, vars []interface{}) {\n\tsql = \"CONSTRAINT ? FOREIGN KEY ? REFERENCES ??\"\n\tif constraint.OnDelete != \"\" {\n\t\tsql += \" ON DELETE \" + constraint.OnDelete\n\t}\n\n\tif constraint.OnUpdate != \"\" {\n\t\tsql += \" ON UPDATE \" + constraint.OnUpdate\n\t}\n\n\tforeignKeys := make([]interface{}, 0, len(constraint.ForeignKeys))\n\tfor _, field := range constraint.ForeignKeys {\n\t\tforeignKeys = append(foreignKeys, clause.Column{Name: field.DBName})\n\t}\n\n\treferences := make([]interface{}, 0, len(constraint.References))\n\tfor _, field := range constraint.References {\n\t\treferences = append(references, clause.Column{Name: field.DBName})\n\t}\n\tvars = append(vars, clause.Table{Name: constraint.Name}, foreignKeys, clause.Table{Name: constraint.ReferenceSchema.Table}, references)\n\treturn\n}\n\nfunc (rel *Relationship) ParseConstraint() *Constraint {\n\tstr := rel.Field.TagSettings[\"CONSTRAINT\"]\n\tif str == \"-\" {\n\t\treturn nil\n\t}\n\n\tif rel.Type == BelongsTo {\n\t\tfor _, r := range rel.FieldSchema.Relationships.Relations {\n\t\t\tif r != rel && r.FieldSchema == rel.Schema && len(rel.References) == len(r.References) {\n\t\t\t\tmatched := true\n\t\t\t\tfor idx, ref := range r.References {\n\t\t\t\t\tif rel.References[idx].PrimaryKey != ref.PrimaryKey ||\n\t\t\t\t\t\trel.References[idx].ForeignKey != ref.ForeignKey ||\n\t\t\t\t\t\trel.References[idx].PrimaryValue != ref.PrimaryValue {\n\t\t\t\t\t\tmatched = false\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif matched {\n\t\t\t\t\treturn nil\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tvar (\n\t\tname     string\n\t\tidx      = strings.IndexByte(str, ',')\n\t\tsettings = ParseTagSetting(str, \",\")\n\t)\n\n\t// optimize match english letters and midline\n\t// The following code is basically called in for.\n\t// In order to avoid the performance problems caused by repeated compilation of regular expressions,\n\t// it only needs to be done once outside, so optimization is done here.\n\tif idx != -1 && regEnLetterAndMidline.MatchString(str[0:idx]) {\n\t\tname = str[0:idx]\n\t} else {\n\t\tname = rel.Schema.namer.RelationshipFKName(*rel)\n\t}\n\n\tconstraint := Constraint{\n\t\tName:     name,\n\t\tField:    rel.Field,\n\t\tOnUpdate: settings[\"ONUPDATE\"],\n\t\tOnDelete: settings[\"ONDELETE\"],\n\t}\n\n\tfor _, ref := range rel.References {\n\t\tif ref.PrimaryKey != nil && (rel.JoinTable == nil || ref.OwnPrimaryKey) {\n\t\t\tconstraint.ForeignKeys = append(constraint.ForeignKeys, ref.ForeignKey)\n\t\t\tconstraint.References = append(constraint.References, ref.PrimaryKey)\n\n\t\t\tif ref.OwnPrimaryKey {\n\t\t\t\tconstraint.Schema = ref.ForeignKey.Schema\n\t\t\t\tconstraint.ReferenceSchema = rel.Schema\n\t\t\t} else {\n\t\t\t\tconstraint.Schema = rel.Schema\n\t\t\t\tconstraint.ReferenceSchema = ref.PrimaryKey.Schema\n\t\t\t}\n\t\t}\n\t}\n\n\treturn &constraint\n}\n\nfunc (rel *Relationship) ToQueryConditions(ctx context.Context, reflectValue reflect.Value) (conds []clause.Expression) {\n\ttable := rel.FieldSchema.Table\n\tforeignFields := []*Field{}\n\trelForeignKeys := []string{}\n\n\tif rel.JoinTable != nil {\n\t\ttable = rel.JoinTable.Table\n\t\tfor _, ref := range rel.References {\n\t\t\tif ref.OwnPrimaryKey {\n\t\t\t\tforeignFields = append(foreignFields, ref.PrimaryKey)\n\t\t\t\trelForeignKeys = append(relForeignKeys, ref.ForeignKey.DBName)\n\t\t\t} else if ref.PrimaryValue != \"\" {\n\t\t\t\tconds = append(conds, clause.Eq{\n\t\t\t\t\tColumn: clause.Column{Table: rel.JoinTable.Table, Name: ref.ForeignKey.DBName},\n\t\t\t\t\tValue:  ref.PrimaryValue,\n\t\t\t\t})\n\t\t\t} else {\n\t\t\t\tconds = append(conds, clause.Eq{\n\t\t\t\t\tColumn: clause.Column{Table: rel.JoinTable.Table, Name: ref.ForeignKey.DBName},\n\t\t\t\t\tValue:  clause.Column{Table: rel.FieldSchema.Table, Name: ref.PrimaryKey.DBName},\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t} else {\n\t\tfor _, ref := range rel.References {\n\t\t\tif ref.OwnPrimaryKey {\n\t\t\t\trelForeignKeys = append(relForeignKeys, ref.ForeignKey.DBName)\n\t\t\t\tforeignFields = append(foreignFields, ref.PrimaryKey)\n\t\t\t} else if ref.PrimaryValue != \"\" {\n\t\t\t\tconds = append(conds, clause.Eq{\n\t\t\t\t\tColumn: clause.Column{Table: rel.FieldSchema.Table, Name: ref.ForeignKey.DBName},\n\t\t\t\t\tValue:  ref.PrimaryValue,\n\t\t\t\t})\n\t\t\t} else {\n\t\t\t\trelForeignKeys = append(relForeignKeys, ref.PrimaryKey.DBName)\n\t\t\t\tforeignFields = append(foreignFields, ref.ForeignKey)\n\t\t\t}\n\t\t}\n\t}\n\n\t_, foreignValues := GetIdentityFieldValuesMap(ctx, reflectValue, foreignFields)\n\tcolumn, values := ToQueryValues(table, relForeignKeys, foreignValues)\n\n\tconds = append(conds, clause.IN{Column: column, Values: values})\n\treturn\n}\n\nfunc copyableDataType(str DataType) bool {\n\tlowerStr := strings.ToLower(string(str))\n\tfor _, s := range []string{\"auto_increment\", \"primary key\"} {\n\t\tif strings.Contains(lowerStr, s) {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n"
  },
  {
    "path": "schema/relationship_test.go",
    "content": "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/tests\"\n)\n\nfunc checkStructRelation(t *testing.T, data interface{}, relations ...Relation) {\n\tif s, err := schema.Parse(data, &sync.Map{}, schema.NamingStrategy{}); err != nil {\n\t\tt.Errorf(\"Failed to parse schema, got error %v\", err)\n\t} else {\n\t\tfor _, rel := range relations {\n\t\t\tcheckSchemaRelation(t, s, rel)\n\t\t}\n\t}\n}\n\nfunc TestBelongsToOverrideForeignKey(t *testing.T) {\n\ttype Profile struct {\n\t\tgorm.Model\n\t\tName string\n\t}\n\n\ttype User struct {\n\t\tgorm.Model\n\t\tProfile      Profile `gorm:\"ForeignKey:ProfileRefer\"`\n\t\tProfileRefer int\n\t}\n\n\tcheckStructRelation(t, &User{}, Relation{\n\t\tName: \"Profile\", Type: schema.BelongsTo, Schema: \"User\", FieldSchema: \"Profile\",\n\t\tReferences: []Reference{{\"ID\", \"Profile\", \"ProfileRefer\", \"User\", \"\", false}},\n\t})\n}\n\nfunc TestBelongsToOverrideReferences(t *testing.T) {\n\ttype Profile struct {\n\t\tgorm.Model\n\t\tRefer string\n\t\tName  string\n\t}\n\n\ttype User struct {\n\t\tgorm.Model\n\t\tProfile   Profile `gorm:\"ForeignKey:ProfileID;References:Refer\"`\n\t\tProfileID int\n\t}\n\n\tcheckStructRelation(t, &User{}, Relation{\n\t\tName: \"Profile\", Type: schema.BelongsTo, Schema: \"User\", FieldSchema: \"Profile\",\n\t\tReferences: []Reference{{\"Refer\", \"Profile\", \"ProfileID\", \"User\", \"\", false}},\n\t})\n}\n\nfunc TestBelongsToWithOnlyReferences(t *testing.T) {\n\ttype Profile struct {\n\t\tgorm.Model\n\t\tRefer string\n\t\tName  string\n\t}\n\n\ttype User struct {\n\t\tgorm.Model\n\t\tProfile      Profile `gorm:\"References:Refer\"`\n\t\tProfileRefer int\n\t}\n\n\tcheckStructRelation(t, &User{}, Relation{\n\t\tName: \"Profile\", Type: schema.BelongsTo, Schema: \"User\", FieldSchema: \"Profile\",\n\t\tReferences: []Reference{{\"Refer\", \"Profile\", \"ProfileRefer\", \"User\", \"\", false}},\n\t})\n}\n\nfunc TestBelongsToWithOnlyReferences2(t *testing.T) {\n\ttype Profile struct {\n\t\tgorm.Model\n\t\tRefer string\n\t\tName  string\n\t}\n\n\ttype User struct {\n\t\tgorm.Model\n\t\tProfile   Profile `gorm:\"References:Refer\"`\n\t\tProfileID int\n\t}\n\n\tcheckStructRelation(t, &User{}, Relation{\n\t\tName: \"Profile\", Type: schema.BelongsTo, Schema: \"User\", FieldSchema: \"Profile\",\n\t\tReferences: []Reference{{\"Refer\", \"Profile\", \"ProfileID\", \"User\", \"\", false}},\n\t})\n}\n\nfunc TestSelfReferentialBelongsTo(t *testing.T) {\n\ttype User struct {\n\t\tID        int32 `gorm:\"primaryKey\"`\n\t\tName      string\n\t\tCreatorID *int32\n\t\tCreator   *User\n\t}\n\n\tcheckStructRelation(t, &User{}, Relation{\n\t\tName: \"Creator\", Type: schema.BelongsTo, Schema: \"User\", FieldSchema: \"User\",\n\t\tReferences: []Reference{{\"ID\", \"User\", \"CreatorID\", \"User\", \"\", false}},\n\t})\n}\n\nfunc TestSelfReferentialBelongsToOverrideReferences(t *testing.T) {\n\ttype User struct {\n\t\tID        int32 `gorm:\"primaryKey\"`\n\t\tName      string\n\t\tCreatedBy *int32\n\t\tCreator   *User `gorm:\"foreignKey:CreatedBy;references:ID\"`\n\t}\n\n\tcheckStructRelation(t, &User{}, Relation{\n\t\tName: \"Creator\", Type: schema.BelongsTo, Schema: \"User\", FieldSchema: \"User\",\n\t\tReferences: []Reference{{\"ID\", \"User\", \"CreatedBy\", \"User\", \"\", false}},\n\t})\n}\n\nfunc TestBelongsToWithMixin(t *testing.T) {\n\ttype Profile struct {\n\t\tgorm.Model\n\t\tRefer string\n\t\tName  string\n\t}\n\n\ttype ProfileMixin struct {\n\t\tProfile      Profile `gorm:\"References:Refer\"`\n\t\tProfileRefer int\n\t}\n\n\ttype User struct {\n\t\tgorm.Model\n\t\tProfileMixin\n\t}\n\n\tcheckStructRelation(t, &User{}, Relation{\n\t\tName: \"Profile\", Type: schema.BelongsTo, Schema: \"User\", FieldSchema: \"Profile\",\n\t\tReferences: []Reference{{\"Refer\", \"Profile\", \"ProfileRefer\", \"User\", \"\", false}},\n\t})\n}\n\nfunc TestHasOneOverrideForeignKey(t *testing.T) {\n\ttype Profile struct {\n\t\tgorm.Model\n\t\tName      string\n\t\tUserRefer uint\n\t}\n\n\ttype User struct {\n\t\tgorm.Model\n\t\tProfile Profile `gorm:\"ForeignKey:UserRefer\"`\n\t}\n\n\tcheckStructRelation(t, &User{}, Relation{\n\t\tName: \"Profile\", Type: schema.HasOne, Schema: \"User\", FieldSchema: \"Profile\",\n\t\tReferences: []Reference{{\"ID\", \"User\", \"UserRefer\", \"Profile\", \"\", true}},\n\t})\n}\n\nfunc TestHasOneOverrideReferences(t *testing.T) {\n\ttype Profile struct {\n\t\tgorm.Model\n\t\tName   string\n\t\tUserID uint\n\t}\n\n\ttype User struct {\n\t\tgorm.Model\n\t\tRefer   string\n\t\tProfile Profile `gorm:\"ForeignKey:UserID;References:Refer\"`\n\t}\n\n\tcheckStructRelation(t, &User{}, Relation{\n\t\tName: \"Profile\", Type: schema.HasOne, Schema: \"User\", FieldSchema: \"Profile\",\n\t\tReferences: []Reference{{\"Refer\", \"User\", \"UserID\", \"Profile\", \"\", true}},\n\t})\n}\n\nfunc TestHasOneOverrideReferences2(t *testing.T) {\n\ttype Profile struct {\n\t\tgorm.Model\n\t\tName string\n\t}\n\n\ttype User struct {\n\t\tgorm.Model\n\t\tProfileID uint     `gorm:\"column:profile_id\"`\n\t\tProfile   *Profile `gorm:\"foreignKey:ID;references:ProfileID\"`\n\t}\n\n\tcheckStructRelation(t, &User{}, Relation{\n\t\tName: \"Profile\", Type: schema.HasOne, Schema: \"User\", FieldSchema: \"Profile\",\n\t\tReferences: []Reference{{\"ProfileID\", \"User\", \"ID\", \"Profile\", \"\", true}},\n\t})\n}\n\nfunc TestHasOneWithOnlyReferences(t *testing.T) {\n\ttype Profile struct {\n\t\tgorm.Model\n\t\tName      string\n\t\tUserRefer uint\n\t}\n\n\ttype User struct {\n\t\tgorm.Model\n\t\tRefer   string\n\t\tProfile Profile `gorm:\"References:Refer\"`\n\t}\n\n\tcheckStructRelation(t, &User{}, Relation{\n\t\tName: \"Profile\", Type: schema.HasOne, Schema: \"User\", FieldSchema: \"Profile\",\n\t\tReferences: []Reference{{\"Refer\", \"User\", \"UserRefer\", \"Profile\", \"\", true}},\n\t})\n}\n\nfunc TestHasOneWithOnlyReferences2(t *testing.T) {\n\ttype Profile struct {\n\t\tgorm.Model\n\t\tName   string\n\t\tUserID uint\n\t}\n\n\ttype User struct {\n\t\tgorm.Model\n\t\tRefer   string\n\t\tProfile Profile `gorm:\"References:Refer\"`\n\t}\n\n\tcheckStructRelation(t, &User{}, Relation{\n\t\tName: \"Profile\", Type: schema.HasOne, Schema: \"User\", FieldSchema: \"Profile\",\n\t\tReferences: []Reference{{\"Refer\", \"User\", \"UserID\", \"Profile\", \"\", true}},\n\t})\n}\n\nfunc TestHasManyOverrideForeignKey(t *testing.T) {\n\ttype Profile struct {\n\t\tgorm.Model\n\t\tName      string\n\t\tUserRefer uint\n\t}\n\n\ttype User struct {\n\t\tgorm.Model\n\t\tProfile []Profile `gorm:\"ForeignKey:UserRefer\"`\n\t}\n\n\tcheckStructRelation(t, &User{}, Relation{\n\t\tName: \"Profile\", Type: schema.HasMany, Schema: \"User\", FieldSchema: \"Profile\",\n\t\tReferences: []Reference{{\"ID\", \"User\", \"UserRefer\", \"Profile\", \"\", true}},\n\t})\n}\n\nfunc TestHasManyOverrideReferences(t *testing.T) {\n\ttype Profile struct {\n\t\tgorm.Model\n\t\tName   string\n\t\tUserID uint\n\t}\n\n\ttype User struct {\n\t\tgorm.Model\n\t\tRefer   string\n\t\tProfile []Profile `gorm:\"ForeignKey:UserID;References:Refer\"`\n\t}\n\n\tcheckStructRelation(t, &User{}, Relation{\n\t\tName: \"Profile\", Type: schema.HasMany, Schema: \"User\", FieldSchema: \"Profile\",\n\t\tReferences: []Reference{{\"Refer\", \"User\", \"UserID\", \"Profile\", \"\", true}},\n\t})\n}\n\nfunc TestMany2ManyOverrideForeignKeyAndReferences(t *testing.T) {\n\ttype Profile struct {\n\t\tgorm.Model\n\t\tName      string\n\t\tUserRefer uint\n\t}\n\n\ttype User struct {\n\t\tgorm.Model\n\t\tProfiles  []Profile `gorm:\"many2many:user_profiles;ForeignKey:Refer;JoinForeignKey:UserReferID;References:UserRefer;JoinReferences:ProfileRefer\"`\n\t\tProfiles2 []Profile `gorm:\"many2many:user_profiles2;ForeignKey:refer;JoinForeignKey:user_refer_id;References:user_refer;JoinReferences:profile_refer\"`\n\t\tRefer     uint\n\t}\n\n\tcheckStructRelation(t, &User{}, Relation{\n\t\tName: \"Profiles\", Type: schema.Many2Many, Schema: \"User\", FieldSchema: \"Profile\",\n\t\tJoinTable: JoinTable{Name: \"user_profiles\", Table: \"user_profiles\"},\n\t\tReferences: []Reference{\n\t\t\t{\"Refer\", \"User\", \"UserReferID\", \"user_profiles\", \"\", true},\n\t\t\t{\"UserRefer\", \"Profile\", \"ProfileRefer\", \"user_profiles\", \"\", false},\n\t\t},\n\t}, Relation{\n\t\tName: \"Profiles2\", Type: schema.Many2Many, Schema: \"User\", FieldSchema: \"Profile\",\n\t\tJoinTable: JoinTable{Name: \"user_profiles2\", Table: \"user_profiles2\"},\n\t\tReferences: []Reference{\n\t\t\t{\"Refer\", \"User\", \"User_refer_id\", \"user_profiles2\", \"\", true},\n\t\t\t{\"UserRefer\", \"Profile\", \"Profile_refer\", \"user_profiles2\", \"\", false},\n\t\t},\n\t})\n}\n\nfunc TestMany2ManyOverrideForeignKey(t *testing.T) {\n\ttype Profile struct {\n\t\tgorm.Model\n\t\tName      string\n\t\tUserRefer uint\n\t}\n\n\ttype User struct {\n\t\tgorm.Model\n\t\tProfiles []Profile `gorm:\"many2many:user_profiles;ForeignKey:Refer;References:UserRefer\"`\n\t\tRefer    uint\n\t}\n\n\tcheckStructRelation(t, &User{}, Relation{\n\t\tName: \"Profiles\", Type: schema.Many2Many, Schema: \"User\", FieldSchema: \"Profile\",\n\t\tJoinTable: JoinTable{Name: \"user_profiles\", Table: \"user_profiles\"},\n\t\tReferences: []Reference{\n\t\t\t{\"Refer\", \"User\", \"UserRefer\", \"user_profiles\", \"\", true},\n\t\t\t{\"UserRefer\", \"Profile\", \"ProfileUserRefer\", \"user_profiles\", \"\", false},\n\t\t},\n\t})\n}\n\nfunc TestMany2ManySharedForeignKey(t *testing.T) {\n\ttype Profile struct {\n\t\tgorm.Model\n\t\tName         string\n\t\tKind         string\n\t\tProfileRefer uint\n\t}\n\n\ttype User struct {\n\t\tgorm.Model\n\t\tProfiles []Profile `gorm:\"many2many:user_profiles;foreignKey:Refer,Kind;joinForeignKey:UserRefer,Kind;References:ProfileRefer,Kind;joinReferences:ProfileR,Kind\"`\n\t\tKind     string\n\t\tRefer    uint\n\t}\n\n\tcheckStructRelation(t, &User{}, Relation{\n\t\tName: \"Profiles\", Type: schema.Many2Many, Schema: \"User\", FieldSchema: \"Profile\",\n\t\tJoinTable: JoinTable{Name: \"user_profiles\", Table: \"user_profiles\"},\n\t\tReferences: []Reference{\n\t\t\t{\"Refer\", \"User\", \"UserRefer\", \"user_profiles\", \"\", true},\n\t\t\t{\"Kind\", \"User\", \"Kind\", \"user_profiles\", \"\", true},\n\t\t\t{\"ProfileRefer\", \"Profile\", \"ProfileR\", \"user_profiles\", \"\", false},\n\t\t\t{\"Kind\", \"Profile\", \"Kind\", \"user_profiles\", \"\", false},\n\t\t},\n\t})\n}\n\nfunc TestMany2ManyOverrideJoinForeignKey(t *testing.T) {\n\ttype Profile struct {\n\t\tgorm.Model\n\t\tName      string\n\t\tUserRefer uint\n\t}\n\n\ttype User struct {\n\t\tgorm.Model\n\t\tProfiles []Profile `gorm:\"many2many:user_profile;JoinForeignKey:UserReferID;JoinReferences:ProfileRefer\"`\n\t\tRefer    uint\n\t}\n\n\tcheckStructRelation(t, &User{}, Relation{\n\t\tName: \"Profiles\", Type: schema.Many2Many, Schema: \"User\", FieldSchema: \"Profile\",\n\t\tJoinTable: JoinTable{Name: \"user_profile\", Table: \"user_profile\"},\n\t\tReferences: []Reference{\n\t\t\t{\"ID\", \"User\", \"UserReferID\", \"user_profile\", \"\", true},\n\t\t\t{\"ID\", \"Profile\", \"ProfileRefer\", \"user_profile\", \"\", false},\n\t\t},\n\t})\n}\n\nfunc TestBuildReadonlyMany2ManyRelation(t *testing.T) {\n\ttype Profile struct {\n\t\tgorm.Model\n\t\tName      string\n\t\tUserRefer uint\n\t}\n\n\ttype User struct {\n\t\tgorm.Model\n\t\tProfiles []Profile `gorm:\"->;many2many:user_profile;JoinForeignKey:UserReferID;JoinReferences:ProfileRefer\"`\n\t\tRefer    uint\n\t}\n\n\tcheckStructRelation(t, &User{}, Relation{\n\t\tName: \"Profiles\", Type: schema.Many2Many, Schema: \"User\", FieldSchema: \"Profile\",\n\t\tJoinTable: JoinTable{Name: \"user_profile\", Table: \"user_profile\"},\n\t\tReferences: []Reference{\n\t\t\t{\"ID\", \"User\", \"UserReferID\", \"user_profile\", \"\", true},\n\t\t\t{\"ID\", \"Profile\", \"ProfileRefer\", \"user_profile\", \"\", false},\n\t\t},\n\t})\n}\n\nfunc TestMany2ManyWithMultiPrimaryKeys(t *testing.T) {\n\ttype Tag struct {\n\t\tID     uint   `gorm:\"primary_key\"`\n\t\tLocale string `gorm:\"primary_key\"`\n\t\tValue  string\n\t}\n\n\ttype Blog struct {\n\t\tID         uint   `gorm:\"primary_key\"`\n\t\tLocale     string `gorm:\"primary_key\"`\n\t\tSubject    string\n\t\tBody       string\n\t\tTags       []Tag `gorm:\"many2many:blog_tags;\"`\n\t\tSharedTags []Tag `gorm:\"many2many:shared_blog_tags;ForeignKey:id;References:id\"`\n\t\tLocaleTags []Tag `gorm:\"many2many:locale_blog_tags;ForeignKey:id,locale;References:id\"`\n\t}\n\n\tcheckStructRelation(t, &Blog{},\n\t\tRelation{\n\t\t\tName: \"Tags\", Type: schema.Many2Many, Schema: \"Blog\", FieldSchema: \"Tag\",\n\t\t\tJoinTable: JoinTable{Name: \"blog_tags\", Table: \"blog_tags\"},\n\t\t\tReferences: []Reference{\n\t\t\t\t{\"ID\", \"Blog\", \"BlogID\", \"blog_tags\", \"\", true},\n\t\t\t\t{\"Locale\", \"Blog\", \"BlogLocale\", \"blog_tags\", \"\", true},\n\t\t\t\t{\"ID\", \"Tag\", \"TagID\", \"blog_tags\", \"\", false},\n\t\t\t\t{\"Locale\", \"Tag\", \"TagLocale\", \"blog_tags\", \"\", false},\n\t\t\t},\n\t\t},\n\t\tRelation{\n\t\t\tName: \"SharedTags\", Type: schema.Many2Many, Schema: \"Blog\", FieldSchema: \"Tag\",\n\t\t\tJoinTable: JoinTable{Name: \"shared_blog_tags\", Table: \"shared_blog_tags\"},\n\t\t\tReferences: []Reference{\n\t\t\t\t{\"ID\", \"Blog\", \"BlogID\", \"shared_blog_tags\", \"\", true},\n\t\t\t\t{\"ID\", \"Tag\", \"TagID\", \"shared_blog_tags\", \"\", false},\n\t\t\t},\n\t\t},\n\t\tRelation{\n\t\t\tName: \"LocaleTags\", Type: schema.Many2Many, Schema: \"Blog\", FieldSchema: \"Tag\",\n\t\t\tJoinTable: JoinTable{Name: \"locale_blog_tags\", Table: \"locale_blog_tags\"},\n\t\t\tReferences: []Reference{\n\t\t\t\t{\"ID\", \"Blog\", \"BlogID\", \"locale_blog_tags\", \"\", true},\n\t\t\t\t{\"Locale\", \"Blog\", \"BlogLocale\", \"locale_blog_tags\", \"\", true},\n\t\t\t\t{\"ID\", \"Tag\", \"TagID\", \"locale_blog_tags\", \"\", false},\n\t\t\t},\n\t\t},\n\t)\n}\n\nfunc TestMultipleMany2Many(t *testing.T) {\n\ttype Thing struct {\n\t\tID int\n\t}\n\n\ttype Person struct {\n\t\tID       int\n\t\tLikes    []Thing `gorm:\"many2many:likes\"`\n\t\tDislikes []Thing `gorm:\"many2many:dislikes\"`\n\t}\n\n\tcheckStructRelation(t, &Person{},\n\t\tRelation{\n\t\t\tName: \"Likes\", Type: schema.Many2Many, Schema: \"Person\", FieldSchema: \"Thing\",\n\t\t\tJoinTable: JoinTable{Name: \"likes\", Table: \"likes\"},\n\t\t\tReferences: []Reference{\n\t\t\t\t{\"ID\", \"Person\", \"PersonID\", \"likes\", \"\", true},\n\t\t\t\t{\"ID\", \"Thing\", \"ThingID\", \"likes\", \"\", false},\n\t\t\t},\n\t\t},\n\t\tRelation{\n\t\t\tName: \"Dislikes\", Type: schema.Many2Many, Schema: \"Person\", FieldSchema: \"Thing\",\n\t\t\tJoinTable: JoinTable{Name: \"dislikes\", Table: \"dislikes\"},\n\t\t\tReferences: []Reference{\n\t\t\t\t{\"ID\", \"Person\", \"PersonID\", \"dislikes\", \"\", true},\n\t\t\t\t{\"ID\", \"Thing\", \"ThingID\", \"dislikes\", \"\", false},\n\t\t\t},\n\t\t},\n\t)\n}\n\nfunc TestSelfReferentialMany2Many(t *testing.T) {\n\ttype User struct {\n\t\tID         int32 `gorm:\"primaryKey\"`\n\t\tName       string\n\t\tCreatedBy  int32\n\t\tCreators   []User      `gorm:\"foreignKey:CreatedBy\"`\n\t\tAnotherPro interface{} `gorm:\"-\"`\n\t}\n\n\tcheckStructRelation(t, &User{}, Relation{\n\t\tName: \"Creators\", Type: schema.HasMany, Schema: \"User\", FieldSchema: \"User\",\n\t\tReferences: []Reference{{\"ID\", \"User\", \"CreatedBy\", \"User\", \"\", true}},\n\t})\n\n\tuser, err := schema.Parse(&User{}, &sync.Map{}, schema.NamingStrategy{})\n\tif err != nil {\n\t\tt.Fatalf(\"failed to parse schema\")\n\t}\n\n\trelSchema := user.Relationships.Relations[\"Creators\"].FieldSchema\n\tif user != relSchema {\n\t\tt.Fatalf(\"schema should be same, expects %p but got %p\", user, relSchema)\n\t}\n}\n\ntype CreatedByModel struct {\n\tCreatedByID uint\n\tCreatedBy   *CreatedUser\n}\n\ntype CreatedUser struct {\n\tgorm.Model\n\tCreatedByModel\n}\n\nfunc TestEmbeddedRelation(t *testing.T) {\n\tcheckStructRelation(t, &CreatedUser{}, Relation{\n\t\tName: \"CreatedBy\", Type: schema.BelongsTo, Schema: \"CreatedUser\", FieldSchema: \"CreatedUser\",\n\t\tReferences: []Reference{\n\t\t\t{\"ID\", \"CreatedUser\", \"CreatedByID\", \"CreatedUser\", \"\", false},\n\t\t},\n\t})\n\n\tuserSchema, err := schema.Parse(&CreatedUser{}, &sync.Map{}, schema.NamingStrategy{})\n\tif err != nil {\n\t\tt.Fatalf(\"failed to parse schema, got error %v\", err)\n\t}\n\n\tif len(userSchema.Relationships.Relations) != 1 {\n\t\tt.Fatalf(\"expects 1 relations, but got %v\", len(userSchema.Relationships.Relations))\n\t}\n\n\tif createdByRel, ok := userSchema.Relationships.Relations[\"CreatedBy\"]; ok {\n\t\tif createdByRel.FieldSchema != userSchema {\n\t\t\tt.Fatalf(\"expects same field schema, but got new %p, old %p\", createdByRel.FieldSchema, userSchema)\n\t\t}\n\t} else {\n\t\tt.Fatalf(\"expects created by relations, but not found\")\n\t}\n}\n\nfunc TestEmbeddedHas(t *testing.T) {\n\ttype Toy struct {\n\t\tID        int\n\t\tName      string\n\t\tOwnerID   int\n\t\tOwnerType string\n\t}\n\ttype User struct {\n\t\tID  int\n\t\tCat struct {\n\t\t\tName string\n\t\t\tToy  Toy   `gorm:\"polymorphic:Owner;\"`\n\t\t\tToys []Toy `gorm:\"polymorphic:Owner;\"`\n\t\t} `gorm:\"embedded;embeddedPrefix:cat_\"`\n\t\tDog struct {\n\t\t\tID     int\n\t\t\tName   string\n\t\t\tUserID int\n\t\t\tToy    Toy   `gorm:\"polymorphic:Owner;\"`\n\t\t\tToys   []Toy `gorm:\"polymorphic:Owner;\"`\n\t\t}\n\t\tToys []Toy `gorm:\"polymorphic:Owner;\"`\n\t}\n\n\ts, err := schema.Parse(&User{}, &sync.Map{}, schema.NamingStrategy{})\n\tif err != nil {\n\t\tt.Fatalf(\"Failed to parse schema, got error %v\", err)\n\t}\n\n\tcheckEmbeddedRelations(t, s.Relationships.EmbeddedRelations, map[string]EmbeddedRelations{\n\t\t\"Cat\": {\n\t\t\tRelations: map[string]Relation{\n\t\t\t\t\"Toy\": {\n\t\t\t\t\tName:        \"Toy\",\n\t\t\t\t\tType:        schema.HasOne,\n\t\t\t\t\tSchema:      \"User\",\n\t\t\t\t\tFieldSchema: \"Toy\",\n\t\t\t\t\tPolymorphic: Polymorphic{ID: \"OwnerID\", Type: \"OwnerType\", Value: \"users\"},\n\t\t\t\t\tReferences: []Reference{\n\t\t\t\t\t\t{ForeignKey: \"OwnerType\", ForeignSchema: \"Toy\", PrimaryValue: \"users\"},\n\t\t\t\t\t\t{ForeignKey: \"OwnerType\", ForeignSchema: \"Toy\", PrimaryValue: \"users\"},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t\"Toys\": {\n\t\t\t\t\tName:        \"Toys\",\n\t\t\t\t\tType:        schema.HasMany,\n\t\t\t\t\tSchema:      \"User\",\n\t\t\t\t\tFieldSchema: \"Toy\",\n\t\t\t\t\tPolymorphic: Polymorphic{ID: \"OwnerID\", Type: \"OwnerType\", Value: \"users\"},\n\t\t\t\t\tReferences: []Reference{\n\t\t\t\t\t\t{ForeignKey: \"OwnerType\", ForeignSchema: \"Toy\", PrimaryValue: \"users\"},\n\t\t\t\t\t\t{ForeignKey: \"OwnerType\", ForeignSchema: \"Toy\", PrimaryValue: \"users\"},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t})\n}\n\nfunc TestPolymorphic(t *testing.T) {\n\tt.Run(\"has one\", func(t *testing.T) {\n\t\ttype Toy struct {\n\t\t\tID        int\n\t\t\tName      string\n\t\t\tOwnerID   int\n\t\t\tOwnerType string\n\t\t}\n\n\t\ttype Cat struct {\n\t\t\tID   int\n\t\t\tName string\n\t\t\tToy  Toy `gorm:\"polymorphic:Owner;\"`\n\t\t}\n\n\t\ts, err := schema.Parse(&Cat{}, &sync.Map{}, schema.NamingStrategy{})\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"Failed to parse schema, got error %v\", err)\n\t\t}\n\n\t\tcheckEmbeddedRelations(t, s.Relationships.EmbeddedRelations, map[string]EmbeddedRelations{\n\t\t\t\"Cat\": {\n\t\t\t\tRelations: map[string]Relation{\n\t\t\t\t\t\"Toy\": {\n\t\t\t\t\t\tName:        \"Toy\",\n\t\t\t\t\t\tType:        schema.HasOne,\n\t\t\t\t\t\tSchema:      \"User\",\n\t\t\t\t\t\tFieldSchema: \"Toy\",\n\t\t\t\t\t\tPolymorphic: Polymorphic{ID: \"OwnerID\", Type: \"OwnerType\", Value: \"users\"},\n\t\t\t\t\t\tReferences: []Reference{\n\t\t\t\t\t\t\t{ForeignKey: \"OwnerType\", ForeignSchema: \"Toy\", PrimaryValue: \"users\"},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t})\n\t})\n\n\tt.Run(\"has one with custom polymorphic type and id\", func(t *testing.T) {\n\t\ttype Toy struct {\n\t\t\tID    int\n\t\t\tName  string\n\t\t\tRefId int\n\t\t\tType  string\n\t\t}\n\n\t\ttype Cat struct {\n\t\t\tID   int\n\t\t\tName string\n\t\t\tToy  Toy `gorm:\"polymorphic:Owner;polymorphicType:Type;polymorphicId:RefId\"`\n\t\t}\n\n\t\ts, err := schema.Parse(&Cat{}, &sync.Map{}, schema.NamingStrategy{})\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"Failed to parse schema, got error %v\", err)\n\t\t}\n\n\t\tcheckEmbeddedRelations(t, s.Relationships.EmbeddedRelations, map[string]EmbeddedRelations{\n\t\t\t\"Cat\": {\n\t\t\t\tRelations: map[string]Relation{\n\t\t\t\t\t\"Toy\": {\n\t\t\t\t\t\tName:        \"Toy\",\n\t\t\t\t\t\tType:        schema.HasOne,\n\t\t\t\t\t\tSchema:      \"User\",\n\t\t\t\t\t\tFieldSchema: \"Toy\",\n\t\t\t\t\t\tPolymorphic: Polymorphic{ID: \"ref_id\", Type: \"Type\", Value: \"users\"},\n\t\t\t\t\t\tReferences: []Reference{\n\t\t\t\t\t\t\t{ForeignKey: \"Type\", ForeignSchema: \"Toy\", PrimaryValue: \"users\"},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t})\n\t})\n\n\tt.Run(\"has one with only polymorphic type\", func(t *testing.T) {\n\t\ttype Toy struct {\n\t\t\tID      int\n\t\t\tName    string\n\t\t\tOwnerID int\n\t\t\tType    string\n\t\t}\n\n\t\ttype Cat struct {\n\t\t\tID   int\n\t\t\tName string\n\t\t\tToy  Toy `gorm:\"polymorphic:Owner;polymorphicType:Type\"`\n\t\t}\n\n\t\ts, err := schema.Parse(&Cat{}, &sync.Map{}, schema.NamingStrategy{})\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"Failed to parse schema, got error %v\", err)\n\t\t}\n\n\t\tcheckEmbeddedRelations(t, s.Relationships.EmbeddedRelations, map[string]EmbeddedRelations{\n\t\t\t\"Cat\": {\n\t\t\t\tRelations: map[string]Relation{\n\t\t\t\t\t\"Toy\": {\n\t\t\t\t\t\tName:        \"Toy\",\n\t\t\t\t\t\tType:        schema.HasOne,\n\t\t\t\t\t\tSchema:      \"User\",\n\t\t\t\t\t\tFieldSchema: \"Toy\",\n\t\t\t\t\t\tPolymorphic: Polymorphic{ID: \"owner_id\", Type: \"Type\", Value: \"users\"},\n\t\t\t\t\t\tReferences: []Reference{\n\t\t\t\t\t\t\t{ForeignKey: \"Type\", ForeignSchema: \"Toy\", PrimaryValue: \"users\"},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t})\n\t})\n\n\tt.Run(\"has many\", func(t *testing.T) {\n\t\ttype Toy struct {\n\t\t\tID        int\n\t\t\tName      string\n\t\t\tOwnerID   int\n\t\t\tOwnerType string\n\t\t}\n\n\t\ttype Cat struct {\n\t\t\tID   int\n\t\t\tName string\n\t\t\tToys []Toy `gorm:\"polymorphic:Owner;\"`\n\t\t}\n\n\t\ts, err := schema.Parse(&Cat{}, &sync.Map{}, schema.NamingStrategy{})\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"Failed to parse schema, got error %v\", err)\n\t\t}\n\n\t\tcheckEmbeddedRelations(t, s.Relationships.EmbeddedRelations, map[string]EmbeddedRelations{\n\t\t\t\"Cat\": {\n\t\t\t\tRelations: map[string]Relation{\n\t\t\t\t\t\"Toys\": {\n\t\t\t\t\t\tName:        \"Toys\",\n\t\t\t\t\t\tType:        schema.HasMany,\n\t\t\t\t\t\tSchema:      \"User\",\n\t\t\t\t\t\tFieldSchema: \"Toy\",\n\t\t\t\t\t\tPolymorphic: Polymorphic{ID: \"OwnerID\", Type: \"OwnerType\", Value: \"users\"},\n\t\t\t\t\t\tReferences: []Reference{\n\t\t\t\t\t\t\t{ForeignKey: \"OwnerType\", ForeignSchema: \"Toy\", PrimaryValue: \"users\"},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t})\n\t})\n\n\tt.Run(\"has many with custom polymorphic type and id\", func(t *testing.T) {\n\t\ttype Toy struct {\n\t\t\tID    int\n\t\t\tName  string\n\t\t\tRefId int\n\t\t\tType  string\n\t\t}\n\n\t\ttype Cat struct {\n\t\t\tID   int\n\t\t\tName string\n\t\t\tToys []Toy `gorm:\"polymorphicType:Type;polymorphicId:RefId\"`\n\t\t}\n\n\t\ts, err := schema.Parse(&Cat{}, &sync.Map{}, schema.NamingStrategy{})\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"Failed to parse schema, got error %v\", err)\n\t\t}\n\n\t\tcheckEmbeddedRelations(t, s.Relationships.EmbeddedRelations, map[string]EmbeddedRelations{\n\t\t\t\"Cat\": {\n\t\t\t\tRelations: map[string]Relation{\n\t\t\t\t\t\"Toys\": {\n\t\t\t\t\t\tName:        \"Toys\",\n\t\t\t\t\t\tType:        schema.HasMany,\n\t\t\t\t\t\tSchema:      \"User\",\n\t\t\t\t\t\tFieldSchema: \"Toy\",\n\t\t\t\t\t\tPolymorphic: Polymorphic{ID: \"ref_id\", Type: \"Type\", Value: \"users\"},\n\t\t\t\t\t\tReferences: []Reference{\n\t\t\t\t\t\t\t{ForeignKey: \"Type\", ForeignSchema: \"Toy\", PrimaryValue: \"users\"},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t})\n\t})\n}\n\nfunc TestEmbeddedBelongsTo(t *testing.T) {\n\ttype Country struct {\n\t\tID   int `gorm:\"primaryKey\"`\n\t\tName string\n\t}\n\ttype Address struct {\n\t\tCountryID int\n\t\tCountry   Country\n\t}\n\ttype NestedAddress struct {\n\t\tAddress\n\t}\n\ttype CountryMixin struct {\n\t\tCountryID int\n\t\tCountry   Country\n\t}\n\ttype Org struct {\n\t\tID              int\n\t\tPostalAddress   Address `gorm:\"embedded;embeddedPrefix:postal_address_\"`\n\t\tVisitingAddress Address `gorm:\"embedded;embeddedPrefix:visiting_address_\"`\n\t\tAddressID       int\n\t\tAddress         struct {\n\t\t\tID int\n\t\t\tAddress\n\t\t}\n\t\tNestedAddress *NestedAddress `gorm:\"embedded;embeddedPrefix:nested_address_\"`\n\t\tCountryMixin\n\t}\n\n\ts, err := schema.Parse(&Org{}, &sync.Map{}, schema.NamingStrategy{})\n\tif err != nil {\n\t\tt.Errorf(\"Failed to parse schema, got error %v\", err)\n\t}\n\n\tcheckEmbeddedRelations(t, s.Relationships.EmbeddedRelations, map[string]EmbeddedRelations{\n\t\t\"PostalAddress\": {\n\t\t\tRelations: map[string]Relation{\n\t\t\t\t\"Country\": {\n\t\t\t\t\tName: \"Country\", Type: schema.BelongsTo, Schema: \"Org\", FieldSchema: \"Country\",\n\t\t\t\t\tReferences: []Reference{\n\t\t\t\t\t\t{PrimaryKey: \"ID\", PrimarySchema: \"Country\", ForeignKey: \"CountryID\", ForeignSchema: \"Org\"},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t\"VisitingAddress\": {\n\t\t\tRelations: map[string]Relation{\n\t\t\t\t\"Country\": {\n\t\t\t\t\tName: \"Country\", Type: schema.BelongsTo, Schema: \"Org\", FieldSchema: \"Country\",\n\t\t\t\t\tReferences: []Reference{\n\t\t\t\t\t\t{PrimaryKey: \"ID\", PrimarySchema: \"Country\", ForeignKey: \"CountryID\", ForeignSchema: \"Org\"},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t\"NestedAddress\": {\n\t\t\tRelations: map[string]Relation{\n\t\t\t\t\"Country\": {\n\t\t\t\t\tName: \"Country\", Type: schema.BelongsTo, Schema: \"Org\", FieldSchema: \"Country\",\n\t\t\t\t\tReferences: []Reference{\n\t\t\t\t\t\t{PrimaryKey: \"ID\", PrimarySchema: \"Country\", ForeignKey: \"CountryID\", ForeignSchema: \"Org\"},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t})\n}\n\nfunc TestVariableRelation(t *testing.T) {\n\tvar result struct {\n\t\tUser\n\t}\n\n\tcheckStructRelation(t, &result, Relation{\n\t\tName: \"Account\", Type: schema.HasOne, Schema: \"\", FieldSchema: \"Account\",\n\t\tReferences: []Reference{\n\t\t\t{\"ID\", \"\", \"UserID\", \"Account\", \"\", true},\n\t\t},\n\t})\n\n\tcheckStructRelation(t, &result, Relation{\n\t\tName: \"Company\", Type: schema.BelongsTo, Schema: \"\", FieldSchema: \"Company\",\n\t\tReferences: []Reference{\n\t\t\t{\"ID\", \"Company\", \"CompanyID\", \"\", \"\", false},\n\t\t},\n\t})\n}\n\nfunc TestSameForeignKey(t *testing.T) {\n\ttype UserAux struct {\n\t\tgorm.Model\n\t\tAux  string\n\t\tUUID string\n\t}\n\n\ttype User struct {\n\t\tgorm.Model\n\t\tName string\n\t\tUUID string\n\t\tAux  *UserAux `gorm:\"foreignkey:UUID;references:UUID\"`\n\t}\n\n\tcheckStructRelation(t, &User{},\n\t\tRelation{\n\t\t\tName: \"Aux\", Type: schema.HasOne, Schema: \"User\", FieldSchema: \"UserAux\",\n\t\t\tReferences: []Reference{\n\t\t\t\t{\"UUID\", \"User\", \"UUID\", \"UserAux\", \"\", true},\n\t\t\t},\n\t\t},\n\t)\n}\n\nfunc TestBelongsToSameForeignKey(t *testing.T) {\n\ttype User struct {\n\t\tgorm.Model\n\t\tName string\n\t\tUUID string\n\t}\n\n\ttype UserAux struct {\n\t\tgorm.Model\n\t\tAux  string\n\t\tUUID string\n\t\tUser User `gorm:\"ForeignKey:UUID;references:UUID;belongsTo\"`\n\t}\n\n\tcheckStructRelation(t, &UserAux{},\n\t\tRelation{\n\t\t\tName: \"User\", Type: schema.BelongsTo, Schema: \"UserAux\", FieldSchema: \"User\",\n\t\t\tReferences: []Reference{\n\t\t\t\t{\"UUID\", \"User\", \"UUID\", \"UserAux\", \"\", false},\n\t\t\t},\n\t\t},\n\t)\n}\n\nfunc TestHasOneWithSameForeignKey(t *testing.T) {\n\ttype Profile struct {\n\t\tgorm.Model\n\t\tName         string\n\t\tProfileRefer int // not used in relationship\n\t}\n\n\ttype User struct {\n\t\tgorm.Model\n\t\tProfile      Profile `gorm:\"ForeignKey:ID;references:ProfileRefer\"`\n\t\tProfileRefer int\n\t}\n\n\tcheckStructRelation(t, &User{}, Relation{\n\t\tName: \"Profile\", Type: schema.HasOne, Schema: \"User\", FieldSchema: \"Profile\",\n\t\tReferences: []Reference{{\"ProfileRefer\", \"User\", \"ID\", \"Profile\", \"\", true}},\n\t})\n}\n\nfunc TestHasManySameForeignKey(t *testing.T) {\n\ttype Profile struct {\n\t\tgorm.Model\n\t\tName      string\n\t\tUserRefer uint\n\t}\n\n\ttype User struct {\n\t\tgorm.Model\n\t\tUserRefer uint\n\t\tProfile   []Profile `gorm:\"ForeignKey:UserRefer\"`\n\t}\n\n\tcheckStructRelation(t, &User{}, Relation{\n\t\tName: \"Profile\", Type: schema.HasMany, Schema: \"User\", FieldSchema: \"Profile\",\n\t\tReferences: []Reference{{\"ID\", \"User\", \"UserRefer\", \"Profile\", \"\", true}},\n\t})\n}\n\ntype Author struct {\n\tgorm.Model\n}\n\ntype Book struct {\n\tgorm.Model\n\tAuthor   Author\n\tAuthorID uint\n}\n\nfunc (Book) TableName() string {\n\treturn \"my_schema.a_very_very_very_very_very_very_very_very_long_table_name\"\n}\n\nfunc TestParseConstraintNameWithSchemaQualifiedLongTableName(t *testing.T) {\n\ts, err := schema.Parse(\n\t\t&Book{},\n\t\t&sync.Map{},\n\t\tschema.NamingStrategy{IdentifierMaxLength: 64},\n\t)\n\tif err != nil {\n\t\tt.Fatalf(\"Failed to parse schema\")\n\t}\n\n\texpectedConstraintName := \"fk_my_schema_a_very_very_very_very_very_very_very_very_l4db13eec\"\n\tconstraint := s.Relationships.Relations[\"Author\"].ParseConstraint()\n\n\tif constraint.Name != expectedConstraintName {\n\t\tt.Fatalf(\n\t\t\t\"expected constraint name %s, got %s\",\n\t\t\texpectedConstraintName,\n\t\t\tconstraint.Name,\n\t\t)\n\t}\n}\n\ntype InfoRelation struct {\n\tID    int\n\tCode  string\n\tInfo1 []*Info1 `gorm:\"foreignkey:Code;references:Code\"`\n\tInfo2 []*Info2 `gorm:\"foreignkey:Code;references:Code\"`\n}\n\ntype Info1 struct {\n\tCreatedAt time.Time\n\tUpdatedAt time.Time\n\tCode      string\n\tRelation  []*InfoRelation `gorm:\"foreignkey:Code;references:Code\"`\n}\n\ntype Info2 struct {\n\tCreatedAt time.Time\n\tUpdatedAt time.Time\n\tCode      string\n\tRelation  []*InfoRelation `gorm:\"foreignkey:Code;references:Code\"`\n}\n\nfunc TestDataRace(t *testing.T) {\n\tsyncMap := &sync.Map{}\n\tfor i := 0; i < 10; i++ {\n\t\tgo func() {\n\t\t\tschema.Parse(&Info1{}, syncMap, schema.NamingStrategy{IdentifierMaxLength: 64})\n\t\t}()\n\n\t\tgo func() {\n\t\t\tschema.Parse(&Info2{}, syncMap, schema.NamingStrategy{IdentifierMaxLength: 64})\n\t\t}()\n\n\t\tgo func() {\n\t\t\tvar result User\n\t\t\tschema.Parse(&result, syncMap, schema.NamingStrategy{IdentifierMaxLength: 64})\n\t\t}()\n\t\tgo func() {\n\t\t\tvar result tests.Account\n\t\t\tschema.Parse(&result, syncMap, schema.NamingStrategy{IdentifierMaxLength: 64})\n\t\t}()\n\t}\n}\n"
  },
  {
    "path": "schema/schema.go",
    "content": "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/clause\"\n\t\"gorm.io/gorm/logger\"\n)\n\ntype callbackType string\n\nconst (\n\tcallbackTypeBeforeCreate callbackType = \"BeforeCreate\"\n\tcallbackTypeBeforeUpdate callbackType = \"BeforeUpdate\"\n\tcallbackTypeAfterCreate  callbackType = \"AfterCreate\"\n\tcallbackTypeAfterUpdate  callbackType = \"AfterUpdate\"\n\tcallbackTypeBeforeSave   callbackType = \"BeforeSave\"\n\tcallbackTypeAfterSave    callbackType = \"AfterSave\"\n\tcallbackTypeBeforeDelete callbackType = \"BeforeDelete\"\n\tcallbackTypeAfterDelete  callbackType = \"AfterDelete\"\n\tcallbackTypeAfterFind    callbackType = \"AfterFind\"\n)\n\n// ErrUnsupportedDataType unsupported data type\nvar ErrUnsupportedDataType = errors.New(\"unsupported data type\")\n\ntype Schema struct {\n\tName                      string\n\tModelType                 reflect.Type\n\tTable                     string\n\tPrioritizedPrimaryField   *Field\n\tDBNames                   []string\n\tPrimaryFields             []*Field\n\tPrimaryFieldDBNames       []string\n\tFields                    []*Field\n\tFieldsByName              map[string]*Field\n\tFieldsByBindName          map[string]*Field // embedded fields is 'Embed.Field'\n\tFieldsByDBName            map[string]*Field\n\tFieldsWithDefaultDBValue  []*Field // fields with default value assigned by database\n\tRelationships             Relationships\n\tCreateClauses             []clause.Interface\n\tQueryClauses              []clause.Interface\n\tUpdateClauses             []clause.Interface\n\tDeleteClauses             []clause.Interface\n\tBeforeCreate, AfterCreate bool\n\tBeforeUpdate, AfterUpdate bool\n\tBeforeDelete, AfterDelete bool\n\tBeforeSave, AfterSave     bool\n\tAfterFind                 bool\n\terr                       error\n\tinitialized               chan struct{}\n\tnamer                     Namer\n\tcacheStore                *sync.Map\n}\n\nfunc (schema *Schema) String() string {\n\tif schema.ModelType.Name() == \"\" {\n\t\treturn fmt.Sprintf(\"%s(%s)\", schema.Name, schema.Table)\n\t}\n\treturn fmt.Sprintf(\"%s.%s\", schema.ModelType.PkgPath(), schema.ModelType.Name())\n}\n\nfunc (schema *Schema) MakeSlice() reflect.Value {\n\tslice := reflect.MakeSlice(reflect.SliceOf(reflect.PointerTo(schema.ModelType)), 0, 20)\n\tresults := reflect.New(slice.Type())\n\tresults.Elem().Set(slice)\n\n\treturn results\n}\n\nfunc (schema *Schema) LookUpField(name string) *Field {\n\tif field, ok := schema.FieldsByDBName[name]; ok {\n\t\treturn field\n\t}\n\tif field, ok := schema.FieldsByName[name]; ok {\n\t\treturn field\n\t}\n\n\t// Lookup field using namer-driven ColumnName\n\tif schema.namer == nil {\n\t\treturn nil\n\t}\n\tnamerColumnName := schema.namer.ColumnName(schema.Table, name)\n\tif field, ok := schema.FieldsByDBName[namerColumnName]; ok {\n\t\treturn field\n\t}\n\n\treturn nil\n}\n\n// LookUpFieldByBindName looks for the closest field in the embedded struct.\n//\n//\ttype Struct struct {\n//\t\tEmbedded struct {\n//\t\t\tID string // is selected by LookUpFieldByBindName([]string{\"Embedded\", \"ID\"}, \"ID\")\n//\t\t}\n//\t\tID string // is selected by LookUpFieldByBindName([]string{\"ID\"}, \"ID\")\n//\t}\nfunc (schema *Schema) LookUpFieldByBindName(bindNames []string, name string) *Field {\n\tfor i := len(bindNames) - 1; i >= 0; i-- {\n\t\tfind := strings.Join(bindNames[:i], \".\") + \".\" + name\n\t\tif field, ok := schema.FieldsByBindName[find]; ok {\n\t\t\treturn field\n\t\t}\n\t}\n\treturn nil\n}\n\ntype Tabler interface {\n\tTableName() string\n}\n\ntype TablerWithNamer interface {\n\tTableName(Namer) string\n}\n\nvar callbackTypes = []callbackType{\n\tcallbackTypeBeforeCreate, callbackTypeAfterCreate,\n\tcallbackTypeBeforeUpdate, callbackTypeAfterUpdate,\n\tcallbackTypeBeforeSave, callbackTypeAfterSave,\n\tcallbackTypeBeforeDelete, callbackTypeAfterDelete,\n\tcallbackTypeAfterFind,\n}\n\n// Parse get data type from dialector\nfunc Parse(dest interface{}, cacheStore *sync.Map, namer Namer) (*Schema, error) {\n\treturn ParseWithSpecialTableName(dest, cacheStore, namer, \"\")\n}\n\n// ParseWithSpecialTableName get data type from dialector with extra schema table\nfunc ParseWithSpecialTableName(dest interface{}, cacheStore *sync.Map, namer Namer, specialTableName string) (*Schema, error) {\n\tif dest == nil {\n\t\treturn nil, fmt.Errorf(\"%w: %+v\", ErrUnsupportedDataType, dest)\n\t}\n\n\tmodelType := reflect.ValueOf(dest).Type()\n\tif modelType.Kind() == reflect.Ptr {\n\t\tmodelType = modelType.Elem()\n\t}\n\n\tif modelType.Kind() != reflect.Struct {\n\t\tif modelType.Kind() == reflect.Interface {\n\t\t\tmodelType = reflect.Indirect(reflect.ValueOf(dest)).Elem().Type()\n\t\t}\n\n\t\tfor modelType.Kind() == reflect.Slice || modelType.Kind() == reflect.Array || modelType.Kind() == reflect.Ptr {\n\t\t\tmodelType = modelType.Elem()\n\t\t}\n\n\t\tif modelType.Kind() != reflect.Struct {\n\t\t\tif modelType.PkgPath() == \"\" {\n\t\t\t\treturn nil, fmt.Errorf(\"%w: %+v\", ErrUnsupportedDataType, dest)\n\t\t\t}\n\t\t\treturn nil, fmt.Errorf(\"%w: %s.%s\", ErrUnsupportedDataType, modelType.PkgPath(), modelType.Name())\n\t\t}\n\t}\n\n\t// Cache the Schema for performance,\n\t// Use the modelType or modelType + schemaTable (if it present) as cache key.\n\tvar schemaCacheKey interface{} = modelType\n\tif specialTableName != \"\" {\n\t\tschemaCacheKey = fmt.Sprintf(\"%p-%s\", modelType, specialTableName)\n\t}\n\n\t// Load exist schema cache, return if exists\n\tif v, ok := cacheStore.Load(schemaCacheKey); ok {\n\t\ts := v.(*Schema)\n\t\t// Wait for the initialization of other goroutines to complete\n\t\t<-s.initialized\n\t\treturn s, s.err\n\t}\n\n\tvar tableName string\n\tmodelValue := reflect.New(modelType)\n\tif specialTableName != \"\" {\n\t\ttableName = specialTableName\n\t} else if en, ok := namer.(embeddedNamer); ok {\n\t\ttableName = en.Table\n\t} else if tabler, ok := modelValue.Interface().(Tabler); ok {\n\t\ttableName = tabler.TableName()\n\t} else if tabler, ok := modelValue.Interface().(TablerWithNamer); ok {\n\t\ttableName = tabler.TableName(namer)\n\t} else {\n\t\ttableName = namer.TableName(modelType.Name())\n\t}\n\n\tschema := &Schema{\n\t\tName:             modelType.Name(),\n\t\tModelType:        modelType,\n\t\tTable:            tableName,\n\t\tDBNames:          make([]string, 0, 10),\n\t\tFields:           make([]*Field, 0, 10),\n\t\tFieldsByName:     make(map[string]*Field, 10),\n\t\tFieldsByBindName: make(map[string]*Field, 10),\n\t\tFieldsByDBName:   make(map[string]*Field, 10),\n\t\tRelationships:    Relationships{Relations: map[string]*Relationship{}},\n\t\tcacheStore:       cacheStore,\n\t\tnamer:            namer,\n\t\tinitialized:      make(chan struct{}),\n\t}\n\t// When the schema initialization is completed, the channel will be closed\n\tdefer close(schema.initialized)\n\n\t// Load exist schema cache, return if exists\n\tif v, ok := cacheStore.Load(schemaCacheKey); ok {\n\t\ts := v.(*Schema)\n\t\t// Wait for the initialization of other goroutines to complete\n\t\t<-s.initialized\n\t\treturn s, s.err\n\t}\n\n\tfor i := 0; i < modelType.NumField(); i++ {\n\t\tif fieldStruct := modelType.Field(i); ast.IsExported(fieldStruct.Name) {\n\t\t\tif field := schema.ParseField(fieldStruct); field.EmbeddedSchema != nil {\n\t\t\t\tschema.Fields = append(schema.Fields, field.EmbeddedSchema.Fields...)\n\t\t\t} else {\n\t\t\t\tschema.Fields = append(schema.Fields, field)\n\t\t\t}\n\t\t}\n\t}\n\n\tfor _, field := range schema.Fields {\n\t\tif field.DBName == \"\" && field.DataType != \"\" {\n\t\t\tfield.DBName = namer.ColumnName(schema.Table, field.Name)\n\t\t}\n\n\t\tbindName := field.BindName()\n\t\tif field.DBName != \"\" {\n\t\t\t// nonexistence or shortest path or first appear prioritized if has permission\n\t\t\tif v, ok := schema.FieldsByDBName[field.DBName]; !ok || ((field.Creatable || field.Updatable || field.Readable) && len(field.BindNames) < len(v.BindNames)) {\n\t\t\t\tif _, ok := schema.FieldsByDBName[field.DBName]; !ok {\n\t\t\t\t\tschema.DBNames = append(schema.DBNames, field.DBName)\n\t\t\t\t}\n\t\t\t\tschema.FieldsByDBName[field.DBName] = field\n\t\t\t\tschema.FieldsByName[field.Name] = field\n\t\t\t\tschema.FieldsByBindName[bindName] = field\n\n\t\t\t\tif v != nil && v.PrimaryKey {\n\t\t\t\t\t// remove the existing primary key field\n\t\t\t\t\tfor idx, f := range schema.PrimaryFields {\n\t\t\t\t\t\tif f.DBName == v.DBName {\n\t\t\t\t\t\t\tschema.PrimaryFields = append(schema.PrimaryFields[0:idx], schema.PrimaryFields[idx+1:]...)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif field.PrimaryKey {\n\t\t\t\t\tschema.PrimaryFields = append(schema.PrimaryFields, field)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif of, ok := schema.FieldsByName[field.Name]; !ok || of.TagSettings[\"-\"] == \"-\" {\n\t\t\tschema.FieldsByName[field.Name] = field\n\t\t}\n\t\tif of, ok := schema.FieldsByBindName[bindName]; !ok || of.TagSettings[\"-\"] == \"-\" {\n\t\t\tschema.FieldsByBindName[bindName] = field\n\t\t}\n\n\t\tfield.setupValuerAndSetter(modelType)\n\t}\n\n\tprioritizedPrimaryField := schema.LookUpField(\"id\")\n\tif prioritizedPrimaryField == nil {\n\t\tprioritizedPrimaryField = schema.LookUpField(\"ID\")\n\t}\n\n\tif prioritizedPrimaryField != nil {\n\t\tif prioritizedPrimaryField.PrimaryKey {\n\t\t\tschema.PrioritizedPrimaryField = prioritizedPrimaryField\n\t\t} else if len(schema.PrimaryFields) == 0 {\n\t\t\tprioritizedPrimaryField.PrimaryKey = true\n\t\t\tschema.PrioritizedPrimaryField = prioritizedPrimaryField\n\t\t\tschema.PrimaryFields = append(schema.PrimaryFields, prioritizedPrimaryField)\n\t\t}\n\t}\n\n\tif schema.PrioritizedPrimaryField == nil {\n\t\tif len(schema.PrimaryFields) == 1 {\n\t\t\tschema.PrioritizedPrimaryField = schema.PrimaryFields[0]\n\t\t} else if len(schema.PrimaryFields) > 1 {\n\t\t\t// If there are multiple primary keys, the AUTOINCREMENT field is prioritized\n\t\t\tfor _, field := range schema.PrimaryFields {\n\t\t\t\tif field.AutoIncrement {\n\t\t\t\t\tschema.PrioritizedPrimaryField = field\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tfor _, field := range schema.PrimaryFields {\n\t\tschema.PrimaryFieldDBNames = append(schema.PrimaryFieldDBNames, field.DBName)\n\t}\n\n\t_, embedded := schema.cacheStore.Load(embeddedCacheKey)\n\trelationshipFields := []*Field{}\n\tfor _, field := range schema.Fields {\n\t\tif field.DataType != \"\" && field.HasDefaultValue && field.DefaultValueInterface == nil {\n\t\t\tschema.FieldsWithDefaultDBValue = append(schema.FieldsWithDefaultDBValue, field)\n\t\t}\n\n\t\tif !embedded {\n\t\t\tif field.DataType == \"\" && field.GORMDataType == \"\" && (field.Creatable || field.Updatable || field.Readable) {\n\t\t\t\trelationshipFields = append(relationshipFields, field)\n\t\t\t\tschema.FieldsByName[field.Name] = field\n\t\t\t\tschema.FieldsByBindName[field.BindName()] = field\n\t\t\t}\n\n\t\t\tfieldValue := reflect.New(field.IndirectFieldType).Interface()\n\t\t\tif fc, ok := fieldValue.(CreateClausesInterface); ok {\n\t\t\t\tfield.Schema.CreateClauses = append(field.Schema.CreateClauses, fc.CreateClauses(field)...)\n\t\t\t}\n\n\t\t\tif fc, ok := fieldValue.(QueryClausesInterface); ok {\n\t\t\t\tfield.Schema.QueryClauses = append(field.Schema.QueryClauses, fc.QueryClauses(field)...)\n\t\t\t}\n\n\t\t\tif fc, ok := fieldValue.(UpdateClausesInterface); ok {\n\t\t\t\tfield.Schema.UpdateClauses = append(field.Schema.UpdateClauses, fc.UpdateClauses(field)...)\n\t\t\t}\n\n\t\t\tif fc, ok := fieldValue.(DeleteClausesInterface); ok {\n\t\t\t\tfield.Schema.DeleteClauses = append(field.Schema.DeleteClauses, fc.DeleteClauses(field)...)\n\t\t\t}\n\t\t}\n\t}\n\n\tif field := schema.PrioritizedPrimaryField; field != nil {\n\t\tswitch field.GORMDataType {\n\t\tcase Int, Uint:\n\t\t\tif _, ok := field.TagSettings[\"AUTOINCREMENT\"]; !ok {\n\t\t\t\tif !field.HasDefaultValue || field.DefaultValueInterface != nil {\n\t\t\t\t\tschema.FieldsWithDefaultDBValue = append(schema.FieldsWithDefaultDBValue, field)\n\t\t\t\t}\n\n\t\t\t\tfield.HasDefaultValue = true\n\t\t\t\tfield.AutoIncrement = true\n\t\t\t}\n\t\t}\n\t}\n\n\t// Cache the schema\n\tif v, loaded := cacheStore.LoadOrStore(schemaCacheKey, schema); loaded {\n\t\ts := v.(*Schema)\n\t\t// Wait for the initialization of other goroutines to complete\n\t\t<-s.initialized\n\t\treturn s, s.err\n\t}\n\n\tdefer func() {\n\t\tif schema.err != nil {\n\t\t\tlogger.Default.Error(context.Background(), schema.err.Error())\n\t\t\tcacheStore.Delete(modelType)\n\t\t}\n\t}()\n\n\tfor _, cbName := range callbackTypes {\n\t\tif methodValue := modelValue.MethodByName(string(cbName)); methodValue.IsValid() {\n\t\t\tswitch methodValue.Type().String() {\n\t\t\tcase \"func(*gorm.DB) error\":\n\t\t\t\texpectedPkgPath := path.Dir(reflect.TypeOf(schema).Elem().PkgPath())\n\t\t\t\tif inVarPkg := methodValue.Type().In(0).Elem().PkgPath(); inVarPkg == expectedPkgPath {\n\t\t\t\t\treflect.Indirect(reflect.ValueOf(schema)).FieldByName(string(cbName)).SetBool(true)\n\t\t\t\t} else {\n\t\t\t\t\tlogger.Default.Warn(context.Background(), \"In model %v, the hook function `%v(*gorm.DB) error` has an incorrect parameter type. The expected parameter type is `%v`, but the provided type is `%v`.\", schema, cbName, expectedPkgPath, inVarPkg)\n\t\t\t\t\t// PASS\n\t\t\t\t}\n\t\t\tdefault:\n\t\t\t\tlogger.Default.Warn(context.Background(), \"Model %v don't match %vInterface, should be `%v(*gorm.DB) error`. Please see https://gorm.io/docs/hooks.html\", schema, cbName, cbName)\n\t\t\t}\n\t\t}\n\t}\n\n\t// parse relationships\n\tfor _, field := range relationshipFields {\n\t\tif schema.parseRelation(field); schema.err != nil {\n\t\t\treturn schema, schema.err\n\t\t}\n\t}\n\n\treturn schema, schema.err\n}\n\nfunc getOrParse(dest interface{}, cacheStore *sync.Map, namer Namer) (*Schema, error) {\n\tmodelType := reflect.ValueOf(dest).Type()\n\n\tif modelType.Kind() != reflect.Struct {\n\t\tfor modelType.Kind() == reflect.Slice || modelType.Kind() == reflect.Array || modelType.Kind() == reflect.Ptr {\n\t\t\tmodelType = modelType.Elem()\n\t\t}\n\n\t\tif modelType.Kind() != reflect.Struct {\n\t\t\tif modelType.PkgPath() == \"\" {\n\t\t\t\treturn nil, fmt.Errorf(\"%w: %+v\", ErrUnsupportedDataType, dest)\n\t\t\t}\n\t\t\treturn nil, fmt.Errorf(\"%w: %s.%s\", ErrUnsupportedDataType, modelType.PkgPath(), modelType.Name())\n\t\t}\n\t}\n\n\tif v, ok := cacheStore.Load(modelType); ok {\n\t\treturn v.(*Schema), nil\n\t}\n\n\treturn Parse(dest, cacheStore, namer)\n}\n"
  },
  {
    "path": "schema/schema_helper_test.go",
    "content": "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/utils/tests\"\n)\n\nfunc checkSchema(t *testing.T, s *schema.Schema, v *schema.Schema, primaryFields []string) {\n\tt.Run(\"CheckSchema/\"+s.Name, func(t *testing.T) {\n\t\ttests.AssertObjEqual(t, s, v, \"Name\", \"Table\")\n\n\t\tfor idx, field := range primaryFields {\n\t\t\tvar found bool\n\t\t\tfor _, f := range s.PrimaryFields {\n\t\t\t\tif f.Name == field {\n\t\t\t\t\tfound = true\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif idx == 0 {\n\t\t\t\tif field != s.PrioritizedPrimaryField.Name {\n\t\t\t\t\tt.Errorf(\"schema %v prioritized primary field should be %v, but got %v\", s, field, s.PrioritizedPrimaryField.Name)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif !found {\n\t\t\t\tt.Errorf(\"schema %v failed to found primary key: %v\", s, field)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc checkSchemaField(t *testing.T, s *schema.Schema, f *schema.Field, fc func(*schema.Field)) {\n\tt.Run(\"CheckField/\"+f.Name, func(t *testing.T) {\n\t\tif fc != nil {\n\t\t\tfc(f)\n\t\t}\n\n\t\tif f.TagSettings == nil {\n\t\t\tif f.Tag != \"\" {\n\t\t\t\tf.TagSettings = schema.ParseTagSetting(f.Tag.Get(\"gorm\"), \";\")\n\t\t\t} else {\n\t\t\t\tf.TagSettings = map[string]string{}\n\t\t\t}\n\t\t}\n\n\t\tparsedField, ok := s.FieldsByDBName[f.DBName]\n\t\tif !ok {\n\t\t\tparsedField, ok = s.FieldsByName[f.Name]\n\t\t}\n\n\t\tif !ok {\n\t\t\tt.Errorf(\"schema %v failed to look up field with name %v\", s, f.Name)\n\t\t} else {\n\t\t\ttests.AssertObjEqual(t, parsedField, f, \"Name\", \"DBName\", \"BindNames\", \"DataType\", \"PrimaryKey\", \"AutoIncrement\", \"Creatable\", \"Updatable\", \"Readable\", \"HasDefaultValue\", \"DefaultValue\", \"NotNull\", \"Unique\", \"Comment\", \"Size\", \"Precision\", \"TagSettings\")\n\n\t\t\tif f.DBName != \"\" {\n\t\t\t\tif field, ok := s.FieldsByDBName[f.DBName]; !ok || parsedField != field {\n\t\t\t\t\tt.Errorf(\"schema %v failed to look up field with dbname %v\", s, f.DBName)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfor _, name := range []string{f.DBName, f.Name} {\n\t\t\t\tif name != \"\" {\n\t\t\t\t\tif field := s.LookUpField(name); field == nil || (field.Name != name && field.DBName != name) {\n\t\t\t\t\t\tt.Errorf(\"schema %v failed to look up field with dbname %v\", s, f.DBName)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif f.PrimaryKey {\n\t\t\t\tvar found bool\n\t\t\t\tfor _, primaryField := range s.PrimaryFields {\n\t\t\t\t\tif primaryField == parsedField {\n\t\t\t\t\t\tfound = true\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif !found {\n\t\t\t\t\tt.Errorf(\"schema %v doesn't include field %v\", s, f.Name)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t})\n}\n\ntype Relation struct {\n\tName        string\n\tType        schema.RelationshipType\n\tSchema      string\n\tFieldSchema string\n\tPolymorphic Polymorphic\n\tJoinTable   JoinTable\n\tReferences  []Reference\n}\n\ntype Polymorphic struct {\n\tID    string\n\tType  string\n\tValue string\n}\n\ntype JoinTable struct {\n\tName   string\n\tTable  string\n\tFields []schema.Field\n}\n\ntype Reference struct {\n\tPrimaryKey    string\n\tPrimarySchema string\n\tForeignKey    string\n\tForeignSchema string\n\tPrimaryValue  string\n\tOwnPrimaryKey bool\n}\n\nfunc checkSchemaRelation(t *testing.T, s *schema.Schema, relation Relation) {\n\tt.Run(\"CheckRelation/\"+relation.Name, func(t *testing.T) {\n\t\tif r, ok := s.Relationships.Relations[relation.Name]; ok {\n\t\t\tif r.Name != relation.Name {\n\t\t\t\tt.Errorf(\"schema %v relation name expects %v, but got %v\", s, r.Name, relation.Name)\n\t\t\t}\n\n\t\t\tif r.Type != relation.Type {\n\t\t\t\tt.Errorf(\"schema %v relation name expects %v, but got %v\", s, r.Type, relation.Type)\n\t\t\t}\n\n\t\t\tif r.Schema.Name != relation.Schema {\n\t\t\t\tt.Errorf(\"schema %v relation's schema expects %v, but got %v\", s, relation.Schema, r.Schema.Name)\n\t\t\t}\n\n\t\t\tif r.FieldSchema.Name != relation.FieldSchema {\n\t\t\t\tt.Errorf(\"schema %v field relation's schema expects %v, but got %v\", s, relation.FieldSchema, r.FieldSchema.Name)\n\t\t\t}\n\n\t\t\tif r.Polymorphic != nil {\n\t\t\t\tif r.Polymorphic.PolymorphicID.Name != relation.Polymorphic.ID {\n\t\t\t\t\tt.Errorf(\"schema %v relation's polymorphic id field expects %v, but got %v\", s, relation.Polymorphic.ID, r.Polymorphic.PolymorphicID.Name)\n\t\t\t\t}\n\n\t\t\t\tif r.Polymorphic.PolymorphicType.Name != relation.Polymorphic.Type {\n\t\t\t\t\tt.Errorf(\"schema %v relation's polymorphic type field expects %v, but got %v\", s, relation.Polymorphic.Type, r.Polymorphic.PolymorphicType.Name)\n\t\t\t\t}\n\n\t\t\t\tif r.Polymorphic.Value != relation.Polymorphic.Value {\n\t\t\t\t\tt.Errorf(\"schema %v relation's polymorphic value expects %v, but got %v\", s, relation.Polymorphic.Value, r.Polymorphic.Value)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif r.JoinTable != nil {\n\t\t\t\tif r.JoinTable.Name != relation.JoinTable.Name {\n\t\t\t\t\tt.Errorf(\"schema %v relation's join table name expects %v, but got %v\", s, relation.JoinTable.Name, r.JoinTable.Name)\n\t\t\t\t}\n\n\t\t\t\tif r.JoinTable.Table != relation.JoinTable.Table {\n\t\t\t\t\tt.Errorf(\"schema %v relation's join table tablename expects %v, but got %v\", s, relation.JoinTable.Table, r.JoinTable.Table)\n\t\t\t\t}\n\n\t\t\t\tfor i := range relation.JoinTable.Fields {\n\t\t\t\t\tcheckSchemaField(t, r.JoinTable, &relation.JoinTable.Fields[i], nil)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif len(relation.References) != len(r.References) {\n\t\t\t\tt.Errorf(\"schema %v relation's reference's count doesn't match, expects %v, but got %v\", s, len(relation.References), len(r.References))\n\t\t\t}\n\n\t\t\tfor _, ref := range relation.References {\n\t\t\t\tvar found bool\n\t\t\t\tfor _, rf := range r.References {\n\t\t\t\t\tif (rf.PrimaryKey == nil || (rf.PrimaryKey.Name == ref.PrimaryKey && rf.PrimaryKey.Schema.Name == ref.PrimarySchema)) && (rf.PrimaryValue == ref.PrimaryValue) && (rf.ForeignKey.Name == ref.ForeignKey && rf.ForeignKey.Schema.Name == ref.ForeignSchema) && (rf.OwnPrimaryKey == ref.OwnPrimaryKey) {\n\t\t\t\t\t\tfound = true\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif !found {\n\t\t\t\t\tvar refs []string\n\t\t\t\t\tfor _, rf := range r.References {\n\t\t\t\t\t\tvar primaryKey, primaryKeySchema string\n\t\t\t\t\t\tif rf.PrimaryKey != nil {\n\t\t\t\t\t\t\tprimaryKey, primaryKeySchema = rf.PrimaryKey.Name, rf.PrimaryKey.Schema.Name\n\t\t\t\t\t\t}\n\t\t\t\t\t\trefs = append(refs, fmt.Sprintf(\n\t\t\t\t\t\t\t\"{PrimaryKey: %v PrimaryKeySchame: %v ForeignKey: %v ForeignKeySchema: %v PrimaryValue: %v OwnPrimaryKey: %v}\",\n\t\t\t\t\t\t\tprimaryKey, primaryKeySchema, rf.ForeignKey.Name, rf.ForeignKey.Schema.Name, rf.PrimaryValue, rf.OwnPrimaryKey,\n\t\t\t\t\t\t))\n\t\t\t\t\t}\n\t\t\t\t\tt.Errorf(\"schema %v relation %v failed to found reference %+v, has %v\", s, relation.Name, ref, strings.Join(refs, \", \"))\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tt.Errorf(\"schema %v failed to find relations by name %v\", s, relation.Name)\n\t\t}\n\t})\n}\n\ntype EmbeddedRelations struct {\n\tRelations         map[string]Relation\n\tEmbeddedRelations map[string]EmbeddedRelations\n}\n\nfunc checkEmbeddedRelations(t *testing.T, actual map[string]*schema.Relationships, expected map[string]EmbeddedRelations) {\n\tfor name, relations := range actual {\n\t\trs := expected[name]\n\t\tt.Run(\"CheckEmbeddedRelations/\"+name, func(t *testing.T) {\n\t\t\tif len(relations.Relations) != len(rs.Relations) {\n\t\t\t\tt.Errorf(\"schema relations count don't match, expects %d, got %d\", len(rs.Relations), len(relations.Relations))\n\t\t\t}\n\t\t\tif len(relations.EmbeddedRelations) != len(rs.EmbeddedRelations) {\n\t\t\t\tt.Errorf(\"schema embedded relations count don't match, expects %d, got %d\", len(rs.EmbeddedRelations), len(relations.EmbeddedRelations))\n\t\t\t}\n\t\t\tfor n, rel := range relations.Relations {\n\t\t\t\tif r, ok := rs.Relations[n]; !ok {\n\t\t\t\t\tt.Errorf(\"failed to find relation by name %s\", n)\n\t\t\t\t} else {\n\t\t\t\t\tcheckSchemaRelation(t, &schema.Schema{\n\t\t\t\t\t\tRelationships: schema.Relationships{\n\t\t\t\t\t\t\tRelations: map[string]*schema.Relationship{n: rel},\n\t\t\t\t\t\t},\n\t\t\t\t\t}, r)\n\t\t\t\t}\n\t\t\t}\n\t\t\tcheckEmbeddedRelations(t, relations.EmbeddedRelations, rs.EmbeddedRelations)\n\t\t})\n\t}\n}\n\nfunc checkField(t *testing.T, s *schema.Schema, value reflect.Value, values map[string]interface{}) {\n\tfor k, v := range values {\n\t\tt.Run(\"CheckField/\"+k, func(t *testing.T) {\n\t\t\tfv, _ := s.FieldsByDBName[k].ValueOf(context.Background(), value)\n\t\t\ttests.AssertEqual(t, v, fv)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "schema/schema_test.go",
    "content": "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/tests\"\n)\n\nfunc TestParseSchema(t *testing.T) {\n\tuser, err := schema.Parse(&tests.User{}, &sync.Map{}, schema.NamingStrategy{})\n\tif err != nil {\n\t\tt.Fatalf(\"failed to parse user, got error %v\", err)\n\t}\n\n\tcheckUserSchema(t, user)\n}\n\nfunc TestParseSchemaWithMap(t *testing.T) {\n\ttype User struct {\n\t\ttests.User\n\t\tAttrs map[string]string `gorm:\"type:Map(String,String);\"`\n\t}\n\n\tuser, err := schema.Parse(&User{}, &sync.Map{}, schema.NamingStrategy{})\n\tif err != nil {\n\t\tt.Fatalf(\"failed to parse user with map, got error %v\", err)\n\t}\n\n\tif field := user.FieldsByName[\"Attrs\"]; field.DataType != \"Map(String,String)\" {\n\t\tt.Errorf(\"failed to parse user field Attrs\")\n\t}\n}\n\nfunc TestParseSchemaWithPointerFields(t *testing.T) {\n\tuser, err := schema.Parse(&User{}, &sync.Map{}, schema.NamingStrategy{})\n\tif err != nil {\n\t\tt.Fatalf(\"failed to parse pointer user, got error %v\", err)\n\t}\n\n\tcheckUserSchema(t, user)\n}\n\nfunc checkUserSchema(t *testing.T, user *schema.Schema) {\n\t// check schema\n\tcheckSchema(t, user, &schema.Schema{Name: \"User\", Table: \"users\"}, []string{\"ID\"})\n\n\t// check fields\n\tfields := []schema.Field{\n\t\t{Name: \"ID\", DBName: \"id\", BindNames: []string{\"Model\", \"ID\"}, DataType: schema.Uint, PrimaryKey: true, Tag: `gorm:\"primarykey\"`, TagSettings: map[string]string{\"PRIMARYKEY\": \"PRIMARYKEY\"}, Size: 64, HasDefaultValue: true, AutoIncrement: true},\n\t\t{Name: \"CreatedAt\", DBName: \"created_at\", BindNames: []string{\"Model\", \"CreatedAt\"}, DataType: schema.Time},\n\t\t{Name: \"UpdatedAt\", DBName: \"updated_at\", BindNames: []string{\"Model\", \"UpdatedAt\"}, DataType: schema.Time},\n\t\t{Name: \"DeletedAt\", DBName: \"deleted_at\", BindNames: []string{\"Model\", \"DeletedAt\"}, Tag: `gorm:\"index\"`, DataType: schema.Time},\n\t\t{Name: \"Name\", DBName: \"name\", BindNames: []string{\"Name\"}, DataType: schema.String},\n\t\t{Name: \"Age\", DBName: \"age\", BindNames: []string{\"Age\"}, DataType: schema.Uint, Size: 64},\n\t\t{Name: \"Birthday\", DBName: \"birthday\", BindNames: []string{\"Birthday\"}, DataType: schema.Time},\n\t\t{Name: \"CompanyID\", DBName: \"company_id\", BindNames: []string{\"CompanyID\"}, DataType: schema.Int, Size: 64},\n\t\t{Name: \"ManagerID\", DBName: \"manager_id\", BindNames: []string{\"ManagerID\"}, DataType: schema.Uint, Size: 64},\n\t\t{Name: \"Active\", DBName: \"active\", BindNames: []string{\"Active\"}, DataType: schema.Bool},\n\t}\n\n\tfor i := range fields {\n\t\tcheckSchemaField(t, user, &fields[i], func(f *schema.Field) {\n\t\t\tf.Creatable = true\n\t\t\tf.Updatable = true\n\t\t\tf.Readable = true\n\t\t})\n\t}\n\n\t// check relations\n\trelations := []Relation{\n\t\t{\n\t\t\tName: \"Account\", Type: schema.HasOne, Schema: \"User\", FieldSchema: \"Account\",\n\t\t\tReferences: []Reference{{\"ID\", \"User\", \"UserID\", \"Account\", \"\", true}},\n\t\t},\n\t\t{\n\t\t\tName: \"Pets\", Type: schema.HasMany, Schema: \"User\", FieldSchema: \"Pet\",\n\t\t\tReferences: []Reference{{\"ID\", \"User\", \"UserID\", \"Pet\", \"\", true}},\n\t\t},\n\t\t{\n\t\t\tName: \"Toys\", Type: schema.HasMany, Schema: \"User\", FieldSchema: \"Toy\",\n\t\t\tPolymorphic: Polymorphic{ID: \"OwnerID\", Type: \"OwnerType\", Value: \"users\"},\n\t\t\tReferences:  []Reference{{\"ID\", \"User\", \"OwnerID\", \"Toy\", \"\", true}, {\"\", \"\", \"OwnerType\", \"Toy\", \"users\", false}},\n\t\t},\n\t\t{\n\t\t\tName: \"Company\", Type: schema.BelongsTo, Schema: \"User\", FieldSchema: \"Company\",\n\t\t\tReferences: []Reference{{\"ID\", \"Company\", \"CompanyID\", \"User\", \"\", false}},\n\t\t},\n\t\t{\n\t\t\tName: \"Manager\", Type: schema.BelongsTo, Schema: \"User\", FieldSchema: \"User\",\n\t\t\tReferences: []Reference{{\"ID\", \"User\", \"ManagerID\", \"User\", \"\", false}},\n\t\t},\n\t\t{\n\t\t\tName: \"Team\", Type: schema.HasMany, Schema: \"User\", FieldSchema: \"User\",\n\t\t\tReferences: []Reference{{\"ID\", \"User\", \"ManagerID\", \"User\", \"\", true}},\n\t\t},\n\t\t{\n\t\t\tName: \"Languages\", Type: schema.Many2Many, Schema: \"User\", FieldSchema: \"Language\",\n\t\t\tJoinTable: JoinTable{Name: \"UserSpeak\", Table: \"user_speaks\", Fields: []schema.Field{\n\t\t\t\t{\n\t\t\t\t\tName: \"UserID\", DBName: \"user_id\", BindNames: []string{\"UserID\"}, DataType: schema.Uint,\n\t\t\t\t\tTag: `gorm:\"primarykey\"`, Creatable: true, Updatable: true, Readable: true, PrimaryKey: true, Size: 64,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tName: \"LanguageCode\", DBName: \"language_code\", BindNames: []string{\"LanguageCode\"}, DataType: schema.String,\n\t\t\t\t\tTag: `gorm:\"primarykey\"`, Creatable: true, Updatable: true, Readable: true, PrimaryKey: true,\n\t\t\t\t},\n\t\t\t}},\n\t\t\tReferences: []Reference{{\"ID\", \"User\", \"UserID\", \"UserSpeak\", \"\", true}, {\"Code\", \"Language\", \"LanguageCode\", \"UserSpeak\", \"\", false}},\n\t\t},\n\t\t{\n\t\t\tName: \"Friends\", Type: schema.Many2Many, Schema: \"User\", FieldSchema: \"User\",\n\t\t\tJoinTable: JoinTable{Name: \"user_friends\", Table: \"user_friends\", Fields: []schema.Field{\n\t\t\t\t{\n\t\t\t\t\tName: \"UserID\", DBName: \"user_id\", BindNames: []string{\"UserID\"}, DataType: schema.Uint,\n\t\t\t\t\tTag: `gorm:\"primarykey\"`, Creatable: true, Updatable: true, Readable: true, PrimaryKey: true, Size: 64,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tName: \"FriendID\", DBName: \"friend_id\", BindNames: []string{\"FriendID\"}, DataType: schema.Uint,\n\t\t\t\t\tTag: `gorm:\"primarykey\"`, Creatable: true, Updatable: true, Readable: true, PrimaryKey: true, Size: 64,\n\t\t\t\t},\n\t\t\t}},\n\t\t\tReferences: []Reference{{\"ID\", \"User\", \"UserID\", \"user_friends\", \"\", true}, {\"ID\", \"User\", \"FriendID\", \"user_friends\", \"\", false}},\n\t\t},\n\t}\n\n\tfor _, relation := range relations {\n\t\tcheckSchemaRelation(t, user, relation)\n\t}\n}\n\nfunc TestParseSchemaWithAdvancedDataType(t *testing.T) {\n\tuser, err := schema.Parse(&AdvancedDataTypeUser{}, &sync.Map{}, schema.NamingStrategy{})\n\tif err != nil {\n\t\tt.Fatalf(\"failed to parse pointer user, got error %v\", err)\n\t}\n\n\t// check schema\n\tcheckSchema(t, user, &schema.Schema{Name: \"AdvancedDataTypeUser\", Table: \"advanced_data_type_users\"}, []string{\"ID\"})\n\n\t// check fields\n\tfields := []schema.Field{\n\t\t{Name: \"ID\", DBName: \"id\", BindNames: []string{\"ID\"}, DataType: schema.Int, PrimaryKey: true, Size: 64, HasDefaultValue: true, AutoIncrement: true},\n\t\t{Name: \"Name\", DBName: \"name\", BindNames: []string{\"Name\"}, DataType: schema.String},\n\t\t{Name: \"Birthday\", DBName: \"birthday\", BindNames: []string{\"Birthday\"}, DataType: schema.Time},\n\t\t{Name: \"RegisteredAt\", DBName: \"registered_at\", BindNames: []string{\"RegisteredAt\"}, DataType: schema.Time},\n\t\t{Name: \"DeletedAt\", DBName: \"deleted_at\", BindNames: []string{\"DeletedAt\"}, DataType: schema.Time},\n\t\t{Name: \"Active\", DBName: \"active\", BindNames: []string{\"Active\"}, DataType: schema.Bool},\n\t\t{Name: \"Admin\", DBName: \"admin\", BindNames: []string{\"Admin\"}, DataType: schema.Bool},\n\t}\n\n\tfor i := range fields {\n\t\tcheckSchemaField(t, user, &fields[i], func(f *schema.Field) {\n\t\t\tf.Creatable = true\n\t\t\tf.Updatable = true\n\t\t\tf.Readable = true\n\t\t})\n\t}\n}\n\ntype CustomizeTable struct{}\n\nfunc (CustomizeTable) TableName() string {\n\treturn \"customize\"\n}\n\nfunc TestCustomizeTableName(t *testing.T) {\n\tcustomize, err := schema.Parse(&CustomizeTable{}, &sync.Map{}, schema.NamingStrategy{})\n\tif err != nil {\n\t\tt.Fatalf(\"failed to parse pointer user, got error %v\", err)\n\t}\n\n\tif customize.Table != \"customize\" {\n\t\tt.Errorf(\"Failed to customize table with TableName method\")\n\t}\n}\n\nfunc TestNestedModel(t *testing.T) {\n\tversionUser, err := schema.Parse(&VersionUser{}, &sync.Map{}, schema.NamingStrategy{})\n\tif err != nil {\n\t\tt.Fatalf(\"failed to parse nested user, got error %v\", err)\n\t}\n\n\tfields := []schema.Field{\n\t\t{Name: \"ID\", DBName: \"id\", BindNames: []string{\"VersionModel\", \"BaseModel\", \"ID\"}, DataType: schema.Uint, PrimaryKey: true, Size: 64, HasDefaultValue: true, AutoIncrement: true},\n\t\t{Name: \"CreatedBy\", DBName: \"created_by\", BindNames: []string{\"VersionModel\", \"BaseModel\", \"CreatedBy\"}, DataType: schema.Uint, Size: 64},\n\t\t{Name: \"Version\", DBName: \"version\", BindNames: []string{\"VersionModel\", \"Version\"}, DataType: schema.Int, Size: 64},\n\t}\n\n\tfor _, f := range fields {\n\t\tcheckSchemaField(t, versionUser, &f, func(f *schema.Field) {\n\t\t\tf.Creatable = true\n\t\t\tf.Updatable = true\n\t\t\tf.Readable = true\n\t\t})\n\t}\n}\n\nfunc TestEmbeddedStruct(t *testing.T) {\n\ttype CorpBase struct {\n\t\tgorm.Model\n\t\tOwnerID string\n\t}\n\n\ttype Company struct {\n\t\tID      int\n\t\tOwnerID int\n\t\tName    string\n\t\tIgnored string `gorm:\"-\"`\n\t}\n\n\ttype Corp struct {\n\t\tCorpBase\n\t\tBase Company `gorm:\"embedded;embeddedPrefix:company_\"`\n\t}\n\n\tcropSchema, err := schema.Parse(&Corp{}, &sync.Map{}, schema.NamingStrategy{})\n\tif err != nil {\n\t\tt.Fatalf(\"failed to parse embedded struct with primary key, got error %v\", err)\n\t}\n\n\tfields := []schema.Field{\n\t\t{Name: \"ID\", DBName: \"id\", BindNames: []string{\"CorpBase\", \"Model\", \"ID\"}, DataType: schema.Uint, PrimaryKey: true, Size: 64, HasDefaultValue: true, AutoIncrement: true, TagSettings: map[string]string{\"PRIMARYKEY\": \"PRIMARYKEY\"}},\n\t\t{Name: \"ID\", DBName: \"company_id\", BindNames: []string{\"Base\", \"ID\"}, DataType: schema.Int, Size: 64, TagSettings: map[string]string{\"EMBEDDED\": \"EMBEDDED\", \"EMBEDDEDPREFIX\": \"company_\"}},\n\t\t{Name: \"Name\", DBName: \"company_name\", BindNames: []string{\"Base\", \"Name\"}, DataType: schema.String, TagSettings: map[string]string{\"EMBEDDED\": \"EMBEDDED\", \"EMBEDDEDPREFIX\": \"company_\"}},\n\t\t{Name: \"Ignored\", BindNames: []string{\"Base\", \"Ignored\"}, TagSettings: map[string]string{\"-\": \"-\", \"EMBEDDED\": \"EMBEDDED\", \"EMBEDDEDPREFIX\": \"company_\"}},\n\t\t{Name: \"OwnerID\", DBName: \"company_owner_id\", BindNames: []string{\"Base\", \"OwnerID\"}, DataType: schema.Int, Size: 64, TagSettings: map[string]string{\"EMBEDDED\": \"EMBEDDED\", \"EMBEDDEDPREFIX\": \"company_\"}},\n\t\t{Name: \"OwnerID\", DBName: \"owner_id\", BindNames: []string{\"CorpBase\", \"OwnerID\"}, DataType: schema.String},\n\t}\n\n\tfor _, f := range fields {\n\t\tcheckSchemaField(t, cropSchema, &f, func(f *schema.Field) {\n\t\t\tif f.Name != \"Ignored\" {\n\t\t\t\tf.Creatable = true\n\t\t\t\tf.Updatable = true\n\t\t\t\tf.Readable = true\n\t\t\t}\n\t\t})\n\t}\n}\n\ntype CustomizedNamingStrategy struct {\n\tschema.NamingStrategy\n}\n\nfunc (ns CustomizedNamingStrategy) ColumnName(table, column string) string {\n\tbaseColumnName := ns.NamingStrategy.ColumnName(table, column)\n\n\tif table == \"\" {\n\t\treturn baseColumnName\n\t}\n\n\ts := strings.Split(table, \"_\")\n\n\tvar prefix string\n\tswitch len(s) {\n\tcase 1:\n\t\tprefix = s[0][:3]\n\tcase 2:\n\t\tprefix = s[0][:1] + s[1][:2]\n\tdefault:\n\t\tprefix = s[0][:1] + s[1][:1] + s[2][:1]\n\t}\n\treturn prefix + \"_\" + baseColumnName\n}\n\nfunc TestEmbeddedStructForCustomizedNamingStrategy(t *testing.T) {\n\ttype CorpBase struct {\n\t\tgorm.Model\n\t\tOwnerID string\n\t}\n\n\ttype Company struct {\n\t\tID      int\n\t\tOwnerID int\n\t\tName    string\n\t\tIgnored string `gorm:\"-\"`\n\t}\n\n\ttype Corp struct {\n\t\tCorpBase\n\t\tBase Company `gorm:\"embedded;embeddedPrefix:company_\"`\n\t}\n\n\tcropSchema, err := schema.Parse(&Corp{}, &sync.Map{}, CustomizedNamingStrategy{schema.NamingStrategy{}})\n\tif err != nil {\n\t\tt.Fatalf(\"failed to parse embedded struct with primary key, got error %v\", err)\n\t}\n\n\tfields := []schema.Field{\n\t\t{Name: \"ID\", DBName: \"cor_id\", BindNames: []string{\"CorpBase\", \"Model\", \"ID\"}, DataType: schema.Uint, PrimaryKey: true, Size: 64, HasDefaultValue: true, AutoIncrement: true, TagSettings: map[string]string{\"PRIMARYKEY\": \"PRIMARYKEY\"}},\n\t\t{Name: \"ID\", DBName: \"company_cor_id\", BindNames: []string{\"Base\", \"ID\"}, DataType: schema.Int, Size: 64, TagSettings: map[string]string{\"EMBEDDED\": \"EMBEDDED\", \"EMBEDDEDPREFIX\": \"company_\"}},\n\t\t{Name: \"Name\", DBName: \"company_cor_name\", BindNames: []string{\"Base\", \"Name\"}, DataType: schema.String, TagSettings: map[string]string{\"EMBEDDED\": \"EMBEDDED\", \"EMBEDDEDPREFIX\": \"company_\"}},\n\t\t{Name: \"Ignored\", BindNames: []string{\"Base\", \"Ignored\"}, TagSettings: map[string]string{\"-\": \"-\", \"EMBEDDED\": \"EMBEDDED\", \"EMBEDDEDPREFIX\": \"company_\"}},\n\t\t{Name: \"OwnerID\", DBName: \"company_cor_owner_id\", BindNames: []string{\"Base\", \"OwnerID\"}, DataType: schema.Int, Size: 64, TagSettings: map[string]string{\"EMBEDDED\": \"EMBEDDED\", \"EMBEDDEDPREFIX\": \"company_\"}},\n\t\t{Name: \"OwnerID\", DBName: \"cor_owner_id\", BindNames: []string{\"CorpBase\", \"OwnerID\"}, DataType: schema.String},\n\t}\n\n\tfor _, f := range fields {\n\t\tcheckSchemaField(t, cropSchema, &f, func(f *schema.Field) {\n\t\t\tif f.Name != \"Ignored\" {\n\t\t\t\tf.Creatable = true\n\t\t\t\tf.Updatable = true\n\t\t\t\tf.Readable = true\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestCompositePrimaryKeyWithAutoIncrement(t *testing.T) {\n\ttype Product struct {\n\t\tProductID    uint `gorm:\"primaryKey;autoIncrement\"`\n\t\tLanguageCode uint `gorm:\"primaryKey\"`\n\t\tCode         string\n\t\tName         string\n\t}\n\ttype ProductNonAutoIncrement struct {\n\t\tProductID    uint `gorm:\"primaryKey;autoIncrement:false\"`\n\t\tLanguageCode uint `gorm:\"primaryKey\"`\n\t\tCode         string\n\t\tName         string\n\t}\n\n\tproduct, err := schema.Parse(&Product{}, &sync.Map{}, schema.NamingStrategy{})\n\tif err != nil {\n\t\tt.Fatalf(\"failed to parse product struct with composite primary key, got error %v\", err)\n\t}\n\n\tprioritizedPrimaryField := schema.Field{\n\t\tName: \"ProductID\", DBName: \"product_id\", BindNames: []string{\"ProductID\"}, DataType: schema.Uint, PrimaryKey: true, Size: 64, HasDefaultValue: true, AutoIncrement: true, TagSettings: map[string]string{\"PRIMARYKEY\": \"PRIMARYKEY\", \"AUTOINCREMENT\": \"AUTOINCREMENT\"},\n\t}\n\n\tproduct.Fields = []*schema.Field{product.PrioritizedPrimaryField}\n\n\tcheckSchemaField(t, product, &prioritizedPrimaryField, func(f *schema.Field) {\n\t\tf.Creatable = true\n\t\tf.Updatable = true\n\t\tf.Readable = true\n\t})\n\n\tproductNonAutoIncrement, err := schema.Parse(&ProductNonAutoIncrement{}, &sync.Map{}, schema.NamingStrategy{})\n\tif err != nil {\n\t\tt.Fatalf(\"failed to parse productNonAutoIncrement struct with composite primary key, got error %v\", err)\n\t}\n\n\tif productNonAutoIncrement.PrioritizedPrimaryField != nil {\n\t\tt.Fatalf(\"PrioritizedPrimaryField of non autoincrement composite key should be nil\")\n\t}\n}\n"
  },
  {
    "path": "schema/serializer.go",
    "content": "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\"fmt\"\n\t\"math\"\n\t\"reflect\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n)\n\nvar serializerMap = sync.Map{}\n\n// RegisterSerializer register serializer\nfunc RegisterSerializer(name string, serializer SerializerInterface) {\n\tserializerMap.Store(strings.ToLower(name), serializer)\n}\n\n// GetSerializer get serializer\nfunc GetSerializer(name string) (serializer SerializerInterface, ok bool) {\n\tv, ok := serializerMap.Load(strings.ToLower(name))\n\tif ok {\n\t\tserializer, ok = v.(SerializerInterface)\n\t}\n\treturn serializer, ok\n}\n\nfunc init() {\n\tRegisterSerializer(\"json\", JSONSerializer{})\n\tRegisterSerializer(\"unixtime\", UnixSecondSerializer{})\n\tRegisterSerializer(\"gob\", GobSerializer{})\n}\n\n// Serializer field value serializer\ntype serializer struct {\n\tField           *Field\n\tSerializer      SerializerInterface\n\tSerializeValuer SerializerValuerInterface\n\tDestination     reflect.Value\n\tContext         context.Context\n\tvalue           interface{}\n\tfieldValue      interface{}\n}\n\n// Scan implements sql.Scanner interface\nfunc (s *serializer) Scan(value interface{}) error {\n\ts.value = value\n\treturn nil\n}\n\n// Value implements driver.Valuer interface\nfunc (s serializer) Value() (driver.Value, error) {\n\treturn s.SerializeValuer.Value(s.Context, s.Field, s.Destination, s.fieldValue)\n}\n\n// SerializerInterface serializer interface\ntype SerializerInterface interface {\n\tScan(ctx context.Context, field *Field, dst reflect.Value, dbValue interface{}) error\n\tSerializerValuerInterface\n}\n\n// SerializerValuerInterface serializer valuer interface\ntype SerializerValuerInterface interface {\n\tValue(ctx context.Context, field *Field, dst reflect.Value, fieldValue interface{}) (interface{}, error)\n}\n\n// JSONSerializer json serializer\ntype JSONSerializer struct{}\n\n// Scan implements serializer interface\nfunc (JSONSerializer) Scan(ctx context.Context, field *Field, dst reflect.Value, dbValue interface{}) (err error) {\n\tfieldValue := reflect.New(field.FieldType)\n\n\tif dbValue != nil {\n\t\tvar bytes []byte\n\t\tswitch v := dbValue.(type) {\n\t\tcase []byte:\n\t\t\tbytes = v\n\t\tcase string:\n\t\t\tbytes = []byte(v)\n\t\tdefault:\n\t\t\tbytes, err = json.Marshal(v)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\n\t\tif len(bytes) > 0 {\n\t\t\terr = json.Unmarshal(bytes, fieldValue.Interface())\n\t\t}\n\t}\n\n\tfield.ReflectValueOf(ctx, dst).Set(fieldValue.Elem())\n\treturn\n}\n\n// Value implements serializer interface\nfunc (JSONSerializer) Value(ctx context.Context, field *Field, dst reflect.Value, fieldValue interface{}) (interface{}, error) {\n\tresult, err := json.Marshal(fieldValue)\n\tif string(result) == \"null\" {\n\t\tif field.TagSettings[\"NOT NULL\"] != \"\" {\n\t\t\treturn \"\", nil\n\t\t}\n\t\treturn nil, err\n\t}\n\treturn string(result), err\n}\n\n// UnixSecondSerializer json serializer\ntype UnixSecondSerializer struct{}\n\n// Scan implements serializer interface\nfunc (UnixSecondSerializer) Scan(ctx context.Context, field *Field, dst reflect.Value, dbValue interface{}) (err error) {\n\tt := sql.NullTime{}\n\tif err = t.Scan(dbValue); err == nil && t.Valid {\n\t\terr = field.Set(ctx, dst, t.Time.Unix())\n\t}\n\n\treturn\n}\n\n// Value implements serializer interface\nfunc (UnixSecondSerializer) Value(ctx context.Context, field *Field, dst reflect.Value, fieldValue interface{}) (result interface{}, err error) {\n\trv := reflect.ValueOf(fieldValue)\n\tswitch fieldValue.(type) {\n\tcase int, int8, int16, int32, int64:\n\t\tresult = time.Unix(rv.Int(), 0).UTC()\n\tcase uint, uint8, uint16, uint32, uint64:\n\t\tif uv := rv.Uint(); uv > math.MaxInt64 {\n\t\t\terr = fmt.Errorf(\"integer overflow conversion uint64(%d) -> int64\", uv)\n\t\t} else {\n\t\t\tresult = time.Unix(int64(uv), 0).UTC() //nolint:gosec\n\t\t}\n\tcase *int, *int8, *int16, *int32, *int64:\n\t\tif rv.IsZero() {\n\t\t\treturn nil, nil\n\t\t}\n\t\tresult = time.Unix(rv.Elem().Int(), 0).UTC()\n\tcase *uint, *uint8, *uint16, *uint32, *uint64:\n\t\tif rv.IsZero() {\n\t\t\treturn nil, nil\n\t\t}\n\t\tif uv := rv.Elem().Uint(); uv > math.MaxInt64 {\n\t\t\terr = fmt.Errorf(\"integer overflow conversion uint64(%d) -> int64\", uv)\n\t\t} else {\n\t\t\tresult = time.Unix(int64(uv), 0).UTC() //nolint:gosec\n\t\t}\n\tdefault:\n\t\terr = fmt.Errorf(\"invalid field type %#v for UnixSecondSerializer, only int, uint supported\", fieldValue)\n\t}\n\treturn\n}\n\n// GobSerializer gob serializer\ntype GobSerializer struct{}\n\n// Scan implements serializer interface\nfunc (GobSerializer) Scan(ctx context.Context, field *Field, dst reflect.Value, dbValue interface{}) (err error) {\n\tfieldValue := reflect.New(field.FieldType)\n\n\tif dbValue != nil {\n\t\tvar bytesValue []byte\n\t\tswitch v := dbValue.(type) {\n\t\tcase []byte:\n\t\t\tbytesValue = v\n\t\tdefault:\n\t\t\treturn fmt.Errorf(\"failed to unmarshal gob value: %#v\", dbValue)\n\t\t}\n\t\tif len(bytesValue) > 0 {\n\t\t\tdecoder := gob.NewDecoder(bytes.NewBuffer(bytesValue))\n\t\t\terr = decoder.Decode(fieldValue.Interface())\n\t\t}\n\t}\n\tfield.ReflectValueOf(ctx, dst).Set(fieldValue.Elem())\n\treturn\n}\n\n// Value implements serializer interface\nfunc (GobSerializer) Value(ctx context.Context, field *Field, dst reflect.Value, fieldValue interface{}) (interface{}, error) {\n\tbuf := new(bytes.Buffer)\n\terr := gob.NewEncoder(buf).Encode(fieldValue)\n\treturn buf.Bytes(), err\n}\n"
  },
  {
    "path": "schema/serializer_test.go",
    "content": "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 *testing.T) {\n\tvar (\n\t\tintValue      = math.MaxInt64\n\t\tint8Value     = int8(math.MaxInt8)\n\t\tint16Value    = int16(math.MaxInt16)\n\t\tint32Value    = int32(math.MaxInt32)\n\t\tint64Value    = int64(math.MaxInt64)\n\t\tuintValue     = uint(math.MaxInt64)\n\t\tuint8Value    = uint8(math.MaxUint8)\n\t\tuint16Value   = uint16(math.MaxUint16)\n\t\tuint32Value   = uint32(math.MaxUint32)\n\t\tuint64Value   = uint64(math.MaxInt64)\n\t\tmaxInt64Plus1 = uint64(math.MaxInt64 + 1)\n\n\t\tintPtrValue      = &intValue\n\t\tint8PtrValue     = &int8Value\n\t\tint16PtrValue    = &int16Value\n\t\tint32PtrValue    = &int32Value\n\t\tint64PtrValue    = &int64Value\n\t\tuintPtrValue     = &uintValue\n\t\tuint8PtrValue    = &uint8Value\n\t\tuint16PtrValue   = &uint16Value\n\t\tuint32PtrValue   = &uint32Value\n\t\tuint64PtrValue   = &uint64Value\n\t\tmaxInt64Plus1Ptr = &maxInt64Plus1\n\t)\n\ttests := []struct {\n\t\tname    string\n\t\tvalue   interface{}\n\t\twant    interface{}\n\t\twantErr bool\n\t}{\n\t\t{\n\t\t\tname:    \"int\",\n\t\t\tvalue:   intValue,\n\t\t\twant:    time.Unix(int64(intValue), 0).UTC(),\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname:    \"int8\",\n\t\t\tvalue:   int8Value,\n\t\t\twant:    time.Unix(int64(int8Value), 0).UTC(),\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname:    \"int16\",\n\t\t\tvalue:   int16Value,\n\t\t\twant:    time.Unix(int64(int16Value), 0).UTC(),\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname:    \"int32\",\n\t\t\tvalue:   int32Value,\n\t\t\twant:    time.Unix(int64(int32Value), 0).UTC(),\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname:    \"int64\",\n\t\t\tvalue:   int64Value,\n\t\t\twant:    time.Unix(int64Value, 0).UTC(),\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname:    \"uint\",\n\t\t\tvalue:   uintValue,\n\t\t\twant:    time.Unix(int64(uintValue), 0).UTC(), //nolint:gosec\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname:    \"uint8\",\n\t\t\tvalue:   uint8Value,\n\t\t\twant:    time.Unix(int64(uint8Value), 0).UTC(),\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname:    \"uint16\",\n\t\t\tvalue:   uint16Value,\n\t\t\twant:    time.Unix(int64(uint16Value), 0).UTC(),\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname:    \"uint32\",\n\t\t\tvalue:   uint32Value,\n\t\t\twant:    time.Unix(int64(uint32Value), 0).UTC(),\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname:    \"uint64\",\n\t\t\tvalue:   uint64Value,\n\t\t\twant:    time.Unix(int64(uint64Value), 0).UTC(), //nolint:gosec\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname:    \"maxInt64+1\",\n\t\t\tvalue:   maxInt64Plus1,\n\t\t\twant:    nil,\n\t\t\twantErr: true,\n\t\t},\n\t\t{\n\t\t\tname:    \"*int\",\n\t\t\tvalue:   intPtrValue,\n\t\t\twant:    time.Unix(int64(*intPtrValue), 0).UTC(),\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname:    \"*int8\",\n\t\t\tvalue:   int8PtrValue,\n\t\t\twant:    time.Unix(int64(*int8PtrValue), 0).UTC(),\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname:    \"*int16\",\n\t\t\tvalue:   int16PtrValue,\n\t\t\twant:    time.Unix(int64(*int16PtrValue), 0).UTC(),\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname:    \"*int32\",\n\t\t\tvalue:   int32PtrValue,\n\t\t\twant:    time.Unix(int64(*int32PtrValue), 0).UTC(),\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname:    \"*int64\",\n\t\t\tvalue:   int64PtrValue,\n\t\t\twant:    time.Unix(*int64PtrValue, 0).UTC(),\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname:    \"*uint\",\n\t\t\tvalue:   uintPtrValue,\n\t\t\twant:    time.Unix(int64(*uintPtrValue), 0).UTC(), //nolint:gosec\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname:    \"*uint8\",\n\t\t\tvalue:   uint8PtrValue,\n\t\t\twant:    time.Unix(int64(*uint8PtrValue), 0).UTC(),\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname:    \"*uint16\",\n\t\t\tvalue:   uint16PtrValue,\n\t\t\twant:    time.Unix(int64(*uint16PtrValue), 0).UTC(),\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname:    \"*uint32\",\n\t\t\tvalue:   uint32PtrValue,\n\t\t\twant:    time.Unix(int64(*uint32PtrValue), 0).UTC(),\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname:    \"*uint64\",\n\t\t\tvalue:   uint64PtrValue,\n\t\t\twant:    time.Unix(int64(*uint64PtrValue), 0).UTC(), //nolint:gosec\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname:    \"pointer to maxInt64+1\",\n\t\t\tvalue:   maxInt64Plus1Ptr,\n\t\t\twant:    nil,\n\t\t\twantErr: true,\n\t\t},\n\t\t{\n\t\t\tname:    \"nil pointer\",\n\t\t\tvalue:   (*int)(nil),\n\t\t\twant:    nil,\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname:    \"invalid type\",\n\t\t\tvalue:   \"invalid\",\n\t\t\twant:    nil,\n\t\t\twantErr: true,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tgot, err := UnixSecondSerializer{}.Value(context.Background(), nil, reflect.Value{}, tt.value)\n\t\t\tif (err != nil) != tt.wantErr {\n\t\t\t\tt.Fatalf(\"UnixSecondSerializer.Value() error = %v, wantErr %v\", err, tt.wantErr)\n\t\t\t}\n\t\t\tif err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif tt.want == nil && got == nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif tt.want == nil {\n\t\t\t\tt.Fatalf(\"UnixSecondSerializer.Value() = %v, want nil\", got)\n\t\t\t}\n\t\t\tif got == nil {\n\t\t\t\tt.Fatalf(\"UnixSecondSerializer.Value() = nil, want %v\", tt.want)\n\t\t\t}\n\t\t\tif gotTime, ok := got.(time.Time); !ok {\n\t\t\t\tt.Errorf(\"UnixSecondSerializer.Value() returned %T, expected time.Time\", got)\n\t\t\t} else if !tt.want.(time.Time).Equal(gotTime) {\n\t\t\t\tt.Errorf(\"UnixSecondSerializer.Value() = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "schema/utils.go",
    "content": "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\"\n)\n\nvar embeddedCacheKey = \"embedded_cache_store\"\n\nfunc ParseTagSetting(str string, sep string) map[string]string {\n\tsettings := map[string]string{}\n\tnames := strings.Split(str, sep)\n\n\tvar parsedNames []string\n\tfor i := 0; i < len(names); i++ {\n\t\ts := names[i]\n\t\tfor strings.HasSuffix(s, \"\\\\\") && i+1 < len(names) {\n\t\t\ti++\n\t\t\ts = s[:len(s)-1] + sep + names[i]\n\t\t}\n\t\tparsedNames = append(parsedNames, s)\n\t}\n\n\tfor _, tag := range parsedNames {\n\t\tvalues := strings.Split(tag, \":\")\n\t\tk := strings.TrimSpace(strings.ToUpper(values[0]))\n\t\tif len(values) >= 2 {\n\t\t\tval := strings.Join(values[1:], \":\")\n\t\t\tval = strings.ReplaceAll(val, `\\\"`, `\"`)\n\t\t\tsettings[k] = val\n\t\t} else if k != \"\" {\n\t\t\tsettings[k] = k\n\t\t}\n\t}\n\n\treturn settings\n}\n\nfunc toColumns(val string) (results []string) {\n\tif val != \"\" {\n\t\tfor _, v := range strings.Split(val, \",\") {\n\t\t\tresults = append(results, strings.TrimSpace(v))\n\t\t}\n\t}\n\treturn\n}\n\nfunc removeSettingFromTag(tag reflect.StructTag, names ...string) reflect.StructTag {\n\tfor _, name := range names {\n\t\ttag = reflect.StructTag(regexp.MustCompile(`(?i)(gorm:.*?)(`+name+`(:.*?)?)(;|(\"))`).ReplaceAllString(string(tag), \"${1}${5}\"))\n\t}\n\treturn tag\n}\n\nfunc appendSettingFromTag(tag reflect.StructTag, value string) reflect.StructTag {\n\tt := tag.Get(\"gorm\")\n\tif strings.Contains(t, value) {\n\t\treturn tag\n\t}\n\treturn reflect.StructTag(fmt.Sprintf(`gorm:\"%s;%s\"`, value, t))\n}\n\n// GetRelationsValues get relations's values from a reflect value\nfunc GetRelationsValues(ctx context.Context, reflectValue reflect.Value, rels []*Relationship) (reflectResults reflect.Value) {\n\tfor _, rel := range rels {\n\t\treflectResults = reflect.MakeSlice(reflect.SliceOf(reflect.PointerTo(rel.FieldSchema.ModelType)), 0, 1)\n\n\t\tappendToResults := func(value reflect.Value) {\n\t\t\tif _, isZero := rel.Field.ValueOf(ctx, value); !isZero {\n\t\t\t\tresult := reflect.Indirect(rel.Field.ReflectValueOf(ctx, value))\n\t\t\t\tswitch result.Kind() {\n\t\t\t\tcase reflect.Struct:\n\t\t\t\t\treflectResults = reflect.Append(reflectResults, result.Addr())\n\t\t\t\tcase reflect.Slice, reflect.Array:\n\t\t\t\t\tfor i := 0; i < result.Len(); i++ {\n\t\t\t\t\t\tif elem := result.Index(i); elem.Kind() == reflect.Ptr {\n\t\t\t\t\t\t\treflectResults = reflect.Append(reflectResults, elem)\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\treflectResults = reflect.Append(reflectResults, elem.Addr())\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tswitch reflectValue.Kind() {\n\t\tcase reflect.Struct:\n\t\t\tappendToResults(reflectValue)\n\t\tcase reflect.Slice:\n\t\t\tfor i := 0; i < reflectValue.Len(); i++ {\n\t\t\t\tappendToResults(reflectValue.Index(i))\n\t\t\t}\n\t\t}\n\n\t\treflectValue = reflectResults\n\t}\n\n\treturn\n}\n\n// GetIdentityFieldValuesMap get identity map from fields\nfunc GetIdentityFieldValuesMap(ctx context.Context, reflectValue reflect.Value, fields []*Field) (map[string][]reflect.Value, [][]interface{}) {\n\tvar (\n\t\tresults       = [][]interface{}{}\n\t\tdataResults   = map[string][]reflect.Value{}\n\t\tloaded        = map[interface{}]bool{}\n\t\tnotZero, zero bool\n\t)\n\n\tif reflectValue.Kind() == reflect.Ptr ||\n\t\treflectValue.Kind() == reflect.Interface {\n\t\treflectValue = reflectValue.Elem()\n\t}\n\n\tswitch reflectValue.Kind() {\n\tcase reflect.Map:\n\t\tresults = [][]interface{}{make([]interface{}, len(fields))}\n\t\tfor idx, field := range fields {\n\t\t\tmapValue := reflectValue.MapIndex(reflect.ValueOf(field.DBName))\n\t\t\tif mapValue.IsZero() {\n\t\t\t\tmapValue = reflectValue.MapIndex(reflect.ValueOf(field.Name))\n\t\t\t}\n\t\t\tresults[0][idx] = mapValue.Interface()\n\t\t}\n\n\t\tdataResults[utils.ToStringKey(results[0]...)] = []reflect.Value{reflectValue}\n\tcase reflect.Struct:\n\t\tresults = [][]interface{}{make([]interface{}, len(fields))}\n\n\t\tfor idx, field := range fields {\n\t\t\tresults[0][idx], zero = field.ValueOf(ctx, reflectValue)\n\t\t\tnotZero = notZero || !zero\n\t\t}\n\n\t\tif !notZero {\n\t\t\treturn nil, nil\n\t\t}\n\n\t\tdataResults[utils.ToStringKey(results[0]...)] = []reflect.Value{reflectValue}\n\tcase reflect.Slice, reflect.Array:\n\t\tfor i := 0; i < reflectValue.Len(); i++ {\n\t\t\telem := reflectValue.Index(i)\n\t\t\telemKey := elem.Interface()\n\t\t\tif elem.Kind() != reflect.Ptr && elem.CanAddr() {\n\t\t\t\telemKey = elem.Addr().Interface()\n\t\t\t}\n\n\t\t\tif _, ok := loaded[elemKey]; ok {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tloaded[elemKey] = true\n\n\t\t\tfieldValues := make([]interface{}, len(fields))\n\t\t\tnotZero = false\n\t\t\tfor idx, field := range fields {\n\t\t\t\tfieldValues[idx], zero = field.ValueOf(ctx, elem)\n\t\t\t\tnotZero = notZero || !zero\n\t\t\t}\n\n\t\t\tif notZero {\n\t\t\t\tdataKey := utils.ToStringKey(fieldValues...)\n\t\t\t\tif _, ok := dataResults[dataKey]; !ok {\n\t\t\t\t\tresults = append(results, fieldValues)\n\t\t\t\t\tdataResults[dataKey] = []reflect.Value{elem}\n\t\t\t\t} else {\n\t\t\t\t\tdataResults[dataKey] = append(dataResults[dataKey], elem)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn dataResults, results\n}\n\n// GetIdentityFieldValuesMapFromValues get identity map from fields\nfunc GetIdentityFieldValuesMapFromValues(ctx context.Context, values []interface{}, fields []*Field) (map[string][]reflect.Value, [][]interface{}) {\n\tresultsMap := map[string][]reflect.Value{}\n\tresults := [][]interface{}{}\n\n\tfor _, v := range values {\n\t\trm, rs := GetIdentityFieldValuesMap(ctx, reflect.Indirect(reflect.ValueOf(v)), fields)\n\t\tfor k, v := range rm {\n\t\t\tresultsMap[k] = append(resultsMap[k], v...)\n\t\t}\n\t\tresults = append(results, rs...)\n\t}\n\treturn resultsMap, results\n}\n\n// ToQueryValues to query values\nfunc ToQueryValues(table string, foreignKeys []string, foreignValues [][]interface{}) (interface{}, []interface{}) {\n\tqueryValues := make([]interface{}, len(foreignValues))\n\tif len(foreignKeys) == 1 {\n\t\tfor idx, r := range foreignValues {\n\t\t\tqueryValues[idx] = r[0]\n\t\t}\n\n\t\treturn clause.Column{Table: table, Name: foreignKeys[0]}, queryValues\n\t}\n\n\tcolumns := make([]clause.Column, len(foreignKeys))\n\tfor idx, key := range foreignKeys {\n\t\tcolumns[idx] = clause.Column{Table: table, Name: key}\n\t}\n\n\tfor idx, r := range foreignValues {\n\t\tqueryValues[idx] = r\n\t}\n\n\treturn columns, queryValues\n}\n\ntype embeddedNamer struct {\n\tTable string\n\tNamer\n}\n"
  },
  {
    "path": "schema/utils_test.go",
    "content": "package schema\n\nimport (\n\t\"reflect\"\n\t\"testing\"\n)\n\nfunc TestRemoveSettingFromTag(t *testing.T) {\n\ttags := map[string]string{\n\t\t`gorm:\"before:value;column:db;after:value\" other:\"before:value;column:db;after:value\"`:  `gorm:\"before:value;after:value\" other:\"before:value;column:db;after:value\"`,\n\t\t`gorm:\"before:value;column:db;\" other:\"before:value;column:db;after:value\"`:             `gorm:\"before:value;\" other:\"before:value;column:db;after:value\"`,\n\t\t`gorm:\"before:value;column:db\" other:\"before:value;column:db;after:value\"`:              `gorm:\"before:value;\" other:\"before:value;column:db;after:value\"`,\n\t\t`gorm:\"column:db\" other:\"before:value;column:db;after:value\"`:                           `gorm:\"\" other:\"before:value;column:db;after:value\"`,\n\t\t`gorm:\"before:value;column:db ;after:value\" other:\"before:value;column:db;after:value\"`: `gorm:\"before:value;after:value\" other:\"before:value;column:db;after:value\"`,\n\t\t`gorm:\"before:value;column:db; after:value\" other:\"before:value;column:db;after:value\"`: `gorm:\"before:value; after:value\" other:\"before:value;column:db;after:value\"`,\n\t\t`gorm:\"before:value;column; after:value\" other:\"before:value;column:db;after:value\"`:    `gorm:\"before:value; after:value\" other:\"before:value;column:db;after:value\"`,\n\t}\n\n\tfor k, v := range tags {\n\t\tif string(removeSettingFromTag(reflect.StructTag(k), \"column\")) != v {\n\t\t\tt.Errorf(\"%v after removeSettingFromTag should equal %v, but got %v\", k, v, removeSettingFromTag(reflect.StructTag(k), \"column\"))\n\t\t}\n\t}\n}\n\nfunc TestParseTagSettingWithDoubleQuoteEscape(t *testing.T) {\n\ttag := `gorm:\"expression:to_tsvector('english', \\\"Name\\\")\"`\n\tsettings := ParseTagSetting(reflect.StructTag(tag).Get(\"gorm\"), \";\")\n\tif v, ok := settings[\"EXPRESSION\"]; !ok || v != `to_tsvector('english', \"Name\")` {\n\t\tt.Errorf(\"ParseTagSetting did not handle escaped double quotes correctly: got %#v\", v)\n\t}\n}\n"
  },
  {
    "path": "soft_delete.go",
    "content": "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\"gorm.io/gorm/clause\"\n\t\"gorm.io/gorm/schema\"\n)\n\ntype DeletedAt sql.NullTime\n\n// Scan implements the Scanner interface.\nfunc (n *DeletedAt) Scan(value interface{}) error {\n\treturn (*sql.NullTime)(n).Scan(value)\n}\n\n// Value implements the driver Valuer interface.\nfunc (n DeletedAt) Value() (driver.Value, error) {\n\tif !n.Valid {\n\t\treturn nil, nil\n\t}\n\treturn n.Time, nil\n}\n\nfunc (n DeletedAt) MarshalJSON() ([]byte, error) {\n\tif n.Valid {\n\t\treturn json.Marshal(n.Time)\n\t}\n\treturn json.Marshal(nil)\n}\n\nfunc (n *DeletedAt) UnmarshalJSON(b []byte) error {\n\tif string(b) == \"null\" {\n\t\tn.Valid = false\n\t\treturn nil\n\t}\n\terr := json.Unmarshal(b, &n.Time)\n\tif err == nil {\n\t\tn.Valid = true\n\t}\n\treturn err\n}\n\nfunc (DeletedAt) QueryClauses(f *schema.Field) []clause.Interface {\n\treturn []clause.Interface{SoftDeleteQueryClause{Field: f, ZeroValue: parseZeroValueTag(f)}}\n}\n\nfunc parseZeroValueTag(f *schema.Field) sql.NullString {\n\tif v, ok := f.TagSettings[\"ZEROVALUE\"]; ok {\n\t\tif _, err := now.Parse(v); err == nil {\n\t\t\treturn sql.NullString{String: v, Valid: true}\n\t\t}\n\t}\n\treturn sql.NullString{Valid: false}\n}\n\ntype SoftDeleteQueryClause struct {\n\tZeroValue sql.NullString\n\tField     *schema.Field\n}\n\nfunc (sd SoftDeleteQueryClause) Name() string {\n\treturn \"\"\n}\n\nfunc (sd SoftDeleteQueryClause) Build(clause.Builder) {\n}\n\nfunc (sd SoftDeleteQueryClause) MergeClause(*clause.Clause) {\n}\n\nfunc (sd SoftDeleteQueryClause) ModifyStatement(stmt *Statement) {\n\tif _, ok := stmt.Clauses[\"soft_delete_enabled\"]; !ok && !stmt.Statement.Unscoped {\n\t\tif c, ok := stmt.Clauses[\"WHERE\"]; ok {\n\t\t\tif where, ok := c.Expression.(clause.Where); ok && len(where.Exprs) >= 1 {\n\t\t\t\tfor _, expr := range where.Exprs {\n\t\t\t\t\tif orCond, ok := expr.(clause.OrConditions); ok && len(orCond.Exprs) == 1 {\n\t\t\t\t\t\twhere.Exprs = []clause.Expression{clause.And(where.Exprs...)}\n\t\t\t\t\t\tc.Expression = where\n\t\t\t\t\t\tstmt.Clauses[\"WHERE\"] = c\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tstmt.AddClause(clause.Where{Exprs: []clause.Expression{\n\t\t\tclause.Eq{Column: clause.Column{Table: clause.CurrentTable, Name: sd.Field.DBName}, Value: sd.ZeroValue},\n\t\t}})\n\t\tstmt.Clauses[\"soft_delete_enabled\"] = clause.Clause{}\n\t}\n}\n\nfunc (DeletedAt) UpdateClauses(f *schema.Field) []clause.Interface {\n\treturn []clause.Interface{SoftDeleteUpdateClause{Field: f, ZeroValue: parseZeroValueTag(f)}}\n}\n\ntype SoftDeleteUpdateClause struct {\n\tZeroValue sql.NullString\n\tField     *schema.Field\n}\n\nfunc (sd SoftDeleteUpdateClause) Name() string {\n\treturn \"\"\n}\n\nfunc (sd SoftDeleteUpdateClause) Build(clause.Builder) {\n}\n\nfunc (sd SoftDeleteUpdateClause) MergeClause(*clause.Clause) {\n}\n\nfunc (sd SoftDeleteUpdateClause) ModifyStatement(stmt *Statement) {\n\tif stmt.SQL.Len() == 0 && !stmt.Statement.Unscoped {\n\t\tSoftDeleteQueryClause(sd).ModifyStatement(stmt)\n\t}\n}\n\nfunc (DeletedAt) DeleteClauses(f *schema.Field) []clause.Interface {\n\treturn []clause.Interface{SoftDeleteDeleteClause{Field: f, ZeroValue: parseZeroValueTag(f)}}\n}\n\ntype SoftDeleteDeleteClause struct {\n\tZeroValue sql.NullString\n\tField     *schema.Field\n}\n\nfunc (sd SoftDeleteDeleteClause) Name() string {\n\treturn \"\"\n}\n\nfunc (sd SoftDeleteDeleteClause) Build(clause.Builder) {\n}\n\nfunc (sd SoftDeleteDeleteClause) MergeClause(*clause.Clause) {\n}\n\nfunc (sd SoftDeleteDeleteClause) ModifyStatement(stmt *Statement) {\n\tif stmt.SQL.Len() == 0 && !stmt.Statement.Unscoped {\n\t\tcurTime := stmt.DB.NowFunc()\n\t\tstmt.AddClause(clause.Set{{Column: clause.Column{Name: sd.Field.DBName}, Value: curTime}})\n\t\tstmt.SetColumn(sd.Field.DBName, curTime, true)\n\n\t\tif stmt.Schema != nil {\n\t\t\t_, queryValues := schema.GetIdentityFieldValuesMap(stmt.Context, stmt.ReflectValue, stmt.Schema.PrimaryFields)\n\t\t\tcolumn, values := schema.ToQueryValues(stmt.Table, stmt.Schema.PrimaryFieldDBNames, queryValues)\n\n\t\t\tif len(values) > 0 {\n\t\t\t\tstmt.AddClause(clause.Where{Exprs: []clause.Expression{clause.IN{Column: column, Values: values}}})\n\t\t\t}\n\n\t\t\tif stmt.ReflectValue.CanAddr() && stmt.Dest != stmt.Model && stmt.Model != nil {\n\t\t\t\t_, queryValues = schema.GetIdentityFieldValuesMap(stmt.Context, reflect.ValueOf(stmt.Model), stmt.Schema.PrimaryFields)\n\t\t\t\tcolumn, values = schema.ToQueryValues(stmt.Table, stmt.Schema.PrimaryFieldDBNames, queryValues)\n\n\t\t\t\tif len(values) > 0 {\n\t\t\t\t\tstmt.AddClause(clause.Where{Exprs: []clause.Expression{clause.IN{Column: column, Values: values}}})\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tSoftDeleteQueryClause(sd).ModifyStatement(stmt)\n\t\tstmt.AddClauseIfNotExists(clause.Update{})\n\t\tstmt.Build(stmt.DB.Callback().Update().Clauses...)\n\t}\n}\n"
  },
  {
    "path": "statement.go",
    "content": "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\t\"strings\"\n\t\"sync\"\n\n\t\"gorm.io/gorm/clause\"\n\t\"gorm.io/gorm/logger\"\n\t\"gorm.io/gorm/schema\"\n\t\"gorm.io/gorm/utils\"\n)\n\n// Statement statement\ntype Statement struct {\n\t*DB\n\tTableExpr            *clause.Expr\n\tTable                string\n\tModel                interface{}\n\tUnscoped             bool\n\tDest                 interface{}\n\tReflectValue         reflect.Value\n\tClauses              map[string]clause.Clause\n\tBuildClauses         []string\n\tDistinct             bool\n\tSelects              []string          // selected columns\n\tOmits                []string          // omit columns\n\tColumnMapping        map[string]string // map columns\n\tJoins                []join\n\tPreloads             map[string][]interface{}\n\tSettings             sync.Map\n\tConnPool             ConnPool\n\tSchema               *schema.Schema\n\tContext              context.Context\n\tRaiseErrorOnNotFound bool\n\tSkipHooks            bool\n\tSQL                  strings.Builder\n\tVars                 []interface{}\n\tCurDestIndex         int\n\tattrs                []interface{}\n\tassigns              []interface{}\n\tscopes               []func(*DB) *DB\n\tResult               *result\n}\n\ntype join struct {\n\tName       string\n\tAlias      string\n\tConds      []interface{}\n\tOn         *clause.Where\n\tSelects    []string\n\tOmits      []string\n\tExpression clause.Expression\n\tJoinType   clause.JoinType\n}\n\n// StatementModifier statement modifier interface\ntype StatementModifier interface {\n\tModifyStatement(*Statement)\n}\n\n// WriteString write string\nfunc (stmt *Statement) WriteString(str string) (int, error) {\n\treturn stmt.SQL.WriteString(str)\n}\n\n// WriteByte write byte\nfunc (stmt *Statement) WriteByte(c byte) error {\n\treturn stmt.SQL.WriteByte(c)\n}\n\n// WriteQuoted write quoted value\nfunc (stmt *Statement) WriteQuoted(value interface{}) {\n\tstmt.QuoteTo(&stmt.SQL, value)\n}\n\n// QuoteTo write quoted value to writer\nfunc (stmt *Statement) QuoteTo(writer clause.Writer, field interface{}) {\n\twrite := func(raw bool, str string) {\n\t\tif raw {\n\t\t\twriter.WriteString(str)\n\t\t} else {\n\t\t\tstmt.DB.Dialector.QuoteTo(writer, str)\n\t\t}\n\t}\n\n\tswitch v := field.(type) {\n\tcase clause.Table:\n\t\tif v.Name == clause.CurrentTable {\n\t\t\tif stmt.TableExpr != nil {\n\t\t\t\tstmt.TableExpr.Build(stmt)\n\t\t\t} else if stmt.Table != \"\" {\n\t\t\t\twrite(v.Raw, stmt.Table)\n\t\t\t} else if stmt.AddError(stmt.Parse(stmt.Model)) == nil {\n\t\t\t\twrite(v.Raw, stmt.Table)\n\t\t\t}\n\t\t} else {\n\t\t\twrite(v.Raw, v.Name)\n\t\t}\n\n\t\tif v.Alias != \"\" {\n\t\t\twriter.WriteByte(' ')\n\t\t\twrite(v.Raw, v.Alias)\n\t\t}\n\tcase clause.Column:\n\t\tif v.Table != \"\" {\n\t\t\tif v.Table == clause.CurrentTable {\n\t\t\t\twrite(v.Raw, stmt.Table)\n\t\t\t} else {\n\t\t\t\twrite(v.Raw, v.Table)\n\t\t\t}\n\t\t\twriter.WriteByte('.')\n\t\t}\n\n\t\tif v.Name == clause.PrimaryKey {\n\t\t\tif stmt.Schema == nil {\n\t\t\t\tstmt.DB.AddError(ErrModelValueRequired)\n\t\t\t} else if stmt.Schema.PrioritizedPrimaryField != nil {\n\t\t\t\twrite(v.Raw, stmt.Schema.PrioritizedPrimaryField.DBName)\n\t\t\t} else if len(stmt.Schema.DBNames) > 0 {\n\t\t\t\twrite(v.Raw, stmt.Schema.DBNames[0])\n\t\t\t} else {\n\t\t\t\tstmt.DB.AddError(ErrModelAccessibleFieldsRequired) //nolint:typecheck,errcheck\n\t\t\t}\n\t\t} else {\n\t\t\twrite(v.Raw, v.Name)\n\t\t}\n\n\t\tif v.Alias != \"\" {\n\t\t\twriter.WriteString(\" AS \")\n\t\t\twrite(v.Raw, v.Alias)\n\t\t}\n\tcase []clause.Column:\n\t\twriter.WriteByte('(')\n\t\tfor idx, d := range v {\n\t\t\tif idx > 0 {\n\t\t\t\twriter.WriteByte(',')\n\t\t\t}\n\t\t\tstmt.QuoteTo(writer, d)\n\t\t}\n\t\twriter.WriteByte(')')\n\tcase clause.Expr:\n\t\tv.Build(stmt)\n\tcase string:\n\t\tstmt.DB.Dialector.QuoteTo(writer, v)\n\tcase []string:\n\t\twriter.WriteByte('(')\n\t\tfor idx, d := range v {\n\t\t\tif idx > 0 {\n\t\t\t\twriter.WriteByte(',')\n\t\t\t}\n\t\t\tstmt.DB.Dialector.QuoteTo(writer, d)\n\t\t}\n\t\twriter.WriteByte(')')\n\tdefault:\n\t\tstmt.DB.Dialector.QuoteTo(writer, fmt.Sprint(field))\n\t}\n}\n\n// Quote returns quoted value\nfunc (stmt *Statement) Quote(field interface{}) string {\n\tvar builder strings.Builder\n\tstmt.QuoteTo(&builder, field)\n\treturn builder.String()\n}\n\n// AddVar add var\nfunc (stmt *Statement) AddVar(writer clause.Writer, vars ...interface{}) {\n\tfor idx, v := range vars {\n\t\tif idx > 0 {\n\t\t\twriter.WriteByte(',')\n\t\t}\n\n\t\tswitch v := v.(type) {\n\t\tcase sql.NamedArg:\n\t\t\tstmt.Vars = append(stmt.Vars, v.Value)\n\t\tcase clause.Column, clause.Table:\n\t\t\tstmt.QuoteTo(writer, v)\n\t\tcase Valuer:\n\t\t\treflectValue := reflect.ValueOf(v)\n\t\t\tif reflectValue.Kind() == reflect.Ptr && reflectValue.IsNil() {\n\t\t\t\tstmt.AddVar(writer, nil)\n\t\t\t} else {\n\t\t\t\tstmt.AddVar(writer, v.GormValue(stmt.Context, stmt.DB))\n\t\t\t}\n\t\tcase clause.Interface:\n\t\t\tc := clause.Clause{Name: v.Name()}\n\t\t\tv.MergeClause(&c)\n\t\t\tc.Build(stmt)\n\t\tcase clause.Expression:\n\t\t\tv.Build(stmt)\n\t\tcase driver.Valuer:\n\t\t\tstmt.Vars = append(stmt.Vars, v)\n\t\t\tstmt.DB.Dialector.BindVarTo(writer, stmt, v)\n\t\tcase []byte:\n\t\t\tstmt.Vars = append(stmt.Vars, v)\n\t\t\tstmt.DB.Dialector.BindVarTo(writer, stmt, v)\n\t\tcase []interface{}:\n\t\t\tif len(v) > 0 {\n\t\t\t\twriter.WriteByte('(')\n\t\t\t\tstmt.AddVar(writer, v...)\n\t\t\t\twriter.WriteByte(')')\n\t\t\t} else {\n\t\t\t\twriter.WriteString(\"(NULL)\")\n\t\t\t}\n\t\tcase interface{ getInstance() *DB }:\n\t\t\tcv := v.getInstance()\n\n\t\t\tsubdb := cv.Session(&Session{Logger: logger.Discard, DryRun: true}).getInstance()\n\t\t\tif cv.Statement.SQL.Len() > 0 {\n\t\t\t\tvar (\n\t\t\t\t\tvars = subdb.Statement.Vars\n\t\t\t\t\tsql  = cv.Statement.SQL.String()\n\t\t\t\t)\n\n\t\t\t\tsubdb.Statement.Vars = make([]interface{}, 0, len(vars))\n\t\t\t\tfor _, vv := range vars {\n\t\t\t\t\tsubdb.Statement.Vars = append(subdb.Statement.Vars, vv)\n\t\t\t\t\tbindvar := strings.Builder{}\n\t\t\t\t\tcv.BindVarTo(&bindvar, subdb.Statement, vv)\n\t\t\t\t\tsql = strings.Replace(sql, bindvar.String(), \"?\", 1)\n\t\t\t\t}\n\n\t\t\t\tsubdb.Statement.SQL.Reset()\n\t\t\t\tsubdb.Statement.Vars = stmt.Vars\n\t\t\t\tif strings.Contains(sql, \"@\") {\n\t\t\t\t\tclause.NamedExpr{SQL: sql, Vars: vars}.Build(subdb.Statement)\n\t\t\t\t} else {\n\t\t\t\t\tclause.Expr{SQL: sql, Vars: vars}.Build(subdb.Statement)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tsubdb.Statement.Vars = append(stmt.Vars, subdb.Statement.Vars...)\n\t\t\t\tsubdb.callbacks.Query().Execute(subdb)\n\t\t\t}\n\n\t\t\twriter.WriteString(subdb.Statement.SQL.String())\n\t\t\tstmt.Vars = subdb.Statement.Vars\n\t\tdefault:\n\t\t\tswitch rv := reflect.ValueOf(v); rv.Kind() {\n\t\t\tcase reflect.Slice, reflect.Array:\n\t\t\t\tif rv.Len() == 0 {\n\t\t\t\t\twriter.WriteString(\"(NULL)\")\n\t\t\t\t} else if rv.Type().Elem() == reflect.TypeOf(uint8(0)) {\n\t\t\t\t\tstmt.Vars = append(stmt.Vars, v)\n\t\t\t\t\tstmt.DB.Dialector.BindVarTo(writer, stmt, v)\n\t\t\t\t} else {\n\t\t\t\t\twriter.WriteByte('(')\n\t\t\t\t\tfor i := 0; i < rv.Len(); i++ {\n\t\t\t\t\t\tif i > 0 {\n\t\t\t\t\t\t\twriter.WriteByte(',')\n\t\t\t\t\t\t}\n\t\t\t\t\t\tstmt.AddVar(writer, rv.Index(i).Interface())\n\t\t\t\t\t}\n\t\t\t\t\twriter.WriteByte(')')\n\t\t\t\t}\n\t\t\tdefault:\n\t\t\t\tstmt.Vars = append(stmt.Vars, v)\n\t\t\t\tstmt.DB.Dialector.BindVarTo(writer, stmt, v)\n\t\t\t}\n\t\t}\n\t}\n}\n\n// AddClause add clause\nfunc (stmt *Statement) AddClause(v clause.Interface) {\n\tif optimizer, ok := v.(StatementModifier); ok {\n\t\toptimizer.ModifyStatement(stmt)\n\t} else {\n\t\tname := v.Name()\n\t\tc := stmt.Clauses[name]\n\t\tc.Name = name\n\t\tv.MergeClause(&c)\n\t\tstmt.Clauses[name] = c\n\t}\n}\n\n// AddClauseIfNotExists add clause if not exists\nfunc (stmt *Statement) AddClauseIfNotExists(v clause.Interface) {\n\tif c, ok := stmt.Clauses[v.Name()]; !ok || c.Expression == nil {\n\t\tstmt.AddClause(v)\n\t}\n}\n\n// BuildCondition build condition\nfunc (stmt *Statement) BuildCondition(query interface{}, args ...interface{}) []clause.Expression {\n\tif s, ok := query.(string); ok {\n\t\t// if it is a number, then treats it as primary key\n\t\tif _, err := strconv.Atoi(s); err != nil {\n\t\t\tif s == \"\" && len(args) == 0 {\n\t\t\t\treturn nil\n\t\t\t}\n\n\t\t\tif len(args) == 0 || (len(args) > 0 && strings.Contains(s, \"?\")) {\n\t\t\t\t// looks like a where condition\n\t\t\t\treturn []clause.Expression{clause.Expr{SQL: s, Vars: args}}\n\t\t\t}\n\n\t\t\tif len(args) > 0 && strings.Contains(s, \"@\") {\n\t\t\t\t// looks like a named query\n\t\t\t\treturn []clause.Expression{clause.NamedExpr{SQL: s, Vars: args}}\n\t\t\t}\n\n\t\t\tif strings.Contains(strings.TrimSpace(s), \" \") {\n\t\t\t\t// looks like a where condition\n\t\t\t\treturn []clause.Expression{clause.Expr{SQL: s, Vars: args}}\n\t\t\t}\n\n\t\t\tif len(args) == 1 {\n\t\t\t\treturn []clause.Expression{clause.Eq{Column: s, Value: args[0]}}\n\t\t\t}\n\t\t}\n\t}\n\n\tconds := make([]clause.Expression, 0, 4)\n\targs = append([]interface{}{query}, args...)\n\tfor idx, arg := range args {\n\t\tif arg == nil {\n\t\t\tcontinue\n\t\t}\n\t\tif valuer, ok := arg.(driver.Valuer); ok {\n\t\t\targ, _ = valuer.Value()\n\t\t}\n\n\t\tcurTable := stmt.Table\n\t\tif curTable == \"\" {\n\t\t\tcurTable = clause.CurrentTable\n\t\t}\n\n\t\tswitch v := arg.(type) {\n\t\tcase clause.Expression:\n\t\t\tconds = append(conds, v)\n\t\tcase []clause.Expression:\n\t\t\tconds = append(conds, v...)\n\t\tcase *DB:\n\t\t\tv.executeScopes()\n\n\t\t\tif cs, ok := v.Statement.Clauses[\"WHERE\"]; ok {\n\t\t\t\tif where, ok := cs.Expression.(clause.Where); ok {\n\t\t\t\t\tif len(where.Exprs) == 1 {\n\t\t\t\t\t\tif orConds, ok := where.Exprs[0].(clause.OrConditions); ok {\n\t\t\t\t\t\t\tif len(orConds.Exprs) == 1 {\n\t\t\t\t\t\t\t\twhere.Exprs[0] = clause.AndConditions(orConds)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tconds = append(conds, clause.And(where.Exprs...))\n\t\t\t\t} else if cs.Expression != nil {\n\t\t\t\t\tconds = append(conds, cs.Expression)\n\t\t\t\t}\n\t\t\t}\n\t\tcase map[interface{}]interface{}:\n\t\t\tfor i, j := range v {\n\t\t\t\tconds = append(conds, clause.Eq{Column: i, Value: j})\n\t\t\t}\n\t\tcase map[string]string:\n\t\t\tkeys := make([]string, 0, len(v))\n\t\t\tfor i := range v {\n\t\t\t\tkeys = append(keys, i)\n\t\t\t}\n\t\t\tsort.Strings(keys)\n\n\t\t\tfor _, key := range keys {\n\t\t\t\tcolumn := clause.Column{Name: key, Table: curTable}\n\t\t\t\tif strings.Contains(key, \".\") {\n\t\t\t\t\tcolumn = clause.Column{Name: key}\n\t\t\t\t}\n\t\t\t\tconds = append(conds, clause.Eq{Column: column, Value: v[key]})\n\t\t\t}\n\t\tcase map[string]interface{}:\n\t\t\tkeys := make([]string, 0, len(v))\n\t\t\tfor i := range v {\n\t\t\t\tkeys = append(keys, i)\n\t\t\t}\n\t\t\tsort.Strings(keys)\n\n\t\t\tfor _, key := range keys {\n\t\t\t\treflectValue := reflect.Indirect(reflect.ValueOf(v[key]))\n\t\t\t\tcolumn := clause.Column{Name: key, Table: curTable}\n\t\t\t\tif strings.Contains(key, \".\") {\n\t\t\t\t\tcolumn = clause.Column{Name: key}\n\t\t\t\t}\n\t\t\t\tswitch reflectValue.Kind() {\n\t\t\t\tcase reflect.Slice, reflect.Array:\n\t\t\t\t\tif _, ok := v[key].(driver.Valuer); ok {\n\t\t\t\t\t\tconds = append(conds, clause.Eq{Column: column, Value: v[key]})\n\t\t\t\t\t} else if _, ok := v[key].(Valuer); ok {\n\t\t\t\t\t\tconds = append(conds, clause.Eq{Column: column, Value: v[key]})\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// optimize reflect value length\n\t\t\t\t\t\tvalueLen := reflectValue.Len()\n\t\t\t\t\t\tvalues := make([]interface{}, valueLen)\n\t\t\t\t\t\tfor i := 0; i < valueLen; i++ {\n\t\t\t\t\t\t\tvalues[i] = reflectValue.Index(i).Interface()\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconds = append(conds, clause.IN{Column: column, Values: values})\n\t\t\t\t\t}\n\t\t\t\tdefault:\n\t\t\t\t\tconds = append(conds, clause.Eq{Column: column, Value: v[key]})\n\t\t\t\t}\n\t\t\t}\n\t\tdefault:\n\t\t\treflectValue := reflect.Indirect(reflect.ValueOf(arg))\n\t\t\tfor reflectValue.Kind() == reflect.Ptr {\n\t\t\t\treflectValue = reflectValue.Elem()\n\t\t\t}\n\n\t\t\tif s, err := schema.Parse(arg, stmt.DB.cacheStore, stmt.DB.NamingStrategy); err == nil {\n\t\t\t\tselectedColumns := map[string]bool{}\n\t\t\t\tif idx == 0 {\n\t\t\t\t\tfor _, v := range args[1:] {\n\t\t\t\t\t\tif vs, ok := v.(string); ok {\n\t\t\t\t\t\t\tselectedColumns[vs] = true\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\trestricted := len(selectedColumns) != 0\n\n\t\t\t\tswitch reflectValue.Kind() {\n\t\t\t\tcase reflect.Struct:\n\t\t\t\t\tfor _, field := range s.Fields {\n\t\t\t\t\t\tselected := selectedColumns[field.DBName] || selectedColumns[field.Name]\n\t\t\t\t\t\tif selected || (!restricted && field.Readable) {\n\t\t\t\t\t\t\tif v, isZero := field.ValueOf(stmt.Context, reflectValue); !isZero || selected {\n\t\t\t\t\t\t\t\tif field.DBName != \"\" {\n\t\t\t\t\t\t\t\t\tconds = append(conds, clause.Eq{Column: clause.Column{Table: curTable, Name: field.DBName}, Value: v})\n\t\t\t\t\t\t\t\t} else if field.DataType != \"\" {\n\t\t\t\t\t\t\t\t\tconds = append(conds, clause.Eq{Column: clause.Column{Table: curTable, Name: field.Name}, Value: v})\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\tcase reflect.Slice, reflect.Array:\n\t\t\t\t\tfor i := 0; i < reflectValue.Len(); i++ {\n\t\t\t\t\t\tfor _, field := range s.Fields {\n\t\t\t\t\t\t\tselected := selectedColumns[field.DBName] || selectedColumns[field.Name]\n\t\t\t\t\t\t\tif selected || (!restricted && field.Readable) {\n\t\t\t\t\t\t\t\tif v, isZero := field.ValueOf(stmt.Context, reflectValue.Index(i)); !isZero || selected {\n\t\t\t\t\t\t\t\t\tif field.DBName != \"\" {\n\t\t\t\t\t\t\t\t\t\tconds = append(conds, clause.Eq{Column: clause.Column{Table: curTable, Name: field.DBName}, Value: v})\n\t\t\t\t\t\t\t\t\t} else if field.DataType != \"\" {\n\t\t\t\t\t\t\t\t\t\tconds = append(conds, clause.Eq{Column: clause.Column{Table: curTable, Name: field.Name}, Value: v})\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif restricted {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t} else if !reflectValue.IsValid() {\n\t\t\t\tstmt.AddError(ErrInvalidData)\n\t\t\t} else if len(conds) == 0 {\n\t\t\t\tif len(args) == 1 {\n\t\t\t\t\tswitch reflectValue.Kind() {\n\t\t\t\t\tcase reflect.Slice, reflect.Array:\n\t\t\t\t\t\t// optimize reflect value length\n\t\t\t\t\t\tvalueLen := reflectValue.Len()\n\t\t\t\t\t\tvalues := make([]interface{}, valueLen)\n\t\t\t\t\t\tfor i := 0; i < valueLen; i++ {\n\t\t\t\t\t\t\tvalues[i] = reflectValue.Index(i).Interface()\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif len(values) > 0 {\n\t\t\t\t\t\t\tconds = append(conds, clause.IN{Column: clause.Column{Table: curTable, Name: clause.PrimaryKey}, Values: values})\n\t\t\t\t\t\t\treturn []clause.Expression{clause.And(conds...)}\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn nil\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconds = append(conds, clause.IN{Column: clause.Column{Table: curTable, Name: clause.PrimaryKey}, Values: args})\n\t\t\t}\n\t\t}\n\t}\n\n\tif len(conds) > 0 {\n\t\treturn []clause.Expression{clause.And(conds...)}\n\t}\n\treturn nil\n}\n\n// Build build sql with clauses names\nfunc (stmt *Statement) Build(clauses ...string) {\n\tvar firstClauseWritten bool\n\n\tfor _, name := range clauses {\n\t\tif c, ok := stmt.Clauses[name]; ok {\n\t\t\tif firstClauseWritten {\n\t\t\t\tstmt.WriteByte(' ')\n\t\t\t}\n\n\t\t\tfirstClauseWritten = true\n\t\t\tif b, ok := stmt.DB.ClauseBuilders[name]; ok {\n\t\t\t\tb(c, stmt)\n\t\t\t} else {\n\t\t\t\tc.Build(stmt)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc (stmt *Statement) Parse(value interface{}) (err error) {\n\treturn stmt.ParseWithSpecialTableName(value, \"\")\n}\n\nfunc (stmt *Statement) ParseWithSpecialTableName(value interface{}, specialTableName string) (err error) {\n\tif stmt.Schema, err = schema.ParseWithSpecialTableName(value, stmt.DB.cacheStore, stmt.DB.NamingStrategy, specialTableName); err == nil && stmt.Table == \"\" {\n\t\tif tables := strings.Split(stmt.Schema.Table, \".\"); len(tables) == 2 {\n\t\t\tstmt.TableExpr = &clause.Expr{SQL: stmt.Quote(stmt.Schema.Table)}\n\t\t\tstmt.Table = tables[1]\n\t\t\treturn\n\t\t}\n\n\t\tstmt.Table = stmt.Schema.Table\n\t}\n\treturn err\n}\n\nfunc (stmt *Statement) clone() *Statement {\n\tnewStmt := &Statement{\n\t\tTableExpr:            stmt.TableExpr,\n\t\tTable:                stmt.Table,\n\t\tModel:                stmt.Model,\n\t\tUnscoped:             stmt.Unscoped,\n\t\tDest:                 stmt.Dest,\n\t\tReflectValue:         stmt.ReflectValue,\n\t\tClauses:              map[string]clause.Clause{},\n\t\tDistinct:             stmt.Distinct,\n\t\tSelects:              stmt.Selects,\n\t\tOmits:                stmt.Omits,\n\t\tColumnMapping:        stmt.ColumnMapping,\n\t\tPreloads:             map[string][]interface{}{},\n\t\tConnPool:             stmt.ConnPool,\n\t\tSchema:               stmt.Schema,\n\t\tContext:              stmt.Context,\n\t\tRaiseErrorOnNotFound: stmt.RaiseErrorOnNotFound,\n\t\tSkipHooks:            stmt.SkipHooks,\n\t\tResult:               stmt.Result,\n\t}\n\n\tif stmt.SQL.Len() > 0 {\n\t\tnewStmt.SQL.WriteString(stmt.SQL.String())\n\t\tnewStmt.Vars = make([]interface{}, 0, len(stmt.Vars))\n\t\tnewStmt.Vars = append(newStmt.Vars, stmt.Vars...)\n\t}\n\n\tfor k, c := range stmt.Clauses {\n\t\tnewStmt.Clauses[k] = c\n\t}\n\n\tfor k, p := range stmt.Preloads {\n\t\tnewStmt.Preloads[k] = p\n\t}\n\n\tif len(stmt.Joins) > 0 {\n\t\tnewStmt.Joins = make([]join, len(stmt.Joins))\n\t\tcopy(newStmt.Joins, stmt.Joins)\n\t}\n\n\tif len(stmt.scopes) > 0 {\n\t\tnewStmt.scopes = make([]func(*DB) *DB, len(stmt.scopes))\n\t\tcopy(newStmt.scopes, stmt.scopes)\n\t}\n\n\tstmt.Settings.Range(func(k, v interface{}) bool {\n\t\tnewStmt.Settings.Store(k, v)\n\t\treturn true\n\t})\n\n\treturn newStmt\n}\n\n// SetColumn set column's value\n//\n//\tstmt.SetColumn(\"Name\", \"jinzhu\") // Hooks Method\n//\tstmt.SetColumn(\"Name\", \"jinzhu\", true) // Callbacks Method\nfunc (stmt *Statement) SetColumn(name string, value interface{}, fromCallbacks ...bool) {\n\tif v, ok := stmt.Dest.(map[string]interface{}); ok {\n\t\tv[name] = value\n\t} else if v, ok := stmt.Dest.([]map[string]interface{}); ok {\n\t\tfor _, m := range v {\n\t\t\tm[name] = value\n\t\t}\n\t} else if stmt.Schema != nil {\n\t\tif field := stmt.Schema.LookUpField(name); field != nil {\n\t\t\tdestValue := reflect.ValueOf(stmt.Dest)\n\t\t\tfor destValue.Kind() == reflect.Ptr {\n\t\t\t\tdestValue = destValue.Elem()\n\t\t\t}\n\n\t\t\tif stmt.ReflectValue != destValue {\n\t\t\t\tif !destValue.CanAddr() {\n\t\t\t\t\tdestValueCanAddr := reflect.New(destValue.Type())\n\t\t\t\t\tdestValueCanAddr.Elem().Set(destValue)\n\t\t\t\t\tstmt.Dest = destValueCanAddr.Interface()\n\t\t\t\t\tdestValue = destValueCanAddr.Elem()\n\t\t\t\t}\n\n\t\t\t\tswitch destValue.Kind() {\n\t\t\t\tcase reflect.Struct:\n\t\t\t\t\tstmt.AddError(field.Set(stmt.Context, destValue, value))\n\t\t\t\tdefault:\n\t\t\t\t\tstmt.AddError(ErrInvalidData)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tswitch stmt.ReflectValue.Kind() {\n\t\t\tcase reflect.Slice, reflect.Array:\n\t\t\t\tif len(fromCallbacks) > 0 {\n\t\t\t\t\tfor i := 0; i < stmt.ReflectValue.Len(); i++ {\n\t\t\t\t\t\tstmt.AddError(field.Set(stmt.Context, stmt.ReflectValue.Index(i), value))\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tstmt.AddError(field.Set(stmt.Context, stmt.ReflectValue.Index(stmt.CurDestIndex), value))\n\t\t\t\t}\n\t\t\tcase reflect.Struct:\n\t\t\t\tif !stmt.ReflectValue.CanAddr() {\n\t\t\t\t\tstmt.AddError(ErrInvalidValue)\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tstmt.AddError(field.Set(stmt.Context, stmt.ReflectValue, value))\n\t\t\t}\n\t\t} else {\n\t\t\tstmt.AddError(ErrInvalidField)\n\t\t}\n\t} else {\n\t\tstmt.AddError(ErrInvalidField)\n\t}\n}\n\n// Changed check model changed or not when updating\nfunc (stmt *Statement) Changed(fields ...string) bool {\n\tmodelValue := stmt.ReflectValue\n\tswitch modelValue.Kind() {\n\tcase reflect.Slice, reflect.Array:\n\t\tmodelValue = stmt.ReflectValue.Index(stmt.CurDestIndex)\n\t}\n\n\tselectColumns, restricted := stmt.SelectAndOmitColumns(false, true)\n\tchanged := func(field *schema.Field) bool {\n\t\tfieldValue, _ := field.ValueOf(stmt.Context, modelValue)\n\t\tif v, ok := selectColumns[field.DBName]; (ok && v) || (!ok && !restricted) {\n\t\t\tif mv, mok := stmt.Dest.(map[string]interface{}); mok {\n\t\t\t\tif fv, ok := mv[field.Name]; ok {\n\t\t\t\t\treturn !utils.AssertEqual(fv, fieldValue)\n\t\t\t\t} else if fv, ok := mv[field.DBName]; ok {\n\t\t\t\t\treturn !utils.AssertEqual(fv, fieldValue)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tdestValue := reflect.ValueOf(stmt.Dest)\n\t\t\t\tfor destValue.Kind() == reflect.Ptr {\n\t\t\t\t\tdestValue = destValue.Elem()\n\t\t\t\t}\n\t\t\t\tif descSchema, err := schema.Parse(stmt.Dest, stmt.DB.cacheStore, stmt.DB.NamingStrategy); err == nil {\n\t\t\t\t\tif destField := descSchema.LookUpField(field.DBName); destField != nil {\n\t\t\t\t\t\tchangedValue, zero := destField.ValueOf(stmt.Context, destValue)\n\t\t\t\t\t\tif v {\n\t\t\t\t\t\t\treturn !utils.AssertEqual(changedValue, fieldValue)\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn !zero && !utils.AssertEqual(changedValue, fieldValue)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn false\n\t}\n\n\tif len(fields) == 0 {\n\t\tfor _, field := range stmt.Schema.FieldsByDBName {\n\t\t\tif changed(field) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\t} else {\n\t\tfor _, name := range fields {\n\t\t\tif field := stmt.Schema.LookUpField(name); field != nil {\n\t\t\t\tif changed(field) {\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn false\n}\n\nvar matchName = func() func(tableColumn string) (table, column string) {\n\tnameMatcher := regexp.MustCompile(`^(?:\\W?(\\w+?)\\W?\\.)?(?:(\\*)|\\W?(\\w+?)\\W?)$`)\n\treturn func(tableColumn string) (table, column string) {\n\t\tif matches := nameMatcher.FindStringSubmatch(tableColumn); len(matches) == 4 {\n\t\t\ttable = matches[1]\n\t\t\tstar := matches[2]\n\t\t\tcolumnName := matches[3]\n\t\t\tif star != \"\" {\n\t\t\t\treturn table, star\n\t\t\t}\n\t\t\treturn table, columnName\n\t\t}\n\t\treturn \"\", \"\"\n\t}\n}()\n\n// SelectAndOmitColumns get select and omit columns, select -> true, omit -> false\nfunc (stmt *Statement) SelectAndOmitColumns(requireCreate, requireUpdate bool) (map[string]bool, bool) {\n\tresults := map[string]bool{}\n\tnotRestricted := false\n\n\tprocessColumn := func(column string, result bool) {\n\t\tif stmt.Schema == nil {\n\t\t\tresults[column] = result\n\t\t} else if column == \"*\" {\n\t\t\tnotRestricted = result\n\t\t\tfor _, dbName := range stmt.Schema.DBNames {\n\t\t\t\tresults[dbName] = result\n\t\t\t}\n\t\t} else if column == clause.Associations {\n\t\t\tfor _, rel := range stmt.Schema.Relationships.Relations {\n\t\t\t\tresults[rel.Name] = result\n\t\t\t}\n\t\t} else if field := stmt.Schema.LookUpField(column); field != nil && field.DBName != \"\" {\n\t\t\tresults[field.DBName] = result\n\t\t} else if table, col := matchName(column); col != \"\" && (table == stmt.Table || table == \"\") {\n\t\t\tif col == \"*\" {\n\t\t\t\tfor _, dbName := range stmt.Schema.DBNames {\n\t\t\t\t\tresults[dbName] = result\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tresults[col] = result\n\t\t\t}\n\t\t} else {\n\t\t\tresults[column] = result\n\t\t}\n\t}\n\n\t// select columns\n\tfor _, column := range stmt.Selects {\n\t\tprocessColumn(column, true)\n\t}\n\n\t// omit columns\n\tfor _, column := range stmt.Omits {\n\t\tprocessColumn(column, false)\n\t}\n\n\tif stmt.Schema != nil {\n\t\tfor _, field := range stmt.Schema.FieldsByName {\n\t\t\tname := field.DBName\n\t\t\tif name == \"\" {\n\t\t\t\tname = field.Name\n\t\t\t}\n\n\t\t\tif requireCreate && !field.Creatable {\n\t\t\t\tresults[name] = false\n\t\t\t} else if requireUpdate && !field.Updatable {\n\t\t\t\tresults[name] = false\n\t\t\t}\n\t\t}\n\t}\n\n\treturn results, !notRestricted && len(stmt.Selects) > 0\n}\n"
  },
  {
    "path": "statement_test.go",
    "content": "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.T) {\n\tfor whereCount := 1; whereCount <= 8; whereCount++ {\n\t\tt.Run(fmt.Sprintf(\"w=%d\", whereCount), func(t *testing.T) {\n\t\t\ts := new(Statement)\n\t\t\tfor w := 0; w < whereCount; w++ {\n\t\t\t\ts = s.clone()\n\t\t\t\ts.AddClause(clause.Where{\n\t\t\t\t\tExprs: s.BuildCondition(fmt.Sprintf(\"where%d\", w)),\n\t\t\t\t})\n\t\t\t}\n\n\t\t\ts1 := s.clone()\n\t\t\ts1.AddClause(clause.Where{\n\t\t\t\tExprs: s.BuildCondition(\"FINAL1\"),\n\t\t\t})\n\t\t\ts2 := s.clone()\n\t\t\ts2.AddClause(clause.Where{\n\t\t\t\tExprs: s.BuildCondition(\"FINAL2\"),\n\t\t\t})\n\n\t\t\tif reflect.DeepEqual(s1.Clauses[\"WHERE\"], s2.Clauses[\"WHERE\"]) {\n\t\t\t\tt.Errorf(\"Where conditions should be different\")\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestNilCondition(t *testing.T) {\n\ts := new(Statement)\n\tif len(s.BuildCondition(nil)) != 0 {\n\t\tt.Errorf(\"Nil condition should be empty\")\n\t}\n}\n\nfunc TestNameMatcher(t *testing.T) {\n\tfor k, v := range map[string][]string{\n\t\t\"table.name\":         {\"table\", \"name\"},\n\t\t\"`table`.`name`\":     {\"table\", \"name\"},\n\t\t\"'table'.'name'\":     {\"table\", \"name\"},\n\t\t\"'table'.name\":       {\"table\", \"name\"},\n\t\t\"table1.name_23\":     {\"table1\", \"name_23\"},\n\t\t\"`table_1`.`name23`\": {\"table_1\", \"name23\"},\n\t\t\"'table23'.'name_1'\": {\"table23\", \"name_1\"},\n\t\t\"'table23'.name1\":    {\"table23\", \"name1\"},\n\t\t\"'name1'\":            {\"\", \"name1\"},\n\t\t\"`name_1`\":           {\"\", \"name_1\"},\n\t\t\"`Name_1`\":           {\"\", \"Name_1\"},\n\t\t\"`Table`.`nAme`\":     {\"Table\", \"nAme\"},\n\t\t\"my_table.*\":         {\"my_table\", \"*\"},\n\t\t\"`my_table`.*\":       {\"my_table\", \"*\"},\n\t\t\"User__Company.*\":    {\"User__Company\", \"*\"},\n\t\t\"`User__Company`.*\":  {\"User__Company\", \"*\"},\n\t\t`\"User__Company\".*`:  {\"User__Company\", \"*\"},\n\t\t`\"table\".\"*\"`:        {\"\", \"\"},\n\t} {\n\t\tif table, column := matchName(k); table != v[0] || column != v[1] {\n\t\t\tt.Errorf(\"failed to match value: %v, got %v, expect: %v\", k, []string{table, column}, v)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "tests/.gitignore",
    "content": "go.sum\n"
  },
  {
    "path": "tests/README.md",
    "content": "# 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",
    "content": "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\"\n)\n\n// BelongsToCompany and BelongsToUser models for belongs to tests - using existing User and Company models\n\n// Test Set + Create with Association OpCreate operation using real database\nfunc TestClauseAssociationSetCreateWithOpCreate(t *testing.T) {\n\tctx := context.Background()\n\n\t// First create a user with Set + Create\n\terr := gorm.G[User](DB).Set(\n\t\tclause.Assignment{Column: clause.Column{Name: \"name\"}, Value: \"TestClauseAssociationSetCreateWithOpCreate\"},\n\t\tclause.Assignment{Column: clause.Column{Name: \"age\"}, Value: 25},\n\t).Create(ctx)\n\tif err != nil {\n\t\tt.Fatalf(\"Set Create failed: %v\", err)\n\t}\n\n\t// Find the created user\n\tvar user User\n\tif err := DB.Where(\"name = ?\", \"TestClauseAssociationSetCreateWithOpCreate\").First(&user).Error; err != nil {\n\t\tt.Fatalf(\"failed to find created user: %v\", err)\n\t}\n\n\t// Test Set + Update with Association OpCreate\n\tassocOp := clause.Association{\n\t\tAssociation: \"Pets\",\n\t\tType:        clause.OpCreate,\n\t\tSet: []clause.Assignment{\n\t\t\t{Column: clause.Column{Name: \"name\"}, Value: \"test-pet\"},\n\t\t},\n\t}\n\n\trows, err := gorm.G[User](DB).Where(\"id = ?\", user.ID).Set(assocOp).Update(ctx)\n\tif err != nil {\n\t\tt.Fatalf(\"Set Update with association failed: %v\", err)\n\t}\n\t// Only association operations were executed; no row update is expected\n\tif rows != 0 {\n\t\tt.Fatalf(\"expected 0 rows affected for association-only update, got %d\", rows)\n\t}\n\n\t// Verify the association was created using real database query\n\tAssertAssociationCount(t, &user, \"Pets\", 1, \"after Set Update with association\")\n}\n\n// Test Set + Update with Association OpCreate operation using real database\nfunc TestClauseAssociationSetUpdateWithOpCreate(t *testing.T) {\n\tctx := context.Background()\n\n\t// Create a user with a pet first using real database\n\tuser := User{Name: \"TestClauseAssociationSetUpdateWithOpCreate\", Age: 25}\n\tuser.Pets = []*Pet{{Name: \"original-pet\"}}\n\tif err := DB.Create(&user).Error; err != nil {\n\t\tt.Fatalf(\"failed to create user with pet: %v\", err)\n\t}\n\n\t// Verify initial state using real database query\n\tAssertAssociationCount(t, user, \"Pets\", 1, \"before update\")\n\n\t// Test Set + Update with Association OpCreate\n\tassocOp := clause.Association{\n\t\tAssociation: \"Pets\",\n\t\tType:        clause.OpCreate,\n\t\tSet: []clause.Assignment{\n\t\t\t{Column: clause.Column{Name: \"name\"}, Value: \"new-pet\"},\n\t\t},\n\t}\n\n\trows, err := gorm.G[User](DB).Where(\"id = ?\", user.ID).Set(assocOp).Update(ctx)\n\tif err != nil {\n\t\tt.Fatalf(\"Set Update with association failed: %v\", err)\n\t}\n\t// Only association operations were executed; no row update is expected\n\tif rows != 0 {\n\t\tt.Fatalf(\"expected 0 rows affected for association-only update, got %d\", rows)\n\t}\n\n\t// Verify the association was updated using real database query\n\tvar updatedUser User\n\tif err := DB.Preload(\"Pets\").Where(\"id = ?\", user.ID).First(&updatedUser).Error; err != nil {\n\t\tt.Fatalf(\"failed to find updated user: %v\", err)\n\t}\n\n\tif len(updatedUser.Pets) != 2 {\n\t\tt.Fatalf(\"expected 2 pets, got %d\", len(updatedUser.Pets))\n\t}\n\n\tpetNames := make(map[string]bool)\n\tfor _, pet := range updatedUser.Pets {\n\t\tpetNames[pet.Name] = true\n\t}\n\n\tif !petNames[\"original-pet\"] {\n\t\tt.Error(\"original pet not found\")\n\t}\n\n\tif !petNames[\"new-pet\"] {\n\t\tt.Error(\"new pet not found\")\n\t}\n}\n\n// Test Set + Create with multiple associations using real database\nfunc TestClauseAssociationSetCreateWithMultipleAssociations(t *testing.T) {\n\tctx := context.Background()\n\n\t// First create a user with Set + Create using real database\n\terr := gorm.G[User](DB).Set(\n\t\tclause.Assignment{Column: clause.Column{Name: \"name\"}, Value: \"TestClauseAssociationSetCreateWithMultipleAssociations\"},\n\t\tclause.Assignment{Column: clause.Column{Name: \"age\"}, Value: 25},\n\t).Create(ctx)\n\tif err != nil {\n\t\tt.Fatalf(\"Set Create failed: %v\", err)\n\t}\n\n\t// Find the created user using real database query\n\tvar user User\n\tif err := DB.Where(\"name = ?\", \"TestClauseAssociationSetCreateWithMultipleAssociations\").First(&user).Error; err != nil {\n\t\tt.Fatalf(\"failed to find created user: %v\", err)\n\t}\n\n\t// Test Set + Update with multiple association operations\n\tassocOp1 := clause.Association{\n\t\tAssociation: \"Pets\",\n\t\tType:        clause.OpCreate,\n\t\tSet: []clause.Assignment{\n\t\t\t{Column: clause.Column{Name: \"name\"}, Value: \"test-pet-1\"},\n\t\t},\n\t}\n\n\tassocOp2 := clause.Association{\n\t\tAssociation: \"Toys\",\n\t\tType:        clause.OpCreate,\n\t\tSet: []clause.Assignment{\n\t\t\t{Column: clause.Column{Name: \"name\"}, Value: \"test-toy-1\"},\n\t\t},\n\t}\n\n\trows, err := gorm.G[User](DB).Where(\"id = ?\", user.ID).Set(assocOp1, assocOp2).Update(ctx)\n\tif err != nil {\n\t\tt.Fatalf(\"Set Update with multiple associations failed: %v\", err)\n\t}\n\t// Only association operations were executed; no row update is expected\n\tif rows != 0 {\n\t\tt.Fatalf(\"expected 0 rows affected for association-only update, got %d\", rows)\n\t}\n\n\t// Verify both associations were created using real database queries\n\tAssertAssociationCount(t, &user, \"Pets\", 1, \"after Set Update with multiple associations\")\n\tAssertAssociationCount(t, &user, \"Toys\", 1, \"after Set Update with multiple associations\")\n}\n\n// Test Set + Update with multiple associations using real database\nfunc TestClauseAssociationSetUpdateWithMultipleAssociations(t *testing.T) {\n\tctx := context.Background()\n\n\t// Create a user with initial associations using real database\n\tuser := User{Name: \"TestClauseAssociationSetUpdateWithMultipleAssociations\", Age: 25}\n\tuser.Pets = []*Pet{{Name: \"original-pet\"}}\n\tuser.Toys = []Toy{{Name: \"original-toy\"}}\n\tif err := DB.Create(&user).Error; err != nil {\n\t\tt.Fatalf(\"failed to create user with associations: %v\", err)\n\t}\n\n\t// Verify initial state using real database queries\n\tAssertAssociationCount(t, user, \"Pets\", 1, \"before update\")\n\tAssertAssociationCount(t, user, \"Toys\", 1, \"before update\")\n\n\t// Test Set + Update with multiple association operations\n\tassocOp1 := clause.Association{\n\t\tAssociation: \"Pets\",\n\t\tType:        clause.OpCreate,\n\t\tSet: []clause.Assignment{\n\t\t\t{Column: clause.Column{Name: \"name\"}, Value: \"new-pet\"},\n\t\t},\n\t}\n\n\tassocOp2 := clause.Association{\n\t\tAssociation: \"Toys\",\n\t\tType:        clause.OpCreate,\n\t\tSet: []clause.Assignment{\n\t\t\t{Column: clause.Column{Name: \"name\"}, Value: \"new-toy\"},\n\t\t},\n\t}\n\n\trows, err := gorm.G[User](DB).Where(\"id = ?\", user.ID).Set(assocOp1, assocOp2).Update(ctx)\n\tif err != nil {\n\t\tt.Fatalf(\"Set Update with multiple associations failed: %v\", err)\n\t}\n\t// Only association operations were executed; no row update is expected\n\tif rows != 0 {\n\t\tt.Fatalf(\"expected 0 rows affected for association-only update, got %d\", rows)\n\t}\n\n\t// Verify both associations were updated using real database queries\n\tvar updatedUser User\n\tif err := DB.Preload(\"Pets\").Preload(\"Toys\").Where(\"id = ?\", user.ID).First(&updatedUser).Error; err != nil {\n\t\tt.Fatalf(\"failed to find updated user: %v\", err)\n\t}\n\n\tif len(updatedUser.Pets) != 2 {\n\t\tt.Fatalf(\"expected 2 pets, got %d\", len(updatedUser.Pets))\n\t}\n\n\tif len(updatedUser.Toys) != 2 {\n\t\tt.Fatalf(\"expected 2 toys, got %d\", len(updatedUser.Toys))\n\t}\n}\n\n// Test Set + Update with Association OpUnlink operation using real database\nfunc TestClauseAssociationSetUpdateWithOpUnlink(t *testing.T) {\n\tctx := context.Background()\n\n\t// Create a user with pets using real database\n\tuser := User{Name: \"TestClauseAssociationSetUpdateWithOpUnlink\", Age: 25}\n\tuser.Pets = []*Pet{{Name: \"pet-to-unlink\"}, {Name: \"pet-to-keep\"}}\n\tif err := DB.Create(&user).Error; err != nil {\n\t\tt.Fatalf(\"failed to create user with pets: %v\", err)\n\t}\n\n\t// Verify initial state using real database query\n\tAssertAssociationCount(t, user, \"Pets\", 2, \"before unlink\")\n\n\t// Get the pet to unlink using real database query\n\tvar petToUnlink Pet\n\tif err := DB.Where(\"name = ?\", \"pet-to-unlink\").First(&petToUnlink).Error; err != nil {\n\t\tt.Fatalf(\"failed to find pet to unlink: %v\", err)\n\t}\n\n\t// Test Set + Update with Association OpUnlink\n\tassocOp := clause.Association{\n\t\tAssociation: \"Pets\",\n\t\tType:        clause.OpUnlink,\n\t\tConditions: []clause.Expression{\n\t\t\tclause.Eq{Column: clause.Column{Name: \"id\"}, Value: petToUnlink.ID},\n\t\t},\n\t}\n\n\trows, err := gorm.G[User](DB).Where(\"id = ?\", user.ID).Set(assocOp).Update(ctx)\n\tif err != nil {\n\t\tt.Fatalf(\"Set Update with association unlink failed: %v\", err)\n\t}\n\t// Only association operations were executed; no row update is expected\n\tif rows != 0 {\n\t\tt.Fatalf(\"expected 0 rows affected for association-only update, got %d\", rows)\n\t}\n\n\t// Verify only one pet remains using real database query\n\tvar updatedUser User\n\tif err := DB.Preload(\"Pets\").Where(\"id = ?\", user.ID).First(&updatedUser).Error; err != nil {\n\t\tt.Fatalf(\"failed to find updated user: %v\", err)\n\t}\n\n\tif len(updatedUser.Pets) != 1 {\n\t\tt.Fatalf(\"expected 1 pet after unlink, got %d\", len(updatedUser.Pets))\n\t}\n\n\tif updatedUser.Pets[0].Name != \"pet-to-keep\" {\n\t\tt.Errorf(\"expected pet-to-keep, got %s\", updatedUser.Pets[0].Name)\n\t}\n\n\t// Verify the unlinked pet still exists in the database using real database query\n\tvar count int64\n\tif err := DB.Model(&Pet{}).Where(\"id = ?\", petToUnlink.ID).Count(&count).Error; err != nil {\n\t\tt.Fatalf(\"failed to count pet: %v\", err)\n\t}\n\tif count != 1 {\n\t\tt.Error(\"unlinked pet should still exist in database\")\n\t}\n}\n\n// Test Set + Update with Association OpCreate operation using real database\nfunc TestClauseAssociationSetUpdateWithOpCreateValues(t *testing.T) {\n\tctx := context.Background()\n\n\t// Create a user first using real database\n\tuser := User{Name: \"TestClauseAssociationSetUpdateWithOpCreate\", Age: 25}\n\tif err := DB.Create(&user).Error; err != nil {\n\t\tt.Fatalf(\"failed to create user: %v\", err)\n\t}\n\n\t// Create a pet object\n\tnewPet := Pet{Name: \"created-pet\"}\n\n\t// Test Set + Update with Association OpCreate\n\tassocOp := clause.Association{\n\t\tAssociation: \"Pets\",\n\t\tType:        clause.OpCreate,\n\t\tValues:      []interface{}{&newPet},\n\t}\n\n\trows, err := gorm.G[User](DB).Where(\"id = ?\", user.ID).Set(assocOp).Update(ctx)\n\tif err != nil {\n\t\tt.Fatalf(\"Set Update with association create values failed: %v\", err)\n\t}\n\t// Only association operations were executed; no row update is expected\n\tif rows != 0 {\n\t\tt.Fatalf(\"expected 0 rows affected for association-only update, got %d\", rows)\n\t}\n\n\t// Verify the pet was created and associated using real database query\n\tvar updatedUser User\n\tif err := DB.Preload(\"Pets\").Where(\"id = ?\", user.ID).First(&updatedUser).Error; err != nil {\n\t\tt.Fatalf(\"failed to find updated user: %v\", err)\n\t}\n\n\tif len(updatedUser.Pets) != 1 {\n\t\tt.Fatalf(\"expected 1 pet, got %d\", len(updatedUser.Pets))\n\t}\n\n\tif updatedUser.Pets[0].Name != \"created-pet\" {\n\t\tt.Errorf(\"expected created-pet, got %s\", updatedUser.Pets[0].Name)\n\t}\n}\n\n// Test Set + Create with many-to-many associations using real database\nfunc TestClauseAssociationSetCreateWithManyToMany(t *testing.T) {\n\tctx := context.Background()\n\n\t// Create a user first using real database\n\tuser := User{Name: \"TestClauseAssociationSetCreateWithManyToMany\", Age: 25}\n\tif err := DB.Create(&user).Error; err != nil {\n\t\tt.Fatalf(\"failed to create user: %v\", err)\n\t}\n\n\t// Create languages using real database\n\tlangs := []Language{\n\t\t{Code: \"en\", Name: \"English\"},\n\t\t{Code: \"fr\", Name: \"French\"},\n\t}\n\tfor _, lang := range langs {\n\t\tDB.FirstOrCreate(&lang, \"code = ?\", lang.Code)\n\t}\n\n\t// Test Set + Update with many-to-many association\n\tassocOp := clause.Association{\n\t\tAssociation: \"Languages\",\n\t\tType:        clause.OpCreate,\n\t\tValues:      []interface{}{langs[0], langs[1]},\n\t}\n\n\trows, err := gorm.G[User](DB).Where(\"id = ?\", user.ID).Set(assocOp).Update(ctx)\n\tif err != nil {\n\t\tt.Fatalf(\"Set Update with many-to-many association failed: %v\", err)\n\t}\n\t// Only association operations were executed; no row update is expected\n\tif rows != 0 {\n\t\tt.Fatalf(\"expected 0 rows affected for association-only update, got %d\", rows)\n\t}\n\n\t// Verify the languages were associated using real database query\n\tvar updatedUser User\n\tif err := DB.Preload(\"Languages\").Where(\"id = ?\", user.ID).First(&updatedUser).Error; err != nil {\n\t\tt.Fatalf(\"failed to find updated user: %v\", err)\n\t}\n\n\tif len(updatedUser.Languages) != 2 {\n\t\tt.Fatalf(\"expected 2 languages, got %d\", len(updatedUser.Languages))\n\t}\n}\n\n// Test Set + Create with belongs-to associations using real database\nfunc TestClauseAssociationSetCreateWithBelongsTo(t *testing.T) {\n\tctx := context.Background()\n\n\t// Create a company first using real database\n\tcompany := Company{Name: \"Test Company\"}\n\tif err := DB.Create(&company).Error; err != nil {\n\t\tt.Fatalf(\"failed to create company: %v\", err)\n\t}\n\n\t// Test Set + Create with belongs-to association using field assignment\n\terr := gorm.G[User](DB).Set(\n\t\tclause.Assignment{Column: clause.Column{Name: \"name\"}, Value: \"TestClauseAssociationSetCreateWithBelongsTo\"},\n\t\tclause.Assignment{Column: clause.Column{Name: \"age\"}, Value: 25},\n\t\tclause.Assignment{Column: clause.Column{Name: \"company_id\"}, Value: company.ID},\n\t).Create(ctx)\n\tif err != nil {\n\t\tt.Fatalf(\"Set Create with belongs-to association failed: %v\", err)\n\t}\n\n\t// Verify the user was created with company association using real database query\n\tvar newUser User\n\tif err := DB.Preload(\"Company\").Where(\"name = ?\", \"TestClauseAssociationSetCreateWithBelongsTo\").First(&newUser).Error; err != nil {\n\t\tt.Fatalf(\"failed to find created user: %v\", err)\n\t}\n\n\tif newUser.Company.ID != company.ID {\n\t\tt.Errorf(\"expected company ID %d, got %d\", company.ID, newUser.Company.ID)\n\t}\n\n\tif newUser.Company.Name != company.Name {\n\t\tt.Errorf(\"expected company name %s, got %s\", company.Name, newUser.Company.Name)\n\t}\n}\n\n// BelongsTo: create and assign company via OpCreate\nfunc TestClauseAssociationSetUpdateBelongsToCreateValues(t *testing.T) {\n\tctx := context.Background()\n\n\tuser := User{Name: \"TestClauseAssociationSetUpdateBelongsToCreateValues\", Age: 26}\n\tif err := DB.Create(&user).Error; err != nil {\n\t\tt.Fatalf(\"failed to create user: %v\", err)\n\t}\n\n\tassocOp := clause.Association{Association: \"Company\", Type: clause.OpCreate, Values: []interface{}{Company{Name: \"Belongs-To-Co\"}}}\n\tif rows, err := gorm.G[User](DB).Where(\"id = ?\", user.ID).Set(assocOp).Update(ctx); err != nil {\n\t\tt.Fatalf(\"Set Update belongs-to create values failed: %v\", err)\n\t} else if rows != 0 {\n\t\tt.Fatalf(\"expected 0 rows affected for association-only update, got %d\", rows)\n\t}\n\n\tvar got User\n\tif err := DB.Preload(\"Company\").First(&got, user.ID).Error; err != nil {\n\t\tt.Fatalf(\"failed preload company: %v\", err)\n\t}\n\tif got.Company.ID == 0 || got.Company.Name != \"Belongs-To-Co\" {\n\t\tt.Fatalf(\"expected Company assigned, got %+v\", got.Company)\n\t}\n}\n\n// Mixed fields + association: update Age and create a pet together\nfunc TestClauseAssociationSetUpdateMixedFieldAndAssociation(t *testing.T) {\n\tctx := context.Background()\n\tuser := User{Name: \"TestClauseAssociationSetUpdateMixed\", Age: 20}\n\tif err := DB.Create(&user).Error; err != nil {\n\t\tt.Fatalf(\"create user: %v\", err)\n\t}\n\n\tassocOp := clause.Association{Association: \"Pets\", Type: clause.OpCreate, Set: []clause.Assignment{{Column: clause.Column{Name: \"name\"}, Value: \"mix-pet\"}}}\n\trows, err := gorm.G[User](DB).Where(\"id = ?\", user.ID).Set(\n\t\tassocOp,\n\t\tclause.Assignment{Column: clause.Column{Name: \"age\"}, Value: 30},\n\t).Update(ctx)\n\tif err != nil {\n\t\tt.Fatalf(\"Set Update mixed failed: %v\", err)\n\t}\n\tif rows != 1 {\n\t\tt.Fatalf(\"expected 1 row affected for field update, got %d\", rows)\n\t}\n\n\tvar got User\n\tif err := DB.Preload(\"Pets\").First(&got, user.ID).Error; err != nil {\n\t\tt.Fatalf(\"load user: %v\", err)\n\t}\n\tif got.Age != 30 {\n\t\tt.Fatalf(\"expected age 30, got %d\", got.Age)\n\t}\n\tif len(got.Pets) != 1 || got.Pets[0].Name != \"mix-pet\" {\n\t\tt.Fatalf(\"expected pet created, got %+v\", got.Pets)\n\t}\n}\n\n// HasOne unlink clears NamedPet\nfunc TestClauseAssociationSetUpdateHasOneUnlink(t *testing.T) {\n\tctx := context.Background()\n\tuser := User{Name: \"TestClauseAssociationSetUpdateHasOneUnlink\", Age: 25}\n\tuser.NamedPet = &Pet{Name: \"np\"}\n\tif err := DB.Create(&user).Error; err != nil {\n\t\tt.Fatalf(\"create: %v\", err)\n\t}\n\n\tassocOp := clause.Association{Association: \"NamedPet\", Type: clause.OpUnlink}\n\tif rows, err := gorm.G[User](DB).Where(\"id = ?\", user.ID).Set(assocOp).Update(ctx); err != nil {\n\t\tt.Fatalf(\"Set Update has-one unlink failed: %v\", err)\n\t} else if rows != 0 {\n\t\tt.Fatalf(\"expected 0 rows affected for association-only update, got %d\", rows)\n\t}\n\n\tvar got User\n\tif err := DB.Preload(\"NamedPet\").First(&got, user.ID).Error; err != nil {\n\t\tt.Fatalf(\"load user: %v\", err)\n\t}\n\tif got.NamedPet != nil {\n\t\tt.Fatalf(\"expected NamedPet cleared, got %+v\", got.NamedPet)\n\t}\n}\n\n// Many-to-Many create with Set\nfunc TestClauseAssociationSetUpdateManyToManyCreateWithSet(t *testing.T) {\n\tctx := context.Background()\n\tuser := User{Name: \"TestClauseAssociationSetUpdateMany2ManyCreateWithSet\", Age: 25}\n\tif err := DB.Create(&user).Error; err != nil {\n\t\tt.Fatalf(\"create user: %v\", err)\n\t}\n\n\tassocOp := clause.Association{\n\t\tAssociation: \"Languages\", Type: clause.OpCreate,\n\t\tSet: []clause.Assignment{{Column: clause.Column{Name: \"code\"}, Value: \"it\"}, {Column: clause.Column{Name: \"name\"}, Value: \"Italian\"}},\n\t}\n\tif rows, err := gorm.G[User](DB).Where(\"id = ?\", user.ID).Set(assocOp).Update(ctx); err != nil {\n\t\tt.Fatalf(\"Set Update many2many create with set failed: %v\", err)\n\t} else if rows != 0 {\n\t\tt.Fatalf(\"expected 0 rows affected, got %d\", rows)\n\t}\n\n\tAssertAssociationCount(t, user, \"Languages\", 1, \"after create language\")\n}\n\n// Many-to-Many clear\nfunc TestClauseAssociationSetUpdateManyToManyClear(t *testing.T) {\n\tctx := context.Background()\n\tuser := User{Name: \"TestClauseAssociationSetUpdateMany2ManyClear\", Age: 25}\n\tif err := DB.Create(&user).Error; err != nil {\n\t\tt.Fatalf(\"create user: %v\", err)\n\t}\n\tlangs := []Language{{Code: \"pt\", Name: \"Portuguese\"}, {Code: \"ru\", Name: \"Russian\"}}\n\tfor _, l := range langs {\n\t\tDB.FirstOrCreate(&l, \"code = ?\", l.Code)\n\t}\n\tif err := DB.Model(&user).Association(\"Languages\").Append(&langs); err != nil {\n\t\tt.Fatalf(\"append: %v\", err)\n\t}\n\tAssertAssociationCount(t, user, \"Languages\", 2, \"before clear\")\n\n\tassocOp := clause.Association{Association: \"Languages\", Type: clause.OpUnlink}\n\tif rows, err := gorm.G[User](DB).Where(\"id = ?\", user.ID).Set(assocOp).Update(ctx); err != nil {\n\t\tt.Fatalf(\"Set Update many2many clear failed: %v\", err)\n\t} else if rows != 0 {\n\t\tt.Fatalf(\"expected 0 rows affected, got %d\", rows)\n\t}\n\tAssertAssociationCount(t, user, \"Languages\", 0, \"after clear\")\n}\n\n// Polymorphic Tools create and unlink\nfunc TestClauseAssociationSetUpdatePolymorphicTools(t *testing.T) {\n\tctx := context.Background()\n\tuser := User{Name: \"TestClauseAssociationSetUpdatePolymorphicTools\", Age: 25}\n\tif err := DB.Create(&user).Error; err != nil {\n\t\tt.Fatalf(\"create user: %v\", err)\n\t}\n\n\tcreateOp := clause.Association{Association: \"Tools\", Type: clause.OpCreate, Values: []interface{}{Tools{Name: \"wrench\"}}}\n\tif rows, err := gorm.G[User](DB).Where(\"id = ?\", user.ID).Set(createOp).Update(ctx); err != nil {\n\t\tt.Fatalf(\"create tools: %v\", err)\n\t} else if rows != 0 {\n\t\tt.Fatalf(\"rows %d\", rows)\n\t}\n\tAssertAssociationCount(t, user, \"Tools\", 1, \"after create tool\")\n\n\tunlinkOp := clause.Association{Association: \"Tools\", Type: clause.OpUnlink}\n\tif rows, err := gorm.G[User](DB).Where(\"id = ?\", user.ID).Set(unlinkOp).Update(ctx); err != nil {\n\t\tt.Fatalf(\"unlink tools: %v\", err)\n\t} else if rows != 0 {\n\t\tt.Fatalf(\"rows %d\", rows)\n\t}\n\tAssertAssociationCount(t, user, \"Tools\", 0, \"after clear tools\")\n}\n\n// Invalid association should return error\nfunc TestClauseAssociationSetUpdateInvalidAssociation(t *testing.T) {\n\tctx := context.Background()\n\tuser := User{Name: \"TestClauseAssociationSetUpdateInvalidAssociation\", Age: 25}\n\tif err := DB.Create(&user).Error; err != nil {\n\t\tt.Fatalf(\"create user: %v\", err)\n\t}\n\n\tassocOp := clause.Association{Association: \"Invalid\", Type: clause.OpCreate}\n\tif _, err := gorm.G[User](DB).Where(\"id = ?\", user.ID).Set(assocOp).Update(ctx); err == nil {\n\t\tt.Fatalf(\"expected error for invalid association, got nil\")\n\t}\n}\n\n// No owner matched; should be no-op\nfunc TestClauseAssociationSetUpdateNoOwnerMatch(t *testing.T) {\n\tctx := context.Background()\n\tassocOp := clause.Association{Association: \"Pets\", Type: clause.OpCreate, Set: []clause.Assignment{{Column: clause.Column{Name: \"name\"}, Value: \"won't-create\"}}}\n\tif rows, err := gorm.G[User](DB).Where(\"id = ?\", -1).Set(assocOp).Update(ctx); err != nil {\n\t\tt.Fatalf(\"unexpected error: %v\", err)\n\t} else if rows != 0 {\n\t\tt.Fatalf(\"expected 0 rows, got %d\", rows)\n\t}\n}\n\n// OpDelete/OpUpdate should work for associations\nfunc TestClauseAssociationSetUpdateAndDelete(t *testing.T) {\n\tctx := context.Background()\n\tuser := User{Name: \"TestClauseAssociationSetUpdateAndDelete\", Age: 25}\n\tuser.Pets = []*Pet{{Name: \"before\"}}\n\tif err := DB.Create(&user).Error; err != nil {\n\t\tt.Fatalf(\"create user: %v\", err)\n\t}\n\tAssertAssociationCount(t, user, \"Pets\", 1, \"before update/delete\")\n\n\t// Update pet name via OpUpdate\n\tupdOp := clause.Association{Association: \"Pets\", Type: clause.OpUpdate, Set: []clause.Assignment{{Column: clause.Column{Name: \"name\"}, Value: \"x\"}}}\n\tif _, err := gorm.G[User](DB).Where(\"id = ?\", user.ID).Set(updOp).Update(ctx); err != nil {\n\t\tt.Fatalf(\"OpUpdate failed: %v\", err)\n\t}\n\tvar got User\n\tif err := DB.Preload(\"Pets\").First(&got, user.ID).Error; err != nil {\n\t\tt.Fatalf(\"load user: %v\", err)\n\t}\n\tif len(got.Pets) != 1 || got.Pets[0].Name != \"x\" {\n\t\tt.Fatalf(\"expected updated pet name, got %+v\", got.Pets)\n\t}\n\n\t// Delete pets via OpDelete\n\tdelOp := clause.Association{Association: \"Pets\", Type: clause.OpDelete}\n\tif _, err := gorm.G[User](DB).Where(\"id = ?\", user.ID).Set(delOp).Update(ctx); err != nil {\n\t\tt.Fatalf(\"OpDelete failed: %v\", err)\n\t}\n\tAssertAssociationCount(t, user, \"Pets\", 0, \"after delete\")\n}\n\n// HasOne: update and delete NamedPet via OpUpdate/OpDelete\nfunc TestClauseAssociationSetUpdateAndDeleteHasOne(t *testing.T) {\n\tctx := context.Background()\n\tuser := User{Name: \"TestClauseAssociationSetUpdateAndDeleteHasOne\", Age: 25}\n\tuser.NamedPet = &Pet{Name: \"np-before\"}\n\tif err := DB.Create(&user).Error; err != nil {\n\t\tt.Fatalf(\"create user: %v\", err)\n\t}\n\tAssertAssociationCount(t, user, \"NamedPet\", 1, \"before\")\n\n\tupd := clause.Association{Association: \"NamedPet\", Type: clause.OpUpdate, Set: []clause.Assignment{{Column: clause.Column{Name: \"name\"}, Value: \"np-after\"}}}\n\tif _, err := gorm.G[User](DB).Where(\"id = ?\", user.ID).Set(upd).Update(ctx); err != nil {\n\t\tt.Fatalf(\"OpUpdate has-one failed: %v\", err)\n\t}\n\tvar u1 User\n\tif err := DB.Preload(\"NamedPet\").First(&u1, user.ID).Error; err != nil {\n\t\tt.Fatalf(\"load: %v\", err)\n\t}\n\tif u1.NamedPet == nil || u1.NamedPet.Name != \"np-after\" {\n\t\tt.Fatalf(\"expected name updated, got %+v\", u1.NamedPet)\n\t}\n\n\tdel := clause.Association{Association: \"NamedPet\", Type: clause.OpDelete}\n\tif _, err := gorm.G[User](DB).Where(\"id = ?\", user.ID).Set(del).Update(ctx); err != nil {\n\t\tt.Fatalf(\"OpDelete has-one failed: %v\", err)\n\t}\n\tAssertAssociationCount(t, user, \"NamedPet\", 0, \"after delete\")\n}\n\n// Many2Many append with map using Association API (regression for map support)\nfunc TestAssociationMany2ManyAppendMap_GenericFile(t *testing.T) {\n\tuser := User{Name: \"AssocM2MAppendMapGeneric\", Age: 28}\n\tif err := DB.Create(&user).Error; err != nil {\n\t\tt.Fatalf(\"create user: %v\", err)\n\t}\n\n\tif err := DB.Model(&user).Association(\"Languages\").Append(map[string]interface{}{\n\t\t\"code\": \"gm2m_map_1\", \"name\": \"GMap1\",\n\t}); err != nil {\n\t\tt.Fatalf(\"append map: %v\", err)\n\t}\n\tAssertAssociationCount(t, user, \"Languages\", 1, \"after append 1 map (generic file)\")\n\n\t// Append more maps individually\n\tif err := DB.Model(&user).Association(\"Languages\").Append(map[string]interface{}{\"code\": \"gm2m_map_2\", \"name\": \"GMap2\"}); err != nil {\n\t\tt.Fatalf(\"append map 2: %v\", err)\n\t}\n\tif err := DB.Model(&user).Association(\"Languages\").Append(map[string]interface{}{\"code\": \"gm2m_map_3\", \"name\": \"GMap3\"}); err != nil {\n\t\tt.Fatalf(\"append map 3: %v\", err)\n\t}\n\tAssertAssociationCount(t, user, \"Languages\", 3, \"after append 3 maps total (generic file)\")\n}\n\n// BelongsTo: update and delete Company via OpUpdate/OpDelete\nfunc TestClauseAssociationSetUpdateAndDeleteBelongsTo(t *testing.T) {\n\tctx := context.Background()\n\n\t// Create company and user with company\n\tcompany := Company{Name: \"Electronics\"}\n\tif err := DB.Create(&company).Error; err != nil {\n\t\tt.Fatalf(\"create company: %v\", err)\n\t}\n\n\tuser := User{Name: \"John\", Age: 25, CompanyID: &company.ID}\n\tif err := DB.Create(&user).Error; err != nil {\n\t\tt.Fatalf(\"create user: %v\", err)\n\t}\n\n\t// Verify association exists\n\tAssertAssociationCount(t, &user, \"Company\", 1, \"before\")\n\n\t// Update company name via OpUpdate\n\tupd := clause.Association{Association: \"Company\", Type: clause.OpUpdate, Set: []clause.Assignment{{Column: clause.Column{Name: \"name\"}, Value: \"Electronics-New\"}}}\n\tif _, err := gorm.G[User](DB).Where(\"id = ?\", user.ID).Set(upd).Update(ctx); err != nil {\n\t\tt.Fatalf(\"OpUpdate belongs-to failed: %v\", err)\n\t}\n\n\tvar u1 User\n\tif err := DB.Preload(\"Company\").First(&u1, user.ID).Error; err != nil {\n\t\tt.Fatalf(\"load: %v\", err)\n\t}\n\n\tif u1.Company.ID == 0 || u1.Company.Name != \"Electronics-New\" {\n\t\tt.Fatalf(\"expected company updated, got %+v\", u1.Company)\n\t}\n\n\t// Unlink company association via OpUnlink (instead of OpDelete which would try to delete the company record)\n\tunlink := clause.Association{Association: \"Company\", Type: clause.OpUnlink}\n\tif _, err := gorm.G[User](DB).Where(\"id = ?\", user.ID).Set(unlink).Update(ctx); err != nil {\n\t\tt.Fatalf(\"OpUnlink belongs-to failed: %v\", err)\n\t}\n\n\tvar u2 User\n\tif err := DB.Preload(\"Company\").First(&u2, user.ID).Error; err != nil {\n\t\tt.Fatalf(\"load: %v\", err)\n\t}\n\n\tif u2.Company.ID != 0 {\n\t\tt.Fatalf(\"expected company association cleared due to unlink, got %+v\", u2.Company)\n\t}\n}\n\n// Many2Many: update and delete via Set\nfunc TestClauseAssociationSetUpdateAndDeleteMany2Many(t *testing.T) {\n\tctx := context.Background()\n\tuser := User{Name: \"TestClauseAssociationSetUpdateAndDeleteMany2Many\", Age: 25}\n\tif err := DB.Create(&user).Error; err != nil {\n\t\tt.Fatalf(\"create user: %v\", err)\n\t}\n\tlangs := []Language{{Code: \"es\", Name: \"Spanish\"}, {Code: \"de\", Name: \"German\"}}\n\tfor _, l := range langs {\n\t\tDB.FirstOrCreate(&l, \"code = ?\", l.Code)\n\t}\n\tif err := DB.Model(&user).Association(\"Languages\").Append(&langs); err != nil {\n\t\tt.Fatalf(\"append: %v\", err)\n\t}\n\tAssertAssociationCount(t, user, \"Languages\", 2, \"before\")\n\n\tupd := clause.Association{Association: \"Languages\", Type: clause.OpUpdate, Set: []clause.Assignment{{Column: clause.Column{Name: \"name\"}, Value: \"Espanol\"}}, Conditions: []clause.Expression{clause.Eq{Column: clause.Column{Name: \"code\"}, Value: \"es\"}}}\n\tif _, err := gorm.G[User](DB).Where(\"id = ?\", user.ID).Set(upd).Update(ctx); err != nil {\n\t\tt.Fatalf(\"OpUpdate m2m failed: %v\", err)\n\t}\n\tvar es Language\n\tif err := DB.First(&es, \"code = ?\", \"es\").Error; err != nil {\n\t\tt.Fatalf(\"load lang: %v\", err)\n\t}\n\tif es.Name != \"Espanol\" {\n\t\tt.Fatalf(\"expected updated language name, got %s\", es.Name)\n\t}\n\n\tdel := clause.Association{Association: \"Languages\", Type: clause.OpDelete, Conditions: []clause.Expression{clause.Eq{Column: clause.Column{Name: \"code\"}, Value: \"es\"}}}\n\tif _, err := gorm.G[User](DB).Where(\"id = ?\", user.ID).Set(del).Update(ctx); err != nil {\n\t\tt.Fatalf(\"OpDelete m2m failed: %v\", err)\n\t}\n\tAssertAssociationCount(t, user, \"Languages\", 1, \"after delete one\")\n\t// language row remains\n\tvar count int64\n\tif err := DB.Model(&Language{}).Where(\"code = ?\", \"es\").Count(&count).Error; err != nil {\n\t\tt.Fatalf(\"count lang: %v\", err)\n\t}\n\tif count != 1 {\n\t\tt.Fatalf(\"expected language row still exists, got %d\", count)\n\t}\n}\n\n// Multi-owners: HasMany update and delete\nfunc TestClauseAssociationSetUpdateAndDeleteManyOwnersHasMany(t *testing.T) {\n\tctx := context.Background()\n\tu1 := User{Name: \"MultiOwners-HasMany-1\", Age: 21}\n\tu1.Pets = []*Pet{{Name: \"p1\"}}\n\tu2 := User{Name: \"MultiOwners-HasMany-2\", Age: 22}\n\tu2.Pets = []*Pet{{Name: \"p2\"}}\n\tif err := DB.Create(&u1).Error; err != nil {\n\t\tt.Fatalf(\"create u1: %v\", err)\n\t}\n\tif err := DB.Create(&u2).Error; err != nil {\n\t\tt.Fatalf(\"create u2: %v\", err)\n\t}\n\tAssertAssociationCount(t, u1, \"Pets\", 1, \"before\")\n\tAssertAssociationCount(t, u2, \"Pets\", 1, \"before\")\n\n\tupd := clause.Association{Association: \"Pets\", Type: clause.OpUpdate, Set: []clause.Assignment{{Column: clause.Column{Name: \"name\"}, Value: \"x\"}}}\n\tif _, err := gorm.G[User](DB).Where(\"id IN ?\", []uint{u1.ID, u2.ID}).Set(upd).Update(ctx); err != nil {\n\t\tt.Fatalf(\"OpUpdate has-many failed: %v\", err)\n\t}\n\tvar got1, got2 User\n\tif err := DB.Preload(\"Pets\").First(&got1, u1.ID).Error; err != nil {\n\t\tt.Fatalf(\"load u1: %v\", err)\n\t}\n\tif err := DB.Preload(\"Pets\").First(&got2, u2.ID).Error; err != nil {\n\t\tt.Fatalf(\"load u2: %v\", err)\n\t}\n\tif len(got1.Pets) != 1 || got1.Pets[0].Name != \"x\" {\n\t\tt.Fatalf(\"u1 pet not updated: %+v\", got1.Pets)\n\t}\n\tif len(got2.Pets) != 1 || got2.Pets[0].Name != \"x\" {\n\t\tt.Fatalf(\"u2 pet not updated: %+v\", got2.Pets)\n\t}\n\n\tdel := clause.Association{Association: \"Pets\", Type: clause.OpDelete}\n\tif _, err := gorm.G[User](DB).Where(\"id IN ?\", []uint{u1.ID, u2.ID}).Set(del).Update(ctx); err != nil {\n\t\tt.Fatalf(\"OpDelete has-many failed: %v\", err)\n\t}\n\tAssertAssociationCount(t, u1, \"Pets\", 0, \"after delete\")\n\tAssertAssociationCount(t, u2, \"Pets\", 0, \"after delete\")\n}\n\n// Multi-owners: BelongsTo update and delete\n\nfunc TestClauseAssociationSetUpdateAndDeleteManyOwnersBelongsTo(t *testing.T) {\n\tctx := context.Background()\n\n\t// Create companies\n\tc1 := Company{Name: \"Electronics\"}\n\tc2 := Company{Name: \"Books\"}\n\tif err := DB.Create(&c1).Error; err != nil {\n\t\tt.Fatalf(\"create c1: %v\", err)\n\t}\n\tif err := DB.Create(&c2).Error; err != nil {\n\t\tt.Fatalf(\"create c2: %v\", err)\n\t}\n\n\t// Create users with companies\n\tu1 := User{Name: \"John\", Age: 25, CompanyID: &c1.ID}\n\tu2 := User{Name: \"Jane\", Age: 30, CompanyID: &c2.ID}\n\tif err := DB.Create(&u1).Error; err != nil {\n\t\tt.Fatalf(\"create u1: %v\", err)\n\t}\n\tif err := DB.Create(&u2).Error; err != nil {\n\t\tt.Fatalf(\"create u2: %v\", err)\n\t}\n\n\t// Verify associations exist\n\tAssertAssociationCount(t, &u1, \"Company\", 1, \"before\")\n\tAssertAssociationCount(t, &u2, \"Company\", 1, \"before\")\n\n\t// Update companies via OpUpdate for multiple users\n\tupd := clause.Association{Association: \"Company\", Type: clause.OpUpdate, Set: []clause.Assignment{{Column: clause.Column{Name: \"name\"}, Value: \"Category-New\"}}}\n\tif _, err := gorm.G[User](DB).Where(\"id IN ?\", []uint{u1.ID, u2.ID}).Set(upd).Update(ctx); err != nil {\n\t\tt.Fatalf(\"OpUpdate belongs-to failed: %v\", err)\n\t}\n\n\t// Check if companies were updated\n\tvar g1, g2 User\n\tif err := DB.Preload(\"Company\").First(&g1, u1.ID).Error; err != nil {\n\t\tt.Fatalf(\"load u1: %v\", err)\n\t}\n\tif err := DB.Preload(\"Company\").First(&g2, u2.ID).Error; err != nil {\n\t\tt.Fatalf(\"load u2: %v\", err)\n\t}\n\n\tif (g1.Company.ID == 0 || g1.Company.Name != \"Category-New\") || (g2.Company.ID == 0 || g2.Company.Name != \"Category-New\") {\n\t\tt.Fatalf(\"company names not updated: %+v, %+v\", g1.Company, g2.Company)\n\t}\n\n\t// Unlink companies via OpUnlink for multiple users (instead of OpDelete which would try to delete the company records)\n\tunlink := clause.Association{Association: \"Company\", Type: clause.OpUnlink}\n\tif _, err := gorm.G[User](DB).Where(\"id IN ?\", []uint{u1.ID, u2.ID}).Set(unlink).Update(ctx); err != nil {\n\t\tt.Fatalf(\"OpUnlink belongs-to failed: %v\", err)\n\t}\n\n\t// Reload users to reflect the changes in the database\n\tif err := DB.First(&u1, u1.ID).Error; err != nil {\n\t\tt.Fatalf(\"reload u1: %v\", err)\n\t}\n\tif err := DB.First(&u2, u2.ID).Error; err != nil {\n\t\tt.Fatalf(\"reload u2: %v\", err)\n\t}\n\n\t// Check if company associations were cleared\n\tAssertAssociationCount(t, &u1, \"Company\", 0, \"after unlink\")\n\tAssertAssociationCount(t, &u2, \"Company\", 0, \"after unlink\")\n}\n\n// Multi-owners: Many2Many update and delete\nfunc TestClauseAssociationSetUpdateAndDeleteManyOwnersMany2Many(t *testing.T) {\n\tctx := context.Background()\n\tu1 := User{Name: \"MultiOwners-M2M-1\", Age: 21}\n\tu2 := User{Name: \"MultiOwners-M2M-2\", Age: 22}\n\tif err := DB.Create(&u1).Error; err != nil {\n\t\tt.Fatalf(\"create u1: %v\", err)\n\t}\n\tif err := DB.Create(&u2).Error; err != nil {\n\t\tt.Fatalf(\"create u2: %v\", err)\n\t}\n\tl1 := Language{Code: \"zz\", Name: \"ZZ\"}\n\tl2 := Language{Code: \"yy\", Name: \"YY\"}\n\tDB.FirstOrCreate(&l1, \"code = ?\", l1.Code)\n\tDB.FirstOrCreate(&l2, \"code = ?\", l2.Code)\n\tif err := DB.Model(&u1).Association(\"Languages\").Append(&l1, &l2); err != nil {\n\t\tt.Fatalf(\"append u1: %v\", err)\n\t}\n\tif err := DB.Model(&u2).Association(\"Languages\").Append(&l1, &l2); err != nil {\n\t\tt.Fatalf(\"append u2: %v\", err)\n\t}\n\tAssertAssociationCount(t, u1, \"Languages\", 2, \"before\")\n\tAssertAssociationCount(t, u2, \"Languages\", 2, \"before\")\n\n\tupd := clause.Association{Association: \"Languages\", Type: clause.OpUpdate, Set: []clause.Assignment{{Column: clause.Column{Name: \"name\"}, Value: \"ZZZ\"}}, Conditions: []clause.Expression{clause.Eq{Column: clause.Column{Name: \"code\"}, Value: \"zz\"}}}\n\tif _, err := gorm.G[User](DB).Where(\"id IN ?\", []uint{u1.ID, u2.ID}).Set(upd).Update(ctx); err != nil {\n\t\tt.Fatalf(\"OpUpdate m2m failed: %v\", err)\n\t}\n\tvar l Language\n\tif err := DB.First(&l, \"code = ?\", \"zz\").Error; err != nil {\n\t\tt.Fatalf(\"load lang: %v\", err)\n\t}\n\tif l.Name != \"ZZZ\" {\n\t\tt.Fatalf(\"expected lang updated, got %s\", l.Name)\n\t}\n\n\tdel := clause.Association{Association: \"Languages\", Type: clause.OpDelete, Conditions: []clause.Expression{clause.Eq{Column: clause.Column{Name: \"code\"}, Value: \"zz\"}}}\n\tif _, err := gorm.G[User](DB).Where(\"id IN ?\", []uint{u1.ID, u2.ID}).Set(del).Update(ctx); err != nil {\n\t\tt.Fatalf(\"OpDelete m2m failed: %v\", err)\n\t}\n\tAssertAssociationCount(t, u1, \"Languages\", 1, \"after delete\")\n\tAssertAssociationCount(t, u2, \"Languages\", 1, \"after delete\")\n}\n\n// Test Set + Update with has-one (NamedPet) using OpCreate\nfunc TestClauseAssociationSetUpdateHasOneCreateValues(t *testing.T) {\n\tctx := context.Background()\n\n\tuser := User{Name: \"TestClauseAssociationSetUpdateHasOneCreateValues\", Age: 25}\n\tif err := DB.Create(&user).Error; err != nil {\n\t\tt.Fatalf(\"failed to create user: %v\", err)\n\t}\n\n\tassocOp := clause.Association{\n\t\tAssociation: \"NamedPet\",\n\t\tType:        clause.OpCreate,\n\t\tValues:      []interface{}{Pet{Name: \"named-pet\"}},\n\t}\n\n\trows, err := gorm.G[User](DB).Where(\"id = ?\", user.ID).Set(assocOp).Update(ctx)\n\tif err != nil {\n\t\tt.Fatalf(\"Set Update has-one create values failed: %v\", err)\n\t}\n\tif rows != 0 {\n\t\tt.Fatalf(\"expected 0 rows affected for association-only update, got %d\", rows)\n\t}\n\n\tvar updated User\n\tif err := DB.Preload(\"NamedPet\").First(&updated, user.ID).Error; err != nil {\n\t\tt.Fatalf(\"failed to load user: %v\", err)\n\t}\n\tif updated.NamedPet == nil || updated.NamedPet.Name != \"named-pet\" {\n\t\tt.Fatalf(\"expected named-pet created, got %+v\", updated.NamedPet)\n\t}\n}\n\n// Test Set + Update to clear all has-many (Pets) via OpUnlink without conditions\nfunc TestClauseAssociationSetUpdateHasManyClear(t *testing.T) {\n\tctx := context.Background()\n\n\tuser := User{Name: \"TestClauseAssociationSetUpdateHasManyClear\", Age: 25}\n\tuser.Pets = []*Pet{{Name: \"p1\"}, {Name: \"p2\"}}\n\tif err := DB.Create(&user).Error; err != nil {\n\t\tt.Fatalf(\"failed to create user: %v\", err)\n\t}\n\tAssertAssociationCount(t, user, \"Pets\", 2, \"before clear\")\n\n\tassocOp := clause.Association{Association: \"Pets\", Type: clause.OpUnlink}\n\tif rows, err := gorm.G[User](DB).Where(\"id = ?\", user.ID).Set(assocOp).Update(ctx); err != nil {\n\t\tt.Fatalf(\"Set Update has-many clear failed: %v\", err)\n\t} else if rows != 0 {\n\t\tt.Fatalf(\"expected 0 rows affected for association-only update, got %d\", rows)\n\t}\n\n\tAssertAssociationCount(t, user, \"Pets\", 0, \"after clear\")\n}\n\n// Test Set + Update with many-to-many unlink specific records using conditions\nfunc TestClauseAssociationSetUpdateManyToManyUnlink(t *testing.T) {\n\tctx := context.Background()\n\n\tuser := User{Name: \"TestClauseAssociationSetUpdateManyToManyUnlink\", Age: 25}\n\tif err := DB.Create(&user).Error; err != nil {\n\t\tt.Fatalf(\"failed to create user: %v\", err)\n\t}\n\n\tlangs := []Language{{Code: \"es\", Name: \"Spanish\"}, {Code: \"de\", Name: \"German\"}}\n\tfor _, l := range langs {\n\t\tDB.FirstOrCreate(&l, \"code = ?\", l.Code)\n\t}\n\n\t// Associate both\n\tif err := DB.Model(&user).Association(\"Languages\").Append(&langs); err != nil {\n\t\tt.Fatalf(\"failed to append languages: %v\", err)\n\t}\n\tAssertAssociationCount(t, user, \"Languages\", 2, \"before unlink\")\n\n\tassocOp := clause.Association{\n\t\tAssociation: \"Languages\",\n\t\tType:        clause.OpUnlink,\n\t\tConditions:  []clause.Expression{clause.Eq{Column: clause.Column{Name: \"code\"}, Value: \"es\"}},\n\t}\n\tif rows, err := gorm.G[User](DB).Where(\"id = ?\", user.ID).Set(assocOp).Update(ctx); err != nil {\n\t\tt.Fatalf(\"Set Update many-to-many unlink failed: %v\", err)\n\t} else if rows != 0 {\n\t\tt.Fatalf(\"expected 0 rows affected for association-only update, got %d\", rows)\n\t}\n\tAssertAssociationCount(t, user, \"Languages\", 1, \"after unlink one\")\n}\n\n// Test Set + Update with polymorphic has-many (Toys) using OpCreate\nfunc TestClauseAssociationSetUpdatePolymorphicCreate(t *testing.T) {\n\tctx := context.Background()\n\tuser := User{Name: \"TestClauseAssociationSetUpdatePolymorphicCreate\", Age: 25}\n\tif err := DB.Create(&user).Error; err != nil {\n\t\tt.Fatalf(\"failed to create user: %v\", err)\n\t}\n\n\tassocOp := clause.Association{\n\t\tAssociation: \"Toys\",\n\t\tType:        clause.OpCreate,\n\t\tSet:         []clause.Assignment{{Column: clause.Column{Name: \"name\"}, Value: \"yo-yo\"}},\n\t}\n\tif rows, err := gorm.G[User](DB).Where(\"id = ?\", user.ID).Set(assocOp).Update(ctx); err != nil {\n\t\tt.Fatalf(\"Set Update polymorphic create failed: %v\", err)\n\t} else if rows != 0 {\n\t\tt.Fatalf(\"expected 0 rows affected for association-only update, got %d\", rows)\n\t}\n\n\tAssertAssociationCount(t, user, \"Toys\", 1, \"after create toy\")\n}\n\n// Test Set + Update across multiple owners\nfunc TestClauseAssociationSetUpdateMultipleOwners(t *testing.T) {\n\tctx := context.Background()\n\n\tu1 := User{Name: \"SetUpdateMultipleOwners-1\", Age: 20}\n\tu2 := User{Name: \"SetUpdateMultipleOwners-2\", Age: 21}\n\tif err := DB.Create(&u1).Error; err != nil {\n\t\tt.Fatalf(\"create u1: %v\", err)\n\t}\n\tif err := DB.Create(&u2).Error; err != nil {\n\t\tt.Fatalf(\"create u2: %v\", err)\n\t}\n\n\tassocOp := clause.Association{Association: \"Pets\", Type: clause.OpCreate, Set: []clause.Assignment{{Column: clause.Column{Name: \"name\"}, Value: \"multi-pet\"}}}\n\tif rows, err := gorm.G[User](DB).Where(\"name IN ?\", []string{u1.Name, u2.Name}).Set(assocOp).Update(ctx); err != nil {\n\t\tt.Fatalf(\"Set Update multi owners failed: %v\", err)\n\t} else if rows != 0 {\n\t\tt.Fatalf(\"expected 0 rows affected for association-only update, got %d\", rows)\n\t}\n\n\tAssertAssociationCount(t, u1, \"Pets\", 1, \"u1 after create\")\n\tAssertAssociationCount(t, u2, \"Pets\", 1, \"u2 after create\")\n}\n"
  },
  {
    "path": "tests/associations_belongs_to_test.go",
    "content": "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(t *testing.T) {\n\tuser := *GetUser(\"belongs-to\", Config{Company: true, Manager: true})\n\n\tif err := DB.Create(&user).Error; err != nil {\n\t\tt.Fatalf(\"errors happened when create: %v\", err)\n\t}\n\n\tCheckUser(t, user, user)\n\n\t// Find\n\tvar user2 User\n\tDB.Find(&user2, \"id = ?\", user.ID)\n\tpointerOfUser := &user2\n\tif err := DB.Model(&pointerOfUser).Association(\"Company\").Find(&user2.Company); err != nil {\n\t\tt.Errorf(\"failed to query users, got error %#v\", err)\n\t}\n\tuser2.Manager = &User{}\n\tDB.Model(&user2).Association(\"Manager\").Find(user2.Manager)\n\tCheckUser(t, user2, user)\n\n\t// Count\n\tAssertAssociationCount(t, user, \"Company\", 1, \"\")\n\tAssertAssociationCount(t, user, \"Manager\", 1, \"\")\n\n\t// Append\n\tcompany := Company{Name: \"company-belongs-to-append\"}\n\tmanager := GetUser(\"manager-belongs-to-append\", Config{})\n\n\tif err := DB.Model(&user2).Association(\"Company\").Append(&company); err != nil {\n\t\tt.Fatalf(\"Error happened when append Company, got %v\", err)\n\t}\n\n\tif company.ID == 0 {\n\t\tt.Fatalf(\"Company's ID should be created\")\n\t}\n\n\tif err := DB.Model(&user2).Association(\"Manager\").Append(manager); err != nil {\n\t\tt.Fatalf(\"Error happened when append Manager, got %v\", err)\n\t}\n\n\tif manager.ID == 0 {\n\t\tt.Fatalf(\"Manager's ID should be created\")\n\t}\n\n\tuser.Company = company\n\tuser.Manager = manager\n\tuser.CompanyID = &company.ID\n\tuser.ManagerID = &manager.ID\n\tCheckUser(t, user2, user)\n\n\tAssertAssociationCount(t, user2, \"Company\", 1, \"AfterAppend\")\n\tAssertAssociationCount(t, user2, \"Manager\", 1, \"AfterAppend\")\n\n\t// Replace\n\tcompany2 := Company{Name: \"company-belongs-to-replace\"}\n\tmanager2 := GetUser(\"manager-belongs-to-replace\", Config{})\n\n\tif err := DB.Model(&user2).Association(\"Company\").Replace(&company2); err != nil {\n\t\tt.Fatalf(\"Error happened when replace Company, got %v\", err)\n\t}\n\n\tif company2.ID == 0 {\n\t\tt.Fatalf(\"Company's ID should be created\")\n\t}\n\n\tif err := DB.Model(&user2).Association(\"Manager\").Replace(manager2); err != nil {\n\t\tt.Fatalf(\"Error happened when replace Manager, got %v\", err)\n\t}\n\n\tif manager2.ID == 0 {\n\t\tt.Fatalf(\"Manager's ID should be created\")\n\t}\n\n\tuser.Company = company2\n\tuser.Manager = manager2\n\tuser.CompanyID = &company2.ID\n\tuser.ManagerID = &manager2.ID\n\tCheckUser(t, user2, user)\n\n\tAssertAssociationCount(t, user2, \"Company\", 1, \"AfterReplace\")\n\tAssertAssociationCount(t, user2, \"Manager\", 1, \"AfterReplace\")\n\n\t// Delete\n\tif err := DB.Model(&user2).Association(\"Company\").Delete(&Company{}); err != nil {\n\t\tt.Fatalf(\"Error happened when delete Company, got %v\", err)\n\t}\n\tAssertAssociationCount(t, user2, \"Company\", 1, \"after delete non-existing data\")\n\n\tif err := DB.Model(&user2).Association(\"Company\").Delete(&company2); err != nil {\n\t\tt.Fatalf(\"Error happened when delete Company, got %v\", err)\n\t}\n\tAssertAssociationCount(t, user2, \"Company\", 0, \"after delete\")\n\n\tif err := DB.Model(&user2).Association(\"Manager\").Delete(&User{}); err != nil {\n\t\tt.Fatalf(\"Error happened when delete Manager, got %v\", err)\n\t}\n\tAssertAssociationCount(t, user2, \"Manager\", 1, \"after delete non-existing data\")\n\n\tif err := DB.Model(&user2).Association(\"Manager\").Delete(manager2); err != nil {\n\t\tt.Fatalf(\"Error happened when delete Manager, got %v\", err)\n\t}\n\tAssertAssociationCount(t, user2, \"Manager\", 0, \"after delete\")\n\n\t// Prepare Data for Clear\n\tif err := DB.Model(&user2).Association(\"Company\").Append(&company); err != nil {\n\t\tt.Fatalf(\"Error happened when append Company, got %v\", err)\n\t}\n\n\tif err := DB.Model(&user2).Association(\"Manager\").Append(manager); err != nil {\n\t\tt.Fatalf(\"Error happened when append Manager, got %v\", err)\n\t}\n\n\tAssertAssociationCount(t, user2, \"Company\", 1, \"after prepare data\")\n\tAssertAssociationCount(t, user2, \"Manager\", 1, \"after prepare data\")\n\n\t// Clear\n\tif err := DB.Model(&user2).Association(\"Company\").Clear(); err != nil {\n\t\tt.Errorf(\"Error happened when clear Company, got %v\", err)\n\t}\n\n\tif err := DB.Model(&user2).Association(\"Manager\").Clear(); err != nil {\n\t\tt.Errorf(\"Error happened when clear Manager, got %v\", err)\n\t}\n\n\tAssertAssociationCount(t, user2, \"Company\", 0, \"after clear\")\n\tAssertAssociationCount(t, user2, \"Manager\", 0, \"after clear\")\n\n\t// unexist company id\n\tunexistCompanyID := company.ID + 9999999\n\tuser = User{Name: \"invalid-user-with-invalid-belongs-to-foreign-key\", CompanyID: &unexistCompanyID}\n\tif err := DB.Create(&user).Error; err == nil {\n\t\ttidbSkip(t, \"not support the foreign key feature\")\n\t\tt.Errorf(\"should have gotten foreign key violation error\")\n\t}\n}\n\nfunc TestBelongsToAssociationForSlice(t *testing.T) {\n\tusers := []User{\n\t\t*GetUser(\"slice-belongs-to-1\", Config{Company: true, Manager: true}),\n\t\t*GetUser(\"slice-belongs-to-2\", Config{Company: true, Manager: false}),\n\t\t*GetUser(\"slice-belongs-to-3\", Config{Company: true, Manager: true}),\n\t}\n\n\tDB.Create(&users)\n\n\tAssertAssociationCount(t, users, \"Company\", 3, \"\")\n\tAssertAssociationCount(t, users, \"Manager\", 2, \"\")\n\n\t// Find\n\tvar companies []Company\n\tif DB.Model(&users).Association(\"Company\").Find(&companies); len(companies) != 3 {\n\t\tt.Errorf(\"companies count should be %v, but got %v\", 3, len(companies))\n\t}\n\n\tvar managers []User\n\tif DB.Model(&users).Association(\"Manager\").Find(&managers); len(managers) != 2 {\n\t\tt.Errorf(\"managers count should be %v, but got %v\", 2, len(managers))\n\t}\n\n\t// Append\n\tDB.Model(&users).Association(\"Company\").Append(\n\t\t&Company{Name: \"company-slice-append-1\"},\n\t\t&Company{Name: \"company-slice-append-2\"},\n\t\t&Company{Name: \"company-slice-append-3\"},\n\t)\n\n\tAssertAssociationCount(t, users, \"Company\", 3, \"After Append\")\n\n\tDB.Model(&users).Association(\"Manager\").Append(\n\t\tGetUser(\"manager-slice-belongs-to-1\", Config{}),\n\t\tGetUser(\"manager-slice-belongs-to-2\", Config{}),\n\t\tGetUser(\"manager-slice-belongs-to-3\", Config{}),\n\t)\n\tAssertAssociationCount(t, users, \"Manager\", 3, \"After Append\")\n\n\tif err := DB.Model(&users).Association(\"Manager\").Append(\n\t\tGetUser(\"manager-slice-belongs-to-test-1\", Config{}),\n\t).Error; err == nil {\n\t\tt.Errorf(\"unmatched length when update user's manager\")\n\t}\n\n\t// Replace -> same as append\n\n\t// Delete\n\tif err := DB.Model(&users).Association(\"Company\").Delete(&users[0].Company); err != nil {\n\t\tt.Errorf(\"no error should happened when deleting company, but got %v\", err)\n\t}\n\n\tif users[0].CompanyID != nil || users[0].Company.ID != 0 {\n\t\tt.Errorf(\"users[0]'s company should be deleted'\")\n\t}\n\n\tAssertAssociationCount(t, users, \"Company\", 2, \"After Delete\")\n\n\t// Clear\n\tDB.Model(&users).Association(\"Company\").Clear()\n\tAssertAssociationCount(t, users, \"Company\", 0, \"After Clear\")\n\n\tDB.Model(&users).Association(\"Manager\").Clear()\n\tAssertAssociationCount(t, users, \"Manager\", 0, \"After Clear\")\n\n\t// shared company\n\tcompany := Company{Name: \"shared\"}\n\tif err := DB.Model(&users[0]).Association(\"Company\").Append(&company); err != nil {\n\t\tt.Errorf(\"Error happened when append company to user, got %v\", err)\n\t}\n\n\tif err := DB.Model(&users[1]).Association(\"Company\").Append(&company); err != nil {\n\t\tt.Errorf(\"Error happened when append company to user, got %v\", err)\n\t}\n\n\tif users[0].CompanyID == nil || users[1].CompanyID == nil || *users[0].CompanyID != *users[1].CompanyID {\n\t\tt.Errorf(\"user's company id should exists and equal, but its: %v, %v\", users[0].CompanyID, users[1].CompanyID)\n\t}\n\n\tDB.Model(&users[0]).Association(\"Company\").Delete(&company)\n\tAssertAssociationCount(t, users[0], \"Company\", 0, \"After Delete\")\n\tAssertAssociationCount(t, users[1], \"Company\", 1, \"After other user Delete\")\n}\n\nfunc TestBelongsToDefaultValue(t *testing.T) {\n\ttype Org struct {\n\t\tID string\n\t}\n\ttype BelongsToUser struct {\n\t\tOrgID string\n\t\tOrg   Org `gorm:\"default:NULL\"`\n\t}\n\n\ttx := DB.Session(&gorm.Session{})\n\ttx.Config.DisableForeignKeyConstraintWhenMigrating = true\n\tAssertEqual(t, DB.Config.DisableForeignKeyConstraintWhenMigrating, false)\n\n\ttx.Migrator().DropTable(&BelongsToUser{}, &Org{})\n\ttx.AutoMigrate(&BelongsToUser{}, &Org{})\n\n\tuser := &BelongsToUser{\n\t\tOrg: Org{\n\t\t\tID: \"BelongsToUser_Org_1\",\n\t\t},\n\t}\n\terr := DB.Create(&user).Error\n\tAssertEqual(t, err, nil)\n}\n\nfunc TestBelongsToAssociationUnscoped(t *testing.T) {\n\ttype ItemParent struct {\n\t\tgorm.Model\n\t\tLogo string `gorm:\"not null;type:varchar(50)\"`\n\t}\n\ttype ItemChild struct {\n\t\tgorm.Model\n\t\tName         string `gorm:\"type:varchar(50)\"`\n\t\tItemParentID uint\n\t\tItemParent   ItemParent\n\t}\n\n\ttx := DB.Session(&gorm.Session{})\n\ttx.Migrator().DropTable(&ItemParent{}, &ItemChild{})\n\ttx.AutoMigrate(&ItemParent{}, &ItemChild{})\n\n\titem := ItemChild{\n\t\tName: \"name\",\n\t\tItemParent: ItemParent{\n\t\t\tLogo: \"logo\",\n\t\t},\n\t}\n\tif err := tx.Create(&item).Error; err != nil {\n\t\tt.Fatalf(\"failed to create items, got error: %v\", err)\n\t}\n\n\t// test replace\n\tif err := tx.Model(&item).Association(\"ItemParent\").Unscoped().Replace(&ItemParent{\n\t\tLogo: \"updated logo\",\n\t}); err != nil {\n\t\tt.Errorf(\"failed to replace item parent, got error: %v\", err)\n\t}\n\n\tvar parents []ItemParent\n\tif err := tx.Find(&parents).Error; err != nil {\n\t\tt.Errorf(\"failed to find item parent, got error: %v\", err)\n\t}\n\tif len(parents) != 1 {\n\t\tt.Errorf(\"expected %d parents, got %d\", 1, len(parents))\n\t}\n\n\t// test delete\n\tif err := tx.Model(&item).Association(\"ItemParent\").Unscoped().Delete(&parents); err != nil {\n\t\tt.Errorf(\"failed to delete item parent, got error: %v\", err)\n\t}\n\tif err := tx.Find(&parents).Error; err != nil {\n\t\tt.Errorf(\"failed to find item parent, got error: %v\", err)\n\t}\n\tif len(parents) != 0 {\n\t\tt.Errorf(\"expected %d parents, got %d\", 0, len(parents))\n\t}\n}\n"
  },
  {
    "path": "tests/associations_has_many_test.go",
    "content": "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 *testing.T) {\n\tuser := *GetUser(\"hasmany\", Config{Pets: 2})\n\n\tif err := DB.Create(&user).Error; err != nil {\n\t\tt.Fatalf(\"errors happened when create: %v\", err)\n\t}\n\n\tCheckUser(t, user, user)\n\n\t// Find\n\tvar user2 User\n\tDB.Find(&user2, \"id = ?\", user.ID)\n\tDB.Model(&user2).Association(\"Pets\").Find(&user2.Pets)\n\tCheckUser(t, user2, user)\n\n\tvar pets []Pet\n\tDB.Model(&user).Where(\"name = ?\", user.Pets[0].Name).Association(\"Pets\").Find(&pets)\n\n\tif len(pets) != 1 {\n\t\tt.Fatalf(\"should only find one pets, but got %v\", len(pets))\n\t}\n\n\tCheckPet(t, pets[0], *user.Pets[0])\n\n\tif count := DB.Model(&user).Where(\"name = ?\", user.Pets[1].Name).Association(\"Pets\").Count(); count != 1 {\n\t\tt.Fatalf(\"should only find one pets, but got %v\", count)\n\t}\n\n\tif count := DB.Model(&user).Where(\"name = ?\", \"not found\").Association(\"Pets\").Count(); count != 0 {\n\t\tt.Fatalf(\"should only find no pet with invalid conditions, but got %v\", count)\n\t}\n\n\t// Count\n\tAssertAssociationCount(t, user, \"Pets\", 2, \"\")\n\n\t// Append\n\tpet := Pet{Name: \"pet-has-many-append\"}\n\n\tif err := DB.Model(&user2).Association(\"Pets\").Append(&pet); err != nil {\n\t\tt.Fatalf(\"Error happened when append account, got %v\", err)\n\t}\n\n\tif pet.ID == 0 {\n\t\tt.Fatalf(\"Pet's ID should be created\")\n\t}\n\n\tuser.Pets = append(user.Pets, &pet)\n\tCheckUser(t, user2, user)\n\n\tAssertAssociationCount(t, user, \"Pets\", 3, \"AfterAppend\")\n\n\tpets2 := []Pet{{Name: \"pet-has-many-append-1-1\"}, {Name: \"pet-has-many-append-1-1\"}}\n\n\tif err := DB.Model(&user2).Association(\"Pets\").Append(&pets2); err != nil {\n\t\tt.Fatalf(\"Error happened when append pet, got %v\", err)\n\t}\n\n\tfor _, pet := range pets2 {\n\t\tpet := pet\n\t\tif pet.ID == 0 {\n\t\t\tt.Fatalf(\"Pet's ID should be created\")\n\t\t}\n\n\t\tuser.Pets = append(user.Pets, &pet)\n\t}\n\n\tCheckUser(t, user2, user)\n\n\tAssertAssociationCount(t, user, \"Pets\", 5, \"AfterAppendSlice\")\n\n\t// Replace\n\tpet2 := Pet{Name: \"pet-has-many-replace\"}\n\n\tif err := DB.Model(&user2).Association(\"Pets\").Replace(&pet2); err != nil {\n\t\tt.Fatalf(\"Error happened when append pet, got %v\", err)\n\t}\n\n\tif pet2.ID == 0 {\n\t\tt.Fatalf(\"pet2's ID should be created\")\n\t}\n\n\tuser.Pets = []*Pet{&pet2}\n\tCheckUser(t, user2, user)\n\n\tAssertAssociationCount(t, user2, \"Pets\", 1, \"AfterReplace\")\n\n\t// Delete\n\tif err := DB.Model(&user2).Association(\"Pets\").Delete(&Pet{}); err != nil {\n\t\tt.Fatalf(\"Error happened when delete pet, got %v\", err)\n\t}\n\tAssertAssociationCount(t, user2, \"Pets\", 1, \"after delete non-existing data\")\n\n\tif err := DB.Model(&user2).Association(\"Pets\").Delete(&pet2); err != nil {\n\t\tt.Fatalf(\"Error happened when delete Pets, got %v\", err)\n\t}\n\tAssertAssociationCount(t, user2, \"Pets\", 0, \"after delete\")\n\n\t// Prepare Data for Clear\n\tif err := DB.Model(&user2).Association(\"Pets\").Append(&pet); err != nil {\n\t\tt.Fatalf(\"Error happened when append Pets, got %v\", err)\n\t}\n\n\tAssertAssociationCount(t, user2, \"Pets\", 1, \"after prepare data\")\n\n\t// Clear\n\tif err := DB.Model(&user2).Association(\"Pets\").Clear(); err != nil {\n\t\tt.Errorf(\"Error happened when clear Pets, got %v\", err)\n\t}\n\n\tAssertAssociationCount(t, user2, \"Pets\", 0, \"after clear\")\n}\n\nfunc TestSingleTableHasManyAssociation(t *testing.T) {\n\tuser := *GetUser(\"hasmany\", Config{Team: 2})\n\n\tif err := DB.Create(&user).Error; err != nil {\n\t\tt.Fatalf(\"errors happened when create: %v\", err)\n\t}\n\n\tCheckUser(t, user, user)\n\n\t// Find\n\tvar user2 User\n\tDB.Find(&user2, \"id = ?\", user.ID)\n\tDB.Model(&user2).Association(\"Team\").Find(&user2.Team)\n\tCheckUser(t, user2, user)\n\n\t// Count\n\tAssertAssociationCount(t, user, \"Team\", 2, \"\")\n\n\t// Append\n\tteam := *GetUser(\"team\", Config{})\n\n\tif err := DB.Model(&user2).Association(\"Team\").Append(&team); err != nil {\n\t\tt.Fatalf(\"Error happened when append account, got %v\", err)\n\t}\n\n\tif team.ID == 0 {\n\t\tt.Fatalf(\"Team's ID should be created\")\n\t}\n\n\tuser.Team = append(user.Team, team)\n\tCheckUser(t, user2, user)\n\n\tAssertAssociationCount(t, user, \"Team\", 3, \"AfterAppend\")\n\n\tteams := []User{*GetUser(\"team-append-1\", Config{}), *GetUser(\"team-append-2\", Config{})}\n\n\tif err := DB.Model(&user2).Association(\"Team\").Append(&teams); err != nil {\n\t\tt.Fatalf(\"Error happened when append team, got %v\", err)\n\t}\n\n\tfor _, team := range teams {\n\t\tteam := team\n\t\tif team.ID == 0 {\n\t\t\tt.Fatalf(\"Team's ID should be created\")\n\t\t}\n\n\t\tuser.Team = append(user.Team, team)\n\t}\n\n\tCheckUser(t, user2, user)\n\n\tAssertAssociationCount(t, user, \"Team\", 5, \"AfterAppendSlice\")\n\n\t// Replace\n\tteam2 := *GetUser(\"team-replace\", Config{})\n\n\tif err := DB.Model(&user2).Association(\"Team\").Replace(&team2); err != nil {\n\t\tt.Fatalf(\"Error happened when append team, got %v\", err)\n\t}\n\n\tif team2.ID == 0 {\n\t\tt.Fatalf(\"team2's ID should be created\")\n\t}\n\n\tuser.Team = []User{team2}\n\tCheckUser(t, user2, user)\n\n\tAssertAssociationCount(t, user2, \"Team\", 1, \"AfterReplace\")\n\n\t// Delete\n\tif err := DB.Model(&user2).Association(\"Team\").Delete(&User{}); err != nil {\n\t\tt.Fatalf(\"Error happened when delete team, got %v\", err)\n\t}\n\tAssertAssociationCount(t, user2, \"Team\", 1, \"after delete non-existing data\")\n\n\tif err := DB.Model(&user2).Association(\"Team\").Delete(&team2); err != nil {\n\t\tt.Fatalf(\"Error happened when delete Team, got %v\", err)\n\t}\n\tAssertAssociationCount(t, user2, \"Team\", 0, \"after delete\")\n\n\t// Prepare Data for Clear\n\tif err := DB.Model(&user2).Association(\"Team\").Append(&team); err != nil {\n\t\tt.Fatalf(\"Error happened when append Team, got %v\", err)\n\t}\n\n\tAssertAssociationCount(t, user2, \"Team\", 1, \"after prepare data\")\n\n\t// Clear\n\tif err := DB.Model(&user2).Association(\"Team\").Clear(); err != nil {\n\t\tt.Errorf(\"Error happened when clear Team, got %v\", err)\n\t}\n\n\tAssertAssociationCount(t, user2, \"Team\", 0, \"after clear\")\n}\n\nfunc TestHasManyAssociationForSlice(t *testing.T) {\n\tusers := []User{\n\t\t*GetUser(\"slice-hasmany-1\", Config{Pets: 2}),\n\t\t*GetUser(\"slice-hasmany-2\", Config{Pets: 0}),\n\t\t*GetUser(\"slice-hasmany-3\", Config{Pets: 4}),\n\t}\n\n\tDB.Create(&users)\n\n\t// Count\n\tAssertAssociationCount(t, users, \"Pets\", 6, \"\")\n\n\t// Find\n\tvar pets []Pet\n\tif DB.Model(&users).Association(\"Pets\").Find(&pets); len(pets) != 6 {\n\t\tt.Errorf(\"pets count should be %v, but got %v\", 6, len(pets))\n\t}\n\n\t// Append\n\tDB.Model(&users).Association(\"Pets\").Append(\n\t\t&Pet{Name: \"pet-slice-append-1\"},\n\t\t[]*Pet{{Name: \"pet-slice-append-2-1\"}, {Name: \"pet-slice-append-2-2\"}},\n\t\t&Pet{Name: \"pet-slice-append-3\"},\n\t)\n\n\tAssertAssociationCount(t, users, \"Pets\", 10, \"After Append\")\n\n\t// Replace -> same as append\n\tDB.Model(&users).Association(\"Pets\").Replace(\n\t\t[]*Pet{{Name: \"pet-slice-replace-1-1\"}, {Name: \"pet-slice-replace-1-2\"}},\n\t\t[]*Pet{{Name: \"pet-slice-replace-2-1\"}, {Name: \"pet-slice-replace-2-2\"}},\n\t\t&Pet{Name: \"pet-slice-replace-3\"},\n\t)\n\n\tAssertAssociationCount(t, users, \"Pets\", 5, \"After Append\")\n\n\t// Delete\n\tif err := DB.Model(&users).Association(\"Pets\").Delete(&users[2].Pets); err != nil {\n\t\tt.Errorf(\"no error should happened when deleting pet, but got %v\", err)\n\t}\n\n\tAssertAssociationCount(t, users, \"Pets\", 4, \"after delete\")\n\n\tif err := DB.Model(&users).Association(\"Pets\").Delete(users[0].Pets[0], users[1].Pets[1]); err != nil {\n\t\tt.Errorf(\"no error should happened when deleting pet, but got %v\", err)\n\t}\n\n\tAssertAssociationCount(t, users, \"Pets\", 2, \"after delete\")\n\n\t// Clear\n\tDB.Model(&users).Association(\"Pets\").Clear()\n\tAssertAssociationCount(t, users, \"Pets\", 0, \"After Clear\")\n}\n\nfunc TestSingleTableHasManyAssociationForSlice(t *testing.T) {\n\tusers := []User{\n\t\t*GetUser(\"slice-hasmany-1\", Config{Team: 2}),\n\t\t*GetUser(\"slice-hasmany-2\", Config{Team: 0}),\n\t\t*GetUser(\"slice-hasmany-3\", Config{Team: 4}),\n\t}\n\n\tif err := DB.Create(&users).Error; err != nil {\n\t\tt.Fatalf(\"errors happened when create: %v\", err)\n\t}\n\n\t// Count\n\tAssertAssociationCount(t, users, \"Team\", 6, \"\")\n\n\t// Find\n\tvar teams []User\n\tif DB.Model(&users).Association(\"Team\").Find(&teams); len(teams) != 6 {\n\t\tt.Errorf(\"teams count should be %v, but got %v\", 6, len(teams))\n\t}\n\n\t// Append\n\tDB.Model(&users).Association(\"Team\").Append(\n\t\t&User{Name: \"pet-slice-append-1\"},\n\t\t[]*User{{Name: \"pet-slice-append-2-1\"}, {Name: \"pet-slice-append-2-2\"}},\n\t\t&User{Name: \"pet-slice-append-3\"},\n\t)\n\n\tAssertAssociationCount(t, users, \"Team\", 10, \"After Append\")\n\n\t// Replace -> same as append\n\tDB.Model(&users).Association(\"Team\").Replace(\n\t\t[]*User{{Name: \"pet-slice-replace-1-1\"}, {Name: \"pet-slice-replace-1-2\"}},\n\t\t[]*User{{Name: \"pet-slice-replace-2-1\"}, {Name: \"pet-slice-replace-2-2\"}},\n\t\t&User{Name: \"pet-slice-replace-3\"},\n\t)\n\n\tAssertAssociationCount(t, users, \"Team\", 5, \"After Append\")\n\n\t// Delete\n\tif err := DB.Model(&users).Association(\"Team\").Delete(&users[2].Team); err != nil {\n\t\tt.Errorf(\"no error should happened when deleting pet, but got %v\", err)\n\t}\n\n\tAssertAssociationCount(t, users, \"Team\", 4, \"after delete\")\n\n\tif err := DB.Model(&users).Association(\"Team\").Delete(users[0].Team[0], users[1].Team[1]); err != nil {\n\t\tt.Errorf(\"no error should happened when deleting pet, but got %v\", err)\n\t}\n\n\tAssertAssociationCount(t, users, \"Team\", 2, \"after delete\")\n\n\t// Clear\n\tDB.Model(&users).Association(\"Team\").Clear()\n\tAssertAssociationCount(t, users, \"Team\", 0, \"After Clear\")\n}\n\nfunc TestPolymorphicHasManyAssociation(t *testing.T) {\n\tuser := *GetUser(\"hasmany\", Config{Toys: 2})\n\n\tif err := DB.Create(&user).Error; err != nil {\n\t\tt.Fatalf(\"errors happened when create: %v\", err)\n\t}\n\n\tCheckUser(t, user, user)\n\n\t// Find\n\tvar user2 User\n\tDB.Find(&user2, \"id = ?\", user.ID)\n\tDB.Model(&user2).Association(\"Toys\").Find(&user2.Toys)\n\tCheckUser(t, user2, user)\n\n\t// Count\n\tAssertAssociationCount(t, user, \"Toys\", 2, \"\")\n\n\t// Append\n\ttoy := Toy{Name: \"toy-has-many-append\"}\n\n\tif err := DB.Model(&user2).Association(\"Toys\").Append(&toy); err != nil {\n\t\tt.Fatalf(\"Error happened when append account, got %v\", err)\n\t}\n\n\tif toy.ID == 0 {\n\t\tt.Fatalf(\"Toy's ID should be created\")\n\t}\n\n\tuser.Toys = append(user.Toys, toy)\n\tCheckUser(t, user2, user)\n\n\tAssertAssociationCount(t, user, \"Toys\", 3, \"AfterAppend\")\n\n\ttoys := []Toy{{Name: \"toy-has-many-append-1-1\"}, {Name: \"toy-has-many-append-1-1\"}}\n\n\tif err := DB.Model(&user2).Association(\"Toys\").Append(&toys); err != nil {\n\t\tt.Fatalf(\"Error happened when append toy, got %v\", err)\n\t}\n\n\tfor _, toy := range toys {\n\t\ttoy := toy\n\t\tif toy.ID == 0 {\n\t\t\tt.Fatalf(\"Toy's ID should be created\")\n\t\t}\n\n\t\tuser.Toys = append(user.Toys, toy)\n\t}\n\n\tCheckUser(t, user2, user)\n\n\tAssertAssociationCount(t, user, \"Toys\", 5, \"AfterAppendSlice\")\n\n\t// Replace\n\ttoy2 := Toy{Name: \"toy-has-many-replace\"}\n\n\tif err := DB.Model(&user2).Association(\"Toys\").Replace(&toy2); err != nil {\n\t\tt.Fatalf(\"Error happened when append toy, got %v\", err)\n\t}\n\n\tif toy2.ID == 0 {\n\t\tt.Fatalf(\"toy2's ID should be created\")\n\t}\n\n\tuser.Toys = []Toy{toy2}\n\tCheckUser(t, user2, user)\n\n\tAssertAssociationCount(t, user2, \"Toys\", 1, \"AfterReplace\")\n\n\t// Delete\n\tif err := DB.Model(&user2).Association(\"Toys\").Delete(&Toy{}); err != nil {\n\t\tt.Fatalf(\"Error happened when delete toy, got %v\", err)\n\t}\n\tAssertAssociationCount(t, user2, \"Toys\", 1, \"after delete non-existing data\")\n\n\tif err := DB.Model(&user2).Association(\"Toys\").Delete(&toy2); err != nil {\n\t\tt.Fatalf(\"Error happened when delete Toys, got %v\", err)\n\t}\n\tAssertAssociationCount(t, user2, \"Toys\", 0, \"after delete\")\n\n\t// Prepare Data for Clear\n\tif err := DB.Model(&user2).Association(\"Toys\").Append(&toy); err != nil {\n\t\tt.Fatalf(\"Error happened when append Toys, got %v\", err)\n\t}\n\n\tAssertAssociationCount(t, user2, \"Toys\", 1, \"after prepare data\")\n\n\t// Clear\n\tif err := DB.Model(&user2).Association(\"Toys\").Clear(); err != nil {\n\t\tt.Errorf(\"Error happened when clear Toys, got %v\", err)\n\t}\n\n\tAssertAssociationCount(t, user2, \"Toys\", 0, \"after clear\")\n}\n\nfunc TestPolymorphicHasManyAssociationForSlice(t *testing.T) {\n\tusers := []User{\n\t\t*GetUser(\"slice-hasmany-1\", Config{Toys: 2}),\n\t\t*GetUser(\"slice-hasmany-2\", Config{Toys: 0, Tools: 2}),\n\t\t*GetUser(\"slice-hasmany-3\", Config{Toys: 4}),\n\t}\n\n\tDB.Create(&users)\n\n\t// Count\n\tAssertAssociationCount(t, users, \"Toys\", 6, \"\")\n\tAssertAssociationCount(t, users, \"Tools\", 2, \"\")\n\n\t// Find\n\tvar toys []Toy\n\tif DB.Model(&users).Association(\"Toys\").Find(&toys); len(toys) != 6 {\n\t\tt.Errorf(\"toys count should be %v, but got %v\", 6, len(toys))\n\t}\n\n\t// Find Tools (polymorphic with custom type and id)\n\tvar tools []Tools\n\tDB.Model(&users).Association(\"Tools\").Find(&tools)\n\n\tif len(tools) != 2 {\n\t\tt.Errorf(\"tools count should be %v, but got %v\", 2, len(tools))\n\t}\n\n\t// Append\n\tDB.Model(&users).Association(\"Toys\").Append(\n\t\t&Toy{Name: \"toy-slice-append-1\"},\n\t\t[]Toy{{Name: \"toy-slice-append-2-1\"}, {Name: \"toy-slice-append-2-2\"}},\n\t\t&Toy{Name: \"toy-slice-append-3\"},\n\t)\n\n\tAssertAssociationCount(t, users, \"Toys\", 10, \"After Append\")\n\n\t// Replace -> same as append\n\tDB.Model(&users).Association(\"Toys\").Replace(\n\t\t[]*Toy{{Name: \"toy-slice-replace-1-1\"}, {Name: \"toy-slice-replace-1-2\"}},\n\t\t[]*Toy{{Name: \"toy-slice-replace-2-1\"}, {Name: \"toy-slice-replace-2-2\"}},\n\t\t&Toy{Name: \"toy-slice-replace-3\"},\n\t)\n\n\tAssertAssociationCount(t, users, \"Toys\", 5, \"After Append\")\n\n\t// Delete\n\tif err := DB.Model(&users).Association(\"Toys\").Delete(&users[2].Toys); err != nil {\n\t\tt.Errorf(\"no error should happened when deleting toy, but got %v\", err)\n\t}\n\n\tAssertAssociationCount(t, users, \"Toys\", 4, \"after delete\")\n\n\tif err := DB.Model(&users).Association(\"Toys\").Delete(users[0].Toys[0], users[1].Toys[1]); err != nil {\n\t\tt.Errorf(\"no error should happened when deleting toy, but got %v\", err)\n\t}\n\n\tAssertAssociationCount(t, users, \"Toys\", 2, \"after delete\")\n\n\t// Clear\n\tDB.Model(&users).Association(\"Toys\").Clear()\n\tAssertAssociationCount(t, users, \"Toys\", 0, \"After Clear\")\n}\n\nfunc TestHasManyAssociationUnscoped(t *testing.T) {\n\ttype ItemContent struct {\n\t\tgorm.Model\n\t\tItemID       uint   `gorm:\"not null\"`\n\t\tName         string `gorm:\"not null;type:varchar(50)\"`\n\t\tLanguageCode string `gorm:\"not null;type:varchar(2)\"`\n\t}\n\ttype Item struct {\n\t\tgorm.Model\n\t\tLogo     string        `gorm:\"not null;type:varchar(50)\"`\n\t\tContents []ItemContent `gorm:\"foreignKey:ItemID\"`\n\t}\n\n\ttx := DB.Session(&gorm.Session{})\n\ttx.Migrator().DropTable(&ItemContent{}, &Item{})\n\ttx.AutoMigrate(&ItemContent{}, &Item{})\n\n\titem := Item{\n\t\tLogo: \"logo\",\n\t\tContents: []ItemContent{\n\t\t\t{Name: \"name\", LanguageCode: \"en\"},\n\t\t\t{Name: \"ar name\", LanguageCode: \"ar\"},\n\t\t},\n\t}\n\tif err := tx.Create(&item).Error; err != nil {\n\t\tt.Fatalf(\"failed to create items, got error: %v\", err)\n\t}\n\n\t// test Replace\n\tif err := tx.Model(&item).Association(\"Contents\").Unscoped().Replace([]ItemContent{\n\t\t{Name: \"updated name\", LanguageCode: \"en\"},\n\t\t{Name: \"ar updated name\", LanguageCode: \"ar\"},\n\t\t{Name: \"le nom\", LanguageCode: \"fr\"},\n\t}); err != nil {\n\t\tt.Errorf(\"failed to replace item content, got error: %v\", err)\n\t}\n\n\tif count := tx.Model(&item).Association(\"Contents\").Count(); count != 3 {\n\t\tt.Errorf(\"expected %d contents, got %d\", 3, count)\n\t}\n\n\tvar contents []ItemContent\n\tif err := tx.Find(&contents).Error; err != nil {\n\t\tt.Errorf(\"failed to find contents, got error: %v\", err)\n\t}\n\tif len(contents) != 3 {\n\t\tt.Errorf(\"expected %d contents, got %d\", 3, len(contents))\n\t}\n\n\t// test delete\n\tif err := tx.Model(&item).Association(\"Contents\").Unscoped().Delete(&contents[0]); err != nil {\n\t\tt.Errorf(\"failed to delete Contents, got error: %v\", err)\n\t}\n\tif count := tx.Model(&item).Association(\"Contents\").Count(); count != 2 {\n\t\tt.Errorf(\"expected %d contents, got %d\", 2, count)\n\t}\n\n\t// test clear\n\tif err := tx.Model(&item).Association(\"Contents\").Unscoped().Clear(); err != nil {\n\t\tt.Errorf(\"failed to clear contents association, got error: %v\", err)\n\t}\n\tif count := tx.Model(&item).Association(\"Contents\").Count(); count != 0 {\n\t\tt.Errorf(\"expected %d contents, got %d\", 0, count)\n\t}\n\n\tif err := tx.Find(&contents).Error; err != nil {\n\t\tt.Errorf(\"failed to find contents, got error: %v\", err)\n\t}\n\tif len(contents) != 0 {\n\t\tt.Errorf(\"expected %d contents, got %d\", 0, len(contents))\n\t}\n}\n\nfunc TestHasManyAssociationReplaceWithStructValue(t *testing.T) {\n\tuser := User{Name: \"HasManyAssociationReplaceWithStructValue\", Languages: []Language{{Name: \"EN\", Code: \"en\"}}}\n\n\tif err := DB.Create(&user).Error; err != nil {\n\t\tt.Fatalf(\"errors happened when create: %v\", err)\n\t}\n\n\tif err := DB.Model(&user).Association(\"Languages\").Replace(Language{Name: \"DE\", Code: \"de\"}, Language{Name: \"FR\", Code: \"fr\"}); err != nil {\n\t\tt.Error(\"expected association error to be not nil\")\n\t}\n\n\tvar result User\n\tDB.Preload(\"Languages\").Where(\"name = ?\", \"HasManyAssociationReplaceWithStructValue\").Find(&result)\n\tif len(result.Languages) != 2 {\n\t\tt.Errorf(\"invalid languages found for user, got %v\", result.Languages)\n\t}\n}\n"
  },
  {
    "path": "tests/associations_has_one_test.go",
    "content": "package tests_test\n\nimport (\n\t\"testing\"\n\n\t. \"gorm.io/gorm/utils/tests\"\n)\n\nfunc TestHasOneAssociation(t *testing.T) {\n\tuser := *GetUser(\"hasone\", Config{Account: true})\n\n\tif err := DB.Create(&user).Error; err != nil {\n\t\tt.Fatalf(\"errors happened when create: %v\", err)\n\t}\n\n\tCheckUser(t, user, user)\n\n\t// Find\n\tvar user2 User\n\tDB.Find(&user2, \"id = ?\", user.ID)\n\tDB.Model(&user2).Association(\"Account\").Find(&user2.Account)\n\tCheckUser(t, user2, user)\n\n\t// Count\n\tAssertAssociationCount(t, user, \"Account\", 1, \"\")\n\n\t// Append\n\taccount := Account{Number: \"account-has-one-append\"}\n\n\tif err := DB.Model(&user2).Association(\"Account\").Append(&account); err != nil {\n\t\tt.Fatalf(\"Error happened when append account, got %v\", err)\n\t}\n\n\tif account.ID == 0 {\n\t\tt.Fatalf(\"Account's ID should be created\")\n\t}\n\n\tuser.Account = account\n\tCheckUser(t, user2, user)\n\n\tAssertAssociationCount(t, user, \"Account\", 1, \"AfterAppend\")\n\n\t// Replace\n\taccount2 := Account{Number: \"account-has-one-replace\"}\n\n\tif err := DB.Model(&user2).Association(\"Account\").Replace(&account2); err != nil {\n\t\tt.Fatalf(\"Error happened when append Account, got %v\", err)\n\t}\n\n\tif account2.ID == 0 {\n\t\tt.Fatalf(\"account2's ID should be created\")\n\t}\n\n\tuser.Account = account2\n\tCheckUser(t, user2, user)\n\n\tAssertAssociationCount(t, user2, \"Account\", 1, \"AfterReplace\")\n\n\t// Delete\n\tif err := DB.Model(&user2).Association(\"Account\").Delete(&Account{}); err != nil {\n\t\tt.Fatalf(\"Error happened when delete account, got %v\", err)\n\t}\n\tAssertAssociationCount(t, user2, \"Account\", 1, \"after delete non-existing data\")\n\n\tif err := DB.Model(&user2).Association(\"Account\").Delete(&account2); err != nil {\n\t\tt.Fatalf(\"Error happened when delete Account, got %v\", err)\n\t}\n\tAssertAssociationCount(t, user2, \"Account\", 0, \"after delete\")\n\n\t// Prepare Data for Clear\n\taccount = Account{Number: \"account-has-one-append\"}\n\tif err := DB.Model(&user2).Association(\"Account\").Append(&account); err != nil {\n\t\tt.Fatalf(\"Error happened when append Account, got %v\", err)\n\t}\n\n\tAssertAssociationCount(t, user2, \"Account\", 1, \"after prepare data\")\n\n\t// Clear\n\tif err := DB.Model(&user2).Association(\"Account\").Clear(); err != nil {\n\t\tt.Errorf(\"Error happened when clear Account, got %v\", err)\n\t}\n\n\tAssertAssociationCount(t, user2, \"Account\", 0, \"after clear\")\n}\n\nfunc TestHasOneAssociationWithSelect(t *testing.T) {\n\tuser := *GetUser(\"hasone\", Config{Account: true})\n\n\tDB.Omit(\"Account.Number\").Create(&user)\n\n\tAssertAssociationCount(t, user, \"Account\", 1, \"\")\n\n\tvar account Account\n\tDB.Model(&user).Association(\"Account\").Find(&account)\n\tif account.Number != \"\" {\n\t\tt.Errorf(\"account's number should not be saved\")\n\t}\n}\n\nfunc TestHasOneAssociationForSlice(t *testing.T) {\n\tusers := []User{\n\t\t*GetUser(\"slice-hasone-1\", Config{Account: true}),\n\t\t*GetUser(\"slice-hasone-2\", Config{Account: false}),\n\t\t*GetUser(\"slice-hasone-3\", Config{Account: true}),\n\t}\n\n\tDB.Create(&users)\n\n\t// Count\n\tAssertAssociationCount(t, users, \"Account\", 2, \"\")\n\n\t// Find\n\tvar accounts []Account\n\tif DB.Model(&users).Association(\"Account\").Find(&accounts); len(accounts) != 2 {\n\t\tt.Errorf(\"accounts count should be %v, but got %v\", 3, len(accounts))\n\t}\n\n\t// Append\n\tDB.Model(&users).Association(\"Account\").Append(\n\t\t&Account{Number: \"account-slice-append-1\"},\n\t\t&Account{Number: \"account-slice-append-2\"},\n\t\t&Account{Number: \"account-slice-append-3\"},\n\t)\n\n\tAssertAssociationCount(t, users, \"Account\", 3, \"After Append\")\n\n\t// Replace -> same as append\n\n\t// Delete\n\tif err := DB.Model(&users).Association(\"Account\").Delete(&users[0].Account); err != nil {\n\t\tt.Errorf(\"no error should happened when deleting account, but got %v\", err)\n\t}\n\n\tAssertAssociationCount(t, users, \"Account\", 2, \"after delete\")\n\n\t// Clear\n\tDB.Model(&users).Association(\"Account\").Clear()\n\tAssertAssociationCount(t, users, \"Account\", 0, \"After Clear\")\n}\n\nfunc TestPolymorphicHasOneAssociation(t *testing.T) {\n\tpet := Pet{Name: \"hasone\", Toy: Toy{Name: \"toy-has-one\"}}\n\n\tif err := DB.Create(&pet).Error; err != nil {\n\t\tt.Fatalf(\"errors happened when create: %v\", err)\n\t}\n\n\tCheckPet(t, pet, pet)\n\n\t// Find\n\tvar pet2 Pet\n\tDB.Find(&pet2, \"id = ?\", pet.ID)\n\tDB.Model(&pet2).Association(\"Toy\").Find(&pet2.Toy)\n\tCheckPet(t, pet2, pet)\n\n\t// Count\n\tAssertAssociationCount(t, pet, \"Toy\", 1, \"\")\n\n\t// Append\n\ttoy := Toy{Name: \"toy-has-one-append\"}\n\n\tif err := DB.Model(&pet2).Association(\"Toy\").Append(&toy); err != nil {\n\t\tt.Fatalf(\"Error happened when append toy, got %v\", err)\n\t}\n\n\tif toy.ID == 0 {\n\t\tt.Fatalf(\"Toy's ID should be created\")\n\t}\n\n\tpet.Toy = toy\n\tCheckPet(t, pet2, pet)\n\n\tAssertAssociationCount(t, pet, \"Toy\", 1, \"AfterAppend\")\n\n\t// Replace\n\ttoy2 := Toy{Name: \"toy-has-one-replace\"}\n\n\tif err := DB.Model(&pet2).Association(\"Toy\").Replace(&toy2); err != nil {\n\t\tt.Fatalf(\"Error happened when append Toy, got %v\", err)\n\t}\n\n\tif toy2.ID == 0 {\n\t\tt.Fatalf(\"toy2's ID should be created\")\n\t}\n\n\tpet.Toy = toy2\n\tCheckPet(t, pet2, pet)\n\n\tAssertAssociationCount(t, pet2, \"Toy\", 1, \"AfterReplace\")\n\n\t// Delete\n\tif err := DB.Model(&pet2).Association(\"Toy\").Delete(&Toy{}); err != nil {\n\t\tt.Fatalf(\"Error happened when delete toy, got %v\", err)\n\t}\n\tAssertAssociationCount(t, pet2, \"Toy\", 1, \"after delete non-existing data\")\n\n\tif err := DB.Model(&pet2).Association(\"Toy\").Delete(&toy2); err != nil {\n\t\tt.Fatalf(\"Error happened when delete Toy, got %v\", err)\n\t}\n\tAssertAssociationCount(t, pet2, \"Toy\", 0, \"after delete\")\n\n\t// Prepare Data for Clear\n\ttoy = Toy{Name: \"toy-has-one-append\"}\n\tif err := DB.Model(&pet2).Association(\"Toy\").Append(&toy); err != nil {\n\t\tt.Fatalf(\"Error happened when append Toy, got %v\", err)\n\t}\n\n\tAssertAssociationCount(t, pet2, \"Toy\", 1, \"after prepare data\")\n\n\t// Clear\n\tif err := DB.Model(&pet2).Association(\"Toy\").Clear(); err != nil {\n\t\tt.Errorf(\"Error happened when clear Toy, got %v\", err)\n\t}\n\n\tAssertAssociationCount(t, pet2, \"Toy\", 0, \"after clear\")\n}\n\nfunc TestPolymorphicHasOneAssociationForSlice(t *testing.T) {\n\tpets := []Pet{\n\t\t{Name: \"hasone-1\", Toy: Toy{Name: \"toy-has-one\"}},\n\t\t{Name: \"hasone-2\", Toy: Toy{}},\n\t\t{Name: \"hasone-3\", Toy: Toy{Name: \"toy-has-one\"}},\n\t}\n\n\tDB.Create(&pets)\n\n\t// Count\n\tAssertAssociationCount(t, pets, \"Toy\", 2, \"\")\n\n\t// Find\n\tvar toys []Toy\n\tif DB.Model(&pets).Association(\"Toy\").Find(&toys); len(toys) != 2 {\n\t\tt.Errorf(\"toys count should be %v, but got %v\", 3, len(toys))\n\t}\n\n\t// Append\n\tDB.Model(&pets).Association(\"Toy\").Append(\n\t\t&Toy{Name: \"toy-slice-append-1\"},\n\t\t&Toy{Name: \"toy-slice-append-2\"},\n\t\t&Toy{Name: \"toy-slice-append-3\"},\n\t)\n\n\tAssertAssociationCount(t, pets, \"Toy\", 3, \"After Append\")\n\n\t// Replace -> same as append\n\n\t// Delete\n\tif err := DB.Model(&pets).Association(\"Toy\").Delete(&pets[0].Toy); err != nil {\n\t\tt.Errorf(\"no error should happened when deleting toy, but got %v\", err)\n\t}\n\n\tAssertAssociationCount(t, pets, \"Toy\", 2, \"after delete\")\n\n\t// Clear\n\tDB.Model(&pets).Association(\"Toy\").Clear()\n\tAssertAssociationCount(t, pets, \"Toy\", 0, \"After Clear\")\n}\n\nfunc TestHasOneAssociationReplaceWithNonValidValue(t *testing.T) {\n\tuser := User{Name: \"jinzhu\", Account: Account{Number: \"1\"}}\n\n\tif err := DB.Create(&user).Error; err != nil {\n\t\tt.Fatalf(\"errors happened when create: %v\", err)\n\t}\n\n\tif err := DB.Model(&user).Association(\"Languages\").Replace(Account{Number: \"2\"}); err == nil {\n\t\tt.Error(\"expected association error to be not nil\")\n\t}\n}\n"
  },
  {
    "path": "tests/associations_many2many_test.go",
    "content": "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/tests\"\n)\n\nfunc TestMany2ManyAssociation(t *testing.T) {\n\tuser := *GetUser(\"many2many\", Config{Languages: 2})\n\n\tif err := DB.Create(&user).Error; err != nil {\n\t\tt.Fatalf(\"errors happened when create: %v\", err)\n\t}\n\n\tCheckUser(t, user, user)\n\n\t// Find\n\tvar user2 User\n\tDB.Find(&user2, \"id = ?\", user.ID)\n\tDB.Model(&user2).Association(\"Languages\").Find(&user2.Languages)\n\n\tCheckUser(t, user2, user)\n\n\t// Count\n\tAssertAssociationCount(t, user, \"Languages\", 2, \"\")\n\n\t// Append\n\tlanguage := Language{Code: \"language-many2many-append\", Name: \"language-many2many-append\"}\n\tDB.Create(&language)\n\n\tif err := DB.Model(&user2).Association(\"Languages\").Append(&language); err != nil {\n\t\tt.Fatalf(\"Error happened when append account, got %v\", err)\n\t}\n\n\tuser.Languages = append(user.Languages, language)\n\tCheckUser(t, user2, user)\n\n\tAssertAssociationCount(t, user, \"Languages\", 3, \"AfterAppend\")\n\n\tlanguages := []Language{\n\t\t{Code: \"language-many2many-append-1-1\", Name: \"language-many2many-append-1-1\"},\n\t\t{Code: \"language-many2many-append-2-1\", Name: \"language-many2many-append-2-1\"},\n\t}\n\tDB.Create(&languages)\n\n\tif err := DB.Model(&user2).Association(\"Languages\").Append(&languages); err != nil {\n\t\tt.Fatalf(\"Error happened when append language, got %v\", err)\n\t}\n\n\tuser.Languages = append(user.Languages, languages...)\n\n\tCheckUser(t, user2, user)\n\n\tAssertAssociationCount(t, user, \"Languages\", 5, \"AfterAppendSlice\")\n\n\t// Replace\n\tlanguage2 := Language{Code: \"language-many2many-replace\", Name: \"language-many2many-replace\"}\n\tDB.Create(&language2)\n\n\tif err := DB.Model(&user2).Association(\"Languages\").Replace(&language2); err != nil {\n\t\tt.Fatalf(\"Error happened when append language, got %v\", err)\n\t}\n\n\tuser.Languages = []Language{language2}\n\tCheckUser(t, user2, user)\n\n\tAssertAssociationCount(t, user2, \"Languages\", 1, \"AfterReplace\")\n\n\t// Delete\n\tif err := DB.Model(&user2).Association(\"Languages\").Delete(&Language{}); err != nil {\n\t\tt.Fatalf(\"Error happened when delete language, got %v\", err)\n\t}\n\tAssertAssociationCount(t, user2, \"Languages\", 1, \"after delete non-existing data\")\n\n\tif err := DB.Model(&user2).Association(\"Languages\").Delete(&language2); err != nil {\n\t\tt.Fatalf(\"Error happened when delete Languages, got %v\", err)\n\t}\n\tAssertAssociationCount(t, user2, \"Languages\", 0, \"after delete\")\n\n\t// Prepare Data for Clear\n\tif err := DB.Model(&user2).Association(\"Languages\").Append(&language); err != nil {\n\t\tt.Fatalf(\"Error happened when append Languages, got %v\", err)\n\t}\n\n\tAssertAssociationCount(t, user2, \"Languages\", 1, \"after prepare data\")\n\n\t// Clear\n\tif err := DB.Model(&user2).Association(\"Languages\").Clear(); err != nil {\n\t\tt.Errorf(\"Error happened when clear Languages, got %v\", err)\n\t}\n\n\tAssertAssociationCount(t, user2, \"Languages\", 0, \"after clear\")\n}\n\nfunc TestMany2ManyOmitAssociations(t *testing.T) {\n\ttidbSkip(t, \"not support the foreign key feature\")\n\n\tuser := *GetUser(\"many2many_omit_associations\", Config{Languages: 2})\n\n\tif err := DB.Omit(\"Languages.*\").Create(&user).Error; err == nil {\n\t\tt.Fatalf(\"should raise error when create users without languages reference\")\n\t}\n\n\tif err := DB.Create(&user.Languages).Error; err != nil {\n\t\tt.Fatalf(\"no error should happen when create languages, but got %v\", err)\n\t}\n\n\tif err := DB.Omit(\"Languages.*\").Create(&user).Error; err != nil {\n\t\tt.Fatalf(\"no error should happen when create user when languages exists, but got %v\", err)\n\t}\n\n\t// Find\n\tvar languages []Language\n\tif DB.Model(&user).Association(\"Languages\").Find(&languages); len(languages) != 2 {\n\t\tt.Errorf(\"languages count should be %v, but got %v\", 2, len(languages))\n\t}\n\n\tnewLang := Language{Code: \"omitmany2many\", Name: \"omitmany2many\"}\n\tif err := DB.Model(&user).Omit(\"Languages.*\").Association(\"Languages\").Replace(&newLang); err == nil {\n\t\tt.Errorf(\"should failed to insert languages due to constraint failed, error: %v\", err)\n\t}\n}\n\nfunc TestMany2ManyAssociationForSlice(t *testing.T) {\n\tusers := []User{\n\t\t*GetUser(\"slice-many2many-1\", Config{Languages: 2}),\n\t\t*GetUser(\"slice-many2many-2\", Config{Languages: 0}),\n\t\t*GetUser(\"slice-many2many-3\", Config{Languages: 4}),\n\t}\n\n\tDB.Create(&users)\n\n\t// Count\n\tAssertAssociationCount(t, users, \"Languages\", 6, \"\")\n\n\t// Find\n\tvar languages []Language\n\tif DB.Model(&users).Association(\"Languages\").Find(&languages); len(languages) != 6 {\n\t\tt.Errorf(\"languages count should be %v, but got %v\", 6, len(languages))\n\t}\n\n\t// Append\n\tlanguages1 := []Language{\n\t\t{Code: \"language-many2many-append-1\", Name: \"language-many2many-append-1\"},\n\t}\n\tlanguages2 := []Language{}\n\tlanguages3 := []Language{\n\t\t{Code: \"language-many2many-append-3-1\", Name: \"language-many2many-append-3-1\"},\n\t\t{Code: \"language-many2many-append-3-2\", Name: \"language-many2many-append-3-2\"},\n\t}\n\tDB.Create(&languages1)\n\tDB.Create(&languages3)\n\n\tDB.Model(&users).Association(\"Languages\").Append(&languages1, &languages2, &languages3)\n\n\tAssertAssociationCount(t, users, \"Languages\", 9, \"After Append\")\n\n\tlanguages2_1 := []*Language{\n\t\t{Code: \"language-slice-replace-1-1\", Name: \"language-slice-replace-1-1\"},\n\t\t{Code: \"language-slice-replace-1-2\", Name: \"language-slice-replace-1-2\"},\n\t}\n\tlanguages2_2 := []*Language{\n\t\t{Code: \"language-slice-replace-2-1\", Name: \"language-slice-replace-2-1\"},\n\t\t{Code: \"language-slice-replace-2-2\", Name: \"language-slice-replace-2-2\"},\n\t}\n\tlanguages2_3 := &Language{Code: \"language-slice-replace-3\", Name: \"language-slice-replace-3\"}\n\tDB.Create(&languages2_1)\n\tDB.Create(&languages2_2)\n\tDB.Create(&languages2_3)\n\n\t// Replace\n\tDB.Model(&users).Association(\"Languages\").Replace(&languages2_1, &languages2_2, languages2_3)\n\n\tAssertAssociationCount(t, users, \"Languages\", 5, \"After Replace\")\n\n\t// Delete\n\tif err := DB.Model(&users).Association(\"Languages\").Delete(&users[2].Languages); err != nil {\n\t\tt.Errorf(\"no error should happened when deleting language, but got %v\", err)\n\t}\n\n\tAssertAssociationCount(t, users, \"Languages\", 4, \"after delete\")\n\n\tif err := DB.Model(&users).Association(\"Languages\").Delete(users[0].Languages[0], users[1].Languages[1]); err != nil {\n\t\tt.Errorf(\"no error should happened when deleting language, but got %v\", err)\n\t}\n\n\tAssertAssociationCount(t, users, \"Languages\", 2, \"after delete\")\n\n\t// Clear\n\tDB.Model(&users).Association(\"Languages\").Clear()\n\tAssertAssociationCount(t, users, \"Languages\", 0, \"After Clear\")\n}\n\nfunc TestSingleTableMany2ManyAssociation(t *testing.T) {\n\tuser := *GetUser(\"many2many\", Config{Friends: 2})\n\n\tif err := DB.Create(&user).Error; err != nil {\n\t\tt.Fatalf(\"errors happened when create: %v\", err)\n\t}\n\n\tCheckUser(t, user, user)\n\n\t// Find\n\tvar user2 User\n\tDB.Find(&user2, \"id = ?\", user.ID)\n\tDB.Model(&user2).Association(\"Friends\").Find(&user2.Friends)\n\n\tCheckUser(t, user2, user)\n\n\t// Count\n\tAssertAssociationCount(t, user, \"Friends\", 2, \"\")\n\n\t// Append\n\tfriend := *GetUser(\"friend\", Config{})\n\n\tif err := DB.Model(&user2).Association(\"Friends\").Append(&friend); err != nil {\n\t\tt.Fatalf(\"Error happened when append account, got %v\", err)\n\t}\n\n\tuser.Friends = append(user.Friends, &friend)\n\tCheckUser(t, user2, user)\n\n\tAssertAssociationCount(t, user, \"Friends\", 3, \"AfterAppend\")\n\n\tfriends := []*User{GetUser(\"friend-append-1\", Config{}), GetUser(\"friend-append-2\", Config{})}\n\n\tif err := DB.Model(&user2).Association(\"Friends\").Append(&friends); err != nil {\n\t\tt.Fatalf(\"Error happened when append friend, got %v\", err)\n\t}\n\n\tuser.Friends = append(user.Friends, friends...)\n\n\tCheckUser(t, user2, user)\n\n\tAssertAssociationCount(t, user, \"Friends\", 5, \"AfterAppendSlice\")\n\n\t// Replace\n\tfriend2 := *GetUser(\"friend-replace-2\", Config{})\n\n\tif err := DB.Model(&user2).Association(\"Friends\").Replace(&friend2); err != nil {\n\t\tt.Fatalf(\"Error happened when append friend, got %v\", err)\n\t}\n\n\tuser.Friends = []*User{&friend2}\n\tCheckUser(t, user2, user)\n\n\tAssertAssociationCount(t, user2, \"Friends\", 1, \"AfterReplace\")\n\n\t// Delete\n\tif err := DB.Model(&user2).Association(\"Friends\").Delete(&User{}); err != nil {\n\t\tt.Fatalf(\"Error happened when delete friend, got %v\", err)\n\t}\n\tAssertAssociationCount(t, user2, \"Friends\", 1, \"after delete non-existing data\")\n\n\tif err := DB.Model(&user2).Association(\"Friends\").Delete(&friend2); err != nil {\n\t\tt.Fatalf(\"Error happened when delete Friends, got %v\", err)\n\t}\n\tAssertAssociationCount(t, user2, \"Friends\", 0, \"after delete\")\n\n\t// Prepare Data for Clear\n\tif err := DB.Model(&user2).Association(\"Friends\").Append(&friend); err != nil {\n\t\tt.Fatalf(\"Error happened when append Friends, got %v\", err)\n\t}\n\n\tAssertAssociationCount(t, user2, \"Friends\", 1, \"after prepare data\")\n\n\t// Clear\n\tif err := DB.Model(&user2).Association(\"Friends\").Clear(); err != nil {\n\t\tt.Errorf(\"Error happened when clear Friends, got %v\", err)\n\t}\n\n\tAssertAssociationCount(t, user2, \"Friends\", 0, \"after clear\")\n}\n\nfunc TestSingleTableMany2ManyAssociationForSlice(t *testing.T) {\n\tusers := []User{\n\t\t*GetUser(\"slice-many2many-1\", Config{Team: 2}),\n\t\t*GetUser(\"slice-many2many-2\", Config{Team: 0}),\n\t\t*GetUser(\"slice-many2many-3\", Config{Team: 4}),\n\t}\n\n\tDB.Create(&users)\n\n\t// Count\n\tAssertAssociationCount(t, users, \"Team\", 6, \"\")\n\n\t// Find\n\tvar teams []User\n\tif DB.Model(&users).Association(\"Team\").Find(&teams); len(teams) != 6 {\n\t\tt.Errorf(\"teams count should be %v, but got %v\", 6, len(teams))\n\t}\n\n\t// Append\n\tteams1 := []User{*GetUser(\"friend-append-1\", Config{})}\n\tteams2 := []User{}\n\tteams3 := []*User{GetUser(\"friend-append-3-1\", Config{}), GetUser(\"friend-append-3-2\", Config{})}\n\n\tDB.Model(&users).Association(\"Team\").Append(&teams1, &teams2, &teams3)\n\n\tAssertAssociationCount(t, users, \"Team\", 9, \"After Append\")\n\n\tteams2_1 := []User{*GetUser(\"friend-replace-1\", Config{}), *GetUser(\"friend-replace-2\", Config{})}\n\tteams2_2 := []User{*GetUser(\"friend-replace-2-1\", Config{}), *GetUser(\"friend-replace-2-2\", Config{})}\n\tteams2_3 := GetUser(\"friend-replace-3-1\", Config{})\n\n\t// Replace\n\tDB.Model(&users).Association(\"Team\").Replace(&teams2_1, &teams2_2, teams2_3)\n\n\tAssertAssociationCount(t, users, \"Team\", 5, \"After Replace\")\n\n\t// Delete\n\tif err := DB.Model(&users).Association(\"Team\").Delete(&users[2].Team); err != nil {\n\t\tt.Errorf(\"no error should happened when deleting team, but got %v\", err)\n\t}\n\n\tAssertAssociationCount(t, users, \"Team\", 4, \"after delete\")\n\n\tif err := DB.Model(&users).Association(\"Team\").Delete(users[0].Team[0], users[1].Team[1]); err != nil {\n\t\tt.Errorf(\"no error should happened when deleting team, but got %v\", err)\n\t}\n\n\tAssertAssociationCount(t, users, \"Team\", 2, \"after delete\")\n\n\t// Clear\n\tDB.Model(&users).Association(\"Team\").Clear()\n\tAssertAssociationCount(t, users, \"Team\", 0, \"After Clear\")\n}\n\nfunc TestDuplicateMany2ManyAssociation(t *testing.T) {\n\tuser1 := User{Name: \"TestDuplicateMany2ManyAssociation-1\", Languages: []Language{\n\t\t{Code: \"TestDuplicateMany2ManyAssociation-language-1\"},\n\t\t{Code: \"TestDuplicateMany2ManyAssociation-language-2\"},\n\t}}\n\n\tuser2 := User{Name: \"TestDuplicateMany2ManyAssociation-1\", Languages: []Language{\n\t\t{Code: \"TestDuplicateMany2ManyAssociation-language-1\"},\n\t\t{Code: \"TestDuplicateMany2ManyAssociation-language-3\"},\n\t}}\n\tusers := []*User{&user1, &user2}\n\tvar err error\n\terr = DB.Session(&gorm.Session{FullSaveAssociations: true}).Save(users).Error\n\tAssertEqual(t, nil, err)\n\n\tvar findUser1 User\n\terr = DB.Preload(\"Languages\").Where(\"id = ?\", user1.ID).First(&findUser1).Error\n\tAssertEqual(t, nil, err)\n\tAssertEqual(t, user1, findUser1)\n\n\tvar findUser2 User\n\terr = DB.Preload(\"Languages\").Where(\"id = ?\", user2.ID).First(&findUser2).Error\n\tAssertEqual(t, nil, err)\n\tAssertEqual(t, user2, findUser2)\n}\n\nfunc TestConcurrentMany2ManyAssociation(t *testing.T) {\n\tdb, err := OpenTestConnection(&gorm.Config{})\n\tif err != nil {\n\t\tt.Fatalf(\"open test connection failed, err: %+v\", err)\n\t}\n\n\tcount := 3\n\n\tvar languages []Language\n\tfor i := 0; i < count; i++ {\n\t\tlanguage := Language{Code: fmt.Sprintf(\"consurrent %d\", i)}\n\t\tdb.Create(&language)\n\t\tlanguages = append(languages, language)\n\t}\n\n\tuser := User{}\n\tdb.Create(&user)\n\tdb.Preload(\"Languages\").FirstOrCreate(&user)\n\n\tvar wg sync.WaitGroup\n\tfor i := 0; i < count; i++ {\n\t\twg.Add(1)\n\t\tgo func(user User, language Language) {\n\t\t\terr := db.Model(&user).Association(\"Languages\").Append(&language)\n\t\t\tAssertEqual(t, err, nil)\n\n\t\t\twg.Done()\n\t\t}(user, languages[i])\n\t}\n\twg.Wait()\n\n\tvar find User\n\terr = db.Preload(clause.Associations).Where(\"id = ?\", user.ID).First(&find).Error\n\tAssertEqual(t, err, nil)\n\tAssertAssociationCount(t, find, \"Languages\", int64(count), \"after concurrent append\")\n}\n\nfunc TestMany2ManyDuplicateBelongsToAssociation(t *testing.T) {\n\tuser1 := User{Name: \"TestMany2ManyDuplicateBelongsToAssociation-1\", Friends: []*User{\n\t\t{Name: \"TestMany2ManyDuplicateBelongsToAssociation-friend-1\", Company: Company{\n\t\t\tID:   1,\n\t\t\tName: \"Test-company-1\",\n\t\t}},\n\t}}\n\n\tuser2 := User{Name: \"TestMany2ManyDuplicateBelongsToAssociation-2\", Friends: []*User{\n\t\t{Name: \"TestMany2ManyDuplicateBelongsToAssociation-friend-2\", Company: Company{\n\t\t\tID:   1,\n\t\t\tName: \"Test-company-1\",\n\t\t}},\n\t}}\n\tusers := []*User{&user1, &user2}\n\tvar err error\n\terr = DB.Session(&gorm.Session{FullSaveAssociations: true}).Save(users).Error\n\tAssertEqual(t, nil, err)\n\n\tvar findUser1 User\n\terr = DB.Preload(\"Friends.Company\").Where(\"id = ?\", user1.ID).First(&findUser1).Error\n\tAssertEqual(t, nil, err)\n\tAssertEqual(t, user1, findUser1)\n\n\tvar findUser2 User\n\terr = DB.Preload(\"Friends.Company\").Where(\"id = ?\", user2.ID).First(&findUser2).Error\n\tAssertEqual(t, nil, err)\n\tAssertEqual(t, user2, findUser2)\n}\n"
  },
  {
    "path": "tests/associations_test.go",
    "content": "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/utils/tests\"\n)\n\nfunc AssertAssociationCount(t *testing.T, data interface{}, name string, result int64, reason string) {\n\tif count := DB.Model(data).Association(name).Count(); count != result {\n\t\tt.Fatalf(\"invalid %v count %v, expects: %v got %v\", name, reason, result, count)\n\t}\n\n\tvar newUser User\n\tif user, ok := data.(User); ok {\n\t\tDB.Find(&newUser, \"id = ?\", user.ID)\n\t} else if user, ok := data.(*User); ok {\n\t\tDB.Find(&newUser, \"id = ?\", user.ID)\n\t}\n\n\tif newUser.ID != 0 {\n\t\tif count := DB.Model(&newUser).Association(name).Count(); count != result {\n\t\t\tt.Fatalf(\"invalid %v count %v, expects: %v got %v\", name, reason, result, count)\n\t\t}\n\t}\n}\n\nfunc TestInvalidAssociation(t *testing.T) {\n\tuser := *GetUser(\"invalid\", Config{Company: true, Manager: true})\n\tif err := DB.Model(&user).Association(\"Invalid\").Find(&user.Company).Error; err == nil {\n\t\tt.Fatalf(\"should return errors for invalid association, but got nil\")\n\t}\n}\n\nfunc TestAssociationNotNullClear(t *testing.T) {\n\ttype Profile struct {\n\t\tgorm.Model\n\t\tNumber   string\n\t\tMemberID uint `gorm:\"not null\"`\n\t}\n\n\ttype Member struct {\n\t\tgorm.Model\n\t\tProfiles []Profile\n\t}\n\n\tDB.Migrator().DropTable(&Member{}, &Profile{})\n\n\tif err := DB.AutoMigrate(&Member{}, &Profile{}); err != nil {\n\t\tt.Fatalf(\"Failed to migrate, got error: %v\", err)\n\t}\n\n\tmember := &Member{\n\t\tProfiles: []Profile{{\n\t\t\tNumber: \"1\",\n\t\t}, {\n\t\t\tNumber: \"2\",\n\t\t}},\n\t}\n\n\tif err := DB.Create(&member).Error; err != nil {\n\t\tt.Fatalf(\"Failed to create test data, got error: %v\", err)\n\t}\n\n\tif err := DB.Model(member).Association(\"Profiles\").Clear(); err == nil {\n\t\tt.Fatalf(\"No error occurred during clearind not null association\")\n\t}\n}\n\nfunc TestForeignKeyConstraints(t *testing.T) {\n\ttidbSkip(t, \"not support the foreign key feature\")\n\n\ttype Profile struct {\n\t\tID       uint\n\t\tName     string\n\t\tMemberID uint\n\t}\n\n\ttype Member struct {\n\t\tID      uint\n\t\tRefer   uint `gorm:\"uniqueIndex\"`\n\t\tName    string\n\t\tProfile Profile `gorm:\"Constraint:OnUpdate:CASCADE,OnDelete:CASCADE;FOREIGNKEY:MemberID;References:Refer\"`\n\t}\n\n\tDB.Migrator().DropTable(&Profile{}, &Member{})\n\n\tif err := DB.AutoMigrate(&Profile{}, &Member{}); err != nil {\n\t\tt.Fatalf(\"Failed to migrate, got error: %v\", err)\n\t}\n\n\tmember := Member{Refer: 1, Name: \"foreign_key_constraints\", Profile: Profile{Name: \"my_profile\"}}\n\n\tDB.Create(&member)\n\n\tvar profile Profile\n\tif err := DB.First(&profile, \"id = ?\", member.Profile.ID).Error; err != nil {\n\t\tt.Fatalf(\"failed to find profile, got error: %v\", err)\n\t} else if profile.MemberID != member.ID {\n\t\tt.Fatalf(\"member id is not equal: expects: %v, got: %v\", member.ID, profile.MemberID)\n\t}\n\n\tmember.Profile = Profile{}\n\tDB.Model(&member).Update(\"Refer\", 100)\n\n\tvar profile2 Profile\n\tif err := DB.First(&profile2, \"id = ?\", profile.ID).Error; err != nil {\n\t\tt.Fatalf(\"failed to find profile, got error: %v\", err)\n\t} else if profile2.MemberID != 100 {\n\t\tt.Fatalf(\"member id is not equal: expects: %v, got: %v\", 100, profile2.MemberID)\n\t}\n\n\tif r := DB.Delete(&member); r.Error != nil || r.RowsAffected != 1 {\n\t\tt.Fatalf(\"Should delete member, got error: %v, affected: %v\", r.Error, r.RowsAffected)\n\t}\n\n\tvar result Member\n\tif err := DB.First(&result, member.ID).Error; err == nil {\n\t\tt.Fatalf(\"Should not find deleted member\")\n\t}\n\n\tif err := DB.First(&profile2, profile.ID).Error; err == nil {\n\t\tt.Fatalf(\"Should not find deleted profile\")\n\t}\n}\n\nfunc TestForeignKeyConstraintsBelongsTo(t *testing.T) {\n\ttidbSkip(t, \"not support the foreign key feature\")\n\n\ttype Profile struct {\n\t\tID    uint\n\t\tName  string\n\t\tRefer uint `gorm:\"uniqueIndex\"`\n\t}\n\n\ttype Member struct {\n\t\tID        uint\n\t\tName      string\n\t\tProfileID uint\n\t\tProfile   Profile `gorm:\"Constraint:OnUpdate:CASCADE,OnDelete:CASCADE;FOREIGNKEY:ProfileID;References:Refer\"`\n\t}\n\n\tDB.Migrator().DropTable(&Profile{}, &Member{})\n\n\tif err := DB.AutoMigrate(&Profile{}, &Member{}); err != nil {\n\t\tt.Fatalf(\"Failed to migrate, got error: %v\", err)\n\t}\n\n\tmember := Member{Name: \"foreign_key_constraints_belongs_to\", Profile: Profile{Name: \"my_profile_belongs_to\", Refer: 1}}\n\n\tDB.Create(&member)\n\n\tvar profile Profile\n\tif err := DB.First(&profile, \"id = ?\", member.Profile.ID).Error; err != nil {\n\t\tt.Fatalf(\"failed to find profile, got error: %v\", err)\n\t} else if profile.Refer != member.ProfileID {\n\t\tt.Fatalf(\"member id is not equal: expects: %v, got: %v\", profile.Refer, member.ProfileID)\n\t}\n\n\tDB.Model(&profile).Update(\"Refer\", 100)\n\n\tvar member2 Member\n\tif err := DB.First(&member2, \"id = ?\", member.ID).Error; err != nil {\n\t\tt.Fatalf(\"failed to find member, got error: %v\", err)\n\t} else if member2.ProfileID != 100 {\n\t\tt.Fatalf(\"member id is not equal: expects: %v, got: %v\", 100, member2.ProfileID)\n\t}\n\n\tif r := DB.Delete(&profile); r.Error != nil || r.RowsAffected != 1 {\n\t\tt.Fatalf(\"Should delete member, got error: %v, affected: %v\", r.Error, r.RowsAffected)\n\t}\n\n\tvar result Member\n\tif err := DB.First(&result, member.ID).Error; err == nil {\n\t\tt.Fatalf(\"Should not find deleted member\")\n\t}\n\n\tif err := DB.First(&profile, profile.ID).Error; err == nil {\n\t\tt.Fatalf(\"Should not find deleted profile\")\n\t}\n}\n\nfunc TestFullSaveAssociations(t *testing.T) {\n\tcoupon := &Coupon{\n\t\tAppliesToProduct: []*CouponProduct{\n\t\t\t{ProductId: \"full-save-association-product1\"},\n\t\t},\n\t\tAmountOff:  10,\n\t\tPercentOff: 0.0,\n\t}\n\n\terr := DB.\n\t\tSession(&gorm.Session{FullSaveAssociations: true}).\n\t\tCreate(coupon).Error\n\tif err != nil {\n\t\tt.Errorf(\"Failed, got error: %v\", err)\n\t}\n\n\tif DB.First(&Coupon{}, \"id = ?\", coupon.ID).Error != nil {\n\t\tt.Errorf(\"Failed to query saved coupon\")\n\t}\n\n\tif DB.First(&CouponProduct{}, \"coupon_id = ? AND product_id = ?\", coupon.ID, \"full-save-association-product1\").Error != nil {\n\t\tt.Errorf(\"Failed to query saved association\")\n\t}\n\n\torders := []Order{{Num: \"order1\", Coupon: coupon}, {Num: \"order2\", Coupon: coupon}}\n\tif err := DB.Create(&orders).Error; err != nil {\n\t\tt.Errorf(\"failed to create orders, got %v\", err)\n\t}\n\n\tcoupon2 := Coupon{\n\t\tAppliesToProduct: []*CouponProduct{{Desc: \"coupon-description\"}},\n\t}\n\n\tDB.Session(&gorm.Session{FullSaveAssociations: true}).Create(&coupon2)\n\tvar result Coupon\n\tif err := DB.Preload(\"AppliesToProduct\").First(&result, \"id = ?\", coupon2.ID).Error; err != nil {\n\t\tt.Errorf(\"Failed to create coupon w/o name, got error: %v\", err)\n\t}\n\n\tif len(result.AppliesToProduct) != 1 {\n\t\tt.Errorf(\"Failed to preload AppliesToProduct\")\n\t}\n}\n\nfunc TestSaveBelongsCircularReference(t *testing.T) {\n\tparent := Parent{}\n\tDB.Create(&parent)\n\n\tchild := Child{ParentID: &parent.ID, Parent: &parent}\n\tDB.Create(&child)\n\n\tparent.FavChildID = child.ID\n\tparent.FavChild = &child\n\tDB.Save(&parent)\n\n\tvar parent1 Parent\n\tDB.First(&parent1, parent.ID)\n\tAssertObjEqual(t, parent, parent1, \"ID\", \"FavChildID\")\n\n\t// Save and Updates is the same\n\tDB.Updates(&parent)\n\tDB.First(&parent1, parent.ID)\n\tAssertObjEqual(t, parent, parent1, \"ID\", \"FavChildID\")\n}\n\nfunc TestSaveHasManyCircularReference(t *testing.T) {\n\tparent := Parent{}\n\tDB.Create(&parent)\n\n\tchild := Child{ParentID: &parent.ID, Parent: &parent, Name: \"HasManyCircularReference\"}\n\tchild1 := Child{ParentID: &parent.ID, Parent: &parent, Name: \"HasManyCircularReference1\"}\n\n\tparent.Children = []*Child{&child, &child1}\n\tDB.Save(&parent)\n\n\tvar children []*Child\n\tDB.Where(\"parent_id = ?\", parent.ID).Find(&children)\n\tif len(children) != len(parent.Children) ||\n\t\tchildren[0].ID != parent.Children[0].ID ||\n\t\tchildren[1].ID != parent.Children[1].ID {\n\t\tt.Errorf(\"circular reference children save not equal children:%v parent.Children:%v\",\n\t\t\tchildren, parent.Children)\n\t}\n}\n\nfunc TestAssociationError(t *testing.T) {\n\tuser := *GetUser(\"TestAssociationError\", Config{Pets: 2, Company: true, Account: true, Languages: 2})\n\tDB.Create(&user)\n\n\tvar user1 User\n\tDB.Preload(\"Company\").Preload(\"Pets\").Preload(\"Account\").Preload(\"Languages\").First(&user1)\n\n\tvar emptyUser User\n\tvar err error\n\t// belongs to\n\terr = DB.Model(&emptyUser).Association(\"Company\").Delete(&user1.Company)\n\tAssertEqual(t, err, gorm.ErrPrimaryKeyRequired)\n\t// has many\n\terr = DB.Model(&emptyUser).Association(\"Pets\").Delete(&user1.Pets)\n\tAssertEqual(t, err, gorm.ErrPrimaryKeyRequired)\n\t// has one\n\terr = DB.Model(&emptyUser).Association(\"Account\").Delete(&user1.Account)\n\tAssertEqual(t, err, gorm.ErrPrimaryKeyRequired)\n\t// many to many\n\terr = DB.Model(&emptyUser).Association(\"Languages\").Delete(&user1.Languages)\n\tAssertEqual(t, err, gorm.ErrPrimaryKeyRequired)\n}\n\ntype (\n\tmyType           string\n\temptyQueryClause struct {\n\t\tField *schema.Field\n\t}\n)\n\nfunc (myType) QueryClauses(f *schema.Field) []clause.Interface {\n\treturn []clause.Interface{emptyQueryClause{Field: f}}\n}\n\nfunc (sd emptyQueryClause) Name() string {\n\treturn \"empty\"\n}\n\nfunc (sd emptyQueryClause) Build(clause.Builder) {\n}\n\nfunc (sd emptyQueryClause) MergeClause(*clause.Clause) {\n}\n\nfunc (sd emptyQueryClause) ModifyStatement(stmt *gorm.Statement) {\n\t// do nothing\n}\n\nfunc TestAssociationEmptyQueryClause(t *testing.T) {\n\ttype Organization struct {\n\t\tgorm.Model\n\t\tName string\n\t}\n\ttype Region struct {\n\t\tgorm.Model\n\t\tName          string\n\t\tOrganizations []Organization `gorm:\"many2many:region_orgs;\"`\n\t}\n\ttype RegionOrg struct {\n\t\tRegionId       uint\n\t\tOrganizationId uint\n\t\tEmpty          myType\n\t}\n\tif err := DB.SetupJoinTable(&Region{}, \"Organizations\", &RegionOrg{}); err != nil {\n\t\tt.Fatalf(\"Failed to set up join table, got error: %s\", err)\n\t}\n\tif err := DB.Migrator().DropTable(&Organization{}, &Region{}); err != nil {\n\t\tt.Fatalf(\"Failed to migrate, got error: %s\", err)\n\t}\n\tif err := DB.AutoMigrate(&Organization{}, &Region{}); err != nil {\n\t\tt.Fatalf(\"Failed to migrate, got error: %v\", err)\n\t}\n\tregion := &Region{Name: \"Region1\"}\n\tif err := DB.Create(region).Error; err != nil {\n\t\tt.Fatalf(\"fail to create region %v\", err)\n\t}\n\tvar orgs []Organization\n\n\tif err := DB.Model(&Region{}).Association(\"Organizations\").Find(&orgs); err != nil {\n\t\tt.Fatalf(\"fail to find region organizations %v\", err)\n\t} else {\n\t\tAssertEqual(t, len(orgs), 0)\n\t}\n}\n\ntype AssociationEmptyUser struct {\n\tID   uint\n\tName string\n\tPets []AssociationEmptyPet\n}\n\ntype AssociationEmptyPet struct {\n\tAssociationEmptyUserID *uint  `gorm:\"uniqueIndex:uniq_user_id_name\"`\n\tName                   string `gorm:\"uniqueIndex:uniq_user_id_name;size:256\"`\n}\n\nfunc TestAssociationEmptyPrimaryKey(t *testing.T) {\n\tif DB.Dialector.Name() != \"mysql\" {\n\t\tt.Skip()\n\t}\n\tDB.Migrator().DropTable(&AssociationEmptyUser{}, &AssociationEmptyPet{})\n\tDB.AutoMigrate(&AssociationEmptyUser{}, &AssociationEmptyPet{})\n\n\tid := uint(100)\n\tuser := AssociationEmptyUser{\n\t\tID:   id,\n\t\tName: \"jinzhu\",\n\t\tPets: []AssociationEmptyPet{\n\t\t\t{AssociationEmptyUserID: &id, Name: \"bar\"},\n\t\t\t{AssociationEmptyUserID: &id, Name: \"foo\"},\n\t\t},\n\t}\n\n\terr := DB.Session(&gorm.Session{FullSaveAssociations: true}).Create(&user).Error\n\tif err != nil {\n\t\tt.Fatalf(\"Failed to create, got error: %v\", err)\n\t}\n\n\tvar result AssociationEmptyUser\n\terr = DB.Preload(\"Pets\").First(&result, &id).Error\n\tif err != nil {\n\t\tt.Fatalf(\"Failed to find, got error: %v\", err)\n\t}\n\n\tAssertEqual(t, result, user)\n}\n\n// Ensure Association.Append/Replace supports map for many2many\nfunc TestAssociationMany2ManyAppendMap(t *testing.T) {\n\tuser := *GetUser(\"assoc_m2m_append_map\", Config{})\n\tif err := DB.Create(&user).Error; err != nil {\n\t\tt.Fatalf(\"create user: %v\", err)\n\t}\n\n\t// Append single map\n\tif err := DB.Model(&user).Association(\"Languages\").Append(map[string]interface{}{\n\t\t\"code\": \"am2m_map_1\", \"name\": \"AppendMap1\",\n\t}); err != nil {\n\t\tt.Fatalf(\"append map: %v\", err)\n\t}\n\tAssertAssociationCount(t, user, \"Languages\", 1, \"after append 1 map\")\n\n\t// Append more maps individually\n\tif err := DB.Model(&user).Association(\"Languages\").Append(map[string]interface{}{\"code\": \"am2m_map_2\", \"name\": \"AppendMap2\"}); err != nil {\n\t\tt.Fatalf(\"append map 2: %v\", err)\n\t}\n\tif err := DB.Model(&user).Association(\"Languages\").Append(map[string]interface{}{\"code\": \"am2m_map_3\", \"name\": \"AppendMap3\"}); err != nil {\n\t\tt.Fatalf(\"append map 3: %v\", err)\n\t}\n\tAssertAssociationCount(t, user, \"Languages\", 3, \"after append 3 maps total\")\n\n\t// Verify codes exist\n\tvar langs []Language\n\tif err := DB.Model(&user).Association(\"Languages\").Find(&langs); err != nil {\n\t\tt.Fatalf(\"find languages: %v\", err)\n\t}\n\tcodeSet := map[string]bool{}\n\tfor _, l := range langs {\n\t\tcodeSet[l.Code] = true\n\t}\n\tfor _, c := range []string{\"am2m_map_1\", \"am2m_map_2\", \"am2m_map_3\"} {\n\t\tif !codeSet[c] {\n\t\t\tt.Fatalf(\"expected language code %s present\", c)\n\t\t}\n\t}\n}\n\nfunc TestAssociationMany2ManyReplaceMap(t *testing.T) {\n\tuser := *GetUser(\"assoc_m2m_replace_map\", Config{})\n\tif err := DB.Create(&user).Error; err != nil {\n\t\tt.Fatalf(\"create user: %v\", err)\n\t}\n\n\t// Prime with one language\n\tif err := DB.Model(&user).Association(\"Languages\").Append(&Language{Code: \"prime\", Name: \"Prime\"}); err != nil {\n\t\tt.Fatalf(\"prime append: %v\", err)\n\t}\n\tAssertAssociationCount(t, user, \"Languages\", 1, \"before replace\")\n\n\t// Replace with a new map value\n\tif err := DB.Model(&user).Association(\"Languages\").Replace(map[string]interface{}{\n\t\t\"code\": \"rm2m_map_1\", \"name\": \"ReplaceMap1\",\n\t}); err != nil {\n\t\tt.Fatalf(\"replace map: %v\", err)\n\t}\n\tAssertAssociationCount(t, user, \"Languages\", 1, \"after replace with 1 map\")\n\n\tvar langs []Language\n\tif err := DB.Model(&user).Association(\"Languages\").Find(&langs); err != nil {\n\t\tt.Fatalf(\"find languages after replace: %v\", err)\n\t}\n\tif len(langs) != 1 || langs[0].Code != \"rm2m_map_1\" {\n\t\tt.Fatalf(\"expected only rm2m_map_1 after replace, got %+v\", langs)\n\t}\n}\n"
  },
  {
    "path": "tests/benchmark_test.go",
    "content": "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\tuser := *GetUser(\"bench\", Config{})\n\n\tfor x := 0; x < b.N; x++ {\n\t\tuser.ID = 0\n\t\tDB.Create(&user)\n\t}\n}\n\nfunc BenchmarkFind(b *testing.B) {\n\tuser := *GetUser(\"find\", Config{})\n\tDB.Create(&user)\n\n\tfor x := 0; x < b.N; x++ {\n\t\tDB.Find(&User{}, \"id = ?\", user.ID)\n\t}\n}\n\nfunc BenchmarkScan(b *testing.B) {\n\tuser := *GetUser(\"scan\", Config{})\n\tDB.Create(&user)\n\n\tvar u User\n\tb.ResetTimer()\n\tfor x := 0; x < b.N; x++ {\n\t\tDB.Raw(\"select * from users where id = ?\", user.ID).Scan(&u)\n\t}\n}\n\nfunc BenchmarkScanSlice(b *testing.B) {\n\tDB.Exec(\"delete from users\")\n\tfor i := 0; i < 10_000; i++ {\n\t\tuser := *GetUser(fmt.Sprintf(\"scan-%d\", i), Config{})\n\t\tDB.Create(&user)\n\t}\n\n\tvar u []User\n\tb.ResetTimer()\n\tfor x := 0; x < b.N; x++ {\n\t\tDB.Raw(\"select * from users\").Scan(&u)\n\t}\n}\n\nfunc BenchmarkScanSlicePointer(b *testing.B) {\n\tDB.Exec(\"delete from users\")\n\tfor i := 0; i < 10_000; i++ {\n\t\tuser := *GetUser(fmt.Sprintf(\"scan-%d\", i), Config{})\n\t\tDB.Create(&user)\n\t}\n\n\tvar u []*User\n\tb.ResetTimer()\n\tfor x := 0; x < b.N; x++ {\n\t\tDB.Raw(\"select * from users\").Scan(&u)\n\t}\n}\n\nfunc BenchmarkUpdate(b *testing.B) {\n\tuser := *GetUser(\"find\", Config{})\n\tDB.Create(&user)\n\n\tfor x := 0; x < b.N; x++ {\n\t\tDB.Model(&user).Updates(map[string]interface{}{\"Age\": x})\n\t}\n}\n\nfunc BenchmarkDelete(b *testing.B) {\n\tuser := *GetUser(\"find\", Config{})\n\n\tfor x := 0; x < b.N; x++ {\n\t\tuser.ID = 0\n\t\tDB.Create(&user)\n\t\tDB.Delete(&user)\n\t}\n}\n"
  },
  {
    "path": "tests/callbacks_test.go",
    "content": "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(v interface{}, fnames []string) (result bool, msg string) {\n\tvar (\n\t\tgot   []string\n\t\tfuncs = reflect.ValueOf(v).Elem().FieldByName(\"fns\")\n\t)\n\n\tfor i := 0; i < funcs.Len(); i++ {\n\t\tgot = append(got, getFuncName(funcs.Index(i)))\n\t}\n\n\treturn fmt.Sprint(got) == fmt.Sprint(fnames), fmt.Sprintf(\"expects %v, got %v\", fnames, got)\n}\n\nfunc getFuncName(fc interface{}) string {\n\treflectValue, ok := fc.(reflect.Value)\n\tif !ok {\n\t\treflectValue = reflect.ValueOf(fc)\n\t}\n\n\tfnames := strings.Split(runtime.FuncForPC(reflectValue.Pointer()).Name(), \".\")\n\treturn fnames[len(fnames)-1]\n}\n\nfunc c1(*gorm.DB) {}\nfunc c2(*gorm.DB) {}\nfunc c3(*gorm.DB) {}\nfunc c4(*gorm.DB) {}\nfunc c5(*gorm.DB) {}\nfunc c6(*gorm.DB) {}\n\nfunc TestCallbacks(t *testing.T) {\n\ttype callback struct {\n\t\tname    string\n\t\tbefore  string\n\t\tafter   string\n\t\tremove  bool\n\t\treplace bool\n\t\terr     string\n\t\tmatch   func(*gorm.DB) bool\n\t\th       func(*gorm.DB)\n\t}\n\n\tdatas := []struct {\n\t\tcallbacks []callback\n\t\terr       string\n\t\tresults   []string\n\t}{\n\t\t{\n\t\t\tcallbacks: []callback{{h: c1}, {h: c2}, {h: c3}, {h: c4}, {h: c5}},\n\t\t\tresults:   []string{\"c1\", \"c2\", \"c3\", \"c4\", \"c5\"},\n\t\t},\n\t\t{\n\t\t\tcallbacks: []callback{{h: c1}, {h: c2}, {h: c3}, {h: c4}, {h: c5, before: \"c4\"}},\n\t\t\tresults:   []string{\"c1\", \"c2\", \"c3\", \"c5\", \"c4\"},\n\t\t},\n\t\t{\n\t\t\tcallbacks: []callback{{h: c1}, {h: c2}, {h: c3}, {h: c4, after: \"c5\"}, {h: c5}},\n\t\t\tresults:   []string{\"c1\", \"c2\", \"c3\", \"c5\", \"c4\"},\n\t\t},\n\t\t{\n\t\t\tcallbacks: []callback{{h: c1}, {h: c2}, {h: c3}, {h: c4, after: \"c5\"}, {h: c5, before: \"c4\"}},\n\t\t\tresults:   []string{\"c1\", \"c2\", \"c3\", \"c5\", \"c4\"},\n\t\t},\n\t\t{\n\t\t\tcallbacks: []callback{{h: c1}, {h: c2, before: \"c4\", after: \"c5\"}, {h: c3}, {h: c4}, {h: c5}},\n\t\t\tresults:   []string{\"c1\", \"c5\", \"c2\", \"c3\", \"c4\"},\n\t\t},\n\t\t{\n\t\t\tcallbacks: []callback{{h: c1, after: \"c3\"}, {h: c2, before: \"c4\", after: \"c5\"}, {h: c3, before: \"c5\"}, {h: c4}, {h: c5}},\n\t\t\tresults:   []string{\"c3\", \"c1\", \"c5\", \"c2\", \"c4\"},\n\t\t},\n\t\t{\n\t\t\tcallbacks: []callback{{h: c1, before: \"c4\", after: \"c3\"}, {h: c2, before: \"c4\", after: \"c5\"}, {h: c3, before: \"c5\"}, {h: c4}, {h: c5}},\n\t\t\tresults:   []string{\"c3\", \"c1\", \"c5\", \"c2\", \"c4\"},\n\t\t},\n\t\t{\n\t\t\tcallbacks: []callback{{h: c1, before: \"c3\", after: \"c4\"}, {h: c2, before: \"c4\", after: \"c5\"}, {h: c3, before: \"c5\"}, {h: c4}, {h: c5}},\n\t\t\terr:       \"conflicting\",\n\t\t},\n\t\t{\n\t\t\tcallbacks: []callback{{h: c1}, {h: c2, before: \"c4\", after: \"c5\"}, {h: c3}, {h: c4}, {h: c5}, {h: c2, remove: true}},\n\t\t\tresults:   []string{\"c1\", \"c3\", \"c4\", \"c5\"},\n\t\t},\n\t\t{\n\t\t\tcallbacks: []callback{{h: c1}, {name: \"c\", h: c2}, {h: c3}, {name: \"c\", h: c4, replace: true}},\n\t\t\tresults:   []string{\"c1\", \"c4\", \"c3\"},\n\t\t},\n\t\t{\n\t\t\tcallbacks: []callback{{h: c1}, {h: c2, before: \"c4\", after: \"c5\"}, {h: c3}, {h: c4}, {h: c5, before: \"*\"}},\n\t\t\tresults:   []string{\"c5\", \"c1\", \"c2\", \"c3\", \"c4\"},\n\t\t},\n\t\t{\n\t\t\tcallbacks: []callback{{h: c1}, {h: c2, before: \"c4\", after: \"c5\"}, {h: c3, before: \"*\"}, {h: c4}, {h: c5, before: \"*\"}},\n\t\t\tresults:   []string{\"c3\", \"c5\", \"c1\", \"c2\", \"c4\"},\n\t\t},\n\t\t{\n\t\t\tcallbacks: []callback{{h: c1}, {h: c2, before: \"c4\", after: \"c5\"}, {h: c3, before: \"c4\", after: \"*\"}, {h: c4, after: \"*\"}, {h: c5, before: \"*\"}},\n\t\t\tresults:   []string{\"c5\", \"c1\", \"c2\", \"c3\", \"c4\"},\n\t\t},\n\t}\n\n\tfor idx, data := range datas {\n\t\tdb, err := gorm.Open(nil, nil)\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\tcallbacks := db.Callback()\n\n\t\tfor _, c := range data.callbacks {\n\t\t\tvar v interface{} = callbacks.Create()\n\t\t\tcallMethod := func(s interface{}, name string, args ...interface{}) {\n\t\t\t\tvar argValues []reflect.Value\n\t\t\t\tfor _, arg := range args {\n\t\t\t\t\targValues = append(argValues, reflect.ValueOf(arg))\n\t\t\t\t}\n\n\t\t\t\tresults := reflect.ValueOf(s).MethodByName(name).Call(argValues)\n\t\t\t\tif len(results) > 0 {\n\t\t\t\t\tv = results[0].Interface()\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif c.name == \"\" {\n\t\t\t\tc.name = getFuncName(c.h)\n\t\t\t}\n\n\t\t\tif c.before != \"\" {\n\t\t\t\tcallMethod(v, \"Before\", c.before)\n\t\t\t}\n\n\t\t\tif c.after != \"\" {\n\t\t\t\tcallMethod(v, \"After\", c.after)\n\t\t\t}\n\n\t\t\tif c.match != nil {\n\t\t\t\tcallMethod(v, \"Match\", c.match)\n\t\t\t}\n\n\t\t\tif c.remove {\n\t\t\t\tcallMethod(v, \"Remove\", c.name)\n\t\t\t} else if c.replace {\n\t\t\t\tcallMethod(v, \"Replace\", c.name, c.h)\n\t\t\t} else {\n\t\t\t\tcallMethod(v, \"Register\", c.name, c.h)\n\t\t\t}\n\n\t\t\tif e, ok := v.(error); !ok || e != nil {\n\t\t\t\terr = e\n\t\t\t}\n\t\t}\n\n\t\tif len(data.err) > 0 && err == nil {\n\t\t\tt.Errorf(\"callbacks tests #%v should got error %v, but not\", idx+1, data.err)\n\t\t} else if len(data.err) == 0 && err != nil {\n\t\t\tt.Errorf(\"callbacks tests #%v should not got error, but got %v\", idx+1, err)\n\t\t}\n\n\t\tif ok, msg := assertCallbacks(callbacks.Create(), data.results); !ok {\n\t\t\tt.Errorf(\"callbacks tests #%v failed, got %v\", idx+1, msg)\n\t\t}\n\t}\n}\n\nfunc TestPluginCallbacks(t *testing.T) {\n\tdb, _ := gorm.Open(nil, nil)\n\tcreateCallback := db.Callback().Create()\n\n\tcreateCallback.Before(\"*\").Register(\"plugin_1_fn1\", c1)\n\tcreateCallback.After(\"*\").Register(\"plugin_1_fn2\", c2)\n\n\tif ok, msg := assertCallbacks(createCallback, []string{\"c1\", \"c2\"}); !ok {\n\t\tt.Errorf(\"callbacks tests failed, got %v\", msg)\n\t}\n\n\t// plugin 2\n\tcreateCallback.Before(\"*\").Register(\"plugin_2_fn1\", c3)\n\tif ok, msg := assertCallbacks(createCallback, []string{\"c3\", \"c1\", \"c2\"}); !ok {\n\t\tt.Errorf(\"callbacks tests failed, got %v\", msg)\n\t}\n\n\tcreateCallback.After(\"*\").Register(\"plugin_2_fn2\", c4)\n\tif ok, msg := assertCallbacks(createCallback, []string{\"c3\", \"c1\", \"c2\", \"c4\"}); !ok {\n\t\tt.Errorf(\"callbacks tests failed, got %v\", msg)\n\t}\n\n\t// plugin 3\n\tcreateCallback.Before(\"*\").Register(\"plugin_3_fn1\", c5)\n\tif ok, msg := assertCallbacks(createCallback, []string{\"c5\", \"c3\", \"c1\", \"c2\", \"c4\"}); !ok {\n\t\tt.Errorf(\"callbacks tests failed, got %v\", msg)\n\t}\n\n\tcreateCallback.After(\"*\").Register(\"plugin_3_fn2\", c6)\n\tif ok, msg := assertCallbacks(createCallback, []string{\"c5\", \"c3\", \"c1\", \"c2\", \"c4\", \"c6\"}); !ok {\n\t\tt.Errorf(\"callbacks tests failed, got %v\", msg)\n\t}\n}\n\nfunc TestCallbacksGet(t *testing.T) {\n\tdb, _ := gorm.Open(nil, nil)\n\tcreateCallback := db.Callback().Create()\n\n\tcreateCallback.Before(\"*\").Register(\"c1\", c1)\n\tif cb := createCallback.Get(\"c1\"); reflect.DeepEqual(cb, c1) {\n\t\tt.Errorf(\"callbacks tests failed, got: %p, want: %p\", cb, c1)\n\t}\n\n\tcreateCallback.Remove(\"c1\")\n\tif cb := createCallback.Get(\"c2\"); cb != nil {\n\t\tt.Errorf(\"callbacks test failed. got: %p, want: nil\", cb)\n\t}\n}\n\nfunc TestCallbacksRemove(t *testing.T) {\n\tdb, _ := gorm.Open(nil, nil)\n\tcreateCallback := db.Callback().Create()\n\n\tcreateCallback.Before(\"*\").Register(\"c1\", c1)\n\tcreateCallback.After(\"*\").Register(\"c2\", c2)\n\tcreateCallback.Before(\"c4\").Register(\"c3\", c3)\n\tcreateCallback.After(\"c2\").Register(\"c4\", c4)\n\n\t// callbacks: []string{\"c1\", \"c3\", \"c4\", \"c2\"}\n\tcreateCallback.Remove(\"c1\")\n\tif ok, msg := assertCallbacks(createCallback, []string{\"c3\", \"c4\", \"c2\"}); !ok {\n\t\tt.Errorf(\"callbacks tests failed, got %v\", msg)\n\t}\n\n\tcreateCallback.Remove(\"c4\")\n\tif ok, msg := assertCallbacks(createCallback, []string{\"c3\", \"c2\"}); !ok {\n\t\tt.Errorf(\"callbacks tests failed, got %v\", msg)\n\t}\n\n\tcreateCallback.Remove(\"c2\")\n\tif ok, msg := assertCallbacks(createCallback, []string{\"c3\"}); !ok {\n\t\tt.Errorf(\"callbacks tests failed, got %v\", msg)\n\t}\n\n\tcreateCallback.Remove(\"c3\")\n\tif ok, msg := assertCallbacks(createCallback, []string{}); !ok {\n\t\tt.Errorf(\"callbacks tests failed, got %v\", msg)\n\t}\n}\n"
  },
  {
    "path": "tests/chainable_api_test.go",
    "content": "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// testDialector is a minimal Dialector implementation used only for unit tests in-memory.\ntype testDialector struct{}\n\nfunc (d testDialector) Name() string                                   { return \"test\" }\nfunc (d testDialector) Initialize(*gorm.DB) error                      { return nil }\nfunc (d testDialector) Migrator(db *gorm.DB) gorm.Migrator             { return nil }\nfunc (d testDialector) DataTypeOf(*schema.Field) string                { return \"\" }\nfunc (d testDialector) DefaultValueOf(*schema.Field) clause.Expression { return clause.Expr{} }\nfunc (d testDialector) BindVarTo(writer clause.Writer, stmt *gorm.Statement, v interface{}) {\n\t// write a simple placeholder\n\twriter.WriteByte('?')\n}\nfunc (d testDialector) QuoteTo(writer clause.Writer, s string)         { writer.WriteString(s) }\nfunc (d testDialector) Explain(sql string, vars ...interface{}) string { return sql }\n\n// newTestDB returns a minimal *DB with an initialized Statement suitable for unit tests\nfunc newTestDB() *gorm.DB {\n\td := testDialector{}\n\tcfg := &gorm.Config{Dialector: d}\n\tdb := &gorm.DB{Config: cfg}\n\tstmt := &gorm.Statement{\n\t\tDB:       db,\n\t\tClauses:  map[string]clause.Clause{},\n\t\tPreloads: map[string][]interface{}{},\n\t\tContext:  context.Background(),\n\t\tVars:     make([]interface{}, 0),\n\t}\n\tdb.Statement = stmt\n\treturn db\n}\n\nfunc TestChainableAPI(t *testing.T) {\n\tdb := newTestDB()\n\n\t// Model\n\tm := &struct{ ID int }{}\n\ttx := db.Model(m)\n\tif tx.Statement.Model != m {\n\t\tt.Fatalf(\"Model not set, got %v\", tx.Statement.Model)\n\t}\n\n\t// Table\n\ttx = tx.Table(\"users\")\n\tif tx.Statement.Table != \"users\" {\n\t\tt.Fatalf(\"Table not set, got %v\", tx.Statement.Table)\n\t}\n\tif tx.Statement.TableExpr == nil {\n\t\tt.Fatalf(\"TableExpr expected to be set\")\n\t}\n\n\t// Distinct + Select\n\ttx = tx.Distinct(\"name\", \"age\")\n\tif !tx.Statement.Distinct {\n\t\tt.Fatalf(\"Distinct expected true\")\n\t}\n\tif len(tx.Statement.Selects) != 2 || tx.Statement.Selects[0] != \"name\" {\n\t\tt.Fatalf(\"Selects expected [name age], got %v\", tx.Statement.Selects)\n\t}\n\n\t// Where\n\ttx = tx.Where(\"age = ?\", 20)\n\tc, ok := tx.Statement.Clauses[\"WHERE\"]\n\tif !ok {\n\t\tt.Fatalf(\"WHERE clause expected\")\n\t}\n\tif where, ok := c.Expression.(clause.Where); !ok || len(where.Exprs) == 0 {\n\t\tt.Fatalf(\"WHERE expressions expected, got %v\", c.Expression)\n\t}\n\n\t// Order\n\ttx = tx.Order(\"name DESC\")\n\tif _, ok := tx.Statement.Clauses[\"ORDER BY\"]; !ok {\n\t\tt.Fatalf(\"ORDER BY clause expected\")\n\t}\n\n\t// Limit / Offset\n\ttx = tx.Limit(10).Offset(5)\n\tif cl, ok := tx.Statement.Clauses[\"LIMIT\"]; !ok {\n\t\tt.Fatalf(\"LIMIT clause expected\")\n\t} else {\n\t\tif limit, ok := cl.Expression.(clause.Limit); !ok || limit.Limit == nil || *limit.Limit != 10 || limit.Offset != 5 {\n\t\t\tt.Fatalf(\"LIMIT/Offset values unexpected: %v\", cl.Expression)\n\t\t}\n\t}\n\n\t// Joins\n\ttx = tx.Joins(\"JOIN accounts ON accounts.user_id = users.id\")\n\tif len(tx.Statement.Joins) == 0 {\n\t\tt.Fatalf(\"Joins expected\")\n\t}\n\tif tx.Statement.Joins[0].Name != \"JOIN accounts ON accounts.user_id = users.id\" {\n\t\tt.Fatalf(\"Join name mismatch: %v\", tx.Statement.Joins[0].Name)\n\t}\n\n\t// Preload\n\ttx = tx.Preload(\"Orders\", \"state != ?\", \"cancelled\")\n\targs, ok := tx.Statement.Preloads[\"Orders\"]\n\tif !ok || len(args) != 2 {\n\t\tt.Fatalf(\"Preload expected with args, got %v\", tx.Statement.Preloads)\n\t}\n\n\t// Scopes: just ensure calling Scopes doesn't panic and returns a DB\n\ttx = tx.Scopes(func(d *gorm.DB) *gorm.DB { return d.Where(\"status = ?\", \"ok\") })\n\tif tx == nil {\n\t\tt.Fatalf(\"Scopes returned nil\")\n\t}\n\n\t// Unscoped\n\ttx = tx.Unscoped()\n\tif !tx.Statement.Unscoped {\n\t\tt.Fatalf(\"Unscoped expected to be true\")\n\t}\n\n\t// Raw\n\ttx = tx.Raw(\"SELECT ? as x\", 1)\n\tif tx.Statement.SQL.Len() == 0 {\n\t\tt.Fatalf(\"Raw SQL expected to be built\")\n\t}\n\tif len(tx.Statement.Vars) != 1 || tx.Statement.Vars[0] != 1 {\n\t\tt.Fatalf(\"Raw Vars expected to contain 1, got %v\", tx.Statement.Vars)\n\t}\n}\n"
  },
  {
    "path": "tests/compose.yml",
    "content": "services:\n  mysql:\n    image: 'mysql:latest'\n    ports:\n      - \"127.0.0.1:9910:3306\"\n    environment:\n      - MYSQL_DATABASE=gorm\n      - MYSQL_USER=gorm\n      - MYSQL_PASSWORD=gorm\n      - MYSQL_RANDOM_ROOT_PASSWORD=\"yes\"\n  postgres:\n    image: 'postgres:latest'\n    ports:\n      - \"127.0.0.1:9920:5432\"\n    environment:\n      - TZ=Asia/Shanghai\n      - POSTGRES_DB=gorm\n      - POSTGRES_USER=gorm\n      - POSTGRES_PASSWORD=gorm\n  mssql:\n    image: '${MSSQL_IMAGE}:latest'\n    ports:\n      - \"127.0.0.1:9930:1433\"\n    environment:\n      - TZ=Asia/Shanghai\n      - ACCEPT_EULA=Y\n      - MSSQL_SA_PASSWORD=LoremIpsum86\n  tidb:\n    image: 'pingcap/tidb:v6.5.0'\n    ports:\n      - \"127.0.0.1:9940:4000\"\n    command: /tidb-server -store unistore -path \"\" -lease 0s > tidb.log 2>&1 &\n  gaussdb:\n    image: 'opengauss/opengauss:7.0.0-RC1.B023'\n    hostname: opengauss-server\n    ports:\n      - \"127.0.0.1:9950:5432\"\n    environment:\n      - TZ=Asia/Shanghai\n      - GS_PASSWORD=Gaussdb@123\n      - GS_CLUSTER_NAME=opengauss_cluster\n      - PGDATA=/var/lib/opengauss/data\n    entrypoint: \"\"\n    command: |-\n      /bin/sh -c 'set -euo pipefail;\n      /usr/local/bin/entrypoint.sh gaussdb &\n      counter=1;\n      while [ \"$$counter\" -le 20 ]; do\n        if su - omm -c \"gsql -U omm -d postgres -c \\\"SELECT 1;\\\"\"; then\n          echo \"Creating database gorm...\";\n          su - omm -c \"gsql -U omm -d postgres -c \\\"CREATE DATABASE gorm DBCOMPATIBILITY '\\'PG\\'';\\\"\";\n          echo \"Database initialized successfully\";\n          break;\n        fi;\n        echo \"Waiting for database to be ready... ($$counter/12)\";\n        sleep 5;\n        counter=$$(($$counter + 1));\n      done;\n      # timeout handling\n      if [ $$counter -gt 20 ]; then\n        echo \"Error: Database failed to start within timeout\";\n        exit 1;\n      fi;\n      # keep the container running: wait for the database process in the foreground\n      wait\n      '"
  },
  {
    "path": "tests/connection_test.go",
    "content": "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 *testing.T) {\n\texpectedName := \"test\"\n\tvar actualName string\n\n\tsetSQL, getSQL := getSetSQL(DB.Dialector.Name())\n\tif len(setSQL) == 0 || len(getSQL) == 0 {\n\t\treturn\n\t}\n\n\terr := DB.Connection(func(tx *gorm.DB) error {\n\t\tif err := tx.Exec(setSQL, expectedName).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif err := tx.Raw(getSQL).Scan(&actualName).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t})\n\tif err != nil {\n\t\tt.Errorf(\"WithSingleConnection should work, but got err %v\", err)\n\t}\n\n\tif actualName != expectedName {\n\t\tt.Errorf(\"WithSingleConnection() method should get correct value, expect: %v, got %v\", expectedName, actualName)\n\t}\n}\n\nfunc getSetSQL(driverName string) (string, string) {\n\tswitch driverName {\n\tcase mysql.Dialector{}.Name():\n\t\treturn \"SET @testName := ?\", \"SELECT @testName\"\n\tdefault:\n\t\treturn \"\", \"\"\n\t}\n}\n"
  },
  {
    "path": "tests/connpool_test.go",
    "content": "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/gorm\"\n\t. \"gorm.io/gorm/utils/tests\"\n)\n\ntype wrapperTx struct {\n\t*sql.Tx\n\tconn *wrapperConnPool\n}\n\nfunc (c *wrapperTx) PrepareContext(ctx context.Context, query string) (*sql.Stmt, error) {\n\tc.conn.got = append(c.conn.got, query)\n\treturn c.Tx.PrepareContext(ctx, query)\n}\n\nfunc (c *wrapperTx) ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error) {\n\tc.conn.got = append(c.conn.got, query)\n\treturn c.Tx.ExecContext(ctx, query, args...)\n}\n\nfunc (c *wrapperTx) QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error) {\n\tc.conn.got = append(c.conn.got, query)\n\treturn c.Tx.QueryContext(ctx, query, args...)\n}\n\nfunc (c *wrapperTx) QueryRowContext(ctx context.Context, query string, args ...interface{}) *sql.Row {\n\tc.conn.got = append(c.conn.got, query)\n\treturn c.Tx.QueryRowContext(ctx, query, args...)\n}\n\ntype wrapperConnPool struct {\n\tdb     *sql.DB\n\tgot    []string\n\texpect []string\n}\n\nfunc (c *wrapperConnPool) Ping() error {\n\treturn c.db.Ping()\n}\n\n// If you use BeginTx returned *sql.Tx as shown below then you can't record queries in a transaction.\n//\n//\tfunc (c *wrapperConnPool) BeginTx(ctx context.Context, opts *sql.TxOptions) (*sql.Tx, error) {\n//\t\t return c.db.BeginTx(ctx, opts)\n//\t}\n//\n// You should use BeginTx returned gorm.Tx which could wrap *sql.Tx then you can record all queries.\nfunc (c *wrapperConnPool) BeginTx(ctx context.Context, opts *sql.TxOptions) (gorm.ConnPool, error) {\n\ttx, err := c.db.BeginTx(ctx, opts)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &wrapperTx{Tx: tx, conn: c}, nil\n}\n\nfunc (c *wrapperConnPool) PrepareContext(ctx context.Context, query string) (*sql.Stmt, error) {\n\tc.got = append(c.got, query)\n\treturn c.db.PrepareContext(ctx, query)\n}\n\nfunc (c *wrapperConnPool) ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error) {\n\tc.got = append(c.got, query)\n\treturn c.db.ExecContext(ctx, query, args...)\n}\n\nfunc (c *wrapperConnPool) QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error) {\n\tc.got = append(c.got, query)\n\treturn c.db.QueryContext(ctx, query, args...)\n}\n\nfunc (c *wrapperConnPool) QueryRowContext(ctx context.Context, query string, args ...interface{}) *sql.Row {\n\tc.got = append(c.got, query)\n\treturn c.db.QueryRowContext(ctx, query, args...)\n}\n\nfunc TestConnPoolWrapper(t *testing.T) {\n\tdialect := os.Getenv(\"GORM_DIALECT\")\n\tif dialect != \"mysql\" {\n\t\tt.SkipNow()\n\t}\n\n\tdbDSN := os.Getenv(\"GORM_DSN\")\n\tif dbDSN == \"\" {\n\t\tdbDSN = \"gorm:gorm@tcp(localhost:9910)/gorm?charset=utf8&parseTime=True&loc=Local\"\n\t}\n\tnativeDB, err := sql.Open(\"mysql\", dbDSN)\n\tif err != nil {\n\t\tt.Fatalf(\"Should open db success, but got %v\", err)\n\t}\n\n\tconn := &wrapperConnPool{\n\t\tdb: nativeDB,\n\t\texpect: []string{\n\t\t\t\"SELECT VERSION()\",\n\t\t\t\"INSERT INTO `users` (`created_at`,`updated_at`,`deleted_at`,`name`,`age`,`birthday`,`company_id`,`manager_id`,`active`) VALUES (?,?,?,?,?,?,?,?,?)\",\n\t\t\t\"SELECT * FROM `users` WHERE name = ? AND `users`.`deleted_at` IS NULL ORDER BY `users`.`id` LIMIT ?\",\n\t\t\t\"INSERT INTO `users` (`created_at`,`updated_at`,`deleted_at`,`name`,`age`,`birthday`,`company_id`,`manager_id`,`active`) VALUES (?,?,?,?,?,?,?,?,?)\",\n\t\t\t\"SELECT * FROM `users` WHERE name = ? AND `users`.`deleted_at` IS NULL ORDER BY `users`.`id` LIMIT ?\",\n\t\t\t\"SELECT * FROM `users` WHERE name = ? AND `users`.`deleted_at` IS NULL ORDER BY `users`.`id` LIMIT ?\",\n\t\t\t\"INSERT INTO `users` (`created_at`,`updated_at`,`deleted_at`,`name`,`age`,`birthday`,`company_id`,`manager_id`,`active`) VALUES (?,?,?,?,?,?,?,?,?)\",\n\t\t\t\"SELECT * FROM `users` WHERE name = ? AND `users`.`deleted_at` IS NULL ORDER BY `users`.`id` LIMIT ?\",\n\t\t\t\"SELECT * FROM `users` WHERE name = ? AND `users`.`deleted_at` IS NULL ORDER BY `users`.`id` LIMIT ?\",\n\t\t},\n\t}\n\n\tdefer func() {\n\t\tif !reflect.DeepEqual(conn.got, conn.expect) {\n\t\t\tt.Errorf(\"expect %#v but got %#v\", conn.expect, conn.got)\n\t\t}\n\t}()\n\n\tdb, err := gorm.Open(mysql.New(mysql.Config{Conn: conn, DisableWithReturning: true}))\n\tdb.Logger = DB.Logger\n\tif err != nil {\n\t\tt.Fatalf(\"Should open db success, but got %v\", err)\n\t}\n\n\ttx := db.Begin()\n\tuser := *GetUser(\"transaction\", Config{})\n\n\tif err = tx.Save(&user).Error; err != nil {\n\t\tt.Fatalf(\"No error should raise, but got %v\", err)\n\t}\n\n\tif err = tx.First(&User{}, \"name = ?\", \"transaction\").Error; err != nil {\n\t\tt.Fatalf(\"Should find saved record, but got %v\", err)\n\t}\n\n\tuser1 := *GetUser(\"transaction1-1\", Config{})\n\n\tif err = tx.Save(&user1).Error; err != nil {\n\t\tt.Fatalf(\"No error should raise, but got %v\", err)\n\t}\n\n\tif err = tx.First(&User{}, \"name = ?\", user1.Name).Error; err != nil {\n\t\tt.Fatalf(\"Should find saved record, but got %v\", err)\n\t}\n\n\tif sqlTx, ok := tx.Statement.ConnPool.(gorm.TxCommitter); !ok || sqlTx == nil {\n\t\tt.Fatalf(\"Should return the underlying sql.Tx\")\n\t}\n\n\ttx.Rollback()\n\n\tif err = db.First(&User{}, \"name = ?\", \"transaction\").Error; err == nil {\n\t\tt.Fatalf(\"Should not find record after rollback, but got %v\", err)\n\t}\n\n\ttxDB := db.Where(\"fake_name = ?\", \"fake_name\")\n\ttx2 := txDB.Session(&gorm.Session{NewDB: true}).Begin()\n\tuser2 := *GetUser(\"transaction-2\", Config{})\n\tif err = tx2.Save(&user2).Error; err != nil {\n\t\tt.Fatalf(\"No error should raise, but got %v\", err)\n\t}\n\n\tif err = tx2.First(&User{}, \"name = ?\", \"transaction-2\").Error; err != nil {\n\t\tt.Fatalf(\"Should find saved record, but got %v\", err)\n\t}\n\n\ttx2.Commit()\n\n\tif err = db.First(&User{}, \"name = ?\", \"transaction-2\").Error; err != nil {\n\t\tt.Fatalf(\"Should be able to find committed record, but got %v\", err)\n\t}\n}\n"
  },
  {
    "path": "tests/count_test.go",
    "content": "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\nfunc TestCountWithGroup(t *testing.T) {\n\tDB.Create([]Company{\n\t\t{Name: \"company_count_group_a\"},\n\t\t{Name: \"company_count_group_a\"},\n\t\t{Name: \"company_count_group_a\"},\n\t\t{Name: \"company_count_group_b\"},\n\t\t{Name: \"company_count_group_c\"},\n\t})\n\n\tvar count1 int64\n\tif err := DB.Model(&Company{}).Where(\"name = ?\", \"company_count_group_a\").Group(\"name\").Count(&count1).Error; err != nil {\n\t\tt.Errorf(\"Count should work, but got err %v\", err)\n\t}\n\tif count1 != 1 {\n\t\tt.Errorf(\"Count with group should be 1, but got count: %v\", count1)\n\t}\n\n\tvar count2 int64\n\tif err := DB.Model(&Company{}).Where(\"name in ?\", []string{\"company_count_group_b\", \"company_count_group_c\"}).Group(\"name\").Count(&count2).Error; err != nil {\n\t\tt.Errorf(\"Count should work, but got err %v\", err)\n\t}\n\tif count2 != 2 {\n\t\tt.Errorf(\"Count with group should be 2, but got count: %v\", count2)\n\t}\n}\n\nfunc TestCount(t *testing.T) {\n\tvar (\n\t\tuser1                 = *GetUser(\"count-1\", Config{})\n\t\tuser2                 = *GetUser(\"count-2\", Config{})\n\t\tuser3                 = *GetUser(\"count-3\", Config{})\n\t\tusers                 []User\n\t\tcount, count1, count2 int64\n\t)\n\n\tDB.Save(&user1).Save(&user2).Save(&user3)\n\n\tif err := DB.Where(\"name = ?\", user1.Name).Or(\"name = ?\", user3.Name).Find(&users).Count(&count).Error; err != nil {\n\t\tt.Errorf(\"Count should work, but got err %v\", err)\n\t}\n\n\tif count != int64(len(users)) {\n\t\tt.Errorf(\"Count() method should get correct value, expect: %v, got %v\", count, len(users))\n\t}\n\n\tif err := DB.Model(&User{}).Where(\"name = ?\", user1.Name).Or(\"name = ?\", user3.Name).Count(&count).Find(&users).Error; err != nil {\n\t\tt.Errorf(\"Count should work, but got err %v\", err)\n\t}\n\n\tif count != int64(len(users)) {\n\t\tt.Errorf(\"Count() method should get correct value, expect: %v, got %v\", count, len(users))\n\t}\n\n\tDB.Model(&User{}).Where(\"name = ?\", user1.Name).Count(&count1).Or(\"name in ?\", []string{user2.Name, user3.Name}).Count(&count2)\n\tif count1 != 1 || count2 != 3 {\n\t\tt.Errorf(\"multiple count in chain should works\")\n\t}\n\n\ttx := DB.Model(&User{}).Where(\"name = ?\", user1.Name).Session(&gorm.Session{})\n\ttx.Count(&count1)\n\ttx.Or(\"name in ?\", []string{user2.Name, user3.Name}).Count(&count2)\n\tif count1 != 1 || count2 != 3 {\n\t\tt.Errorf(\"count after new session should works\")\n\t}\n\n\tvar count3 int64\n\tif err := DB.Model(&User{}).Where(\"name in ?\", []string{user2.Name, user2.Name, user3.Name}).Group(\"id\").Count(&count3).Error; err != nil {\n\t\tt.Errorf(\"Error happened when count with group, but got %v\", err)\n\t}\n\n\tif count3 != 2 {\n\t\tt.Errorf(\"Should get correct count for count with group, but got %v\", count3)\n\t}\n\n\tdryDB := DB.Session(&gorm.Session{DryRun: true})\n\tresult := dryDB.Table(\"users\").Select(\"name\").Count(&count)\n\tif !regexp.MustCompile(`SELECT COUNT\\(.name.\\) FROM .*users.*`).MatchString(result.Statement.SQL.String()) {\n\t\tt.Fatalf(\"Build count with select, but got %v\", result.Statement.SQL.String())\n\t}\n\n\tresult = dryDB.Table(\"users\").Distinct(\"name\").Count(&count)\n\tif !regexp.MustCompile(`SELECT COUNT\\(DISTINCT\\(.name.\\)\\) FROM .*users.*`).MatchString(result.Statement.SQL.String()) {\n\t\tt.Fatalf(\"Build count with select, but got %v\", result.Statement.SQL.String())\n\t}\n\n\tvar count4 int64\n\tif err := DB.Table(\"users\").Joins(\"LEFT JOIN companies on companies.name = users.name\").Where(\"users.name = ?\", user1.Name).Count(&count4).Error; err != nil || count4 != 1 {\n\t\tt.Errorf(\"count with join, got error: %v, count %v\", err, count4)\n\t}\n\n\tvar count5 int64\n\tif err := DB.Table(\"users\").Where(\"users.name = ?\", user1.Name).Order(\"name\").Count(&count5).Error; err != nil || count5 != 1 {\n\t\tt.Errorf(\"count with join, got error: %v, count %v\", err, count)\n\t}\n\n\tvar count6 int64\n\tif err := DB.Model(&User{}).Where(\"name in ?\", []string{user1.Name, user2.Name, user3.Name}).Select(\n\t\t\"(CASE WHEN name=? THEN ? ELSE ? END) as name\", \"count-1\", \"main\", \"other\",\n\t).Count(&count6).Find(&users).Error; err != nil || count6 != 3 {\n\t\tt.Fatalf(\"Count should work, but got err %v\", err)\n\t}\n\n\texpects := []User{{Name: \"main\"}, {Name: \"other\"}, {Name: \"other\"}}\n\tsort.SliceStable(users, func(i, j int) bool {\n\t\treturn strings.Compare(users[i].Name, users[j].Name) < 0\n\t})\n\n\tAssertEqual(t, users, expects)\n\n\tvar count7 int64\n\tif err := DB.Model(&User{}).Where(\"name in ?\", []string{user1.Name, user2.Name, user3.Name}).Select(\n\t\t\"(CASE WHEN name=? THEN ? ELSE ? END) as name, age\", \"count-1\", \"main\", \"other\",\n\t).Count(&count7).Find(&users).Error; err != nil || count7 != 3 {\n\t\tt.Fatalf(\"Count should work, but got err %v\", err)\n\t}\n\n\texpects = []User{{Name: \"main\", Age: 18}, {Name: \"other\", Age: 18}, {Name: \"other\", Age: 18}}\n\tsort.SliceStable(users, func(i, j int) bool {\n\t\treturn strings.Compare(users[i].Name, users[j].Name) < 0\n\t})\n\n\tAssertEqual(t, users, expects)\n\n\tvar count8 int64\n\tif err := DB.Model(&User{}).Where(\"name in ?\", []string{user1.Name, user2.Name, user3.Name}).Select(\n\t\t\"(CASE WHEN age=18 THEN 1 ELSE 2 END) as age\", \"name\",\n\t).Count(&count8).Find(&users).Error; err != nil || count8 != 3 {\n\t\tt.Fatalf(\"Count should work, but got err %v\", err)\n\t}\n\n\texpects = []User{{Name: \"count-1\", Age: 1}, {Name: \"count-2\", Age: 1}, {Name: \"count-3\", Age: 1}}\n\tsort.SliceStable(users, func(i, j int) bool {\n\t\treturn strings.Compare(users[i].Name, users[j].Name) < 0\n\t})\n\n\tAssertEqual(t, users, expects)\n\n\tvar count9 int64\n\tif err := DB.Scopes(func(tx *gorm.DB) *gorm.DB {\n\t\treturn tx.Table(\"users\")\n\t}).Where(\"name in ?\", []string{user1.Name, user2.Name, user3.Name}).Count(&count9).Find(&users).Error; err != nil || count9 != 3 {\n\t\tt.Fatalf(\"Count should work, but got err %v\", err)\n\t}\n\n\tvar count10 int64\n\tif err := DB.Model(&User{}).Select(\"*\").Where(\"name in ?\", []string{user1.Name, user2.Name, user3.Name}).Count(&count10).Error; err != nil || count10 != 3 {\n\t\tt.Fatalf(\"Count should be 3, but got count: %v err %v\", count10, err)\n\t}\n\n\tvar count11 int64\n\tsameUsers := make([]*User, 0)\n\tfor i := 0; i < 3; i++ {\n\t\tsameUsers = append(sameUsers, GetUser(\"count-4\", Config{}))\n\t}\n\tDB.Create(sameUsers)\n\n\tif err := DB.Model(&User{}).Where(\"name = ?\", \"count-4\").Group(\"name\").Count(&count11).Error; err != nil || count11 != 1 {\n\t\tt.Fatalf(\"Count should be 1, but got count: %v err %v\", count11, err)\n\t}\n\n\tvar count12 int64\n\tif err := DB.Table(\"users\").\n\t\tWhere(\"name in ?\", []string{user1.Name, user2.Name, user3.Name}).\n\t\tPreload(\"Toys\", func(db *gorm.DB) *gorm.DB {\n\t\t\treturn db.Table(\"toys\").Select(\"name\")\n\t\t}).Count(&count12).Error; err == nil {\n\t\tt.Errorf(\"error should raise when using preload without schema\")\n\t}\n\n\tvar count13 int64\n\tif err := DB.Model(User{}).\n\t\tWhere(\"name in ?\", []string{user1.Name, user2.Name, user3.Name}).\n\t\tPreload(\"Toys\", func(db *gorm.DB) *gorm.DB {\n\t\t\treturn db.Table(\"toys\").Select(\"name\")\n\t\t}).Count(&count13).Error; err != nil {\n\t\tt.Errorf(\"no error should raise when using count with preload, but got %v\", err)\n\t}\n}\n"
  },
  {
    "path": "tests/create_test.go",
    "content": "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\"gorm.io/gorm/clause\"\n\t. \"gorm.io/gorm/utils/tests\"\n)\n\nfunc TestCreate(t *testing.T) {\n\tu1 := *GetUser(\"create\", Config{})\n\n\tif results := DB.Create(&u1); results.Error != nil {\n\t\tt.Fatalf(\"errors happened when create: %v\", results.Error)\n\t} else if results.RowsAffected != 1 {\n\t\tt.Fatalf(\"rows affected expects: %v, got %v\", 1, results.RowsAffected)\n\t}\n\n\tif u1.ID == 0 {\n\t\tt.Errorf(\"user's primary key should has value after create, got : %v\", u1.ID)\n\t}\n\n\tif u1.CreatedAt.IsZero() {\n\t\tt.Errorf(\"user's created at should be not zero\")\n\t}\n\n\tif u1.UpdatedAt.IsZero() {\n\t\tt.Errorf(\"user's updated at should be not zero\")\n\t}\n\n\tvar newUser User\n\tif err := DB.Where(\"id = ?\", u1.ID).First(&newUser).Error; err != nil {\n\t\tt.Fatalf(\"errors happened when query: %v\", err)\n\t} else {\n\t\tCheckUser(t, newUser, u1)\n\t}\n\n\ttype user struct {\n\t\tID   int `gorm:\"primaryKey;->:false\"`\n\t\tName string\n\t\tAge  int\n\t}\n\n\tvar u2 user\n\tif results := DB.Create(&u2); results.Error != nil {\n\t\tt.Fatalf(\"errors happened when create: %v\", results.Error)\n\t} else if results.RowsAffected != 1 {\n\t\tt.Fatalf(\"rows affected expects: %v, got %v\", 1, results.RowsAffected)\n\t}\n\n\tif u2.ID != 0 {\n\t\tt.Errorf(\"don't have the permission to read primary key from db, but got %v\", u2.ID)\n\t}\n}\n\nfunc TestCreateInBatches(t *testing.T) {\n\tusers := []User{\n\t\t*GetUser(\"create_in_batches_1\", Config{Account: true, Pets: 2, Toys: 3, Company: true, Manager: true, Team: 0, Languages: 1, Friends: 1}),\n\t\t*GetUser(\"create_in_batches_2\", Config{Account: false, Pets: 2, Toys: 4, Company: false, Manager: false, Team: 1, Languages: 3, Friends: 5}),\n\t\t*GetUser(\"create_in_batches_3\", Config{Account: true, Pets: 0, Toys: 3, Company: true, Manager: false, Team: 4, Languages: 0, Friends: 1}),\n\t\t*GetUser(\"create_in_batches_4\", Config{Account: true, Pets: 3, Toys: 0, Company: false, Manager: true, Team: 0, Languages: 3, Friends: 0}),\n\t\t*GetUser(\"create_in_batches_5\", Config{Account: false, Pets: 0, Toys: 3, Company: true, Manager: false, Team: 1, Languages: 3, Friends: 1}),\n\t\t*GetUser(\"create_in_batches_6\", Config{Account: true, Pets: 4, Toys: 3, Company: false, Manager: true, Team: 1, Languages: 3, Friends: 0}),\n\t}\n\n\tresult := DB.CreateInBatches(&users, 2)\n\tif result.RowsAffected != int64(len(users)) {\n\t\tt.Errorf(\"affected rows should be %v, but got %v\", len(users), result.RowsAffected)\n\t}\n\n\tfor _, user := range users {\n\t\tif user.ID == 0 {\n\t\t\tt.Fatalf(\"failed to fill user's ID, got %v\", user.ID)\n\t\t} else {\n\t\t\tvar newUser User\n\t\t\tif err := DB.Where(\"id = ?\", user.ID).Preload(clause.Associations).First(&newUser).Error; err != nil {\n\t\t\t\tt.Fatalf(\"errors happened when query: %v\", err)\n\t\t\t} else {\n\t\t\t\tCheckUser(t, newUser, user)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc TestCreateInBatchesWithDefaultSize(t *testing.T) {\n\tusers := []User{\n\t\t*GetUser(\"create_with_default_batch_size_1\", Config{Account: true, Pets: 2, Toys: 3, Company: true, Manager: true, Team: 0, Languages: 1, Friends: 1}),\n\t\t*GetUser(\"create_with_default_batch_sizs_2\", Config{Account: false, Pets: 2, Toys: 4, Company: false, Manager: false, Team: 1, Languages: 3, Friends: 5}),\n\t\t*GetUser(\"create_with_default_batch_sizs_3\", Config{Account: true, Pets: 0, Toys: 3, Company: true, Manager: false, Team: 4, Languages: 0, Friends: 1}),\n\t\t*GetUser(\"create_with_default_batch_sizs_4\", Config{Account: true, Pets: 3, Toys: 0, Company: false, Manager: true, Team: 0, Languages: 3, Friends: 0}),\n\t\t*GetUser(\"create_with_default_batch_sizs_5\", Config{Account: false, Pets: 0, Toys: 3, Company: true, Manager: false, Team: 1, Languages: 3, Friends: 1}),\n\t\t*GetUser(\"create_with_default_batch_sizs_6\", Config{Account: true, Pets: 4, Toys: 3, Company: false, Manager: true, Team: 1, Languages: 3, Friends: 0}),\n\t}\n\n\tresult := DB.Session(&gorm.Session{CreateBatchSize: 2}).Create(&users)\n\tif result.RowsAffected != int64(len(users)) {\n\t\tt.Errorf(\"affected rows should be %v, but got %v\", len(users), result.RowsAffected)\n\t}\n\n\tfor _, user := range users {\n\t\tif user.ID == 0 {\n\t\t\tt.Fatalf(\"failed to fill user's ID, got %v\", user.ID)\n\t\t} else {\n\t\t\tvar newUser User\n\t\t\tif err := DB.Where(\"id = ?\", user.ID).Preload(clause.Associations).First(&newUser).Error; err != nil {\n\t\t\t\tt.Fatalf(\"errors happened when query: %v\", err)\n\t\t\t} else {\n\t\t\t\tCheckUser(t, newUser, user)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc TestCreateFromMap(t *testing.T) {\n\tif err := DB.Model(&User{}).Create(map[string]interface{}{\"Name\": \"create_from_map\", \"Age\": 18}).Error; err != nil {\n\t\tt.Fatalf(\"failed to create data from map, got error: %v\", err)\n\t}\n\n\tvar result User\n\tif err := DB.Where(\"name = ?\", \"create_from_map\").First(&result).Error; err != nil || result.Age != 18 {\n\t\tt.Fatalf(\"failed to create from map, got error %v\", err)\n\t}\n\n\tif err := DB.Model(&User{}).Create(map[string]interface{}{\"name\": \"create_from_map_1\", \"age\": 18}).Error; err != nil {\n\t\tt.Fatalf(\"failed to create data from map, got error: %v\", err)\n\t}\n\n\tvar result1 User\n\tif err := DB.Where(\"name = ?\", \"create_from_map_1\").First(&result1).Error; err != nil || result1.Age != 18 {\n\t\tt.Fatalf(\"failed to create from map, got error %v\", err)\n\t}\n\n\tdatas := []map[string]interface{}{\n\t\t{\"Name\": \"create_from_map_2\", \"Age\": 19},\n\t\t{\"name\": \"create_from_map_3\", \"Age\": 20},\n\t}\n\n\tif err := DB.Model(&User{}).Create(&datas).Error; err != nil {\n\t\tt.Fatalf(\"failed to create data from slice of map, got error: %v\", err)\n\t}\n\n\tvar result2 User\n\tif err := DB.Where(\"name = ?\", \"create_from_map_2\").First(&result2).Error; err != nil || result2.Age != 19 {\n\t\tt.Fatalf(\"failed to query data after create from slice of map, got error %v\", err)\n\t}\n\n\tvar result3 User\n\tif err := DB.Where(\"name = ?\", \"create_from_map_3\").First(&result3).Error; err != nil || result3.Age != 20 {\n\t\tt.Fatalf(\"failed to query data after create from slice of map, got error %v\", err)\n\t}\n}\n\nfunc TestCreateWithAssociations(t *testing.T) {\n\tuser := *GetUser(\"create_with_associations\", Config{\n\t\tAccount:   true,\n\t\tPets:      2,\n\t\tToys:      3,\n\t\tCompany:   true,\n\t\tManager:   true,\n\t\tTeam:      4,\n\t\tLanguages: 3,\n\t\tFriends:   1,\n\t})\n\n\tif err := DB.Create(&user).Error; err != nil {\n\t\tt.Fatalf(\"errors happened when create: %v\", err)\n\t}\n\n\tCheckUser(t, user, user)\n\n\tvar user2 User\n\tDB.Preload(\"Account\").Preload(\"Pets\").Preload(\"Toys\").Preload(\"Company\").Preload(\"Manager\").Preload(\"Team\").Preload(\"Languages\").Preload(\"Friends\").Find(&user2, \"id = ?\", user.ID)\n\tCheckUser(t, user2, user)\n}\n\nfunc TestBulkCreateWithAssociations(t *testing.T) {\n\tusers := []User{\n\t\t*GetUser(\"bulk_1\", Config{Account: true, Pets: 2, Toys: 3, Company: true, Manager: true, Team: 0, Languages: 1, Friends: 1}),\n\t\t*GetUser(\"bulk_2\", Config{Account: false, Pets: 2, Toys: 4, Company: false, Manager: false, Team: 1, Languages: 3, Friends: 5}),\n\t\t*GetUser(\"bulk_3\", Config{Account: true, Pets: 0, Toys: 3, Company: true, Manager: false, Team: 4, Languages: 0, Friends: 1}),\n\t\t*GetUser(\"bulk_4\", Config{Account: true, Pets: 3, Toys: 0, Company: false, Manager: true, Team: 0, Languages: 3, Friends: 0}),\n\t\t*GetUser(\"bulk_5\", Config{Account: false, Pets: 0, Toys: 3, Company: true, Manager: false, Team: 1, Languages: 3, Friends: 1}),\n\t\t*GetUser(\"bulk_6\", Config{Account: true, Pets: 4, Toys: 3, Company: false, Manager: true, Team: 1, Languages: 3, Friends: 0}),\n\t\t*GetUser(\"bulk_7\", Config{Account: true, Pets: 1, Toys: 3, Company: true, Manager: true, Team: 4, Languages: 3, Friends: 1}),\n\t\t*GetUser(\"bulk_8\", Config{Account: false, Pets: 0, Toys: 0, Company: false, Manager: false, Team: 0, Languages: 0, Friends: 0}),\n\t}\n\n\tif results := DB.Create(&users); results.Error != nil {\n\t\tt.Fatalf(\"errors happened when create: %v\", results.Error)\n\t} else if results.RowsAffected != int64(len(users)) {\n\t\tt.Fatalf(\"rows affected expects: %v, got %v\", len(users), results.RowsAffected)\n\t}\n\n\tvar userIDs []uint\n\tfor _, user := range users {\n\t\tuserIDs = append(userIDs, user.ID)\n\t\tCheckUser(t, user, user)\n\t}\n\n\tvar users2 []User\n\tDB.Preload(\"Account\").Preload(\"Pets\").Preload(\"Toys\").Preload(\"Company\").Preload(\"Manager\").Preload(\"Team\").Preload(\"Languages\").Preload(\"Friends\").Find(&users2, \"id IN ?\", userIDs)\n\tfor idx, user := range users2 {\n\t\tCheckUser(t, user, users[idx])\n\t}\n}\n\nfunc TestBulkCreatePtrDataWithAssociations(t *testing.T) {\n\tusers := []*User{\n\t\tGetUser(\"bulk_ptr_1\", Config{Account: true, Pets: 2, Toys: 3, Company: true, Manager: true, Team: 0, Languages: 1, Friends: 1}),\n\t\tGetUser(\"bulk_ptr_2\", Config{Account: false, Pets: 2, Toys: 4, Company: false, Manager: false, Team: 1, Languages: 3, Friends: 5}),\n\t\tGetUser(\"bulk_ptr_3\", Config{Account: true, Pets: 0, Toys: 3, Company: true, Manager: false, Team: 4, Languages: 0, Friends: 1}),\n\t\tGetUser(\"bulk_ptr_4\", Config{Account: true, Pets: 3, Toys: 0, Company: false, Manager: true, Team: 0, Languages: 3, Friends: 0}),\n\t\tGetUser(\"bulk_ptr_5\", Config{Account: false, Pets: 0, Toys: 3, Company: true, Manager: false, Team: 1, Languages: 3, Friends: 1}),\n\t\tGetUser(\"bulk_ptr_6\", Config{Account: true, Pets: 4, Toys: 3, Company: false, Manager: true, Team: 1, Languages: 3, Friends: 0}),\n\t\tGetUser(\"bulk_ptr_7\", Config{Account: true, Pets: 1, Toys: 3, Company: true, Manager: true, Team: 4, Languages: 3, Friends: 1}),\n\t\tGetUser(\"bulk_ptr_8\", Config{Account: false, Pets: 0, Toys: 0, Company: false, Manager: false, Team: 0, Languages: 0, Friends: 0}),\n\t}\n\n\tif err := DB.Create(&users).Error; err != nil {\n\t\tt.Fatalf(\"errors happened when create: %v\", err)\n\t}\n\n\tvar userIDs []uint\n\tfor _, user := range users {\n\t\tuserIDs = append(userIDs, user.ID)\n\t\tCheckUser(t, *user, *user)\n\t}\n\n\tvar users2 []User\n\tDB.Preload(\"Account\").Preload(\"Pets\").Preload(\"Toys\").Preload(\"Company\").Preload(\"Manager\").Preload(\"Team\").Preload(\"Languages\").Preload(\"Friends\").Find(&users2, \"id IN ?\", userIDs)\n\tfor idx, user := range users2 {\n\t\tCheckUser(t, user, *users[idx])\n\t}\n}\n\nfunc TestPolymorphicHasOne(t *testing.T) {\n\tt.Run(\"Struct\", func(t *testing.T) {\n\t\tpet := Pet{\n\t\t\tName: \"PolymorphicHasOne\",\n\t\t\tToy:  Toy{Name: \"Toy-PolymorphicHasOne\"},\n\t\t}\n\n\t\tif err := DB.Create(&pet).Error; err != nil {\n\t\t\tt.Fatalf(\"errors happened when create: %v\", err)\n\t\t}\n\n\t\tCheckPet(t, pet, pet)\n\n\t\tvar pet2 Pet\n\t\tDB.Preload(\"Toy\").Find(&pet2, \"id = ?\", pet.ID)\n\t\tCheckPet(t, pet2, pet)\n\t})\n\n\tt.Run(\"Slice\", func(t *testing.T) {\n\t\tpets := []Pet{{\n\t\t\tName: \"PolymorphicHasOne-Slice-1\",\n\t\t\tToy:  Toy{Name: \"Toy-PolymorphicHasOne-Slice-1\"},\n\t\t}, {\n\t\t\tName: \"PolymorphicHasOne-Slice-2\",\n\t\t\tToy:  Toy{Name: \"Toy-PolymorphicHasOne-Slice-2\"},\n\t\t}, {\n\t\t\tName: \"PolymorphicHasOne-Slice-3\",\n\t\t\tToy:  Toy{Name: \"Toy-PolymorphicHasOne-Slice-3\"},\n\t\t}}\n\n\t\tif err := DB.Create(&pets).Error; err != nil {\n\t\t\tt.Fatalf(\"errors happened when create: %v\", err)\n\t\t}\n\n\t\tvar petIDs []uint\n\t\tfor _, pet := range pets {\n\t\t\tpetIDs = append(petIDs, pet.ID)\n\t\t\tCheckPet(t, pet, pet)\n\t\t}\n\n\t\tvar pets2 []Pet\n\t\tDB.Preload(\"Toy\").Find(&pets2, \"id IN ?\", petIDs)\n\t\tfor idx, pet := range pets2 {\n\t\t\tCheckPet(t, pet, pets[idx])\n\t\t}\n\t})\n\n\tt.Run(\"SliceOfPtr\", func(t *testing.T) {\n\t\tpets := []*Pet{{\n\t\t\tName: \"PolymorphicHasOne-Slice-1\",\n\t\t\tToy:  Toy{Name: \"Toy-PolymorphicHasOne-Slice-1\"},\n\t\t}, {\n\t\t\tName: \"PolymorphicHasOne-Slice-2\",\n\t\t\tToy:  Toy{Name: \"Toy-PolymorphicHasOne-Slice-2\"},\n\t\t}, {\n\t\t\tName: \"PolymorphicHasOne-Slice-3\",\n\t\t\tToy:  Toy{Name: \"Toy-PolymorphicHasOne-Slice-3\"},\n\t\t}}\n\n\t\tif err := DB.Create(&pets).Error; err != nil {\n\t\t\tt.Fatalf(\"errors happened when create: %v\", err)\n\t\t}\n\n\t\tfor _, pet := range pets {\n\t\t\tCheckPet(t, *pet, *pet)\n\t\t}\n\t})\n\n\tt.Run(\"Array\", func(t *testing.T) {\n\t\tpets := [...]Pet{{\n\t\t\tName: \"PolymorphicHasOne-Array-1\",\n\t\t\tToy:  Toy{Name: \"Toy-PolymorphicHasOne-Array-1\"},\n\t\t}, {\n\t\t\tName: \"PolymorphicHasOne-Array-2\",\n\t\t\tToy:  Toy{Name: \"Toy-PolymorphicHasOne-Array-2\"},\n\t\t}, {\n\t\t\tName: \"PolymorphicHasOne-Array-3\",\n\t\t\tToy:  Toy{Name: \"Toy-PolymorphicHasOne-Array-3\"},\n\t\t}}\n\n\t\tif err := DB.Create(&pets).Error; err != nil {\n\t\t\tt.Fatalf(\"errors happened when create: %v\", err)\n\t\t}\n\n\t\tfor _, pet := range pets {\n\t\t\tCheckPet(t, pet, pet)\n\t\t}\n\t})\n\n\tt.Run(\"ArrayPtr\", func(t *testing.T) {\n\t\tpets := [...]*Pet{{\n\t\t\tName: \"PolymorphicHasOne-Array-1\",\n\t\t\tToy:  Toy{Name: \"Toy-PolymorphicHasOne-Array-1\"},\n\t\t}, {\n\t\t\tName: \"PolymorphicHasOne-Array-2\",\n\t\t\tToy:  Toy{Name: \"Toy-PolymorphicHasOne-Array-2\"},\n\t\t}, {\n\t\t\tName: \"PolymorphicHasOne-Array-3\",\n\t\t\tToy:  Toy{Name: \"Toy-PolymorphicHasOne-Array-3\"},\n\t\t}}\n\n\t\tif err := DB.Create(&pets).Error; err != nil {\n\t\t\tt.Fatalf(\"errors happened when create: %v\", err)\n\t\t}\n\n\t\tfor _, pet := range pets {\n\t\t\tCheckPet(t, *pet, *pet)\n\t\t}\n\t})\n}\n\nfunc TestCreateEmptyStruct(t *testing.T) {\n\ttype EmptyStruct struct {\n\t\tID uint\n\t}\n\tDB.Migrator().DropTable(&EmptyStruct{})\n\n\tif err := DB.AutoMigrate(&EmptyStruct{}); err != nil {\n\t\tt.Errorf(\"no error should happen when auto migrate, but got %v\", err)\n\t}\n\n\tif err := DB.Create(&EmptyStruct{}).Error; err != nil {\n\t\tt.Errorf(\"No error should happen when creating user, but got %v\", err)\n\t}\n}\n\nfunc TestCreateEmptySlice(t *testing.T) {\n\tdata := []User{}\n\tif err := DB.Create(&data).Error; err != gorm.ErrEmptySlice {\n\t\tt.Errorf(\"no data should be created, got %v\", err)\n\t}\n\n\tsliceMap := []map[string]interface{}{}\n\tif err := DB.Model(&User{}).Create(&sliceMap).Error; err != gorm.ErrEmptySlice {\n\t\tt.Errorf(\"no data should be created, got %v\", err)\n\t}\n}\n\nfunc TestCreateInvalidSlice(t *testing.T) {\n\tusers := []*User{\n\t\tGetUser(\"invalid_slice_1\", Config{}),\n\t\tGetUser(\"invalid_slice_2\", Config{}),\n\t\tnil,\n\t}\n\n\tif err := DB.Create(&users).Error; !errors.Is(err, gorm.ErrInvalidData) {\n\t\tt.Errorf(\"should returns error invalid data when creating from slice that contains invalid data\")\n\t}\n}\n\nfunc TestCreateWithExistingTimestamp(t *testing.T) {\n\tuser := User{Name: \"CreateUserExistingTimestamp\"}\n\tcurTime := now.MustParse(\"2016-01-01\")\n\tuser.CreatedAt = curTime\n\tuser.UpdatedAt = curTime\n\tDB.Save(&user)\n\n\tAssertEqual(t, user.CreatedAt, curTime)\n\tAssertEqual(t, user.UpdatedAt, curTime)\n\n\tvar newUser User\n\tDB.First(&newUser, user.ID)\n\n\tAssertEqual(t, newUser.CreatedAt, curTime)\n\tAssertEqual(t, newUser.UpdatedAt, curTime)\n}\n\nfunc TestCreateWithNowFuncOverride(t *testing.T) {\n\tuser := User{Name: \"CreateUserTimestampOverride\"}\n\tcurTime := now.MustParse(\"2016-01-01\")\n\n\tNEW := DB.Session(&gorm.Session{\n\t\tNowFunc: func() time.Time {\n\t\t\treturn curTime\n\t\t},\n\t})\n\n\tNEW.Save(&user)\n\n\tAssertEqual(t, user.CreatedAt, curTime)\n\tAssertEqual(t, user.UpdatedAt, curTime)\n\n\tvar newUser User\n\tNEW.First(&newUser, user.ID)\n\n\tAssertEqual(t, newUser.CreatedAt, curTime)\n\tAssertEqual(t, newUser.UpdatedAt, curTime)\n}\n\nfunc TestCreateWithNoGORMPrimaryKey(t *testing.T) {\n\ttype JoinTable struct {\n\t\tUserID   uint\n\t\tFriendID uint\n\t}\n\n\tDB.Migrator().DropTable(&JoinTable{})\n\tif err := DB.AutoMigrate(&JoinTable{}); err != nil {\n\t\tt.Errorf(\"no error should happen when auto migrate, but got %v\", err)\n\t}\n\n\tjt := JoinTable{UserID: 1, FriendID: 2}\n\terr := DB.Create(&jt).Error\n\tif err != nil {\n\t\tt.Errorf(\"No error should happen when create a record without a GORM primary key. But in the database this primary key exists and is the union of 2 or more fields\\n But got: %s\", err)\n\t}\n}\n\nfunc TestSelectWithCreate(t *testing.T) {\n\tuser := *GetUser(\"select_create\", Config{Account: true, Pets: 3, Toys: 3, Company: true, Manager: true, Team: 3, Languages: 3, Friends: 4})\n\tDB.Select(\"Account\", \"Toys\", \"Manager\", \"ManagerID\", \"Languages\", \"Name\", \"CreatedAt\", \"Age\", \"Active\").Create(&user)\n\n\tvar user2 User\n\tDB.Preload(\"Account\").Preload(\"Pets\").Preload(\"Toys\").Preload(\"Company\").Preload(\"Manager\").Preload(\"Team\").Preload(\"Languages\").Preload(\"Friends\").First(&user2, user.ID)\n\n\tuser.Birthday = nil\n\tuser.Pets = nil\n\tuser.Company = Company{}\n\tuser.Team = nil\n\tuser.Friends = nil\n\n\tCheckUser(t, user2, user)\n}\n\nfunc TestOmitWithCreate(t *testing.T) {\n\tuser := *GetUser(\"omit_create\", Config{Account: true, Pets: 3, Toys: 3, Company: true, Manager: true, Team: 3, Languages: 3, Friends: 4})\n\tDB.Omit(\"Account\", \"Toys\", \"Manager\", \"Birthday\").Create(&user)\n\n\tvar result User\n\tDB.Preload(\"Account\").Preload(\"Pets\").Preload(\"Toys\").Preload(\"Company\").Preload(\"Manager\").Preload(\"Team\").Preload(\"Languages\").Preload(\"Friends\").First(&result, user.ID)\n\n\tuser.Birthday = nil\n\tuser.Account = Account{}\n\tuser.Toys = nil\n\tuser.Manager = nil\n\n\tCheckUser(t, result, user)\n\n\tuser2 := *GetUser(\"omit_create\", Config{Account: true, Pets: 3, Toys: 3, Company: true, Manager: true, Team: 3, Languages: 3, Friends: 4})\n\tDB.Omit(clause.Associations).Create(&user2)\n\n\tvar result2 User\n\tDB.Preload(clause.Associations).First(&result2, user2.ID)\n\n\tuser2.Account = Account{}\n\tuser2.Toys = nil\n\tuser2.Manager = nil\n\tuser2.Company = Company{}\n\tuser2.Pets = nil\n\tuser2.Team = nil\n\tuser2.Languages = nil\n\tuser2.Friends = nil\n\n\tCheckUser(t, result2, user2)\n}\n\nfunc TestFirstOrCreateNotExistsTable(t *testing.T) {\n\tcompany := Company{Name: \"first_or_create_if_not_exists_table\"}\n\tif err := DB.Table(\"not_exists\").FirstOrCreate(&company).Error; err == nil {\n\t\tt.Errorf(\"not exists table, but err is nil\")\n\t}\n}\n\nfunc TestFirstOrCreateWithPrimaryKey(t *testing.T) {\n\tcompany := Company{ID: 100, Name: \"company100_with_primarykey\"}\n\tDB.FirstOrCreate(&company)\n\n\tif company.ID != 100 {\n\t\tt.Errorf(\"invalid primary key after creating, got %v\", company.ID)\n\t}\n\n\tcompanies := []Company{\n\t\t{ID: 101, Name: \"company101_with_primarykey\"},\n\t\t{ID: 102, Name: \"company102_with_primarykey\"},\n\t}\n\tDB.Create(&companies)\n\n\tif companies[0].ID != 101 || companies[1].ID != 102 {\n\t\tt.Errorf(\"invalid primary key after creating, got %v, %v\", companies[0].ID, companies[1].ID)\n\t}\n}\n\nfunc TestCreateFromSubQuery(t *testing.T) {\n\tuser := User{Name: \"jinzhu\"}\n\n\tDB.Create(&user)\n\n\tsubQuery := DB.Table(\"users\").Where(\"name=?\", user.Name).Select(\"id\")\n\n\tresult := DB.Session(&gorm.Session{DryRun: true}).Model(&Pet{}).Create([]map[string]interface{}{\n\t\t{\n\t\t\t\"name\":    \"cat\",\n\t\t\t\"user_id\": gorm.Expr(\"(?)\", DB.Table(\"(?) as tmp\", subQuery).Select(\"@uid:=id\")),\n\t\t},\n\t\t{\n\t\t\t\"name\":    \"dog\",\n\t\t\t\"user_id\": gorm.Expr(\"@uid\"),\n\t\t},\n\t})\n\n\tif !regexp.MustCompile(`INSERT INTO .pets. \\(.name.,.user_id.\\) .*VALUES \\(.+,\\(SELECT @uid:=id FROM \\(SELECT id FROM .users. WHERE name=.+\\) as tmp\\)\\),\\(.+,@uid\\)`).MatchString(result.Statement.SQL.String()) {\n\t\tt.Errorf(\"invalid insert SQL, got %v\", result.Statement.SQL.String())\n\t}\n}\n\nfunc TestCreateNilPointer(t *testing.T) {\n\tvar user *User\n\n\terr := DB.Create(user).Error\n\tif err == nil || err != gorm.ErrInvalidValue {\n\t\tt.Fatalf(\"it is not ErrInvalidValue\")\n\t}\n}\n\nfunc TestFirstOrCreateRowsAffected(t *testing.T) {\n\tuser := User{Name: \"TestFirstOrCreateRowsAffected\"}\n\n\tres := DB.FirstOrCreate(&user, \"name = ?\", user.Name)\n\tif res.Error != nil || res.RowsAffected != 1 {\n\t\tt.Fatalf(\"first or create rows affect err:%v rows:%d\", res.Error, res.RowsAffected)\n\t}\n\n\tres = DB.FirstOrCreate(&user, \"name = ?\", user.Name)\n\tif res.Error != nil || res.RowsAffected != 0 {\n\t\tt.Fatalf(\"first or create rows affect err:%v rows:%d\", res.Error, res.RowsAffected)\n\t}\n}\n\nfunc TestCreateWithAutoIncrementCompositeKey(t *testing.T) {\n\ttype CompositeKeyProduct struct {\n\t\tProductID    int `gorm:\"primaryKey;autoIncrement:true;\"` // primary key\n\t\tLanguageCode int `gorm:\"primaryKey;\"`                    // primary key\n\t\tCode         string\n\t\tName         string\n\t}\n\n\tif err := DB.Migrator().DropTable(&CompositeKeyProduct{}); err != nil {\n\t\tt.Fatalf(\"failed to migrate, got error %v\", err)\n\t}\n\tif err := DB.AutoMigrate(&CompositeKeyProduct{}); err != nil {\n\t\tt.Fatalf(\"failed to migrate, got error %v\", err)\n\t}\n\n\tprod := &CompositeKeyProduct{\n\t\tLanguageCode: 56,\n\t\tCode:         \"Code56\",\n\t\tName:         \"ProductName56\",\n\t}\n\tif err := DB.Create(&prod).Error; err != nil {\n\t\tt.Fatalf(\"failed to create, got error %v\", err)\n\t}\n\n\tnewProd := &CompositeKeyProduct{}\n\tif err := DB.First(&newProd).Error; err != nil {\n\t\tt.Fatalf(\"errors happened when query: %v\", err)\n\t} else {\n\t\tAssertObjEqual(t, newProd, prod, \"ProductID\", \"LanguageCode\", \"Code\", \"Name\")\n\t}\n}\n\nfunc TestCreateOnConflictWithDefaultNull(t *testing.T) {\n\ttype OnConflictUser struct {\n\t\tID     string\n\t\tName   string `gorm:\"default:null\"`\n\t\tEmail  string\n\t\tMobile string `gorm:\"default:'133xxxx'\"`\n\t}\n\n\terr := DB.Migrator().DropTable(&OnConflictUser{})\n\tAssertEqual(t, err, nil)\n\terr = DB.AutoMigrate(&OnConflictUser{})\n\tAssertEqual(t, err, nil)\n\n\tu := OnConflictUser{\n\t\tID:     \"on-conflict-user-id\",\n\t\tName:   \"on-conflict-user-name\",\n\t\tEmail:  \"on-conflict-user-email\",\n\t\tMobile: \"on-conflict-user-mobile\",\n\t}\n\terr = DB.Create(&u).Error\n\tAssertEqual(t, err, nil)\n\n\tu.Name = \"on-conflict-user-name-2\"\n\tu.Email = \"on-conflict-user-email-2\"\n\tu.Mobile = \"\"\n\terr = DB.Clauses(clause.OnConflict{UpdateAll: true}).Create(&u).Error\n\tAssertEqual(t, err, nil)\n\n\tvar u2 OnConflictUser\n\terr = DB.Where(\"id = ?\", u.ID).First(&u2).Error\n\tAssertEqual(t, err, nil)\n\tAssertEqual(t, u2.Name, \"on-conflict-user-name-2\")\n\tAssertEqual(t, u2.Email, \"on-conflict-user-email-2\")\n\tAssertEqual(t, u2.Mobile, \"133xxxx\")\n}\n\nfunc TestCreateFromMapWithoutPK(t *testing.T) {\n\tif !isMysql() {\n\t\tt.Skipf(\"This test case skipped, because of only supporting for mysql\")\n\t}\n\n\t// case 1: one record, create from map[string]interface{}\n\tmapValue1 := map[string]interface{}{\"name\": \"create_from_map_with_schema1\", \"age\": 1}\n\tif err := DB.Model(&User{}).Create(mapValue1).Error; err != nil {\n\t\tt.Fatalf(\"failed to create data from map, got error: %v\", err)\n\t}\n\n\tif _, ok := mapValue1[\"id\"]; !ok {\n\t\tt.Fatal(\"failed to create data from map with table, returning map has no primary key\")\n\t}\n\n\tvar result1 User\n\tif err := DB.Where(\"name = ?\", \"create_from_map_with_schema1\").First(&result1).Error; err != nil || result1.Age != 1 {\n\t\tt.Fatalf(\"failed to create from map, got error %v\", err)\n\t}\n\n\tvar idVal int64\n\t_, ok := mapValue1[\"id\"].(uint)\n\tif ok {\n\t\tt.Skipf(\"This test case skipped, because the db supports returning\")\n\t}\n\n\tidVal, ok = mapValue1[\"id\"].(int64)\n\tif !ok {\n\t\tt.Fatal(\"ret result missing id\")\n\t}\n\n\tif int64(result1.ID) != idVal {\n\t\tt.Fatal(\"failed to create data from map with table, @id != id\")\n\t}\n\n\t// case2: one record, create from *map[string]interface{}\n\tmapValue2 := map[string]interface{}{\"name\": \"create_from_map_with_schema2\", \"age\": 1}\n\tif err := DB.Model(&User{}).Create(&mapValue2).Error; err != nil {\n\t\tt.Fatalf(\"failed to create data from map, got error: %v\", err)\n\t}\n\n\tif _, ok := mapValue2[\"id\"]; !ok {\n\t\tt.Fatal(\"failed to create data from map with table, returning map has no primary key\")\n\t}\n\n\tvar result2 User\n\tif err := DB.Where(\"name = ?\", \"create_from_map_with_schema2\").First(&result2).Error; err != nil || result2.Age != 1 {\n\t\tt.Fatalf(\"failed to create from map, got error %v\", err)\n\t}\n\n\t_, ok = mapValue2[\"id\"].(uint)\n\tif ok {\n\t\tt.Skipf(\"This test case skipped, because the db supports returning\")\n\t}\n\n\tidVal, ok = mapValue2[\"id\"].(int64)\n\tif !ok {\n\t\tt.Fatal(\"ret result missing id\")\n\t}\n\n\tif int64(result2.ID) != idVal {\n\t\tt.Fatal(\"failed to create data from map with table, @id != id\")\n\t}\n\n\t// case 3: records\n\tvalues := []map[string]interface{}{\n\t\t{\"name\": \"create_from_map_with_schema11\", \"age\": 1}, {\"name\": \"create_from_map_with_schema12\", \"age\": 1},\n\t}\n\n\tbeforeLen := len(values)\n\tif err := DB.Model(&User{}).Create(&values).Error; err != nil {\n\t\tt.Fatalf(\"failed to create data from map, got error: %v\", err)\n\t}\n\n\t// mariadb with returning, values will be appended with id map\n\tif len(values) == beforeLen*2 {\n\t\tt.Skipf(\"This test case skipped, because the db supports returning\")\n\t}\n\n\tfor i := range values {\n\t\tv, ok := values[i][\"id\"]\n\t\tif !ok {\n\t\t\tt.Fatal(\"failed to create data from map with table, returning map has no primary key\")\n\t\t}\n\n\t\tvar result User\n\t\tif err := DB.Where(\"name = ?\", fmt.Sprintf(\"create_from_map_with_schema1%d\", i+1)).First(&result).Error; err != nil || result.Age != 1 {\n\t\t\tt.Fatalf(\"failed to create from map, got error %v\", err)\n\t\t}\n\t\tif int64(result.ID) != v.(int64) {\n\t\t\tt.Fatal(\"failed to create data from map with table, @id != id\")\n\t\t}\n\t}\n}\n\nfunc TestCreateFromMapWithTable(t *testing.T) {\n\ttableDB := DB.Table(\"users\")\n\tsupportLastInsertID := isMysql() || isSqlite()\n\n\t// case 1: create from map[string]interface{}\n\trecord := map[string]interface{}{\"name\": \"create_from_map_with_table\", \"age\": 18}\n\tif err := tableDB.Create(record).Error; err != nil {\n\t\tt.Fatalf(\"failed to create data from map with table, got error: %v\", err)\n\t}\n\n\tif _, ok := record[\"@id\"]; !ok && supportLastInsertID {\n\t\tt.Fatal(\"failed to create data from map with table, returning map has no key '@id'\")\n\t}\n\n\tvar res map[string]interface{}\n\tif err := tableDB.Select([]string{\"id\", \"name\", \"age\"}).Where(\"name = ?\", \"create_from_map_with_table\").Find(&res).Error; err != nil || res[\"age\"] != int64(18) {\n\t\tt.Fatalf(\"failed to create from map, got error %v\", err)\n\t}\n\n\tif _, ok := record[\"@id\"]; ok && fmt.Sprint(res[\"id\"]) != fmt.Sprint(record[\"@id\"]) {\n\t\tt.Fatalf(\"failed to create data from map with table, @id != id, got %v, expect %v\", res[\"id\"], record[\"@id\"])\n\t}\n\n\t// case 2: create from *map[string]interface{}\n\trecord1 := map[string]interface{}{\"name\": \"create_from_map_with_table_1\", \"age\": 18}\n\ttableDB2 := DB.Table(\"users\")\n\tif err := tableDB2.Create(&record1).Error; err != nil {\n\t\tt.Fatalf(\"failed to create data from map, got error: %v\", err)\n\t}\n\tif _, ok := record1[\"@id\"]; !ok && supportLastInsertID {\n\t\tt.Fatal(\"failed to create data from map with table, returning map has no key '@id'\")\n\t}\n\n\tvar res1 map[string]interface{}\n\tif err := tableDB2.Select([]string{\"id\", \"name\", \"age\"}).Where(\"name = ?\", \"create_from_map_with_table_1\").Find(&res1).Error; err != nil || res1[\"age\"] != int64(18) {\n\t\tt.Fatalf(\"failed to create from map, got error %v\", err)\n\t}\n\n\tif _, ok := record1[\"@id\"]; ok && fmt.Sprint(res1[\"id\"]) != fmt.Sprint(record1[\"@id\"]) {\n\t\tt.Fatal(\"failed to create data from map with table, @id != id\")\n\t}\n\n\t// case 3: create from []map[string]interface{}\n\trecords := []map[string]interface{}{\n\t\t{\"name\": \"create_from_map_with_table_2\", \"age\": 19},\n\t\t{\"name\": \"create_from_map_with_table_3\", \"age\": 20},\n\t}\n\n\ttableDB = DB.Table(\"users\")\n\tif err := tableDB.Create(&records).Error; err != nil {\n\t\tt.Fatalf(\"failed to create data from slice of map, got error: %v\", err)\n\t}\n\n\tif _, ok := records[0][\"@id\"]; !ok && supportLastInsertID {\n\t\tt.Fatal(\"failed to create data from map with table, returning map has no key '@id'\")\n\t}\n\n\tif _, ok := records[1][\"@id\"]; !ok && supportLastInsertID {\n\t\tt.Fatal(\"failed to create data from map with table, returning map has no key '@id'\")\n\t}\n\n\tvar res2 map[string]interface{}\n\tif err := tableDB.Select([]string{\"id\", \"name\", \"age\"}).Where(\"name = ?\", \"create_from_map_with_table_2\").Find(&res2).Error; err != nil || res2[\"age\"] != int64(19) {\n\t\tt.Fatalf(\"failed to query data after create from slice of map, got error %v\", err)\n\t}\n\n\tvar res3 map[string]interface{}\n\tif err := DB.Table(\"users\").Select([]string{\"id\", \"name\", \"age\"}).Where(\"name = ?\", \"create_from_map_with_table_3\").Find(&res3).Error; err != nil || res3[\"age\"] != int64(20) {\n\t\tt.Fatalf(\"failed to query data after create from slice of map, got error %v\", err)\n\t}\n\n\tif _, ok := records[0][\"@id\"]; ok && fmt.Sprint(res2[\"id\"]) != fmt.Sprint(records[0][\"@id\"]) {\n\t\tt.Errorf(\"failed to create data from map with table, @id != id, got %v, expect %v\", res2[\"id\"], records[0][\"@id\"])\n\t}\n\n\tif _, ok := records[1][\"id\"]; ok && fmt.Sprint(res3[\"id\"]) != fmt.Sprint(records[1][\"@id\"]) {\n\t\tt.Errorf(\"failed to create data from map with table, @id != id\")\n\t}\n}\n"
  },
  {
    "path": "tests/customize_field_test.go",
    "content": "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 TestCustomizeColumn(t *testing.T) {\n\ttype CustomizeColumn struct {\n\t\tID   int64      `gorm:\"column:mapped_id; primary_key:yes\"`\n\t\tName string     `gorm:\"column:mapped_name\"`\n\t\tDate *time.Time `gorm:\"column:mapped_time\"`\n\t}\n\n\tDB.Migrator().DropTable(&CustomizeColumn{})\n\tDB.AutoMigrate(&CustomizeColumn{})\n\n\texpected := \"foo\"\n\tnow := time.Now()\n\tcc := CustomizeColumn{ID: 666, Name: expected, Date: &now}\n\n\tif count := DB.Create(&cc).RowsAffected; count != 1 {\n\t\tt.Error(\"There should be one record be affected when create record\")\n\t}\n\n\tvar cc1 CustomizeColumn\n\tDB.First(&cc1, \"mapped_name = ?\", \"foo\")\n\n\tif cc1.Name != expected {\n\t\tt.Errorf(\"Failed to query CustomizeColumn\")\n\t}\n\n\tcc.Name = \"bar\"\n\tDB.Save(&cc)\n\n\tvar cc2 CustomizeColumn\n\tDB.First(&cc2, \"mapped_id = ?\", 666)\n\tif cc2.Name != \"bar\" {\n\t\tt.Errorf(\"Failed to query CustomizeColumn\")\n\t}\n}\n\nfunc TestCustomColumnAndIgnoredFieldClash(t *testing.T) {\n\t// Make sure an ignored field does not interfere with another field's custom\n\t// column name that matches the ignored field.\n\ttype CustomColumnAndIgnoredFieldClash struct {\n\t\tBody    string `gorm:\"-\"`\n\t\tRawBody string `gorm:\"column:body\"`\n\t}\n\n\tDB.Migrator().DropTable(&CustomColumnAndIgnoredFieldClash{})\n\n\tif err := DB.AutoMigrate(&CustomColumnAndIgnoredFieldClash{}); err != nil {\n\t\tt.Errorf(\"Should not raise error: %v\", err)\n\t}\n}\n\nfunc TestCustomizeField(t *testing.T) {\n\ttype CustomizeFieldStruct struct {\n\t\tgorm.Model\n\t\tName                    string\n\t\tFieldAllowCreate        string `gorm:\"<-:create\"`\n\t\tFieldAllowUpdate        string `gorm:\"<-:update\"`\n\t\tFieldAllowSave          string `gorm:\"<-\"`\n\t\tFieldAllowSave2         string `gorm:\"<-:create,update\"`\n\t\tFieldAllowSave3         string `gorm:\"->:false;<-:create\"`\n\t\tFieldReadonly           string `gorm:\"->\"`\n\t\tFieldIgnore             string `gorm:\"-\"`\n\t\tAutoUnixCreateTime      int32  `gorm:\"autocreatetime\"`\n\t\tAutoUnixMilliCreateTime int    `gorm:\"autocreatetime:milli\"`\n\t\tAutoUnixNanoCreateTime  int64  `gorm:\"autocreatetime:nano\"`\n\t\tAutoUnixUpdateTime      uint32 `gorm:\"autoupdatetime\"`\n\t\tAutoUnixMilliUpdateTime int    `gorm:\"autoupdatetime:milli\"`\n\t\tAutoUnixNanoUpdateTime  uint64 `gorm:\"autoupdatetime:nano\"`\n\t}\n\n\tDB.Migrator().DropTable(&CustomizeFieldStruct{})\n\n\tif err := DB.AutoMigrate(&CustomizeFieldStruct{}); err != nil {\n\t\tt.Errorf(\"Failed to migrate, got error: %v\", err)\n\t}\n\n\tif DB.Migrator().HasColumn(&CustomizeFieldStruct{}, \"FieldIgnore\") {\n\t\tt.Errorf(\"FieldIgnore should not be created\")\n\t}\n\n\tif DB.Migrator().HasColumn(&CustomizeFieldStruct{}, \"field_ignore\") {\n\t\tt.Errorf(\"FieldIgnore should not be created\")\n\t}\n\n\tgenerateStruct := func(name string) *CustomizeFieldStruct {\n\t\treturn &CustomizeFieldStruct{\n\t\t\tName:             name,\n\t\t\tFieldAllowCreate: name + \"_allow_create\",\n\t\t\tFieldAllowUpdate: name + \"_allow_update\",\n\t\t\tFieldAllowSave:   name + \"_allow_save\",\n\t\t\tFieldAllowSave2:  name + \"_allow_save2\",\n\t\t\tFieldAllowSave3:  name + \"_allow_save3\",\n\t\t\tFieldReadonly:    name + \"_allow_readonly\",\n\t\t\tFieldIgnore:      name + \"_allow_ignore\",\n\t\t}\n\t}\n\n\tcreate := generateStruct(\"create\")\n\tDB.Create(&create)\n\n\tvar result CustomizeFieldStruct\n\tDB.Find(&result, \"name = ?\", \"create\")\n\n\tAssertObjEqual(t, result, create, \"Name\", \"FieldAllowCreate\", \"FieldAllowSave\", \"FieldAllowSave2\")\n\n\tif result.FieldAllowUpdate != \"\" || result.FieldReadonly != \"\" || result.FieldIgnore != \"\" || result.FieldAllowSave3 != \"\" {\n\t\tt.Fatalf(\"invalid result: %#v\", result)\n\t}\n\n\tif int(result.AutoUnixCreateTime) != int(result.AutoUnixUpdateTime) || result.AutoUnixCreateTime == 0 {\n\t\tt.Fatalf(\"invalid create/update unix time: %#v\", result)\n\t}\n\n\tif int(result.AutoUnixMilliCreateTime) != int(result.AutoUnixMilliUpdateTime) || result.AutoUnixMilliCreateTime == 0 || int(result.AutoUnixMilliCreateTime)/int(result.AutoUnixCreateTime) < 1e3 {\n\t\tt.Fatalf(\"invalid create/update unix milli time: %#v\", result)\n\t}\n\n\tif int(result.AutoUnixNanoCreateTime) != int(result.AutoUnixNanoUpdateTime) || result.AutoUnixNanoCreateTime == 0 || int(result.AutoUnixNanoCreateTime)/int(result.AutoUnixCreateTime) < 1e6 {\n\t\tt.Fatalf(\"invalid create/update unix nano time: %#v\", result)\n\t}\n\n\tresult.FieldAllowUpdate = \"field_allow_update_updated\"\n\tresult.FieldReadonly = \"field_readonly_updated\"\n\tresult.FieldIgnore = \"field_ignore_updated\"\n\tDB.Save(&result)\n\n\tvar result2 CustomizeFieldStruct\n\tDB.Find(&result2, \"name = ?\", \"create\")\n\n\tif result2.FieldAllowUpdate != result.FieldAllowUpdate || result2.FieldReadonly != \"\" || result2.FieldIgnore != \"\" {\n\t\tt.Fatalf(\"invalid updated result: %#v\", result2)\n\t}\n\n\tif err := DB.Where(CustomizeFieldStruct{Name: create.Name, FieldReadonly: create.FieldReadonly, FieldIgnore: create.FieldIgnore}).First(&CustomizeFieldStruct{}).Error; err == nil {\n\t\tt.Fatalf(\"Should failed to find result\")\n\t}\n\n\tif err := DB.Table(\"customize_field_structs\").Where(\"1 = 1\").UpdateColumn(\"field_readonly\", \"readonly\").Error; err != nil {\n\t\tt.Fatalf(\"failed to update field_readonly column\")\n\t}\n\n\tif err := DB.Where(CustomizeFieldStruct{Name: create.Name, FieldReadonly: \"readonly\", FieldIgnore: create.FieldIgnore}).First(&CustomizeFieldStruct{}).Error; err != nil {\n\t\tt.Fatalf(\"Should find result\")\n\t}\n\n\tvar result3 CustomizeFieldStruct\n\tDB.Find(&result3, \"name = ?\", \"create\")\n\n\tif result3.FieldReadonly != \"readonly\" {\n\t\tt.Fatalf(\"invalid updated result: %#v\", result3)\n\t}\n\n\tvar result4 CustomizeFieldStruct\n\tif err := DB.First(&result4, \"field_allow_save3 = ?\", create.FieldAllowSave3).Error; err != nil {\n\t\tt.Fatalf(\"failed to query with inserted field, got error %v\", err)\n\t}\n\n\tAssertEqual(t, result3, result4)\n\n\tcreateWithDefaultTime := generateStruct(\"create_with_default_time\")\n\tcreateWithDefaultTime.AutoUnixCreateTime = 100\n\tcreateWithDefaultTime.AutoUnixUpdateTime = 100\n\tcreateWithDefaultTime.AutoUnixMilliCreateTime = 100\n\tcreateWithDefaultTime.AutoUnixMilliUpdateTime = 100\n\tcreateWithDefaultTime.AutoUnixNanoCreateTime = 100\n\tcreateWithDefaultTime.AutoUnixNanoUpdateTime = 100\n\tDB.Create(&createWithDefaultTime)\n\n\tvar createWithDefaultTimeResult CustomizeFieldStruct\n\tDB.Find(&createWithDefaultTimeResult, \"name = ?\", createWithDefaultTime.Name)\n\n\tif int(createWithDefaultTimeResult.AutoUnixCreateTime) != int(createWithDefaultTimeResult.AutoUnixUpdateTime) || createWithDefaultTimeResult.AutoUnixCreateTime != 100 {\n\t\tt.Fatalf(\"invalid create/update unix time: %#v\", createWithDefaultTimeResult)\n\t}\n\n\tif int(createWithDefaultTimeResult.AutoUnixMilliCreateTime) != int(createWithDefaultTimeResult.AutoUnixMilliUpdateTime) || createWithDefaultTimeResult.AutoUnixMilliCreateTime != 100 {\n\t\tt.Fatalf(\"invalid create/update unix milli time: %#v\", createWithDefaultTimeResult)\n\t}\n\n\tif int(createWithDefaultTimeResult.AutoUnixNanoCreateTime) != int(createWithDefaultTimeResult.AutoUnixNanoUpdateTime) || createWithDefaultTimeResult.AutoUnixNanoCreateTime != 100 {\n\t\tt.Fatalf(\"invalid create/update unix nano time: %#v\", createWithDefaultTimeResult)\n\t}\n}\n"
  },
  {
    "path": "tests/default_value_test.go",
    "content": "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 struct {\n\t\tgorm.Model\n\t\tEmail   string    `gorm:\"not null;index:,unique\"`\n\t\tName    string    `gorm:\"notNull;default:foo\"`\n\t\tName2   string    `gorm:\"size:233;not null;default:'foo'\"`\n\t\tName3   string    `gorm:\"size:233;notNull;default:''\"`\n\t\tAge     int       `gorm:\"default:18\"`\n\t\tCreated time.Time `gorm:\"default:2000-01-02\"`\n\t\tEnabled bool      `gorm:\"default:true\"`\n\t}\n\n\tDB.Migrator().DropTable(&Harumph{})\n\n\tif err := DB.AutoMigrate(&Harumph{}); err != nil {\n\t\tt.Fatalf(\"Failed to migrate with default value, got error: %v\", err)\n\t}\n\n\tharumph := Harumph{Email: \"hello@gorm.io\"}\n\tif err := DB.Create(&harumph).Error; err != nil {\n\t\tt.Fatalf(\"Failed to create data with default value, got error: %v\", err)\n\t} else if harumph.Name != \"foo\" || harumph.Name2 != \"foo\" || harumph.Name3 != \"\" || harumph.Age != 18 || !harumph.Enabled || harumph.Created.Format(\"20060102\") != \"20000102\" {\n\t\tt.Fatalf(\"Failed to create data with default value, got: %+v\", harumph)\n\t}\n\n\tvar result Harumph\n\tif err := DB.First(&result, \"email = ?\", \"hello@gorm.io\").Error; err != nil {\n\t\tt.Fatalf(\"Failed to find created data, got error: %v\", err)\n\t} else if result.Name != \"foo\" || result.Name2 != \"foo\" || result.Name3 != \"\" || result.Age != 18 || !result.Enabled || result.Created.Format(\"20060102\") != \"20000102\" {\n\t\tt.Fatalf(\"Failed to find created data with default data, got %+v\", result)\n\t}\n\n\ttype Harumph2 struct {\n\t\tID      int       `gorm:\"default:0\"`\n\t\tEmail   string    `gorm:\"not null;index:,unique\"`\n\t\tName    string    `gorm:\"notNull;default:foo\"`\n\t\tName2   string    `gorm:\"size:233;not null;default:'foo'\"`\n\t\tName3   string    `gorm:\"size:233;notNull;default:''\"`\n\t\tAge     int       `gorm:\"default:18\"`\n\t\tCreated time.Time `gorm:\"default:2000-01-02\"`\n\t\tEnabled bool      `gorm:\"default:true\"`\n\t}\n\n\tharumph2 := Harumph2{ID: 2, Email: \"hello2@gorm.io\"}\n\tif err := DB.Table(\"harumphs\").Create(&harumph2).Error; err != nil {\n\t\tt.Fatalf(\"Failed to create data with default value, got error: %v\", err)\n\t} else if harumph2.ID != 2 || harumph2.Name != \"foo\" || harumph2.Name2 != \"foo\" || harumph2.Name3 != \"\" || harumph2.Age != 18 || !harumph2.Enabled || harumph2.Created.Format(\"20060102\") != \"20000102\" {\n\t\tt.Fatalf(\"Failed to create data with default value, got: %+v\", harumph2)\n\t}\n}\n"
  },
  {
    "path": "tests/delete_test.go",
    "content": "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)\n\nfunc TestDelete(t *testing.T) {\n\tusers := []User{*GetUser(\"delete\", Config{}), *GetUser(\"delete\", Config{}), *GetUser(\"delete\", Config{})}\n\n\tif err := DB.Create(&users).Error; err != nil {\n\t\tt.Errorf(\"errors happened when create: %v\", err)\n\t}\n\n\tfor _, user := range users {\n\t\tif user.ID == 0 {\n\t\t\tt.Fatalf(\"user's primary key should has value after create, got : %v\", user.ID)\n\t\t}\n\t}\n\n\tif res := DB.Delete(&users[1]); res.Error != nil || res.RowsAffected != 1 {\n\t\tt.Errorf(\"errors happened when delete: %v, affected: %v\", res.Error, res.RowsAffected)\n\t}\n\n\tvar result User\n\tif err := DB.Where(\"id = ?\", users[1].ID).First(&result).Error; err == nil || !errors.Is(err, gorm.ErrRecordNotFound) {\n\t\tt.Errorf(\"should returns record not found error, but got %v\", err)\n\t}\n\n\tfor _, user := range []User{users[0], users[2]} {\n\t\tresult = User{}\n\t\tif err := DB.Where(\"id = ?\", user.ID).First(&result).Error; err != nil {\n\t\t\tt.Errorf(\"no error should returns when query %v, but got %v\", user.ID, err)\n\t\t}\n\t}\n\n\tfor _, user := range []User{users[0], users[2]} {\n\t\tresult = User{}\n\t\tif err := DB.Where(\"id = ?\", user.ID).First(&result).Error; err != nil {\n\t\t\tt.Errorf(\"no error should returns when query %v, but got %v\", user.ID, err)\n\t\t}\n\t}\n\n\tif err := DB.Delete(&users[0]).Error; err != nil {\n\t\tt.Errorf(\"errors happened when delete: %v\", err)\n\t}\n\n\tif err := DB.Delete(&User{}).Error; err != gorm.ErrMissingWhereClause {\n\t\tt.Errorf(\"errors happened when delete: %v\", err)\n\t}\n\n\tif err := DB.Where(\"id = ?\", users[0].ID).First(&result).Error; err == nil || !errors.Is(err, gorm.ErrRecordNotFound) {\n\t\tt.Errorf(\"should returns record not found error, but got %v\", err)\n\t}\n}\n\nfunc TestDeleteWithTable(t *testing.T) {\n\ttype UserWithDelete struct {\n\t\tgorm.Model\n\t\tName string\n\t}\n\n\tDB.Table(\"deleted_users\").Migrator().DropTable(UserWithDelete{})\n\tDB.Table(\"deleted_users\").AutoMigrate(UserWithDelete{})\n\n\tuser := UserWithDelete{Name: \"delete1\"}\n\tDB.Table(\"deleted_users\").Create(&user)\n\n\tvar result UserWithDelete\n\tif err := DB.Table(\"deleted_users\").First(&result).Error; err != nil {\n\t\tt.Errorf(\"failed to find deleted user, got error %v\", err)\n\t}\n\n\tAssertEqual(t, result, user)\n\n\tif err := DB.Table(\"deleted_users\").Delete(&result).Error; err != nil {\n\t\tt.Errorf(\"failed to delete user, got error %v\", err)\n\t}\n\n\tvar result2 UserWithDelete\n\tif err := DB.Table(\"deleted_users\").First(&result2, user.ID).Error; !errors.Is(err, gorm.ErrRecordNotFound) {\n\t\tt.Errorf(\"should raise record not found error, but got error %v\", err)\n\t}\n\n\tvar result3 UserWithDelete\n\tif err := DB.Table(\"deleted_users\").Unscoped().First(&result3, user.ID).Error; err != nil {\n\t\tt.Fatalf(\"failed to find record, got error %v\", err)\n\t}\n\n\tif err := DB.Table(\"deleted_users\").Unscoped().Delete(&result).Error; err != nil {\n\t\tt.Errorf(\"failed to delete user with unscoped, got error %v\", err)\n\t}\n\n\tvar result4 UserWithDelete\n\tif err := DB.Table(\"deleted_users\").Unscoped().First(&result4, user.ID).Error; !errors.Is(err, gorm.ErrRecordNotFound) {\n\t\tt.Errorf(\"should raise record not found error, but got error %v\", err)\n\t}\n}\n\nfunc TestInlineCondDelete(t *testing.T) {\n\tuser1 := *GetUser(\"inline_delete_1\", Config{})\n\tuser2 := *GetUser(\"inline_delete_2\", Config{})\n\tDB.Save(&user1).Save(&user2)\n\n\tif DB.Delete(&User{}, user1.ID).Error != nil {\n\t\tt.Errorf(\"No error should happen when delete a record\")\n\t} else if err := DB.Where(\"name = ?\", user1.Name).First(&User{}).Error; !errors.Is(err, gorm.ErrRecordNotFound) {\n\t\tt.Errorf(\"User can't be found after delete\")\n\t}\n\n\tif err := DB.Delete(&User{}, \"name = ?\", user2.Name).Error; err != nil {\n\t\tt.Errorf(\"No error should happen when delete a record, err=%s\", err)\n\t} else if err := DB.Where(\"name = ?\", user2.Name).First(&User{}).Error; !errors.Is(err, gorm.ErrRecordNotFound) {\n\t\tt.Errorf(\"User can't be found after delete\")\n\t}\n}\n\nfunc TestBlockGlobalDelete(t *testing.T) {\n\tif err := DB.Delete(&User{}).Error; err == nil || !errors.Is(err, gorm.ErrMissingWhereClause) {\n\t\tt.Errorf(\"should returns missing WHERE clause while deleting error\")\n\t}\n\n\tif err := DB.Session(&gorm.Session{AllowGlobalUpdate: true}).Delete(&User{}).Error; err != nil {\n\t\tt.Errorf(\"should returns no error while enable global update, but got err %v\", err)\n\t}\n}\n\nfunc TestDeleteWithAssociations(t *testing.T) {\n\tuser := GetUser(\"delete_with_associations\", Config{Account: true, Pets: 2, Toys: 4, Company: true, Manager: true, Team: 1, Languages: 1, Friends: 1})\n\n\tif err := DB.Create(user).Error; err != nil {\n\t\tt.Fatalf(\"failed to create user, got error %v\", err)\n\t}\n\n\tif err := DB.Select(clause.Associations, \"Pets.Toy\").Delete(&user).Error; err != nil {\n\t\tt.Fatalf(\"failed to delete user, got error %v\", err)\n\t}\n\n\tfor key, value := range map[string]int64{\"Account\": 1, \"Pets\": 2, \"Toys\": 4, \"Company\": 1, \"Manager\": 1, \"Team\": 1, \"Languages\": 0, \"Friends\": 0} {\n\t\tif count := DB.Unscoped().Model(&user).Association(key).Count(); count != value {\n\t\t\tt.Errorf(\"user's %v expects: %v, got %v\", key, value, count)\n\t\t}\n\t}\n\n\tfor key, value := range map[string]int64{\"Account\": 0, \"Pets\": 0, \"Toys\": 0, \"Company\": 1, \"Manager\": 1, \"Team\": 0, \"Languages\": 0, \"Friends\": 0} {\n\t\tif count := DB.Model(&user).Association(key).Count(); count != value {\n\t\t\tt.Errorf(\"user's %v expects: %v, got %v\", key, value, count)\n\t\t}\n\t}\n}\n\nfunc TestDeleteAssociationsWithUnscoped(t *testing.T) {\n\tuser := GetUser(\"unscoped_delete_with_associations\", Config{Account: true, Pets: 2, Toys: 4, Company: true, Manager: true, Team: 1, Languages: 1, Friends: 1})\n\n\tif err := DB.Create(user).Error; err != nil {\n\t\tt.Fatalf(\"failed to create user, got error %v\", err)\n\t}\n\n\tif err := DB.Unscoped().Select(clause.Associations, \"Pets.Toy\").Delete(&user).Error; err != nil {\n\t\tt.Fatalf(\"failed to delete user, got error %v\", err)\n\t}\n\n\tfor key, value := range map[string]int64{\"Account\": 0, \"Pets\": 0, \"Toys\": 0, \"Company\": 1, \"Manager\": 1, \"Team\": 0, \"Languages\": 0, \"Friends\": 0} {\n\t\tif count := DB.Unscoped().Model(&user).Association(key).Count(); count != value {\n\t\t\tt.Errorf(\"user's %v expects: %v, got %v\", key, value, count)\n\t\t}\n\t}\n\n\tfor key, value := range map[string]int64{\"Account\": 0, \"Pets\": 0, \"Toys\": 0, \"Company\": 1, \"Manager\": 1, \"Team\": 0, \"Languages\": 0, \"Friends\": 0} {\n\t\tif count := DB.Model(&user).Association(key).Count(); count != value {\n\t\t\tt.Errorf(\"user's %v expects: %v, got %v\", key, value, count)\n\t\t}\n\t}\n}\n\nfunc TestDeleteSliceWithAssociations(t *testing.T) {\n\tusers := []User{\n\t\t*GetUser(\"delete_slice_with_associations1\", Config{Account: true, Pets: 4, Toys: 1, Company: true, Manager: true, Team: 1, Languages: 1, Friends: 4}),\n\t\t*GetUser(\"delete_slice_with_associations2\", Config{Account: true, Pets: 3, Toys: 2, Company: true, Manager: true, Team: 2, Languages: 2, Friends: 3}),\n\t\t*GetUser(\"delete_slice_with_associations3\", Config{Account: true, Pets: 2, Toys: 3, Company: true, Manager: true, Team: 3, Languages: 3, Friends: 2}),\n\t\t*GetUser(\"delete_slice_with_associations4\", Config{Account: true, Pets: 1, Toys: 4, Company: true, Manager: true, Team: 4, Languages: 4, Friends: 1}),\n\t}\n\n\tif err := DB.Create(users).Error; err != nil {\n\t\tt.Fatalf(\"failed to create user, got error %v\", err)\n\t}\n\n\tif err := DB.Select(clause.Associations).Delete(&users).Error; err != nil {\n\t\tt.Fatalf(\"failed to delete user, got error %v\", err)\n\t}\n\n\tfor key, value := range map[string]int64{\"Account\": 4, \"Pets\": 10, \"Toys\": 10, \"Company\": 4, \"Manager\": 4, \"Team\": 10, \"Languages\": 0, \"Friends\": 0} {\n\t\tif count := DB.Unscoped().Model(&users).Association(key).Count(); count != value {\n\t\t\tt.Errorf(\"user's %v expects: %v, got %v\", key, value, count)\n\t\t}\n\t}\n\n\tfor key, value := range map[string]int64{\"Account\": 0, \"Pets\": 0, \"Toys\": 0, \"Company\": 4, \"Manager\": 4, \"Team\": 0, \"Languages\": 0, \"Friends\": 0} {\n\t\tif count := DB.Model(&users).Association(key).Count(); count != value {\n\t\t\tt.Errorf(\"user's %v expects: %v, got %v\", key, value, count)\n\t\t}\n\t}\n}\n\n// only sqlite, postgres, gaussdb, sqlserver support returning\nfunc TestSoftDeleteReturning(t *testing.T) {\n\tif DB.Dialector.Name() != \"sqlite\" && DB.Dialector.Name() != \"postgres\" && DB.Dialector.Name() != \"gaussdb\" && DB.Dialector.Name() != \"sqlserver\" {\n\t\treturn\n\t}\n\n\tusers := []*User{\n\t\tGetUser(\"delete-returning-1\", Config{}),\n\t\tGetUser(\"delete-returning-2\", Config{}),\n\t\tGetUser(\"delete-returning-3\", Config{}),\n\t}\n\tDB.Create(&users)\n\n\tvar results []User\n\tDB.Where(\"name IN ?\", []string{users[0].Name, users[1].Name}).Clauses(clause.Returning{}).Delete(&results)\n\tif len(results) != 2 {\n\t\tt.Errorf(\"failed to return delete data, got %v\", results)\n\t}\n\n\tvar count int64\n\tDB.Model(&User{}).Where(\"name IN ?\", []string{users[0].Name, users[1].Name, users[2].Name}).Count(&count)\n\tif count != 1 {\n\t\tt.Errorf(\"failed to delete data, current count %v\", count)\n\t}\n}\n\nfunc TestDeleteReturning(t *testing.T) {\n\tif DB.Dialector.Name() != \"sqlite\" && DB.Dialector.Name() != \"postgres\" && DB.Dialector.Name() != \"gaussdb\" && DB.Dialector.Name() != \"sqlserver\" {\n\t\treturn\n\t}\n\n\tcompanies := []Company{\n\t\t{Name: \"delete-returning-1\"},\n\t\t{Name: \"delete-returning-2\"},\n\t\t{Name: \"delete-returning-3\"},\n\t}\n\tDB.Create(&companies)\n\n\tvar results []Company\n\tDB.Where(\"name IN ?\", []string{companies[0].Name, companies[1].Name}).Clauses(clause.Returning{}).Delete(&results)\n\tif len(results) != 2 {\n\t\tt.Errorf(\"failed to return delete data, got %v\", results)\n\t}\n\n\tvar count int64\n\tDB.Model(&Company{}).Where(\"name IN ?\", []string{companies[0].Name, companies[1].Name, companies[2].Name}).Count(&count)\n\tif count != 1 {\n\t\tt.Errorf(\"failed to delete data, current count %v\", count)\n\t}\n}\n"
  },
  {
    "path": "tests/distinct_test.go",
    "content": "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 *testing.T) {\n\tusers := []User{\n\t\t*GetUser(\"distinct\", Config{}),\n\t\t*GetUser(\"distinct\", Config{}),\n\t\t*GetUser(\"distinct\", Config{}),\n\t\t*GetUser(\"distinct-2\", Config{}),\n\t\t*GetUser(\"distinct-3\", Config{}),\n\t}\n\tusers[0].Age = 20\n\n\tif err := DB.Create(&users).Error; err != nil {\n\t\tt.Fatalf(\"errors happened when create users: %v\", err)\n\t}\n\n\tvar names []string\n\tDB.Table(\"users\").Where(\"name like ?\", \"distinct%\").Order(\"name\").Pluck(\"name\", &names)\n\tAssertEqual(t, names, []string{\"distinct\", \"distinct\", \"distinct\", \"distinct-2\", \"distinct-3\"})\n\n\tvar names1 []string\n\tDB.Model(&User{}).Where(\"name like ?\", \"distinct%\").Distinct().Order(\"name\").Pluck(\"Name\", &names1)\n\n\tAssertEqual(t, names1, []string{\"distinct\", \"distinct-2\", \"distinct-3\"})\n\n\tvar names2 []string\n\tDB.Scopes(func(db *gorm.DB) *gorm.DB {\n\t\treturn db.Table(\"users\")\n\t}).Where(\"name like ?\", \"distinct%\").Order(\"name\").Pluck(\"name\", &names2)\n\tAssertEqual(t, names2, []string{\"distinct\", \"distinct\", \"distinct\", \"distinct-2\", \"distinct-3\"})\n\n\tvar results []User\n\tif err := DB.Distinct(\"name\", \"age\").Where(\"name like ?\", \"distinct%\").Order(\"name, age desc\").Find(&results).Error; err != nil {\n\t\tt.Errorf(\"failed to query users, got error: %v\", err)\n\t}\n\n\texpects := []User{\n\t\t{Name: \"distinct\", Age: 20},\n\t\t{Name: \"distinct\", Age: 18},\n\t\t{Name: \"distinct-2\", Age: 18},\n\t\t{Name: \"distinct-3\", Age: 18},\n\t}\n\n\tif len(results) != 4 {\n\t\tt.Fatalf(\"invalid results length found, expects: %v, got %v\", len(expects), len(results))\n\t}\n\n\tfor idx, expect := range expects {\n\t\tAssertObjEqual(t, results[idx], expect, \"Name\", \"Age\")\n\t}\n\n\tvar count int64\n\tif err := DB.Model(&User{}).Where(\"name like ?\", \"distinct%\").Count(&count).Error; err != nil || count != 5 {\n\t\tt.Errorf(\"failed to query users count, got error: %v, count: %v\", err, count)\n\t}\n\n\tif err := DB.Model(&User{}).Distinct(\"name\").Where(\"name like ?\", \"distinct%\").Count(&count).Error; err != nil || count != 3 {\n\t\tt.Errorf(\"failed to query users count, got error: %v, count %v\", err, count)\n\t}\n\n\tdryDB := DB.Session(&gorm.Session{DryRun: true})\n\tr := dryDB.Distinct(\"u.id, u.*\").Table(\"user_speaks as s\").Joins(\"inner join users as u on u.id = s.user_id\").Where(\"s.language_code ='US' or s.language_code ='ES'\").Find(&User{})\n\tif !regexp.MustCompile(`SELECT DISTINCT u\\.id, u\\.\\* FROM user_speaks as s inner join users as u`).MatchString(r.Statement.SQL.String()) {\n\t\tt.Fatalf(\"Build Distinct with u.*, but got %v\", r.Statement.SQL.String())\n\t}\n}\n"
  },
  {
    "path": "tests/embedded_struct_test.go",
    "content": "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/gorm\"\n\t. \"gorm.io/gorm/utils/tests\"\n)\n\nfunc TestEmbeddedStruct(t *testing.T) {\n\ttype ReadOnly struct {\n\t\tReadOnly *bool\n\t}\n\n\ttype BasePost struct {\n\t\tId    int64\n\t\tTitle string\n\t\tURL   string\n\t\tReadOnly\n\t}\n\n\ttype Author struct {\n\t\tID    string\n\t\tName  string\n\t\tEmail string\n\t}\n\n\ttype HNPost struct {\n\t\tBasePost\n\t\tAuthor  `gorm:\"EmbeddedPrefix:user_\"` // Embedded struct\n\t\tUpvotes int32\n\t}\n\n\ttype EngadgetPost struct {\n\t\tBasePost BasePost `gorm:\"Embedded\"`\n\t\tAuthor   *Author  `gorm:\"Embedded;EmbeddedPrefix:author_\"` // Embedded struct\n\t\tImageUrl string\n\t}\n\n\tDB.Migrator().DropTable(&HNPost{}, &EngadgetPost{})\n\tif err := DB.Migrator().AutoMigrate(&HNPost{}, &EngadgetPost{}); err != nil {\n\t\tt.Fatalf(\"failed to auto migrate, got error: %v\", err)\n\t}\n\n\tfor _, name := range []string{\"author_id\", \"author_name\", \"author_email\"} {\n\t\tif !DB.Migrator().HasColumn(&EngadgetPost{}, name) {\n\t\t\tt.Errorf(\"should has prefixed column %v\", name)\n\t\t}\n\t}\n\n\tstmt := gorm.Statement{DB: DB}\n\tif err := stmt.Parse(&EngadgetPost{}); err != nil {\n\t\tt.Fatalf(\"failed to parse embedded struct\")\n\t} else if len(stmt.Schema.PrimaryFields) != 1 {\n\t\tt.Errorf(\"should have only one primary field with embedded struct, but got %v\", len(stmt.Schema.PrimaryFields))\n\t}\n\n\tfor _, name := range []string{\"user_id\", \"user_name\", \"user_email\"} {\n\t\tif !DB.Migrator().HasColumn(&HNPost{}, name) {\n\t\t\tt.Errorf(\"should has prefixed column %v\", name)\n\t\t}\n\t}\n\n\t// save embedded struct\n\tDB.Save(&HNPost{BasePost: BasePost{Title: \"news\"}})\n\tDB.Save(&HNPost{BasePost: BasePost{Title: \"hn_news\"}})\n\tvar news HNPost\n\tif err := DB.First(&news, \"title = ?\", \"hn_news\").Error; err != nil {\n\t\tt.Errorf(\"no error should happen when query with embedded struct, but got %v\", err)\n\t} else if news.Title != \"hn_news\" {\n\t\tt.Errorf(\"embedded struct's value should be scanned correctly\")\n\t}\n\n\tDB.Save(&EngadgetPost{BasePost: BasePost{Title: \"engadget_news\"}, Author: &Author{Name: \"Edward\"}})\n\tDB.Save(&EngadgetPost{BasePost: BasePost{Title: \"engadget_article\"}, Author: &Author{Name: \"George\"}})\n\tvar egNews EngadgetPost\n\tif err := DB.First(&egNews, \"title = ?\", \"engadget_news\").Error; err != nil {\n\t\tt.Errorf(\"no error should happen when query with embedded struct, but got %v\", err)\n\t} else if egNews.BasePost.Title != \"engadget_news\" {\n\t\tt.Errorf(\"embedded struct's value should be scanned correctly\")\n\t}\n\n\tvar egPosts []EngadgetPost\n\tif err := DB.Order(\"author_name asc\").Find(&egPosts).Error; err != nil {\n\t\tt.Fatalf(\"no error should happen when query with embedded struct, but got %v\", err)\n\t}\n\texpectAuthors := []string{\"Edward\", \"George\"}\n\tfor i, post := range egPosts {\n\t\tt.Log(i, post.Author)\n\t\tif want := expectAuthors[i]; post.Author.Name != want {\n\t\t\tt.Errorf(\"expected author %s got %s\", want, post.Author.Name)\n\t\t}\n\t}\n}\n\nfunc TestEmbeddedPointerTypeStruct(t *testing.T) {\n\ttype BasePost struct {\n\t\tId    int64\n\t\tTitle string\n\t\tURL   string\n\t}\n\n\ttype Author struct {\n\t\tID          string\n\t\tName        string\n\t\tEmail       string\n\t\tAge         int\n\t\tContent     Content\n\t\tContentPtr  *Content\n\t\tBirthday    time.Time\n\t\tBirthdayPtr *time.Time\n\t}\n\n\ttype HNPost struct {\n\t\t*BasePost\n\t\tUpvotes int32\n\t\t*Author `gorm:\"EmbeddedPrefix:user_\"` // Embedded struct\n\t}\n\n\tDB.Migrator().DropTable(&HNPost{})\n\tif err := DB.Migrator().AutoMigrate(&HNPost{}); err != nil {\n\t\tt.Fatalf(\"failed to auto migrate, got error: %v\", err)\n\t}\n\n\tDB.Create(&HNPost{BasePost: &BasePost{Title: \"embedded_pointer_type\"}})\n\n\tvar hnPost HNPost\n\tif err := DB.First(&hnPost, \"title = ?\", \"embedded_pointer_type\").Error; err != nil {\n\t\tt.Errorf(\"No error should happen when find embedded pointer type, but got %v\", err)\n\t}\n\n\tif hnPost.Title != \"embedded_pointer_type\" {\n\t\tt.Errorf(\"Should find correct value for embedded pointer type\")\n\t}\n\n\tif hnPost.Author != nil {\n\t\tt.Errorf(\"Expected to get back a nil Author but got: %v\", hnPost.Author)\n\t}\n\n\tnow := time.Now().Round(time.Second)\n\tNewPost := HNPost{\n\t\tBasePost: &BasePost{Title: \"embedded_pointer_type2\"},\n\t\tAuthor: &Author{\n\t\t\tName:        \"test\",\n\t\t\tContent:     Content{\"test\"},\n\t\t\tContentPtr:  nil,\n\t\t\tBirthday:    now,\n\t\t\tBirthdayPtr: nil,\n\t\t},\n\t}\n\tDB.Create(&NewPost)\n\n\thnPost = HNPost{}\n\tif err := DB.First(&hnPost, \"title = ?\", NewPost.Title).Error; err != nil {\n\t\tt.Errorf(\"No error should happen when find embedded pointer type, but got %v\", err)\n\t}\n\n\tif hnPost.Title != NewPost.Title {\n\t\tt.Errorf(\"Should find correct value for embedded pointer type\")\n\t}\n\n\tif hnPost.Author.Name != NewPost.Author.Name {\n\t\tt.Errorf(\"Expected to get Author name %v but got: %v\", NewPost.Author.Name, hnPost.Author.Name)\n\t}\n\n\tif !reflect.DeepEqual(NewPost.Author.Content, hnPost.Author.Content) {\n\t\tt.Errorf(\"Expected to get Author content %v but got: %v\", NewPost.Author.Content, hnPost.Author.Content)\n\t}\n\n\tif hnPost.Author.ContentPtr != nil {\n\t\tt.Errorf(\"Expected to get nil Author contentPtr but got: %v\", hnPost.Author.ContentPtr)\n\t}\n\n\tif NewPost.Author.Birthday.UnixMilli() != hnPost.Author.Birthday.UnixMilli() {\n\t\tt.Errorf(\"Expected to get Author birthday with %+v but got: %+v\", NewPost.Author.Birthday, hnPost.Author.Birthday)\n\t}\n\n\tif hnPost.Author.BirthdayPtr != nil {\n\t\tt.Errorf(\"Expected to get nil Author birthdayPtr but got: %+v\", hnPost.Author.BirthdayPtr)\n\t}\n}\n\ntype Content struct {\n\tContent interface{} `gorm:\"type:String\"`\n}\n\nfunc (c Content) Value() (driver.Value, error) {\n\t// mssql driver with issue on handling null bytes https://github.com/denisenkom/go-mssqldb/issues/530,\n\tb, err := json.Marshal(c)\n\treturn string(b[:]), err\n}\n\nfunc (c *Content) Scan(src interface{}) error {\n\tvar value Content\n\tstr, ok := src.(string)\n\tif !ok {\n\t\tbyt, ok := src.([]byte)\n\t\tif !ok {\n\t\t\treturn errors.New(\"Embedded.Scan byte assertion failed\")\n\t\t}\n\t\tif err := json.Unmarshal(byt, &value); err != nil {\n\t\t\treturn err\n\t\t}\n\t} else {\n\t\tif err := json.Unmarshal([]byte(str), &value); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\t*c = value\n\n\treturn nil\n}\n\nfunc TestEmbeddedScanValuer(t *testing.T) {\n\ttype HNPost struct {\n\t\tgorm.Model\n\t\tContent\n\t}\n\n\tDB.Migrator().DropTable(&HNPost{})\n\tif err := DB.Migrator().AutoMigrate(&HNPost{}); err != nil {\n\t\tt.Fatalf(\"failed to auto migrate, got error: %v\", err)\n\t}\n\n\thnPost := HNPost{Content: Content{Content: \"hello world\"}}\n\n\tif err := DB.Create(&hnPost).Error; err != nil {\n\t\tt.Errorf(\"Failed to create got error %v\", err)\n\t}\n}\n\nfunc TestEmbeddedRelations(t *testing.T) {\n\ttype EmbUser struct {\n\t\tgorm.Model\n\t\tName      string\n\t\tAge       uint\n\t\tLanguages []Language `gorm:\"many2many:EmbUserSpeak;\"`\n\t}\n\n\ttype AdvancedUser struct {\n\t\tEmbUser  `gorm:\"embedded\"`\n\t\tAdvanced bool\n\t}\n\n\tDB.Migrator().DropTable(&AdvancedUser{})\n\n\tif err := DB.AutoMigrate(&AdvancedUser{}); err != nil {\n\t\tif DB.Dialector.Name() != \"sqlite\" {\n\t\t\tt.Errorf(\"Failed to auto migrate advanced user, got error %v\", err)\n\t\t}\n\t}\n}\n\nfunc TestEmbeddedTagSetting(t *testing.T) {\n\ttype Tag1 struct {\n\t\tId int64 `gorm:\"autoIncrement\"`\n\t}\n\ttype Tag2 struct {\n\t\tId int64\n\t}\n\n\ttype EmbeddedTag struct {\n\t\tTag1 Tag1 `gorm:\"Embedded;\"`\n\t\tTag2 Tag2 `gorm:\"Embedded;EmbeddedPrefix:t2_\"`\n\t\tName string\n\t}\n\n\tDB.Migrator().DropTable(&EmbeddedTag{})\n\terr := DB.Migrator().AutoMigrate(&EmbeddedTag{})\n\tAssertEqual(t, err, nil)\n\n\tt1 := EmbeddedTag{Name: \"embedded_tag\"}\n\terr = DB.Save(&t1).Error\n\tAssertEqual(t, err, nil)\n\tif t1.Tag1.Id == 0 {\n\t\tt.Errorf(\"embedded struct's primary field should be rewritten\")\n\t}\n}\n"
  },
  {
    "path": "tests/error_translator_test.go",
    "content": "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 TestDialectorWithErrorTranslatorSupport(t *testing.T) {\n\t// it shouldn't translate error when the TranslateError flag is false\n\ttranslatedErr := errors.New(\"translated error\")\n\tuntranslatedErr := errors.New(\"some random error\")\n\tdb, _ := gorm.Open(tests.DummyDialector{TranslatedErr: translatedErr})\n\n\terr := db.AddError(untranslatedErr)\n\tif !errors.Is(err, untranslatedErr) {\n\t\tt.Fatalf(\"expected err: %v got err: %v\", untranslatedErr, err)\n\t}\n\n\t// it should translate error when the TranslateError flag is true\n\tdb, _ = gorm.Open(tests.DummyDialector{TranslatedErr: translatedErr}, &gorm.Config{TranslateError: true})\n\n\terr = db.AddError(untranslatedErr)\n\tif !errors.Is(err, translatedErr) {\n\t\tt.Fatalf(\"expected err: %v got err: %v\", translatedErr, err)\n\t}\n}\n\nfunc TestSupportedDialectorWithErrDuplicatedKey(t *testing.T) {\n\ttype City struct {\n\t\tgorm.Model\n\t\tName string `gorm:\"unique\"`\n\t}\n\n\tdb, err := OpenTestConnection(&gorm.Config{TranslateError: true})\n\tif err != nil {\n\t\tt.Fatalf(\"failed to connect database, got error %v\", err)\n\t}\n\n\tdialectors := map[string]bool{\"sqlite\": true, \"postgres\": true, \"gaussdb\": true, \"mysql\": true, \"sqlserver\": true}\n\tif supported, found := dialectors[db.Dialector.Name()]; !(found && supported) {\n\t\treturn\n\t}\n\n\tDB.Migrator().DropTable(&City{})\n\n\tif err = db.AutoMigrate(&City{}); err != nil {\n\t\tt.Fatalf(\"failed to migrate cities table, got error: %v\", err)\n\t}\n\n\terr = db.Create(&City{Name: \"Kabul\"}).Error\n\tif err != nil {\n\t\tt.Fatalf(\"failed to create record: %v\", err)\n\t}\n\n\terr = db.Create(&City{Name: \"Kabul\"}).Error\n\tif !errors.Is(err, gorm.ErrDuplicatedKey) {\n\t\tt.Fatalf(\"expected err: %v got err: %v\", gorm.ErrDuplicatedKey, err)\n\t}\n}\n\nfunc TestSupportedDialectorWithErrForeignKeyViolated(t *testing.T) {\n\ttidbSkip(t, \"not support the foreign key feature\")\n\n\ttype City struct {\n\t\tgorm.Model\n\t\tName string `gorm:\"unique\"`\n\t}\n\n\ttype Museum struct {\n\t\tgorm.Model\n\t\tName   string `gorm:\"unique\"`\n\t\tCityID uint\n\t\tCity   City `gorm:\"Constraint:OnUpdate:CASCADE,OnDelete:CASCADE;FOREIGNKEY:CityID;References:ID\"`\n\t}\n\n\tdb, err := OpenTestConnection(&gorm.Config{TranslateError: true})\n\tif err != nil {\n\t\tt.Fatalf(\"failed to connect database, got error %v\", err)\n\t}\n\n\tdialectors := map[string]bool{\"sqlite\": true, \"postgres\": true, \"gaussdb\": true, \"mysql\": true, \"sqlserver\": true}\n\tif supported, found := dialectors[db.Dialector.Name()]; !(found && supported) {\n\t\treturn\n\t}\n\n\tDB.Migrator().DropTable(&City{}, &Museum{})\n\n\tif err = db.AutoMigrate(&City{}, &Museum{}); err != nil {\n\t\tt.Fatalf(\"failed to migrate countries & cities tables, got error: %v\", err)\n\t}\n\n\tcity := City{Name: \"Amsterdam\"}\n\n\terr = db.Create(&city).Error\n\tif err != nil {\n\t\tt.Fatalf(\"failed to create city: %v\", err)\n\t}\n\n\terr = db.Create(&Museum{Name: \"Eye Filmmuseum\", CityID: city.ID}).Error\n\tif err != nil {\n\t\tt.Fatalf(\"failed to create museum: %v\", err)\n\t}\n\n\terr = db.Create(&Museum{Name: \"Dungeon\", CityID: 123}).Error\n\tif !errors.Is(err, gorm.ErrForeignKeyViolated) {\n\t\tt.Fatalf(\"expected err: %v got err: %v\", gorm.ErrForeignKeyViolated, err)\n\t}\n}\n"
  },
  {
    "path": "tests/gaussdb_test.go",
    "content": "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.io/gorm/clause\"\n\t. \"gorm.io/gorm/utils/tests\"\n)\n\nfunc TestGaussDBReturningIDWhichHasStringType(t *testing.T) {\n\tt.Skipf(\"This test case skipped, because of gaussdb not support pgcrypto extension and gen_random_uuid() function\")\n\tif DB.Dialector.Name() != \"gaussdb\" {\n\t\tt.Skip()\n\t}\n\n\ttype Yasuo struct {\n\t\t// TODO: function gen_random_uuid() does not exist\n\t\tID        string `gorm:\"default:gen_random_uuid()\"`\n\t\tName      string\n\t\tCreatedAt time.Time `gorm:\"type:TIMESTAMP WITHOUT TIME ZONE\"`\n\t\tUpdatedAt time.Time `gorm:\"type:TIMESTAMP WITHOUT TIME ZONE;default:current_timestamp\"`\n\t}\n\n\tif err := DB.Exec(\"CREATE EXTENSION IF NOT EXISTS pgcrypto;\").Error; err != nil {\n\t\tt.Errorf(\"Failed to create extension pgcrypto, got error %v\", err)\n\t}\n\n\tDB.Migrator().DropTable(&Yasuo{})\n\n\tif err := DB.AutoMigrate(&Yasuo{}); err != nil {\n\t\tt.Fatalf(\"Failed to migrate for uuid default value, got error: %v\", err)\n\t}\n\n\tyasuo := Yasuo{Name: \"jinzhu\"}\n\tif err := DB.Create(&yasuo).Error; err != nil {\n\t\tt.Fatalf(\"should be able to create data, but got %v\", err)\n\t}\n\n\tif yasuo.ID == \"\" {\n\t\tt.Fatal(\"should be able to has ID, but got zero value\")\n\t}\n\n\tvar result Yasuo\n\tif err := DB.First(&result, \"id = ?\", yasuo.ID).Error; err != nil || yasuo.Name != \"jinzhu\" {\n\t\tt.Errorf(\"No error should happen, but got %v\", err)\n\t}\n\n\tif err := DB.Where(\"id = $1\", yasuo.ID).First(&Yasuo{}).Error; err != nil || yasuo.Name != \"jinzhu\" {\n\t\tt.Errorf(\"No error should happen, but got %v\", err)\n\t}\n\n\tyasuo.Name = \"jinzhu1\"\n\tif err := DB.Save(&yasuo).Error; err != nil {\n\t\tt.Errorf(\"Failed to update date, got error %v\", err)\n\t}\n\n\tif err := DB.First(&result, \"id = ?\", yasuo.ID).Error; err != nil || yasuo.Name != \"jinzhu1\" {\n\t\tt.Errorf(\"No error should happen, but got %v\", err)\n\t}\n}\n\nfunc TestGaussDB(t *testing.T) {\n\tt.Skipf(\"This test case skipped, because of gaussdb not support pgcrypto extension and gen_random_uuid() function\")\n\tif DB.Dialector.Name() != \"gaussdb\" {\n\t\tt.Skip()\n\t}\n\n\ttype Harumph struct {\n\t\tgorm.Model\n\t\tName string `gorm:\"check:name_checker,name <> ''\"`\n\t\t// TODO: function gen_random_uuid() does not exist\n\t\tTest      uuid.UUID      `gorm:\"type:uuid;not null;default:gen_random_uuid()\"`\n\t\tCreatedAt time.Time      `gorm:\"type:TIMESTAMP WITHOUT TIME ZONE\"`\n\t\tUpdatedAt time.Time      `gorm:\"type:TIMESTAMP WITHOUT TIME ZONE;default:current_timestamp\"`\n\t\tThings    pq.StringArray `gorm:\"type:text[]\"`\n\t}\n\n\tif err := DB.Exec(\"CREATE EXTENSION IF NOT EXISTS pgcrypto;\").Error; err != nil {\n\t\tt.Errorf(\"Failed to create extension pgcrypto, got error %v\", err)\n\t}\n\n\tDB.Migrator().DropTable(&Harumph{})\n\n\tif err := DB.AutoMigrate(&Harumph{}); err != nil {\n\t\tt.Fatalf(\"Failed to migrate for uuid default value, got error: %v\", err)\n\t}\n\n\tharumph := Harumph{}\n\tif err := DB.Create(&harumph).Error; err == nil {\n\t\tt.Fatalf(\"should failed to create data, name can't be blank\")\n\t}\n\n\tharumph = Harumph{Name: \"jinzhu\"}\n\tif err := DB.Create(&harumph).Error; err != nil {\n\t\tt.Fatalf(\"should be able to create data, but got %v\", err)\n\t}\n\n\tvar result Harumph\n\tif err := DB.First(&result, \"id = ?\", harumph.ID).Error; err != nil || harumph.Name != \"jinzhu\" {\n\t\tt.Errorf(\"No error should happen, but got %v\", err)\n\t}\n\n\tif err := DB.Where(\"id = $1\", harumph.ID).First(&Harumph{}).Error; err != nil || harumph.Name != \"jinzhu\" {\n\t\tt.Errorf(\"No error should happen, but got %v\", err)\n\t}\n\n\tharumph.Name = \"jinzhu1\"\n\tif err := DB.Save(&harumph).Error; err != nil {\n\t\tt.Errorf(\"Failed to update date, got error %v\", err)\n\t}\n\n\tif err := DB.First(&result, \"id = ?\", harumph.ID).Error; err != nil || harumph.Name != \"jinzhu1\" {\n\t\tt.Errorf(\"No error should happen, but got %v\", err)\n\t}\n\n\tDB.Migrator().DropTable(\"log_usage\")\n\n\tif err := DB.Exec(`\nCREATE TABLE public.log_usage (\n    log_id bigint NOT NULL\n);\n\nALTER TABLE public.log_usage ALTER COLUMN log_id ADD GENERATED BY DEFAULT AS IDENTITY (\n    SEQUENCE NAME public.log_usage_log_id_seq\n    START WITH 1\n    INCREMENT BY 1\n    NO MINVALUE\n    NO MAXVALUE\n    CACHE 1\n);\n\t`).Error; err != nil {\n\t\tt.Fatalf(\"failed to create table, got error %v\", err)\n\t}\n\n\tcolumns, err := DB.Migrator().ColumnTypes(\"log_usage\")\n\tif err != nil {\n\t\tt.Fatalf(\"failed to get columns, got error %v\", err)\n\t}\n\n\thasLogID := false\n\tfor _, column := range columns {\n\t\tif column.Name() == \"log_id\" {\n\t\t\thasLogID = true\n\t\t\tautoIncrement, ok := column.AutoIncrement()\n\t\t\tif !ok || !autoIncrement {\n\t\t\t\tt.Fatalf(\"column log_id should be auto incrementment\")\n\t\t\t}\n\t\t}\n\t}\n\n\tif !hasLogID {\n\t\tt.Fatalf(\"failed to found column log_id\")\n\t}\n}\n\nfunc TestGaussDBMany2ManyWithDefaultValueUUID(t *testing.T) {\n\tt.Skipf(\"This test case skipped, because of gaussdb does not have 'uuid-ossp' extension\")\n\tif DB.Dialector.Name() != \"gaussdb\" {\n\t\tt.Skip()\n\t}\n\n\tif err := DB.Exec(`create extension if not exists \"uuid-ossp\"`).Error; err != nil {\n\t\tt.Fatalf(\"Failed to create 'uuid-ossp' extension, but got error %v\", err)\n\t}\n\n\tDB.Migrator().DropTable(&Post{}, &Category{}, \"post_categories\")\n\tDB.AutoMigrate(&Post{}, &Category{})\n\n\tpost := Post{\n\t\tTitle: \"Hello World\",\n\t\tCategories: []*Category{\n\t\t\t{Title: \"Coding\"},\n\t\t\t{Title: \"Golang\"},\n\t\t},\n\t}\n\n\tif err := DB.Create(&post).Error; err != nil {\n\t\tt.Errorf(\"Failed, got error: %v\", err)\n\t}\n}\n\nfunc TestGaussDBOnConstraint(t *testing.T) {\n\tt.Skipf(\"This test case skipped, because of gaussdb not support 'ON CONSTRAINT' statement\")\n\tif DB.Dialector.Name() != \"gaussdb\" {\n\t\tt.Skip()\n\t}\n\n\ttype Thing struct {\n\t\tgorm.Model\n\t\tSomeID  string\n\t\tOtherID string\n\t\tData    string\n\t}\n\n\tDB.Migrator().DropTable(&Thing{})\n\tDB.Migrator().CreateTable(&Thing{})\n\tif err := DB.Exec(\"ALTER TABLE things ADD CONSTRAINT some_id_other_id_unique UNIQUE (some_id, other_id)\").Error; err != nil {\n\t\tt.Error(err)\n\t}\n\n\tthing := Thing{\n\t\tSomeID:  \"1234\",\n\t\tOtherID: \"1234\",\n\t\tData:    \"something\",\n\t}\n\n\tDB.Create(&thing)\n\n\tthing2 := Thing{\n\t\tSomeID:  \"1234\",\n\t\tOtherID: \"1234\",\n\t\tData:    \"something else\",\n\t}\n\n\tresult := DB.Clauses(clause.OnConflict{\n\t\tOnConstraint: \"some_id_other_id_unique\",\n\t\tUpdateAll:    true,\n\t}).Create(&thing2)\n\tif result.Error != nil {\n\t\tt.Errorf(\"creating second thing: %v\", result.Error)\n\t}\n\n\tvar things []Thing\n\tif err := DB.Find(&things).Error; err != nil {\n\t\tt.Errorf(\"Failed, got error: %v\", err)\n\t}\n\n\tif len(things) > 1 {\n\t\tt.Errorf(\"expected 1 thing got more\")\n\t}\n}\n\nfunc TestGaussDBAlterColumnDataType(t *testing.T) {\n\tif DB.Dialector.Name() != \"gaussdb\" {\n\t\tt.Skip()\n\t}\n\tDB.Migrator().DropTable(&Company{})\n\tDB.AutoMigrate(Company{})\n\tif err := DB.Table(\"companies\").Migrator().AlterColumn(CompanyNew{}, \"name\"); err != nil {\n\t\tt.Fatalf(\"failed to alter column from string to int, got error %v\", err)\n\t}\n\n\tDB.AutoMigrate(Company{})\n}\n"
  },
  {
    "path": "tests/generics_test.go",
    "content": "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\"testing\"\n\n\t\"github.com/google/uuid\"\n\t\"gorm.io/driver/mysql\"\n\t\"gorm.io/gorm\"\n\t\"gorm.io/gorm/clause\"\n\t. \"gorm.io/gorm/utils/tests\"\n)\n\nfunc TestGenericsCreate(t *testing.T) {\n\tctx := context.Background()\n\n\tuser := User{Name: \"TestGenericsCreate\", Age: 18}\n\terr := gorm.G[User](DB).Create(ctx, &user)\n\tif err != nil {\n\t\tt.Fatalf(\"Create failed: %v\", err)\n\t}\n\tif user.ID == 0 {\n\t\tt.Fatalf(\"no primary key found for %v\", user)\n\t}\n\n\tif u, err := gorm.G[User](DB).Where(\"name = ?\", user.Name).First(ctx); err != nil {\n\t\tt.Fatalf(\"failed to find user, got error: %v\", err)\n\t} else if u.Name != user.Name || u.ID != user.ID {\n\t\tt.Errorf(\"found invalid user, got %v, expect %v\", u, user)\n\t}\n\n\tif u, err := gorm.G[User](DB).Where(\"name = ?\", user.Name).Take(ctx); err != nil {\n\t\tt.Fatalf(\"failed to find user, got error: %v\", err)\n\t} else if u.Name != user.Name || u.ID != user.ID {\n\t\tt.Errorf(\"found invalid user, got %v, expect %v\", u, user)\n\t}\n\n\tif u, err := gorm.G[User](DB).Select(\"name\").Where(\"name = ?\", user.Name).First(ctx); err != nil {\n\t\tt.Fatalf(\"failed to find user, got error: %v\", err)\n\t} else if u.Name != user.Name || u.Age != 0 {\n\t\tt.Errorf(\"found invalid user, got %v, expect %v\", u, user)\n\t}\n\n\tif u, err := gorm.G[User](DB).Omit(\"name\").Where(\"name = ?\", user.Name).First(ctx); err != nil {\n\t\tt.Fatalf(\"failed to find user, got error: %v\", err)\n\t} else if u.Name != \"\" || u.Age != user.Age {\n\t\tt.Errorf(\"found invalid user, got %v, expect %v\", u, user)\n\t}\n\n\tresult := struct {\n\t\tID   int\n\t\tName string\n\t}{}\n\tif err := gorm.G[User](DB).Where(\"name = ?\", user.Name).Scan(ctx, &result); err != nil {\n\t\tt.Fatalf(\"failed to scan user, got error: %v\", err)\n\t} else if result.Name != user.Name || uint(result.ID) != user.ID {\n\t\tt.Errorf(\"found invalid user, got %v, expect %v\", result, user)\n\t}\n\n\tmapResult, err := gorm.G[map[string]interface{}](DB).Table(\"users\").Where(\"name = ?\", user.Name).MapColumns(map[string]string{\"name\": \"user_name\"}).Take(ctx)\n\tif v := mapResult[\"user_name\"]; fmt.Sprint(v) != user.Name {\n\t\tt.Errorf(\"failed to find map results, got %v, err %v\", mapResult, err)\n\t}\n\n\tselectOnly := User{Name: \"GenericsCreateSelectOnly\", Age: 99}\n\tif err := gorm.G[User](DB).Select(\"name\").Create(ctx, &selectOnly); err != nil {\n\t\tt.Fatalf(\"failed to create with Select, got error: %v\", err)\n\t}\n\n\tif selectOnly.ID == 0 {\n\t\tt.Fatalf(\"no primary key found for select-only user: %v\", selectOnly)\n\t}\n\n\tif stored, err := gorm.G[User](DB).Where(\"id = ?\", selectOnly.ID).First(ctx); err != nil {\n\t\tt.Fatalf(\"failed to reload select-only user, got error: %v\", err)\n\t} else if stored.Name != selectOnly.Name || stored.Age != 0 {\n\t\tt.Errorf(\"unexpected select-only user state, got %#v\", stored)\n\t}\n\n\tomitAge := User{Name: \"GenericsCreateOmitAge\", Age: 88}\n\tif err := gorm.G[User](DB).Omit(\"age\").Create(ctx, &omitAge); err != nil {\n\t\tt.Fatalf(\"failed to create with Omit, got error: %v\", err)\n\t}\n\n\tif omitAge.ID == 0 {\n\t\tt.Fatalf(\"no primary key found for omit-age user: %v\", omitAge)\n\t}\n\n\tif stored, err := gorm.G[User](DB).Where(\"id = ?\", omitAge.ID).First(ctx); err != nil {\n\t\tt.Fatalf(\"failed to reload omit-age user, got error: %v\", err)\n\t} else if stored.Name != omitAge.Name || stored.Age != 0 {\n\t\tt.Errorf(\"unexpected omit-age user state, got %#v\", stored)\n\t}\n}\n\nfunc TestGenericsCreateInBatches(t *testing.T) {\n\tbatch := []User{\n\t\t{Name: \"GenericsCreateInBatches1\"},\n\t\t{Name: \"GenericsCreateInBatches2\"},\n\t\t{Name: \"GenericsCreateInBatches3\"},\n\t}\n\tctx := context.Background()\n\n\tif err := gorm.G[User](DB).CreateInBatches(ctx, &batch, 2); err != nil {\n\t\tt.Fatalf(\"CreateInBatches failed: %v\", err)\n\t}\n\n\tfor _, u := range batch {\n\t\tif u.ID == 0 {\n\t\t\tt.Fatalf(\"no primary key found for %v\", u)\n\t\t}\n\t}\n\n\tcount, err := gorm.G[User](DB).Where(\"name like ?\", \"GenericsCreateInBatches%\").Count(ctx, \"*\")\n\tif err != nil {\n\t\tt.Fatalf(\"Count failed: %v\", err)\n\t}\n\tif count != 3 {\n\t\tt.Errorf(\"expected 3 records, got %d\", count)\n\t}\n\n\tfound, err := gorm.G[User](DB).Raw(\"SELECT * FROM users WHERE name LIKE ?\", \"GenericsCreateInBatches%\").Find(ctx)\n\tif len(found) != len(batch) {\n\t\tt.Errorf(\"expected %d from Raw Find, got %d\", len(batch), len(found))\n\t}\n\n\tfound, err = gorm.G[User](DB).Where(\"name like ?\", \"GenericsCreateInBatches%\").Limit(2).Find(ctx)\n\tif len(found) != 2 {\n\t\tt.Errorf(\"expected %d from Raw Find, got %d\", 2, len(found))\n\t}\n\n\tfound, err = gorm.G[User](DB).Where(\"name like ?\", \"GenericsCreateInBatches%\").Offset(2).Limit(2).Find(ctx)\n\tif len(found) != 1 {\n\t\tt.Errorf(\"expected %d from Raw Find, got %d\", 1, len(found))\n\t}\n}\n\nfunc TestGenericsExecAndUpdate(t *testing.T) {\n\tctx := context.Background()\n\n\tname := \"GenericsExec\"\n\tif err := gorm.G[User](DB).Exec(ctx, \"INSERT INTO users(name) VALUES(?)\", name); err != nil {\n\t\tt.Fatalf(\"Exec insert failed: %v\", err)\n\t}\n\n\tname2 := \"GenericsExec2\"\n\tif err := gorm.G[User](DB).Exec(ctx, \"INSERT INTO ?(name) VALUES(?)\", clause.Table{Name: clause.CurrentTable}, name2); err != nil {\n\t\tt.Fatalf(\"Exec insert failed: %v\", err)\n\t}\n\n\tu, err := gorm.G[User](DB).Table(\"users as u\").Where(\"u.name = ?\", name).First(ctx)\n\tif err != nil {\n\t\tt.Fatalf(\"failed to find user, got error: %v\", err)\n\t} else if u.Name != name || u.ID == 0 {\n\t\tt.Errorf(\"found invalid user, got %v\", u)\n\t}\n\n\tname += \"Update\"\n\trows, err := gorm.G[User](DB).Where(\"id = ?\", u.ID).Update(ctx, \"name\", name)\n\tif rows != 1 {\n\t\tt.Fatalf(\"failed to get affected rows, got %d, should be %d\", rows, 1)\n\t}\n\n\tnu, err := gorm.G[User](DB).Where(\"name = ?\", name).First(ctx)\n\tif err != nil {\n\t\tt.Fatalf(\"failed to find user, got error: %v\", err)\n\t} else if nu.Name != name || u.ID != nu.ID {\n\t\tt.Fatalf(\"found invalid user, got %v, expect %v\", nu.ID, u.ID)\n\t}\n\n\trows, err = gorm.G[User](DB).Where(\"id = ?\", u.ID).Updates(ctx, User{Name: \"GenericsExecUpdates\", Age: 18})\n\tif rows != 1 {\n\t\tt.Fatalf(\"failed to get affected rows, got %d, should be %d\", rows, 1)\n\t}\n\n\tnu, err = gorm.G[User](DB).Where(\"id = ?\", u.ID).Last(ctx)\n\tif err != nil {\n\t\tt.Fatalf(\"failed to find user, got error: %v\", err)\n\t} else if nu.Name != \"GenericsExecUpdates\" || nu.Age != 18 || u.ID != nu.ID {\n\t\tt.Fatalf(\"found invalid user, got %v, expect %v\", nu.ID, u.ID)\n\t}\n}\n\nfunc TestGenericsRow(t *testing.T) {\n\tctx := context.Background()\n\n\tuser := User{Name: \"GenericsRow\"}\n\tif err := gorm.G[User](DB).Create(ctx, &user); err != nil {\n\t\tt.Fatalf(\"Create failed: %v\", err)\n\t}\n\n\trawSQLUserRow := gorm.G[User](DB).Raw(\"SELECT name FROM ? WHERE id = ?\", clause.Table{Name: clause.CurrentTable}, user.ID).Row(ctx)\n\tvar name string\n\tif err := rawSQLUserRow.Scan(&name); err != nil {\n\t\tt.Fatalf(\"rawSQLUserRow scan failed: %v\", err)\n\t}\n\tif name != user.Name {\n\t\tt.Errorf(\"expected %s, got %s\", user.Name, name)\n\t}\n\n\tvar scannedUserName string\n\tselectUserRow := gorm.G[User](DB).Select(\"name\").Where(\"name = ?\", user.Name).Row(ctx)\n\tif err := selectUserRow.Scan(&scannedUserName); err != nil {\n\t\tt.Fatalf(\"selectUserRow scan failed: %v\", err)\n\t}\n\tif name != user.Name {\n\t\tt.Errorf(\"expected %s, got %s\", user.Name, scannedUserName)\n\t}\n\n\tuser2 := User{Name: \"GenericsRow2\"}\n\tif err := gorm.G[User](DB).Create(ctx, &user2); err != nil {\n\t\tt.Fatalf(\"Create failed: %v\", err)\n\t}\n\trawSQLUserRows, err := gorm.G[User](DB).Raw(\"SELECT name FROM users WHERE id IN ?\", []uint{user.ID, user2.ID}).Rows(ctx)\n\tif err != nil {\n\t\tt.Fatalf(\"rawSQLUserRows failed: %v\", err)\n\t}\n\n\tcount := 0\n\tfor rawSQLUserRows.Next() {\n\t\tvar name string\n\t\tif err := rawSQLUserRows.Scan(&name); err != nil {\n\t\t\tt.Fatalf(\"rawSQLUserRows.Scan failed: %v\", err)\n\t\t}\n\t\tcount++\n\t}\n\tif count != 2 {\n\t\tt.Errorf(\"expected 2 rows, got %d\", count)\n\t}\n\n\tselectNameUserRows, err := gorm.G[User](DB).Select(\"name\").Where(\"id IN ?\", []uint{user.ID, user2.ID}).Rows(ctx)\n\tif err != nil {\n\t\tt.Fatalf(\"selectNameUserRows failed: %v\", err)\n\t}\n\tcount = 0\n\tfor selectNameUserRows.Next() {\n\t\tvar name string\n\t\tif err := selectNameUserRows.Scan(&name); err != nil {\n\t\t\tt.Fatalf(\"selectNameUserRows.Scan failed: %v\", err)\n\t\t}\n\t\tcount++\n\t}\n\tif count != 2 {\n\t\tt.Errorf(\"expected 2 rows, got %d\", count)\n\t}\n\n\tfullUserRows, err := gorm.G[User](DB).Where(\"id IN ?\", []uint{user.ID, user2.ID}).Rows(ctx)\n\tif err != nil {\n\t\tt.Fatalf(\"Rows failed: %v\", err)\n\t}\n\tcount = 0\n\tfor fullUserRows.Next() {\n\t\tvar scannedUser User\n\t\tif err := DB.ScanRows(fullUserRows, &scannedUser); err != nil {\n\t\t\tt.Fatalf(\"DB.ScanRows failed: %v\", err)\n\t\t}\n\t\tcount++\n\t}\n\tif count != 2 {\n\t\tt.Errorf(\"expected 2 rows, got %d\", count)\n\t}\n}\n\nfunc TestGenericsDelete(t *testing.T) {\n\tctx := context.Background()\n\n\tu := User{Name: \"GenericsDelete\"}\n\tif err := gorm.G[User](DB).Create(ctx, &u); err != nil {\n\t\tt.Fatalf(\"Create failed: %v\", err)\n\t}\n\n\trows, err := gorm.G[User](DB).Where(\"id = ?\", u.ID).Delete(ctx)\n\tif err != nil {\n\t\tt.Fatalf(\"Delete failed: %v\", err)\n\t}\n\tif rows != 1 {\n\t\tt.Errorf(\"expected 1 row deleted, got %d\", rows)\n\t}\n\n\t_, err = gorm.G[User](DB).Where(\"id = ?\", u.ID).First(ctx)\n\tif err != gorm.ErrRecordNotFound {\n\t\tt.Fatalf(\"User after delete failed: %v\", err)\n\t}\n}\n\nfunc TestGenericsFindInBatches(t *testing.T) {\n\tctx := context.Background()\n\n\tusers := []User{\n\t\t{Name: \"GenericsFindBatchA\"},\n\t\t{Name: \"GenericsFindBatchB\"},\n\t\t{Name: \"GenericsFindBatchC\"},\n\t\t{Name: \"GenericsFindBatchD\"},\n\t\t{Name: \"GenericsFindBatchE\"},\n\t}\n\tif err := gorm.G[User](DB).CreateInBatches(ctx, &users, len(users)); err != nil {\n\t\tt.Fatalf(\"CreateInBatches failed: %v\", err)\n\t}\n\n\ttotal := 0\n\terr := gorm.G[User](DB).Where(\"name like ?\", \"GenericsFindBatch%\").FindInBatches(ctx, 2, func(chunk []User, batch int) error {\n\t\tif len(chunk) > 2 {\n\t\t\tt.Errorf(\"batch size exceed 2: got %d\", len(chunk))\n\t\t}\n\n\t\ttotal += len(chunk)\n\t\treturn nil\n\t})\n\tif err != nil {\n\t\tt.Fatalf(\"FindInBatches failed: %v\", err)\n\t}\n\n\tif total != len(users) {\n\t\tt.Errorf(\"expected total %d, got %d\", len(users), total)\n\t}\n}\n\nfunc TestGenericsScopes(t *testing.T) {\n\tctx := context.Background()\n\n\tusers := []User{{Name: \"GenericsScopes1\"}, {Name: \"GenericsScopes2\"}, {Name: \"GenericsScopes3\"}}\n\terr := gorm.G[User](DB).CreateInBatches(ctx, &users, len(users))\n\tif err != nil {\n\t\tt.Fatalf(\"CreateInBatches failed: %v\", err)\n\t}\n\n\tfilterName1 := func(stmt *gorm.Statement) {\n\t\tstmt.Where(\"name = ?\", \"GenericsScopes1\")\n\t}\n\n\tresults, err := gorm.G[User](DB).Scopes(filterName1).Find(ctx)\n\tif err != nil {\n\t\tt.Fatalf(\"Scopes failed: %v\", err)\n\t}\n\tif len(results) != 1 || results[0].Name != \"GenericsScopes1\" {\n\t\tt.Fatalf(\"Scopes expected 1, got %d\", len(results))\n\t}\n\n\tnotResult, err := gorm.G[User](DB).Where(\"name like ?\", \"GenericsScopes%\").Not(\"name = ?\", \"GenericsScopes1\").Order(\"name\").Find(ctx)\n\tif len(notResult) != 2 {\n\t\tt.Fatalf(\"expected 2 results, got %d\", len(notResult))\n\t} else if notResult[0].Name != \"GenericsScopes2\" || notResult[1].Name != \"GenericsScopes3\" {\n\t\tt.Fatalf(\"expected names 'GenericsScopes2' and 'GenericsScopes3', got %s and %s\", notResult[0].Name, notResult[1].Name)\n\t}\n\n\torResult, err := gorm.G[User](DB).Or(\"name = ?\", \"GenericsScopes1\").Or(\"name = ?\", \"GenericsScopes2\").Order(\"name\").Find(ctx)\n\tif len(orResult) != 2 {\n\t\tt.Fatalf(\"expected 2 results, got %d\", len(notResult))\n\t} else if orResult[0].Name != \"GenericsScopes1\" || orResult[1].Name != \"GenericsScopes2\" {\n\t\tt.Fatalf(\"expected names 'GenericsScopes2' and 'GenericsScopes3', got %s and %s\", orResult[0].Name, orResult[1].Name)\n\t}\n}\n\nfunc TestGenericsJoins(t *testing.T) {\n\tctx := context.Background()\n\tdb := gorm.G[User](DB)\n\n\tu := User{Name: \"GenericsJoins\", Company: Company{Name: \"GenericsCompany\"}}\n\tu2 := User{Name: \"GenericsJoins_2\", Company: Company{Name: \"GenericsCompany_2\"}}\n\tu3 := User{Name: \"GenericsJoins_3\", Company: Company{Name: \"GenericsCompany_3\"}}\n\tdb.CreateInBatches(ctx, &[]User{u3, u, u2}, 10)\n\n\t// Inner JOIN + WHERE\n\tresult, err := db.Joins(clause.Has(\"Company\"), func(db gorm.JoinBuilder, joinTable clause.Table, curTable clause.Table) error {\n\t\tdb.Where(\"?.name = ?\", joinTable, u.Company.Name)\n\t\treturn nil\n\t}).First(ctx)\n\tif err != nil {\n\t\tt.Fatalf(\"Joins failed: %v\", err)\n\t}\n\tif result.Name != u.Name || result.Company.Name != u.Company.Name {\n\t\tt.Fatalf(\"Joins expected %s, got %+v\", u.Name, result)\n\t}\n\n\t// Inner JOIN + WHERE with map\n\tresult, err = db.Joins(clause.Has(\"Company\"), func(db gorm.JoinBuilder, joinTable clause.Table, curTable clause.Table) error {\n\t\tdb.Where(map[string]any{\"name\": u.Company.Name})\n\t\treturn nil\n\t}).First(ctx)\n\tif err != nil {\n\t\tt.Fatalf(\"Joins failed: %v\", err)\n\t}\n\tif result.Name != u.Name || result.Company.Name != u.Company.Name {\n\t\tt.Fatalf(\"Joins expected %s, got %+v\", u.Name, result)\n\t}\n\n\t// Left JOIN w/o WHERE\n\tresult, err = db.Joins(clause.LeftJoin.Association(\"Company\"), nil).Where(map[string]any{\"name\": u.Name}).First(ctx)\n\tif err != nil {\n\t\tt.Fatalf(\"Joins failed: %v\", err)\n\t}\n\tif result.Name != u.Name || result.Company.Name != u.Company.Name {\n\t\tt.Fatalf(\"Joins expected %s, got %+v\", u.Name, result)\n\t}\n\n\t// Left JOIN + Alias WHERE\n\tresult, err = db.Joins(clause.LeftJoin.Association(\"Company\").As(\"t\"), func(db gorm.JoinBuilder, joinTable clause.Table, curTable clause.Table) error {\n\t\tif joinTable.Name != \"t\" {\n\t\t\tt.Fatalf(\"Join table should be t, but got %v\", joinTable.Name)\n\t\t}\n\t\tdb.Where(\"?.name = ?\", joinTable, u.Company.Name)\n\t\treturn nil\n\t}).Where(map[string]any{\"name\": u.Name}).First(ctx)\n\tif err != nil {\n\t\tt.Fatalf(\"Joins failed: %v\", err)\n\t}\n\tif result.Name != u.Name || result.Company.Name != u.Company.Name {\n\t\tt.Fatalf(\"Joins expected %s, got %+v\", u.Name, result)\n\t}\n\n\t// Raw Subquery JOIN + WHERE\n\tresult, err = db.Joins(clause.LeftJoin.AssociationFrom(\"Company\", gorm.G[Company](DB)).As(\"t\"),\n\t\tfunc(db gorm.JoinBuilder, joinTable clause.Table, curTable clause.Table) error {\n\t\t\tif joinTable.Name != \"t\" {\n\t\t\t\tt.Fatalf(\"Join table should be t, but got %v\", joinTable.Name)\n\t\t\t}\n\t\t\tdb.Where(\"?.name = ?\", joinTable, u.Company.Name)\n\t\t\treturn nil\n\t\t},\n\t).Where(map[string]any{\"name\": u2.Name}).First(ctx)\n\tif err != nil {\n\t\tt.Fatalf(\"Raw subquery join failed: %v\", err)\n\t}\n\tif result.Name != u2.Name || result.Company.Name != u.Company.Name || result.Company.ID == 0 {\n\t\tt.Fatalf(\"Joins expected %s, got %+v\", u.Name, result)\n\t}\n\n\t// Raw Subquery JOIN + WHERE + Select\n\tresult, err = db.Joins(clause.LeftJoin.AssociationFrom(\"Company\", gorm.G[Company](DB).Select(\"Name\")).As(\"t\"),\n\t\tfunc(db gorm.JoinBuilder, joinTable clause.Table, curTable clause.Table) error {\n\t\t\tif joinTable.Name != \"t\" {\n\t\t\t\tt.Fatalf(\"Join table should be t, but got %v\", joinTable.Name)\n\t\t\t}\n\t\t\tdb.Where(\"?.name = ?\", joinTable, u.Company.Name)\n\t\t\treturn nil\n\t\t},\n\t).Where(map[string]any{\"name\": u2.Name}).First(ctx)\n\tif err != nil {\n\t\tt.Fatalf(\"Raw subquery join failed: %v\", err)\n\t}\n\tif result.Name != u2.Name || result.Company.Name != u.Company.Name || result.Company.ID != 0 {\n\t\tt.Fatalf(\"Joins expected %s, got %+v\", u.Name, result)\n\t}\n\n\t_, err = db.Joins(clause.Has(\"Company\"), func(db gorm.JoinBuilder, joinTable clause.Table, curTable clause.Table) error {\n\t\treturn errors.New(\"join error\")\n\t}).First(ctx)\n\tif err == nil {\n\t\tt.Fatalf(\"Joins should got error, but got nil\")\n\t}\n}\n\nfunc TestGenericsNestedJoins(t *testing.T) {\n\tusers := []User{\n\t\t{\n\t\t\tName: \"generics-nested-joins-1\",\n\t\t\tManager: &User{\n\t\t\t\tName: \"generics-nested-joins-manager-1\",\n\t\t\t\tCompany: Company{\n\t\t\t\t\tName: \"generics-nested-joins-manager-company-1\",\n\t\t\t\t},\n\t\t\t\tNamedPet: &Pet{\n\t\t\t\t\tName: \"generics-nested-joins-manager-namepet-1\",\n\t\t\t\t\tToy: Toy{\n\t\t\t\t\t\tName: \"generics-nested-joins-manager-namepet-toy-1\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tNamedPet: &Pet{Name: \"generics-nested-joins-namepet-1\", Toy: Toy{Name: \"generics-nested-joins-namepet-toy-1\"}},\n\t\t},\n\t\t{\n\t\t\tName:     \"generics-nested-joins-2\",\n\t\t\tManager:  GetUser(\"generics-nested-joins-manager-2\", Config{Company: true, NamedPet: true}),\n\t\t\tNamedPet: &Pet{Name: \"generics-nested-joins-namepet-2\", Toy: Toy{Name: \"generics-nested-joins-namepet-toy-2\"}},\n\t\t},\n\t}\n\n\tctx := context.Background()\n\tdb := gorm.G[User](DB)\n\tdb.CreateInBatches(ctx, &users, 100)\n\n\tvar userIDs []uint\n\tfor _, user := range users {\n\t\tuserIDs = append(userIDs, user.ID)\n\t}\n\n\tusers2, err := db.Joins(clause.LeftJoin.Association(\"Manager\"), nil).\n\t\tJoins(clause.LeftJoin.Association(\"Manager.Company\"), nil).\n\t\tJoins(clause.LeftJoin.Association(\"Manager.NamedPet.Toy\"), nil).\n\t\tJoins(clause.LeftJoin.Association(\"NamedPet.Toy\"), nil).\n\t\tJoins(clause.LeftJoin.Association(\"NamedPet\").As(\"t\"), nil).\n\t\tWhere(map[string]any{\"id\": userIDs}).Find(ctx)\n\n\tif err != nil {\n\t\tt.Fatalf(\"Failed to load with joins, got error: %v\", err)\n\t} else if len(users2) != len(users) {\n\t\tt.Fatalf(\"Failed to load join users, got: %v, expect: %v\", len(users2), len(users))\n\t}\n\n\tsort.Slice(users2, func(i, j int) bool {\n\t\treturn users2[i].ID > users2[j].ID\n\t})\n\n\tsort.Slice(users, func(i, j int) bool {\n\t\treturn users[i].ID > users[j].ID\n\t})\n\n\tfor idx, user := range users {\n\t\t// user\n\t\tCheckUser(t, user, users2[idx])\n\t\tif users2[idx].Manager == nil {\n\t\t\tt.Fatalf(\"Failed to load Manager\")\n\t\t}\n\t\t// manager\n\t\tCheckUser(t, *user.Manager, *users2[idx].Manager)\n\t\t// user pet\n\t\tif users2[idx].NamedPet == nil {\n\t\t\tt.Fatalf(\"Failed to load NamedPet\")\n\t\t}\n\t\tCheckPet(t, *user.NamedPet, *users2[idx].NamedPet)\n\t\t// manager pet\n\t\tif users2[idx].Manager.NamedPet == nil {\n\t\t\tt.Fatalf(\"Failed to load NamedPet\")\n\t\t}\n\t\tCheckPet(t, *user.Manager.NamedPet, *users2[idx].Manager.NamedPet)\n\t}\n}\n\nfunc TestGenericsPreloads(t *testing.T) {\n\tctx := context.Background()\n\tdb := gorm.G[User](DB)\n\n\tu := *GetUser(\"GenericsPreloads_1\", Config{Company: true, Pets: 3, Friends: 7})\n\tu2 := *GetUser(\"GenericsPreloads_2\", Config{Company: true, Pets: 5, Friends: 5})\n\tu3 := *GetUser(\"GenericsPreloads_3\", Config{Company: true, Pets: 7, Friends: 3})\n\tnames := []string{u.Name, u2.Name, u3.Name}\n\n\tdb.CreateInBatches(ctx, &[]User{u3, u, u2}, 10)\n\n\tresult, err := db.Preload(\"Company\", nil).Preload(\"Pets\", nil).Where(\"name = ?\", u.Name).First(ctx)\n\tif err != nil {\n\t\tt.Fatalf(\"Preload failed: %v\", err)\n\t}\n\n\tif result.Name != u.Name || result.Company.Name != u.Company.Name || len(result.Pets) != len(u.Pets) {\n\t\tt.Fatalf(\"Preload expected %s, got %+v\", u.Name, result)\n\t}\n\n\tresults, err := db.Preload(\"Company\", func(db gorm.PreloadBuilder) error {\n\t\tdb.Where(\"name = ?\", u.Company.Name)\n\t\treturn nil\n\t}).Where(\"name in ?\", names).Find(ctx)\n\tif err != nil {\n\t\tt.Fatalf(\"Preload failed: %v\", err)\n\t}\n\tfor _, result := range results {\n\t\tif result.Name == u.Name {\n\t\t\tif result.Company.Name != u.Company.Name {\n\t\t\t\tt.Fatalf(\"Preload user %v company should be %v, but got %+v\", u.Name, u.Company.Name, result.Company.Name)\n\t\t\t}\n\t\t} else if result.Company.Name != \"\" {\n\t\t\tt.Fatalf(\"Preload other company should not loaded, user %v company expect %v but got %+v\", u.Name, u.Company.Name, result.Company.Name)\n\t\t}\n\t}\n\n\t_, err = db.Preload(\"Company\", func(db gorm.PreloadBuilder) error {\n\t\treturn errors.New(\"preload error\")\n\t}).Where(\"name in ?\", names).Find(ctx)\n\tif err == nil {\n\t\tt.Fatalf(\"Preload should failed, but got nil\")\n\t}\n\n\tif DB.Dialector.Name() == \"mysql\" {\n\t\t// mysql 5.7 doesn't support row_number()\n\t\tif strings.HasPrefix(DB.Dialector.(*mysql.Dialector).ServerVersion, \"5.7\") {\n\t\t\treturn\n\t\t}\n\t}\n\tresults, err = db.Preload(\"Pets\", func(db gorm.PreloadBuilder) error {\n\t\tdb.LimitPerRecord(5)\n\t\treturn nil\n\t}).Where(\"name in ?\", names).Find(ctx)\n\n\tfor _, result := range results {\n\t\tif result.Name == u.Name {\n\t\t\tif len(result.Pets) != len(u.Pets) {\n\t\t\t\tt.Fatalf(\"Preload user %v pets should be %v, but got %+v\", u.Name, u.Pets, result.Pets)\n\t\t\t}\n\t\t} else if len(result.Pets) != 5 {\n\t\t\tt.Fatalf(\"Preload user %v pets should be 5, but got %+v\", result.Name, result.Pets)\n\t\t}\n\t}\n\n\tif DB.Dialector.Name() == \"sqlserver\" {\n\t\t// sqlserver doesn't support order by in subquery\n\t\treturn\n\t}\n\tresults, err = db.Preload(\"Pets\", func(db gorm.PreloadBuilder) error {\n\t\tdb.Order(\"name desc\").LimitPerRecord(5)\n\t\treturn nil\n\t}).Where(\"name in ?\", names).Find(ctx)\n\n\tfor _, result := range results {\n\t\tif result.Name == u.Name {\n\t\t\tif len(result.Pets) != len(u.Pets) {\n\t\t\t\tt.Fatalf(\"Preload user %v pets should be %v, but got %+v\", u.Name, u.Pets, result.Pets)\n\t\t\t}\n\t\t} else if len(result.Pets) != 5 {\n\t\t\tt.Fatalf(\"Preload user %v pets should be 5, but got %+v\", result.Name, result.Pets)\n\t\t}\n\t\tfor i := 1; i < len(result.Pets); i++ {\n\t\t\tif result.Pets[i-1].Name < result.Pets[i].Name {\n\t\t\t\tt.Fatalf(\"Preload user %v pets not ordered correctly, last %v, cur %v\", result.Name, result.Pets[i-1], result.Pets[i])\n\t\t\t}\n\t\t}\n\t}\n\n\tresults, err = db.Preload(\"Pets\", func(db gorm.PreloadBuilder) error {\n\t\tdb.Order(\"name\").LimitPerRecord(5)\n\t\treturn nil\n\t}).Preload(\"Friends\", func(db gorm.PreloadBuilder) error {\n\t\tdb.Order(\"name\")\n\t\treturn nil\n\t}).Where(\"name in ?\", names).Find(ctx)\n\n\tfor _, result := range results {\n\t\tif result.Name == u.Name {\n\t\t\tif len(result.Pets) != len(u.Pets) {\n\t\t\t\tt.Fatalf(\"Preload user %v pets should be %v, but got %+v\", u.Name, u.Pets, result.Pets)\n\t\t\t}\n\t\t\tif len(result.Friends) != len(u.Friends) {\n\t\t\t\tt.Fatalf(\"Preload user %v pets should be %v, but got %+v\", u.Name, u.Pets, result.Pets)\n\t\t\t}\n\t\t} else if len(result.Pets) != 5 || len(result.Friends) == 0 {\n\t\t\tt.Fatalf(\"Preload user %v pets should be 5, but got %+v\", result.Name, result.Pets)\n\t\t}\n\t\tfor i := 1; i < len(result.Pets); i++ {\n\t\t\tif result.Pets[i-1].Name > result.Pets[i].Name {\n\t\t\t\tt.Fatalf(\"Preload user %v pets not ordered correctly, last %v, cur %v\", result.Name, result.Pets[i-1], result.Pets[i])\n\t\t\t}\n\t\t}\n\t\tfor i := 1; i < len(result.Pets); i++ {\n\t\t\tif result.Pets[i-1].Name > result.Pets[i].Name {\n\t\t\t\tt.Fatalf(\"Preload user %v friends not ordered correctly, last %v, cur %v\", result.Name, result.Pets[i-1], result.Pets[i])\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc TestGenericsNestedPreloads(t *testing.T) {\n\tuser := *GetUser(\"generics_nested_preload\", Config{Pets: 2})\n\tuser.Friends = []*User{GetUser(\"generics_nested_preload\", Config{Pets: 5})}\n\n\tctx := context.Background()\n\tdb := gorm.G[User](DB)\n\n\tfor idx, pet := range user.Pets {\n\t\tpet.Toy = Toy{Name: \"toy_nested_preload_\" + strconv.Itoa(idx+1)}\n\t}\n\n\tif err := db.Create(ctx, &user); err != nil {\n\t\tt.Fatalf(\"errors happened when create: %v\", err)\n\t}\n\n\tuser2, err := db.Preload(\"Pets.Toy\", nil).Preload(\"Friends.Pets\", func(db gorm.PreloadBuilder) error {\n\t\treturn nil\n\t}).Where(user.ID).Take(ctx)\n\tif err != nil {\n\t\tt.Errorf(\"failed to nested preload user\")\n\t}\n\tCheckUser(t, user2, user)\n\tif len(user.Pets) == 0 || len(user.Friends) == 0 || len(user.Friends[0].Pets) == 0 {\n\t\tt.Fatalf(\"failed to nested preload\")\n\t}\n\n\tif DB.Dialector.Name() == \"mysql\" {\n\t\t// mysql 5.7 doesn't support row_number()\n\t\tif strings.HasPrefix(DB.Dialector.(*mysql.Dialector).ServerVersion, \"5.7\") {\n\t\t\treturn\n\t\t}\n\t}\n\tif DB.Dialector.Name() == \"sqlserver\" {\n\t\t// sqlserver doesn't support order by in subquery\n\t\treturn\n\t}\n\n\tuser3, err := db.Preload(\"Pets.Toy\", nil).Preload(\"Friends.Pets\", func(db gorm.PreloadBuilder) error {\n\t\tdb.LimitPerRecord(3)\n\t\treturn nil\n\t}).Where(user.ID).Take(ctx)\n\tif err != nil {\n\t\tt.Errorf(\"failed to nested preload user\")\n\t}\n\tCheckUser(t, user3, user)\n\n\tif len(user3.Friends) != 1 || len(user3.Friends[0].Pets) != 3 {\n\t\tt.Errorf(\"failed to nested preload with limit per record\")\n\t}\n}\n\nfunc TestGenericsDistinct(t *testing.T) {\n\tctx := context.Background()\n\n\tbatch := []User{\n\t\t{Name: \"GenericsDistinctDup\"},\n\t\t{Name: \"GenericsDistinctDup\"},\n\t\t{Name: \"GenericsDistinctUnique\"},\n\t}\n\tif err := gorm.G[User](DB).CreateInBatches(ctx, &batch, len(batch)); err != nil {\n\t\tt.Fatalf(\"CreateInBatches failed: %v\", err)\n\t}\n\n\tresults, err := gorm.G[User](DB).Where(\"name like ?\", \"GenericsDistinct%\").Distinct(\"name\").Find(ctx)\n\tif err != nil {\n\t\tt.Fatalf(\"Distinct Find failed: %v\", err)\n\t}\n\n\tif len(results) != 2 {\n\t\tt.Errorf(\"expected 2 distinct names, got %d\", len(results))\n\t}\n\n\tvar names []string\n\tfor _, u := range results {\n\t\tnames = append(names, u.Name)\n\t}\n\tsort.Strings(names)\n\texpected := []string{\"GenericsDistinctDup\", \"GenericsDistinctUnique\"}\n\tif !reflect.DeepEqual(names, expected) {\n\t\tt.Errorf(\"expected names %v, got %v\", expected, names)\n\t}\n}\n\nfunc TestGenericsSetCreate(t *testing.T) {\n\tctx := context.Background()\n\n\tname := \"GenericsSetCreate\"\n\tage := uint(21)\n\n\terr := gorm.G[User](DB).Set(\n\t\tclause.Assignment{Column: clause.Column{Name: \"name\"}, Value: name},\n\t\tclause.Assignment{Column: clause.Column{Name: \"age\"}, Value: age},\n\t).Create(ctx)\n\tif err != nil {\n\t\tt.Fatalf(\"Set Create failed: %v\", err)\n\t}\n\n\tu, err := gorm.G[User](DB).Where(\"name = ?\", name).First(ctx)\n\tif err != nil {\n\t\tt.Fatalf(\"failed to find created user: %v\", err)\n\t}\n\tif u.ID == 0 || u.Name != name || u.Age != age {\n\t\tt.Fatalf(\"created user mismatch, got %+v\", u)\n\t}\n}\n\nfunc TestGenericsSetUpdate(t *testing.T) {\n\tctx := context.Background()\n\n\t// prepare\n\tu := User{Name: \"GenericsSetUpdate_Before\", Age: 30}\n\tif err := gorm.G[User](DB).Create(ctx, &u); err != nil {\n\t\tt.Fatalf(\"prepare user failed: %v\", err)\n\t}\n\n\t// update with Set after chain\n\tnewName := \"GenericsSetUpdate_After\"\n\tnewAge := uint(31)\n\trows, err := gorm.G[User](DB).\n\t\tWhere(\"id = ?\", u.ID).\n\t\tSet(\n\t\t\tclause.Assignment{Column: clause.Column{Name: \"name\"}, Value: newName},\n\t\t\tclause.Assignment{Column: clause.Column{Name: \"age\"}, Value: newAge},\n\t\t).\n\t\tUpdate(ctx)\n\tif err != nil {\n\t\tt.Fatalf(\"Set Update failed: %v\", err)\n\t}\n\tif rows != 1 {\n\t\tt.Fatalf(\"expected 1 row affected, got %d\", rows)\n\t}\n\n\tnu, err := gorm.G[User](DB).Where(\"id = ?\", u.ID).First(ctx)\n\tif err != nil {\n\t\tt.Fatalf(\"failed to query updated user: %v\", err)\n\t}\n\tif nu.Name != newName || nu.Age != newAge {\n\t\tt.Fatalf(\"updated user mismatch, got %+v\", nu)\n\t}\n}\n\nfunc TestGenericsGroupHaving(t *testing.T) {\n\tctx := context.Background()\n\n\tbatch := []User{\n\t\t{Name: \"GenericsGroupHavingMulti\"},\n\t\t{Name: \"GenericsGroupHavingMulti\"},\n\t\t{Name: \"GenericsGroupHavingSingle\"},\n\t}\n\tif err := gorm.G[User](DB).CreateInBatches(ctx, &batch, len(batch)); err != nil {\n\t\tt.Fatalf(\"CreateInBatches failed: %v\", err)\n\t}\n\n\tgrouped, err := gorm.G[User](DB).Select(\"name\").Where(\"name like ?\", \"GenericsGroupHaving%\").Group(\"name\").Having(\"COUNT(id) > ?\", 1).Find(ctx)\n\tif err != nil {\n\t\tt.Fatalf(\"Group+Having Find failed: %v\", err)\n\t}\n\n\tif len(grouped) != 1 {\n\t\tt.Errorf(\"expected 1 group with count>1, got %d\", len(grouped))\n\t} else if grouped[0].Name != \"GenericsGroupHavingMulti\" {\n\t\tt.Errorf(\"expected group name 'GenericsGroupHavingMulti', got '%s'\", grouped[0].Name)\n\t}\n}\n\nfunc TestGenericsSubQuery(t *testing.T) {\n\tctx := context.Background()\n\tusers := []User{\n\t\t{Name: \"GenericsSubquery_1\", Age: 10},\n\t\t{Name: \"GenericsSubquery_2\", Age: 20},\n\t\t{Name: \"GenericsSubquery_3\", Age: 30},\n\t\t{Name: \"GenericsSubquery_4\", Age: 40},\n\t}\n\n\tif err := gorm.G[User](DB).CreateInBatches(ctx, &users, len(users)); err != nil {\n\t\tt.Fatalf(\"CreateInBatches failed: %v\", err)\n\t}\n\n\tresults, err := gorm.G[User](DB).Where(\"name IN (?)\", gorm.G[User](DB).Select(\"name\").Where(\"name LIKE ?\", \"GenericsSubquery%\")).Find(ctx)\n\tif err != nil {\n\t\tt.Fatalf(\"got error: %v\", err)\n\t}\n\n\tif len(results) != 4 {\n\t\tt.Errorf(\"Four users should be found, instead found %d\", len(results))\n\t}\n\n\tresults, err = gorm.G[User](DB).Where(\"name IN (?)\", gorm.G[User](DB).Select(\"name\").Where(\"name IN ?\", []string{\"GenericsSubquery_1\", \"GenericsSubquery_2\"}).Or(\"name = ?\", \"GenericsSubquery_3\")).Find(ctx)\n\tif err != nil {\n\t\tt.Fatalf(\"got error: %v\", err)\n\t}\n\n\tif len(results) != 3 {\n\t\tt.Errorf(\"Three users should be found, instead found %d\", len(results))\n\t}\n}\n\nfunc TestGenericsUpsert(t *testing.T) {\n\tctx := context.Background()\n\tlang := Language{Code: \"upsert\", Name: \"Upsert\"}\n\n\tif err := gorm.G[Language](DB, clause.OnConflict{DoNothing: true}).Create(ctx, &lang); err != nil {\n\t\tt.Fatalf(\"failed to upsert, got %v\", err)\n\t}\n\n\tlang2 := Language{Code: \"upsert\", Name: \"Upsert\"}\n\tif err := gorm.G[Language](DB, clause.OnConflict{DoNothing: true}).Create(ctx, &lang2); err != nil {\n\t\tt.Fatalf(\"failed to upsert, got %v\", err)\n\t}\n\n\tlangs, err := gorm.G[Language](DB).Where(\"code = ?\", lang.Code).Find(ctx)\n\tif err != nil {\n\t\tt.Errorf(\"no error should happen when find languages with code, but got %v\", err)\n\t} else if len(langs) != 1 {\n\t\tt.Errorf(\"should only find only 1 languages, but got %+v\", langs)\n\t}\n\n\tlang3 := Language{Code: \"upsert\", Name: \"Upsert\"}\n\tif err := gorm.G[Language](DB, clause.OnConflict{\n\t\tColumns:   []clause.Column{{Name: \"code\"}},\n\t\tDoUpdates: clause.Assignments(map[string]interface{}{\"name\": \"upsert-new\"}),\n\t}).Create(ctx, &lang3); err != nil {\n\t\tt.Fatalf(\"failed to upsert, got %v\", err)\n\t}\n\n\tif langs, err := gorm.G[Language](DB).Where(\"code = ?\", lang.Code).Find(ctx); err != nil {\n\t\tt.Errorf(\"no error should happen when find languages with code, but got %v\", err)\n\t} else if len(langs) != 1 {\n\t\tt.Errorf(\"should only find only 1 languages, but got %+v\", langs)\n\t} else if langs[0].Name != \"upsert-new\" {\n\t\tt.Errorf(\"should update name on conflict, but got name %+v\", langs[0].Name)\n\t}\n}\n\nfunc TestGenericsWithResult(t *testing.T) {\n\tctx := context.Background()\n\tusers := []User{{Name: \"TestGenericsWithResult\", Age: 18}, {Name: \"TestGenericsWithResult2\", Age: 18}}\n\n\tresult := gorm.WithResult()\n\terr := gorm.G[User](DB, result).CreateInBatches(ctx, &users, 2)\n\tif err != nil {\n\t\tt.Errorf(\"failed to create users WithResult\")\n\t}\n\n\tif result.RowsAffected != 2 {\n\t\tt.Errorf(\"failed to get affected rows, got %d, should be %d\", result.RowsAffected, 2)\n\t}\n}\n\nfunc TestGenericsReuse(t *testing.T) {\n\tctx := context.Background()\n\tusers := []User{{Name: \"TestGenericsReuse1\", Age: 18}, {Name: \"TestGenericsReuse2\", Age: 18}}\n\n\terr := gorm.G[User](DB).CreateInBatches(ctx, &users, 2)\n\tif err != nil {\n\t\tt.Errorf(\"failed to create users\")\n\t}\n\n\treusedb := gorm.G[User](DB).Where(\"name like ?\", \"TestGenericsReuse%\")\n\n\tsg := sync.WaitGroup{}\n\tfor i := 0; i < 5; i++ {\n\t\tsg.Add(1)\n\n\t\tgo func() {\n\t\t\tif u1, err := reusedb.Where(\"id = ?\", users[0].ID).First(ctx); err != nil {\n\t\t\t\tt.Errorf(\"failed to find user, got error: %v\", err)\n\t\t\t} else if u1.Name != users[0].Name || u1.ID != users[0].ID {\n\t\t\t\tt.Errorf(\"found invalid user, got %v, expect %v\", u1, users[0])\n\t\t\t}\n\n\t\t\tif u2, err := reusedb.Where(\"id = ?\", users[1].ID).First(ctx); err != nil {\n\t\t\t\tt.Errorf(\"failed to find user, got error: %v\", err)\n\t\t\t} else if u2.Name != users[1].Name || u2.ID != users[1].ID {\n\t\t\t\tt.Errorf(\"found invalid user, got %v, expect %v\", u2, users[1])\n\t\t\t}\n\n\t\t\tif users, err := reusedb.Where(\"id IN ?\", []uint{users[0].ID, users[1].ID}).Find(ctx); err != nil {\n\t\t\t\tt.Errorf(\"failed to find user, got error: %v\", err)\n\t\t\t} else if len(users) != 2 {\n\t\t\t\tt.Errorf(\"should find 2 users, but got %d\", len(users))\n\t\t\t}\n\t\t\tsg.Done()\n\t\t}()\n\t}\n\tsg.Wait()\n}\n\nfunc TestGenericsWithTransaction(t *testing.T) {\n\tctx := context.Background()\n\ttx := DB.Begin()\n\tif tx.Error != nil {\n\t\tt.Fatalf(\"failed to begin transaction: %v\", tx.Error)\n\t}\n\n\tusers := []User{{Name: \"TestGenericsTransaction\", Age: 18}, {Name: \"TestGenericsTransaction2\", Age: 18}}\n\terr := gorm.G[User](tx).CreateInBatches(ctx, &users, 2)\n\n\tcount, err := gorm.G[User](tx).Where(\"name like ?\", \"TestGenericsTransaction%\").Count(ctx, \"*\")\n\tif err != nil {\n\t\tt.Fatalf(\"Count failed: %v\", err)\n\t}\n\tif count != 2 {\n\t\tt.Errorf(\"expected 2 records, got %d\", count)\n\t}\n\n\tif err := tx.Rollback().Error; err != nil {\n\t\tt.Fatalf(\"failed to rollback transaction: %v\", err)\n\t}\n\n\tcount2, err := gorm.G[User](DB).Where(\"name like ?\", \"TestGenericsTransaction%\").Count(ctx, \"*\")\n\tif err != nil {\n\t\tt.Fatalf(\"Count failed: %v\", err)\n\t}\n\tif count2 != 0 {\n\t\tt.Errorf(\"expected 0 records after rollback, got %d\", count2)\n\t}\n}\n\nfunc TestGenericsToSQL(t *testing.T) {\n\tctx := context.Background()\n\tsql := DB.ToSQL(func(tx *gorm.DB) *gorm.DB {\n\t\tgorm.G[User](tx).Limit(10).Find(ctx)\n\t\treturn tx\n\t})\n\n\tif !regexp.MustCompile(\"SELECT \\\\* FROM .users..* 10\").MatchString(sql) {\n\t\tt.Errorf(\"ToSQL: got wrong sql with Generics API %v\", sql)\n\t}\n}\n\nfunc TestGenericsScanUUID(t *testing.T) {\n\tctx := context.Background()\n\tusers := []User{\n\t\t{Name: uuid.NewString(), Age: 21},\n\t\t{Name: uuid.NewString(), Age: 22},\n\t\t{Name: uuid.NewString(), Age: 23},\n\t}\n\n\tif err := gorm.G[User](DB).CreateInBatches(ctx, &users, 2); err != nil {\n\t\tt.Fatalf(\"CreateInBatches failed: %v\", err)\n\t}\n\n\tuserIds := []uuid.UUID{}\n\tif err := gorm.G[User](DB).Select(\"name\").Where(\"id in ?\", []uint{users[0].ID, users[1].ID, users[2].ID}).Order(\"age\").Scan(ctx, &userIds); err != nil || len(users) != 3 {\n\t\tt.Fatalf(\"Scan failed: %v, userids %v\", err, userIds)\n\t}\n\n\tif userIds[0].String() != users[0].Name || userIds[1].String() != users[1].Name || userIds[2].String() != users[2].Name {\n\t\tt.Fatalf(\"wrong uuid scanned\")\n\t}\n}\n\nfunc TestGenericsCount(t *testing.T) {\n\tctx := context.Background()\n\n\t// Just test that the API can be called\n\t_, err := gorm.G[User](DB).Count(ctx, \"*\")\n\tif err != nil {\n\t\tt.Fatalf(\"Count failed: %v\", err)\n\t}\n}\n\nfunc TestGenericsUpdate(t *testing.T) {\n\tctx := context.Background()\n\n\t// Just test that the API can be called\n\t_, err := gorm.G[User](DB).Where(\"id = ?\", 1).Update(ctx, \"name\", \"test\")\n\tif err != nil {\n\t\tt.Fatalf(\"Update failed: %v\", err)\n\t}\n}\n\nfunc TestGenericsUpdates(t *testing.T) {\n\tctx := context.Background()\n\n\t// Just test that the API can be called\n\t_, err := gorm.G[User](DB).Where(\"id = ?\", 1).Updates(ctx, User{Name: \"test\"})\n\tif err != nil {\n\t\tt.Fatalf(\"Updates failed: %v\", err)\n\t}\n}\n\nfunc TestGenericsDeleteAPI(t *testing.T) {\n\tctx := context.Background()\n\n\t// Just test that the API can be called\n\t_, err := gorm.G[User](DB).Where(\"id = ?\", 1).Delete(ctx)\n\tif err != nil {\n\t\tt.Fatalf(\"Delete failed: %v\", err)\n\t}\n}\n\nfunc TestGenericsAssociation(t *testing.T) {\n\t// Test basic Association creation\n\tassoc := clause.Association{\n\t\tAssociation: \"Orders\",\n\t\tType:        clause.OpCreate,\n\t\tSet: []clause.Assignment{\n\t\t\t{Column: clause.Column{Name: \"amount\"}, Value: 100},\n\t\t\t{Column: clause.Column{Name: \"state\"}, Value: \"new\"},\n\t\t},\n\t}\n\n\t// Verify it implements Assigner interface\n\tassignments := assoc.Assignments()\n\tif len(assignments) != 0 {\n\t\tt.Errorf(\"Association.Assignments() should return empty slice, got %v\", assignments)\n\t}\n\n\t// Verify it implements AssociationAssigner interface\n\tassocAssignments := assoc.AssociationAssignments()\n\tif len(assocAssignments) != 1 {\n\t\tt.Errorf(\"Association.AssociationAssignments() should return slice with one element, got %v\", assocAssignments)\n\t}\n\n\tif assocAssignments[0].Association != \"Orders\" {\n\t\tt.Errorf(\"Association.AssociationAssignments()[0].Association should be 'Orders', got %v\", assocAssignments[0].Association)\n\t}\n\n\t// Test different association operation types\n\toperations := []struct {\n\t\tType     clause.AssociationOpType\n\t\tTypeName string\n\t}{\n\t\t{clause.OpUnlink, \"OpUnlink\"},\n\t\t{clause.OpDelete, \"OpDelete\"},\n\t\t{clause.OpUpdate, \"OpUpdate\"},\n\t\t{clause.OpCreate, \"OpCreate\"},\n\t}\n\n\tfor _, op := range operations {\n\t\tassoc := clause.Association{\n\t\t\tAssociation: \"Orders\",\n\t\t\tType:        op.Type,\n\t\t}\n\n\t\tif assoc.Type != op.Type {\n\t\t\tt.Errorf(\"Association type should be %s, got %v\", op.TypeName, assoc.Type)\n\t\t}\n\t}\n}\n\nfunc TestGenericsAssociationSlice(t *testing.T) {\n\t// Test that a slice of Association can be used\n\tassociations := []clause.Association{\n\t\t{Association: \"Orders\", Type: clause.OpDelete},\n\t\t{Association: \"Profiles\", Type: clause.OpUpdate},\n\t}\n\n\t// In practice, each Association would be processed individually\n\t// since []clause.Association doesn't implement AssociationAssigner directly\n\tfor i, assoc := range associations {\n\t\tassigns := assoc.AssociationAssignments()\n\t\tif len(assigns) != 1 {\n\t\t\tt.Errorf(\"Association %d should return one assignment, got %v\", i, len(assigns))\n\t\t}\n\t\tif assigns[0].Association != assoc.Association {\n\t\t\tt.Errorf(\"Association %d name should be %s, got %v\", i, assoc.Association, assigns[0].Association)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "tests/go.mod",
    "content": "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/lib/pq v1.10.9\n\tgithub.com/stretchr/testify v1.11.1\n\tgorm.io/driver/gaussdb v0.1.0\n\tgorm.io/driver/mysql v1.6.0\n\tgorm.io/driver/postgres v1.6.0\n\tgorm.io/driver/sqlite v1.6.0\n\tgorm.io/driver/sqlserver v1.6.1\n\tgorm.io/gorm v1.31.0\n)\n\nrequire (\n\tfilippo.io/edwards25519 v1.1.0 // indirect\n\tgithub.com/HuaweiCloudDeveloper/gaussdb-go v1.0.0-rc1 // indirect\n\tgithub.com/davecgh/go-spew v1.1.1 // indirect\n\tgithub.com/go-sql-driver/mysql v1.9.3 // indirect\n\tgithub.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect\n\tgithub.com/golang-sql/sqlexp v0.1.0 // indirect\n\tgithub.com/jackc/pgpassfile v1.0.0 // indirect\n\tgithub.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect\n\tgithub.com/jackc/pgx/v5 v5.7.6 // indirect\n\tgithub.com/jackc/puddle/v2 v2.2.2 // indirect\n\tgithub.com/jinzhu/inflection v1.0.0 // indirect\n\tgithub.com/mattn/go-sqlite3 v1.14.32 // indirect\n\tgithub.com/microsoft/go-mssqldb v1.9.3 // indirect\n\tgithub.com/pmezard/go-difflib v1.0.0 // indirect\n\tgithub.com/tjfoc/gmsm v1.4.1 // indirect\n\tgolang.org/x/crypto v0.43.0 // indirect\n\tgolang.org/x/sync v0.17.0 // indirect\n\tgolang.org/x/text v0.30.0 // indirect\n\tgopkg.in/yaml.v3 v3.0.1 // indirect\n)\n\nreplace gorm.io/gorm => ../\n"
  },
  {
    "path": "tests/gorm_test.go",
    "content": "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 := \"gorm:gorm@tcp(localhost:9910)/gorm?loc=Asia%2FHongKong\" // invalid loc\n\t_, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})\n\tif err == nil {\n\t\tt.Fatalf(\"should returns error but got nil\")\n\t}\n}\n\nfunc TestReturningWithNullToZeroValues(t *testing.T) {\n\tdialect := DB.Dialector.Name()\n\tswitch dialect {\n\tcase \"mysql\", \"sqlserver\":\n\t\t// these dialects do not support the \"returning\" clause\n\t\treturn\n\tdefault:\n\t\t// This user struct will leverage the existing users table, but override\n\t\t// the Name field to default to null.\n\t\ttype user struct {\n\t\t\tgorm.Model\n\t\t\tName string `gorm:\"default:null\"`\n\t\t}\n\t\tu1 := user{}\n\n\t\tif results := DB.Create(&u1); results.Error != nil {\n\t\t\tt.Fatalf(\"errors happened on create: %v\", results.Error)\n\t\t} else if results.RowsAffected != 1 {\n\t\t\tt.Fatalf(\"rows affected expects: %v, got %v\", 1, results.RowsAffected)\n\t\t} else if u1.ID == 0 {\n\t\t\tt.Fatalf(\"ID expects : not equal 0, got %v\", u1.ID)\n\t\t}\n\n\t\tgot := user{}\n\t\tresults := DB.First(&got, \"id = ?\", u1.ID)\n\t\tif results.Error != nil {\n\t\t\tt.Fatalf(\"errors happened on first: %v\", results.Error)\n\t\t} else if results.RowsAffected != 1 {\n\t\t\tt.Fatalf(\"rows affected expects: %v, got %v\", 1, results.RowsAffected)\n\t\t} else if got.ID != u1.ID {\n\t\t\tt.Fatalf(\"first expects: %v, got %v\", u1, got)\n\t\t}\n\n\t\tresults = DB.Select(\"id, name\").Find(&got)\n\t\tif results.Error != nil {\n\t\t\tt.Fatalf(\"errors happened on first: %v\", results.Error)\n\t\t} else if results.RowsAffected != 1 {\n\t\t\tt.Fatalf(\"rows affected expects: %v, got %v\", 1, results.RowsAffected)\n\t\t} else if got.ID != u1.ID {\n\t\t\tt.Fatalf(\"select expects: %v, got %v\", u1, got)\n\t\t}\n\n\t\tu1.Name = \"jinzhu\"\n\t\tif results := DB.Save(&u1); results.Error != nil {\n\t\t\tt.Fatalf(\"errors happened on update: %v\", results.Error)\n\t\t} else if results.RowsAffected != 1 {\n\t\t\tt.Fatalf(\"rows affected expects: %v, got %v\", 1, results.RowsAffected)\n\t\t}\n\n\t\tu1 = user{} // important to reinitialize this before creating it again\n\t\tu2 := user{}\n\t\tdb := DB.Session(&gorm.Session{CreateBatchSize: 10})\n\n\t\tif results := db.Create([]*user{&u1, &u2}); results.Error != nil {\n\t\t\tt.Fatalf(\"errors happened on create: %v\", results.Error)\n\t\t} else if results.RowsAffected != 2 {\n\t\t\tt.Fatalf(\"rows affected expects: %v, got %v\", 1, results.RowsAffected)\n\t\t} else if u1.ID == 0 {\n\t\t\tt.Fatalf(\"ID expects : not equal 0, got %v\", u1.ID)\n\t\t} else if u2.ID == 0 {\n\t\t\tt.Fatalf(\"ID expects : not equal 0, got %v\", u2.ID)\n\t\t}\n\n\t\tvar gotUsers []user\n\t\tresults = DB.Where(\"id in (?, ?)\", u1.ID, u2.ID).Order(\"id asc\").Select(\"id, name\").Find(&gotUsers)\n\t\tif results.Error != nil {\n\t\t\tt.Fatalf(\"errors happened on first: %v\", results.Error)\n\t\t} else if results.RowsAffected != 2 {\n\t\t\tt.Fatalf(\"rows affected expects: %v, got %v\", 2, results.RowsAffected)\n\t\t} else if gotUsers[0].ID != u1.ID {\n\t\t\tt.Fatalf(\"select expects: %v, got %v\", u1.ID, gotUsers[0].ID)\n\t\t} else if gotUsers[1].ID != u2.ID {\n\t\t\tt.Fatalf(\"select expects: %v, got %v\", u2.ID, gotUsers[1].ID)\n\t\t}\n\n\t\tu1.Name = \"Jinzhu\"\n\t\tu2.Name = \"Zhang\"\n\t\tif results := DB.Save([]*user{&u1, &u2}); results.Error != nil {\n\t\t\tt.Fatalf(\"errors happened on update: %v\", results.Error)\n\t\t} else if results.RowsAffected != 2 {\n\t\t\tt.Fatalf(\"rows affected expects: %v, got %v\", 1, results.RowsAffected)\n\t\t}\n\n\t}\n}\n"
  },
  {
    "path": "tests/group_by_test.go",
    "content": "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 := []User{{\n\t\tName:     \"groupby\",\n\t\tAge:      10,\n\t\tBirthday: Now(),\n\t\tActive:   true,\n\t}, {\n\t\tName:     \"groupby\",\n\t\tAge:      20,\n\t\tBirthday: Now(),\n\t}, {\n\t\tName:     \"groupby\",\n\t\tAge:      30,\n\t\tBirthday: Now(),\n\t\tActive:   true,\n\t}, {\n\t\tName:     \"groupby1\",\n\t\tAge:      110,\n\t\tBirthday: Now(),\n\t}, {\n\t\tName:     \"groupby1\",\n\t\tAge:      220,\n\t\tBirthday: Now(),\n\t\tActive:   true,\n\t}, {\n\t\tName:     \"groupby1\",\n\t\tAge:      330,\n\t\tBirthday: Now(),\n\t\tActive:   true,\n\t}}\n\n\tif err := DB.Create(&users).Error; err != nil {\n\t\tt.Errorf(\"errors happened when create: %v\", err)\n\t}\n\n\tvar name string\n\tvar total int\n\tif err := DB.Model(&User{}).Select(\"name, sum(age)\").Where(\"name = ?\", \"groupby\").Group(\"name\").Row().Scan(&name, &total); err != nil {\n\t\tt.Errorf(\"no error should happen, but got %v\", err)\n\t}\n\n\tif name != \"groupby\" || total != 60 {\n\t\tt.Errorf(\"name should be groupby, but got %v, total should be 60, but got %v\", name, total)\n\t}\n\n\tif err := DB.Model(&User{}).Select(\"name, sum(age)\").Where(\"name = ?\", \"groupby\").Group(\"users.name\").Row().Scan(&name, &total); err != nil {\n\t\tt.Errorf(\"no error should happen, but got %v\", err)\n\t}\n\n\tif name != \"groupby\" || total != 60 {\n\t\tt.Errorf(\"name should be groupby, but got %v, total should be 60, but got %v\", name, total)\n\t}\n\n\tif err := DB.Model(&User{}).Select(\"name, sum(age) as total\").Where(\"name LIKE ?\", \"groupby%\").Group(\"name\").Having(\"name = ?\", \"groupby1\").Row().Scan(&name, &total); err != nil {\n\t\tt.Errorf(\"no error should happen, but got %v\", err)\n\t}\n\n\tif name != \"groupby1\" || total != 660 {\n\t\tt.Errorf(\"name should be groupby, but got %v, total should be 660, but got %v\", name, total)\n\t}\n\n\tresult := struct {\n\t\tName  string\n\t\tTotal int64\n\t}{}\n\n\tif err := DB.Model(&User{}).Select(\"name, sum(age) as total\").Where(\"name LIKE ?\", \"groupby%\").Group(\"name\").Having(\"name = ?\", \"groupby1\").Find(&result).Error; err != nil {\n\t\tt.Errorf(\"no error should happen, but got %v\", err)\n\t}\n\n\tif result.Name != \"groupby1\" || result.Total != 660 {\n\t\tt.Errorf(\"name should be groupby, total should be 660, but got %+v\", result)\n\t}\n\n\tif err := DB.Model(&User{}).Select(\"name, sum(age) as total\").Where(\"name LIKE ?\", \"groupby%\").Group(\"name\").Having(\"name = ?\", \"groupby1\").Scan(&result).Error; err != nil {\n\t\tt.Errorf(\"no error should happen, but got %v\", err)\n\t}\n\n\tif result.Name != \"groupby1\" || result.Total != 660 {\n\t\tt.Errorf(\"name should be groupby, total should be 660, but got %+v\", result)\n\t}\n\n\tvar active bool\n\tif err := DB.Model(&User{}).Select(\"name, active, sum(age)\").Where(\"name = ? and active = ?\", \"groupby\", true).Group(\"name\").Group(\"active\").Row().Scan(&name, &active, &total); err != nil {\n\t\tt.Errorf(\"no error should happen, but got %v\", err)\n\t}\n\n\tif name != \"groupby\" || active != true || total != 40 {\n\t\tt.Errorf(\"group by two columns, name %v, age %v, active: %v\", name, total, active)\n\t}\n\n\tif DB.Dialector.Name() == \"mysql\" {\n\t\tif err := DB.Model(&User{}).Select(\"name, age as total\").Where(\"name LIKE ?\", \"groupby%\").Having(\"total > ?\", 300).Scan(&result).Error; err != nil {\n\t\t\tt.Errorf(\"no error should happen, but got %v\", err)\n\t\t}\n\n\t\tif result.Name != \"groupby1\" || result.Total != 330 {\n\t\t\tt.Errorf(\"name should be groupby, total should be 660, but got %+v\", result)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "tests/helper_test.go",
    "content": "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/utils/tests\"\n)\n\ntype Config struct {\n\tAccount   bool\n\tPets      int\n\tToys      int\n\tCompany   bool\n\tManager   bool\n\tTeam      int\n\tLanguages int\n\tFriends   int\n\tNamedPet  bool\n\tTools     int\n}\n\nfunc GetUser(name string, config Config) *User {\n\tvar (\n\t\tbirthday = time.Now().Round(time.Second)\n\t\tuser     = User{\n\t\t\tName:     name,\n\t\t\tAge:      18,\n\t\t\tBirthday: &birthday,\n\t\t}\n\t)\n\n\tif config.Account {\n\t\tuser.Account = Account{Number: name + \"_account\"}\n\t}\n\n\tfor i := 0; i < config.Pets; i++ {\n\t\tuser.Pets = append(user.Pets, &Pet{Name: name + \"_pet_\" + strconv.Itoa(i+1)})\n\t}\n\n\tfor i := 0; i < config.Toys; i++ {\n\t\tuser.Toys = append(user.Toys, Toy{Name: name + \"_toy_\" + strconv.Itoa(i+1)})\n\t}\n\n\tfor i := 0; i < config.Tools; i++ {\n\t\tuser.Tools = append(user.Tools, Tools{Name: name + \"_tool_\" + strconv.Itoa(i+1)})\n\t}\n\n\tif config.Company {\n\t\tuser.Company = Company{Name: \"company-\" + name}\n\t}\n\n\tif config.Manager {\n\t\tuser.Manager = GetUser(name+\"_manager\", Config{})\n\t}\n\n\tfor i := 0; i < config.Team; i++ {\n\t\tuser.Team = append(user.Team, *GetUser(name+\"_team_\"+strconv.Itoa(i+1), Config{}))\n\t}\n\n\tfor i := 0; i < config.Languages; i++ {\n\t\tname := name + \"_locale_\" + strconv.Itoa(i+1)\n\t\tlanguage := Language{Code: name, Name: name}\n\t\tuser.Languages = append(user.Languages, language)\n\t}\n\n\tfor i := 0; i < config.Friends; i++ {\n\t\tuser.Friends = append(user.Friends, GetUser(name+\"_friend_\"+strconv.Itoa(i+1), Config{}))\n\t}\n\n\tif config.NamedPet {\n\t\tuser.NamedPet = &Pet{Name: name + \"_namepet\"}\n\t}\n\n\treturn &user\n}\n\nfunc CheckPetUnscoped(t *testing.T, pet Pet, expect Pet) {\n\tdoCheckPet(t, pet, expect, true)\n}\n\nfunc CheckPet(t *testing.T, pet Pet, expect Pet) {\n\tdoCheckPet(t, pet, expect, false)\n}\n\nfunc doCheckPet(t *testing.T, pet Pet, expect Pet, unscoped bool) {\n\tif pet.ID != 0 {\n\t\tvar newPet Pet\n\t\tif err := db(unscoped).Where(\"id = ?\", pet.ID).First(&newPet).Error; err != nil {\n\t\t\tt.Fatalf(\"errors happened when query: %v\", err)\n\t\t} else {\n\t\t\tAssertObjEqual(t, newPet, pet, \"ID\", \"CreatedAt\", \"UpdatedAt\", \"DeletedAt\", \"UserID\", \"Name\")\n\t\t\tAssertObjEqual(t, newPet, expect, \"ID\", \"CreatedAt\", \"UpdatedAt\", \"DeletedAt\", \"UserID\", \"Name\")\n\t\t}\n\t}\n\n\tAssertObjEqual(t, pet, expect, \"ID\", \"CreatedAt\", \"UpdatedAt\", \"DeletedAt\", \"UserID\", \"Name\")\n\n\tAssertObjEqual(t, pet.Toy, expect.Toy, \"ID\", \"CreatedAt\", \"UpdatedAt\", \"DeletedAt\", \"Name\", \"OwnerID\", \"OwnerType\")\n\n\tif expect.Toy.Name != \"\" && expect.Toy.OwnerType != \"pets\" {\n\t\tt.Errorf(\"toys's OwnerType, expect: %v, got %v\", \"pets\", expect.Toy.OwnerType)\n\t}\n}\n\nfunc CheckUserUnscoped(t *testing.T, user User, expect User) {\n\tdoCheckUser(t, user, expect, true)\n}\n\nfunc CheckUser(t *testing.T, user User, expect User) {\n\tdoCheckUser(t, user, expect, false)\n}\n\nfunc doCheckUser(t *testing.T, user User, expect User, unscoped bool) {\n\tif user.ID != 0 {\n\t\tvar newUser User\n\t\tif err := db(unscoped).Where(\"id = ?\", user.ID).First(&newUser).Error; err != nil {\n\t\t\tt.Fatalf(\"errors happened when query: %v\", err)\n\t\t} else {\n\t\t\tAssertObjEqual(t, newUser, user, \"ID\", \"CreatedAt\", \"UpdatedAt\", \"DeletedAt\", \"Name\", \"Age\", \"Birthday\",\n\t\t\t\t\"CompanyID\", \"ManagerID\", \"Active\")\n\t\t}\n\t}\n\n\tAssertObjEqual(t, user, expect, \"ID\", \"CreatedAt\", \"UpdatedAt\", \"DeletedAt\", \"Name\", \"Age\", \"Birthday\", \"CompanyID\",\n\t\t\"ManagerID\", \"Active\")\n\n\tt.Run(\"Account\", func(t *testing.T) {\n\t\tAssertObjEqual(t, user.Account, expect.Account, \"ID\", \"CreatedAt\", \"UpdatedAt\", \"DeletedAt\", \"UserID\", \"Number\")\n\n\t\tif user.Account.Number != \"\" {\n\t\t\tif !user.Account.UserID.Valid {\n\t\t\t\tt.Errorf(\"Account's foreign key should be saved\")\n\t\t\t} else {\n\t\t\t\tvar account Account\n\t\t\t\tdb(unscoped).First(&account, \"user_id = ?\", user.ID)\n\t\t\t\tAssertObjEqual(t, account, user.Account, \"ID\", \"CreatedAt\", \"UpdatedAt\", \"DeletedAt\", \"UserID\",\n\t\t\t\t\t\"Number\")\n\t\t\t}\n\t\t}\n\t})\n\n\tt.Run(\"Pets\", func(t *testing.T) {\n\t\tif len(user.Pets) != len(expect.Pets) {\n\t\t\tt.Fatalf(\"pets should equal, expect: %v, got %v\", len(expect.Pets), len(user.Pets))\n\t\t}\n\n\t\tsort.Slice(user.Pets, func(i, j int) bool {\n\t\t\treturn user.Pets[i].ID > user.Pets[j].ID\n\t\t})\n\n\t\tsort.Slice(expect.Pets, func(i, j int) bool {\n\t\t\treturn expect.Pets[i].ID > expect.Pets[j].ID\n\t\t})\n\n\t\tfor idx, pet := range user.Pets {\n\t\t\tif pet == nil || expect.Pets[idx] == nil {\n\t\t\t\tt.Errorf(\"pets#%v should equal, expect: %v, got %v\", idx, expect.Pets[idx], pet)\n\t\t\t} else {\n\t\t\t\tdoCheckPet(t, *pet, *expect.Pets[idx], unscoped)\n\t\t\t}\n\t\t}\n\t})\n\n\tt.Run(\"Toys\", func(t *testing.T) {\n\t\tif len(user.Toys) != len(expect.Toys) {\n\t\t\tt.Fatalf(\"toys should equal, expect: %v, got %v\", len(expect.Toys), len(user.Toys))\n\t\t}\n\n\t\tsort.Slice(user.Toys, func(i, j int) bool {\n\t\t\treturn user.Toys[i].ID > user.Toys[j].ID\n\t\t})\n\n\t\tsort.Slice(expect.Toys, func(i, j int) bool {\n\t\t\treturn expect.Toys[i].ID > expect.Toys[j].ID\n\t\t})\n\n\t\tfor idx, toy := range user.Toys {\n\t\t\tif toy.OwnerType != \"users\" {\n\t\t\t\tt.Errorf(\"toys's OwnerType, expect: %v, got %v\", \"users\", toy.OwnerType)\n\t\t\t}\n\n\t\t\tAssertObjEqual(t, toy, expect.Toys[idx], \"ID\", \"CreatedAt\", \"UpdatedAt\", \"Name\", \"OwnerID\", \"OwnerType\")\n\t\t}\n\t})\n\n\tt.Run(\"Company\", func(t *testing.T) {\n\t\tAssertObjEqual(t, user.Company, expect.Company, \"ID\", \"Name\")\n\t})\n\n\tt.Run(\"Manager\", func(t *testing.T) {\n\t\tif user.Manager != nil {\n\t\t\tif user.ManagerID == nil {\n\t\t\t\tt.Errorf(\"Manager's foreign key should be saved\")\n\t\t\t} else {\n\t\t\t\tvar manager User\n\t\t\t\tdb(unscoped).First(&manager, \"id = ?\", *user.ManagerID)\n\t\t\t\tAssertObjEqual(t, manager, user.Manager, \"ID\", \"CreatedAt\", \"UpdatedAt\", \"DeletedAt\", \"Name\", \"Age\",\n\t\t\t\t\t\"Birthday\", \"CompanyID\", \"ManagerID\", \"Active\")\n\t\t\t\tAssertObjEqual(t, manager, expect.Manager, \"ID\", \"CreatedAt\", \"UpdatedAt\", \"DeletedAt\", \"Name\", \"Age\",\n\t\t\t\t\t\"Birthday\", \"CompanyID\", \"ManagerID\", \"Active\")\n\t\t\t}\n\t\t} else if user.ManagerID != nil {\n\t\t\tt.Errorf(\"Manager should not be created for zero value, got: %+v\", user.ManagerID)\n\t\t}\n\t})\n\n\tt.Run(\"Team\", func(t *testing.T) {\n\t\tif len(user.Team) != len(expect.Team) {\n\t\t\tt.Fatalf(\"Team should equal, expect: %v, got %v\", len(expect.Team), len(user.Team))\n\t\t}\n\n\t\tsort.Slice(user.Team, func(i, j int) bool {\n\t\t\treturn user.Team[i].ID > user.Team[j].ID\n\t\t})\n\n\t\tsort.Slice(expect.Team, func(i, j int) bool {\n\t\t\treturn expect.Team[i].ID > expect.Team[j].ID\n\t\t})\n\n\t\tfor idx, team := range user.Team {\n\t\t\tAssertObjEqual(t, team, expect.Team[idx], \"ID\", \"CreatedAt\", \"UpdatedAt\", \"DeletedAt\", \"Name\", \"Age\",\n\t\t\t\t\"Birthday\", \"CompanyID\", \"ManagerID\", \"Active\")\n\t\t}\n\t})\n\n\tt.Run(\"Languages\", func(t *testing.T) {\n\t\tif len(user.Languages) != len(expect.Languages) {\n\t\t\tt.Fatalf(\"Languages should equal, expect: %v, got %v\", len(expect.Languages), len(user.Languages))\n\t\t}\n\n\t\tsort.Slice(user.Languages, func(i, j int) bool {\n\t\t\treturn strings.Compare(user.Languages[i].Code, user.Languages[j].Code) > 0\n\t\t})\n\n\t\tsort.Slice(expect.Languages, func(i, j int) bool {\n\t\t\treturn strings.Compare(expect.Languages[i].Code, expect.Languages[j].Code) > 0\n\t\t})\n\t\tfor idx, language := range user.Languages {\n\t\t\tAssertObjEqual(t, language, expect.Languages[idx], \"Code\", \"Name\")\n\t\t}\n\t})\n\n\tt.Run(\"Friends\", func(t *testing.T) {\n\t\tif len(user.Friends) != len(expect.Friends) {\n\t\t\tt.Fatalf(\"Friends should equal, expect: %v, got %v\", len(expect.Friends), len(user.Friends))\n\t\t}\n\n\t\tsort.Slice(user.Friends, func(i, j int) bool {\n\t\t\treturn user.Friends[i].ID > user.Friends[j].ID\n\t\t})\n\n\t\tsort.Slice(expect.Friends, func(i, j int) bool {\n\t\t\treturn expect.Friends[i].ID > expect.Friends[j].ID\n\t\t})\n\n\t\tfor idx, friend := range user.Friends {\n\t\t\tAssertObjEqual(t, friend, expect.Friends[idx], \"ID\", \"CreatedAt\", \"UpdatedAt\", \"DeletedAt\", \"Name\", \"Age\",\n\t\t\t\t\"Birthday\", \"CompanyID\", \"ManagerID\", \"Active\")\n\t\t}\n\t})\n}\n\nfunc tidbSkip(t *testing.T, reason string) {\n\tif isTiDB() {\n\t\tt.Skipf(\"This test case skipped, because of TiDB '%s'\", reason)\n\t}\n}\n\nfunc isTiDB() bool {\n\treturn os.Getenv(\"GORM_DIALECT\") == \"tidb\"\n}\n\nfunc isMysql() bool {\n\treturn os.Getenv(\"GORM_DIALECT\") == \"mysql\"\n}\n\nfunc isSqlite() bool {\n\treturn os.Getenv(\"GORM_DIALECT\") == \"sqlite\"\n}\n\nfunc db(unscoped bool) *gorm.DB {\n\tif unscoped {\n\t\treturn DB.Unscoped()\n\t} else {\n\t\treturn DB\n\t}\n}\n"
  },
  {
    "path": "tests/hooks_test.go",
    "content": "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/utils/tests\"\n)\n\ntype Product struct {\n\tgorm.Model\n\tName                  string\n\tCode                  string\n\tPrice                 float64\n\tAfterFindCallTimes    int64\n\tBeforeCreateCallTimes int64\n\tAfterCreateCallTimes  int64\n\tBeforeUpdateCallTimes int64\n\tAfterUpdateCallTimes  int64\n\tBeforeSaveCallTimes   int64\n\tAfterSaveCallTimes    int64\n\tBeforeDeleteCallTimes int64\n\tAfterDeleteCallTimes  int64\n}\n\nfunc (s *Product) BeforeCreate(tx *gorm.DB) (err error) {\n\tif s.Code == \"Invalid\" {\n\t\terr = errors.New(\"invalid product\")\n\t}\n\ts.BeforeCreateCallTimes = s.BeforeCreateCallTimes + 1\n\treturn\n}\n\nfunc (s *Product) BeforeUpdate(tx *gorm.DB) (err error) {\n\tif s.Code == \"dont_update\" {\n\t\terr = errors.New(\"can't update\")\n\t}\n\ts.BeforeUpdateCallTimes = s.BeforeUpdateCallTimes + 1\n\treturn\n}\n\nfunc (s *Product) BeforeSave(tx *gorm.DB) (err error) {\n\tif s.Code == \"dont_save\" {\n\t\terr = errors.New(\"can't save\")\n\t}\n\ts.BeforeSaveCallTimes = s.BeforeSaveCallTimes + 1\n\treturn\n}\n\nfunc (s *Product) AfterFind(tx *gorm.DB) (err error) {\n\ts.AfterFindCallTimes = s.AfterFindCallTimes + 1\n\treturn\n}\n\nfunc (s *Product) AfterCreate(tx *gorm.DB) (err error) {\n\treturn tx.Model(s).UpdateColumn(\"AfterCreateCallTimes\", s.AfterCreateCallTimes+1).Error\n}\n\nfunc (s *Product) AfterUpdate(tx *gorm.DB) (err error) {\n\ts.AfterUpdateCallTimes = s.AfterUpdateCallTimes + 1\n\treturn\n}\n\nfunc (s *Product) AfterSave(tx *gorm.DB) (err error) {\n\tif s.Code == \"after_save_error\" {\n\t\terr = errors.New(\"can't save\")\n\t}\n\ts.AfterSaveCallTimes = s.AfterSaveCallTimes + 1\n\treturn\n}\n\nfunc (s *Product) BeforeDelete(tx *gorm.DB) (err error) {\n\tif s.Code == \"dont_delete\" {\n\t\terr = errors.New(\"can't delete\")\n\t}\n\ts.BeforeDeleteCallTimes = s.BeforeDeleteCallTimes + 1\n\treturn\n}\n\nfunc (s *Product) AfterDelete(tx *gorm.DB) (err error) {\n\tif s.Code == \"after_delete_error\" {\n\t\terr = errors.New(\"can't delete\")\n\t}\n\ts.AfterDeleteCallTimes = s.AfterDeleteCallTimes + 1\n\treturn\n}\n\nfunc (s *Product) GetCallTimes() []int64 {\n\treturn []int64{s.BeforeCreateCallTimes, s.BeforeSaveCallTimes, s.BeforeUpdateCallTimes, s.AfterCreateCallTimes, s.AfterSaveCallTimes, s.AfterUpdateCallTimes, s.BeforeDeleteCallTimes, s.AfterDeleteCallTimes, s.AfterFindCallTimes}\n}\n\nfunc TestRunCallbacks(t *testing.T) {\n\tDB.Migrator().DropTable(&Product{})\n\tDB.AutoMigrate(&Product{})\n\n\tp := Product{Code: \"unique_code\", Price: 100}\n\tDB.Save(&p)\n\n\tif !reflect.DeepEqual(p.GetCallTimes(), []int64{1, 1, 0, 1, 1, 0, 0, 0, 0}) {\n\t\tt.Fatalf(\"Callbacks should be invoked successfully, %v\", p.GetCallTimes())\n\t}\n\n\tDB.Where(\"Code = ?\", \"unique_code\").First(&p)\n\tif !reflect.DeepEqual(p.GetCallTimes(), []int64{1, 1, 0, 1, 0, 0, 0, 0, 1}) {\n\t\tt.Fatalf(\"After callbacks values are not saved, %v\", p.GetCallTimes())\n\t}\n\n\tp.Price = 200\n\tDB.Save(&p)\n\tif !reflect.DeepEqual(p.GetCallTimes(), []int64{1, 2, 1, 1, 1, 1, 0, 0, 1}) {\n\t\tt.Fatalf(\"After update callbacks should be invoked successfully, %v\", p.GetCallTimes())\n\t}\n\n\tvar products []Product\n\tDB.Find(&products, \"code = ?\", \"unique_code\")\n\tif products[0].AfterFindCallTimes != 2 {\n\t\tt.Fatalf(\"AfterFind callbacks should work with slice, called %v\", products[0].AfterFindCallTimes)\n\t}\n\n\tDB.Where(\"Code = ?\", \"unique_code\").First(&p)\n\tif !reflect.DeepEqual(p.GetCallTimes(), []int64{1, 2, 1, 1, 0, 0, 0, 0, 2}) {\n\t\tt.Fatalf(\"After update callbacks values are not saved, %v\", p.GetCallTimes())\n\t}\n\n\tDB.Delete(&p)\n\tif !reflect.DeepEqual(p.GetCallTimes(), []int64{1, 2, 1, 1, 0, 0, 1, 1, 2}) {\n\t\tt.Fatalf(\"After delete callbacks should be invoked successfully, %v\", p.GetCallTimes())\n\t}\n\n\tif DB.Where(\"Code = ?\", \"unique_code\").First(&p).Error == nil {\n\t\tt.Fatalf(\"Can't find a deleted record\")\n\t}\n\n\tbeforeCallTimes := p.AfterFindCallTimes\n\tif DB.Where(\"Code = ?\", \"unique_code\").Find(&p).Error != nil {\n\t\tt.Fatalf(\"Find don't raise error when record not found\")\n\t}\n\n\tif p.AfterFindCallTimes != beforeCallTimes {\n\t\tt.Fatalf(\"AfterFind should not be called\")\n\t}\n}\n\nfunc TestCallbacksWithErrors(t *testing.T) {\n\tDB.Migrator().DropTable(&Product{})\n\tDB.AutoMigrate(&Product{})\n\n\tp := Product{Code: \"Invalid\", Price: 100}\n\tif DB.Save(&p).Error == nil {\n\t\tt.Fatalf(\"An error from before create callbacks happened when create with invalid value\")\n\t}\n\n\tif DB.Where(\"code = ?\", \"Invalid\").First(&Product{}).Error == nil {\n\t\tt.Fatalf(\"Should not save record that have errors\")\n\t}\n\n\tif DB.Save(&Product{Code: \"dont_save\", Price: 100}).Error == nil {\n\t\tt.Fatalf(\"An error from after create callbacks happened when create with invalid value\")\n\t}\n\n\tp2 := Product{Code: \"update_callback\", Price: 100}\n\tDB.Save(&p2)\n\n\tp2.Code = \"dont_update\"\n\tif DB.Save(&p2).Error == nil {\n\t\tt.Fatalf(\"An error from before update callbacks happened when update with invalid value\")\n\t}\n\n\tif DB.Where(\"code = ?\", \"update_callback\").First(&Product{}).Error != nil {\n\t\tt.Fatalf(\"Record Should not be updated due to errors happened in before update callback\")\n\t}\n\n\tif DB.Where(\"code = ?\", \"dont_update\").First(&Product{}).Error == nil {\n\t\tt.Fatalf(\"Record Should not be updated due to errors happened in before update callback\")\n\t}\n\n\tp2.Code = \"dont_save\"\n\tif DB.Save(&p2).Error == nil {\n\t\tt.Fatalf(\"An error from before save callbacks happened when update with invalid value\")\n\t}\n\n\tp3 := Product{Code: \"dont_delete\", Price: 100}\n\tDB.Save(&p3)\n\tif DB.Delete(&p3).Error == nil {\n\t\tt.Fatalf(\"An error from before delete callbacks happened when delete\")\n\t}\n\n\tif DB.Where(\"Code = ?\", \"dont_delete\").First(&p3).Error != nil {\n\t\tt.Fatalf(\"An error from before delete callbacks happened\")\n\t}\n\n\tp4 := Product{Code: \"after_save_error\", Price: 100}\n\tDB.Save(&p4)\n\tif err := DB.First(&Product{}, \"code = ?\", \"after_save_error\").Error; err == nil {\n\t\tt.Fatalf(\"Record should be reverted if get an error in after save callback\")\n\t}\n\n\tp5 := Product{Code: \"after_delete_error\", Price: 100}\n\tDB.Save(&p5)\n\tif err := DB.First(&Product{}, \"code = ?\", \"after_delete_error\").Error; err != nil {\n\t\tt.Fatalf(\"Record should be found\")\n\t}\n\n\tDB.Delete(&p5)\n\tif err := DB.First(&Product{}, \"code = ?\", \"after_delete_error\").Error; err != nil {\n\t\tt.Fatalf(\"Record shouldn't be deleted because of an error happened in after delete callback\")\n\t}\n}\n\ntype Product2 struct {\n\tgorm.Model\n\tName  string\n\tCode  string\n\tPrice int64\n\tOwner string\n}\n\nfunc (s Product2) BeforeCreate(tx *gorm.DB) (err error) {\n\tif !strings.HasSuffix(s.Name, \"_clone\") {\n\t\tnewProduft := s\n\t\tnewProduft.Price *= 2\n\t\tnewProduft.Name += \"_clone\"\n\t\terr = tx.Create(&newProduft).Error\n\t}\n\n\tif s.Name == \"Invalid\" {\n\t\treturn errors.New(\"invalid\")\n\t}\n\n\treturn nil\n}\n\nfunc (s *Product2) BeforeUpdate(tx *gorm.DB) (err error) {\n\ttx.Statement.Where(\"owner != ?\", \"admin\")\n\treturn\n}\n\nfunc TestUseDBInHooks(t *testing.T) {\n\tDB.Migrator().DropTable(&Product2{})\n\tDB.AutoMigrate(&Product2{})\n\n\tproduct := Product2{Name: \"Invalid\", Price: 100}\n\n\tif err := DB.Create(&product).Error; err == nil {\n\t\tt.Fatalf(\"should returns error %v when creating product, but got nil\", err)\n\t}\n\n\tproduct2 := Product2{Name: \"Nice\", Price: 100}\n\n\tif err := DB.Create(&product2).Error; err != nil {\n\t\tt.Fatalf(\"Failed to create product, got error: %v\", err)\n\t}\n\n\tvar result Product2\n\tif err := DB.First(&result, \"name = ?\", \"Nice\").Error; err != nil {\n\t\tt.Fatalf(\"Failed to query product, got error: %v\", err)\n\t}\n\n\tvar resultClone Product2\n\tif err := DB.First(&resultClone, \"name = ?\", \"Nice_clone\").Error; err != nil {\n\t\tt.Fatalf(\"Failed to find cloned product, got error: %v\", err)\n\t}\n\n\tresult.Price *= 2\n\tresult.Name += \"_clone\"\n\tAssertObjEqual(t, result, resultClone, \"Price\", \"Name\")\n\n\tDB.Model(&result).Update(\"Price\", 500)\n\tvar result2 Product2\n\tDB.First(&result2, \"name = ?\", \"Nice\")\n\n\tif result2.Price != 500 {\n\t\tt.Errorf(\"Failed to update product's price, expects: %v, got %v\", 500, result2.Price)\n\t}\n\n\tproduct3 := Product2{Name: \"Nice2\", Price: 600, Owner: \"admin\"}\n\tif err := DB.Create(&product3).Error; err != nil {\n\t\tt.Fatalf(\"Failed to create product, got error: %v\", err)\n\t}\n\n\tvar result3 Product2\n\tif err := DB.First(&result3, \"name = ?\", \"Nice2\").Error; err != nil {\n\t\tt.Fatalf(\"Failed to query product, got error: %v\", err)\n\t}\n\n\tDB.Model(&result3).Update(\"Price\", 800)\n\tvar result4 Product2\n\tDB.First(&result4, \"name = ?\", \"Nice2\")\n\n\tif result4.Price != 600 {\n\t\tt.Errorf(\"Admin product's price should not be changed, expects: %v, got %v\", 600, result4.Price)\n\t}\n}\n\ntype Product3 struct {\n\tgorm.Model\n\tName  string\n\tCode  string\n\tPrice int64\n\tOwner string\n}\n\nfunc (s Product3) BeforeCreate(tx *gorm.DB) (err error) {\n\ttx.Statement.SetColumn(\"Price\", s.Price+100)\n\treturn nil\n}\n\nfunc (s Product3) BeforeUpdate(tx *gorm.DB) (err error) {\n\tif tx.Statement.Changed() {\n\t\ttx.Statement.SetColumn(\"Price\", s.Price+10)\n\t}\n\n\tif tx.Statement.Changed(\"Code\") {\n\t\ts.Price += 20\n\t\ttx.Statement.SetColumn(\"Price\", s.Price+30)\n\t}\n\treturn nil\n}\n\nfunc TestSetColumn(t *testing.T) {\n\tDB.Migrator().DropTable(&Product3{})\n\tDB.AutoMigrate(&Product3{})\n\n\tproduct := Product3{Name: \"Product\", Price: 0}\n\tDB.Create(&product)\n\n\tif product.Price != 100 {\n\t\tt.Errorf(\"invalid price after create, got %+v\", product)\n\t}\n\n\tDB.Model(&product).Select(\"code\", \"price\").Updates(map[string]interface{}{\"code\": \"L1212\"})\n\n\tif product.Price != 150 || product.Code != \"L1212\" {\n\t\tt.Errorf(\"invalid data after update, got %+v\", product)\n\t}\n\n\t// Code not changed, price should not change\n\tDB.Model(&product).Updates(map[string]interface{}{\"Name\": \"Product New\"})\n\n\tif product.Name != \"Product New\" || product.Price != 160 || product.Code != \"L1212\" {\n\t\tt.Errorf(\"invalid data after update, got %+v\", product)\n\t}\n\n\t// Code changed, but not selected, price should not change\n\tDB.Model(&product).Select(\"Name\", \"Price\").Updates(map[string]interface{}{\"Name\": \"Product New2\", \"code\": \"L1213\"})\n\n\tif product.Name != \"Product New2\" || product.Price != 170 || product.Code != \"L1212\" {\n\t\tt.Errorf(\"invalid data after update, got %+v\", product)\n\t}\n\n\t// Code changed, price should changed\n\tDB.Model(&product).Select(\"Name\", \"Code\", \"Price\").Updates(map[string]interface{}{\"Name\": \"Product New3\", \"code\": \"L1213\"})\n\n\tif product.Name != \"Product New3\" || product.Price != 220 || product.Code != \"L1213\" {\n\t\tt.Errorf(\"invalid data after update, got %+v\", product)\n\t}\n\n\tvar result Product3\n\tDB.First(&result, product.ID)\n\n\tAssertEqual(t, result, product)\n\n\t// Select to change Code, but nothing updated, price should not change\n\tDB.Model(&product).Select(\"code\").Updates(Product3{Name: \"L1214\", Code: \"L1213\"})\n\n\tif product.Price != 220 || product.Code != \"L1213\" || product.Name != \"Product New3\" {\n\t\tt.Errorf(\"invalid data after update, got %+v\", product)\n\t}\n\n\tDB.Model(&product).Updates(Product3{Code: \"L1214\"})\n\tif product.Price != 270 || product.Code != \"L1214\" {\n\t\tt.Errorf(\"invalid data after update, got %+v\", product)\n\t}\n\n\t// Code changed, price should changed\n\tDB.Model(&product).Select(\"Name\", \"Code\", \"Price\").Updates(Product3{Name: \"Product New4\", Code: \"\"})\n\tif product.Name != \"Product New4\" || product.Price != 320 || product.Code != \"\" {\n\t\tt.Errorf(\"invalid data after update, got %+v\", product)\n\t}\n\n\tDB.Model(&product).UpdateColumns(Product3{Code: \"L1215\"})\n\tif product.Price != 320 || product.Code != \"L1215\" {\n\t\tt.Errorf(\"invalid data after update, got %+v\", product)\n\t}\n\n\tDB.Model(&product).Session(&gorm.Session{SkipHooks: true}).Updates(Product3{Code: \"L1216\"})\n\tif product.Price != 320 || product.Code != \"L1216\" {\n\t\tt.Errorf(\"invalid data after update, got %+v\", product)\n\t}\n\n\tvar result2 Product3\n\tDB.First(&result2, product.ID)\n\n\tAssertEqual(t, result2, product)\n\n\tproduct2 := Product3{Name: \"Product\", Price: 0}\n\tDB.Session(&gorm.Session{SkipHooks: true}).Create(&product2)\n\n\tif product2.Price != 0 {\n\t\tt.Errorf(\"invalid price after create without hooks, got %+v\", product2)\n\t}\n}\n\nfunc TestHooksForSlice(t *testing.T) {\n\tDB.Migrator().DropTable(&Product3{})\n\tDB.AutoMigrate(&Product3{})\n\n\tproducts := []*Product3{\n\t\t{Name: \"Product-1\", Price: 100},\n\t\t{Name: \"Product-2\", Price: 200},\n\t\t{Name: \"Product-3\", Price: 300},\n\t}\n\n\tDB.Create(&products)\n\n\tfor idx, value := range []int64{200, 300, 400} {\n\t\tif products[idx].Price != value {\n\t\t\tt.Errorf(\"invalid price for product #%v, expects: %v, got %v\", idx, value, products[idx].Price)\n\t\t}\n\t}\n\n\tDB.Model(&products).Update(\"Name\", \"product-name\")\n\n\t// will set all product's price to last product's price + 10\n\tfor idx, value := range []int64{410, 410, 410} {\n\t\tif products[idx].Price != value {\n\t\t\tt.Errorf(\"invalid price for product #%v, expects: %v, got %v\", idx, value, products[idx].Price)\n\t\t}\n\t}\n\n\tproducts2 := []Product3{\n\t\t{Name: \"Product-1\", Price: 100},\n\t\t{Name: \"Product-2\", Price: 200},\n\t\t{Name: \"Product-3\", Price: 300},\n\t}\n\n\tDB.Create(&products2)\n\n\tfor idx, value := range []int64{200, 300, 400} {\n\t\tif products2[idx].Price != value {\n\t\t\tt.Errorf(\"invalid price for product #%v, expects: %v, got %v\", idx, value, products2[idx].Price)\n\t\t}\n\t}\n\n\tDB.Model(&products2).Update(\"Name\", \"product-name\")\n\n\t// will set all product's price to last product's price + 10\n\tfor idx, value := range []int64{410, 410, 410} {\n\t\tif products2[idx].Price != value {\n\t\t\tt.Errorf(\"invalid price for product #%v, expects: %v, got %v\", idx, value, products2[idx].Price)\n\t\t}\n\t}\n}\n\ntype Product4 struct {\n\tgorm.Model\n\tName  string\n\tCode  string\n\tPrice int64\n\tOwner string\n\tItem  ProductItem\n}\n\ntype ProductItem struct {\n\tgorm.Model\n\tCode               string\n\tProduct4ID         uint\n\tAfterFindCallTimes int\n}\n\nfunc (pi ProductItem) BeforeCreate(*gorm.DB) error {\n\tif pi.Code == \"invalid\" {\n\t\treturn errors.New(\"invalid item\")\n\t}\n\treturn nil\n}\n\nfunc (pi *ProductItem) AfterFind(*gorm.DB) error {\n\tpi.AfterFindCallTimes = pi.AfterFindCallTimes + 1\n\treturn nil\n}\n\nfunc TestFailedToSaveAssociationShouldRollback(t *testing.T) {\n\tDB.Migrator().DropTable(&Product4{}, &ProductItem{})\n\tDB.AutoMigrate(&Product4{}, &ProductItem{})\n\n\tproduct := Product4{Name: \"Product-1\", Price: 100, Item: ProductItem{Code: \"invalid\"}}\n\tif err := DB.Create(&product).Error; err == nil {\n\t\tt.Errorf(\"should got failed to save, but error is nil\")\n\t}\n\n\tif DB.First(&Product4{}, \"name = ?\", product.Name).Error == nil {\n\t\tt.Errorf(\"should got RecordNotFound, but got nil\")\n\t}\n\n\tproduct = Product4{Name: \"Product-2\", Price: 100, Item: ProductItem{Code: \"valid\"}}\n\tif err := DB.Create(&product).Error; err != nil {\n\t\tt.Errorf(\"should create product, but got error %v\", err)\n\t}\n\n\tif err := DB.First(&Product4{}, \"name = ?\", product.Name).Error; err != nil {\n\t\tt.Errorf(\"should find product, but got error %v\", err)\n\t}\n\n\tvar productWithItem Product4\n\tif err := DB.Session(&gorm.Session{SkipHooks: true}).Preload(\"Item\").First(&productWithItem, \"name = ?\", product.Name).Error; err != nil {\n\t\tt.Errorf(\"should find product, but got error %v\", err)\n\t}\n\n\tif productWithItem.Item.AfterFindCallTimes != 0 {\n\t\tt.Fatalf(\"AfterFind should not be called times:%d\", productWithItem.Item.AfterFindCallTimes)\n\t}\n}\n\ntype Product5 struct {\n\tgorm.Model\n\tName string\n}\n\nvar beforeUpdateCall int\n\nfunc (p *Product5) BeforeUpdate(*gorm.DB) error {\n\tbeforeUpdateCall = beforeUpdateCall + 1\n\treturn nil\n}\n\nfunc TestUpdateCallbacks(t *testing.T) {\n\tDB.Migrator().DropTable(&Product5{})\n\tDB.AutoMigrate(&Product5{})\n\n\tp := Product5{Name: \"unique_code\"}\n\tDB.Model(&Product5{}).Create(&p)\n\n\terr := DB.Model(&Product5{}).Where(\"id\", p.ID).Update(\"name\", \"update_name_1\").Error\n\tif err != nil {\n\t\tt.Fatalf(\"should update success, but got err %v\", err)\n\t}\n\tif beforeUpdateCall != 1 {\n\t\tt.Fatalf(\"before update should be called\")\n\t}\n\n\terr = DB.Model(Product5{}).Where(\"id\", p.ID).Update(\"name\", \"update_name_2\").Error\n\tif !errors.Is(err, gorm.ErrInvalidValue) {\n\t\tt.Fatalf(\"should got RecordNotFound, but got %v\", err)\n\t}\n\tif beforeUpdateCall != 1 {\n\t\tt.Fatalf(\"before update should not be called\")\n\t}\n\n\terr = DB.Model([1]*Product5{&p}).Update(\"name\", \"update_name_3\").Error\n\tif err != nil {\n\t\tt.Fatalf(\"should update success, but got err %v\", err)\n\t}\n\tif beforeUpdateCall != 2 {\n\t\tt.Fatalf(\"before update should be called\")\n\t}\n\n\terr = DB.Model([1]Product5{p}).Update(\"name\", \"update_name_4\").Error\n\tif !errors.Is(err, gorm.ErrInvalidValue) {\n\t\tt.Fatalf(\"should got RecordNotFound, but got %v\", err)\n\t}\n\tif beforeUpdateCall != 2 {\n\t\tt.Fatalf(\"before update should not be called\")\n\t}\n}\n\ntype Product6 struct {\n\tgorm.Model\n\tName string\n\tItem *ProductItem2\n}\n\ntype ProductItem2 struct {\n\tgorm.Model\n\tProduct6ID uint\n}\n\nfunc (p *Product6) BeforeDelete(tx *gorm.DB) error {\n\tif err := tx.Delete(&p.Item).Error; err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc TestPropagateUnscoped(t *testing.T) {\n\t_DB, err := OpenTestConnection(&gorm.Config{\n\t\tPropagateUnscoped: true,\n\t})\n\tif err != nil {\n\t\tlog.Printf(\"failed to connect database, got error %v\", err)\n\t\tos.Exit(1)\n\t}\n\n\t_DB.Migrator().DropTable(&Product6{}, &ProductItem2{})\n\t_DB.AutoMigrate(&Product6{}, &ProductItem2{})\n\n\tp := Product6{\n\t\tName: \"unique_code\",\n\t\tItem: &ProductItem2{},\n\t}\n\t_DB.Model(&Product6{}).Create(&p)\n\n\tif err := _DB.Unscoped().Delete(&p).Error; err != nil {\n\t\tt.Fatalf(\"unscoped did not propagate\")\n\t}\n}\n"
  },
  {
    "path": "tests/joins_table_test.go",
    "content": "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        int\n\tName      string\n\tAddresses []Address `gorm:\"many2many:person_addresses;\"`\n\tDeletedAt gorm.DeletedAt\n}\n\ntype Address struct {\n\tID   uint\n\tName string\n}\n\ntype PersonAddress struct {\n\tPersonID  int\n\tAddressID int\n\tCreatedAt time.Time\n\tDeletedAt gorm.DeletedAt\n}\n\nfunc TestOverrideJoinTable(t *testing.T) {\n\tDB.Migrator().DropTable(&Person{}, &Address{}, &PersonAddress{})\n\n\tif err := DB.SetupJoinTable(&Person{}, \"Addresses\", &PersonAddress{}); err != nil {\n\t\tt.Fatalf(\"Failed to setup join table for person, got error %v\", err)\n\t}\n\n\tif err := DB.AutoMigrate(&Person{}, &Address{}); err != nil {\n\t\tt.Fatalf(\"Failed to migrate, got %v\", err)\n\t}\n\n\taddress1 := Address{Name: \"address 1\"}\n\taddress2 := Address{Name: \"address 2\"}\n\tperson := Person{Name: \"person\", Addresses: []Address{address1, address2}}\n\tDB.Create(&person)\n\n\tvar addresses1 []Address\n\tif err := DB.Model(&person).Association(\"Addresses\").Find(&addresses1); err != nil || len(addresses1) != 2 {\n\t\tt.Fatalf(\"Failed to find address, got error %v, length: %v\", err, len(addresses1))\n\t}\n\n\tif err := DB.Model(&person).Association(\"Addresses\").Delete(&person.Addresses[0]); err != nil {\n\t\tt.Fatalf(\"Failed to delete address, got error %v\", err)\n\t}\n\n\tif len(person.Addresses) != 1 {\n\t\tt.Fatalf(\"Should have one address left\")\n\t}\n\n\tif DB.Find(&[]PersonAddress{}, \"person_id = ?\", person.ID).RowsAffected != 1 {\n\t\tt.Fatalf(\"Should found one address\")\n\t}\n\n\tvar addresses2 []Address\n\tif err := DB.Model(&person).Association(\"Addresses\").Find(&addresses2); err != nil || len(addresses2) != 1 {\n\t\tt.Fatalf(\"Failed to find address, got error %v, length: %v\", err, len(addresses2))\n\t}\n\n\tif DB.Model(&person).Association(\"Addresses\").Count() != 1 {\n\t\tt.Fatalf(\"Should found one address\")\n\t}\n\n\tvar addresses3 []Address\n\tif err := DB.Unscoped().Model(&person).Association(\"Addresses\").Find(&addresses3); err != nil || len(addresses3) != 2 {\n\t\tt.Fatalf(\"Failed to find address, got error %v, length: %v\", err, len(addresses3))\n\t}\n\n\tif DB.Unscoped().Find(&[]PersonAddress{}, \"person_id = ?\", person.ID).RowsAffected != 2 {\n\t\tt.Fatalf(\"Should found soft deleted addresses with unscoped\")\n\t}\n\n\tif DB.Unscoped().Model(&person).Association(\"Addresses\").Count() != 2 {\n\t\tt.Fatalf(\"Should found soft deleted addresses with unscoped\")\n\t}\n\n\tDB.Model(&person).Association(\"Addresses\").Clear()\n\n\tif DB.Model(&person).Association(\"Addresses\").Count() != 0 {\n\t\tt.Fatalf(\"Should deleted all addresses\")\n\t}\n\n\tif DB.Unscoped().Model(&person).Association(\"Addresses\").Count() != 2 {\n\t\tt.Fatalf(\"Should found soft deleted addresses with unscoped\")\n\t}\n\n\tDB.Unscoped().Model(&person).Association(\"Addresses\").Clear()\n\n\tif DB.Unscoped().Model(&person).Association(\"Addresses\").Count() != 0 {\n\t\tt.Fatalf(\"address should be deleted when clear with unscoped\")\n\t}\n\n\taddress2_1 := Address{Name: \"address 2-1\"}\n\taddress2_2 := Address{Name: \"address 2-2\"}\n\tperson2 := Person{Name: \"person_2\", Addresses: []Address{address2_1, address2_2}}\n\tDB.Create(&person2)\n\tif err := DB.Select(clause.Associations).Delete(&person2).Error; err != nil {\n\t\tt.Fatalf(\"failed to delete person, got error: %v\", err)\n\t}\n\n\tif count := DB.Unscoped().Model(&person2).Association(\"Addresses\").Count(); count != 2 {\n\t\tt.Errorf(\"person's addresses expects 2, got %v\", count)\n\t}\n\n\tif count := DB.Model(&person2).Association(\"Addresses\").Count(); count != 0 {\n\t\tt.Errorf(\"person's addresses expects 2, got %v\", count)\n\t}\n}\n"
  },
  {
    "path": "tests/joins_test.go",
    "content": "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\t. \"gorm.io/gorm/utils/tests\"\n)\n\nfunc TestJoins(t *testing.T) {\n\tuser := *GetUser(\"joins-1\", Config{Company: true, Manager: true, Account: true, NamedPet: false})\n\n\tDB.Create(&user)\n\n\tvar user2 User\n\tif err := DB.Joins(\"NamedPet\").Joins(\"Company\").Joins(\"Manager\").Joins(\"Account\").First(&user2, \"users.name = ?\", user.Name).Error; err != nil {\n\t\tt.Fatalf(\"Failed to load with joins, got error: %v\", err)\n\t}\n\n\tCheckUser(t, user2, user)\n}\n\nfunc TestJoinsForSlice(t *testing.T) {\n\tusers := []User{\n\t\t*GetUser(\"slice-joins-1\", Config{Company: true, Manager: true, Account: true}),\n\t\t*GetUser(\"slice-joins-2\", Config{Company: true, Manager: true, Account: true}),\n\t\t*GetUser(\"slice-joins-3\", Config{Company: true, Manager: true, Account: true}),\n\t}\n\n\tDB.Create(&users)\n\n\tvar userIDs []uint\n\tfor _, user := range users {\n\t\tuserIDs = append(userIDs, user.ID)\n\t}\n\n\tvar users2 []User\n\tif err := DB.Joins(\"Company\").Joins(\"Manager\").Joins(\"Account\").Find(&users2, \"users.id IN ?\", userIDs).Error; err != nil {\n\t\tt.Fatalf(\"Failed to load with joins, got error: %v\", err)\n\t} else if len(users2) != len(users) {\n\t\tt.Fatalf(\"Failed to load join users, got: %v, expect: %v\", len(users2), len(users))\n\t}\n\n\tsort.Slice(users2, func(i, j int) bool {\n\t\treturn users2[i].ID > users2[j].ID\n\t})\n\n\tsort.Slice(users, func(i, j int) bool {\n\t\treturn users[i].ID > users[j].ID\n\t})\n\n\tfor idx, user := range users {\n\t\tCheckUser(t, user, users2[idx])\n\t}\n}\n\nfunc TestJoinConds(t *testing.T) {\n\tuser := *GetUser(\"joins-conds\", Config{Account: true, Pets: 3})\n\tDB.Save(&user)\n\n\tvar users1 []User\n\tDB.Joins(\"inner join pets on pets.user_id = users.id\").Where(\"users.name = ?\", user.Name).Find(&users1)\n\tif len(users1) != 3 {\n\t\tt.Errorf(\"should find two users using left join, but got %v\", len(users1))\n\t}\n\n\tvar users2 []User\n\tDB.Joins(\"inner join pets on pets.user_id = users.id AND pets.name = ?\", user.Pets[0].Name).Where(\"users.name = ?\", user.Name).First(&users2)\n\tif len(users2) != 1 {\n\t\tt.Errorf(\"should find one users using left join with conditions, but got %v\", len(users2))\n\t}\n\n\tvar users3 []User\n\tDB.Joins(\"inner join pets on pets.user_id = users.id AND pets.name = ?\", user.Pets[0].Name).Joins(\"join accounts on accounts.user_id = users.id AND accounts.number = ?\", user.Account.Number).Where(\"users.name = ?\", user.Name).First(&users3)\n\tif len(users3) != 1 {\n\t\tt.Errorf(\"should find one users using multiple left join conditions, but got %v\", len(users3))\n\t}\n\n\tvar users4 []User\n\tDB.Joins(\"inner join pets on pets.user_id = users.id AND pets.name = ?\", user.Pets[0].Name).Joins(\"join accounts on accounts.user_id = users.id AND accounts.number = ?\", user.Account.Number+\"non-exist\").Where(\"users.name = ?\", user.Name).First(&users4)\n\tif len(users4) != 0 {\n\t\tt.Errorf(\"should find no user when searching with unexisting credit card, but got %v\", len(users4))\n\t}\n\n\tvar users5 []User\n\tdb5 := DB.Joins(\"inner join pets on pets.user_id = users.id AND pets.name = ?\", user.Pets[0].Name).Joins(\"join accounts on accounts.user_id = users.id AND accounts.number = ?\", user.Account.Number).Where(User{Model: gorm.Model{ID: 1}}).Where(Account{Model: gorm.Model{ID: 1}}).Not(Pet{Model: gorm.Model{ID: 1}}).Find(&users5)\n\tif db5.Error != nil {\n\t\tt.Errorf(\"Should not raise error for join where identical fields in different tables. Error: %s\", db5.Error.Error())\n\t}\n\n\tvar users6 []User\n\tDB.Joins(\"inner join pets on pets.user_id = users.id AND pets.name = @Name\", user.Pets[0]).Where(\"users.name = ?\", user.Name).First(&users6)\n\tif len(users6) != 1 {\n\t\tt.Errorf(\"should find one users using left join with conditions, but got %v\", len(users6))\n\t}\n\n\tdryDB := DB.Session(&gorm.Session{DryRun: true})\n\tstmt := dryDB.Joins(\"left join pets on pets.user_id = users.id AND pets.name = ?\", user.Pets[0].Name).Joins(\"join accounts on accounts.user_id = users.id AND accounts.number = ?\", user.Account.Number).Where(User{Model: gorm.Model{ID: 1}}).Where(Account{Model: gorm.Model{ID: 1}}).Not(Pet{Model: gorm.Model{ID: 1}}).Find(&users5).Statement\n\n\tif !regexp.MustCompile(\"SELECT .* FROM .users. left join pets.*join accounts.*\").MatchString(stmt.SQL.String()) {\n\t\tt.Errorf(\"joins should be ordered, but got %v\", stmt.SQL.String())\n\t}\n\n\tiv := DB.Table(`table_invoices`).Select(`seller, SUM(total) as total, SUM(paid) as paid, SUM(balance) as balance`).Group(`seller`)\n\tstmt = dryDB.Table(`table_employees`).Select(`id, name, iv.total, iv.paid, iv.balance`).Joins(`LEFT JOIN (?) AS iv ON iv.seller = table_employees.id`, iv).Scan(&user).Statement\n\tif !regexp.MustCompile(\"SELECT id, name, iv.total, iv.paid, iv.balance FROM .table_employees. LEFT JOIN \\\\(SELECT seller, SUM\\\\(total\\\\) as total, SUM\\\\(paid\\\\) as paid, SUM\\\\(balance\\\\) as balance FROM .table_invoices. GROUP BY .seller.\\\\) AS iv ON iv.seller = table_employees.id\").MatchString(stmt.SQL.String()) {\n\t\tt.Errorf(\"joins should be ordered, but got %v\", stmt.SQL.String())\n\t}\n}\n\nfunc TestJoinOn(t *testing.T) {\n\tuser := *GetUser(\"joins-on\", Config{Pets: 2})\n\tDB.Save(&user)\n\n\tvar user1 User\n\tonQuery := DB.Where(&Pet{Name: \"joins-on_pet_1\"})\n\n\tif err := DB.Joins(\"NamedPet\", onQuery).Where(\"users.name = ?\", user.Name).First(&user1).Error; err != nil {\n\t\tt.Fatalf(\"Failed to load with joins on, got error: %v\", err)\n\t}\n\n\tAssertEqual(t, user1.NamedPet.Name, \"joins-on_pet_1\")\n\n\tonQuery2 := DB.Where(&Pet{Name: \"joins-on_pet_2\"})\n\tvar user2 User\n\tif err := DB.Joins(\"NamedPet\", onQuery2).Where(\"users.name = ?\", user.Name).First(&user2).Error; err != nil {\n\t\tt.Fatalf(\"Failed to load with joins on, got error: %v\", err)\n\t}\n\tAssertEqual(t, user2.NamedPet.Name, \"joins-on_pet_2\")\n}\n\nfunc TestJoinsWithSelect(t *testing.T) {\n\ttype result struct {\n\t\tID    uint\n\t\tPetID uint\n\t\tName  string\n\t}\n\n\tuser := *GetUser(\"joins_with_select\", Config{Pets: 2})\n\tDB.Save(&user)\n\n\tvar results []result\n\n\tDB.Table(\"users\").Select(\"users.id, pets.id as pet_id, pets.name\").Joins(\"left join pets on pets.user_id = users.id\").Where(\"users.name = ?\", \"joins_with_select\").Scan(&results)\n\n\tsort.Slice(results, func(i, j int) bool {\n\t\treturn results[i].PetID > results[j].PetID\n\t})\n\n\tsort.Slice(results, func(i, j int) bool {\n\t\treturn user.Pets[i].ID > user.Pets[j].ID\n\t})\n\n\tif len(results) != 2 || results[0].Name != user.Pets[0].Name || results[1].Name != user.Pets[1].Name {\n\t\tt.Errorf(\"Should find all two pets with Join select, got %+v\", results)\n\t}\n}\n\nfunc TestJoinWithOmit(t *testing.T) {\n\tuser := *GetUser(\"joins_with_omit\", Config{Pets: 2})\n\tDB.Save(&user)\n\n\tresults := make([]*User, 0)\n\n\tif err := DB.Table(\"users\").Omit(\"name\").Where(\"users.name = ?\", \"joins_with_omit\").Joins(\"left join pets on pets.user_id = users.id\").Find(&results).Error; err != nil {\n\t\treturn\n\t}\n\n\tif len(results) != 2 || results[0].Name != \"\" || results[1].Name != \"\" {\n\t\tt.Errorf(\"Should find all two pets with Join omit and should not find user's name, got %+v\", results)\n\t\treturn\n\t}\n}\n\nfunc TestJoinCount(t *testing.T) {\n\tcompanyA := Company{Name: \"A\"}\n\tcompanyB := Company{Name: \"B\"}\n\tDB.Create(&companyA)\n\tDB.Create(&companyB)\n\n\tuser := User{Name: \"kingGo\", CompanyID: &companyB.ID}\n\tDB.Create(&user)\n\n\tquery := DB.Model(&User{}).Joins(\"Company\")\n\n\tvar total int64\n\tquery.Count(&total)\n\n\tvar result User\n\n\tif err := query.First(&result, user.ID).Error; err != nil {\n\t\tt.Fatalf(\"Failed, got error: %v\", err)\n\t}\n\n\tif result.ID != user.ID {\n\t\tt.Fatalf(\"result's id, %d, doesn't match user's id, %d\", result.ID, user.ID)\n\t}\n\t// should find company\n\tif result.Company.ID != *user.CompanyID {\n\t\tt.Fatalf(\"result's id, %d, doesn't match user's company id, %d\", result.Company.ID, *user.CompanyID)\n\t}\n}\n\nfunc TestJoinWithSoftDeleted(t *testing.T) {\n\tuser := GetUser(\"TestJoinWithSoftDeletedUser\", Config{Account: true, NamedPet: true})\n\tDB.Create(&user)\n\n\tvar user1 User\n\tDB.Model(&User{}).Joins(\"NamedPet\").Joins(\"Account\").First(&user1, user.ID)\n\tif user1.NamedPet == nil || user1.Account.ID == 0 {\n\t\tt.Fatalf(\"joins NamedPet and Account should not empty:%v\", user1)\n\t}\n\n\t// Account should empty\n\tDB.Delete(&user1.Account)\n\n\tvar user2 User\n\tDB.Model(&User{}).Joins(\"NamedPet\").Joins(\"Account\").First(&user2, user.ID)\n\tif user2.NamedPet == nil || user2.Account.ID != 0 {\n\t\tt.Fatalf(\"joins Account should not empty:%v\", user2)\n\t}\n\n\t// NamedPet should empty\n\tDB.Delete(&user1.NamedPet)\n\n\tvar user3 User\n\tDB.Model(&User{}).Joins(\"NamedPet\").Joins(\"Account\").First(&user3, user.ID)\n\tif user3.NamedPet != nil || user2.Account.ID != 0 {\n\t\tt.Fatalf(\"joins NamedPet and Account should not empty:%v\", user2)\n\t}\n}\n\nfunc TestInnerJoins(t *testing.T) {\n\tuser := *GetUser(\"inner-joins-1\", Config{Company: true, Manager: true, Account: true, NamedPet: false})\n\n\tDB.Create(&user)\n\n\tvar user2 User\n\tvar err error\n\terr = DB.InnerJoins(\"Company\").InnerJoins(\"Manager\").InnerJoins(\"Account\").First(&user2, \"users.name = ?\", user.Name).Error\n\tAssertEqual(t, err, nil)\n\tCheckUser(t, user2, user)\n\n\t// inner join and NamedPet is nil\n\terr = DB.InnerJoins(\"NamedPet\").InnerJoins(\"Company\").InnerJoins(\"Manager\").InnerJoins(\"Account\").First(&user2, \"users.name = ?\", user.Name).Error\n\tAssertEqual(t, err, gorm.ErrRecordNotFound)\n\n\t// mixed inner join and left join\n\tvar user3 User\n\terr = DB.Joins(\"NamedPet\").InnerJoins(\"Company\").InnerJoins(\"Manager\").InnerJoins(\"Account\").First(&user3, \"users.name = ?\", user.Name).Error\n\tAssertEqual(t, err, nil)\n\tCheckUser(t, user3, user)\n}\n\nfunc TestJoinWithSameColumnName(t *testing.T) {\n\tuser := GetUser(\"TestJoinWithSameColumnName\", Config{\n\t\tLanguages: 1,\n\t\tPets:      1,\n\t})\n\tDB.Create(user)\n\ttype UserSpeak struct {\n\t\tUserID       uint\n\t\tLanguageCode string\n\t}\n\ttype Result struct {\n\t\tUser\n\t\tUserSpeak\n\t\tLanguage\n\t\tPet\n\t}\n\n\tresults := make([]Result, 0, 1)\n\tDB.Select(\"users.*, user_speaks.*,  languages.*, pets.*\").Table(\"users\").Joins(\"JOIN user_speaks ON user_speaks.user_id = users.id\").\n\t\tJoins(\"JOIN languages ON languages.code = user_speaks.language_code\").\n\t\tJoins(\"LEFT OUTER JOIN pets ON pets.user_id = users.id\").Find(&results)\n\n\tif len(results) == 0 {\n\t\tt.Fatalf(\"no record find\")\n\t} else if results[0].Pet.UserID == nil || *(results[0].Pet.UserID) != user.ID {\n\t\tt.Fatalf(\"wrong user id in pet\")\n\t} else if results[0].Pet.Name != user.Pets[0].Name {\n\t\tt.Fatalf(\"wrong pet name\")\n\t}\n}\n\nfunc TestJoinArgsWithDB(t *testing.T) {\n\tuser := *GetUser(\"joins-args-db\", Config{Pets: 2})\n\tDB.Save(&user)\n\n\t// test where\n\tvar user1 User\n\tonQuery := DB.Where(&Pet{Name: \"joins-args-db_pet_2\"})\n\tif err := DB.Joins(\"NamedPet\", onQuery).Where(\"users.name = ?\", user.Name).First(&user1).Error; err != nil {\n\t\tt.Fatalf(\"Failed to load with joins on, got error: %v\", err)\n\t}\n\n\tAssertEqual(t, user1.NamedPet.Name, \"joins-args-db_pet_2\")\n\n\t// test where and omit\n\tonQuery2 := DB.Where(&Pet{Name: \"joins-args-db_pet_2\"}).Omit(\"Name\")\n\tvar user2 User\n\tif err := DB.Joins(\"NamedPet\", onQuery2).Where(\"users.name = ?\", user.Name).First(&user2).Error; err != nil {\n\t\tt.Fatalf(\"Failed to load with joins on, got error: %v\", err)\n\t}\n\tAssertEqual(t, user2.NamedPet.ID, user1.NamedPet.ID)\n\tAssertEqual(t, user2.NamedPet.Name, \"\")\n\n\t// test where and select\n\tonQuery3 := DB.Where(&Pet{Name: \"joins-args-db_pet_2\"}).Select(\"Name\")\n\tvar user3 User\n\tif err := DB.Joins(\"NamedPet\", onQuery3).Where(\"users.name = ?\", user.Name).First(&user3).Error; err != nil {\n\t\tt.Fatalf(\"Failed to load with joins on, got error: %v\", err)\n\t}\n\tAssertEqual(t, user3.NamedPet.ID, 0)\n\tAssertEqual(t, user3.NamedPet.Name, \"joins-args-db_pet_2\")\n\n\t// test select\n\tonQuery4 := DB.Select(\"ID\")\n\tvar user4 User\n\tif err := DB.Joins(\"NamedPet\", onQuery4).Where(\"users.name = ?\", user.Name).First(&user4).Error; err != nil {\n\t\tt.Fatalf(\"Failed to load with joins on, got error: %v\", err)\n\t}\n\tif user4.NamedPet.ID == 0 {\n\t\tt.Fatal(\"Pet ID can not be empty\")\n\t}\n\tAssertEqual(t, user4.NamedPet.Name, \"\")\n}\n\nfunc TestNestedJoins(t *testing.T) {\n\tusers := []User{\n\t\t{\n\t\t\tName: \"nested-joins-1\",\n\t\t\tManager: &User{\n\t\t\t\tName: \"nested-joins-manager-1\",\n\t\t\t\tCompany: Company{\n\t\t\t\t\tName: \"nested-joins-manager-company-1\",\n\t\t\t\t},\n\t\t\t\tNamedPet: &Pet{\n\t\t\t\t\tName: \"nested-joins-manager-namepet-1\",\n\t\t\t\t\tToy: Toy{\n\t\t\t\t\t\tName: \"nested-joins-manager-namepet-toy-1\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tNamedPet: &Pet{Name: \"nested-joins-namepet-1\", Toy: Toy{Name: \"nested-joins-namepet-toy-1\"}},\n\t\t},\n\t\t{\n\t\t\tName:     \"nested-joins-2\",\n\t\t\tManager:  GetUser(\"nested-joins-manager-2\", Config{Company: true, NamedPet: true}),\n\t\t\tNamedPet: &Pet{Name: \"nested-joins-namepet-2\", Toy: Toy{Name: \"nested-joins-namepet-toy-2\"}},\n\t\t},\n\t}\n\n\tDB.Create(&users)\n\n\tvar userIDs []uint\n\tfor _, user := range users {\n\t\tuserIDs = append(userIDs, user.ID)\n\t}\n\n\tvar users2 []User\n\tif err := DB.\n\t\tJoins(\"Manager\").\n\t\tJoins(\"Manager.Company\").\n\t\tJoins(\"Manager.NamedPet\").\n\t\tJoins(\"Manager.NamedPet.Toy\").\n\t\tJoins(\"NamedPet\").\n\t\tJoins(\"NamedPet.Toy\").\n\t\tFind(&users2, \"users.id IN ?\", userIDs).Error; err != nil {\n\t\tt.Fatalf(\"Failed to load with joins, got error: %v\", err)\n\t} else if len(users2) != len(users) {\n\t\tt.Fatalf(\"Failed to load join users, got: %v, expect: %v\", len(users2), len(users))\n\t}\n\n\tsort.Slice(users2, func(i, j int) bool {\n\t\treturn users2[i].ID > users2[j].ID\n\t})\n\n\tsort.Slice(users, func(i, j int) bool {\n\t\treturn users[i].ID > users[j].ID\n\t})\n\n\tfor idx, user := range users {\n\t\t// user\n\t\tCheckUser(t, user, users2[idx])\n\t\tif users2[idx].Manager == nil {\n\t\t\tt.Fatalf(\"Failed to load Manager\")\n\t\t}\n\t\t// manager\n\t\tCheckUser(t, *user.Manager, *users2[idx].Manager)\n\t\t// user pet\n\t\tif users2[idx].NamedPet == nil {\n\t\t\tt.Fatalf(\"Failed to load NamedPet\")\n\t\t}\n\t\tCheckPet(t, *user.NamedPet, *users2[idx].NamedPet)\n\t\t// manager pet\n\t\tif users2[idx].Manager.NamedPet == nil {\n\t\t\tt.Fatalf(\"Failed to load NamedPet\")\n\t\t}\n\t\tCheckPet(t, *user.Manager.NamedPet, *users2[idx].Manager.NamedPet)\n\t}\n}\n\nfunc TestJoinsPreload_Issue7013(t *testing.T) {\n\tmanager := &User{Name: \"Manager\"}\n\tDB.Create(manager)\n\n\tvar userIDs []uint\n\tfor i := 0; i < 21; i++ {\n\t\tuser := &User{Name: fmt.Sprintf(\"User%d\", i), ManagerID: &manager.ID}\n\t\tDB.Create(user)\n\t\tuserIDs = append(userIDs, user.ID)\n\t}\n\n\tvar entries []User\n\tassert.NotPanics(t, func() {\n\t\tassert.NoError(t,\n\t\t\tDB.Preload(\"Manager.Team\").\n\t\t\t\tJoins(\"Manager.Company\").\n\t\t\t\tFind(&entries).Error)\n\t})\n}\n\nfunc TestJoinsPreload_Issue7013_RelationEmpty(t *testing.T) {\n\ttype (\n\t\tFurniture struct {\n\t\t\tgorm.Model\n\t\t\tOwnerID *uint\n\t\t}\n\n\t\tOwner struct {\n\t\t\tgorm.Model\n\t\t\tFurnitures []Furniture\n\t\t\tCompanyID  *uint\n\t\t\tCompany    Company\n\t\t}\n\n\t\tBuilding struct {\n\t\t\tgorm.Model\n\t\t\tName    string\n\t\t\tOwnerID *uint\n\t\t\tOwner   Owner\n\t\t}\n\t)\n\n\tDB.Migrator().DropTable(&Building{}, &Owner{}, &Furniture{})\n\tDB.Migrator().AutoMigrate(&Building{}, &Owner{}, &Furniture{})\n\n\thome := &Building{Name: \"relation_empty\"}\n\tDB.Create(home)\n\n\tvar entries []Building\n\tassert.NotPanics(t, func() {\n\t\tassert.NoError(t,\n\t\t\tDB.Preload(\"Owner.Furnitures\").\n\t\t\t\tJoins(\"Owner.Company\").\n\t\t\t\tFind(&entries).Error)\n\t})\n\n\tAssertEqual(t, entries, []Building{{Model: home.Model, Name: \"relation_empty\", Owner: Owner{Company: Company{}}}})\n}\n\nfunc TestJoinsPreload_Issue7013_NoEntries(t *testing.T) {\n\tvar entries []User\n\tassert.NotPanics(t, func() {\n\t\tassert.NoError(t,\n\t\t\tDB.Preload(\"Manager.Team\").\n\t\t\t\tJoins(\"Manager.Company\").\n\t\t\t\tWhere(\"1 <> 1\").\n\t\t\t\tFind(&entries).Error)\n\t})\n\n\tAssertEqual(t, len(entries), 0)\n}\n"
  },
  {
    "path": "tests/lru_test.go",
    "content": "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/gorm/internal/lru\"\n)\n\nfunc TestLRU_Add_ExistingKey_UpdatesValueAndExpiresAt(t *testing.T) {\n\tlru := lru.NewLRU[string, int](10, nil, time.Hour)\n\tlru.Add(\"key1\", 1)\n\tlru.Add(\"key1\", 2)\n\n\tif value, ok := lru.Get(\"key1\"); !ok || value != 2 {\n\t\tt.Errorf(\"Expected value to be updated to 2, got %v\", value)\n\t}\n}\n\nfunc TestLRU_Add_NewKey_AddsEntry(t *testing.T) {\n\tlru := lru.NewLRU[string, int](10, nil, time.Hour)\n\tlru.Add(\"key1\", 1)\n\n\tif value, ok := lru.Get(\"key1\"); !ok || value != 1 {\n\t\tt.Errorf(\"Expected key1 to be added with value 1, got %v\", value)\n\t}\n}\n\nfunc TestLRU_Add_ExceedsSize_RemovesOldest(t *testing.T) {\n\tlru := lru.NewLRU[string, int](2, nil, time.Hour)\n\tlru.Add(\"key1\", 1)\n\tlru.Add(\"key2\", 2)\n\tlru.Add(\"key3\", 3)\n\n\tif _, ok := lru.Get(\"key1\"); ok {\n\t\tt.Errorf(\"Expected key1 to be removed, but it still exists\")\n\t}\n}\n\nfunc TestLRU_Add_UnlimitedSize_NoEviction(t *testing.T) {\n\tlru := lru.NewLRU[string, int](0, nil, time.Hour)\n\tlru.Add(\"key1\", 1)\n\tlru.Add(\"key2\", 2)\n\tlru.Add(\"key3\", 3)\n\n\tif _, ok := lru.Get(\"key1\"); !ok {\n\t\tt.Errorf(\"Expected key1 to exist, but it was evicted\")\n\t}\n}\n\nfunc TestLRU_Add_Eviction(t *testing.T) {\n\tlru := lru.NewLRU[string, int](0, nil, time.Second*2)\n\tlru.Add(\"key1\", 1)\n\tlru.Add(\"key2\", 2)\n\tlru.Add(\"key3\", 3)\n\ttime.Sleep(time.Second * 3)\n\tif lru.Cap() != 0 {\n\t\tt.Errorf(\"Expected lru to be empty, but it was not\")\n\t}\n\n}\n\nfunc BenchmarkLRU_Rand_NoExpire(b *testing.B) {\n\tl := lru.NewLRU[int64, int64](8192, nil, 0)\n\n\ttrace := make([]int64, b.N*2)\n\tfor i := 0; i < b.N*2; i++ {\n\t\ttrace[i] = getRand(b) % 32768\n\t}\n\n\tb.ResetTimer()\n\n\tvar hit, miss int\n\tfor i := 0; i < 2*b.N; i++ {\n\t\tif i%2 == 0 {\n\t\t\tl.Add(trace[i], trace[i])\n\t\t} else {\n\t\t\tif _, ok := l.Get(trace[i]); ok {\n\t\t\t\thit++\n\t\t\t} else {\n\t\t\t\tmiss++\n\t\t\t}\n\t\t}\n\t}\n\tb.Logf(\"hit: %d miss: %d ratio: %f\", hit, miss, float64(hit)/float64(hit+miss))\n}\n\nfunc BenchmarkLRU_Freq_NoExpire(b *testing.B) {\n\tl := lru.NewLRU[int64, int64](8192, nil, 0)\n\n\ttrace := make([]int64, b.N*2)\n\tfor i := 0; i < b.N*2; i++ {\n\t\tif i%2 == 0 {\n\t\t\ttrace[i] = getRand(b) % 16384\n\t\t} else {\n\t\t\ttrace[i] = getRand(b) % 32768\n\t\t}\n\t}\n\n\tb.ResetTimer()\n\n\tfor i := 0; i < b.N; i++ {\n\t\tl.Add(trace[i], trace[i])\n\t}\n\tvar hit, miss int\n\tfor i := 0; i < b.N; i++ {\n\t\tif _, ok := l.Get(trace[i]); ok {\n\t\t\thit++\n\t\t} else {\n\t\t\tmiss++\n\t\t}\n\t}\n\tb.Logf(\"hit: %d miss: %d ratio: %f\", hit, miss, float64(hit)/float64(hit+miss))\n}\n\nfunc BenchmarkLRU_Rand_WithExpire(b *testing.B) {\n\tl := lru.NewLRU[int64, int64](8192, nil, time.Millisecond*10)\n\n\ttrace := make([]int64, b.N*2)\n\tfor i := 0; i < b.N*2; i++ {\n\t\ttrace[i] = getRand(b) % 32768\n\t}\n\n\tb.ResetTimer()\n\n\tvar hit, miss int\n\tfor i := 0; i < 2*b.N; i++ {\n\t\tif i%2 == 0 {\n\t\t\tl.Add(trace[i], trace[i])\n\t\t} else {\n\t\t\tif _, ok := l.Get(trace[i]); ok {\n\t\t\t\thit++\n\t\t\t} else {\n\t\t\t\tmiss++\n\t\t\t}\n\t\t}\n\t}\n\tb.Logf(\"hit: %d miss: %d ratio: %f\", hit, miss, float64(hit)/float64(hit+miss))\n}\n\nfunc BenchmarkLRU_Freq_WithExpire(b *testing.B) {\n\tl := lru.NewLRU[int64, int64](8192, nil, time.Millisecond*10)\n\n\ttrace := make([]int64, b.N*2)\n\tfor i := 0; i < b.N*2; i++ {\n\t\tif i%2 == 0 {\n\t\t\ttrace[i] = getRand(b) % 16384\n\t\t} else {\n\t\t\ttrace[i] = getRand(b) % 32768\n\t\t}\n\t}\n\n\tb.ResetTimer()\n\n\tfor i := 0; i < b.N; i++ {\n\t\tl.Add(trace[i], trace[i])\n\t}\n\tvar hit, miss int\n\tfor i := 0; i < b.N; i++ {\n\t\tif _, ok := l.Get(trace[i]); ok {\n\t\t\thit++\n\t\t} else {\n\t\t\tmiss++\n\t\t}\n\t}\n\tb.Logf(\"hit: %d miss: %d ratio: %f\", hit, miss, float64(hit)/float64(hit+miss))\n}\n\nfunc TestLRUNoPurge(t *testing.T) {\n\tlc := lru.NewLRU[string, string](10, nil, 0)\n\n\tlc.Add(\"key1\", \"val1\")\n\tif lc.Len() != 1 {\n\t\tt.Fatalf(\"length differs from expected\")\n\t}\n\n\tv, ok := lc.Peek(\"key1\")\n\tif v != \"val1\" {\n\t\tt.Fatalf(\"value differs from expected\")\n\t}\n\tif !ok {\n\t\tt.Fatalf(\"should be true\")\n\t}\n\n\tif !lc.Contains(\"key1\") {\n\t\tt.Fatalf(\"should contain key1\")\n\t}\n\tif lc.Contains(\"key2\") {\n\t\tt.Fatalf(\"should not contain key2\")\n\t}\n\n\tv, ok = lc.Peek(\"key2\")\n\tif v != \"\" {\n\t\tt.Fatalf(\"should be empty\")\n\t}\n\tif ok {\n\t\tt.Fatalf(\"should be false\")\n\t}\n\n\tif !reflect.DeepEqual(lc.Keys(), []string{\"key1\"}) {\n\t\tt.Fatalf(\"value differs from expected\")\n\t}\n\n\tif lc.Resize(0) != 0 {\n\t\tt.Fatalf(\"evicted count differs from expected\")\n\t}\n\tif lc.Resize(2) != 0 {\n\t\tt.Fatalf(\"evicted count differs from expected\")\n\t}\n\tlc.Add(\"key2\", \"val2\")\n\tif lc.Resize(1) != 1 {\n\t\tt.Fatalf(\"evicted count differs from expected\")\n\t}\n}\n\nfunc TestLRUEdgeCases(t *testing.T) {\n\tlc := lru.NewLRU[string, *string](2, nil, 0)\n\n\t// Adding a nil value\n\tlc.Add(\"key1\", nil)\n\n\tvalue, exists := lc.Get(\"key1\")\n\tif value != nil || !exists {\n\t\tt.Fatalf(\"unexpected value or existence flag for key1: value=%v, exists=%v\", value, exists)\n\t}\n\n\t// Adding an entry with the same key but different value\n\tnewVal := \"val1\"\n\tlc.Add(\"key1\", &newVal)\n\n\tvalue, exists = lc.Get(\"key1\")\n\tif value != &newVal || !exists {\n\t\tt.Fatalf(\"unexpected value or existence flag for key1: value=%v, exists=%v\", value, exists)\n\t}\n}\n\nfunc TestLRU_Values(t *testing.T) {\n\tlc := lru.NewLRU[string, string](3, nil, 0)\n\n\tlc.Add(\"key1\", \"val1\")\n\tlc.Add(\"key2\", \"val2\")\n\tlc.Add(\"key3\", \"val3\")\n\n\tvalues := lc.Values()\n\tif !reflect.DeepEqual(values, []string{\"val1\", \"val2\", \"val3\"}) {\n\t\tt.Fatalf(\"values differs from expected\")\n\t}\n}\n\n// func TestExpirableMultipleClose(_ *testing.T) {\n//\tlc :=lru.NewLRU[string, string](10, nil, 0)\n//\tlc.Close()\n//\t// should not panic\n//\tlc.Close()\n// }\n\nfunc TestLRUWithPurge(t *testing.T) {\n\tvar evicted []string\n\tlc := lru.NewLRU(10, func(key string, value string) { evicted = append(evicted, key, value) }, 150*time.Millisecond)\n\n\tk, v, ok := lc.GetOldest()\n\tif k != \"\" {\n\t\tt.Fatalf(\"should be empty\")\n\t}\n\tif v != \"\" {\n\t\tt.Fatalf(\"should be empty\")\n\t}\n\tif ok {\n\t\tt.Fatalf(\"should be false\")\n\t}\n\n\tlc.Add(\"key1\", \"val1\")\n\n\ttime.Sleep(100 * time.Millisecond) // not enough to expire\n\tif lc.Len() != 1 {\n\t\tt.Fatalf(\"length differs from expected\")\n\t}\n\n\tv, ok = lc.Get(\"key1\")\n\tif v != \"val1\" {\n\t\tt.Fatalf(\"value differs from expected\")\n\t}\n\tif !ok {\n\t\tt.Fatalf(\"should be true\")\n\t}\n\n\ttime.Sleep(200 * time.Millisecond) // expire\n\tv, ok = lc.Get(\"key1\")\n\tif ok {\n\t\tt.Fatalf(\"should be false\")\n\t}\n\tif v != \"\" {\n\t\tt.Fatalf(\"should be nil\")\n\t}\n\n\tif lc.Len() != 0 {\n\t\tt.Fatalf(\"length differs from expected\")\n\t}\n\tif !reflect.DeepEqual(evicted, []string{\"key1\", \"val1\"}) {\n\t\tt.Fatalf(\"value differs from expected\")\n\t}\n\n\t// add new entry\n\tlc.Add(\"key2\", \"val2\")\n\tif lc.Len() != 1 {\n\t\tt.Fatalf(\"length differs from expected\")\n\t}\n\n\tk, v, ok = lc.GetOldest()\n\tif k != \"key2\" {\n\t\tt.Fatalf(\"value differs from expected\")\n\t}\n\tif v != \"val2\" {\n\t\tt.Fatalf(\"value differs from expected\")\n\t}\n\tif !ok {\n\t\tt.Fatalf(\"should be true\")\n\t}\n\n}\n\nfunc TestLRUWithPurgeEnforcedBySize(t *testing.T) {\n\tlc := lru.NewLRU[string, string](10, nil, time.Hour)\n\n\tfor i := 0; i < 100; i++ {\n\t\ti := i\n\t\tlc.Add(fmt.Sprintf(\"key%d\", i), fmt.Sprintf(\"val%d\", i))\n\t\tv, ok := lc.Get(fmt.Sprintf(\"key%d\", i))\n\t\tif v != fmt.Sprintf(\"val%d\", i) {\n\t\t\tt.Fatalf(\"value differs from expected\")\n\t\t}\n\t\tif !ok {\n\t\t\tt.Fatalf(\"should be true\")\n\t\t}\n\t\tif lc.Len() > 20 {\n\t\t\tt.Fatalf(\"length should be less than 20\")\n\t\t}\n\t}\n\n\tif lc.Len() != 10 {\n\t\tt.Fatalf(\"length differs from expected\")\n\t}\n}\n\nfunc TestLRUConcurrency(t *testing.T) {\n\tlc := lru.NewLRU[string, string](0, nil, 0)\n\twg := sync.WaitGroup{}\n\twg.Add(1000)\n\tfor i := 0; i < 1000; i++ {\n\t\tgo func(i int) {\n\t\t\tlc.Add(fmt.Sprintf(\"key-%d\", i/10), fmt.Sprintf(\"val-%d\", i/10))\n\t\t\twg.Done()\n\t\t}(i)\n\t}\n\twg.Wait()\n\tif lc.Len() != 100 {\n\t\tt.Fatalf(\"length differs from expected\")\n\t}\n}\n\nfunc TestLRUInvalidateAndEvict(t *testing.T) {\n\tvar evicted int\n\tlc := lru.NewLRU(-1, func(_, _ string) { evicted++ }, 0)\n\n\tlc.Add(\"key1\", \"val1\")\n\tlc.Add(\"key2\", \"val2\")\n\n\tval, ok := lc.Get(\"key1\")\n\tif !ok {\n\t\tt.Fatalf(\"should be true\")\n\t}\n\tif val != \"val1\" {\n\t\tt.Fatalf(\"value differs from expected\")\n\t}\n\tif evicted != 0 {\n\t\tt.Fatalf(\"value differs from expected\")\n\t}\n\n\tlc.Remove(\"key1\")\n\tif evicted != 1 {\n\t\tt.Fatalf(\"value differs from expected\")\n\t}\n\tval, ok = lc.Get(\"key1\")\n\tif val != \"\" {\n\t\tt.Fatalf(\"should be empty\")\n\t}\n\tif ok {\n\t\tt.Fatalf(\"should be false\")\n\t}\n}\n\nfunc TestLoadingExpired(t *testing.T) {\n\tlc := lru.NewLRU[string, string](0, nil, time.Millisecond*5)\n\n\tlc.Add(\"key1\", \"val1\")\n\tif lc.Len() != 1 {\n\t\tt.Fatalf(\"length differs from expected\")\n\t}\n\n\tv, ok := lc.Peek(\"key1\")\n\tif v != \"val1\" {\n\t\tt.Fatalf(\"value differs from expected\")\n\t}\n\tif !ok {\n\t\tt.Fatalf(\"should be true\")\n\t}\n\n\tv, ok = lc.Get(\"key1\")\n\tif v != \"val1\" {\n\t\tt.Fatalf(\"value differs from expected\")\n\t}\n\tif !ok {\n\t\tt.Fatalf(\"should be true\")\n\t}\n\n\tfor {\n\t\tresult, ok := lc.Get(\"key1\")\n\t\tif ok && result == \"\" {\n\t\t\tt.Fatalf(\"ok should return a result\")\n\t\t}\n\t\tif !ok {\n\t\t\tbreak\n\t\t}\n\t}\n\n\ttime.Sleep(time.Millisecond * 100) // wait for expiration reaper\n\tif lc.Len() != 0 {\n\t\tt.Fatalf(\"length differs from expected\")\n\t}\n\n\tv, ok = lc.Peek(\"key1\")\n\tif v != \"\" {\n\t\tt.Fatalf(\"should be empty\")\n\t}\n\tif ok {\n\t\tt.Fatalf(\"should be false\")\n\t}\n\n\tv, ok = lc.Get(\"key1\")\n\tif v != \"\" {\n\t\tt.Fatalf(\"should be empty\")\n\t}\n\tif ok {\n\t\tt.Fatalf(\"should be false\")\n\t}\n}\n\nfunc TestLRURemoveOldest(t *testing.T) {\n\tlc := lru.NewLRU[string, string](2, nil, 0)\n\n\tif lc.Cap() != 2 {\n\t\tt.Fatalf(\"expect cap is 2\")\n\t}\n\n\tk, v, ok := lc.RemoveOldest()\n\tif k != \"\" {\n\t\tt.Fatalf(\"should be empty\")\n\t}\n\tif v != \"\" {\n\t\tt.Fatalf(\"should be empty\")\n\t}\n\tif ok {\n\t\tt.Fatalf(\"should be false\")\n\t}\n\n\tok = lc.Remove(\"non_existent\")\n\tif ok {\n\t\tt.Fatalf(\"should be false\")\n\t}\n\n\tlc.Add(\"key1\", \"val1\")\n\tif lc.Len() != 1 {\n\t\tt.Fatalf(\"length differs from expected\")\n\t}\n\n\tv, ok = lc.Get(\"key1\")\n\tif !ok {\n\t\tt.Fatalf(\"should be true\")\n\t}\n\tif v != \"val1\" {\n\t\tt.Fatalf(\"value differs from expected\")\n\t}\n\n\tif !reflect.DeepEqual(lc.Keys(), []string{\"key1\"}) {\n\t\tt.Fatalf(\"value differs from expected\")\n\t}\n\tif lc.Len() != 1 {\n\t\tt.Fatalf(\"length differs from expected\")\n\t}\n\n\tlc.Add(\"key2\", \"val2\")\n\tif !reflect.DeepEqual(lc.Keys(), []string{\"key1\", \"key2\"}) {\n\t\tt.Fatalf(\"value differs from expected\")\n\t}\n\tif lc.Len() != 2 {\n\t\tt.Fatalf(\"length differs from expected\")\n\t}\n\n\tk, v, ok = lc.RemoveOldest()\n\tif k != \"key1\" {\n\t\tt.Fatalf(\"value differs from expected\")\n\t}\n\tif v != \"val1\" {\n\t\tt.Fatalf(\"value differs from expected\")\n\t}\n\tif !ok {\n\t\tt.Fatalf(\"should be true\")\n\t}\n\n\tif !reflect.DeepEqual(lc.Keys(), []string{\"key2\"}) {\n\t\tt.Fatalf(\"value differs from expected\")\n\t}\n\tif lc.Len() != 1 {\n\t\tt.Fatalf(\"length differs from expected\")\n\t}\n}\n\nfunc getRand(tb testing.TB) int64 {\n\tout, err := rand.Int(rand.Reader, big.NewInt(math.MaxInt64))\n\tif err != nil {\n\t\ttb.Fatal(err)\n\t}\n\treturn out.Int64()\n}\n"
  },
  {
    "path": "tests/main_test.go",
    "content": "package tests_test\n\nimport (\n\t\"testing\"\n\n\t. \"gorm.io/gorm/utils/tests\"\n)\n\nfunc TestExceptionsWithInvalidSql(t *testing.T) {\n\tif name := DB.Dialector.Name(); name == \"sqlserver\" {\n\t\tt.Skip(\"skip sqlserver due to it will raise data race for invalid sql\")\n\t}\n\n\tvar columns []string\n\tif DB.Where(\"sdsd.zaaa = ?\", \"sd;;;aa\").Pluck(\"aaa\", &columns).Error == nil {\n\t\tt.Errorf(\"Should got error with invalid SQL\")\n\t}\n\n\tif DB.Model(&User{}).Where(\"sdsd.zaaa = ?\", \"sd;;;aa\").Pluck(\"aaa\", &columns).Error == nil {\n\t\tt.Errorf(\"Should got error with invalid SQL\")\n\t}\n\n\tif DB.Where(\"sdsd.zaaa = ?\", \"sd;;;aa\").Find(&User{}).Error == nil {\n\t\tt.Errorf(\"Should got error with invalid SQL\")\n\t}\n\n\tvar count1, count2 int64\n\tDB.Model(&User{}).Count(&count1)\n\tif count1 <= 0 {\n\t\tt.Errorf(\"Should find some users\")\n\t}\n\n\tif DB.Where(\"name = ?\", \"jinzhu; delete * from users\").First(&User{}).Error == nil {\n\t\tt.Errorf(\"Should got error with invalid SQL\")\n\t}\n\n\tDB.Model(&User{}).Count(&count2)\n\tif count1 != count2 {\n\t\tt.Errorf(\"No user should not be deleted by invalid SQL\")\n\t}\n}\n\nfunc TestSetAndGet(t *testing.T) {\n\tif value, ok := DB.Set(\"hello\", \"world\").Get(\"hello\"); !ok {\n\t\tt.Errorf(\"Should be able to get setting after set\")\n\t} else if value.(string) != \"world\" {\n\t\tt.Errorf(\"Set value should not be changed\")\n\t}\n\n\tif _, ok := DB.Get(\"non_existing\"); ok {\n\t\tt.Errorf(\"Get non existing key should return error\")\n\t}\n}\n"
  },
  {
    "path": "tests/migrate_test.go",
    "content": "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\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"gorm.io/driver/gaussdb\"\n\t\"gorm.io/driver/postgres\"\n\n\t\"gorm.io/gorm\"\n\t\"gorm.io/gorm/clause\"\n\t\"gorm.io/gorm/migrator\"\n\t\"gorm.io/gorm/schema\"\n\t\"gorm.io/gorm/utils\"\n\t. \"gorm.io/gorm/utils/tests\"\n)\n\nfunc TestMigrate(t *testing.T) {\n\tallModels := []interface{}{&User{}, &Account{}, &Pet{}, &Company{}, &Toy{}, &Language{}, &Tools{}, &Man{}}\n\trand.Seed(time.Now().UnixNano())\n\trand.Shuffle(len(allModels), func(i, j int) { allModels[i], allModels[j] = allModels[j], allModels[i] })\n\tDB.Migrator().DropTable(\"user_speaks\", \"user_friends\", \"ccc\")\n\n\tif err := DB.Migrator().DropTable(allModels...); err != nil {\n\t\tt.Fatalf(\"Failed to drop table, got error %v\", err)\n\t}\n\n\tif err := DB.AutoMigrate(allModels...); err != nil {\n\t\tt.Fatalf(\"Failed to auto migrate, got error %v\", err)\n\t}\n\n\tif tables, err := DB.Migrator().GetTables(); err != nil {\n\t\tt.Fatalf(\"Failed to get database all tables, but got error %v\", err)\n\t} else {\n\t\tfor _, t1 := range []string{\"users\", \"accounts\", \"pets\", \"companies\", \"toys\", \"languages\", \"tools\"} {\n\t\t\thasTable := false\n\t\t\tfor _, t2 := range tables {\n\t\t\t\tif t2 == t1 {\n\t\t\t\t\thasTable = true\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t\tif !hasTable {\n\t\t\t\tt.Fatalf(\"Failed to get table %v when GetTables\", t1)\n\t\t\t}\n\t\t}\n\t}\n\n\tfor _, m := range allModels {\n\t\tif !DB.Migrator().HasTable(m) {\n\t\t\tt.Fatalf(\"Failed to create table for %#v\", m)\n\t\t}\n\t}\n\n\tDB.Scopes(func(db *gorm.DB) *gorm.DB {\n\t\treturn db.Table(\"ccc\")\n\t}).Migrator().CreateTable(&Company{})\n\n\tif !DB.Migrator().HasTable(\"ccc\") {\n\t\tt.Errorf(\"failed to create table ccc\")\n\t}\n\n\tfor _, indexes := range [][2]string{\n\t\t{\"user_speaks\", \"fk_user_speaks_user\"},\n\t\t{\"user_speaks\", \"fk_user_speaks_language\"},\n\t\t{\"user_friends\", \"fk_user_friends_user\"},\n\t\t{\"user_friends\", \"fk_user_friends_friends\"},\n\t\t{\"accounts\", \"fk_users_account\"},\n\t\t{\"users\", \"fk_users_team\"},\n\t\t{\"users\", \"fk_users_company\"},\n\t} {\n\t\tif !DB.Migrator().HasConstraint(indexes[0], indexes[1]) {\n\t\t\tt.Fatalf(\"Failed to find index for many2many for %v %v\", indexes[0], indexes[1])\n\t\t}\n\t}\n}\n\nfunc TestAutoMigrateInt8PGAndGaussDB(t *testing.T) {\n\tif DB.Dialector.Name() != \"postgres\" && DB.Dialector.Name() != \"gaussdb\" {\n\t\treturn\n\t}\n\n\ttype Smallint int8\n\n\ttype MigrateInt struct {\n\t\tInt8 Smallint\n\t}\n\n\ttracer := Tracer{\n\t\tLogger: DB.Config.Logger,\n\t\tTest: func(ctx context.Context, begin time.Time, fc func() (sql string, rowsAffected int64), err error) {\n\t\t\tsql, _ := fc()\n\t\t\tif strings.HasPrefix(sql, \"ALTER TABLE \\\"migrate_ints\\\" ALTER COLUMN \\\"int8\\\" TYPE smallint\") {\n\t\t\t\tt.Fatalf(\"shouldn't execute ALTER COLUMN TYPE if such type is already existed in DB schema: sql: %s\",\n\t\t\t\t\tsql)\n\t\t\t}\n\t\t},\n\t}\n\n\tDB.Migrator().DropTable(&MigrateInt{})\n\n\t// The first AutoMigrate to make table with field with correct type\n\tif err := DB.AutoMigrate(&MigrateInt{}); err != nil {\n\t\tt.Fatalf(\"Failed to auto migrate: error: %v\", err)\n\t}\n\n\t// make new session to set custom logger tracer\n\tsession := DB.Session(&gorm.Session{Logger: tracer})\n\n\t// The second AutoMigrate to catch an error\n\tif err := session.AutoMigrate(&MigrateInt{}); err != nil {\n\t\tt.Fatalf(\"Failed to auto migrate: error: %v\", err)\n\t}\n}\n\nfunc TestAutoMigrateSelfReferential(t *testing.T) {\n\ttype MigratePerson struct {\n\t\tID        uint\n\t\tName      string\n\t\tManagerID *uint\n\t\tManager   *MigratePerson\n\t}\n\n\tDB.Migrator().DropTable(&MigratePerson{})\n\n\tif err := DB.AutoMigrate(&MigratePerson{}); err != nil {\n\t\tt.Fatalf(\"Failed to auto migrate, but got error %v\", err)\n\t}\n\n\tif !DB.Migrator().HasConstraint(\"migrate_people\", \"fk_migrate_people_manager\") {\n\t\tt.Fatalf(\"Failed to find has one constraint between people and managers\")\n\t}\n}\n\nfunc TestAutoMigrateNullable(t *testing.T) {\n\ttype MigrateNullableColumn struct {\n\t\tID    uint\n\t\tBonus float64 `gorm:\"not null\"`\n\t\tStock float64\n\t}\n\n\tDB.Migrator().DropTable(&MigrateNullableColumn{})\n\n\tDB.AutoMigrate(&MigrateNullableColumn{})\n\n\ttype MigrateNullableColumn2 struct {\n\t\tID    uint\n\t\tBonus float64\n\t\tStock float64 `gorm:\"not null\"`\n\t}\n\n\tif err := DB.Table(\"migrate_nullable_columns\").AutoMigrate(&MigrateNullableColumn2{}); err != nil {\n\t\tt.Fatalf(\"failed to auto migrate, got error: %v\", err)\n\t}\n\n\tcolumnTypes, err := DB.Table(\"migrate_nullable_columns\").Migrator().ColumnTypes(&MigrateNullableColumn{})\n\tif err != nil {\n\t\tt.Fatalf(\"failed to get column types, got error: %v\", err)\n\t}\n\n\tfor _, columnType := range columnTypes {\n\t\tswitch columnType.Name() {\n\t\tcase \"bonus\":\n\t\t\t// allow to change non-nullable to nullable\n\t\t\tif nullable, _ := columnType.Nullable(); !nullable {\n\t\t\t\tt.Fatalf(\"bonus's nullable should be true, bug got %t\", nullable)\n\t\t\t}\n\t\tcase \"stock\":\n\t\t\t// do not allow to change nullable to non-nullable\n\t\t\tif nullable, _ := columnType.Nullable(); !nullable {\n\t\t\t\tt.Fatalf(\"stock's nullable should be true, bug got %t\", nullable)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc TestSmartMigrateColumn(t *testing.T) {\n\tfullSupported := map[string]bool{\"mysql\": true, \"postgres\": true, \"gaussdb\": true}[DB.Dialector.Name()]\n\n\ttype UserMigrateColumn struct {\n\t\tID       uint\n\t\tName     string\n\t\tSalary   float64\n\t\tBirthday time.Time `gorm:\"precision:4\"`\n\t}\n\n\tDB.Migrator().DropTable(&UserMigrateColumn{})\n\n\tDB.AutoMigrate(&UserMigrateColumn{})\n\n\ttype UserMigrateColumn2 struct {\n\t\tID                  uint\n\t\tName                string    `gorm:\"size:128\"`\n\t\tSalary              float64   `gorm:\"precision:2\"`\n\t\tBirthday            time.Time `gorm:\"precision:2\"`\n\t\tNameIgnoreMigration string    `gorm:\"size:100\"`\n\t}\n\n\tif err := DB.Table(\"user_migrate_columns\").AutoMigrate(&UserMigrateColumn2{}); err != nil {\n\t\tt.Fatalf(\"failed to auto migrate, got error: %v\", err)\n\t}\n\n\tcolumnTypes, err := DB.Table(\"user_migrate_columns\").Migrator().ColumnTypes(&UserMigrateColumn{})\n\tif err != nil {\n\t\tt.Fatalf(\"failed to get column types, got error: %v\", err)\n\t}\n\n\tfor _, columnType := range columnTypes {\n\t\tswitch columnType.Name() {\n\t\tcase \"name\":\n\t\t\tif length, _ := columnType.Length(); (fullSupported || length != 0) && length != 128 {\n\t\t\t\tt.Fatalf(\"name's length should be 128, but got %v\", length)\n\t\t\t}\n\t\tcase \"salary\":\n\t\t\tif precision, o, _ := columnType.DecimalSize(); (fullSupported || precision != 0) && precision != 2 {\n\t\t\t\tt.Fatalf(\"salary's precision should be 2, but got %v %v\", precision, o)\n\t\t\t}\n\t\tcase \"birthday\":\n\t\t\tif precision, _, _ := columnType.DecimalSize(); (fullSupported || precision != 0) && precision != 2 {\n\t\t\t\tt.Fatalf(\"birthday's precision should be 2, but got %v\", precision)\n\t\t\t}\n\t\t}\n\t}\n\n\ttype UserMigrateColumn3 struct {\n\t\tID                  uint\n\t\tName                string    `gorm:\"size:256\"`\n\t\tSalary              float64   `gorm:\"precision:3\"`\n\t\tBirthday            time.Time `gorm:\"precision:3\"`\n\t\tNameIgnoreMigration string    `gorm:\"size:128;-:migration\"`\n\t}\n\n\tif err := DB.Table(\"user_migrate_columns\").AutoMigrate(&UserMigrateColumn3{}); err != nil {\n\t\tt.Fatalf(\"failed to auto migrate, got error: %v\", err)\n\t}\n\n\tcolumnTypes, err = DB.Table(\"user_migrate_columns\").Migrator().ColumnTypes(&UserMigrateColumn{})\n\tif err != nil {\n\t\tt.Fatalf(\"failed to get column types, got error: %v\", err)\n\t}\n\n\tfor _, columnType := range columnTypes {\n\t\tswitch columnType.Name() {\n\t\tcase \"name\":\n\t\t\tif length, _ := columnType.Length(); (fullSupported || length != 0) && length != 256 {\n\t\t\t\tt.Fatalf(\"name's length should be 128, but got %v\", length)\n\t\t\t}\n\t\tcase \"salary\":\n\t\t\tif precision, _, _ := columnType.DecimalSize(); (fullSupported || precision != 0) && precision != 3 {\n\t\t\t\tt.Fatalf(\"salary's precision should be 2, but got %v\", precision)\n\t\t\t}\n\t\tcase \"birthday\":\n\t\t\tif precision, _, _ := columnType.DecimalSize(); (fullSupported || precision != 0) && precision != 3 {\n\t\t\t\tt.Fatalf(\"birthday's precision should be 2, but got %v\", precision)\n\t\t\t}\n\t\tcase \"name_ignore_migration\":\n\t\t\tif length, _ := columnType.Length(); (fullSupported || length != 0) && length != 100 {\n\t\t\t\tt.Fatalf(\"name_ignore_migration's length should still be 100 but got %v\", length)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc TestSmartMigrateColumnGaussDB(t *testing.T) {\n\tfullSupported := map[string]bool{\"mysql\": true, \"gaussdb\": true}[DB.Dialector.Name()]\n\n\ttype UserMigrateColumn struct {\n\t\tID       uint\n\t\tName     string\n\t\tSalary   float64\n\t\tBirthday time.Time `gorm:\"precision:4\"`\n\t}\n\n\tDB.Migrator().DropTable(&UserMigrateColumn{})\n\n\tDB.AutoMigrate(&UserMigrateColumn{})\n\n\ttype UserMigrateColumn2 struct {\n\t\tID                  uint\n\t\tName                string    `gorm:\"size:128\"`\n\t\tSalary              float64   `gorm:\"precision:2\"`\n\t\tBirthday            time.Time `gorm:\"precision:2\"`\n\t\tNameIgnoreMigration string    `gorm:\"size:100\"`\n\t}\n\n\tif err := DB.Table(\"user_migrate_columns\").AutoMigrate(&UserMigrateColumn2{}); err != nil {\n\t\tt.Fatalf(\"failed to auto migrate, got error: %v\", err)\n\t}\n\n\tcolumnTypes, err := DB.Table(\"user_migrate_columns\").Migrator().ColumnTypes(&UserMigrateColumn{})\n\tif err != nil {\n\t\tt.Fatalf(\"failed to get column types, got error: %v\", err)\n\t}\n\n\tfor _, columnType := range columnTypes {\n\t\tswitch columnType.Name() {\n\t\tcase \"name\":\n\t\t\tif length, _ := columnType.Length(); (fullSupported || length != 0) && length != 128 {\n\t\t\t\tt.Fatalf(\"name's length should be 128, but got %v\", length)\n\t\t\t}\n\t\tcase \"salary\":\n\t\t\tif precision, o, _ := columnType.DecimalSize(); (fullSupported || precision != 0) && precision != 2 {\n\t\t\t\tt.Fatalf(\"salary's precision should be 2, but got %v %v\", precision, o)\n\t\t\t}\n\t\tcase \"birthday\":\n\t\t\tif precision, _, _ := columnType.DecimalSize(); (fullSupported || precision != 0) && precision != 2 {\n\t\t\t\tt.Fatalf(\"birthday's precision should be 2, but got %v\", precision)\n\t\t\t}\n\t\t}\n\t}\n\n\ttype UserMigrateColumn3 struct {\n\t\tID                  uint\n\t\tName                string    `gorm:\"size:256\"`\n\t\tSalary              float64   `gorm:\"precision:3\"`\n\t\tBirthday            time.Time `gorm:\"precision:3\"`\n\t\tNameIgnoreMigration string    `gorm:\"size:128;-:migration\"`\n\t}\n\n\tif err := DB.Table(\"user_migrate_columns\").AutoMigrate(&UserMigrateColumn3{}); err != nil {\n\t\tt.Fatalf(\"failed to auto migrate, got error: %v\", err)\n\t}\n\n\tcolumnTypes, err = DB.Table(\"user_migrate_columns\").Migrator().ColumnTypes(&UserMigrateColumn{})\n\tif err != nil {\n\t\tt.Fatalf(\"failed to get column types, got error: %v\", err)\n\t}\n\n\tfor _, columnType := range columnTypes {\n\t\tswitch columnType.Name() {\n\t\tcase \"name\":\n\t\t\tif length, _ := columnType.Length(); (fullSupported || length != 0) && length != 256 {\n\t\t\t\tt.Fatalf(\"name's length should be 128, but got %v\", length)\n\t\t\t}\n\t\tcase \"salary\":\n\t\t\tif precision, _, _ := columnType.DecimalSize(); (fullSupported || precision != 0) && precision != 3 {\n\t\t\t\tt.Fatalf(\"salary's precision should be 2, but got %v\", precision)\n\t\t\t}\n\t\tcase \"birthday\":\n\t\t\tif precision, _, _ := columnType.DecimalSize(); (fullSupported || precision != 0) && precision != 3 {\n\t\t\t\tt.Fatalf(\"birthday's precision should be 2, but got %v\", precision)\n\t\t\t}\n\t\tcase \"name_ignore_migration\":\n\t\t\tif length, _ := columnType.Length(); (fullSupported || length != 0) && length != 100 {\n\t\t\t\tt.Fatalf(\"name_ignore_migration's length should still be 100 but got %v\", length)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc TestMigrateWithColumnComment(t *testing.T) {\n\ttype UserWithColumnComment struct {\n\t\tgorm.Model\n\t\tName string `gorm:\"size:111;comment:this is a 字段\"`\n\t}\n\n\tif err := DB.Migrator().DropTable(&UserWithColumnComment{}); err != nil {\n\t\tt.Fatalf(\"Failed to drop table, got error %v\", err)\n\t}\n\n\tif err := DB.AutoMigrate(&UserWithColumnComment{}); err != nil {\n\t\tt.Fatalf(\"Failed to auto migrate, but got error %v\", err)\n\t}\n}\n\nfunc TestMigrateWithIndexComment(t *testing.T) {\n\tif DB.Dialector.Name() != \"mysql\" {\n\t\tt.Skip()\n\t}\n\n\ttype UserWithIndexComment struct {\n\t\tgorm.Model\n\t\tName string `gorm:\"size:111;index:,comment:这是一个index\"`\n\t}\n\n\tif err := DB.Migrator().DropTable(&UserWithIndexComment{}); err != nil {\n\t\tt.Fatalf(\"Failed to drop table, got error %v\", err)\n\t}\n\n\tif err := DB.AutoMigrate(&UserWithIndexComment{}); err != nil {\n\t\tt.Fatalf(\"Failed to auto migrate, but got error %v\", err)\n\t}\n}\n\nfunc TestMigrateWithUniqueIndex(t *testing.T) {\n\ttype UserWithUniqueIndex struct {\n\t\tID    int\n\t\tName  string    `gorm:\"size:20;index:idx_name,unique\"`\n\t\tDate  time.Time `gorm:\"index:idx_name,unique\"`\n\t\tUName string    `gorm:\"uniqueIndex;size:255\"`\n\t}\n\n\tDB.Migrator().DropTable(&UserWithUniqueIndex{})\n\tif err := DB.AutoMigrate(&UserWithUniqueIndex{}); err != nil {\n\t\tt.Fatalf(\"failed to migrate, got %v\", err)\n\t}\n\n\tif !DB.Migrator().HasIndex(&UserWithUniqueIndex{}, \"idx_name\") {\n\t\tt.Errorf(\"Failed to find created index\")\n\t}\n\n\tif !DB.Migrator().HasIndex(&UserWithUniqueIndex{}, \"idx_user_with_unique_indices_u_name\") {\n\t\tt.Errorf(\"Failed to find created index\")\n\t}\n\n\tif err := DB.AutoMigrate(&UserWithUniqueIndex{}); err != nil {\n\t\tt.Fatalf(\"failed to migrate, got %v\", err)\n\t}\n\n\tif !DB.Migrator().HasIndex(&UserWithUniqueIndex{}, \"idx_user_with_unique_indices_u_name\") {\n\t\tt.Errorf(\"Failed to find created index\")\n\t}\n}\n\nfunc TestMigrateTable(t *testing.T) {\n\ttype TableStruct struct {\n\t\tgorm.Model\n\t\tName string\n\t}\n\n\tDB.Migrator().DropTable(&TableStruct{})\n\tDB.AutoMigrate(&TableStruct{})\n\n\tif !DB.Migrator().HasTable(&TableStruct{}) {\n\t\tt.Fatalf(\"should found created table\")\n\t}\n\n\ttype NewTableStruct struct {\n\t\tgorm.Model\n\t\tName string\n\t}\n\n\tif err := DB.Migrator().RenameTable(&TableStruct{}, &NewTableStruct{}); err != nil {\n\t\tt.Fatalf(\"Failed to rename table, got error %v\", err)\n\t}\n\n\tif !DB.Migrator().HasTable(\"new_table_structs\") {\n\t\tt.Fatal(\"should found renamed table\")\n\t}\n\n\tDB.Migrator().DropTable(\"new_table_structs\")\n\n\tif DB.Migrator().HasTable(&NewTableStruct{}) {\n\t\tt.Fatal(\"should not found dropped table\")\n\t}\n}\n\nfunc TestMigrateWithQuotedIndex(t *testing.T) {\n\tif DB.Dialector.Name() != \"mysql\" {\n\t\tt.Skip()\n\t}\n\n\ttype QuotedIndexStruct struct {\n\t\tgorm.Model\n\t\tName string `gorm:\"size:255;index:AS\"` // AS is one of MySQL reserved words\n\t}\n\n\tif err := DB.Migrator().DropTable(&QuotedIndexStruct{}); err != nil {\n\t\tt.Fatalf(\"Failed to drop table, got error %v\", err)\n\t}\n\n\tif err := DB.AutoMigrate(&QuotedIndexStruct{}); err != nil {\n\t\tt.Fatalf(\"Failed to auto migrate, but got error %v\", err)\n\t}\n}\n\nfunc TestMigrateIndexes(t *testing.T) {\n\ttype IndexStruct struct {\n\t\tgorm.Model\n\t\tName string `gorm:\"size:255;index\"`\n\t}\n\n\tDB.Migrator().DropTable(&IndexStruct{})\n\tDB.AutoMigrate(&IndexStruct{})\n\n\tif err := DB.Migrator().DropIndex(&IndexStruct{}, \"Name\"); err != nil {\n\t\tt.Fatalf(\"Failed to drop index for user's name, got err %v\", err)\n\t}\n\n\tif err := DB.Migrator().CreateIndex(&IndexStruct{}, \"Name\"); err != nil {\n\t\tt.Fatalf(\"Got error when tried to create index: %+v\", err)\n\t}\n\n\tif !DB.Migrator().HasIndex(&IndexStruct{}, \"Name\") {\n\t\tt.Fatalf(\"Failed to find index for user's name\")\n\t}\n\n\tif err := DB.Migrator().DropIndex(&IndexStruct{}, \"Name\"); err != nil {\n\t\tt.Fatalf(\"Failed to drop index for user's name, got err %v\", err)\n\t}\n\n\tif DB.Migrator().HasIndex(&IndexStruct{}, \"Name\") {\n\t\tt.Fatalf(\"Should not find index for user's name after delete\")\n\t}\n\n\tif err := DB.Migrator().CreateIndex(&IndexStruct{}, \"Name\"); err != nil {\n\t\tt.Fatalf(\"Got error when tried to create index: %+v\", err)\n\t}\n\n\tif err := DB.Migrator().RenameIndex(&IndexStruct{}, \"idx_index_structs_name\", \"idx_users_name_1\"); err != nil {\n\t\tt.Fatalf(\"no error should happen when rename index, but got %v\", err)\n\t}\n\n\tif !DB.Migrator().HasIndex(&IndexStruct{}, \"idx_users_name_1\") {\n\t\tt.Fatalf(\"Should find index for user's name after rename\")\n\t}\n\n\tif err := DB.Migrator().DropIndex(&IndexStruct{}, \"idx_users_name_1\"); err != nil {\n\t\tt.Fatalf(\"Failed to drop index for user's name, got err %v\", err)\n\t}\n\n\tif DB.Migrator().HasIndex(&IndexStruct{}, \"idx_users_name_1\") {\n\t\tt.Fatalf(\"Should not find index for user's name after delete\")\n\t}\n}\n\nfunc TestTiDBMigrateColumns(t *testing.T) {\n\tif !isTiDB() {\n\t\tt.Skip()\n\t}\n\n\t// TiDB can't change column constraint and has auto_random feature\n\ttype ColumnStruct struct {\n\t\tID    int `gorm:\"primarykey;default:auto_random()\"`\n\t\tName  string\n\t\tAge   int    `gorm:\"default:18;comment:my age\"`\n\t\tCode  string `gorm:\"unique;comment:my code;\"`\n\t\tCode2 string\n\t\tCode3 string `gorm:\"unique\"`\n\t}\n\n\tDB.Migrator().DropTable(&ColumnStruct{})\n\n\tif err := DB.AutoMigrate(&ColumnStruct{}); err != nil {\n\t\tt.Errorf(\"Failed to migrate, got %v\", err)\n\t}\n\n\ttype ColumnStruct2 struct {\n\t\tID    int    `gorm:\"primarykey;default:auto_random()\"`\n\t\tName  string `gorm:\"size:100\"`\n\t\tCode  string `gorm:\"unique;comment:my code2;default:hello\"`\n\t\tCode2 string `gorm:\"comment:my code2;default:hello\"`\n\t}\n\n\tif err := DB.Table(\"column_structs\").Migrator().AlterColumn(&ColumnStruct{}, \"Name\"); err != nil {\n\t\tt.Fatalf(\"no error should happened when alter column, but got %v\", err)\n\t}\n\n\tif err := DB.Table(\"column_structs\").AutoMigrate(&ColumnStruct2{}); err != nil {\n\t\tt.Fatalf(\"no error should happened when auto migrate column, but got %v\", err)\n\t}\n\n\tif columnTypes, err := DB.Migrator().ColumnTypes(&ColumnStruct{}); err != nil {\n\t\tt.Fatalf(\"no error should returns for ColumnTypes\")\n\t} else {\n\t\tstmt := &gorm.Statement{DB: DB}\n\t\tstmt.Parse(&ColumnStruct2{})\n\n\t\tfor _, columnType := range columnTypes {\n\t\t\tswitch columnType.Name() {\n\t\t\tcase \"id\":\n\t\t\t\tif v, ok := columnType.PrimaryKey(); !ok || !v {\n\t\t\t\t\tt.Fatalf(\"column id primary key should be correct, name: %v, column: %#v\", columnType.Name(),\n\t\t\t\t\t\tcolumnType)\n\t\t\t\t}\n\t\t\tcase \"name\":\n\t\t\t\tdataType := DB.Dialector.DataTypeOf(stmt.Schema.LookUpField(columnType.Name()))\n\t\t\t\tif !strings.Contains(strings.ToUpper(dataType), strings.ToUpper(columnType.DatabaseTypeName())) {\n\t\t\t\t\tt.Fatalf(\"column name type should be correct, name: %v, length: %v, expects: %v, column: %#v\",\n\t\t\t\t\t\tcolumnType.Name(), columnType.DatabaseTypeName(), dataType, columnType)\n\t\t\t\t}\n\t\t\t\tif length, ok := columnType.Length(); !ok || length != 100 {\n\t\t\t\t\tt.Fatalf(\"column name length should be correct, name: %v, length: %v, expects: %v, column: %#v\",\n\t\t\t\t\t\tcolumnType.Name(), length, 100, columnType)\n\t\t\t\t}\n\t\t\tcase \"age\":\n\t\t\t\tif v, ok := columnType.DefaultValue(); !ok || v != \"18\" {\n\t\t\t\t\tt.Fatalf(\"column age default value should be correct, name: %v, column: %#v\", columnType.Name(),\n\t\t\t\t\t\tcolumnType)\n\t\t\t\t}\n\t\t\t\tif v, ok := columnType.Comment(); !ok || v != \"my age\" {\n\t\t\t\t\tt.Fatalf(\"column age comment should be correct, name: %v, column: %#v\", columnType.Name(),\n\t\t\t\t\t\tcolumnType)\n\t\t\t\t}\n\t\t\tcase \"code\":\n\t\t\t\tif v, ok := columnType.Unique(); !ok || !v {\n\t\t\t\t\tt.Fatalf(\"column code unique should be correct, name: %v, column: %#v\", columnType.Name(),\n\t\t\t\t\t\tcolumnType)\n\t\t\t\t}\n\t\t\t\tif v, ok := columnType.DefaultValue(); !ok || v != \"hello\" {\n\t\t\t\t\tt.Fatalf(\"column code default value should be correct, name: %v, column: %#v, default value: %v\",\n\t\t\t\t\t\tcolumnType.Name(), columnType, v)\n\t\t\t\t}\n\t\t\t\tif v, ok := columnType.Comment(); !ok || v != \"my code2\" {\n\t\t\t\t\tt.Fatalf(\"column code comment should be correct, name: %v, column: %#v\", columnType.Name(),\n\t\t\t\t\t\tcolumnType)\n\t\t\t\t}\n\t\t\tcase \"code2\":\n\t\t\t\t// Code2 string `gorm:\"comment:my code2;default:hello\"`\n\t\t\t\tif v, ok := columnType.DefaultValue(); !ok || v != \"hello\" {\n\t\t\t\t\tt.Fatalf(\"column code default value should be correct, name: %v, column: %#v, default value: %v\",\n\t\t\t\t\t\tcolumnType.Name(), columnType, v)\n\t\t\t\t}\n\t\t\t\tif v, ok := columnType.Comment(); !ok || v != \"my code2\" {\n\t\t\t\t\tt.Fatalf(\"column code comment should be correct, name: %v, column: %#v\", columnType.Name(),\n\t\t\t\t\t\tcolumnType)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\ttype NewColumnStruct struct {\n\t\tgorm.Model\n\t\tName    string\n\t\tNewName string\n\t}\n\n\tif err := DB.Table(\"column_structs\").Migrator().AddColumn(&NewColumnStruct{}, \"NewName\"); err != nil {\n\t\tt.Fatalf(\"Failed to add column, got %v\", err)\n\t}\n\n\tif !DB.Table(\"column_structs\").Migrator().HasColumn(&NewColumnStruct{}, \"NewName\") {\n\t\tt.Fatalf(\"Failed to find added column\")\n\t}\n\n\tif err := DB.Table(\"column_structs\").Migrator().DropColumn(&NewColumnStruct{}, \"NewName\"); err != nil {\n\t\tt.Fatalf(\"Failed to add column, got %v\", err)\n\t}\n\n\tif DB.Table(\"column_structs\").Migrator().HasColumn(&NewColumnStruct{}, \"NewName\") {\n\t\tt.Fatalf(\"Found deleted column\")\n\t}\n\n\tif err := DB.Table(\"column_structs\").Migrator().AddColumn(&NewColumnStruct{}, \"NewName\"); err != nil {\n\t\tt.Fatalf(\"Failed to add column, got %v\", err)\n\t}\n\n\tif err := DB.Table(\"column_structs\").Migrator().RenameColumn(&NewColumnStruct{}, \"NewName\",\n\t\t\"new_new_name\"); err != nil {\n\t\tt.Fatalf(\"Failed to add column, got %v\", err)\n\t}\n\n\tif !DB.Table(\"column_structs\").Migrator().HasColumn(&NewColumnStruct{}, \"new_new_name\") {\n\t\tt.Fatalf(\"Failed to found renamed column\")\n\t}\n\n\tif err := DB.Table(\"column_structs\").Migrator().DropColumn(&NewColumnStruct{}, \"new_new_name\"); err != nil {\n\t\tt.Fatalf(\"Failed to add column, got %v\", err)\n\t}\n\n\tif DB.Table(\"column_structs\").Migrator().HasColumn(&NewColumnStruct{}, \"new_new_name\") {\n\t\tt.Fatalf(\"Found deleted column\")\n\t}\n}\n\nfunc TestMigrateColumns(t *testing.T) {\n\ttidbSkip(t, \"use another test case\")\n\n\tsqlite := DB.Dialector.Name() == \"sqlite\"\n\tsqlserver := DB.Dialector.Name() == \"sqlserver\"\n\n\ttype ColumnStruct struct {\n\t\tgorm.Model\n\t\tName  string\n\t\tAge   int    `gorm:\"default:18;comment:my age\"`\n\t\tCode  string `gorm:\"unique;comment:my code;\"`\n\t\tCode2 string\n\t\tCode3 string `gorm:\"unique\"`\n\t}\n\n\tDB.Migrator().DropTable(&ColumnStruct{})\n\n\tif err := DB.AutoMigrate(&ColumnStruct{}); err != nil {\n\t\tt.Errorf(\"Failed to migrate, got %v\", err)\n\t}\n\n\ttype ColumnStruct2 struct {\n\t\tgorm.Model\n\t\tName  string `gorm:\"size:100\"`\n\t\tCode  string `gorm:\"unique;comment:my code2;default:hello\"`\n\t\tCode2 string `gorm:\"unique\"`\n\t\t// Code3 string\n\t}\n\n\tif err := DB.Table(\"column_structs\").Migrator().AlterColumn(&ColumnStruct{}, \"Name\"); err != nil {\n\t\tt.Fatalf(\"no error should happened when alter column, but got %v\", err)\n\t}\n\n\tif err := DB.Table(\"column_structs\").AutoMigrate(&ColumnStruct2{}); err != nil {\n\t\tt.Fatalf(\"no error should happened when auto migrate column, but got %v\", err)\n\t}\n\n\tif columnTypes, err := DB.Migrator().ColumnTypes(&ColumnStruct{}); err != nil {\n\t\tt.Fatalf(\"no error should returns for ColumnTypes\")\n\t} else {\n\t\tstmt := &gorm.Statement{DB: DB}\n\t\tstmt.Parse(&ColumnStruct2{})\n\n\t\tfor _, columnType := range columnTypes {\n\t\t\tswitch columnType.Name() {\n\t\t\tcase \"id\":\n\t\t\t\tif v, ok := columnType.PrimaryKey(); !ok || !v {\n\t\t\t\t\tt.Fatalf(\"column id primary key should be correct, name: %v, column: %#v\", columnType.Name(),\n\t\t\t\t\t\tcolumnType)\n\t\t\t\t}\n\t\t\tcase \"name\":\n\t\t\t\tdataType := DB.Dialector.DataTypeOf(stmt.Schema.LookUpField(columnType.Name()))\n\t\t\t\tif !strings.Contains(strings.ToUpper(dataType), strings.ToUpper(columnType.DatabaseTypeName())) {\n\t\t\t\t\tt.Fatalf(\"column name type should be correct, name: %v, length: %v, expects: %v, column: %#v\",\n\t\t\t\t\t\tcolumnType.Name(), columnType.DatabaseTypeName(), dataType, columnType)\n\t\t\t\t}\n\t\t\t\tif length, ok := columnType.Length(); !sqlite && (!ok || length != 100) {\n\t\t\t\t\tt.Fatalf(\"column name length should be correct, name: %v, length: %v, expects: %v, column: %#v\",\n\t\t\t\t\t\tcolumnType.Name(), length, 100, columnType)\n\t\t\t\t}\n\t\t\tcase \"age\":\n\t\t\t\tif v, ok := columnType.DefaultValue(); !ok || v != \"18\" {\n\t\t\t\t\tt.Fatalf(\"column age default value should be correct, name: %v, column: %#v\", columnType.Name(),\n\t\t\t\t\t\tcolumnType)\n\t\t\t\t}\n\t\t\t\tif v, ok := columnType.Comment(); !sqlite && !sqlserver && (!ok || v != \"my age\") {\n\t\t\t\t\tt.Fatalf(\"column age comment should be correct, name: %v, column: %#v\", columnType.Name(),\n\t\t\t\t\t\tcolumnType)\n\t\t\t\t}\n\t\t\tcase \"code\":\n\t\t\t\tif v, ok := columnType.Unique(); !ok || !v {\n\t\t\t\t\tt.Fatalf(\"column code unique should be correct, name: %v, column: %#v\", columnType.Name(),\n\t\t\t\t\t\tcolumnType)\n\t\t\t\t}\n\t\t\t\tif v, ok := columnType.DefaultValue(); !sqlserver && (!ok || v != \"hello\") {\n\t\t\t\t\tt.Fatalf(\"column code default value should be correct, name: %v, column: %#v, default value: %v\",\n\t\t\t\t\t\tcolumnType.Name(), columnType, v)\n\t\t\t\t}\n\t\t\t\tif v, ok := columnType.Comment(); !sqlite && !sqlserver && (!ok || v != \"my code2\") {\n\t\t\t\t\tt.Fatalf(\"column code comment should be correct, name: %v, column: %#v\", columnType.Name(),\n\t\t\t\t\t\tcolumnType)\n\t\t\t\t}\n\t\t\tcase \"code2\":\n\t\t\t\tif v, ok := columnType.Unique(); !sqlserver && (!ok || !v) {\n\t\t\t\t\tt.Fatalf(\"column code2 unique should be correct, name: %v, column: %#v\", columnType.Name(),\n\t\t\t\t\t\tcolumnType)\n\t\t\t\t}\n\t\t\tcase \"code3\":\n\t\t\t\t// TODO\n\t\t\t\t// if v, ok := columnType.Unique(); !ok || v {\n\t\t\t\t// \tt.Fatalf(\"column code unique should be correct, name: %v, column: %#v\", columnType.Name(), columnType)\n\t\t\t\t// }\n\t\t\t}\n\t\t}\n\t}\n\n\ttype NewColumnStruct struct {\n\t\tgorm.Model\n\t\tName    string\n\t\tNewName string\n\t}\n\n\tif err := DB.Table(\"column_structs\").Migrator().AddColumn(&NewColumnStruct{}, \"NewName\"); err != nil {\n\t\tt.Fatalf(\"Failed to add column, got %v\", err)\n\t}\n\n\tif !DB.Table(\"column_structs\").Migrator().HasColumn(&NewColumnStruct{}, \"NewName\") {\n\t\tt.Fatalf(\"Failed to find added column\")\n\t}\n\n\tif err := DB.Table(\"column_structs\").Migrator().DropColumn(&NewColumnStruct{}, \"NewName\"); err != nil {\n\t\tt.Fatalf(\"Failed to add column, got %v\", err)\n\t}\n\n\tif DB.Table(\"column_structs\").Migrator().HasColumn(&NewColumnStruct{}, \"NewName\") {\n\t\tt.Fatalf(\"Found deleted column\")\n\t}\n\n\tif err := DB.Table(\"column_structs\").Migrator().AddColumn(&NewColumnStruct{}, \"NewName\"); err != nil {\n\t\tt.Fatalf(\"Failed to add column, got %v\", err)\n\t}\n\n\tif err := DB.Table(\"column_structs\").Migrator().RenameColumn(&NewColumnStruct{}, \"NewName\",\n\t\t\"new_new_name\"); err != nil {\n\t\tt.Fatalf(\"Failed to add column, got %v\", err)\n\t}\n\n\tif !DB.Table(\"column_structs\").Migrator().HasColumn(&NewColumnStruct{}, \"new_new_name\") {\n\t\tt.Fatalf(\"Failed to found renamed column\")\n\t}\n\n\tif err := DB.Table(\"column_structs\").Migrator().DropColumn(&NewColumnStruct{}, \"new_new_name\"); err != nil {\n\t\tt.Fatalf(\"Failed to add column, got %v\", err)\n\t}\n\n\tif DB.Table(\"column_structs\").Migrator().HasColumn(&NewColumnStruct{}, \"new_new_name\") {\n\t\tt.Fatalf(\"Found deleted column\")\n\t}\n}\n\nfunc TestMigrateConstraint(t *testing.T) {\n\tnames := []string{\"Account\", \"fk_users_account\", \"Pets\", \"fk_users_pets\", \"Company\", \"fk_users_company\", \"Team\", \"fk_users_team\", \"Languages\", \"fk_users_languages\"}\n\n\tfor _, name := range names {\n\t\tif !DB.Migrator().HasConstraint(&User{}, name) {\n\t\t\tDB.Migrator().CreateConstraint(&User{}, name)\n\t\t}\n\n\t\tif err := DB.Migrator().DropConstraint(&User{}, name); err != nil {\n\t\t\tt.Fatalf(\"failed to drop constraint %v, got error %v\", name, err)\n\t\t}\n\n\t\tif DB.Migrator().HasConstraint(&User{}, name) {\n\t\t\tt.Fatalf(\"constraint %v should been deleted\", name)\n\t\t}\n\n\t\tif err := DB.Migrator().CreateConstraint(&User{}, name); err != nil {\n\t\t\tt.Fatalf(\"failed to create constraint %v, got error %v\", name, err)\n\t\t}\n\n\t\tif !DB.Migrator().HasConstraint(&User{}, name) {\n\t\t\tt.Fatalf(\"failed to found constraint %v\", name)\n\t\t}\n\t}\n}\n\ntype DynamicUser struct {\n\tgorm.Model\n\tName      string\n\tCompanyID string `gorm:\"index\"`\n}\n\n// To test auto migrate crate indexes for dynamic table name\n// https://github.com/go-gorm/gorm/issues/4752\nfunc TestMigrateIndexesWithDynamicTableName(t *testing.T) {\n\t// Create primary table\n\tif err := DB.AutoMigrate(&DynamicUser{}); err != nil {\n\t\tt.Fatalf(\"AutoMigrate create table error: %#v\", err)\n\t}\n\n\t// Create sub tables\n\tfor _, v := range []string{\"01\", \"02\", \"03\"} {\n\t\ttableName := \"dynamic_users_\" + v\n\t\tm := DB.Scopes(func(db *gorm.DB) *gorm.DB {\n\t\t\treturn db.Table(tableName)\n\t\t}).Migrator()\n\n\t\tif err := m.AutoMigrate(&DynamicUser{}); err != nil {\n\t\t\tt.Fatalf(\"AutoMigrate create table error: %#v\", err)\n\t\t}\n\n\t\tif !m.HasTable(tableName) {\n\t\t\tt.Fatalf(\"AutoMigrate expected %#v exist, but not.\", tableName)\n\t\t}\n\n\t\tif !m.HasIndex(&DynamicUser{}, \"CompanyID\") {\n\t\t\tt.Fatalf(\"Should have index on %s\", \"CompanyI.\")\n\t\t}\n\n\t\tif !m.HasIndex(&DynamicUser{}, \"DeletedAt\") {\n\t\t\tt.Fatalf(\"Should have index on deleted_at.\")\n\t\t}\n\t}\n}\n\n// check column order after migration, flaky test\n// https://github.com/go-gorm/gorm/issues/4351\nfunc TestMigrateColumnOrder(t *testing.T) {\n\ttype UserMigrateColumn struct {\n\t\tID uint\n\t}\n\tDB.Migrator().DropTable(&UserMigrateColumn{})\n\tDB.AutoMigrate(&UserMigrateColumn{})\n\n\ttype UserMigrateColumn2 struct {\n\t\tID  uint\n\t\tF1  string\n\t\tF2  string\n\t\tF3  string\n\t\tF4  string\n\t\tF5  string\n\t\tF6  string\n\t\tF7  string\n\t\tF8  string\n\t\tF9  string\n\t\tF10 string\n\t\tF11 string\n\t\tF12 string\n\t\tF13 string\n\t\tF14 string\n\t\tF15 string\n\t\tF16 string\n\t\tF17 string\n\t\tF18 string\n\t\tF19 string\n\t\tF20 string\n\t\tF21 string\n\t\tF22 string\n\t\tF23 string\n\t\tF24 string\n\t\tF25 string\n\t\tF26 string\n\t\tF27 string\n\t\tF28 string\n\t\tF29 string\n\t\tF30 string\n\t\tF31 string\n\t\tF32 string\n\t\tF33 string\n\t\tF34 string\n\t\tF35 string\n\t}\n\tif err := DB.Table(\"user_migrate_columns\").AutoMigrate(&UserMigrateColumn2{}); err != nil {\n\t\tt.Fatalf(\"failed to auto migrate, got error: %v\", err)\n\t}\n\n\tcolumnTypes, err := DB.Table(\"user_migrate_columns\").Migrator().ColumnTypes(&UserMigrateColumn2{})\n\tif err != nil {\n\t\tt.Fatalf(\"failed to get column types, got error: %v\", err)\n\t}\n\ttyp := reflect.Indirect(reflect.ValueOf(&UserMigrateColumn2{})).Type()\n\tnumField := typ.NumField()\n\tif numField != len(columnTypes) {\n\t\tt.Fatalf(\"column's number not match struct and ddl, %d != %d\", numField, len(columnTypes))\n\t}\n\tnamer := schema.NamingStrategy{}\n\tfor i := 0; i < numField; i++ {\n\t\texpectName := namer.ColumnName(\"\", typ.Field(i).Name)\n\t\tif columnTypes[i].Name() != expectName {\n\t\t\tt.Fatalf(\"column order not match struct and ddl, idx %d: %s != %s\",\n\t\t\t\ti, columnTypes[i].Name(), expectName)\n\t\t}\n\t}\n}\n\n// https://github.com/go-gorm/gorm/issues/5047\nfunc TestMigrateSerialColumn(t *testing.T) {\n\tif DB.Dialector.Name() != \"postgres\" && DB.Dialector.Name() != \"gaussdb\" {\n\t\treturn\n\t}\n\n\ttype Event struct {\n\t\tID  uint `gorm:\"primarykey\"`\n\t\tUID uint32\n\t}\n\n\ttype Event1 struct {\n\t\tID  uint   `gorm:\"primarykey\"`\n\t\tUID uint32 `gorm:\"not null;autoIncrement\"`\n\t}\n\n\ttype Event2 struct {\n\t\tID  uint   `gorm:\"primarykey\"`\n\t\tUID uint16 `gorm:\"not null;autoIncrement\"`\n\t}\n\n\tvar err error\n\terr = DB.Migrator().DropTable(&Event{})\n\tif err != nil {\n\t\tt.Errorf(\"DropTable err:%v\", err)\n\t}\n\n\t// create sequence\n\terr = DB.Table(\"events\").AutoMigrate(&Event1{})\n\tif err != nil {\n\t\tt.Errorf(\"AutoMigrate err:%v\", err)\n\t}\n\n\t// delete sequence\n\terr = DB.Table(\"events\").AutoMigrate(&Event{})\n\tif err != nil {\n\t\tt.Errorf(\"AutoMigrate err:%v\", err)\n\t}\n\n\t// update sequence\n\terr = DB.Table(\"events\").AutoMigrate(&Event1{})\n\tif err != nil {\n\t\tt.Errorf(\"AutoMigrate err:%v\", err)\n\t}\n\terr = DB.Table(\"events\").AutoMigrate(&Event2{})\n\tif err != nil {\n\t\tt.Errorf(\"AutoMigrate err:%v\", err)\n\t}\n\n\tDB.Table(\"events\").Save(&Event2{})\n\tDB.Table(\"events\").Save(&Event2{})\n\tDB.Table(\"events\").Save(&Event2{})\n\n\tevents := make([]*Event, 0)\n\tDB.Table(\"events\").Find(&events)\n\n\tAssertEqual(t, 3, len(events))\n\tfor _, v := range events {\n\t\tAssertEqual(t, v.ID, v.UID)\n\t}\n}\n\n// https://github.com/go-gorm/gorm/issues/5300\nfunc TestMigrateWithSpecialName(t *testing.T) {\n\tvar err error\n\terr = DB.AutoMigrate(&Coupon{})\n\tif err != nil {\n\t\tt.Fatalf(\"AutoMigrate err:%v\", err)\n\t}\n\terr = DB.Table(\"coupon_product_1\").AutoMigrate(&CouponProduct{})\n\tif err != nil {\n\t\tt.Fatalf(\"AutoMigrate err:%v\", err)\n\t}\n\terr = DB.Table(\"coupon_product_2\").AutoMigrate(&CouponProduct{})\n\tif err != nil {\n\t\tt.Fatalf(\"AutoMigrate err:%v\", err)\n\t}\n\n\tAssertEqual(t, true, DB.Migrator().HasTable(\"coupons\"))\n\tAssertEqual(t, true, DB.Migrator().HasTable(\"coupon_product_1\"))\n\tAssertEqual(t, true, DB.Migrator().HasTable(\"coupon_product_2\"))\n}\n\n// https://github.com/go-gorm/gorm/issues/4760\nfunc TestMigrateAutoIncrement(t *testing.T) {\n\ttype AutoIncrementStruct struct {\n\t\tID     int64   `gorm:\"primarykey;autoIncrement\"`\n\t\tField1 uint32  `gorm:\"column:field1\"`\n\t\tField2 float32 `gorm:\"column:field2\"`\n\t}\n\n\tif err := DB.AutoMigrate(&AutoIncrementStruct{}); err != nil {\n\t\tt.Fatalf(\"AutoMigrate err: %v\", err)\n\t}\n\n\tconst ROWS = 10\n\tfor idx := 0; idx < ROWS; idx++ {\n\t\tif err := DB.Create(&AutoIncrementStruct{}).Error; err != nil {\n\t\t\tt.Fatalf(\"create auto_increment_struct fail, err: %v\", err)\n\t\t}\n\t}\n\n\trows := make([]*AutoIncrementStruct, 0, ROWS)\n\tif err := DB.Order(\"id ASC\").Find(&rows).Error; err != nil {\n\t\tt.Fatalf(\"find auto_increment_struct fail, err: %v\", err)\n\t}\n\n\tids := make([]int64, 0, len(rows))\n\tfor _, row := range rows {\n\t\tids = append(ids, row.ID)\n\t}\n\tlastID := ids[len(ids)-1]\n\n\tif err := DB.Where(\"id IN (?)\", ids).Delete(&AutoIncrementStruct{}).Error; err != nil {\n\t\tt.Fatalf(\"delete auto_increment_struct fail, err: %v\", err)\n\t}\n\n\tnewRow := &AutoIncrementStruct{}\n\tif err := DB.Create(newRow).Error; err != nil {\n\t\tt.Fatalf(\"create auto_increment_struct fail, err: %v\", err)\n\t}\n\n\tAssertEqual(t, newRow.ID, lastID+1)\n}\n\n// https://github.com/go-gorm/gorm/issues/5320\nfunc TestPrimarykeyID(t *testing.T) {\n\tif DB.Dialector.Name() != \"postgres\" {\n\t\treturn\n\t}\n\n\ttype MissPKLanguage struct {\n\t\tID   string `gorm:\"type:uuid;default:uuid_generate_v4()\"`\n\t\tName string\n\t}\n\n\ttype MissPKUser struct {\n\t\tID              string           `gorm:\"type:uuid;default:uuid_generate_v4()\"`\n\t\tMissPKLanguages []MissPKLanguage `gorm:\"many2many:miss_pk_user_languages;\"`\n\t}\n\n\tvar err error\n\terr = DB.Migrator().DropTable(&MissPKUser{}, &MissPKLanguage{})\n\tif err != nil {\n\t\tt.Fatalf(\"DropTable err:%v\", err)\n\t}\n\n\tDB.Exec(`CREATE EXTENSION IF NOT EXISTS \"uuid-ossp\";`)\n\n\terr = DB.AutoMigrate(&MissPKUser{}, &MissPKLanguage{})\n\tif err != nil {\n\t\tt.Fatalf(\"AutoMigrate err:%v\", err)\n\t}\n\n\t// patch\n\terr = DB.AutoMigrate(&MissPKUser{}, &MissPKLanguage{})\n\tif err != nil {\n\t\tt.Fatalf(\"AutoMigrate err:%v\", err)\n\t}\n}\n\nfunc TestPrimarykeyIDGaussDB(t *testing.T) {\n\tt.Skipf(\"This test case skipped, because of gaussdb not support uuid-ossp plugin (SQLSTATE 58P01)\")\n\tif DB.Dialector.Name() != \"gaussdb\" {\n\t\treturn\n\t}\n\n\ttype MissPKLanguage struct {\n\t\tID   string `gorm:\"type:uuid;default:uuid_generate_v4()\"`\n\t\tName string\n\t}\n\n\ttype MissPKUser struct {\n\t\tID              string           `gorm:\"type:uuid;default:uuid_generate_v4()\"`\n\t\tMissPKLanguages []MissPKLanguage `gorm:\"many2many:miss_pk_user_languages;\"`\n\t}\n\n\tvar err error\n\terr = DB.Migrator().DropTable(&MissPKUser{}, &MissPKLanguage{})\n\tif err != nil {\n\t\tt.Fatalf(\"DropTable err:%v\", err)\n\t}\n\t// TODO: ERROR: could not open extension control file: No such file or directory (SQLSTATE 58P01)\n\tDB.Exec(`CREATE EXTENSION IF NOT EXISTS \"uuid-ossp\";`)\n\n\terr = DB.AutoMigrate(&MissPKUser{}, &MissPKLanguage{})\n\tif err != nil {\n\t\tt.Fatalf(\"AutoMigrate err:%v\", err)\n\t}\n\n\t// patch\n\terr = DB.AutoMigrate(&MissPKUser{}, &MissPKLanguage{})\n\tif err != nil {\n\t\tt.Fatalf(\"AutoMigrate err:%v\", err)\n\t}\n}\n\nfunc TestCurrentTimestamp(t *testing.T) {\n\tif DB.Dialector.Name() != \"mysql\" {\n\t\treturn\n\t}\n\ttype CurrentTimestampTest struct {\n\t\tID     string     `gorm:\"primary_key\"`\n\t\tTimeAt *time.Time `gorm:\"type:datetime;not null;default:CURRENT_TIMESTAMP;unique\"`\n\t}\n\tvar err error\n\terr = DB.Migrator().DropTable(&CurrentTimestampTest{})\n\tif err != nil {\n\t\tt.Errorf(\"DropTable err:%v\", err)\n\t}\n\terr = DB.AutoMigrate(&CurrentTimestampTest{})\n\tif err != nil {\n\t\tt.Fatalf(\"AutoMigrate err:%v\", err)\n\t}\n\n\terr = DB.AutoMigrate(&CurrentTimestampTest{})\n\tif err != nil {\n\t\tt.Fatalf(\"AutoMigrate err:%v\", err)\n\t}\n\tAssertEqual(t, true, DB.Migrator().HasConstraint(&CurrentTimestampTest{}, \"uni_current_timestamp_tests_time_at\"))\n\tAssertEqual(t, false, DB.Migrator().HasIndex(&CurrentTimestampTest{}, \"time_at\"))\n\tAssertEqual(t, false, DB.Migrator().HasIndex(&CurrentTimestampTest{}, \"time_at_2\"))\n}\n\nfunc TestUniqueColumn(t *testing.T) {\n\tif DB.Dialector.Name() != \"mysql\" {\n\t\treturn\n\t}\n\n\ttype UniqueTest struct {\n\t\tID   string `gorm:\"primary_key\"`\n\t\tName string `gorm:\"unique\"`\n\t}\n\n\ttype UniqueTest2 struct {\n\t\tID   string `gorm:\"primary_key\"`\n\t\tName string `gorm:\"unique;default:NULL\"`\n\t}\n\n\ttype UniqueTest3 struct {\n\t\tID   string `gorm:\"primary_key\"`\n\t\tName string `gorm:\"unique;default:''\"`\n\t}\n\n\ttype UniqueTest4 struct {\n\t\tID   string `gorm:\"primary_key\"`\n\t\tName string `gorm:\"unique;default:'123'\"`\n\t}\n\n\tvar err error\n\terr = DB.Migrator().DropTable(&UniqueTest{})\n\tif err != nil {\n\t\tt.Errorf(\"DropTable err:%v\", err)\n\t}\n\n\terr = DB.AutoMigrate(&UniqueTest{})\n\tif err != nil {\n\t\tt.Fatalf(\"AutoMigrate err:%v\", err)\n\t}\n\n\t// null -> null\n\terr = DB.AutoMigrate(&UniqueTest{})\n\tif err != nil {\n\t\tt.Fatalf(\"AutoMigrate err:%v\", err)\n\t}\n\n\tct, err := findColumnType(&UniqueTest{}, \"name\")\n\tif err != nil {\n\t\tt.Fatalf(\"findColumnType err:%v\", err)\n\t}\n\n\tvalue, ok := ct.DefaultValue()\n\tAssertEqual(t, \"\", value)\n\tAssertEqual(t, false, ok)\n\n\t// null -> null\n\terr = DB.Table(\"unique_tests\").AutoMigrate(&UniqueTest2{})\n\tif err != nil {\n\t\tt.Fatalf(\"AutoMigrate err:%v\", err)\n\t}\n\n\t// not trigger alert column\n\tAssertEqual(t, true, DB.Migrator().HasConstraint(&UniqueTest{}, \"uni_unique_tests_name\"))\n\tAssertEqual(t, false, DB.Migrator().HasIndex(&UniqueTest{}, \"name\"))\n\tAssertEqual(t, false, DB.Migrator().HasIndex(&UniqueTest{}, \"name_1\"))\n\tAssertEqual(t, false, DB.Migrator().HasIndex(&UniqueTest{}, \"name_2\"))\n\n\tct, err = findColumnType(&UniqueTest{}, \"name\")\n\tif err != nil {\n\t\tt.Fatalf(\"findColumnType err:%v\", err)\n\t}\n\n\tvalue, ok = ct.DefaultValue()\n\tAssertEqual(t, \"\", value)\n\tAssertEqual(t, false, ok)\n\n\ttidbSkip(t, \"can't change column constraint\")\n\n\t// null -> empty string\n\terr = DB.Table(\"unique_tests\").AutoMigrate(&UniqueTest3{})\n\tif err != nil {\n\t\tt.Fatalf(\"AutoMigrate err:%v\", err)\n\t}\n\n\tct, err = findColumnType(&UniqueTest{}, \"name\")\n\tif err != nil {\n\t\tt.Fatalf(\"findColumnType err:%v\", err)\n\t}\n\n\tvalue, ok = ct.DefaultValue()\n\tAssertEqual(t, \"\", value)\n\tAssertEqual(t, true, ok)\n\n\t//  empty string -> 123\n\terr = DB.Table(\"unique_tests\").AutoMigrate(&UniqueTest4{})\n\tif err != nil {\n\t\tt.Fatalf(\"AutoMigrate err:%v\", err)\n\t}\n\n\tct, err = findColumnType(&UniqueTest{}, \"name\")\n\tif err != nil {\n\t\tt.Fatalf(\"findColumnType err:%v\", err)\n\t}\n\n\tvalue, ok = ct.DefaultValue()\n\tAssertEqual(t, \"123\", value)\n\tAssertEqual(t, true, ok)\n\n\t//  123 -> null\n\terr = DB.Table(\"unique_tests\").AutoMigrate(&UniqueTest2{})\n\tif err != nil {\n\t\tt.Fatalf(\"AutoMigrate err:%v\", err)\n\t}\n\n\tct, err = findColumnType(&UniqueTest{}, \"name\")\n\tif err != nil {\n\t\tt.Fatalf(\"findColumnType err:%v\", err)\n\t}\n\n\tvalue, ok = ct.DefaultValue()\n\tAssertEqual(t, \"\", value)\n\tAssertEqual(t, false, ok)\n}\n\nfunc findColumnType(dest interface{}, columnName string) (\n\tfoundColumn gorm.ColumnType, err error,\n) {\n\tcolumnTypes, err := DB.Migrator().ColumnTypes(dest)\n\tif err != nil {\n\t\terr = fmt.Errorf(\"ColumnTypes err:%v\", err)\n\t\treturn\n\t}\n\n\tfor _, c := range columnTypes {\n\t\tif c.Name() == columnName {\n\t\t\tfoundColumn = c\n\t\t\tbreak\n\t\t}\n\t}\n\treturn\n}\n\nfunc TestInvalidCachedPlanSimpleProtocol(t *testing.T) {\n\tif DB.Dialector.Name() != \"postgres\" {\n\t\treturn\n\t}\n\n\tdb, err := gorm.Open(postgres.Open(postgresDSN), &gorm.Config{})\n\tif err != nil {\n\t\tt.Errorf(\"Open err:%v\", err)\n\t}\n\n\ttype Object1 struct{}\n\ttype Object2 struct {\n\t\tField1 string\n\t}\n\ttype Object3 struct {\n\t\tField2 string\n\t}\n\tdb.Migrator().DropTable(\"objects\")\n\n\terr = db.Table(\"objects\").AutoMigrate(&Object1{})\n\tif err != nil {\n\t\tt.Errorf(\"AutoMigrate err:%v\", err)\n\t}\n\n\terr = db.Table(\"objects\").AutoMigrate(&Object2{})\n\tif err != nil {\n\t\tt.Errorf(\"AutoMigrate err:%v\", err)\n\t}\n\n\terr = db.Table(\"objects\").AutoMigrate(&Object3{})\n\tif err != nil {\n\t\tt.Errorf(\"AutoMigrate err:%v\", err)\n\t}\n}\n\n// TODO: ERROR: must have at least one column (SQLSTATE 0A000)\nfunc TestInvalidCachedPlanSimpleProtocolGaussDB(t *testing.T) {\n\tt.Skipf(\"This test case skipped, because of gaussdb not support creaing empty table(SQLSTATE 0A000)\")\n\tif DB.Dialector.Name() != \"gaussdb\" {\n\t\treturn\n\t}\n\n\tdb, err := gorm.Open(gaussdb.Open(gaussdbDSN), &gorm.Config{})\n\tif err != nil {\n\t\tt.Errorf(\"Open err:%v\", err)\n\t}\n\n\ttype Object1 struct{}\n\ttype Object2 struct {\n\t\tField1 string\n\t}\n\ttype Object3 struct {\n\t\tField2 string\n\t}\n\tdb.Migrator().DropTable(\"objects\")\n\n\terr = db.Table(\"objects\").AutoMigrate(&Object1{})\n\tif err != nil {\n\t\tt.Errorf(\"AutoMigrate err:%v\", err)\n\t}\n\n\terr = db.Table(\"objects\").AutoMigrate(&Object2{})\n\tif err != nil {\n\t\tt.Errorf(\"AutoMigrate err:%v\", err)\n\t}\n\n\terr = db.Table(\"objects\").AutoMigrate(&Object3{})\n\tif err != nil {\n\t\tt.Errorf(\"AutoMigrate err:%v\", err)\n\t}\n}\n\nfunc TestDifferentTypeWithoutDeclaredLength(t *testing.T) {\n\ttype DiffType struct {\n\t\tID   uint\n\t\tName string `gorm:\"type:varchar(20)\"`\n\t}\n\n\ttype DiffType1 struct {\n\t\tID   uint\n\t\tName string `gorm:\"type:text\"`\n\t}\n\n\tvar err error\n\tDB.Migrator().DropTable(&DiffType{})\n\n\terr = DB.AutoMigrate(&DiffType{})\n\tif err != nil {\n\t\tt.Errorf(\"AutoMigrate err:%v\", err)\n\t}\n\n\tct, err := findColumnType(&DiffType{}, \"name\")\n\tif err != nil {\n\t\tt.Errorf(\"findColumnType err:%v\", err)\n\t}\n\n\tAssertEqual(t, \"varchar\", strings.ToLower(ct.DatabaseTypeName()))\n\n\terr = DB.Table(\"diff_types\").AutoMigrate(&DiffType1{})\n\tif err != nil {\n\t\tt.Errorf(\"AutoMigrate err:%v\", err)\n\t}\n\n\tct, err = findColumnType(&DiffType{}, \"name\")\n\tif err != nil {\n\t\tt.Errorf(\"findColumnType err:%v\", err)\n\t}\n\n\tAssertEqual(t, \"text\", strings.ToLower(ct.DatabaseTypeName()))\n}\n\nfunc TestMigrateArrayTypeModel(t *testing.T) {\n\tif DB.Dialector.Name() != \"postgres\" && DB.Dialector.Name() != \"gaussdb\" {\n\t\treturn\n\t}\n\n\ttype ArrayTypeModel struct {\n\t\tID              uint\n\t\tNumber          string     `gorm:\"type:varchar(51);NOT NULL\"`\n\t\tTextArray       []string   `gorm:\"type:text[];NOT NULL\"`\n\t\tNestedTextArray [][]string `gorm:\"type:text[][]\"`\n\t\tNestedIntArray  [][]int64  `gorm:\"type:integer[3][3]\"`\n\t}\n\n\tvar err error\n\tDB.Migrator().DropTable(&ArrayTypeModel{})\n\n\terr = DB.AutoMigrate(&ArrayTypeModel{})\n\tAssertEqual(t, nil, err)\n\n\tct, err := findColumnType(&ArrayTypeModel{}, \"number\")\n\tAssertEqual(t, nil, err)\n\tAssertEqual(t, \"varchar\", ct.DatabaseTypeName())\n\n\tct, err = findColumnType(&ArrayTypeModel{}, \"text_array\")\n\tAssertEqual(t, nil, err)\n\tAssertEqual(t, \"text[]\", ct.DatabaseTypeName())\n\n\tct, err = findColumnType(&ArrayTypeModel{}, \"nested_text_array\")\n\tAssertEqual(t, nil, err)\n\tAssertEqual(t, \"text[]\", ct.DatabaseTypeName())\n\n\tct, err = findColumnType(&ArrayTypeModel{}, \"nested_int_array\")\n\tAssertEqual(t, nil, err)\n\tAssertEqual(t, \"integer[]\", ct.DatabaseTypeName())\n}\n\ntype mockMigrator struct {\n\tgorm.Migrator\n}\n\nfunc (mm mockMigrator) AlterColumn(dst interface{}, field string) error {\n\terr := mm.Migrator.AlterColumn(dst, field)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn fmt.Errorf(\"trigger alter column error, field: %s\", field)\n}\n\nfunc TestMigrateDonotAlterColumn(t *testing.T) {\n\twrapMockMigrator := func(m gorm.Migrator) mockMigrator {\n\t\treturn mockMigrator{\n\t\t\tMigrator: m,\n\t\t}\n\t}\n\tm := DB.Migrator()\n\tmockM := wrapMockMigrator(m)\n\n\ttype NotTriggerUpdate struct {\n\t\tID  uint\n\t\tF1  uint16\n\t\tF2  uint32\n\t\tF3  int\n\t\tF4  int64\n\t\tF5  string\n\t\tF6  float32\n\t\tF7  float64\n\t\tF8  time.Time\n\t\tF9  bool\n\t\tF10 []byte\n\t}\n\n\tvar err error\n\terr = mockM.DropTable(&NotTriggerUpdate{})\n\tAssertEqual(t, err, nil)\n\terr = mockM.AutoMigrate(&NotTriggerUpdate{})\n\tAssertEqual(t, err, nil)\n\terr = mockM.AutoMigrate(&NotTriggerUpdate{})\n\tAssertEqual(t, err, nil)\n}\n\nfunc TestMigrateSameEmbeddedFieldName(t *testing.T) {\n\ttype UserStat struct {\n\t\tGroundDestroyCount int\n\t}\n\n\ttype GameUser struct {\n\t\tgorm.Model\n\t\tStatAb UserStat `gorm:\"embedded;embeddedPrefix:stat_ab_\"`\n\t}\n\n\ttype UserStat1 struct {\n\t\tGroundDestroyCount string\n\t}\n\n\ttype GroundRate struct {\n\t\tGroundDestroyCount int\n\t}\n\n\ttype GameUser1 struct {\n\t\tgorm.Model\n\t\tStatAb       UserStat1  `gorm:\"embedded;embeddedPrefix:stat_ab_\"`\n\t\tGroundRateRb GroundRate `gorm:\"embedded;embeddedPrefix:rate_ground_rb_\"`\n\t}\n\n\tDB.Migrator().DropTable(&GameUser{})\n\terr := DB.AutoMigrate(&GameUser{})\n\tAssertEqual(t, nil, err)\n\n\terr = DB.Table(\"game_users\").AutoMigrate(&GameUser1{})\n\tAssertEqual(t, nil, err)\n\n\t_, err = findColumnType(&GameUser{}, \"stat_ab_ground_destroy_count\")\n\tAssertEqual(t, nil, err)\n\n\t_, err = findColumnType(&GameUser{}, \"rate_ground_rb_ground_destroy_count\")\n\tAssertEqual(t, nil, err)\n}\n\nfunc TestMigrateWithDefaultValue(t *testing.T) {\n\tif DB.Dialector.Name() == \"sqlserver\" {\n\t\t// sqlserver driver treats NULL and 'NULL' the same\n\t\tt.Skip(\"skip sqlserver\")\n\t}\n\n\ttype NullModel struct {\n\t\tID      uint\n\t\tContent string `gorm:\"default:null\"`\n\t}\n\n\ttype NullStringModel struct {\n\t\tID      uint\n\t\tContent string `gorm:\"default:'null'\"`\n\t\tActive  bool   `gorm:\"default:false\"`\n\t}\n\n\ttableName := \"null_string_model\"\n\n\tDB.Migrator().DropTable(tableName)\n\n\terr := DB.Table(tableName).AutoMigrate(&NullModel{})\n\tAssertEqual(t, err, nil)\n\n\t// default null -> 'null'\n\terr = DB.Table(tableName).AutoMigrate(&NullStringModel{})\n\tAssertEqual(t, err, nil)\n\n\tcolumnType, err := findColumnType(tableName, \"content\")\n\tAssertEqual(t, err, nil)\n\n\tdefVal, ok := columnType.DefaultValue()\n\tAssertEqual(t, defVal, \"null\")\n\tAssertEqual(t, ok, true)\n\n\tcolumnType2, err := findColumnType(tableName, \"active\")\n\tAssertEqual(t, err, nil)\n\n\tdefVal, ok = columnType2.DefaultValue()\n\tbv, _ := strconv.ParseBool(defVal)\n\tAssertEqual(t, bv, false)\n\tAssertEqual(t, ok, true)\n\n\t// default 'null' -> 'null'\n\tsession := DB.Session(&gorm.Session{Logger: Tracer{\n\t\tLogger: DB.Config.Logger,\n\t\tTest: func(ctx context.Context, begin time.Time, fc func() (sql string, rowsAffected int64), err error) {\n\t\t\tsql, _ := fc()\n\t\t\tif strings.HasPrefix(sql, \"ALTER TABLE\") {\n\t\t\t\tt.Errorf(\"shouldn't execute: sql=%s\", sql)\n\t\t\t}\n\t\t},\n\t}})\n\terr = session.Table(tableName).AutoMigrate(&NullStringModel{})\n\tAssertEqual(t, err, nil)\n\n\tcolumnType, err = findColumnType(tableName, \"content\")\n\tAssertEqual(t, err, nil)\n\n\tdefVal, ok = columnType.DefaultValue()\n\tAssertEqual(t, defVal, \"null\")\n\tAssertEqual(t, ok, true)\n\n\t// default 'null' -> null\n\terr = DB.Table(tableName).AutoMigrate(&NullModel{})\n\tAssertEqual(t, err, nil)\n\n\tcolumnType, err = findColumnType(tableName, \"content\")\n\tAssertEqual(t, err, nil)\n\n\tdefVal, ok = columnType.DefaultValue()\n\tAssertEqual(t, defVal, \"\")\n\tAssertEqual(t, ok, false)\n}\n\nfunc TestMigrateMySQLWithCustomizedTypes(t *testing.T) {\n\tif DB.Dialector.Name() != \"mysql\" {\n\t\tt.Skip()\n\t}\n\n\ttype MyTable struct {\n\t\tDef string `gorm:\"size:512;index:idx_def,unique\"`\n\t\tAbc string `gorm:\"size:65000000\"`\n\t}\n\n\tDB.Migrator().DropTable(\"my_tables\")\n\n\tsql := \"CREATE TABLE `my_tables` (`def` varchar(512),`abc` longtext,UNIQUE INDEX `idx_def` (`def`))\"\n\tif err := DB.Exec(sql).Error; err != nil {\n\t\tt.Errorf(\"Failed, got error: %v\", err)\n\t}\n\n\tsession := DB.Session(&gorm.Session{Logger: Tracer{\n\t\tLogger: DB.Config.Logger,\n\t\tTest: func(ctx context.Context, begin time.Time, fc func() (sql string, rowsAffected int64), err error) {\n\t\t\tsql, _ := fc()\n\t\t\tif strings.HasPrefix(sql, \"ALTER TABLE\") {\n\t\t\t\tt.Errorf(\"shouldn't execute: sql=%s\", sql)\n\t\t\t}\n\t\t},\n\t}})\n\n\tif err := session.AutoMigrate(&MyTable{}); err != nil {\n\t\tt.Errorf(\"Failed, got error: %v\", err)\n\t}\n}\n\nfunc TestMigrateIgnoreRelations(t *testing.T) {\n\ttype RelationModel1 struct {\n\t\tID uint\n\t}\n\ttype RelationModel2 struct {\n\t\tID uint\n\t}\n\ttype RelationModel3 struct {\n\t\tID               uint\n\t\tRelationModel1ID uint\n\t\tRelationModel1   *RelationModel1\n\t\tRelationModel2ID uint\n\t\tRelationModel2   *RelationModel2 `gorm:\"-:migration\"`\n\t}\n\n\tvar err error\n\t_ = DB.Migrator().DropTable(&RelationModel1{}, &RelationModel2{}, &RelationModel3{})\n\n\ttx := DB.Session(&gorm.Session{})\n\ttx.IgnoreRelationshipsWhenMigrating = true\n\n\terr = tx.AutoMigrate(&RelationModel3{})\n\tif err != nil {\n\t\tt.Errorf(\"AutoMigrate err:%v\", err)\n\t}\n\n\t// RelationModel3 should be existed\n\t_, err = findColumnType(&RelationModel3{}, \"id\")\n\tAssertEqual(t, nil, err)\n\n\t// RelationModel1 should not be existed\n\t_, err = findColumnType(&RelationModel1{}, \"id\")\n\tif err == nil {\n\t\tt.Errorf(\"RelationModel1 should not be migrated\")\n\t}\n\n\t// RelationModel2 should not be existed\n\t_, err = findColumnType(&RelationModel2{}, \"id\")\n\tif err == nil {\n\t\tt.Errorf(\"RelationModel2 should not be migrated\")\n\t}\n\n\ttx.IgnoreRelationshipsWhenMigrating = false\n\n\terr = tx.AutoMigrate(&RelationModel3{})\n\tif err != nil {\n\t\tt.Errorf(\"AutoMigrate err:%v\", err)\n\t}\n\n\t// RelationModel3 should be existed\n\t_, err = findColumnType(&RelationModel3{}, \"id\")\n\tAssertEqual(t, nil, err)\n\n\t// RelationModel1 should be existed\n\t_, err = findColumnType(&RelationModel1{}, \"id\")\n\tAssertEqual(t, nil, err)\n\n\t// RelationModel2 should not be existed\n\t_, err = findColumnType(&RelationModel2{}, \"id\")\n\tif err == nil {\n\t\tt.Errorf(\"RelationModel2 should not be migrated\")\n\t}\n}\n\nfunc TestMigrateView(t *testing.T) {\n\tDB.Save(GetUser(\"joins-args-db\", Config{Pets: 2}))\n\n\tif err := DB.Migrator().CreateView(\"invalid_users_pets\",\n\t\tgorm.ViewOption{Query: nil}); err != gorm.ErrSubQueryRequired {\n\t\tt.Fatalf(\"no view should be created, got %v\", err)\n\t}\n\n\tquery := DB.Model(&User{}).\n\t\tSelect(\"users.id as users_id, users.name as users_name, pets.id as pets_id, pets.name as pets_name\").\n\t\tJoins(\"inner join pets on pets.user_id = users.id\")\n\n\tif err := DB.Migrator().CreateView(\"users_pets\", gorm.ViewOption{Query: query}); err != nil {\n\t\tt.Fatalf(\"Failed to crate view, got %v\", err)\n\t}\n\n\tvar count int64\n\tif err := DB.Table(\"users_pets\").Count(&count).Error; err != nil {\n\t\tt.Fatalf(\"should found created view\")\n\t}\n\n\tif err := DB.Migrator().DropView(\"users_pets\"); err != nil {\n\t\tt.Fatalf(\"Failed to drop view, got %v\", err)\n\t}\n\n\tquery = DB.Model(&User{}).Where(\"age > ?\", 20)\n\tif err := DB.Migrator().CreateView(\"users_view\", gorm.ViewOption{Query: query}); err != nil {\n\t\tt.Fatalf(\"Failed to crate view, got %v\", err)\n\t}\n\tif err := DB.Migrator().DropView(\"users_view\"); err != nil {\n\t\tt.Fatalf(\"Failed to drop view, got %v\", err)\n\t}\n}\n\nfunc TestMigrateExistingBoolColumnPGAndGaussDB(t *testing.T) {\n\tif DB.Dialector.Name() != \"postgres\" && DB.Dialector.Name() != \"gaussdb\" {\n\t\treturn\n\t}\n\n\ttype ColumnStruct struct {\n\t\tgorm.Model\n\t\tName         string\n\t\tStringBool   string\n\t\tSmallintBool int `gorm:\"type:smallint\"`\n\t}\n\n\ttype ColumnStruct2 struct {\n\t\tgorm.Model\n\t\tName         string\n\t\tStringBool   bool // change existing boolean column from string to boolean\n\t\tSmallintBool bool // change existing boolean column from smallint or other to boolean\n\t}\n\n\tDB.Migrator().DropTable(&ColumnStruct{})\n\n\tif err := DB.AutoMigrate(&ColumnStruct{}); err != nil {\n\t\tt.Errorf(\"Failed to migrate, got %v\", err)\n\t}\n\n\tif err := DB.Table(\"column_structs\").AutoMigrate(&ColumnStruct2{}); err != nil {\n\t\tt.Fatalf(\"no error should happened when auto migrate column, but got %v\", err)\n\t}\n\n\tif columnTypes, err := DB.Migrator().ColumnTypes(&ColumnStruct{}); err != nil {\n\t\tt.Fatalf(\"no error should returns for ColumnTypes\")\n\t} else {\n\t\tstmt := &gorm.Statement{DB: DB}\n\t\tstmt.Parse(&ColumnStruct2{})\n\n\t\tfor _, columnType := range columnTypes {\n\t\t\tswitch columnType.Name() {\n\t\t\tcase \"id\":\n\t\t\t\tif v, ok := columnType.PrimaryKey(); !ok || !v {\n\t\t\t\t\tt.Fatalf(\"column id primary key should be correct, name: %v, column: %#v\", columnType.Name(),\n\t\t\t\t\t\tcolumnType)\n\t\t\t\t}\n\t\t\tcase \"string_bool\":\n\t\t\t\tdataType := DB.Dialector.DataTypeOf(stmt.Schema.LookUpField(columnType.Name()))\n\t\t\t\tif !strings.Contains(strings.ToUpper(dataType), strings.ToUpper(columnType.DatabaseTypeName())) {\n\t\t\t\t\tt.Fatalf(\"column name type should be correct, name: %v, length: %v, expects: %v, column: %#v\",\n\t\t\t\t\t\tcolumnType.Name(), columnType.DatabaseTypeName(), dataType, columnType)\n\t\t\t\t}\n\t\t\tcase \"smallint_bool\":\n\t\t\t\tdataType := DB.Dialector.DataTypeOf(stmt.Schema.LookUpField(columnType.Name()))\n\t\t\t\tif !strings.Contains(strings.ToUpper(dataType), strings.ToUpper(columnType.DatabaseTypeName())) {\n\t\t\t\t\tt.Fatalf(\"column name type should be correct, name: %v, length: %v, expects: %v, column: %#v\",\n\t\t\t\t\t\tcolumnType.Name(), columnType.DatabaseTypeName(), dataType, columnType)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc TestTableType(t *testing.T) {\n\t// currently it is only supported for mysql driver\n\tif !isMysql() {\n\t\treturn\n\t}\n\n\tconst tblName = \"cities\"\n\tconst tblSchema = \"gorm\"\n\tconst tblType = \"BASE TABLE\"\n\tconst tblComment = \"foobar comment\"\n\n\ttype City struct {\n\t\tgorm.Model\n\t\tName string `gorm:\"unique\"`\n\t}\n\n\tDB.Migrator().DropTable(&City{})\n\n\tif err := DB.Set(\"gorm:table_options\",\n\t\tfmt.Sprintf(\"ENGINE InnoDB COMMENT '%s'\", tblComment)).AutoMigrate(&City{}); err != nil {\n\t\tt.Fatalf(\"failed to migrate cities tables, got error: %v\", err)\n\t}\n\n\ttableType, err := DB.Table(\"cities\").Migrator().TableType(&City{})\n\tif err != nil {\n\t\tt.Fatalf(\"failed to get table type, got error %v\", err)\n\t}\n\n\tif tableType.Schema() != tblSchema {\n\t\tt.Fatalf(\"expected tblSchema to be %s but got %s\", tblSchema, tableType.Schema())\n\t}\n\n\tif tableType.Name() != tblName {\n\t\tt.Fatalf(\"expected table name to be %s but got %s\", tblName, tableType.Name())\n\t}\n\n\tif tableType.Type() != tblType {\n\t\tt.Fatalf(\"expected table type to be %s but got %s\", tblType, tableType.Type())\n\t}\n\n\tcomment, ok := tableType.Comment()\n\tif !ok || comment != tblComment {\n\t\tt.Fatalf(\"expected comment %s got %s\", tblComment, comment)\n\t}\n}\n\nfunc TestMigrateWithUniqueIndexAndUnique(t *testing.T) {\n\tconst table = \"unique_struct\"\n\n\tcheckField := func(model interface{}, fieldName string, unique bool, uniqueIndex string) {\n\t\tstmt := &gorm.Statement{DB: DB}\n\t\terr := stmt.Parse(model)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"%v: failed to parse schema, got error: %v\", utils.FileWithLineNum(), err)\n\t\t}\n\t\t_ = stmt.Schema.ParseIndexes()\n\t\tfield := stmt.Schema.LookUpField(fieldName)\n\t\tif field == nil {\n\t\t\tt.Fatalf(\"%v: failed to find column %q\", utils.FileWithLineNum(), fieldName)\n\t\t}\n\t\tif field.Unique != unique {\n\t\t\tt.Fatalf(\"%v: %q column %q unique should be %v but got %v\", utils.FileWithLineNum(), stmt.Schema.Table, fieldName, unique, field.Unique)\n\t\t}\n\t\tif field.UniqueIndex != uniqueIndex {\n\t\t\tt.Fatalf(\"%v: %q column %q uniqueIndex should be %v but got %v\", utils.FileWithLineNum(), stmt.Schema, fieldName, uniqueIndex, field.UniqueIndex)\n\t\t}\n\t}\n\n\ttype ( // not unique\n\t\tUniqueStruct1 struct {\n\t\t\tName string `gorm:\"size:10\"`\n\t\t}\n\t\tUniqueStruct2 struct {\n\t\t\tName string `gorm:\"size:20\"`\n\t\t}\n\t)\n\tcheckField(&UniqueStruct1{}, \"name\", false, \"\")\n\tcheckField(&UniqueStruct2{}, \"name\", false, \"\")\n\n\ttype ( // unique\n\t\tUniqueStruct3 struct {\n\t\t\tName string `gorm:\"size:30;unique\"`\n\t\t}\n\t\tUniqueStruct4 struct {\n\t\t\tName string `gorm:\"size:40;unique\"`\n\t\t}\n\t)\n\tcheckField(&UniqueStruct3{}, \"name\", true, \"\")\n\tcheckField(&UniqueStruct4{}, \"name\", true, \"\")\n\n\ttype ( // uniqueIndex\n\t\tUniqueStruct5 struct {\n\t\t\tName string `gorm:\"size:50;uniqueIndex\"`\n\t\t}\n\t\tUniqueStruct6 struct {\n\t\t\tName string `gorm:\"size:60;uniqueIndex\"`\n\t\t}\n\t\tUniqueStruct7 struct {\n\t\t\tName     string `gorm:\"size:70;uniqueIndex:idx_us6_all_names\"`\n\t\t\tNickName string `gorm:\"size:70;uniqueIndex:idx_us6_all_names\"`\n\t\t}\n\t)\n\tcheckField(&UniqueStruct5{}, \"name\", false, \"idx_unique_struct5_name\")\n\tcheckField(&UniqueStruct6{}, \"name\", false, \"idx_unique_struct6_name\")\n\n\tcheckField(&UniqueStruct7{}, \"name\", false, \"\")\n\tcheckField(&UniqueStruct7{}, \"nick_name\", false, \"\")\n\tcheckField(&UniqueStruct7{}, \"nick_name\", false, \"\")\n\n\ttype UniqueStruct8 struct { // unique and uniqueIndex\n\t\tName string `gorm:\"size:60;unique;index:my_us8_index,unique;\"`\n\t}\n\tcheckField(&UniqueStruct8{}, \"name\", true, \"my_us8_index\")\n\n\ttype TestCase struct {\n\t\tname      string\n\t\tfrom, to  interface{}\n\t\tcheckFunc func(t *testing.T)\n\t}\n\n\tcheckColumnType := func(t *testing.T, fieldName string, unique bool) {\n\t\tcolumnTypes, err := DB.Migrator().ColumnTypes(table)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"%v: failed to get column types, got error: %v\", utils.FileWithLineNum(), err)\n\t\t}\n\t\tvar found gorm.ColumnType\n\t\tfor _, columnType := range columnTypes {\n\t\t\tif columnType.Name() == fieldName {\n\t\t\t\tfound = columnType\n\t\t\t}\n\t\t}\n\t\tif found == nil {\n\t\t\tt.Fatalf(\"%v: failed to find column type %q\", utils.FileWithLineNum(), fieldName)\n\t\t}\n\t\tif actualUnique, ok := found.Unique(); !ok || actualUnique != unique {\n\t\t\tt.Fatalf(\"%v: column %q unique should be %v but got %v\", utils.FileWithLineNum(), fieldName, unique, actualUnique)\n\t\t}\n\t}\n\n\tcheckIndex := func(t *testing.T, expected []gorm.Index) {\n\t\tindexes, err := DB.Migrator().GetIndexes(table)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"%v: failed to get indexes, got error: %v\", utils.FileWithLineNum(), err)\n\t\t}\n\t\tassert.ElementsMatch(t, expected, indexes)\n\t}\n\n\tuniqueIndex := &migrator.Index{TableName: table, NameValue: DB.Config.NamingStrategy.IndexName(table, \"name\"), ColumnList: []string{\"name\"}, PrimaryKeyValue: sql.NullBool{Bool: false, Valid: true}, UniqueValue: sql.NullBool{Bool: true, Valid: true}}\n\tmyIndex := &migrator.Index{TableName: table, NameValue: \"my_us8_index\", ColumnList: []string{\"name\"}, PrimaryKeyValue: sql.NullBool{Bool: false, Valid: true}, UniqueValue: sql.NullBool{Bool: true, Valid: true}}\n\tmulIndex := &migrator.Index{TableName: table, NameValue: \"idx_us6_all_names\", ColumnList: []string{\"name\", \"nick_name\"}, PrimaryKeyValue: sql.NullBool{Bool: false, Valid: true}, UniqueValue: sql.NullBool{Bool: true, Valid: true}}\n\n\tvar checkNotUnique, checkUnique, checkUniqueIndex, checkMyIndex, checkMulIndex func(t *testing.T)\n\t// UniqueAffectedByUniqueIndex is true\n\tif DB.Dialector.Name() == \"mysql\" {\n\t\tuniqueConstraintIndex := &migrator.Index{TableName: table, NameValue: DB.Config.NamingStrategy.UniqueName(table, \"name\"), ColumnList: []string{\"name\"}, PrimaryKeyValue: sql.NullBool{Bool: false, Valid: true}, UniqueValue: sql.NullBool{Bool: true, Valid: true}}\n\t\tcheckNotUnique = func(t *testing.T) {\n\t\t\tcheckColumnType(t, \"name\", false)\n\t\t\tcheckIndex(t, nil)\n\t\t}\n\t\tcheckUnique = func(t *testing.T) {\n\t\t\tcheckColumnType(t, \"name\", true)\n\t\t\tcheckIndex(t, []gorm.Index{uniqueConstraintIndex})\n\t\t}\n\t\tcheckUniqueIndex = func(t *testing.T) {\n\t\t\tcheckColumnType(t, \"name\", true)\n\t\t\tcheckIndex(t, []gorm.Index{uniqueIndex})\n\t\t}\n\t\tcheckMyIndex = func(t *testing.T) {\n\t\t\tcheckColumnType(t, \"name\", true)\n\t\t\tcheckIndex(t, []gorm.Index{uniqueConstraintIndex, myIndex})\n\t\t}\n\t\tcheckMulIndex = func(t *testing.T) {\n\t\t\tcheckColumnType(t, \"name\", false)\n\t\t\tcheckColumnType(t, \"nick_name\", false)\n\t\t\tcheckIndex(t, []gorm.Index{mulIndex})\n\t\t}\n\t} else {\n\t\tcheckNotUnique = func(t *testing.T) { checkColumnType(t, \"name\", false) }\n\t\tcheckUnique = func(t *testing.T) { checkColumnType(t, \"name\", true) }\n\t\tcheckUniqueIndex = func(t *testing.T) {\n\t\t\tcheckColumnType(t, \"name\", false)\n\t\t\tcheckIndex(t, []gorm.Index{uniqueIndex})\n\t\t}\n\t\tcheckMyIndex = func(t *testing.T) {\n\t\t\tcheckColumnType(t, \"name\", true)\n\t\t\tif !DB.Migrator().HasIndex(table, myIndex.Name()) {\n\t\t\t\tt.Errorf(\"%v: should has index %s but not\", utils.FileWithLineNum(), myIndex.Name())\n\t\t\t}\n\t\t}\n\t\tcheckMulIndex = func(t *testing.T) {\n\t\t\tcheckColumnType(t, \"name\", false)\n\t\t\tcheckColumnType(t, \"nick_name\", false)\n\t\t\tif !DB.Migrator().HasIndex(table, mulIndex.Name()) {\n\t\t\t\tt.Errorf(\"%v: should has index %s but not\", utils.FileWithLineNum(), mulIndex.Name())\n\t\t\t}\n\t\t}\n\t}\n\n\ttests := []TestCase{\n\t\t{name: \"notUnique to notUnique\", from: &UniqueStruct1{}, to: &UniqueStruct2{}, checkFunc: checkNotUnique},\n\t\t{name: \"notUnique to unique\", from: &UniqueStruct1{}, to: &UniqueStruct3{}, checkFunc: checkUnique},\n\t\t{name: \"notUnique to uniqueIndex\", from: &UniqueStruct1{}, to: &UniqueStruct5{}, checkFunc: checkUniqueIndex},\n\t\t{name: \"notUnique to uniqueAndUniqueIndex\", from: &UniqueStruct1{}, to: &UniqueStruct8{}, checkFunc: checkMyIndex},\n\t\t{name: \"unique to unique\", from: &UniqueStruct3{}, to: &UniqueStruct4{}, checkFunc: checkUnique},\n\t\t{name: \"unique to uniqueIndex\", from: &UniqueStruct3{}, to: &UniqueStruct5{}, checkFunc: checkUniqueIndex},\n\t\t{name: \"unique to uniqueAndUniqueIndex\", from: &UniqueStruct3{}, to: &UniqueStruct8{}, checkFunc: checkMyIndex},\n\t\t{name: \"uniqueIndex to uniqueIndex\", from: &UniqueStruct5{}, to: &UniqueStruct6{}, checkFunc: checkUniqueIndex},\n\t\t{name: \"uniqueIndex to uniqueAndUniqueIndex\", from: &UniqueStruct5{}, to: &UniqueStruct8{}, checkFunc: checkMyIndex},\n\t\t{name: \"uniqueIndex to multi uniqueIndex\", from: &UniqueStruct5{}, to: &UniqueStruct7{}, checkFunc: checkMulIndex},\n\t}\n\tfor _, test := range tests {\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tif err := DB.Migrator().DropTable(table); err != nil {\n\t\t\t\tt.Fatalf(\"failed to drop table, got error: %v\", err)\n\t\t\t}\n\t\t\tif err := DB.Table(table).AutoMigrate(test.from); err != nil {\n\t\t\t\tt.Fatalf(\"failed to migrate table, got error: %v\", err)\n\t\t\t}\n\t\t\tif err := DB.Table(table).AutoMigrate(test.to); err != nil {\n\t\t\t\tt.Fatalf(\"failed to migrate table, got error: %v\", err)\n\t\t\t}\n\t\t\ttest.checkFunc(t)\n\t\t})\n\t}\n\n\tif DB.Dialector.Name() != \"sqlserver\" {\n\t\t// In SQLServer, If an index or constraint depends on the column,\n\t\t// this column will not be able to run ALTER\n\t\t// see https://stackoverflow.com/questions/19460912/the-object-df-is-dependent-on-column-changing-int-to-double/19461205#19461205\n\t\t// may we need to create another PR to fix it, see https://github.com/go-gorm/sqlserver/pull/106\n\t\ttests = []TestCase{\n\t\t\t{name: \"unique to notUnique\", from: &UniqueStruct3{}, to: &UniqueStruct1{}, checkFunc: checkNotUnique},\n\t\t\t{name: \"uniqueIndex to notUnique\", from: &UniqueStruct5{}, to: &UniqueStruct2{}, checkFunc: checkNotUnique},\n\t\t\t{name: \"uniqueIndex to unique\", from: &UniqueStruct5{}, to: &UniqueStruct3{}, checkFunc: checkUnique},\n\t\t}\n\t}\n\n\tif DB.Dialector.Name() == \"mysql\" {\n\t\tcompatibilityTests := []TestCase{\n\t\t\t{name: \"oldUnique to notUnique\", to: UniqueStruct1{}, checkFunc: checkNotUnique},\n\t\t\t{name: \"oldUnique to unique\", to: UniqueStruct3{}, checkFunc: checkUnique},\n\t\t\t{name: \"oldUnique to uniqueIndex\", to: UniqueStruct5{}, checkFunc: checkUniqueIndex},\n\t\t\t{name: \"oldUnique to uniqueAndUniqueIndex\", to: UniqueStruct8{}, checkFunc: checkMyIndex},\n\t\t}\n\t\tfor _, test := range compatibilityTests {\n\t\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\t\tif err := DB.Migrator().DropTable(table); err != nil {\n\t\t\t\t\tt.Fatalf(\"failed to drop table, got error: %v\", err)\n\t\t\t\t}\n\t\t\t\tif err := DB.Exec(\"CREATE TABLE ? (`name` varchar(10) UNIQUE)\", clause.Table{Name: table}).Error; err != nil {\n\t\t\t\t\tt.Fatalf(\"failed to create table, got error: %v\", err)\n\t\t\t\t}\n\t\t\t\tif err := DB.Table(table).AutoMigrate(test.to); err != nil {\n\t\t\t\t\tt.Fatalf(\"failed to migrate table, got error: %v\", err)\n\t\t\t\t}\n\t\t\t\ttest.checkFunc(t)\n\t\t\t})\n\t\t}\n\t}\n}\n\nfunc testAutoMigrateDecimal(t *testing.T, model1, model2 any) []string {\n\ttracer := Tracer{\n\t\tLogger: DB.Config.Logger,\n\t\tTest: func(ctx context.Context, begin time.Time, fc func() (sql string, rowsAffected int64), err error) {\n\t\t\tsql, _ := fc()\n\t\t\tif strings.HasPrefix(sql, \"ALTER TABLE \") {\n\t\t\t\tt.Fatalf(\"shouldn't execute ALTER COLUMN TYPE if decimal is not change: sql: %s\", sql)\n\t\t\t}\n\t\t},\n\t}\n\tsession := DB.Session(&gorm.Session{Logger: tracer})\n\n\tDB.Migrator().DropTable(model1)\n\tvar modifySql []string\n\tif err := session.AutoMigrate(model1); err != nil {\n\t\tt.Fatalf(\"failed to auto migrate, got error: %v\", err)\n\t}\n\tif err := session.AutoMigrate(model1); err != nil {\n\t\tt.Fatalf(\"failed to auto migrate, got error: %v\", err)\n\t}\n\ttracer2 := Tracer{\n\t\tLogger: DB.Config.Logger,\n\t\tTest: func(ctx context.Context, begin time.Time, fc func() (sql string, rowsAffected int64), err error) {\n\t\t\tsql, _ := fc()\n\t\t\tmodifySql = append(modifySql, sql)\n\t\t},\n\t}\n\tsession2 := DB.Session(&gorm.Session{Logger: tracer2})\n\terr := session2.Table(\"migrate_decimal_columns\").Migrator().AutoMigrate(model2)\n\tif err != nil {\n\t\tt.Fatalf(\"failed to get column types, got error: %v\", err)\n\t}\n\treturn modifySql\n}\n\nfunc decimalColumnsTest[T, T2 any](t *testing.T, expectedSql []string) {\n\tvar t1 T\n\tvar t2 T2\n\tmodSql := testAutoMigrateDecimal(t, t1, t2)\n\tvar alterSQL []string\n\tfor _, sql := range modSql {\n\t\tif strings.HasPrefix(sql, \"ALTER TABLE \") {\n\t\t\talterSQL = append(alterSQL, sql)\n\t\t}\n\t}\n\n\tif len(alterSQL) != 3 {\n\t\tt.Fatalf(\"decimal changed error,expected: %+v,got: %+v.\", expectedSql, alterSQL)\n\t}\n\tfor i := range alterSQL {\n\t\tif alterSQL[i] != expectedSql[i] {\n\t\t\tt.Fatalf(\"decimal changed error,expected: %+v,got: %+v.\", expectedSql, alterSQL)\n\t\t}\n\t}\n}\n\nfunc TestAutoMigrateDecimal(t *testing.T) {\n\tif DB.Dialector.Name() == \"sqlserver\" { // database/sql will replace numeric to decimal. so only support decimal.\n\t\ttype MigrateDecimalColumn struct {\n\t\t\tRecID1 int64 `gorm:\"column:recid1;type:decimal(9,0);not null\" json:\"recid1\"`\n\t\t\tRecID2 int64 `gorm:\"column:recid2;type:decimal(8);not null\" json:\"recid2\"`\n\t\t\tRecID3 int64 `gorm:\"column:recid3;type:decimal(8,1);not null\" json:\"recid3\"`\n\t\t}\n\t\ttype MigrateDecimalColumn2 struct {\n\t\t\tRecID1 int64 `gorm:\"column:recid1;type:decimal(8);not null\" json:\"recid1\"`\n\t\t\tRecID2 int64 `gorm:\"column:recid2;type:decimal(9,1);not null\" json:\"recid2\"`\n\t\t\tRecID3 int64 `gorm:\"column:recid3;type:decimal(9,2);not null\" json:\"recid3\"`\n\t\t}\n\t\texpectedSql := []string{\n\t\t\t`ALTER TABLE \"migrate_decimal_columns\" ALTER COLUMN \"recid1\" decimal(8) NOT NULL`,\n\t\t\t`ALTER TABLE \"migrate_decimal_columns\" ALTER COLUMN \"recid2\" decimal(9,1) NOT NULL`,\n\t\t\t`ALTER TABLE \"migrate_decimal_columns\" ALTER COLUMN \"recid3\" decimal(9,2) NOT NULL`,\n\t\t}\n\t\tdecimalColumnsTest[MigrateDecimalColumn, MigrateDecimalColumn2](t, expectedSql)\n\t} else if DB.Dialector.Name() == \"postgres\" || DB.Dialector.Name() == \"gaussdb\" {\n\t\ttype MigrateDecimalColumn struct {\n\t\t\tRecID1 int64 `gorm:\"column:recid1;type:numeric(9,0);not null\" json:\"recid1\"`\n\t\t\tRecID2 int64 `gorm:\"column:recid2;type:numeric(8);not null\" json:\"recid2\"`\n\t\t\tRecID3 int64 `gorm:\"column:recid3;type:numeric(8,1);not null\" json:\"recid3\"`\n\t\t}\n\t\ttype MigrateDecimalColumn2 struct {\n\t\t\tRecID1 int64 `gorm:\"column:recid1;type:numeric(8);not null\" json:\"recid1\"`\n\t\t\tRecID2 int64 `gorm:\"column:recid2;type:numeric(9,1);not null\" json:\"recid2\"`\n\t\t\tRecID3 int64 `gorm:\"column:recid3;type:numeric(9,2);not null\" json:\"recid3\"`\n\t\t}\n\t\texpectedSql := []string{\n\t\t\t`ALTER TABLE \"migrate_decimal_columns\" ALTER COLUMN \"recid1\" TYPE numeric(8) USING \"recid1\"::numeric(8)`,\n\t\t\t`ALTER TABLE \"migrate_decimal_columns\" ALTER COLUMN \"recid2\" TYPE numeric(9,1) USING \"recid2\"::numeric(9,1)`,\n\t\t\t`ALTER TABLE \"migrate_decimal_columns\" ALTER COLUMN \"recid3\" TYPE numeric(9,2) USING \"recid3\"::numeric(9,2)`,\n\t\t}\n\t\tdecimalColumnsTest[MigrateDecimalColumn, MigrateDecimalColumn2](t, expectedSql)\n\t} else if DB.Dialector.Name() == \"mysql\" {\n\t\ttype MigrateDecimalColumn struct {\n\t\t\tRecID1 int64 `gorm:\"column:recid1;type:decimal(9,0);not null\" json:\"recid1\"`\n\t\t\tRecID2 int64 `gorm:\"column:recid2;type:decimal(8);not null\" json:\"recid2\"`\n\t\t\tRecID3 int64 `gorm:\"column:recid3;type:decimal(8,1);not null\" json:\"recid3\"`\n\t\t}\n\t\ttype MigrateDecimalColumn2 struct {\n\t\t\tRecID1 int64 `gorm:\"column:recid1;type:decimal(8);not null\" json:\"recid1\"`\n\t\t\tRecID2 int64 `gorm:\"column:recid2;type:decimal(9,1);not null\" json:\"recid2\"`\n\t\t\tRecID3 int64 `gorm:\"column:recid3;type:decimal(9,2);not null\" json:\"recid3\"`\n\t\t}\n\t\texpectedSql := []string{\n\t\t\t\"ALTER TABLE `migrate_decimal_columns` MODIFY COLUMN `recid1` decimal(8) NOT NULL\",\n\t\t\t\"ALTER TABLE `migrate_decimal_columns` MODIFY COLUMN `recid2` decimal(9,1) NOT NULL\",\n\t\t\t\"ALTER TABLE `migrate_decimal_columns` MODIFY COLUMN `recid3` decimal(9,2) NOT NULL\",\n\t\t}\n\t\tdecimalColumnsTest[MigrateDecimalColumn, MigrateDecimalColumn2](t, expectedSql)\n\t}\n}\n"
  },
  {
    "path": "tests/multi_primary_keys_test.go",
    "content": "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 struct {\n\tID         uint   `gorm:\"primary_key\"`\n\tLocale     string `gorm:\"primary_key\"`\n\tSubject    string\n\tBody       string\n\tTags       []Tag `gorm:\"many2many:blog_tags;\"`\n\tSharedTags []Tag `gorm:\"many2many:shared_blog_tags;ForeignKey:id;References:id\"`\n\tLocaleTags []Tag `gorm:\"many2many:locale_blog_tags;ForeignKey:id,locale;References:id\"`\n}\n\ntype Tag struct {\n\tID     uint   `gorm:\"primary_key\"`\n\tLocale string `gorm:\"primary_key\"`\n\tValue  string\n\tBlogs  []*Blog `gorm:\"many2many:blog_tags\"`\n}\n\nfunc compareTags(tags []Tag, contents []string) bool {\n\tvar tagContents []string\n\tfor _, tag := range tags {\n\t\ttagContents = append(tagContents, tag.Value)\n\t}\n\tsort.Strings(tagContents)\n\tsort.Strings(contents)\n\treturn reflect.DeepEqual(tagContents, contents)\n}\n\nfunc TestManyToManyWithMultiPrimaryKeys(t *testing.T) {\n\tif name := DB.Dialector.Name(); name == \"sqlite\" || name == \"sqlserver\" {\n\t\tt.Skip(\"skip sqlite, sqlserver due to it doesn't support multiple primary keys with auto increment\")\n\t}\n\n\tif name := DB.Dialector.Name(); name == \"postgres\" || name == \"mysql\" || name == \"gaussdb\" {\n\t\tstmt := gorm.Statement{DB: DB}\n\t\tstmt.Parse(&Blog{})\n\t\tstmt.Schema.LookUpField(\"ID\").Unique = true\n\t\tstmt.Parse(&Tag{})\n\t\tstmt.Schema.LookUpField(\"ID\").Unique = true\n\t\t// postgers only allow unique constraint matching given keys\n\t}\n\n\tDB.Migrator().DropTable(&Blog{}, &Tag{}, \"blog_tags\", \"locale_blog_tags\", \"shared_blog_tags\")\n\tif err := DB.AutoMigrate(&Blog{}, &Tag{}); err != nil {\n\t\tt.Fatalf(\"Failed to auto migrate, got error: %v\", err)\n\t}\n\n\tblog := Blog{\n\t\tLocale:  \"ZH\",\n\t\tSubject: \"subject\",\n\t\tBody:    \"body\",\n\t\tTags: []Tag{\n\t\t\t{Locale: \"ZH\", Value: \"tag1\"},\n\t\t\t{Locale: \"ZH\", Value: \"tag2\"},\n\t\t},\n\t}\n\n\tDB.Save(&blog)\n\tif !compareTags(blog.Tags, []string{\"tag1\", \"tag2\"}) {\n\t\tt.Fatalf(\"Blog should has two tags\")\n\t}\n\n\t// Append\n\ttag3 := &Tag{Locale: \"ZH\", Value: \"tag3\"}\n\tDB.Model(&blog).Association(\"Tags\").Append([]*Tag{tag3})\n\n\tif !compareTags(blog.Tags, []string{\"tag1\", \"tag2\", \"tag3\"}) {\n\t\tt.Fatalf(\"Blog should has three tags after Append\")\n\t}\n\n\tif count := DB.Model(&blog).Association(\"Tags\").Count(); count != 3 {\n\t\tt.Fatalf(\"Blog should has 3 tags after Append, got %v\", count)\n\t}\n\n\tvar tags []Tag\n\tDB.Model(&blog).Association(\"Tags\").Find(&tags)\n\tif !compareTags(tags, []string{\"tag1\", \"tag2\", \"tag3\"}) {\n\t\tt.Fatalf(\"Should find 3 tags\")\n\t}\n\n\tvar blog1 Blog\n\tDB.Preload(\"Tags\").Find(&blog1)\n\tif !compareTags(blog1.Tags, []string{\"tag1\", \"tag2\", \"tag3\"}) {\n\t\tt.Fatalf(\"Preload many2many relations\")\n\t}\n\n\t// Replace\n\ttag5 := &Tag{Locale: \"ZH\", Value: \"tag5\"}\n\ttag6 := &Tag{Locale: \"ZH\", Value: \"tag6\"}\n\tDB.Model(&blog).Association(\"Tags\").Replace(tag5, tag6)\n\tvar tags2 []Tag\n\tDB.Model(&blog).Association(\"Tags\").Find(&tags2)\n\tif !compareTags(tags2, []string{\"tag5\", \"tag6\"}) {\n\t\tt.Fatalf(\"Should find 2 tags after Replace\")\n\t}\n\n\tif DB.Model(&blog).Association(\"Tags\").Count() != 2 {\n\t\tt.Fatalf(\"Blog should has three tags after Replace\")\n\t}\n\n\t// Delete\n\tDB.Model(&blog).Association(\"Tags\").Delete(tag5)\n\tvar tags3 []Tag\n\tDB.Model(&blog).Association(\"Tags\").Find(&tags3)\n\tif !compareTags(tags3, []string{\"tag6\"}) {\n\t\tt.Fatalf(\"Should find 1 tags after Delete\")\n\t}\n\n\tif DB.Model(&blog).Association(\"Tags\").Count() != 1 {\n\t\tt.Fatalf(\"Blog should has three tags after Delete\")\n\t}\n\n\tDB.Model(&blog).Association(\"Tags\").Delete(tag3)\n\tvar tags4 []Tag\n\tDB.Model(&blog).Association(\"Tags\").Find(&tags4)\n\tif !compareTags(tags4, []string{\"tag6\"}) {\n\t\tt.Fatalf(\"Tag should not be deleted when Delete with a unrelated tag\")\n\t}\n\n\t// Clear\n\tDB.Model(&blog).Association(\"Tags\").Clear()\n\tif DB.Model(&blog).Association(\"Tags\").Count() != 0 {\n\t\tt.Fatalf(\"All tags should be cleared\")\n\t}\n}\n\nfunc TestManyToManyWithCustomizedForeignKeys(t *testing.T) {\n\tif name := DB.Dialector.Name(); name == \"sqlite\" || name == \"sqlserver\" {\n\t\tt.Skip(\"skip sqlite, sqlserver due to it doesn't support multiple primary keys with auto increment\")\n\t}\n\n\tif name := DB.Dialector.Name(); name == \"postgres\" {\n\t\tt.Skip(\"skip postgres due to it only allow unique constraint matching given keys\")\n\t}\n\tif name := DB.Dialector.Name(); name == \"gaussdb\" {\n\t\tt.Skip(\"skip gaussdb due to it only allow unique constraint matching given keys\")\n\t}\n\n\tDB.Migrator().DropTable(&Blog{}, &Tag{}, \"blog_tags\", \"locale_blog_tags\", \"shared_blog_tags\")\n\tif err := DB.AutoMigrate(&Blog{}, &Tag{}); err != nil {\n\t\tt.Fatalf(\"Failed to auto migrate, got error: %v\", err)\n\t}\n\n\tblog := Blog{\n\t\tLocale:  \"ZH\",\n\t\tSubject: \"subject\",\n\t\tBody:    \"body\",\n\t\tSharedTags: []Tag{\n\t\t\t{Locale: \"ZH\", Value: \"tag1\"},\n\t\t\t{Locale: \"ZH\", Value: \"tag2\"},\n\t\t},\n\t}\n\tDB.Save(&blog)\n\n\tblog2 := Blog{\n\t\tID:     blog.ID,\n\t\tLocale: \"EN\",\n\t}\n\tDB.Create(&blog2)\n\n\tif !compareTags(blog.SharedTags, []string{\"tag1\", \"tag2\"}) {\n\t\tt.Fatalf(\"Blog should has two tags\")\n\t}\n\n\t// Append\n\ttag3 := &Tag{Locale: \"ZH\", Value: \"tag3\"}\n\tDB.Model(&blog).Association(\"SharedTags\").Append([]*Tag{tag3})\n\tif !compareTags(blog.SharedTags, []string{\"tag1\", \"tag2\", \"tag3\"}) {\n\t\tt.Fatalf(\"Blog should has three tags after Append\")\n\t}\n\n\tif DB.Model(&blog).Association(\"SharedTags\").Count() != 3 {\n\t\tt.Fatalf(\"Blog should has three tags after Append\")\n\t}\n\n\tif DB.Model(&blog2).Association(\"SharedTags\").Count() != 3 {\n\t\tt.Fatalf(\"Blog should has three tags after Append\")\n\t}\n\n\tvar tags []Tag\n\tDB.Model(&blog).Association(\"SharedTags\").Find(&tags)\n\tif !compareTags(tags, []string{\"tag1\", \"tag2\", \"tag3\"}) {\n\t\tt.Fatalf(\"Should find 3 tags\")\n\t}\n\n\tDB.Model(&blog2).Association(\"SharedTags\").Find(&tags)\n\tif !compareTags(tags, []string{\"tag1\", \"tag2\", \"tag3\"}) {\n\t\tt.Fatalf(\"Should find 3 tags\")\n\t}\n\n\tvar blog1 Blog\n\tDB.Preload(\"SharedTags\").Find(&blog1)\n\tif !compareTags(blog1.SharedTags, []string{\"tag1\", \"tag2\", \"tag3\"}) {\n\t\tt.Fatalf(\"Preload many2many relations\")\n\t}\n\n\ttag4 := &Tag{Locale: \"ZH\", Value: \"tag4\"}\n\tDB.Model(&blog2).Association(\"SharedTags\").Append(tag4)\n\n\tDB.Model(&blog).Association(\"SharedTags\").Find(&tags)\n\tif !compareTags(tags, []string{\"tag1\", \"tag2\", \"tag3\", \"tag4\"}) {\n\t\tt.Fatalf(\"Should find 3 tags\")\n\t}\n\n\tDB.Model(&blog2).Association(\"SharedTags\").Find(&tags)\n\tif !compareTags(tags, []string{\"tag1\", \"tag2\", \"tag3\", \"tag4\"}) {\n\t\tt.Fatalf(\"Should find 3 tags\")\n\t}\n\n\t// Replace\n\ttag5 := &Tag{Locale: \"ZH\", Value: \"tag5\"}\n\ttag6 := &Tag{Locale: \"ZH\", Value: \"tag6\"}\n\tDB.Model(&blog2).Association(\"SharedTags\").Replace(tag5, tag6)\n\tvar tags2 []Tag\n\tDB.Model(&blog).Association(\"SharedTags\").Find(&tags2)\n\tif !compareTags(tags2, []string{\"tag5\", \"tag6\"}) {\n\t\tt.Fatalf(\"Should find 2 tags after Replace\")\n\t}\n\n\tDB.Model(&blog2).Association(\"SharedTags\").Find(&tags2)\n\tif !compareTags(tags2, []string{\"tag5\", \"tag6\"}) {\n\t\tt.Fatalf(\"Should find 2 tags after Replace\")\n\t}\n\n\tif DB.Model(&blog).Association(\"SharedTags\").Count() != 2 {\n\t\tt.Fatalf(\"Blog should has three tags after Replace\")\n\t}\n\n\t// Delete\n\tDB.Model(&blog).Association(\"SharedTags\").Delete(tag5)\n\tvar tags3 []Tag\n\tDB.Model(&blog).Association(\"SharedTags\").Find(&tags3)\n\tif !compareTags(tags3, []string{\"tag6\"}) {\n\t\tt.Fatalf(\"Should find 1 tags after Delete\")\n\t}\n\n\tif DB.Model(&blog).Association(\"SharedTags\").Count() != 1 {\n\t\tt.Fatalf(\"Blog should has three tags after Delete\")\n\t}\n\n\tDB.Model(&blog2).Association(\"SharedTags\").Delete(tag3)\n\tvar tags4 []Tag\n\tDB.Model(&blog).Association(\"SharedTags\").Find(&tags4)\n\tif !compareTags(tags4, []string{\"tag6\"}) {\n\t\tt.Fatalf(\"Tag should not be deleted when Delete with a unrelated tag\")\n\t}\n\n\t// Clear\n\tDB.Model(&blog2).Association(\"SharedTags\").Clear()\n\tif DB.Model(&blog).Association(\"SharedTags\").Count() != 0 {\n\t\tt.Fatalf(\"All tags should be cleared\")\n\t}\n}\n\nfunc TestManyToManyWithCustomizedForeignKeys2(t *testing.T) {\n\tif name := DB.Dialector.Name(); name == \"sqlite\" || name == \"sqlserver\" {\n\t\tt.Skip(\"skip sqlite, sqlserver due to it doesn't support multiple primary keys with auto increment\")\n\t}\n\n\tif name := DB.Dialector.Name(); name == \"postgres\" || name == \"mysql\" {\n\t\tt.Skip(\"skip postgres due to it only allow unique constraint matching given keys\")\n\t}\n\n\tif name := DB.Dialector.Name(); name == \"gaussdb\" {\n\t\tt.Skip(\"skip gaussdb due to it only allow unique constraint matching given keys\")\n\t}\n\n\tDB.Migrator().DropTable(&Blog{}, &Tag{}, \"blog_tags\", \"locale_blog_tags\", \"shared_blog_tags\")\n\tif err := DB.AutoMigrate(&Blog{}, &Tag{}); err != nil {\n\t\tt.Fatalf(\"Failed to auto migrate, got error: %v\", err)\n\t}\n\n\tblog := Blog{\n\t\tLocale:  \"ZH\",\n\t\tSubject: \"subject\",\n\t\tBody:    \"body\",\n\t\tLocaleTags: []Tag{\n\t\t\t{Locale: \"ZH\", Value: \"tag1\"},\n\t\t\t{Locale: \"ZH\", Value: \"tag2\"},\n\t\t},\n\t}\n\tDB.Save(&blog)\n\n\tblog2 := Blog{\n\t\tID:     blog.ID,\n\t\tLocale: \"EN\",\n\t}\n\tDB.Create(&blog2)\n\n\t// Append\n\ttag3 := &Tag{Locale: \"ZH\", Value: \"tag3\"}\n\tDB.Model(&blog).Association(\"LocaleTags\").Append([]*Tag{tag3})\n\tif !compareTags(blog.LocaleTags, []string{\"tag1\", \"tag2\", \"tag3\"}) {\n\t\tt.Fatalf(\"Blog should has three tags after Append\")\n\t}\n\n\tif DB.Model(&blog).Association(\"LocaleTags\").Count() != 3 {\n\t\tt.Fatalf(\"Blog should has three tags after Append\")\n\t}\n\n\tif DB.Model(&blog2).Association(\"LocaleTags\").Count() != 0 {\n\t\tt.Fatalf(\"EN Blog should has 0 tags after ZH Blog Append\")\n\t}\n\n\tvar tags []Tag\n\tDB.Model(&blog).Association(\"LocaleTags\").Find(&tags)\n\tif !compareTags(tags, []string{\"tag1\", \"tag2\", \"tag3\"}) {\n\t\tt.Fatalf(\"Should find 3 tags\")\n\t}\n\n\tDB.Model(&blog2).Association(\"LocaleTags\").Find(&tags)\n\tif len(tags) != 0 {\n\t\tt.Fatalf(\"Should find 0 tags for EN Blog\")\n\t}\n\n\tvar blog1 Blog\n\tDB.Preload(\"LocaleTags\").Find(&blog1, \"locale = ? AND id = ?\", \"ZH\", blog.ID)\n\tif !compareTags(blog1.LocaleTags, []string{\"tag1\", \"tag2\", \"tag3\"}) {\n\t\tt.Fatalf(\"Preload many2many relations\")\n\t}\n\n\ttag4 := &Tag{Locale: \"ZH\", Value: \"tag4\"}\n\tDB.Model(&blog2).Association(\"LocaleTags\").Append(tag4)\n\n\tDB.Model(&blog).Association(\"LocaleTags\").Find(&tags)\n\tif !compareTags(tags, []string{\"tag1\", \"tag2\", \"tag3\"}) {\n\t\tt.Fatalf(\"Should find 3 tags for EN Blog\")\n\t}\n\n\tDB.Model(&blog2).Association(\"LocaleTags\").Find(&tags)\n\tif !compareTags(tags, []string{\"tag4\"}) {\n\t\tt.Fatalf(\"Should find 1 tags for EN Blog, but got %v\", tags)\n\t}\n\n\t// Replace\n\ttag5 := &Tag{Locale: \"ZH\", Value: \"tag5\"}\n\ttag6 := &Tag{Locale: \"ZH\", Value: \"tag6\"}\n\tDB.Model(&blog2).Association(\"LocaleTags\").Replace(tag5, tag6)\n\n\tvar tags2 []Tag\n\tDB.Model(&blog).Association(\"LocaleTags\").Find(&tags2)\n\tif !compareTags(tags2, []string{\"tag1\", \"tag2\", \"tag3\"}) {\n\t\tt.Fatalf(\"CN Blog's tags should not be changed after EN Blog Replace\")\n\t}\n\n\tvar blog11 Blog\n\tDB.Preload(\"LocaleTags\").First(&blog11, \"id = ? AND locale = ?\", blog.ID, blog.Locale)\n\tif !compareTags(blog11.LocaleTags, []string{\"tag1\", \"tag2\", \"tag3\"}) {\n\t\tt.Fatalf(\"CN Blog's tags should not be changed after EN Blog Replace\")\n\t}\n\n\tDB.Model(&blog2).Association(\"LocaleTags\").Find(&tags2)\n\tif !compareTags(tags2, []string{\"tag5\", \"tag6\"}) {\n\t\tt.Fatalf(\"Should find 2 tags after Replace\")\n\t}\n\n\tvar blog21 Blog\n\tDB.Preload(\"LocaleTags\").First(&blog21, \"id = ? AND locale = ?\", blog2.ID, blog2.Locale)\n\tif !compareTags(blog21.LocaleTags, []string{\"tag5\", \"tag6\"}) {\n\t\tt.Fatalf(\"EN Blog's tags should be changed after Replace\")\n\t}\n\n\tif DB.Model(&blog).Association(\"LocaleTags\").Count() != 3 {\n\t\tt.Fatalf(\"ZH Blog should has three tags after Replace\")\n\t}\n\n\tif DB.Model(&blog2).Association(\"LocaleTags\").Count() != 2 {\n\t\tt.Fatalf(\"EN Blog should has two tags after Replace\")\n\t}\n\n\t// Delete\n\tDB.Model(&blog).Association(\"LocaleTags\").Delete(tag5)\n\n\tif DB.Model(&blog).Association(\"LocaleTags\").Count() != 3 {\n\t\tt.Fatalf(\"ZH Blog should has three tags after Delete with EN's tag\")\n\t}\n\n\tif DB.Model(&blog2).Association(\"LocaleTags\").Count() != 2 {\n\t\tt.Fatalf(\"EN Blog should has two tags after ZH Blog Delete with EN's tag\")\n\t}\n\n\tDB.Model(&blog2).Association(\"LocaleTags\").Delete(tag5)\n\n\tif DB.Model(&blog).Association(\"LocaleTags\").Count() != 3 {\n\t\tt.Fatalf(\"ZH Blog should has three tags after EN Blog Delete with EN's tag\")\n\t}\n\n\tif DB.Model(&blog2).Association(\"LocaleTags\").Count() != 1 {\n\t\tt.Fatalf(\"EN Blog should has 1 tags after EN Blog Delete with EN's tag\")\n\t}\n\n\t// Clear\n\tDB.Model(&blog2).Association(\"LocaleTags\").Clear()\n\tif DB.Model(&blog).Association(\"LocaleTags\").Count() != 3 {\n\t\tt.Fatalf(\"ZH Blog's tags should not be cleared when clear EN Blog's tags\")\n\t}\n\n\tif DB.Model(&blog2).Association(\"LocaleTags\").Count() != 0 {\n\t\tt.Fatalf(\"EN Blog's tags should be cleared when clear EN Blog's tags\")\n\t}\n\n\tDB.Model(&blog).Association(\"LocaleTags\").Clear()\n\tif DB.Model(&blog).Association(\"LocaleTags\").Count() != 0 {\n\t\tt.Fatalf(\"ZH Blog's tags should be cleared when clear ZH Blog's tags\")\n\t}\n\n\tif DB.Model(&blog2).Association(\"LocaleTags\").Count() != 0 {\n\t\tt.Fatalf(\"EN Blog's tags should be cleared\")\n\t}\n}\n\nfunc TestCompositePrimaryKeysAssociations(t *testing.T) {\n\ttype Label struct {\n\t\tBookID *uint  `gorm:\"primarykey\"`\n\t\tName   string `gorm:\"primarykey\"`\n\t\tValue  string\n\t}\n\n\ttype Book struct {\n\t\tID     int\n\t\tName   string\n\t\tLabels []Label\n\t}\n\n\tDB.Migrator().DropTable(&Label{}, &Book{})\n\tif err := DB.AutoMigrate(&Label{}, &Book{}); err != nil {\n\t\tt.Fatalf(\"failed to migrate, got %v\", err)\n\t}\n\n\tbook := Book{\n\t\tName: \"my book\",\n\t\tLabels: []Label{\n\t\t\t{Name: \"region\", Value: \"emea\"},\n\t\t},\n\t}\n\n\tDB.Create(&book)\n\n\tvar result Book\n\tif err := DB.Preload(\"Labels\").First(&result, book.ID).Error; err != nil {\n\t\tt.Fatalf(\"failed to preload, got error %v\", err)\n\t}\n\n\tAssertEqual(t, book, result)\n}\n"
  },
  {
    "path": "tests/named_argument_test.go",
    "content": "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 TestNamedArg(t *testing.T) {\n\ttype NamedUser struct {\n\t\tgorm.Model\n\t\tName1 string\n\t\tName2 string\n\t\tName3 string\n\t}\n\n\tDB.Migrator().DropTable(&NamedUser{})\n\tDB.AutoMigrate(&NamedUser{})\n\n\tnamedUser := NamedUser{Name1: \"jinzhu1\", Name2: \"jinzhu2\", Name3: \"jinzhu3\"}\n\tDB.Create(&namedUser)\n\n\tvar result NamedUser\n\tDB.First(&result, \"name1 = @name OR name2 = @name OR name3 = @name\", sql.Named(\"name\", \"jinzhu2\"))\n\n\tAssertEqual(t, result, namedUser)\n\n\tvar result2 NamedUser\n\tDB.Where(\"name1 = @name OR name2 = @name OR name3 = @name\", sql.Named(\"name\", \"jinzhu2\")).First(&result2)\n\n\tAssertEqual(t, result2, namedUser)\n\n\tvar result3 NamedUser\n\tDB.Where(\"name1 = @name OR name2 = @name OR name3 = @name\", map[string]interface{}{\"name\": \"jinzhu2\"}).First(&result3)\n\n\tAssertEqual(t, result3, namedUser)\n\n\tvar result4 NamedUser\n\tif err := DB.Raw(\"SELECT * FROM named_users WHERE name1 = @name OR name2 = @name2 OR name3 = @name\", sql.Named(\"name\", \"jinzhu-none\"), sql.Named(\"name2\", \"jinzhu2\")).Find(&result4).Error; err != nil {\n\t\tt.Errorf(\"failed to update with named arg\")\n\t}\n\n\tAssertEqual(t, result4, namedUser)\n\n\tif err := DB.Exec(\"UPDATE named_users SET name1 = @name, name2 = @name2, name3 = @name\", sql.Named(\"name\", \"jinzhu-new\"), sql.Named(\"name2\", \"jinzhu-new2\")).Error; err != nil {\n\t\tt.Errorf(\"failed to update with named arg\")\n\t}\n\n\tnamedUser.Name1 = \"jinzhu-new\"\n\tnamedUser.Name2 = \"jinzhu-new2\"\n\tnamedUser.Name3 = \"jinzhu-new\"\n\n\tvar result5 NamedUser\n\tif err := DB.Raw(\"SELECT * FROM named_users WHERE (name1 = @name AND name3 = @name) AND name2 = @name2\", map[string]interface{}{\"name\": \"jinzhu-new\", \"name2\": \"jinzhu-new2\"}).Find(&result5).Error; err != nil {\n\t\tt.Errorf(\"failed to update with named arg\")\n\t}\n\n\tAssertEqual(t, result5, namedUser)\n\n\tvar result6 NamedUser\n\tif err := DB.Raw(`SELECT * FROM named_users WHERE (name1 = @name\n\tAND name3 = @name) AND name2 = @name2`, map[string]interface{}{\"name\": \"jinzhu-new\", \"name2\": \"jinzhu-new2\"}).Find(&result6).Error; err != nil {\n\t\tt.Errorf(\"failed to update with named arg\")\n\t}\n\n\tAssertEqual(t, result6, namedUser)\n\n\tvar result7 NamedUser\n\tif err := DB.Where(\"name1 = @name OR name2 = @name\", sql.Named(\"name\", \"jinzhu-new\")).Where(\"name3 = 'jinzhu-new3'\").First(&result7).Error; err == nil || !errors.Is(err, gorm.ErrRecordNotFound) {\n\t\tt.Errorf(\"should return record not found error, but got %v\", err)\n\t}\n\n\tDB.Delete(&namedUser)\n\n\tvar result8 NamedUser\n\tif err := DB.Where(\"name1 = @name OR name2 = @name\", map[string]interface{}{\"name\": \"jinzhu-new\"}).First(&result8).Error; err == nil || !errors.Is(err, gorm.ErrRecordNotFound) {\n\t\tt.Errorf(\"should return record not found error, but got %v\", err)\n\t}\n}\n"
  },
  {
    "path": "tests/named_polymorphic_test.go",
    "content": "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         string\n\tPreferredToy Toy `gorm:\"polymorphic:Owner;polymorphicValue:hamster_preferred\"`\n\tOtherToy     Toy `gorm:\"polymorphic:Owner;polymorphicValue:hamster_other\"`\n}\n\nfunc TestNamedPolymorphic(t *testing.T) {\n\tDB.Migrator().DropTable(&Hamster{})\n\tDB.AutoMigrate(&Hamster{})\n\n\thamster := Hamster{Name: \"Mr. Hammond\", PreferredToy: Toy{Name: \"bike\"}, OtherToy: Toy{Name: \"treadmill\"}}\n\tDB.Save(&hamster)\n\n\thamster2 := Hamster{}\n\tDB.Preload(\"PreferredToy\").Preload(\"OtherToy\").Find(&hamster2, hamster.Id)\n\n\tif hamster2.PreferredToy.ID != hamster.PreferredToy.ID || hamster2.PreferredToy.Name != hamster.PreferredToy.Name {\n\t\tt.Errorf(\"Hamster's preferred toy failed to preload\")\n\t}\n\n\tif hamster2.OtherToy.ID != hamster.OtherToy.ID || hamster2.OtherToy.Name != hamster.OtherToy.Name {\n\t\tt.Errorf(\"Hamster's other toy failed to preload\")\n\t}\n\n\t// clear to omit Toy.ID in count\n\thamster2.PreferredToy = Toy{}\n\thamster2.OtherToy = Toy{}\n\n\tif DB.Model(&hamster2).Association(\"PreferredToy\").Count() != 1 {\n\t\tt.Errorf(\"Hamster's preferred toy count should be 1\")\n\t}\n\n\tif DB.Model(&hamster2).Association(\"OtherToy\").Count() != 1 {\n\t\tt.Errorf(\"Hamster's other toy count should be 1\")\n\t}\n\n\t// Query\n\thamsterToy := Toy{}\n\tDB.Model(&hamster).Association(\"PreferredToy\").Find(&hamsterToy)\n\tif hamsterToy.Name != hamster.PreferredToy.Name {\n\t\tt.Errorf(\"Should find has one polymorphic association\")\n\t}\n\n\thamsterToy = Toy{}\n\tDB.Model(&hamster).Association(\"OtherToy\").Find(&hamsterToy)\n\tif hamsterToy.Name != hamster.OtherToy.Name {\n\t\tt.Errorf(\"Should find has one polymorphic association\")\n\t}\n\n\t// Append\n\tDB.Model(&hamster).Association(\"PreferredToy\").Append(&Toy{\n\t\tName: \"bike 2\",\n\t})\n\n\tDB.Model(&hamster).Association(\"OtherToy\").Append(&Toy{\n\t\tName: \"treadmill 2\",\n\t})\n\n\thamsterToy = Toy{}\n\tDB.Model(&hamster).Association(\"PreferredToy\").Find(&hamsterToy)\n\tif hamsterToy.Name != \"bike 2\" {\n\t\tt.Errorf(\"Should update has one polymorphic association with Append\")\n\t}\n\n\thamsterToy = Toy{}\n\tDB.Model(&hamster).Association(\"OtherToy\").Find(&hamsterToy)\n\tif hamsterToy.Name != \"treadmill 2\" {\n\t\tt.Errorf(\"Should update has one polymorphic association with Append\")\n\t}\n\n\tif DB.Model(&hamster2).Association(\"PreferredToy\").Count() != 1 {\n\t\tt.Errorf(\"Hamster's toys count should be 1 after Append\")\n\t}\n\n\tif DB.Model(&hamster2).Association(\"OtherToy\").Count() != 1 {\n\t\tt.Errorf(\"Hamster's toys count should be 1 after Append\")\n\t}\n\n\t// Replace\n\tDB.Model(&hamster).Association(\"PreferredToy\").Replace(&Toy{\n\t\tName: \"bike 3\",\n\t})\n\n\tDB.Model(&hamster).Association(\"OtherToy\").Replace(&Toy{\n\t\tName: \"treadmill 3\",\n\t})\n\n\thamsterToy = Toy{}\n\tDB.Model(&hamster).Association(\"PreferredToy\").Find(&hamsterToy)\n\tif hamsterToy.Name != \"bike 3\" {\n\t\tt.Errorf(\"Should update has one polymorphic association with Replace\")\n\t}\n\n\thamsterToy = Toy{}\n\tDB.Model(&hamster).Association(\"OtherToy\").Find(&hamsterToy)\n\tif hamsterToy.Name != \"treadmill 3\" {\n\t\tt.Errorf(\"Should update has one polymorphic association with Replace\")\n\t}\n\n\tif DB.Model(&hamster2).Association(\"PreferredToy\").Count() != 1 {\n\t\tt.Errorf(\"hamster's toys count should be 1 after Replace\")\n\t}\n\n\tif DB.Model(&hamster2).Association(\"OtherToy\").Count() != 1 {\n\t\tt.Errorf(\"hamster's toys count should be 1 after Replace\")\n\t}\n\n\t// Clear\n\tDB.Model(&hamster).Association(\"PreferredToy\").Append(&Toy{\n\t\tName: \"bike 2\",\n\t})\n\tDB.Model(&hamster).Association(\"OtherToy\").Append(&Toy{\n\t\tName: \"treadmill 2\",\n\t})\n\n\tif DB.Model(&hamster).Association(\"PreferredToy\").Count() != 1 {\n\t\tt.Errorf(\"Hamster's toys should be added with Append\")\n\t}\n\n\tif DB.Model(&hamster).Association(\"OtherToy\").Count() != 1 {\n\t\tt.Errorf(\"Hamster's toys should be added with Append\")\n\t}\n\n\tDB.Model(&hamster).Association(\"PreferredToy\").Clear()\n\n\tif DB.Model(&hamster2).Association(\"PreferredToy\").Count() != 0 {\n\t\tt.Errorf(\"Hamster's preferred toy should be cleared with Clear\")\n\t}\n\n\tif DB.Model(&hamster2).Association(\"OtherToy\").Count() != 1 {\n\t\tt.Errorf(\"Hamster's other toy should be still available\")\n\t}\n\n\tDB.Model(&hamster).Association(\"OtherToy\").Clear()\n\tif DB.Model(&hamster).Association(\"OtherToy\").Count() != 0 {\n\t\tt.Errorf(\"Hamster's other toy should be cleared with Clear\")\n\t}\n}\n"
  },
  {
    "path": "tests/non_std_test.go",
    "content": "package tests_test\n\nimport (\n\t\"testing\"\n\t\"time\"\n)\n\ntype Animal struct {\n\tCounter    uint64 `gorm:\"primary_key:yes\"`\n\tName       string `gorm:\"DEFAULT:'galeone'\"`\n\tFrom       string // test reserved sql keyword as field name\n\tAge        *time.Time\n\tunexported string // unexported value\n\tCreatedAt  time.Time\n\tUpdatedAt  time.Time\n}\n\nfunc TestNonStdPrimaryKeyAndDefaultValues(t *testing.T) {\n\tDB.Migrator().DropTable(&Animal{})\n\tif err := DB.AutoMigrate(&Animal{}); err != nil {\n\t\tt.Fatalf(\"no error should happen when migrate but got %v\", err)\n\t}\n\n\tanimal := Animal{Name: \"Ferdinand\"}\n\tDB.Save(&animal)\n\tupdatedAt1 := animal.UpdatedAt\n\n\tDB.Save(&animal).Update(\"name\", \"Francis\")\n\tif updatedAt1.Format(time.RFC3339Nano) == animal.UpdatedAt.Format(time.RFC3339Nano) {\n\t\tt.Errorf(\"UpdatedAt should be updated\")\n\t}\n\n\tvar animals []Animal\n\tDB.Find(&animals)\n\tif count := DB.Model(Animal{}).Where(\"1=1\").Update(\"CreatedAt\", time.Now().Add(2*time.Hour)).RowsAffected; count != int64(len(animals)) {\n\t\tt.Error(\"RowsAffected should be correct when do batch update\")\n\t}\n\n\tanimal = Animal{From: \"somewhere\"}              // No name fields, should be filled with the default value (galeone)\n\tDB.Save(&animal).Update(\"From\", \"a nice place\") // The name field should be untouched\n\tDB.First(&animal, animal.Counter)\n\tif animal.Name != \"galeone\" {\n\t\tt.Errorf(\"Name fields shouldn't be changed if untouched, but got %v\", animal.Name)\n\t}\n\n\t// When changing a field with a default value, the change must occur\n\tanimal.Name = \"amazing horse\"\n\tDB.Save(&animal)\n\tDB.First(&animal, animal.Counter)\n\tif animal.Name != \"amazing horse\" {\n\t\tt.Errorf(\"Update a filed with a default value should occur. But got %v\\n\", animal.Name)\n\t}\n\n\t// When changing a field with a default value with blank value\n\tanimal.Name = \"\"\n\tDB.Save(&animal)\n\tDB.First(&animal, animal.Counter)\n\tif animal.Name != \"\" {\n\t\tt.Errorf(\"Update a filed to blank with a default value should occur. But got %v\\n\", animal.Name)\n\t}\n}\n"
  },
  {
    "path": "tests/postgres_test.go",
    "content": "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.io/gorm/clause\"\n\t. \"gorm.io/gorm/utils/tests\"\n)\n\nfunc TestPostgresReturningIDWhichHasStringType(t *testing.T) {\n\tif DB.Dialector.Name() != \"postgres\" {\n\t\tt.Skip()\n\t}\n\n\ttype Yasuo struct {\n\t\tID        string `gorm:\"default:gen_random_uuid()\"`\n\t\tName      string\n\t\tCreatedAt time.Time `gorm:\"type:TIMESTAMP WITHOUT TIME ZONE\"`\n\t\tUpdatedAt time.Time `gorm:\"type:TIMESTAMP WITHOUT TIME ZONE;default:current_timestamp\"`\n\t}\n\n\tif err := DB.Exec(\"CREATE EXTENSION IF NOT EXISTS pgcrypto;\").Error; err != nil {\n\t\tt.Errorf(\"Failed to create extension pgcrypto, got error %v\", err)\n\t}\n\n\tDB.Migrator().DropTable(&Yasuo{})\n\n\tif err := DB.AutoMigrate(&Yasuo{}); err != nil {\n\t\tt.Fatalf(\"Failed to migrate for uuid default value, got error: %v\", err)\n\t}\n\n\tyasuo := Yasuo{Name: \"jinzhu\"}\n\tif err := DB.Create(&yasuo).Error; err != nil {\n\t\tt.Fatalf(\"should be able to create data, but got %v\", err)\n\t}\n\n\tif yasuo.ID == \"\" {\n\t\tt.Fatal(\"should be able to has ID, but got zero value\")\n\t}\n\n\tvar result Yasuo\n\tif err := DB.First(&result, \"id = ?\", yasuo.ID).Error; err != nil || yasuo.Name != \"jinzhu\" {\n\t\tt.Errorf(\"No error should happen, but got %v\", err)\n\t}\n\n\tif err := DB.Where(\"id = $1\", yasuo.ID).First(&Yasuo{}).Error; err != nil || yasuo.Name != \"jinzhu\" {\n\t\tt.Errorf(\"No error should happen, but got %v\", err)\n\t}\n\n\tyasuo.Name = \"jinzhu1\"\n\tif err := DB.Save(&yasuo).Error; err != nil {\n\t\tt.Errorf(\"Failed to update date, got error %v\", err)\n\t}\n\n\tif err := DB.First(&result, \"id = ?\", yasuo.ID).Error; err != nil || yasuo.Name != \"jinzhu1\" {\n\t\tt.Errorf(\"No error should happen, but got %v\", err)\n\t}\n}\n\nfunc TestPostgres(t *testing.T) {\n\tif DB.Dialector.Name() != \"postgres\" {\n\t\tt.Skip()\n\t}\n\n\ttype Harumph struct {\n\t\tgorm.Model\n\t\tName      string         `gorm:\"check:name_checker,name <> ''\"`\n\t\tTest      uuid.UUID      `gorm:\"type:uuid;not null;default:gen_random_uuid()\"`\n\t\tCreatedAt time.Time      `gorm:\"type:TIMESTAMP WITHOUT TIME ZONE\"`\n\t\tUpdatedAt time.Time      `gorm:\"type:TIMESTAMP WITHOUT TIME ZONE;default:current_timestamp\"`\n\t\tThings    pq.StringArray `gorm:\"type:text[]\"`\n\t}\n\n\tif err := DB.Exec(\"CREATE EXTENSION IF NOT EXISTS pgcrypto;\").Error; err != nil {\n\t\tt.Errorf(\"Failed to create extension pgcrypto, got error %v\", err)\n\t}\n\n\tDB.Migrator().DropTable(&Harumph{})\n\n\tif err := DB.AutoMigrate(&Harumph{}); err != nil {\n\t\tt.Fatalf(\"Failed to migrate for uuid default value, got error: %v\", err)\n\t}\n\n\tharumph := Harumph{}\n\tif err := DB.Create(&harumph).Error; err == nil {\n\t\tt.Fatalf(\"should failed to create data, name can't be blank\")\n\t}\n\n\tharumph = Harumph{Name: \"jinzhu\"}\n\tif err := DB.Create(&harumph).Error; err != nil {\n\t\tt.Fatalf(\"should be able to create data, but got %v\", err)\n\t}\n\n\tvar result Harumph\n\tif err := DB.First(&result, \"id = ?\", harumph.ID).Error; err != nil || harumph.Name != \"jinzhu\" {\n\t\tt.Errorf(\"No error should happen, but got %v\", err)\n\t}\n\n\tif err := DB.Where(\"id = $1\", harumph.ID).First(&Harumph{}).Error; err != nil || harumph.Name != \"jinzhu\" {\n\t\tt.Errorf(\"No error should happen, but got %v\", err)\n\t}\n\n\tharumph.Name = \"jinzhu1\"\n\tif err := DB.Save(&harumph).Error; err != nil {\n\t\tt.Errorf(\"Failed to update date, got error %v\", err)\n\t}\n\n\tif err := DB.First(&result, \"id = ?\", harumph.ID).Error; err != nil || harumph.Name != \"jinzhu1\" {\n\t\tt.Errorf(\"No error should happen, but got %v\", err)\n\t}\n\n\tDB.Migrator().DropTable(\"log_usage\")\n\n\tif err := DB.Exec(`\nCREATE TABLE public.log_usage (\n    log_id bigint NOT NULL\n);\n\nALTER TABLE public.log_usage ALTER COLUMN log_id ADD GENERATED BY DEFAULT AS IDENTITY (\n    SEQUENCE NAME public.log_usage_log_id_seq\n    START WITH 1\n    INCREMENT BY 1\n    NO MINVALUE\n    NO MAXVALUE\n    CACHE 1\n);\n\t`).Error; err != nil {\n\t\tt.Fatalf(\"failed to create table, got error %v\", err)\n\t}\n\n\tcolumns, err := DB.Migrator().ColumnTypes(\"log_usage\")\n\tif err != nil {\n\t\tt.Fatalf(\"failed to get columns, got error %v\", err)\n\t}\n\n\thasLogID := false\n\tfor _, column := range columns {\n\t\tif column.Name() == \"log_id\" {\n\t\t\thasLogID = true\n\t\t\tautoIncrement, ok := column.AutoIncrement()\n\t\t\tif !ok || !autoIncrement {\n\t\t\t\tt.Fatalf(\"column log_id should be auto incrementment\")\n\t\t\t}\n\t\t}\n\t}\n\n\tif !hasLogID {\n\t\tt.Fatalf(\"failed to found column log_id\")\n\t}\n}\n\ntype Post struct {\n\tID         uuid.UUID `gorm:\"primary_key;type:uuid;default:uuid_generate_v4();\"`\n\tTitle      string\n\tCategories []*Category `gorm:\"Many2Many:post_categories\"`\n}\n\ntype Category struct {\n\tID    uuid.UUID `gorm:\"primary_key;type:uuid;default:uuid_generate_v4();\"`\n\tTitle string\n\tPosts []*Post `gorm:\"Many2Many:post_categories\"`\n}\n\nfunc TestMany2ManyWithDefaultValueUUID(t *testing.T) {\n\tif DB.Dialector.Name() != \"postgres\" {\n\t\tt.Skip()\n\t}\n\n\tif err := DB.Exec(`create extension if not exists \"uuid-ossp\"`).Error; err != nil {\n\t\tt.Fatalf(\"Failed to create 'uuid-ossp' extension, but got error %v\", err)\n\t}\n\n\tDB.Migrator().DropTable(&Post{}, &Category{}, \"post_categories\")\n\tDB.AutoMigrate(&Post{}, &Category{})\n\n\tpost := Post{\n\t\tTitle: \"Hello World\",\n\t\tCategories: []*Category{\n\t\t\t{Title: \"Coding\"},\n\t\t\t{Title: \"Golang\"},\n\t\t},\n\t}\n\n\tif err := DB.Create(&post).Error; err != nil {\n\t\tt.Errorf(\"Failed, got error: %v\", err)\n\t}\n}\n\nfunc TestPostgresOnConstraint(t *testing.T) {\n\tif DB.Dialector.Name() != \"postgres\" {\n\t\tt.Skip()\n\t}\n\n\ttype Thing struct {\n\t\tgorm.Model\n\t\tSomeID  string\n\t\tOtherID string\n\t\tData    string\n\t}\n\n\tDB.Migrator().DropTable(&Thing{})\n\tDB.Migrator().CreateTable(&Thing{})\n\tif err := DB.Exec(\"ALTER TABLE things ADD CONSTRAINT some_id_other_id_unique UNIQUE (some_id, other_id)\").Error; err != nil {\n\t\tt.Error(err)\n\t}\n\n\tthing := Thing{\n\t\tSomeID:  \"1234\",\n\t\tOtherID: \"1234\",\n\t\tData:    \"something\",\n\t}\n\n\tDB.Create(&thing)\n\n\tthing2 := Thing{\n\t\tSomeID:  \"1234\",\n\t\tOtherID: \"1234\",\n\t\tData:    \"something else\",\n\t}\n\n\tresult := DB.Clauses(clause.OnConflict{\n\t\tOnConstraint: \"some_id_other_id_unique\",\n\t\tUpdateAll:    true,\n\t}).Create(&thing2)\n\tif result.Error != nil {\n\t\tt.Errorf(\"creating second thing: %v\", result.Error)\n\t}\n\n\tvar things []Thing\n\tif err := DB.Find(&things).Error; err != nil {\n\t\tt.Errorf(\"Failed, got error: %v\", err)\n\t}\n\n\tif len(things) > 1 {\n\t\tt.Errorf(\"expected 1 thing got more\")\n\t}\n}\n\ntype CompanyNew struct {\n\tID   int\n\tName int\n}\n\nfunc TestAlterColumnDataType(t *testing.T) {\n\tDB.AutoMigrate(Company{})\n\n\tif err := DB.Table(\"companies\").Migrator().AlterColumn(CompanyNew{}, \"name\"); err != nil {\n\t\tt.Fatalf(\"failed to alter column from string to int, got error %v\", err)\n\t}\n\n\tDB.AutoMigrate(Company{})\n}\n"
  },
  {
    "path": "tests/preload_suits_test.go",
    "content": "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/gorm\"\n)\n\nfunc toJSONString(v interface{}) []byte {\n\tr, _ := json.Marshal(v)\n\treturn r\n}\n\nfunc TestNestedPreload1(t *testing.T) {\n\ttype (\n\t\tLevel1 struct {\n\t\t\tID       uint\n\t\t\tValue    string\n\t\t\tLevel2ID uint\n\t\t}\n\t\tLevel2 struct {\n\t\t\tID       uint\n\t\t\tLevel1   Level1\n\t\t\tLevel3ID uint\n\t\t}\n\t\tLevel3 struct {\n\t\t\tID     uint\n\t\t\tName   string\n\t\t\tLevel2 Level2\n\t\t}\n\t)\n\tDB.Migrator().DropTable(&Level3{}, &Level2{}, &Level1{})\n\tif err := DB.AutoMigrate(&Level3{}, &Level2{}, &Level1{}); err != nil {\n\t\tt.Error(err)\n\t}\n\n\twant := Level3{Level2: Level2{Level1: Level1{Value: \"value\"}}}\n\tif err := DB.Create(&want).Error; err != nil {\n\t\tt.Error(err)\n\t}\n\n\tvar got Level3\n\tif err := DB.Preload(\"Level2\").Preload(\"Level2.Level1\").Find(&got).Error; err != nil {\n\t\tt.Error(err)\n\t}\n\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Errorf(\"got %s; want %s\", toJSONString(got), toJSONString(want))\n\t}\n\n\tif err := DB.Preload(\"Level2\").Preload(\"Level2.Level1\").First(&got, \"name = ?\", \"not_found\").Error; err != gorm.ErrRecordNotFound {\n\t\tt.Error(err)\n\t}\n}\n\nfunc TestNestedPreload2(t *testing.T) {\n\ttype (\n\t\tLevel1 struct {\n\t\t\tID       uint\n\t\t\tValue    string\n\t\t\tLevel2ID uint\n\t\t}\n\t\tLevel2 struct {\n\t\t\tID       uint\n\t\t\tLevel1s  []*Level1\n\t\t\tLevel3ID uint\n\t\t}\n\t\tLevel3 struct {\n\t\t\tID      uint\n\t\t\tName    string\n\t\t\tLevel2s []Level2\n\t\t}\n\t)\n\tDB.Migrator().DropTable(&Level3{}, &Level2{}, &Level1{})\n\tif err := DB.AutoMigrate(&Level3{}, &Level2{}, &Level1{}); err != nil {\n\t\tt.Error(err)\n\t}\n\n\twant := Level3{\n\t\tLevel2s: []Level2{\n\t\t\t{\n\t\t\t\tLevel1s: []*Level1{\n\t\t\t\t\t{Value: \"value1\"},\n\t\t\t\t\t{Value: \"value2\"},\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tLevel1s: []*Level1{\n\t\t\t\t\t{Value: \"value3\"},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\tif err := DB.Create(&want).Error; err != nil {\n\t\tt.Error(err)\n\t}\n\n\tvar got Level3\n\tif err := DB.Preload(\"Level2s.Level1s\").Find(&got).Error; err != nil {\n\t\tt.Error(err)\n\t}\n\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Errorf(\"got %s; want %s\", toJSONString(got), toJSONString(want))\n\t}\n}\n\nfunc TestNestedPreload3(t *testing.T) {\n\ttype (\n\t\tLevel1 struct {\n\t\t\tID       uint\n\t\t\tValue    string\n\t\t\tLevel2ID uint\n\t\t}\n\t\tLevel2 struct {\n\t\t\tID       uint\n\t\t\tLevel1   Level1\n\t\t\tLevel3ID uint\n\t\t}\n\t\tLevel3 struct {\n\t\t\tName    string\n\t\t\tID      uint\n\t\t\tLevel2s []Level2\n\t\t}\n\t)\n\tDB.Migrator().DropTable(&Level3{}, &Level2{}, &Level1{})\n\tif err := DB.AutoMigrate(&Level3{}, &Level2{}, &Level1{}); err != nil {\n\t\tt.Error(err)\n\t}\n\n\twant := Level3{\n\t\tLevel2s: []Level2{\n\t\t\t{Level1: Level1{Value: \"value1\"}},\n\t\t\t{Level1: Level1{Value: \"value2\"}},\n\t\t},\n\t}\n\tif err := DB.Create(&want).Error; err != nil {\n\t\tt.Error(err)\n\t}\n\n\tvar got Level3\n\tif err := DB.Preload(\"Level2s.Level1\").Find(&got).Error; err != nil {\n\t\tt.Error(err)\n\t}\n\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Errorf(\"got %s; want %s\", toJSONString(got), toJSONString(want))\n\t}\n}\n\nfunc TestNestedPreload4(t *testing.T) {\n\ttype (\n\t\tLevel1 struct {\n\t\t\tID       uint\n\t\t\tValue    string\n\t\t\tLevel2ID uint\n\t\t}\n\t\tLevel2 struct {\n\t\t\tID       uint\n\t\t\tLevel1s  []Level1\n\t\t\tLevel3ID uint\n\t\t}\n\t\tLevel3 struct {\n\t\t\tID     uint\n\t\t\tName   string\n\t\t\tLevel2 Level2\n\t\t}\n\t)\n\tDB.Migrator().DropTable(&Level3{}, &Level2{}, &Level1{})\n\tif err := DB.AutoMigrate(&Level3{}, &Level2{}, &Level1{}); err != nil {\n\t\tt.Error(err)\n\t}\n\n\twant := Level3{\n\t\tLevel2: Level2{\n\t\t\tLevel1s: []Level1{\n\t\t\t\t{Value: \"value1\"},\n\t\t\t\t{Value: \"value2\"},\n\t\t\t},\n\t\t},\n\t}\n\tif err := DB.Create(&want).Error; err != nil {\n\t\tt.Error(err)\n\t}\n\n\tvar got Level3\n\tif err := DB.Preload(\"Level2.Level1s\").Find(&got).Error; err != nil {\n\t\tt.Error(err)\n\t}\n\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Errorf(\"got %s; want %s\", toJSONString(got), toJSONString(want))\n\t}\n}\n\n// Slice: []Level3\nfunc TestNestedPreload5(t *testing.T) {\n\ttype (\n\t\tLevel1 struct {\n\t\t\tID       uint\n\t\t\tValue    string\n\t\t\tLevel2ID uint\n\t\t}\n\t\tLevel2 struct {\n\t\t\tID       uint\n\t\t\tLevel1   Level1\n\t\t\tLevel3ID uint\n\t\t}\n\t\tLevel3 struct {\n\t\t\tID     uint\n\t\t\tName   string\n\t\t\tLevel2 Level2\n\t\t}\n\t)\n\tDB.Migrator().DropTable(&Level3{}, &Level2{}, &Level1{})\n\tif err := DB.AutoMigrate(&Level3{}, &Level2{}, &Level1{}); err != nil {\n\t\tt.Error(err)\n\t}\n\n\twant := make([]Level3, 2)\n\twant[0] = Level3{Level2: Level2{Level1: Level1{Value: \"value\"}}}\n\tif err := DB.Create(&want[0]).Error; err != nil {\n\t\tt.Error(err)\n\t}\n\twant[1] = Level3{Level2: Level2{Level1: Level1{Value: \"value2\"}}}\n\tif err := DB.Create(&want[1]).Error; err != nil {\n\t\tt.Error(err)\n\t}\n\n\tvar got []Level3\n\tif err := DB.Preload(\"Level2\").Preload(\"Level2.Level1\").Find(&got).Error; err != nil {\n\t\tt.Error(err)\n\t}\n\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Errorf(\"got %s; want %s\", toJSONString(got), toJSONString(want))\n\t}\n}\n\nfunc TestNestedPreload6(t *testing.T) {\n\ttype (\n\t\tLevel1 struct {\n\t\t\tID       uint\n\t\t\tValue    string\n\t\t\tLevel2ID uint\n\t\t}\n\t\tLevel2 struct {\n\t\t\tID       uint\n\t\t\tLevel1s  []Level1\n\t\t\tLevel3ID uint\n\t\t}\n\t\tLevel3 struct {\n\t\t\tID      uint\n\t\t\tName    string\n\t\t\tLevel2s []Level2\n\t\t}\n\t)\n\tDB.Migrator().DropTable(&Level3{}, &Level2{}, &Level1{})\n\tif err := DB.AutoMigrate(&Level3{}, &Level2{}, &Level1{}); err != nil {\n\t\tt.Error(err)\n\t}\n\n\twant := make([]Level3, 2)\n\twant[0] = Level3{\n\t\tLevel2s: []Level2{\n\t\t\t{\n\t\t\t\tLevel1s: []Level1{\n\t\t\t\t\t{Value: \"value1\"},\n\t\t\t\t\t{Value: \"value2\"},\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tLevel1s: []Level1{\n\t\t\t\t\t{Value: \"value3\"},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\tif err := DB.Create(&want[0]).Error; err != nil {\n\t\tt.Error(err)\n\t}\n\n\twant[1] = Level3{\n\t\tLevel2s: []Level2{\n\t\t\t{\n\t\t\t\tLevel1s: []Level1{\n\t\t\t\t\t{Value: \"value3\"},\n\t\t\t\t\t{Value: \"value4\"},\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tLevel1s: []Level1{\n\t\t\t\t\t{Value: \"value5\"},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\tif err := DB.Create(&want[1]).Error; err != nil {\n\t\tt.Error(err)\n\t}\n\n\tvar got []Level3\n\tif err := DB.Preload(\"Level2s.Level1s\").Find(&got).Error; err != nil {\n\t\tt.Error(err)\n\t}\n\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Errorf(\"got %s; want %s\", toJSONString(got), toJSONString(want))\n\t}\n}\n\nfunc TestNestedPreload7(t *testing.T) {\n\ttype (\n\t\tLevel1 struct {\n\t\t\tID       uint\n\t\t\tValue    string\n\t\t\tLevel2ID uint\n\t\t}\n\t\tLevel2 struct {\n\t\t\tID       uint\n\t\t\tLevel1   Level1\n\t\t\tLevel3ID uint\n\t\t}\n\t\tLevel3 struct {\n\t\t\tID      uint\n\t\t\tName    string\n\t\t\tLevel2s []Level2\n\t\t}\n\t)\n\tDB.Migrator().DropTable(&Level3{}, &Level2{}, &Level1{})\n\tif err := DB.AutoMigrate(&Level3{}, &Level2{}, &Level1{}); err != nil {\n\t\tt.Error(err)\n\t}\n\n\twant := make([]Level3, 2)\n\twant[0] = Level3{\n\t\tLevel2s: []Level2{\n\t\t\t{Level1: Level1{Value: \"value1\"}},\n\t\t\t{Level1: Level1{Value: \"value2\"}},\n\t\t},\n\t}\n\tif err := DB.Create(&want[0]).Error; err != nil {\n\t\tt.Error(err)\n\t}\n\n\twant[1] = Level3{\n\t\tLevel2s: []Level2{\n\t\t\t{Level1: Level1{Value: \"value3\"}},\n\t\t\t{Level1: Level1{Value: \"value4\"}},\n\t\t},\n\t}\n\tif err := DB.Create(&want[1]).Error; err != nil {\n\t\tt.Error(err)\n\t}\n\n\tvar got []Level3\n\tif err := DB.Preload(\"Level2s.Level1\").Find(&got).Error; err != nil {\n\t\tt.Error(err)\n\t}\n\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Errorf(\"got %s; want %s\", toJSONString(got), toJSONString(want))\n\t}\n}\n\nfunc TestNestedPreload8(t *testing.T) {\n\ttype (\n\t\tLevel1 struct {\n\t\t\tID       uint\n\t\t\tValue    string\n\t\t\tLevel2ID uint\n\t\t}\n\t\tLevel2 struct {\n\t\t\tID       uint\n\t\t\tLevel1s  []Level1\n\t\t\tLevel3ID uint\n\t\t}\n\t\tLevel3 struct {\n\t\t\tID     uint\n\t\t\tName   string\n\t\t\tLevel2 Level2\n\t\t}\n\t)\n\tDB.Migrator().DropTable(&Level3{}, &Level2{}, &Level1{})\n\tif err := DB.AutoMigrate(&Level3{}, &Level2{}, &Level1{}); err != nil {\n\t\tt.Error(err)\n\t}\n\n\twant := make([]Level3, 2)\n\twant[0] = Level3{\n\t\tLevel2: Level2{\n\t\t\tLevel1s: []Level1{\n\t\t\t\t{Value: \"value1\"},\n\t\t\t\t{Value: \"value2\"},\n\t\t\t},\n\t\t},\n\t}\n\tif err := DB.Create(&want[0]).Error; err != nil {\n\t\tt.Error(err)\n\t}\n\twant[1] = Level3{\n\t\tLevel2: Level2{\n\t\t\tLevel1s: []Level1{\n\t\t\t\t{Value: \"value3\"},\n\t\t\t\t{Value: \"value4\"},\n\t\t\t},\n\t\t},\n\t}\n\tif err := DB.Create(&want[1]).Error; err != nil {\n\t\tt.Error(err)\n\t}\n\n\tvar got []Level3\n\tif err := DB.Preload(\"Level2.Level1s\").Find(&got).Error; err != nil {\n\t\tt.Error(err)\n\t}\n\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Errorf(\"got %s; want %s\", toJSONString(got), toJSONString(want))\n\t}\n}\n\nfunc TestNestedPreload9(t *testing.T) {\n\ttype (\n\t\tLevel0 struct {\n\t\t\tID       uint\n\t\t\tValue    string\n\t\t\tLevel1ID uint\n\t\t}\n\t\tLevel1 struct {\n\t\t\tID         uint\n\t\t\tValue      string\n\t\t\tLevel2ID   *uint\n\t\t\tLevel2_1ID *uint\n\t\t\tLevel0s    []Level0 `json:\",omitempty\"`\n\t\t}\n\t\tLevel2 struct {\n\t\t\tID       uint\n\t\t\tLevel1s  []Level1\n\t\t\tLevel3ID uint\n\t\t}\n\t\tLevel2_1 struct {\n\t\t\tID       uint\n\t\t\tLevel1s  []Level1 `json:\",omitempty\"`\n\t\t\tLevel3ID uint\n\t\t}\n\t\tLevel3 struct {\n\t\t\tID       uint\n\t\t\tName     string\n\t\t\tLevel2   Level2\n\t\t\tLevel2_1 Level2_1\n\t\t}\n\t)\n\tDB.Migrator().DropTable(&Level3{}, &Level2{}, &Level1{}, &Level2_1{}, &Level0{})\n\tif err := DB.AutoMigrate(&Level3{}, &Level2{}, &Level1{}, &Level2_1{}, &Level0{}); err != nil {\n\t\tt.Error(err)\n\t}\n\n\twant := make([]Level3, 2)\n\twant[0] = Level3{\n\t\tLevel2: Level2{\n\t\t\tLevel1s: []Level1{\n\t\t\t\t{Value: \"value1\"},\n\t\t\t\t{Value: \"value2\"},\n\t\t\t},\n\t\t},\n\t\tLevel2_1: Level2_1{\n\t\t\tLevel1s: []Level1{\n\t\t\t\t{\n\t\t\t\t\tValue:   \"value1-1\",\n\t\t\t\t\tLevel0s: []Level0{{Value: \"Level0-1\"}},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tValue:   \"value2-2\",\n\t\t\t\t\tLevel0s: []Level0{{Value: \"Level0-2\"}},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\tif err := DB.Create(&want[0]).Error; err != nil {\n\t\tt.Error(err)\n\t}\n\twant[1] = Level3{\n\t\tLevel2: Level2{\n\t\t\tLevel1s: []Level1{\n\t\t\t\t{Value: \"value3\"},\n\t\t\t\t{Value: \"value4\"},\n\t\t\t},\n\t\t},\n\t\tLevel2_1: Level2_1{\n\t\t\tLevel1s: []Level1{\n\t\t\t\t{\n\t\t\t\t\tValue:   \"value3-3\",\n\t\t\t\t\tLevel0s: []Level0{},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tValue:   \"value4-4\",\n\t\t\t\t\tLevel0s: []Level0{},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\tif err := DB.Create(&want[1]).Error; err != nil {\n\t\tt.Error(err)\n\t}\n\n\tvar got []Level3\n\tif err := DB.Preload(\"Level2\").Preload(\"Level2.Level1s\").Preload(\"Level2_1\").Preload(\"Level2_1.Level1s\").Preload(\"Level2_1.Level1s.Level0s\").Find(&got).Error; err != nil {\n\t\tt.Error(err)\n\t}\n\n\tif string(toJSONString(got)) != string(toJSONString(want)) {\n\t\tt.Errorf(\"got %s; want %s\", toJSONString(got), toJSONString(want))\n\t}\n}\n\ntype LevelA1 struct {\n\tID    uint\n\tValue string\n}\n\ntype LevelA2 struct {\n\tID       uint\n\tValue    string\n\tLevelA3s []*LevelA3 `json:\",omitempty\"`\n}\n\ntype LevelA3 struct {\n\tID        uint\n\tValue     string\n\tLevelA1ID sql.NullInt64\n\tLevelA1   *LevelA1\n\tLevelA2ID sql.NullInt64\n\tLevelA2   *LevelA2\n}\n\nfunc TestNestedPreload10(t *testing.T) {\n\tDB.Migrator().DropTable(&LevelA3{}, &LevelA2{}, &LevelA1{})\n\tif err := DB.AutoMigrate(&LevelA1{}, &LevelA2{}, &LevelA3{}); err != nil {\n\t\tt.Error(err)\n\t}\n\n\tlevelA1 := &LevelA1{Value: \"foo\"}\n\tif err := DB.Save(levelA1).Error; err != nil {\n\t\tt.Error(err)\n\t}\n\n\twant := []*LevelA2{\n\t\t{\n\t\t\tValue: \"bar\",\n\t\t\tLevelA3s: []*LevelA3{\n\t\t\t\t{\n\t\t\t\t\tValue:   \"qux\",\n\t\t\t\t\tLevelA1: levelA1,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tValue:    \"bar 2\",\n\t\t\tLevelA3s: []*LevelA3{},\n\t\t},\n\t}\n\tfor _, levelA2 := range want {\n\t\tif err := DB.Save(levelA2).Error; err != nil {\n\t\t\tt.Error(err)\n\t\t}\n\t}\n\n\tvar got []*LevelA2\n\tif err := DB.Preload(\"LevelA3s.LevelA1\").Find(&got).Error; err != nil {\n\t\tt.Error(err)\n\t}\n\n\tif !reflect.DeepEqual(toJSONString(got), toJSONString(want)) {\n\t\tt.Errorf(\"got %s; want %s\", toJSONString(got), toJSONString(want))\n\t}\n}\n\ntype LevelB1 struct {\n\tID       uint\n\tValue    string\n\tLevelB3s []*LevelB3\n}\n\ntype LevelB2 struct {\n\tID    uint\n\tValue string\n}\n\ntype LevelB3 struct {\n\tID        uint\n\tValue     string\n\tLevelB1ID sql.NullInt64\n\tLevelB1   *LevelB1\n\tLevelB2s  []*LevelB2 `gorm:\"many2many:levelb1_levelb3_levelb2s\" json:\",omitempty\"`\n}\n\nfunc TestNestedPreload11(t *testing.T) {\n\tDB.Migrator().DropTable(&LevelB3{}, &LevelB2{}, &LevelB1{})\n\tif err := DB.AutoMigrate(&LevelB1{}, &LevelB2{}, &LevelB3{}); err != nil {\n\t\tt.Error(err)\n\t}\n\n\tlevelB1 := &LevelB1{Value: \"foo\"}\n\tif err := DB.Create(levelB1).Error; err != nil {\n\t\tt.Error(err)\n\t}\n\n\tlevelB3 := &LevelB3{\n\t\tValue:     \"bar\",\n\t\tLevelB1ID: sql.NullInt64{Valid: true, Int64: int64(levelB1.ID)},\n\t\tLevelB2s:  []*LevelB2{},\n\t}\n\tif err := DB.Create(levelB3).Error; err != nil {\n\t\tt.Error(err)\n\t}\n\tlevelB1.LevelB3s = []*LevelB3{levelB3}\n\n\twant := []*LevelB1{levelB1}\n\tvar got []*LevelB1\n\tif err := DB.Preload(\"LevelB3s.LevelB2s\").Find(&got).Error; err != nil {\n\t\tt.Error(err)\n\t}\n\n\tif !reflect.DeepEqual(toJSONString(got), toJSONString(want)) {\n\t\tt.Errorf(\"got %s; want %s\", toJSONString(got), toJSONString(want))\n\t}\n}\n\ntype LevelC1 struct {\n\tID        uint\n\tValue     string\n\tLevelC2ID uint\n}\n\ntype LevelC2 struct {\n\tID      uint\n\tValue   string\n\tLevelC1 LevelC1\n}\n\ntype LevelC3 struct {\n\tID        uint\n\tValue     string\n\tLevelC2ID uint\n\tLevelC2   LevelC2\n}\n\nfunc TestNestedPreload12(t *testing.T) {\n\tDB.Migrator().DropTable(&LevelC3{}, &LevelC2{}, &LevelC1{})\n\tif err := DB.AutoMigrate(&LevelC1{}, &LevelC2{}, &LevelC3{}); err != nil {\n\t\tt.Error(err)\n\t}\n\n\tlevel2 := LevelC2{\n\t\tValue: \"c2\",\n\t\tLevelC1: LevelC1{\n\t\t\tValue: \"c1\",\n\t\t},\n\t}\n\tDB.Create(&level2)\n\n\twant := []LevelC3{\n\t\t{\n\t\t\tValue:   \"c3-1\",\n\t\t\tLevelC2: level2,\n\t\t}, {\n\t\t\tValue:   \"c3-2\",\n\t\t\tLevelC2: level2,\n\t\t},\n\t}\n\n\tfor i := range want {\n\t\tif err := DB.Create(&want[i]).Error; err != nil {\n\t\t\tt.Error(err)\n\t\t}\n\t}\n\n\tvar got []LevelC3\n\tif err := DB.Preload(\"LevelC2\").Preload(\"LevelC2.LevelC1\").Find(&got).Error; err != nil {\n\t\tt.Error(err)\n\t}\n\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Errorf(\"got %s; want %s\", toJSONString(got), toJSONString(want))\n\t}\n}\n\nfunc TestManyToManyPreloadWithMultiPrimaryKeys(t *testing.T) {\n\tif name := DB.Dialector.Name(); name == \"sqlite\" || name == \"sqlserver\" {\n\t\tt.Skip(\"skip sqlite, sqlserver due to it doesn't support multiple primary keys with auto increment\")\n\t}\n\n\tif name := DB.Dialector.Name(); name == \"mysql\" {\n\t\tt.Skip(\"skip mysql due to it only allow unique constraint matching given keys\")\n\t}\n\n\ttype (\n\t\tLevel1 struct {\n\t\t\tID           uint   `gorm:\"primary_key;\"`\n\t\t\tLanguageCode string `gorm:\"primary_key\"`\n\t\t\tValue        string\n\t\t}\n\t\tLevel2 struct {\n\t\t\tID           uint   `gorm:\"primary_key;\"`\n\t\t\tLanguageCode string `gorm:\"primary_key\"`\n\t\t\tValue        string\n\t\t\tLevel1s      []Level1 `gorm:\"many2many:levels;\"`\n\t\t}\n\t)\n\n\tDB.Migrator().DropTable(&Level2{}, &Level1{})\n\tDB.Migrator().DropTable(\"levels\")\n\n\tif err := DB.AutoMigrate(&Level2{}, &Level1{}); err != nil {\n\t\tt.Error(err)\n\t}\n\n\twant := Level2{Value: \"Bob\", LanguageCode: \"ru\", Level1s: []Level1{\n\t\t{Value: \"ru\", LanguageCode: \"ru\"},\n\t\t{Value: \"en\", LanguageCode: \"en\"},\n\t}}\n\tif err := DB.Save(&want).Error; err != nil {\n\t\tt.Error(err)\n\t}\n\n\twant2 := Level2{Value: \"Tom\", LanguageCode: \"zh\", Level1s: []Level1{\n\t\t{Value: \"zh\", LanguageCode: \"zh\"},\n\t\t{Value: \"de\", LanguageCode: \"de\"},\n\t}}\n\tif err := DB.Save(&want2).Error; err != nil {\n\t\tt.Error(err)\n\t}\n\n\tvar got Level2\n\tif err := DB.Preload(\"Level1s\").Find(&got, \"value = ?\", \"Bob\").Error; err != nil {\n\t\tt.Error(err)\n\t}\n\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Errorf(\"got %s; want %s\", toJSONString(got), toJSONString(want))\n\t}\n\n\tvar got2 Level2\n\tif err := DB.Preload(\"Level1s\").Find(&got2, \"value = ?\", \"Tom\").Error; err != nil {\n\t\tt.Error(err)\n\t}\n\n\tif !reflect.DeepEqual(got2, want2) {\n\t\tt.Errorf(\"got %s; want %s\", toJSONString(got2), toJSONString(want2))\n\t}\n\n\tvar got3 []Level2\n\tif err := DB.Preload(\"Level1s\").Find(&got3, \"value IN (?)\", []string{\"Bob\", \"Tom\"}).Error; err != nil {\n\t\tt.Error(err)\n\t}\n\n\tif !reflect.DeepEqual(got3, []Level2{got, got2}) {\n\t\tt.Errorf(\"got %s; want %s\", toJSONString(got3), toJSONString([]Level2{got, got2}))\n\t}\n\n\tvar got4 []Level2\n\tif err := DB.Preload(\"Level1s\", \"value IN (?)\", []string{\"zh\", \"ru\"}).Find(&got4, \"value IN (?)\", []string{\"Bob\", \"Tom\"}).Error; err != nil {\n\t\tt.Error(err)\n\t}\n\n\tvar ruLevel1 Level1\n\tvar zhLevel1 Level1\n\tDB.First(&ruLevel1, \"value = ?\", \"ru\")\n\tDB.First(&zhLevel1, \"value = ?\", \"zh\")\n\n\tgot.Level1s = []Level1{ruLevel1}\n\tgot2.Level1s = []Level1{zhLevel1}\n\tif !reflect.DeepEqual(got4, []Level2{got, got2}) {\n\t\tt.Errorf(\"got %s; want %s\", toJSONString(got4), toJSONString([]Level2{got, got2}))\n\t}\n\n\tif err := DB.Preload(\"Level1s\").Find(&got4, \"value IN (?)\", []string{\"non-existing\"}).Error; err != nil {\n\t\tt.Error(err)\n\t}\n}\n\nfunc TestManyToManyPreloadForNestedPointer(t *testing.T) {\n\ttype (\n\t\tLevel1 struct {\n\t\t\tID    uint\n\t\t\tValue string\n\t\t}\n\t\tLevel2 struct {\n\t\t\tID      uint\n\t\t\tValue   string\n\t\t\tLevel1s []*Level1 `gorm:\"many2many:levels;\"`\n\t\t}\n\t\tLevel3 struct {\n\t\t\tID       uint\n\t\t\tValue    string\n\t\t\tLevel2ID sql.NullInt64\n\t\t\tLevel2   *Level2\n\t\t}\n\t)\n\n\tDB.Migrator().DropTable(&Level3{}, &Level2{}, &Level1{})\n\tDB.Migrator().DropTable(\"levels\")\n\n\tif err := DB.AutoMigrate(&Level3{}, &Level2{}, &Level1{}); err != nil {\n\t\tt.Error(err)\n\t}\n\n\twant := Level3{\n\t\tValue: \"Bob\",\n\t\tLevel2: &Level2{\n\t\t\tValue: \"Foo\",\n\t\t\tLevel1s: []*Level1{\n\t\t\t\t{Value: \"ru\"},\n\t\t\t\t{Value: \"en\"},\n\t\t\t},\n\t\t},\n\t}\n\tif err := DB.Save(&want).Error; err != nil {\n\t\tt.Error(err)\n\t}\n\n\twant2 := Level3{\n\t\tValue: \"Tom\",\n\t\tLevel2: &Level2{\n\t\t\tValue: \"Bar\",\n\t\t\tLevel1s: []*Level1{\n\t\t\t\t{Value: \"zh\"},\n\t\t\t\t{Value: \"de\"},\n\t\t\t},\n\t\t},\n\t}\n\tif err := DB.Save(&want2).Error; err != nil {\n\t\tt.Error(err)\n\t}\n\n\tvar got Level3\n\tif err := DB.Preload(\"Level2.Level1s\").Find(&got, \"value = ?\", \"Bob\").Error; err != nil {\n\t\tt.Error(err)\n\t}\n\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Errorf(\"got %s; want %s\", toJSONString(got), toJSONString(want))\n\t}\n\n\tvar got2 Level3\n\tif err := DB.Preload(\"Level2.Level1s\").Find(&got2, \"value = ?\", \"Tom\").Error; err != nil {\n\t\tt.Error(err)\n\t}\n\n\tif !reflect.DeepEqual(got2, want2) {\n\t\tt.Errorf(\"got %s; want %s\", toJSONString(got2), toJSONString(want2))\n\t}\n\n\tvar got3 []Level3\n\tif err := DB.Preload(\"Level2.Level1s\").Find(&got3, \"value IN (?)\", []string{\"Bob\", \"Tom\"}).Error; err != nil {\n\t\tt.Error(err)\n\t}\n\n\tif !reflect.DeepEqual(got3, []Level3{got, got2}) {\n\t\tt.Errorf(\"got %s; want %s\", toJSONString(got3), toJSONString([]Level3{got, got2}))\n\t}\n\n\tvar got4 []Level3\n\tif err := DB.Preload(\"Level2.Level1s\", \"value IN (?)\", []string{\"zh\", \"ru\"}).Find(&got4, \"value IN (?)\", []string{\"Bob\", \"Tom\"}).Error; err != nil {\n\t\tt.Error(err)\n\t}\n\n\tvar got5 Level3\n\tDB.Preload(\"Level2.Level1s\").Find(&got5, \"value = ?\", \"bogus\")\n\n\tvar ruLevel1 Level1\n\tvar zhLevel1 Level1\n\tDB.First(&ruLevel1, \"value = ?\", \"ru\")\n\tDB.First(&zhLevel1, \"value = ?\", \"zh\")\n\n\tgot.Level2.Level1s = []*Level1{&ruLevel1}\n\tgot2.Level2.Level1s = []*Level1{&zhLevel1}\n\tif !reflect.DeepEqual(got4, []Level3{got, got2}) {\n\t\tt.Errorf(\"got %s; want %s\", toJSONString(got4), toJSONString([]Level3{got, got2}))\n\t}\n}\n\nfunc TestNestedManyToManyPreload(t *testing.T) {\n\ttype (\n\t\tLevel1 struct {\n\t\t\tID    uint\n\t\t\tValue string\n\t\t}\n\t\tLevel2 struct {\n\t\t\tID      uint\n\t\t\tValue   string\n\t\t\tLevel1s []*Level1 `gorm:\"many2many:level1_level2;\"`\n\t\t}\n\t\tLevel3 struct {\n\t\t\tID      uint\n\t\t\tValue   string\n\t\t\tLevel2s []Level2 `gorm:\"many2many:level2_level3;\"`\n\t\t}\n\t)\n\n\tDB.Migrator().DropTable(&Level3{}, &Level2{}, &Level1{}, \"level1_level2\", \"level2_level3\")\n\n\tif err := DB.AutoMigrate(&Level3{}, &Level2{}, &Level1{}); err != nil {\n\t\tt.Error(err)\n\t}\n\n\twant := Level3{\n\t\tValue: \"Level3\",\n\t\tLevel2s: []Level2{\n\t\t\t{\n\t\t\t\tValue: \"Bob\",\n\t\t\t\tLevel1s: []*Level1{\n\t\t\t\t\t{Value: \"ru\"},\n\t\t\t\t\t{Value: \"en\"},\n\t\t\t\t},\n\t\t\t}, {\n\t\t\t\tValue: \"Tom\",\n\t\t\t\tLevel1s: []*Level1{\n\t\t\t\t\t{Value: \"zh\"},\n\t\t\t\t\t{Value: \"de\"},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\n\tif err := DB.Save(&want).Error; err != nil {\n\t\tt.Error(err)\n\t}\n\n\tvar got Level3\n\tif err := DB.Preload(\"Level2s\").Preload(\"Level2s.Level1s\").Find(&got, \"value = ?\", \"Level3\").Error; err != nil {\n\t\tt.Error(err)\n\t}\n\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Errorf(\"got %s; want %s\", toJSONString(got), toJSONString(want))\n\t}\n\n\tif err := DB.Preload(\"Level2s.Level1s\").First(&got, \"value = ?\", \"not_found\").Error; err != gorm.ErrRecordNotFound {\n\t\tt.Error(err)\n\t}\n}\n\nfunc TestNestedManyToManyPreload2(t *testing.T) {\n\ttype (\n\t\tLevel1 struct {\n\t\t\tID    uint\n\t\t\tValue string\n\t\t}\n\t\tLevel2 struct {\n\t\t\tID      uint\n\t\t\tValue   string\n\t\t\tLevel1s []*Level1 `gorm:\"many2many:level1_level2;\"`\n\t\t}\n\t\tLevel3 struct {\n\t\t\tID       uint\n\t\t\tValue    string\n\t\t\tLevel2ID sql.NullInt64\n\t\t\tLevel2   *Level2\n\t\t}\n\t)\n\n\tDB.Migrator().DropTable(&Level3{}, &Level2{}, &Level1{})\n\tDB.Migrator().DropTable(\"level1_level2\")\n\n\tif err := DB.AutoMigrate(&Level3{}, &Level2{}, &Level1{}); err != nil {\n\t\tt.Error(err)\n\t}\n\n\twant := Level3{\n\t\tValue: \"Level3\",\n\t\tLevel2: &Level2{\n\t\t\tValue: \"Bob\",\n\t\t\tLevel1s: []*Level1{\n\t\t\t\t{Value: \"ru\"},\n\t\t\t\t{Value: \"en\"},\n\t\t\t},\n\t\t},\n\t}\n\n\tif err := DB.Save(&want).Error; err != nil {\n\t\tt.Error(err)\n\t}\n\n\tvar got Level3\n\tif err := DB.Preload(\"Level2.Level1s\").Find(&got, \"value = ?\", \"Level3\").Error; err != nil {\n\t\tt.Error(err)\n\t}\n\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Errorf(\"got %s; want %s\", toJSONString(got), toJSONString(want))\n\t}\n\n\tif err := DB.Preload(\"Level2.Level1s\").First(&got, \"value = ?\", \"not_found\").Error; err != gorm.ErrRecordNotFound {\n\t\tt.Error(err)\n\t}\n}\n\nfunc TestNestedManyToManyPreload3(t *testing.T) {\n\ttype (\n\t\tLevel1 struct {\n\t\t\tID    uint\n\t\t\tValue string\n\t\t}\n\t\tLevel2 struct {\n\t\t\tID      uint\n\t\t\tValue   string\n\t\t\tLevel1s []*Level1 `gorm:\"many2many:level1_level2;\"`\n\t\t}\n\t\tLevel3 struct {\n\t\t\tID       uint\n\t\t\tValue    string\n\t\t\tLevel2ID sql.NullInt64\n\t\t\tLevel2   *Level2\n\t\t}\n\t)\n\n\tDB.Migrator().DropTable(&Level3{}, &Level2{}, &Level1{}, \"level1_level2\")\n\n\tif err := DB.AutoMigrate(&Level3{}, &Level2{}, &Level1{}); err != nil {\n\t\tt.Error(err)\n\t}\n\n\tlevel1Zh := &Level1{Value: \"zh\"}\n\tlevel1Ru := &Level1{Value: \"ru\"}\n\tlevel1En := &Level1{Value: \"en\"}\n\n\tlevel21 := &Level2{\n\t\tValue:   \"Level2-1\",\n\t\tLevel1s: []*Level1{level1Zh, level1Ru},\n\t}\n\n\tlevel22 := &Level2{\n\t\tValue:   \"Level2-2\",\n\t\tLevel1s: []*Level1{level1Zh, level1En},\n\t}\n\n\twants := []*Level3{\n\t\t{\n\t\t\tValue:  \"Level3-1\",\n\t\t\tLevel2: level21,\n\t\t},\n\t\t{\n\t\t\tValue:  \"Level3-2\",\n\t\t\tLevel2: level22,\n\t\t},\n\t\t{\n\t\t\tValue:  \"Level3-3\",\n\t\t\tLevel2: level21,\n\t\t},\n\t}\n\n\tfor _, want := range wants {\n\t\tif err := DB.Save(want).Error; err != nil {\n\t\t\tt.Error(err)\n\t\t}\n\t}\n\n\tvar gots []*Level3\n\tif err := DB.Preload(\"Level2.Level1s\", func(db *gorm.DB) *gorm.DB {\n\t\treturn db.Order(\"level1.id ASC\")\n\t}).Find(&gots).Error; err != nil {\n\t\tt.Error(err)\n\t}\n\n\tif !reflect.DeepEqual(gots, wants) {\n\t\tt.Errorf(\"got %s; want %s\", toJSONString(gots), toJSONString(wants))\n\t}\n}\n\nfunc TestNestedManyToManyPreload3ForStruct(t *testing.T) {\n\ttype (\n\t\tLevel1 struct {\n\t\t\tID    uint\n\t\t\tValue string\n\t\t}\n\t\tLevel2 struct {\n\t\t\tID      uint\n\t\t\tValue   string\n\t\t\tLevel1s []Level1 `gorm:\"many2many:level1_level2;\"`\n\t\t}\n\t\tLevel3 struct {\n\t\t\tID       uint\n\t\t\tValue    string\n\t\t\tLevel2ID sql.NullInt64\n\t\t\tLevel2   Level2\n\t\t}\n\t)\n\n\tDB.Migrator().DropTable(&Level3{}, &Level2{}, &Level1{})\n\tDB.Migrator().DropTable(\"level1_level2\")\n\n\tif err := DB.AutoMigrate(&Level3{}, &Level2{}, &Level1{}); err != nil {\n\t\tt.Error(err)\n\t}\n\n\tlevel1Zh := Level1{Value: \"zh\"}\n\tlevel1Ru := Level1{Value: \"ru\"}\n\tlevel1En := Level1{Value: \"en\"}\n\n\tlevel21 := Level2{\n\t\tValue:   \"Level2-1\",\n\t\tLevel1s: []Level1{level1Zh, level1Ru},\n\t}\n\n\tlevel22 := Level2{\n\t\tValue:   \"Level2-2\",\n\t\tLevel1s: []Level1{level1Zh, level1En},\n\t}\n\n\twants := []*Level3{\n\t\t{\n\t\t\tValue:  \"Level3-1\",\n\t\t\tLevel2: level21,\n\t\t},\n\t\t{\n\t\t\tValue:  \"Level3-2\",\n\t\t\tLevel2: level22,\n\t\t},\n\t\t{\n\t\t\tValue:  \"Level3-3\",\n\t\t\tLevel2: level21,\n\t\t},\n\t}\n\n\tfor _, want := range wants {\n\t\tif err := DB.Save(want).Error; err != nil {\n\t\t\tt.Error(err)\n\t\t}\n\t}\n\n\tvar gots []*Level3\n\tif err := DB.Preload(\"Level2.Level1s\", func(db *gorm.DB) *gorm.DB {\n\t\treturn db.Order(\"level1.id ASC\")\n\t}).Find(&gots).Error; err != nil {\n\t\tt.Error(err)\n\t}\n\n\tif !reflect.DeepEqual(gots, wants) {\n\t\tt.Errorf(\"got %s; want %s\", toJSONString(gots), toJSONString(wants))\n\t}\n}\n\nfunc TestNestedManyToManyPreload4(t *testing.T) {\n\ttype (\n\t\tLevel4 struct {\n\t\t\tID       uint\n\t\t\tValue    string\n\t\t\tLevel3ID uint\n\t\t}\n\t\tLevel3 struct {\n\t\t\tID      uint\n\t\t\tValue   string\n\t\t\tLevel4s []*Level4\n\t\t}\n\t\tLevel2 struct {\n\t\t\tID      uint\n\t\t\tValue   string\n\t\t\tLevel3s []*Level3 `gorm:\"many2many:level2_level3;\"`\n\t\t}\n\t\tLevel1 struct {\n\t\t\tID      uint\n\t\t\tValue   string\n\t\t\tLevel2s []*Level2 `gorm:\"many2many:level1_level2;\"`\n\t\t}\n\t)\n\n\tDB.Migrator().DropTable(\"level1_level2\", \"level2_level3\")\n\tDB.Migrator().DropTable(&Level3{}, &Level2{}, &Level1{}, &Level4{})\n\n\tdummy := Level1{\n\t\tValue: \"Level1\",\n\t\tLevel2s: []*Level2{{\n\t\t\tValue: \"Level2\",\n\t\t\tLevel3s: []*Level3{{\n\t\t\t\tValue: \"Level3\",\n\t\t\t\tLevel4s: []*Level4{{\n\t\t\t\t\tValue: \"Level4\",\n\t\t\t\t}},\n\t\t\t}},\n\t\t}},\n\t}\n\n\tif err := DB.AutoMigrate(&Level4{}, &Level3{}, &Level2{}, &Level1{}); err != nil {\n\t\tt.Error(err)\n\t}\n\n\tif err := DB.Save(&dummy).Error; err != nil {\n\t\tt.Error(err)\n\t}\n\n\tvar level1 Level1\n\tif err := DB.Preload(\"Level2s\").Preload(\"Level2s.Level3s\").Preload(\"Level2s.Level3s.Level4s\").First(&level1).Error; err != nil {\n\t\tt.Error(err)\n\t}\n}\n\nfunc TestManyToManyPreloadForPointer(t *testing.T) {\n\ttype (\n\t\tLevel1 struct {\n\t\t\tID    uint\n\t\t\tValue string\n\t\t}\n\t\tLevel2 struct {\n\t\t\tID      uint\n\t\t\tValue   string\n\t\t\tLevel1s []*Level1 `gorm:\"many2many:levels;\"`\n\t\t}\n\t)\n\n\tDB.Migrator().DropTable(\"levels\", &Level2{}, &Level1{})\n\n\tif err := DB.AutoMigrate(&Level2{}, &Level1{}); err != nil {\n\t\tt.Error(err)\n\t}\n\n\twant := Level2{Value: \"Bob\", Level1s: []*Level1{\n\t\t{Value: \"ru\"},\n\t\t{Value: \"en\"},\n\t}}\n\tif err := DB.Save(&want).Error; err != nil {\n\t\tt.Error(err)\n\t}\n\n\twant2 := Level2{Value: \"Tom\", Level1s: []*Level1{\n\t\t{Value: \"zh\"},\n\t\t{Value: \"de\"},\n\t}}\n\tif err := DB.Save(&want2).Error; err != nil {\n\t\tt.Error(err)\n\t}\n\n\tvar got Level2\n\tif err := DB.Preload(\"Level1s\").Find(&got, \"value = ?\", \"Bob\").Error; err != nil {\n\t\tt.Error(err)\n\t}\n\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Errorf(\"got %s; want %s\", toJSONString(got), toJSONString(want))\n\t}\n\n\tvar got2 Level2\n\tif err := DB.Preload(\"Level1s\").Find(&got2, \"value = ?\", \"Tom\").Error; err != nil {\n\t\tt.Error(err)\n\t}\n\n\tif !reflect.DeepEqual(got2, want2) {\n\t\tt.Errorf(\"got %s; want %s\", toJSONString(got2), toJSONString(want2))\n\t}\n\n\tvar got3 []Level2\n\tif err := DB.Preload(\"Level1s\").Find(&got3, \"value IN (?)\", []string{\"Bob\", \"Tom\"}).Error; err != nil {\n\t\tt.Error(err)\n\t}\n\n\tif !reflect.DeepEqual(got3, []Level2{got, got2}) {\n\t\tt.Errorf(\"got %s; want %s\", toJSONString(got3), toJSONString([]Level2{got, got2}))\n\t}\n\n\tvar got4 []Level2\n\tif err := DB.Preload(\"Level1s\", \"value IN (?)\", []string{\"zh\", \"ru\"}).Find(&got4, \"value IN (?)\", []string{\"Bob\", \"Tom\"}).Error; err != nil {\n\t\tt.Error(err)\n\t}\n\n\tvar got5 Level2\n\tDB.Preload(\"Level1s\").First(&got5, \"value = ?\", \"bogus\")\n\n\tvar ruLevel1 Level1\n\tvar zhLevel1 Level1\n\tDB.First(&ruLevel1, \"value = ?\", \"ru\")\n\tDB.First(&zhLevel1, \"value = ?\", \"zh\")\n\n\tgot.Level1s = []*Level1{&ruLevel1}\n\tgot2.Level1s = []*Level1{&zhLevel1}\n\tif !reflect.DeepEqual(got4, []Level2{got, got2}) {\n\t\tt.Errorf(\"got %s; want %s\", toJSONString(got4), toJSONString([]Level2{got, got2}))\n\t}\n}\n\nfunc TestNilPointerSlice(t *testing.T) {\n\ttype (\n\t\tLevel3 struct {\n\t\t\tID    uint\n\t\t\tValue string\n\t\t}\n\t\tLevel2 struct {\n\t\t\tID       uint\n\t\t\tValue    string\n\t\t\tLevel3ID uint\n\t\t\tLevel3   *Level3\n\t\t}\n\t\tLevel1 struct {\n\t\t\tID       uint\n\t\t\tValue    string\n\t\t\tLevel2ID *uint\n\t\t\tLevel2   *Level2\n\t\t}\n\t)\n\n\tDB.Migrator().DropTable(&Level3{}, &Level2{}, &Level1{})\n\tif err := DB.AutoMigrate(&Level3{}, &Level2{}, &Level1{}); err != nil {\n\t\tt.Error(err)\n\t}\n\n\twant := Level1{\n\t\tValue: \"Bob\",\n\t\tLevel2: &Level2{\n\t\t\tValue: \"en\",\n\t\t\tLevel3: &Level3{\n\t\t\t\tValue: \"native\",\n\t\t\t},\n\t\t},\n\t}\n\tif err := DB.Save(&want).Error; err != nil {\n\t\tt.Error(err)\n\t}\n\n\twant2 := Level1{\n\t\tValue:  \"Tom\",\n\t\tLevel2: nil,\n\t}\n\tif err := DB.Save(&want2).Error; err != nil {\n\t\tt.Fatalf(\"Got error %v\", err)\n\t}\n\n\tvar got []Level1\n\tif err := DB.Preload(\"Level2\").Preload(\"Level2.Level3\").Find(&got).Error; err != nil {\n\t\tt.Error(err)\n\t}\n\n\tif len(got) != 2 {\n\t\tt.Errorf(\"got %v items, expected 2\", len(got))\n\t}\n\n\tif !reflect.DeepEqual(got[0], want) && !reflect.DeepEqual(got[1], want) {\n\t\tt.Fatalf(\"got %s; want array containing %s\", toJSONString(got), toJSONString(want))\n\t}\n\n\tif !reflect.DeepEqual(got[0], want2) && !reflect.DeepEqual(got[1], want2) {\n\t\tt.Errorf(\"got %s; want array containing %s\", toJSONString(got), toJSONString(want2))\n\t}\n}\n\nfunc TestNilPointerSlice2(t *testing.T) {\n\ttype (\n\t\tLevel4 struct {\n\t\t\tID uint\n\t\t}\n\t\tLevel3 struct {\n\t\t\tID       uint\n\t\t\tLevel4ID sql.NullInt64 `sql:\"index\"`\n\t\t\tLevel4   *Level4\n\t\t}\n\t\tLevel2 struct {\n\t\t\tID      uint\n\t\t\tLevel3s []*Level3 `gorm:\"many2many:level2_level3s\"`\n\t\t}\n\t\tLevel1 struct {\n\t\t\tID       uint\n\t\t\tLevel2ID sql.NullInt64 `sql:\"index\"`\n\t\t\tLevel2   *Level2\n\t\t}\n\t)\n\n\tDB.Migrator().DropTable(&Level3{}, &Level2{}, &Level1{}, &Level4{})\n\n\tif err := DB.AutoMigrate(new(Level4), new(Level3), new(Level2), new(Level1)); err != nil {\n\t\tt.Error(err)\n\t}\n\n\twant := new(Level1)\n\tif err := DB.Save(want).Error; err != nil {\n\t\tt.Error(err)\n\t}\n\n\tgot := new(Level1)\n\terr := DB.Preload(\"Level2.Level3s.Level4\").Last(&got).Error\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Errorf(\"got %s; want %s\", toJSONString(got), toJSONString(want))\n\t}\n}\n\nfunc TestPrefixedPreloadDuplication(t *testing.T) {\n\ttype (\n\t\tLevel4 struct {\n\t\t\tID       uint\n\t\t\tName     string\n\t\t\tLevel3ID uint\n\t\t}\n\t\tLevel3 struct {\n\t\t\tID      uint\n\t\t\tName    string\n\t\t\tLevel4s []*Level4 `json:\",omitempty\"`\n\t\t}\n\t\tLevel2 struct {\n\t\t\tID       uint\n\t\t\tName     string\n\t\t\tLevel3ID sql.NullInt64 `sql:\"index\"`\n\t\t\tLevel3   *Level3\n\t\t}\n\t\tLevel1 struct {\n\t\t\tID       uint\n\t\t\tName     string\n\t\t\tLevel2ID sql.NullInt64 `sql:\"index\"`\n\t\t\tLevel2   *Level2\n\t\t}\n\t)\n\n\tDB.Migrator().DropTable(&Level3{}, &Level2{}, &Level1{}, &Level4{})\n\n\tif err := DB.AutoMigrate(new(Level3), new(Level4), new(Level2), new(Level1)); err != nil {\n\t\tt.Error(err)\n\t}\n\n\tlvl := &Level3{}\n\tif err := DB.Save(lvl).Error; err != nil {\n\t\tt.Error(err)\n\t}\n\n\tsublvl1 := &Level4{Level3ID: lvl.ID}\n\tif err := DB.Save(sublvl1).Error; err != nil {\n\t\tt.Error(err)\n\t}\n\tsublvl2 := &Level4{Level3ID: lvl.ID}\n\tif err := DB.Save(sublvl2).Error; err != nil {\n\t\tt.Error(err)\n\t}\n\n\tlvl.Level4s = []*Level4{sublvl1, sublvl2}\n\n\twant1 := Level1{\n\t\tLevel2: &Level2{\n\t\t\tLevel3: lvl,\n\t\t},\n\t}\n\tif err := DB.Save(&want1).Error; err != nil {\n\t\tt.Error(err)\n\t}\n\n\twant2 := Level1{\n\t\tLevel2: &Level2{\n\t\t\tLevel3: lvl,\n\t\t},\n\t}\n\tif err := DB.Save(&want2).Error; err != nil {\n\t\tt.Error(err)\n\t}\n\n\twant := []Level1{want1, want2}\n\n\tvar got []Level1\n\terr := DB.Preload(\"Level2.Level3.Level4s\").Find(&got).Error\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\n\tfor _, level1 := range append(got, want...) {\n\t\tsort.Slice(level1.Level2.Level3.Level4s, func(i, j int) bool {\n\t\t\treturn level1.Level2.Level3.Level4s[i].ID > level1.Level2.Level3.Level4s[j].ID\n\t\t})\n\t}\n\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Errorf(\"got %s; want %s\", toJSONString(got), toJSONString(want))\n\t}\n}\n\nfunc TestPreloadManyToManyCallbacks(t *testing.T) {\n\ttype (\n\t\tLevel2 struct {\n\t\t\tID   uint\n\t\t\tName string\n\t\t}\n\t\tLevel1 struct {\n\t\t\tID      uint\n\t\t\tName    string\n\t\t\tLevel2s []Level2 `gorm:\"many2many:level1_level2s\"`\n\t\t}\n\t)\n\n\tDB.Migrator().DropTable(\"level1_level2s\", &Level2{}, &Level1{})\n\n\tif err := DB.AutoMigrate(new(Level1), new(Level2)); err != nil {\n\t\tt.Error(err)\n\t}\n\n\tlvl := Level1{\n\t\tName: \"l1\",\n\t\tLevel2s: []Level2{\n\t\t\t{Name: \"l2-1\"}, {Name: \"l2-2\"},\n\t\t},\n\t}\n\tDB.Save(&lvl)\n\n\tvar called int64\n\tDB.Callback().Query().After(\"gorm:query\").Register(\"TestPreloadManyToManyCallbacks\", func(_ *gorm.DB) {\n\t\tatomic.AddInt64(&called, 1)\n\t})\n\n\tDB.Preload(\"Level2s\").First(&Level1{}, \"id = ?\", lvl.ID)\n\n\tif called != 3 {\n\t\tt.Errorf(\"Wanted callback to be called 3 times but got %d\", called)\n\t}\n}\n"
  },
  {
    "path": "tests/preload_test.go",
    "content": "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.io/gorm\"\n\t\"gorm.io/gorm/clause\"\n\t. \"gorm.io/gorm/utils/tests\"\n)\n\nfunc TestPreloadWithAssociations(t *testing.T) {\n\tuser := *GetUser(\"preload_with_associations\", Config{\n\t\tAccount:   true,\n\t\tPets:      2,\n\t\tToys:      3,\n\t\tCompany:   true,\n\t\tManager:   true,\n\t\tTeam:      4,\n\t\tLanguages: 3,\n\t\tFriends:   1,\n\t})\n\n\tif err := DB.Create(&user).Error; err != nil {\n\t\tt.Fatalf(\"errors happened when create: %v\", err)\n\t}\n\n\tCheckUser(t, user, user)\n\n\tvar user2 User\n\tDB.Preload(clause.Associations).Find(&user2, \"id = ?\", user.ID)\n\tCheckUser(t, user2, user)\n\n\tuser3 := *GetUser(\"preload_with_associations_new\", Config{\n\t\tAccount:   true,\n\t\tPets:      2,\n\t\tToys:      3,\n\t\tCompany:   true,\n\t\tManager:   true,\n\t\tTeam:      4,\n\t\tLanguages: 3,\n\t\tFriends:   1,\n\t})\n\n\tDB.Preload(clause.Associations).Find(&user3, \"id = ?\", user.ID)\n\tCheckUser(t, user3, user)\n}\n\nfunc TestNestedPreload(t *testing.T) {\n\tuser := *GetUser(\"nested_preload\", Config{Pets: 2})\n\n\tfor idx, pet := range user.Pets {\n\t\tpet.Toy = Toy{Name: \"toy_nested_preload_\" + strconv.Itoa(idx+1)}\n\t}\n\n\tif err := DB.Create(&user).Error; err != nil {\n\t\tt.Fatalf(\"errors happened when create: %v\", err)\n\t}\n\n\tvar user2 User\n\tDB.Preload(\"Pets.Toy\").Find(&user2, \"id = ?\", user.ID)\n\tCheckUser(t, user2, user)\n\n\tvar user3 User\n\tDB.Preload(clause.Associations+\".\"+clause.Associations).Find(&user3, \"id = ?\", user.ID)\n\tCheckUser(t, user3, user)\n\n\tvar user4 *User\n\tDB.Preload(\"Pets.Toy\").Find(&user4, \"id = ?\", user.ID)\n\tCheckUser(t, *user4, user)\n}\n\nfunc TestNestedPreloadForSlice(t *testing.T) {\n\tusers := []User{\n\t\t*GetUser(\"slice_nested_preload_1\", Config{Pets: 2}),\n\t\t*GetUser(\"slice_nested_preload_2\", Config{Pets: 0}),\n\t\t*GetUser(\"slice_nested_preload_3\", Config{Pets: 3}),\n\t}\n\n\tfor _, user := range users {\n\t\tfor idx, pet := range user.Pets {\n\t\t\tpet.Toy = Toy{Name: user.Name + \"_toy_nested_preload_\" + strconv.Itoa(idx+1)}\n\t\t}\n\t}\n\n\tif err := DB.Create(&users).Error; err != nil {\n\t\tt.Fatalf(\"errors happened when create: %v\", err)\n\t}\n\n\tvar userIDs []uint\n\tfor _, user := range users {\n\t\tuserIDs = append(userIDs, user.ID)\n\t}\n\n\tvar users2 []User\n\tDB.Preload(\"Pets.Toy\").Find(&users2, \"id IN ?\", userIDs)\n\n\tfor idx, user := range users2 {\n\t\tCheckUser(t, user, users[idx])\n\t}\n}\n\nfunc TestPreloadWithConds(t *testing.T) {\n\tusers := []User{\n\t\t*GetUser(\"slice_nested_preload_1\", Config{Account: true}),\n\t\t*GetUser(\"slice_nested_preload_2\", Config{Account: false}),\n\t\t*GetUser(\"slice_nested_preload_3\", Config{Account: true}),\n\t}\n\n\tif err := DB.Create(&users).Error; err != nil {\n\t\tt.Fatalf(\"errors happened when create: %v\", err)\n\t}\n\n\tvar userIDs []uint\n\tfor _, user := range users {\n\t\tuserIDs = append(userIDs, user.ID)\n\t}\n\n\tvar users2 []User\n\tDB.Preload(\"Account\", clause.Eq{Column: \"number\", Value: users[0].Account.Number}).Find(&users2, \"id IN ?\", userIDs)\n\tsort.Slice(users2, func(i, j int) bool {\n\t\treturn users2[i].ID < users2[j].ID\n\t})\n\n\tfor idx, user := range users2[1:2] {\n\t\tif user.Account.Number != \"\" {\n\t\t\tt.Errorf(\"No account should found for user %v but got %v\", idx+2, user.Account.Number)\n\t\t}\n\t}\n\n\tCheckUser(t, users2[0], users[0])\n\n\tvar users3 []User\n\tif err := DB.Preload(\"Account\", func(tx *gorm.DB) *gorm.DB {\n\t\treturn tx.Table(\"accounts AS a\").Select(\"a.*\")\n\t}).Find(&users3, \"id IN ?\", userIDs).Error; err != nil {\n\t\tt.Errorf(\"failed to query, got error %v\", err)\n\t}\n\tsort.Slice(users3, func(i, j int) bool {\n\t\treturn users2[i].ID < users2[j].ID\n\t})\n\n\tfor i, u := range users3 {\n\t\tCheckUser(t, u, users[i])\n\t}\n\n\tvar user4 User\n\tDB.Delete(&users3[0].Account)\n\n\tif err := DB.Preload(clause.Associations).Take(&user4, \"id = ?\", users3[0].ID).Error; err != nil || user4.Account.ID != 0 {\n\t\tt.Errorf(\"failed to query, got error %v, account: %#v\", err, user4.Account)\n\t}\n\n\tif err := DB.Preload(clause.Associations, func(tx *gorm.DB) *gorm.DB {\n\t\treturn tx.Unscoped()\n\t}).Take(&user4, \"id = ?\", users3[0].ID).Error; err != nil || user4.Account.ID == 0 {\n\t\tt.Errorf(\"failed to query, got error %v, account: %#v\", err, user4.Account)\n\t}\n}\n\nfunc TestNestedPreloadWithConds(t *testing.T) {\n\tusers := []User{\n\t\t*GetUser(\"slice_nested_preload_1\", Config{Pets: 2}),\n\t\t*GetUser(\"slice_nested_preload_2\", Config{Pets: 0}),\n\t\t*GetUser(\"slice_nested_preload_3\", Config{Pets: 3}),\n\t}\n\n\tfor _, user := range users {\n\t\tfor idx, pet := range user.Pets {\n\t\t\tpet.Toy = Toy{Name: user.Name + \"_toy_nested_preload_\" + strconv.Itoa(idx+1)}\n\t\t}\n\t}\n\n\tif err := DB.Create(&users).Error; err != nil {\n\t\tt.Fatalf(\"errors happened when create: %v\", err)\n\t}\n\n\tvar userIDs []uint\n\tfor _, user := range users {\n\t\tuserIDs = append(userIDs, user.ID)\n\t}\n\n\tvar users2 []User\n\tDB.Preload(\"Pets.Toy\", \"name like ?\", `%preload_3`).Find(&users2, \"id IN ?\", userIDs)\n\n\tfor idx, user := range users2[0:2] {\n\t\tfor _, pet := range user.Pets {\n\t\t\tif pet.Toy.Name != \"\" {\n\t\t\t\tt.Errorf(\"No toy should for user %v's pet %v but got %v\", idx+1, pet.Name, pet.Toy.Name)\n\t\t\t}\n\t\t}\n\t}\n\n\tif len(users2[2].Pets) != 3 {\n\t\tt.Errorf(\"Invalid pet toys found for user 3 got %v\", len(users2[2].Pets))\n\t} else {\n\t\tsort.Slice(users2[2].Pets, func(i, j int) bool {\n\t\t\treturn users2[2].Pets[i].ID < users2[2].Pets[j].ID\n\t\t})\n\n\t\tfor _, pet := range users2[2].Pets[0:2] {\n\t\t\tif pet.Toy.Name != \"\" {\n\t\t\t\tt.Errorf(\"No toy should for user %v's pet %v but got %v\", 3, pet.Name, pet.Toy.Name)\n\t\t\t}\n\t\t}\n\n\t\tCheckPet(t, *users2[2].Pets[2], *users[2].Pets[2])\n\t}\n}\n\nfunc TestPreloadEmptyData(t *testing.T) {\n\tuser := *GetUser(\"user_without_associations\", Config{})\n\tDB.Create(&user)\n\n\tDB.Preload(\"Team\").Preload(\"Languages\").Preload(\"Friends\").First(&user, \"name = ?\", user.Name)\n\n\tif r, err := json.Marshal(&user); err != nil {\n\t\tt.Errorf(\"failed to marshal users, got error %v\", err)\n\t} else if !regexp.MustCompile(`\"Team\":\\[\\],\"Languages\":\\[\\],\"Friends\":\\[\\]`).MatchString(string(r)) {\n\t\tt.Errorf(\"json marshal is not empty slice, got %v\", string(r))\n\t}\n\n\tvar results []User\n\tDB.Preload(\"Team\").Preload(\"Languages\").Preload(\"Friends\").Find(&results, \"name = ?\", user.Name)\n\n\tif r, err := json.Marshal(&results); err != nil {\n\t\tt.Errorf(\"failed to marshal users, got error %v\", err)\n\t} else if !regexp.MustCompile(`\"Team\":\\[\\],\"Languages\":\\[\\],\"Friends\":\\[\\]`).MatchString(string(r)) {\n\t\tt.Errorf(\"json marshal is not empty slice, got %v\", string(r))\n\t}\n}\n\nfunc TestPreloadGoroutine(t *testing.T) {\n\tvar wg sync.WaitGroup\n\n\twg.Add(10)\n\tfor i := 0; i < 10; i++ {\n\t\tgo func() {\n\t\t\tdefer wg.Done()\n\t\t\tvar user2 []User\n\t\t\ttx := DB.Where(\"id = ?\", 1).Session(&gorm.Session{})\n\n\t\t\tif err := tx.Preload(\"Team\").Find(&user2).Error; err != nil {\n\t\t\t\tt.Error(err)\n\t\t\t}\n\t\t}()\n\t}\n\twg.Wait()\n}\n\nfunc TestPreloadWithDiffModel(t *testing.T) {\n\tuser := *GetUser(\"preload_with_diff_model\", Config{Account: true})\n\n\tif err := DB.Create(&user).Error; err != nil {\n\t\tt.Fatalf(\"errors happened when create: %v\", err)\n\t}\n\n\tvar result struct {\n\t\tSomething string\n\t\tUser\n\t}\n\n\tDB.Model(User{}).Preload(\"Account\", clause.Eq{Column: \"number\", Value: user.Account.Number}).Select(\n\t\t\"users.*, 'yo' as something\").First(&result, \"name = ?\", user.Name)\n\n\tCheckUser(t, user, result.User)\n}\n\nfunc TestNestedPreloadWithUnscoped(t *testing.T) {\n\tuser := *GetUser(\"nested_preload\", Config{Pets: 1})\n\tpet := user.Pets[0]\n\tpet.Toy = Toy{Name: \"toy_nested_preload_\" + strconv.Itoa(1)}\n\tpet.Toy = Toy{Name: \"toy_nested_preload_\" + strconv.Itoa(2)}\n\n\tif err := DB.Create(&user).Error; err != nil {\n\t\tt.Fatalf(\"errors happened when create: %v\", err)\n\t}\n\n\tvar user2 User\n\tDB.Preload(\"Pets.Toy\").Find(&user2, \"id = ?\", user.ID)\n\tCheckUser(t, user2, user)\n\n\tDB.Delete(&pet)\n\n\tvar user3 User\n\tDB.Preload(clause.Associations+\".\"+clause.Associations).Find(&user3, \"id = ?\", user.ID)\n\tif len(user3.Pets) != 0 {\n\t\tt.Fatalf(\"User.Pet[0] was deleted and should not exist.\")\n\t}\n\n\tvar user4 *User\n\tDB.Preload(\"Pets.Toy\").Find(&user4, \"id = ?\", user.ID)\n\tif len(user4.Pets) != 0 {\n\t\tt.Fatalf(\"User.Pet[0] was deleted and should not exist.\")\n\t}\n\n\tvar user5 User\n\tDB.Unscoped().Preload(clause.Associations+\".\"+clause.Associations).Find(&user5, \"id = ?\", user.ID)\n\tCheckUserUnscoped(t, user5, user)\n\n\tvar user6 *User\n\tDB.Unscoped().Preload(\"Pets.Toy\").Find(&user6, \"id = ?\", user.ID)\n\tCheckUserUnscoped(t, *user6, user)\n}\n\nfunc TestNestedPreloadWithNestedJoin(t *testing.T) {\n\ttype (\n\t\tPreload struct {\n\t\t\tID       uint\n\t\t\tValue    string\n\t\t\tNestedID uint\n\t\t}\n\t\tJoin struct {\n\t\t\tID       uint\n\t\t\tValue    string\n\t\t\tNestedID uint\n\t\t}\n\t\tNested struct {\n\t\t\tID       uint\n\t\t\tPreloads []*Preload\n\t\t\tJoin     Join\n\t\t\tValueID  uint\n\t\t}\n\t\tValue struct {\n\t\t\tID     uint\n\t\t\tName   string\n\t\t\tNested Nested\n\t\t}\n\t)\n\n\tDB.Migrator().DropTable(&Preload{}, &Join{}, &Nested{}, &Value{})\n\tDB.Migrator().AutoMigrate(&Preload{}, &Join{}, &Nested{}, &Value{})\n\n\tvalue1 := Value{\n\t\tName: \"value\",\n\t\tNested: Nested{\n\t\t\tPreloads: []*Preload{\n\t\t\t\t{Value: \"p1\"}, {Value: \"p2\"},\n\t\t\t},\n\t\t\tJoin: Join{Value: \"j1\"},\n\t\t},\n\t}\n\tvalue2 := Value{\n\t\tName: \"value2\",\n\t\tNested: Nested{\n\t\t\tPreloads: []*Preload{\n\t\t\t\t{Value: \"p3\"}, {Value: \"p4\"}, {Value: \"p5\"},\n\t\t\t},\n\t\t\tJoin: Join{Value: \"j2\"},\n\t\t},\n\t}\n\n\tvalues := []*Value{&value1, &value2}\n\tif err := DB.Create(&values).Error; err != nil {\n\t\tt.Errorf(\"failed to create value, got err: %v\", err)\n\t}\n\n\tvar find1 Value\n\terr := DB.Joins(\"Nested\").Joins(\"Nested.Join\").Preload(\"Nested.Preloads\").First(&find1, value1.ID).Error\n\tif err != nil {\n\t\tt.Errorf(\"failed to find value, got err: %v\", err)\n\t}\n\tAssertEqual(t, find1, value1)\n\n\tvar find2 Value\n\t// Joins will automatically add Nested queries.\n\terr = DB.Joins(\"Nested.Join\").Preload(\"Nested.Preloads\").First(&find2, value2.ID).Error\n\tif err != nil {\n\t\tt.Errorf(\"failed to find value, got err: %v\", err)\n\t}\n\tAssertEqual(t, find2, value2)\n\n\tvar finds []Value\n\terr = DB.Joins(\"Nested.Join\").Joins(\"Nested\").Preload(\"Nested.Preloads\").Find(&finds).Error\n\tif err != nil {\n\t\tt.Errorf(\"failed to find value, got err: %v\", err)\n\t}\n\tAssertEqual(t, len(finds), 2)\n\tAssertEqual(t, finds[0], value1)\n\tAssertEqual(t, finds[1], value2)\n}\n\nfunc TestMergeNestedPreloadWithNestedJoin(t *testing.T) {\n\tusers := []User{\n\t\t{\n\t\t\tName: \"TestMergeNestedPreloadWithNestedJoin-1\",\n\t\t\tManager: &User{\n\t\t\t\tName: \"Alexis Manager\",\n\t\t\t\tTools: []Tools{\n\t\t\t\t\t{Name: \"Alexis Tool 1\"},\n\t\t\t\t\t{Name: \"Alexis Tool 2\"},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tName: \"TestMergeNestedPreloadWithNestedJoin-2\",\n\t\t\tManager: &User{\n\t\t\t\tName: \"Jinzhu Manager\",\n\t\t\t\tTools: []Tools{\n\t\t\t\t\t{Name: \"Jinzhu Tool 1\"},\n\t\t\t\t\t{Name: \"Jinzhu Tool 2\"},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\n\tDB.Create(&users)\n\n\tquery := make([]string, 0)\n\tsess := DB.Session(&gorm.Session{Logger: Tracer{\n\t\tLogger: DB.Config.Logger,\n\t\tTest: func(ctx context.Context, begin time.Time, fc func() (sql string, rowsAffected int64), err error) {\n\t\t\tsql, _ := fc()\n\t\t\tquery = append(query, sql)\n\t\t},\n\t}})\n\n\tvar result []User\n\terr := sess.\n\t\tJoins(\"Manager\").\n\t\tPreload(\"Manager.Tools\").\n\t\tWhere(\"users.name Like ?\", \"TestMergeNestedPreloadWithNestedJoin%\").\n\t\tFind(&result).Error\n\n\tif err != nil {\n\t\tt.Fatalf(\"failed to preload and find users: %v\", err)\n\t}\n\n\tAssertEqual(t, result, users)\n\tAssertEqual(t, len(query), 2) // Check preload queries are merged\n\n\tif !regexp.MustCompile(`SELECT \\* FROM .*tools.* WHERE .*IN.*`).MatchString(query[0]) {\n\t\tt.Fatalf(\"Expected first query to preload manager tools, got: %s\", query[0])\n\t}\n}\n\nfunc TestNestedPreloadWithPointerJoin(t *testing.T) {\n\ttype (\n\t\tPreload struct {\n\t\t\tID     uint\n\t\t\tValue  string\n\t\t\tJoinID uint\n\t\t}\n\t\tJoin struct {\n\t\t\tID       uint\n\t\t\tValue    string\n\t\t\tPreload  Preload\n\t\t\tNestedID uint\n\t\t}\n\t\tNested struct {\n\t\t\tID      uint\n\t\t\tJoin    Join\n\t\t\tValueID uint\n\t\t}\n\t\tValue struct {\n\t\t\tID     uint\n\t\t\tName   string\n\t\t\tNested *Nested\n\t\t}\n\t)\n\n\tDB.Migrator().DropTable(&Preload{}, &Join{}, &Nested{}, &Value{})\n\tDB.Migrator().AutoMigrate(&Preload{}, &Join{}, &Nested{}, &Value{})\n\n\tvalue := Value{\n\t\tName: \"value\",\n\t\tNested: &Nested{\n\t\t\tJoin: Join{\n\t\t\t\tValue: \"j1\",\n\t\t\t\tPreload: Preload{\n\t\t\t\t\tValue: \"p1\",\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\n\tif err := DB.Create(&value).Error; err != nil {\n\t\tt.Errorf(\"failed to create value, got err: %v\", err)\n\t}\n\n\tvar find1 Value\n\terr := DB.Table(\"values\").Joins(\"Nested\").Joins(\"Nested.Join\").Preload(\"Nested.Join.Preload\").First(&find1).Error\n\tif err != nil {\n\t\tt.Errorf(\"failed to find value, got err: %v\", err)\n\t}\n\tAssertEqual(t, find1, value)\n}\n\nfunc TestEmbedPreload(t *testing.T) {\n\ttype Country struct {\n\t\tID   int `gorm:\"primaryKey\"`\n\t\tName string\n\t}\n\ttype EmbeddedAddress struct {\n\t\tID        int\n\t\tName      string\n\t\tCountryID *int\n\t\tCountry   *Country\n\t}\n\ttype NestedAddress struct {\n\t\tEmbeddedAddress\n\t}\n\ttype Org struct {\n\t\tID              int\n\t\tPostalAddress   EmbeddedAddress `gorm:\"embedded;embeddedPrefix:postal_address_\"`\n\t\tVisitingAddress EmbeddedAddress `gorm:\"embedded;embeddedPrefix:visiting_address_\"`\n\t\tAddressID       *int\n\t\tAddress         *EmbeddedAddress\n\t\tNestedAddress   NestedAddress `gorm:\"embedded;embeddedPrefix:nested_address_\"`\n\t}\n\n\tDB.Migrator().DropTable(&Org{}, &EmbeddedAddress{}, &Country{})\n\tDB.AutoMigrate(&Org{}, &EmbeddedAddress{}, &Country{})\n\n\torg := Org{\n\t\tPostalAddress:   EmbeddedAddress{Name: \"a1\", Country: &Country{Name: \"c1\"}},\n\t\tVisitingAddress: EmbeddedAddress{Name: \"a2\", Country: &Country{Name: \"c2\"}},\n\t\tAddress:         &EmbeddedAddress{Name: \"a3\", Country: &Country{Name: \"c3\"}},\n\t\tNestedAddress: NestedAddress{\n\t\t\tEmbeddedAddress: EmbeddedAddress{Name: \"a4\", Country: &Country{Name: \"c4\"}},\n\t\t},\n\t}\n\tif err := DB.Create(&org).Error; err != nil {\n\t\tt.Errorf(\"failed to create org, got err: %v\", err)\n\t}\n\n\ttests := []struct {\n\t\tname     string\n\t\tpreloads map[string][]interface{}\n\t\texpect   Org\n\t}{\n\t\t{\n\t\t\tname:     \"address country\",\n\t\t\tpreloads: map[string][]interface{}{\"Address.Country\": {}},\n\t\t\texpect: Org{\n\t\t\t\tID: org.ID,\n\t\t\t\tPostalAddress: EmbeddedAddress{\n\t\t\t\t\tID:        org.PostalAddress.ID,\n\t\t\t\t\tName:      org.PostalAddress.Name,\n\t\t\t\t\tCountryID: org.PostalAddress.CountryID,\n\t\t\t\t\tCountry:   nil,\n\t\t\t\t},\n\t\t\t\tVisitingAddress: EmbeddedAddress{\n\t\t\t\t\tID:        org.VisitingAddress.ID,\n\t\t\t\t\tName:      org.VisitingAddress.Name,\n\t\t\t\t\tCountryID: org.VisitingAddress.CountryID,\n\t\t\t\t\tCountry:   nil,\n\t\t\t\t},\n\t\t\t\tAddressID: org.AddressID,\n\t\t\t\tAddress:   org.Address,\n\t\t\t\tNestedAddress: NestedAddress{EmbeddedAddress{\n\t\t\t\t\tID:        org.NestedAddress.ID,\n\t\t\t\t\tName:      org.NestedAddress.Name,\n\t\t\t\t\tCountryID: org.NestedAddress.CountryID,\n\t\t\t\t\tCountry:   nil,\n\t\t\t\t}},\n\t\t\t},\n\t\t}, {\n\t\t\tname:     \"postal address country\",\n\t\t\tpreloads: map[string][]interface{}{\"PostalAddress.Country\": {}},\n\t\t\texpect: Org{\n\t\t\t\tID:            org.ID,\n\t\t\t\tPostalAddress: org.PostalAddress,\n\t\t\t\tVisitingAddress: EmbeddedAddress{\n\t\t\t\t\tID:        org.VisitingAddress.ID,\n\t\t\t\t\tName:      org.VisitingAddress.Name,\n\t\t\t\t\tCountryID: org.VisitingAddress.CountryID,\n\t\t\t\t\tCountry:   nil,\n\t\t\t\t},\n\t\t\t\tAddressID: org.AddressID,\n\t\t\t\tAddress:   nil,\n\t\t\t\tNestedAddress: NestedAddress{EmbeddedAddress{\n\t\t\t\t\tID:        org.NestedAddress.ID,\n\t\t\t\t\tName:      org.NestedAddress.Name,\n\t\t\t\t\tCountryID: org.NestedAddress.CountryID,\n\t\t\t\t\tCountry:   nil,\n\t\t\t\t}},\n\t\t\t},\n\t\t}, {\n\t\t\tname:     \"nested address country\",\n\t\t\tpreloads: map[string][]interface{}{\"NestedAddress.Country\": {}},\n\t\t\texpect: Org{\n\t\t\t\tID: org.ID,\n\t\t\t\tPostalAddress: EmbeddedAddress{\n\t\t\t\t\tID:        org.PostalAddress.ID,\n\t\t\t\t\tName:      org.PostalAddress.Name,\n\t\t\t\t\tCountryID: org.PostalAddress.CountryID,\n\t\t\t\t\tCountry:   nil,\n\t\t\t\t},\n\t\t\t\tVisitingAddress: EmbeddedAddress{\n\t\t\t\t\tID:        org.VisitingAddress.ID,\n\t\t\t\t\tName:      org.VisitingAddress.Name,\n\t\t\t\t\tCountryID: org.VisitingAddress.CountryID,\n\t\t\t\t\tCountry:   nil,\n\t\t\t\t},\n\t\t\t\tAddressID:     org.AddressID,\n\t\t\t\tAddress:       nil,\n\t\t\t\tNestedAddress: org.NestedAddress,\n\t\t\t},\n\t\t}, {\n\t\t\tname: \"associations\",\n\t\t\tpreloads: map[string][]interface{}{\n\t\t\t\tclause.Associations: {},\n\t\t\t\t// clause.Associations won’t preload nested associations\n\t\t\t\t\"Address.Country\": {},\n\t\t\t},\n\t\t\texpect: org,\n\t\t},\n\t}\n\n\tfor _, test := range tests {\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tactual := Org{}\n\t\t\ttx := DB.Where(\"id = ?\", org.ID).Session(&gorm.Session{})\n\t\t\tfor name, args := range test.preloads {\n\t\t\t\ttx = tx.Preload(name, args...)\n\t\t\t}\n\t\t\tif err := tx.Find(&actual).Error; err != nil {\n\t\t\t\tt.Errorf(\"failed to find org, got err: %v\", err)\n\t\t\t}\n\t\t\tAssertEqual(t, actual, test.expect)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "tests/prepared_stmt_test.go",
    "content": "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/tests\"\n)\n\nfunc TestPreparedStmt(t *testing.T) {\n\ttx := DB.Session(&gorm.Session{PrepareStmt: true})\n\n\tif _, ok := tx.ConnPool.(*gorm.PreparedStmtDB); !ok {\n\t\tt.Fatalf(\"should assign PreparedStatement Manager back to database when using PrepareStmt mode\")\n\t}\n\n\tctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)\n\tdefer cancel()\n\ttxCtx := tx.WithContext(ctx)\n\n\tuser := *GetUser(\"prepared_stmt\", Config{})\n\n\ttxCtx.Create(&user)\n\n\tvar result1 User\n\tif err := txCtx.Find(&result1, user.ID).Error; err != nil {\n\t\tt.Fatalf(\"no error should happen but got %v\", err)\n\t}\n\n\ttime.Sleep(time.Second)\n\n\tvar result2 User\n\tif err := tx.Find(&result2, user.ID).Error; err != nil {\n\t\tt.Fatalf(\"no error should happen but got %v\", err)\n\t}\n\n\tuser2 := *GetUser(\"prepared_stmt2\", Config{})\n\tif err := txCtx.Create(&user2).Error; err == nil {\n\t\tt.Fatalf(\"should failed to create with timeout context\")\n\t}\n\n\tif err := tx.Create(&user2).Error; err != nil {\n\t\tt.Fatalf(\"failed to create, got error %v\", err)\n\t}\n\n\tvar result3 User\n\tif err := tx.Find(&result3, user2.ID).Error; err != nil {\n\t\tt.Fatalf(\"no error should happen but got %v\", err)\n\t}\n}\n\nfunc TestPreparedStmtFromTransaction(t *testing.T) {\n\tdb := DB.Session(&gorm.Session{PrepareStmt: true, SkipDefaultTransaction: true})\n\n\ttx := db.Begin()\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\ttx.Rollback()\n\t\t}\n\t}()\n\tif err := tx.Error; err != nil {\n\t\tt.Errorf(\"Failed to start transaction, got error %v\\n\", err)\n\t}\n\n\tif err := tx.Where(\"name=?\", \"zzjin\").Delete(&User{}).Error; err != nil {\n\t\ttx.Rollback()\n\t\tt.Errorf(\"Failed to run one transaction, got error %v\\n\", err)\n\t}\n\n\tif err := tx.Create(&User{Name: \"zzjin\"}).Error; err != nil {\n\t\ttx.Rollback()\n\t\tt.Errorf(\"Failed to run one transaction, got error %v\\n\", err)\n\t}\n\n\tif err := tx.Commit().Error; err != nil {\n\t\tt.Errorf(\"Failed to commit transaction, got error %v\\n\", err)\n\t}\n\n\tif result := db.Where(\"name=?\", \"zzjin\").Delete(&User{}); result.Error != nil || result.RowsAffected != 1 {\n\t\tt.Fatalf(\"Failed, got error: %v, rows affected: %v\", result.Error, result.RowsAffected)\n\t}\n\n\ttx2 := db.Begin()\n\tif result := tx2.Where(\"name=?\", \"zzjin\").Delete(&User{}); result.Error != nil || result.RowsAffected != 0 {\n\t\tt.Fatalf(\"Failed, got error: %v, rows affected: %v\", result.Error, result.RowsAffected)\n\t}\n\ttx2.Commit()\n}\n\nfunc TestPreparedStmtLruFromTransaction(t *testing.T) {\n\tdb, _ := OpenTestConnection(&gorm.Config{PrepareStmt: true, PrepareStmtMaxSize: 10, PrepareStmtTTL: 20 * time.Second})\n\n\ttx := db.Begin()\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\ttx.Rollback()\n\t\t}\n\t}()\n\tif err := tx.Error; err != nil {\n\t\tt.Errorf(\"Failed to start transaction, got error %v\\n\", err)\n\t}\n\n\tif err := tx.Where(\"name=?\", \"zzjin\").Delete(&User{}).Error; err != nil {\n\t\ttx.Rollback()\n\t\tt.Errorf(\"Failed to run one transaction, got error %v\\n\", err)\n\t}\n\n\tif err := tx.Create(&User{Name: \"zzjin\"}).Error; err != nil {\n\t\ttx.Rollback()\n\t\tt.Errorf(\"Failed to run one transaction, got error %v\\n\", err)\n\t}\n\n\tif err := tx.Commit().Error; err != nil {\n\t\tt.Errorf(\"Failed to commit transaction, got error %v\\n\", err)\n\t}\n\n\tif result := db.Where(\"name=?\", \"zzjin\").Delete(&User{}); result.Error != nil || result.RowsAffected != 1 {\n\t\tt.Fatalf(\"Failed, got error: %v, rows affected: %v\", result.Error, result.RowsAffected)\n\t}\n\n\ttx2 := db.Begin()\n\tif result := tx2.Where(\"name=?\", \"zzjin\").Delete(&User{}); result.Error != nil || result.RowsAffected != 0 {\n\t\tt.Fatalf(\"Failed, got error: %v, rows affected: %v\", result.Error, result.RowsAffected)\n\t}\n\n\ttx2.Commit()\n\t// Attempt to convert the connection pool of tx to the *gorm.PreparedStmtDB type.\n\t// If the conversion is successful, ok will be true and conn will be the converted object;\n\t// otherwise, ok will be false and conn will be nil.\n\tconn, ok := tx.ConnPool.(*gorm.PreparedStmtDB)\n\t// Get the number of statement keys stored in the PreparedStmtDB.\n\tlens := len(conn.Stmts.Keys())\n\t// Check if the number of stored statement keys is 0.\n\tif lens == 0 {\n\t\t// If the number is 0, it means there are no statements stored in the LRU cache.\n\t\t// The test fails and an error message is output.\n\t\tt.Fatalf(\"lru should not be empty\")\n\t}\n\t// Wait for 40 seconds to give the statements in the cache enough time to expire.\n\ttime.Sleep(time.Second * 40)\n\t// Assert whether the connection pool of tx is successfully converted to the *gorm.PreparedStmtDB type.\n\tAssertEqual(t, ok, true)\n\t// Assert whether the number of statement keys stored in the PreparedStmtDB is 0 after 40 seconds.\n\t// If it is not 0, it means the statements in the cache have not expired as expected.\n\tAssertEqual(t, len(conn.Stmts.Keys()), 0)\n\n}\n\nfunc TestPreparedStmtDeadlock(t *testing.T) {\n\ttx, err := OpenTestConnection(&gorm.Config{})\n\tAssertEqual(t, err, nil)\n\n\tsqlDB, _ := tx.DB()\n\tsqlDB.SetMaxOpenConns(1)\n\n\ttx = tx.Session(&gorm.Session{PrepareStmt: true})\n\n\twg := sync.WaitGroup{}\n\tfor i := 0; i < 100; i++ {\n\t\twg.Add(1)\n\t\tgo func() {\n\t\t\tuser := User{Name: \"jinzhu\"}\n\t\t\ttx.Create(&user)\n\n\t\t\tvar result User\n\t\t\ttx.First(&result)\n\t\t\twg.Done()\n\t\t}()\n\t}\n\twg.Wait()\n\n\tconn, ok := tx.ConnPool.(*gorm.PreparedStmtDB)\n\tAssertEqual(t, ok, true)\n\tAssertEqual(t, len(conn.Stmts.Keys()), 2)\n\tfor _, stmt := range conn.Stmts.Keys() {\n\t\tif stmt == \"\" {\n\t\t\tt.Fatalf(\"stmt cannot bee nil\")\n\t\t}\n\t}\n\n\tAssertEqual(t, sqlDB.Stats().InUse, 0)\n}\n\nfunc TestPreparedStmtInTransaction(t *testing.T) {\n\tuser := User{Name: \"jinzhu\"}\n\n\tif err := DB.Transaction(func(tx *gorm.DB) error {\n\t\ttx.Session(&gorm.Session{PrepareStmt: true}).Create(&user)\n\t\treturn errors.New(\"test\")\n\t}); err == nil {\n\t\tt.Error(err)\n\t}\n\n\tvar result User\n\tif err := DB.First(&result, user.ID).Error; err == nil {\n\t\tt.Errorf(\"Failed, got error: %v\", err)\n\t}\n}\n\nfunc TestPreparedStmtClose(t *testing.T) {\n\ttx := DB.Session(&gorm.Session{PrepareStmt: true})\n\n\tuser := *GetUser(\"prepared_stmt_close\", Config{})\n\ttx = tx.Create(&user)\n\n\tpdb, ok := tx.ConnPool.(*gorm.PreparedStmtDB)\n\tif !ok {\n\t\tt.Fatalf(\"should assign PreparedStatement Manager back to database when using PrepareStmt mode\")\n\t}\n\n\tpdb.Mux.Lock()\n\tif len(pdb.Stmts.Keys()) == 0 {\n\t\tpdb.Mux.Unlock()\n\t\tt.Fatalf(\"prepared stmt can not be empty\")\n\t}\n\tpdb.Mux.Unlock()\n\n\tpdb.Close()\n\tpdb.Mux.Lock()\n\tdefer pdb.Mux.Unlock()\n\tif len(pdb.Stmts.Keys()) != 0 {\n\t\tt.Fatalf(\"prepared stmt should be empty\")\n\t}\n}\n\nfunc isUsingClosedConnError(err error) bool {\n\t// https://github.com/golang/go/blob/e705a2d16e4ece77e08e80c168382cdb02890f5b/src/database/sql/sql.go#L2717\n\treturn err.Error() == \"sql: statement is closed\"\n}\n\n// TestPreparedStmtConcurrentClose test calling close and executing SQL concurrently\n// this test making sure that the gorm would not get a Segmentation Fault, and the only error cause by this is using a closed Stmt\nfunc TestPreparedStmtConcurrentClose(t *testing.T) {\n\tname := \"prepared_stmt_concurrent_close\"\n\tuser := *GetUser(name, Config{})\n\tcreateTx := DB.Session(&gorm.Session{}).Create(&user)\n\tif createTx.Error != nil {\n\t\tt.Fatalf(\"failed to prepare record due to %s, test cannot be continue\", createTx.Error)\n\t}\n\n\t// create a new connection to keep away from other tests\n\ttx, err := OpenTestConnection(&gorm.Config{PrepareStmt: true})\n\tif err != nil {\n\t\tt.Fatalf(\"failed to open test connection due to %s\", err)\n\t}\n\tpdb, ok := tx.ConnPool.(*gorm.PreparedStmtDB)\n\tif !ok {\n\t\tt.Fatalf(\"should assign PreparedStatement Manager back to database when using PrepareStmt mode\")\n\t}\n\n\tloopCount := 100\n\tvar wg sync.WaitGroup\n\tvar unexpectedError bool\n\twriterFinish := make(chan struct{})\n\n\twg.Add(1)\n\tgo func(id uint) {\n\t\tdefer wg.Done()\n\t\tdefer close(writerFinish)\n\n\t\tfor j := 0; j < loopCount; j++ {\n\t\t\tvar tmp User\n\t\t\terr := tx.Session(&gorm.Session{}).First(&tmp, id).Error\n\t\t\tif err == nil || isUsingClosedConnError(err) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tt.Errorf(\"failed to read user of id %d due to %s, there should not be error\", id, err)\n\t\t\tunexpectedError = true\n\t\t\tbreak\n\t\t}\n\t}(user.ID)\n\n\twg.Add(1)\n\tgo func() {\n\t\tdefer wg.Done()\n\t\t<-writerFinish\n\t\tpdb.Close()\n\t}()\n\n\twg.Wait()\n\n\tif unexpectedError {\n\t\tt.Fatalf(\"should is a unexpected error\")\n\t}\n}\n"
  },
  {
    "path": "tests/query_test.go",
    "content": "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\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"gorm.io/gorm\"\n\t\"gorm.io/gorm/clause\"\n\t. \"gorm.io/gorm/utils/tests\"\n)\n\nfunc TestFind(t *testing.T) {\n\tusers := []User{\n\t\t*GetUser(\"find\", Config{}),\n\t\t*GetUser(\"find\", Config{}),\n\t\t*GetUser(\"find\", Config{}),\n\t}\n\n\tif err := DB.Create(&users).Error; err != nil {\n\t\tt.Fatalf(\"errors happened when create users: %v\", err)\n\t}\n\n\tt.Run(\"First\", func(t *testing.T) {\n\t\tvar first User\n\t\tif err := DB.Where(\"name = ?\", \"find\").First(&first).Error; err != nil {\n\t\t\tt.Errorf(\"errors happened when query first: %v\", err)\n\t\t} else {\n\t\t\tCheckUser(t, first, users[0])\n\t\t}\n\t})\n\n\tt.Run(\"Last\", func(t *testing.T) {\n\t\tvar last User\n\t\tif err := DB.Where(\"name = ?\", \"find\").Last(&last).Error; err != nil {\n\t\t\tt.Errorf(\"errors happened when query last: %v\", err)\n\t\t} else {\n\t\t\tCheckUser(t, last, users[2])\n\t\t}\n\t})\n\n\tvar all []User\n\tif err := DB.Where(\"name = ?\", \"find\").Find(&all).Error; err != nil || len(all) != 3 {\n\t\tt.Errorf(\"errors happened when query find: %v, length: %v\", err, len(all))\n\t} else {\n\t\tfor idx, user := range users {\n\t\t\tt.Run(\"FindAll#\"+strconv.Itoa(idx+1), func(t *testing.T) {\n\t\t\t\tCheckUser(t, all[idx], user)\n\t\t\t})\n\t\t}\n\t}\n\n\tt.Run(\"FirstMap\", func(t *testing.T) {\n\t\tfirst := map[string]interface{}{}\n\t\tif err := DB.Model(&User{}).Where(\"name = ?\", \"find\").First(first).Error; err != nil {\n\t\t\tt.Errorf(\"errors happened when query first: %v\", err)\n\t\t} else {\n\t\t\tfor _, name := range []string{\"Name\", \"Age\", \"Birthday\"} {\n\t\t\t\tt.Run(name, func(t *testing.T) {\n\t\t\t\t\tdbName := DB.NamingStrategy.ColumnName(\"\", name)\n\n\t\t\t\t\tswitch name {\n\t\t\t\t\tcase \"Name\":\n\t\t\t\t\t\tif _, ok := first[dbName].(string); !ok {\n\t\t\t\t\t\t\tt.Errorf(\"invalid data type for %v, got %#v\", dbName, first[dbName])\n\t\t\t\t\t\t}\n\t\t\t\t\tcase \"Age\":\n\t\t\t\t\t\tif _, ok := first[dbName].(uint); !ok {\n\t\t\t\t\t\t\tt.Errorf(\"invalid data type for %v, got %#v\", dbName, first[dbName])\n\t\t\t\t\t\t}\n\t\t\t\t\tcase \"Birthday\":\n\t\t\t\t\t\tif _, ok := first[dbName].(*time.Time); !ok {\n\t\t\t\t\t\t\tt.Errorf(\"invalid data type for %v, got %#v\", dbName, first[dbName])\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\treflectValue := reflect.Indirect(reflect.ValueOf(users[0]))\n\t\t\t\t\tAssertEqual(t, first[dbName], reflectValue.FieldByName(name).Interface())\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t})\n\n\tt.Run(\"FirstMapWithTable\", func(t *testing.T) {\n\t\tfirst := map[string]interface{}{}\n\t\tif err := DB.Table(\"users\").Where(\"name = ?\", \"find\").Find(first).Error; err != nil {\n\t\t\tt.Errorf(\"errors happened when query first: %v\", err)\n\t\t} else {\n\t\t\tfor _, name := range []string{\"Name\", \"Age\", \"Birthday\"} {\n\t\t\t\tt.Run(name, func(t *testing.T) {\n\t\t\t\t\tdbName := DB.NamingStrategy.ColumnName(\"\", name)\n\t\t\t\t\tresultType := reflect.ValueOf(first[dbName]).Type().Name()\n\n\t\t\t\t\tswitch name {\n\t\t\t\t\tcase \"Name\":\n\t\t\t\t\t\tif !strings.Contains(resultType, \"string\") {\n\t\t\t\t\t\t\tt.Errorf(\"invalid data type for %v, got %v %#v\", dbName, resultType, first[dbName])\n\t\t\t\t\t\t}\n\t\t\t\t\tcase \"Age\":\n\t\t\t\t\t\tif !strings.Contains(resultType, \"int\") {\n\t\t\t\t\t\t\tt.Errorf(\"invalid data type for %v, got %v %#v\", dbName, resultType, first[dbName])\n\t\t\t\t\t\t}\n\t\t\t\t\tcase \"Birthday\":\n\t\t\t\t\t\tif !strings.Contains(resultType, \"Time\") && !(DB.Dialector.Name() == \"sqlite\" && strings.Contains(resultType, \"string\")) {\n\t\t\t\t\t\t\tt.Errorf(\"invalid data type for %v, got %v %#v\", dbName, resultType, first[dbName])\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\treflectValue := reflect.Indirect(reflect.ValueOf(users[0]))\n\t\t\t\t\tAssertEqual(t, first[dbName], reflectValue.FieldByName(name).Interface())\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t})\n\n\tt.Run(\"FirstPtrMap\", func(t *testing.T) {\n\t\tfirst := map[string]interface{}{}\n\t\tif err := DB.Model(&User{}).Where(\"name = ?\", \"find\").First(&first).Error; err != nil {\n\t\t\tt.Errorf(\"errors happened when query first: %v\", err)\n\t\t} else {\n\t\t\tfor _, name := range []string{\"Name\", \"Age\", \"Birthday\"} {\n\t\t\t\tt.Run(name, func(t *testing.T) {\n\t\t\t\t\tdbName := DB.NamingStrategy.ColumnName(\"\", name)\n\t\t\t\t\treflectValue := reflect.Indirect(reflect.ValueOf(users[0]))\n\t\t\t\t\tAssertEqual(t, first[dbName], reflectValue.FieldByName(name).Interface())\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t})\n\n\tt.Run(\"FirstSliceOfMap\", func(t *testing.T) {\n\t\tallMap := []map[string]interface{}{}\n\t\tif err := DB.Model(&User{}).Where(\"name = ?\", \"find\").Find(&allMap).Error; err != nil {\n\t\t\tt.Errorf(\"errors happened when query find: %v\", err)\n\t\t} else {\n\t\t\tfor idx, user := range users {\n\t\t\t\tt.Run(\"FindAllMap#\"+strconv.Itoa(idx+1), func(t *testing.T) {\n\t\t\t\t\tfor _, name := range []string{\"Name\", \"Age\", \"Birthday\"} {\n\t\t\t\t\t\tt.Run(name, func(t *testing.T) {\n\t\t\t\t\t\t\tdbName := DB.NamingStrategy.ColumnName(\"\", name)\n\n\t\t\t\t\t\t\tswitch name {\n\t\t\t\t\t\t\tcase \"Name\":\n\t\t\t\t\t\t\t\tif _, ok := allMap[idx][dbName].(string); !ok {\n\t\t\t\t\t\t\t\t\tt.Errorf(\"invalid data type for %v, got %#v\", dbName, allMap[idx][dbName])\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcase \"Age\":\n\t\t\t\t\t\t\t\tif _, ok := allMap[idx][dbName].(uint); !ok {\n\t\t\t\t\t\t\t\t\tt.Errorf(\"invalid data type for %v, got %#v\", dbName, allMap[idx][dbName])\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcase \"Birthday\":\n\t\t\t\t\t\t\t\tif _, ok := allMap[idx][dbName].(*time.Time); !ok {\n\t\t\t\t\t\t\t\t\tt.Errorf(\"invalid data type for %v, got %#v\", dbName, allMap[idx][dbName])\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treflectValue := reflect.Indirect(reflect.ValueOf(user))\n\t\t\t\t\t\t\tAssertEqual(t, allMap[idx][dbName], reflectValue.FieldByName(name).Interface())\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t})\n\n\tt.Run(\"FindSliceOfMapWithTable\", func(t *testing.T) {\n\t\tallMap := []map[string]interface{}{}\n\t\tif err := DB.Table(\"users\").Where(\"name = ?\", \"find\").Find(&allMap).Error; err != nil {\n\t\t\tt.Errorf(\"errors happened when query find: %v\", err)\n\t\t} else {\n\t\t\tfor idx, user := range users {\n\t\t\t\tt.Run(\"FindAllMap#\"+strconv.Itoa(idx+1), func(t *testing.T) {\n\t\t\t\t\tfor _, name := range []string{\"Name\", \"Age\", \"Birthday\"} {\n\t\t\t\t\t\tt.Run(name, func(t *testing.T) {\n\t\t\t\t\t\t\tdbName := DB.NamingStrategy.ColumnName(\"\", name)\n\t\t\t\t\t\t\tresultType := reflect.ValueOf(allMap[idx][dbName]).Type().Name()\n\n\t\t\t\t\t\t\tswitch name {\n\t\t\t\t\t\t\tcase \"Name\":\n\t\t\t\t\t\t\t\tif !strings.Contains(resultType, \"string\") {\n\t\t\t\t\t\t\t\t\tt.Errorf(\"invalid data type for %v, got %v %#v\", dbName, resultType, allMap[idx][dbName])\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcase \"Age\":\n\t\t\t\t\t\t\t\tif !strings.Contains(resultType, \"int\") {\n\t\t\t\t\t\t\t\t\tt.Errorf(\"invalid data type for %v, got %v %#v\", dbName, resultType, allMap[idx][dbName])\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcase \"Birthday\":\n\t\t\t\t\t\t\t\tif !strings.Contains(resultType, \"Time\") && !(DB.Dialector.Name() == \"sqlite\" && strings.Contains(resultType, \"string\")) {\n\t\t\t\t\t\t\t\t\tt.Errorf(\"invalid data type for %v, got %v %#v\", dbName, resultType, allMap[idx][dbName])\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treflectValue := reflect.Indirect(reflect.ValueOf(user))\n\t\t\t\t\t\t\tAssertEqual(t, allMap[idx][dbName], reflectValue.FieldByName(name).Interface())\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t})\n\n\tvar models []User\n\tif err := DB.Where(\"name in (?)\", []string{\"find\"}).Find(&models).Error; err != nil || len(models) != 3 {\n\t\tt.Errorf(\"errors happened when query find with in clause: %v, length: %v\", err, len(models))\n\t} else {\n\t\tfor idx, user := range users {\n\t\t\tt.Run(\"FindWithInClause#\"+strconv.Itoa(idx+1), func(t *testing.T) {\n\t\t\t\tCheckUser(t, models[idx], user)\n\t\t\t})\n\t\t}\n\t}\n\n\t// test array\n\tvar models2 [3]User\n\tif err := DB.Where(\"name in (?)\", []string{\"find\"}).Find(&models2).Error; err != nil {\n\t\tt.Errorf(\"errors happened when query find with in clause: %v, length: %v\", err, len(models2))\n\t} else {\n\t\tfor idx, user := range users {\n\t\t\tt.Run(\"FindWithInClause#\"+strconv.Itoa(idx+1), func(t *testing.T) {\n\t\t\t\tCheckUser(t, models2[idx], user)\n\t\t\t})\n\t\t}\n\t}\n\n\t// test smaller array\n\tvar models3 [2]User\n\tif err := DB.Where(\"name in (?)\", []string{\"find\"}).Find(&models3).Error; err != nil {\n\t\tt.Errorf(\"errors happened when query find with in clause: %v, length: %v\", err, len(models3))\n\t} else {\n\t\tfor idx, user := range users[:2] {\n\t\t\tt.Run(\"FindWithInClause#\"+strconv.Itoa(idx+1), func(t *testing.T) {\n\t\t\t\tCheckUser(t, models3[idx], user)\n\t\t\t})\n\t\t}\n\t}\n\n\tvar none []User\n\tif err := DB.Where(\"name in (?)\", []string{}).Find(&none).Error; err != nil || len(none) != 0 {\n\t\tt.Errorf(\"errors happened when query find with in clause and zero length parameter: %v, length: %v\", err, len(none))\n\t}\n}\n\nfunc TestQueryWithAssociation(t *testing.T) {\n\tuser := *GetUser(\"query_with_association\", Config{Account: true, Pets: 2, Toys: 1, Company: true, Manager: true, Team: 2, Languages: 1, Friends: 3})\n\n\tif err := DB.Create(&user).Error; err != nil {\n\t\tt.Fatalf(\"errors happened when create user: %v\", err)\n\t}\n\n\tuser.CreatedAt = time.Time{}\n\tuser.UpdatedAt = time.Time{}\n\tif err := DB.Where(&user).First(&User{}).Error; err != nil {\n\t\tt.Errorf(\"search with struct with association should returns no error, but got %v\", err)\n\t}\n\n\tif err := DB.Where(user).First(&User{}).Error; err != nil {\n\t\tt.Errorf(\"search with struct with association should returns no error, but got %v\", err)\n\t}\n}\n\nfunc TestFindInBatches(t *testing.T) {\n\tusers := []User{\n\t\t*GetUser(\"find_in_batches\", Config{}),\n\t\t*GetUser(\"find_in_batches\", Config{}),\n\t\t*GetUser(\"find_in_batches\", Config{}),\n\t\t*GetUser(\"find_in_batches\", Config{}),\n\t\t*GetUser(\"find_in_batches\", Config{}),\n\t\t*GetUser(\"find_in_batches\", Config{}),\n\t}\n\n\tDB.Create(&users)\n\n\tvar (\n\t\tresults    []User\n\t\ttotalBatch int\n\t)\n\n\tif result := DB.Table(\"users as u\").Where(\"name = ?\", users[0].Name).FindInBatches(&results, 2, func(tx *gorm.DB, batch int) error {\n\t\ttotalBatch += batch\n\n\t\tif tx.RowsAffected != 2 {\n\t\t\tt.Errorf(\"Incorrect affected rows, expects: 2, got %v\", tx.RowsAffected)\n\t\t}\n\n\t\tif len(results) != 2 {\n\t\t\tt.Errorf(\"Incorrect users length, expects: 2, got %v\", len(results))\n\t\t}\n\n\t\tfor idx := range results {\n\t\t\tresults[idx].Name = results[idx].Name + \"_new\"\n\t\t}\n\n\t\tif err := tx.Save(results).Error; err != nil {\n\t\t\tt.Fatalf(\"failed to save users, got error %v\", err)\n\t\t}\n\n\t\treturn nil\n\t}); result.Error != nil || result.RowsAffected != 6 {\n\t\tt.Errorf(\"Failed to batch find, got error %v, rows affected: %v\", result.Error, result.RowsAffected)\n\t}\n\n\tif totalBatch != 6 {\n\t\tt.Errorf(\"incorrect total batch, expects: %v, got %v\", 6, totalBatch)\n\t}\n\n\tvar count int64\n\tDB.Model(&User{}).Where(\"name = ?\", \"find_in_batches_new\").Count(&count)\n\tif count != 6 {\n\t\tt.Errorf(\"incorrect count after update, expects: %v, got %v\", 6, count)\n\t}\n}\n\nfunc TestFindInBatchesWithOffsetLimit(t *testing.T) {\n\tusers := []User{\n\t\t*GetUser(\"find_in_batches_with_offset_limit\", Config{}),\n\t\t*GetUser(\"find_in_batches_with_offset_limit\", Config{}),\n\t\t*GetUser(\"find_in_batches_with_offset_limit\", Config{}),\n\t\t*GetUser(\"find_in_batches_with_offset_limit\", Config{}),\n\t\t*GetUser(\"find_in_batches_with_offset_limit\", Config{}),\n\t\t*GetUser(\"find_in_batches_with_offset_limit\", Config{}),\n\t\t*GetUser(\"find_in_batches_with_offset_limit\", Config{}),\n\t\t*GetUser(\"find_in_batches_with_offset_limit\", Config{}),\n\t\t*GetUser(\"find_in_batches_with_offset_limit\", Config{}),\n\t\t*GetUser(\"find_in_batches_with_offset_limit\", Config{}),\n\t}\n\n\tDB.Create(&users)\n\n\tvar (\n\t\tsub, results []User\n\t\tlastBatch    int\n\t)\n\n\t// offset limit\n\tif result := DB.Offset(3).Limit(5).Where(\"name = ?\", users[0].Name).FindInBatches(&sub, 2, func(tx *gorm.DB, batch int) error {\n\t\tresults = append(results, sub...)\n\t\tlastBatch = batch\n\t\treturn nil\n\t}); result.Error != nil || result.RowsAffected != 5 {\n\t\tt.Errorf(\"Failed to batch find, got error %v, rows affected: %v\", result.Error, result.RowsAffected)\n\t}\n\tif lastBatch != 3 {\n\t\tt.Fatalf(\"incorrect last batch, expected: %v, got: %v\", 3, lastBatch)\n\t}\n\n\ttargetUsers := users[3:8]\n\tfor i := 0; i < len(targetUsers); i++ {\n\t\tAssertEqual(t, results[i], targetUsers[i])\n\t}\n\n\tvar sub1 []User\n\t// limit < batchSize\n\tif result := DB.Limit(5).Where(\"name = ?\", users[0].Name).FindInBatches(&sub1, 10, func(tx *gorm.DB, batch int) error {\n\t\treturn nil\n\t}); result.Error != nil || result.RowsAffected != 5 {\n\t\tt.Errorf(\"Failed to batch find, got error %v, rows affected: %v\", result.Error, result.RowsAffected)\n\t}\n\n\tvar sub2 []User\n\t// only offset\n\tif result := DB.Offset(3).Where(\"name = ?\", users[0].Name).FindInBatches(&sub2, 2, func(tx *gorm.DB, batch int) error {\n\t\treturn nil\n\t}); result.Error != nil || result.RowsAffected != 7 {\n\t\tt.Errorf(\"Failed to batch find, got error %v, rows affected: %v\", result.Error, result.RowsAffected)\n\t}\n\n\tvar sub3 []User\n\tif result := DB.Limit(4).Where(\"name = ?\", users[0].Name).FindInBatches(&sub3, 2, func(tx *gorm.DB, batch int) error {\n\t\treturn nil\n\t}); result.Error != nil || result.RowsAffected != 4 {\n\t\tt.Errorf(\"Failed to batch find, got error %v, rows affected: %v\", result.Error, result.RowsAffected)\n\t}\n}\n\nfunc TestFindInBatchesWithError(t *testing.T) {\n\tif name := DB.Dialector.Name(); name == \"sqlserver\" {\n\t\tt.Skip(\"skip sqlserver due to it will raise data race for invalid sql\")\n\t}\n\n\tusers := []User{\n\t\t*GetUser(\"find_in_batches_with_error\", Config{}),\n\t\t*GetUser(\"find_in_batches_with_error\", Config{}),\n\t\t*GetUser(\"find_in_batches_with_error\", Config{}),\n\t\t*GetUser(\"find_in_batches_with_error\", Config{}),\n\t\t*GetUser(\"find_in_batches_with_error\", Config{}),\n\t\t*GetUser(\"find_in_batches_with_error\", Config{}),\n\t}\n\n\tDB.Create(&users)\n\n\tvar (\n\t\tresults    []User\n\t\ttotalBatch int\n\t)\n\n\tif result := DB.Table(\"wrong_table\").Where(\"name = ?\", users[0].Name).FindInBatches(&results, 2, func(tx *gorm.DB, batch int) error {\n\t\ttotalBatch += batch\n\t\treturn nil\n\t}); result.Error == nil || result.RowsAffected > 0 {\n\t\tt.Fatal(\"expected errors to have occurred, but nothing happened\")\n\t}\n\tif totalBatch != 0 {\n\t\tt.Fatalf(\"incorrect total batch, expected: %v, got: %v\", 0, totalBatch)\n\t}\n\n\tif result := DB.Omit(\"id\").Where(\"name = ?\", users[0].Name).FindInBatches(&results, 2, func(tx *gorm.DB, batch int) error {\n\t\ttotalBatch += batch\n\t\treturn nil\n\t}); result.Error != gorm.ErrPrimaryKeyRequired {\n\t\tt.Fatal(\"expected errors to have occurred, but nothing happened\")\n\t}\n}\n\nfunc TestFillSmallerStruct(t *testing.T) {\n\tuser := User{Name: \"SmallerUser\", Age: 100}\n\tDB.Save(&user)\n\ttype SimpleUser struct {\n\t\tID        int64\n\t\tName      string\n\t\tUpdatedAt time.Time\n\t\tCreatedAt time.Time\n\t}\n\n\tvar simpleUser SimpleUser\n\tif err := DB.Table(\"users\").Where(\"name = ?\", user.Name).First(&simpleUser).Error; err != nil {\n\t\tt.Fatalf(\"Failed to query smaller user, got error %v\", err)\n\t}\n\n\tAssertObjEqual(t, user, simpleUser, \"Name\", \"ID\", \"UpdatedAt\", \"CreatedAt\")\n\n\tvar simpleUser2 SimpleUser\n\tif err := DB.Model(&User{}).Select(\"id\").First(&simpleUser2, user.ID).Error; err != nil {\n\t\tt.Fatalf(\"Failed to query smaller user, got error %v\", err)\n\t}\n\n\tAssertObjEqual(t, user, simpleUser2, \"ID\")\n\n\tvar simpleUsers []SimpleUser\n\tif err := DB.Model(&User{}).Select(\"id\").Find(&simpleUsers, user.ID).Error; err != nil || len(simpleUsers) != 1 {\n\t\tt.Fatalf(\"Failed to query smaller user, got error %v\", err)\n\t}\n\n\tAssertObjEqual(t, user, simpleUsers[0], \"ID\")\n\n\tresult := DB.Session(&gorm.Session{DryRun: true}).Model(&User{}).Find(&simpleUsers, user.ID)\n\n\tif !regexp.MustCompile(\"SELECT .*id.*name.*updated_at.*created_at.* FROM .*users\").MatchString(result.Statement.SQL.String()) {\n\t\tt.Fatalf(\"SQL should include selected names, but got %v\", result.Statement.SQL.String())\n\t}\n\n\tresult = DB.Session(&gorm.Session{DryRun: true}).Model(&User{}).Find(&User{}, user.ID)\n\n\tif regexp.MustCompile(\"SELECT .*name.* FROM .*users\").MatchString(result.Statement.SQL.String()) {\n\t\tt.Fatalf(\"SQL should not include selected names, but got %v\", result.Statement.SQL.String())\n\t}\n\n\tresult = DB.Session(&gorm.Session{DryRun: true}).Model(&User{}).Find(&[]User{}, user.ID)\n\n\tif regexp.MustCompile(\"SELECT .*name.* FROM .*users\").MatchString(result.Statement.SQL.String()) {\n\t\tt.Fatalf(\"SQL should not include selected names, but got %v\", result.Statement.SQL.String())\n\t}\n\n\tresult = DB.Session(&gorm.Session{DryRun: true}).Model(&User{}).Find(&[]*User{}, user.ID)\n\n\tif regexp.MustCompile(\"SELECT .*name.* FROM .*users\").MatchString(result.Statement.SQL.String()) {\n\t\tt.Fatalf(\"SQL should not include selected names, but got %v\", result.Statement.SQL.String())\n\t}\n}\n\nfunc TestFillSmallerStructWithAllFields(t *testing.T) {\n\tuser := User{Name: \"SmallerUser\", Age: 100}\n\tDB.Save(&user)\n\ttype SimpleUser struct {\n\t\tID        int64\n\t\tName      string\n\t\tUpdatedAt time.Time\n\t\tCreatedAt time.Time\n\t}\n\tvar simpleUsers []SimpleUser\n\tdryDB := DB.Session(&gorm.Session{DryRun: true, QueryFields: true})\n\n\tresult := dryDB.Model(&User{}).Find(&simpleUsers, user.ID)\n\tif !regexp.MustCompile(\"SELECT .users.*id.*users.*name.*users.*updated_at.*users.*created_at.* FROM .*users\").MatchString(result.Statement.SQL.String()) {\n\t\tt.Fatalf(\"SQL should include selected names, but got %v\", result.Statement.SQL.String())\n\t}\n\n\tresult = dryDB.Model(&User{}).Find(&User{}, user.ID)\n\tif regexp.MustCompile(\"SELECT \\\\* FROM .*users\").MatchString(result.Statement.SQL.String()) {\n\t\tt.Fatalf(\"SQL should not include a * wildcard, but got %v\", result.Statement.SQL.String())\n\t}\n\n\tresult = dryDB.Model(&User{}).Find(&[]User{}, user.ID)\n\tif regexp.MustCompile(\"SELECT \\\\* FROM .*users\").MatchString(result.Statement.SQL.String()) {\n\t\tt.Fatalf(\"SQL should not include a * wildcard, but got %v\", result.Statement.SQL.String())\n\t}\n\n\tresult = dryDB.Model(&User{}).Find(&[]*User{}, user.ID)\n\tif regexp.MustCompile(\"SELECT \\\\* FROM .*users\").MatchString(result.Statement.SQL.String()) {\n\t\tt.Fatalf(\"SQL should not include a * wildcard, but got %v\", result.Statement.SQL.String())\n\t}\n}\n\nfunc TestNot(t *testing.T) {\n\tdryDB := DB.Session(&gorm.Session{DryRun: true})\n\n\tresult := dryDB.Not(map[string]interface{}{\"name\": \"jinzhu\"}).Find(&User{})\n\tif !regexp.MustCompile(\"SELECT \\\\* FROM .*users.* WHERE .*name.* <> .+\").MatchString(result.Statement.SQL.String()) {\n\t\tt.Fatalf(\"Build NOT condition, but got %v\", result.Statement.SQL.String())\n\t}\n\n\tresult = dryDB.Where(\"name = ?\", \"jinzhu1\").Not(\"name = ?\", \"jinzhu2\").Find(&User{})\n\tif !regexp.MustCompile(\"SELECT \\\\* FROM .*users.* WHERE .*name.* = .+ AND NOT.*name.* = .+\").MatchString(result.Statement.SQL.String()) {\n\t\tt.Fatalf(\"Build NOT condition, but got %v\", result.Statement.SQL.String())\n\t}\n\n\tresult = dryDB.Where(map[string]interface{}{\"name\": []string{\"jinzhu\", \"jinzhu 2\"}}).Find(&User{})\n\tif !regexp.MustCompile(\"SELECT \\\\* FROM .*users.* WHERE .*name.* IN \\\\(.+,.+\\\\)\").MatchString(result.Statement.SQL.String()) {\n\t\tt.Fatalf(\"Build NOT condition, but got %v\", result.Statement.SQL.String())\n\t}\n\n\tresult = dryDB.Not(\"name = ?\", \"jinzhu\").Find(&User{})\n\tif !regexp.MustCompile(\"SELECT \\\\* FROM .*users.* WHERE NOT.*name.* = .+\").MatchString(result.Statement.SQL.String()) {\n\t\tt.Fatalf(\"Build NOT condition, but got %v\", result.Statement.SQL.String())\n\t}\n\n\tresult = dryDB.Not(map[string]interface{}{\"name\": []string{}}).Find(&User{})\n\tif !regexp.MustCompile(\"SELECT \\\\* FROM .*users.* WHERE .*name.* IS NOT NULL\").MatchString(result.Statement.SQL.String()) {\n\t\tt.Fatalf(\"Build NOT condition, but got %v\", result.Statement.SQL.String())\n\t}\n\n\tresult = dryDB.Not(map[string]interface{}{\"name\": []string{\"jinzhu\", \"jinzhu 2\"}}).Find(&User{})\n\tif !regexp.MustCompile(\"SELECT \\\\* FROM .*users.* WHERE .*name.* NOT IN \\\\(.+,.+\\\\)\").MatchString(result.Statement.SQL.String()) {\n\t\tt.Fatalf(\"Build NOT condition, but got %v\", result.Statement.SQL.String())\n\t}\n\n\tresult = dryDB.Not([]int64{1, 2}).First(&User{})\n\tif !regexp.MustCompile(\"SELECT \\\\* FROM .*users.* WHERE .*id.* NOT IN \\\\(.+,.+\\\\)\").MatchString(result.Statement.SQL.String()) {\n\t\tt.Fatalf(\"Build NOT condition, but got %v\", result.Statement.SQL.String())\n\t}\n\n\tresult = dryDB.Not([]int64{}).First(&User{})\n\tif !regexp.MustCompile(\"SELECT \\\\* FROM .*users.* WHERE .users.\\\\..deleted_at. IS NULL ORDER BY\").MatchString(result.Statement.SQL.String()) {\n\t\tt.Fatalf(\"Build NOT condition, but got %v\", result.Statement.SQL.String())\n\t}\n\n\tresult = dryDB.Not(User{Name: \"jinzhu\", Age: 18}).First(&User{})\n\tif !regexp.MustCompile(\"SELECT \\\\* FROM .*users.* WHERE .*users.*..*name.* <> .+ AND .*users.*..*age.* <> .+\").MatchString(result.Statement.SQL.String()) {\n\t\tt.Fatalf(\"Build NOT condition, but got %v\", result.Statement.SQL.String())\n\t}\n\n\tresult = dryDB.Not(DB.Where(\"manager IS NULL\").Where(\"age >= ?\", 20)).Find(&User{})\n\tif !regexp.MustCompile(\"SELECT \\\\* FROM .*users.* WHERE NOT \\\\(manager IS NULL AND age >= .+\\\\) AND .users.\\\\..deleted_at. IS NULL\").MatchString(result.Statement.SQL.String()) {\n\t\tt.Fatalf(\"Build NOT condition, but got %v\", result.Statement.SQL.String())\n\t}\n\n\tresult = dryDB.Not(DB.Where(\"manager IS NULL\").Or(\"age >= ?\", 20)).Find(&User{})\n\tif !regexp.MustCompile(`SELECT \\* FROM .*users.* WHERE NOT \\(manager IS NULL OR age >= .+\\) AND .users.\\..deleted_at. IS NULL`).MatchString(result.Statement.SQL.String()) {\n\t\tt.Fatalf(\"Build NOT condition, but got %v\", result.Statement.SQL.String())\n\t}\n}\n\nfunc TestNotWithAllFields(t *testing.T) {\n\tdryDB := DB.Session(&gorm.Session{DryRun: true, QueryFields: true})\n\tuserQuery := \"SELECT .*users.*id.*users.*created_at.*users.*updated_at.*users.*deleted_at.*users.*name\" +\n\t\t\".*users.*age.*users.*birthday.*users.*company_id.*users.*manager_id.*users.*active.* FROM .*users.* \"\n\n\tresult := dryDB.Not(map[string]interface{}{\"users.name\": \"jinzhu\"}).Find(&User{})\n\n\tif !regexp.MustCompile(userQuery + \"WHERE .*users.*name.* <> .+\").MatchString(result.Statement.SQL.String()) {\n\t\tt.Fatalf(\"Build NOT condition, but got %v\", result.Statement.SQL.String())\n\t}\n\n\tresult = dryDB.Where(\"users.name = ?\", \"jinzhu1\").Not(\"users.name = ?\", \"jinzhu2\").Find(&User{})\n\tif !regexp.MustCompile(userQuery + \"WHERE .*users.*name.* = .+ AND NOT .*users.*name.* = .+\").MatchString(result.Statement.SQL.String()) {\n\t\tt.Fatalf(\"Build NOT condition, but got %v\", result.Statement.SQL.String())\n\t}\n\n\tresult = dryDB.Where(map[string]interface{}{\"users.name\": []string{\"jinzhu\", \"jinzhu 2\"}}).Find(&User{})\n\tif !regexp.MustCompile(userQuery + \"WHERE .*users.*name.* IN \\\\(.+,.+\\\\)\").MatchString(result.Statement.SQL.String()) {\n\t\tt.Fatalf(\"Build NOT condition, but got %v\", result.Statement.SQL.String())\n\t}\n\n\tresult = dryDB.Not(\"users.name = ?\", \"jinzhu\").Find(&User{})\n\tif !regexp.MustCompile(userQuery + \"WHERE NOT .*users.*name.* = .+\").MatchString(result.Statement.SQL.String()) {\n\t\tt.Fatalf(\"Build NOT condition, but got %v\", result.Statement.SQL.String())\n\t}\n\n\tresult = dryDB.Not(map[string]interface{}{\"users.name\": []string{\"jinzhu\", \"jinzhu 2\"}}).Find(&User{})\n\tif !regexp.MustCompile(userQuery + \"WHERE .*users.*name.* NOT IN \\\\(.+,.+\\\\)\").MatchString(result.Statement.SQL.String()) {\n\t\tt.Fatalf(\"Build NOT condition, but got %v\", result.Statement.SQL.String())\n\t}\n\n\tresult = dryDB.Not([]int64{1, 2}).First(&User{})\n\tif !regexp.MustCompile(userQuery + \"WHERE .*users.*id.* NOT IN \\\\(.+,.+\\\\)\").MatchString(result.Statement.SQL.String()) {\n\t\tt.Fatalf(\"Build NOT condition, but got %v\", result.Statement.SQL.String())\n\t}\n\n\tresult = dryDB.Not([]int64{}).First(&User{})\n\tif !regexp.MustCompile(userQuery + \"WHERE .users.\\\\..deleted_at. IS NULL ORDER BY\").MatchString(result.Statement.SQL.String()) {\n\t\tt.Fatalf(\"Build NOT condition, but got %v\", result.Statement.SQL.String())\n\t}\n\n\tresult = dryDB.Not(User{Name: \"jinzhu\", Age: 18}).First(&User{})\n\tif !regexp.MustCompile(userQuery + \"WHERE .*users.*..*name.* <> .+ AND .*users.*..*age.* <> .+\").MatchString(result.Statement.SQL.String()) {\n\t\tt.Fatalf(\"Build NOT condition, but got %v\", result.Statement.SQL.String())\n\t}\n}\n\nfunc TestOr(t *testing.T) {\n\tdryDB := DB.Session(&gorm.Session{DryRun: true})\n\n\tvar count int64\n\tresult := dryDB.Model(&User{}).Or(\"role = ?\", \"admin\").Count(&count)\n\tif !regexp.MustCompile(\"SELECT count\\\\(\\\\*\\\\) FROM .*users.* WHERE role = .+ AND .*users.*\\\\..*deleted_at.* IS NULL\").MatchString(result.Statement.SQL.String()) {\n\t\tt.Fatalf(\"Build OR condition, but got %v\", result.Statement.SQL.String())\n\t}\n\n\tresult = dryDB.Where(\"role = ?\", \"admin\").Where(DB.Or(\"role = ?\", \"super_admin\")).Find(&User{})\n\tif !regexp.MustCompile(\"SELECT \\\\* FROM .*users.* WHERE .*role.* = .+ AND .*role.* = .+\").MatchString(result.Statement.SQL.String()) {\n\t\tt.Fatalf(\"Build OR condition, but got %v\", result.Statement.SQL.String())\n\t}\n\n\tresult = dryDB.Where(\"role = ?\", \"admin\").Where(DB.Or(\"role = ?\", \"super_admin\").Or(\"role = ?\", \"admin\")).Find(&User{})\n\tif !regexp.MustCompile(\"SELECT \\\\* FROM .*users.* WHERE .*role.* = .+ AND (.*role.* = .+ OR .*role.* = .+)\").MatchString(result.Statement.SQL.String()) {\n\t\tt.Fatalf(\"Build OR condition, but got %v\", result.Statement.SQL.String())\n\t}\n\n\tsub := dryDB.Clauses(clause.Where{\n\t\tExprs: []clause.Expression{\n\t\t\tclause.OrConditions{\n\t\t\t\tExprs: []clause.Expression{\n\t\t\t\t\tclause.Expr{SQL: \"role = ?\", Vars: []interface{}{\"super_admin\"}},\n\t\t\t\t\tclause.Expr{SQL: \"role = ?\", Vars: []interface{}{\"admin\"}},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t})\n\tresult = dryDB.Where(sub).Find(&User{})\n\tif !regexp.MustCompile(\"SELECT \\\\* FROM .*users.* WHERE .*role.* = .+ OR .*role.* = .+\").MatchString(result.Statement.SQL.String()) {\n\t\tt.Fatalf(\"Build OR condition, but got %v\", result.Statement.SQL.String())\n\t}\n\n\tresult = dryDB.Where(\"role = ?\", \"admin\").Or(\"role = ?\", \"super_admin\").Find(&User{})\n\tif !regexp.MustCompile(\"SELECT \\\\* FROM .*users.* WHERE .*role.* = .+ OR .*role.* = .+\").MatchString(result.Statement.SQL.String()) {\n\t\tt.Fatalf(\"Build OR condition, but got %v\", result.Statement.SQL.String())\n\t}\n\n\tresult = dryDB.Where(\"name = ?\", \"jinzhu\").Or(User{Name: \"jinzhu 2\", Age: 18}).Find(&User{})\n\tif !regexp.MustCompile(\"SELECT \\\\* FROM .*users.* WHERE .*name.* = .+ OR \\\\(.*name.* AND .*age.*\\\\)\").MatchString(result.Statement.SQL.String()) {\n\t\tt.Fatalf(\"Build OR condition, but got %v\", result.Statement.SQL.String())\n\t}\n\n\tresult = dryDB.Where(\"name = ?\", \"jinzhu\").Or(map[string]interface{}{\"name\": \"jinzhu 2\", \"age\": 18}).Find(&User{})\n\tif !regexp.MustCompile(\"SELECT \\\\* FROM .*users.* WHERE .*name.* = .+ OR \\\\(.*age.* AND .*name.*\\\\)\").MatchString(result.Statement.SQL.String()) {\n\t\tt.Fatalf(\"Build OR condition, but got %v\", result.Statement.SQL.String())\n\t}\n}\n\nfunc TestOrWithAllFields(t *testing.T) {\n\tdryDB := DB.Session(&gorm.Session{DryRun: true, QueryFields: true})\n\tuserQuery := \"SELECT .*users.*id.*users.*created_at.*users.*updated_at.*users.*deleted_at.*users.*name\" +\n\t\t\".*users.*age.*users.*birthday.*users.*company_id.*users.*manager_id.*users.*active.* FROM .*users.* \"\n\n\tresult := dryDB.Where(\"role = ?\", \"admin\").Or(\"role = ?\", \"super_admin\").Find(&User{})\n\tif !regexp.MustCompile(userQuery + \"WHERE .*role.* = .+ OR .*role.* = .+\").MatchString(result.Statement.SQL.String()) {\n\t\tt.Fatalf(\"Build OR condition, but got %v\", result.Statement.SQL.String())\n\t}\n\n\tresult = dryDB.Where(\"users.name = ?\", \"jinzhu\").Or(User{Name: \"jinzhu 2\", Age: 18}).Find(&User{})\n\tif !regexp.MustCompile(userQuery + \"WHERE .*users.*name.* = .+ OR \\\\(.*users.*name.* AND .*users.*age.*\\\\)\").MatchString(result.Statement.SQL.String()) {\n\t\tt.Fatalf(\"Build OR condition, but got %v\", result.Statement.SQL.String())\n\t}\n\n\tresult = dryDB.Where(\"users.name = ?\", \"jinzhu\").Or(map[string]interface{}{\"name\": \"jinzhu 2\", \"age\": 18}).Find(&User{})\n\tif !regexp.MustCompile(userQuery + \"WHERE .*users.*name.* = .+ OR \\\\(.*age.* AND .*name.*\\\\)\").MatchString(result.Statement.SQL.String()) {\n\t\tt.Fatalf(\"Build OR condition, but got %v\", result.Statement.SQL.String())\n\t}\n}\n\ntype Int64 int64\n\nfunc (v Int64) Value() (driver.Value, error) {\n\treturn v - 1, nil\n}\n\nfunc (f *Int64) Scan(v interface{}) error {\n\ty := v.(int64)\n\t*f = Int64(y + 1)\n\treturn nil\n}\n\nfunc TestPluck(t *testing.T) {\n\tusers := []*User{\n\t\tGetUser(\"pluck-user1\", Config{}),\n\t\tGetUser(\"pluck-user2\", Config{}),\n\t\tGetUser(\"pluck-user3\", Config{}),\n\t}\n\n\tDB.Create(&users)\n\n\tvar names []string\n\tif err := DB.Model(User{}).Where(\"name like ?\", \"pluck-user%\").Order(\"name\").Pluck(\"name\", &names).Error; err != nil {\n\t\tt.Errorf(\"got error when pluck name: %v\", err)\n\t}\n\n\tvar names2 []string\n\tif err := DB.Model(User{}).Where(\"name like ?\", \"pluck-user%\").Order(\"name desc\").Pluck(\"name\", &names2).Error; err != nil {\n\t\tt.Errorf(\"got error when pluck name: %v\", err)\n\t}\n\n\tsort.Slice(names2, func(i, j int) bool { return names2[i] < names2[j] })\n\tAssertEqual(t, names, names2)\n\n\tvar ids []int\n\tif err := DB.Model(User{}).Where(\"name like ?\", \"pluck-user%\").Pluck(\"id\", &ids).Error; err != nil {\n\t\tt.Errorf(\"got error when pluck id: %v\", err)\n\t}\n\n\tvar ids2 []Int64\n\tif err := DB.Model(User{}).Where(\"name like ?\", \"pluck-user%\").Pluck(\"id\", &ids2).Error; err != nil {\n\t\tt.Errorf(\"got error when pluck id: %v\", err)\n\t}\n\n\tfor idx, name := range names {\n\t\tif name != users[idx].Name {\n\t\t\tt.Errorf(\"Unexpected result on pluck name, got %+v\", names)\n\t\t}\n\t}\n\n\tfor idx, id := range ids {\n\t\tif int(id) != int(users[idx].ID) {\n\t\t\tt.Errorf(\"Unexpected result on pluck id, got %+v\", ids)\n\t\t}\n\t}\n\n\tfor idx, id := range ids2 {\n\t\tif int(id) != int(users[idx].ID+1) {\n\t\t\tt.Errorf(\"Unexpected result on pluck id, got %+v\", ids)\n\t\t}\n\t}\n\n\tvar times []time.Time\n\tif err := DB.Model(User{}).Where(\"name like ?\", \"pluck-user%\").Pluck(\"created_at\", &times).Error; err != nil {\n\t\tt.Errorf(\"got error when pluck time: %v\", err)\n\t}\n\n\tfor idx, tv := range times {\n\t\tAssertEqual(t, tv, users[idx].CreatedAt)\n\t}\n\n\tvar ptrtimes []*time.Time\n\tif err := DB.Model(User{}).Where(\"name like ?\", \"pluck-user%\").Pluck(\"created_at\", &ptrtimes).Error; err != nil {\n\t\tt.Errorf(\"got error when pluck time: %v\", err)\n\t}\n\n\tfor idx, tv := range ptrtimes {\n\t\tAssertEqual(t, tv, users[idx].CreatedAt)\n\t}\n\n\tvar nulltimes []sql.NullTime\n\tif err := DB.Model(User{}).Where(\"name like ?\", \"pluck-user%\").Pluck(\"created_at\", &nulltimes).Error; err != nil {\n\t\tt.Errorf(\"got error when pluck time: %v\", err)\n\t}\n\n\tfor idx, tv := range nulltimes {\n\t\tAssertEqual(t, tv.Time, users[idx].CreatedAt)\n\t}\n}\n\nfunc TestSelect(t *testing.T) {\n\tuser := User{Name: \"SelectUser1\"}\n\tDB.Save(&user)\n\n\tvar result User\n\tDB.Where(\"name = ?\", user.Name).Select(\"name\").Find(&result)\n\tif result.ID != 0 {\n\t\tt.Errorf(\"Should not have ID because only selected name, %+v\", result.ID)\n\t}\n\n\tif user.Name != result.Name {\n\t\tt.Errorf(\"Should have user Name when selected it\")\n\t}\n\n\tvar result2 User\n\tDB.Where(\"name = ?\", user.Name).Select(\"name as name\").Find(&result2)\n\tif result2.ID != 0 {\n\t\tt.Errorf(\"Should not have ID because only selected name, %+v\", result2.ID)\n\t}\n\n\tif user.Name != result2.Name {\n\t\tt.Errorf(\"Should have user Name when selected it\")\n\t}\n\n\tdryDB := DB.Session(&gorm.Session{DryRun: true})\n\tr := dryDB.Select(\"name\", \"age\").Find(&User{})\n\tif !regexp.MustCompile(\"SELECT .*name.*,.*age.* FROM .*users.*\").MatchString(r.Statement.SQL.String()) {\n\t\tt.Fatalf(\"Build Select with strings, but got %v\", r.Statement.SQL.String())\n\t}\n\n\tr = dryDB.Select([]string{\"name\", \"age\"}).Find(&User{})\n\tif !regexp.MustCompile(\"SELECT .*name.*,.*age.* FROM .*users.*\").MatchString(r.Statement.SQL.String()) {\n\t\tt.Fatalf(\"Build Select with slice, but got %v\", r.Statement.SQL.String())\n\t}\n\n\t// SELECT COALESCE(age,'42') FROM users;\n\tr = dryDB.Table(\"users\").Select(\"COALESCE(age,?)\", 42).Find(&User{})\n\tif !regexp.MustCompile(`SELECT COALESCE\\(age,.*\\) FROM .*users.*`).MatchString(r.Statement.SQL.String()) {\n\t\tt.Fatalf(\"Build Select with func, but got %v\", r.Statement.SQL.String())\n\t}\n\n\t// named arguments\n\tr = dryDB.Table(\"users\").Select(\"COALESCE(age, @default)\", sql.Named(\"default\", 42)).Find(&User{})\n\tif !regexp.MustCompile(`SELECT COALESCE\\(age,.*\\) FROM .*users.*`).MatchString(r.Statement.SQL.String()) {\n\t\tt.Fatalf(\"Build Select with func, but got %v\", r.Statement.SQL.String())\n\t}\n\n\tif _, err := DB.Table(\"users\").Select(\"COALESCE(age,?)\", \"42\").Rows(); err != nil {\n\t\tt.Fatalf(\"Failed, got error: %v\", err)\n\t}\n\n\tr = dryDB.Select(\"u.*\").Table(\"users as u\").First(&User{}, user.ID)\n\tif !regexp.MustCompile(`SELECT u\\.\\* FROM .*users.*`).MatchString(r.Statement.SQL.String()) {\n\t\tt.Fatalf(\"Build Select with u.*, but got %v\", r.Statement.SQL.String())\n\t}\n\n\tr = dryDB.Select(\"count(*)\").Select(\"u.*\").Table(\"users as u\").First(&User{}, user.ID)\n\tif !regexp.MustCompile(`SELECT u\\.\\* FROM .*users.*`).MatchString(r.Statement.SQL.String()) {\n\t\tt.Fatalf(\"Build Select with u.*, but got %v\", r.Statement.SQL.String())\n\t}\n}\n\nfunc TestOmit(t *testing.T) {\n\tuser := User{Name: \"OmitUser1\", Age: 20}\n\tDB.Save(&user)\n\n\tvar result User\n\tDB.Where(\"name = ?\", user.Name).Omit(\"name\").Find(&result)\n\tif result.ID == 0 {\n\t\tt.Errorf(\"Should not have ID because only selected name, %+v\", result.ID)\n\t}\n\n\tif result.Name != \"\" || result.Age != 20 {\n\t\tt.Errorf(\"User Name should be omitted, got %v, Age should be ok, got %v\", result.Name, result.Age)\n\t}\n}\n\nfunc TestOmitWithAllFields(t *testing.T) {\n\tuser := User{Name: \"OmitUser1\", Age: 20}\n\tDB.Save(&user)\n\n\tvar userResult User\n\tDB.Session(&gorm.Session{QueryFields: true}).Where(\"users.name = ?\", user.Name).Omit(\"name\").Find(&userResult)\n\tif userResult.ID == 0 {\n\t\tt.Errorf(\"Should not have ID because only selected name, %+v\", userResult.ID)\n\t}\n\n\tif userResult.Name != \"\" || userResult.Age != 20 {\n\t\tt.Errorf(\"User Name should be omitted, got %v, Age should be ok, got %v\", userResult.Name, userResult.Age)\n\t}\n\n\tdryDB := DB.Session(&gorm.Session{DryRun: true, QueryFields: true})\n\tuserQuery := \"SELECT .*users.*id.*users.*created_at.*users.*updated_at.*users.*deleted_at.*users.*birthday\" +\n\t\t\".*users.*company_id.*users.*manager_id.*users.*active.* FROM .*users.* \"\n\n\tresult := dryDB.Omit(\"name, age\").Find(&User{})\n\tif !regexp.MustCompile(userQuery).MatchString(result.Statement.SQL.String()) {\n\t\tt.Fatalf(\"SQL must include table name and selected fields, got %v\", result.Statement.SQL.String())\n\t}\n}\n\nfunc TestMapColumns(t *testing.T) {\n\tuser := User{Name: \"MapColumnsUser\", Age: 12}\n\tDB.Save(&user)\n\n\ttype result struct {\n\t\tName     string\n\t\tNickname string\n\t\tAge      uint\n\t}\n\tvar res result\n\tDB.Table(\"users\").Where(\"name = ?\", user.Name).MapColumns(map[string]string{\"name\": \"nickname\"}).Scan(&res)\n\tif res.Nickname != user.Name {\n\t\tt.Errorf(\"Expected res.Nickname to be %s, but got %s\", user.Name, res.Nickname)\n\t}\n\tif res.Name != \"\" {\n\t\tt.Errorf(\"Expected res.Name to be empty, but got %s\", res.Name)\n\t}\n\tif res.Age != user.Age {\n\t\tt.Errorf(\"Expected res.Age to be %d, but got %d\", user.Age, res.Age)\n\t}\n}\n\nfunc TestPluckWithSelect(t *testing.T) {\n\tusers := []User{\n\t\t{Name: \"pluck_with_select_1\", Age: 25},\n\t\t{Name: \"pluck_with_select_2\", Age: 26},\n\t}\n\n\tDB.Create(&users)\n\n\tvar userAges []int\n\terr := DB.Model(&User{}).Where(\"name like ?\", \"pluck_with_select%\").Select(\"age + 1 as user_age\").Pluck(\"user_age\", &userAges).Error\n\tif err != nil {\n\t\tt.Fatalf(\"got error when pluck user_age: %v\", err)\n\t}\n\n\tsort.Ints(userAges)\n\n\tAssertEqual(t, userAges, []int{26, 27})\n}\n\nfunc TestSelectWithVariables(t *testing.T) {\n\tDB.Save(&User{Name: \"select_with_variables\"})\n\n\trows, _ := DB.Table(\"users\").Where(\"name = ?\", \"select_with_variables\").Select(\"? as fake\", gorm.Expr(\"name\")).Rows()\n\n\tif !rows.Next() {\n\t\tt.Errorf(\"Should have returned at least one row\")\n\t} else {\n\t\tcolumns, _ := rows.Columns()\n\t\tAssertEqual(t, columns, []string{\"fake\"})\n\t}\n\n\trows.Close()\n}\n\nfunc TestSelectWithArrayInput(t *testing.T) {\n\tDB.Save(&User{Name: \"select_with_array\", Age: 42})\n\n\tvar user User\n\tDB.Select([]string{\"name\", \"age\"}).Where(\"age = 42 AND name = ?\", \"select_with_array\").First(&user)\n\n\tif user.Name != \"select_with_array\" || user.Age != 42 {\n\t\tt.Errorf(\"Should have selected both age and name\")\n\t}\n}\n\nfunc TestCustomizedTypePrimaryKey(t *testing.T) {\n\ttype ID uint\n\ttype CustomizedTypePrimaryKey struct {\n\t\tID   ID\n\t\tName string\n\t}\n\n\tDB.Migrator().DropTable(&CustomizedTypePrimaryKey{})\n\tif err := DB.AutoMigrate(&CustomizedTypePrimaryKey{}); err != nil {\n\t\tt.Fatalf(\"failed to migrate, got error %v\", err)\n\t}\n\n\tp1 := CustomizedTypePrimaryKey{Name: \"p1\"}\n\tp2 := CustomizedTypePrimaryKey{Name: \"p2\"}\n\tp3 := CustomizedTypePrimaryKey{Name: \"p3\"}\n\tDB.Create(&p1)\n\tDB.Create(&p2)\n\tDB.Create(&p3)\n\n\tvar p CustomizedTypePrimaryKey\n\n\tif err := DB.First(&p, p2.ID).Error; err != nil {\n\t\tt.Errorf(\"No error should returns, but got %v\", err)\n\t}\n\n\tAssertEqual(t, p, p2)\n\n\tif err := DB.First(&p, \"id = ?\", p2.ID).Error; err != nil {\n\t\tt.Errorf(\"No error should happen when querying with customized type for primary key, got err %v\", err)\n\t}\n\n\tAssertEqual(t, p, p2)\n}\n\nfunc TestStringPrimaryKeyForNumericValueStartingWithZero(t *testing.T) {\n\ttype AddressByZipCode struct {\n\t\tZipCode string `gorm:\"primary_key\"`\n\t\tAddress string\n\t}\n\n\tDB.Migrator().DropTable(&AddressByZipCode{})\n\tif err := DB.AutoMigrate(&AddressByZipCode{}); err != nil {\n\t\tt.Fatalf(\"failed to migrate, got error %v\", err)\n\t}\n\n\taddress := AddressByZipCode{ZipCode: \"00501\", Address: \"Holtsville\"}\n\tDB.Create(&address)\n\n\tvar result AddressByZipCode\n\tDB.First(&result, \"00501\")\n\n\tAssertEqual(t, result, address)\n}\n\nfunc TestSearchWithEmptyChain(t *testing.T) {\n\tuser := User{Name: \"search_with_empty_chain\", Age: 1}\n\tDB.Create(&user)\n\n\tvar result User\n\tif DB.Where(\"\").Where(\"\").First(&result).Error != nil {\n\t\tt.Errorf(\"Should not raise any error if searching with empty strings\")\n\t}\n\n\tresult = User{}\n\tif DB.Where(&User{}).Where(\"name = ?\", user.Name).First(&result).Error != nil {\n\t\tt.Errorf(\"Should not raise any error if searching with empty struct\")\n\t}\n\n\tresult = User{}\n\tif DB.Where(map[string]interface{}{}).Where(\"name = ?\", user.Name).First(&result).Error != nil {\n\t\tt.Errorf(\"Should not raise any error if searching with empty map\")\n\t}\n}\n\nfunc TestOrder(t *testing.T) {\n\tdryDB := DB.Session(&gorm.Session{DryRun: true})\n\n\tresult := dryDB.Order(\"\").Find(&User{})\n\tif !regexp.MustCompile(\"SELECT \\\\* FROM .*users.* IS NULL$\").MatchString(result.Statement.SQL.String()) {\n\t\tt.Fatalf(\"Build Order condition, but got %v\", result.Statement.SQL.String())\n\t}\n\n\tresult = dryDB.Order(nil).Find(&User{})\n\tif !regexp.MustCompile(\"SELECT \\\\* FROM .*users.* IS NULL$\").MatchString(result.Statement.SQL.String()) {\n\t\tt.Fatalf(\"Build Order condition, but got %v\", result.Statement.SQL.String())\n\t}\n\n\tresult = dryDB.Order(\"age desc, name\").Find(&User{})\n\tif !regexp.MustCompile(\"SELECT \\\\* FROM .*users.* ORDER BY age desc, name\").MatchString(result.Statement.SQL.String()) {\n\t\tt.Fatalf(\"Build Order condition, but got %v\", result.Statement.SQL.String())\n\t}\n\n\tresult = dryDB.Order(\"age desc\").Order(\"name\").Find(&User{})\n\tif !regexp.MustCompile(\"SELECT \\\\* FROM .*users.* ORDER BY age desc,name\").MatchString(result.Statement.SQL.String()) {\n\t\tt.Fatalf(\"Build Order condition, but got %v\", result.Statement.SQL.String())\n\t}\n\n\tstmt := dryDB.Clauses(clause.OrderBy{\n\t\tExpression: clause.Expr{SQL: \"FIELD(id,?)\", Vars: []interface{}{[]int{1, 2, 3}}, WithoutParentheses: true},\n\t}).Find(&User{}).Statement\n\n\texplainedSQL := dryDB.Dialector.Explain(stmt.SQL.String(), stmt.Vars...)\n\tif !regexp.MustCompile(\"SELECT \\\\* FROM .*users.* ORDER BY FIELD\\\\(id,1,2,3\\\\)\").MatchString(explainedSQL) {\n\t\tt.Fatalf(\"Build Order condition, but got %v\", explainedSQL)\n\t}\n}\n\nfunc TestOrderWithAllFields(t *testing.T) {\n\tdryDB := DB.Session(&gorm.Session{DryRun: true, QueryFields: true})\n\tuserQuery := \"SELECT .*users.*id.*users.*created_at.*users.*updated_at.*users.*deleted_at.*users.*name.*users.*age\" +\n\t\t\".*users.*birthday.*users.*company_id.*users.*manager_id.*users.*active.* FROM .*users.* \"\n\n\tresult := dryDB.Order(\"users.age desc, users.name\").Find(&User{})\n\tif !regexp.MustCompile(userQuery + \"users.age desc, users.name\").MatchString(result.Statement.SQL.String()) {\n\t\tt.Fatalf(\"Build Order condition, but got %v\", result.Statement.SQL.String())\n\t}\n\n\tresult = dryDB.Order(\"users.age desc\").Order(\"users.name\").Find(&User{})\n\tif !regexp.MustCompile(userQuery + \"ORDER BY users.age desc,users.name\").MatchString(result.Statement.SQL.String()) {\n\t\tt.Fatalf(\"Build Order condition, but got %v\", result.Statement.SQL.String())\n\t}\n\n\tstmt := dryDB.Clauses(clause.OrderBy{\n\t\tExpression: clause.Expr{SQL: \"FIELD(id,?)\", Vars: []interface{}{[]int{1, 2, 3}}, WithoutParentheses: true},\n\t}).Find(&User{}).Statement\n\n\texplainedSQL := dryDB.Dialector.Explain(stmt.SQL.String(), stmt.Vars...)\n\tif !regexp.MustCompile(userQuery + \"ORDER BY FIELD\\\\(id,1,2,3\\\\)\").MatchString(explainedSQL) {\n\t\tt.Fatalf(\"Build Order condition, but got %v\", explainedSQL)\n\t}\n}\n\nfunc TestLimit(t *testing.T) {\n\tusers := []User{\n\t\t{Name: \"LimitUser1\", Age: 1},\n\t\t{Name: \"LimitUser2\", Age: 10},\n\t\t{Name: \"LimitUser3\", Age: 20},\n\t\t{Name: \"LimitUser4\", Age: 10},\n\t\t{Name: \"LimitUser5\", Age: 20},\n\t\t{Name: \"LimitUser6\", Age: 20},\n\t}\n\n\tDB.Create(&users)\n\n\tvar users1, users2, users3 []User\n\tDB.Order(\"age desc\").Limit(3).Find(&users1).Limit(5).Find(&users2).Limit(-1).Find(&users3)\n\n\tif len(users1) != 3 || len(users2) != 5 || len(users3) <= 5 {\n\t\tt.Errorf(\"Limit should works, users1 %v users2 %v users3 %v\", len(users1), len(users2), len(users3))\n\t}\n}\n\nfunc TestOffset(t *testing.T) {\n\tfor i := 0; i < 20; i++ {\n\t\tDB.Save(&User{Name: fmt.Sprintf(\"OffsetUser%v\", i)})\n\t}\n\tvar users1, users2, users3, users4 []User\n\n\tDB.Limit(100).Where(\"name like ?\", \"OffsetUser%\").Order(\"age desc\").Find(&users1).Offset(3).Find(&users2).Offset(5).Find(&users3).Offset(-1).Find(&users4)\n\n\tif (len(users1) != len(users4)) || (len(users1)-len(users2) != 3) || (len(users1)-len(users3) != 5) {\n\t\tt.Errorf(\"Offset should work\")\n\t}\n\n\tDB.Where(\"name like ?\", \"OffsetUser%\").Order(\"age desc\").Find(&users1).Offset(3).Find(&users2).Offset(5).Find(&users3).Offset(-1).Find(&users4)\n\n\tif (len(users1) != len(users4)) || (len(users1)-len(users2) != 3) || (len(users1)-len(users3) != 5) {\n\t\tt.Errorf(\"Offset should work without limit.\")\n\t}\n}\n\nfunc TestSearchWithMap(t *testing.T) {\n\tusers := []User{\n\t\t*GetUser(\"map_search_user1\", Config{}),\n\t\t*GetUser(\"map_search_user2\", Config{}),\n\t\t*GetUser(\"map_search_user3\", Config{}),\n\t\t*GetUser(\"map_search_user4\", Config{Company: true}),\n\t}\n\n\tDB.Create(&users)\n\n\tvar user User\n\tDB.First(&user, map[string]interface{}{\"name\": users[0].Name})\n\tCheckUser(t, user, users[0])\n\n\tuser = User{}\n\tDB.First(&user, map[string]interface{}{\"users.name\": users[0].Name})\n\tCheckUser(t, user, users[0])\n\n\tuser = User{}\n\tDB.Where(map[string]interface{}{\"name\": users[1].Name}).First(&user)\n\tCheckUser(t, user, users[1])\n\n\tvar results []User\n\tDB.Where(map[string]interface{}{\"name\": users[2].Name}).Find(&results)\n\tif len(results) != 1 {\n\t\tt.Fatalf(\"Search all records with inline map\")\n\t}\n\n\tCheckUser(t, results[0], users[2])\n\n\tvar results2 []User\n\tDB.Find(&results2, map[string]interface{}{\"name\": users[3].Name, \"company_id\": nil})\n\tif len(results2) != 0 {\n\t\tt.Errorf(\"Search all records with inline map containing null value finding 0 records\")\n\t}\n\n\tDB.Find(&results2, map[string]interface{}{\"name\": users[0].Name, \"company_id\": nil})\n\tif len(results2) != 1 {\n\t\tt.Errorf(\"Search all records with inline map containing null value finding 1 record\")\n\t}\n\n\tDB.Find(&results2, map[string]interface{}{\"name\": users[3].Name, \"company_id\": users[3].CompanyID})\n\tif len(results2) != 1 {\n\t\tt.Errorf(\"Search all records with inline multiple value map\")\n\t}\n}\n\nfunc TestSearchWithStruct(t *testing.T) {\n\tdryRunDB := DB.Session(&gorm.Session{DryRun: true})\n\n\tresult := dryRunDB.Where(User{Name: \"jinzhu\"}).Find(&User{})\n\tif !regexp.MustCompile(`WHERE .users.\\..name. = .{1,3} AND .users.\\..deleted_at. IS NULL`).MatchString(result.Statement.SQL.String()) {\n\t\tt.Errorf(\"invalid query SQL, got %v\", result.Statement.SQL.String())\n\t}\n\n\tresult = dryRunDB.Where(User{Name: \"jinzhu\", Age: 18}).Find(&User{})\n\tif !regexp.MustCompile(`WHERE \\(.users.\\..name. = .{1,3} AND .users.\\..age. = .{1,3}\\) AND .users.\\..deleted_at. IS NULL`).MatchString(result.Statement.SQL.String()) {\n\t\tt.Errorf(\"invalid query SQL, got %v\", result.Statement.SQL.String())\n\t}\n\n\tresult = dryRunDB.Where(User{Name: \"jinzhu\"}, \"name\", \"Age\").Find(&User{})\n\tif !regexp.MustCompile(`WHERE \\(.users.\\..name. = .{1,3} AND .users.\\..age. = .{1,3}\\) AND .users.\\..deleted_at. IS NULL`).MatchString(result.Statement.SQL.String()) {\n\t\tt.Errorf(\"invalid query SQL, got %v\", result.Statement.SQL.String())\n\t}\n\n\tresult = dryRunDB.Where(User{Name: \"jinzhu\"}, \"age\").Find(&User{})\n\tif !regexp.MustCompile(`WHERE .users.\\..age. = .{1,3} AND .users.\\..deleted_at. IS NULL`).MatchString(result.Statement.SQL.String()) {\n\t\tt.Errorf(\"invalid query SQL, got %v\", result.Statement.SQL.String())\n\t}\n}\n\nfunc TestSubQuery(t *testing.T) {\n\tusers := []User{\n\t\t{Name: \"subquery_1\", Age: 10},\n\t\t{Name: \"subquery_2\", Age: 20},\n\t\t{Name: \"subquery_3\", Age: 30},\n\t\t{Name: \"subquery_4\", Age: 40},\n\t}\n\n\tDB.Create(&users)\n\n\tif err := DB.Select(\"*\").Where(\"name IN (?)\", DB.Select(\"name\").Table(\"users\").Where(\"name LIKE ?\", \"subquery_%\")).Find(&users).Error; err != nil {\n\t\tt.Fatalf(\"got error: %v\", err)\n\t}\n\n\tif len(users) != 4 {\n\t\tt.Errorf(\"Four users should be found, instead found %d\", len(users))\n\t}\n\n\tDB.Select(\"*\").Where(\"name LIKE ?\", \"subquery%\").Where(\"age >= (?)\", DB.\n\t\tSelect(\"AVG(age)\").Table(\"users\").Where(\"name LIKE ?\", \"subquery%\")).Find(&users)\n\n\tif len(users) != 2 {\n\t\tt.Errorf(\"Two users should be found, instead found %d\", len(users))\n\t}\n}\n\nfunc TestSubQueryWithRaw(t *testing.T) {\n\tusers := []User{\n\t\t{Name: \"subquery_raw_1\", Age: 10},\n\t\t{Name: \"subquery_raw_2\", Age: 20},\n\t\t{Name: \"subquery_raw_3\", Age: 30},\n\t\t{Name: \"subquery_raw_4\", Age: 40},\n\t}\n\tDB.Create(&users)\n\n\tvar count int64\n\terr := DB.Raw(\"select count(*) from (?) tmp where 1 = ? AND name IN (?)\", DB.Raw(\"select name from users where age >= ? and name in (?)\", 10, []string{\"subquery_raw_1\", \"subquery_raw_2\", \"subquery_raw_3\"}), 1, DB.Raw(\"select name from users where age >= ? and name in (?)\", 20, []string{\"subquery_raw_1\", \"subquery_raw_2\", \"subquery_raw_3\"})).Scan(&count).Error\n\tif err != nil {\n\t\tt.Errorf(\"Expected to get no errors, but got %v\", err)\n\t}\n\n\tif count != 2 {\n\t\tt.Errorf(\"Row count must be 2, instead got %d\", count)\n\t}\n\n\terr = DB.Raw(\"select count(*) from (?) tmp\",\n\t\tDB.Table(\"users\").\n\t\t\tSelect(\"name\").\n\t\t\tWhere(\"age >= ? and name in (?)\", 20, []string{\"subquery_raw_1\", \"subquery_raw_3\"}).\n\t\t\tGroup(\"name\"),\n\t).Count(&count).Error\n\tif err != nil {\n\t\tt.Errorf(\"Expected to get no errors, but got %v\", err)\n\t}\n\n\tif count != 1 {\n\t\tt.Errorf(\"Row count must be 1, instead got %d\", count)\n\t}\n\n\terr = DB.Raw(\"select count(*) from (?) tmp\",\n\t\tDB.Table(\"users\").\n\t\t\tSelect(\"name\").\n\t\t\tWhere(\"name LIKE ?\", \"subquery_raw%\").\n\t\t\tNot(\"age <= ?\", 10).Not(\"name IN (?)\", []string{\"subquery_raw_1\", \"subquery_raw_3\"}).\n\t\t\tGroup(\"name\"),\n\t).Count(&count).Error\n\tif err != nil {\n\t\tt.Errorf(\"Expected to get no errors, but got %v\", err)\n\t}\n\n\tif count != 2 {\n\t\tt.Errorf(\"Row count must be 2, instead got %d\", count)\n\t}\n}\n\nfunc TestSubQueryWithHaving(t *testing.T) {\n\tusers := []User{\n\t\t{Name: \"subquery_having_1\", Age: 10},\n\t\t{Name: \"subquery_having_2\", Age: 20},\n\t\t{Name: \"subquery_having_3\", Age: 30},\n\t\t{Name: \"subquery_having_4\", Age: 40},\n\t}\n\tDB.Create(&users)\n\n\tvar results []User\n\tDB.Select(\"AVG(age) as avgage\").Where(\"name LIKE ?\", \"subquery_having%\").Group(\"name\").Having(\"AVG(age) > (?)\", DB.\n\t\tSelect(\"AVG(age)\").Where(\"name LIKE ?\", \"subquery_having%\").Table(\"users\")).Find(&results)\n\n\tif len(results) != 2 {\n\t\tt.Errorf(\"Two user group should be found, instead found %d\", len(results))\n\t}\n}\n\nfunc TestScanNullValue(t *testing.T) {\n\tuser := GetUser(\"scan_null_value\", Config{})\n\tDB.Create(&user)\n\n\tif err := DB.Model(&user).Update(\"age\", nil).Error; err != nil {\n\t\tt.Fatalf(\"failed to update column age for struct, got error %v\", err)\n\t}\n\n\tvar result User\n\tif err := DB.First(&result, \"id = ?\", user.ID).Error; err != nil {\n\t\tt.Fatalf(\"failed to query struct data with null age, got error %v\", err)\n\t}\n\n\tAssertEqual(t, result, user)\n\n\tusers := []User{\n\t\t*GetUser(\"scan_null_value_for_slice_1\", Config{}),\n\t\t*GetUser(\"scan_null_value_for_slice_2\", Config{}),\n\t\t*GetUser(\"scan_null_value_for_slice_3\", Config{}),\n\t}\n\tDB.Create(&users)\n\n\tif err := DB.Model(&users[0]).Update(\"age\", nil).Error; err != nil {\n\t\tt.Fatalf(\"failed to update column age for struct, got error %v\", err)\n\t}\n\n\tvar results []User\n\tif err := DB.Find(&results, \"name like ?\", \"scan_null_value_for_slice%\").Error; err != nil {\n\t\tt.Fatalf(\"failed to query slice data with null age, got error %v\", err)\n\t}\n}\n\nfunc TestQueryWithTableAndConditions(t *testing.T) {\n\tresult := DB.Session(&gorm.Session{DryRun: true}).Table(\"user\").Find(&User{}, User{Name: \"jinzhu\"})\n\n\tif !regexp.MustCompile(`SELECT \\* FROM .user. WHERE .user.\\..name. = .+ AND .user.\\..deleted_at. IS NULL`).MatchString(result.Statement.SQL.String()) {\n\t\tt.Errorf(\"invalid query SQL, got %v\", result.Statement.SQL.String())\n\t}\n}\n\nfunc TestQueryWithTableAndConditionsAndAllFields(t *testing.T) {\n\tresult := DB.Session(&gorm.Session{DryRun: true, QueryFields: true}).Table(\"user\").Find(&User{}, User{Name: \"jinzhu\"})\n\tuserQuery := \"SELECT .*user.*id.*user.*created_at.*user.*updated_at.*user.*deleted_at.*user.*name.*user.*age\" +\n\t\t\".*user.*birthday.*user.*company_id.*user.*manager_id.*user.*active.* FROM .user. \"\n\n\tif !regexp.MustCompile(userQuery + `WHERE .user.\\..name. = .+ AND .user.\\..deleted_at. IS NULL`).MatchString(result.Statement.SQL.String()) {\n\t\tt.Errorf(\"invalid query SQL, got %v\", result.Statement.SQL.String())\n\t}\n}\n\ntype DoubleInt64 struct {\n\tdata int64\n}\n\nfunc (t *DoubleInt64) Scan(val interface{}) error {\n\tswitch v := val.(type) {\n\tcase int64:\n\t\tt.data = v * 2\n\t\treturn nil\n\tdefault:\n\t\treturn fmt.Errorf(\"DoubleInt64 cant not scan with:%v\", v)\n\t}\n}\n\n// https://github.com/go-gorm/gorm/issues/5091\nfunc TestQueryScannerWithSingleColumn(t *testing.T) {\n\tuser := User{Name: \"scanner_raw_1\", Age: 10}\n\tDB.Create(&user)\n\n\tvar result1 DoubleInt64\n\tif err := DB.Model(&User{}).Where(\"name LIKE ?\", \"scanner_raw_%\").Limit(1).Pluck(\n\t\t\"age\", &result1).Error; err != nil {\n\t\tt.Errorf(\"Failed, got error: %v\", err)\n\t}\n\n\tAssertEqual(t, result1.data, 20)\n\n\tvar result2 DoubleInt64\n\tif err := DB.Model(&User{}).Where(\"name LIKE ?\", \"scanner_raw_%\").Limit(1).Select(\n\t\t\"age\").Scan(&result2).Error; err != nil {\n\t\tt.Errorf(\"Failed, got error: %v\", err)\n\t}\n\n\tAssertEqual(t, result2.data, 20)\n}\n\nfunc TestQueryResetNullValue(t *testing.T) {\n\ttype QueryResetItem struct {\n\t\tID   string `gorm:\"type:varchar(5)\"`\n\t\tName string\n\t}\n\n\ttype QueryResetNullValue struct {\n\t\tID      int\n\t\tName    string     `gorm:\"default:NULL\"`\n\t\tFlag    bool       `gorm:\"default:NULL\"`\n\t\tNumber1 int64      `gorm:\"default:NULL\"`\n\t\tNumber2 uint64     `gorm:\"default:NULL\"`\n\t\tNumber3 float64    `gorm:\"default:NULL\"`\n\t\tNow     *time.Time `gorm:\"default:NULL\"`\n\t\tItem1Id string\n\t\tItem1   *QueryResetItem `gorm:\"references:ID\"`\n\t\tItem2Id string\n\t\tItem2   *QueryResetItem `gorm:\"references:ID\"`\n\t}\n\n\tDB.Migrator().DropTable(&QueryResetNullValue{}, &QueryResetItem{})\n\tDB.AutoMigrate(&QueryResetNullValue{}, &QueryResetItem{})\n\n\tnow := time.Now()\n\tq1 := QueryResetNullValue{\n\t\tName:    \"name\",\n\t\tFlag:    true,\n\t\tNumber1: 100,\n\t\tNumber2: 200,\n\t\tNumber3: 300.1,\n\t\tNow:     &now,\n\t\tItem1: &QueryResetItem{\n\t\t\tID:   \"u_1_1\",\n\t\t\tName: \"item_1_1\",\n\t\t},\n\t\tItem2: &QueryResetItem{\n\t\t\tID:   \"u_1_2\",\n\t\t\tName: \"item_1_2\",\n\t\t},\n\t}\n\n\tq2 := QueryResetNullValue{\n\t\tItem1: &QueryResetItem{\n\t\t\tID:   \"u_2_1\",\n\t\t\tName: \"item_2_1\",\n\t\t},\n\t\tItem2: &QueryResetItem{\n\t\t\tID:   \"u_2_2\",\n\t\t\tName: \"item_2_2\",\n\t\t},\n\t}\n\n\tvar err error\n\terr = DB.Create(&q1).Error\n\tif err != nil {\n\t\tt.Errorf(\"failed to create:%v\", err)\n\t}\n\n\terr = DB.Create(&q2).Error\n\tif err != nil {\n\t\tt.Errorf(\"failed to create:%v\", err)\n\t}\n\n\tvar qs []QueryResetNullValue\n\terr = DB.Joins(\"Item1\").Joins(\"Item2\").Find(&qs).Error\n\tif err != nil {\n\t\tt.Errorf(\"failed to find:%v\", err)\n\t}\n\n\tif len(qs) != 2 {\n\t\tt.Fatalf(\"find count not equal:%d\", len(qs))\n\t}\n\n\tAssertEqual(t, q1, qs[0])\n\tAssertEqual(t, q2, qs[1])\n}\n\nfunc TestQueryError(t *testing.T) {\n\ttype P struct{}\n\tvar p1 P\n\terr := DB.Take(&p1, 1).Error\n\tAssertEqual(t, err, gorm.ErrModelAccessibleFieldsRequired)\n\n\tvar p2 interface{}\n\n\terr = DB.Table(\"ps\").Clauses(clause.Eq{Column: clause.Column{\n\t\tTable: clause.CurrentTable, Name: clause.PrimaryKey,\n\t}, Value: 1}).Scan(&p2).Error\n\tAssertEqual(t, err, gorm.ErrModelValueRequired)\n}\n\nfunc TestQueryScanToArray(t *testing.T) {\n\terr := DB.Create(&User{Name: \"testname1\", Age: 10}).Error\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tusers := [2]*User{{Name: \"1\"}, {Name: \"2\"}}\n\terr = DB.Model(&User{}).Where(\"name = ?\", \"testname1\").Find(&users).Error\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif users[0] == nil || users[0].Name != \"testname1\" {\n\t\tt.Error(\"users[0] not covered\")\n\t}\n\tif users[1] != nil {\n\t\tt.Error(\"users[1] should be empty\")\n\t}\n}\n"
  },
  {
    "path": "tests/scan_test.go",
    "content": "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/tests\"\n)\n\ntype PersonAddressInfo struct {\n\tPerson  *Person  `gorm:\"embedded\"`\n\tAddress *Address `gorm:\"embedded\"`\n}\n\nfunc TestScan(t *testing.T) {\n\tuser1 := User{Name: \"ScanUser1\", Age: 1}\n\tuser2 := User{Name: \"ScanUser2\", Age: 10}\n\tuser3 := User{Name: \"ScanUser3\", Age: 20}\n\tDB.Save(&user1).Save(&user2).Save(&user3)\n\n\ttype result struct {\n\t\tID   uint\n\t\tName string\n\t\tAge  int\n\t}\n\n\tvar res result\n\tDB.Table(\"users\").Select(\"id, name, age\").Where(\"id = ?\", user3.ID).Scan(&res)\n\tif res.ID != user3.ID || res.Name != user3.Name || res.Age != int(user3.Age) {\n\t\tt.Fatalf(\"Scan into struct should work, got %#v, should %#v\", res, user3)\n\t}\n\n\tvar resPointer *result\n\tif err := DB.Table(\"users\").Select(\"id, name, age\").Where(\"id = ?\", user3.ID).Scan(&resPointer).Error; err != nil {\n\t\tt.Fatalf(\"Failed to query with pointer of value, got error %v\", err)\n\t} else if resPointer.ID != user3.ID || resPointer.Name != user3.Name || resPointer.Age != int(user3.Age) {\n\t\tt.Fatalf(\"Scan into struct should work, got %#v, should %#v\", res, user3)\n\t}\n\n\tDB.Table(\"users\").Select(\"id, name, age\").Where(\"id = ?\", user2.ID).Scan(&res)\n\tif res.ID != user2.ID || res.Name != user2.Name || res.Age != int(user2.Age) {\n\t\tt.Fatalf(\"Scan into struct should work, got %#v, should %#v\", res, user2)\n\t}\n\n\tDB.Model(&User{Model: gorm.Model{ID: user3.ID}}).Select(\"id, name, age\").Scan(&res)\n\tif res.ID != user3.ID || res.Name != user3.Name || res.Age != int(user3.Age) {\n\t\tt.Fatalf(\"Scan into struct should work, got %#v, should %#v\", res, user3)\n\t}\n\n\tdoubleAgeRes := &result{}\n\tif err := DB.Table(\"users\").Select(\"age + age as age\").Where(\"id = ?\", user3.ID).Scan(&doubleAgeRes).Error; err != nil {\n\t\tt.Errorf(\"Scan to pointer of pointer\")\n\t}\n\n\tif doubleAgeRes.Age != int(res.Age)*2 {\n\t\tt.Errorf(\"Scan double age as age, expect: %v, got %v\", res.Age*2, doubleAgeRes.Age)\n\t}\n\n\tvar results []result\n\tDB.Table(\"users\").Select(\"name, age\").Where(\"id in ?\", []uint{user2.ID, user3.ID}).Scan(&results)\n\n\tsort.Slice(results, func(i, j int) bool {\n\t\treturn strings.Compare(results[i].Name, results[j].Name) <= -1\n\t})\n\n\tif len(results) != 2 || results[0].Name != user2.Name || results[1].Name != user3.Name {\n\t\tt.Errorf(\"Scan into struct map, got %#v\", results)\n\t}\n\n\ttype ID uint64\n\tvar id ID\n\tDB.Raw(\"select id from users where id = ?\", user2.ID).Scan(&id)\n\tif uint(id) != user2.ID {\n\t\tt.Errorf(\"Failed to scan to customized data type\")\n\t}\n\n\tvar resInt interface{}\n\tresInt = &User{}\n\tif err := DB.Table(\"users\").Select(\"id, name, age\").Where(\"id = ?\", user3.ID).Find(&resInt).Error; err != nil {\n\t\tt.Fatalf(\"Failed to query with pointer of value, got error %v\", err)\n\t} else if resInt.(*User).ID != user3.ID || resInt.(*User).Name != user3.Name || resInt.(*User).Age != user3.Age {\n\t\tt.Fatalf(\"Scan into struct should work, got %#v, should %#v\", resInt, user3)\n\t}\n\n\tvar resInt2 interface{}\n\tresInt2 = &User{}\n\tif err := DB.Table(\"users\").Select(\"id, name, age\").Where(\"id = ?\", user3.ID).Scan(&resInt2).Error; err != nil {\n\t\tt.Fatalf(\"Failed to query with pointer of value, got error %v\", err)\n\t} else if resInt2.(*User).ID != user3.ID || resInt2.(*User).Name != user3.Name || resInt2.(*User).Age != user3.Age {\n\t\tt.Fatalf(\"Scan into struct should work, got %#v, should %#v\", resInt2, user3)\n\t}\n\n\tvar resInt3 interface{}\n\tresInt3 = []User{}\n\tif err := DB.Table(\"users\").Select(\"id, name, age\").Where(\"id = ?\", user3.ID).Find(&resInt3).Error; err != nil {\n\t\tt.Fatalf(\"Failed to query with pointer of value, got error %v\", err)\n\t} else if rus := resInt3.([]User); len(rus) == 0 || rus[0].ID != user3.ID || rus[0].Name != user3.Name || rus[0].Age != user3.Age {\n\t\tt.Fatalf(\"Scan into struct should work, got %#v, should %#v\", resInt3, user3)\n\t}\n\n\tvar resInt4 interface{}\n\tresInt4 = []User{}\n\tif err := DB.Table(\"users\").Select(\"id, name, age\").Where(\"id = ?\", user3.ID).Scan(&resInt4).Error; err != nil {\n\t\tt.Fatalf(\"Failed to query with pointer of value, got error %v\", err)\n\t} else if rus := resInt4.([]User); len(rus) == 0 || rus[0].ID != user3.ID || rus[0].Name != user3.Name || rus[0].Age != user3.Age {\n\t\tt.Fatalf(\"Scan into struct should work, got %#v, should %#v\", resInt4, user3)\n\t}\n\n\tvar resInt5 interface{}\n\tresInt5 = []User{}\n\tif err := DB.Table(\"users\").Select(\"id, name, age\").Where(\"id IN ?\", []uint{user1.ID, user2.ID, user3.ID}).Find(&resInt5).Error; err != nil {\n\t\tt.Fatalf(\"Failed to query with pointer of value, got error %v\", err)\n\t} else if rus := resInt5.([]User); len(rus) != 3 {\n\t\tt.Fatalf(\"Scan into struct should work, got %+v, len %v\", resInt5, len(rus))\n\t}\n}\n\nfunc TestScanRows(t *testing.T) {\n\tuser1 := User{Name: \"ScanRowsUser1\", Age: 1}\n\tuser2 := User{Name: \"ScanRowsUser2\", Age: 10}\n\tuser3 := User{Name: \"ScanRowsUser3\", Age: 20}\n\tDB.Save(&user1).Save(&user2).Save(&user3)\n\n\trows, err := DB.Table(\"users\").Where(\"name = ? or name = ?\", user2.Name, user3.Name).Select(\"name, age\").Rows()\n\tif err != nil {\n\t\tt.Errorf(\"No error should happen, got %v\", err)\n\t}\n\n\ttype Result struct {\n\t\tName string\n\t\tAge  int\n\t}\n\n\tvar results []Result\n\tfor rows.Next() {\n\t\tvar result Result\n\t\tif err := DB.ScanRows(rows, &result); err != nil {\n\t\t\tt.Errorf(\"should get no error, but got %v\", err)\n\t\t}\n\t\tresults = append(results, result)\n\t}\n\n\tsort.Slice(results, func(i, j int) bool {\n\t\treturn strings.Compare(results[i].Name, results[j].Name) <= -1\n\t})\n\n\tif !reflect.DeepEqual(results, []Result{{Name: \"ScanRowsUser2\", Age: 10}, {Name: \"ScanRowsUser3\", Age: 20}}) {\n\t\tt.Errorf(\"Should find expected results, got %+v\", results)\n\t}\n\n\tvar ages int\n\tif err := DB.Table(\"users\").Where(\"name = ? or name = ?\", user2.Name, user3.Name).Select(\"SUM(age)\").Scan(&ages).Error; err != nil || ages != 30 {\n\t\tt.Fatalf(\"failed to scan ages, got error %v, ages: %v\", err, ages)\n\t}\n\n\tvar name string\n\tif err := DB.Table(\"users\").Where(\"name = ?\", user2.Name).Select(\"name\").Scan(&name).Error; err != nil || name != user2.Name {\n\t\tt.Fatalf(\"failed to scan name, got error %v, name: %v\", err, name)\n\t}\n}\n\nfunc TestScanRowsNullValuesScanToFieldDefault(t *testing.T) {\n\tDB.Save(&User{})\n\n\trows, err := DB.Table(\"users\").\n\t\tSelect(`\n\t\t\tNULL AS bool_field,\n\t\t\tNULL AS int_field,\n\t\t\tNULL AS int8_field,\n\t\t\tNULL AS int16_field,\n\t\t\tNULL AS int32_field,\n\t\t\tNULL AS int64_field,\n\t\t\tNULL AS uint_field,\n\t\t\tNULL AS uint8_field,\n\t\t\tNULL AS uint16_field,\n\t\t\tNULL AS uint32_field,\n\t\t\tNULL AS uint64_field,\n\t\t\tNULL AS float32_field,\n\t\t\tNULL AS float64_field,\n\t\t\tNULL AS string_field,\n\t\t\tNULL AS time_field,\n\t\t\tNULL AS time_ptr_field,\n\t\t\tNULL AS embedded_int_field,\n\t\t\tNULL AS nested_embedded_int_field,\n\t\t\tNULL AS embedded_ptr_int_field\n        `).Rows()\n\tif err != nil {\n\t\tt.Errorf(\"No error should happen, got %v\", err)\n\t}\n\n\ttype NestedEmbeddedStruct struct {\n\t\tNestedEmbeddedIntField            int\n\t\tNestedEmbeddedIntFieldWithDefault int `gorm:\"default:2\"`\n\t}\n\n\ttype EmbeddedStruct struct {\n\t\tEmbeddedIntField     int\n\t\tNestedEmbeddedStruct `gorm:\"embedded\"`\n\t}\n\n\ttype EmbeddedPtrStruct struct {\n\t\tEmbeddedPtrIntField   int\n\t\t*NestedEmbeddedStruct `gorm:\"embedded\"`\n\t}\n\n\ttype Result struct {\n\t\tBoolField          bool\n\t\tIntField           int\n\t\tInt8Field          int8\n\t\tInt16Field         int16\n\t\tInt32Field         int32\n\t\tInt64Field         int64\n\t\tUIntField          uint\n\t\tUInt8Field         uint8\n\t\tUInt16Field        uint16\n\t\tUInt32Field        uint32\n\t\tUInt64Field        uint64\n\t\tFloat32Field       float32\n\t\tFloat64Field       float64\n\t\tStringField        string\n\t\tTimeField          time.Time\n\t\tTimePtrField       *time.Time\n\t\tEmbeddedStruct     `gorm:\"embedded\"`\n\t\t*EmbeddedPtrStruct `gorm:\"embedded\"`\n\t}\n\n\tcurrTime := time.Now()\n\treusedVar := Result{\n\t\tBoolField:         true,\n\t\tIntField:          1,\n\t\tInt8Field:         1,\n\t\tInt16Field:        1,\n\t\tInt32Field:        1,\n\t\tInt64Field:        1,\n\t\tUIntField:         1,\n\t\tUInt8Field:        1,\n\t\tUInt16Field:       1,\n\t\tUInt32Field:       1,\n\t\tUInt64Field:       1,\n\t\tFloat32Field:      1.1,\n\t\tFloat64Field:      1.1,\n\t\tStringField:       \"hello\",\n\t\tTimeField:         currTime,\n\t\tTimePtrField:      &currTime,\n\t\tEmbeddedStruct:    EmbeddedStruct{EmbeddedIntField: 1, NestedEmbeddedStruct: NestedEmbeddedStruct{NestedEmbeddedIntField: 1, NestedEmbeddedIntFieldWithDefault: 2}},\n\t\tEmbeddedPtrStruct: &EmbeddedPtrStruct{EmbeddedPtrIntField: 1, NestedEmbeddedStruct: &NestedEmbeddedStruct{NestedEmbeddedIntField: 1, NestedEmbeddedIntFieldWithDefault: 2}},\n\t}\n\n\tfor rows.Next() {\n\t\tif err := DB.ScanRows(rows, &reusedVar); err != nil {\n\t\t\tt.Errorf(\"should get no error, but got %v\", err)\n\t\t}\n\t}\n\n\tif !reflect.DeepEqual(reusedVar, Result{}) {\n\t\tt.Errorf(\"Should find zero values in struct fields, got %+v\\n\", reusedVar)\n\t}\n}\n\nfunc TestScanToEmbedded(t *testing.T) {\n\tperson1 := Person{Name: \"person 1\"}\n\tperson2 := Person{Name: \"person 2\"}\n\tDB.Save(&person1).Save(&person2)\n\n\taddress1 := Address{Name: \"address 1\"}\n\taddress2 := Address{Name: \"address 2\"}\n\tDB.Save(&address1).Save(&address2)\n\n\tDB.Create(&PersonAddress{PersonID: person1.ID, AddressID: int(address1.ID)})\n\tDB.Create(&PersonAddress{PersonID: person1.ID, AddressID: int(address2.ID)})\n\tDB.Create(&PersonAddress{PersonID: person2.ID, AddressID: int(address1.ID)})\n\n\tvar personAddressInfoList []*PersonAddressInfo\n\tif err := DB.Select(\"people.*, addresses.*\").\n\t\tTable(\"people\").\n\t\tJoins(\"inner join person_addresses on people.id = person_addresses.person_id\").\n\t\tJoins(\"inner join addresses on person_addresses.address_id = addresses.id\").\n\t\tFind(&personAddressInfoList).Error; err != nil {\n\t\tt.Errorf(\"Failed to run join query, got error: %v\", err)\n\t}\n\n\tpersonMatched := false\n\taddressMatched := false\n\n\tfor _, info := range personAddressInfoList {\n\t\tif info.Person == nil {\n\t\t\tt.Fatalf(\"Failed, expected not nil, got person nil\")\n\t\t}\n\t\tif info.Address == nil {\n\t\t\tt.Fatalf(\"Failed, expected not nil, got address nil\")\n\t\t}\n\t\tif info.Person.ID == person1.ID {\n\t\t\tpersonMatched = true\n\t\t\tif info.Person.Name != person1.Name {\n\t\t\t\tt.Errorf(\"Failed, expected %v, got %v\", person1.Name, info.Person.Name)\n\t\t\t}\n\t\t}\n\t\tif info.Address.ID == address1.ID {\n\t\t\taddressMatched = true\n\t\t\tif info.Address.Name != address1.Name {\n\t\t\t\tt.Errorf(\"Failed, expected %v, got %v\", address1.Name, info.Address.Name)\n\t\t\t}\n\t\t}\n\t}\n\n\tif !personMatched {\n\t\tt.Errorf(\"Failed, no person matched\")\n\t}\n\tif !addressMatched {\n\t\tt.Errorf(\"Failed, no address matched\")\n\t}\n\n\tpersonDupField := Person{ID: person1.ID}\n\tif err := DB.Select(\"people.id, people.*\").\n\t\tFirst(&personDupField).Error; err != nil {\n\t\tt.Errorf(\"Failed to run join query, got error: %v\", err)\n\t}\n\tAssertEqual(t, person1, personDupField)\n\n\tuser := User{\n\t\tName: \"TestScanToEmbedded_1\",\n\t\tManager: &User{\n\t\t\tName:    \"TestScanToEmbedded_1_m1\",\n\t\t\tManager: &User{Name: \"TestScanToEmbedded_1_m1_m1\"},\n\t\t},\n\t}\n\tDB.Create(&user)\n\n\ttype UserScan struct {\n\t\tID        uint\n\t\tName      string\n\t\tManagerID *uint\n\t}\n\tvar user2 UserScan\n\terr := DB.Raw(\"SELECT * FROM users INNER JOIN users Manager ON users.manager_id = Manager.id WHERE users.id = ?\", user.ID).Scan(&user2).Error\n\tAssertEqual(t, err, nil)\n}\n"
  },
  {
    "path": "tests/scanner_valuer_test.go",
    "content": "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\"reflect\"\n\t\"regexp\"\n\t\"strconv\"\n\t\"testing\"\n\t\"time\"\n\n\t\"gorm.io/gorm\"\n\t\"gorm.io/gorm/clause\"\n\t. \"gorm.io/gorm/utils/tests\"\n)\n\nfunc TestScannerValuer(t *testing.T) {\n\tDB.Migrator().DropTable(&ScannerValuerStruct{})\n\tif err := DB.Migrator().AutoMigrate(&ScannerValuerStruct{}); err != nil {\n\t\tt.Fatalf(\"no error should happen when migrate scanner, valuer struct, got error %v\", err)\n\t}\n\n\tdata := ScannerValuerStruct{\n\t\tName:     sql.NullString{String: \"name\", Valid: true},\n\t\tGender:   &sql.NullString{String: \"M\", Valid: true},\n\t\tAge:      sql.NullInt64{Int64: 18, Valid: true},\n\t\tMale:     sql.NullBool{Bool: true, Valid: true},\n\t\tHeight:   sql.NullFloat64{Float64: 1.8888, Valid: true},\n\t\tBirthday: sql.NullTime{Time: time.Now(), Valid: true},\n\t\tAllergen: NullString{sql.NullString{String: \"Allergen\", Valid: true}},\n\t\tPassword: EncryptedData(\"pass1\"),\n\t\tBytes:    []byte(\"byte\"),\n\t\tNum:      18,\n\t\tStrings:  StringsSlice{\"a\", \"b\", \"c\"},\n\t\tStructs: StructsSlice{\n\t\t\t{\"name1\", \"value1\"},\n\t\t\t{\"name2\", \"value2\"},\n\t\t},\n\t\tRole:             Role{Name: \"admin\"},\n\t\tExampleStruct:    ExampleStruct{\"name\", \"value1\"},\n\t\tExampleStructPtr: &ExampleStruct{\"name\", \"value2\"},\n\t}\n\n\tif err := DB.Create(&data).Error; err != nil {\n\t\tt.Fatalf(\"No error should happened when create scanner valuer struct, but got %v\", err)\n\t}\n\n\tvar result ScannerValuerStruct\n\n\tif err := DB.Find(&result, \"id = ?\", data.ID).Error; err != nil {\n\t\tt.Fatalf(\"no error should happen when query scanner, valuer struct, but got %v\", err)\n\t}\n\n\tif result.ExampleStructPtr.Val != \"value2\" {\n\t\tt.Errorf(`ExampleStructPtr.Val should equal to \"value2\", but got %v`, result.ExampleStructPtr.Val)\n\t}\n\n\tif result.ExampleStruct.Val != \"value1\" {\n\t\tt.Errorf(`ExampleStruct.Val should equal to \"value1\", but got %#v`, result.ExampleStruct)\n\t}\n\tAssertObjEqual(t, data, result, \"Name\", \"Gender\", \"Age\", \"Male\", \"Height\", \"Birthday\", \"Password\", \"Bytes\", \"Num\", \"Strings\", \"Structs\")\n}\n\nfunc TestScannerValuerWithFirstOrCreate(t *testing.T) {\n\tDB.Migrator().DropTable(&ScannerValuerStruct{})\n\tif err := DB.Migrator().AutoMigrate(&ScannerValuerStruct{}); err != nil {\n\t\tt.Errorf(\"no error should happen when migrate scanner, valuer struct\")\n\t}\n\n\tdata := ScannerValuerStruct{\n\t\tName:             sql.NullString{String: \"name\", Valid: true},\n\t\tGender:           &sql.NullString{String: \"M\", Valid: true},\n\t\tAge:              sql.NullInt64{Int64: 18, Valid: true},\n\t\tExampleStruct:    ExampleStruct{\"name\", \"value1\"},\n\t\tExampleStructPtr: &ExampleStruct{\"name\", \"value2\"},\n\t}\n\n\tvar result ScannerValuerStruct\n\ttx := DB.Where(data).FirstOrCreate(&result)\n\n\tif tx.RowsAffected != 1 {\n\t\tt.Errorf(\"RowsAffected should be 1 after create some record\")\n\t}\n\n\tif tx.Error != nil {\n\t\tt.Errorf(\"Should not raise any error, but got %v\", tx.Error)\n\t}\n\n\tAssertObjEqual(t, result, data, \"Name\", \"Gender\", \"Age\")\n\n\tif err := DB.Where(data).Assign(ScannerValuerStruct{Age: sql.NullInt64{Int64: 18, Valid: true}}).FirstOrCreate(&result).Error; err != nil {\n\t\tt.Errorf(\"Should not raise any error, but got %v\", err)\n\t}\n\n\tif result.Age.Int64 != 18 {\n\t\tt.Errorf(\"should update age to 18\")\n\t}\n\n\tvar result2 ScannerValuerStruct\n\tif err := DB.First(&result2, result.ID).Error; err != nil {\n\t\tt.Errorf(\"got error %v when query with %v\", err, result.ID)\n\t}\n\n\tAssertObjEqual(t, result2, result, \"ID\", \"CreatedAt\", \"UpdatedAt\", \"Name\", \"Gender\", \"Age\")\n}\n\nfunc TestInvalidValuer(t *testing.T) {\n\tDB.Migrator().DropTable(&ScannerValuerStruct{})\n\tif err := DB.Migrator().AutoMigrate(&ScannerValuerStruct{}); err != nil {\n\t\tt.Errorf(\"no error should happen when migrate scanner, valuer struct\")\n\t}\n\n\tdata := ScannerValuerStruct{\n\t\tPassword:         EncryptedData(\"xpass1\"),\n\t\tExampleStruct:    ExampleStruct{\"name\", \"value1\"},\n\t\tExampleStructPtr: &ExampleStruct{\"name\", \"value2\"},\n\t}\n\n\tif err := DB.Create(&data).Error; err == nil {\n\t\tt.Errorf(\"Should failed to create data with invalid data\")\n\t}\n\n\tdata.Password = EncryptedData(\"pass1\")\n\tif err := DB.Create(&data).Error; err != nil {\n\t\tt.Errorf(\"Should got no error when creating data, but got %v\", err)\n\t}\n\n\tif err := DB.Model(&data).Update(\"password\", EncryptedData(\"xnewpass\")).Error; err == nil {\n\t\tt.Errorf(\"Should failed to update data with invalid data\")\n\t}\n\n\tif err := DB.Model(&data).Update(\"password\", EncryptedData(\"newpass\")).Error; err != nil {\n\t\tt.Errorf(\"Should got no error update data with valid data, but got %v\", err)\n\t}\n\n\tAssertEqual(t, data.Password, EncryptedData(\"newpass\"))\n}\n\ntype ScannerValuerStruct struct {\n\tgorm.Model\n\tName             sql.NullString\n\tGender           *sql.NullString\n\tAge              sql.NullInt64\n\tMale             sql.NullBool\n\tHeight           sql.NullFloat64\n\tBirthday         sql.NullTime\n\tAllergen         NullString\n\tPassword         EncryptedData\n\tBytes            []byte\n\tNum              Num\n\tStrings          StringsSlice\n\tStructs          StructsSlice\n\tRole             Role\n\tUserID           *sql.NullInt64\n\tUser             User\n\tEmptyTime        EmptyTime\n\tExampleStruct    ExampleStruct\n\tExampleStructPtr *ExampleStruct\n}\n\ntype EncryptedData []byte\n\nfunc (data *EncryptedData) Scan(value interface{}) error {\n\tif b, ok := value.([]byte); ok {\n\t\tif len(b) < 3 || b[0] != '*' || b[1] != '*' || b[2] != '*' {\n\t\t\treturn errors.New(\"Too short\")\n\t\t}\n\n\t\t*data = append((*data)[0:], b[3:]...)\n\t\treturn nil\n\t} else if s, ok := value.(string); ok {\n\t\t*data = []byte(s[3:])\n\t\treturn nil\n\t}\n\n\treturn errors.New(\"Bytes expected\")\n}\n\nfunc (data EncryptedData) Value() (driver.Value, error) {\n\tif len(data) > 0 && data[0] == 'x' {\n\t\t// needed to test failures\n\t\treturn nil, errors.New(\"Should not start with 'x'\")\n\t}\n\n\t// prepend asterisks\n\treturn append([]byte(\"***\"), data...), nil\n}\n\ntype Num int64\n\nfunc (i *Num) Scan(src interface{}) error {\n\tswitch s := src.(type) {\n\tcase []byte:\n\t\tn, _ := strconv.Atoi(string(s))\n\t\t*i = Num(n)\n\tcase int64:\n\t\t*i = Num(s)\n\tdefault:\n\t\treturn errors.New(\"Cannot scan NamedInt from \" + reflect.ValueOf(src).String())\n\t}\n\treturn nil\n}\n\ntype StringsSlice []string\n\nfunc (l StringsSlice) Value() (driver.Value, error) {\n\tbytes, err := json.Marshal(l)\n\treturn string(bytes), err\n}\n\nfunc (l *StringsSlice) Scan(input interface{}) error {\n\tswitch value := input.(type) {\n\tcase string:\n\t\treturn json.Unmarshal([]byte(value), l)\n\tcase []byte:\n\t\treturn json.Unmarshal(value, l)\n\tdefault:\n\t\treturn errors.New(\"not supported\")\n\t}\n}\n\ntype ExampleStruct struct {\n\tName string\n\tVal  string\n}\n\nfunc (ExampleStruct) GormDataType() string {\n\treturn \"bytes\"\n}\n\nfunc (s ExampleStruct) Value() (driver.Value, error) {\n\tif len(s.Name) == 0 {\n\t\treturn nil, nil\n\t}\n\t// for test, has no practical meaning\n\ts.Name = \"\"\n\treturn json.Marshal(s)\n}\n\nfunc (s *ExampleStruct) Scan(src interface{}) error {\n\tswitch value := src.(type) {\n\tcase string:\n\t\treturn json.Unmarshal([]byte(value), s)\n\tcase []byte:\n\t\treturn json.Unmarshal(value, s)\n\tdefault:\n\t\treturn errors.New(\"not supported\")\n\t}\n}\n\ntype StructsSlice []ExampleStruct\n\nfunc (l StructsSlice) Value() (driver.Value, error) {\n\tbytes, err := json.Marshal(l)\n\treturn string(bytes), err\n}\n\nfunc (l *StructsSlice) Scan(input interface{}) error {\n\tswitch value := input.(type) {\n\tcase string:\n\t\treturn json.Unmarshal([]byte(value), l)\n\tcase []byte:\n\t\treturn json.Unmarshal(value, l)\n\tdefault:\n\t\treturn errors.New(\"not supported\")\n\t}\n}\n\ntype Role struct {\n\tName string `gorm:\"size:256\"`\n}\n\nfunc (role *Role) Scan(value interface{}) error {\n\tif b, ok := value.([]uint8); ok {\n\t\trole.Name = string(b)\n\t} else {\n\t\trole.Name = value.(string)\n\t}\n\treturn nil\n}\n\nfunc (role Role) Value() (driver.Value, error) {\n\treturn role.Name, nil\n}\n\nfunc (role Role) IsAdmin() bool {\n\treturn role.Name == \"admin\"\n}\n\ntype EmptyTime struct {\n\ttime.Time\n}\n\nfunc (t *EmptyTime) Scan(v interface{}) error {\n\tnullTime := sql.NullTime{}\n\terr := nullTime.Scan(v)\n\tt.Time = nullTime.Time\n\treturn err\n}\n\nfunc (t EmptyTime) Value() (driver.Value, error) {\n\treturn time.Now() /* pass tests, mysql 8 doesn't support 0000-00-00 by default */, nil\n}\n\ntype NullString struct {\n\tsql.NullString\n}\n\ntype Point struct {\n\tX, Y int\n}\n\nfunc (point Point) GormDataType() string {\n\treturn \"geo\"\n}\n\nfunc (point Point) GormValue(ctx context.Context, db *gorm.DB) clause.Expr {\n\treturn clause.Expr{\n\t\tSQL:  \"ST_PointFromText(?)\",\n\t\tVars: []interface{}{fmt.Sprintf(\"POINT(%d %d)\", point.X, point.Y)},\n\t}\n}\n\nfunc TestGORMValuer(t *testing.T) {\n\ttype UserWithPoint struct {\n\t\tName  string\n\t\tPoint Point\n\t}\n\n\tdryRunDB := DB.Session(&gorm.Session{DryRun: true})\n\n\tstmt := dryRunDB.Create(&UserWithPoint{\n\t\tName:  \"jinzhu\",\n\t\tPoint: Point{X: 100, Y: 100},\n\t}).Statement\n\n\tif stmt.SQL.String() == \"\" || len(stmt.Vars) != 2 {\n\t\tt.Errorf(\"Failed to generate sql, got %v\", stmt.SQL.String())\n\t}\n\n\tif !regexp.MustCompile(`INSERT INTO .user_with_points. \\(.name.,.point.\\) VALUES \\(.+,ST_PointFromText\\(.+\\)\\)`).MatchString(stmt.SQL.String()) {\n\t\tt.Errorf(\"insert with sql.Expr, but got %v\", stmt.SQL.String())\n\t}\n\n\tif !reflect.DeepEqual([]interface{}{\"jinzhu\", \"POINT(100 100)\"}, stmt.Vars) {\n\t\tt.Errorf(\"generated vars is not equal, got %v\", stmt.Vars)\n\t}\n\n\tstmt = dryRunDB.Model(UserWithPoint{}).Create(map[string]interface{}{\n\t\t\"Name\":  \"jinzhu\",\n\t\t\"Point\": clause.Expr{SQL: \"ST_PointFromText(?)\", Vars: []interface{}{\"POINT(100 100)\"}},\n\t}).Statement\n\n\tif !regexp.MustCompile(`INSERT INTO .user_with_points. \\(.name.,.point.\\) VALUES \\(.+,ST_PointFromText\\(.+\\)\\)`).MatchString(stmt.SQL.String()) {\n\t\tt.Errorf(\"insert with sql.Expr, but got %v\", stmt.SQL.String())\n\t}\n\n\tif !reflect.DeepEqual([]interface{}{\"jinzhu\", \"POINT(100 100)\"}, stmt.Vars) {\n\t\tt.Errorf(\"generated vars is not equal, got %v\", stmt.Vars)\n\t}\n\n\tstmt = dryRunDB.Table(\"user_with_points\").Create(&map[string]interface{}{\n\t\t\"Name\":  \"jinzhu\",\n\t\t\"Point\": clause.Expr{SQL: \"ST_PointFromText(?)\", Vars: []interface{}{\"POINT(100 100)\"}},\n\t}).Statement\n\n\tif !regexp.MustCompile(`INSERT INTO .user_with_points. \\(.Name.,.Point.\\) VALUES \\(.+,ST_PointFromText\\(.+\\)\\)`).MatchString(stmt.SQL.String()) {\n\t\tt.Errorf(\"insert with sql.Expr, but got %v\", stmt.SQL.String())\n\t}\n\n\tif !reflect.DeepEqual([]interface{}{\"jinzhu\", \"POINT(100 100)\"}, stmt.Vars) {\n\t\tt.Errorf(\"generated vars is not equal, got %v\", stmt.Vars)\n\t}\n\n\tstmt = dryRunDB.Session(&gorm.Session{\n\t\tAllowGlobalUpdate: true,\n\t}).Model(&UserWithPoint{}).Updates(UserWithPoint{\n\t\tName:  \"jinzhu\",\n\t\tPoint: Point{X: 100, Y: 100},\n\t}).Statement\n\n\tif !regexp.MustCompile(`UPDATE .user_with_points. SET .name.=.+,.point.=ST_PointFromText\\(.+\\)`).MatchString(stmt.SQL.String()) {\n\t\tt.Errorf(\"update with sql.Expr, but got %v\", stmt.SQL.String())\n\t}\n\n\tif !reflect.DeepEqual([]interface{}{\"jinzhu\", \"POINT(100 100)\"}, stmt.Vars) {\n\t\tt.Errorf(\"generated vars is not equal, got %v\", stmt.Vars)\n\t}\n}\n"
  },
  {
    "path": "tests/scopes_test.go",
    "content": "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 *gorm.DB) *gorm.DB {\n\treturn d.Where(\"name in (?)\", []string{\"ScopeUser1\", \"ScopeUser2\"})\n}\n\nfunc NameIn2And3(d *gorm.DB) *gorm.DB {\n\treturn d.Where(\"name in (?)\", []string{\"ScopeUser2\", \"ScopeUser3\"})\n}\n\nfunc NameIn(names []string) func(d *gorm.DB) *gorm.DB {\n\treturn func(d *gorm.DB) *gorm.DB {\n\t\treturn d.Where(\"name in (?)\", names)\n\t}\n}\n\nfunc TestScopes(t *testing.T) {\n\tusers := []*User{\n\t\tGetUser(\"ScopeUser1\", Config{}),\n\t\tGetUser(\"ScopeUser2\", Config{}),\n\t\tGetUser(\"ScopeUser3\", Config{}),\n\t}\n\n\tDB.Create(&users)\n\n\tvar users1, users2, users3 []User\n\tDB.Scopes(NameIn1And2).Find(&users1)\n\tif len(users1) != 2 {\n\t\tt.Errorf(\"Should found two users's name in 1, 2, but got %v\", len(users1))\n\t}\n\n\tDB.Scopes(NameIn1And2, NameIn2And3).Find(&users2)\n\tif len(users2) != 1 {\n\t\tt.Errorf(\"Should found one user's name is 2, but got %v\", len(users2))\n\t}\n\n\tDB.Scopes(NameIn([]string{users[0].Name, users[2].Name})).Find(&users3)\n\tif len(users3) != 2 {\n\t\tt.Errorf(\"Should found two users's name in 1, 3, but got %v\", len(users3))\n\t}\n\n\tdb := DB.Scopes(func(tx *gorm.DB) *gorm.DB {\n\t\treturn tx.Table(\"custom_table\")\n\t}).Session(&gorm.Session{})\n\n\tdb.AutoMigrate(&User{})\n\tif db.Find(&User{}).Statement.Table != \"custom_table\" {\n\t\tt.Errorf(\"failed to call Scopes\")\n\t}\n\n\tresult := DB.Scopes(NameIn1And2, func(tx *gorm.DB) *gorm.DB {\n\t\treturn tx.Session(&gorm.Session{})\n\t}).Find(&users1)\n\n\tif result.RowsAffected != 2 {\n\t\tt.Errorf(\"Should found two users's name in 1, 2, but got %v\", result.RowsAffected)\n\t}\n\n\tvar maxId int64\n\tuserTable := func(db *gorm.DB) *gorm.DB {\n\t\treturn db.WithContext(context.Background()).Table(\"users\")\n\t}\n\tif err := DB.Scopes(userTable).Select(\"max(id)\").Scan(&maxId).Error; err != nil {\n\t\tt.Errorf(\"select max(id)\")\n\t}\n}\n\nfunc TestComplexScopes(t *testing.T) {\n\ttests := []struct {\n\t\tname     string\n\t\tqueryFn  func(tx *gorm.DB) *gorm.DB\n\t\texpected string\n\t}{\n\t\t{\n\t\t\tname: \"depth_1\",\n\t\t\tqueryFn: func(tx *gorm.DB) *gorm.DB {\n\t\t\t\treturn tx.Scopes(\n\t\t\t\t\tfunc(d *gorm.DB) *gorm.DB { return d.Where(\"a = 1\") },\n\t\t\t\t\tfunc(d *gorm.DB) *gorm.DB {\n\t\t\t\t\t\treturn d.Where(DB.Or(\"b = 2\").Or(\"c = 3\"))\n\t\t\t\t\t},\n\t\t\t\t).Find(&Language{})\n\t\t\t},\n\t\t\texpected: `SELECT * FROM \"languages\" WHERE a = 1 AND (b = 2 OR c = 3)`,\n\t\t}, {\n\t\t\tname: \"depth_1_pre_cond\",\n\t\t\tqueryFn: func(tx *gorm.DB) *gorm.DB {\n\t\t\t\treturn tx.Where(\"z = 0\").Scopes(\n\t\t\t\t\tfunc(d *gorm.DB) *gorm.DB { return d.Where(\"a = 1\") },\n\t\t\t\t\tfunc(d *gorm.DB) *gorm.DB {\n\t\t\t\t\t\treturn d.Or(DB.Where(\"b = 2\").Or(\"c = 3\"))\n\t\t\t\t\t},\n\t\t\t\t).Find(&Language{})\n\t\t\t},\n\t\t\texpected: `SELECT * FROM \"languages\" WHERE z = 0 AND a = 1 OR (b = 2 OR c = 3)`,\n\t\t}, {\n\t\t\tname: \"depth_2\",\n\t\t\tqueryFn: func(tx *gorm.DB) *gorm.DB {\n\t\t\t\treturn tx.Scopes(\n\t\t\t\t\tfunc(d *gorm.DB) *gorm.DB { return d.Model(&Language{}) },\n\t\t\t\t\tfunc(d *gorm.DB) *gorm.DB {\n\t\t\t\t\t\treturn d.\n\t\t\t\t\t\t\tOr(DB.Scopes(\n\t\t\t\t\t\t\t\tfunc(d *gorm.DB) *gorm.DB { return d.Where(\"a = 1\") },\n\t\t\t\t\t\t\t\tfunc(d *gorm.DB) *gorm.DB { return d.Where(\"b = 2\") },\n\t\t\t\t\t\t\t)).\n\t\t\t\t\t\t\tOr(\"c = 3\")\n\t\t\t\t\t},\n\t\t\t\t\tfunc(d *gorm.DB) *gorm.DB { return d.Where(\"d = 4\") },\n\t\t\t\t).Find(&Language{})\n\t\t\t},\n\t\t\texpected: `SELECT * FROM \"languages\" WHERE d = 4 OR c = 3 OR (a = 1 AND b = 2)`,\n\t\t},\n\t}\n\n\tfor _, test := range tests {\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tassertEqualSQL(t, test.expected, DB.ToSQL(test.queryFn))\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "tests/serializer_test.go",
    "content": "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.io/gorm/schema\"\n\t. \"gorm.io/gorm/utils/tests\"\n)\n\ntype SerializerStruct struct {\n\tgorm.Model\n\tName                   []byte                 `gorm:\"json\"`\n\tRoles                  Roles                  `gorm:\"serializer:json\"`\n\tRoles2                 *Roles                 `gorm:\"serializer:json\"`\n\tRoles3                 *Roles                 `gorm:\"serializer:json;not null\"`\n\tContracts              map[string]interface{} `gorm:\"serializer:json\"`\n\tJobInfo                Job                    `gorm:\"type:bytes;serializer:gob\"`\n\tCreatedTime            int64                  `gorm:\"serializer:unixtime;type:datetime\"` // store time in db, use int as field type\n\tUpdatedTime            *int64                 `gorm:\"serializer:unixtime;type:datetime\"` // store time in db, use int as field type\n\tCustomSerializerString string                 `gorm:\"serializer:custom\"`\n\tEncryptedString        EncryptedString\n}\n\ntype SerializerPostgresStruct struct {\n\tgorm.Model\n\tName                   []byte                 `gorm:\"json\"`\n\tRoles                  Roles                  `gorm:\"serializer:json\"`\n\tRoles2                 *Roles                 `gorm:\"serializer:json\"`\n\tRoles3                 *Roles                 `gorm:\"serializer:json;not null\"`\n\tContracts              map[string]interface{} `gorm:\"serializer:json\"`\n\tJobInfo                Job                    `gorm:\"type:bytes;serializer:gob\"`\n\tCreatedTime            int64                  `gorm:\"serializer:unixtime;type:timestamptz\"` // store time in db, use int as field type\n\tUpdatedTime            *int64                 `gorm:\"serializer:unixtime;type:timestamptz\"` // store time in db, use int as field type\n\tCustomSerializerString string                 `gorm:\"serializer:custom\"`\n\tEncryptedString        EncryptedString\n}\n\nfunc (*SerializerPostgresStruct) TableName() string { return \"serializer_structs\" }\n\nfunc adaptorSerializerModel(s *SerializerStruct) interface{} {\n\tif DB.Dialector.Name() == \"postgres\" || DB.Dialector.Name() == \"gaussdb\" {\n\t\tsps := SerializerPostgresStruct(*s)\n\t\treturn &sps\n\t}\n\treturn s\n}\n\ntype Roles []string\n\ntype Job struct {\n\tTitle    string\n\tNumber   int\n\tLocation string\n\tIsIntern bool\n}\n\ntype EncryptedString string\n\nfunc (es *EncryptedString) Scan(ctx context.Context, field *schema.Field, dst reflect.Value, dbValue interface{}) (err error) {\n\tswitch value := dbValue.(type) {\n\tcase []byte:\n\t\t*es = EncryptedString(bytes.TrimPrefix(value, []byte(\"hello\")))\n\tcase string:\n\t\t*es = EncryptedString(strings.TrimPrefix(value, \"hello\"))\n\tdefault:\n\t\treturn fmt.Errorf(\"unsupported data %#v\", dbValue)\n\t}\n\treturn nil\n}\n\nfunc (es EncryptedString) Value(ctx context.Context, field *schema.Field, dst reflect.Value, fieldValue interface{}) (interface{}, error) {\n\treturn \"hello\" + string(es), nil\n}\n\ntype CustomSerializer struct {\n\tprefix []byte\n}\n\nfunc NewCustomSerializer(prefix string) *CustomSerializer {\n\treturn &CustomSerializer{prefix: []byte(prefix)}\n}\n\nfunc (c *CustomSerializer) Scan(ctx context.Context, field *schema.Field, dst reflect.Value, dbValue interface{}) (err error) {\n\tswitch value := dbValue.(type) {\n\tcase []byte:\n\t\terr = field.Set(ctx, dst, bytes.TrimPrefix(value, c.prefix))\n\tcase string:\n\t\terr = field.Set(ctx, dst, strings.TrimPrefix(value, string(c.prefix)))\n\tdefault:\n\t\terr = fmt.Errorf(\"unsupported data %#v\", dbValue)\n\t}\n\treturn err\n}\n\nfunc (c *CustomSerializer) Value(ctx context.Context, field *schema.Field, dst reflect.Value, fieldValue interface{}) (interface{}, error) {\n\treturn fmt.Sprintf(\"%s%s\", c.prefix, fieldValue), nil\n}\n\nfunc TestSerializer(t *testing.T) {\n\tschema.RegisterSerializer(\"custom\", NewCustomSerializer(\"hello\"))\n\tDB.Migrator().DropTable(adaptorSerializerModel(&SerializerStruct{}))\n\tif err := DB.Migrator().AutoMigrate(adaptorSerializerModel(&SerializerStruct{})); err != nil {\n\t\tt.Fatalf(\"no error should happen when migrate scanner, valuer struct, got error %v\", err)\n\t}\n\n\tcreatedAt := time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC)\n\tupdatedAt := createdAt.Unix()\n\n\tdata := SerializerStruct{\n\t\tName:            []byte(\"jinzhu\"),\n\t\tRoles:           []string{\"r1\", \"r2\"},\n\t\tContracts:       map[string]interface{}{\"name\": \"jinzhu\", \"age\": 10},\n\t\tEncryptedString: EncryptedString(\"pass\"),\n\t\tCreatedTime:     createdAt.Unix(),\n\t\tUpdatedTime:     &updatedAt,\n\t\tJobInfo: Job{\n\t\t\tTitle:    \"programmer\",\n\t\t\tNumber:   9920,\n\t\t\tLocation: \"Kenmawr\",\n\t\t\tIsIntern: false,\n\t\t},\n\t\tCustomSerializerString: \"world\",\n\t}\n\n\tif err := DB.Create(&data).Error; err != nil {\n\t\tt.Fatalf(\"failed to create data, got error %v\", err)\n\t}\n\n\tvar result SerializerStruct\n\tif err := DB.Where(\"roles2 IS NULL AND roles3 = ?\", \"\").First(&result, data.ID).Error; err != nil {\n\t\tt.Fatalf(\"failed to query data, got error %v\", err)\n\t}\n\n\tAssertEqual(t, result, data)\n\n\tif err := DB.Model(&result).Update(\"roles\", \"\").Error; err != nil {\n\t\tt.Fatalf(\"failed to update data's roles, got error %v\", err)\n\t}\n\n\tif err := DB.First(&result, data.ID).Error; err != nil {\n\t\tt.Fatalf(\"failed to query data, got error %v\", err)\n\t}\n}\n\nfunc TestSerializerZeroValue(t *testing.T) {\n\tschema.RegisterSerializer(\"custom\", NewCustomSerializer(\"hello\"))\n\tDB.Migrator().DropTable(adaptorSerializerModel(&SerializerStruct{}))\n\tif err := DB.Migrator().AutoMigrate(adaptorSerializerModel(&SerializerStruct{})); err != nil {\n\t\tt.Fatalf(\"no error should happen when migrate scanner, valuer struct, got error %v\", err)\n\t}\n\n\tdata := SerializerStruct{}\n\n\tif err := DB.Create(&data).Error; err != nil {\n\t\tt.Fatalf(\"failed to create data, got error %v\", err)\n\t}\n\n\tvar result SerializerStruct\n\tif err := DB.First(&result, data.ID).Error; err != nil {\n\t\tt.Fatalf(\"failed to query data, got error %v\", err)\n\t}\n\n\tAssertEqual(t, result, data)\n\n\tif err := DB.Model(&result).Update(\"roles\", \"\").Error; err != nil {\n\t\tt.Fatalf(\"failed to update data's roles, got error %v\", err)\n\t}\n\n\tif err := DB.First(&result, data.ID).Error; err != nil {\n\t\tt.Fatalf(\"failed to query data, got error %v\", err)\n\t}\n}\n\nfunc TestSerializerAssignFirstOrCreate(t *testing.T) {\n\tschema.RegisterSerializer(\"custom\", NewCustomSerializer(\"hello\"))\n\tDB.Migrator().DropTable(adaptorSerializerModel(&SerializerStruct{}))\n\tif err := DB.Migrator().AutoMigrate(adaptorSerializerModel(&SerializerStruct{})); err != nil {\n\t\tt.Fatalf(\"no error should happen when migrate scanner, valuer struct, got error %v\", err)\n\t}\n\n\tcreatedAt := time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC)\n\n\tdata := SerializerStruct{\n\t\tName:            []byte(\"ag9920\"),\n\t\tRoles:           []string{\"r1\", \"r2\"},\n\t\tContracts:       map[string]interface{}{\"name\": \"jing1\", \"age\": 11},\n\t\tEncryptedString: EncryptedString(\"pass\"),\n\t\tCreatedTime:     createdAt.Unix(),\n\t\tJobInfo: Job{\n\t\t\tTitle:    \"programmer\",\n\t\t\tNumber:   9920,\n\t\t\tLocation: \"Shadyside\",\n\t\t\tIsIntern: false,\n\t\t},\n\t\tCustomSerializerString: \"world\",\n\t}\n\n\t// first time insert record\n\tout := SerializerStruct{}\n\tif err := DB.Assign(data).FirstOrCreate(&out).Error; err != nil {\n\t\tt.Fatalf(\"failed to FirstOrCreate Assigned data, got error %v\", err)\n\t}\n\n\tvar result SerializerStruct\n\tif err := DB.First(&result, out.ID).Error; err != nil {\n\t\tt.Fatalf(\"failed to query data, got error %v\", err)\n\t}\n\tAssertEqual(t, result, out)\n\n\t// update record\n\tdata.Roles = append(data.Roles, \"r3\")\n\tdata.JobInfo.Location = \"Gates Hillman Complex\"\n\tif err := DB.Assign(data).FirstOrCreate(&out).Error; err != nil {\n\t\tt.Fatalf(\"failed to FirstOrCreate Assigned data, got error %v\", err)\n\t}\n\tif err := DB.First(&result, out.ID).Error; err != nil {\n\t\tt.Fatalf(\"failed to query data, got error %v\", err)\n\t}\n\n\tAssertEqual(t, result.Roles, data.Roles)\n\tAssertEqual(t, result.JobInfo.Location, data.JobInfo.Location)\n}\n\n// Test for: panic when serializer field with any type is nil\nfunc TestSerializerWithAnyType(t *testing.T) {\n\ttype ProductWithAny struct {\n\t\tgorm.Model\n\t\tName string\n\t\tData any `gorm:\"serializer:json\"`\n\t}\n\n\tDB.Migrator().DropTable(&ProductWithAny{})\n\tif err := DB.AutoMigrate(&ProductWithAny{}); err != nil {\n\t\tt.Fatalf(\"failed to migrate ProductWithAny, got error %v\", err)\n\t}\n\n\t// Test creating record with nil any field\n\tproduct := ProductWithAny{Name: \"Product 1\"}\n\tif err := DB.Create(&product).Error; err != nil {\n\t\tt.Fatalf(\"failed to create product with nil any field, got error %v\", err)\n\t}\n\n\t// Test updating/saving record with nil any field (should not panic)\n\tproduct.Name = \"Product 1 (Updated)\"\n\tif err := DB.Save(&product).Error; err != nil {\n\t\tt.Fatalf(\"failed to save product with nil any field, got error %v\", err)\n\t}\n\n\t// Verify the record was saved correctly\n\tvar result ProductWithAny\n\tif err := DB.First(&result, product.ID).Error; err != nil {\n\t\tt.Fatalf(\"failed to query product, got error %v\", err)\n\t}\n\n\tif result.Name != \"Product 1 (Updated)\" {\n\t\tt.Errorf(\"expected name to be 'Product 1 (Updated)', got %s\", result.Name)\n\t}\n\n\tif result.Data != nil {\n\t\tt.Errorf(\"expected Data to be nil, got %v\", result.Data)\n\t}\n\n\t// Test with non-nil value\n\tdataValue := map[string]interface{}{\"key\": \"value\"}\n\tproduct2 := ProductWithAny{Name: \"Product 2\", Data: dataValue}\n\tif err := DB.Create(&product2).Error; err != nil {\n\t\tt.Fatalf(\"failed to create product with non-nil any field, got error %v\", err)\n\t}\n\n\tvar result2 ProductWithAny\n\tif err := DB.First(&result2, product2.ID).Error; err != nil {\n\t\tt.Fatalf(\"failed to query product2, got error %v\", err)\n\t}\n\n\tif result2.Data == nil {\n\t\tt.Error(\"expected Data to be non-nil\")\n\t}\n}\n"
  },
  {
    "path": "tests/soft_delete_test.go",
    "content": "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\"gorm.io/gorm\"\n\t. \"gorm.io/gorm/utils/tests\"\n)\n\nfunc TestSoftDelete(t *testing.T) {\n\tuser := *GetUser(\"SoftDelete\", Config{})\n\tDB.Save(&user)\n\n\tvar count int64\n\tvar age uint\n\n\tif DB.Model(&User{}).Where(\"name = ?\", user.Name).Count(&count).Error != nil || count != 1 {\n\t\tt.Errorf(\"Count soft deleted record, expects: %v, got: %v\", 1, count)\n\t}\n\n\tif DB.Model(&User{}).Select(\"age\").Where(\"name = ?\", user.Name).Scan(&age).Error != nil || age != user.Age {\n\t\tt.Errorf(\"Age soft deleted record, expects: %v, got: %v\", 0, age)\n\t}\n\n\tif err := DB.Delete(&user).Error; err != nil {\n\t\tt.Fatalf(\"No error should happen when soft delete user, but got %v\", err)\n\t}\n\n\tif sql.NullTime(user.DeletedAt).Time.IsZero() {\n\t\tt.Fatalf(\"user's deleted at is zero\")\n\t}\n\n\tsql := DB.Session(&gorm.Session{DryRun: true}).Delete(&user).Statement.SQL.String()\n\tif !regexp.MustCompile(`UPDATE .users. SET .deleted_at.=.* WHERE .users.\\..id. = .* AND .users.\\..deleted_at. IS NULL`).MatchString(sql) {\n\t\tt.Fatalf(\"invalid sql generated, got %v\", sql)\n\t}\n\n\tsql = DB.Session(&gorm.Session{DryRun: true}).Table(\"user u\").Select(\"name\").Find(&User{}).Statement.SQL.String()\n\tif !regexp.MustCompile(`SELECT .name. FROM user u WHERE .u.\\..deleted_at. IS NULL`).MatchString(sql) {\n\t\tt.Errorf(\"Table with escape character, got %v\", sql)\n\t}\n\n\tif DB.First(&User{}, \"name = ?\", user.Name).Error == nil {\n\t\tt.Errorf(\"Can't find a soft deleted record\")\n\t}\n\n\tcount = 0\n\tif DB.Model(&User{}).Where(\"name = ?\", user.Name).Count(&count).Error != nil || count != 0 {\n\t\tt.Errorf(\"Count soft deleted record, expects: %v, got: %v\", 0, count)\n\t}\n\n\tage = 0\n\tif DB.Model(&User{}).Select(\"age\").Where(\"name = ?\", user.Name).Scan(&age).Error != nil || age != 0 {\n\t\tt.Errorf(\"Age soft deleted record, expects: %v, got: %v\", 0, age)\n\t}\n\n\tif err := DB.Unscoped().First(&User{}, \"name = ?\", user.Name).Error; err != nil {\n\t\tt.Errorf(\"Should find soft deleted record with Unscoped, but got err %s\", err)\n\t}\n\n\tcount = 0\n\tif DB.Unscoped().Model(&User{}).Where(\"name = ?\", user.Name).Count(&count).Error != nil || count != 1 {\n\t\tt.Errorf(\"Count soft deleted record, expects: %v, count: %v\", 1, count)\n\t}\n\n\tage = 0\n\tif DB.Unscoped().Model(&User{}).Select(\"age\").Where(\"name = ?\", user.Name).Scan(&age).Error != nil || age != user.Age {\n\t\tt.Errorf(\"Age soft deleted record, expects: %v, got: %v\", 0, age)\n\t}\n\n\tDB.Unscoped().Delete(&user)\n\tif err := DB.Unscoped().First(&User{}, \"name = ?\", user.Name).Error; !errors.Is(err, gorm.ErrRecordNotFound) {\n\t\tt.Errorf(\"Can't find permanently deleted record\")\n\t}\n}\n\nfunc TestDeletedAtUnMarshal(t *testing.T) {\n\texpected := &gorm.Model{}\n\tb, _ := json.Marshal(expected)\n\n\tresult := &gorm.Model{}\n\t_ = json.Unmarshal(b, result)\n\tif result.DeletedAt != expected.DeletedAt {\n\t\tt.Errorf(\"Failed, result.DeletedAt: %v is not same as expected.DeletedAt: %v\", result.DeletedAt, expected.DeletedAt)\n\t}\n}\n\nfunc TestDeletedAtOneOr(t *testing.T) {\n\tactualSQL := DB.ToSQL(func(tx *gorm.DB) *gorm.DB {\n\t\treturn tx.Or(\"id = ?\", 1).Find(&User{})\n\t})\n\n\tif !regexp.MustCompile(` WHERE id = 1 AND .users.\\..deleted_at. IS NULL`).MatchString(actualSQL) {\n\t\tt.Fatalf(\"invalid sql generated, got %v\", actualSQL)\n\t}\n}\n\nfunc TestSoftDeleteZeroValue(t *testing.T) {\n\ttype SoftDeleteBook struct {\n\t\tID        uint\n\t\tName      string\n\t\tPages     uint\n\t\tDeletedAt gorm.DeletedAt `gorm:\"zeroValue:'1970-01-01 00:00:01'\"`\n\t}\n\tDB.Migrator().DropTable(&SoftDeleteBook{})\n\tif err := DB.AutoMigrate(&SoftDeleteBook{}); err != nil {\n\t\tt.Fatalf(\"failed to auto migrate soft delete table\")\n\t}\n\n\tbook := SoftDeleteBook{Name: \"jinzhu\", Pages: 10}\n\tDB.Save(&book)\n\n\tvar count int64\n\tif DB.Model(&SoftDeleteBook{}).Where(\"name = ?\", book.Name).Count(&count).Error != nil || count != 1 {\n\t\tt.Errorf(\"Count soft deleted record, expects: %v, got: %v\", 1, count)\n\t}\n\n\tvar pages uint\n\tif DB.Model(&SoftDeleteBook{}).Select(\"pages\").Where(\"name = ?\", book.Name).Scan(&pages).Error != nil || pages != book.Pages {\n\t\tt.Errorf(\"Pages soft deleted record, expects: %v, got: %v\", 0, pages)\n\t}\n\n\tif err := DB.Delete(&book).Error; err != nil {\n\t\tt.Fatalf(\"No error should happen when soft delete user, but got %v\", err)\n\t}\n\n\tzeroTime, _ := now.Parse(\"1970-01-01 00:00:01\")\n\tif book.DeletedAt.Time.Equal(zeroTime) {\n\t\tt.Errorf(\"book's deleted at should not be zero, DeletedAt: %v\", book.DeletedAt)\n\t}\n\n\tif DB.First(&SoftDeleteBook{}, \"name = ?\", book.Name).Error == nil {\n\t\tt.Errorf(\"Can't find a soft deleted record\")\n\t}\n\n\tcount = 0\n\tif DB.Model(&SoftDeleteBook{}).Where(\"name = ?\", book.Name).Count(&count).Error != nil || count != 0 {\n\t\tt.Errorf(\"Count soft deleted record, expects: %v, got: %v\", 0, count)\n\t}\n\n\tpages = 0\n\tif err := DB.Model(&SoftDeleteBook{}).Select(\"pages\").Where(\"name = ?\", book.Name).Scan(&pages).Error; err != nil || pages != 0 {\n\t\tt.Fatalf(\"Age soft deleted record, expects: %v, got: %v, err %v\", 0, pages, err)\n\t}\n\n\tif err := DB.Unscoped().First(&SoftDeleteBook{}, \"name = ?\", book.Name).Error; err != nil {\n\t\tt.Errorf(\"Should find soft deleted record with Unscoped, but got err %s\", err)\n\t}\n\n\tcount = 0\n\tif DB.Unscoped().Model(&SoftDeleteBook{}).Where(\"name = ?\", book.Name).Count(&count).Error != nil || count != 1 {\n\t\tt.Errorf(\"Count soft deleted record, expects: %v, count: %v\", 1, count)\n\t}\n\n\tpages = 0\n\tif DB.Unscoped().Model(&SoftDeleteBook{}).Select(\"pages\").Where(\"name = ?\", book.Name).Scan(&pages).Error != nil || pages != book.Pages {\n\t\tt.Errorf(\"Age soft deleted record, expects: %v, got: %v\", 0, pages)\n\t}\n\n\tDB.Unscoped().Delete(&book)\n\tif err := DB.Unscoped().First(&SoftDeleteBook{}, \"name = ?\", book.Name).Error; !errors.Is(err, gorm.ErrRecordNotFound) {\n\t\tt.Errorf(\"Can't find permanently deleted record\")\n\t}\n}\n"
  },
  {
    "path": "tests/sql_builder_test.go",
    "content": "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/gorm/utils/tests\"\n)\n\nfunc TestRow(t *testing.T) {\n\tuser1 := User{Name: \"RowUser1\", Age: 1}\n\tuser2 := User{Name: \"RowUser2\", Age: 10}\n\tuser3 := User{Name: \"RowUser3\", Age: 20}\n\tDB.Save(&user1).Save(&user2).Save(&user3)\n\n\trow := DB.Table(\"users\").Where(\"name = ?\", user2.Name).Select(\"age\").Row()\n\n\tvar age int64\n\tif err := row.Scan(&age); err != nil {\n\t\tt.Fatalf(\"Failed to scan age, got %v\", err)\n\t}\n\n\tif age != 10 {\n\t\tt.Errorf(\"Scan with Row, age expects: %v, got %v\", user2.Age, age)\n\t}\n\n\ttable := \"gorm.users\"\n\tif DB.Dialector.Name() != \"mysql\" || isTiDB() {\n\t\ttable = \"users\" // other databases doesn't support select with `database.table`\n\t}\n\n\tDB.Table(table).Where(map[string]interface{}{\"name\": user2.Name}).Update(\"age\", 20)\n\n\trow = DB.Table(table+\" as u\").Where(\"u.name = ?\", user2.Name).Select(\"age\").Row()\n\tif err := row.Scan(&age); err != nil {\n\t\tt.Fatalf(\"Failed to scan age, got %v\", err)\n\t}\n\n\tif age != 20 {\n\t\tt.Errorf(\"Scan with Row, age expects: %v, got %v\", user2.Age, age)\n\t}\n}\n\nfunc TestRows(t *testing.T) {\n\tuser1 := User{Name: \"RowsUser1\", Age: 1}\n\tuser2 := User{Name: \"RowsUser2\", Age: 10}\n\tuser3 := User{Name: \"RowsUser3\", Age: 20}\n\tDB.Save(&user1).Save(&user2).Save(&user3)\n\n\trows, err := DB.Table(\"users\").Where(\"name = ? or name = ?\", user2.Name, user3.Name).Select(\"name, age\").Rows()\n\tif err != nil {\n\t\tt.Errorf(\"Not error should happen, got %v\", err)\n\t}\n\n\tcount := 0\n\tfor rows.Next() {\n\t\tvar name string\n\t\tvar age int64\n\t\trows.Scan(&name, &age)\n\t\tcount++\n\t}\n\n\tif count != 2 {\n\t\tt.Errorf(\"Should found two records\")\n\t}\n}\n\nfunc TestRaw(t *testing.T) {\n\tuser1 := User{Name: \"ExecRawSqlUser1\", Age: 1}\n\tuser2 := User{Name: \"ExecRawSqlUser2\", Age: 10}\n\tuser3 := User{Name: \"ExecRawSqlUser3\", Age: 20}\n\tDB.Save(&user1).Save(&user2).Save(&user3)\n\n\ttype result struct {\n\t\tName  string\n\t\tEmail string\n\t}\n\n\tvar results []result\n\tDB.Raw(\"SELECT name, age FROM users WHERE name = ? or name = ?\", user2.Name, user3.Name).Scan(&results)\n\tif len(results) != 2 || results[0].Name != user2.Name || results[1].Name != user3.Name {\n\t\tt.Errorf(\"Raw with scan\")\n\t}\n\n\trows, _ := DB.Raw(\"select name, age from users where name = ?\", user3.Name).Rows()\n\tcount := 0\n\tfor rows.Next() {\n\t\tcount++\n\t}\n\tif count != 1 {\n\t\tt.Errorf(\"Raw with Rows should find one record with name 3\")\n\t}\n\n\tDB.Exec(\"update users set name=? where name in (?)\", \"jinzhu-raw\", []string{user1.Name, user2.Name, user3.Name})\n\tif DB.Where(\"name in (?)\", []string{user1.Name, user2.Name, user3.Name}).First(&User{}).Error != gorm.ErrRecordNotFound {\n\t\tt.Error(\"Raw sql to update records\")\n\t}\n\n\tDB.Exec(\"update users set age=? where name = ?\", gorm.Expr(\"age * ? + ?\", 2, 10), \"jinzhu-raw\")\n\n\tvar age int\n\tDB.Raw(\"select sum(age) from users where name = ?\", \"jinzhu-raw\").Scan(&age)\n\n\tif age != ((1+10+20)*2 + 30) {\n\t\tt.Errorf(\"Invalid age, got %v\", age)\n\t}\n}\n\nfunc TestRowsWithGroup(t *testing.T) {\n\tusers := []User{\n\t\t{Name: \"having_user_1\", Age: 1},\n\t\t{Name: \"having_user_2\", Age: 10},\n\t\t{Name: \"having_user_1\", Age: 20},\n\t\t{Name: \"having_user_1\", Age: 30},\n\t}\n\n\tDB.Create(&users)\n\n\trows, err := DB.Select(\"name, count(*) as total\").Table(\"users\").Group(\"name\").Having(\"name IN ?\", []string{users[0].Name, users[1].Name}).Rows()\n\tif err != nil {\n\t\tt.Fatalf(\"got error %v\", err)\n\t}\n\n\tdefer rows.Close()\n\tfor rows.Next() {\n\t\tvar name string\n\t\tvar total int64\n\t\trows.Scan(&name, &total)\n\n\t\tif name == users[0].Name && total != 3 {\n\t\t\tt.Errorf(\"Should have one user having name %v\", users[0].Name)\n\t\t} else if name == users[1].Name && total != 1 {\n\t\t\tt.Errorf(\"Should have two users having name %v\", users[1].Name)\n\t\t}\n\t}\n}\n\nfunc TestQueryRaw(t *testing.T) {\n\tusers := []*User{\n\t\tGetUser(\"row_query_user\", Config{}),\n\t\tGetUser(\"row_query_user\", Config{}),\n\t\tGetUser(\"row_query_user\", Config{}),\n\t}\n\tDB.Create(&users)\n\n\tvar user User\n\tDB.Raw(\"select * from users WHERE id = ?\", users[1].ID).First(&user)\n\tCheckUser(t, user, *users[1])\n}\n\nfunc TestDryRun(t *testing.T) {\n\tuser := *GetUser(\"dry-run\", Config{})\n\n\tdryRunDB := DB.Session(&gorm.Session{DryRun: true})\n\n\tstmt := dryRunDB.Create(&user).Statement\n\tif stmt.SQL.String() == \"\" || len(stmt.Vars) != 9 {\n\t\tt.Errorf(\"Failed to generate sql, got %v\", stmt.SQL.String())\n\t}\n\n\tstmt2 := dryRunDB.Find(&user, \"id = ?\", user.ID).Statement\n\tif stmt2.SQL.String() == \"\" || len(stmt2.Vars) != 1 {\n\t\tt.Errorf(\"Failed to generate sql, got %v\", stmt2.SQL.String())\n\t}\n}\n\ntype ageInt int8\n\nfunc (ageInt) String() string {\n\treturn \"age\"\n}\n\ntype ageBool bool\n\nfunc (ageBool) String() string {\n\treturn \"age\"\n}\n\ntype ageUint64 uint64\n\nfunc (ageUint64) String() string {\n\treturn \"age\"\n}\n\ntype ageFloat float64\n\nfunc (ageFloat) String() string {\n\treturn \"age\"\n}\n\nfunc TestExplainSQL(t *testing.T) {\n\tuser := *GetUser(\"explain-sql\", Config{})\n\tdryRunDB := DB.Session(&gorm.Session{DryRun: true})\n\n\tstmt := dryRunDB.Model(&user).Where(\"id = ?\", 1).Updates(map[string]interface{}{\"age\": ageInt(8)}).Statement\n\tsql := DB.Dialector.Explain(stmt.SQL.String(), stmt.Vars...)\n\tif !regexp.MustCompile(`.*age.*=8,`).MatchString(sql) {\n\t\tt.Errorf(\"Failed to generate sql, got %v\", sql)\n\t}\n\n\tstmt = dryRunDB.Model(&user).Where(\"id = ?\", 1).Updates(map[string]interface{}{\"age\": ageUint64(10241024)}).Statement\n\tsql = DB.Dialector.Explain(stmt.SQL.String(), stmt.Vars...)\n\tif !regexp.MustCompile(`.*age.*=10241024,`).MatchString(sql) {\n\t\tt.Errorf(\"Failed to generate sql, got %v\", sql)\n\t}\n\n\tstmt = dryRunDB.Model(&user).Where(\"id = ?\", 1).Updates(map[string]interface{}{\"age\": ageBool(false)}).Statement\n\tsql = DB.Dialector.Explain(stmt.SQL.String(), stmt.Vars...)\n\tif !regexp.MustCompile(`.*age.*=false,`).MatchString(sql) {\n\t\tt.Errorf(\"Failed to generate sql, got %v\", sql)\n\t}\n\n\tstmt = dryRunDB.Model(&user).Where(\"id = ?\", 1).Updates(map[string]interface{}{\"age\": ageFloat(0.12345678)}).Statement\n\tsql = DB.Dialector.Explain(stmt.SQL.String(), stmt.Vars...)\n\tif !regexp.MustCompile(`.*age.*=0.123457,`).MatchString(sql) {\n\t\tt.Errorf(\"Failed to generate sql, got %v\", sql)\n\t}\n}\n\nfunc TestGroupConditions(t *testing.T) {\n\ttype Pizza struct {\n\t\tID   uint\n\t\tName string\n\t\tSize string\n\t}\n\tdryRunDB := DB.Session(&gorm.Session{DryRun: true})\n\n\tstmt := dryRunDB.Where(\n\t\tDB.Where(\"pizza = ?\", \"pepperoni\").Where(DB.Where(\"size = ?\", \"small\").Or(\"size = ?\", \"medium\")),\n\t).Or(\n\t\tDB.Where(\"pizza = ?\", \"hawaiian\").Where(\"size = ?\", \"xlarge\"),\n\t).Find(&Pizza{}).Statement\n\n\texecStmt := dryRunDB.Exec(\"WHERE (pizza = ? AND (size = ? OR size = ?)) OR (pizza = ? AND size = ?)\", \"pepperoni\", \"small\", \"medium\", \"hawaiian\", \"xlarge\").Statement\n\n\tresult := DB.Dialector.Explain(stmt.SQL.String(), stmt.Vars...)\n\texpects := DB.Dialector.Explain(execStmt.SQL.String(), execStmt.Vars...)\n\n\tif !strings.HasSuffix(result, expects) {\n\t\tt.Errorf(\"expects: %v, got %v\", expects, result)\n\t}\n\n\tstmt2 := dryRunDB.Where(\n\t\tDB.Scopes(NameIn1And2),\n\t).Or(\n\t\tDB.Where(\"pizza = ?\", \"hawaiian\").Where(\"size = ?\", \"xlarge\"),\n\t).Find(&Pizza{}).Statement\n\n\texecStmt2 := dryRunDB.Exec(`WHERE name in ? OR (pizza = ? AND size = ?)`, []string{\"ScopeUser1\", \"ScopeUser2\"}, \"hawaiian\", \"xlarge\").Statement\n\n\tresult2 := DB.Dialector.Explain(stmt2.SQL.String(), stmt2.Vars...)\n\texpects2 := DB.Dialector.Explain(execStmt2.SQL.String(), execStmt2.Vars...)\n\n\tif !strings.HasSuffix(result2, expects2) {\n\t\tt.Errorf(\"expects: %v, got %v\", expects2, result2)\n\t}\n}\n\nfunc TestCombineStringConditions(t *testing.T) {\n\tdryRunDB := DB.Session(&gorm.Session{DryRun: true})\n\tsql := dryRunDB.Where(\"a = ? or b = ?\", \"a\", \"b\").Find(&User{}).Statement.SQL.String()\n\tif !regexp.MustCompile(`WHERE \\(a = .+ or b = .+\\) AND .users.\\..deleted_at. IS NULL`).MatchString(sql) {\n\t\tt.Fatalf(\"invalid sql generated, got %v\", sql)\n\t}\n\n\tsql = dryRunDB.Where(\"a = ? or b = ?\", \"a\", \"b\").Or(\"c = ? and d = ?\", \"c\", \"d\").Find(&User{}).Statement.SQL.String()\n\tif !regexp.MustCompile(`WHERE \\(\\(a = .+ or b = .+\\) OR \\(c = .+ and d = .+\\)\\) AND .users.\\..deleted_at. IS NULL`).MatchString(sql) {\n\t\tt.Fatalf(\"invalid sql generated, got %v\", sql)\n\t}\n\n\tsql = dryRunDB.Where(\"a = ? or b = ?\", \"a\", \"b\").Or(\"c = ?\", \"c\").Find(&User{}).Statement.SQL.String()\n\tif !regexp.MustCompile(`WHERE \\(\\(a = .+ or b = .+\\) OR c = .+\\) AND .users.\\..deleted_at. IS NULL`).MatchString(sql) {\n\t\tt.Fatalf(\"invalid sql generated, got %v\", sql)\n\t}\n\n\tsql = dryRunDB.Where(\"a = ? or b = ?\", \"a\", \"b\").Or(\"c = ? and d = ?\", \"c\", \"d\").Or(\"e = ? and f = ?\", \"e\", \"f\").Find(&User{}).Statement.SQL.String()\n\tif !regexp.MustCompile(`WHERE \\(\\(a = .+ or b = .+\\) OR \\(c = .+ and d = .+\\) OR \\(e = .+ and f = .+\\)\\) AND .users.\\..deleted_at. IS NULL`).MatchString(sql) {\n\t\tt.Fatalf(\"invalid sql generated, got %v\", sql)\n\t}\n\n\tsql = dryRunDB.Where(\"a = ? or b = ?\", \"a\", \"b\").Where(\"c = ? and d = ?\", \"c\", \"d\").Not(\"e = ? and f = ?\", \"e\", \"f\").Find(&User{}).Statement.SQL.String()\n\tif !regexp.MustCompile(`WHERE \\(a = .+ or b = .+\\) AND \\(c = .+ and d = .+\\) AND NOT \\(e = .+ and f = .+\\) AND .users.\\..deleted_at. IS NULL`).MatchString(sql) {\n\t\tt.Fatalf(\"invalid sql generated, got %v\", sql)\n\t}\n\n\tsql = dryRunDB.Where(\"a = ? or b = ?\", \"a\", \"b\").Where(\"c = ?\", \"c\").Not(\"e = ? and f = ?\", \"e\", \"f\").Find(&User{}).Statement.SQL.String()\n\tif !regexp.MustCompile(`WHERE \\(a = .+ or b = .+\\) AND c = .+ AND NOT \\(e = .+ and f = .+\\) AND .users.\\..deleted_at. IS NULL`).MatchString(sql) {\n\t\tt.Fatalf(\"invalid sql generated, got %v\", sql)\n\t}\n\n\tsql = dryRunDB.Where(\"a = ? or b = ?\", \"a\", \"b\").Where(\"c = ? and d = ?\", \"c\", \"d\").Not(\"e = ?\", \"e\").Find(&User{}).Statement.SQL.String()\n\tif !regexp.MustCompile(`WHERE \\(a = .+ or b = .+\\) AND \\(c = .+ and d = .+\\) AND NOT e = .+ AND .users.\\..deleted_at. IS NULL`).MatchString(sql) {\n\t\tt.Fatalf(\"invalid sql generated, got %v\", sql)\n\t}\n\n\tsql = dryRunDB.Where(\"a = ? or b = ?\", \"a\", \"b\").Unscoped().Find(&User{}).Statement.SQL.String()\n\tif !regexp.MustCompile(`WHERE a = .+ or b = .+$`).MatchString(sql) {\n\t\tt.Fatalf(\"invalid sql generated, got %v\", sql)\n\t}\n\n\tsql = dryRunDB.Or(\"a = ? or b = ?\", \"a\", \"b\").Unscoped().Find(&User{}).Statement.SQL.String()\n\tif !regexp.MustCompile(`WHERE a = .+ or b = .+$`).MatchString(sql) {\n\t\tt.Fatalf(\"invalid sql generated, got %v\", sql)\n\t}\n\n\tsql = dryRunDB.Not(\"a = ? or b = ?\", \"a\", \"b\").Unscoped().Find(&User{}).Statement.SQL.String()\n\tif !regexp.MustCompile(`WHERE NOT \\(a = .+ or b = .+\\)$`).MatchString(sql) {\n\t\tt.Fatalf(\"invalid sql generated, got %v\", sql)\n\t}\n}\n\nfunc TestFromWithJoins(t *testing.T) {\n\tvar result User\n\n\tnewDB := DB.Session(&gorm.Session{NewDB: true, DryRun: true}).Table(\"users\")\n\n\tnewDB.Clauses(\n\t\tclause.From{\n\t\t\tTables: []clause.Table{{Name: \"users\"}},\n\t\t\tJoins: []clause.Join{\n\t\t\t\t{\n\t\t\t\t\tTable: clause.Table{Name: \"companies\", Raw: false},\n\t\t\t\t\tON: clause.Where{\n\t\t\t\t\t\tExprs: []clause.Expression{\n\t\t\t\t\t\t\tclause.Eq{\n\t\t\t\t\t\t\t\tColumn: clause.Column{\n\t\t\t\t\t\t\t\t\tTable: \"users\",\n\t\t\t\t\t\t\t\t\tName:  \"company_id\",\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tValue: clause.Column{\n\t\t\t\t\t\t\t\t\tTable: \"companies\",\n\t\t\t\t\t\t\t\t\tName:  \"id\",\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t)\n\n\tnewDB.Joins(\"inner join rgs on rgs.id = user.id\")\n\n\tstmt := newDB.First(&result).Statement\n\tstr := stmt.SQL.String()\n\n\tif !strings.Contains(str, \"rgs.id = user.id\") {\n\t\tt.Errorf(\"The second join condition is over written instead of combining\")\n\t}\n\n\tif !strings.Contains(str, \"`users`.`company_id` = `companies`.`id`\") && !strings.Contains(str, \"\\\"users\\\".\\\"company_id\\\" = \\\"companies\\\".\\\"id\\\"\") {\n\t\tt.Errorf(\"The first join condition is over written instead of combining\")\n\t}\n}\n\nfunc TestToSQL(t *testing.T) {\n\t// By default DB.DryRun should false\n\tif DB.DryRun {\n\t\tt.Fatal(\"Failed expect DB.DryRun to be false\")\n\t}\n\n\tif DB.Dialector.Name() == \"sqlserver\" {\n\t\tt.Skip(\"Skip SQL Server for this test, because it too difference with other dialects.\")\n\t}\n\n\tdate, _ := time.ParseInLocation(\"2006-01-02\", \"2021-10-18\", time.Local)\n\n\t// find\n\tsql := DB.ToSQL(func(tx *gorm.DB) *gorm.DB {\n\t\treturn tx.Model(&User{}).Where(\"id = ?\", 100).Limit(10).Order(\"age desc\").Find(&[]User{})\n\t})\n\tassertEqualSQL(t, `SELECT * FROM \"users\" WHERE id = 100 AND \"users\".\"deleted_at\" IS NULL ORDER BY age desc LIMIT 10`, sql)\n\n\t// after model changed\n\tif DB.Statement.DryRun || DB.DryRun {\n\t\tt.Fatal(\"Failed expect DB.DryRun and DB.Statement.ToSQL to be false\")\n\t}\n\n\tif DB.Statement.SQL.String() != \"\" {\n\t\tt.Fatal(\"Failed expect DB.Statement.SQL to be empty\")\n\t}\n\n\t// first\n\tsql = DB.ToSQL(func(tx *gorm.DB) *gorm.DB {\n\t\treturn tx.Model(&User{}).Where(&User{Name: \"foo\", Age: 20}).Limit(10).Offset(5).Order(\"name ASC\").First(&User{})\n\t})\n\tassertEqualSQL(t, `SELECT * FROM \"users\" WHERE (\"users\".\"name\" = 'foo' AND \"users\".\"age\" = 20) AND \"users\".\"deleted_at\" IS NULL ORDER BY name ASC,\"users\".\"id\" LIMIT 1 OFFSET 5`, sql)\n\n\t// last and unscoped\n\tsql = DB.ToSQL(func(tx *gorm.DB) *gorm.DB {\n\t\treturn tx.Model(&User{}).Unscoped().Where(&User{Name: \"bar\", Age: 12}).Limit(10).Offset(5).Order(\"name ASC\").Last(&User{})\n\t})\n\tassertEqualSQL(t, `SELECT * FROM \"users\" WHERE \"users\".\"name\" = 'bar' AND \"users\".\"age\" = 12 ORDER BY name ASC,\"users\".\"id\" DESC LIMIT 1 OFFSET 5`, sql)\n\n\t// create\n\tuser := &User{Name: \"foo\", Age: 20}\n\tuser.CreatedAt = date\n\tuser.UpdatedAt = date\n\tsql = DB.ToSQL(func(tx *gorm.DB) *gorm.DB {\n\t\treturn tx.Model(&User{}).Create(user)\n\t})\n\tassertEqualSQL(t, `INSERT INTO \"users\" (\"created_at\",\"updated_at\",\"deleted_at\",\"name\",\"age\",\"birthday\",\"company_id\",\"manager_id\",\"active\") VALUES ('2021-10-18 00:00:00','2021-10-18 00:00:00',NULL,'foo',20,NULL,NULL,NULL,false) RETURNING \"id\"`, sql)\n\n\t// save\n\tuser = &User{Name: \"foo\", Age: 20}\n\tuser.CreatedAt = date\n\tuser.UpdatedAt = date\n\tsql = DB.ToSQL(func(tx *gorm.DB) *gorm.DB {\n\t\treturn tx.Model(&User{}).Save(user)\n\t})\n\tassertEqualSQL(t, `INSERT INTO \"users\" (\"created_at\",\"updated_at\",\"deleted_at\",\"name\",\"age\",\"birthday\",\"company_id\",\"manager_id\",\"active\") VALUES ('2021-10-18 00:00:00','2021-10-18 00:00:00',NULL,'foo',20,NULL,NULL,NULL,false) RETURNING \"id\"`, sql)\n\n\t// updates\n\tuser = &User{Name: \"bar\", Age: 22}\n\tuser.CreatedAt = date\n\tuser.UpdatedAt = date\n\tsql = DB.ToSQL(func(tx *gorm.DB) *gorm.DB {\n\t\treturn tx.Model(&User{}).Where(\"id = ?\", 100).Updates(user)\n\t})\n\tassertEqualSQL(t, `UPDATE \"users\" SET \"created_at\"='2021-10-18 00:00:00',\"updated_at\"='2021-10-18 19:50:09.438',\"name\"='bar',\"age\"=22 WHERE id = 100 AND \"users\".\"deleted_at\" IS NULL`, sql)\n\n\t// update\n\tsql = DB.ToSQL(func(tx *gorm.DB) *gorm.DB {\n\t\treturn tx.Model(&User{}).Where(\"id = ?\", 100).Update(\"name\", \"Foo bar\")\n\t})\n\tassertEqualSQL(t, `UPDATE \"users\" SET \"name\"='Foo bar',\"updated_at\"='2021-10-18 19:50:09.438' WHERE id = 100 AND \"users\".\"deleted_at\" IS NULL`, sql)\n\n\t// UpdateColumn\n\tsql = DB.ToSQL(func(tx *gorm.DB) *gorm.DB {\n\t\treturn tx.Model(&User{}).Where(\"id = ?\", 100).UpdateColumn(\"name\", \"Foo bar\")\n\t})\n\tassertEqualSQL(t, `UPDATE \"users\" SET \"name\"='Foo bar' WHERE id = 100 AND \"users\".\"deleted_at\" IS NULL`, sql)\n\n\t// UpdateColumns\n\tsql = DB.ToSQL(func(tx *gorm.DB) *gorm.DB {\n\t\treturn tx.Model(&User{}).Where(\"id = ?\", 100).UpdateColumns(User{Name: \"Foo\", Age: 100})\n\t})\n\tassertEqualSQL(t, `UPDATE \"users\" SET \"name\"='Foo',\"age\"=100 WHERE id = 100 AND \"users\".\"deleted_at\" IS NULL`, sql)\n\n\t// after model changed\n\tif DB.Statement.DryRun || DB.DryRun {\n\t\tt.Fatal(\"Failed expect DB.DryRun and DB.Statement.ToSQL to be false\")\n\t}\n\n\t// UpdateColumns\n\tsql = DB.ToSQL(func(tx *gorm.DB) *gorm.DB {\n\t\treturn tx.Raw(\"SELECT * FROM users ?\", clause.OrderBy{\n\t\t\tColumns: []clause.OrderByColumn{{Column: clause.Column{Name: \"id\", Raw: true}, Desc: true}},\n\t\t})\n\t})\n\tassertEqualSQL(t, `SELECT * FROM users ORDER BY id DESC`, sql)\n}\n\n// assertEqualSQL for assert that the sql is equal, this method will ignore quote, and dialect specials.\nfunc assertEqualSQL(t *testing.T, expected string, actually string) {\n\tt.Helper()\n\n\t// replace SQL quote, convert into postgresql like \"\"\n\texpected = replaceQuoteInSQL(expected)\n\tactually = replaceQuoteInSQL(actually)\n\n\t// ignore updated_at value, because it's generated in Gorm internal, can't to mock value on update.\n\tupdatedAtRe := regexp.MustCompile(`(?i)\"updated_at\"=\".+?\"`)\n\tactually = updatedAtRe.ReplaceAllString(actually, `\"updated_at\"=?`)\n\texpected = updatedAtRe.ReplaceAllString(expected, `\"updated_at\"=?`)\n\n\t// ignore RETURNING \"id\" (only in PostgreSQL)\n\treturningRe := regexp.MustCompile(`(?i)RETURNING \"id\"`)\n\tactually = returningRe.ReplaceAllString(actually, ``)\n\texpected = returningRe.ReplaceAllString(expected, ``)\n\n\tactually = strings.TrimSpace(actually)\n\texpected = strings.TrimSpace(expected)\n\n\tif actually != expected {\n\t\tt.Fatalf(\"\\nexpected: %s\\nactually: %s\", expected, actually)\n\t}\n}\n\nfunc replaceQuoteInSQL(sql string) string {\n\t// convert single quote into double quote\n\tsql = strings.ReplaceAll(sql, `'`, `\"`)\n\n\t// convert dialect special quote into double quote\n\tswitch DB.Dialector.Name() {\n\tcase \"postgres\", \"gaussdb\":\n\t\tsql = strings.ReplaceAll(sql, `\"`, `\"`)\n\tcase \"mysql\", \"sqlite\":\n\t\tsql = strings.ReplaceAll(sql, \"`\", `\"`)\n\tcase \"sqlserver\":\n\t\tsql = strings.ReplaceAll(sql, `'`, `\"`)\n\t}\n\n\treturn sql\n}\n"
  },
  {
    "path": "tests/submodel_test.go",
    "content": "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\tDetail string\n}\n\n// Panic-safe BeforeUpdate hook that checks for Changed(\"age\")\nfunc (m *Man) BeforeUpdate(tx *gorm.DB) (err error) {\n\tif !tx.Statement.Changed(\"age\") {\n\t\treturn nil\n\t}\n\treturn nil\n}\n\nfunc TestSubModel(t *testing.T) {\n\tman := Man{Age: 18, Name: \"random-name\"}\n\tif err := DB.Create(&man).Error; err != nil {\n\t\tt.Fatalf(\"unexpected error: %v\", err)\n\t}\n\n\tif err := DB.Model(&man).Where(\"id = ?\", man.ID).Updates(struct {\n\t\tAge int\n\t}{Age: 20}).Error; err != nil {\n\t\tt.Fatalf(\"unexpected error: %v\", err)\n\t}\n\n\tvar result = struct {\n\t\tID  int\n\t\tAge int\n\t}{}\n\tif err := DB.Model(&man).Where(\"id = ?\", man.ID).Find(&result).Error; err != nil {\n\t\tt.Fatalf(\"unexpected error: %v\", err)\n\t}\n\tif result.ID != man.ID || result.Age != 20 {\n\t\tt.Fatalf(\"expected ID %d and Age 20, got ID %d and age\", result.ID, result.Age)\n\t}\n}\n"
  },
  {
    "path": "tests/table_test.go",
    "content": "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.io/gorm\"\n\t\"gorm.io/gorm/schema\"\n\t\"gorm.io/gorm/utils/tests\"\n\t. \"gorm.io/gorm/utils/tests\"\n)\n\ntype UserWithTable struct {\n\tgorm.Model\n\tName string\n}\n\nfunc (UserWithTable) TableName() string {\n\treturn \"gorm.user\"\n}\n\nfunc TestTable(t *testing.T) {\n\tdryDB := DB.Session(&gorm.Session{DryRun: true})\n\n\tr := dryDB.Table(\"`user`\").Find(&User{}).Statement\n\tif !regexp.MustCompile(\"SELECT \\\\* FROM `user`\").MatchString(r.Statement.SQL.String()) {\n\t\tt.Errorf(\"Table with escape character, got %v\", r.Statement.SQL.String())\n\t}\n\n\tr = dryDB.Table(\"user as u\").Select(\"name\").Find(&User{}).Statement\n\tif !regexp.MustCompile(\"SELECT .name. FROM user as u WHERE .u.\\\\..deleted_at. IS NULL\").MatchString(r.Statement.SQL.String()) {\n\t\tt.Errorf(\"Table with escape character, got %v\", r.Statement.SQL.String())\n\t}\n\n\tr = dryDB.Table(\"`people`\").Table(\"`user`\").Find(&User{}).Statement\n\tif !regexp.MustCompile(\"SELECT \\\\* FROM `user`\").MatchString(r.Statement.SQL.String()) {\n\t\tt.Errorf(\"Table with escape character, got %v\", r.Statement.SQL.String())\n\t}\n\n\tr = dryDB.Table(\"people as p\").Table(\"user as u\").Find(&User{}).Statement\n\tif !regexp.MustCompile(\"SELECT \\\\* FROM user as u WHERE .u.\\\\..deleted_at. IS NULL\").MatchString(r.Statement.SQL.String()) {\n\t\tt.Errorf(\"Table with escape character, got %v\", r.Statement.SQL.String())\n\t}\n\n\tr = dryDB.Table(\"people as p\").Table(\"user\").Find(&User{}).Statement\n\tif !regexp.MustCompile(\"SELECT \\\\* FROM .user. WHERE .user.\\\\..deleted_at. IS NULL\").MatchString(r.Statement.SQL.String()) {\n\t\tt.Errorf(\"Table with escape character, got %v\", r.Statement.SQL.String())\n\t}\n\n\tr = dryDB.Table(\"gorm.people\").Table(\"user\").Find(&User{}).Statement\n\tif !regexp.MustCompile(\"SELECT \\\\* FROM .user. WHERE .user.\\\\..deleted_at. IS NULL\").MatchString(r.Statement.SQL.String()) {\n\t\tt.Errorf(\"Table with escape character, got %v\", r.Statement.SQL.String())\n\t}\n\n\tr = dryDB.Table(\"gorm.user\").Select(\"name\").Find(&User{}).Statement\n\tif !regexp.MustCompile(\"SELECT .name. FROM .gorm.\\\\..user. WHERE .user.\\\\..deleted_at. IS NULL\").MatchString(r.Statement.SQL.String()) {\n\t\tt.Errorf(\"Table with escape character, got %v\", r.Statement.SQL.String())\n\t}\n\n\tr = dryDB.Select(\"name\").Find(&UserWithTable{}).Statement\n\tif !regexp.MustCompile(\"SELECT .name. FROM .gorm.\\\\..user. WHERE .user.\\\\..deleted_at. IS NULL\").MatchString(r.Statement.SQL.String()) {\n\t\tt.Errorf(\"Table with escape character, got %v\", r.Statement.SQL.String())\n\t}\n\n\tr = dryDB.Create(&UserWithTable{}).Statement\n\tif DB.Dialector.Name() != \"sqlite\" {\n\t\tif !regexp.MustCompile(`INSERT INTO .gorm.\\..user. (.*name.*) VALUES (.*)`).MatchString(r.Statement.SQL.String()) {\n\t\t\tt.Errorf(\"Table with escape character, got %v\", r.Statement.SQL.String())\n\t\t}\n\t} else {\n\t\tif !regexp.MustCompile(`INSERT INTO .user. (.*name.*) VALUES (.*)`).MatchString(r.Statement.SQL.String()) {\n\t\t\tt.Errorf(\"Table with escape character, got %v\", r.Statement.SQL.String())\n\t\t}\n\t}\n\n\tr = dryDB.Table(\"(?) as u\", DB.Model(&User{}).Select(\"name\")).Find(&User{}).Statement\n\tif !regexp.MustCompile(\"SELECT \\\\* FROM \\\\(SELECT .name. FROM .users. WHERE .users.\\\\..deleted_at. IS NULL\\\\) as u WHERE .u.\\\\..deleted_at. IS NULL\").MatchString(r.Statement.SQL.String()) {\n\t\tt.Errorf(\"Table with escape character, got %v\", r.Statement.SQL.String())\n\t}\n\n\tr = dryDB.Table(\"(?) as u, (?) as p\", DB.Model(&User{}).Select(\"name\"), DB.Model(&Pet{}).Select(\"name\")).Find(&User{}).Statement\n\tif !regexp.MustCompile(\"SELECT \\\\* FROM \\\\(SELECT .name. FROM .users. WHERE .users.\\\\..deleted_at. IS NULL\\\\) as u, \\\\(SELECT .name. FROM .pets. WHERE .pets.\\\\..deleted_at. IS NULL\\\\) as p WHERE .u.\\\\..deleted_at. IS NULL\").MatchString(r.Statement.SQL.String()) {\n\t\tt.Errorf(\"Table with escape character, got %v\", r.Statement.SQL.String())\n\t}\n\n\tr = dryDB.Where(\"name = ?\", 1).Table(\"(?) as u, (?) as p\", DB.Model(&User{}).Select(\"name\").Where(\"name = ?\", 2), DB.Model(&Pet{}).Where(\"name = ?\", 4).Select(\"name\")).Where(\"name = ?\", 3).Find(&User{}).Statement\n\tif !regexp.MustCompile(\"SELECT \\\\* FROM \\\\(SELECT .name. FROM .users. WHERE name = .+ AND .users.\\\\..deleted_at. IS NULL\\\\) as u, \\\\(SELECT .name. FROM .pets. WHERE name = .+ AND .pets.\\\\..deleted_at. IS NULL\\\\) as p WHERE name = .+ AND name = .+ AND .u.\\\\..deleted_at. IS NULL\").MatchString(r.Statement.SQL.String()) {\n\t\tt.Errorf(\"Table with escape character, got %v\", r.Statement.SQL.String())\n\t}\n\n\tAssertEqual(t, r.Statement.Vars, []interface{}{2, 4, 1, 3})\n}\n\nfunc TestTableWithAllFields(t *testing.T) {\n\tdryDB := DB.Session(&gorm.Session{DryRun: true, QueryFields: true})\n\tuserQuery := \"SELECT .*user.*id.*user.*created_at.*user.*updated_at.*user.*deleted_at.*user.*name.*user.*age\" +\n\t\t\".*user.*birthday.*user.*company_id.*user.*manager_id.*user.*active.* \"\n\n\tr := dryDB.Table(\"`user`\").Find(&User{}).Statement\n\tif !regexp.MustCompile(userQuery + \"FROM `user`\").MatchString(r.Statement.SQL.String()) {\n\t\tt.Errorf(\"Table with escape character, got %v\", r.Statement.SQL.String())\n\t}\n\n\tr = dryDB.Table(\"user as u\").Select(\"name\").Find(&User{}).Statement\n\tif !regexp.MustCompile(\"SELECT .name. FROM user as u WHERE .u.\\\\..deleted_at. IS NULL\").MatchString(r.Statement.SQL.String()) {\n\t\tt.Errorf(\"Table with escape character, got %v\", r.Statement.SQL.String())\n\t}\n\n\tr = dryDB.Table(\"gorm.user\").Select(\"name\").Find(&User{}).Statement\n\tif !regexp.MustCompile(\"SELECT .name. FROM .gorm.\\\\..user. WHERE .user.\\\\..deleted_at. IS NULL\").MatchString(r.Statement.SQL.String()) {\n\t\tt.Errorf(\"Table with escape character, got %v\", r.Statement.SQL.String())\n\t}\n\n\tr = dryDB.Select(\"name\").Find(&UserWithTable{}).Statement\n\tif !regexp.MustCompile(\"SELECT .name. FROM .gorm.\\\\..user. WHERE .user.\\\\..deleted_at. IS NULL\").MatchString(r.Statement.SQL.String()) {\n\t\tt.Errorf(\"Table with escape character, got %v\", r.Statement.SQL.String())\n\t}\n\n\tr = dryDB.Create(&UserWithTable{}).Statement\n\tif DB.Dialector.Name() != \"sqlite\" {\n\t\tif !regexp.MustCompile(`INSERT INTO .gorm.\\..user. (.*name.*) VALUES (.*)`).MatchString(r.Statement.SQL.String()) {\n\t\t\tt.Errorf(\"Table with escape character, got %v\", r.Statement.SQL.String())\n\t\t}\n\t} else {\n\t\tif !regexp.MustCompile(`INSERT INTO .user. (.*name.*) VALUES (.*)`).MatchString(r.Statement.SQL.String()) {\n\t\t\tt.Errorf(\"Table with escape character, got %v\", r.Statement.SQL.String())\n\t\t}\n\t}\n\n\tuserQueryCharacter := \"SELECT .*u.*id.*u.*created_at.*u.*updated_at.*u.*deleted_at.*u.*name.*u.*age.*u.*birthday\" +\n\t\t\".*u.*company_id.*u.*manager_id.*u.*active.* \"\n\n\tr = dryDB.Table(\"(?) as u\", DB.Model(&User{}).Select(\"name\")).Find(&User{}).Statement\n\tif !regexp.MustCompile(userQueryCharacter + \"FROM \\\\(SELECT .name. FROM .users. WHERE .users.\\\\..deleted_at. IS NULL\\\\) as u WHERE .u.\\\\..deleted_at. IS NULL\").MatchString(r.Statement.SQL.String()) {\n\t\tt.Errorf(\"Table with escape character, got %v\", r.Statement.SQL.String())\n\t}\n\n\tr = dryDB.Table(\"(?) as u, (?) as p\", DB.Model(&User{}).Select(\"name\"), DB.Model(&Pet{}).Select(\"name\")).Find(&User{}).Statement\n\tif !regexp.MustCompile(userQueryCharacter + \"FROM \\\\(SELECT .name. FROM .users. WHERE .users.\\\\..deleted_at. IS NULL\\\\) as u, \\\\(SELECT .name. FROM .pets. WHERE .pets.\\\\..deleted_at. IS NULL\\\\) as p WHERE .u.\\\\..deleted_at. IS NULL\").MatchString(r.Statement.SQL.String()) {\n\t\tt.Errorf(\"Table with escape character, got %v\", r.Statement.SQL.String())\n\t}\n\n\tr = dryDB.Where(\"name = ?\", 1).Table(\"(?) as u, (?) as p\", DB.Model(&User{}).Select(\"name\").Where(\"name = ?\", 2), DB.Model(&Pet{}).Where(\"name = ?\", 4).Select(\"name\")).Where(\"name = ?\", 3).Find(&User{}).Statement\n\tif !regexp.MustCompile(userQueryCharacter + \"FROM \\\\(SELECT .name. FROM .users. WHERE name = .+ AND .users.\\\\..deleted_at. IS NULL\\\\) as u, \\\\(SELECT .name. FROM .pets. WHERE name = .+ AND .pets.\\\\..deleted_at. IS NULL\\\\) as p WHERE name = .+ AND name = .+ AND .u.\\\\..deleted_at. IS NULL\").MatchString(r.Statement.SQL.String()) {\n\t\tt.Errorf(\"Table with escape character, got %v\", r.Statement.SQL.String())\n\t}\n\n\tAssertEqual(t, r.Statement.Vars, []interface{}{2, 4, 1, 3})\n}\n\ntype UserWithTableNamer struct {\n\tgorm.Model\n\tName string\n}\n\nfunc (UserWithTableNamer) TableName(namer schema.Namer) string {\n\treturn namer.TableName(\"user\")\n}\n\nfunc TestTableWithNamer(t *testing.T) {\n\tdb, _ := gorm.Open(tests.DummyDialector{}, &gorm.Config{\n\t\tNamingStrategy: schema.NamingStrategy{\n\t\t\tTablePrefix: \"t_\",\n\t\t},\n\t})\n\n\tsql := db.ToSQL(func(tx *gorm.DB) *gorm.DB {\n\t\treturn tx.Model(&UserWithTableNamer{}).Find(&UserWithTableNamer{})\n\t})\n\n\tif !regexp.MustCompile(\"SELECT \\\\* FROM `t_users`\").MatchString(sql) {\n\t\tt.Errorf(\"Table with namer, got %v\", sql)\n\t}\n}\n\nfunc TestPostgresTableWithIdentifierLength(t *testing.T) {\n\tif DB.Dialector.Name() != \"postgres\" {\n\t\treturn\n\t}\n\n\ttype LongString struct {\n\t\tThisIsAVeryVeryVeryVeryVeryVeryVeryVeryVeryLongString string `gorm:\"unique\"`\n\t}\n\n\tt.Run(\"default\", func(t *testing.T) {\n\t\tdb, _ := gorm.Open(postgres.Open(postgresDSN), &gorm.Config{})\n\t\tuser, err := schema.Parse(&LongString{}, &sync.Map{}, db.Config.NamingStrategy)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"failed to parse user unique, got error %v\", err)\n\t\t}\n\n\t\tconstraints := user.ParseUniqueConstraints()\n\t\tif len(constraints) != 1 {\n\t\t\tt.Fatalf(\"failed to find unique constraint, got %v\", constraints)\n\t\t}\n\n\t\tfor key := range constraints {\n\t\t\tif len(key) != 63 {\n\t\t\t\tt.Errorf(\"failed to find unique constraint, got %v\", constraints)\n\t\t\t}\n\t\t}\n\t})\n\n\tt.Run(\"naming strategy\", func(t *testing.T) {\n\t\tdb, _ := gorm.Open(postgres.Open(postgresDSN), &gorm.Config{\n\t\t\tNamingStrategy: schema.NamingStrategy{},\n\t\t})\n\n\t\tuser, err := schema.Parse(&LongString{}, &sync.Map{}, db.Config.NamingStrategy)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"failed to parse user unique, got error %v\", err)\n\t\t}\n\n\t\tconstraints := user.ParseUniqueConstraints()\n\t\tif len(constraints) != 1 {\n\t\t\tt.Fatalf(\"failed to find unique constraint, got %v\", constraints)\n\t\t}\n\n\t\tfor key := range constraints {\n\t\t\tif len(key) != 63 {\n\t\t\t\tt.Errorf(\"failed to find unique constraint, got %v\", constraints)\n\t\t\t}\n\t\t}\n\t})\n\n\tt.Run(\"namer\", func(t *testing.T) {\n\t\tuname := \"custom_unique_name\"\n\t\tdb, _ := gorm.Open(postgres.Open(postgresDSN), &gorm.Config{\n\t\t\tNamingStrategy: mockUniqueNamingStrategy{\n\t\t\t\tUName: uname,\n\t\t\t},\n\t\t})\n\n\t\tuser, err := schema.Parse(&LongString{}, &sync.Map{}, db.Config.NamingStrategy)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"failed to parse user unique, got error %v\", err)\n\t\t}\n\n\t\tconstraints := user.ParseUniqueConstraints()\n\t\tif len(constraints) != 1 {\n\t\t\tt.Fatalf(\"failed to find unique constraint, got %v\", constraints)\n\t\t}\n\n\t\tfor key := range constraints {\n\t\t\tif key != uname {\n\t\t\t\tt.Errorf(\"failed to find unique constraint, got %v\", constraints)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc TestGaussDBTableWithIdentifierLength(t *testing.T) {\n\tif DB.Dialector.Name() != \"gaussdb\" {\n\t\treturn\n\t}\n\n\ttype LongString struct {\n\t\tThisIsAVeryVeryVeryVeryVeryVeryVeryVeryVeryLongString string `gorm:\"unique\"`\n\t}\n\n\tt.Run(\"default\", func(t *testing.T) {\n\t\tdb, _ := gorm.Open(gaussdb.Open(gaussdbDSN), &gorm.Config{})\n\t\tuser, err := schema.Parse(&LongString{}, &sync.Map{}, db.Config.NamingStrategy)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"failed to parse user unique, got error %v\", err)\n\t\t}\n\n\t\tconstraints := user.ParseUniqueConstraints()\n\t\tif len(constraints) != 1 {\n\t\t\tt.Fatalf(\"failed to find unique constraint, got %v\", constraints)\n\t\t}\n\n\t\tfor key := range constraints {\n\t\t\tif len(key) != 63 {\n\t\t\t\tt.Errorf(\"failed to find unique constraint, got %v\", constraints)\n\t\t\t}\n\t\t}\n\t})\n\n\tt.Run(\"naming strategy\", func(t *testing.T) {\n\t\tdb, _ := gorm.Open(gaussdb.Open(gaussdbDSN), &gorm.Config{\n\t\t\tNamingStrategy: schema.NamingStrategy{},\n\t\t})\n\n\t\tuser, err := schema.Parse(&LongString{}, &sync.Map{}, db.Config.NamingStrategy)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"failed to parse user unique, got error %v\", err)\n\t\t}\n\n\t\tconstraints := user.ParseUniqueConstraints()\n\t\tif len(constraints) != 1 {\n\t\t\tt.Fatalf(\"failed to find unique constraint, got %v\", constraints)\n\t\t}\n\n\t\tfor key := range constraints {\n\t\t\tif len(key) != 63 {\n\t\t\t\tt.Errorf(\"failed to find unique constraint, got %v\", constraints)\n\t\t\t}\n\t\t}\n\t})\n\n\tt.Run(\"namer\", func(t *testing.T) {\n\t\tuname := \"custom_unique_name\"\n\t\tdb, _ := gorm.Open(gaussdb.Open(gaussdbDSN), &gorm.Config{\n\t\t\tNamingStrategy: mockUniqueNamingStrategy{\n\t\t\t\tUName: uname,\n\t\t\t},\n\t\t})\n\n\t\tuser, err := schema.Parse(&LongString{}, &sync.Map{}, db.Config.NamingStrategy)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"failed to parse user unique, got error %v\", err)\n\t\t}\n\n\t\tconstraints := user.ParseUniqueConstraints()\n\t\tif len(constraints) != 1 {\n\t\t\tt.Fatalf(\"failed to find unique constraint, got %v\", constraints)\n\t\t}\n\n\t\tfor key := range constraints {\n\t\t\tif key != uname {\n\t\t\t\tt.Errorf(\"failed to find unique constraint, got %v\", constraints)\n\t\t\t}\n\t\t}\n\t})\n}\n\ntype mockUniqueNamingStrategy struct {\n\tUName string\n\tschema.NamingStrategy\n}\n\nfunc (a mockUniqueNamingStrategy) UniqueName(table, column string) string {\n\treturn a.UName\n}\n"
  },
  {
    "path": "tests/tests_all.sh",
    "content": "#!/bin/bash -e\n\ndialects=(\"sqlite\" \"mysql\" \"postgres\" \"gaussdb\" \"sqlserver\" \"tidb\")\n\nif [[ $(pwd) == *\"gorm/tests\"* ]]; then\n  cd ..\nfi\n\nif [ -d tests ]\nthen\n  cd tests\n  go get -u -t ./...\n  go mod download\n  go mod tidy\n  cd ..\nfi\n\n# SqlServer for Mac M1\nif [[ -z $GITHUB_ACTION && -d tests ]]; then\n  cd tests\n  if [[ $(uname -a) == *\" arm64\" ]]; then\n    MSSQL_IMAGE=mcr.microsoft.com/azure-sql-edge docker compose up -d --wait\n    go install github.com/microsoft/go-sqlcmd/cmd/sqlcmd@latest || true\n    for query in \\\n      \"IF DB_ID('gorm') IS NULL CREATE DATABASE gorm\" \\\n      \"IF SUSER_ID (N'gorm') IS NULL CREATE LOGIN gorm WITH PASSWORD = 'LoremIpsum86';\" \\\n      \"IF USER_ID (N'gorm') IS NULL CREATE USER gorm FROM LOGIN gorm; ALTER SERVER ROLE sysadmin ADD MEMBER [gorm];\"\n    do\n      SQLCMDPASSWORD=LoremIpsum86 sqlcmd -U sa -S localhost:9930 -Q \"$query\" > /dev/null || true\n    done\n  else\n    MSSQL_IMAGE=mcr.microsoft.com/mssql/server docker compose up -d --wait\n  fi\n  cd ..\nfi\n\n\nfor dialect in \"${dialects[@]}\" ; do\n  if [ \"$GORM_DIALECT\" = \"\" ] || [ \"$GORM_DIALECT\" = \"${dialect}\" ]\n  then\n    echo \"testing ${dialect}...\"\n\n    if [ \"$GORM_VERBOSE\" = \"\" ]\n    then\n      GORM_DIALECT=${dialect} go test -race -count=1 ./...\n      if [ -d tests ]\n      then\n        cd tests\n        GORM_DIALECT=${dialect} go test -race -count=1 ./...\n        cd ..\n      fi\n    else\n      GORM_DIALECT=${dialect} go test -race -count=1 -v ./...\n      if [ -d tests ]\n      then\n        cd tests\n        GORM_DIALECT=${dialect} go test -race -count=1 -v ./...\n        cd ..\n      fi\n    fi\n  fi\ndone\n"
  },
  {
    "path": "tests/tests_test.go",
    "content": "//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.io/driver/gaussdb\"\n\t\"gorm.io/driver/mysql\"\n\t\"gorm.io/driver/postgres\"\n\t\"gorm.io/driver/sqlite\"\n\t\"gorm.io/driver/sqlserver\"\n\t\"gorm.io/gorm\"\n\t\"gorm.io/gorm/logger\"\n\t. \"gorm.io/gorm/utils/tests\"\n)\n\nvar DB *gorm.DB\nvar (\n\tmysqlDSN     = \"gorm:gorm@tcp(localhost:9910)/gorm?charset=utf8&parseTime=True&loc=Local\"\n\tpostgresDSN  = \"user=gorm password=gorm dbname=gorm host=localhost port=9920 sslmode=disable TimeZone=Asia/Shanghai\"\n\tgaussdbDSN   = \"user=gaussdb password=Gaussdb@123 dbname=gorm host=localhost port=9950 sslmode=disable TimeZone=Asia/Shanghai\"\n\tsqlserverDSN = \"sqlserver://sa:LoremIpsum86@localhost:9930?database=master\"\n\ttidbDSN      = \"root:@tcp(localhost:9940)/test?charset=utf8&parseTime=True&loc=Local\"\n)\n\nfunc init() {\n\tvar err error\n\tif DB, err = OpenTestConnection(&gorm.Config{}); err != nil {\n\t\tlog.Printf(\"failed to connect database, got error %v\", err)\n\t\tos.Exit(1)\n\t} else {\n\t\tsqlDB, err := DB.DB()\n\t\tif err != nil {\n\t\t\tlog.Printf(\"failed to connect database, got error %v\", err)\n\t\t\tos.Exit(1)\n\t\t}\n\n\t\terr = sqlDB.Ping()\n\t\tif err != nil {\n\t\t\tlog.Printf(\"failed to ping sqlDB, got error %v\", err)\n\t\t\tos.Exit(1)\n\t\t}\n\n\t\tRunMigrations()\n\t}\n}\n\nfunc OpenTestConnection(cfg *gorm.Config) (db *gorm.DB, err error) {\n\tdbDSN := os.Getenv(\"GORM_DSN\")\n\tswitch os.Getenv(\"GORM_DIALECT\") {\n\tcase \"mysql\":\n\t\tlog.Println(\"testing mysql...\")\n\t\tif dbDSN == \"\" {\n\t\t\tdbDSN = mysqlDSN\n\t\t}\n\t\tdb, err = gorm.Open(mysql.Open(dbDSN), cfg)\n\tcase \"postgres\":\n\t\tlog.Println(\"testing postgres...\")\n\t\tif dbDSN == \"\" {\n\t\t\tdbDSN = postgresDSN\n\t\t}\n\t\tdb, err = gorm.Open(postgres.New(postgres.Config{\n\t\t\tDSN:                  dbDSN,\n\t\t\tPreferSimpleProtocol: true,\n\t\t}), cfg)\n\tcase \"gaussdb\":\n\t\tlog.Println(\"testing gaussdb...\")\n\t\tif dbDSN == \"\" {\n\t\t\tdbDSN = gaussdbDSN\n\t\t}\n\t\tdb, err = gorm.Open(gaussdb.New(gaussdb.Config{\n\t\t\tDSN:                  dbDSN,\n\t\t\tPreferSimpleProtocol: true,\n\t\t}), cfg)\n\tcase \"sqlserver\":\n\t\t// go install github.com/microsoft/go-sqlcmd/cmd/sqlcmd@latest\n\t\t// SQLCMDPASSWORD=LoremIpsum86 sqlcmd -U sa -S localhost:9930\n\t\t// CREATE DATABASE gorm;\n\t\t// GO\n\t\t// CREATE LOGIN gorm WITH PASSWORD = 'LoremIpsum86';\n\t\t// CREATE USER gorm FROM LOGIN gorm;\n\t\t// ALTER SERVER ROLE sysadmin ADD MEMBER [gorm];\n\t\t// GO\n\t\tlog.Println(\"testing sqlserver...\")\n\t\tif dbDSN == \"\" {\n\t\t\tdbDSN = sqlserverDSN\n\t\t}\n\t\tdb, err = gorm.Open(sqlserver.Open(dbDSN), cfg)\n\tcase \"tidb\":\n\t\tlog.Println(\"testing tidb...\")\n\t\tif dbDSN == \"\" {\n\t\t\tdbDSN = tidbDSN\n\t\t}\n\t\tdb, err = gorm.Open(mysql.Open(dbDSN), cfg)\n\tdefault:\n\t\tlog.Println(\"testing sqlite3...\")\n\t\tdb, err = gorm.Open(sqlite.Open(filepath.Join(os.TempDir(), \"gorm.db\")), cfg)\n\t\tif err == nil {\n\t\t\tdb.Exec(\"PRAGMA foreign_keys = ON\")\n\t\t}\n\t}\n\n\tif err != nil {\n\t\treturn\n\t}\n\n\tif debug := os.Getenv(\"DEBUG\"); debug == \"true\" {\n\t\tdb.Logger = db.Logger.LogMode(logger.Info)\n\t} else if debug == \"false\" {\n\t\tdb.Logger = db.Logger.LogMode(logger.Silent)\n\t}\n\n\treturn\n}\n\nfunc RunMigrations() {\n\tvar err error\n\tallModels := []interface{}{&User{}, &Account{}, &Pet{}, &Company{}, &Toy{}, &Language{}, &Coupon{}, &CouponProduct{}, &Order{}, &Parent{}, &Child{}, &Tools{}}\n\trand.Seed(time.Now().UnixNano())\n\trand.Shuffle(len(allModels), func(i, j int) { allModels[i], allModels[j] = allModels[j], allModels[i] })\n\n\tDB.Migrator().DropTable(\"user_friends\", \"user_speaks\")\n\n\tif err = DB.Migrator().DropTable(allModels...); err != nil {\n\t\tlog.Printf(\"Failed to drop table, got error %v\\n\", err)\n\t\tos.Exit(1)\n\t}\n\n\tif err = DB.AutoMigrate(allModels...); err != nil {\n\t\tlog.Printf(\"Failed to auto migrate, but got error %v\\n\", err)\n\t\tos.Exit(1)\n\t}\n\n\tfor _, m := range allModels {\n\t\tif !DB.Migrator().HasTable(m) {\n\t\t\tlog.Printf(\"Failed to create table for %#v\\n\", m)\n\t\t\tos.Exit(1)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "tests/tracer_test.go",
    "content": "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\n\tTest   func(ctx context.Context, begin time.Time, fc func() (sql string, rowsAffected int64), err error)\n}\n\nfunc (S Tracer) LogMode(level logger.LogLevel) logger.Interface {\n\treturn S.Logger.LogMode(level)\n}\n\nfunc (S Tracer) Info(ctx context.Context, s string, i ...interface{}) {\n\tS.Logger.Info(ctx, s, i...)\n}\n\nfunc (S Tracer) Warn(ctx context.Context, s string, i ...interface{}) {\n\tS.Logger.Warn(ctx, s, i...)\n}\n\nfunc (S Tracer) Error(ctx context.Context, s string, i ...interface{}) {\n\tS.Logger.Error(ctx, s, i...)\n}\n\nfunc (S Tracer) Trace(ctx context.Context, begin time.Time, fc func() (sql string, rowsAffected int64), err error) {\n\tS.Logger.Trace(ctx, begin, fc, err)\n\tS.Test(ctx, begin, fc, err)\n}\n"
  },
  {
    "path": "tests/transaction_test.go",
    "content": "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\nfunc TestTransaction(t *testing.T) {\n\ttx := DB.Begin()\n\tuser := *GetUser(\"transaction\", Config{})\n\n\tif err := tx.Save(&user).Error; err != nil {\n\t\tt.Fatalf(\"No error should raise, but got %v\", err)\n\t}\n\n\tif err := tx.First(&User{}, \"name = ?\", \"transaction\").Error; err != nil {\n\t\tt.Fatalf(\"Should find saved record, but got %v\", err)\n\t}\n\n\tuser1 := *GetUser(\"transaction1-1\", Config{})\n\n\tif err := tx.Save(&user1).Error; err != nil {\n\t\tt.Fatalf(\"No error should raise, but got %v\", err)\n\t}\n\n\tif err := tx.First(&User{}, \"name = ?\", user1.Name).Error; err != nil {\n\t\tt.Fatalf(\"Should find saved record, but got %v\", err)\n\t}\n\n\tif sqlTx, ok := tx.Statement.ConnPool.(gorm.TxCommitter); !ok || sqlTx == nil {\n\t\tt.Fatalf(\"Should return the underlying sql.Tx\")\n\t}\n\n\ttx.Rollback()\n\n\tif err := DB.First(&User{}, \"name = ?\", \"transaction\").Error; err == nil {\n\t\tt.Fatalf(\"Should not find record after rollback, but got %v\", err)\n\t}\n\n\ttxDB := DB.Where(\"fake_name = ?\", \"fake_name\")\n\ttx2 := txDB.Session(&gorm.Session{NewDB: true}).Begin()\n\tuser2 := *GetUser(\"transaction-2\", Config{})\n\tif err := tx2.Save(&user2).Error; err != nil {\n\t\tt.Fatalf(\"No error should raise, but got %v\", err)\n\t}\n\n\tif err := tx2.First(&User{}, \"name = ?\", \"transaction-2\").Error; err != nil {\n\t\tt.Fatalf(\"Should find saved record, but got %v\", err)\n\t}\n\n\ttx2.Commit()\n\n\tif err := DB.First(&User{}, \"name = ?\", \"transaction-2\").Error; err != nil {\n\t\tt.Fatalf(\"Should be able to find committed record, but got %v\", err)\n\t}\n\n\tt.Run(\"this is test nested transaction and prepareStmt coexist case\", func(t *testing.T) {\n\t\t// enable prepare statement\n\t\ttx3 := DB.Session(&gorm.Session{PrepareStmt: true})\n\t\tif err := tx3.Transaction(func(tx4 *gorm.DB) error {\n\t\t\t// nested transaction\n\t\t\treturn tx4.Transaction(func(tx5 *gorm.DB) error {\n\t\t\t\treturn tx5.First(&User{}, \"name = ?\", \"transaction-2\").Error\n\t\t\t})\n\t\t}); err != nil {\n\t\t\tt.Fatalf(\"prepare statement and nested transaction coexist: %v\", err)\n\t\t}\n\t})\n}\n\nfunc TestCancelTransaction(t *testing.T) {\n\tctx := context.Background()\n\tctx, cancelFunc := context.WithCancel(ctx)\n\tcancelFunc()\n\n\tuser := *GetUser(\"cancel_transaction\", Config{})\n\tDB.Create(&user)\n\n\terr := DB.WithContext(ctx).Transaction(func(tx *gorm.DB) error {\n\t\tvar result User\n\t\ttx.First(&result, user.ID)\n\t\treturn nil\n\t})\n\n\tif err == nil {\n\t\tt.Fatalf(\"Transaction should get error when using cancelled context\")\n\t}\n}\n\nfunc TestTransactionWithBlock(t *testing.T) {\n\tassertPanic := func(f func()) {\n\t\tdefer func() {\n\t\t\tif r := recover(); r == nil {\n\t\t\t\tt.Fatalf(\"The code did not panic\")\n\t\t\t}\n\t\t}()\n\t\tf()\n\t}\n\n\t// rollback\n\terr := DB.Transaction(func(tx *gorm.DB) error {\n\t\tuser := *GetUser(\"transaction-block\", Config{})\n\t\tif err := tx.Save(&user).Error; err != nil {\n\t\t\tt.Fatalf(\"No error should raise\")\n\t\t}\n\n\t\tif err := tx.First(&User{}, \"name = ?\", user.Name).Error; err != nil {\n\t\t\tt.Fatalf(\"Should find saved record\")\n\t\t}\n\n\t\treturn errors.New(\"the error message\")\n\t})\n\n\tif err != nil && err.Error() != \"the error message\" {\n\t\tt.Fatalf(\"Transaction return error will equal the block returns error\")\n\t}\n\n\tif err := DB.First(&User{}, \"name = ?\", \"transaction-block\").Error; err == nil {\n\t\tt.Fatalf(\"Should not find record after rollback\")\n\t}\n\n\t// commit\n\tDB.Transaction(func(tx *gorm.DB) error {\n\t\tuser := *GetUser(\"transaction-block-2\", Config{})\n\t\tif err := tx.Save(&user).Error; err != nil {\n\t\t\tt.Fatalf(\"No error should raise\")\n\t\t}\n\n\t\tif err := tx.First(&User{}, \"name = ?\", user.Name).Error; err != nil {\n\t\t\tt.Fatalf(\"Should find saved record\")\n\t\t}\n\t\treturn nil\n\t})\n\n\tif err := DB.First(&User{}, \"name = ?\", \"transaction-block-2\").Error; err != nil {\n\t\tt.Fatalf(\"Should be able to find committed record\")\n\t}\n\n\t// panic will rollback\n\tassertPanic(func() {\n\t\tDB.Transaction(func(tx *gorm.DB) error {\n\t\t\tuser := *GetUser(\"transaction-block-3\", Config{})\n\t\t\tif err := tx.Save(&user).Error; err != nil {\n\t\t\t\tt.Fatalf(\"No error should raise\")\n\t\t\t}\n\n\t\t\tif err := tx.First(&User{}, \"name = ?\", user.Name).Error; err != nil {\n\t\t\t\tt.Fatalf(\"Should find saved record\")\n\t\t\t}\n\n\t\t\tpanic(\"force panic\")\n\t\t})\n\t})\n\n\tif err := DB.First(&User{}, \"name = ?\", \"transaction-block-3\").Error; err == nil {\n\t\tt.Fatalf(\"Should not find record after panic rollback\")\n\t}\n}\n\nfunc TestTransactionRaiseErrorOnRollbackAfterCommit(t *testing.T) {\n\ttx := DB.Begin()\n\tuser := User{Name: \"transaction\"}\n\tif err := tx.Save(&user).Error; err != nil {\n\t\tt.Fatalf(\"No error should raise\")\n\t}\n\n\tif err := tx.Commit().Error; err != nil {\n\t\tt.Fatalf(\"Commit should not raise error\")\n\t}\n\n\tif err := tx.Rollback().Error; err == nil {\n\t\tt.Fatalf(\"Rollback after commit should raise error\")\n\t}\n}\n\nfunc TestTransactionWithSavePoint(t *testing.T) {\n\ttx := DB.Begin()\n\n\tuser := *GetUser(\"transaction-save-point\", Config{})\n\ttx.Create(&user)\n\n\tif err := tx.First(&User{}, \"name = ?\", user.Name).Error; err != nil {\n\t\tt.Fatalf(\"Should find saved record\")\n\t}\n\n\tif err := tx.SavePoint(\"save_point1\").Error; err != nil {\n\t\tt.Fatalf(\"Failed to save point, got error %v\", err)\n\t}\n\n\tuser1 := *GetUser(\"transaction-save-point-1\", Config{})\n\ttx.Create(&user1)\n\n\tif err := tx.First(&User{}, \"name = ?\", user1.Name).Error; err != nil {\n\t\tt.Fatalf(\"Should find saved record\")\n\t}\n\n\tif err := tx.RollbackTo(\"save_point1\").Error; err != nil {\n\t\tt.Fatalf(\"Failed to save point, got error %v\", err)\n\t}\n\n\tif err := tx.First(&User{}, \"name = ?\", user1.Name).Error; err == nil {\n\t\tt.Fatalf(\"Should not find rollbacked record\")\n\t}\n\n\tif err := tx.SavePoint(\"save_point2\").Error; err != nil {\n\t\tt.Fatalf(\"Failed to save point, got error %v\", err)\n\t}\n\n\tuser2 := *GetUser(\"transaction-save-point-2\", Config{})\n\ttx.Create(&user2)\n\n\tif err := tx.First(&User{}, \"name = ?\", user2.Name).Error; err != nil {\n\t\tt.Fatalf(\"Should find saved record\")\n\t}\n\n\tif err := tx.Commit().Error; err != nil {\n\t\tt.Fatalf(\"Failed to commit, got error %v\", err)\n\t}\n\n\tif err := DB.First(&User{}, \"name = ?\", user.Name).Error; err != nil {\n\t\tt.Fatalf(\"Should find saved record\")\n\t}\n\n\tif err := DB.First(&User{}, \"name = ?\", user1.Name).Error; err == nil {\n\t\tt.Fatalf(\"Should not find rollbacked record\")\n\t}\n\n\tif err := DB.First(&User{}, \"name = ?\", user2.Name).Error; err != nil {\n\t\tt.Fatalf(\"Should find saved record\")\n\t}\n}\n\nfunc TestNestedTransactionWithBlock(t *testing.T) {\n\tvar (\n\t\tuser  = *GetUser(\"transaction-nested\", Config{})\n\t\tuser1 = *GetUser(\"transaction-nested-1\", Config{})\n\t\tuser2 = *GetUser(\"transaction-nested-2\", Config{})\n\t)\n\n\tif err := DB.Transaction(func(tx *gorm.DB) error {\n\t\ttx.Create(&user)\n\n\t\tif err := tx.First(&User{}, \"name = ?\", user.Name).Error; err != nil {\n\t\t\tt.Fatalf(\"Should find saved record\")\n\t\t}\n\n\t\tif err := tx.Transaction(func(tx1 *gorm.DB) error {\n\t\t\ttx1.Create(&user1)\n\n\t\t\tif err := tx1.First(&User{}, \"name = ?\", user1.Name).Error; err != nil {\n\t\t\t\tt.Fatalf(\"Should find saved record\")\n\t\t\t}\n\n\t\t\treturn errors.New(\"rollback\")\n\t\t}); err == nil {\n\t\t\tt.Fatalf(\"nested transaction should returns error\")\n\t\t}\n\n\t\tif err := tx.First(&User{}, \"name = ?\", user1.Name).Error; err == nil {\n\t\t\tt.Fatalf(\"Should not find rollbacked record\")\n\t\t}\n\n\t\tif err := tx.Transaction(func(tx2 *gorm.DB) error {\n\t\t\ttx2.Create(&user2)\n\n\t\t\tif err := tx2.First(&User{}, \"name = ?\", user2.Name).Error; err != nil {\n\t\t\t\tt.Fatalf(\"Should find saved record\")\n\t\t\t}\n\n\t\t\treturn nil\n\t\t}); err != nil {\n\t\t\tt.Fatalf(\"nested transaction returns error: %v\", err)\n\t\t}\n\n\t\tif err := tx.First(&User{}, \"name = ?\", user2.Name).Error; err != nil {\n\t\t\tt.Fatalf(\"Should find saved record\")\n\t\t}\n\t\treturn nil\n\t}); err != nil {\n\t\tt.Fatalf(\"no error should return, but got %v\", err)\n\t}\n\n\tif err := DB.First(&User{}, \"name = ?\", user.Name).Error; err != nil {\n\t\tt.Fatalf(\"Should find saved record\")\n\t}\n\n\tif err := DB.First(&User{}, \"name = ?\", user1.Name).Error; err == nil {\n\t\tt.Fatalf(\"Should not find rollbacked record\")\n\t}\n\n\tif err := DB.First(&User{}, \"name = ?\", user2.Name).Error; err != nil {\n\t\tt.Fatalf(\"Should find saved record\")\n\t}\n}\n\nfunc TestDeeplyNestedTransactionWithBlockAndWrappedCallback(t *testing.T) {\n\ttransaction := func(ctx context.Context, db *gorm.DB, callback func(ctx context.Context, db *gorm.DB) error) error {\n\t\treturn db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {\n\t\t\treturn callback(ctx, tx)\n\t\t})\n\t}\n\tvar (\n\t\tuser  = *GetUser(\"transaction-nested\", Config{})\n\t\tuser1 = *GetUser(\"transaction-nested-1\", Config{})\n\t\tuser2 = *GetUser(\"transaction-nested-2\", Config{})\n\t)\n\n\tif err := transaction(context.Background(), DB, func(ctx context.Context, tx *gorm.DB) error {\n\t\ttx.Create(&user)\n\n\t\tif err := tx.First(&User{}, \"name = ?\", user.Name).Error; err != nil {\n\t\t\tt.Fatalf(\"Should find saved record\")\n\t\t}\n\n\t\tif err := transaction(ctx, tx, func(ctx context.Context, tx1 *gorm.DB) error {\n\t\t\ttx1.Create(&user1)\n\n\t\t\tif err := tx1.First(&User{}, \"name = ?\", user1.Name).Error; err != nil {\n\t\t\t\tt.Fatalf(\"Should find saved record\")\n\t\t\t}\n\n\t\t\tif err := transaction(ctx, tx1, func(ctx context.Context, tx2 *gorm.DB) error {\n\t\t\t\ttx2.Create(&user2)\n\n\t\t\t\tif err := tx2.First(&User{}, \"name = ?\", user2.Name).Error; err != nil {\n\t\t\t\t\tt.Fatalf(\"Should find saved record\")\n\t\t\t\t}\n\n\t\t\t\treturn errors.New(\"inner rollback\")\n\t\t\t}); err == nil {\n\t\t\t\tt.Fatalf(\"nested transaction has no error\")\n\t\t\t}\n\n\t\t\treturn errors.New(\"rollback\")\n\t\t}); err == nil {\n\t\t\tt.Fatalf(\"nested transaction should returns error\")\n\t\t}\n\n\t\tif err := tx.First(&User{}, \"name = ?\", user1.Name).Error; err == nil {\n\t\t\tt.Fatalf(\"Should not find rollbacked record\")\n\t\t}\n\n\t\tif err := tx.First(&User{}, \"name = ?\", user2.Name).Error; err != nil {\n\t\t\tt.Fatalf(\"Should find saved record\")\n\t\t}\n\t\treturn nil\n\t}); err != nil {\n\t\tt.Fatalf(\"no error should return, but got %v\", err)\n\t}\n\n\tif err := DB.First(&User{}, \"name = ?\", user.Name).Error; err != nil {\n\t\tt.Fatalf(\"Should find saved record\")\n\t}\n\n\tif err := DB.First(&User{}, \"name = ?\", user1.Name).Error; err == nil {\n\t\tt.Fatalf(\"Should not find rollbacked parent record\")\n\t}\n\n\tif err := DB.First(&User{}, \"name = ?\", user2.Name).Error; err != nil {\n\t\tt.Fatalf(\"Should not find rollbacked nested record\")\n\t}\n}\n\nfunc TestDisabledNestedTransaction(t *testing.T) {\n\tvar (\n\t\tuser  = *GetUser(\"transaction-nested\", Config{})\n\t\tuser1 = *GetUser(\"transaction-nested-1\", Config{})\n\t\tuser2 = *GetUser(\"transaction-nested-2\", Config{})\n\t)\n\n\tif err := DB.Session(&gorm.Session{DisableNestedTransaction: true}).Transaction(func(tx *gorm.DB) error {\n\t\ttx.Create(&user)\n\n\t\tif err := tx.First(&User{}, \"name = ?\", user.Name).Error; err != nil {\n\t\t\tt.Fatalf(\"Should find saved record\")\n\t\t}\n\n\t\tif err := tx.Transaction(func(tx1 *gorm.DB) error {\n\t\t\ttx1.Create(&user1)\n\n\t\t\tif err := tx1.First(&User{}, \"name = ?\", user1.Name).Error; err != nil {\n\t\t\t\tt.Fatalf(\"Should find saved record\")\n\t\t\t}\n\n\t\t\treturn errors.New(\"rollback\")\n\t\t}); err == nil {\n\t\t\tt.Fatalf(\"nested transaction should returns error\")\n\t\t}\n\n\t\tif err := tx.First(&User{}, \"name = ?\", user1.Name).Error; err != nil {\n\t\t\tt.Fatalf(\"Should not rollback record if disabled nested transaction support\")\n\t\t}\n\n\t\tif err := tx.Transaction(func(tx2 *gorm.DB) error {\n\t\t\ttx2.Create(&user2)\n\n\t\t\tif err := tx2.First(&User{}, \"name = ?\", user2.Name).Error; err != nil {\n\t\t\t\tt.Fatalf(\"Should find saved record\")\n\t\t\t}\n\n\t\t\treturn nil\n\t\t}); err != nil {\n\t\t\tt.Fatalf(\"nested transaction returns error: %v\", err)\n\t\t}\n\n\t\tif err := tx.First(&User{}, \"name = ?\", user2.Name).Error; err != nil {\n\t\t\tt.Fatalf(\"Should find saved record\")\n\t\t}\n\t\treturn nil\n\t}); err != nil {\n\t\tt.Fatalf(\"no error should return, but got %v\", err)\n\t}\n\n\tif err := DB.First(&User{}, \"name = ?\", user.Name).Error; err != nil {\n\t\tt.Fatalf(\"Should find saved record\")\n\t}\n\n\tif err := DB.First(&User{}, \"name = ?\", user1.Name).Error; err != nil {\n\t\tt.Fatalf(\"Should not rollback record if disabled nested transaction support\")\n\t}\n\n\tif err := DB.First(&User{}, \"name = ?\", user2.Name).Error; err != nil {\n\t\tt.Fatalf(\"Should find saved record\")\n\t}\n}\n\nfunc TestTransactionOnClosedConn(t *testing.T) {\n\tDB, err := OpenTestConnection(&gorm.Config{})\n\tif err != nil {\n\t\tt.Fatalf(\"failed to connect database, got error %v\", err)\n\t}\n\trawDB, _ := DB.DB()\n\trawDB.Close()\n\n\tif err := DB.Transaction(func(tx *gorm.DB) error {\n\t\treturn nil\n\t}); err == nil {\n\t\tt.Errorf(\"should returns error when commit with closed conn, got error %v\", err)\n\t}\n\n\tif err := DB.Session(&gorm.Session{PrepareStmt: true}).Transaction(func(tx *gorm.DB) error {\n\t\treturn nil\n\t}); err == nil {\n\t\tt.Errorf(\"should returns error when commit with closed conn, got error %v\", err)\n\t}\n}\n\nfunc TestTransactionWithHooks(t *testing.T) {\n\tuser := GetUser(\"tTestTransactionWithHooks\", Config{Account: true})\n\tDB.Create(&user)\n\n\tvar err error\n\terr = DB.Transaction(func(tx *gorm.DB) error {\n\t\treturn tx.Model(&User{}).Limit(1).Transaction(func(tx2 *gorm.DB) error {\n\t\t\treturn tx2.Scan(&User{}).Error\n\t\t})\n\t})\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\n\t// method with hooks\n\terr = DB.Transaction(func(tx1 *gorm.DB) error {\n\t\t// callMethod do\n\t\ttx2 := tx1.Find(&User{}).Session(&gorm.Session{NewDB: true})\n\t\t// trx in hooks\n\t\treturn tx2.Transaction(func(tx3 *gorm.DB) error {\n\t\t\treturn tx3.Where(\"user_id\", user.ID).Delete(&Account{}).Error\n\t\t})\n\t})\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n}\n\nfunc TestTransactionWithDefaultTimeout(t *testing.T) {\n\tdb, err := OpenTestConnection(&gorm.Config{DefaultTransactionTimeout: 2 * time.Second})\n\tif err != nil {\n\t\tt.Fatalf(\"failed to connect database, got error %v\", err)\n\t}\n\n\ttx := db.Begin()\n\ttime.Sleep(3 * time.Second)\n\tif err = tx.Find(&User{}).Error; err == nil {\n\t\tt.Errorf(\"should return error when transaction timeout, got error %v\", err)\n\t}\n}\n"
  },
  {
    "path": "tests/update_belongs_to_test.go",
    "content": "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 *testing.T) {\n\tuser := *GetUser(\"update-belongs-to\", Config{})\n\n\tif err := DB.Create(&user).Error; err != nil {\n\t\tt.Fatalf(\"errors happened when create: %v\", err)\n\t}\n\n\tuser.Company = Company{Name: \"company-belongs-to-association\"}\n\tuser.Manager = &User{Name: \"manager-belongs-to-association\"}\n\tif err := DB.Save(&user).Error; err != nil {\n\t\tt.Fatalf(\"errors happened when update: %v\", err)\n\t}\n\n\tvar user2 User\n\tDB.Preload(\"Company\").Preload(\"Manager\").Find(&user2, \"id = ?\", user.ID)\n\tCheckUser(t, user2, user)\n\n\tuser.Company.Name += \"new\"\n\tuser.Manager.Name += \"new\"\n\tif err := DB.Save(&user).Error; err != nil {\n\t\tt.Fatalf(\"errors happened when update: %v\", err)\n\t}\n\n\tvar user3 User\n\tDB.Preload(\"Company\").Preload(\"Manager\").Find(&user3, \"id = ?\", user.ID)\n\tCheckUser(t, user2, user3)\n\n\tif err := DB.Session(&gorm.Session{FullSaveAssociations: true}).Save(&user).Error; err != nil {\n\t\tt.Fatalf(\"errors happened when update: %v\", err)\n\t}\n\n\tvar user4 User\n\tDB.Preload(\"Company\").Preload(\"Manager\").Find(&user4, \"id = ?\", user.ID)\n\tCheckUser(t, user4, user)\n\n\tuser.Company.Name += \"new2\"\n\tuser.Manager.Name += \"new2\"\n\tif err := DB.Session(&gorm.Session{FullSaveAssociations: true}).Select(\"`Company`\").Save(&user).Error; err != nil {\n\t\tt.Fatalf(\"errors happened when update: %v\", err)\n\t}\n\n\tvar user5 User\n\tDB.Preload(\"Company\").Preload(\"Manager\").Find(&user5, \"id = ?\", user.ID)\n\tif user5.Manager.Name != user4.Manager.Name {\n\t\tt.Errorf(\"should not update user's manager\")\n\t} else {\n\t\tuser.Manager.Name = user4.Manager.Name\n\t}\n\tCheckUser(t, user, user5)\n}\n"
  },
  {
    "path": "tests/update_has_many_test.go",
    "content": "package tests_test\n\nimport (\n\t\"testing\"\n\n\t\"gorm.io/gorm\"\n\t. \"gorm.io/gorm/utils/tests\"\n)\n\nfunc TestUpdateHasManyAssociations(t *testing.T) {\n\tuser := *GetUser(\"update-has-many\", Config{})\n\n\tif err := DB.Create(&user).Error; err != nil {\n\t\tt.Fatalf(\"errors happened when create: %v\", err)\n\t}\n\n\tuser.Pets = []*Pet{{Name: \"pet1\"}, {Name: \"pet2\"}}\n\tif err := DB.Save(&user).Error; err != nil {\n\t\tt.Fatalf(\"errors happened when update: %v\", err)\n\t}\n\n\tvar user2 User\n\tDB.Preload(\"Pets\").Find(&user2, \"id = ?\", user.ID)\n\tCheckUser(t, user2, user)\n\n\tfor _, pet := range user.Pets {\n\t\tpet.Name += \"new\"\n\t}\n\n\tif err := DB.Save(&user).Error; err != nil {\n\t\tt.Fatalf(\"errors happened when update: %v\", err)\n\t}\n\n\tvar user3 User\n\tDB.Preload(\"Pets\").Find(&user3, \"id = ?\", user.ID)\n\tCheckUser(t, user2, user3)\n\n\tif err := DB.Session(&gorm.Session{FullSaveAssociations: true}).Save(&user).Error; err != nil {\n\t\tt.Fatalf(\"errors happened when update: %v\", err)\n\t}\n\n\tvar user4 User\n\tDB.Preload(\"Pets\").Find(&user4, \"id = ?\", user.ID)\n\tCheckUser(t, user4, user)\n\n\tt.Run(\"Polymorphic\", func(t *testing.T) {\n\t\tuser := *GetUser(\"update-has-many\", Config{})\n\n\t\tif err := DB.Create(&user).Error; err != nil {\n\t\t\tt.Fatalf(\"errors happened when create: %v\", err)\n\t\t}\n\n\t\tuser.Toys = []Toy{{Name: \"toy1\"}, {Name: \"toy2\"}}\n\t\tif err := DB.Save(&user).Error; err != nil {\n\t\t\tt.Fatalf(\"errors happened when update: %v\", err)\n\t\t}\n\n\t\tvar user2 User\n\t\tDB.Preload(\"Toys\").Find(&user2, \"id = ?\", user.ID)\n\t\tCheckUser(t, user2, user)\n\n\t\tfor idx := range user.Toys {\n\t\t\tuser.Toys[idx].Name += \"new\"\n\t\t}\n\n\t\tif err := DB.Save(&user).Error; err != nil {\n\t\t\tt.Fatalf(\"errors happened when update: %v\", err)\n\t\t}\n\n\t\tvar user3 User\n\t\tDB.Preload(\"Toys\").Find(&user3, \"id = ?\", user.ID)\n\t\tCheckUser(t, user2, user3)\n\n\t\tif err := DB.Session(&gorm.Session{FullSaveAssociations: true}).Save(&user).Error; err != nil {\n\t\t\tt.Fatalf(\"errors happened when update: %v\", err)\n\t\t}\n\n\t\tvar user4 User\n\t\tDB.Preload(\"Toys\").Find(&user4, \"id = ?\", user.ID)\n\t\tCheckUser(t, user4, user)\n\t})\n}\n"
  },
  {
    "path": "tests/update_has_one_test.go",
    "content": "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 TestUpdateHasOne(t *testing.T) {\n\tuser := *GetUser(\"update-has-one\", Config{})\n\n\tif err := DB.Create(&user).Error; err != nil {\n\t\tt.Fatalf(\"errors happened when create: %v\", err)\n\t}\n\n\tuser.Account = Account{Number: \"account-has-one-association\"}\n\n\tif err := DB.Save(&user).Error; err != nil {\n\t\tt.Fatalf(\"errors happened when update: %v\", err)\n\t}\n\n\tvar user2 User\n\tDB.Preload(\"Account\").Find(&user2, \"id = ?\", user.ID)\n\tCheckUser(t, user2, user)\n\n\tuser.Account.Number += \"new\"\n\tif err := DB.Save(&user).Error; err != nil {\n\t\tt.Fatalf(\"errors happened when update: %v\", err)\n\t}\n\n\tvar user3 User\n\tDB.Preload(\"Account\").Find(&user3, \"id = ?\", user.ID)\n\n\tCheckUser(t, user2, user3)\n\tlastUpdatedAt := user2.Account.UpdatedAt\n\ttime.Sleep(time.Second)\n\n\tif err := DB.Session(&gorm.Session{FullSaveAssociations: true}).Save(&user).Error; err != nil {\n\t\tt.Fatalf(\"errors happened when update: %v\", err)\n\t}\n\n\tvar user4 User\n\tDB.Preload(\"Account\").Find(&user4, \"id = ?\", user.ID)\n\n\tif lastUpdatedAt.Format(time.RFC3339) == user4.Account.UpdatedAt.Format(time.RFC3339) {\n\t\tt.Fatalf(\"updated at should be updated, but not, old: %v, new %v\", lastUpdatedAt.Format(time.RFC3339), user3.Account.UpdatedAt.Format(time.RFC3339))\n\t} else {\n\t\tuser.Account.UpdatedAt = user4.Account.UpdatedAt\n\t\tCheckUser(t, user4, user)\n\t}\n\n\tt.Run(\"Polymorphic\", func(t *testing.T) {\n\t\tpet := Pet{Name: \"create\"}\n\n\t\tif err := DB.Create(&pet).Error; err != nil {\n\t\t\tt.Fatalf(\"errors happened when create: %v\", err)\n\t\t}\n\n\t\tpet.Toy = Toy{Name: \"Update-HasOneAssociation-Polymorphic\"}\n\n\t\tif err := DB.Save(&pet).Error; err != nil {\n\t\t\tt.Fatalf(\"errors happened when create: %v\", err)\n\t\t}\n\n\t\tvar pet2 Pet\n\t\tDB.Preload(\"Toy\").Find(&pet2, \"id = ?\", pet.ID)\n\t\tCheckPet(t, pet2, pet)\n\n\t\tpet.Toy.Name += \"new\"\n\t\tif err := DB.Save(&pet).Error; err != nil {\n\t\t\tt.Fatalf(\"errors happened when update: %v\", err)\n\t\t}\n\n\t\tvar pet3 Pet\n\t\tDB.Preload(\"Toy\").Find(&pet3, \"id = ?\", pet.ID)\n\t\tCheckPet(t, pet2, pet3)\n\n\t\tif err := DB.Session(&gorm.Session{FullSaveAssociations: true}).Save(&pet).Error; err != nil {\n\t\t\tt.Fatalf(\"errors happened when update: %v\", err)\n\t\t}\n\n\t\tvar pet4 Pet\n\t\tDB.Preload(\"Toy\").Find(&pet4, \"id = ?\", pet.ID)\n\t\tCheckPet(t, pet4, pet)\n\t})\n\n\tt.Run(\"Restriction\", func(t *testing.T) {\n\t\ttype CustomizeAccount struct {\n\t\t\tgorm.Model\n\t\t\tUserID  sql.NullInt64\n\t\t\tNumber  string `gorm:\"<-:create\"`\n\t\t\tNumber2 string\n\t\t}\n\n\t\ttype CustomizeUser struct {\n\t\t\tgorm.Model\n\t\t\tName    string\n\t\t\tAccount CustomizeAccount `gorm:\"foreignkey:UserID\"`\n\t\t}\n\n\t\tDB.Migrator().DropTable(&CustomizeUser{})\n\t\tDB.Migrator().DropTable(&CustomizeAccount{})\n\n\t\tif err := DB.AutoMigrate(&CustomizeUser{}); err != nil {\n\t\t\tt.Fatalf(\"failed to migrate, got error: %v\", err)\n\t\t}\n\t\tif err := DB.AutoMigrate(&CustomizeAccount{}); err != nil {\n\t\t\tt.Fatalf(\"failed to migrate, got error: %v\", err)\n\t\t}\n\n\t\tnumber := \"number-has-one-associations\"\n\t\tcusUser := CustomizeUser{\n\t\t\tName: \"update-has-one-associations\",\n\t\t\tAccount: CustomizeAccount{\n\t\t\t\tNumber:  number,\n\t\t\t\tNumber2: number,\n\t\t\t},\n\t\t}\n\n\t\tif err := DB.Create(&cusUser).Error; err != nil {\n\t\t\tt.Fatalf(\"errors happened when create: %v\", err)\n\t\t}\n\t\tcusUser.Account.Number += \"-update\"\n\t\tcusUser.Account.Number2 += \"-update\"\n\t\tif err := DB.Session(&gorm.Session{FullSaveAssociations: true}).Updates(&cusUser).Error; err != nil {\n\t\t\tt.Fatalf(\"errors happened when create: %v\", err)\n\t\t}\n\n\t\tvar account2 CustomizeAccount\n\t\tDB.Find(&account2, \"user_id = ?\", cusUser.ID)\n\t\tAssertEqual(t, account2.Number, number)\n\t\tAssertEqual(t, account2.Number2, cusUser.Account.Number2)\n\t})\n}\n"
  },
  {
    "path": "tests/update_many2many_test.go",
    "content": "package tests_test\n\nimport (\n\t\"testing\"\n\n\t\"gorm.io/gorm\"\n\t. \"gorm.io/gorm/utils/tests\"\n)\n\nfunc TestUpdateMany2ManyAssociations(t *testing.T) {\n\tuser := *GetUser(\"update-many2many\", Config{})\n\n\tif err := DB.Create(&user).Error; err != nil {\n\t\tt.Fatalf(\"errors happened when create: %v\", err)\n\t}\n\n\tuser.Languages = []Language{{Code: \"zh-CN\", Name: \"Chinese\"}, {Code: \"en\", Name: \"English\"}}\n\tfor _, lang := range user.Languages {\n\t\tDB.Create(&lang)\n\t}\n\tuser.Friends = []*User{{Name: \"friend-1\"}, {Name: \"friend-2\"}}\n\n\tif err := DB.Save(&user).Error; err != nil {\n\t\tt.Fatalf(\"errors happened when update: %v\", err)\n\t}\n\n\tvar user2 User\n\tDB.Preload(\"Languages\").Preload(\"Friends\").Find(&user2, \"id = ?\", user.ID)\n\tCheckUser(t, user2, user)\n\n\tfor idx := range user.Friends {\n\t\tuser.Friends[idx].Name += \"new\"\n\t}\n\n\tfor idx := range user.Languages {\n\t\tuser.Languages[idx].Name += \"new\"\n\t}\n\n\tif err := DB.Save(&user).Error; err != nil {\n\t\tt.Fatalf(\"errors happened when update: %v\", err)\n\t}\n\n\tvar user3 User\n\tDB.Preload(\"Languages\").Preload(\"Friends\").Find(&user3, \"id = ?\", user.ID)\n\tCheckUser(t, user2, user3)\n\n\tif err := DB.Session(&gorm.Session{FullSaveAssociations: true}).Save(&user).Error; err != nil {\n\t\tt.Fatalf(\"errors happened when update: %v\", err)\n\t}\n\n\tvar user4 User\n\tDB.Preload(\"Languages\").Preload(\"Friends\").Find(&user4, \"id = ?\", user.ID)\n\tCheckUser(t, user4, user)\n}\n"
  },
  {
    "path": "tests/update_test.go",
    "content": "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/clause\"\n\t\"gorm.io/gorm/utils\"\n\t. \"gorm.io/gorm/utils/tests\"\n)\n\nfunc TestUpdate(t *testing.T) {\n\tvar (\n\t\tusers = []*User{\n\t\t\tGetUser(\"update-1\", Config{}),\n\t\t\tGetUser(\"update-2\", Config{}),\n\t\t\tGetUser(\"update-3\", Config{}),\n\t\t}\n\t\tuser          = users[1]\n\t\tlastUpdatedAt time.Time\n\t)\n\n\tcheckUpdatedAtChanged := func(name string, n time.Time) {\n\t\tif n.UnixNano() == lastUpdatedAt.UnixNano() {\n\t\t\tt.Errorf(\"%v: user's updated at should be changed, but got %v, was %v\", name, n, lastUpdatedAt)\n\t\t}\n\t\tlastUpdatedAt = n\n\t}\n\n\tcheckOtherData := func(name string) {\n\t\tvar first, last User\n\t\tif err := DB.Where(\"id = ?\", users[0].ID).First(&first).Error; err != nil {\n\t\t\tt.Errorf(\"errors happened when query before user: %v\", err)\n\t\t}\n\t\tCheckUser(t, first, *users[0])\n\n\t\tif err := DB.Where(\"id = ?\", users[2].ID).First(&last).Error; err != nil {\n\t\t\tt.Errorf(\"errors happened when query after user: %v\", err)\n\t\t}\n\t\tCheckUser(t, last, *users[2])\n\t}\n\n\tif err := DB.Create(&users).Error; err != nil {\n\t\tt.Fatalf(\"errors happened when create: %v\", err)\n\t} else if user.ID == 0 {\n\t\tt.Fatalf(\"user's primary value should not zero, %v\", user.ID)\n\t} else if user.UpdatedAt.IsZero() {\n\t\tt.Fatalf(\"user's updated at should not zero, %v\", user.UpdatedAt)\n\t}\n\tlastUpdatedAt = user.UpdatedAt\n\n\tif err := DB.Model(user).Update(\"Age\", 10).Error; err != nil {\n\t\tt.Errorf(\"errors happened when update: %v\", err)\n\t} else if user.Age != 10 {\n\t\tt.Errorf(\"Age should equals to 10, but got %v\", user.Age)\n\t}\n\tcheckUpdatedAtChanged(\"Update\", user.UpdatedAt)\n\tcheckOtherData(\"Update\")\n\n\tvar result User\n\tif err := DB.Where(\"id = ?\", user.ID).First(&result).Error; err != nil {\n\t\tt.Errorf(\"errors happened when query: %v\", err)\n\t} else {\n\t\tCheckUser(t, result, *user)\n\t}\n\n\tvalues := map[string]interface{}{\"Active\": true, \"age\": 5}\n\tif res := DB.Model(user).Updates(values); res.Error != nil {\n\t\tt.Errorf(\"errors happened when update: %v\", res.Error)\n\t} else if res.RowsAffected != 1 {\n\t\tt.Errorf(\"rows affected should be 1, but got : %v\", res.RowsAffected)\n\t} else if user.Age != 5 {\n\t\tt.Errorf(\"Age should equals to 5, but got %v\", user.Age)\n\t} else if user.Active != true {\n\t\tt.Errorf(\"Active should be true, but got %v\", user.Active)\n\t}\n\tcheckUpdatedAtChanged(\"Updates with map\", user.UpdatedAt)\n\tcheckOtherData(\"Updates with map\")\n\n\tvar result2 User\n\tif err := DB.Where(\"id = ?\", user.ID).First(&result2).Error; err != nil {\n\t\tt.Errorf(\"errors happened when query: %v\", err)\n\t} else {\n\t\tCheckUser(t, result2, *user)\n\t}\n\n\tif err := DB.Model(user).Updates(User{Age: 2}).Error; err != nil {\n\t\tt.Errorf(\"errors happened when update: %v\", err)\n\t} else if user.Age != 2 {\n\t\tt.Errorf(\"Age should equals to 2, but got %v\", user.Age)\n\t}\n\tcheckUpdatedAtChanged(\"Updates with struct\", user.UpdatedAt)\n\tcheckOtherData(\"Updates with struct\")\n\n\tvar result3 User\n\tif err := DB.Where(\"id = ?\", user.ID).First(&result3).Error; err != nil {\n\t\tt.Errorf(\"errors happened when query: %v\", err)\n\t} else {\n\t\tCheckUser(t, result3, *user)\n\t}\n\n\tuser.Active = false\n\tuser.Age = 1\n\tif err := DB.Save(user).Error; err != nil {\n\t\tt.Errorf(\"errors happened when update: %v\", err)\n\t} else if user.Age != 1 {\n\t\tt.Errorf(\"Age should equals to 1, but got %v\", user.Age)\n\t} else if user.Active != false {\n\t\tt.Errorf(\"Active should equals to false, but got %v\", user.Active)\n\t}\n\tcheckUpdatedAtChanged(\"Save\", user.UpdatedAt)\n\tcheckOtherData(\"Save\")\n\n\tvar result4 User\n\tif err := DB.Where(\"id = ?\", user.ID).First(&result4).Error; err != nil {\n\t\tt.Errorf(\"errors happened when query: %v\", err)\n\t} else {\n\t\tCheckUser(t, result4, *user)\n\t}\n\n\tif rowsAffected := DB.Model([]User{result4}).Where(\"age > 0\").Update(\"name\", \"jinzhu\").RowsAffected; rowsAffected != 1 {\n\t\tt.Errorf(\"should only update one record, but got %v\", rowsAffected)\n\t}\n\n\tif rowsAffected := DB.Model(users).Where(\"age > 0\").Update(\"name\", \"jinzhu\").RowsAffected; rowsAffected != 3 {\n\t\tt.Errorf(\"should only update one record, but got %v\", rowsAffected)\n\t}\n}\n\nfunc TestUpdates(t *testing.T) {\n\tusers := []*User{\n\t\tGetUser(\"updates_01\", Config{}),\n\t\tGetUser(\"updates_02\", Config{}),\n\t}\n\n\tDB.Create(&users)\n\tlastUpdatedAt := users[0].UpdatedAt\n\n\t// update with map\n\tif res := DB.Model(users[0]).Updates(map[string]interface{}{\"name\": \"updates_01_newname\", \"age\": 100}); res.Error != nil || res.RowsAffected != 1 {\n\t\tt.Errorf(\"Failed to update users\")\n\t}\n\n\tif users[0].Name != \"updates_01_newname\" || users[0].Age != 100 {\n\t\tt.Errorf(\"Record should be updated also with map\")\n\t}\n\n\tif users[0].UpdatedAt.UnixNano() == lastUpdatedAt.UnixNano() {\n\t\tt.Errorf(\"User's updated at should be changed, but got %v, was %v\", users[0].UpdatedAt.UnixNano(), lastUpdatedAt)\n\t}\n\n\t// user2 should not be updated\n\tvar user1, user2 User\n\tDB.First(&user1, users[0].ID)\n\tDB.First(&user2, users[1].ID)\n\tCheckUser(t, user1, *users[0])\n\tCheckUser(t, user2, *users[1])\n\n\t// update with struct\n\ttime.Sleep(1 * time.Second)\n\tDB.Table(\"users\").Where(\"name in ?\", []string{users[1].Name}).Updates(User{Name: \"updates_02_newname\"})\n\n\tvar user3 User\n\tif err := DB.First(&user3, \"name = ?\", \"updates_02_newname\").Error; err != nil {\n\t\tt.Errorf(\"User2's name should be updated\")\n\t}\n\n\tif user2.UpdatedAt.Format(time.RFC1123Z) == user3.UpdatedAt.Format(time.RFC1123Z) {\n\t\tt.Errorf(\"User's updated at should be changed, old %v, new %v\", user2.UpdatedAt.Format(time.RFC1123Z), user3.UpdatedAt.Format(time.RFC1123Z))\n\t}\n\n\t// update with gorm exprs\n\tif err := DB.Model(&user3).Updates(map[string]interface{}{\"age\": gorm.Expr(\"age + ?\", 100)}).Error; err != nil {\n\t\tt.Errorf(\"Not error should happen when updating with gorm expr, but got %v\", err)\n\t}\n\tvar user4 User\n\tDB.First(&user4, user3.ID)\n\n\tuser3.Age += 100\n\tAssertObjEqual(t, user4, user3, \"UpdatedAt\", \"Age\")\n}\n\nfunc TestUpdateColumn(t *testing.T) {\n\tusers := []*User{\n\t\tGetUser(\"update_column_01\", Config{}),\n\t\tGetUser(\"update_column_02\", Config{}),\n\t}\n\n\tDB.Create(&users)\n\tlastUpdatedAt := users[1].UpdatedAt\n\n\t// update with map\n\tDB.Model(users[1]).UpdateColumns(map[string]interface{}{\"name\": \"update_column_02_newname\", \"age\": 100})\n\tif users[1].Name != \"update_column_02_newname\" || users[1].Age != 100 {\n\t\tt.Errorf(\"user 2 should be updated with update column\")\n\t}\n\tAssertEqual(t, lastUpdatedAt.UnixNano(), users[1].UpdatedAt.UnixNano())\n\n\t// user2 should not be updated\n\tvar user1, user2 User\n\tDB.First(&user1, users[0].ID)\n\tDB.First(&user2, users[1].ID)\n\tCheckUser(t, user1, *users[0])\n\tCheckUser(t, user2, *users[1])\n\n\tDB.Model(users[1]).UpdateColumn(\"name\", \"update_column_02_newnew\").UpdateColumn(\"age\", 19)\n\tAssertEqual(t, lastUpdatedAt.UnixNano(), users[1].UpdatedAt.UnixNano())\n\n\tif users[1].Name != \"update_column_02_newnew\" {\n\t\tt.Errorf(\"user 2's name should be updated, but got %v\", users[1].Name)\n\t}\n\n\tif users[1].Age != 19 {\n\t\tt.Errorf(\"user 2's name should be updated, but got %v\", users[1].Age)\n\t}\n\n\tDB.Model(users[1]).UpdateColumn(\"age\", gorm.Expr(\"age + 100 - 50\"))\n\tvar user3 User\n\tDB.First(&user3, users[1].ID)\n\n\tusers[1].Age += 50\n\tCheckUser(t, user3, *users[1])\n\n\t// update with struct\n\tDB.Model(users[1]).UpdateColumns(User{Name: \"update_column_02_newnew2\", Age: 200})\n\tif users[1].Name != \"update_column_02_newnew2\" || users[1].Age != 200 {\n\t\tt.Errorf(\"user 2 should be updated with update column\")\n\t}\n\tAssertEqual(t, lastUpdatedAt.UnixNano(), users[1].UpdatedAt.UnixNano())\n\n\t// user2 should not be updated\n\tvar user5, user6 User\n\tDB.First(&user5, users[0].ID)\n\tDB.First(&user6, users[1].ID)\n\tCheckUser(t, user5, *users[0])\n\tCheckUser(t, user6, *users[1])\n}\n\nfunc TestBlockGlobalUpdate(t *testing.T) {\n\tif err := DB.Model(&User{}).Update(\"name\", \"jinzhu\").Error; err == nil || !errors.Is(err, gorm.ErrMissingWhereClause) {\n\t\tt.Errorf(\"should returns missing WHERE clause while updating error, got err %v\", err)\n\t}\n\n\tif err := DB.Session(&gorm.Session{AllowGlobalUpdate: true}).Model(&User{}).Update(\"name\", \"jinzhu\").Error; err != nil {\n\t\tt.Errorf(\"should returns no error while enable global update, but got err %v\", err)\n\t}\n}\n\nfunc TestSelectWithUpdate(t *testing.T) {\n\tuser := *GetUser(\"select_update\", Config{Account: true, Pets: 3, Toys: 3, Company: true, Manager: true, Team: 3, Languages: 3, Friends: 4})\n\tDB.Create(&user)\n\n\tvar result User\n\tDB.First(&result, user.ID)\n\n\tuser2 := *GetUser(\"select_update_new\", Config{Account: true, Pets: 3, Toys: 3, Company: true, Manager: true, Team: 3, Languages: 3, Friends: 4})\n\tresult.Name = user2.Name\n\tresult.Age = 50\n\tresult.Account = user2.Account\n\tresult.Pets = user2.Pets\n\tresult.Toys = user2.Toys\n\tresult.Company = user2.Company\n\tresult.Manager = user2.Manager\n\tresult.Team = user2.Team\n\tresult.Languages = user2.Languages\n\tresult.Friends = user2.Friends\n\n\tDB.Select(\"Name\", \"Account\", \"Toys\", \"Manager\", \"ManagerID\", \"Languages\").Save(&result)\n\n\tvar result2 User\n\tDB.Preload(\"Account\").Preload(\"Pets\").Preload(\"Toys\").Preload(\"Company\").Preload(\"Manager\").Preload(\"Team\").Preload(\"Languages\").Preload(\"Friends\").First(&result2, user.ID)\n\n\tresult.Languages = append(user.Languages, result.Languages...)\n\tresult.Toys = append(user.Toys, result.Toys...)\n\n\tsort.Slice(result.Languages, func(i, j int) bool {\n\t\treturn strings.Compare(result.Languages[i].Code, result.Languages[j].Code) > 0\n\t})\n\n\tsort.Slice(result.Toys, func(i, j int) bool {\n\t\treturn result.Toys[i].ID < result.Toys[j].ID\n\t})\n\n\tsort.Slice(result2.Languages, func(i, j int) bool {\n\t\treturn strings.Compare(result2.Languages[i].Code, result2.Languages[j].Code) > 0\n\t})\n\n\tsort.Slice(result2.Toys, func(i, j int) bool {\n\t\treturn result2.Toys[i].ID < result2.Toys[j].ID\n\t})\n\n\tAssertObjEqual(t, result2, result, \"Name\", \"Account\", \"Toys\", \"Manager\", \"ManagerID\", \"Languages\")\n\n\tDB.Model(&result).Select(\"Name\", \"Age\").Updates(User{Name: \"update_with_select\"})\n\tif result.Age != 0 || result.Name != \"update_with_select\" {\n\t\tt.Fatalf(\"Failed to update struct with select, got %+v\", result)\n\t}\n\tAssertObjEqual(t, result, user, \"UpdatedAt\")\n\n\tvar result3 User\n\tDB.First(&result3, result.ID)\n\tAssertObjEqual(t, result, result3, \"Name\", \"Age\", \"UpdatedAt\")\n\n\tDB.Model(&result).Select(\"Name\", \"Age\", \"UpdatedAt\").Updates(User{Name: \"update_with_select\"})\n\n\tif utils.AssertEqual(result.UpdatedAt, user.UpdatedAt) {\n\t\tt.Fatalf(\"Update struct should update UpdatedAt, was %+v, got %+v\", result.UpdatedAt, user.UpdatedAt)\n\t}\n\n\tAssertObjEqual(t, result, User{Name: \"update_with_select\"}, \"Name\", \"Age\")\n}\n\nfunc TestSelectWithUpdateWithMap(t *testing.T) {\n\tuser := *GetUser(\"select_update_map\", Config{Account: true, Pets: 3, Toys: 3, Company: true, Manager: true, Team: 3, Languages: 3, Friends: 4})\n\tDB.Create(&user)\n\n\tvar result User\n\tDB.First(&result, user.ID)\n\n\tuser2 := *GetUser(\"select_update_map_new\", Config{Account: true, Pets: 3, Toys: 3, Company: true, Manager: true, Team: 3, Languages: 3, Friends: 4})\n\tupdateValues := map[string]interface{}{\n\t\t\"Name\":      user2.Name,\n\t\t\"Age\":       50,\n\t\t\"Account\":   user2.Account,\n\t\t\"Pets\":      user2.Pets,\n\t\t\"Toys\":      user2.Toys,\n\t\t\"Company\":   user2.Company,\n\t\t\"Manager\":   user2.Manager,\n\t\t\"Team\":      user2.Team,\n\t\t\"Languages\": user2.Languages,\n\t\t\"Friends\":   user2.Friends,\n\t}\n\n\tDB.Model(&result).Omit(\"name\", \"updated_at\").Updates(updateValues)\n\n\tvar result2 User\n\tDB.Preload(\"Account\").Preload(\"Pets\").Preload(\"Toys\").Preload(\"Company\").Preload(\"Manager\").Preload(\"Team\").Preload(\"Languages\").Preload(\"Friends\").First(&result2, user.ID)\n\n\tresult.Languages = append(user.Languages, result.Languages...)\n\tresult.Toys = append(user.Toys, result.Toys...)\n\n\tsort.Slice(result.Languages, func(i, j int) bool {\n\t\treturn strings.Compare(result.Languages[i].Code, result.Languages[j].Code) > 0\n\t})\n\n\tsort.Slice(result.Toys, func(i, j int) bool {\n\t\treturn result.Toys[i].ID < result.Toys[j].ID\n\t})\n\n\tsort.Slice(result2.Languages, func(i, j int) bool {\n\t\treturn strings.Compare(result2.Languages[i].Code, result2.Languages[j].Code) > 0\n\t})\n\n\tsort.Slice(result2.Toys, func(i, j int) bool {\n\t\treturn result2.Toys[i].ID < result2.Toys[j].ID\n\t})\n\n\tAssertObjEqual(t, result2, result, \"Name\", \"Account\", \"Toys\", \"Manager\", \"ManagerID\", \"Languages\")\n}\n\nfunc TestWithUpdateWithInvalidMap(t *testing.T) {\n\tuser := *GetUser(\"update_with_invalid_map\", Config{})\n\tDB.Create(&user)\n\n\tif err := DB.Model(&user).Updates(map[string]string{\"name\": \"jinzhu\"}).Error; !errors.Is(err, gorm.ErrInvalidData) {\n\t\tt.Errorf(\"should returns error for unsupported updating data\")\n\t}\n}\n\nfunc TestOmitWithUpdate(t *testing.T) {\n\tuser := *GetUser(\"omit_update\", Config{Account: true, Pets: 3, Toys: 3, Company: true, Manager: true, Team: 3, Languages: 3, Friends: 4})\n\tDB.Create(&user)\n\n\tvar result User\n\tDB.First(&result, user.ID)\n\n\tuser2 := *GetUser(\"omit_update_new\", Config{Account: true, Pets: 3, Toys: 3, Company: true, Manager: true, Team: 3, Languages: 3, Friends: 4})\n\tresult.Name = user2.Name\n\tresult.Age = 50\n\tresult.Account = user2.Account\n\tresult.Pets = user2.Pets\n\tresult.Toys = user2.Toys\n\tresult.Company = user2.Company\n\tresult.Manager = user2.Manager\n\tresult.Team = user2.Team\n\tresult.Languages = user2.Languages\n\tresult.Friends = user2.Friends\n\n\tDB.Omit(\"Name\", \"Account\", \"Toys\", \"Manager\", \"ManagerID\", \"Languages\").Save(&result)\n\n\tvar result2 User\n\tDB.Preload(\"Account\").Preload(\"Pets\").Preload(\"Toys\").Preload(\"Company\").Preload(\"Manager\").Preload(\"Team\").Preload(\"Languages\").Preload(\"Friends\").First(&result2, user.ID)\n\n\tresult.Pets = append(user.Pets, result.Pets...)\n\tresult.Team = append(user.Team, result.Team...)\n\tresult.Friends = append(user.Friends, result.Friends...)\n\n\tsort.Slice(result.Pets, func(i, j int) bool {\n\t\treturn result.Pets[i].ID < result.Pets[j].ID\n\t})\n\tsort.Slice(result.Team, func(i, j int) bool {\n\t\treturn result.Team[i].ID < result.Team[j].ID\n\t})\n\tsort.Slice(result.Friends, func(i, j int) bool {\n\t\treturn result.Friends[i].ID < result.Friends[j].ID\n\t})\n\tsort.Slice(result2.Pets, func(i, j int) bool {\n\t\treturn result2.Pets[i].ID < result2.Pets[j].ID\n\t})\n\tsort.Slice(result2.Team, func(i, j int) bool {\n\t\treturn result2.Team[i].ID < result2.Team[j].ID\n\t})\n\tsort.Slice(result2.Friends, func(i, j int) bool {\n\t\treturn result2.Friends[i].ID < result2.Friends[j].ID\n\t})\n\n\tAssertObjEqual(t, result2, result, \"Age\", \"Pets\", \"Company\", \"CompanyID\", \"Team\", \"Friends\")\n}\n\nfunc TestOmitWithUpdateWithMap(t *testing.T) {\n\tuser := *GetUser(\"omit_update_map\", Config{Account: true, Pets: 3, Toys: 3, Company: true, Manager: true, Team: 3, Languages: 3, Friends: 4})\n\tDB.Create(&user)\n\n\tvar result User\n\tDB.First(&result, user.ID)\n\n\tuser2 := *GetUser(\"omit_update_map_new\", Config{Account: true, Pets: 3, Toys: 3, Company: true, Manager: true, Team: 3, Languages: 3, Friends: 4})\n\tupdateValues := map[string]interface{}{\n\t\t\"Name\":      user2.Name,\n\t\t\"Age\":       50,\n\t\t\"Account\":   user2.Account,\n\t\t\"Pets\":      user2.Pets,\n\t\t\"Toys\":      user2.Toys,\n\t\t\"Company\":   user2.Company,\n\t\t\"Manager\":   user2.Manager,\n\t\t\"Team\":      user2.Team,\n\t\t\"Languages\": user2.Languages,\n\t\t\"Friends\":   user2.Friends,\n\t}\n\n\tDB.Model(&result).Omit(\"Name\", \"Account\", \"Toys\", \"Manager\", \"ManagerID\", \"Languages\").Updates(updateValues)\n\n\tvar result2 User\n\tDB.Preload(\"Account\").Preload(\"Pets\").Preload(\"Toys\").Preload(\"Company\").Preload(\"Manager\").Preload(\"Team\").Preload(\"Languages\").Preload(\"Friends\").First(&result2, user.ID)\n\n\tresult.Pets = append(user.Pets, result.Pets...)\n\tresult.Team = append(user.Team, result.Team...)\n\tresult.Friends = append(user.Friends, result.Friends...)\n\n\tsort.Slice(result.Pets, func(i, j int) bool {\n\t\treturn result.Pets[i].ID < result.Pets[j].ID\n\t})\n\tsort.Slice(result.Team, func(i, j int) bool {\n\t\treturn result.Team[i].ID < result.Team[j].ID\n\t})\n\tsort.Slice(result.Friends, func(i, j int) bool {\n\t\treturn result.Friends[i].ID < result.Friends[j].ID\n\t})\n\tsort.Slice(result2.Pets, func(i, j int) bool {\n\t\treturn result2.Pets[i].ID < result2.Pets[j].ID\n\t})\n\tsort.Slice(result2.Team, func(i, j int) bool {\n\t\treturn result2.Team[i].ID < result2.Team[j].ID\n\t})\n\tsort.Slice(result2.Friends, func(i, j int) bool {\n\t\treturn result2.Friends[i].ID < result2.Friends[j].ID\n\t})\n\n\tAssertObjEqual(t, result2, result, \"Age\", \"Pets\", \"Company\", \"CompanyID\", \"Team\", \"Friends\")\n}\n\nfunc TestSelectWithUpdateColumn(t *testing.T) {\n\tuser := *GetUser(\"select_with_update_column\", Config{Account: true, Pets: 3, Toys: 3, Company: true, Manager: true, Team: 3, Languages: 3, Friends: 4})\n\tDB.Create(&user)\n\n\tupdateValues := map[string]interface{}{\"Name\": \"new_name\", \"Age\": 50}\n\n\tvar result User\n\tDB.First(&result, user.ID)\n\n\ttime.Sleep(time.Second)\n\tlastUpdatedAt := result.UpdatedAt\n\tDB.Model(&result).Select(\"Name\").Updates(updateValues)\n\n\tvar result2 User\n\tDB.First(&result2, user.ID)\n\n\tif lastUpdatedAt.Format(time.RFC3339Nano) == result2.UpdatedAt.Format(time.RFC3339Nano) {\n\t\tt.Errorf(\"UpdatedAt should be changed\")\n\t}\n\n\tif result2.Name == user.Name || result2.Age != user.Age {\n\t\tt.Errorf(\"Should only update users with name column\")\n\t}\n}\n\nfunc TestOmitWithUpdateColumn(t *testing.T) {\n\tuser := *GetUser(\"omit_with_update_column\", Config{Account: true, Pets: 3, Toys: 3, Company: true, Manager: true, Team: 3, Languages: 3, Friends: 4})\n\tDB.Create(&user)\n\n\tupdateValues := map[string]interface{}{\"Name\": \"new_name\", \"Age\": 50}\n\n\tvar result User\n\tDB.First(&result, user.ID)\n\tDB.Model(&result).Omit(\"Name\").UpdateColumns(updateValues)\n\n\tvar result2 User\n\tDB.First(&result2, user.ID)\n\n\tif result2.Name != user.Name || result2.Age == user.Age {\n\t\tt.Errorf(\"Should only update users with name column\")\n\t}\n}\n\nfunc TestUpdateColumnsSkipsAssociations(t *testing.T) {\n\tuser := *GetUser(\"update_column_skips_association\", Config{})\n\tDB.Create(&user)\n\n\t// Update a single field of the user and verify that the changed address is not stored.\n\tnewAge := uint(100)\n\tuser.Account.Number = \"new_account_number\"\n\tdb := DB.Model(&user).UpdateColumns(User{Age: newAge})\n\n\tif db.RowsAffected != 1 {\n\t\tt.Errorf(\"Expected RowsAffected=1 but instead RowsAffected=%v\", db.RowsAffected)\n\t}\n\n\t// Verify that Age now=`newAge`.\n\tresult := &User{}\n\tresult.ID = user.ID\n\tDB.Preload(\"Account\").First(result)\n\n\tif result.Age != newAge {\n\t\tt.Errorf(\"Expected freshly queried user to have Age=%v but instead found Age=%v\", newAge, result.Age)\n\t}\n\n\tif result.Account.Number != user.Account.Number {\n\t\tt.Errorf(\"account number should not been changed, expects: %v, got %v\", user.Account.Number, result.Account.Number)\n\t}\n}\n\nfunc TestUpdatesWithBlankValues(t *testing.T) {\n\tuser := *GetUser(\"updates_with_blank_value\", Config{})\n\tDB.Save(&user)\n\n\tvar user2 User\n\tuser2.ID = user.ID\n\tDB.Model(&user2).Updates(&User{Age: 100})\n\n\tvar result User\n\tDB.First(&result, user.ID)\n\n\tif result.Name != user.Name || result.Age != 100 {\n\t\tt.Errorf(\"user's name should not be updated\")\n\t}\n}\n\nfunc TestUpdatesTableWithIgnoredValues(t *testing.T) {\n\ttype ElementWithIgnoredField struct {\n\t\tId           int64\n\t\tValue        string\n\t\tIgnoredField int64 `gorm:\"-\"`\n\t}\n\tDB.Migrator().DropTable(&ElementWithIgnoredField{})\n\tDB.AutoMigrate(&ElementWithIgnoredField{})\n\n\telem := ElementWithIgnoredField{Value: \"foo\", IgnoredField: 10}\n\tDB.Save(&elem)\n\n\tDB.Model(&ElementWithIgnoredField{}).\n\t\tWhere(\"id = ?\", elem.Id).\n\t\tUpdates(&ElementWithIgnoredField{Value: \"bar\", IgnoredField: 100})\n\n\tvar result ElementWithIgnoredField\n\tif err := DB.First(&result, elem.Id).Error; err != nil {\n\t\tt.Errorf(\"error getting an element from database: %s\", err.Error())\n\t}\n\n\tif result.IgnoredField != 0 {\n\t\tt.Errorf(\"element's ignored field should not be updated\")\n\t}\n}\n\nfunc TestUpdateFromSubQuery(t *testing.T) {\n\tuser := *GetUser(\"update_from_sub_query\", Config{Company: true})\n\tif err := DB.Create(&user).Error; err != nil {\n\t\tt.Errorf(\"failed to create user, got error: %v\", err)\n\t}\n\n\tif err := DB.Model(&user).Update(\"name\", DB.Model(&Company{}).Select(\"name\").Where(\"companies.id = users.company_id\")).Error; err != nil {\n\t\tt.Errorf(\"failed to update with sub query, got error %v\", err)\n\t}\n\n\tvar result User\n\tDB.First(&result, user.ID)\n\n\tif result.Name != user.Company.Name {\n\t\tt.Errorf(\"name should be %v, but got %v\", user.Company.Name, result.Name)\n\t}\n\n\tDB.Model(&user.Company).Update(\"Name\", \"new company name\")\n\tif err := DB.Table(\"users\").Where(\"1 = 1\").Update(\"name\", DB.Table(\"companies\").Select(\"name\").Where(\"companies.id = users.company_id\")).Error; err != nil {\n\t\tt.Errorf(\"failed to update with sub query, got error %v\", err)\n\t}\n\n\tDB.First(&result, user.ID)\n\tif result.Name != \"new company name\" {\n\t\tt.Errorf(\"name should be %v, but got %v\", user.Company.Name, result.Name)\n\t}\n}\n\nfunc TestIdempotentSave(t *testing.T) {\n\tcreate := Company{\n\t\tName: \"company_idempotent\",\n\t}\n\tDB.Create(&create)\n\n\tvar company Company\n\tif err := DB.Find(&company, \"id = ?\", create.ID).Error; err != nil {\n\t\tt.Fatalf(\"failed to find created company, got err: %v\", err)\n\t}\n\n\tif err := DB.Save(&company).Error; err != nil || company.ID != create.ID {\n\t\tt.Errorf(\"failed to save company, got err: %v\", err)\n\t}\n\tif err := DB.Save(&company).Error; err != nil || company.ID != create.ID {\n\t\tt.Errorf(\"failed to save company, got err: %v\", err)\n\t}\n}\n\nfunc TestSave(t *testing.T) {\n\tuser := *GetUser(\"save\", Config{})\n\tDB.Create(&user)\n\n\tif err := DB.First(&User{}, \"name = ?\", \"save\").Error; err != nil {\n\t\tt.Fatalf(\"failed to find created user\")\n\t}\n\n\tuser.Name = \"save2\"\n\tDB.Save(&user)\n\n\tvar result User\n\tif err := DB.First(&result, \"name = ?\", \"save2\").Error; err != nil || result.ID != user.ID {\n\t\tt.Fatalf(\"failed to find updated user\")\n\t}\n\n\tuser2 := *GetUser(\"save2\", Config{})\n\tDB.Create(&user2)\n\n\ttime.Sleep(time.Second)\n\tuser1UpdatedAt := result.UpdatedAt\n\tuser2UpdatedAt := user2.UpdatedAt\n\tusers := []*User{&result, &user2}\n\tDB.Save(&users)\n\n\tif user1UpdatedAt.Format(time.RFC1123Z) == result.UpdatedAt.Format(time.RFC1123Z) {\n\t\tt.Fatalf(\"user's updated at should be changed, expects: %+v, got: %+v\", user1UpdatedAt, result.UpdatedAt)\n\t}\n\n\tif user2UpdatedAt.Format(time.RFC1123Z) == user2.UpdatedAt.Format(time.RFC1123Z) {\n\t\tt.Fatalf(\"user's updated at should be changed, expects: %+v, got: %+v\", user2UpdatedAt, user2.UpdatedAt)\n\t}\n\n\tDB.First(&result)\n\tif user1UpdatedAt.Format(time.RFC1123Z) == result.UpdatedAt.Format(time.RFC1123Z) {\n\t\tt.Fatalf(\"user's updated at should be changed after reload, expects: %+v, got: %+v\", user1UpdatedAt, result.UpdatedAt)\n\t}\n\n\tDB.First(&user2)\n\tif user2UpdatedAt.Format(time.RFC1123Z) == user2.UpdatedAt.Format(time.RFC1123Z) {\n\t\tt.Fatalf(\"user2's updated at should be changed after reload, expects: %+v, got: %+v\", user2UpdatedAt, user2.UpdatedAt)\n\t}\n\n\tdryDB := DB.Session(&gorm.Session{DryRun: true})\n\tstmt := dryDB.Save(&user).Statement\n\tif !regexp.MustCompile(`.users.\\..deleted_at. IS NULL`).MatchString(stmt.SQL.String()) {\n\t\tt.Fatalf(\"invalid updating SQL, got %v\", stmt.SQL.String())\n\t}\n\n\tdryDB = DB.Session(&gorm.Session{DryRun: true})\n\tstmt = dryDB.Unscoped().Save(&user).Statement\n\tif !regexp.MustCompile(`WHERE .id. = [^ ]+$`).MatchString(stmt.SQL.String()) {\n\t\tt.Fatalf(\"invalid updating SQL, got %v\", stmt.SQL.String())\n\t}\n\n\tuser3 := *GetUser(\"save3\", Config{})\n\tDB.Create(&user3)\n\n\tif err := DB.First(&User{}, \"name = ?\", \"save3\").Error; err != nil {\n\t\tt.Fatalf(\"failed to find created user\")\n\t}\n\n\tuser3.Name = \"save3_\"\n\tif err := DB.Model(User{Model: user3.Model}).Save(&user3).Error; err != nil {\n\t\tt.Fatalf(\"failed to save user, got %v\", err)\n\t}\n\n\tvar result2 User\n\tif err := DB.First(&result2, \"name = ?\", \"save3_\").Error; err != nil || result2.ID != user3.ID {\n\t\tt.Fatalf(\"failed to find updated user, got %v\", err)\n\t}\n\n\tif err := DB.Model(User{Model: user3.Model}).Save(&struct {\n\t\tgorm.Model\n\t\tPlaceholder string\n\t\tName        string\n\t}{\n\t\tModel:       user3.Model,\n\t\tPlaceholder: \"placeholder\",\n\t\tName:        \"save3__\",\n\t}).Error; err != nil {\n\t\tt.Fatalf(\"failed to update user, got %v\", err)\n\t}\n\n\tvar result3 User\n\tif err := DB.First(&result3, \"name = ?\", \"save3__\").Error; err != nil || result3.ID != user3.ID {\n\t\tt.Fatalf(\"failed to find updated user\")\n\t}\n}\n\nfunc TestSaveWithPrimaryValue(t *testing.T) {\n\tlang := Language{Code: \"save\", Name: \"save\"}\n\tif result := DB.Save(&lang); result.RowsAffected != 1 {\n\t\tt.Errorf(\"should create language, rows affected: %v\", result.RowsAffected)\n\t}\n\n\tvar result Language\n\tDB.First(&result, \"code = ?\", \"save\")\n\tAssertEqual(t, result, lang)\n\n\tlang.Name = \"save name2\"\n\tif result := DB.Save(&lang); result.RowsAffected != 1 {\n\t\tt.Errorf(\"should update language\")\n\t}\n\n\tvar result2 Language\n\tDB.First(&result2, \"code = ?\", \"save\")\n\tAssertEqual(t, result2, lang)\n\n\tDB.Table(\"langs\").Migrator().DropTable(&Language{})\n\tDB.Table(\"langs\").AutoMigrate(&Language{})\n\n\tif err := DB.Table(\"langs\").Save(&lang).Error; err != nil {\n\t\tt.Errorf(\"no error should happen when creating data, but got %v\", err)\n\t}\n\n\tvar result3 Language\n\tif err := DB.Table(\"langs\").First(&result3, \"code = ?\", lang.Code).Error; err != nil || result3.Name != lang.Name {\n\t\tt.Errorf(\"failed to find created record, got error: %v, result: %+v\", err, result3)\n\t}\n\n\tlang.Name += \"name2\"\n\tif err := DB.Table(\"langs\").Save(&lang).Error; err != nil {\n\t\tt.Errorf(\"no error should happen when creating data, but got %v\", err)\n\t}\n\n\tvar result4 Language\n\tif err := DB.Table(\"langs\").First(&result4, \"code = ?\", lang.Code).Error; err != nil || result4.Name != lang.Name {\n\t\tt.Errorf(\"failed to find created record, got error: %v, result: %+v\", err, result4)\n\t}\n}\n\n// only sqlite, postgres, gaussdb, sqlserver support returning\nfunc TestUpdateReturning(t *testing.T) {\n\tif DB.Dialector.Name() != \"sqlite\" && DB.Dialector.Name() != \"postgres\" && DB.Dialector.Name() != \"gaussdb\" && DB.Dialector.Name() != \"sqlserver\" {\n\t\treturn\n\t}\n\n\tusers := []*User{\n\t\tGetUser(\"update-returning-1\", Config{}),\n\t\tGetUser(\"update-returning-2\", Config{}),\n\t\tGetUser(\"update-returning-3\", Config{}),\n\t}\n\tDB.Create(&users)\n\n\tvar results []User\n\tDB.Model(&results).Where(\"name IN ?\", []string{users[0].Name, users[1].Name}).Clauses(clause.Returning{}).Update(\"age\", 88)\n\tif len(results) != 2 || results[0].Age != 88 || results[1].Age != 88 {\n\t\tt.Errorf(\"failed to return updated data, got %v\", results)\n\t}\n\n\tif err := DB.Model(&results[0]).Updates(map[string]interface{}{\"age\": gorm.Expr(\"age + ?\", 100)}).Error; err != nil {\n\t\tt.Errorf(\"Not error should happen when updating with gorm expr, but got %v\", err)\n\t}\n\n\tif err := DB.Model(&results[1]).Clauses(clause.Returning{Columns: []clause.Column{{Name: \"age\"}}}).Updates(map[string]interface{}{\"age\": gorm.Expr(\"age + ?\", 100)}).Error; err != nil {\n\t\tt.Errorf(\"Not error should happen when updating with gorm expr, but got %v\", err)\n\t}\n\n\tif results[1].Age-results[0].Age != 100 {\n\t\tt.Errorf(\"failed to return updated age column\")\n\t}\n}\n\nfunc TestUpdateWithDiffSchema(t *testing.T) {\n\tuser := GetUser(\"update-diff-schema-1\", Config{})\n\tDB.Create(&user)\n\n\ttype UserTemp struct {\n\t\tName string\n\t}\n\n\terr := DB.Model(&user).Updates(&UserTemp{Name: \"update-diff-schema-2\"}).Error\n\tAssertEqual(t, err, nil)\n\tAssertEqual(t, \"update-diff-schema-2\", user.Name)\n}\n\ntype TokenOwner struct {\n\tID    int\n\tName  string\n\tToken Token `gorm:\"foreignKey:UserID\"`\n}\n\nfunc (t *TokenOwner) BeforeSave(tx *gorm.DB) error {\n\tt.Name += \"_name\"\n\treturn nil\n}\n\ntype Token struct {\n\tUserID  int    `gorm:\"primary_key\"`\n\tContent string `gorm:\"type:varchar(100)\"`\n}\n\nfunc (t *Token) BeforeSave(tx *gorm.DB) error {\n\tt.Content += \"_encrypted\"\n\treturn nil\n}\n\nfunc TestSaveWithHooks(t *testing.T) {\n\tDB.Migrator().DropTable(&Token{}, &TokenOwner{})\n\tDB.AutoMigrate(&Token{}, &TokenOwner{})\n\n\tsaveTokenOwner := func(owner *TokenOwner) (*TokenOwner, error) {\n\t\tvar newOwner TokenOwner\n\t\tif err := DB.Transaction(func(tx *gorm.DB) error {\n\t\t\tif err := tx.Session(&gorm.Session{FullSaveAssociations: true}).Save(owner).Error; err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif err := tx.Preload(\"Token\").First(&newOwner, owner.ID).Error; err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\treturn nil\n\t\t}); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn &newOwner, nil\n\t}\n\n\towner := TokenOwner{\n\t\tName:  \"user\",\n\t\tToken: Token{Content: \"token\"},\n\t}\n\to1, err := saveTokenOwner(&owner)\n\tif err != nil {\n\t\tt.Errorf(\"failed to save token owner, got error: %v\", err)\n\t}\n\tif o1.Name != \"user_name\" {\n\t\tt.Errorf(`owner name should be \"user_name\", but got: \"%s\"`, o1.Name)\n\t}\n\tif o1.Token.Content != \"token_encrypted\" {\n\t\tt.Errorf(`token content should be \"token_encrypted\", but got: \"%s\"`, o1.Token.Content)\n\t}\n\n\towner = TokenOwner{\n\t\tID:    owner.ID,\n\t\tName:  \"user\",\n\t\tToken: Token{Content: \"token2\"},\n\t}\n\to2, err := saveTokenOwner(&owner)\n\tif err != nil {\n\t\tt.Errorf(\"failed to save token owner, got error: %v\", err)\n\t}\n\tif o2.Name != \"user_name\" {\n\t\tt.Errorf(`owner name should be \"user_name\", but got: \"%s\"`, o2.Name)\n\t}\n\tif o2.Token.Content != \"token2_encrypted\" {\n\t\tt.Errorf(`token content should be \"token2_encrypted\", but got: \"%s\"`, o2.Token.Content)\n\t}\n}\n\n// only postgres, gaussdb, sqlserver, sqlite support update from\nfunc TestUpdateFrom(t *testing.T) {\n\tif DB.Dialector.Name() != \"postgres\" && DB.Dialector.Name() != \"gaussdb\" && DB.Dialector.Name() != \"sqlite\" && DB.Dialector.Name() != \"sqlserver\" {\n\t\treturn\n\t}\n\n\tusers := []*User{\n\t\tGetUser(\"update-from-1\", Config{Account: true}),\n\t\tGetUser(\"update-from-2\", Config{Account: true}),\n\t\tGetUser(\"update-from-3\", Config{}),\n\t}\n\n\tif err := DB.Create(&users).Error; err != nil {\n\t\tt.Fatalf(\"errors happened when create: %v\", err)\n\t} else if users[0].ID == 0 {\n\t\tt.Fatalf(\"user's primary value should not zero, %v\", users[0].ID)\n\t} else if users[0].UpdatedAt.IsZero() {\n\t\tt.Fatalf(\"user's updated at should not zero, %v\", users[0].UpdatedAt)\n\t}\n\n\tif rowsAffected := DB.Model(&User{}).Clauses(clause.From{Tables: []clause.Table{{Name: \"accounts\"}}}).Where(\"accounts.user_id = users.id AND accounts.number = ? AND accounts.deleted_at IS NULL\", users[0].Account.Number).Update(\"name\", \"franco\").RowsAffected; rowsAffected != 1 {\n\t\tt.Errorf(\"should only update one record, but got %v\", rowsAffected)\n\t}\n\n\tvar result User\n\tif err := DB.Where(\"id = ?\", users[0].ID).First(&result).Error; err != nil {\n\t\tt.Errorf(\"errors happened when query before user: %v\", err)\n\t} else if result.UpdatedAt.UnixNano() == users[0].UpdatedAt.UnixNano() {\n\t\tt.Errorf(\"user's updated at should be changed, but got %v, was %v\", result.UpdatedAt, users[0].UpdatedAt)\n\t} else if result.Name != \"franco\" {\n\t\tt.Errorf(\"user's name should be updated\")\n\t}\n\n\tif rowsAffected := DB.Model(&User{}).Clauses(clause.From{Tables: []clause.Table{{Name: \"accounts\"}}}).Where(\"accounts.user_id = users.id AND accounts.number IN ? AND accounts.deleted_at IS NULL\", []string{users[0].Account.Number, users[1].Account.Number}).Update(\"name\", gorm.Expr(\"accounts.number\")).RowsAffected; rowsAffected != 2 {\n\t\tt.Errorf(\"should update two records, but got %v\", rowsAffected)\n\t}\n\n\tvar results []User\n\tif err := DB.Preload(\"Account\").Find(&results, []uint{users[0].ID, users[1].ID}).Error; err != nil {\n\t\tt.Errorf(\"Not error should happen when finding users, but got %v\", err)\n\t}\n\n\tfor _, user := range results {\n\t\tif user.Name != user.Account.Number {\n\t\t\tt.Errorf(\"user's name should be equal to the account's number %v, but got %v\", user.Account.Number, user.Name)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "tests/upsert_test.go",
    "content": "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/tests\"\n)\n\nfunc TestUpsert(t *testing.T) {\n\tlang := Language{Code: \"upsert\", Name: \"Upsert\"}\n\tif err := DB.Clauses(clause.OnConflict{DoNothing: true}).Create(&lang).Error; err != nil {\n\t\tt.Fatalf(\"failed to upsert, got %v\", err)\n\t}\n\n\tlang2 := Language{Code: \"upsert\", Name: \"Upsert\"}\n\tif err := DB.Clauses(clause.OnConflict{DoNothing: true}).Create(&lang2).Error; err != nil {\n\t\tt.Fatalf(\"failed to upsert, got %v\", err)\n\t}\n\n\tvar langs []Language\n\tif err := DB.Find(&langs, \"code = ?\", lang.Code).Error; err != nil {\n\t\tt.Errorf(\"no error should happen when find languages with code, but got %v\", err)\n\t} else if len(langs) != 1 {\n\t\tt.Errorf(\"should only find only 1 languages, but got %+v\", langs)\n\t}\n\n\tlang3 := Language{Code: \"upsert\", Name: \"Upsert\"}\n\tif err := DB.Clauses(clause.OnConflict{\n\t\tColumns:   []clause.Column{{Name: \"code\"}},\n\t\tDoUpdates: clause.Assignments(map[string]interface{}{\"name\": \"upsert-new\"}),\n\t}).Create(&lang3).Error; err != nil {\n\t\tt.Fatalf(\"failed to upsert, got %v\", err)\n\t}\n\n\tif err := DB.Find(&langs, \"code = ?\", lang.Code).Error; err != nil {\n\t\tt.Errorf(\"no error should happen when find languages with code, but got %v\", err)\n\t} else if len(langs) != 1 {\n\t\tt.Errorf(\"should only find only 1 languages, but got %+v\", langs)\n\t} else if langs[0].Name != \"upsert-new\" {\n\t\tt.Errorf(\"should update name on conflict, but got name %+v\", langs[0].Name)\n\t}\n\n\tlang = Language{Code: \"upsert\", Name: \"Upsert-Newname\"}\n\tif err := DB.Clauses(clause.OnConflict{UpdateAll: true}).Create(&lang).Error; err != nil {\n\t\tt.Fatalf(\"failed to upsert, got %v\", err)\n\t}\n\n\tvar result Language\n\tif err := DB.Find(&result, \"code = ?\", lang.Code).Error; err != nil || result.Name != lang.Name {\n\t\tt.Fatalf(\"failed to upsert, got name %v\", result.Name)\n\t}\n\n\tif name := DB.Dialector.Name(); name != \"sqlserver\" {\n\t\ttype RestrictedLanguage struct {\n\t\t\tCode string `gorm:\"primarykey\"`\n\t\t\tName string\n\t\t\tLang string `gorm:\"<-:create\"`\n\t\t}\n\n\t\tr := DB.Session(&gorm.Session{DryRun: true}).Clauses(clause.OnConflict{UpdateAll: true}).Create(&RestrictedLanguage{Code: \"upsert_code\", Name: \"upsert_name\", Lang: \"upsert_lang\"})\n\t\tif !regexp.MustCompile(`INTO .restricted_languages. .*\\(.code.,.name.,.lang.\\) .* (SET|UPDATE) .name.=.*.name.\\W*$`).MatchString(r.Statement.SQL.String()) {\n\t\t\tt.Errorf(\"Table with escape character, got %v\", r.Statement.SQL.String())\n\t\t}\n\t}\n\n\tuser := *GetUser(\"upsert_on_conflict\", Config{})\n\tuser.Age = 20\n\tif err := DB.Create(&user).Error; err != nil {\n\t\tt.Errorf(\"failed to create user, got error %v\", err)\n\t}\n\n\tvar user2 User\n\tDB.First(&user2, user.ID)\n\tuser2.Age = 30\n\ttime.Sleep(time.Second)\n\tif err := DB.Clauses(clause.OnConflict{UpdateAll: true}).Create(&user2).Error; err != nil {\n\t\tt.Fatalf(\"failed to onconflict create user, got error %v\", err)\n\t} else {\n\t\tvar user3 User\n\t\tDB.First(&user3, user.ID)\n\t\tif user3.UpdatedAt.UnixNano() == user2.UpdatedAt.UnixNano() {\n\t\t\tt.Fatalf(\"failed to update user's updated_at, old: %v, new: %v\", user2.UpdatedAt, user3.UpdatedAt)\n\t\t}\n\t}\n}\n\nfunc TestUpsertSlice(t *testing.T) {\n\tlangs := []Language{\n\t\t{Code: \"upsert-slice1\", Name: \"Upsert-slice1\"},\n\t\t{Code: \"upsert-slice2\", Name: \"Upsert-slice2\"},\n\t\t{Code: \"upsert-slice3\", Name: \"Upsert-slice3\"},\n\t}\n\tDB.Clauses(clause.OnConflict{DoNothing: true}).Create(&langs)\n\n\tvar langs2 []Language\n\tif err := DB.Find(&langs2, \"code LIKE ?\", \"upsert-slice%\").Error; err != nil {\n\t\tt.Errorf(\"no error should happen when find languages with code, but got %v\", err)\n\t} else if len(langs2) != 3 {\n\t\tt.Errorf(\"should only find only 3 languages, but got %+v\", langs2)\n\t}\n\n\tDB.Clauses(clause.OnConflict{DoNothing: true}).Create(&langs)\n\tvar langs3 []Language\n\tif err := DB.Find(&langs3, \"code LIKE ?\", \"upsert-slice%\").Error; err != nil {\n\t\tt.Errorf(\"no error should happen when find languages with code, but got %v\", err)\n\t} else if len(langs3) != 3 {\n\t\tt.Errorf(\"should only find only 3 languages, but got %+v\", langs3)\n\t}\n\n\tfor idx, lang := range langs {\n\t\tlang.Name = lang.Name + \"_new\"\n\t\tlangs[idx] = lang\n\t}\n\n\tif err := DB.Clauses(clause.OnConflict{\n\t\tColumns:   []clause.Column{{Name: \"code\"}},\n\t\tDoUpdates: clause.AssignmentColumns([]string{\"name\"}),\n\t}).Create(&langs).Error; err != nil {\n\t\tt.Fatalf(\"failed to upsert, got %v\", err)\n\t}\n\n\tfor _, lang := range langs {\n\t\tvar results []Language\n\t\tif err := DB.Find(&results, \"code = ?\", lang.Code).Error; err != nil {\n\t\t\tt.Errorf(\"no error should happen when find languages with code, but got %v\", err)\n\t\t} else if len(results) != 1 {\n\t\t\tt.Errorf(\"should only find only 1 languages, but got %+v\", langs)\n\t\t} else if results[0].Name != lang.Name {\n\t\t\tt.Errorf(\"should update name on conflict, but got name %+v\", results[0].Name)\n\t\t}\n\t}\n}\n\nfunc TestUpsertSliceWithReturning(t *testing.T) {\n\tlangs := []Language{\n\t\t{Code: \"upsert-slice1\", Name: \"Upsert-slice1\"},\n\t\t{Code: \"upsert-slice2\", Name: \"Upsert-slice2\"},\n\t\t{Code: \"upsert-slice3\", Name: \"Upsert-slice3\"},\n\t}\n\tDB.Clauses(clause.OnConflict{DoNothing: true}).Create(&langs)\n\n\tvar langs2 []Language\n\tif err := DB.Find(&langs2, \"code LIKE ?\", \"upsert-slice%\").Error; err != nil {\n\t\tt.Errorf(\"no error should happen when find languages with code, but got %v\", err)\n\t} else if len(langs2) != 3 {\n\t\tt.Errorf(\"should only find only 3 languages, but got %+v\", langs2)\n\t}\n\n\tDB.Clauses(clause.OnConflict{DoNothing: true}).Create(&langs)\n\tvar langs3 []Language\n\tif err := DB.Find(&langs3, \"code LIKE ?\", \"upsert-slice%\").Error; err != nil {\n\t\tt.Errorf(\"no error should happen when find languages with code, but got %v\", err)\n\t} else if len(langs3) != 3 {\n\t\tt.Errorf(\"should only find only 3 languages, but got %+v\", langs3)\n\t}\n\n\tfor idx, lang := range langs {\n\t\tlang.Name = lang.Name + \"_new\"\n\t\tlangs[idx] = lang\n\t}\n\n\tif err := DB.Clauses(clause.OnConflict{\n\t\tColumns:   []clause.Column{{Name: \"code\"}},\n\t\tDoUpdates: clause.AssignmentColumns([]string{\"name\"}),\n\t}, clause.Returning{}).CreateInBatches(&langs, len(langs)).Error; err != nil {\n\t\tt.Fatalf(\"failed to upsert, got %v\", err)\n\t}\n\n\tfor _, lang := range langs {\n\t\tvar results []Language\n\t\tif err := DB.Find(&results, \"code = ?\", lang.Code).Error; err != nil {\n\t\t\tt.Errorf(\"no error should happen when find languages with code, but got %v\", err)\n\t\t} else if len(results) != 1 {\n\t\t\tt.Errorf(\"should only find only 1 languages, but got %+v\", langs)\n\t\t} else if results[0].Name != lang.Name {\n\t\t\tt.Errorf(\"should update name on conflict, but got name %+v\", results[0].Name)\n\t\t}\n\t}\n}\n\nfunc TestUpsertWithSave(t *testing.T) {\n\tlangs := []Language{\n\t\t{Code: \"upsert-save-1\", Name: \"Upsert-save-1\"},\n\t\t{Code: \"upsert-save-2\", Name: \"Upsert-save-2\"},\n\t}\n\n\tif err := DB.Save(&langs).Error; err != nil {\n\t\tt.Errorf(\"Failed to create, got error %v\", err)\n\t}\n\n\tfor _, lang := range langs {\n\t\tvar result Language\n\t\tif err := DB.First(&result, \"code = ?\", lang.Code).Error; err != nil {\n\t\t\tt.Errorf(\"Failed to query lang, got error %v\", err)\n\t\t} else {\n\t\t\tAssertEqual(t, result, lang)\n\t\t}\n\t}\n\n\tfor idx, lang := range langs {\n\t\tlang.Name += \"_new\"\n\t\tlangs[idx] = lang\n\t}\n\n\tif err := DB.Save(&langs).Error; err != nil {\n\t\tt.Errorf(\"Failed to upsert, got error %v\", err)\n\t}\n\n\tfor _, lang := range langs {\n\t\tvar result Language\n\t\tif err := DB.First(&result, \"code = ?\", lang.Code).Error; err != nil {\n\t\t\tt.Errorf(\"Failed to query lang, got error %v\", err)\n\t\t} else {\n\t\t\tAssertEqual(t, result, lang)\n\t\t}\n\t}\n\n\tlang := Language{Code: \"upsert-save-3\", Name: \"Upsert-save-3\"}\n\tif err := DB.Save(&lang).Error; err != nil {\n\t\tt.Errorf(\"Failed to create, got error %v\", err)\n\t}\n\n\tvar result Language\n\tif err := DB.First(&result, \"code = ?\", lang.Code).Error; err != nil {\n\t\tt.Errorf(\"Failed to query lang, got error %v\", err)\n\t} else {\n\t\tAssertEqual(t, result, lang)\n\t}\n\n\tlang.Name += \"_new\"\n\tif err := DB.Save(&lang).Error; err != nil {\n\t\tt.Errorf(\"Failed to create, got error %v\", err)\n\t}\n\n\tvar result2 Language\n\tif err := DB.First(&result2, \"code = ?\", lang.Code).Error; err != nil {\n\t\tt.Errorf(\"Failed to query lang, got error %v\", err)\n\t} else {\n\t\tAssertEqual(t, result2, lang)\n\t}\n}\n\nfunc TestFindOrInitialize(t *testing.T) {\n\tvar user1, user2, user3, user4, user5, user6 User\n\tif err := DB.Where(&User{Name: \"find or init\", Age: 33}).FirstOrInit(&user1).Error; err != nil {\n\t\tt.Errorf(\"no error should happen when FirstOrInit, but got %v\", err)\n\t}\n\n\tif user1.Name != \"find or init\" || user1.ID != 0 || user1.Age != 33 {\n\t\tt.Errorf(\"user should be initialized with search value\")\n\t}\n\n\tDB.Where(User{Name: \"find or init\", Age: 33}).FirstOrInit(&user2)\n\tif user2.Name != \"find or init\" || user2.ID != 0 || user2.Age != 33 {\n\t\tt.Errorf(\"user should be initialized with search value\")\n\t}\n\n\tDB.FirstOrInit(&user3, map[string]interface{}{\"name\": \"find or init 2\"})\n\tif user3.Name != \"find or init 2\" || user3.ID != 0 {\n\t\tt.Errorf(\"user should be initialized with inline search value\")\n\t}\n\n\tDB.Where(&User{Name: \"find or init\"}).Attrs(User{Age: 44}).FirstOrInit(&user4)\n\tif user4.Name != \"find or init\" || user4.ID != 0 || user4.Age != 44 {\n\t\tt.Errorf(\"user should be initialized with search value and attrs\")\n\t}\n\n\tDB.Where(&User{Name: \"find or init\"}).Assign(\"age\", 44).FirstOrInit(&user4)\n\tif user4.Name != \"find or init\" || user4.ID != 0 || user4.Age != 44 {\n\t\tt.Errorf(\"user should be initialized with search value and assign attrs\")\n\t}\n\n\tDB.Save(&User{Name: \"find or init\", Age: 33})\n\tDB.Where(&User{Name: \"find or init\"}).Attrs(\"age\", 44).FirstOrInit(&user5)\n\tif user5.Name != \"find or init\" || user5.ID == 0 || user5.Age != 33 {\n\t\tt.Errorf(\"user should be found and not initialized by Attrs\")\n\t}\n\n\tDB.Where(&User{Name: \"find or init\", Age: 33}).FirstOrInit(&user6)\n\tif user6.Name != \"find or init\" || user6.ID == 0 || user6.Age != 33 {\n\t\tt.Errorf(\"user should be found with FirstOrInit\")\n\t}\n\n\tDB.Where(&User{Name: \"find or init\"}).Assign(User{Age: 44}).FirstOrInit(&user6)\n\tif user6.Name != \"find or init\" || user6.ID == 0 || user6.Age != 44 {\n\t\tt.Errorf(\"user should be found and updated with assigned attrs\")\n\t}\n}\n\nfunc TestFindOrCreate(t *testing.T) {\n\tvar user1, user2, user3, user4, user5, user6, user7, user8 User\n\tif err := DB.Where(&User{Name: \"find or create\", Age: 33}).FirstOrCreate(&user1).Error; err != nil {\n\t\tt.Errorf(\"no error should happen when FirstOrInit, but got %v\", err)\n\t}\n\n\tif user1.Name != \"find or create\" || user1.ID == 0 || user1.Age != 33 {\n\t\tt.Errorf(\"user should be created with search value\")\n\t}\n\n\tDB.Where(&User{Name: \"find or create\", Age: 33}).FirstOrCreate(&user2)\n\tif user1.ID != user2.ID || user2.Name != \"find or create\" || user2.ID == 0 || user2.Age != 33 {\n\t\tt.Errorf(\"user should be created with search value\")\n\t}\n\n\tDB.FirstOrCreate(&user3, map[string]interface{}{\"name\": \"find or create 2\"})\n\tif user3.Name != \"find or create 2\" || user3.ID == 0 {\n\t\tt.Errorf(\"user should be created with inline search value\")\n\t}\n\n\tDB.Where(&User{Name: \"find or create 3\"}).Attrs(\"age\", 44).FirstOrCreate(&user4)\n\tif user4.Name != \"find or create 3\" || user4.ID == 0 || user4.Age != 44 {\n\t\tt.Errorf(\"user should be created with search value and attrs\")\n\t}\n\n\tupdatedAt1 := user4.UpdatedAt\n\tDB.Where(&User{Name: \"find or create 3\"}).Assign(\"age\", 55).FirstOrCreate(&user4)\n\n\tif user4.Age != 55 {\n\t\tt.Errorf(\"Failed to set change to 55, got %v\", user4.Age)\n\t}\n\n\tif updatedAt1.Format(time.RFC3339Nano) == user4.UpdatedAt.Format(time.RFC3339Nano) {\n\t\tt.Errorf(\"UpdateAt should be changed when update values with assign\")\n\t}\n\n\tDB.Where(&User{Name: \"find or create 4\"}).Assign(User{Age: 44}).FirstOrCreate(&user4)\n\tif user4.Name != \"find or create 4\" || user4.ID == 0 || user4.Age != 44 {\n\t\tt.Errorf(\"user should be created with search value and assigned attrs\")\n\t}\n\n\tDB.Where(&User{Name: \"find or create\"}).Attrs(\"age\", 44).FirstOrInit(&user5)\n\tif user5.Name != \"find or create\" || user5.ID == 0 || user5.Age != 33 {\n\t\tt.Errorf(\"user should be found and not initialized by Attrs\")\n\t}\n\n\tDB.Where(&User{Name: \"find or create\"}).Assign(User{Age: 44}).FirstOrCreate(&user6)\n\tif user6.Name != \"find or create\" || user6.ID == 0 || user6.Age != 44 {\n\t\tt.Errorf(\"user should be found and updated with assigned attrs\")\n\t}\n\n\tDB.Where(&User{Name: \"find or create\"}).Find(&user7)\n\tif user7.Name != \"find or create\" || user7.ID == 0 || user7.Age != 44 {\n\t\tt.Errorf(\"user should be found and updated with assigned attrs\")\n\t}\n\n\tDB.Where(&User{Name: \"find or create embedded struct\"}).Assign(User{Age: 44, Account: Account{Number: \"1231231231\"}, Pets: []*Pet{{Name: \"first_or_create_pet1\"}, {Name: \"first_or_create_pet2\"}}}).FirstOrCreate(&user8)\n\tif err := DB.Where(\"name = ?\", \"first_or_create_pet1\").First(&Pet{}).Error; err != nil {\n\t\tt.Errorf(\"has many association should be saved\")\n\t}\n\n\tif err := DB.Where(\"number = ?\", \"1231231231\").First(&Account{}).Error; err != nil {\n\t\tt.Errorf(\"belongs to association should be saved\")\n\t}\n}\n\nfunc TestUpdateWithMissWhere(t *testing.T) {\n\ttype User struct {\n\t\tID   uint   `gorm:\"column:id;<-:create\"`\n\t\tName string `gorm:\"column:name\"`\n\t}\n\tuser := User{ID: 1, Name: \"king\"}\n\ttx := DB.Session(&gorm.Session{DryRun: true}).Save(&user)\n\n\tif err := tx.Error; err != nil {\n\t\tt.Fatalf(\"failed to update user,missing where condition,err=%+v\", err)\n\t}\n\n\tif !regexp.MustCompile(\"WHERE .id. = [^ ]+$\").MatchString(tx.Statement.SQL.String()) {\n\t\tt.Fatalf(\"invalid updating SQL, got %v\", tx.Statement.SQL.String())\n\t}\n}\n"
  },
  {
    "path": "utils/tests/dummy_dialecter.go",
    "content": "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.io/gorm/schema\"\n)\n\ntype DummyDialector struct {\n\tTranslatedErr error\n}\n\nfunc (DummyDialector) Name() string {\n\treturn \"dummy\"\n}\n\nfunc (DummyDialector) Initialize(db *gorm.DB) error {\n\tcallbacks.RegisterDefaultCallbacks(db, &callbacks.Config{\n\t\tCreateClauses:        []string{\"INSERT\", \"VALUES\", \"ON CONFLICT\", \"RETURNING\"},\n\t\tUpdateClauses:        []string{\"UPDATE\", \"SET\", \"WHERE\", \"RETURNING\"},\n\t\tDeleteClauses:        []string{\"DELETE\", \"FROM\", \"WHERE\", \"RETURNING\"},\n\t\tLastInsertIDReversed: true,\n\t})\n\n\treturn nil\n}\n\nfunc (DummyDialector) DefaultValueOf(field *schema.Field) clause.Expression {\n\treturn clause.Expr{SQL: \"DEFAULT\"}\n}\n\nfunc (DummyDialector) Migrator(*gorm.DB) gorm.Migrator {\n\treturn nil\n}\n\nfunc (DummyDialector) BindVarTo(writer clause.Writer, stmt *gorm.Statement, v interface{}) {\n\twriter.WriteByte('?')\n}\n\nfunc (DummyDialector) QuoteTo(writer clause.Writer, str string) {\n\tvar (\n\t\tunderQuoted, selfQuoted bool\n\t\tcontinuousBacktick      int8\n\t\tshiftDelimiter          int8\n\t)\n\n\tfor _, v := range []byte(str) {\n\t\tswitch v {\n\t\tcase '`':\n\t\t\tcontinuousBacktick++\n\t\t\tif continuousBacktick == 2 {\n\t\t\t\twriter.WriteString(\"``\")\n\t\t\t\tcontinuousBacktick = 0\n\t\t\t}\n\t\tcase '.':\n\t\t\tif continuousBacktick > 0 || !selfQuoted {\n\t\t\t\tshiftDelimiter = 0\n\t\t\t\tunderQuoted = false\n\t\t\t\tcontinuousBacktick = 0\n\t\t\t\twriter.WriteByte('`')\n\t\t\t}\n\t\t\twriter.WriteByte(v)\n\t\t\tcontinue\n\t\tdefault:\n\t\t\tif shiftDelimiter-continuousBacktick <= 0 && !underQuoted {\n\t\t\t\twriter.WriteByte('`')\n\t\t\t\tunderQuoted = true\n\t\t\t\tif selfQuoted = continuousBacktick > 0; selfQuoted {\n\t\t\t\t\tcontinuousBacktick -= 1\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfor ; continuousBacktick > 0; continuousBacktick -= 1 {\n\t\t\t\twriter.WriteString(\"``\")\n\t\t\t}\n\n\t\t\twriter.WriteByte(v)\n\t\t}\n\t\tshiftDelimiter++\n\t}\n\n\tif continuousBacktick > 0 && !selfQuoted {\n\t\twriter.WriteString(\"``\")\n\t}\n\twriter.WriteByte('`')\n}\n\nfunc (DummyDialector) Explain(sql string, vars ...interface{}) string {\n\treturn logger.ExplainSQL(sql, nil, `\"`, vars...)\n}\n\nfunc (DummyDialector) DataTypeOf(*schema.Field) string {\n\treturn \"\"\n}\n\nfunc (d DummyDialector) Translate(err error) error {\n\treturn d.TranslatedErr\n}\n"
  },
  {
    "path": "utils/tests/models.go",
    "content": "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` (has many) and `Toys` (has many - polymorphic)\n// He works in a Company (belongs to), he has a Manager (belongs to - single-table), and also managed a Team (has many - single-table)\n// He speaks many languages (many to many) and has many friends (many to many - single-table)\n// His pet also has one Toy (has one - polymorphic)\n// NamedPet is a reference to a named `Pet` (has one)\ntype User struct {\n\tgorm.Model\n\tName      string\n\tAge       uint\n\tBirthday  *time.Time\n\tAccount   Account\n\tPets      []*Pet\n\tNamedPet  *Pet\n\tToys      []Toy   `gorm:\"polymorphic:Owner\"`\n\tTools     []Tools `gorm:\"polymorphicType:Type;polymorphicId:CustomID\"`\n\tCompanyID *int\n\tCompany   Company\n\tManagerID *uint\n\tManager   *User\n\tTeam      []User     `gorm:\"foreignkey:ManagerID\"`\n\tLanguages []Language `gorm:\"many2many:UserSpeak;\"`\n\tFriends   []*User    `gorm:\"many2many:user_friends;\"`\n\tActive    bool\n}\n\ntype Account struct {\n\tgorm.Model\n\tUserID sql.NullInt64\n\tNumber string\n}\n\ntype Pet struct {\n\tgorm.Model\n\tUserID *uint\n\tName   string\n\tToy    Toy `gorm:\"polymorphic:Owner;\"`\n}\n\ntype Toy struct {\n\tgorm.Model\n\tName      string\n\tOwnerID   string\n\tOwnerType string\n}\n\ntype Tools struct {\n\tgorm.Model\n\tName     string\n\tCustomID string\n\tType     string\n}\n\ntype Company struct {\n\tID   int\n\tName string\n}\n\ntype Language struct {\n\tCode string `gorm:\"primarykey\"`\n\tName string\n}\n\ntype Coupon struct {\n\tID               int              `gorm:\"primarykey; size:255\"`\n\tAppliesToProduct []*CouponProduct `gorm:\"foreignKey:CouponId;constraint:OnDelete:CASCADE\"`\n\tAmountOff        uint32           `gorm:\"column:amount_off\"`\n\tPercentOff       float32          `gorm:\"column:percent_off\"`\n}\n\ntype CouponProduct struct {\n\tCouponId  int    `gorm:\"primarykey;size:255\"`\n\tProductId string `gorm:\"primarykey;size:255\"`\n\tDesc      string\n}\n\ntype Order struct {\n\tgorm.Model\n\tNum      string\n\tCoupon   *Coupon\n\tCouponID string\n}\n\ntype Parent struct {\n\tgorm.Model\n\tFavChildID uint\n\tFavChild   *Child\n\tChildren   []*Child\n}\n\ntype Child struct {\n\tgorm.Model\n\tName     string\n\tParentID *uint\n\tParent   *Parent\n}\n"
  },
  {
    "path": "utils/tests/utils.go",
    "content": "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\nfunc AssertObjEqual(t *testing.T, r, e interface{}, names ...string) {\n\tfor _, name := range names {\n\t\trv := reflect.Indirect(reflect.ValueOf(r))\n\t\tev := reflect.Indirect(reflect.ValueOf(e))\n\t\tif rv.IsValid() != ev.IsValid() {\n\t\t\tt.Errorf(\"%v: expect: %+v, got %+v\", utils.FileWithLineNum(), r, e)\n\t\t\treturn\n\t\t}\n\t\tgot := rv.FieldByName(name).Interface()\n\t\texpect := ev.FieldByName(name).Interface()\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tAssertEqual(t, got, expect)\n\t\t})\n\t}\n}\n\nfunc AssertEqual(t *testing.T, got, expect interface{}) {\n\tif !reflect.DeepEqual(got, expect) {\n\t\tisEqual := func() {\n\t\t\tif curTime, ok := got.(time.Time); ok {\n\t\t\t\tformat := \"2006-01-02T15:04:05Z07:00\"\n\n\t\t\t\tif curTime.Round(time.Second).UTC().Format(format) != expect.(time.Time).Round(time.Second).UTC().Format(format) && curTime.Truncate(time.Second).UTC().Format(format) != expect.(time.Time).Truncate(time.Second).UTC().Format(format) {\n\t\t\t\t\tt.Errorf(\"%v: expect: %v, got %v after time round\", utils.FileWithLineNum(), expect.(time.Time), curTime)\n\t\t\t\t}\n\t\t\t} else if fmt.Sprint(got) != fmt.Sprint(expect) {\n\t\t\t\tt.Errorf(\"%v: expect: %#v, got %#v\", utils.FileWithLineNum(), expect, got)\n\t\t\t}\n\t\t}\n\n\t\tif fmt.Sprint(got) == fmt.Sprint(expect) {\n\t\t\treturn\n\t\t}\n\n\t\tif reflect.Indirect(reflect.ValueOf(got)).IsValid() != reflect.Indirect(reflect.ValueOf(expect)).IsValid() {\n\t\t\tt.Errorf(\"%v: expect: %+v, got %+v\", utils.FileWithLineNum(), expect, got)\n\t\t\treturn\n\t\t}\n\n\t\tif valuer, ok := got.(driver.Valuer); ok {\n\t\t\tgot, _ = valuer.Value()\n\t\t}\n\n\t\tif valuer, ok := expect.(driver.Valuer); ok {\n\t\t\texpect, _ = valuer.Value()\n\t\t}\n\n\t\tif got != nil {\n\t\t\tgot = reflect.Indirect(reflect.ValueOf(got)).Interface()\n\t\t}\n\n\t\tif expect != nil {\n\t\t\texpect = reflect.Indirect(reflect.ValueOf(expect)).Interface()\n\t\t}\n\n\t\tif reflect.ValueOf(got).IsValid() != reflect.ValueOf(expect).IsValid() {\n\t\t\tt.Errorf(\"%v: expect: %+v, got %+v\", utils.FileWithLineNum(), expect, got)\n\t\t\treturn\n\t\t}\n\n\t\tif reflect.ValueOf(got).Kind() == reflect.Slice {\n\t\t\tif reflect.ValueOf(expect).Kind() == reflect.Slice {\n\t\t\t\tif reflect.ValueOf(got).Len() == reflect.ValueOf(expect).Len() {\n\t\t\t\t\tfor i := 0; i < reflect.ValueOf(got).Len(); i++ {\n\t\t\t\t\t\tname := fmt.Sprintf(reflect.ValueOf(got).Type().Name()+\" #%v\", i)\n\t\t\t\t\t\tt.Run(name, func(t *testing.T) {\n\t\t\t\t\t\t\tAssertEqual(t, reflect.ValueOf(got).Index(i).Interface(), reflect.ValueOf(expect).Index(i).Interface())\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tname := reflect.ValueOf(got).Type().Elem().Name()\n\t\t\t\t\tt.Errorf(\"%v expects length: %v, got %v (expects: %+v, got %+v)\", name, reflect.ValueOf(expect).Len(), reflect.ValueOf(got).Len(), expect, got)\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\tif reflect.ValueOf(got).Kind() == reflect.Struct {\n\t\t\tif reflect.ValueOf(expect).Kind() == reflect.Struct {\n\t\t\t\tif reflect.ValueOf(got).NumField() == reflect.ValueOf(expect).NumField() {\n\t\t\t\t\texported := false\n\t\t\t\t\tfor i := 0; i < reflect.ValueOf(got).NumField(); i++ {\n\t\t\t\t\t\tif fieldStruct := reflect.ValueOf(got).Type().Field(i); ast.IsExported(fieldStruct.Name) {\n\t\t\t\t\t\t\texported = true\n\t\t\t\t\t\t\tfield := reflect.ValueOf(got).Field(i)\n\t\t\t\t\t\t\tt.Run(fieldStruct.Name, func(t *testing.T) {\n\t\t\t\t\t\t\t\tAssertEqual(t, field.Interface(), reflect.ValueOf(expect).Field(i).Interface())\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif exported {\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif reflect.ValueOf(got).Type().ConvertibleTo(reflect.ValueOf(expect).Type()) {\n\t\t\tgot = reflect.ValueOf(got).Convert(reflect.ValueOf(expect).Type()).Interface()\n\t\t\tisEqual()\n\t\t} else if reflect.ValueOf(expect).Type().ConvertibleTo(reflect.ValueOf(got).Type()) {\n\t\t\texpect = reflect.ValueOf(got).Convert(reflect.ValueOf(got).Type()).Interface()\n\t\t\tisEqual()\n\t\t} else {\n\t\t\tt.Errorf(\"%v: expect: %+v, got %+v\", utils.FileWithLineNum(), expect, got)\n\t\t\treturn\n\t\t}\n\t}\n}\n\nfunc Now() *time.Time {\n\tnow := time.Now()\n\treturn &now\n}\n"
  },
  {
    "path": "utils/utils.go",
    "content": "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\"unicode\"\n)\n\nvar gormSourceDir string\n\nfunc init() {\n\t_, file, _, _ := runtime.Caller(0)\n\t// compatible solution to get gorm source directory with various operating systems\n\tgormSourceDir = sourceDir(file)\n}\n\nfunc sourceDir(file string) string {\n\tdir := filepath.Dir(file)\n\tdir = filepath.Dir(dir)\n\n\ts := filepath.Dir(dir)\n\tif filepath.Base(s) != \"gorm.io\" {\n\t\ts = dir\n\t}\n\treturn filepath.ToSlash(s) + \"/\"\n}\n\n// CallerFrame retrieves the first relevant stack frame outside of GORM's internal implementation files.\n// It skips:\n//   - GORM's core source files (identified by gormSourceDir prefix)\n//   - Exclude test files (*_test.go)\n//   - go-gorm/gen's Generated files (*.gen.go)\nfunc CallerFrame() runtime.Frame {\n\tpcs := [13]uintptr{}\n\t// the third caller usually from gorm internal\n\tlen := runtime.Callers(3, pcs[:])\n\tframes := runtime.CallersFrames(pcs[:len])\n\tfor i := 0; i < len; i++ {\n\t\t// second return value is \"more\", not \"ok\"\n\t\tframe, _ := frames.Next()\n\t\tif (!strings.HasPrefix(frame.File, gormSourceDir) ||\n\t\t\tstrings.HasSuffix(frame.File, \"_test.go\")) && !strings.HasSuffix(frame.File, \".gen.go\") {\n\t\t\treturn frame\n\t\t}\n\t}\n\n\treturn runtime.Frame{}\n}\n\n// FileWithLineNum return the file name and line number of the current file\nfunc FileWithLineNum() string {\n\tframe := CallerFrame()\n\tif frame.PC != 0 {\n\t\treturn string(strconv.AppendInt(append([]byte(frame.File), ':'), int64(frame.Line), 10))\n\t}\n\n\treturn \"\"\n}\n\nfunc IsInvalidDBNameChar(c rune) bool {\n\treturn !unicode.IsLetter(c) && !unicode.IsNumber(c) && c != '.' && c != '*' && c != '_' && c != '$' && c != '@'\n}\n\n// CheckTruth check string true or not\nfunc CheckTruth(vals ...string) bool {\n\tfor _, val := range vals {\n\t\tif val != \"\" && !strings.EqualFold(val, \"false\") {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc ToStringKey(values ...interface{}) string {\n\tresults := make([]string, len(values))\n\n\tfor idx, value := range values {\n\t\tif valuer, ok := value.(driver.Valuer); ok {\n\t\t\tvalue, _ = valuer.Value()\n\t\t}\n\n\t\tswitch v := value.(type) {\n\t\tcase string:\n\t\t\tresults[idx] = v\n\t\tcase []byte:\n\t\t\tresults[idx] = string(v)\n\t\tcase uint:\n\t\t\tresults[idx] = strconv.FormatUint(uint64(v), 10)\n\t\tdefault:\n\t\t\tresults[idx] = \"nil\"\n\t\t\tvv := reflect.ValueOf(v)\n\t\t\tif vv.IsValid() && !vv.IsZero() {\n\t\t\t\tresults[idx] = fmt.Sprint(reflect.Indirect(vv).Interface())\n\t\t\t}\n\t\t}\n\t}\n\n\treturn strings.Join(results, \"_\")\n}\n\nfunc Contains(elems []string, elem string) bool {\n\tfor _, e := range elems {\n\t\tif elem == e {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc AssertEqual(x, y interface{}) bool {\n\tif reflect.DeepEqual(x, y) {\n\t\treturn true\n\t}\n\tif x == nil || y == nil {\n\t\treturn false\n\t}\n\n\txval := reflect.ValueOf(x)\n\tyval := reflect.ValueOf(y)\n\tif xval.Kind() == reflect.Ptr && xval.IsNil() ||\n\t\tyval.Kind() == reflect.Ptr && yval.IsNil() {\n\t\treturn false\n\t}\n\n\tif valuer, ok := x.(driver.Valuer); ok {\n\t\tx, _ = valuer.Value()\n\t}\n\tif valuer, ok := y.(driver.Valuer); ok {\n\t\ty, _ = valuer.Value()\n\t}\n\treturn reflect.DeepEqual(x, y)\n}\n\nfunc ToString(value interface{}) string {\n\tswitch v := value.(type) {\n\tcase string:\n\t\treturn v\n\tcase int:\n\t\treturn strconv.FormatInt(int64(v), 10)\n\tcase int8:\n\t\treturn strconv.FormatInt(int64(v), 10)\n\tcase int16:\n\t\treturn strconv.FormatInt(int64(v), 10)\n\tcase int32:\n\t\treturn strconv.FormatInt(int64(v), 10)\n\tcase int64:\n\t\treturn strconv.FormatInt(v, 10)\n\tcase uint:\n\t\treturn strconv.FormatUint(uint64(v), 10)\n\tcase uint8:\n\t\treturn strconv.FormatUint(uint64(v), 10)\n\tcase uint16:\n\t\treturn strconv.FormatUint(uint64(v), 10)\n\tcase uint32:\n\t\treturn strconv.FormatUint(uint64(v), 10)\n\tcase uint64:\n\t\treturn strconv.FormatUint(v, 10)\n\t}\n\treturn \"\"\n}\n\nconst nestedRelationSplit = \"__\"\n\n// NestedRelationName nested relationships like `Manager__Company`\nfunc NestedRelationName(prefix, name string) string {\n\treturn prefix + nestedRelationSplit + name\n}\n\n// SplitNestedRelationName Split nested relationships to `[]string{\"Manager\",\"Company\"}`\nfunc SplitNestedRelationName(name string) []string {\n\treturn strings.Split(name, nestedRelationSplit)\n}\n\n// JoinNestedRelationNames nested relationships like `Manager__Company`\nfunc JoinNestedRelationNames(relationNames []string) string {\n\treturn strings.Join(relationNames, nestedRelationSplit)\n}\n\n// RTrimSlice Right trims the given slice by given length\nfunc RTrimSlice[T any](v []T, trimLen int) []T {\n\tif trimLen >= len(v) { // trimLen greater than slice len means fully sliced\n\t\treturn v[:0]\n\t}\n\tif trimLen < 0 { // negative trimLen is ignored\n\t\treturn v[:]\n\t}\n\treturn v[:len(v)-trimLen]\n}\n"
  },
  {
    "path": "utils/utils_test.go",
    "content": "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 TestIsInvalidDBNameChar(t *testing.T) {\n\tfor _, db := range []string{\"db\", \"dbName\", \"db_name\", \"db1\", \"1dbname\", \"db$name\"} {\n\t\tif fields := strings.FieldsFunc(db, IsInvalidDBNameChar); len(fields) != 1 {\n\t\t\tt.Fatalf(\"failed to parse db name %v\", db)\n\t\t}\n\t}\n}\n\nfunc TestCheckTruth(t *testing.T) {\n\tcheckTruthTests := []struct {\n\t\tv   string\n\t\tout bool\n\t}{\n\t\t{\"123\", true},\n\t\t{\"true\", true},\n\t\t{\"\", false},\n\t\t{\"false\", false},\n\t\t{\"False\", false},\n\t\t{\"FALSE\", false},\n\t\t{\"\\u0046alse\", false},\n\t}\n\n\tfor _, test := range checkTruthTests {\n\t\tt.Run(test.v, func(t *testing.T) {\n\t\t\tif out := CheckTruth(test.v); out != test.out {\n\t\t\t\tt.Errorf(\"CheckTruth(%s) want: %t, got: %t\", test.v, test.out, out)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestToStringKey(t *testing.T) {\n\tcases := []struct {\n\t\tvalues []interface{}\n\t\tkey    string\n\t}{\n\t\t{[]interface{}{\"a\"}, \"a\"},\n\t\t{[]interface{}{1, 2, 3}, \"1_2_3\"},\n\t\t{[]interface{}{1, nil, 3}, \"1_nil_3\"},\n\t\t{[]interface{}{[]interface{}{1, 2, 3}}, \"[1 2 3]\"},\n\t\t{[]interface{}{[]interface{}{\"1\", \"2\", \"3\"}}, \"[1 2 3]\"},\n\t\t{[]interface{}{[]interface{}{\"1\", nil, \"3\"}}, \"[1 <nil> 3]\"},\n\t}\n\tfor _, c := range cases {\n\t\tif key := ToStringKey(c.values...); key != c.key {\n\t\t\tt.Errorf(\"%v: expected %v, got %v\", c.values, c.key, key)\n\t\t}\n\t}\n}\n\nfunc TestContains(t *testing.T) {\n\tcontainsTests := []struct {\n\t\tname  string\n\t\telems []string\n\t\telem  string\n\t\tout   bool\n\t}{\n\t\t{\"exists\", []string{\"1\", \"2\", \"3\"}, \"1\", true},\n\t\t{\"not exists\", []string{\"1\", \"2\", \"3\"}, \"4\", false},\n\t}\n\tfor _, test := range containsTests {\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tif out := Contains(test.elems, test.elem); test.out != out {\n\t\t\t\tt.Errorf(\"Contains(%v, %s) want: %t, got: %t\", test.elems, test.elem, test.out, out)\n\t\t\t}\n\t\t})\n\t}\n}\n\ntype ModifyAt sql.NullTime\n\n// Value return a Unix time.\nfunc (n ModifyAt) Value() (driver.Value, error) {\n\tif !n.Valid {\n\t\treturn nil, nil\n\t}\n\treturn n.Time.Unix(), nil\n}\n\nfunc TestAssertEqual(t *testing.T) {\n\tnow := time.Now()\n\tassertEqualTests := []struct {\n\t\tname     string\n\t\tsrc, dst interface{}\n\t\tout      bool\n\t}{\n\t\t{\"error equal\", errors.New(\"1\"), errors.New(\"1\"), true},\n\t\t{\"error not equal\", errors.New(\"1\"), errors.New(\"2\"), false},\n\t\t{\"driver.Valuer equal\", ModifyAt{Time: now, Valid: true}, ModifyAt{Time: now, Valid: true}, true},\n\t\t{\"driver.Valuer not equal\", ModifyAt{Time: now, Valid: true}, ModifyAt{Time: now.Add(time.Second), Valid: true}, false},\n\t\t{\"driver.Valuer equal (ptr to nil ptr)\", (*ModifyAt)(nil), &ModifyAt{}, false},\n\t}\n\tfor _, test := range assertEqualTests {\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tif out := AssertEqual(test.src, test.dst); test.out != out {\n\t\t\t\tt.Errorf(\"AssertEqual(%v, %v) want: %t, got: %t\", test.src, test.dst, test.out, out)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestToString(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\tin   interface{}\n\t\tout  string\n\t}{\n\t\t{\"int\", math.MaxInt64, \"9223372036854775807\"},\n\t\t{\"int8\", int8(math.MaxInt8), \"127\"},\n\t\t{\"int16\", int16(math.MaxInt16), \"32767\"},\n\t\t{\"int32\", int32(math.MaxInt32), \"2147483647\"},\n\t\t{\"int64\", int64(math.MaxInt64), \"9223372036854775807\"},\n\t\t{\"uint\", uint(math.MaxUint64), \"18446744073709551615\"},\n\t\t{\"uint8\", uint8(math.MaxUint8), \"255\"},\n\t\t{\"uint16\", uint16(math.MaxUint16), \"65535\"},\n\t\t{\"uint32\", uint32(math.MaxUint32), \"4294967295\"},\n\t\t{\"uint64\", uint64(math.MaxUint64), \"18446744073709551615\"},\n\t\t{\"string\", \"abc\", \"abc\"},\n\t\t{\"other\", true, \"\"},\n\t}\n\tfor _, test := range tests {\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tif out := ToString(test.in); test.out != out {\n\t\t\t\tt.Fatalf(\"ToString(%v) want: %s, got: %s\", test.in, test.out, out)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestRTrimSlice(t *testing.T) {\n\ttests := []struct {\n\t\tname     string\n\t\tinput    []int\n\t\ttrimLen  int\n\t\texpected []int\n\t}{\n\t\t{\n\t\t\tname:     \"Trim two elements from end\",\n\t\t\tinput:    []int{1, 2, 3, 4, 5},\n\t\t\ttrimLen:  2,\n\t\t\texpected: []int{1, 2, 3},\n\t\t},\n\t\t{\n\t\t\tname:     \"Trim entire slice\",\n\t\t\tinput:    []int{1, 2, 3},\n\t\t\ttrimLen:  3,\n\t\t\texpected: []int{},\n\t\t},\n\t\t{\n\t\t\tname:     \"Trim length greater than slice length\",\n\t\t\tinput:    []int{1, 2, 3},\n\t\t\ttrimLen:  5,\n\t\t\texpected: []int{},\n\t\t},\n\t\t{\n\t\t\tname:     \"Zero trim length\",\n\t\t\tinput:    []int{1, 2, 3},\n\t\t\ttrimLen:  0,\n\t\t\texpected: []int{1, 2, 3},\n\t\t},\n\t\t{\n\t\t\tname:     \"Trim one element from end\",\n\t\t\tinput:    []int{1, 2, 3},\n\t\t\ttrimLen:  1,\n\t\t\texpected: []int{1, 2},\n\t\t},\n\t\t{\n\t\t\tname:     \"Empty slice\",\n\t\t\tinput:    []int{},\n\t\t\ttrimLen:  2,\n\t\t\texpected: []int{},\n\t\t},\n\t\t{\n\t\t\tname:     \"Negative trim length (should be treated as zero)\",\n\t\t\tinput:    []int{1, 2, 3},\n\t\t\ttrimLen:  -1,\n\t\t\texpected: []int{1, 2, 3},\n\t\t},\n\t}\n\n\tfor _, testcase := range tests {\n\t\tt.Run(testcase.name, func(t *testing.T) {\n\t\t\tresult := RTrimSlice(testcase.input, testcase.trimLen)\n\t\t\tif !AssertEqual(result, testcase.expected) {\n\t\t\t\tt.Errorf(\"RTrimSlice(%v, %d) = %v; want %v\", testcase.input, testcase.trimLen, result, testcase.expected)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "utils/utils_unix_test.go",
    "content": "//go:build unix\n// +build unix\n\npackage utils\n\nimport (\n\t\"testing\"\n)\n\nfunc TestSourceDir(t *testing.T) {\n\tcases := []struct {\n\t\tfile string\n\t\twant string\n\t}{\n\t\t{\n\t\t\tfile: \"/Users/name/go/pkg/mod/gorm.io/gorm@v1.2.3/utils/utils.go\",\n\t\t\twant: \"/Users/name/go/pkg/mod/gorm.io/\",\n\t\t},\n\t\t{\n\t\t\tfile: \"/go/work/proj/gorm/utils/utils.go\",\n\t\t\twant: \"/go/work/proj/gorm/\",\n\t\t},\n\t\t{\n\t\t\tfile: \"/go/work/proj/gorm_alias/utils/utils.go\",\n\t\t\twant: \"/go/work/proj/gorm_alias/\",\n\t\t},\n\t\t{\n\t\t\tfile: \"/go/work/proj/my.gorm.io/gorm@v1.2.3/utils/utils.go\",\n\t\t\twant: \"/go/work/proj/my.gorm.io/gorm@v1.2.3/\",\n\t\t},\n\t}\n\tfor _, c := range cases {\n\t\ts := sourceDir(c.file)\n\t\tif s != c.want {\n\t\t\tt.Fatalf(\"%s: expected %s, got %s\", c.file, c.want, s)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "utils/utils_windows_test.go",
    "content": "package utils\n\nimport (\n\t\"testing\"\n)\n\nfunc TestSourceDir(t *testing.T) {\n\tcases := []struct {\n\t\tfile string\n\t\twant string\n\t}{\n\t\t{\n\t\t\tfile: `C:/Users/name/go/pkg/mod/gorm.io/gorm@v1.2.3/utils/utils.go`,\n\t\t\twant: `C:/Users/name/go/pkg/mod/gorm.io/`,\n\t\t},\n\t\t{\n\t\t\tfile: `C:/go/work/proj/gorm/utils/utils.go`,\n\t\t\twant: `C:/go/work/proj/gorm/`,\n\t\t},\n\t\t{\n\t\t\tfile: `C:/go/work/proj/gorm_alias/utils/utils.go`,\n\t\t\twant: `C:/go/work/proj/gorm_alias/`,\n\t\t},\n\t\t{\n\t\t\tfile: `C:/go/work/proj/my.gorm.io/gorm@v1.2.3/utils/utils.go`,\n\t\t\twant: `C:/go/work/proj/my.gorm.io/gorm@v1.2.3/`,\n\t\t},\n\t}\n\tfor _, c := range cases {\n\t\ts := sourceDir(c.file)\n\t\tif s != c.want {\n\t\t\tt.Fatalf(\"%s: expected %s, got %s\", c.file, c.want, s)\n\t\t}\n\t}\n}\n"
  }
]